summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am110
-rw-r--r--src/daemon/caps.c15
-rw-r--r--src/daemon/cmdline.c5
-rw-r--r--src/daemon/daemon-conf.c40
-rw-r--r--src/daemon/daemon.conf.in12
-rw-r--r--src/daemon/main.c66
-rw-r--r--src/daemon/polkit.c172
-rw-r--r--src/daemon/pulseaudio-system.conf37
-rw-r--r--src/map-file7
-rw-r--r--src/modules/alsa/alsa-mixer.c49
-rw-r--r--src/modules/alsa/alsa-mixer.h2
-rw-r--r--src/modules/alsa/alsa-sink.c324
-rw-r--r--src/modules/alsa/alsa-source.c306
-rw-r--r--src/modules/alsa/alsa-util.c250
-rw-r--r--src/modules/alsa/alsa-util.h10
l---------src/modules/alsa/mixer/Makefile1
l---------src/modules/alsa/mixer/paths/Makefile1
-rw-r--r--src/modules/alsa/mixer/paths/analog-input.conf.common8
-rw-r--r--src/modules/alsa/mixer/paths/analog-output-headphones.conf4
-rw-r--r--src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf13
-rw-r--r--src/modules/alsa/mixer/paths/analog-output-mono.conf13
-rw-r--r--src/modules/alsa/mixer/paths/analog-output.conf13
-rw-r--r--src/modules/alsa/mixer/paths/analog-output.conf.common4
l---------src/modules/alsa/mixer/profile-sets/Makefile1
-rw-r--r--src/modules/alsa/module-alsa-card.c2
-rw-r--r--src/modules/bluetooth/bluetooth-util.c61
-rw-r--r--src/modules/bluetooth/bluetooth-util.h10
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c233
-rw-r--r--src/modules/bluetooth/module-bluetooth-discover.c7
-rw-r--r--src/modules/gconf/module-gconf.c9
-rw-r--r--src/modules/hal-util.c2
-rw-r--r--src/modules/module-always-sink.c6
-rw-r--r--src/modules/module-combine.c572
-rw-r--r--src/modules/module-console-kit.c2
-rw-r--r--src/modules/module-detect.c6
-rw-r--r--src/modules/module-device-restore.c2
-rw-r--r--src/modules/module-hal-detect-compat.c84
-rw-r--r--src/modules/module-hal-detect.c42
-rw-r--r--src/modules/module-intended-roles.c54
-rw-r--r--src/modules/module-ladspa-sink.c239
-rw-r--r--src/modules/module-lirc.c28
-rw-r--r--src/modules/module-loopback.c784
-rw-r--r--src/modules/module-match.c5
-rw-r--r--src/modules/module-mmkbd-evdev.c49
-rw-r--r--src/modules/module-null-sink.c5
-rw-r--r--src/modules/module-pipe-sink.c6
-rw-r--r--src/modules/module-pipe-source.c2
-rw-r--r--src/modules/module-position-event-sounds.c57
-rw-r--r--src/modules/module-remap-sink.c168
-rw-r--r--src/modules/module-rescue-streams.c179
-rw-r--r--src/modules/module-sine.c2
-rw-r--r--src/modules/module-solaris.c104
-rw-r--r--src/modules/module-stream-restore.c134
-rw-r--r--src/modules/module-suspend-on-idle.c30
-rw-r--r--src/modules/module-tunnel.c49
-rw-r--r--src/modules/module-udev-detect.c391
-rw-r--r--src/modules/module-volume-restore.c9
-rw-r--r--src/modules/oss/module-oss.c16
-rw-r--r--src/modules/raop/base64.c1
-rw-r--r--src/modules/raop/module-raop-sink.c6
-rw-r--r--src/modules/reserve-monitor.c51
-rw-r--r--src/modules/reserve.c25
-rw-r--r--src/modules/rtp/module-rtp-recv.c5
-rw-r--r--src/modules/rtp/module-rtp-send.c3
-rw-r--r--src/modules/rtp/rtsp_client.c12
-rw-r--r--src/modules/x11/module-x11-publish.c2
-rw-r--r--src/pulse/channelmap.c18
-rw-r--r--src/pulse/channelmap.h18
-rw-r--r--src/pulse/client-conf.c21
-rw-r--r--src/pulse/client.conf.in2
-rw-r--r--src/pulse/context.c46
-rw-r--r--src/pulse/context.h7
-rw-r--r--src/pulse/def.h1
-rw-r--r--src/pulse/error.c4
-rw-r--r--src/pulse/ext-stream-restore.c18
-rw-r--r--src/pulse/glib-mainloop.h4
-rw-r--r--src/pulse/internal.h5
-rw-r--r--src/pulse/introspect.c2
-rw-r--r--src/pulse/introspect.h12
-rw-r--r--src/pulse/mainloop.c13
-rw-r--r--src/pulse/mainloop.h4
-rw-r--r--src/pulse/operation.h6
-rw-r--r--src/pulse/proplist.c5
-rw-r--r--src/pulse/proplist.h20
-rw-r--r--src/pulse/pulseaudio.h16
-rw-r--r--src/pulse/sample.c53
-rw-r--r--src/pulse/sample.h7
-rw-r--r--src/pulse/scache.c14
-rw-r--r--src/pulse/scache.h4
-rw-r--r--src/pulse/simple.c63
-rw-r--r--src/pulse/stream.c162
-rw-r--r--src/pulse/stream.h87
-rw-r--r--src/pulse/thread-mainloop.c13
-rw-r--r--src/pulse/thread-mainloop.h22
-rw-r--r--src/pulse/utf8.c9
-rw-r--r--src/pulse/util.c52
-rw-r--r--src/pulse/volume.c172
-rw-r--r--src/pulse/volume.h44
-rw-r--r--src/pulsecore/asyncmsgq.c42
-rw-r--r--src/pulsecore/asyncmsgq.h4
-rw-r--r--src/pulsecore/aupdate.c8
-rw-r--r--src/pulsecore/aupdate.h4
-rw-r--r--src/pulsecore/authkey.c37
-rw-r--r--src/pulsecore/cli-command.c58
-rw-r--r--src/pulsecore/cli-text.c24
-rw-r--r--src/pulsecore/conf-parser.c26
-rw-r--r--src/pulsecore/conf-parser.h1
-rw-r--r--src/pulsecore/core-scache.c9
-rw-r--r--src/pulsecore/core-util.c325
-rw-r--r--src/pulsecore/core-util.h13
-rw-r--r--src/pulsecore/core.c2
-rw-r--r--src/pulsecore/core.h3
-rw-r--r--src/pulsecore/cpu-arm.c139
-rw-r--r--src/pulsecore/cpu-arm.h (renamed from src/daemon/polkit.h)25
-rw-r--r--src/pulsecore/cpu-x86.c125
-rw-r--r--src/pulsecore/cpu-x86.h71
-rw-r--r--src/pulsecore/database-simple.c510
-rw-r--r--src/pulsecore/flist.c22
-rw-r--r--src/pulsecore/hook-list.c2
-rw-r--r--src/pulsecore/llist.h3
-rw-r--r--src/pulsecore/lock-autospawn.c66
-rw-r--r--src/pulsecore/macro.h79
-rw-r--r--src/pulsecore/memblock.c28
-rw-r--r--src/pulsecore/memblockq.c6
-rw-r--r--src/pulsecore/memblockq.h3
-rw-r--r--src/pulsecore/memtrap.c6
-rw-r--r--src/pulsecore/msgobject.c14
-rw-r--r--src/pulsecore/msgobject.h8
-rw-r--r--src/pulsecore/namereg.c75
-rw-r--r--src/pulsecore/namereg.h1
-rw-r--r--src/pulsecore/object.c18
-rw-r--r--src/pulsecore/object.h51
-rw-r--r--src/pulsecore/pdispatch.c36
-rw-r--r--src/pulsecore/pdispatch.h6
-rw-r--r--src/pulsecore/pid.c4
-rw-r--r--src/pulsecore/play-memblockq.c5
-rw-r--r--src/pulsecore/proplist-util.c6
-rw-r--r--src/pulsecore/protocol-esound.c11
-rw-r--r--src/pulsecore/protocol-http.c2
-rw-r--r--src/pulsecore/protocol-native.c154
-rw-r--r--src/pulsecore/protocol-native.h2
-rw-r--r--src/pulsecore/protocol-simple.c12
-rw-r--r--src/pulsecore/ratelimit.h18
-rw-r--r--src/pulsecore/remap.c204
-rw-r--r--src/pulsecore/remap.h48
-rw-r--r--src/pulsecore/remap_mmx.c161
-rw-r--r--src/pulsecore/remap_sse.c159
-rw-r--r--src/pulsecore/resampler.c240
-rw-r--r--src/pulsecore/rtpoll.c28
-rw-r--r--src/pulsecore/rtpoll.h4
-rw-r--r--src/pulsecore/sample-util.c410
-rw-r--r--src/pulsecore/sample-util.h7
-rw-r--r--src/pulsecore/sconv-s16le.c42
-rw-r--r--src/pulsecore/sconv.c188
-rw-r--r--src/pulsecore/sconv.h6
-rw-r--r--src/pulsecore/sconv_sse.c235
-rw-r--r--src/pulsecore/shm.c3
-rw-r--r--src/pulsecore/sink-input.c399
-rw-r--r--src/pulsecore/sink-input.h61
-rw-r--r--src/pulsecore/sink.c801
-rw-r--r--src/pulsecore/sink.h51
-rw-r--r--src/pulsecore/socket-client.c4
-rw-r--r--src/pulsecore/sound-file-stream.c8
-rw-r--r--src/pulsecore/source-output.c92
-rw-r--r--src/pulsecore/source-output.h24
-rw-r--r--src/pulsecore/source.c286
-rw-r--r--src/pulsecore/source.h33
-rw-r--r--src/pulsecore/start-child.c20
-rw-r--r--src/pulsecore/svolume_arm.c195
-rw-r--r--src/pulsecore/svolume_c.c335
-rw-r--r--src/pulsecore/svolume_mmx.c316
-rw-r--r--src/pulsecore/svolume_sse.c317
-rw-r--r--src/pulsecore/thread-mq.c11
-rw-r--r--src/pulsecore/thread-mq.h8
-rw-r--r--src/pulsecore/time-smoother.c41
-rw-r--r--src/pulsecore/time-smoother.h2
-rw-r--r--src/pulsecore/usergroup.c372
-rw-r--r--src/pulsecore/usergroup.h51
-rw-r--r--src/pulsecore/vector.h3
-rw-r--r--src/tests/envelope-test.c3
-rw-r--r--src/tests/get-binary-name-test.c25
-rw-r--r--src/tests/interpol-test.c81
-rw-r--r--src/tests/mix-test.c81
-rw-r--r--src/tests/remix-test.c3
-rw-r--r--src/tests/resampler-test.c3
-rw-r--r--src/tests/stripnul.c2
-rw-r--r--src/tests/usergroup-test.c161
-rw-r--r--src/tests/voltest.c70
-rw-r--r--src/utils/pacat.c135
-rw-r--r--src/utils/pacmd.c62
-rw-r--r--src/utils/pactl.c268
-rw-r--r--src/utils/padsp.c14
193 files changed, 10549 insertions, 3430 deletions
diff --git a/src/.gitignore b/src/.gitignore
index 82331524..6cd173c0 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,3 +1,4 @@
+usergroup-test
sigbus-test
TAGS
alsa-time-test
diff --git a/src/Makefile.am b/src/Makefile.am
index c022fa7c..6544e2aa 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,6 +32,7 @@ xdgautostartdir=$(sysconfdir)/xdg/autostart
alsaprofilesetsdir=$(datadir)/pulseaudio/alsa-mixer/profile-sets
alsapathsdir=$(datadir)/pulseaudio/alsa-mixer/paths
udevrulesdir=/lib/udev/rules.d
+dbuspolicydir=$(sysconfdir)/dbus-1/system.d
###################################
# Defines #
@@ -73,6 +74,7 @@ AM_CFLAGS = \
$(LIBSAMPLERATE_CFLAGS) \
$(LIBSNDFILE_CFLAGS) \
$(LIBSPEEX_CFLAGS) \
+ -DPA_BUILDDIR=\"$(abs_builddir)\" \
-DPA_DLSEARCHPATH=\"$(modlibexecdir)\" \
-DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\" \
-DPA_BINARY=\"$(PA_BINARY)\" \
@@ -82,8 +84,8 @@ AM_CFLAGS = \
-DAO_REQUIRE_CAS \
-DPULSE_LOCALEDIR=\"$(pulselocaledir)\" \
-DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\" \
- -DPA_ALSA_PATHS_DIR=\"$(alsapathsdir)\" \
- -DPA_ALSA_PROFILE_SETS_DIR=\"$(alsaprofilesetsdir)\"
+ -DPA_ALSA_PATHS_DIR=\"$(alsapathsdir)\" \
+ -DPA_ALSA_PROFILE_SETS_DIR=\"$(alsaprofilesetsdir)\"
AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS)
@@ -119,6 +121,7 @@ EXTRA_DIST = \
modules/module-defs.h.m4 \
daemon/pulseaudio.desktop.in \
map-file \
+ daemon/pulseaudio-system.conf \
modules/alsa/mixer/profile-sets/default.conf \
modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf \
modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf \
@@ -145,6 +148,9 @@ pulseconf_DATA = \
daemon.conf \
client.conf
+dbuspolicy_DATA = \
+ daemon/pulseaudio-system.conf
+
if HAVE_X11
xdgautostart_in_files = \
daemon/pulseaudio.desktop.in
@@ -171,8 +177,8 @@ pulseaudio_SOURCES = \
daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
daemon/main.c
-pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
-pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)
+pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(DBUS_CFLAGS)
+pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(DBUS_LIBS)
# This is needed because automake doesn't properly expand the foreach below
pulseaudio_DEPENDENCIES = libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(PREOPEN_LIBS)
@@ -274,7 +280,8 @@ TESTS = \
proplist-test \
lock-autospawn-test \
prioq-test \
- sigbus-test
+ sigbus-test \
+ usergroup-test
TESTS_BINARIES = \
mainloop-test \
@@ -312,7 +319,8 @@ TESTS_BINARIES = \
stripnul \
lock-autospawn-test \
prioq-test \
- sigbus-test
+ sigbus-test \
+ usergroup-test
if HAVE_SIGXCPU
#TESTS += \
@@ -488,18 +496,18 @@ sig2str_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
resampler_test_SOURCES = tests/resampler-test.c
resampler_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-resampler_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-resampler_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+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_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-mix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-mix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+mix_test_CFLAGS = $(AM_CFLAGS)
+mix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
remix_test_SOURCES = tests/remix-test.c
remix_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-remix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-remix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+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_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
@@ -508,38 +516,38 @@ smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
envelope_test_SOURCES = tests/envelope-test.c
envelope_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-envelope_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+envelope_test_CFLAGS = $(AM_CFLAGS)
+envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
proplist_test_SOURCES = tests/proplist-test.c
proplist_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-proplist_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+proplist_test_CFLAGS = $(AM_CFLAGS)
+proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
rtstutter_SOURCES = tests/rtstutter.c
rtstutter_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-rtstutter_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+rtstutter_CFLAGS = $(AM_CFLAGS)
+rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
stripnul_SOURCES = tests/stripnul.c
stripnul_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+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_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-lock_autospawn_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-lock_autospawn_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+lock_autospawn_test_CFLAGS = $(AM_CFLAGS)
+lock_autospawn_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
prioq_test_SOURCES = tests/prioq-test.c
prioq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-prioq_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-prioq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+prioq_test_CFLAGS = $(AM_CFLAGS)
+prioq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
sigbus_test_SOURCES = tests/sigbus-test.c
sigbus_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-sigbus_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-sigbus_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+sigbus_test_CFLAGS = $(AM_CFLAGS)
+sigbus_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
gtk_test_SOURCES = tests/gtk-test.c
gtk_test_LDADD = $(AM_LDADD) libpulse.la libpulse-mainloop-glib.la
@@ -551,6 +559,11 @@ alsa_time_test_LDADD = $(AM_LDADD)
alsa_time_test_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
alsa_time_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(ASOUNDLIB_LIBS)
+usergroup_test_SOURCES = tests/usergroup-test.c
+usergroup_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la
+usergroup_test_CFLAGS = $(AM_CFLAGS)
+usergroup_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
###################################
# Common library #
###################################
@@ -615,6 +628,7 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \
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/winsock.h
@@ -776,7 +790,7 @@ libpulse_mainloop_glib_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version
# OSS emulation #
###################################
-if HAVE_OSS
+if HAVE_OSS_WRAPPER
lib_LTLIBRARIES += libpulsedsp.la
bin_SCRIPTS += utils/padsp
endif
@@ -817,11 +831,18 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \
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/rtpoll.c pulsecore/rtpoll.h \
pulsecore/sample-util.c pulsecore/sample-util.h \
+ pulsecore/cpu-arm.c pulsecore/cpu-arm.h \
+ pulsecore/cpu-x86.c pulsecore/cpu-x86.h \
+ pulsecore/svolume_c.c pulsecore/svolume_arm.c \
+ pulsecore/svolume_mmx.c pulsecore/svolume_sse.c \
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/shm.c pulsecore/shm.h \
@@ -837,9 +858,9 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \
pulsecore/time-smoother.c pulsecore/time-smoother.h \
pulsecore/database.h
-libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(WINSOCK_CFLAGS) $(LIBOIL_CFLAGS)
+libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(WINSOCK_CFLAGS)
libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS = -avoid-version
-libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la libpulsecore-foreign.la
+libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la libpulsecore-foreign.la
if HAVE_X11
libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/x11wrap.c pulsecore/x11wrap.h
@@ -865,6 +886,9 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(TDB_CFLAGS)
libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(TDB_LIBS)
endif
+if HAVE_SIMPLEDB
+libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/database-simple.c
+endif
# We split the foreign code off to not be annoyed by warnings we don't care about
noinst_LTLIBRARIES = libpulsecore-foreign.la
@@ -987,8 +1011,8 @@ modlibexec_LTLIBRARIES += \
module-tunnel-source.la \
module-position-event-sounds.la \
module-augment-properties.la \
- module-cork-music-on-phone.la
-
+ module-cork-music-on-phone.la \
+ module-loopback.la
# See comment at librtp.la above
if !OS_IS_WIN32
@@ -1031,7 +1055,7 @@ modlibexec_LTLIBRARIES += \
module-x11-cork-request.la
endif
-if HAVE_OSS
+if HAVE_OSS_OUTPUT
modlibexec_LTLIBRARIES += \
liboss-util.la \
module-oss.la
@@ -1120,6 +1144,11 @@ modlibexec_LTLIBRARIES += \
module-hal-detect.la
endif
+if HAVE_HAL_COMPAT
+modlibexec_LTLIBRARIES += \
+ module-hal-detect.la
+endif
+
if HAVE_UDEV
modlibexec_LTLIBRARIES += \
module-udev-detect.la
@@ -1221,7 +1250,8 @@ SYMDEF_FILES = \
modules/module-position-event-sounds-symdef.h \
modules/module-augment-properties-symdef.h \
modules/module-cork-music-on-phone-symdef.h \
- modules/module-console-kit-symdef.h
+ modules/module-console-kit-symdef.h \
+ modules/module-loopback-symdef.h
EXTRA_DIST += $(SYMDEF_FILES)
BUILT_SOURCES += $(SYMDEF_FILES)
@@ -1363,6 +1393,10 @@ module_tunnel_source_la_SOURCES = modules/module-tunnel.c
module_tunnel_source_la_LDFLAGS = $(MODULE_LDFLAGS)
module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_loopback_la_SOURCES = modules/module-loopback.c
+module_loopback_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_loopback_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+
# X11
module_x11_bell_la_SOURCES = modules/x11/module-x11-bell.c
@@ -1576,10 +1610,16 @@ module_jack_source_la_LDFLAGS = $(MODULE_LDFLAGS)
module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la $(JACK_LIBS) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
+if HAVE_HAL_COMPAT
+module_hal_detect_la_SOURCES = modules/module-hal-detect-compat.c
+module_hal_detect_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_hal_detect_la_CFLAGS = $(AM_CFLAGS)
+else
module_hal_detect_la_SOURCES = modules/module-hal-detect.c
-module_hal_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+endif
+module_hal_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
module_udev_detect_la_SOURCES = modules/module-udev-detect.c
module_udev_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
@@ -1698,7 +1738,7 @@ daemon.conf: daemon/daemon.conf.in Makefile
-e 's,@PA_DEFAULT_CONFIG_FILE\@,$(DEFAULT_CONFIG_DIR),g' < $< > $@
install-exec-hook:
- chown root $(DESTDIR)$(bindir)/pulseaudio ; true
+ -chown root $(DESTDIR)$(pulselibexecdir)/proximity-helper
-chmod u+s $(DESTDIR)$(pulselibexecdir)/proximity-helper
ln -sf pacat $(DESTDIR)$(bindir)/parec
ln -sf pacat $(DESTDIR)$(bindir)/pamon
diff --git a/src/daemon/caps.c b/src/daemon/caps.c
index 294be494..76b62e03 100644
--- a/src/daemon/caps.c
+++ b/src/daemon/caps.c
@@ -57,24 +57,29 @@ void pa_drop_root(void) {
#ifdef HAVE_GETUID
uid_t uid;
+ gid_t gid;
+ pa_log_debug(_("Cleaning up privileges."));
uid = getuid();
- if (uid == 0 || geteuid() != 0)
- return;
-
- pa_log_info(_("Dropping root privileges."));
+ 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);
#endif
#ifdef HAVE_SYS_PRCTL_H
@@ -82,7 +87,7 @@ void pa_drop_root(void) {
#endif
#ifdef HAVE_SYS_CAPABILITY_H
- {
+ if (uid != 0) {
cap_t caps;
pa_assert_se(caps = cap_init());
pa_assert_se(cap_clear(caps) == 0);
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index ecb38486..f6cdcdc8 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -385,11 +385,6 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
pa_xfree(conf->script_commands);
conf->script_commands = pa_strbuf_tostring_free(buf);
- if (!conf->script_commands) {
- pa_xfree(conf->script_commands);
- conf->script_commands = NULL;
- }
-
*d = optind;
return 0;
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index 9010f2f6..6e7926f8 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -83,7 +83,7 @@ static const pa_daemon_conf default_conf = {
.config_file = NULL,
.use_pid_file = TRUE,
.system_instance = FALSE,
- .no_cpu_limit = FALSE,
+ .no_cpu_limit = TRUE,
.disable_shm = FALSE,
.lock_memory = FALSE,
.default_n_fragments = 4,
@@ -133,9 +133,25 @@ static const pa_daemon_conf default_conf = {
};
pa_daemon_conf* pa_daemon_conf_new(void) {
- pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
+ pa_daemon_conf *c;
+
+ c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
+
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+
+ /* We abuse __OPTIMIZE__ as a check whether we are a debug build
+ * or not. If we are and are run from the build tree then we
+ * override the search path to point to our build tree */
+
+ 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 "/.libs/");
+
+ } else
+
+#endif
+ c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
- c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
return c;
}
@@ -441,11 +457,15 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "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 },
{ "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 },
{ "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL },
{ "lock-memory", pa_config_parse_bool, &c->lock_memory, NULL },
{ "exit-idle-time", pa_config_parse_int, &c->exit_idle_time, NULL },
@@ -465,7 +485,9 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "default-fragment-size-msec", parse_fragment_size_msec, c, NULL },
{ "nice-level", parse_nice_level, c, NULL },
{ "disable-remixing", pa_config_parse_bool, &c->disable_remixing, NULL },
+ { "enable-remixing", pa_config_parse_not_bool, &c->disable_remixing, 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 },
{ "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 },
@@ -623,12 +645,12 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "nice-level = %i\n", c->nice_level);
pa_strbuf_printf(s, "realtime-scheduling = %s\n", pa_yes_no(c->realtime_scheduling));
pa_strbuf_printf(s, "realtime-priority = %i\n", c->realtime_priority);
- pa_strbuf_printf(s, "disallow-module-loading = %s\n", pa_yes_no(c->disallow_module_loading));
- pa_strbuf_printf(s, "disallow-exit = %s\n", pa_yes_no(c->disallow_exit));
+ 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));
- pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit));
- pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm));
+ pa_strbuf_printf(s, "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);
@@ -639,8 +661,8 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
- pa_strbuf_printf(s, "disable-remixing = %s\n", pa_yes_no(c->disable_remixing));
- pa_strbuf_printf(s, "disable-lfe-remixing = %s\n", pa_yes_no(c->disable_lfe_remixing));
+ pa_strbuf_printf(s, "enable-remixing = %s\n", pa_yes_no(!c->disable_remixing));
+ pa_strbuf_printf(s, "enable-lfe-remixing = %s\n", pa_yes_no(!c->disable_lfe_remixing));
pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));
pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate);
pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels);
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index 6931359c..db2059e1 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -21,14 +21,14 @@
; daemonize = no
; fail = yes
-; disallow-module-loading = no
-; disallow-exit = no
+; allow-module-loading = yes
+; allow-exit = yes
; use-pid-file = yes
; system-instance = no
-; disable-shm = no
+; enable-shm = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
; lock-memory = no
-; no-cpu-limit = no
+; cpu-limit = no
; high-priority = yes
; nice-level = -11
@@ -51,8 +51,8 @@
; log-backtrace = 0
; resample-method = speex-float-3
-; disable-remixing = no
-; disable-lfe-remixing = yes
+; enable-remixing = yes
+; enable-lfe-remixing = no
; flat-volumes = yes
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 0f6fc907..af59adef 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -39,8 +39,6 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <liboil/liboil.h>
-
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
@@ -95,6 +93,8 @@
#ifdef HAVE_DBUS
#include <pulsecore/dbus-shared.h>
#endif
+#include <pulsecore/cpu-arm.h>
+#include <pulsecore/cpu-x86.h>
#include "cmdline.h"
#include "cpulimit.h"
@@ -109,7 +109,7 @@ int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
#endif
-#ifdef HAVE_OSS
+#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. */
@@ -259,9 +259,14 @@ static int change_user(void) {
pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
/* Relevant for pa_runtime_path() */
- pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
- pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_CONFIG_PATH);
- pa_set_env("PULSE_STATE_PATH", PA_SYSTEM_STATE_PATH);
+ 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 dropped root privileges."));
@@ -401,6 +406,36 @@ int main(int argc, char *argv[]) {
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;
+
+ /* 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 ((rp = pa_readlink("/proc/self/exe"))) {
+
+ if (pa_streq(rp, PA_BINARY))
+ pa_assert_se(execv(rp, argv) == 0);
+ else
+ pa_log_warn("/proc/self/exe does not point to " PA_BINARY ", cannot self execute. Are you playing games?");
+
+ pa_xfree(rp);
+
+ } else
+ pa_log_warn("Couldn't read /proc/self/exe, cannot self execute. Running in a chroot()?");
+ }
+#endif
+
if ((e = getenv("PULSE_PASSED_FD"))) {
passed_fd = atoi(e);
@@ -411,10 +446,13 @@ int main(int argc, char *argv[]) {
/* 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_all(passed_fd, -1);
pa_reset_sigs(-1);
pa_unblock_sigs(-1);
+ pa_reset_priority();
setlocale(LC_ALL, "");
pa_init_i18n();
@@ -668,7 +706,7 @@ int main(int argc, char *argv[]) {
#endif
}
- pa_set_env("PULSE_INTERNAL", "1");
+ pa_set_env_and_record("PULSE_INTERNAL", "1");
pa_assert_se(chdir("/") == 0);
umask(0022);
@@ -683,7 +721,7 @@ int main(int argc, char *argv[]) {
if (change_user() < 0)
goto finish;
- pa_set_env("PULSE_SYSTEM", conf->system_instance ? "1" : "0");
+ 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);
@@ -741,6 +779,8 @@ int main(int argc, char *argv[]) {
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())
@@ -788,6 +828,11 @@ int main(int argc, char *argv[]) {
pa_memtrap_install();
+ if (!getenv("PULSE_NO_SIMD")) {
+ pa_cpu_init_x86();
+ pa_cpu_init_arm();
+ }
+
pa_assert_se(mainloop = pa_mainloop_new());
if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm, conf->shm_size))) {
@@ -827,8 +872,6 @@ int main(int argc, char *argv[]) {
win32_timer = pa_mainloop_get_api(mainloop)->rtclock_time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL);
#endif
- oil_init();
-
if (!conf->no_cpu_limit)
pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
@@ -927,6 +970,9 @@ finish:
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
diff --git a/src/daemon/polkit.c b/src/daemon/polkit.c
deleted file mode 100644
index 9799e094..00000000
--- a/src/daemon/polkit.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/***
- 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, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <inttypes.h>
-
-#include <dbus/dbus.h>
-#include <polkit-dbus/polkit-dbus.h>
-
-#include <pulse/i18n.h>
-
-#include <pulsecore/log.h>
-#include <pulsecore/macro.h>
-
-#include "polkit.h"
-
-int pa_polkit_check(const char *action_id) {
- int ret = -1;
- DBusError dbus_error;
- DBusConnection *bus = NULL;
- PolKitCaller *caller = NULL;
- PolKitAction *action = NULL;
- PolKitContext *context = NULL;
- PolKitError *polkit_error = NULL;
- PolKitSession *session = NULL;
- PolKitResult polkit_result;
-
- dbus_error_init(&dbus_error);
-
- if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error))) {
- pa_log_error(_("Cannot connect to system bus: %s"), dbus_error.message);
- goto finish;
- }
-
- /* There seems to be a bug in some versions of D-Bus that causes
- * dbus_shutdown() to call exit() when a connection without this
- * flag disabled was created during runtime.*/
- dbus_connection_set_exit_on_disconnect(bus, FALSE);
-
- if (!(caller = polkit_caller_new_from_pid(bus, getpid(), &dbus_error))) {
- pa_log_error(_("Cannot get caller from PID: %s"), dbus_error.message);
- goto finish;
- }
-
- /* This function is called when PulseAudio is called SUID root. We
- * want to authenticate the real user that called us and not the
- * effective user we gained through being SUID root. Hence we
- * overwrite the UID caller data here explicitly, just for
- * paranoia. In fact PolicyKit should fill in the UID here anyway
- * -- an not the EUID or any other user id. */
-
- if (!(polkit_caller_set_uid(caller, getuid()))) {
- pa_log_error(_("Cannot set UID on caller object."));
- goto finish;
- }
-
- if (!(polkit_caller_get_ck_session(caller, &session))) {
- pa_log_error(_("Failed to get CK session."));
- goto finish;
- }
-
- /* We need to overwrite the UID in both the caller and the session
- * object */
-
- if (!(polkit_session_set_uid(session, getuid()))) {
- pa_log_error(_("Cannot set UID on session object."));
- goto finish;
- }
-
- if (!(action = polkit_action_new())) {
- pa_log_error(_("Cannot allocate PolKitAction."));
- goto finish;
- }
-
- if (!polkit_action_set_action_id(action, action_id)) {
- pa_log_error(_("Cannot set action_id"));
- goto finish;
- }
-
- if (!(context = polkit_context_new())) {
- pa_log_error(_("Cannot allocate PolKitContext."));
- goto finish;
- }
-
- if (!polkit_context_init(context, &polkit_error)) {
- pa_log_error(_("Cannot initialize PolKitContext: %s"), polkit_error_get_error_message(polkit_error));
- goto finish;
- }
-
- for (;;) {
-
- polkit_result = polkit_context_is_caller_authorized(context, action, caller, TRUE, &polkit_error);
-
- if (polkit_error_is_set(polkit_error)) {
- pa_log_error(_("Could not determine whether caller is authorized: %s"), polkit_error_get_error_message(polkit_error));
- goto finish;
- }
-
- if (polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
- polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
- polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS ||
- polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_ONE_SHOT ||
- polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH ||
- polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_SESSION ||
- polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_ALWAYS ||
- polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_ONE_SHOT
- ) {
-
- if (polkit_auth_obtain(action_id, 0, getpid(), &dbus_error)) {
- polkit_result = POLKIT_RESULT_YES;
- break;
- }
-
- if (dbus_error_is_set(&dbus_error)) {
- pa_log_error(_("Cannot obtain auth: %s"), dbus_error.message);
- goto finish;
- }
- }
-
- break;
- }
-
- if (polkit_result != POLKIT_RESULT_YES && polkit_result != POLKIT_RESULT_NO)
- pa_log_warn(_("PolicyKit responded with '%s'"), polkit_result_to_string_representation(polkit_result));
-
- ret = polkit_result == POLKIT_RESULT_YES;
-
-finish:
-
- if (caller)
- polkit_caller_unref(caller);
-
- if (action)
- polkit_action_unref(action);
-
- if (context)
- polkit_context_unref(context);
-
- if (bus)
- dbus_connection_unref(bus);
-
- dbus_error_free(&dbus_error);
-
- if (polkit_error)
- polkit_error_free(polkit_error);
-
- return ret;
-}
diff --git a/src/daemon/pulseaudio-system.conf b/src/daemon/pulseaudio-system.conf
new file mode 100644
index 00000000..edddaf93
--- /dev/null
+++ b/src/daemon/pulseaudio-system.conf
@@ -0,0 +1,37 @@
+<?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, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<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"/>
+
+ <!-- Allow pulseaudio to talk to HAL for device detection -->
+ <allow send_destination="org.freedesktop.Hal" send_interface="org.freedesktop.Hal.Manager"/>
+ <allow send_destination="org.freedesktop.Hal" send_interface="org.freedesktop.Hal.Device"/>
+ </policy>
+
+</busconfig>
diff --git a/src/map-file b/src/map-file
index a1d0a061..95b2803a 100644
--- a/src/map-file
+++ b/src/map-file
@@ -123,13 +123,18 @@ 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_position;
+pa_cvolume_inc;
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;
@@ -219,6 +224,8 @@ 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;
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index a5515e1b..685169b9 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -929,7 +929,7 @@ static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
pa_alsa_element *e;
- int r;
+ int r = 0;
pa_assert(m);
pa_assert(p);
@@ -940,7 +940,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
PA_LLIST_FOREACH(e, p->elements) {
switch (e->switch_use) {
- case PA_ALSA_SWITCH_MUTE:
case PA_ALSA_SWITCH_OFF:
r = element_set_switch(e, m, FALSE);
break;
@@ -949,6 +948,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
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;
@@ -960,7 +960,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
switch (e->volume_use) {
case PA_ALSA_VOLUME_OFF:
- case PA_ALSA_VOLUME_MERGE:
r = element_mute_volume(e, m);
break;
@@ -968,6 +967,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
r = element_zero_volume(e, m);
break;
+ case PA_ALSA_VOLUME_MERGE:
case PA_ALSA_VOLUME_IGNORE:
r = 0;
break;
@@ -1849,7 +1849,12 @@ pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction)
items[1].data = &p->description;
items[2].data = &p->name;
- fn = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR);
+ fn = pa_maybe_prefix_path(fname,
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+ pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
+#endif
+ PA_ALSA_PATHS_DIR);
+
r = pa_config_parse(fn, NULL, items, p);
pa_xfree(fn);
@@ -2838,9 +2843,9 @@ static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
if (bonus) {
if (pa_channel_map_equal(&m->channel_map, bonus))
- m->priority += 5000;
+ m->priority += 50;
else if (m->channel_map.channels == bonus->channels)
- m->priority += 4000;
+ m->priority += 30;
}
return 0;
@@ -2884,7 +2889,7 @@ static void profile_set_add_auto_pair(
else
name = pa_sprintf_malloc("input:%s", n->name);
- if ((p = pa_hashmap_get(ps->profiles, name))) {
+ if (pa_hashmap_get(ps->profiles, name)) {
pa_xfree(name);
return;
}
@@ -3110,7 +3115,12 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel
if (!fname)
fname = "default.conf";
- fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR);
+ fn = pa_maybe_prefix_path(fname,
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+ pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
+#endif
+ PA_ALSA_PROFILE_SETS_DIR);
+
r = pa_config_parse(fn, NULL, items, ps);
pa_xfree(fn);
@@ -3135,7 +3145,13 @@ fail:
return NULL;
}
-void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) {
+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 *state;
pa_alsa_profile *p, *last = NULL;
pa_alsa_mapping *m;
@@ -3150,6 +3166,7 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons
PA_HASHMAP_FOREACH(p, ps->profiles, state) {
pa_sample_spec try_ss;
pa_channel_map try_map;
+ snd_pcm_uframes_t try_period_size, try_buffer_size;
uint32_t idx;
/* Is this already marked that it is supported? (i.e. from the config file) */
@@ -3203,13 +3220,18 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons
try_ss = *ss;
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;
+
if (!(m ->output_pcm = pa_alsa_open_by_template(
m->device_strings,
dev_id,
NULL,
&try_ss, &try_map,
SND_PCM_STREAM_PLAYBACK,
- NULL, NULL, 0, NULL, NULL,
+ &try_period_size, &try_buffer_size, 0, NULL, NULL,
TRUE))) {
p->supported = FALSE;
break;
@@ -3227,13 +3249,18 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons
try_ss = *ss;
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;
+
if (!(m ->input_pcm = pa_alsa_open_by_template(
m->device_strings,
dev_id,
NULL,
&try_ss, &try_map,
SND_PCM_STREAM_CAPTURE,
- NULL, NULL, 0, NULL, NULL,
+ &try_period_size, &try_buffer_size, 0, NULL, NULL,
TRUE))) {
p->supported = FALSE;
break;
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 76788183..a0d4fcbe 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -269,7 +269,7 @@ void pa_alsa_mapping_dump(pa_alsa_mapping *m);
void pa_alsa_profile_dump(pa_alsa_profile *p);
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);
+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);
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 0cde694c..22e88b4a 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -62,11 +62,26 @@
/* #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_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- On underrun, increase watermark by this */
-#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 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 theshold, increase the watermark */
+#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms -- If the buffer level didn't drop below this theshold in the verification time, decrease the watermark */
+
+/* Note that TSCHED_WATERMARK_INC_THRESHOLD_USEC == 0 means tht 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_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 inteval */
+
+#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 */
struct userdata {
pa_core *core;
@@ -94,9 +109,13 @@ struct userdata {
hwbuf_unused,
min_sleep,
min_wakeup,
- watermark_step;
+ watermark_inc_step,
+ watermark_dec_step,
+ watermark_inc_threshold,
+ watermark_dec_threshold;
+
+ pa_usec_t watermark_dec_not_before;
- unsigned nfragments;
pa_memchunk memchunk;
char *device_name; /* name of the PCM device */
@@ -113,6 +132,8 @@ struct userdata {
pa_smoother *smoother;
uint64_t write_count;
uint64_t since_start;
+ pa_usec_t smoother_interval;
+ pa_usec_t last_smoother_update;
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
@@ -241,6 +262,7 @@ 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);
@@ -255,6 +277,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) {
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;
@@ -265,7 +288,7 @@ static void fix_tsched_watermark(struct userdata *u) {
u->tsched_watermark = u->min_wakeup;
}
-static void adjust_after_underrun(struct userdata *u) {
+static void increase_watermark(struct userdata *u) {
size_t old_watermark;
pa_usec_t old_min_latency, new_min_latency;
@@ -274,31 +297,64 @@ static void adjust_after_underrun(struct userdata *u) {
/* 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_step);
+ 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_notice("Increasing wakeup watermark to %0.2f ms",
- (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
+ pa_log_info("Increasing wakeup watermark to %0.2f ms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
return;
}
/* Hmm, we cannot increase the watermark any further, hence let's raise the latency */
old_min_latency = u->sink->thread_info.min_latency;
- new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC);
+ 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_notice("Increasing minimal latency to %0.2f ms",
- (double) new_min_latency / PA_USEC_PER_MSEC);
+ 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);
- return;
}
/* When we reach this we're officialy 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) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / 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;
@@ -306,6 +362,7 @@ static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*p
pa_assert(process_usec);
pa_assert(u);
+ pa_assert(u->use_tsched);
usec = pa_sink_get_requested_latency_within_thread(u->sink);
@@ -340,6 +397,9 @@ static int try_recover(struct userdata *u, const char *call, int err) {
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;
@@ -350,42 +410,65 @@ static int try_recover(struct userdata *u, const char *call, int err) {
return 0;
}
-static size_t check_left_to_play(struct userdata *u, size_t n_bytes) {
+static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) {
size_t left_to_play;
+ pa_bool_t 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) {
+ 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;
#ifdef DEBUG_TIMING
- pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
+ PA_DEBUG_TRAP;
#endif
- } else {
- left_to_play = 0;
+ if (!u->first && !u->after_rewind)
+ if (pa_log_ratelimit())
+ pa_log_info("Underrun!");
+ }
#ifdef DEBUG_TIMING
- PA_DEBUG_TRAP;
+ 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) {
+ pa_bool_t 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;
- if (pa_log_ratelimit())
- pa_log_info("Underrun!");
+ /* 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 (u->use_tsched)
- adjust_after_underrun(u);
+ 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, pa_bool_t polled) {
+static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
pa_bool_t work_done = TRUE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_play;
@@ -401,6 +484,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
snd_pcm_sframes_t n;
size_t n_bytes;
int r;
+ pa_bool_t after_avail = TRUE;
/* First we determine how many samples are missing to fill the
* buffer up to 100% */
@@ -419,7 +503,8 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
pa_log_debug("avail: %lu", (unsigned long) n_bytes);
#endif
- left_to_play = check_left_to_play(u, n_bytes);
+ left_to_play = check_left_to_play(u, n_bytes, on_timeout);
+ on_timeout = FALSE;
if (u->use_tsched)
@@ -484,6 +569,9 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
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;
@@ -494,6 +582,12 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size)
frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size;
+ 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);
@@ -545,7 +639,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
return work_done ? 1 : 0;
}
-static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
+static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
pa_bool_t work_done = FALSE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_play;
@@ -561,6 +655,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
snd_pcm_sframes_t n;
size_t n_bytes;
int r;
+ pa_bool_t after_avail = TRUE;
if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
@@ -571,7 +666,8 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
}
n_bytes = (size_t) n * u->frame_size;
- left_to_play = check_left_to_play(u, n_bytes);
+ left_to_play = check_left_to_play(u, n_bytes, on_timeout);
+ on_timeout = FALSE;
if (u->use_tsched)
@@ -631,16 +727,23 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
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);
- pa_assert(frames != 0);
-
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;
+
u->memchunk.index += (size_t) frames * u->frame_size;
u->memchunk.length -= (size_t) frames * u->frame_size;
@@ -700,18 +803,27 @@ static void update_smoother(struct userdata *u) {
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;
- /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
- if (now1 <= 0)
- now1 = pa_rtclock_now();
-
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 pa_usec_t sink_get_latency(struct userdata *u) {
@@ -830,8 +942,7 @@ static int unsuspend(struct userdata *u) {
pa_sample_spec ss;
int err;
pa_bool_t b, d;
- unsigned nfrags;
- snd_pcm_uframes_t period_size;
+ snd_pcm_uframes_t period_size, buffer_size;
pa_assert(u);
pa_assert(!u->pcm_handle);
@@ -839,7 +950,7 @@ static int unsuspend(struct userdata *u) {
pa_log_info("Trying resume...");
if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK,
- /*SND_PCM_NONBLOCK|*/
+ SND_PCM_NONBLOCK|
SND_PCM_NO_AUTO_RESAMPLE|
SND_PCM_NO_AUTO_CHANNELS|
SND_PCM_NO_AUTO_FORMAT)) < 0) {
@@ -848,12 +959,12 @@ static int unsuspend(struct userdata *u) {
}
ss = u->sink->sample_spec;
- nfrags = u->nfragments;
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, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
+ 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;
}
@@ -868,10 +979,11 @@ static int unsuspend(struct userdata *u) {
goto fail;
}
- if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) {
- pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)",
- (unsigned long) u->nfragments, (unsigned long) u->fragment_size,
- (unsigned long) nfrags, period_size * u->frame_size);
+ 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->fragment_size), (unsigned long) (period_size*u->frame_size));
goto fail;
}
@@ -881,6 +993,11 @@ static int unsuspend(struct userdata *u) {
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;
@@ -894,7 +1011,7 @@ fail:
u->pcm_handle = NULL;
}
- return -1;
+ return -PA_ERR_IO;
}
/* Called from IO context */
@@ -918,28 +1035,33 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
- case PA_SINK_SUSPENDED:
+ case PA_SINK_SUSPENDED: {
+ int r;
+
pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
- if (suspend(u) < 0)
- return -1;
+ if ((r = suspend(u)) < 0)
+ return r;
break;
+ }
case PA_SINK_IDLE:
- case PA_SINK_RUNNING:
+ case PA_SINK_RUNNING: {
+ int r;
if (u->sink->thread_info.state == PA_SINK_INIT) {
if (build_pollfd(u) < 0)
- return -1;
+ return -PA_ERR_IO;
}
if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
- if (unsuspend(u) < 0)
- return -1;
+ if ((r = unsuspend(u)) < 0)
+ return r;
}
break;
+ }
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
@@ -967,7 +1089,7 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t new_state) {
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 -1;
+ return -PA_ERR_BUSY;
return 0;
}
@@ -982,7 +1104,7 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
return 0;
if (mask & SND_CTL_EVENT_MASK_VALUE) {
- pa_sink_get_volume(u->sink, TRUE, FALSE);
+ pa_sink_get_volume(u->sink, TRUE);
pa_sink_get_mute(u->sink, TRUE);
}
@@ -1009,15 +1131,11 @@ static void sink_get_volume_cb(pa_sink *s) {
if (pa_cvolume_equal(&u->hardware_volume, &r))
return;
- s->virtual_volume = u->hardware_volume = r;
+ s->real_volume = u->hardware_volume = r;
- if (u->mixer_path->has_dB) {
- pa_cvolume reset;
-
- /* Hmm, so the hardware volume changed, let's reset our software volume */
- pa_cvolume_reset(&reset, s->sample_spec.channels);
- pa_sink_set_soft_volume(s, &reset);
- }
+ /* 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) {
@@ -1030,7 +1148,7 @@ static void sink_set_volume_cb(pa_sink *s) {
pa_assert(u->mixer_handle);
/* Shift up by the base volume */
- pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->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) < 0)
return;
@@ -1041,13 +1159,26 @@ static void sink_set_volume_cb(pa_sink *s) {
u->hardware_volume = r;
if (u->mixer_path->has_dB) {
+ pa_cvolume new_soft_volume;
+ pa_bool_t accurate_enough;
/* Match exactly what the user requested by software */
- pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
+ pa_sw_cvolume_divide(&new_soft_volume, &s->real_volume, &u->hardware_volume);
- pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_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(t, sizeof(t), &s->real_volume));
pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
- pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
+ pa_log_debug("Calculated software volume: %s (accurate-enough=%s)", pa_cvolume_snprint(t, sizeof(t), &new_soft_volume),
+ 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(t, sizeof(t), &r));
@@ -1055,7 +1186,7 @@ static void sink_set_volume_cb(pa_sink *s) {
/* We can't match exactly what the user requested, hence let's
* at least tell the user about it */
- s->virtual_volume = r;
+ s->real_volume = r;
}
}
@@ -1177,8 +1308,11 @@ static int process_rewind(struct userdata *u) {
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));
- return -1;
+ 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;
@@ -1186,7 +1320,7 @@ static int process_rewind(struct userdata *u) {
if (rewind_nbytes <= 0)
pa_log_info("Tried rewind, but was apparently not possible.");
else {
- u->write_count -= out_frames * u->frame_size;
+ u->write_count -= rewind_nbytes;
pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
pa_sink_process_rewind(u->sink, rewind_nbytes);
@@ -1224,15 +1358,16 @@ static void thread_func(void *userdata) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
int work_done;
pa_usec_t sleep_usec = 0;
+ pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);
if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
if (process_rewind(u) < 0)
goto fail;
if (u->use_mmap)
- work_done = mmap_write(u, &sleep_usec, revents & POLLOUT);
+ work_done = mmap_write(u, &sleep_usec, revents & POLLOUT, on_timeout);
else
- work_done = unix_write(u, &sleep_usec, revents & POLLOUT);
+ work_done = unix_write(u, &sleep_usec, revents & POLLOUT, on_timeout);
if (work_done < 0)
goto fail;
@@ -1264,7 +1399,8 @@ static void thread_func(void *userdata) {
* we have filled the buffer at least once
* completely.*/
- pa_log_debug("Cutting sleep time for the initial iterations by half.");
+ if (pa_log_ratelimit())
+ pa_log_debug("Cutting sleep time for the initial iterations by half.");
sleep_usec /= 2;
}
@@ -1506,8 +1642,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
const char *dev_id = NULL;
pa_sample_spec ss, requested_ss;
pa_channel_map map;
- uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
- snd_pcm_uframes_t period_frames, tsched_frames;
+ 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;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_sink_new_data data;
@@ -1541,8 +1677,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
goto fail;
}
- hwbuf_size = frag_size * nfrags;
+ 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) {
@@ -1582,6 +1720,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
5,
pa_rtclock_now(),
TRUE);
+ u->smoother_interval = SMOOTHER_MIN_INTERVAL;
dev_id = pa_modargs_get_value(
ma, "device_id",
@@ -1608,7 +1747,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
&u->device_name,
&ss, &map,
SND_PCM_STREAM_PLAYBACK,
- &nfrags, &period_frames, tsched_frames,
+ &period_frames, &buffer_frames, tsched_frames,
&b, &d, mapping)))
goto fail;
@@ -1623,7 +1762,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
&u->device_name,
&ss, &map,
SND_PCM_STREAM_PLAYBACK,
- &nfrags, &period_frames, tsched_frames,
+ &period_frames, &buffer_frames, tsched_frames,
&b, &d, profile_set, &mapping)))
goto fail;
@@ -1635,7 +1774,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
&u->device_name,
&ss, &map,
SND_PCM_STREAM_PLAYBACK,
- &nfrags, &period_frames, tsched_frames,
+ &period_frames, &buffer_frames, tsched_frames,
&b, &d, FALSE)))
goto fail;
}
@@ -1661,11 +1800,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
u->use_tsched = use_tsched = FALSE;
}
- if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) {
- pa_log_info("Device is not a hardware device, disabling timer-based scheduling.");
- u->use_tsched = use_tsched = FALSE;
- }
-
if (u->use_mmap)
pa_log_info("Successfully enabled mmap() mode.");
@@ -1687,7 +1821,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
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) (period_frames * frame_size * nfrags));
+ 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"));
@@ -1728,21 +1862,28 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_sink_set_rtpoll(u->sink, u->rtpoll);
u->frame_size = frame_size;
- u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);
- u->nfragments = nfrags;
- u->hwbuf_size = u->fragment_size * nfrags;
- u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec);
+ 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 %u fragments of size %lu bytes, buffer time is %0.2fms",
- nfrags, (long unsigned) u->fragment_size,
+ 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);
pa_sink_set_max_rewind(u->sink, u->hwbuf_size);
if (u->use_tsched) {
- u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->sink->sample_spec);
+ u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_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);
@@ -1756,6 +1897,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
} 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)
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index a6760e1e..fa3ac0aa 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -59,11 +59,24 @@
/* #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_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 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_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;
@@ -91,9 +104,12 @@ struct userdata {
hwbuf_unused,
min_sleep,
min_wakeup,
- watermark_step;
+ watermark_inc_step,
+ watermark_dec_step,
+ watermark_inc_threshold,
+ watermark_dec_threshold;
- unsigned nfragments;
+ pa_usec_t watermark_dec_not_before;
char *device_name;
char *control_device;
@@ -106,6 +122,8 @@ struct userdata {
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;
@@ -234,6 +252,7 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) {
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);
@@ -248,6 +267,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) {
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;
@@ -258,7 +278,7 @@ static void fix_tsched_watermark(struct userdata *u) {
u->tsched_watermark = u->min_wakeup;
}
-static void adjust_after_overrun(struct userdata *u) {
+static void increase_watermark(struct userdata *u) {
size_t old_watermark;
pa_usec_t old_min_latency, new_min_latency;
@@ -267,36 +287,72 @@ static void adjust_after_overrun(struct userdata *u) {
/* 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_step);
-
+ 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_notice("Increasing wakeup watermark to %0.2f ms",
- (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+ pa_log_info("Increasing wakeup watermark to %0.2f ms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
return;
}
/* Hmm, we cannot increase the watermark any further, hence let's raise the latency */
old_min_latency = u->source->thread_info.min_latency;
- new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC);
+ 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_notice("Increasing minimal latency to %0.2f ms",
- (double) new_min_latency / PA_USEC_PER_MSEC);
+ 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);
- return;
}
/* When we reach this we're officialy 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) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+
+ /* We don't change the latency range*/
+
+restart:
+ u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC;
+}
+
static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
pa_usec_t wm, usec;
+ pa_assert(sleep_usec);
+ pa_assert(process_usec);
+
pa_assert(u);
+ pa_assert(u->use_tsched);
usec = pa_source_get_requested_latency_within_thread(u->source);
@@ -333,6 +389,9 @@ static int try_recover(struct userdata *u, const char *call, int err) {
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;
@@ -342,24 +401,23 @@ static int try_recover(struct userdata *u, const char *call, int err) {
return 0;
}
-static size_t check_left_to_record(struct userdata *u, size_t n_bytes) {
+static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) {
size_t left_to_record;
size_t rec_space = u->hwbuf_size - u->hwbuf_unused;
+ pa_bool_t 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) {
+ if (n_bytes <= rec_space)
left_to_record = rec_space - n_bytes;
+ else {
-#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
-
- } else {
+ /* We got a dropout. What a mess! */
left_to_record = 0;
+ overrun = TRUE;
#ifdef DEBUG_TIMING
PA_DEBUG_TRAP;
@@ -367,15 +425,36 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) {
if (pa_log_ratelimit())
pa_log_info("Overrun!");
+ }
- if (u->use_tsched)
- adjust_after_overrun(u);
+#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) {
+ pa_bool_t 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, pa_bool_t polled) {
+static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
pa_bool_t work_done = FALSE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_record;
@@ -391,6 +470,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
snd_pcm_sframes_t n;
size_t n_bytes;
int r;
+ pa_bool_t after_avail = TRUE;
if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
@@ -406,7 +486,8 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
pa_log_debug("avail: %lu", (unsigned long) n_bytes);
#endif
- left_to_record = check_left_to_record(u, n_bytes);
+ left_to_record = check_left_to_record(u, n_bytes, on_timeout);
+ on_timeout = FALSE;
if (u->use_tsched)
if (!polled &&
@@ -463,6 +544,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
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;
@@ -473,6 +557,12 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size)
frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size;
+ 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);
@@ -523,7 +613,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
return work_done ? 1 : 0;
}
-static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
+static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
int work_done = FALSE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_record;
@@ -539,6 +629,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
snd_pcm_sframes_t n;
size_t n_bytes;
int r;
+ pa_bool_t after_avail = TRUE;
if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
@@ -549,7 +640,8 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
}
n_bytes = (size_t) n * u->frame_size;
- left_to_record = check_left_to_record(u, n_bytes);
+ left_to_record = check_left_to_record(u, n_bytes, on_timeout);
+ on_timeout = FALSE;
if (u->use_tsched)
if (!polled &&
@@ -599,17 +691,26 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames);
pa_memblock_release(chunk.memblock);
- pa_assert(frames != 0);
-
if (PA_UNLIKELY(frames < 0)) {
pa_memblock_unref(chunk.memblock);
- if ((r = try_recover(u, "snd_pcm_readi", (int) (frames))) == 0)
+ 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;
@@ -666,15 +767,23 @@ static void update_smoother(struct userdata *u) {
now1 = pa_timespec_load(&htstamp);
}
- position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size);
-
/* 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 pa_usec_t source_get_latency(struct userdata *u) {
@@ -780,8 +889,7 @@ static int unsuspend(struct userdata *u) {
pa_sample_spec ss;
int err;
pa_bool_t b, d;
- unsigned nfrags;
- snd_pcm_uframes_t period_size;
+ snd_pcm_uframes_t period_size, buffer_size;
pa_assert(u);
pa_assert(!u->pcm_handle);
@@ -789,7 +897,7 @@ static int unsuspend(struct userdata *u) {
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_NONBLOCK|
SND_PCM_NO_AUTO_RESAMPLE|
SND_PCM_NO_AUTO_CHANNELS|
SND_PCM_NO_AUTO_FORMAT)) < 0) {
@@ -798,12 +906,12 @@ static int unsuspend(struct userdata *u) {
}
ss = u->source->sample_spec;
- nfrags = u->nfragments;
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, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
+ 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;
}
@@ -818,10 +926,11 @@ static int unsuspend(struct userdata *u) {
goto fail;
}
- if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) {
- pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)",
- (unsigned long) u->nfragments, (unsigned long) u->fragment_size,
- (unsigned long) nfrags, period_size * u->frame_size);
+ 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->fragment_size), (unsigned long) (period_size*u->frame_size));
goto fail;
}
@@ -834,7 +943,11 @@ static int unsuspend(struct userdata *u) {
/* FIXME: We need to reload the volume somehow */
snd_pcm_start(u->pcm_handle);
- pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);
+
+ u->read_count = 0;
+ pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE);
+ u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+ u->last_smoother_update = 0;
pa_log_info("Resumed successfully...");
@@ -846,7 +959,7 @@ fail:
u->pcm_handle = NULL;
}
- return -1;
+ return -PA_ERR_IO;
}
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
@@ -869,30 +982,34 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
- case PA_SOURCE_SUSPENDED:
+ case PA_SOURCE_SUSPENDED: {
+ int r;
pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
- if (suspend(u) < 0)
- return -1;
+ if ((r = suspend(u)) < 0)
+ return r;
break;
+ }
case PA_SOURCE_IDLE:
- case PA_SOURCE_RUNNING:
+ case PA_SOURCE_RUNNING: {
+ int r;
if (u->source->thread_info.state == PA_SOURCE_INIT) {
if (build_pollfd(u) < 0)
- return -1;
+ return -PA_ERR_IO;
snd_pcm_start(u->pcm_handle);
}
if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
- if (unsuspend(u) < 0)
- return -1;
+ if ((r = unsuspend(u)) < 0)
+ return r;
}
break;
+ }
case PA_SOURCE_UNLINKED:
case PA_SOURCE_INIT:
@@ -920,7 +1037,7 @@ static int source_set_state_cb(pa_source *s, pa_source_state_t new_state) {
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 -1;
+ return -PA_ERR_BUSY;
return 0;
}
@@ -962,15 +1079,11 @@ static void source_get_volume_cb(pa_source *s) {
if (pa_cvolume_equal(&u->hardware_volume, &r))
return;
- s->virtual_volume = u->hardware_volume = r;
-
- if (u->mixer_path->has_dB) {
- pa_cvolume reset;
+ s->volume = u->hardware_volume = r;
- /* Hmm, so the hardware volume changed, let's reset our software volume */
- pa_cvolume_reset(&reset, s->sample_spec.channels);
- pa_source_set_soft_volume(s, &reset);
- }
+ /* 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) {
@@ -983,7 +1096,7 @@ static void source_set_volume_cb(pa_source *s) {
pa_assert(u->mixer_handle);
/* Shift up by the base volume */
- pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume);
+ pa_sw_cvolume_divide_scalar(&r, &s->volume, s->base_volume);
if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
return;
@@ -994,13 +1107,26 @@ static void source_set_volume_cb(pa_source *s) {
u->hardware_volume = r;
if (u->mixer_path->has_dB) {
+ pa_cvolume new_soft_volume;
+ pa_bool_t accurate_enough;
/* Match exactly what the user requested by software */
- pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
+ pa_sw_cvolume_divide(&new_soft_volume, &s->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(t, sizeof(t), &s->virtual_volume));
+ pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume));
pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
- pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
+ pa_log_debug("Calculated software volume: %s (accurate-enough=%s)", pa_cvolume_snprint(t, sizeof(t), &new_soft_volume),
+ 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(t, sizeof(t), &r));
@@ -1008,7 +1134,7 @@ static void source_set_volume_cb(pa_source *s) {
/* We can't match exactly what the user requested, hence let's
* at least tell the user about it */
- s->virtual_volume = r;
+ s->volume = r;
}
}
@@ -1107,11 +1233,12 @@ static void thread_func(void *userdata) {
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
int work_done;
pa_usec_t sleep_usec = 0;
+ pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);
if (u->use_mmap)
- work_done = mmap_read(u, &sleep_usec, revents & POLLIN);
+ work_done = mmap_read(u, &sleep_usec, revents & POLLIN, on_timeout);
else
- work_done = unix_read(u, &sleep_usec, revents & POLLIN);
+ work_done = unix_read(u, &sleep_usec, revents & POLLIN, on_timeout);
if (work_done < 0)
goto fail;
@@ -1358,8 +1485,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
const char *dev_id = NULL;
pa_sample_spec ss, requested_ss;
pa_channel_map map;
- uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
- snd_pcm_uframes_t period_frames, tsched_frames;
+ 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;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_source_new_data data;
@@ -1393,8 +1520,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail;
}
- hwbuf_size = frag_size * nfrags;
+ 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) {
@@ -1433,6 +1562,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
5,
pa_rtclock_now(),
FALSE);
+ u->smoother_interval = SMOOTHER_MIN_INTERVAL;
dev_id = pa_modargs_get_value(
ma, "device_id",
@@ -1459,7 +1589,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
&u->device_name,
&ss, &map,
SND_PCM_STREAM_CAPTURE,
- &nfrags, &period_frames, tsched_frames,
+ &period_frames, &buffer_frames, tsched_frames,
&b, &d, mapping)))
goto fail;
@@ -1473,7 +1603,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
&u->device_name,
&ss, &map,
SND_PCM_STREAM_CAPTURE,
- &nfrags, &period_frames, tsched_frames,
+ &period_frames, &buffer_frames, tsched_frames,
&b, &d, profile_set, &mapping)))
goto fail;
@@ -1484,7 +1614,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
&u->device_name,
&ss, &map,
SND_PCM_STREAM_CAPTURE,
- &nfrags, &period_frames, tsched_frames,
+ &period_frames, &buffer_frames, tsched_frames,
&b, &d, FALSE)))
goto fail;
}
@@ -1510,11 +1640,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
u->use_tsched = use_tsched = FALSE;
}
- if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) {
- pa_log_info("Device is not a hardware device, disabling timer-based scheduling.");
- u->use_tsched = use_tsched = FALSE;
- }
-
if (u->use_mmap)
pa_log_info("Successfully enabled mmap() mode.");
@@ -1536,7 +1661,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
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) (period_frames * frame_size * nfrags));
+ 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"));
@@ -1577,18 +1702,25 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_source_set_rtpoll(u->source, u->rtpoll);
u->frame_size = frame_size;
- u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);
- u->nfragments = nfrags;
- u->hwbuf_size = u->fragment_size * nfrags;
- u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec);
+ 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 %u fragments of size %lu bytes, buffer time is %0.2fms",
- nfrags, (long unsigned) u->fragment_size,
+ 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->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->source->sample_spec);
+ u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_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);
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 1f3e5dcd..56d60dfb 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -93,6 +93,7 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
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)
@@ -148,33 +149,71 @@ try_auto:
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;
+}
+
/* Set the hardware parameters of the given ALSA device. Returns the
- * selected fragment settings in *period and *period_size */
+ * selected fragment settings in *buffer_size and *period_size. If tsched mode can be enabled */
int pa_alsa_set_hw_params(
snd_pcm_t *pcm_handle,
pa_sample_spec *ss,
- uint32_t *periods,
snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched,
pa_bool_t require_exact_channel_number) {
int ret = -1;
+ snd_pcm_hw_params_t *hwparams, *hwparams_copy;
+ int dir;
snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
- unsigned int _periods = periods ? *periods : 0;
- unsigned int r = ss->rate;
- unsigned int c = ss->channels;
- pa_sample_format_t f = ss->format;
- snd_pcm_hw_params_t *hwparams;
+ snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
pa_bool_t _use_mmap = use_mmap && *use_mmap;
pa_bool_t _use_tsched = use_tsched && *use_tsched;
- int dir;
+ 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));
@@ -208,111 +247,143 @@ int pa_alsa_set_hw_params(
if (!_use_mmap)
_use_tsched = FALSE;
- if ((ret = set_format(pcm_handle, hwparams, &f)) < 0)
+ if (!pa_alsa_pcm_is_hw(pcm_handle))
+ _use_tsched = FALSE;
+
+ if ((ret = set_format(pcm_handle, hwparams, &_ss.format)) < 0)
goto finish;
- if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0) {
+ 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;
}
if (require_exact_channel_number) {
- if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) {
- pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", c, pa_alsa_strerror(ret));
+ 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", c, pa_alsa_strerror(ret));
+ pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", _ss.channels, pa_alsa_strerror(ret));
goto finish;
}
- }
- if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) {
- pa_log_debug("snd_pcm_hw_params_set_periods_integer() failed: %s", pa_alsa_strerror(ret));
- goto finish;
+ _ss.channels = c;
}
- if (_period_size && tsched_size && _periods) {
-
- /* Adjust the buffer sizes, if we didn't get the rate we were asking for */
- _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
- tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
+ if (_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 (_use_tsched) {
- snd_pcm_uframes_t buffer_size = 0;
+ 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, &buffer_size)) < 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 %u ms", (unsigned) buffer_size * 1000 / r);
+ 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, period size second.");
+ goto success;
+ }
- _period_size = tsched_size;
- _periods = 1;
+ /* 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, buffer size second.");
+ goto success;
+ }
}
- if (_period_size > 0 && _periods > 0) {
- snd_pcm_uframes_t buffer_size;
+ if (_buffer_size > 0) {
+ snd_pcm_hw_params_copy(hwparams_copy, hwparams);
- buffer_size = _periods * _period_size;
-
- if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
- pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret));
+ /* 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 second.");
+ goto success;
+ }
}
- if (_periods > 0) {
-
- /* First we pass 0 as direction to get exactly what we
- * asked for. That this is necessary is presumably a bug
- * in ALSA. All in all this is mostly a hint to ALSA, so
- * we don't care if this fails. */
-
- dir = 0;
- if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) {
- dir = 1;
- if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) {
- dir = -1;
- if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
- pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret));
- }
+ 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 second.");
+ goto success;
}
}
}
- if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
+ 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 != r)
- pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);
+ 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 != c)
- pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);
+ 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 != f)
- 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(f));
+ 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_prepare(pcm_handle)) < 0) {
pa_log_info("snd_pcm_prepare() failed: %s", pa_alsa_strerror(ret));
goto finish;
}
+ 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_periods(hwparams, &_periods, &dir)) < 0) {
- pa_log_info("snd_pcm_hw_params_get_period{s|_size}() failed: %s", pa_alsa_strerror(ret));
+ (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 the sample rate deviates too much, we need to resample */
- if (r < ss->rate*.95 || r > ss->rate*1.05)
- ss->rate = r;
- ss->channels = (uint8_t) c;
- ss->format = f;
+ if (_ss.rate < ss->rate*.95 || _ss.rate > ss->rate*1.05)
+ ss->rate = _ss.rate;
+ ss->channels = _ss.channels;
+ ss->format = _ss.format;
- pa_assert(_periods > 0);
pa_assert(_period_size > 0);
+ pa_assert(_buffer_size > 0);
- if (periods)
- *periods = _periods;
+ if (buffer_size)
+ *buffer_size = _buffer_size;
if (period_size)
*period_size = _period_size;
@@ -390,8 +461,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
pa_sample_spec *ss,
pa_channel_map* map,
int mode,
- uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched,
@@ -407,8 +478,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
pa_assert(dev);
pa_assert(ss);
pa_assert(map);
- pa_assert(nfrags);
- pa_assert(period_size);
pa_assert(ps);
/* First we try to find a device string with a superset of the
@@ -430,8 +499,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
ss,
map,
mode,
- nfrags,
period_size,
+ buffer_size,
tsched_size,
use_mmap,
use_tsched,
@@ -457,8 +526,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
ss,
map,
mode,
- nfrags,
period_size,
+ buffer_size,
tsched_size,
use_mmap,
use_tsched,
@@ -475,7 +544,18 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
/* 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, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE);
+ 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)
@@ -490,8 +570,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
pa_sample_spec *ss,
pa_channel_map* map,
int mode,
- uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched,
@@ -505,8 +585,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
pa_assert(dev);
pa_assert(ss);
pa_assert(map);
- pa_assert(nfrags);
- pa_assert(period_size);
pa_assert(m);
try_ss.channels = m->channel_map.channels;
@@ -521,8 +599,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
&try_ss,
&try_map,
mode,
- nfrags,
period_size,
+ buffer_size,
tsched_size,
use_mmap,
use_tsched,
@@ -544,8 +622,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_sample_spec *ss,
pa_channel_map* map,
int mode,
- uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched,
@@ -576,7 +654,15 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_log_debug("Managed to open %s", d);
- if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) {
+ 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;
@@ -629,8 +715,8 @@ snd_pcm_t *pa_alsa_open_by_template(
pa_sample_spec *ss,
pa_channel_map* map,
int mode,
- uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched,
@@ -650,8 +736,8 @@ snd_pcm_t *pa_alsa_open_by_template(
ss,
map,
mode,
- nfrags,
period_size,
+ buffer_size,
tsched_size,
use_mmap,
use_tsched,
@@ -897,7 +983,7 @@ void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name) {
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'", name);
+ pa_log_warn("Error opening low-level control device '%s': %s", name, snd_strerror(err));
return;
}
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 830a922e..265cd28c 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -42,8 +42,8 @@
int pa_alsa_set_hw_params(
snd_pcm_t *pcm_handle,
pa_sample_spec *ss, /* modified at return */
- uint32_t *periods, /* 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,
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
@@ -60,8 +60,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
- uint32_t *nfrags, /* 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,
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
@@ -75,8 +75,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
- uint32_t *nfrags, /* 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,
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
@@ -89,8 +89,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
- uint32_t *nfrags, /* 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,
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
@@ -104,8 +104,8 @@ snd_pcm_t *pa_alsa_open_by_template(
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
- uint32_t *nfrags, /* 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,
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
diff --git a/src/modules/alsa/mixer/Makefile b/src/modules/alsa/mixer/Makefile
new file mode 120000
index 00000000..b4955194
--- /dev/null
+++ b/src/modules/alsa/mixer/Makefile
@@ -0,0 +1 @@
+../../../pulse/Makefile \ No newline at end of file
diff --git a/src/modules/alsa/mixer/paths/Makefile b/src/modules/alsa/mixer/paths/Makefile
new file mode 120000
index 00000000..dc23aaa2
--- /dev/null
+++ b/src/modules/alsa/mixer/paths/Makefile
@@ -0,0 +1 @@
+../../../../pulse/Makefile \ No newline at end of file
diff --git a/src/modules/alsa/mixer/paths/analog-input.conf.common b/src/modules/alsa/mixer/paths/analog-input.conf.common
index 6728a6ae..87af38b3 100644
--- a/src/modules/alsa/mixer/paths/analog-input.conf.common
+++ b/src/modules/alsa/mixer/paths/analog-input.conf.common
@@ -78,6 +78,10 @@ priority = 19
name = input-microphone
priority = 19
+[Option Input Source:Internal Mic]
+name = input-microphone
+priority = 19
+
[Option Input Source:Line]
name = input-linein
priority = 18
@@ -90,6 +94,10 @@ priority = 18
name = input-linein
priority = 18
+[Option Input Source:Docking-Station]
+name = input-docking
+priority = 17
+
;;; ' Capture Source'
[Element Capture Source]
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
index c018e0eb..691cb3f2 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
@@ -44,6 +44,10 @@ volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
+[Element Speaker]
+switch = off
+volume = off
+
[Element Front]
switch = off
volume = off
diff --git a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf
index 7a267890..3457d4a2 100644
--- a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf
@@ -41,9 +41,18 @@ volume = merge
override-map.1 = lfe
override-map.2 = lfe,lfe
+; 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 = off
-volume = off
+switch = mute
+volume = zero
+
+[Element Speaker]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
[Element Front]
switch = off
diff --git a/src/modules/alsa/mixer/paths/analog-output-mono.conf b/src/modules/alsa/mixer/paths/analog-output-mono.conf
index f6cb9f8a..dc270cfe 100644
--- a/src/modules/alsa/mixer/paths/analog-output-mono.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-mono.conf
@@ -38,9 +38,18 @@ 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 = off
-volume = off
+switch = mute
+volume = zero
+
+[Element Speaker]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
[Element Front]
switch = off
diff --git a/src/modules/alsa/mixer/paths/analog-output.conf b/src/modules/alsa/mixer/paths/analog-output.conf
index ea108aaf..f71a05a1 100644
--- a/src/modules/alsa/mixer/paths/analog-output.conf
+++ b/src/modules/alsa/mixer/paths/analog-output.conf
@@ -37,9 +37,18 @@ override-map.2 = all-left,all-right
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 = off
-volume = off
+switch = mute
+volume = zero
+
+[Element Speaker]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
[Element Front]
switch = mute
diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common
index cc1185f4..3c6ce803 100644
--- a/src/modules/alsa/mixer/paths/analog-output.conf.common
+++ b/src/modules/alsa/mixer/paths/analog-output.conf.common
@@ -104,8 +104,8 @@ switch = select
[Option External Amplifier:on]
name = output-amplifier-on
-priority = 0
+priority = 10
[Option External Amplifier:off]
name = output-amplifier-off
-priority = 10
+priority = 0
diff --git a/src/modules/alsa/mixer/profile-sets/Makefile b/src/modules/alsa/mixer/profile-sets/Makefile
new file mode 120000
index 00000000..dc23aaa2
--- /dev/null
+++ b/src/modules/alsa/mixer/profile-sets/Makefile
@@ -0,0 +1 @@
+../../../../pulse/Makefile \ No newline at end of file
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 55f6a6e2..6bea33d7 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -329,7 +329,7 @@ int pa__init(pa_module *m) {
if (!u->profile_set)
goto fail;
- pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec);
+ 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_card_new_data_init(&data);
data.driver = __FILE__;
diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 66e1c31e..f8c5b778 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1,7 +1,7 @@
/***
This file is part of PulseAudio.
- Copyright 2008 Joao Paulo Rechi Vita
+ Copyright 2008-2009 Joao 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
@@ -95,6 +95,7 @@ static pa_bluetooth_device* device_new(const char *path) {
d->audio_state = PA_BT_AUDIO_STATE_INVALID;
d->audio_sink_state = PA_BT_AUDIO_STATE_INVALID;
+ d->audio_source_state = PA_BT_AUDIO_STATE_INVALID;
d->headset_state = PA_BT_AUDIO_STATE_INVALID;
return d;
@@ -122,9 +123,10 @@ static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
return
d->device_info_valid &&
- (d->audio_state != PA_BT_AUDIO_STATE_INVALID ||
- d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
- d->headset_state != PA_BT_AUDIO_STATE_INVALID);
+ (d->audio_state != PA_BT_AUDIO_STATE_INVALID &&
+ (d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
+ d->audio_source_state != PA_BT_AUDIO_STATE_INVALID ||
+ d->headset_state != PA_BT_AUDIO_STATE_INVALID));
}
static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
@@ -226,10 +228,6 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
node = uuid_new(value);
PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
- /* 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 */
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
- send_and_add_to_pending(y, d, m, get_properties_reply);
-
/* Vudentz said the interfaces are here when the UUIDs are announced */
if (strcasecmp(HSP_HS_UUID, value) == 0 || strcasecmp(HFP_HS_UUID, value) == 0) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Headset", "GetProperties"));
@@ -237,8 +235,15 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
} else if (strcasecmp(A2DP_SINK_UUID, value) == 0) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSink", "GetProperties"));
send_and_add_to_pending(y, d, m, get_properties_reply);
+ } else if (strcasecmp(A2DP_SOURCE_UUID, value) == 0) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSource", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
}
+ /* 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 */
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+
if (!dbus_message_iter_next(&ai))
break;
}
@@ -278,7 +283,7 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *state, DBusMessa
dbus_message_iter_recurse(i, &variant_i);
-/* pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|Headset}.%s", key); */
+/* pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|AudioSource|Headset}.%s", key); */
switch (dbus_message_iter_get_arg_type(&variant_i)) {
@@ -390,6 +395,9 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
} else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) {
if (parse_audio_property(y, &d->audio_sink_state, &dict_i) < 0)
goto finish;
+ } else if (dbus_message_has_interface(p->message, "org.bluez.AudioSource")) {
+ if (parse_audio_property(y, &d->audio_source_state, &dict_i) < 0)
+ goto finish;
}
}
@@ -440,8 +448,8 @@ static void found_device(pa_bluetooth_discovery *y, const char* path) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties"));
send_and_add_to_pending(y, d, m, get_properties_reply);
- /* Before we read the other properties (Audio, AudioSink, Headset) we wait
- * that the UUID is read */
+ /* Before we read the other properties (Audio, AudioSink, AudioSource,
+ * Headset) we wait that the UUID is read */
}
static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
@@ -616,6 +624,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
} 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.Device", "PropertyChanged")) {
pa_bluetooth_device *d;
@@ -643,6 +652,9 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
} else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) {
if (parse_audio_property(y, &d->audio_sink_state, &arg_i) < 0)
goto fail;
+ } else if (dbus_message_has_interface(m, "org.bluez.AudioSource")) {
+ if (parse_audio_property(y, &d->audio_source_state, &arg_i) < 0)
+ goto fail;
}
run_callback(y, d, FALSE);
@@ -650,6 +662,21 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ } else if (dbus_message_is_signal(m, "org.bluez.Device", "DisconnectRequested")) {
+ pa_bluetooth_device *d;
+
+ if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
+ /* Device will disconnect in 2 sec */
+ d->audio_state = PA_BT_AUDIO_STATE_DISCONNECTED;
+ d->audio_sink_state = PA_BT_AUDIO_STATE_DISCONNECTED;
+ d->audio_source_state = PA_BT_AUDIO_STATE_DISCONNECTED;
+ d->headset_state = PA_BT_AUDIO_STATE_DISCONNECTED;
+
+ run_callback(y, 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;
@@ -758,14 +785,16 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
if (pa_dbus_add_matches(
pa_dbus_connection_get(y->connection), &err,
- "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'",
+ "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.Device',member='DisconnectRequested'",
"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'", NULL) < 0) {
+ "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'", NULL) < 0) {
pa_log("Failed to add D-Bus matches: %s", err.message);
goto fail;
}
@@ -809,15 +838,17 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
if (y->connection) {
pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
- "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'",
+ "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.Device',member='DisconnectRequested'",
"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'", NULL);
+ "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'", NULL);
dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 265caf40..e2a0c3d5 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -4,7 +4,7 @@
/***
This file is part of PulseAudio.
- Copyright 2008 Joao Paulo Rechi Vita
+ Copyright 2008-2009 Joao 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
@@ -53,14 +53,13 @@ struct pa_bluetooth_uuid {
PA_LLIST_FIELDS(pa_bluetooth_uuid);
};
-/* This enum is shared among Audio, Headset, and AudioSink, although not all values are acceptable in all profiles */
+/* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */
typedef enum pa_bt_audio_state {
PA_BT_AUDIO_STATE_INVALID = -1,
PA_BT_AUDIO_STATE_DISCONNECTED,
PA_BT_AUDIO_STATE_CONNECTING,
PA_BT_AUDIO_STATE_CONNECTED,
- PA_BT_AUDIO_STATE_PLAYING,
- PA_BT_AUDIO_STATE_LAST
+ PA_BT_AUDIO_STATE_PLAYING
} pa_bt_audio_state_t;
struct pa_bluetooth_device {
@@ -85,6 +84,9 @@ struct pa_bluetooth_device {
/* AudioSink state */
pa_bt_audio_state_t audio_sink_state;
+ /* AudioSource state */
+ pa_bt_audio_state_t audio_source_state;
+
/* Headset state */
pa_bt_audio_state_t headset_state;
};
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 0560ef32..4592fca1 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1,7 +1,7 @@
/***
This file is part of PulseAudio.
- Copyright 2008 Joao Paulo Rechi Vita
+ Copyright 2008-2009 Joao 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
@@ -129,6 +129,7 @@ struct hsp_info {
enum profile {
PROFILE_A2DP,
+ PROFILE_A2DP_SOURCE,
PROFILE_HSP,
PROFILE_OFF
};
@@ -178,6 +179,7 @@ struct userdata {
};
#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)
@@ -219,9 +221,7 @@ static int service_recv(struct userdata *u, bt_audio_msg_header_t *msg, size_t r
pa_assert(u);
pa_assert(u->service_fd >= 0);
pa_assert(msg);
-
- if (room <= 0)
- room = BT_SUGGESTED_BUFFER_SIZE;
+ pa_assert(room >= sizeof(*msg));
pa_log_debug("Trying to receive message from audio service...");
@@ -234,6 +234,11 @@ static int service_recv(struct userdata *u, bt_audio_msg_header_t *msg, size_t r
return -1;
}
+ if (msg->length > room) {
+ pa_log_error("Not enough room.");
+ return -1;
+ }
+
/* Secondly, read the payload */
if (msg->length > sizeof(*msg)) {
@@ -307,7 +312,7 @@ static int parse_caps(struct userdata *u, uint8_t seid, const struct bt_get_capa
pa_log_debug("Payload size is %lu %lu", (unsigned long) bytes_left, (unsigned long) sizeof(*codec));
- if ((u->profile == PROFILE_A2DP && codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP) ||
+ if (((u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) && codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP) ||
(u->profile == PROFILE_HSP && codec->transport != BT_CAPABILITIES_TRANSPORT_SCO)) {
pa_log_error("Got capabilities for wrong codec.");
return -1;
@@ -344,6 +349,26 @@ static int parse_caps(struct userdata *u, uint8_t seid, const struct bt_get_capa
return codec->seid;
memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities));
+
+ } else if (u->profile == PROFILE_A2DP_SOURCE) {
+
+ while (bytes_left > 0) {
+ if ((codec->type == BT_A2DP_SBC_SOURCE) && !codec->lock)
+ break;
+
+ bytes_left -= codec->length;
+ codec = (const codec_capabilities_t*) ((const uint8_t*) codec + codec->length);
+ }
+
+ if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities))
+ return -1;
+
+ pa_assert(codec->type == BT_A2DP_SBC_SOURCE);
+
+ if (codec->configured && seid == 0)
+ return codec->seid;
+
+ memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities));
}
return 0;
@@ -368,7 +393,7 @@ static int get_caps(struct userdata *u, uint8_t seid) {
msg.getcaps_req.seid = seid;
pa_strlcpy(msg.getcaps_req.object, u->path, sizeof(msg.getcaps_req.object));
- if (u->profile == PROFILE_A2DP)
+ if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE)
msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
else {
pa_assert(u->profile == PROFILE_HSP);
@@ -451,7 +476,7 @@ static int setup_a2dp(struct userdata *u) {
};
pa_assert(u);
- pa_assert(u->profile == PROFILE_A2DP);
+ pa_assert(u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE);
cap = &u->a2dp.sbc_capabilities;
@@ -652,8 +677,8 @@ static int set_conf(struct userdata *u) {
msg.open_req.h.length = sizeof(msg.open_req);
pa_strlcpy(msg.open_req.object, u->path, sizeof(msg.open_req.object));
- msg.open_req.seid = u->profile == PROFILE_A2DP ? u->a2dp.sbc_capabilities.capability.seid : BT_A2DP_SEID_RANGE + 1;
- msg.open_req.lock = u->profile == PROFILE_A2DP ? BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK;
+ msg.open_req.seid = (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) ? u->a2dp.sbc_capabilities.capability.seid : BT_A2DP_SEID_RANGE + 1;
+ msg.open_req.lock = (u->profile == PROFILE_A2DP) ? BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK;
if (service_send(u, &msg.open_req.h) < 0)
return -1;
@@ -661,7 +686,7 @@ static int set_conf(struct userdata *u) {
if (service_expect(u, &msg.open_rsp.h, sizeof(msg), BT_OPEN, sizeof(msg.open_rsp)) < 0)
return -1;
- if (u->profile == PROFILE_A2DP ) {
+ if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) {
u->sample_spec.format = PA_SAMPLE_S16LE;
if (setup_a2dp(u) < 0)
@@ -679,7 +704,7 @@ static int set_conf(struct userdata *u) {
msg.setconf_req.h.name = BT_SET_CONFIGURATION;
msg.setconf_req.h.length = sizeof(msg.setconf_req);
- if (u->profile == PROFILE_A2DP) {
+ if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) {
memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities, sizeof(u->a2dp.sbc_capabilities));
} else {
msg.setconf_req.codec.transport = BT_CAPABILITIES_TRANSPORT_SCO;
@@ -697,7 +722,7 @@ static int set_conf(struct userdata *u) {
u->link_mtu = msg.setconf_rsp.link_mtu;
/* setup SBC encoder now we agree on parameters */
- if (u->profile == PROFILE_A2DP) {
+ if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) {
setup_sbc(&u->a2dp);
u->block_size =
@@ -881,7 +906,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
*((pa_usec_t*) data) = wi > ri ? wi - ri : 0;
}
- *((pa_usec_t*) data) += u->sink->fixed_latency;
+ *((pa_usec_t*) data) += u->sink->thread_info.fixed_latency;
return 0;
}
}
@@ -943,7 +968,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
- *((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->fixed_latency;
+ *((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->thread_info.fixed_latency;
return 0;
}
@@ -1250,6 +1275,119 @@ static int a2dp_process_render(struct userdata *u) {
return ret;
}
+static int a2dp_process_push(struct userdata *u) {
+ int ret = 0;
+ pa_memchunk memchunk;
+
+ pa_assert(u);
+ pa_assert(u->profile == PROFILE_A2DP_SOURCE);
+ pa_assert(u->source);
+ pa_assert(u->read_smoother);
+
+ memchunk.memblock = pa_memblock_new(u->core->mempool, u->block_size);
+ memchunk.index = memchunk.length = 0;
+
+ for (;;) {
+ pa_bool_t 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;
+ unsigned frame_count;
+
+ 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);
+
+ u->read_index += (uint64_t) l;
+
+ /* 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();
+ }
+
+ 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);
+
+ 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 && to_write > 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 -1;
+ }
+
+/* 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); */
+
+ pa_assert_fp((size_t) decoded <= to_decode);
+ pa_assert_fp((size_t) decoded == a2dp->frame_length);
+
+ pa_assert_fp((size_t) written <= to_write);
+ 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;
+
+ frame_count++;
+ }
+
+ pa_memblock_release(memchunk.memblock);
+
+ pa_source_post(u->source, &memchunk);
+
+ ret = 1;
+ break;
+ }
+
+ pa_memblock_unref(memchunk.memblock);
+
+ return ret;
+}
+
static void thread_func(void *userdata) {
struct userdata *u = userdata;
unsigned do_write = 0;
@@ -1262,11 +1400,11 @@ static void thread_func(void *userdata) {
if (u->core->realtime_scheduling)
pa_make_realtime(u->core->realtime_priority);
+ pa_thread_mq_install(&u->thread_mq);
+
if (start_stream_fd(u) < 0)
goto fail;
- pa_thread_mq_install(&u->thread_mq);
-
for (;;) {
struct pollfd *pollfd;
int ret;
@@ -1285,7 +1423,12 @@ static void thread_func(void *userdata) {
if (pollfd && (pollfd->revents & POLLIN)) {
int n_read;
- if ((n_read = hsp_process_push(u)) < 0)
+ if (u->profile == PROFILE_HSP)
+ n_read = hsp_process_push(u);
+ else
+ n_read = a2dp_process_push(u);
+
+ if (n_read < 0)
goto fail;
/* We just read something, so we are supposed to write something, too */
@@ -1319,18 +1462,21 @@ static void thread_func(void *userdata) {
if (u->write_index > 0 && audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC) {
pa_usec_t skip_usec;
uint64_t skip_bytes;
- pa_memchunk tmp;
skip_usec = audio_to_send - MAX_PLAYBACK_CATCH_UP_USEC;
skip_bytes = pa_usec_to_bytes(skip_usec, &u->sample_spec);
- pa_log_warn("Skipping %llu us (= %llu bytes) in audio stream",
- (unsigned long long) skip_usec,
- (unsigned long long) skip_bytes);
+ if (skip_bytes > 0) {
+ pa_memchunk tmp;
- pa_sink_render_full(u->sink, skip_bytes, &tmp);
- pa_memblock_unref(tmp.memblock);
- u->write_index += skip_bytes;
+ 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;
+ }
}
do_write = 1;
@@ -1446,12 +1592,12 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) {
pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
- pa_sink_volume_changed(u->sink, &v, TRUE);
+ pa_sink_volume_changed(u->sink, &v);
} else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) {
pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
- pa_source_volume_changed(u->source, &v, TRUE);
+ pa_source_volume_changed(u->source, &v);
}
}
}
@@ -1473,12 +1619,12 @@ static void sink_set_volume_cb(pa_sink *s) {
if (u->profile != PROFILE_HSP)
return;
- gain = (pa_cvolume_max(&s->virtual_volume) * 15) / PA_VOLUME_NORM;
+ gain = (pa_cvolume_max(&s->real_volume) * 15) / PA_VOLUME_NORM;
if (gain > 15)
gain = 15;
- pa_cvolume_set(&s->virtual_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ pa_cvolume_set(&s->real_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetSpeakerGain"));
pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
@@ -1497,12 +1643,12 @@ static void source_set_volume_cb(pa_source *s) {
if (u->profile != PROFILE_HSP)
return;
- gain = (pa_cvolume_max(&s->virtual_volume) * 15) / PA_VOLUME_NORM;
+ gain = (pa_cvolume_max(&s->volume) * 15) / PA_VOLUME_NORM;
if (gain > 15)
gain = 15;
- pa_cvolume_set(&s->virtual_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ pa_cvolume_set(&s->volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetMicrophoneGain"));
pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
@@ -1684,7 +1830,7 @@ static int add_source(struct userdata *u) {
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", u->profile == PROFILE_A2DP ? "a2dp" : "hsp");
+ pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP_SOURCE ? "a2dp_source" : "hsp");
if (u->profile == PROFILE_HSP)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
data.card = u->card;
@@ -1709,7 +1855,7 @@ static int add_source(struct userdata *u) {
u->source->parent.process_msg = source_process_msg;
pa_source_set_fixed_latency(u->source,
- (/* u->profile == PROFILE_A2DP ? FIXED_LATENCY_RECORD_A2DP : */ FIXED_LATENCY_RECORD_HSP) +
+ (u->profile == PROFILE_A2DP_SOURCE ? FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_HSP) +
pa_bytes_to_usec(u->block_size, &u->sample_spec));
}
@@ -1736,7 +1882,8 @@ static void shutdown_bt(struct userdata *u) {
if (u->service_fd >= 0) {
pa_close(u->service_fd);
u->service_fd = -1;
- u->service_write_type = u->service_write_type = 0;
+ u->service_write_type = 0;
+ u->service_read_type = 0;
}
if (u->write_memchunk.memblock) {
@@ -1752,7 +1899,8 @@ static int init_bt(struct userdata *u) {
shutdown_bt(u);
u->stream_write_type = 0;
- u->service_write_type = u->service_write_type = 0;
+ u->service_write_type = 0;
+ u->service_read_type = 0;
if ((u->service_fd = bt_audio_service_open()) < 0) {
pa_log_error("Couldn't connect to bluetooth audio service");
@@ -1804,7 +1952,8 @@ static int init_profile(struct userdata *u) {
if (add_sink(u) < 0)
r = -1;
- if (u->profile == PROFILE_HSP)
+ if (u->profile == PROFILE_HSP ||
+ u->profile == PROFILE_A2DP_SOURCE)
if (add_source(u) < 0)
r = -1;
@@ -2045,6 +2194,20 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
pa_hashmap_put(data.profiles, p->name, p);
}
+ if (pa_bluetooth_uuid_has(device->uuids, A2DP_SOURCE_UUID)) {
+ p = pa_card_profile_new("a2dp_source", _("High Fidelity Capture (A2DP)"), sizeof(enum profile));
+ p->priority = 10;
+ p->n_sinks = 0;
+ p->n_sources = 1;
+ p->max_sink_channels = 0;
+ p->max_source_channels = 2;
+
+ d = PA_CARD_PROFILE_DATA(p);
+ *d = PROFILE_A2DP_SOURCE;
+
+ pa_hashmap_put(data.profiles, p->name, p);
+ }
+
if (pa_bluetooth_uuid_has(device->uuids, HSP_HS_UUID) ||
pa_bluetooth_uuid_has(device->uuids, HFP_HS_UUID)) {
p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile));
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 788fee00..02fd4240 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -1,7 +1,7 @@
/***
This file is part of PulseAudio.
- Copyright 2008 Joao Paulo Rechi Vita
+ Copyright 2008-2009 Joao 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
@@ -84,7 +84,7 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const
mi = pa_hashmap_get(u->hashmap, d->path);
if (!d->dead &&
- d->device_connected > 0 && d->audio_state >= PA_BT_AUDIO_STATE_CONNECTED) {
+ d->device_connected > 0 && (d->audio_state >= PA_BT_AUDIO_STATE_CONNECTED || d->audio_source_state >= PA_BT_AUDIO_STATE_CONNECTED)) {
if (!mi) {
pa_module *m = NULL;
@@ -116,6 +116,9 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const
}
#endif
+ if (d->audio_source_state >= PA_BT_AUDIO_STATE_CONNECTED)
+ args = pa_sprintf_malloc("%s profile=\"a2dp_source\"", args);
+
pa_log_debug("Loading module-bluetooth-device %s", args);
m = pa_module_load(u->module->core, "module-bluetooth-device", args);
pa_xfree(args);
diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c
index c01ebbf6..85523b39 100644
--- a/src/modules/gconf/module-gconf.c
+++ b/src/modules/gconf/module-gconf.c
@@ -52,9 +52,6 @@ PA_MODULE_LOAD_ONCE(TRUE);
#define MAX_MODULES 10
#define BUF_MAX 2048
-/* #undef PA_GCONF_HELPER */
-/* #define PA_GCONF_HELPER "/home/lennart/projects/pulseaudio/src/gconf-helper" */
-
struct module_item {
char *name;
char *args;
@@ -343,7 +340,11 @@ int pa__init(pa_module*m) {
u->io_event = NULL;
u->buf_fill = 0;
- if ((u->fd = pa_start_child_for_read(PA_GCONF_HELPER, NULL, &u->pid)) < 0)
+ if ((u->fd = pa_start_child_for_read(
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+ pa_run_from_build_tree() ? PA_BUILDDIR "/.libs/gconf-helper" :
+#endif
+ PA_GCONF_HELPER, NULL, &u->pid)) < 0)
goto fail;
u->io_event = m->core->mainloop->io_new(
diff --git a/src/modules/hal-util.c b/src/modules/hal-util.c
index e2a2d8d7..2d59f51d 100644
--- a/src/modules/hal-util.c
+++ b/src/modules/hal-util.c
@@ -65,7 +65,7 @@ int pa_hal_get_info(pa_core *core, pa_proplist *p, int card) {
goto finish;
}
- if (!(udis = libhal_find_device_by_capability(hal, "sound", &n, &error)) < 0) {
+ if (!(udis = libhal_find_device_by_capability(hal, "sound", &n, &error))) {
pa_log_error("Couldn't find devices: %s: %s", error.name, error.message);
goto finish;
}
diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
index aee1c650..3d7de9c6 100644
--- a/src/modules/module-always-sink.c
+++ b/src/modules/module-always-sink.c
@@ -24,6 +24,7 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/i18n.h>
#include <pulsecore/core.h>
#include <pulsecore/sink-input.h>
@@ -35,7 +36,7 @@
#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_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(
@@ -78,7 +79,8 @@ static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata*
u->ignore = TRUE;
- t = pa_sprintf_malloc("sink_name=%s", u->sink_name);
+ 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);
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index 16de6890..a186c899 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -58,7 +58,7 @@ PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"sink_properties=<properties for the sink> "
"slaves=<slave sinks> "
- "adjust_time=<seconds> "
+ "adjust_time=<how often to readjust rates in s> "
"resample_method=<method> "
"format=<sample format> "
"rate=<sample rate> "
@@ -69,7 +69,7 @@ PA_MODULE_USAGE(
#define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
-#define DEFAULT_ADJUST_TIME 10
+#define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
#define BLOCK_USEC (PA_USEC_PER_MSEC * 200)
@@ -91,6 +91,7 @@ struct output {
pa_sink *sink;
pa_sink_input *sink_input;
+ pa_bool_t ignore_state_change;
pa_asyncmsgq *inq, /* Message queue from the sink thread to this sink input */
*outq; /* Message queue from this sink input to the sink thread */
@@ -99,9 +100,12 @@ struct output {
pa_memblockq *memblockq;
+ /* For communication of the stream latencies to the main thread */
pa_usec_t total_latency;
+ /* For coomunication of the stream parameters to the sink thread */
pa_atomic_t max_request;
+ pa_atomic_t requested_latency;
PA_LLIST_FIELDS(struct output);
};
@@ -116,7 +120,7 @@ struct userdata {
pa_rtpoll *rtpoll;
pa_time_event *time_event;
- uint32_t adjust_time;
+ pa_usec_t adjust_time;
pa_bool_t automatic;
pa_bool_t auto_desc;
@@ -125,8 +129,6 @@ struct userdata {
pa_resample_method_t resample_method;
- struct timeval adjust_timestamp;
-
pa_usec_t block_usec;
pa_idxset* outputs; /* managed in main context */
@@ -146,13 +148,16 @@ enum {
SINK_MESSAGE_REMOVE_OUTPUT,
SINK_MESSAGE_NEED,
SINK_MESSAGE_UPDATE_LATENCY,
- SINK_MESSAGE_UPDATE_MAX_REQUEST
+ SINK_MESSAGE_UPDATE_MAX_REQUEST,
+ SINK_MESSAGE_UPDATE_REQUESTED_LATENCY
};
enum {
SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
};
+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);
@@ -172,7 +177,7 @@ static void adjust_rates(struct userdata *u) {
if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
return;
- for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+ PA_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)))
@@ -189,6 +194,11 @@ static void adjust_rates(struct userdata *u) {
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)
@@ -203,22 +213,22 @@ static void adjust_rates(struct userdata *u) {
base_rate = u->sink->sample_spec.rate;
- for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+ PA_IDXSET_FOREACH(o, u->outputs, idx) {
uint32_t r = base_rate;
if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
continue;
if (o->total_latency < target_latency)
- r -= (uint32_t) ((((double) (target_latency - o->total_latency))/(double)u->adjust_time)*(double)r/PA_USEC_PER_SEC);
+ r -= (uint32_t) ((((double) (target_latency - o->total_latency))/(double)u->adjust_time)*(double)r);
else if (o->total_latency > target_latency)
- r += (uint32_t) ((((double) (o->total_latency - target_latency))/(double)u->adjust_time)*(double)r/PA_USEC_PER_SEC);
+ r += (uint32_t) ((((double) (o->total_latency - target_latency))/(double)u->adjust_time)*(double)r);
if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) {
- pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), base_rate, r);
+ pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->sink->name, base_rate, r);
pa_sink_input_set_rate(o->sink_input, base_rate);
} else {
- pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), r, (double) r / base_rate, (float) o->total_latency);
+ pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", o->sink_input->sink->name, r, (double) r / base_rate, (float) o->total_latency);
pa_sink_input_set_rate(o->sink_input, r);
}
}
@@ -235,7 +245,7 @@ static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct tim
adjust_rates(u);
- pa_core_rttime_restart(u->core, e, pa_rtclock_now() + u->adjust_time * PA_USEC_PER_SEC);
+ 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) {
@@ -355,18 +365,15 @@ static void render_memblock(struct userdata *u, struct output *o, size_t length)
u->thread_info.counter += chunk.length;
/* OK, let's send this data to the other threads */
- for (j = u->thread_info.active_outputs; j; j = j->next)
-
- /* Send to other outputs, which are not the requesting
- * one */
+ PA_LLIST_FOREACH(j, u->thread_info.active_outputs) {
+ if (j == o)
+ continue;
- if (j != o)
- pa_asyncmsgq_post(j->inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL);
+ pa_asyncmsgq_post(j->inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL);
+ }
/* And place it directly into the requesting output's queue */
- if (o)
- pa_memblockq_push_align(o->memblockq, &chunk);
-
+ pa_memblockq_push_align(o->memblockq, &chunk);
pa_memblock_unref(chunk.memblock);
}
}
@@ -402,10 +409,18 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
/* 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;
}
@@ -440,13 +455,35 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
return;
pa_atomic_store(&o->max_request, (int) nbytes);
-
pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL);
}
+/* Called from thread context */
+static void sink_input_update_sink_requested_latency_cb(pa_sink_input *i) {
+ struct output *o;
+ pa_usec_t c;
+
+ pa_assert(i);
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(o = i->userdata);
+
+ c = pa_sink_get_requested_latency_within_thread(i->sink);
+
+ if (c == (pa_usec_t) -1)
+ c = i->sink->thread_info.max_latency;
+
+ if (pa_atomic_load(&o->requested_latency) == (int) c)
+ return;
+
+ pa_atomic_store(&o->requested_latency, (int) c);
+ pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_REQUESTED_LATENCY, 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 c;
pa_sink_input_assert_ref(i);
pa_assert_se(o = i->userdata);
@@ -455,14 +492,24 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_assert(!o->inq_rtpoll_item_read && !o->outq_rtpoll_item_write);
o->inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
- i->sink->rtpoll,
+ i->sink->thread_info.rtpoll,
PA_RTPOLL_LATE, /* This one is not that important, since we check for data in _peek() anyway. */
o->inq);
o->outq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
- i->sink->rtpoll,
+ i->sink->thread_info.rtpoll,
PA_RTPOLL_EARLY,
o->outq);
+
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
+
+ pa_atomic_store(&o->max_request, (int) pa_sink_input_get_max_request(i));
+
+ c = pa_sink_get_requested_latency_within_thread(i->sink);
+ pa_atomic_store(&o->requested_latency, (int) (c == (pa_usec_t) -1 ? 0 : c));
+
+ pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL);
+ pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_REQUESTED_LATENCY, NULL, 0, NULL, NULL);
}
/* Called from I/O thread context */
@@ -472,14 +519,15 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(o = i->userdata);
- /* Shut down the queue from the sink thread to us */
- pa_assert(o->inq_rtpoll_item_read && o->outq_rtpoll_item_write);
-
- pa_rtpoll_item_free(o->inq_rtpoll_item_read);
- o->inq_rtpoll_item_read = NULL;
+ if (o->inq_rtpoll_item_read) {
+ pa_rtpoll_item_free(o->inq_rtpoll_item_read);
+ o->inq_rtpoll_item_read = NULL;
+ }
- pa_rtpoll_item_free(o->outq_rtpoll_item_write);
- o->outq_rtpoll_item_write = NULL;
+ 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 */
@@ -493,20 +541,6 @@ static void sink_input_kill_cb(pa_sink_input *i) {
output_free(o);
}
-/* Called from IO thread context */
-static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
- struct userdata *u;
-
- pa_sink_input_assert_ref(i);
- pa_assert_se(u = i->userdata);
-
- /* If we are added for the first time, ask for a rewinding so that
- * we are heard right-away. */
- if (PA_SINK_INPUT_IS_LINKED(state) &&
- i->thread_info.state == PA_SINK_INPUT_INIT)
- pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
-}
-
/* Called from thread context */
static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct output *o = PA_SINK_INPUT(obj)->userdata;
@@ -537,37 +571,6 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64
}
/* Called from main context */
-static void disable_output(struct output *o) {
- pa_assert(o);
-
- if (!o->sink_input)
- return;
-
- pa_sink_input_unlink(o->sink_input);
- pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
- pa_sink_input_unref(o->sink_input);
- o->sink_input = NULL;
-}
-
-/* Called from main context */
-static void enable_output(struct output *o) {
- pa_assert(o);
-
- if (o->sink_input)
- return;
-
- if (output_create_sink_input(o) >= 0) {
-
- pa_memblockq_flush_write(o->memblockq);
-
- pa_sink_input_put(o->sink_input);
-
- if (o->userdata->sink && PA_SINK_IS_LINKED(pa_sink_get_state(o->userdata->sink)))
- pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
- }
-}
-
-/* Called from main context */
static void suspend(struct userdata *u) {
struct output *o;
uint32_t idx;
@@ -575,8 +578,8 @@ static void suspend(struct userdata *u) {
pa_assert(u);
/* Let's suspend by unlinking all streams */
- for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
- disable_output(o);
+ PA_IDXSET_FOREACH(o, u->outputs, idx)
+ output_disable(o);
pa_log_info("Device suspended...");
}
@@ -589,13 +592,8 @@ static void unsuspend(struct userdata *u) {
pa_assert(u);
/* Let's resume */
- for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
-
- pa_sink_suspend(o->sink, FALSE, PA_SUSPEND_IDLE);
-
- if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
- enable_output(o);
- }
+ PA_IDXSET_FOREACH(o, u->outputs, idx)
+ output_enable(o);
pa_log_info("Resumed successfully...");
}
@@ -639,7 +637,13 @@ static void update_max_request(struct userdata *u) {
size_t max_request = 0;
struct output *o;
- for (o = u->thread_info.active_outputs; o; o = o->next) {
+ 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)
@@ -652,6 +656,67 @@ static void update_max_request(struct userdata *u) {
pa_sink_set_max_request_within_thread(u->sink, max_request);
}
+/* Called from IO context */
+static void update_fixed_latency(struct userdata *u) {
+ pa_usec_t fixed_latency = 0;
+ struct output *o;
+
+ pa_assert(u);
+ pa_sink_assert_io_context(u->sink);
+
+ /* Collects the requested_latency values of all streams and sets
+ * the largest one as fixed_latency locally */
+
+ PA_LLIST_FOREACH(o, u->thread_info.active_outputs) {
+ pa_usec_t rl = (size_t) pa_atomic_load(&o->requested_latency);
+
+ if (rl > fixed_latency)
+ fixed_latency = rl;
+ }
+
+ if (fixed_latency <= 0)
+ fixed_latency = u->block_usec;
+
+ pa_sink_set_fixed_latency_within_thread(u->sink, fixed_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 && !o->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->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+ o->userdata->rtpoll,
+ PA_RTPOLL_EARLY,
+ o->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->inq_rtpoll_item_write) {
+ pa_rtpoll_item_free(o->inq_rtpoll_item_write);
+ o->inq_rtpoll_item_write = 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;
@@ -684,42 +749,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return 0;
}
- case SINK_MESSAGE_ADD_OUTPUT: {
- struct output *op = data;
-
- PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, op);
-
- pa_assert(!op->outq_rtpoll_item_read && !op->inq_rtpoll_item_write);
-
- op->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
- u->rtpoll,
- PA_RTPOLL_EARLY-1, /* This item is very important */
- op->outq);
- op->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
- u->rtpoll,
- PA_RTPOLL_EARLY,
- op->inq);
-
+ case SINK_MESSAGE_ADD_OUTPUT:
+ output_add_within_thread(data);
update_max_request(u);
+ update_fixed_latency(u);
return 0;
- }
-
- case SINK_MESSAGE_REMOVE_OUTPUT: {
- struct output *op = data;
-
- PA_LLIST_REMOVE(struct output, u->thread_info.active_outputs, op);
-
- pa_assert(op->outq_rtpoll_item_read && op->inq_rtpoll_item_write);
-
- pa_rtpoll_item_free(op->outq_rtpoll_item_read);
- op->outq_rtpoll_item_read = NULL;
-
- pa_rtpoll_item_free(op->inq_rtpoll_item_write);
- op->inq_rtpoll_item_write = NULL;
+ case SINK_MESSAGE_REMOVE_OUTPUT:
+ output_remove_within_thread(data);
update_max_request(u);
+ update_fixed_latency(u);
return 0;
- }
case SINK_MESSAGE_NEED:
render_memblock(u, (struct output*) data, (size_t) offset);
@@ -741,10 +781,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
}
case SINK_MESSAGE_UPDATE_MAX_REQUEST:
-
update_max_request(u);
break;
- }
+
+ case SINK_MESSAGE_UPDATE_REQUESTED_LATENCY:
+ update_fixed_latency(u);
+ break;
+}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
@@ -767,7 +810,7 @@ static void update_description(struct userdata *u) {
t = pa_xstrdup("Simultaneous output to");
- for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+ PA_IDXSET_FOREACH(o, u->outputs, idx) {
char *e;
if (first) {
@@ -801,8 +844,9 @@ static int output_create_sink_input(struct output *o) {
pa_sink_input_new_data_set_channel_map(&data, &o->userdata->sink->channel_map);
data.module = o->userdata->module;
data.resample_method = o->userdata->resample_method;
+ 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, o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
+ pa_sink_input_new(&o->sink_input, o->userdata->core, &data);
pa_sink_input_new_data_done(&data);
@@ -812,9 +856,9 @@ static int output_create_sink_input(struct output *o) {
o->sink_input->parent.process_msg = sink_input_process_msg;
o->sink_input->pop = sink_input_pop_cb;
o->sink_input->process_rewind = sink_input_process_rewind_cb;
- o->sink_input->state_change = sink_input_state_change_cb;
o->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
o->sink_input->update_max_request = sink_input_update_max_request_cb;
+ o->sink_input->update_sink_requested_latency = sink_input_update_sink_requested_latency_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;
@@ -825,22 +869,19 @@ static int output_create_sink_input(struct output *o) {
return 0;
}
+/* Called from main context */
static struct output *output_new(struct userdata *u, pa_sink *sink) {
struct output *o;
- pa_sink_state_t state;
pa_assert(u);
pa_assert(sink);
pa_assert(u->sink);
- o = pa_xnew(struct output, 1);
+ o = pa_xnew0(struct output, 1);
o->userdata = u;
o->inq = pa_asyncmsgq_new(0);
o->outq = pa_asyncmsgq_new(0);
- o->inq_rtpoll_item_write = o->inq_rtpoll_item_read = NULL;
- o->outq_rtpoll_item_write = o->outq_rtpoll_item_read = NULL;
o->sink = sink;
- o->sink_input = NULL;
o->memblockq = pa_memblockq_new(
0,
MEMBLOCKQ_MAXLENGTH,
@@ -850,84 +891,135 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
0,
0,
NULL);
- pa_atomic_store(&o->max_request, 0);
- PA_LLIST_INIT(struct output, o);
pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
+ update_description(u);
- state = pa_sink_get_state(u->sink);
-
- if (state != PA_SINK_INIT)
- pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
- else {
- /* If the sink is not yet started, we need to do the activation ourselves */
- PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, o);
-
- o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
- u->rtpoll,
- PA_RTPOLL_EARLY-1, /* This item is very important */
- o->outq);
- o->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
- u->rtpoll,
- PA_RTPOLL_EARLY,
- o->inq);
- }
+ return o;
+}
- if (PA_SINK_IS_OPENED(state) || state == PA_SINK_INIT) {
- pa_sink_suspend(sink, FALSE, PA_SUSPEND_IDLE);
+/* Called from main context */
+static void output_free(struct output *o) {
+ pa_assert(o);
- if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
- if (output_create_sink_input(o) < 0)
- goto fail;
- }
+ output_disable(o);
- update_description(u);
+ pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL));
+ update_description(o->userdata);
- return o;
+ if (o->inq_rtpoll_item_read)
+ pa_rtpoll_item_free(o->inq_rtpoll_item_read);
+ if (o->inq_rtpoll_item_write)
+ pa_rtpoll_item_free(o->inq_rtpoll_item_write);
-fail:
+ 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) {
- pa_idxset_remove_by_data(u->outputs, o, NULL);
+ if (o->inq)
+ pa_asyncmsgq_unref(o->inq);
- if (o->sink_input) {
- pa_sink_input_unlink(o->sink_input);
- pa_sink_input_unref(o->sink_input);
- }
+ 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 (o->memblockq)
- pa_memblockq_free(o->memblockq);
+ if (pa_sink_get_state(o->sink) != PA_SINK_INIT) {
- if (o->inq)
- pa_asyncmsgq_unref(o->inq);
+ /* First 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);
- if (o->outq)
- pa_asyncmsgq_unref(o->outq);
+ /* Then we enable the sink input. That means that the sink
+ * is now asked for new data. */
+ pa_sink_input_put(o->sink_input);
- pa_xfree(o);
+ } else
+ /* Hmm the sink is not yet started, do things right here */
+ output_add_within_thread(o);
}
- return NULL;
+ o->ignore_state_change = FALSE;
}
+/* Called from main context */
+static void output_disable(struct output *o) {
+ pa_assert(o);
+
+ if (!o->sink_input)
+ return;
+
+ /* First we disable the sink input. That means that the sink is
+ * not asked for new data anymore */
+ pa_sink_input_unlink(o->sink_input);
+
+ /* Then 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);
+
+ /* Now dellocate the stream */
+ pa_sink_input_unref(o->sink_input);
+ o->sink_input = NULL;
+
+ /* Finally, drop all queued data */
+ pa_memblockq_flush_write(o->memblockq);
+ pa_asyncmsgq_flush(o->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 pa_bool_t 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 == u->sink)
+ if (!(s->flags & PA_SINK_LATENCY))
return FALSE;
if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS)))
- if (strcmp(t, "sound"))
+ 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;
@@ -940,18 +1032,17 @@ static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata
return PA_HOOK_OK;
pa_log_info("Configuring new sink: %s", s->name);
-
if (!(o = output_new(u, s))) {
pa_log("Failed to create sink input on sink '%s'.", s->name);
return PA_HOOK_OK;
}
- if (o->sink_input)
- pa_sink_input_put(o->sink_input);
+ 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;
@@ -962,13 +1053,14 @@ static struct output* find_output(struct userdata *u, pa_sink *s) {
if (u->sink == s)
return NULL;
- for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+ 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;
@@ -980,26 +1072,25 @@ static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userd
return PA_HOOK_OK;
pa_log_info("Unconfiguring sink: %s", s->name);
-
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;
- pa_sink_state_t state;
if (!(o = find_output(u, s)))
return PA_HOOK_OK;
- state = pa_sink_get_state(s);
-
- if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input)
- enable_output(o);
+ /* 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;
- if (state == PA_SINK_SUSPENDED && o->sink_input)
- disable_output(o);
+ output_verify(o);
return PA_HOOK_OK;
}
@@ -1014,6 +1105,7 @@ int pa__init(pa_module*m) {
struct output *o;
uint32_t idx;
pa_sink_new_data data;
+ uint32_t adjust_time_sec;
pa_assert(m);
@@ -1029,23 +1121,13 @@ int pa__init(pa_module*m) {
}
}
- m->userdata = u = pa_xnew(struct userdata, 1);
+ m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
- u->sink = NULL;
- u->time_event = NULL;
- u->adjust_time = DEFAULT_ADJUST_TIME;
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
- u->thread = NULL;
u->resample_method = resample_method;
u->outputs = pa_idxset_new(NULL, NULL);
- memset(&u->adjust_timestamp, 0, sizeof(u->adjust_timestamp));
- u->sink_put_slot = u->sink_unlink_slot = u->sink_state_changed_slot = NULL;
- PA_LLIST_HEAD_INIT(struct output, u->thread_info.active_outputs);
- pa_atomic_store(&u->thread_info.running, FALSE);
- u->thread_info.in_null_mode = FALSE;
- u->thread_info.counter = 0;
u->thread_info.smoother = pa_smoother_new(
PA_USEC_PER_SEC,
PA_USEC_PER_SEC*2,
@@ -1055,16 +1137,73 @@ int pa__init(pa_module*m) {
0,
FALSE);
- if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 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;
+
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;
+ pa_bool_t 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;
@@ -1095,7 +1234,6 @@ int pa__init(pa_module*m) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output");
}
-
u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
@@ -1149,7 +1287,7 @@ int pa__init(pa_module*m) {
/* We're in automatic mode, we add every sink that matches our needs */
- for (s = pa_idxset_first(m->core->sinks, &idx); s; s = pa_idxset_next(m->core->sinks, &idx)) {
+ PA_IDXSET_FOREACH(s, m->core->sinks, idx) {
if (!is_suitable_sink(u, s))
continue;
@@ -1174,12 +1312,11 @@ int pa__init(pa_module*m) {
/* Activate the sink and the sink inputs */
pa_sink_put(u->sink);
- for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
- if (o->sink_input)
- pa_sink_input_put(o->sink_input);
+ 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 * PA_USEC_PER_SEC, time_callback, u);
+ u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u);
pa_modargs_free(ma);
@@ -1195,37 +1332,6 @@ fail:
return -1;
}
-static void output_free(struct output *o) {
- pa_assert(o);
-
- disable_output(o);
-
- pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL));
-
- update_description(o->userdata);
-
- if (o->inq_rtpoll_item_read)
- pa_rtpoll_item_free(o->inq_rtpoll_item_read);
- if (o->inq_rtpoll_item_write)
- pa_rtpoll_item_free(o->inq_rtpoll_item_write);
-
- if (o->outq_rtpoll_item_read)
- pa_rtpoll_item_free(o->outq_rtpoll_item_read);
- if (o->outq_rtpoll_item_write)
- pa_rtpoll_item_free(o->outq_rtpoll_item_write);
-
- if (o->inq)
- pa_asyncmsgq_unref(o->inq);
-
- if (o->outq)
- pa_asyncmsgq_unref(o->outq);
-
- if (o->memblockq)
- pa_memblockq_free(o->memblockq);
-
- pa_xfree(o);
-}
-
void pa__done(pa_module*m) {
struct userdata *u;
struct output *o;
diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
index a666073c..103f5c48 100644
--- a/src/modules/module-console-kit.c
+++ b/src/modules/module-console-kit.c
@@ -187,7 +187,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
}
add_session(u, path);
- return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionRemoved")) {
@@ -202,7 +201,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
}
remove_session(u, path);
- return DBUS_HANDLER_RESULT_HANDLED;
}
finish:
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index 18479df3..b1f24e15 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -50,7 +50,7 @@ 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>");
-PA_MODULE_DEPRECATED("Please use module-hal-detect instead of module-detect!");
+PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-detect!");
static const char* const valid_modargs[] = {
"just-one",
@@ -119,7 +119,7 @@ static int detect_alsa(pa_core *c, int just_one) {
}
#endif
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_OUTPUT
static int detect_oss(pa_core *c, int just_one) {
FILE *f;
int n = 0, b = 0;
@@ -240,7 +240,7 @@ int pa__init(pa_module*m) {
#ifdef HAVE_ALSA
if ((n = detect_alsa(m->core, just_one)) <= 0)
#endif
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_OUTPUT
if ((n = detect_oss(m->core, just_one)) <= 0)
#endif
#ifdef HAVE_SOLARIS
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 120b762c..da6c9666 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -218,7 +218,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (sink->save_volume) {
entry.channel_map = sink->channel_map;
- entry.volume = *pa_sink_get_volume(sink, FALSE, TRUE);
+ entry.volume = *pa_sink_get_volume(sink, FALSE);
entry.volume_valid = TRUE;
}
diff --git a/src/modules/module-hal-detect-compat.c b/src/modules/module-hal-detect-compat.c
new file mode 100644
index 00000000..14cf8143
--- /dev/null
+++ b/src/modules/module-hal-detect-compat.c
@@ -0,0 +1,84 @@
+/***
+ 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, write to 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/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;
+ pa_bool_t 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-hal-detect.c b/src/modules/module-hal-detect.c
index 658b3e55..18519131 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -55,14 +55,16 @@ PA_MODULE_AUTHOR("Shahms King");
PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
-#if defined(HAVE_ALSA) && defined(HAVE_OSS)
+#if defined(HAVE_ALSA) && defined(HAVE_OSS_OUTPUT)
PA_MODULE_USAGE("api=<alsa or oss> "
- "tsched=<enable system timer based scheduling mode?>");
+ "tsched=<enable system timer based scheduling mode?>"
+ "subdevices=<init all subdevices>");
#elif defined(HAVE_ALSA)
PA_MODULE_USAGE("api=<alsa> "
"tsched=<enable system timer based scheduling mode?>");
-#elif defined(HAVE_OSS)
-PA_MODULE_USAGE("api=<oss>");
+#elif defined(HAVE_OSS_OUTPUT)
+PA_MODULE_USAGE("api=<oss>"
+ "subdevices=<init all subdevices>");
#endif
PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!");
@@ -82,6 +84,9 @@ struct userdata {
#ifdef HAVE_ALSA
pa_bool_t use_tsched;
#endif
+#ifdef HAVE_OSS_OUTPUT
+ pa_bool_t init_subdevs;
+#endif
};
#define CAPABILITY_ALSA "alsa"
@@ -92,6 +97,9 @@ static const char* const valid_modargs[] = {
#ifdef HAVE_ALSA
"tsched",
#endif
+#ifdef HAVE_OSS_OUTPUT
+ "subdevices",
+#endif
NULL
};
@@ -262,9 +270,9 @@ fail:
#endif
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_OUTPUT
-static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi) {
+static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi, pa_bool_t init_subdevices) {
char *class = NULL, *dev = NULL, *e;
int device;
pa_bool_t r = FALSE;
@@ -294,7 +302,7 @@ static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi)
/* We only care for the main device */
device = libhal_device_get_property_int(context, udi, "oss.device", &error);
- if (dbus_error_is_set(&error) || device != 0)
+ if (dbus_error_is_set(&error) || (device != 0 && init_subdevices == FALSE))
goto finish;
r = TRUE;
@@ -324,7 +332,7 @@ static int hal_device_load_oss(struct userdata *u, const char *udi, struct devic
pa_assert(d);
/* We only care for OSS PCM devices */
- if (!hal_oss_device_is_pcm(u->context, udi))
+ if (!hal_oss_device_is_pcm(u->context, udi, u->init_subdevs))
goto fail;
/* We store only one entry per card, hence we look for the originating device */
@@ -394,7 +402,7 @@ static struct device* hal_device_add(struct userdata *u, const char *udi) {
if (pa_streq(u->capability, CAPABILITY_ALSA))
r = hal_device_load_alsa(u, udi, d);
#endif
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_OUTPUT
if (pa_streq(u->capability, CAPABILITY_OSS))
r = hal_device_load_oss(u, udi, d);
#endif
@@ -427,9 +435,7 @@ static int hal_device_add_all(struct userdata *u) {
int i;
for (i = 0; i < n; i++) {
- struct device *d;
-
- if ((d = hal_device_add(u, udis[i]))) {
+ if (hal_device_add(u, udis[i])) {
count++;
pa_log_debug("Loaded device %s", udis[i]);
} else
@@ -615,8 +621,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
}
- return DBUS_HANDLER_RESULT_HANDLED;
-
} else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
/* We use this message to avoid a dirty race condition when we
get an ACLAdded message before the previously owning PA
@@ -660,7 +664,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
/* Yes, we don't check the UDI for validity, but hopefully HAL will */
device_added_cb(u->context, udi);
- return DBUS_HANDLER_RESULT_HANDLED;
}
finish:
@@ -753,7 +756,7 @@ int pa__init(pa_module*m) {
api = pa_modargs_get_value(ma, "api", "oss");
#endif
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_OUTPUT
if (pa_streq(api, "oss"))
u->capability = CAPABILITY_OSS;
#endif
@@ -763,6 +766,13 @@ int pa__init(pa_module*m) {
goto fail;
}
+#ifdef HAVE_OSS_OUTPUT
+ if (pa_modargs_get_value_boolean(ma, "subdevices", &u->init_subdevs) < 0) {
+ pa_log("Failed to parse subdevices= argument.");
+ goto fail;
+ }
+#endif
+
if (!(u->connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
goto fail;
diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c
index c697209a..b9924dfd 100644
--- a/src/modules/module-intended-roles.c
+++ b/src/modules/module-intended-roles.c
@@ -127,6 +127,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
if (s == def)
continue;
+ if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)))
+ continue;
+
if (role_match(s->proplist, role)) {
new_data->sink = s;
new_data->save_sink = FALSE;
@@ -173,6 +176,9 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
if (s == def)
continue;
+ if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+ continue;
+
if (role_match(s->proplist, role)) {
new_data->source = s;
new_data->save_source = FALSE;
@@ -201,6 +207,17 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct
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;
@@ -237,6 +254,17 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source,
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;
@@ -275,24 +303,28 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
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 (def != sink && role_match(def->proplist, role)) {
- pa_sink_input_move_to(si, def, FALSE);
- continue;
- }
+ if (def != sink && role_match(def->proplist, role))
+ if (pa_sink_input_move_to(si, def, FALSE) >= 0)
+ continue;
/* Try to find some other fitting sink */
PA_IDXSET_FOREACH(d, c->sinks, jdx) {
if (d == def || d == sink)
continue;
- if (role_match(d->proplist, role)) {
- pa_sink_input_move_to(si, d, FALSE);
- break;
- }
+ 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;
}
}
@@ -325,6 +357,9 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
if (so->direct_on_input)
continue;
+ if (!so->source)
+ continue;
+
if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
continue;
@@ -339,6 +374,9 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
if (d == def || d == source)
continue;
+ if (!PA_SOURCE_IS_LINKED(pa_source_get_state(d)))
+ continue;
+
if (role_match(d->proplist, role) && !source->monitor_of == !d->monitor_of) {
pa_source_output_move_to(so, d, FALSE);
break;
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index 21f4a8f1..994c778f 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -64,10 +64,9 @@ PA_MODULE_USAGE(
#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
struct userdata {
- pa_core *core;
pa_module *module;
- pa_sink *sink, *master;
+ pa_sink *sink;
pa_sink_input *sink_input;
const LADSPA_Descriptor *descriptor;
@@ -83,6 +82,8 @@ struct userdata {
LADSPA_Data control_out;
pa_memblockq *memblockq;
+
+ pa_bool_t auto_desc;
};
static const char* const valid_modargs[] = {
@@ -100,69 +101,111 @@ static const char* const valid_modargs[] = {
};
/* Called from I/O thread context */
-static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+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: {
- pa_usec_t usec = 0;
+ 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)) {
+ *((pa_usec_t*) data) = 0;
+ return 0;
+ }
+
+ *((pa_usec_t*) data) =
- /* Get the latency of the master sink */
- if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
- usec = 0;
+ /* Get the latency of the master sink */
+ pa_sink_get_latency_within_thread(u->sink_input->sink) +
- /* Add the latency internal to our sink input on top */
- usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+ /* 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);
- *((pa_usec_t*) data) = usec;
return 0;
- }
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
/* Called from main context */
-static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+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) &&
- u->sink_input &&
- PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
-
- pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+ 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) {
+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(pa_sink *s) {
+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;
@@ -175,8 +218,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
pa_assert(chunk);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_OPENED(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;
@@ -225,9 +268,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
- return;
-
if (u->sink->thread_info.rewind_nbytes > 0) {
size_t max_rewrite;
@@ -263,9 +303,6 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
-
pa_memblockq_set_maxrewind(u->memblockq, nbytes);
pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
}
@@ -277,9 +314,6 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
-
pa_sink_set_max_request_within_thread(u->sink, nbytes);
}
@@ -290,24 +324,28 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
-
pa_sink_set_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_detach_cb(pa_sink_input *i) {
+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);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
+ 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);
pa_sink_detach_within_thread(u->sink);
- pa_sink_set_asyncmsgq(u->sink, NULL);
+
pa_sink_set_rtpoll(u->sink, NULL);
}
@@ -318,14 +356,13 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
+ pa_sink_set_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));
+ pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
- pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
- pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
pa_sink_attach_within_thread(u->sink);
-
- pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
}
/* Called from main context */
@@ -335,14 +372,18 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- pa_sink_unlink(u->sink);
+ /* The order here matters! We first kill the sink input, followed
+ * by the sink. That means the sink callbacks must be protected
+ * against an unconnected sink input! */
pa_sink_input_unlink(u->sink_input);
+ pa_sink_unlink(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
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);
}
@@ -372,13 +413,59 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
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_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;
char *t;
- const char *z;
pa_sink *master;
pa_sink_input_new_data sink_input_data;
pa_sink_new_data sink_data;
@@ -392,7 +479,7 @@ int pa__init(pa_module*m) {
pa_assert(m);
- pa_assert(sizeof(LADSPA_Data) == sizeof(float));
+ pa_assert_cc(sizeof(LADSPA_Data) == sizeof(float));
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
@@ -425,12 +512,8 @@ int pa__init(pa_module*m) {
cdata = pa_modargs_get_value(ma, "control", NULL);
u = pa_xnew0(struct userdata, 1);
- u->core = m->core;
u->module = m;
m->userdata = u;
- u->master = master;
- u->sink = NULL;
- u->sink_input = NULL;
u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
if (!(e = getenv("LADSPA_PATH")))
@@ -694,11 +777,8 @@ int pa__init(pa_module*m) {
sink_data.module = m;
if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name);
- sink_data.namereg_fail = FALSE;
pa_sink_new_data_set_sample_spec(&sink_data, &ss);
pa_sink_new_data_set_channel_map(&sink_data, &map);
- z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
- pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", label, z ? z : master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin);
@@ -714,7 +794,16 @@ int pa__init(pa_module*m) {
goto fail;
}
- u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
+ 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,
+ PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME|
+ (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)));
pa_sink_new_data_done(&sink_data);
if (!u->sink) {
@@ -722,26 +811,27 @@ int pa__init(pa_module*m) {
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->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;
+ u->sink->set_volume = sink_set_volume_cb;
+ u->sink->set_mute = sink_set_mute_cb;
u->sink->userdata = u;
pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
- pa_sink_set_rtpoll(u->sink, master->rtpoll);
/* Create sink input */
pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__;
sink_input_data.module = m;
- sink_input_data.sink = u->master;
+ sink_input_data.sink = master;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
- pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 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)
@@ -752,11 +842,15 @@ int pa__init(pa_module*m) {
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->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;
+ 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;
pa_sink_put(u->sink);
@@ -797,15 +891,20 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
return;
- if (u->sink) {
- pa_sink_unlink(u->sink);
- pa_sink_unref(u->sink);
- }
+ /* See comments in sink_input_kill_cb() above regarding
+ * destruction order! */
- if (u->sink_input) {
+ if (u->sink_input)
pa_sink_input_unlink(u->sink_input);
+
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->sink_input)
pa_sink_input_unref(u->sink_input);
- }
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
for (c = 0; c < u->channels; c++)
if (u->handle[c]) {
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
index 06efeb8f..d0e902f6 100644
--- a/src/modules/module-lirc.c
+++ b/src/modules/module-lirc.c
@@ -63,6 +63,8 @@ struct userdata {
float mute_toggle_save;
};
+#define DELTA (PA_VOLUME_NORM/20)
+
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;
@@ -119,32 +121,17 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
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 {
- int i;
- pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE);
-
-#define DELTA (PA_VOLUME_NORM/20)
+ pa_cvolume cv = *pa_sink_get_volume(s, FALSE);
switch (volchange) {
case UP:
- for (i = 0; i < cv.channels; i++) {
- if (cv.values[i] < PA_VOLUME_MAX - DELTA)
- cv.values[i] += DELTA;
- else
- cv.values[i] = PA_VOLUME_MAX;
- }
-
- pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
+ pa_cvolume_inc(&cv, DELTA);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case DOWN:
- for (i = 0; i < cv.channels; i++) {
- if (cv.values[i] > DELTA)
- cv.values[i] -= DELTA;
- else
- cv.values[i] = PA_VOLUME_MUTED;
- }
-
- pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
+ pa_cvolume_dec(&cv, DELTA);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case MUTE:
@@ -156,7 +143,6 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
break;
case MUTE_TOGGLE:
-
pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE), TRUE);
break;
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
new file mode 100644
index 00000000..29c3ddab
--- /dev/null
+++ b/src/modules/module-loopback.c
@@ -0,0 +1,784 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#include <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>");
+
+#define DEFAULT_LATENCY_MSEC 200
+
+#define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
+
+#define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+
+ 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;
+ pa_usec_t adjust_time;
+
+ int64_t recv_counter;
+ int64_t send_counter;
+
+ size_t skip;
+ pa_usec_t latency;
+
+ pa_bool_t in_pop;
+ size_t min_memblockq_length;
+
+ struct {
+ int64_t send_counter;
+ size_t source_output_buffer;
+ pa_usec_t source_latency;
+
+ int64_t recv_counter;
+ size_t sink_input_buffer;
+ pa_usec_t sink_latency;
+
+ size_t min_memblockq_length;
+ size_t max_request;
+ } latency_snapshot;
+};
+
+static const char* const valid_modargs[] = {
+ "source",
+ "sink",
+ "latency",
+ "format",
+ "rate",
+ "channels",
+ "channel_map",
+ NULL,
+};
+
+enum {
+ SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
+ SINK_INPUT_MESSAGE_REWIND,
+ SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT,
+ SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
+};
+
+enum {
+ SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
+};
+
+/* Called from main context */
+static void teardown(struct userdata *u) {
+ pa_assert(u);
+ pa_assert_ctl_context();
+
+ if (u->sink_input)
+ pa_sink_input_unlink(u->sink_input);
+
+ if (u->source_output)
+ pa_source_output_unlink(u->source_output);
+
+ if (u->sink_input) {
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+ }
+
+ if (u->source_output) {
+ pa_source_output_unref(u->source_output);
+ u->source_output = NULL;
+ }
+}
+
+/* Called from main context */
+static void adjust_rates(struct userdata *u) {
+ size_t buffer, fs;
+ uint32_t old_rate, base_rate, new_rate;
+ pa_usec_t buffer_latency;
+
+ pa_assert(u);
+ pa_assert_ctl_context();
+
+ pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
+ pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
+
+ buffer =
+ u->latency_snapshot.sink_input_buffer +
+ u->latency_snapshot.source_output_buffer;
+
+ 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));
+
+ buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec);
+
+ pa_log_info("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) buffer_latency / PA_USEC_PER_MSEC,
+ (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC,
+ ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC);
+
+ pa_log_info("Should buffer %zu bytes, buffered at minimum %zu bytes",
+ u->latency_snapshot.max_request*2,
+ u->latency_snapshot.min_memblockq_length);
+
+ fs = pa_frame_size(&u->sink_input->sample_spec);
+ old_rate = u->sink_input->sample_spec.rate;
+ base_rate = u->source_output->sample_spec.rate;
+
+ if (u->latency_snapshot.min_memblockq_length < u->latency_snapshot.max_request*2)
+ new_rate = base_rate - (((u->latency_snapshot.max_request*2 - u->latency_snapshot.min_memblockq_length) / fs) *PA_USEC_PER_SEC)/u->adjust_time;
+ else
+ new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time;
+
+ 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 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);
+
+ adjust_rates(u);
+}
+
+/* Called from input thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+ struct userdata *u;
+ pa_memchunk copy;
+
+ pa_source_output_assert_ref(o);
+ pa_source_output_assert_io_context(o);
+ pa_assert_se(u = o->userdata);
+
+ if (u->skip > chunk->length) {
+ u->skip -= chunk->length;
+ return;
+ }
+
+ if (u->skip > 0) {
+ copy = *chunk;
+ copy.index += u->skip;
+ copy.length -= u->skip;
+ u->skip = 0;
+
+ chunk = &copy;
+ }
+
+ pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, 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 output 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;
+ u->latency_snapshot.source_output_buffer = u->source_output->thread_info.resampler ? pa_resampler_result(u->source_output->thread_info.resampler, length) : length;
+ u->latency_snapshot.source_latency = pa_source_get_latency_within_thread(u->source_output->source);
+
+ return 0;
+ }
+ }
+
+ return pa_source_output_process_msg(obj, code, data, offset, chunk);
+}
+
+/* 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);
+
+ u->rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+ o->source->thread_info.rtpoll,
+ PA_RTPOLL_LATE,
+ u->asyncmsgq);
+}
+
+/* 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 (u->rtpoll_item_write) {
+ pa_rtpoll_item_free(u->rtpoll_item_write);
+ u->rtpoll_item_write = NULL;
+ }
+}
+
+/* Called from output thread context */
+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_source_output_assert_io_context(o);
+ pa_assert_se(u = o->userdata);
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT) {
+
+ u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source),
+ u->latency),
+ &o->sample_spec);
+
+ pa_log_info("Skipping %lu bytes", (unsigned long) u->skip);
+ }
+}
+
+/* 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 pa_bool_t 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);
+
+ 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) {
+ pa_proplist *p;
+ const char *n;
+ struct userdata *u;
+
+ pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
+ pa_assert_se(u = o->userdata);
+
+ p = pa_proplist_new();
+ pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+
+ if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
+ pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
+
+ pa_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p);
+ pa_proplist_free(p);
+}
+
+/* Called from output thread context */
+static void update_min_memblockq_length(struct userdata *u) {
+ size_t length;
+
+ pa_assert(u);
+ pa_sink_input_assert_io_context(u->sink_input);
+
+ length = pa_memblockq_get_length(u->memblockq);
+
+ if (u->min_memblockq_length == (size_t) -1 ||
+ length < u->min_memblockq_length)
+ u->min_memblockq_length = length;
+}
+
+/* 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);
+
+ u->in_pop = TRUE;
+ while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0)
+ ;
+ u->in_pop = FALSE;
+
+ if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
+ pa_log_info("Coud not peek into queue");
+ return -1;
+ }
+
+ chunk->length = PA_MIN(chunk->length, nbytes);
+ pa_memblockq_drop(u->memblockq, chunk->length);
+
+ update_min_memblockq_length(u);
+
+ 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;
+
+ switch (code) {
+
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = data;
+
+ pa_sink_input_assert_io_context(u->sink_input);
+
+ *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_sink_input_assert_io_context(u->sink_input);
+
+ if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
+ pa_memblockq_push_align(u->memblockq, chunk);
+ else
+ pa_memblockq_flush_write(u->memblockq);
+
+ update_min_memblockq_length(u);
+
+ /* Is this the end of an underrun? Then let's start things
+ * right-away */
+ if (!u->in_pop &&
+ u->sink_input->thread_info.underrun_for > 0 &&
+ pa_memblockq_is_readable(u->memblockq)) {
+
+ 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->recv_counter += (int64_t) chunk->length;
+
+ return 0;
+
+ case SINK_INPUT_MESSAGE_REWIND:
+
+ pa_sink_input_assert_io_context(u->sink_input);
+
+ if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
+ pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, TRUE);
+ else
+ pa_memblockq_flush_write(u->memblockq);
+
+ u->recv_counter -= offset;
+
+ update_min_memblockq_length(u);
+
+ return 0;
+
+ case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: {
+ size_t length;
+
+ update_min_memblockq_length(u);
+
+ length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq);
+
+ u->latency_snapshot.recv_counter = u->recv_counter;
+ u->latency_snapshot.sink_input_buffer =
+ pa_memblockq_get_length(u->memblockq) +
+ (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, length) : length);
+ u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink);
+
+ u->latency_snapshot.max_request = pa_sink_input_get_max_request(u->sink_input);
+
+ u->latency_snapshot.min_memblockq_length = u->min_memblockq_length;
+ u->min_memblockq_length = (size_t) -1;
+
+ return 0;
+ }
+
+ case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED: {
+ /* This message is sent from the IO thread to the main
+ * thread! So don't be confused. All the user cases above
+ * are executed in thread context, but this one is not! */
+
+ pa_assert_ctl_context();
+
+ adjust_rates(u);
+ return 0;
+ }
+ }
+
+ return pa_sink_input_process_msg(obj, code, data, offset, chunk);
+}
+
+/* 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));
+
+ u->min_memblockq_length = (size_t) -1;
+}
+
+/* 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");
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED, NULL, 0, NULL, NULL);
+}
+
+/* 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 main thread */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+ struct userdata *u;
+ pa_proplist *p;
+ const char *n;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
+ pa_assert_se(u = i->userdata);
+
+ p = pa_proplist_new();
+ pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+
+ if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
+ pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
+
+ pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p);
+ pa_proplist_free(p);
+}
+
+/* Called from main thread */
+static pa_bool_t 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->source->monitor_of)
+ return TRUE;
+
+ return dest != u->source_output->source->monitor_of;
+}
+
+int pa__init(pa_module *m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ pa_sink *sink;
+ pa_sink_input_new_data sink_input_data;
+ pa_source *source;
+ pa_source_output_new_data source_output_data;
+ uint32_t latency_msec;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ pa_memchunk silence;
+ uint32_t adjust_time_sec;
+ const char *n;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ if (!(source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
+ pa_log("No such source.");
+ 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 = sink->sample_spec;
+ map = sink->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;
+ }
+
+ latency_msec = DEFAULT_LATENCY_MSEC;
+ if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 2000) {
+ 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;
+
+ 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;
+ sink_input_data.sink = sink;
+
+ pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Loopback of %s",
+ pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ if ((n = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)))
+ pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
+ 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_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->kill = sink_input_kill_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->userdata = u;
+
+ pa_sink_input_set_requested_latency(u->sink_input, u->latency/3);
+
+ pa_source_output_new_data_init(&source_output_data);
+ source_output_data.driver = __FILE__;
+ source_output_data.module = m;
+ source_output_data.source = source;
+ pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Loopback to %s",
+ pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ if ((n = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)))
+ pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
+ 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_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+
+ 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->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;
+
+ pa_source_output_set_requested_latency(u->source_output, u->latency/3);
+
+ pa_sink_input_get_silence(u->sink_input, &silence);
+ u->memblockq = pa_memblockq_new(
+ 0, /* idx */
+ MEMBLOCKQ_MAXLENGTH, /* maxlength */
+ MEMBLOCKQ_MAXLENGTH, /* tlength */
+ pa_frame_size(&ss), /* base */
+ 0, /* prebuf */
+ 0, /* minreq */
+ 0, /* maxrewind */
+ &silence); /* silence frame */
+ pa_memblock_unref(silence.memblock);
+
+ u->asyncmsgq = pa_asyncmsgq_new(0);
+
+ pa_sink_input_put(u->sink_input);
+ pa_source_output_put(u->source_output);
+
+ 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;
+
+ teardown(u);
+
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
+
+ if (u->asyncmsgq)
+ pa_asyncmsgq_unref(u->asyncmsgq);
+
+ if (u->time_event)
+ u->core->mainloop->time_free(u->time_event);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 625f2a8b..0bd781d2 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -216,7 +216,7 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
pa_cvolume cv;
pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
- pa_sink_input_set_volume(si, &cv, TRUE, TRUE);
+ pa_sink_input_set_volume(si, &cv, TRUE, FALSE);
}
}
}
@@ -243,6 +243,9 @@ int pa__init(pa_module*m) {
if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)
goto fail;
+ /* FIXME: Doing this asynchronously is just broken. This needs to
+ * use a hook! */
+
u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);
pa_modargs_free(ma);
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
index b30fae51..516bf413 100644
--- a/src/modules/module-mmkbd-evdev.c
+++ b/src/modules/module-mmkbd-evdev.c
@@ -65,6 +65,8 @@ struct userdata {
pa_module *module;
};
+#define DELTA (PA_VOLUME_NORM/20)
+
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;
@@ -85,14 +87,27 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
}
if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) {
- enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID;
+ 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;
+ case KEY_VOLUMEDOWN:
+ volchange = DOWN;
+ break;
+
+ case KEY_VOLUMEUP:
+ volchange = UP;
+ break;
+
+ case KEY_MUTE:
+ volchange = MUTE_TOGGLE;
+ break;
}
if (volchange != INVALID) {
@@ -101,36 +116,20 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
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 {
- int i;
- pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE);
-
-#define DELTA (PA_VOLUME_NORM/20)
+ pa_cvolume cv = *pa_sink_get_volume(s, FALSE);
switch (volchange) {
case UP:
- for (i = 0; i < cv.channels; i++) {
- if (cv.values[i] < PA_VOLUME_MAX - DELTA)
- cv.values[i] += DELTA;
- else
- cv.values[i] = PA_VOLUME_MAX;
- }
-
- pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
+ pa_cvolume_inc(&cv, DELTA);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case DOWN:
- for (i = 0; i < cv.channels; i++) {
- if (cv.values[i] > DELTA)
- cv.values[i] -= DELTA;
- else
- cv.values[i] = PA_VOLUME_MUTED;
- }
-
- pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
+ pa_cvolume_dec(&cv, DELTA);
+ 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;
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index 36c50b05..74a2ebb1 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -35,6 +35,7 @@
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
+#include <pulse/i18n.h>
#include <pulsecore/macro.h>
#include <pulsecore/sink.h>
@@ -51,7 +52,7 @@
#include "module-null-sink-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("Clocked NULL sink");
+PA_MODULE_DESCRIPTION(_("Clocked NULL sink"));
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
@@ -287,7 +288,7 @@ int pa__init(pa_module*m) {
pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output"));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "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) {
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 8a7dc846..9c169327 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -122,7 +122,7 @@ static int process_render(struct userdata *u) {
pa_assert(u);
if (u->memchunk.length <= 0)
- pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
+ pa_sink_render(u->sink, pa_pipe_buf(u->fd), &u->memchunk);
pa_assert(u->memchunk.length > 0);
@@ -299,8 +299,8 @@ int pa__init(pa_module*m) {
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, PIPE_BUF);
- pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(PIPE_BUF, &u->sink->sample_spec));
+ pa_sink_set_max_request(u->sink, pa_pipe_buf(u->fd));
+ pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(pa_pipe_buf(u->fd), &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);
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index e5609fb5..49104f8d 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -142,7 +142,7 @@ static void thread_func(void *userdata) {
void *p;
if (!u->memchunk.memblock) {
- u->memchunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF);
+ u->memchunk.memblock = pa_memblock_new(u->core->mempool, pa_pipe_buf(u->fd));
u->memchunk.index = u->memchunk.length = 0;
}
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index e191ec33..fa8f73d8 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -57,35 +57,68 @@ struct userdata {
pa_hook_slot *sink_input_fixate_hook_slot;
};
+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_warn("Property hpos/vpos out of range %0.2f", *f);
+ return -1;
+ }
+
+ 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;
+ const char *hpos, *vpos, *role;
double f;
char t[PA_CVOLUME_SNPRINT_MAX];
pa_cvolume v;
pa_assert(data);
- if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS)))
+ if (!(role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE)))
return PA_HOOK_OK;
- if (pa_atod(hpos, &f) < 0) {
- pa_log_warn("Failed to parse "PA_PROP_EVENT_MOUSE_HPOS" property '%s'.", hpos);
+ if (!pa_streq(role, "event"))
return PA_HOOK_OK;
- }
- if (f < 0.0 || f > 1.0) {
- pa_log_warn("Property "PA_PROP_EVENT_MOUSE_HPOS" out of range %0.2f", f);
+ 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);
+ }
}
- pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
+ if (vpos) {
+ if (parse_pos(vpos, &f) < 0)
+ return PA_HOOK_OK;
- pa_cvolume_reset(&v, data->sample_spec.channels);
- pa_cvolume_set_balance(&v, &data->channel_map, f*2.0-1.0);
+ 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(t, sizeof(t), &v));
-
- pa_sink_input_new_data_apply_volume_factor(data, &v);
+ pa_sink_input_new_data_apply_volume_factor_sink(data, &v);
return PA_HOOK_OK;
}
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 119f5b9f..43748bd0 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -1,7 +1,7 @@
/***
This file is part of PulseAudio.
- Copyright 2004-2008 Lennart Poettering
+ 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
@@ -48,17 +48,18 @@ PA_MODULE_USAGE(
"master=<name of sink to remap> "
"master_channel_map=<channel map> "
"format=<sample format> "
- "channels=<number of channels> "
"rate=<sample rate> "
+ "channels=<number of channels> "
"channel_map=<channel map> "
"remix=<remix channels?>");
struct userdata {
- pa_core *core;
pa_module *module;
- pa_sink *sink, *master;
+ pa_sink *sink;
pa_sink_input *sink_input;
+
+ pa_bool_t auto_desc;
};
static const char* const valid_modargs[] = {
@@ -80,19 +81,24 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch (code) {
- case PA_SINK_MESSAGE_GET_LATENCY: {
- pa_usec_t usec = 0;
+ 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)) {
+ *((pa_usec_t*) data) = 0;
+ return 0;
+ }
- /* Get the latency of the master sink */
- if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
- usec = 0;
+ *((pa_usec_t*) data) =
+ /* Get the latency of the master sink */
+ pa_sink_get_latency_within_thread(u->sink_input->sink) +
- /* Add the latency internal to our sink input on top */
- usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+ /* 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);
- *((pa_usec_t*) data) = usec;
return 0;
- }
}
return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -105,12 +111,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
- if (PA_SINK_IS_LINKED(state) &&
- u->sink_input &&
- PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
-
- pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+ 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;
}
@@ -121,6 +126,10 @@ static void sink_request_rewind(pa_sink *s) {
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);
}
@@ -131,6 +140,10 @@ static void sink_update_requested_latency(pa_sink *s) {
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,
@@ -145,8 +158,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
pa_assert(chunk);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_OPENED(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;
@@ -160,9 +173,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_OPENED(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;
@@ -178,9 +188,6 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
-
pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
}
@@ -191,9 +198,6 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
-
pa_sink_set_max_request_within_thread(u->sink, nbytes);
}
@@ -204,24 +208,28 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
-
pa_sink_set_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_detach_cb(pa_sink_input *i) {
+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);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
+ 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);
pa_sink_detach_within_thread(u->sink);
- pa_sink_set_asyncmsgq(u->sink, NULL);
+
pa_sink_set_rtpoll(u->sink, NULL);
}
@@ -232,14 +240,13 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
+ pa_sink_set_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));
+ pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
- pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
- pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
pa_sink_attach_within_thread(u->sink);
-
- pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
}
/* Called from main context */
@@ -249,14 +256,18 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- pa_sink_unlink(u->sink);
+ /* The order here matters! We first kill the sink input, followed
+ * by the sink. That means the sink callbacks must be protected
+ * against an unconnected sink input! */
pa_sink_input_unlink(u->sink_input);
+ pa_sink_unlink(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
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);
}
@@ -286,12 +297,37 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
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 *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_channel_map sink_map, stream_map;
pa_modargs *ma;
- const char *k;
pa_sink *master;
pa_sink_input_new_data sink_input_data;
pa_sink_new_data sink_data;
@@ -336,12 +372,8 @@ int pa__init(pa_module*m) {
}
u = pa_xnew0(struct userdata, 1);
- u->core = m->core;
u->module = m;
m->userdata = u;
- u->master = master;
- u->sink = NULL;
- u->sink_input = NULL;
/* Create sink */
pa_sink_new_data_init(&sink_data);
@@ -351,8 +383,6 @@ int pa__init(pa_module*m) {
sink_data.name = pa_sprintf_malloc("%s.remapped", master->name);
pa_sink_new_data_set_sample_spec(&sink_data, &ss);
pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
- k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
- pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
@@ -362,7 +392,14 @@ int pa__init(pa_module*m) {
goto fail;
}
- u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
+ 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) {
@@ -377,19 +414,19 @@ int pa__init(pa_module*m) {
u->sink->userdata = u;
pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
- pa_sink_set_rtpoll(u->sink, master->rtpoll);
/* Create sink input */
pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__;
sink_input_data.module = m;
- sink_input_data.sink = u->master;
+ sink_input_data.sink = master;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
+ sink_input_data.flags = (remix ? 0 : PA_SINK_INPUT_NO_REMIX);
- pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
+ 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)
@@ -400,11 +437,13 @@ int pa__init(pa_module*m) {
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->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;
u->sink_input->userdata = u;
pa_sink_put(u->sink);
@@ -440,15 +479,20 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
return;
- if (u->sink) {
- pa_sink_unlink(u->sink);
- pa_sink_unref(u->sink);
- }
+ /* See comments in sink_input_kill_cb() above regarding
+ * destruction order! */
- if (u->sink_input) {
+ if (u->sink_input)
pa_sink_input_unlink(u->sink_input);
+
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (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-rescue-streams.c b/src/modules/module-rescue-streams.c
index c23feceb..722d84b2 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -45,13 +45,46 @@ static const char* const valid_modargs[] = {
};
struct userdata {
- pa_hook_slot *sink_slot, *source_slot;
+ pa_hook_slot
+ *sink_unlink_slot,
+ *source_unlink_slot,
+ *sink_input_move_fail_slot,
+ *source_output_move_fail_slot;
};
-static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip) {
+ pa_sink *target, *def;
+ uint32_t idx;
+
+ pa_assert(c);
+ pa_assert(i);
+
+ def = pa_namereg_get_default_sink(c);
+
+ if (def && def != skip && pa_sink_input_may_move_to(i, def))
+ return def;
+
+ PA_IDXSET_FOREACH(target, c->sinks, idx) {
+ if (target == def)
+ 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))
+ return target;
+ }
+
+ pa_log_debug("No evacuation sink found.");
+ return NULL;
+}
+
+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_sink *target;
pa_assert(c);
pa_assert(sink);
@@ -65,21 +98,12 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
return PA_HOOK_OK;
}
- if (!(target = pa_namereg_get_default_sink(c)) || target == sink) {
-
- PA_IDXSET_FOREACH(target, c->sinks, idx)
- if (target != sink)
- break;
-
- if (!target) {
- pa_log_debug("No evacuation sink found.");
- return PA_HOOK_OK;
- }
- }
+ PA_IDXSET_FOREACH(i, sink->inputs, idx) {
+ pa_sink *target;
- pa_assert(target != sink);
+ if (!(target = find_evacuation_sink(c, i, sink)))
+ continue;
- PA_IDXSET_FOREACH(i, sink->inputs, idx) {
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);
@@ -91,9 +115,66 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
return PA_HOOK_OK;
}
-static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void* userdata) {
+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("Sucessfully 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, *def;
+ uint32_t idx;
+
+ pa_assert(c);
+ pa_assert(o);
+
+ def = pa_namereg_get_default_source(c);
+
+ if (def && def != skip && pa_source_output_may_move_to(o, def))
+ return def;
+
+ PA_IDXSET_FOREACH(target, c->sources, idx) {
+ if (target == def)
+ continue;
+
+ if (target == skip)
+ continue;
+
+ if (!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))
+ return target;
+ }
+
+ pa_log_debug("No evacuation source found.");
+ return NULL;
+}
+
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, void* userdata) {
pa_source_output *o;
- pa_source *target;
uint32_t idx;
pa_assert(c);
@@ -108,21 +189,12 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
return PA_HOOK_OK;
}
- if (!(target = pa_namereg_get_default_source(c)) || target == source) {
-
- PA_IDXSET_FOREACH(target, c->sources, idx)
- if (target != source && !target->monitor_of == !source->monitor_of)
- break;
-
- if (!target) {
- pa_log_info("No evacuation source found.");
- return PA_HOOK_OK;
- }
- }
+ PA_IDXSET_FOREACH(o, source->outputs, idx) {
+ pa_source *target;
- pa_assert(target != source);
+ if (!(target = find_evacuation_source(c, o, source)))
+ continue;
- PA_IDXSET_FOREACH(o, source->outputs, idx) {
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);
@@ -134,6 +206,31 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
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 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("Sucessfully moved source input %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;
@@ -148,8 +245,11 @@ int pa__init(pa_module*m) {
m->userdata = u = pa_xnew(struct userdata, 1);
/* A little bit later than module-stream-restore, module-intended-roles... */
- u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_hook_callback, u);
- u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_hook_callback, u);
+ 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;
@@ -163,10 +263,15 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
return;
- if (u->sink_slot)
- pa_hook_slot_free(u->sink_slot);
- if (u->source_slot)
- pa_hook_slot_free(u->source_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->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-sine.c b/src/modules/module-sine.c
index 0be1d722..69b20028 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -163,7 +163,7 @@ int pa__init(pa_module*m) {
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, 0);
+ pa_sink_input_new(&u->sink_input, m->core, &data);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index 0920d25e..b0d4db43 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -60,6 +60,7 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/thread.h>
+#include <pulsecore/time-smoother.h>
#include "module-solaris-symdef.h"
@@ -110,6 +111,8 @@ struct userdata {
uint32_t prev_playback_samples, prev_record_samples;
int32_t minimum_request;
+
+ pa_smoother *smoother;
};
static const char* const valid_modargs[] = {
@@ -133,6 +136,9 @@ static const char* const valid_modargs[] = {
#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;
@@ -145,7 +151,12 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {
/* 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! */
+ /*
+ * 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 {
@@ -155,6 +166,8 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {
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));
+
return u->written_bytes - played_bytes;
}
@@ -387,6 +400,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
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;
@@ -398,6 +413,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
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) {
if (unsuspend(u) < 0)
return -1;
@@ -479,7 +496,7 @@ static void sink_set_volume(pa_sink *s) {
if (u->fd >= 0) {
AUDIO_INITINFO(&info);
- info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+ info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.play.gain <= AUDIO_MAX_GAIN);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
@@ -501,8 +518,7 @@ static void sink_get_volume(pa_sink *s) {
if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
else
- pa_cvolume_set(&s->virtual_volume, s->sample_spec.channels,
- info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+ pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
}
}
@@ -515,7 +531,7 @@ static void source_set_volume(pa_source *s) {
if (u->fd >= 0) {
AUDIO_INITINFO(&info);
- info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+ info.play.gain = pa_cvolume_max(&s->volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.play.gain <= AUDIO_MAX_GAIN);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
@@ -537,8 +553,7 @@ static void source_get_volume(pa_source *s) {
if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
else
- pa_cvolume_set(&s->virtual_volume, s->sample_spec.channels,
- info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+ pa_cvolume_set(&s->volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
}
}
@@ -585,6 +600,10 @@ static void process_rewind(struct userdata *u) {
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);
}
@@ -606,11 +625,13 @@ static void thread_func(void *userdata) {
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 (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
- pa_usec_t xtime0;
+ pa_usec_t xtime0, ysleep_interval, xsleep_interval;
uint64_t buffered_bytes;
if (u->sink->thread_info.rewind_requested)
@@ -629,12 +650,15 @@ static void thread_func(void *userdata) {
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
@@ -651,39 +675,32 @@ static void thread_func(void *userdata) {
if (len < (size_t) u->minimum_request)
break;
- if (u->memchunk.length < len)
+ 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, u->memchunk.length, NULL);
+ w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, len, &write_type);
pa_memblock_release(u->memchunk.memblock);
if (w <= 0) {
- switch (errno) {
- case EINTR:
- continue;
- case EAGAIN:
- /* If the buffer_size is too big, we get EAGAIN. Avoiding that limit by trial and error
- * is not ideal, but I don't know how to get the system to tell me what the limit is.
- */
- u->buffer_size = u->buffer_size * 18 / 25;
- u->buffer_size -= u->buffer_size % u->frame_size;
- u->buffer_size = PA_MAX(u->buffer_size, 2 * u->minimum_request);
- pa_sink_set_max_request_within_thread(u->sink, u->buffer_size);
- pa_sink_set_max_rewind_within_thread(u->sink, u->buffer_size);
- pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
- break;
- default:
- pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
- goto fail;
+ 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.length -= 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);
@@ -691,7 +708,9 @@ static void thread_func(void *userdata) {
}
}
- pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
+ 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);
@@ -797,7 +816,7 @@ static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void
pa_log_debug("caught signal");
if (u->sink) {
- pa_sink_get_volume(u->sink, TRUE, FALSE);
+ pa_sink_get_volume(u->sink, TRUE);
pa_sink_get_mute(u->sink, TRUE);
}
@@ -812,7 +831,7 @@ int pa__init(pa_module *m) {
pa_channel_map map;
pa_modargs *ma = NULL;
uint32_t buffer_length_msec;
- int fd;
+ int fd = -1;
pa_sink_new_data sink_new_data;
pa_source_new_data source_new_data;
char const *name;
@@ -838,6 +857,9 @@ int pa__init(pa_module *m) {
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.
@@ -861,7 +883,13 @@ int pa__init(pa_module *m) {
}
u->buffer_size = pa_usec_to_bytes(1000 * buffer_length_msec, &ss);
if (u->buffer_size < 2 * u->minimum_request) {
- pa_log("supplied buffer size argument is too small");
+ 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;
}
@@ -924,6 +952,7 @@ int pa__init(pa_module *m) {
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));
u->source->get_volume = source_get_volume;
u->source->set_volume = source_set_volume;
@@ -966,15 +995,15 @@ int pa__init(pa_module *m) {
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);
u->sink->get_volume = sink_get_volume;
u->sink->set_volume = sink_set_volume;
u->sink->get_mute = sink_get_mute;
u->sink->set_mute = sink_set_mute;
u->sink->refresh_volume = u->sink->refresh_muted = TRUE;
-
- pa_sink_set_max_request(u->sink, u->buffer_size);
- pa_sink_set_max_rewind(u->sink, u->buffer_size);
} else
u->sink = NULL;
@@ -1075,6 +1104,9 @@ void pa__done(pa_module *m) {
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
index 8c0bb6b0..d6e3c153 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -102,15 +102,16 @@ struct userdata {
pa_idxset *subscribed;
};
-#define ENTRY_VERSION 2
+#define ENTRY_VERSION 3
struct entry {
uint8_t version;
- pa_bool_t muted_valid:1, volume_valid:1, device_valid:1;
+ pa_bool_t muted_valid:1, volume_valid:1, device_valid:1, card_valid:1;
pa_bool_t muted:1;
pa_channel_map channel_map;
pa_cvolume volume;
char device[PA_NAME_MAX];
+ char card[PA_NAME_MAX];
} PA_GCC_PACKED;
enum {
@@ -196,11 +197,21 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
goto fail;
}
+ if (!memchr(e->card, 0, sizeof(e->card))) {
+ pa_log_warn("Database contains entry for stream %s with missing NUL byte in card name", name);
+ 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;
@@ -252,6 +263,10 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
(a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
return FALSE;
+ if (a->card_valid != b->card_valid ||
+ (a->card_valid && strncmp(a->card, b->card, sizeof(a->card))))
+ return FALSE;
+
if (a->muted_valid != b->muted_valid ||
(a->muted_valid && (a->muted != b->muted)))
return FALSE;
@@ -308,6 +323,11 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (sink_input->save_sink) {
pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
entry.device_valid = TRUE;
+
+ if (sink_input->sink->card) {
+ pa_strlcpy(entry.card, sink_input->sink->card->name, sizeof(entry.card));
+ entry.card_valid = TRUE;
+ }
}
} else {
@@ -327,6 +347,11 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (source_output->save_source) {
pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
entry.device_valid = source_output->save_source;
+
+ if (source_output->source->card) {
+ pa_strlcpy(entry.card, source_output->source->card->name, sizeof(entry.card));
+ entry.card_valid = TRUE;
+ }
}
}
@@ -368,19 +393,28 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
if (!(name = get_name(new_data->proplist, "sink-input")))
return PA_HOOK_OK;
- if ((e = read_entry(u, name))) {
+ if (new_data->sink)
+ pa_log_debug("Not restoring device for stream %s, because already set.", name);
+ else if ((e = read_entry(u, name))) {
+ pa_sink *s = NULL;
- if (e->device_valid) {
- pa_sink *s;
+ if (e->device_valid)
+ s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK);
- if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
- if (!new_data->sink) {
- pa_log_info("Restoring device for stream %s.", name);
- new_data->sink = s;
- new_data->save_sink = TRUE;
- } else
- pa_log_debug("Not restoring device for stream %s, because already set.", name);
- }
+ 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))) {
+ pa_log_info("Restoring device for stream %s.", name);
+ new_data->sink = s;
+ new_data->save_sink = TRUE;
}
pa_xfree(e);
@@ -455,18 +489,28 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
if (!(name = get_name(new_data->proplist, "source-output")))
return PA_HOOK_OK;
- if ((e = read_entry(u, name))) {
- pa_source *s;
+ if (new_data->source)
+ pa_log_debug("Not restoring device for stream %s, because already set", name);
+ else if ((e = read_entry(u, name))) {
+ pa_source *s = NULL;
- if (e->device_valid) {
- if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) {
- if (!new_data->source) {
- pa_log_info("Restoring device for stream %s.", name);
- new_data->source = s;
- new_data->save_source = TRUE;
- } else
- pa_log_debug("Not restoring device for stream %s, because already set", name);
- }
+ 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);
+ new_data->source = s;
+ new_data->save_source = TRUE;
}
pa_xfree(e);
@@ -496,6 +540,17 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct
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 (!(name = get_name(si->proplist, "sink-input")))
continue;
@@ -534,6 +589,16 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source,
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 sink 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 = get_name(so->proplist, "source-input")))
continue;
@@ -567,6 +632,9 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
char *name;
struct entry *e;
+ if (!si->sink)
+ continue;
+
if (!(name = get_name(si->proplist, "sink-input")))
continue;
@@ -575,7 +643,9 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
if (e->device_valid) {
pa_sink *d;
- if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) && d != sink)
+ 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);
}
@@ -605,6 +675,12 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
char *name;
struct entry *e;
+ if (so->direct_on_input)
+ continue;
+
+ if (!so->source)
+ continue;
+
if (!(name = get_name(so->proplist, "source-output")))
continue;
@@ -613,7 +689,9 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
if (e->device_valid) {
pa_source *d;
- if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) && d != source)
+ 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);
}
@@ -856,6 +934,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
data.data = &entry;
data.size = sizeof(entry);
+ 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 (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
if (apply_immediately)
apply_entry(u, name, &entry);
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 70a7b049..7adaa0b1 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -145,6 +145,9 @@ static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_
pa_assert(data);
pa_assert(u);
+ if (data->flags & PA_SINK_INPUT_START_CORKED)
+ return PA_HOOK_OK;
+
if ((d = pa_hashmap_get(u->device_infos, data->sink)))
resume(d);
@@ -158,6 +161,9 @@ static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_outpu
pa_assert(data);
pa_assert(u);
+ if (data->flags & PA_SOURCE_OUTPUT_START_CORKED)
+ return PA_HOOK_OK;
+
if (data->source->monitor_of)
d = pa_hashmap_get(u->device_infos, data->source->monitor_of);
else
@@ -226,11 +232,16 @@ static pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input
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);
@@ -265,6 +276,9 @@ static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_
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
@@ -279,6 +293,7 @@ static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_
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);
@@ -292,15 +307,11 @@ static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_inp
}
static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
- pa_source_output_state_t state;
-
pa_assert(c);
pa_source_output_assert_ref(s);
pa_assert(u);
- state = pa_source_output_get_state(s);
-
- if (state == PA_SOURCE_OUTPUT_RUNNING) {
+ if (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_RUNNING) {
struct device_info *d;
if (s->source->monitor_of)
@@ -387,22 +398,17 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
pa_sink *s = PA_SINK(o);
pa_sink_state_t state = pa_sink_get_state(s);
- if (pa_sink_check_suspend(s) <= 0) {
-
+ if (pa_sink_check_suspend(s) <= 0)
if (PA_SINK_IS_OPENED(state))
restart(d);
- }
-
} else if (pa_source_isinstance(o)) {
pa_source *s = PA_SOURCE(o);
pa_source_state_t state = pa_source_get_state(s);
- if (pa_source_check_suspend(s) <= 0) {
-
+ if (pa_source_check_suspend(s) <= 0)
if (PA_SOURCE_IS_OPENED(state))
restart(d);
- }
}
return PA_HOOK_OK;
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index d1153829..af4b8b2a 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -55,6 +55,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/proplist-util.h>
#include <pulsecore/auth-cookie.h>
+#include <pulsecore/mcalign.h>
#ifdef TUNNEL_SINK
#include "module-tunnel-sink-symdef.h"
@@ -194,6 +195,7 @@ struct userdata {
#else
char *source_name;
pa_source *source;
+ pa_mcalign *mcalign;
#endif
pa_auth_cookie *auth_cookie;
@@ -330,7 +332,7 @@ static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
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, fragsize, prebuf, minreq;
+ uint32_t channel, maxlength, tlength = 0, fragsize, prebuf, minreq;
pa_usec_t usec;
pa_assert(pd);
@@ -614,14 +616,23 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return 0;
}
- case SOURCE_MESSAGE_POST:
+ case SOURCE_MESSAGE_POST: {
+ pa_memchunk c;
- if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
- pa_source_post(u->source, chunk);
+ pa_mcalign_push(u->mcalign, chunk);
- u->counter += (int64_t) chunk->length;
+ 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:
@@ -1086,7 +1097,7 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag
uint32_t idx, owner_module, client, sink;
pa_usec_t buffer_usec, sink_usec;
const char *name, *driver, *resample_method;
- pa_bool_t mute;
+ pa_bool_t mute = FALSE;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_cvolume volume;
@@ -1151,13 +1162,13 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_assert(u->sink);
if ((u->version < 11 || !!mute == !!u->sink->muted) &&
- pa_cvolume_equal(&volume, &u->sink->virtual_volume))
+ pa_cvolume_equal(&volume, &u->sink->real_volume))
return;
- pa_sink_volume_changed(u->sink, &volume, FALSE);
+ pa_sink_volume_changed(u->sink, &volume);
if (u->version >= 11)
- pa_sink_mute_changed(u->sink, mute, FALSE);
+ pa_sink_mute_changed(u->sink, mute);
return;
@@ -1334,12 +1345,11 @@ static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32
/* Called from main context */
static void start_subscribe(struct userdata *u) {
pa_tagstruct *t;
- uint32_t tag;
pa_assert(u);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
- pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, u->ctag++);
pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
#ifdef TUNNEL_SINK
PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
@@ -1515,7 +1525,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
reply = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
- pa_tagstruct_putu32(reply, tag = u->ctag++);
+ pa_tagstruct_putu32(reply, u->ctag++);
if (u->version >= 13) {
pa_proplist *pl;
@@ -1742,7 +1752,6 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
static void sink_set_volume(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
- uint32_t tag;
pa_assert(sink);
u = sink->userdata;
@@ -1750,9 +1759,9 @@ static void sink_set_volume(pa_sink *sink) {
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
- pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, u->ctag++);
pa_tagstruct_putu32(t, u->device_index);
- pa_tagstruct_put_cvolume(t, &sink->virtual_volume);
+ pa_tagstruct_put_cvolume(t, &sink->real_volume);
pa_pstream_send_tagstruct(u->pstream, t);
}
@@ -1760,7 +1769,6 @@ static void sink_set_volume(pa_sink *sink) {
static void sink_set_mute(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
- uint32_t tag;
pa_assert(sink);
u = sink->userdata;
@@ -1771,7 +1779,7 @@ static void sink_set_mute(pa_sink *sink) {
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
- pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, u->ctag++);
pa_tagstruct_putu32(t, u->device_index);
pa_tagstruct_put_boolean(t, !!sink->muted);
pa_pstream_send_tagstruct(u->pstream, t);
@@ -1937,6 +1945,8 @@ int pa__init(pa_module*m) {
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
pa_xfree(dn);
@@ -2030,6 +2040,11 @@ void pa__done(pa_module*m) {
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
diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c
index 1ad6fa2d..1b1e9c1a 100644
--- a/src/modules/module-udev-detect.c
+++ b/src/modules/module-udev-detect.c
@@ -25,13 +25,17 @@
#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 "module-udev-detect-symdef.h"
@@ -39,18 +43,25 @@ 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?> "
+ "ignore_dB=<ignore dB information from the device?>");
struct device {
char *path;
- pa_bool_t accessible;
+ pa_bool_t need_verify;
char *card_name;
+ char *args;
uint32_t module;
+ pa_ratelimit ratelimit;
};
struct userdata {
pa_core *core;
pa_hashmap *devices;
- pa_bool_t use_tsched;
+
+ pa_bool_t use_tsched:1;
+ pa_bool_t ignore_dB:1;
struct udev* udev;
struct udev_monitor *monitor;
@@ -62,14 +73,18 @@ struct userdata {
static const char* const valid_modargs[] = {
"tsched",
+ "ignore_dB",
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);
}
@@ -88,35 +103,204 @@ static const char *path_get_card_id(const char *path) {
return e + 5;
}
+static pa_bool_t 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;
+ size_t len;
+ struct dirent *space = NULL, *de;
+ pa_bool_t busy = FALSE;
+ int r;
+
+ 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;
+ }
+
+ len = offsetof(struct dirent, d_name) + fpathconf(dirfd(card_dir), _PC_NAME_MAX) + 1;
+ space = pa_xmalloc(len);
+
+ for (;;) {
+ de = NULL;
+
+ if ((r = readdir_r(card_dir, space, &de)) != 0) {
+ pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r));
+ goto fail;
+ }
+
+ if (!de)
+ break;
+
+ if (!pa_startswith(de->d_name, "pcm"))
+ 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];
+
+ if ((r = readdir_r(pcm_dir, space, &de)) != 0) {
+ pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r));
+ 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 = fopen(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);
+ pa_xfree(space);
+
+ 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;
+ pa_bool_t accessible;
pa_assert(u);
pa_assert(d);
- if (!(card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD)))
- return;
-
cd = pa_sprintf_malloc("%s/snd/controlC%s", udev_get_dev_path(u->udev), path_get_card_id(d->path));
- d->accessible = access(cd, W_OK) >= 0;
- pa_log_info("%s is accessible: %s", cd, pa_yes_no(d->accessible));
+ accessible = access(cd, R_OK|W_OK) >= 0;
+ pa_log_debug("%s is accessible: %s", cd, pa_yes_no(accessible));
+
pa_xfree(cd);
- pa_card_suspend(card, !d->accessible, PA_SUSPEND_SESSION);
+ if (d->module == PA_INVALID_INDEX) {
+
+ /* If we are not loaded, try to load */
+
+ if (accessible) {
+ pa_module *m;
+ pa_bool_t 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 distuingish 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("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_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 *card_name, *args;
- pa_module *m;
char *n;
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))) {
@@ -124,42 +308,34 @@ static void card_changed(struct userdata *u, struct udev_device *dev) {
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);
+ d->args = pa_sprintf_malloc("device_id=\"%s\" "
+ "name=\"%s\" "
+ "card_name=\"%s\" "
+ "tsched=%s "
+ "ignore_dB=%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->ignore_dB));
+ pa_xfree(n);
- card_name = pa_sprintf_malloc("alsa_card.%s", n);
- args = pa_sprintf_malloc("device_id=\"%s\" "
- "name=\"%s\" "
- "card_name=\"%s\" "
- "tsched=%i "
- "card_properties=\"module-udev-detect.discovered=1\"",
- path_get_card_id(path),
- n,
- card_name,
- (int) u->use_tsched);
-
- pa_log_debug("Loading module-alsa-card with arguments '%s'", args);
- m = pa_module_load(u->core, "module-alsa-card", args);
- pa_xfree(args);
-
- if (m) {
- pa_log_info("Card %s (%s) added.", path, n);
-
- d = pa_xnew(struct device, 1);
- d->path = pa_xstrdup(path);
- d->card_name = card_name;
- d->module = m->index;
- d->accessible = TRUE;
-
- pa_hashmap_put(u->devices, d->path, d);
- } else
- pa_xfree(card_name);
+ pa_hashmap_put(u->devices, d->path, d);
- pa_xfree(n);
+ verify_access(u, d);
}
static void remove_card(struct userdata *u, struct udev_device *dev) {
@@ -172,7 +348,10 @@ static void remove_card(struct userdata *u, struct udev_device *dev) {
return;
pa_log_info("Card %s removed.", d->path);
- pa_module_unload_request_by_index(u->core, d->module, TRUE);
+
+ if (d->module != PA_INVALID_INDEX)
+ pa_module_unload_request_by_index(u->core, d->module, TRUE);
+
device_free(d);
}
@@ -249,6 +428,34 @@ fail:
u->udev_io = NULL;
}
+static pa_bool_t pcm_node_belongs_to_device(
+ struct device *d,
+ const char *node) {
+
+ char *cd;
+ pa_bool_t b;
+
+ cd = pa_sprintf_malloc("pcmC%sD", path_get_card_id(d->path));
+ b = pa_startswith(node, cd);
+ pa_xfree(cd);
+
+ return b;
+}
+
+static pa_bool_t control_node_belongs_to_device(
+ struct device *d,
+ const char *node) {
+
+ char *cd;
+ pa_bool_t 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,
@@ -262,10 +469,13 @@ static void inotify_cb(
} buf;
struct userdata *u = userdata;
static int type = 0;
- pa_bool_t verify = FALSE;
+ pa_bool_t 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) {
@@ -277,25 +487,60 @@ static void inotify_cb(
goto fail;
}
- if ((buf.e.mask & IN_CLOSE_WRITE) && pa_startswith(buf.e.name, "pcmC"))
- verify = TRUE;
+ 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;
+ }
}
- if (verify) {
- struct device *d;
- void *state;
-
- pa_log_debug("Verifying access.");
-
- PA_HASHMAP_FOREACH(d, u->devices, state)
+ PA_HASHMAP_FOREACH(d, u->devices, state)
+ if (d->need_verify) {
+ d->need_verify = FALSE;
verify_access(u, d);
- }
+ }
- return;
+ if (!deleted)
+ return;
fail:
- a->io_free(u->inotify_io);
- u->inotify_io = NULL;
+ if (u->inotify_io) {
+ a->io_free(u->inotify_io);
+ u->inotify_io = NULL;
+ }
if (u->inotify_fd >= 0) {
pa_close(u->inotify_fd);
@@ -307,17 +552,38 @@ static int setup_inotify(struct userdata *u) {
char *dev_snd;
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;
}
dev_snd = pa_sprintf_malloc("%s/snd", udev_get_dev_path(u->udev));
- r = inotify_add_watch(u->inotify_fd, dev_snd, IN_CLOSE_WRITE);
+ r = inotify_add_watch(u->inotify_fd, dev_snd, IN_ATTRIB|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF);
pa_xfree(dev_snd);
if (r < 0) {
- pa_log("inotify_add_watch() failed: %s", pa_cstrerror(errno));
+ 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;
}
@@ -332,6 +598,7 @@ int pa__init(pa_module *m) {
struct udev_enumerate *enumerate = NULL;
struct udev_list_entry *item = NULL, *first = NULL;
int fd;
+ pa_bool_t use_tsched = TRUE, ignore_dB = FALSE;
pa_assert(m);
@@ -343,13 +610,19 @@ int pa__init(pa_module *m) {
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- u->use_tsched = TRUE;
u->inotify_fd = -1;
- if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) {
- pa_log("Failed to parse tsched argument.");
+ 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_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+ pa_log("Failed to parse ignore_dB= argument.");
goto fail;
}
+ u->ignore_dB = ignore_dB;
if (!(u->udev = udev_new())) {
pa_log("Failed to initialize udev library.");
@@ -402,7 +675,7 @@ int pa__init(pa_module *m) {
udev_enumerate_unref(enumerate);
- pa_log_info("Loaded %u modules.", pa_hashmap_size(u->devices));
+ pa_log_info("Found %u cards.", pa_hashmap_size(u->devices));
pa_modargs_free(ma);
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
index 91da598e..6e484eae 100644
--- a/src/modules/module-volume-restore.c
+++ b/src/modules/module-volume-restore.c
@@ -48,6 +48,7 @@ static const char* const valid_modargs[] = {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
pa_bool_t restore_device = TRUE, restore_volume = TRUE;
+ pa_module *n;
char *t;
pa_assert(m);
@@ -66,13 +67,15 @@ int pa__init(pa_module*m) {
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));
- pa_module_load(m->core, "module-stream-restore", t);
+ n = pa_module_load(m->core, "module-stream-restore", t);
pa_xfree(t);
- pa_module_unload_request(m, TRUE);
+ if (n)
+ pa_module_unload_request(m, TRUE);
pa_modargs_free(ma);
- return 0;
+
+ return n ? 0 : -1;
fail:
if (ma)
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index c44b882b..71536260 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -812,11 +812,11 @@ static void sink_get_volume(pa_sink *s) {
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->virtual_volume) >= 0)
+ 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->virtual_volume) >= 0)
+ 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));
@@ -830,11 +830,11 @@ static void sink_set_volume(pa_sink *s) {
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->virtual_volume) >= 0)
+ 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->virtual_volume) >= 0)
+ 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));
@@ -848,11 +848,11 @@ static void source_get_volume(pa_source *s) {
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->virtual_volume) >= 0)
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->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->virtual_volume) >= 0)
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume) >= 0)
return;
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
@@ -866,11 +866,11 @@ static void source_set_volume(pa_source *s) {
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->virtual_volume) >= 0)
+ if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->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->virtual_volume) >= 0)
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume) >= 0)
return;
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c
index e1cbed02..5b061034 100644
--- a/src/modules/raop/base64.c
+++ b/src/modules/raop/base64.c
@@ -57,7 +57,6 @@ int pa_base64_encode(const void *data, int size, char **str)
p = s = pa_xnew(char, size * 4 / 3 + 4);
q = (const unsigned char *) data;
- i = 0;
for (i = 0; i < size;) {
c = q[i++];
c *= 256;
diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index 9699132d..ac48ab10 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -283,15 +283,15 @@ static void sink_set_volume_cb(pa_sink *s) {
/* 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->virtual_volume);
+ v = pa_cvolume_max(&s->real_volume);
/* 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->virtual_volume, &hw);
+ pa_sw_cvolume_divide(&s->soft_volume, &s->real_volume, &hw);
- pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
+ pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->real_volume));
pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &hw));
pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
diff --git a/src/modules/reserve-monitor.c b/src/modules/reserve-monitor.c
index 13ecde2b..ab453e61 100644
--- a/src/modules/reserve-monitor.c
+++ b/src/modules/reserve-monitor.c
@@ -38,6 +38,7 @@ struct rm_monitor {
char *device_name;
char *service_name;
+ char *match;
DBusConnection *connection;
@@ -51,12 +52,18 @@ struct rm_monitor {
#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
+#define SERVICE_FILTER \
+ "type='signal'," \
+ "sender='" DBUS_SERVICE_DBUS "'," \
+ "interface='" DBUS_INTERFACE_DBUS "'," \
+ "member='NameOwnerChanged'," \
+ "arg0='%s'"
+
static DBusHandlerResult filter_handler(
DBusConnection *c,
DBusMessage *s,
void *userdata) {
- DBusMessage *reply;
rm_monitor *m;
DBusError error;
@@ -97,31 +104,10 @@ static DBusHandlerResult filter_handler(
}
}
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
invalid:
- if (!(reply = dbus_message_new_error(
- s,
- 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;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
int rm_watch(
@@ -175,11 +161,13 @@ int rm_watch(
m->filtering = 1;
- dbus_bus_add_match(m->connection,
- "type='signal',"
- "sender='" DBUS_SERVICE_DBUS "',"
- "interface='" DBUS_INTERFACE_DBUS "',"
- "member='NameOwnerChanged'", error);
+ 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;
@@ -220,10 +208,8 @@ void rm_release(rm_monitor *m) {
if (m->matching)
dbus_bus_remove_match(
m->connection,
- "type='signal',"
- "sender='" DBUS_SERVICE_DBUS "',"
- "interface='" DBUS_INTERFACE_DBUS "',"
- "member='NameOwnerChanged'", NULL);
+ m->match,
+ NULL);
if (m->filtering)
dbus_connection_remove_filter(
@@ -233,6 +219,7 @@ void rm_release(rm_monitor *m) {
free(m->device_name);
free(m->service_name);
+ free(m->match);
if (m->connection)
dbus_connection_unref(m->connection);
diff --git a/src/modules/reserve.c b/src/modules/reserve.c
index 5597f177..b4c168cf 100644
--- a/src/modules/reserve.c
+++ b/src/modules/reserve.c
@@ -291,7 +291,6 @@ static DBusHandlerResult filter_handler(
DBusMessage *m,
void *userdata) {
- DBusMessage *reply;
rd_device *d;
DBusError error;
@@ -323,35 +322,13 @@ static DBusHandlerResult filter_handler(
rd_release(d);
}
- return DBUS_HANDLER_RESULT_HANDLED;
}
}
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
invalid:
- 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;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 5caf8272..1a05f57d 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -361,7 +361,7 @@ static void sink_input_attach(pa_sink_input *i) {
pa_assert_se(s = i->userdata);
pa_assert(!s->rtpoll_item);
- s->rtpoll_item = pa_rtpoll_item_new(i->sink->rtpoll, PA_RTPOLL_LATE, 1);
+ 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;
@@ -501,8 +501,9 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
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_VARIABLE_RATE);
+ pa_sink_input_new(&s->sink_input, u->module->core, &data);
pa_sink_input_new_data_done(&data);
if (!s->sink_input) {
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index f147364d..8e1cfe36 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -330,8 +330,9 @@ int pa__init(pa_module*m) {
data.source = s;
pa_source_output_new_data_set_sample_spec(&data, &ss);
pa_source_output_new_data_set_channel_map(&data, &cm);
+ data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND;
- pa_source_output_new(&o, m->core, &data, PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND);
+ pa_source_output_new(&o, m->core, &data);
pa_source_output_new_data_done(&data);
if (!o) {
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 72d304e8..915c1072 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -214,11 +214,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
/* 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_tostring_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, pa_strbuf_tostring_free(c->header_buffer));
+ 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;
+ c->header_buffer = NULL;
}
pa_log_debug("Full response received. Dispatching");
@@ -240,9 +242,11 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
}
if (c->last_header) {
+ char *tmp = pa_strbuf_tostring_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, pa_strbuf_tostring_free(c->header_buffer));
+ 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;
@@ -452,6 +456,8 @@ static int rtsp_exec(pa_rtsp_client* c, const char* cmd,
l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
pa_xfree(hdrs);
+ /* FIXME: this is broken, not necessarily all bytes are written */
+
return 0;
}
diff --git a/src/modules/x11/module-x11-publish.c b/src/modules/x11/module-x11-publish.c
index 2c7fdc12..7ee1b6da 100644
--- a/src/modules/x11/module-x11-publish.c
+++ b/src/modules/x11/module-x11-publish.c
@@ -90,7 +90,7 @@ static void publish_servers(struct userdata *u, pa_strlist *l) {
l = pa_strlist_reverse(l);
s = pa_strlist_tostring(l);
- l = pa_strlist_reverse(l);
+ pa_strlist_reverse(l);
pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER", s);
pa_xfree(s);
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index 88823012..9b516262 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -219,11 +219,11 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
case 6:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
- m->map[1] = PA_CHANNEL_POSITION_REAR_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_REAR_RIGHT;
- m->map[5] = PA_CHANNEL_POSITION_LFE;
+ m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+ m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
case 5:
@@ -247,7 +247,7 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_CENTER;
m->map[2] = PA_CHANNEL_POSITION_RIGHT;
- m->map[3] = PA_CHANNEL_POSITION_LFE;
+ m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
default:
@@ -299,6 +299,8 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
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;
@@ -451,6 +453,10 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
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)
@@ -639,6 +645,10 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
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);
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index d7901ac2..469effc8 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -216,17 +216,27 @@ 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 */
+ /**< 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 */
+ /**< 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 */
+ /**< 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,
@@ -282,7 +292,7 @@ pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels,
/** 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 */
+/** 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 */
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index 940d0b67..4aa4ba1f 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -92,16 +92,17 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
/* 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 },
- { "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
- { "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
- { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
- { "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
- { NULL, NULL, NULL, NULL },
+ { "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 },
+ { "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
+ { "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
+ { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
+ { "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL },
+ { "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
+ { NULL, NULL, NULL, NULL },
};
if (filename) {
diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in
index 579bcc20..6c8d371c 100644
--- a/src/pulse/client.conf.in
+++ b/src/pulse/client.conf.in
@@ -29,5 +29,5 @@
; cookie-file =
-; disable-shm = no
+; enable-shm = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 4ded5565..894ab2e0 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -668,11 +668,24 @@ static pa_strlist *prepend_per_user(pa_strlist *l) {
static int context_autospawn(pa_context *c) {
pid_t pid;
int status, r;
-
- pa_log_debug("Trying to autospawn...");
+ 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;
+ }
+
+ if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
+ 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();
@@ -688,23 +701,23 @@ static int context_autospawn(pa_context *c) {
/* Child */
const char *state = NULL;
-#define MAX_ARGS 64
- const char * argv[MAX_ARGS+1];
- int n;
+ 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 */
-
- n = 0;
-
argv[n++] = c->conf->daemon_binary;
argv[n++] = "--start";
- while (n < MAX_ARGS) {
+ while (n < PA_ELEMENTSOF(argv)-1) {
char *a;
if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
@@ -714,10 +727,10 @@ static int context_autospawn(pa_context *c) {
}
argv[n++] = NULL;
+ pa_assert(n <= PA_ELEMENTSOF(argv));
execv(argv[0], (char * const *) argv);
_exit(1);
-#undef MAX_ARGS
}
/* Parent */
@@ -730,9 +743,16 @@ static int context_autospawn(pa_context *c) {
} while (r < 0 && errno == EINTR);
if (r < 0) {
- pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
- pa_context_fail(c, PA_ERR_INTERNAL);
- goto fail;
+
+ if (errno != ESRCH) {
+ 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;
diff --git a/src/pulse/context.h b/src/pulse/context.h
index cd129313..ecff58df 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -147,12 +147,6 @@
* server. A pa_context object wraps a connection to a PulseAudio
* server using its native protocol. */
-/** \example pacat.c
- * A playback and recording tool using the asynchronous API */
-
-/** \example paplay.c
- * A sound file playback tool using the asynchronous API, based on libsndfile */
-
PA_C_DECL_BEGIN
/** An opaque connection context to a daemon */
@@ -267,7 +261,6 @@ pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_even
for mainloop->time_restart). \since 0.9.16 */
void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec);
-
PA_C_DECL_END
#endif
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 08399ca8..1a7da974 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -394,6 +394,7 @@ enum {
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 */
};
diff --git a/src/pulse/error.c b/src/pulse/error.c
index 93a13fc6..e8276990 100644
--- a/src/pulse/error.c
+++ b/src/pulse/error.c
@@ -64,7 +64,9 @@ const char*pa_strerror(int error) {
[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_FORKED] = N_("Client forked"),
+ [PA_ERR_IO] = N_("Input/Output error"),
+ [PA_ERR_BUSY] = N_("Device or resource busy")
};
pa_init_i18n();
diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c
index 63c911f8..10e9fd5d 100644
--- a/src/pulse/ext-stream-restore.c
+++ b/src/pulse/ext-stream-restore.c
@@ -239,13 +239,10 @@ pa_operation *pa_ext_stream_restore_write(
return o;
fail:
- if (o) {
- pa_operation_cancel(o);
- pa_operation_unref(o);
- }
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
- if (t)
- pa_tagstruct_free(t);
+ pa_tagstruct_free(t);
pa_context_set_error(c, PA_ERR_INVALID);
return NULL;
@@ -290,13 +287,10 @@ pa_operation *pa_ext_stream_restore_delete(
return o;
fail:
- if (o) {
- pa_operation_cancel(o);
- pa_operation_unref(o);
- }
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
- if (t)
- pa_tagstruct_free(t);
+ pa_tagstruct_free(t);
pa_context_set_error(c, PA_ERR_INVALID);
return NULL;
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
index 189513a8..67aba27d 100644
--- a/src/pulse/glib-mainloop.h
+++ b/src/pulse/glib-mainloop.h
@@ -56,7 +56,9 @@ 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 */
+/** Return the abstract main loop API vtable for the GLIB main loop
+ object. No need of freeing the API as it is owned by the loop and
+ it is destroyed when this dies */
pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g);
PA_C_DECL_END
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index ec2da85b..e069c9e9 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -151,6 +151,11 @@ struct pa_stream {
uint32_t device_index;
char *device_name;
+ /* playback */
+ pa_memblock *write_memblock;
+ void *write_data;
+
+ /* recording */
pa_memchunk peek_memchunk;
void *peek_data;
pa_memblockq *record_memblockq;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 27a587cb..100413ee 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -834,6 +834,8 @@ pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char*name, p
}
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);
}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index ee982100..68cfc874 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -331,6 +331,12 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i
/** 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);
@@ -557,12 +563,6 @@ pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx,
/** Move the specified source output to a different source. \since 0.9.5 */
pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata);
-/** Suspend/Resume a source. \since 0.9.7 */
-pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata);
-
-/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */
-pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
-
/** Kill a source output. */
pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
index c418d108..93a4742d 100644
--- a/src/pulse/mainloop.c
+++ b/src/pulse/mainloop.c
@@ -765,23 +765,22 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) {
static int calc_next_timeout(pa_mainloop *m) {
pa_time_event *t;
- pa_usec_t usec;
+ pa_usec_t clock_now;
if (!m->n_enabled_time_events)
return -1;
- t = find_next_time_event(m);
- pa_assert(t);
+ pa_assert_se(t = find_next_time_event(m));
- if (t->time == 0)
+ if (t->time <= 0)
return 0;
- usec = t->time - pa_rtclock_now();
+ clock_now = pa_rtclock_now();
- if (usec <= 0)
+ if (t->time <= clock_now)
return 0;
- return (int) (usec / 1000); /* in milliseconds */
+ return (int) ((t->time - clock_now) / 1000); /* in milliseconds */
}
static int dispatch_timeout(pa_mainloop *m) {
diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h
index 4a83ebe8..63abd588 100644
--- a/src/pulse/mainloop.h
+++ b/src/pulse/mainloop.h
@@ -108,7 +108,9 @@ 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. */
+/** Return the abstract main loop abstraction layer vtable for this
+ main loop. No need of freeing the API as it is owned by the loop
+ and it is destroyed when this dies */
pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
/** Shutdown the main loop */
diff --git a/src/pulse/operation.h b/src/pulse/operation.h
index 7b0dabdd..b6b5691d 100644
--- a/src/pulse/operation.h
+++ b/src/pulse/operation.h
@@ -40,7 +40,11 @@ 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. */
+/** 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 */
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index c904f533..048b241a 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -251,7 +251,7 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb
pa_assert(p);
pa_assert(key);
- pa_assert(data);
+ pa_assert(data || nbytes == 0);
if (!property_name_valid(key))
return -1;
@@ -264,7 +264,8 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb
pa_xfree(prop->value);
prop->value = pa_xmalloc(nbytes+1);
- memcpy(prop->value, data, nbytes);
+ if (nbytes > 0)
+ memcpy(prop->value, data, nbytes);
((char*) prop->value)[nbytes] = 0;
prop->nbytes = nbytes;
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index bc4dbd8a..8bf9c482 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -97,6 +97,24 @@ PA_C_DECL_BEGIN
/** 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 an X11 window on the screen: the X11 display string. e.g. ":0.0" */
#define PA_PROP_WINDOW_X11_DISPLAY "window.x11.display"
@@ -197,7 +215,7 @@ PA_C_DECL_BEGIN
/** 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.. */
+/** 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. */
diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
index aa369e69..793ba9b1 100644
--- a/src/pulse/pulseaudio.h
+++ b/src/pulse/pulseaudio.h
@@ -44,15 +44,17 @@
#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 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 and
- * \ref mainloop-signal.h at once */
+ * Include all libpulse header files at once. The following files are
+ * included: \ref mainloop-api.h, \ref sample.h, \ref def.h, \ref
+ * context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, \ref
+ * scache.h, \ref version.h, \ref error.h, \ref channelmap.h, \ref
+ * operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref
+ * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref proplist.h,
+ * \ref timeval.h, \ref rtclock.h and \ref mainloop-signal.h at
+ * once */
/** \mainpage
*
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 0f19f8eb..9698d8a5 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -36,28 +36,27 @@
#include "sample.h"
-size_t pa_sample_size_of_format(pa_sample_format_t f) {
-
- static const size_t table[] = {
- [PA_SAMPLE_U8] = 1,
- [PA_SAMPLE_ULAW] = 1,
- [PA_SAMPLE_ALAW] = 1,
- [PA_SAMPLE_S16LE] = 2,
- [PA_SAMPLE_S16BE] = 2,
- [PA_SAMPLE_FLOAT32LE] = 4,
- [PA_SAMPLE_FLOAT32BE] = 4,
- [PA_SAMPLE_S32LE] = 4,
- [PA_SAMPLE_S32BE] = 4,
- [PA_SAMPLE_S24LE] = 3,
- [PA_SAMPLE_S24BE] = 3,
- [PA_SAMPLE_S24_32LE] = 4,
- [PA_SAMPLE_S24_32BE] = 4
- };
+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(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[f];
+ return size_table[f];
}
size_t pa_sample_size(const pa_sample_spec *spec) {
@@ -65,35 +64,35 @@ size_t pa_sample_size(const pa_sample_spec *spec) {
pa_assert(spec);
pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
- return pa_sample_size_of_format(spec->format);
+ return size_table[spec->format];
}
size_t pa_frame_size(const pa_sample_spec *spec) {
pa_assert(spec);
pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
- return pa_sample_size(spec) * spec->channels;
+ return size_table[spec->format] * spec->channels;
}
size_t pa_bytes_per_second(const pa_sample_spec *spec) {
pa_assert(spec);
pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
- return spec->rate*pa_frame_size(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_return_val_if_fail(pa_sample_spec_valid(spec), 0);
- return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate);
+ 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_return_val_if_fail(pa_sample_spec_valid(spec), 0);
- return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(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) {
@@ -109,12 +108,12 @@ pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec) {
int pa_sample_spec_valid(const pa_sample_spec *spec) {
pa_assert(spec);
- if (spec->rate <= 0 ||
+ if (PA_UNLIKELY (spec->rate <= 0 ||
spec->rate > PA_RATE_MAX ||
spec->channels <= 0 ||
spec->channels > PA_CHANNELS_MAX ||
spec->format >= PA_SAMPLE_MAX ||
- spec->format < 0)
+ spec->format < 0))
return 0;
return 1;
@@ -125,6 +124,10 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
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
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 53d7dea3..7a4a55a0 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -302,6 +302,13 @@ pa_sample_format_t pa_parse_sample_format(const char *format) PA_GCC_PURE;
/** 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);
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index 77f60d72..27da6887 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -187,7 +187,7 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, dev);
- if (volume == (pa_volume_t) -1 && c->version < 15)
+ if (volume == PA_VOLUME_INVALID && c->version < 15)
volume = PA_VOLUME_NORM;
pa_tagstruct_putu32(t, volume);
@@ -216,7 +216,6 @@ pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *na
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
- PA_CHECK_VALIDITY_RETURN_NULL(c, p, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
@@ -228,12 +227,19 @@ pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *na
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, dev);
- if (volume == (pa_volume_t) -1 && c->version < 15)
+ if (volume == PA_VOLUME_INVALID && c->version < 15)
volume = PA_VOLUME_NORM;
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
- pa_tagstruct_put_proplist(t, p);
+
+ 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);
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
index cd579d2e..31cf7b01 100644
--- a/src/pulse/scache.h
+++ b/src/pulse/scache.h
@@ -101,7 +101,7 @@ 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_t) -1 which will leave the decision about the volume to the server side which is a good idea. */ ,
+ 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 */);
@@ -113,7 +113,7 @@ 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_t) -1 which will leave the decision about the volume to the server side which is a good idea. */ ,
+ 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 */);
diff --git a/src/pulse/simple.c b/src/pulse/simple.c
index f4481fc3..9ed7a653 100644
--- a/src/pulse/simple.c
+++ b/src/pulse/simple.c
@@ -70,8 +70,8 @@ struct pa_simple {
#define CHECK_DEAD_GOTO(p, rerror, label) \
do { \
- if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \
- !(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \
+ if (!(p)->context || !PA_CONTEXT_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) \
@@ -157,12 +157,8 @@ pa_simple* pa_simple_new(
CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
- p = pa_xnew(pa_simple, 1);
- p->context = NULL;
- p->stream = NULL;
+ p = pa_xnew0(pa_simple, 1);
p->direction = dir;
- p->read_data = NULL;
- p->read_index = p->read_length = 0;
if (!(p->mainloop = pa_threaded_mainloop_new()))
goto fail;
@@ -182,12 +178,21 @@ pa_simple* pa_simple_new(
if (pa_threaded_mainloop_start(p->mainloop) < 0)
goto unlock_and_fail;
- /* Wait until the context is ready */
- pa_threaded_mainloop_wait(p->mainloop);
+ for (;;) {
+ pa_context_state_t state;
- if (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
- error = pa_context_errno(p->context);
- goto unlock_and_fail;
+ 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))) {
@@ -216,13 +221,21 @@ pa_simple* pa_simple_new(
goto unlock_and_fail;
}
- /* Wait until the stream is ready */
- pa_threaded_mainloop_wait(p->mainloop);
+ for (;;) {
+ pa_stream_state_t state;
- /* Wait until the stream is ready */
- if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
- error = pa_context_errno(p->context);
- goto unlock_and_fail;
+ 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);
@@ -248,8 +261,10 @@ void pa_simple_free(pa_simple *s) {
if (s->stream)
pa_stream_unref(s->stream);
- if (s->context)
+ if (s->context) {
+ pa_context_disconnect(s->context);
pa_context_unref(s->context);
+ }
if (s->mainloop)
pa_threaded_mainloop_free(s->mainloop);
@@ -261,7 +276,8 @@ int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
- CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+ 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);
@@ -300,7 +316,8 @@ int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);
- CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+ 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);
@@ -375,7 +392,7 @@ int pa_simple_drain(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
- while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
@@ -411,7 +428,7 @@ int pa_simple_flush(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
- while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 40556329..2bc2b1e4 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -144,12 +144,13 @@ pa_stream *pa_stream_new_with_proplist(
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;
@@ -221,6 +222,11 @@ static void stream_free(pa_stream *s) {
stream_unlink(s);
+ if (s->write_memblock) {
+ pa_memblock_release(s->write_memblock);
+ pa_memblock_unref(s->write_data);
+ }
+
if (s->peek_memchunk.memblock) {
if (s->peek_data)
pa_memblock_release(s->peek_memchunk.memblock);
@@ -821,7 +827,7 @@ static void create_stream_complete(pa_stream *s) {
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);
+ 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);
}
@@ -861,7 +867,7 @@ static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_s
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;
+ uint32_t requested_bytes = 0;
pa_assert(pd);
pa_assert(s);
@@ -1166,7 +1172,7 @@ int pa_stream_connect_playback(
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags,
- pa_cvolume *volume,
+ const pa_cvolume *volume,
pa_stream *sync_stream) {
pa_assert(s);
@@ -1187,20 +1193,71 @@ int pa_stream_connect_record(
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(
pa_stream *s,
const void *data,
size_t length,
- void (*free_cb)(void *p),
+ pa_free_cb_t free_cb,
int64_t offset,
pa_seek_mode_t seek) {
- pa_memchunk chunk;
- pa_seek_mode_t t_seek;
- int64_t t_offset;
- size_t t_length;
- const void *t_data;
-
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
pa_assert(data);
@@ -1210,46 +1267,71 @@ int pa_stream_write(
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, !free_cb || !s->write_memblock, PA_ERR_INVALID);
- if (length <= 0)
- return 0;
+ if (s->write_memblock) {
+ pa_memchunk chunk;
- t_seek = seek;
- t_offset = offset;
- t_length = length;
- t_data = data;
+ /* pa_stream_write_begin() was called before */
- while (t_length > 0) {
+ pa_memblock_release(s->write_memblock);
- chunk.index = 0;
+ chunk.memblock = s->write_memblock;
+ chunk.index = (const char *) data - (const char *) s->write_data;
+ chunk.length = length;
- if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
- chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1);
- chunk.length = t_length;
- } else {
- void *d;
+ s->write_memblock = NULL;
+ s->write_data = NULL;
- chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool));
- chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
+ pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk);
+ pa_memblock_unref(chunk.memblock);
- d = pa_memblock_acquire(chunk.memblock);
- memcpy(d, t_data, chunk.length);
- pa_memblock_release(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_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
+ /* pa_stream_write_begin() was not called before */
- t_offset = 0;
- t_seek = PA_SEEK_RELATIVE;
+ while (t_length > 0) {
+ pa_memchunk chunk;
- t_data = (const uint8_t*) t_data + chunk.length;
- t_length -= chunk.length;
+ chunk.index = 0;
- pa_memblock_unref(chunk.memblock);
- }
+ if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
+ chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1);
+ chunk.length = t_length;
+ } else {
+ void *d;
+
+ chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool));
+ chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
+
+ d = pa_memblock_acquire(chunk.memblock);
+ memcpy(d, t_data, chunk.length);
+ pa_memblock_release(chunk.memblock);
+ }
- if (free_cb && pa_pstream_get_shm(s->context->pstream))
- free_cb((void*) data);
+ pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
+
+ t_offset = 0;
+ t_seek = PA_SEEK_RELATIVE;
+
+ t_data = (const uint8_t*) t_data + chunk.length;
+ t_length -= chunk.length;
+
+ pa_memblock_unref(chunk.memblock);
+ }
+
+ if (free_cb && pa_pstream_get_shm(s->context->pstream))
+ free_cb((void*) data);
+ }
/* This is obviously wrong since we ignore the seeking index . But
* that's OK, the server side applies the same error */
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 49c132a2..21dd0a85 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -319,7 +319,7 @@ typedef struct pa_stream pa_stream;
typedef void (*pa_stream_success_cb_t) (pa_stream*s, int success, void *userdata);
/** A generic request callback */
-typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdata);
+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);
@@ -405,7 +405,7 @@ int pa_stream_connect_playback(
const char *dev /**< Name of the sink to connect to, or NULL for default */ ,
const pa_buffer_attr *attr /**< Buffering attributes, or NULL for default */,
pa_stream_flags_t flags /**< Additional flags, or 0 for default */,
- pa_cvolume *volume /**< Initial volume, or NULL for default */,
+ 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 */
@@ -418,15 +418,71 @@ int pa_stream_connect_record(
/** Disconnect a stream from a source/sink */
int pa_stream_disconnect(pa_stream *s);
-/** Write some data to the server (for playback sinks), if free_cb is
- * non-NULL this routine is called when all data has been written out
- * and an internal reference to the specified data is kept, the data
- * is not copied. If NULL, the data is copied into an internal
- * buffer. The client my freely seek around in the output buffer. For
+/** 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. On return *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 *nbytes value is invalid. Acessing the
+ * memory returned after the following pa_stream_write() or
+ * pa_stream_cancel_write() is invalid. On invocation only *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 data set to an address within this memory
+ * area and an 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
+ * pointer/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 free_cb
+ * is non-NULL this routine is called when all data has been written
+ * out and an internal reference to the specified data is kept, the
+ * data is not copied. If NULL, the data is copied into an internal
+ * buffer. The client may freely seek around in the output buffer. For
* most applications passing 0 and PA_SEEK_RELATIVE as arguments for
* offset and seek should be useful. Afte ther write call succeeded
* the write index will be a the position after where this chunk of
- * data has been written to. */
+ * 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 */,
@@ -435,11 +491,12 @@ int pa_stream_write(
int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
-/** Read the next fragment from the buffer (for recording).
- * data will point to the actual data and length will contain the size
- * of the data in bytes (which can be less than a complete framgnet).
- * Use pa_stream_drop() to actually remove the data from the
- * buffer. If no data is available will return a NULL pointer */
+/** Read the next fragment from the buffer (for recording streams).
+ * data will point to the actual data and nbytes will contain the size
+ * of the data in bytes (which can be less or more than a complete
+ * fragment). Use pa_stream_drop() to actually remove the data from
+ * the buffer. If no data is available this will return a NULL
+ * pointer */
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
@@ -455,7 +512,9 @@ 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 buffer is empty */
+/** Drain a playback stream. Use this for notification when the buffer
+ * is empty. 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
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
index 6916d867..a2b98ce1 100644
--- a/src/pulse/thread-mainloop.c
+++ b/src/pulse/thread-mainloop.c
@@ -51,7 +51,7 @@
struct pa_threaded_mainloop {
pa_mainloop *real_mainloop;
- int n_waiting;
+ int n_waiting, n_waiting_for_accept;
pa_thread* thread;
pa_mutex* mutex;
@@ -190,8 +190,12 @@ void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
pa_cond_signal(m->cond, 1);
- if (wait_for_accept && m->n_waiting > 0)
- pa_cond_wait(m->accept_cond, m->mutex);
+ if (wait_for_accept) {
+ m->n_waiting_for_accept ++;
+
+ while (m->n_waiting_for_accept > 0)
+ pa_cond_wait(m->accept_cond, m->mutex);
+ }
}
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
@@ -214,6 +218,9 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *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);
}
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index 8eddce4c..2cf496e1 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -137,15 +137,19 @@ PA_C_DECL_BEGIN
* 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.
+ * 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, both pa_threaded_mainloop_wait() may only be called with
+ * 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.
@@ -274,7 +278,9 @@ void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m);
* 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. */
+ * will be acquired again. This function may spuriously wake up even
+ * without _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
@@ -293,7 +299,9 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m);
/** Return the return value as specified with the main loop's quit() routine. */
int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m);
-/** Return the abstract main loop abstraction layer vtable for this main loop. */
+/** Return the abstract main loop abstraction layer vtable for this
+ main loop. No need of freeing the API as it is owned by the loop
+ and it is destroyed when this dies */
pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m);
/** Returns non-zero when called from withing the event loop thread. \since 0.9.7 */
diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c
index 6b58bde3..fe7bcd26 100644
--- a/src/pulse/utf8.c
+++ b/src/pulse/utf8.c
@@ -120,10 +120,8 @@ static char* utf8_validate(const char *str, char *output) {
size = 4;
min = (1 << 16);
val = (uint32_t) (*p & 0x07);
- } else {
- size = 1;
+ } else
goto error;
- }
p++;
if (!is_continuation_char(*p))
@@ -150,12 +148,9 @@ ONE_REMAINING:
if (o) {
memcpy(o, last, (size_t) size);
- o += size - 1;
+ o += size;
}
- if (o)
- o++;
-
continue;
error:
diff --git a/src/pulse/util.c b/src/pulse/util.c
index 6f1e40a9..9440f5de 100644
--- a/src/pulse/util.c
+++ b/src/pulse/util.c
@@ -61,38 +61,40 @@
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
+#include <pulsecore/usergroup.h>
#include "util.h"
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 pw, *r;
+ struct passwd *r;
#endif
pa_assert(s);
pa_assert(l > 0);
- if (!(p = (getuid() == 0 ? "root" : NULL)) &&
- !(p = getenv("USER")) &&
- !(p = getenv("LOGNAME")) &&
- !(p = getenv("USERNAME"))) {
+ if ((p = (getuid() == 0 ? "root" : NULL)) ||
+ (p = getenv("USER")) ||
+ (p = getenv("LOGNAME")) ||
+ (p = getenv("USERNAME")))
+ {
+ name = pa_strlcpy(s, p, l);
+ } else {
#ifdef HAVE_PWD_H
-#ifdef HAVE_GETPWUID_R
- if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
-#else
- /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
- * that do not support getpwuid_r. */
- if ((r = getpwuid(getuid())) == NULL) {
-#endif
+ if ((r = pa_getpwuid_malloc(getuid())) == NULL) {
pa_snprintf(s, l, "%lu", (unsigned long) getuid());
return s;
}
- p = r->pw_name;
+ name = pa_strlcpy(s, r->pw_name, l);
+ pa_getpwuid_free(r);
#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */
DWORD size = sizeof(buf);
@@ -102,7 +104,7 @@ char *pa_get_user_name(char *s, size_t l) {
return NULL;
}
- p = buf;
+ name = pa_strlcpy(s, buf, l);
#else /* HAVE_PWD_H */
@@ -110,7 +112,7 @@ char *pa_get_user_name(char *s, size_t l) {
#endif /* HAVE_PWD_H */
}
- return pa_strlcpy(s, p, l);
+ return name;
}
char *pa_get_host_name(char *s, size_t l) {
@@ -126,11 +128,10 @@ char *pa_get_host_name(char *s, size_t l) {
}
char *pa_get_home_dir(char *s, size_t l) {
- char *e;
+ char *e, *dir;
#ifdef HAVE_PWD_H
- char buf[1024];
- struct passwd pw, *r;
+ struct passwd *r;
#endif
pa_assert(s);
@@ -143,22 +144,19 @@ char *pa_get_home_dir(char *s, size_t l) {
return pa_strlcpy(s, e, l);
#ifdef HAVE_PWD_H
-
errno = 0;
-#ifdef HAVE_GETPWUID_R
- if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
-#else
- /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
- * that do not support getpwuid_r. */
- if ((r = getpwuid(getuid())) == NULL) {
-#endif
+ if ((r = pa_getpwuid_malloc(getuid())) == NULL) {
if (!errno)
errno = ENOENT;
return NULL;
}
- return pa_strlcpy(s, r->pw_dir, l);
+ dir = pa_strlcpy(s, r->pw_dir, l);
+
+ pa_getpwuid_free(r);
+
+ return dir;
#else /* HAVE_PWD_H */
errno = ENOENT;
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 42cde5b9..2d2bba25 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -40,6 +40,10 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
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)
@@ -60,7 +64,7 @@ pa_cvolume* pa_cvolume_init(pa_cvolume *a) {
a->channels = 0;
for (c = 0; c < PA_CHANNELS_MAX; c++)
- a->values[c] = (pa_volume_t) -1;
+ a->values[c] = PA_VOLUME_INVALID;
return a;
}
@@ -122,7 +126,7 @@ pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, p
}
pa_volume_t pa_cvolume_max(const pa_cvolume *a) {
- pa_volume_t m = 0;
+ pa_volume_t m = PA_VOLUME_MUTED;
unsigned c;
pa_assert(a);
@@ -135,9 +139,23 @@ pa_volume_t pa_cvolume_max(const pa_cvolume *a) {
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 = 0;
- unsigned c, n;
+ pa_volume_t m = PA_VOLUME_MUTED;
+ unsigned c;
pa_assert(a);
@@ -146,7 +164,7 @@ pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, p
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
- for (c = n = 0; c < a->channels; c++) {
+ for (c = 0; c < a->channels; c++) {
if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
continue;
@@ -158,17 +176,48 @@ pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, p
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) {
- return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) * pa_sw_volume_to_linear(b));
+
+ pa_return_val_if_fail(a != PA_VOLUME_INVALID, PA_VOLUME_INVALID);
+ pa_return_val_if_fail(b != PA_VOLUME_INVALID, PA_VOLUME_INVALID);
+
+ /* cbrt((a/PA_VOLUME_NORM)^3*(b/PA_VOLUME_NORM)^3)*PA_VOLUME_NORM = a*b/PA_VOLUME_NORM */
+
+ return (pa_volume_t) (((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) / (uint64_t) PA_VOLUME_NORM);
}
pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) {
- double v = pa_sw_volume_to_linear(b);
- if (v <= 0)
+ pa_return_val_if_fail(a != PA_VOLUME_INVALID, PA_VOLUME_INVALID);
+ pa_return_val_if_fail(b != PA_VOLUME_INVALID, PA_VOLUME_INVALID);
+
+ if (b <= PA_VOLUME_MUTED)
return 0;
- return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) / v);
+ return (pa_volume_t) (((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b);
}
/* Amplitude, not power */
@@ -189,6 +238,8 @@ pa_volume_t pa_sw_volume_from_dB(double dB) {
double pa_sw_volume_to_dB(pa_volume_t v) {
+ pa_return_val_if_fail(v != PA_VOLUME_INVALID, PA_DECIBEL_MININFTY);
+
if (v <= PA_VOLUME_MUTED)
return PA_DECIBEL_MININFTY;
@@ -205,14 +256,19 @@ pa_volume_t pa_sw_volume_from_linear(double v) {
*
* 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) (cbrt(v) * PA_VOLUME_NORM);
+ return (pa_volume_t) lround(cbrt(v) * PA_VOLUME_NORM);
}
double pa_sw_volume_to_linear(pa_volume_t v) {
double f;
+ pa_return_val_if_fail(v != PA_VOLUME_INVALID, 0.0);
+
if (v <= PA_VOLUME_MUTED)
return 0.0;
@@ -246,7 +302,7 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
l -= pa_snprintf(e, l, "%s%u: %3u%%",
first ? "" : " ",
channel,
- (c->values[channel]*100)/PA_VOLUME_NORM);
+ (c->values[channel]*100+PA_VOLUME_NORM/2)/PA_VOLUME_NORM);
e = strchr(e, 0);
first = FALSE;
@@ -261,12 +317,12 @@ char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) {
pa_init_i18n();
- if (v == (pa_volume_t) -1) {
+ if (v == PA_VOLUME_INVALID) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
- pa_snprintf(s, l, "%3u%%", (v*100)/PA_VOLUME_NORM);
+ pa_snprintf(s, l, "%3u%%", (v*100+PA_VOLUME_NORM/2)/PA_VOLUME_NORM);
return s;
}
@@ -311,7 +367,7 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) {
pa_init_i18n();
- if (v == (pa_volume_t) -1) {
+ if (v == PA_VOLUME_INVALID) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
@@ -328,6 +384,7 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), 0);
+ pa_return_val_if_fail(v != PA_VOLUME_INVALID, 0);
for (c = 0; c < a->channels; c++)
if (a->values[c] != v)
@@ -361,6 +418,7 @@ pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a,
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+ pa_return_val_if_fail(b != PA_VOLUME_INVALID, NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b);
@@ -395,6 +453,7 @@ pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, p
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+ pa_return_val_if_fail(b != PA_VOLUME_INVALID, NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b);
@@ -413,7 +472,7 @@ int pa_cvolume_valid(const pa_cvolume *v) {
return 0;
for (c = 0; c < v->channels; c++)
- if (v->values[c] == (pa_volume_t) -1)
+ if (v->values[c] == PA_VOLUME_INVALID)
return 0;
return 1;
@@ -451,8 +510,6 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa
pa_assert(from);
pa_assert(to);
- pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
- pa_return_val_if_fail(pa_channel_map_valid(from), NULL);
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);
@@ -554,8 +611,6 @@ float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) {
pa_assert(v);
pa_assert(map);
- pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f);
- pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_balance(map))
@@ -587,12 +642,10 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo
pa_assert(map);
pa_assert(v);
- pa_assert(new_balance >= -1.0f);
- pa_assert(new_balance <= 1.0f);
- pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
- pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
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;
@@ -633,7 +686,7 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
- pa_return_val_if_fail(max != (pa_volume_t) -1, NULL);
+ pa_return_val_if_fail(max != PA_VOLUME_INVALID, NULL);
t = pa_cvolume_max(v);
@@ -652,8 +705,12 @@ pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map
pa_assert(v);
- pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
- pa_return_val_if_fail(max != (pa_volume_t) -1, NULL);
+ pa_return_val_if_fail(max != PA_VOLUME_INVALID, 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);
@@ -704,8 +761,6 @@ float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) {
pa_assert(v);
pa_assert(map);
- pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f);
- pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_fade(map))
@@ -728,12 +783,10 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float
pa_assert(map);
pa_assert(v);
- pa_assert(new_fade >= -1.0f);
- pa_assert(new_fade <= 1.0f);
- pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
- pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
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;
@@ -781,6 +834,7 @@ pa_cvolume* pa_cvolume_set_position(
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(v != PA_VOLUME_INVALID, NULL);
for (c = 0; c < map->channels; c++)
if (map->map[c] == t) {
@@ -812,3 +866,57 @@ pa_volume_t pa_cvolume_get_position(
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(pa_cvolume *v, pa_volume_t inc) {
+ pa_volume_t m;
+
+ pa_assert(v);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(inc != PA_VOLUME_INVALID, NULL);
+
+ m = pa_cvolume_max(v);
+
+ if (m >= PA_VOLUME_MAX - inc)
+ m = PA_VOLUME_MAX;
+ else
+ m += inc;
+
+ return pa_cvolume_scale(v, m);
+}
+
+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(dec != PA_VOLUME_INVALID, 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
index 05b7ebb4..c964020a 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -106,11 +106,14 @@ typedef uint32_t pa_volume_t;
/** Normal volume (100%, 0 dB) */
#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U)
-/** Muted volume (0%, -inf dB) */
+/** Muted (minimal valid) volume (0%, -inf dB) */
#define PA_VOLUME_MUTED ((pa_volume_t) 0U)
-/** Maximum volume we can store. \since 0.9.15 */
-#define PA_VOLUME_MAX ((pa_volume_t) UINT32_MAX)
+/** Maximum valid volume we can store. \since 0.9.15 */
+#define PA_VOLUME_MAX ((pa_volume_t) UINT32_MAX-1)
+
+/** Special 'invalid' volume. \since 0.9.16 */
+#define PA_VOLUME_INVALID ((pa_volume_t) UINT32_MAX)
/** A structure encapsulating a per-channel volume */
typedef struct pa_cvolume {
@@ -195,6 +198,16 @@ pa_volume_t pa_cvolume_max(const pa_cvolume *a) PA_GCC_PURE;
* \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 TRUE when the passed cvolume structure is valid, FALSE otherwise */
int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE;
@@ -213,11 +226,13 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE
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! */
+ * *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! \since
+ * 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);
@@ -228,11 +243,13 @@ pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a,
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! \since 0.9.13 */
+ * *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! \since
+ * 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);
@@ -326,6 +343,19 @@ pa_cvolume* pa_cvolume_set_position(pa_cvolume *cv, const pa_channel_map *map, p
* 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'. The proportions between
+ * the channels are kept. \since 0.9.16 */
+pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc);
+
+/** Increase the volume passed in by 'inc'. 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/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
index 083d9de2..b0804f79 100644
--- a/src/pulsecore/asyncmsgq.c
+++ b/src/pulsecore/asyncmsgq.c
@@ -26,14 +26,16 @@
#include <unistd.h>
#include <errno.h>
+#include <pulse/xmalloc.h>
+
#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/semaphore.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include <pulsecore/flist.h>
-#include <pulse/xmalloc.h>
#include "asyncmsgq.h"
@@ -76,7 +78,7 @@ static void asyncmsgq_free(pa_asyncmsgq *a) {
struct asyncmsgq_item *i;
pa_assert(a);
- while ((i = pa_asyncq_pop(a->asyncq, 0))) {
+ while ((i = pa_asyncq_pop(a->asyncq, FALSE))) {
pa_assert(!i->semaphore);
@@ -246,7 +248,7 @@ int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code) {
pa_memchunk chunk;
int ret;
- if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, 1) < 0)
+ if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, TRUE) < 0)
return -1;
ret = pa_asyncmsgq_dispatch(o, c, data, offset, &chunk);
@@ -269,7 +271,7 @@ int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
- if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, 0) < 0)
+ if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, FALSE) < 0)
return 0;
pa_asyncmsgq_ref(a);
@@ -323,3 +325,35 @@ int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_
return 0;
}
+
+void pa_asyncmsgq_flush(pa_asyncmsgq *a, pa_bool_t 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);
+ }
+}
+
+pa_bool_t 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
index 1f38207a..1085c2f0 100644
--- a/src/pulsecore/asyncmsgq.h
+++ b/src/pulsecore/asyncmsgq.h
@@ -66,6 +66,8 @@ 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, pa_bool_t run);
+
/* For the reading side */
int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
@@ -76,4 +78,6 @@ 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);
+pa_bool_t pa_asyncmsgq_dispatching(pa_asyncmsgq *a);
+
#endif
diff --git a/src/pulsecore/aupdate.c b/src/pulsecore/aupdate.c
index 56ebb8e5..85b6e00e 100644
--- a/src/pulsecore/aupdate.c
+++ b/src/pulsecore/aupdate.c
@@ -39,6 +39,7 @@ struct pa_aupdate {
pa_atomic_t read_lock;
pa_mutex *write_lock;
pa_semaphore *semaphore;
+ pa_bool_t swapped;
};
pa_aupdate *pa_aupdate_new(void) {
@@ -101,6 +102,8 @@ unsigned pa_aupdate_write_begin(pa_aupdate *a) {
n = (unsigned) pa_atomic_load(&a->read_lock);
+ a->swapped = FALSE;
+
return !WHICH(n);
}
@@ -119,11 +122,16 @@ unsigned pa_aupdate_write_swap(pa_aupdate *a) {
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
index 072e382d..fb38ffa2 100644
--- a/src/pulsecore/aupdate.h
+++ b/src/pulsecore/aupdate.h
@@ -93,6 +93,10 @@ unsigned pa_aupdate_write_swap(pa_aupdate *a);
* 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/authkey.c b/src/pulsecore/authkey.c
index 1e31d076..15613e27 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -36,6 +36,7 @@
#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>
@@ -147,47 +148,46 @@ int pa_authkey_load(const char *path, void *data, size_t length) {
/* If the specified file path starts with / return it, otherwise
* return path prepended with home directory */
-static const char *normalize_path(const char *fn, char *s, size_t l) {
+static char *normalize_path(const char *fn) {
pa_assert(fn);
- pa_assert(s);
- pa_assert(l > 0);
#ifndef OS_IS_WIN32
if (fn[0] != '/') {
#else
if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
#endif
- char homedir[PATH_MAX];
+ char *homedir, *s;
- if (!pa_get_home_dir(homedir, sizeof(homedir)))
+ if (!(homedir = pa_get_home_dir_malloc()))
return NULL;
-#ifndef OS_IS_WIN32
- pa_snprintf(s, l, "%s/%s", homedir, fn);
-#else
- pa_snprintf(s, l, "%s\\%s", homedir, fn);
-#endif
+ s = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", homedir, fn);
+ pa_xfree(homedir);
+
return s;
}
- return fn;
+ return pa_xstrdup(fn);
}
/* Load a cookie from a file in the home directory. If the specified
* path starts with /, use it as absolute path instead. */
int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
- char path[PATH_MAX];
- const char *p;
+ char *p;
+ int ret;
pa_assert(fn);
pa_assert(data);
pa_assert(length > 0);
- if (!(p = normalize_path(fn, path, sizeof(path))))
+ if (!(p = normalize_path(fn)))
return -2;
- return pa_authkey_load(p, data, length);
+ ret = pa_authkey_load(p, data, length);
+ pa_xfree(p);
+
+ return ret;
}
/* Store the specified cookie in the specified cookie file */
@@ -195,14 +195,13 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {
int fd = -1;
int unlock = 0, ret = -1;
ssize_t r;
- char path[PATH_MAX];
- const char *p;
+ char *p;
pa_assert(fn);
pa_assert(data);
pa_assert(length > 0);
- if (!(p = normalize_path(fn, path, sizeof(path))))
+ if (!(p = normalize_path(fn)))
return -2;
if ((fd = open(p, O_RDWR|O_CREAT|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
@@ -232,5 +231,7 @@ finish:
}
}
+ pa_xfree(p);
+
return ret;
}
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index e2c3c066..b57919a4 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -328,7 +328,7 @@ static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf
static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
- char s[256];
+ char bytes[PA_BYTES_SNPRINT_MAX];
const pa_mempool_stat *stat;
unsigned k;
pa_sink *def_sink;
@@ -352,22 +352,22 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_allocated),
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->allocated_size)));
+ pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&stat->allocated_size)));
pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_accumulated),
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->accumulated_size)));
+ pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&stat->accumulated_size)));
pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_imported),
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->imported_size)));
+ pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&stat->imported_size)));
pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_exported),
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->exported_size)));
+ pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&stat->exported_size)));
pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_scache_total_size(c)));
+ 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));
@@ -529,8 +529,8 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
- pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE, TRUE);
+ pa_cvolume_set(&cvolume, 1, volume);
+ pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);
return 0;
}
@@ -571,7 +571,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
return -1;
}
- pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
+ pa_cvolume_set(&cvolume, 1, volume);
pa_sink_input_set_volume(si, &cvolume, TRUE, TRUE);
return 0;
}
@@ -607,7 +607,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
return -1;
}
- pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
+ pa_cvolume_set(&cvolume, 1, volume);
pa_source_set_volume(source, &cvolume, TRUE);
return 0;
}
@@ -1549,7 +1549,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_sink *sink;
pa_source *source;
pa_card *card;
- int nl;
+ pa_bool_t nl;
uint32_t idx;
char txt[256];
time_t now;
@@ -1567,7 +1567,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
#endif
- for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
+ PA_IDXSET_FOREACH(m, c->modules, idx) {
pa_strbuf_printf(buf, "load-module %s", m->name);
@@ -1577,58 +1577,58 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_strbuf_puts(buf, "\n");
}
- nl = 0;
-
- for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
+ nl = FALSE;
+ PA_IDXSET_FOREACH(sink, c->sinks, idx) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
- nl = 1;
+ nl = TRUE;
}
- pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE, 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));
}
- for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
+ nl = FALSE;
+ PA_IDXSET_FOREACH(source, c->sources, idx) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
- nl = 1;
+ nl = TRUE;
}
- pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, FALSE)));
+ 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));
}
- for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) {
+ nl = FALSE;
+ PA_IDXSET_FOREACH(card, c->cards, idx) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
- nl = 1;
+ nl = TRUE;
}
if (card->active_profile)
pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name);
}
- nl = 0;
-
+ nl = FALSE;
if ((sink = pa_namereg_get_default_sink(c))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
- nl = 1;
+ nl = TRUE;
}
+
pa_strbuf_printf(buf, "set-default-sink %s\n", sink->name);
}
if ((source = pa_namereg_get_default_source(c))) {
- if (!nl) {
+ if (!nl)
pa_strbuf_puts(buf, "\n");
- nl = 1;
- }
+
pa_strbuf_printf(buf, "set-default-source %s\n", source->name);
}
@@ -1813,8 +1813,6 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_b
ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
- ret = 0;
-
fail:
if (f)
fclose(f);
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 9395513d..23a57d37 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -232,6 +232,7 @@ char *pa_sink_list_to_string(pa_core *c) {
"\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%s%s\n"
"\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
@@ -262,10 +263,11 @@ char *pa_sink_list_to_string(pa_core *c) {
sink->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
sink->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
sink->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
- pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE, FALSE)),
+ sink->priority,
+ pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
- sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE, FALSE)) : "",
- pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE, FALSE), &sink->channel_map),
+ sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "",
+ pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map),
pa_volume_snprint(v, sizeof(v), sink->base_volume),
sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "",
@@ -296,7 +298,7 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf_printf(
s,
"\tfixed latency: %0.2f ms\n",
- (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC);
+ (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);
@@ -356,6 +358,7 @@ char *pa_source_list_to_string(pa_core *c) {
"\tflags: %s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsuspend cause: %s%s%s%s\n"
+ "\tpriority: %u\n"
"\tvolume: %s%s%s\n"
"\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
@@ -383,6 +386,7 @@ char *pa_source_list_to_string(pa_core *c) {
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(cv, sizeof(cv), pa_source_get_volume(source, FALSE)),
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "",
@@ -415,7 +419,7 @@ char *pa_source_list_to_string(pa_core *c) {
pa_strbuf_printf(
s,
"\tfixed latency: %0.2f ms\n",
- (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC);
+ (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);
@@ -482,7 +486,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
s,
" index: %u\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsource: %u <%s>\n"
"\tcurrent latency: %0.2f ms\n"
@@ -501,7 +505,8 @@ char *pa_source_output_list_to_string(pa_core *c) {
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_FAIL_ON_SUSPEND ? "FAIL_ON_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 " : "",
state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
(double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
@@ -564,7 +569,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
s,
" index: %u\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsink: %u <%s>\n"
"\tvolume: %s\n"
@@ -587,7 +592,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
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_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
+ i->flags & PA_SINK_INPUT_NO_CREATE_ON_SUSPEND ? "NO_CREATE_SUSPEND " : "",
+ i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "",
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), &v),
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index 2dc9a223..dd4a99ee 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -113,7 +113,7 @@ static int parse_line(const char *filename, unsigned line, char **section, const
return 0;
if (pa_startswith(b, ".include ")) {
- char *path, *fn;
+ char *path = NULL, *fn;
int r;
fn = strip(b+9);
@@ -278,6 +278,30 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *sectio
return 0;
}
+int pa_config_parse_not_bool(
+ const char *filename, unsigned line,
+ const char *section,
+ const char *lvalue, const char *rvalue,
+ void *data, void *userdata) {
+
+ int k;
+ pa_bool_t *b = data;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if ((k = pa_parse_boolean(rvalue)) < 0) {
+ pa_log("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
+ return -1;
+ }
+
+ *b = !k;
+
+ return 0;
+}
+
int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
char **s = data;
diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h
index 08e17ca7..c6c8a148 100644
--- a/src/pulsecore/conf-parser.h
+++ b/src/pulsecore/conf-parser.h
@@ -47,6 +47,7 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *section
int pa_config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_not_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
#endif
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 4c5a4b26..1fb81d0d 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -335,12 +335,12 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
pass_volume = TRUE;
- if (e->volume_is_set && volume != (pa_volume_t) -1) {
+ if (e->volume_is_set && volume != PA_VOLUME_INVALID) {
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 (volume != (pa_volume_t) -1)
+ else if (volume != PA_VOLUME_INVALID)
pa_cvolume_set(&r, e->sample_spec.channels, volume);
else
pass_volume = FALSE;
@@ -494,13 +494,14 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
struct dirent *e;
while ((e = readdir(dir))) {
- char p[PATH_MAX];
+ char *p;
if (e->d_name[0] == '.')
continue;
- pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
+ p = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", pathname, e->d_name);
add_file(c, p);
+ pa_xfree(p);
}
closedir(dir);
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 04e7eb24..7a9f458c 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -101,6 +101,10 @@
#include "rtkit.h"
#endif
+#ifdef __linux__
+#include <sys/personality.h>
+#endif
+
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/utf8.h>
@@ -111,6 +115,8 @@
#include <pulsecore/macro.h>
#include <pulsecore/thread.h>
#include <pulsecore/strbuf.h>
+#include <pulsecore/usergroup.h>
+#include <pulsecore/strlist.h>
#include "core-util.h"
@@ -119,6 +125,8 @@
#define MSG_NOSIGNAL 0
#endif
+static pa_strlist *recorded_env = NULL;
+
#ifdef OS_IS_WIN32
#define PULSE_ROOTENV "PULSE_ROOT"
@@ -552,12 +560,20 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) {
/* 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);
- strncpy(b, s, l);
- b[l-1] = 0;
+ k = strlen(s);
+
+ if (k > l-1)
+ k = l-1;
+
+ memcpy(b, s, k);
+ b[k] = 0;
+
return b;
}
@@ -575,13 +591,13 @@ static int set_scheduler(int rtprio) {
sp.sched_priority = rtprio;
#ifdef SCHED_RESET_ON_FORK
- if ((r = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp)) == 0) {
+ 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 ((r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp)) == 0) {
+ if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) {
pa_log_debug("SCHED_RR worked.");
return 0;
}
@@ -596,6 +612,11 @@ static int set_scheduler(int rtprio) {
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_realtime(bus, 0, rtprio);
dbus_connection_unref(bus);
@@ -664,6 +685,11 @@ static int set_nice(int nice_level) {
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_unref(bus);
@@ -760,7 +786,6 @@ int pa_match(const char *expr, const char *v) {
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
const char *expr;
- int r;
pa_assert(v);
/* First we check language independant */
@@ -772,12 +797,12 @@ int pa_parse_boolean(const char *v) {
/* And then we check language dependant */
if ((expr = nl_langinfo(YESEXPR)))
if (expr[0])
- if ((r = pa_match(expr, v)) > 0)
+ if (pa_match(expr, v) > 0)
return 1;
if ((expr = nl_langinfo(NOEXPR)))
if (expr[0])
- if ((r = pa_match(expr, v)) > 0)
+ if (pa_match(expr, v) > 0)
return 0;
errno = EINVAL;
@@ -957,54 +982,24 @@ fail:
/* Check whether the specified GID and the group name match */
static int is_group(gid_t gid, const char *name) {
- struct group group, *result = NULL;
- long n;
- void *data;
+ struct group *group = NULL;
int r = -1;
-#ifdef HAVE_GETGRGID_R
-#ifdef _SC_GETGR_R_SIZE_MAX
- n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
- n = -1;
-#endif
- if (n <= 0)
- n = 512;
-
- data = pa_xmalloc((size_t) n);
-
errno = 0;
- if (getgrgid_r(gid, &group, data, (size_t) n, &result) < 0 || !result) {
- pa_log("getgrgid_r(%u): %s", (unsigned) gid, pa_cstrerror(errno));
-
+ if (!(group = pa_getgrgid_malloc(gid)))
+ {
if (!errno)
errno = ENOENT;
- goto finish;
- }
-
- r = strcmp(name, result->gr_name) == 0;
-
-finish:
- pa_xfree(data);
-#else
- /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
- * support getgrgid_r. */
-
- errno = 0;
- if (!(result = getgrgid(gid))) {
- pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno));
-
- if (!errno)
- errno = ENOENT;
+ pa_log("pa_getgrgid_malloc(%u): %s", gid, pa_cstrerror(errno));
goto finish;
}
- r = strcmp(name, result->gr_name) == 0;
+ r = strcmp(name, group->gr_name) == 0;
finish:
-#endif
+ pa_getgrgid_free(group);
return r;
}
@@ -1053,38 +1048,12 @@ finish:
/* Check whether the specifc user id is a member of the specified group */
int pa_uid_in_group(uid_t uid, const char *name) {
- char *g_buf, *p_buf;
- long g_n, p_n;
- struct group grbuf, *gr;
+ struct group *group = NULL;
char **i;
int r = -1;
-#ifdef _SC_GETGR_R_SIZE_MAX
- g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
- g_n = -1;
-#endif
- if (g_n <= 0)
- g_n = 512;
-
- g_buf = pa_xmalloc((size_t) g_n);
-
-#ifdef _SC_GETPW_R_SIZE_MAX
- p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
-#else
- p_n = -1;
-#endif
- if (p_n <= 0)
- p_n = 512;
-
- p_buf = pa_xmalloc((size_t) p_n);
-
errno = 0;
-#ifdef HAVE_GETGRNAM_R
- if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
-#else
- if (!(gr = getgrnam(name)))
-#endif
+ if (!(group = pa_getgrnam_malloc(name)))
{
if (!errno)
errno = ENOENT;
@@ -1092,25 +1061,24 @@ int pa_uid_in_group(uid_t uid, const char *name) {
}
r = 0;
- for (i = gr->gr_mem; *i; i++) {
- struct passwd pwbuf, *pw;
+ for (i = group->gr_mem; *i; i++) {
+ struct passwd *pw = NULL;
-#ifdef HAVE_GETPWNAM_R
- if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
-#else
- if (!(pw = getpwnam(*i)))
-#endif
+ errno = 0;
+ if (!(pw = pa_getpwnam_malloc(*i)))
continue;
- if (pw->pw_uid == uid) {
+ if (pw->pw_uid == uid)
r = 1;
+
+ pa_getpwnam_free(pw);
+
+ if (r == 1)
break;
- }
}
finish:
- pa_xfree(g_buf);
- pa_xfree(p_buf);
+ pa_getgrnam_free(group);
return r;
}
@@ -1118,26 +1086,10 @@ finish:
/* Get the GID of a gfiven group, return (gid_t) -1 on failure. */
gid_t pa_get_gid_of_group(const char *name) {
gid_t ret = (gid_t) -1;
- char *g_buf;
- long g_n;
- struct group grbuf, *gr;
-
-#ifdef _SC_GETGR_R_SIZE_MAX
- g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
- g_n = -1;
-#endif
- if (g_n <= 0)
- g_n = 512;
-
- g_buf = pa_xmalloc((size_t) g_n);
+ struct group *gr = NULL;
errno = 0;
-#ifdef HAVE_GETGRNAM_R
- if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
-#else
- if (!(gr = getgrnam(name)))
-#endif
+ if (!(gr = pa_getgrnam_malloc(name)))
{
if (!errno)
errno = ENOENT;
@@ -1147,7 +1099,7 @@ gid_t pa_get_gid_of_group(const char *name) {
ret = gr->gr_gid;
finish:
- pa_xfree(g_buf);
+ pa_getgrnam_free(gr);
return ret;
}
@@ -1242,7 +1194,7 @@ char* pa_strip_nl(char *s) {
/* Create a temporary lock file and lock it. */
int pa_lock_lockfile(const char *fn) {
- int fd = -1;
+ int fd;
pa_assert(fn);
for (;;) {
@@ -1285,8 +1237,6 @@ int pa_lock_lockfile(const char *fn) {
fd = -1;
goto fail;
}
-
- fd = -1;
}
return fd;
@@ -1328,26 +1278,32 @@ int pa_unlock_lockfile(const char *fn, int fd) {
}
static char *get_pulse_home(void) {
- char h[PATH_MAX];
+ char *h;
struct stat st;
+ char *ret = NULL;
- if (!pa_get_home_dir(h, sizeof(h))) {
+ if (!(h = pa_get_home_dir_malloc())) {
pa_log_error("Failed to get home directory.");
return NULL;
}
if (stat(h, &st) < 0) {
pa_log_error("Failed to stat home directory %s: %s", h, pa_cstrerror(errno));
- return NULL;
+ goto finish;
}
if (st.st_uid != getuid()) {
pa_log_error("Home directory %s not ours.", h);
errno = EACCES;
- return NULL;
+ goto finish;
}
- return pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+ ret = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+
+finish:
+ pa_xfree(h);
+
+ return ret;
}
char *pa_get_state_dir(void) {
@@ -1372,6 +1328,50 @@ char *pa_get_state_dir(void) {
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;
+}
+
+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"
@@ -1481,7 +1481,7 @@ char *pa_get_runtime_dir(void) {
goto fail;
}
- k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s:runtime", d, mid);
+ k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-runtime", d, mid);
pa_xfree(d);
pa_xfree(mid);
@@ -1626,14 +1626,15 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
if (local) {
const char *e;
char *lfn;
- char h[PATH_MAX];
+ char *h;
FILE *f;
if ((e = getenv("PULSE_CONFIG_PATH")))
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
- else if (pa_get_home_dir(h, sizeof(h)))
+ else if ((h = pa_get_home_dir_malloc())) {
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
- else
+ pa_xfree(h);
+ } else
return NULL;
#ifdef OS_IS_WIN32
@@ -1713,13 +1714,14 @@ char *pa_find_config_file(const char *global, const char *local, const char *env
if (local) {
const char *e;
char *lfn;
- char h[PATH_MAX];
+ char *h;
if ((e = getenv("PULSE_CONFIG_PATH")))
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
- else if (pa_get_home_dir(h, sizeof(h)))
+ else if ((h = pa_get_home_dir_malloc())) {
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
- else
+ pa_xfree(h);
+ } else
return NULL;
#ifdef OS_IS_WIN32
@@ -1885,17 +1887,17 @@ char *pa_make_path_absolute(const char *p) {
static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) {
char *rtp;
- if (pa_is_path_absolute(fn))
- return pa_xstrdup(fn);
-
rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
- if (!rtp)
- return NULL;
-
if (fn) {
char *r;
+ if (pa_is_path_absolute(fn))
+ return pa_xstrdup(fn);
+
+ if (!rtp)
+ return NULL;
+
if (prependmid) {
char *mid;
@@ -1904,7 +1906,7 @@ static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) {
return NULL;
}
- r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s:%s", rtp, mid, fn);
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-%s", rtp, mid, fn);
pa_xfree(mid);
} else
r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
@@ -2231,7 +2233,7 @@ int pa_close_all(int except_fd, ...) {
va_end(ap);
r = pa_close_allv(p);
- free(p);
+ pa_xfree(p);
return r;
}
@@ -2402,7 +2404,7 @@ int pa_reset_sigs(int except, ...) {
p[i++] = except;
while ((sig = va_arg(ap, int)) >= 0)
- sig = p[i++];
+ p[i++] = sig;
}
p[i] = -1;
@@ -2459,9 +2461,38 @@ void pa_set_env(const char *key, const char *value) {
pa_assert(key);
pa_assert(value);
+ /* This is not thread-safe */
+
putenv(pa_sprintf_malloc("%s=%s", key, value));
}
+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;
+
+ unsetenv(s);
+ pa_xfree(s);
+ }
+}
+
pa_bool_t pa_in_system_mode(void) {
const char *e;
@@ -2779,3 +2810,47 @@ char* pa_maybe_prefix_path(const char *path, const char *prefix) {
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) {
+
+#ifdef __linux__
+ if (personality(PER_LINUX) < 0)
+ pa_log_warn("Uh, personality() failed: %s", pa_cstrerror(errno));
+#endif
+
+}
+
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+
+pa_bool_t pa_run_from_build_tree(void) {
+ char *rp;
+ pa_bool_t b = FALSE;
+
+ /* We abuse __OPTIMIZE__ as a check whether we are a debug build
+ * or not. */
+
+ if ((rp = pa_readlink("/proc/self/exe"))) {
+ b = pa_startswith(rp, PA_BUILDDIR);
+ pa_xfree(rp);
+ }
+
+ return b;
+}
+
+#endif
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 96a0480a..ccc9a38e 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -126,6 +126,8 @@ 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);
+char *pa_get_binary_name_malloc(void);
char *pa_runtime_path(const char *fn);
char *pa_state_path(const char *fn, pa_bool_t prepend_machine_id);
@@ -193,6 +195,8 @@ 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_set_env_and_record(const char *key, const char *value);
+void pa_unset_env_recorded(void);
pa_bool_t pa_in_system_mode(void);
@@ -236,4 +240,13 @@ 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);
+
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+pa_bool_t pa_run_from_build_tree(void);
+#endif
+
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index f5eb8352..f0726453 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -47,7 +47,7 @@
#include "core.h"
-static PA_DEFINE_CHECK_TYPE(pa_core, pa_msgobject);
+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);
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index e7abd61b..c1002f93 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -83,7 +83,6 @@ typedef enum pa_core_hook {
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_SET_VOLUME,
PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
@@ -166,7 +165,7 @@ struct pa_core {
pa_hook hooks[PA_CORE_HOOK_MAX];
};
-PA_DECLARE_CLASS(pa_core);
+PA_DECLARE_PUBLIC_CLASS(pa_core);
#define PA_CORE(o) pa_core_cast(o)
enum {
diff --git a/src/pulsecore/cpu-arm.c b/src/pulsecore/cpu-arm.c
new file mode 100644
index 00000000..453b7848
--- /dev/null
+++ b/src/pulsecore/cpu-arm.c
@@ -0,0 +1,139 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pulse/xmalloc.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 = open("/proc/cpuinfo", O_RDONLY)) < 0) {
+ pa_xfree(cpuinfo);
+ return NULL;
+ }
+
+ if ((n = pa_read(fd, cpuinfo, MAX_BUFFER-1)) < 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_init_arm (void) {
+#if defined (__arm__)
+#if defined (__linux__)
+ char *cpuinfo, *line;
+ int arch;
+ pa_cpu_arm_flag_t flags = 0;
+
+ /* 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;
+ }
+
+ /* 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"))) {
+ char *state = NULL, *current;
+
+ while ((current = pa_split_spaces (line, &state))) {
+ if (!strcmp (current, "vfp"))
+ flags |= PA_CPU_ARM_VFP;
+ else if (!strcmp (current, "edsp"))
+ flags |= PA_CPU_ARM_EDSP;
+ else if (!strcmp (current, "neon"))
+ flags |= PA_CPU_ARM_NEON;
+ else if (!strcmp (current, "vfpv3"))
+ flags |= PA_CPU_ARM_VFPV3;
+
+ pa_xfree(current);
+ }
+ }
+ pa_xfree(cpuinfo);
+
+ pa_log_info ("CPU flags: %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 " : "");
+#else /* defined (__linux__) */
+ pa_log ("ARM cpu features not yet supported on this OS");
+#endif /* defined (__linux__) */
+
+ if (flags & PA_CPU_ARM_V6)
+ pa_volume_func_init_arm (flags);
+#endif /* defined (__arm__) */
+}
diff --git a/src/daemon/polkit.h b/src/pulsecore/cpu-arm.h
index 018f6ef1..3ccd0708 100644
--- a/src/daemon/polkit.h
+++ b/src/pulsecore/cpu-arm.h
@@ -1,10 +1,11 @@
-#ifndef foopolkithfoo
-#define foopolkithfoo
+#ifndef foocpuarmhfoo
+#define foocpuarmhfoo
/***
This file is part of PulseAudio.
- Copyright 2007 Lennart Poettering
+ 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
@@ -22,6 +23,20 @@
USA.
***/
-int pa_polkit_check(const char *action);
+#include <stdint.h>
-#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_flag_t;
+
+void pa_cpu_init_arm (void);
+
+/* some optimized functions */
+void pa_volume_func_init_arm(pa_cpu_arm_flag_t flags);
+
+#endif /* foocpuarmhfoo */
diff --git a/src/pulsecore/cpu-x86.c b/src/pulsecore/cpu-x86.c
new file mode 100644
index 00000000..f194a608
--- /dev/null
+++ b/src/pulsecore/cpu-x86.c
@@ -0,0 +1,125 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#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_init_x86 (void) {
+#if defined (__i386__) || defined (__amd64__)
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t level;
+ pa_cpu_x86_flag_t 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<<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",
+ (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 " : "");
+
+ /* 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);
+ }
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/cpu-x86.h b/src/pulsecore/cpu-x86.h
new file mode 100644
index 00000000..b40eb5ce
--- /dev/null
+++ b/src/pulsecore/cpu-x86.h
@@ -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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <stdint.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_flag_t;
+
+void pa_cpu_init_x86 (void);
+
+
+#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/database-simple.c b/src/pulsecore/database-simple.c
new file mode 100644
index 00000000..1f4caf71
--- /dev/null
+++ b/src/pulsecore/database-simple.c
@@ -0,0 +1,510 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.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;
+ pa_bool_t 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;
+ pa_bool_t 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, pa_bool_t for_write) {
+ FILE *f;
+ char *path;
+ simple_data *db;
+
+ pa_assert(fn);
+
+ path = pa_sprintf_malloc("%s."CANONICAL_HOST".simple", fn);
+ errno = 0;
+
+ f = fopen(path, "r");
+
+ if (f || errno == ENOENT) { /* file not found is ok */
+ db = pa_xnew0(simple_data, 1);
+ db->map = pa_hashmap_new(hash_func, compare_func);
+ 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_database_clear(database);
+ pa_xfree(db->filename);
+ pa_xfree(db->tmp_filename);
+ pa_hashmap_free(db->map, NULL, NULL);
+ 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, pa_bool_t 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 {
+ /* wont'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;
+ entry *e;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ e = pa_hashmap_remove(db->map, key);
+ if (!e)
+ return -1;
+
+ free_entry(e);
+
+ return 0;
+}
+
+int pa_database_clear(pa_database *database) {
+ simple_data *db = (simple_data*)database;
+ entry *e;
+
+ pa_assert(db);
+
+ while ((e = pa_hashmap_steal_first(db->map)))
+ free_entry(e);
+
+ 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;
+ pa_bool_t 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 = fopen(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/flist.c b/src/pulsecore/flist.c
index 6fb944f9..7e5ee244 100644
--- a/src/pulsecore/flist.c
+++ b/src/pulsecore/flist.c
@@ -130,15 +130,22 @@ void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
}
int pa_flist_push(pa_flist*l, void *p) {
- unsigned idx, n, len;
+ unsigned idx, n;
pa_atomic_ptr_t*cells;
+#ifdef PROFILE
+ unsigned len;
+#endif
pa_assert(l);
pa_assert(p);
cells = PA_FLIST_CELLS(l);
- n = len = l->size + N_EXTRA_SCAN - (unsigned) pa_atomic_load(&l->length);
+ n = l->size + N_EXTRA_SCAN - (unsigned) pa_atomic_load(&l->length);
+
+#ifdef PROFILE
+ len = n;
+#endif
_Y;
idx = reduce(l, (unsigned) pa_atomic_load(&l->write_idx));
@@ -171,14 +178,21 @@ int pa_flist_push(pa_flist*l, void *p) {
}
void* pa_flist_pop(pa_flist*l) {
- unsigned idx, len, n;
+ unsigned idx, n;
pa_atomic_ptr_t *cells;
+#ifdef PROFILE
+ unsigned len;
+#endif
pa_assert(l);
cells = PA_FLIST_CELLS(l);
- n = len = (unsigned) pa_atomic_load(&l->length) + N_EXTRA_SCAN;
+ n = (unsigned) pa_atomic_load(&l->length) + N_EXTRA_SCAN;
+
+#ifdef PROFILE
+ len = n;
+#endif
_Y;
idx = reduce(l, (unsigned) pa_atomic_load(&l->read_idx));
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index a00116d1..d9b9917d 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -97,7 +97,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
hook->n_firing ++;
- for (slot = hook->slots; slot; slot = slot->next) {
+ PA_LLIST_FOREACH(slot, hook->slots) {
if (slot->dead)
continue;
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
index 58b51c68..27f174a9 100644
--- a/src/pulsecore/llist.h
+++ b/src/pulsecore/llist.h
@@ -107,4 +107,7 @@
#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
index 4436974d..c0df7938 100644
--- a/src/pulsecore/lock-autospawn.c
+++ b/src/pulsecore/lock-autospawn.c
@@ -55,10 +55,16 @@ static pa_mutex *mutex;
static unsigned n_ref = 0;
static int lock_fd = -1;
static pa_mutex *lock_fd_mutex = NULL;
-static pa_bool_t taken = FALSE;
-static pa_thread *thread;
+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) {
@@ -67,15 +73,16 @@ static int ref(void) {
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 < 0);
pa_assert(!lock_fd_mutex);
- pa_assert(!taken);
+ 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);
@@ -83,14 +90,14 @@ static int ref(void) {
if (pipe(pipe_fd) < 0)
return -1;
- lock_fd_mutex = pa_mutex_new(FALSE, FALSE);
-
pa_make_fd_cloexec(pipe_fd[0]);
pa_make_fd_cloexec(pipe_fd[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;
}
@@ -107,15 +114,18 @@ static void unref(pa_bool_t after_fork) {
if (n_ref > 0)
return;
- pa_assert(!taken);
-
if (thread) {
pa_thread_free(thread);
thread = NULL;
}
pa_mutex_lock(lock_fd_mutex);
- if (lock_fd >= 0) {
+
+ pa_assert(state != STATE_TAKEN);
+
+ if (state == STATE_OWNING) {
+
+ pa_assert(lock_fd >= 0);
if (after_fork)
pa_close(lock_fd);
@@ -127,10 +137,12 @@ static void unref(pa_bool_t after_fork) {
pa_unlock_lockfile(lf, lock_fd);
pa_xfree(lf);
-
- lock_fd = -1;
}
}
+
+ lock_fd = -1;
+ state = STATE_IDLE;
+
pa_mutex_unlock(lock_fd_mutex);
pa_mutex_free(lock_fd_mutex);
@@ -205,15 +217,24 @@ static void thread_func(void *u) {
if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
pa_log_warn(_("Cannot access autospawn lock."));
- goto finish;
+ goto fail;
}
if ((fd = pa_lock_lockfile(lf)) < 0)
- goto finish;
+ goto fail;
pa_mutex_lock(lock_fd_mutex);
- pa_assert(lock_fd < 0);
+ 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:
@@ -238,12 +259,10 @@ static void create_mutex(void) {
}
static void destroy_mutex(void) {
-
if (mutex)
pa_mutex_free(mutex);
}
-
int pa_autospawn_lock_init(void) {
int ret = -1;
@@ -273,13 +292,18 @@ int pa_autospawn_lock_acquire(pa_bool_t block) {
empty_pipe();
- if (lock_fd >= 0 && !taken) {
- taken = TRUE;
+ if (state == STATE_OWNING) {
+ state = STATE_TAKEN;
ret = 1;
break;
}
- if (lock_fd < 0)
+ if (state == STATE_FAILED) {
+ ret = -1;
+ break;
+ }
+
+ if (state == STATE_IDLE)
if (start_thread() < 0)
break;
@@ -310,8 +334,8 @@ void pa_autospawn_lock_release(void) {
pa_mutex_lock(mutex);
pa_assert(n_ref >= 1);
- pa_assert(taken);
- taken = FALSE;
+ pa_assert(state == STATE_TAKEN);
+ state = STATE_OWNING;
ping();
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index cf662510..bffcc264 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -59,39 +59,42 @@
#endif
/* Rounds down */
-static inline void* pa_align_ptr(const void *p) {
- return (void*) (((size_t) p) & ~(sizeof(void*)-1));
+static inline void* PA_ALIGN_PTR(const void *p) {
+ return (void*) (((size_t) p) & ~(sizeof(void*) - 1));
}
-#define PA_ALIGN_PTR(x) (pa_align_ptr(x))
/* Rounds up */
-static inline size_t pa_align(size_t l) {
- return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*));
+static inline size_t PA_ALIGN(size_t l) {
+ return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1));
}
-#define PA_ALIGN(x) (pa_align(x))
/* Rounds down */
-static inline void* pa_page_align_ptr(const void *p) {
- return (void*) (((size_t) p) & ~(PA_PAGE_SIZE-1));
+static inline void* PA_PAGE_ALIGN_PTR(const void *p) {
+ return (void*) (((size_t) p) & ~(PA_PAGE_SIZE - 1));
}
-#define PA_PAGE_ALIGN_PTR(x) (pa_page_align_ptr(x))
/* Rounds up */
-static inline size_t pa_page_align(size_t l) {
- return ((l + PA_PAGE_SIZE - 1) / PA_PAGE_SIZE) * PA_PAGE_SIZE;
+static inline size_t PA_PAGE_ALIGN(size_t l) {
+ return (l + PA_PAGE_SIZE - 1) & ~(PA_PAGE_SIZE - 1);
}
-#define PA_PAGE_ALIGN(x) (pa_page_align(x))
#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
-/* The users of PA_MIN and PA_MAX should be aware that these macros on
- * non-GCC executed code with side effects twice. It is thus
- * considered misuse to use code with side effects as arguments to MIN
- * and MAX. */
+#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
+
+/* 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); \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a > _b ? _a : _b; \
})
@@ -101,7 +104,8 @@ static inline size_t pa_page_align(size_t l) {
#ifdef __GNUC__
#define PA_MIN(a,b) \
- __extension__ ({ typeof(a) _a = (a); \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a < _b ? _a : _b; \
})
@@ -111,7 +115,8 @@ static inline size_t pa_page_align(size_t l) {
#ifdef __GNUC__
#define PA_CLAMP(x, low, high) \
- __extension__ ({ typeof(x) _x = (x); \
+ __extension__ ({ \
+ typeof(x) _x = (x); \
typeof(low) _low = (low); \
typeof(high) _high = (high); \
((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
@@ -122,7 +127,8 @@ static inline size_t pa_page_align(size_t l) {
#ifdef __GNUC__
#define PA_CLAMP_UNLIKELY(x, low, high) \
- __extension__ ({ typeof(x) _x = (x); \
+ __extension__ ({ \
+ typeof(x) _x = (x); \
typeof(low) _low = (low); \
typeof(high) _high = (high); \
(PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
@@ -135,6 +141,39 @@ static inline size_t pa_page_align(size_t l) {
* 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
+
/* This type is not intended to be used in exported APIs! Use classic "int" there! */
#ifdef HAVE_STD_BOOL
typedef _Bool pa_bool_t;
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index 2c3f98a5..0e40d12b 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -96,6 +96,7 @@ struct pa_memimport_segment {
unsigned n_blocks;
};
+/* A collection of multiple segments */
struct pa_memimport {
pa_mutex *mutex;
@@ -257,7 +258,8 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx));
if (!slot) {
- pa_log_debug("Pool full");
+ if (pa_log_ratelimit())
+ pa_log_debug("Pool full");
pa_atomic_inc(&p->stat.n_pool_full);
return NULL;
}
@@ -514,9 +516,9 @@ static void memblock_free(pa_memblock *b) {
pa_mutex_lock(import->mutex);
- pa_hashmap_remove(
- import->blocks,
- PA_UINT32_TO_PTR(b->per_type.imported.id));
+ 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)
@@ -677,9 +679,9 @@ static void memblock_replace_import(pa_memblock *b) {
pa_mutex_lock(import->mutex);
- pa_hashmap_remove(
- import->blocks,
- PA_UINT32_TO_PTR(b->per_type.imported.id));
+ pa_assert_se(pa_hashmap_remove(
+ import->blocks,
+ PA_UINT32_TO_PTR(b->per_type.imported.id)));
memblock_make_local(b);
@@ -692,7 +694,7 @@ static void memblock_replace_import(pa_memblock *b) {
pa_mempool* pa_mempool_new(pa_bool_t shared, size_t size) {
pa_mempool *p;
- char t1[64], t2[64];
+ char t1[PA_BYTES_SNPRINT_MAX], t2[PA_BYTES_SNPRINT_MAX];
p = pa_xnew(pa_mempool, 1);
@@ -960,6 +962,11 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
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;
@@ -989,12 +996,11 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
seg->n_blocks++;
+ stat_add(b);
+
finish:
pa_mutex_unlock(i->mutex);
- if (b)
- stat_add(b);
-
return b;
}
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index 77f9efc9..32758be3 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -692,6 +692,12 @@ size_t pa_memblockq_get_minreq(pa_memblockq *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);
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index 146d261b..587c364b 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -141,6 +141,9 @@ 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);
diff --git a/src/pulsecore/memtrap.c b/src/pulsecore/memtrap.c
index 7d917450..373872c1 100644
--- a/src/pulsecore/memtrap.c
+++ b/src/pulsecore/memtrap.c
@@ -65,7 +65,7 @@ pa_bool_t pa_memtrap_is_good(pa_memtrap *m) {
}
static void sigsafe_error(const char *s) {
- write(STDERR_FILENO, s, strlen(s));
+ (void) write(STDERR_FILENO, s, strlen(s));
}
static void signal_handler(int sig, siginfo_t* si, void *data) {
@@ -200,13 +200,13 @@ pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) {
goto unlock;
memtrap_unlink(m, j);
- j = pa_aupdate_write_swap(aupdate);
+ pa_aupdate_write_swap(aupdate);
m->start = (void*) start;
m->size = size;
pa_atomic_store(&m->bad, 0);
- j = pa_aupdate_write_swap(aupdate);
+ pa_assert_se(pa_aupdate_write_swap(aupdate) == j);
memtrap_link(m, j);
unlock:
diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
index 6a2a612d..075a28c5 100644
--- a/src/pulsecore/msgobject.c
+++ b/src/pulsecore/msgobject.c
@@ -26,22 +26,22 @@
#include "msgobject.h"
-PA_DEFINE_CHECK_TYPE(pa_msgobject, pa_object);
+PA_DEFINE_PUBLIC_CLASS(pa_msgobject, pa_object);
-pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_name)) {
pa_msgobject *o;
pa_assert(size > sizeof(pa_msgobject));
- pa_assert(type_name);
+ pa_assert(type_id);
if (!check_type)
check_type = pa_msgobject_check_type;
- pa_assert(check_type(type_name));
- pa_assert(check_type("pa_object"));
- pa_assert(check_type("pa_msgobject"));
+ 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_name, check_type));
+ 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
index a35a23b5..ee0ec1ed 100644
--- a/src/pulsecore/msgobject.h
+++ b/src/pulsecore/msgobject.h
@@ -38,15 +38,13 @@ struct pa_msgobject {
int (*process_msg)(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
};
-pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_name));
-int pa_msgobject_check_type(const char *type);
-
-#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), #type, type##_check_type))
+#define pa_msgobject_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_CLASS(pa_msgobject);
+PA_DECLARE_PUBLIC_CLASS(pa_msgobject);
#endif
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index 9df2f583..37755777 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -57,6 +57,8 @@ static pa_bool_t is_valid_char(char c) {
pa_bool_t pa_namereg_is_valid_name(const char *name) {
const char *c;
+ pa_assert(name);
+
if (*name == 0)
return FALSE;
@@ -70,6 +72,25 @@ pa_bool_t pa_namereg_is_valid_name(const char *name) {
return TRUE;
}
+pa_bool_t 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;
@@ -191,7 +212,6 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)))
return s->monitor_source;
-
}
if (!name)
@@ -223,6 +243,9 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) {
pa_assert(c);
+ if (s && !PA_SINK_IS_LINKED(pa_sink_get_state(s)))
+ return NULL;
+
if (c->default_sink != s) {
c->default_sink = s;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
@@ -234,6 +257,9 @@ pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) {
pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) {
pa_assert(c);
+ if (s && !PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+ return NULL;
+
if (c->default_source != s) {
c->default_source = s;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
@@ -243,34 +269,57 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) {
}
pa_sink *pa_namereg_get_default_sink(pa_core *c) {
- pa_sink *s;
+ pa_sink *s, *best = NULL;
+ uint32_t idx;
pa_assert(c);
- if (c->default_sink)
+ if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink)))
return c->default_sink;
- if ((s = pa_idxset_first(c->sinks, NULL)))
- return pa_namereg_set_default_sink(c, s);
+ PA_IDXSET_FOREACH(s, c->sinks, idx)
+ if (PA_SINK_IS_LINKED(pa_sink_get_state(s)))
+ if (!best || s->priority > best->priority)
+ best = s;
+
+ if (best)
+ return pa_namereg_set_default_sink(c, best);
return NULL;
}
pa_source *pa_namereg_get_default_source(pa_core *c) {
- pa_source *s;
+ pa_source *s, *best = NULL;
uint32_t idx;
pa_assert(c);
- if (c->default_source)
+ if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source)))
return c->default_source;
- for (s = PA_SOURCE(pa_idxset_first(c->sources, &idx)); s; s = PA_SOURCE(pa_idxset_next(c->sources, &idx)))
- if (!s->monitor_of)
- return pa_namereg_set_default_source(c, s);
-
- if ((s = pa_idxset_first(c->sources, NULL)))
- return pa_namereg_set_default_source(c, s);
+ /* First, try to find one that isn't a monitor */
+ PA_IDXSET_FOREACH(s, c->sources, idx)
+ if (!s->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+ if (!best ||
+ s->priority > best->priority)
+ best = s;
+
+ if (best)
+ return pa_namereg_set_default_source(c, best);
+
+ /* Then, fallback to a monitor */
+ PA_IDXSET_FOREACH(s, c->sources, idx)
+ if (PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+ if (!best ||
+ s->priority > best->priority ||
+ (s->priority == best->priority &&
+ s->monitor_of &&
+ best->monitor_of &&
+ s->monitor_of->priority > best->monitor_of->priority))
+ best = s;
+
+ if (best)
+ return pa_namereg_set_default_source(c, best);
return NULL;
}
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index 38fae6f5..b5a976d7 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -45,6 +45,7 @@ pa_sink *pa_namereg_get_default_sink(pa_core *c);
pa_source *pa_namereg_get_default_source(pa_core *c);
pa_bool_t pa_namereg_is_valid_name(const char *name);
+pa_bool_t 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/object.c b/src/pulsecore/object.c
index f3ead9c5..099d50d9 100644
--- a/src/pulsecore/object.c
+++ b/src/pulsecore/object.c
@@ -28,21 +28,23 @@
#include "object.h"
-pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+const char pa_object_type_id[] = "pa_object";
+
+pa_object *pa_object_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_id)) {
pa_object *o;
pa_assert(size > sizeof(pa_object));
- pa_assert(type_name);
+ pa_assert(type_id);
if (!check_type)
check_type = pa_object_check_type;
- pa_assert(check_type(type_name));
- pa_assert(check_type("pa_object"));
+ pa_assert(check_type(type_id));
+ pa_assert(check_type(pa_object_type_id));
o = pa_xmalloc(size);
PA_REFCNT_INIT(o);
- o->type_name = type_name;
+ o->type_id = type_id;
o->free = pa_object_free;
o->check_type = check_type;
@@ -65,8 +67,8 @@ void pa_object_unref(pa_object *o) {
}
}
-int pa_object_check_type(const char *type_name) {
- pa_assert(type_name);
+pa_bool_t pa_object_check_type(const char *type_id) {
+ pa_assert(type_id);
- return pa_streq(type_name, "pa_object");
+ return type_id == pa_object_type_id;
}
diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h
index 43e79327..4c120cd5 100644
--- a/src/pulsecore/object.h
+++ b/src/pulsecore/object.h
@@ -34,21 +34,23 @@ typedef struct pa_object pa_object;
struct pa_object {
PA_REFCNT_DECLARE;
- const char *type_name;
+ const char *type_id;
void (*free)(pa_object *o);
- int (*check_type)(const char *type_name);
+ pa_bool_t (*check_type)(const char *type_name);
};
-pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
-#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type, type##_check_type)
+pa_object *pa_object_new_internal(size_t size, const char *type_id, pa_bool_t (*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)
-int pa_object_check_type(const char *type);
+pa_bool_t pa_object_check_type(const char *type_id);
-static inline int pa_object_isinstance(void *o) {
+extern const char pa_object_type_id[];
+
+static inline pa_bool_t pa_object_isinstance(void *o) {
pa_object *obj = (pa_object*) o;
- return obj ? obj->check_type("pa_object") : 0;
+ return obj ? obj->check_type(pa_object_type_id) : TRUE;
}
pa_object *pa_object_ref(pa_object *o);
@@ -60,7 +62,7 @@ static inline int pa_object_refcnt(pa_object *o) {
static inline pa_object* pa_object_cast(void *o) {
pa_object *obj = (pa_object*) o;
- pa_assert(!obj || obj->check_type("pa_object"));
+ pa_assert(!obj || obj->check_type(pa_object_type_id));
return obj;
}
@@ -68,10 +70,10 @@ static inline pa_object* pa_object_cast(void *o) {
#define PA_OBJECT(o) pa_object_cast(o)
-#define PA_DECLARE_CLASS(c) \
- static inline int c##_isinstance(void *o) { \
+#define PA_DECLARE_CLASS_COMMON(c) \
+ static inline pa_bool_t c##_isinstance(void *o) { \
pa_object *obj = (pa_object*) o; \
- return obj ? obj->check_type(#c) : 1; \
+ return obj ? obj->check_type(c##_type_id) : TRUE; \
} \
static inline c* c##_cast(void *o) { \
pa_assert(c##_isinstance(o)); \
@@ -91,12 +93,27 @@ static inline pa_object* pa_object_cast(void *o) {
} \
struct __stupid_useless_struct_to_allow_trailing_semicolon
-#define PA_DEFINE_CHECK_TYPE(c, parent) \
- int c##_check_type(const char *type) { \
- pa_assert(type); \
- if (strcmp(type, #c) == 0) \
- return 1; \
- return parent##_check_type(type); \
+#define PA_DECLARE_PUBLIC_CLASS(c) \
+ extern const char c##_type_id[]; \
+ PA_DECLARE_CLASS_COMMON(c); \
+ pa_bool_t c##_check_type(const char *type_id)
+
+#define PA_DEFINE_PUBLIC_CLASS(c, parent) \
+ const char c##_type_id[] = #c; \
+ pa_bool_t 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 pa_bool_t 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
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index fc8ce76f..f15466ce 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -169,7 +169,7 @@ static const char *command_names[PA_COMMAND_MAX] = {
/* 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",
@@ -180,7 +180,11 @@ static const char *command_names[PA_COMMAND_MAX] = {
/* SERVER->CLIENT */
[PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = "PLAYBACK_BUFFER_ATTR_CHANGED",
- [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = "RECORD_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"
};
#endif
@@ -203,10 +207,10 @@ struct pa_pdispatch {
const pa_pdispatch_cb_t *callback_table;
unsigned n_commands;
PA_LLIST_HEAD(struct reply_info, replies);
- pa_pdispatch_drain_callback drain_callback;
+ pa_pdispatch_drain_cb_t drain_callback;
void *drain_userdata;
const pa_creds *creds;
- pa_bool_t use_rtclock:1;
+ pa_bool_t use_rtclock;
};
static void reply_info_free(struct reply_info *r) {
@@ -225,19 +229,16 @@ static void reply_info_free(struct reply_info *r) {
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, const pa_pdispatch_cb_t *table, unsigned entries) {
pa_pdispatch *pd;
- pa_assert(mainloop);
+ pa_assert(mainloop);
pa_assert((entries && table) || (!entries && !table));
- pd = pa_xnew(pa_pdispatch, 1);
+ 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->drain_callback = NULL;
- pd->drain_userdata = NULL;
- pd->creds = NULL;
pd->use_rtclock = use_rtclock;
return pd;
@@ -317,7 +318,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
struct reply_info *r;
- for (r = pd->replies; r; r = r->next)
+ PA_LLIST_FOREACH(r, pd->replies)
if (r->tag == tag)
break;
@@ -325,9 +326,9 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
run_action(pd, r, command, ts);
} else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
- const pa_pdispatch_cb_t *c = pd->callback_table+command;
+ const pa_pdispatch_cb_t *cb = pd->callback_table+command;
- (*c)(pd, command, tag, ts, userdata);
+ (*cb)(pd, command, tag, ts, userdata);
} else {
pa_log("Received unsupported command %u", command);
goto finish;
@@ -375,7 +376,9 @@ void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa
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_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);
}
@@ -387,7 +390,7 @@ int pa_pdispatch_is_pending(pa_pdispatch *pd) {
return !!pd->replies;
}
-void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
+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));
@@ -402,12 +405,9 @@ void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
pa_assert(pd);
pa_assert(PA_REFCNT_VALUE(pd) >= 1);
- for (r = pd->replies; r; r = n) {
- n = r->next;
-
+ PA_LLIST_FOREACH_SAFE(r, n, pd->replies)
if (r->userdata == userdata)
reply_info_free(r);
- }
}
void pa_pdispatch_unref(pa_pdispatch *pd) {
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
index dae475af..c5431c2e 100644
--- a/src/pulsecore/pdispatch.h
+++ b/src/pulsecore/pdispatch.h
@@ -35,9 +35,9 @@
typedef struct pa_pdispatch pa_pdispatch;
typedef void (*pa_pdispatch_cb_t)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata);
+typedef void (*pa_pdispatch_drain_cb_t)(pa_pdispatch *pd, void *userdata);
-pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, pa_bool_t use_rtclock, const pa_pdispatch_cb_t*table, unsigned entries);
+pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, pa_bool_t 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);
@@ -47,7 +47,7 @@ void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa
int pa_pdispatch_is_pending(pa_pdispatch *pd);
-void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata);
+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);
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index 00878462..996946c2 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -81,7 +81,7 @@ static pid_t read_pid(const char *fn, int fd) {
}
static int open_pid_file(const char *fn, int mode) {
- int fd = -1;
+ int fd;
pa_assert(fn);
@@ -123,8 +123,6 @@ static int open_pid_file(const char *fn, int mode) {
fd = -1;
goto fail;
}
-
- fd = -1;
}
return fd;
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index fceb2ca1..f528c496 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -47,9 +47,8 @@ enum {
MEMBLOCKQ_STREAM_MESSAGE_UNLINK,
};
-PA_DECLARE_CLASS(memblockq_stream);
+PA_DEFINE_PRIVATE_CLASS(memblockq_stream, pa_msgobject);
#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(memblockq_stream, pa_msgobject);
static void memblockq_stream_unlink(memblockq_stream *u) {
pa_assert(u);
@@ -200,7 +199,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
pa_sink_input_new_data_set_volume(&data, volume);
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
- pa_sink_input_new(&u->sink_input, sink->core, &data, 0);
+ pa_sink_input_new(&u->sink_input, sink->core, &data);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index d9769bc7..23864bcb 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -186,10 +186,12 @@ void pa_init_proplist(pa_proplist *p) {
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY)) {
- char t[PATH_MAX];
- if (pa_get_binary_name(t, sizeof(t))) {
+ 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);
}
}
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index f64552aa..2326eb3a 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -120,9 +120,8 @@ typedef struct connection {
pa_time_event *auth_timeout_event;
} connection;
-PA_DECLARE_CLASS(connection);
+PA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
#define CONNECTION(o) (connection_cast(o))
-static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_esound_protocol {
PA_REFCNT_DECLARE;
@@ -430,7 +429,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
sdata.sink = sink;
pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
- pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata, 0);
+ 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.");
@@ -526,7 +525,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
sdata.source = source;
pa_source_output_new_data_set_sample_spec(&sdata, &ss);
- pa_source_output_new(&c->source_output, c->protocol->core, &sdata, 0);
+ 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.");
@@ -772,7 +771,6 @@ static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *
memcpy(&rvolume, data, sizeof(uint32_t));
rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
- data = (const char*)data + sizeof(uint32_t);
if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
pa_cvolume volume;
@@ -810,7 +808,6 @@ static int esd_proto_sample_pan(connection *c, esd_proto_t request, const void *
memcpy(&rvolume, data, sizeof(uint32_t));
rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
- data = (const char*)data + sizeof(uint32_t);
volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
@@ -1124,7 +1121,7 @@ static int do_read(connection *c) {
ssize_t r;
size_t l;
void *p;
- size_t space;
+ size_t space = 0;
pa_assert(c->input_memblockq);
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 5220cc91..c09e5348 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -533,7 +533,7 @@ static void handle_listen_prefix(struct connection *c, const char *source_name)
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, 0);
+ pa_source_output_new(&c->source_output, c->protocol->core, &data);
pa_source_output_new_data_done(&data);
if (!c->source_output) {
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 96184bd2..d06dd4eb 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -98,17 +98,15 @@ typedef struct record_stream {
pa_usec_t current_source_latency;
} record_stream;
-PA_DECLARE_CLASS(record_stream);
#define RECORD_STREAM(o) (record_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(record_stream, pa_msgobject);
+PA_DEFINE_PRIVATE_CLASS(record_stream, pa_msgobject);
typedef struct output_stream {
pa_msgobject parent;
} output_stream;
-PA_DECLARE_CLASS(output_stream);
#define OUTPUT_STREAM(o) (output_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject);
+PA_DEFINE_PRIVATE_CLASS(output_stream, pa_msgobject);
typedef struct playback_stream {
output_stream parent;
@@ -138,9 +136,8 @@ typedef struct playback_stream {
uint64_t playing_for, underrun_for;
} playback_stream;
-PA_DECLARE_CLASS(playback_stream);
#define PLAYBACK_STREAM(o) (playback_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(playback_stream, output_stream);
+PA_DEFINE_PRIVATE_CLASS(playback_stream, output_stream);
typedef struct upload_stream {
output_stream parent;
@@ -156,9 +153,8 @@ typedef struct upload_stream {
pa_proplist *proplist;
} upload_stream;
-PA_DECLARE_CLASS(upload_stream);
#define UPLOAD_STREAM(o) (upload_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(upload_stream, output_stream);
+PA_DEFINE_PRIVATE_CLASS(upload_stream, output_stream);
struct pa_native_connection {
pa_msgobject parent;
@@ -176,9 +172,8 @@ struct pa_native_connection {
pa_time_event *auth_timeout_event;
};
-PA_DECLARE_CLASS(pa_native_connection);
#define PA_NATIVE_CONNECTION(o) (pa_native_connection_cast(o))
-static PA_DEFINE_CHECK_TYPE(pa_native_connection, pa_msgobject);
+PA_DEFINE_PRIVATE_CLASS(pa_native_connection, pa_msgobject);
struct pa_native_protocol {
PA_REFCNT_DECLARE;
@@ -633,7 +628,6 @@ static record_stream* record_stream_new(
record_stream *s;
pa_source_output *source_output = NULL;
- size_t base;
pa_source_output_new_data data;
pa_assert(c);
@@ -653,8 +647,9 @@ static record_stream* record_stream_new(
pa_source_output_new_data_set_channel_map(&data, map);
if (peak_detect)
data.resample_method = PA_RESAMPLER_PEAKS;
+ data.flags = flags;
- *ret = -pa_source_output_new(&source_output, c->protocol->core, &data, flags);
+ *ret = -pa_source_output_new(&source_output, c->protocol->core, &data);
pa_source_output_new_data_done(&data);
@@ -686,7 +681,7 @@ static record_stream* record_stream_new(
0,
s->buffer_attr.maxlength,
0,
- base = pa_frame_size(&source_output->sample_spec),
+ pa_frame_size(&source_output->sample_spec),
1,
0,
0,
@@ -762,6 +757,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
return -1;
switch (code) {
+
case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
pa_tagstruct *t;
int l = 0;
@@ -830,24 +826,26 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
break;
- case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH: {
- pa_tagstruct *t;
+ case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH:
s->buffer_attr.tlength = (uint32_t) offset;
- t = pa_tagstruct_new(NULL, 0);
- 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);
+ if (s->connection->version >= 15) {
+ pa_tagstruct *t;
+
+ t = pa_tagstruct_new(NULL, 0);
+ 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;
@@ -1054,8 +1052,9 @@ static playback_stream* playback_stream_new(
if (muted_set)
pa_sink_input_new_data_set_muted(&data, muted);
data.sync_base = ssync ? ssync->sink_input : NULL;
+ data.flags = flags;
- *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data, flags);
+ *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data);
pa_sink_input_new_data_done(&data);
@@ -1130,6 +1129,12 @@ static void playback_stream_request_bytes(playback_stream *s) {
m = pa_memblockq_pop_missing(s->memblockq);
+ /* pa_log("request_bytes(%lu) (tlength=%lu minreq=%lu length=%lu)", */
+ /* (unsigned long) m, */
+ /* pa_memblockq_get_tlength(s->memblockq), */
+ /* pa_memblockq_get_minreq(s->memblockq), */
+ /* pa_memblockq_get_length(s->memblockq)); */
+
if (m <= 0)
return;
@@ -1143,7 +1148,6 @@ static void playback_stream_request_bytes(playback_stream *s) {
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;
@@ -1288,7 +1292,8 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
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 == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for),
+ (size_t) (s->sink_input->thread_info.underrun_for == (uint64_t) -1 ? 0 :
+ s->sink_input->thread_info.underrun_for),
FALSE, TRUE, FALSE);
}
@@ -1345,7 +1350,9 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
/* pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
- pa_log_warn("Failed to push data into queue");
+
+ if (pa_log_ratelimit())
+ 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);
}
@@ -1617,6 +1624,9 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
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);
@@ -1752,6 +1762,9 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
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);
@@ -1856,7 +1869,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name(sink_name), tag, PA_ERR_INVALID);
+ 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, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
@@ -1951,7 +1964,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
(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_FAIL_ON_SUSPEND : 0);
+ (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0);
/* Only since protocol version 15 there's a seperate muted_set
* flag. For older versions we synthesize it here */
@@ -2105,7 +2118,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name(source_name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
@@ -2207,7 +2220,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
(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_FAIL_ON_SUSPEND : 0);
+ (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0);
s = record_stream_new(c, source, &ss, &map, peak_detect, &attr, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
pa_proplist_free(p);
@@ -2262,6 +2275,8 @@ static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
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 */
}
@@ -2451,7 +2466,7 @@ static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_
}
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, 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;
@@ -2550,7 +2565,7 @@ static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uin
reply = reply_new(tag);
pa_tagstruct_put_usec(reply,
s->current_sink_latency +
- pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec));
+ 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 &&
@@ -2722,7 +2737,7 @@ static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag
return;
}
- CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name(sink_name), tag, PA_ERR_INVALID);
+ 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);
@@ -2826,7 +2841,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
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, FALSE),
+ 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,
@@ -3096,7 +3111,12 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
}
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, !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, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -3323,6 +3343,7 @@ static void command_set_volume(
pa_source *source = NULL;
pa_sink_input *si = NULL;
const char *name = NULL;
+ const char *client_name;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -3337,7 +3358,7 @@ static void command_set_volume(
}
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, !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, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -3369,12 +3390,26 @@ static void command_set_volume(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
- if (sink)
- pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE, TRUE);
- else if (source)
+ 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);
- else if (si)
+ } else if (si) {
+ 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);
+ }
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -3392,7 +3427,7 @@ static void command_set_mute(
pa_sink *sink = NULL;
pa_source *source = NULL;
pa_sink_input *si = NULL;
- const char *name = NULL;
+ const char *name = NULL, *client_name;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -3407,7 +3442,7 @@ static void command_set_mute(
}
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, !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, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -3415,7 +3450,6 @@ static void command_set_mute(
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
@@ -3441,12 +3475,20 @@ static void command_set_mute(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
- if (sink)
+ 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)
+ } 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)
+ } 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);
+ }
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -4065,7 +4107,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
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(name_device), 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, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx_device == PA_INVALID_INDEX || !name_device, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name_device || idx_device == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -4129,7 +4171,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name) || *name == 0, tag, PA_ERR_INVALID);
+ 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, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -4289,7 +4331,7 @@ static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command,
}
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, !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, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -4824,3 +4866,9 @@ pa_pstream* pa_native_connection_get_pstream(pa_native_connection *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
index 8a8d601c..97126274 100644
--- a/src/pulsecore/protocol-native.h
+++ b/src/pulsecore/protocol-native.h
@@ -47,7 +47,6 @@ typedef struct pa_native_options {
char *auth_group;
pa_ip_acl *auth_ip_acl;
pa_auth_cookie *auth_cookie;
-
} pa_native_options;
typedef enum pa_native_hook {
@@ -80,6 +79,7 @@ int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_nativ
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);
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 776d74b6..a9f73896 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -69,9 +69,8 @@ typedef struct connection {
} playback;
} connection;
-PA_DECLARE_CLASS(connection);
+PA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
#define CONNECTION(o) (connection_cast(o))
-static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_simple_protocol {
PA_REFCNT_DECLARE;
@@ -155,7 +154,7 @@ static int do_read(connection *c) {
ssize_t r;
size_t l;
void *p;
- size_t space;
+ size_t space = 0;
connection_assert_ref(c);
@@ -542,7 +541,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
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, 0);
+ pa_sink_input_new(&c->sink_input, p->core, &data);
pa_sink_input_new_data_done(&data);
if (!c->sink_input) {
@@ -594,7 +593,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
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, 0);
+ pa_source_output_new(&c->source_output, p->core, &data);
pa_source_output_new_data_done(&data);
if (!c->source_output) {
@@ -628,8 +627,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
return;
fail:
- if (c)
- connection_unlink(c);
+ connection_unlink(c);
}
void pa_simple_protocol_disconnect(pa_simple_protocol *p, pa_module *m) {
diff --git a/src/pulsecore/ratelimit.h b/src/pulsecore/ratelimit.h
index ec3b5a38..9857a291 100644
--- a/src/pulsecore/ratelimit.h
+++ b/src/pulsecore/ratelimit.h
@@ -26,21 +26,31 @@
#include <pulsecore/macro.h>
typedef struct pa_ratelimit {
- const pa_usec_t interval;
- const unsigned burst;
+ 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, \
+ .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);
+
pa_bool_t pa_ratelimit_test(pa_ratelimit *r);
#endif
diff --git a/src/pulsecore/remap.c b/src/pulsecore/remap.c
new file mode 100644
index 00000000..a0fc85b9
--- /dev/null
+++ b/src/pulsecore/remap.c
@@ -0,0 +1,204 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "remap.h"
+
+static void remap_mono_to_stereo_c (pa_remap_t *m, void *dst, const void *src, unsigned n) {
+ unsigned i;
+
+ switch (*m->format) {
+ case PA_SAMPLE_FLOAT32NE:
+ {
+ float *d, *s;
+
+ d = (float *) dst;
+ s = (float *) src;
+
+ for (i = n >> 2; i; i--) {
+ d[0] = d[1] = s[0];
+ d[2] = d[3] = s[1];
+ d[4] = d[5] = s[2];
+ d[6] = d[7] = s[3];
+ s += 4;
+ d += 8;
+ }
+ for (i = n & 3; i; i--) {
+ d[0] = d[1] = s[0];
+ s++;
+ d += 2;
+ }
+ break;
+ }
+ case PA_SAMPLE_S16NE:
+ {
+ int16_t *d, *s;
+
+ d = (int16_t *) dst;
+ s = (int16_t *) src;
+
+ for (i = n >> 2; i; i--) {
+ d[0] = d[1] = s[0];
+ d[2] = d[3] = s[1];
+ d[4] = d[5] = s[2];
+ d[6] = d[7] = s[3];
+ s += 4;
+ d += 8;
+ }
+ for (i = n & 3; i; i--) {
+ d[0] = d[1] = s[0];
+ s++;
+ d += 2;
+ }
+ break;
+ }
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+static void remap_channels_matrix_c (pa_remap_t *m, void *dst, const void *src, unsigned n) {
+ unsigned oc, ic, i;
+ unsigned n_ic, n_oc;
+
+ n_ic = m->i_ss->channels;
+ n_oc = m->o_ss->channels;
+
+ switch (*m->format) {
+ case PA_SAMPLE_FLOAT32NE:
+ {
+ float *d, *s;
+
+ memset(dst, 0, n * sizeof (float) * n_oc);
+
+ for (oc = 0; oc < n_oc; oc++) {
+
+ for (ic = 0; ic < n_ic; ic++) {
+ float vol;
+
+ vol = m->map_table_f[oc][ic];
+
+ if (vol <= 0.0)
+ continue;
+
+ d = (float *)dst + oc;
+ s = (float *)src + ic;
+
+ if (vol >= 1.0) {
+ 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;
+ }
+ }
+ }
+
+ break;
+ }
+ case PA_SAMPLE_S16NE:
+ {
+ int16_t *d, *s;
+
+ memset(dst, 0, n * sizeof (int16_t) * n_oc);
+
+ for (oc = 0; oc < n_oc; oc++) {
+
+ for (ic = 0; ic < n_ic; ic++) {
+ int32_t vol;
+
+ vol = m->map_table_i[oc][ic];
+
+ if (vol <= 0)
+ continue;
+
+ d = (int16_t *)dst + oc;
+ s = (int16_t *)src + ic;
+
+ 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);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+/* 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;
+
+ 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_f[0][0] >= 1.0 && m->map_table_f[1][0] >= 1.0) {
+ m->do_remap = (pa_do_remap_func_t) remap_mono_to_stereo_c;
+ pa_log_info("Using mono to stereo remapping");
+ } else {
+ m->do_remap = (pa_do_remap_func_t) remap_channels_matrix_c;
+ pa_log_info("Using generic matrix remapping");
+ }
+}
+
+
+/* default C implementation */
+static pa_init_remap_func_t remap_func = init_remap_c;
+
+void pa_init_remap (pa_remap_t *m) {
+ pa_assert (remap_func);
+
+ m->do_remap = NULL;
+
+ /* call the installed remap init function */
+ 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 remap_func;
+}
+
+void pa_set_init_remap_func(pa_init_remap_func_t func) {
+ remap_func = func;
+}
diff --git a/src/pulsecore/remap.h b/src/pulsecore/remap.h
new file mode 100644
index 00000000..32a67cdd
--- /dev/null
+++ b/src/pulsecore/remap.h
@@ -0,0 +1,48 @@
+#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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#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 pa_init_remap (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);
+
+#endif /* fooremapfoo */
diff --git a/src/pulsecore/remap_mmx.c b/src/pulsecore/remap_mmx.c
new file mode 100644
index 00000000..d358a58b
--- /dev/null
+++ b/src/pulsecore/remap_mmx.c
@@ -0,0 +1,161 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/sample.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_mmx (pa_remap_t *m, void *dst, const void *src, unsigned n) {
+ pa_reg_x86 temp, temp2;
+
+ switch (*m->format) {
+ case PA_SAMPLE_FLOAT32NE:
+ {
+ __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"
+ );
+ break;
+ }
+ case PA_SAMPLE_S16NE:
+ {
+ __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"
+ );
+ break;
+ }
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+/* 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_f[0][0] >= 1.0 && m->map_table_f[1][0] >= 1.0) {
+ m->do_remap = (pa_do_remap_func_t) remap_mono_to_stereo_mmx;
+ pa_log_info("Using MMX mono to stereo remapping");
+ }
+}
+#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_sse.c b/src/pulsecore/remap_sse.c
new file mode 100644
index 00000000..0ccf3161
--- /dev/null
+++ b/src/pulsecore/remap_sse.c
@@ -0,0 +1,159 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/sample.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_sse2 (pa_remap_t *m, void *dst, const void *src, unsigned n) {
+ pa_reg_x86 temp, temp2;
+
+ switch (*m->format) {
+ case PA_SAMPLE_FLOAT32NE:
+ {
+ __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"
+ );
+ break;
+ }
+ case PA_SAMPLE_S16NE:
+ {
+ __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"
+ );
+ break;
+ }
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+/* 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_f[0][0] >= 1.0 && m->map_table_f[1][0] >= 1.0) {
+ m->do_remap = (pa_do_remap_func_t) remap_mono_to_stereo_sse2;
+ pa_log_info("Using SSE mono to stereo remapping");
+ }
+}
+#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
index 17fb8480..bed5a20d 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -31,9 +31,6 @@
#include <speex/speex_resampler.h>
-#include <liboil/liboilfuncs.h>
-#include <liboil/liboil.h>
-
#include <pulse/xmalloc.h>
#include <pulsecore/sconv.h>
#include <pulsecore/log.h>
@@ -43,6 +40,7 @@
#include "ffmpeg/avcodec.h"
#include "resampler.h"
+#include "remap.h"
/* Number of samples of extra space we allow the resamplers to return */
#define EXTRA_FRAMES 128
@@ -64,7 +62,7 @@ struct pa_resampler {
pa_convert_func_t to_work_format_func;
pa_convert_func_t from_work_format_func;
- float map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+ pa_remap_t remap;
pa_bool_t map_required;
void (*impl_free)(pa_resampler *r);
@@ -214,6 +212,11 @@ pa_resampler* pa_resampler_new(
r->i_ss = *a;
r->o_ss = *b;
+ /* set up the remap structure */
+ r->remap.i_ss = &r->i_ss;
+ r->remap.o_ss = &r->o_ss;
+ r->remap.format = &r->work_format;
+
if (am)
r->i_cm = *am;
else if (!pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT))
@@ -296,8 +299,7 @@ pa_resampler* pa_resampler_new(
return r;
fail:
- if (r)
- pa_xfree(r);
+ pa_xfree(r);
return NULL;
}
@@ -347,13 +349,17 @@ void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
pa_assert(r);
- return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
+ /* Let's round up here */
+
+ return (((((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) {
pa_assert(r);
- return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz;
+ /* Let's round up here */
+
+ return (((((in_length + r->i_fz-1) / r->i_fz) * 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) {
@@ -576,32 +582,41 @@ static int front_rear_side(pa_channel_position_t p) {
static void calc_map_table(pa_resampler *r) {
unsigned oc, ic;
+ unsigned n_oc, n_ic;
pa_bool_t ic_connected[PA_CHANNELS_MAX];
pa_bool_t remix;
pa_strbuf *s;
char *t;
+ pa_remap_t *m;
pa_assert(r);
if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) && !pa_channel_map_equal(&r->i_cm, &r->o_cm)))))
return;
- memset(r->map_table, 0, sizeof(r->map_table));
+ m = &r->remap;
+
+ n_oc = r->o_ss.channels;
+ n_ic = r->i_ss.channels;
+
+ 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));
remix = (r->flags & (PA_RESAMPLER_NO_REMAP|PA_RESAMPLER_NO_REMIX)) == 0;
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
pa_bool_t oc_connected = FALSE;
pa_channel_position_t b = r->o_cm.map[oc];
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
pa_channel_position_t a = r->i_cm.map[ic];
if (r->flags & PA_RESAMPLER_NO_REMAP) {
/* We shall not do any remapping. Hence, just check by index */
if (ic == oc)
- r->map_table[oc][ic] = 1.0;
+ m->map_table_f[oc][ic] = 1.0;
continue;
}
@@ -610,7 +625,7 @@ static void calc_map_table(pa_resampler *r) {
/* We shall not do any remixing. Hence, just check by name */
if (a == b)
- r->map_table[oc][ic] = 1.0;
+ m->map_table_f[oc][ic] = 1.0;
continue;
}
@@ -685,7 +700,7 @@ static void calc_map_table(pa_resampler *r) {
*/
if (a == b || a == PA_CHANNEL_POSITION_MONO || b == PA_CHANNEL_POSITION_MONO) {
- r->map_table[oc][ic] = 1.0;
+ m->map_table_f[oc][ic] = 1.0;
oc_connected = TRUE;
ic_connected[ic] = TRUE;
@@ -703,14 +718,14 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and on the left side, let's
* average all left side input channels. */
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic]))
n++;
if (n > 0)
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = 1.0f / (float) n;
+ m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@@ -724,14 +739,14 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and on the right side, let's
* average all right side input channels. */
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_right(r->i_cm.map[ic]))
n++;
if (n > 0)
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_right(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = 1.0f / (float) n;
+ m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@@ -745,14 +760,14 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and at the center. Let's
* average all center input channels. */
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_center(r->i_cm.map[ic]))
n++;
if (n > 0) {
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_center(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = 1.0f / (float) n;
+ m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
} else {
@@ -762,14 +777,14 @@ static void calc_map_table(pa_resampler *r) {
n = 0;
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic]))
n++;
if (n > 0)
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = 1.0f / (float) n;
+ m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@@ -783,12 +798,12 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and an LFE. Let's average all
* channels for LFE. */
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (!(r->flags & PA_RESAMPLER_NO_LFE))
- r->map_table[oc][ic] = 1.0f / (float) r->i_ss.channels;
+ m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
else
- r->map_table[oc][ic] = 0;
+ m->map_table_f[oc][ic] = 0;
/* Please note that a channel connected to LFE
* doesn't really count as connected. */
@@ -804,7 +819,7 @@ static void calc_map_table(pa_resampler *r) {
ic_unconnected_center = 0,
ic_unconnected_lfe = 0;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
pa_channel_position_t a = r->i_cm.map[ic];
if (ic_connected[ic])
@@ -827,20 +842,20 @@ static void calc_map_table(pa_resampler *r) {
* the left side by .9 and add in our averaged unconnected
* channels multplied by .1 */
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]))
continue;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .9f;
+ m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_left(r->i_cm.map[ic]))
- r->map_table[oc][ic] = .1f / (float) ic_unconnected_left;
+ m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_left;
}
}
}
@@ -852,20 +867,20 @@ static void calc_map_table(pa_resampler *r) {
* the right side by .9 and add in our averaged unconnected
* channels multplied by .1 */
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_right(r->o_cm.map[oc]))
continue;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .9f;
+ m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_right(r->i_cm.map[ic]))
- r->map_table[oc][ic] = .1f / (float) ic_unconnected_right;
+ m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_right;
}
}
}
@@ -878,20 +893,20 @@ static void calc_map_table(pa_resampler *r) {
* the center side by .9 and add in our averaged unconnected
* channels multplied by .1 */
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_center(r->o_cm.map[oc]))
continue;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .9f;
+ m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_center(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = .1f / (float) ic_unconnected_center;
+ m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_center;
mixed_in = TRUE;
}
}
@@ -909,7 +924,7 @@ static void calc_map_table(pa_resampler *r) {
it into left and right. Using .375 and 0.75 as
factors. */
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic])
continue;
@@ -917,7 +932,7 @@ static void calc_map_table(pa_resampler *r) {
if (!on_center(r->i_cm.map[ic]))
continue;
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
@@ -928,7 +943,7 @@ static void calc_map_table(pa_resampler *r) {
}
}
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
@@ -938,7 +953,7 @@ static void calc_map_table(pa_resampler *r) {
}
}
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
@@ -946,10 +961,10 @@ static void calc_map_table(pa_resampler *r) {
if (ncenter[oc] <= 0)
continue;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .75f;
+ m->map_table_f[oc][ic] *= .75f;
continue;
}
@@ -957,7 +972,7 @@ static void calc_map_table(pa_resampler *r) {
continue;
if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
- r->map_table[oc][ic] = .375f / (float) ncenter[oc];
+ m->map_table_f[oc][ic] = .375f / (float) ncenter[oc];
}
}
}
@@ -968,40 +983,46 @@ static void calc_map_table(pa_resampler *r) {
/* OK, so there is an unconnected LFE channel. Let's mix
* it into all channels, with factor 0.375 */
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (!on_lfe(r->i_cm.map[ic]))
continue;
- for (oc = 0; oc < r->o_ss.channels; oc++)
- r->map_table[oc][ic] = 0.375f / (float) ic_unconnected_lfe;
+ for (oc = 0; oc < n_oc; oc++)
+ m->map_table_f[oc][ic] = 0.375f / (float) ic_unconnected_lfe;
}
}
}
-
+ /* 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 < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
pa_strbuf_printf(s, " I%02u ", ic);
pa_strbuf_puts(s, "\n +");
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
pa_strbuf_printf(s, "------");
pa_strbuf_puts(s, "\n");
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
pa_strbuf_printf(s, "O%02u |", oc);
- for (ic = 0; ic < r->i_ss.channels; ic++)
- pa_strbuf_printf(s, " %1.3f", r->map_table[oc][ic]);
+ 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_tostring_free(s));
pa_xfree(t);
+
+ /* initialize the remapping function */
+ pa_init_remap (m);
}
static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
@@ -1041,41 +1062,10 @@ static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input)
return &r->buf1;
}
-static void vectoradd_s16_with_fraction(
- int16_t *d, int dstr,
- const int16_t *s1, int sstr1,
- const int16_t *s2, int sstr2,
- int n,
- float s3, float s4) {
-
- int32_t i3, i4;
-
- i3 = (int32_t) (s3 * 0x10000);
- i4 = (int32_t) (s4 * 0x10000);
-
- for (; n > 0; n--) {
- int32_t a, b;
-
- a = *s1;
- b = *s2;
-
- a = (a * i3) / 0x10000;
- b = (b * i4) / 0x10000;
-
- *d = (int16_t) (a + b);
-
- s1 = (const int16_t*) ((const uint8_t*) s1 + sstr1);
- s2 = (const int16_t*) ((const uint8_t*) s2 + sstr2);
- d = (int16_t*) ((uint8_t*) d + dstr);
-
- }
-}
-
static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
unsigned in_n_samples, out_n_samples, n_frames;
- int i_skip, o_skip;
- unsigned oc;
void *src, *dst;
+ pa_remap_t *remap;
pa_assert(r);
pa_assert(input);
@@ -1104,76 +1094,14 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
src = ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
dst = pa_memblock_acquire(r->buf2.memblock);
- memset(dst, 0, r->buf2.length);
-
- o_skip = (int) (r->w_sz * r->o_ss.channels);
- i_skip = (int) (r->w_sz * r->i_ss.channels);
-
- switch (r->work_format) {
- case PA_SAMPLE_FLOAT32NE:
-
- for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned ic;
- static const float one = 1.0;
+ remap = &r->remap;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
-
- if (r->map_table[oc][ic] <= 0.0)
- continue;
-
- oil_vectoradd_f32(
- (float*) dst + oc, o_skip,
- (float*) dst + oc, o_skip,
- (float*) src + ic, i_skip,
- (int) n_frames,
- &one, &r->map_table[oc][ic]);
- }
- }
-
- break;
-
- case PA_SAMPLE_S16NE:
-
- for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned ic;
-
- for (ic = 0; ic < r->i_ss.channels; ic++) {
-
- if (r->map_table[oc][ic] <= 0.0)
- continue;
-
- if (r->map_table[oc][ic] >= 1.0) {
- static const int16_t one = 1;
-
- oil_vectoradd_s16(
- (int16_t*) dst + oc, o_skip,
- (int16_t*) dst + oc, o_skip,
- (int16_t*) src + ic, i_skip,
- (int) n_frames,
- &one, &one);
-
- } else
-
- vectoradd_s16_with_fraction(
- (int16_t*) dst + oc, o_skip,
- (int16_t*) dst + oc, o_skip,
- (int16_t*) src + ic, i_skip,
- (int) n_frames,
- 1.0f, r->map_table[oc][ic]);
- }
- }
-
- break;
-
- default:
- pa_assert_not_reached();
- }
+ pa_assert (remap->do_remap);
+ remap->do_remap (remap, dst, src, n_frames);
pa_memblock_release(input->memblock);
pa_memblock_release(r->buf2.memblock);
- r->buf2.length = out_n_samples * r->w_sz;
-
return &r->buf2;
}
@@ -1465,7 +1393,7 @@ static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned
pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
- oil_memcpy((uint8_t*) dst + fz * o_index,
+ memcpy((uint8_t*) dst + fz * o_index,
(uint8_t*) src + fz * j, (int) fz);
}
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
index 42708a8a..666cbc98 100644
--- a/src/pulsecore/rtpoll.c
+++ b/src/pulsecore/rtpoll.c
@@ -63,6 +63,7 @@ struct pa_rtpoll {
pa_bool_t running:1;
pa_bool_t rebuild_needed:1;
pa_bool_t quit:1;
+ pa_bool_t timer_elapsed:1;
#ifdef DEBUG_TIMING
pa_usec_t timestamp;
@@ -94,26 +95,14 @@ PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
pa_rtpoll *pa_rtpoll_new(void) {
pa_rtpoll *p;
- p = pa_xnew(pa_rtpoll, 1);
+ 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);
- p->n_pollfd_used = 0;
-
- pa_zero(p->next_elapse);
- p->timer_enabled = FALSE;
-
- p->running = FALSE;
- p->scan_for_dead = FALSE;
- p->rebuild_needed = FALSE;
- p->quit = FALSE;
-
- PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items);
#ifdef DEBUG_TIMING
p->timestamp = pa_rtclock_now();
- p->slept = p->awake = 0;
#endif
return p;
@@ -229,6 +218,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
pa_assert(!p->running);
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) {
@@ -286,7 +276,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
if (p->rebuild_needed)
rtpoll_rebuild(p);
- memset(&timeout, 0, sizeof(timeout));
+ pa_zero(timeout);
/* Calculate timeout */
if (wait_op && !p->quit && p->timer_enabled) {
@@ -314,9 +304,11 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
r = ppoll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? &ts : NULL, NULL);
}
#else
- r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
+ r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || 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();
@@ -628,3 +620,9 @@ void pa_rtpoll_quit(pa_rtpoll *p) {
p->quit = TRUE;
}
+
+pa_bool_t 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
index d2d69cad..b2a87fca 100644
--- a/src/pulsecore/rtpoll.h
+++ b/src/pulsecore/rtpoll.h
@@ -73,6 +73,10 @@ 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 */
+pa_bool_t 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);
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 5b8ccf59..a26dc876 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -30,9 +30,6 @@
#include <stdio.h>
#include <errno.h>
-#include <liboil/liboilfuncs.h>
-#include <liboil/liboil.h>
-
#include <pulse/timeval.h>
#include <pulsecore/log.h>
@@ -106,29 +103,41 @@ void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
return p;
}
+#define VOLUME_PADDING 32
+
static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
- unsigned channel;
+ unsigned channel, nchannels, padding;
pa_assert(linear);
pa_assert(volume);
- for (channel = 0; channel < volume->channels; channel++)
+ 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;
+ unsigned channel, nchannels, padding;
pa_assert(linear);
pa_assert(volume);
- for (channel = 0; channel < volume->channels; channel++)
+ 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];
+ float linear[PA_CHANNELS_MAX + VOLUME_PADDING];
pa_assert(streams);
pa_assert(spec);
@@ -147,7 +156,7 @@ static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned n
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];
+ float linear[PA_CHANNELS_MAX + VOLUME_PADDING];
pa_assert(streams);
pa_assert(spec);
@@ -690,6 +699,28 @@ size_t pa_mix(
return length;
}
+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,
@@ -697,6 +728,8 @@ void pa_volume_memchunk(
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);
@@ -714,337 +747,19 @@ void pa_volume_memchunk(
return;
}
- ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
-
- switch (spec->format) {
-
- case PA_SAMPLE_S16NE: {
- int16_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (int16_t*) ptr + c->length/sizeof(int16_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- /* Multiplying the 32bit volume factor with the 16bit
- * sample might result in an 48bit value. We want to
- * do without 64 bit integers and hence do the
- * multiplication independantly for the HI and LO part
- * of the volume. */
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t)(*d);
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
- *d = (int16_t) t;
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
-
- break;
- }
-
- case PA_SAMPLE_S16RE: {
- int16_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (int16_t*) ptr + c->length/sizeof(int16_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t) PA_INT16_SWAP(*d);
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
- *d = PA_INT16_SWAP((int16_t) t);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
-
- break;
- }
-
- case PA_SAMPLE_S32NE: {
- int32_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (int32_t*) ptr + c->length/sizeof(int32_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int64_t t;
-
- t = (int64_t)(*d);
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- *d = (int32_t) t;
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S32RE: {
- int32_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (int32_t*) ptr + c->length/sizeof(int32_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int64_t t;
-
- t = (int64_t) PA_INT32_SWAP(*d);
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- *d = PA_INT32_SWAP((int32_t) t);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S24NE: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d += 3) {
- int64_t t;
-
- t = (int64_t)((int32_t) (PA_READ24NE(d) << 8));
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- PA_WRITE24NE(d, ((uint32_t) (int32_t) t) >> 8);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S24RE: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d += 3) {
- int64_t t;
-
- t = (int64_t)((int32_t) (PA_READ24RE(d) << 8));
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- PA_WRITE24RE(d, ((uint32_t) (int32_t) t) >> 8);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S24_32NE: {
- uint32_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint32_t*) ptr + c->length/sizeof(uint32_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int64_t t;
-
- t = (int64_t) ((int32_t) (*d << 8));
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- *d = ((uint32_t) ((int32_t) t)) >> 8;
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S24_32RE: {
- uint32_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint32_t*) ptr + c->length/sizeof(uint32_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int64_t t;
-
- t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*d) << 8));
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- *d = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_U8: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t) *d - 0x80;
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
- *d = (uint8_t) (t + 0x80);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_ULAW: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t) st_ulaw2linear16(*d);
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
- *d = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_ALAW: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t) st_alaw2linear16(*d);
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
- *d = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_FLOAT32NE: {
- float *d;
- int skip;
- unsigned n;
- unsigned channel;
-
- d = ptr;
- skip = (int) (spec->channels * sizeof(float));
- n = (unsigned) (c->length/sizeof(float)/spec->channels);
-
- for (channel = 0; channel < spec->channels; channel ++) {
- float v, *t;
-
- if (PA_UNLIKELY(volume->values[channel] == PA_VOLUME_NORM))
- continue;
-
- v = (float) pa_sw_volume_to_linear(volume->values[channel]);
- t = d + channel;
- oil_scalarmult_f32(t, skip, t, skip, &v, (int) n);
- }
- break;
- }
-
- case PA_SAMPLE_FLOAT32RE: {
- float *d, *e;
- unsigned channel;
- float linear[PA_CHANNELS_MAX];
-
- calc_linear_float_volume(linear, volume);
-
- e = (float*) ptr + c->length/sizeof(float);
-
- for (channel = 0, d = ptr; d < e; d++) {
- float t;
+ if (spec->format < 0 || spec->format > PA_SAMPLE_MAX) {
+ pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
+ return;
+ }
- t = PA_FLOAT32_SWAP(*d);
- t *= linear[channel];
- *d = PA_FLOAT32_SWAP(t);
+ do_volume = pa_get_volume_func (spec->format);
+ pa_assert(do_volume);
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
-
- break;
- }
+ calc_volume_table[spec->format] ((void *)linear, volume);
+ ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
- default:
- pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
- /* If we cannot change the volume, we just don't do it */
- }
+ do_volume (ptr, (void *)linear, spec->channels, c->length);
pa_memblock_release(c->memblock);
}
@@ -1090,7 +805,7 @@ void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, u
d = (uint8_t*) dst + c * ss;
for (j = 0; j < n; j ++) {
- oil_memcpy(d, s, (int) ss);
+ memcpy(d, s, (int) ss);
s = (uint8_t*) s + ss;
d = (uint8_t*) d + fs;
}
@@ -1118,7 +833,7 @@ void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss,
d = dst[c];
for (j = 0; j < n; j ++) {
- oil_memcpy(d, s, (int) ss);
+ memcpy(d, s, (int) ss);
s = (uint8_t*) s + fs;
d = (uint8_t*) d + ss;
}
@@ -1227,10 +942,15 @@ void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const vo
s = src; d = dst;
if (format == PA_SAMPLE_FLOAT32NE) {
+ for (; n > 0; n--) {
+ float f;
- float minus_one = -1.0, plus_one = 1.0;
- oil_clip_f32(d, (int) dstr, s, (int) sstr, (int) n, &minus_one, &plus_one);
+ 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);
@@ -1336,3 +1056,13 @@ void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned
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
index 6a306c11..d0235d60 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -86,6 +86,13 @@ 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, 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) \
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 43b8cb3e..0fefdf1c 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -28,8 +28,6 @@
#include <inttypes.h>
#include <stdio.h>
-#include <liboil/liboilfuncs.h>
-
#include <pulsecore/sconv.h>
#include <pulsecore/macro.h>
#include <pulsecore/log.h>
@@ -86,17 +84,13 @@ void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
pa_assert(b);
#if SWAP_WORDS == 1
-
for (; n > 0; n--) {
int16_t s = *(a++);
*(b++) = ((float) INT16_FROM(s))/(float) 0x7FFF;
}
-
#else
-{
- static const double add = 0, factor = 1.0/0x7FFF;
- oil_scaleconv_f32_s16(b, a, (int) n, &add, &factor);
-}
+ for (; n > 0; n--)
+ *(b++) = ((float) (*(a++)))/(float) 0x7FFF;
#endif
}
@@ -105,17 +99,13 @@ void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {
pa_assert(b);
#if SWAP_WORDS == 1
-
for (; n > 0; n--) {
int32_t s = *(a++);
*(b++) = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
}
-
#else
-{
- static const double add = 0, factor = 1.0/0x7FFFFFFF;
- oil_scaleconv_f32_s32(b, a, (int) n, &add, &factor);
-}
+ for (; n > 0; n--)
+ *(b++) = (float) (((double) (*(a++)))/0x7FFFFFFF);
#endif
}
@@ -124,7 +114,6 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
pa_assert(b);
#if SWAP_WORDS == 1
-
for (; n > 0; n--) {
int16_t s;
float v = *(a++);
@@ -133,12 +122,13 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
s = (int16_t) lrintf(v * 0x7FFF);
*(b++) = INT16_TO(s);
}
-
#else
-{
- static const double add = 0, factor = 0x7FFF;
- oil_scaleconv_s16_f32(b, a, (int) n, &add, &factor);
-}
+ for (; n > 0; n--) {
+ float v = *(a++);
+
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.f);
+ *(b++) = (int16_t) lrintf(v * 0x7FFF);
+ }
#endif
}
@@ -147,7 +137,6 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
pa_assert(b);
#if SWAP_WORDS == 1
-
for (; n > 0; n--) {
int32_t s;
float v = *(a++);
@@ -156,12 +145,13 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
*(b++) = INT32_TO(s);
}
-
#else
-{
- static const double add = 0, factor = 0x7FFFFFFF;
- oil_scaleconv_s32_f32(b, a, (int) n, &add, &factor);
-}
+ for (; n > 0; n--) {
+ float v = *(a++);
+
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+ *(b++) = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+ }
#endif
}
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index d89f4283..301f08b4 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -27,9 +27,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <liboil/liboilfuncs.h>
-#include <liboil/liboil.h>
-
#include <pulsecore/g711.h>
#include <pulsecore/macro.h>
@@ -41,32 +38,31 @@
/* u8 */
static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) {
- static const double add = -1, factor = 1.0/128.0;
-
pa_assert(a);
pa_assert(b);
- oil_scaleconv_f32_u8(b, a, (int) n, &add, &factor);
+ 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) {
- static const double add = 128, factor = 127.0;
-
pa_assert(a);
pa_assert(b);
- oil_scaleconv_u8_f32(b, a, (int) n, &add, &factor);
+ 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) {
- static const int16_t add = -0x80, factor = 0x100;
-
pa_assert(a);
pa_assert(b);
- oil_conv_s16_u8(b, 2, a, 1, (int) n);
- oil_scalaradd_s16(b, 2, b, 2, &add, (int) n);
- oil_scalarmult_s16(b, 2, b, 2, &factor, (int) n);
+ 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) {
@@ -84,7 +80,7 @@ static void float32ne_to_float32ne(unsigned n, const float *a, float *b) {
pa_assert(a);
pa_assert(b);
- oil_memcpy(b, a, (int) (sizeof(float) * n));
+ memcpy(b, a, (int) (sizeof(float) * n));
}
static void float32re_to_float32ne(unsigned n, const float *a, float *b) {
@@ -101,7 +97,7 @@ static void s16ne_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
pa_assert(a);
pa_assert(b);
- oil_memcpy(b, a, (int) (sizeof(int16_t) * n));
+ memcpy(b, a, (int) (sizeof(int16_t) * n));
}
static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
@@ -188,98 +184,130 @@ static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *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) {
- static const pa_convert_func_t table[] = {
- [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_float32ne,
- [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_float32ne,
- [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_float32ne,
- [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
- [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
- [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
- [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
- [PA_SAMPLE_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_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return to_float32ne_table[f];
+}
+
+void pa_set_convert_to_float32ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[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) {
- static const pa_convert_func_t table[] = {
- [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_float32ne,
- [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
- [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
- [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
- [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
- [PA_SAMPLE_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_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return from_float32ne_table[f];
+}
+
+void pa_set_convert_from_float32ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[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) {
- static const pa_convert_func_t table[] = {
- [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_s16ne,
- [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
- [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
- [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
- [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
- [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
- [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
- [PA_SAMPLE_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_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return to_s16ne_table[f];
+}
+
+void pa_set_convert_to_s16ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[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) {
- static const pa_convert_func_t table[] = {
- [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_s16ne,
- [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
- [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
- [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
- [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
- [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
- [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
- [PA_SAMPLE_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_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return from_s16ne_table[f];
+}
+
+void pa_set_convert_from_s16ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[f];
+ from_s16ne_table[f] = func;
}
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
index b00a16a4..cd937559 100644
--- a/src/pulsecore/sconv.h
+++ b/src/pulsecore/sconv.h
@@ -33,4 +33,10 @@ pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) P
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_sse.c b/src/pulsecore/sconv_sse.c
new file mode 100644
index 00000000..3737af2a
--- /dev/null
+++ b/src/pulsecore/sconv_sse.c
@@ -0,0 +1,235 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulsecore/g711.h>
+#include <pulsecore/macro.h>
+
+#include "endianmacros.h"
+
+#include "cpu-x86.h"
+#include "sconv.h"
+
+#if defined (__i386__) || defined (__amd64__)
+
+static const PA_DECLARE_ALIGNED (16, float, one[4]) = { 1.0, 1.0, 1.0, 1.0 };
+static const PA_DECLARE_ALIGNED (16, float, mone[4]) = { -1.0, -1.0, -1.0, -1.0 };
+static const PA_DECLARE_ALIGNED (16, float, scale[4]) = { 0x7fff, 0x7fff, 0x7fff, 0x7fff };
+
+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"
+ " movaps %6, %%xmm6 \n\t"
+ " movaps %7, %%xmm7 \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 (%2, %0, 2), %%xmm0 \n\t" /* read 8 floats */
+ " movups 16(%2, %0, 2), %%xmm2 \n\t"
+ " minps %%xmm5, %%xmm0 \n\t" /* clamp to 1.0 */
+ " minps %%xmm5, %%xmm2 \n\t"
+ " maxps %%xmm6, %%xmm0 \n\t" /* clamp to -1.0 */
+ " maxps %%xmm6, %%xmm2 \n\t"
+ " mulps %%xmm7, %%xmm0 \n\t" /* *= 0x7fff */
+ " mulps %%xmm7, %%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, (%3, %0) \n\t"
+ " movq %%mm2, 8(%3, %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 4f \n\t"
+
+ "3: \n\t"
+ " movss (%2, %0, 2), %%xmm0 \n\t"
+ " minss %%xmm5, %%xmm0 \n\t"
+ " maxss %%xmm6, %%xmm0 \n\t"
+ " mulss %%xmm7, %%xmm0 \n\t"
+ " cvtss2si %%xmm0, %4 \n\t"
+ " movw %w4, (%3, %0) \n\t"
+ " add $2, %0 \n\t"
+ " dec %1 \n\t"
+ " jne 3b \n\t"
+
+ "4: \n\t"
+ " emms \n\t"
+
+ : "=&r" (i), "=&r" (temp)
+ : "r" (a), "r" (b), "r" ((pa_reg_x86)n), "m" (*one), "m" (*mone), "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"
+ " movaps %6, %%xmm6 \n\t"
+ " movaps %7, %%xmm7 \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 (%2, %0, 2), %%xmm0 \n\t" /* read 8 floats */
+ " movups 16(%2, %0, 2), %%xmm2 \n\t"
+ " minps %%xmm5, %%xmm0 \n\t" /* clamp to 1.0 */
+ " minps %%xmm5, %%xmm2 \n\t"
+ " maxps %%xmm6, %%xmm0 \n\t" /* clamp to -1.0 */
+ " maxps %%xmm6, %%xmm2 \n\t"
+ " mulps %%xmm7, %%xmm0 \n\t" /* *= 0x7fff */
+ " mulps %%xmm7, %%xmm2 \n\t"
+
+ " cvtps2dq %%xmm0, %%xmm0 \n\t"
+ " cvtps2dq %%xmm2, %%xmm2 \n\t"
+
+ " packssdw %%xmm2, %%xmm0 \n\t"
+ " movdqu %%xmm0, (%3, %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 4f \n\t"
+
+ "3: \n\t"
+ " movss (%2, %0, 2), %%xmm0 \n\t"
+ " minss %%xmm5, %%xmm0 \n\t"
+ " maxss %%xmm6, %%xmm0 \n\t"
+ " mulss %%xmm7, %%xmm0 \n\t"
+ " cvtss2si %%xmm0, %4 \n\t"
+ " movw %w4, (%3, %0) \n\t"
+ " add $2, %0 \n\t"
+ " dec %1 \n\t"
+ " jne 3b \n\t"
+
+ "4: \n\t"
+
+ : "=&r" (i), "=&r" (temp)
+ : "r" (a), "r" (b), "r" ((pa_reg_x86)n), "m" (*one), "m" (*mone), "m" (*scale)
+ : "cc", "memory"
+ );
+}
+
+#undef RUN_TEST
+
+#ifdef RUN_TEST
+#define SAMPLES 1019
+#define TIMES 1000
+
+static void run_test (void) {
+ int16_t samples[SAMPLES];
+ int16_t samples_ref[SAMPLES];
+ float floats[SAMPLES];
+ int i;
+ pa_usec_t start, stop;
+ pa_convert_func_t func;
+
+ printf ("checking SSE %zd\n", sizeof (samples));
+
+ memset (samples_ref, 0, sizeof (samples_ref));
+ memset (samples, 0, sizeof (samples));
+
+ for (i = 0; i < SAMPLES; i++) {
+ floats[i] = (rand()/(RAND_MAX+2.2)) - 1.1;
+ }
+
+ func = pa_get_convert_from_float32ne_function (PA_SAMPLE_S16LE);
+ func (SAMPLES, floats, samples_ref);
+ pa_sconv_s16le_from_f32ne_sse2 (SAMPLES, floats, samples);
+
+ for (i = 0; i < SAMPLES; i++) {
+ if (samples[i] != samples_ref[i]) {
+ printf ("%d: %04x != %04x (%f)\n", i, samples[i], samples_ref[i],
+ floats[i]);
+ }
+ }
+
+ start = pa_rtclock_now();
+ for (i = 0; i < TIMES; i++) {
+ pa_sconv_s16le_from_f32ne_sse2 (SAMPLES, floats, samples);
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("SSE: %llu usec.", (long long unsigned int)(stop - start));
+
+ start = pa_rtclock_now();
+ for (i = 0; i < TIMES; i++) {
+ func (SAMPLES, floats, samples_ref);
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
+}
+#endif
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+
+void pa_convert_func_init_sse (pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+#ifdef RUN_TEST
+ run_test ();
+#endif
+
+ 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);
+ } else {
+ 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);
+ }
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index 6e428426..fbf777a4 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -60,7 +60,8 @@
#define MADV_REMOVE 9
#endif
-#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
+/* 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
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index a5f96351..744c47ff 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -44,14 +44,15 @@
#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
#define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
-static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
static void sink_input_free(pa_object *o);
+static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
pa_assert(data);
- memset(data, 0, sizeof(*data));
+ pa_zero(*data);
data->resample_method = PA_RESAMPLER_INVALID;
data->proplist = pa_proplist_new();
@@ -91,6 +92,18 @@ void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, co
}
}
+void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor) {
+ pa_assert(data);
+ pa_assert(volume_factor);
+
+ if (data->volume_factor_sink_is_set)
+ pa_sw_cvolume_multiply(&data->volume_factor_sink, &data->volume_factor_sink, volume_factor);
+ else {
+ data->volume_factor_sink_is_set = TRUE;
+ data->volume_factor_sink = *volume_factor;
+ }
+}
+
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
pa_assert(data);
@@ -114,6 +127,7 @@ static void reset_callbacks(pa_sink_input *i) {
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;
@@ -124,14 +138,15 @@ static void reset_callbacks(pa_sink_input *i) {
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_flags_t flags) {
+ pa_sink_input_new_data *data) {
pa_sink_input *i;
pa_resampler *resampler = NULL;
@@ -142,6 +157,7 @@ int pa_sink_input_new(
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);
@@ -172,7 +188,6 @@ int pa_sink_input_new(
pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
}
- pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
if (!data->volume_is_set) {
@@ -181,27 +196,30 @@ int pa_sink_input_new(
data->save_volume = FALSE;
}
- pa_return_val_if_fail(pa_cvolume_valid(&data->volume), -PA_ERR_INVALID);
pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
if (!data->volume_factor_is_set)
pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
- pa_return_val_if_fail(pa_cvolume_valid(&data->volume_factor), -PA_ERR_INVALID);
pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID);
+ if (!data->volume_factor_sink_is_set)
+ pa_cvolume_reset(&data->volume_factor_sink, data->sink->sample_spec.channels);
+
+ pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor_sink, &data->sink->sample_spec), -PA_ERR_INVALID);
+
if (!data->muted_is_set)
data->muted = FALSE;
- if (flags & PA_SINK_INPUT_FIX_FORMAT)
+ if (data->flags & PA_SINK_INPUT_FIX_FORMAT)
data->sample_spec.format = data->sink->sample_spec.format;
- if (flags & PA_SINK_INPUT_FIX_RATE)
+ if (data->flags & PA_SINK_INPUT_FIX_RATE)
data->sample_spec.rate = data->sink->sample_spec.rate;
original_cm = data->channel_map;
- if (flags & PA_SINK_INPUT_FIX_CHANNELS) {
+ if (data->flags & PA_SINK_INPUT_FIX_CHANNELS) {
data->sample_spec.channels = data->sink->sample_spec.channels;
data->channel_map = data->sink->channel_map;
}
@@ -220,7 +238,7 @@ int pa_sink_input_new(
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
return r;
- if ((flags & PA_SINK_INPUT_FAIL_ON_SUSPEND) &&
+ 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;
@@ -231,7 +249,7 @@ int pa_sink_input_new(
return -PA_ERR_TOOLARGE;
}
- if ((flags & PA_SINK_INPUT_VARIABLE_RATE) ||
+ 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)) {
@@ -240,9 +258,9 @@ int pa_sink_input_new(
&data->sample_spec, &data->channel_map,
&data->sink->sample_spec, &data->sink->channel_map,
data->resample_method,
- ((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
- ((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
- (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+ ((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->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -PA_ERR_NOTSUPPORTED;
@@ -255,7 +273,7 @@ int pa_sink_input_new(
i->core = core;
i->state = PA_SINK_INPUT_INIT;
- i->flags = flags;
+ 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;
@@ -268,18 +286,21 @@ int pa_sink_input_new(
i->channel_map = data->channel_map;
if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !data->volume_is_absolute) {
+ 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 */
-
- pa_cvolume v = data->sink->reference_volume;
- pa_cvolume_remap(&v, &data->sink->channel_map, &data->channel_map);
- pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &v);
+ 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->virtual_volume = data->volume;
+ i->volume = data->volume;
i->volume_factor = data->volume_factor;
- pa_cvolume_init(&i->soft_volume);
- memset(i->relative_volume, 0, sizeof(i->relative_volume));
+ i->volume_factor_sink = data->volume_factor_sink;
+ 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->save_volume = data->save_volume;
i->save_sink = data->save_sink;
i->save_muted = data->save_muted;
@@ -348,6 +369,7 @@ int pa_sink_input_new(
/* 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;
@@ -362,6 +384,7 @@ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
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;
@@ -400,7 +423,9 @@ static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state)
void pa_sink_input_unlink(pa_sink_input *i) {
pa_bool_t linked;
pa_source_output *o, *p = NULL;
+
pa_assert(i);
+ pa_assert_ctl_context();
/* See pa_sink_unlink() for a couple of comments how this function
* works */
@@ -439,11 +464,8 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (linked && i->sink) {
/* We might need to update the sink's volume if we are in flat volume mode. */
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
- }
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
+ 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);
@@ -471,6 +493,7 @@ 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);
if (PA_SINK_INPUT_IS_LINKED(i->state))
@@ -478,7 +501,10 @@ static void sink_input_free(pa_object *o) {
pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
- pa_assert(!i->thread_info.attached);
+ /* 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);
@@ -502,7 +528,9 @@ static void sink_input_free(pa_object *o) {
/* 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);
@@ -517,12 +545,10 @@ void pa_sink_input_put(pa_sink_input *i) {
i->state = state;
/* We might need to update the sink's volume if we are in flat volume mode. */
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
- } else
- pa_sink_input_set_relative_volume(i, &i->virtual_volume);
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
+ pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume);
+ else
+ set_real_ratio(i, &i->volume);
i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted;
@@ -538,6 +564,7 @@ void pa_sink_input_put(pa_sink_input *i) {
/* 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);
@@ -548,6 +575,7 @@ 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);
@@ -563,12 +591,13 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
/* Called from thread context */
void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
- pa_bool_t do_volume_adj_here;
+ pa_bool_t do_volume_adj_here, need_volume_factor_sink;
pa_bool_t volume_is_norm;
size_t block_size_max_sink, block_size_max_sink_input;
size_t ilength;
pa_sink_input_assert_ref(i);
+ pa_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);
@@ -610,6 +639,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
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;
@@ -641,6 +671,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
while (tchunk.length > 0) {
pa_memchunk wchunk;
+ pa_bool_t nvfs = need_volume_factor_sink;
wchunk = tchunk;
pa_memblock_ref(wchunk.memblock);
@@ -652,18 +683,41 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
if (do_volume_adj_here && !volume_is_norm) {
pa_memchunk_make_writable(&wchunk, 0);
- if (i->thread_info.muted)
+ if (i->thread_info.muted) {
pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
- else
+ 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 (!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 {
+ } else {
pa_memchunk rchunk;
pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk);
+ if (nvfs) {
+ pa_memchunk_make_writable(&rchunk, 0);
+ pa_volume_memchunk(&rchunk, &i->sink->sample_spec, &i->volume_factor_sink);
+ }
+
/* pa_log_debug("pushing %lu", (unsigned long) rchunk.length); */
if (rchunk.memblock) {
@@ -706,8 +760,9 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
/* 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_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);
@@ -721,8 +776,9 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec *
void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
size_t lbq;
pa_bool_t called = FALSE;
- pa_sink_input_assert_ref(i);
+ 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));
@@ -790,8 +846,28 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
}
/* 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));
@@ -804,6 +880,7 @@ void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the
/* 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));
@@ -814,15 +891,16 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the
/* 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->fixed_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);
+ pa_sink_invalidate_requested_latency(i->sink, TRUE);
return usec;
}
@@ -830,6 +908,7 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa
/* 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);
@@ -841,7 +920,7 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec)
if (i->sink) {
if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
- usec = i->sink->fixed_latency;
+ usec = pa_sink_get_fixed_latency(i->sink);
if (usec != (pa_usec_t) -1) {
pa_usec_t min_latency, max_latency;
@@ -858,6 +937,7 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t 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;
@@ -872,121 +952,101 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
}
/* 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 context */
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t 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(pa_cvolume_compatible(volume, &i->sample_spec));
+ pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
v = i->sink->reference_volume;
pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
- volume = pa_sw_cvolume_multiply(&v, &v, volume);
+
+ 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->virtual_volume))
+ if (pa_cvolume_equal(volume, &i->volume)) {
+ i->save_volume = i->save_volume || save;
return;
+ }
- i->virtual_volume = *volume;
+ i->volume = *volume;
i->save_volume = save;
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
-
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
/* We are in flat volume mode, so let's update all sink input
* volumes and update the flat volume of the sink */
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE, FALSE);
-
- } else {
+ pa_sink_set_volume(i->sink, NULL, TRUE, save);
+ else {
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
- pa_sink_input_set_relative_volume(i, volume);
-
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ set_real_ratio(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);
}
- /* The virtual volume changed, let's tell people so */
+ /* The volume changed, let's tell people so */
+ if (i->volume_changed)
+ i->volume_changed(i);
+
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
/* Called from main context */
pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
- pa_cvolume v = i->sink->reference_volume;
- pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
- pa_sw_cvolume_divide(volume, &i->virtual_volume, &v);
- } else
- *volume = i->virtual_volume;
+ if (absolute || !(i->sink->flags & PA_SINK_FLAT_VOLUME))
+ *volume = i->volume;
+ else
+ *volume = i->reference_ratio;
return volume;
}
/* Called from main context */
-pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) {
- unsigned c;
-
- pa_sink_input_assert_ref(i);
- pa_assert(v);
- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-
- /* This always returns the relative volume. Converts the float
- * version into a pa_cvolume */
-
- v->channels = i->sample_spec.channels;
-
- for (c = 0; c < v->channels; c++)
- v->values[c] = pa_sw_volume_from_linear(i->relative_volume[c]);
-
- return v;
-}
-
-/* Called from main context */
-void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v) {
- unsigned c;
- pa_cvolume _v;
-
- pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
-
- if (!v)
- v = pa_cvolume_reset(&_v, i->sample_spec.channels);
-
- /* This basically calculates:
- *
- * i->relative_volume := v
- * i->soft_volume := i->relative_volume * i->volume_factor */
-
- i->soft_volume.channels = i->sample_spec.channels;
-
- for (c = 0; c < i->sample_spec.channels; c++) {
- i->relative_volume[c] = pa_sw_volume_to_linear(v->values[c]);
-
- i->soft_volume.values[c] = pa_sw_volume_from_linear(
- i->relative_volume[c] *
- pa_sw_volume_to_linear(i->volume_factor.values[c]));
- }
-
- /* We don't copy the data to the thread_info data. That's left for someone else to do */
-}
-
-/* Called from main context */
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {
- pa_assert(i);
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (!i->muted == !mute)
@@ -996,12 +1056,18 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {
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);
}
/* Called from main context */
pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return i->muted;
@@ -1010,6 +1076,7 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
/* Called from main thread */
void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
if (p)
pa_proplist_update(i->proplist, mode, p);
@@ -1023,6 +1090,7 @@ void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_p
/* Called from main context */
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
pa_sink_input_assert_ref(i);
+ pa_assert_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);
@@ -1031,6 +1099,7 @@ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
/* 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);
@@ -1049,13 +1118,14 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
const char *old;
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
if (!name && !pa_proplist_contains(i->proplist, PA_PROP_MEDIA_NAME))
return;
old = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME);
- if (old && name && !strcmp(old, name))
+ if (old && name && pa_streq(old, name))
return;
if (name)
@@ -1072,6 +1142,7 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
/* 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;
}
@@ -1079,6 +1150,7 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
/* Called from main context */
pa_bool_t 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)
@@ -1095,6 +1167,7 @@ pa_bool_t pa_sink_input_may_move(pa_sink_input *i) {
/* Called from main context */
pa_bool_t 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);
@@ -1119,10 +1192,10 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
/* Called from main context */
int pa_sink_input_start_move(pa_sink_input *i) {
pa_source_output *o, *p = NULL;
- pa_sink *origin;
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);
@@ -1132,8 +1205,6 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if ((r = pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i)) < 0)
return r;
- origin = i->sink;
-
/* Kill directly connected outputs */
while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
pa_assert(o != p);
@@ -1147,24 +1218,15 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
pa_assert_se(i->sink->n_corked-- >= 1);
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
-
- /* Make the virtual volume relative */
- pa_sink_input_get_relative_volume(i, &i->virtual_volume);
-
- /* And reset the the relative volume */
- pa_sink_input_set_relative_volume(i, NULL);
-
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
/* We might need to update the sink's volume if we are in flat
* volume mode. */
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
- }
+ 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_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map);
i->sink = NULL;
pa_sink_input_unref(i);
@@ -1177,6 +1239,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
pa_resampler *new_resampler;
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);
@@ -1218,6 +1281,8 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
i->save_sink = save;
pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);
+ 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++;
@@ -1243,16 +1308,15 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
pa_sink_update_status(dest);
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
+ pa_cvolume remapped;
- /* Make relative volume absolute again */
- pa_cvolume t = dest->reference_volume;
- pa_cvolume_remap(&t, &dest->channel_map, &i->channel_map);
- pa_sw_cvolume_multiply(&i->virtual_volume, &i->virtual_volume, &t);
+ /* Make relative volumes absolute */
+ remapped = dest->reference_volume;
+ pa_cvolume_remap(&remapped, &dest->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &i->reference_ratio, &remapped);
/* We might need to update the sink's volume if we are in flat volume mode. */
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
@@ -1261,16 +1325,39 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
/* Notify everyone */
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
+
+ if (i->volume_changed)
+ i->volume_changed(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, pa_bool_t 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);
@@ -1289,6 +1376,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
}
if ((r = pa_sink_input_finish_move(i, dest, save)) < 0) {
+ pa_sink_input_fail_move(i);
pa_sink_input_unref(i);
return r;
}
@@ -1301,7 +1389,9 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
/* Called from IO thread context */
void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
pa_bool_t corking, uncorking;
+
pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
if (state == i->thread_info.state)
return;
@@ -1411,6 +1501,7 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
/* 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;
@@ -1421,6 +1512,7 @@ pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
/* Called from IO context */
pa_bool_t 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);
@@ -1429,7 +1521,13 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
}
/* Called from IO context */
-void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render) {
+void pa_sink_input_request_rewind(
+ pa_sink_input *i,
+ size_t nbytes /* in our sample spec */,
+ pa_bool_t rewrite,
+ pa_bool_t flush,
+ pa_bool_t dont_rewind_render) {
+
size_t lbq;
/* If 'rewrite' is TRUE the sink is rewound as far as requested
@@ -1444,18 +1542,20 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
* dont_rewind_render is TRUE then the render memblockq is not
* rewound. */
- pa_sink_input_assert_ref(i);
-
- nbytes = PA_MAX(i->thread_info.rewrite_nbytes, nbytes);
+ /* nbytes = 0 means maximum rewind request */
-/* pa_log_debug("request rewrite %lu", (unsigned long) nbytes); */
+ 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;
- pa_assert(rewrite || flush);
- pa_assert(!dont_rewind_render || !rewrite);
+ nbytes = PA_MAX(i->thread_info.rewrite_nbytes, nbytes);
+
+ /* pa_log_debug("request rewrite %zu", nbytes); */
/* Calculate how much we can rewind locally without having to
* touch the sink */
@@ -1475,6 +1575,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
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 */
@@ -1511,8 +1612,11 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
/* 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,
@@ -1529,6 +1633,7 @@ void pa_sink_input_send_event(pa_sink_input *i, const char *event, pa_proplist *
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)
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 98144d41..415a801f 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -42,6 +42,7 @@ typedef enum pa_sink_input_state {
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 pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) {
@@ -58,7 +59,8 @@ typedef enum pa_sink_input_flags {
PA_SINK_INPUT_FIX_RATE = 64,
PA_SINK_INPUT_FIX_CHANNELS = 128,
PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
- PA_SINK_INPUT_FAIL_ON_SUSPEND = 512
+ PA_SINK_INPUT_NO_CREATE_ON_SUSPEND = 512,
+ PA_SINK_INPUT_KILL_ON_SUSPEND = 1024
} pa_sink_input_flags_t;
struct pa_sink_input {
@@ -92,10 +94,14 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
/* Also see http://pulseaudio.org/wiki/InternalVolumes */
- pa_cvolume virtual_volume; /* The volume clients are informed about */
- 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 */
- double relative_volume[PA_CHANNELS_MAX]; /* The calculated volume relative to the sink volume as linear factors. */
- pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as relative_volume * volume_factor */
+ 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 */
+ 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_sink; /* A second volume factor in format of the sink this stream is connected to */
+
pa_bool_t muted:1;
/* if TRUE then the source we are connected to and/or the volume
@@ -137,6 +143,10 @@ struct pa_sink_input {
* from IO context. */
void (*update_sink_latency_range) (pa_sink_input *i); /* may be NULL */
+ /* Called whenver 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
@@ -159,7 +169,9 @@ struct pa_sink_input {
/* 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. */
+ * 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
@@ -182,8 +194,16 @@ struct pa_sink_input {
pa_bool_t (*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. */
- void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data);
+ * 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;
@@ -194,7 +214,7 @@ struct pa_sink_input {
pa_bool_t attached:1; /* True only between ->attach() and ->detach() calls */
- /* 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */
+ /* rewrite_nbytes: 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */
pa_bool_t rewrite_flush:1, dont_rewind_render:1;
size_t rewrite_nbytes;
uint64_t underrun_for, playing_for;
@@ -217,7 +237,7 @@ struct pa_sink_input {
void *userdata;
};
-PA_DECLARE_CLASS(pa_sink_input);
+PA_DECLARE_PUBLIC_CLASS(pa_sink_input);
#define PA_SINK_INPUT(o) pa_sink_input_cast(o)
enum {
@@ -238,6 +258,8 @@ typedef struct pa_sink_input_send_event_hook_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;
@@ -253,13 +275,13 @@ typedef struct pa_sink_input_new_data {
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_cvolume volume, volume_factor;
+ pa_cvolume volume, volume_factor, volume_factor_sink;
pa_bool_t muted:1;
pa_bool_t sample_spec_is_set:1;
pa_bool_t channel_map_is_set:1;
- pa_bool_t volume_is_set:1, volume_factor_is_set:1;
+ pa_bool_t volume_is_set:1, volume_factor_is_set:1, volume_factor_sink_is_set:1;
pa_bool_t muted_is_set:1;
pa_bool_t volume_is_absolute:1;
@@ -272,6 +294,7 @@ void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
+void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
@@ -280,8 +303,7 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
int pa_sink_input_new(
pa_sink_input **i,
pa_core *core,
- pa_sink_input_new_data *data,
- pa_sink_input_flags_t flags);
+ pa_sink_input_new_data *data);
void pa_sink_input_put(pa_sink_input *i);
void pa_sink_input_unlink(pa_sink_input* i);
@@ -303,6 +325,10 @@ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+/* 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 */
@@ -313,8 +339,6 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);
pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute);
-pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v);
-
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
@@ -333,6 +357,7 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may thi
* 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, pa_bool_t 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);
@@ -356,7 +381,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
-/* To be used by sink.c only */
-void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v);
+#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
index d8f3c7d1..bda92fcc 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -52,14 +52,14 @@
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
-static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_sink, pa_msgobject);
static void sink_free(pa_object *s);
pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
pa_assert(data);
- memset(data, 0, sizeof(*data));
+ pa_zero(*data);
data->proplist = pa_proplist_new();
return data;
@@ -177,6 +177,7 @@ pa_sink* pa_sink_new(
pa_assert(core);
pa_assert(data);
pa_assert(data->name);
+ pa_assert_ctl_context();
s = pa_msgobject_new(pa_sink);
@@ -211,7 +212,7 @@ pa_sink* pa_sink_new(
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
- pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
if (!data->muted_is_set)
data->muted = FALSE;
@@ -235,6 +236,7 @@ pa_sink* pa_sink_new(
s->core = core;
s->state = PA_SINK_INIT;
s->flags = flags;
+ s->priority = 0;
s->suspend_cause = 0;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
@@ -242,26 +244,25 @@ pa_sink* pa_sink_new(
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->inputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
- s->reference_volume = s->virtual_volume = data->volume;
+ 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;
- s->fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
-
reset_callbacks(s);
s->userdata = NULL;
s->asyncmsgq = NULL;
- s->rtpoll = NULL;
/* As a minor optimization we just steal the list instead of
* copying it here */
@@ -294,6 +295,7 @@ pa_sink* pa_sink_new(
&s->sample_spec,
0);
+ s->thread_info.rtpoll = NULL;
s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
@@ -306,7 +308,9 @@ pa_sink* pa_sink_new(
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;
+ /* 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)
@@ -348,6 +352,7 @@ pa_sink* pa_sink_new(
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;
@@ -360,6 +365,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
pa_sink_state_t original_state;
pa_assert(s);
+ pa_assert_ctl_context();
if (s->state == state)
return 0;
@@ -396,9 +402,9 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
/* We're suspending or resuming, tell everyone about it */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
+ PA_IDXSET_FOREACH(i, s->inputs, idx)
if (s->state == PA_SINK_SUSPENDED &&
- (i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND))
+ (i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND))
pa_sink_input_kill(i);
else if (i->suspend)
i->suspend(i, state == PA_SINK_SUSPENDED);
@@ -413,12 +419,12 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
/* 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);
/* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
- pa_assert(s->rtpoll);
pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
/* Generally, flags should be initialized via pa_sink_new(). As a
@@ -431,16 +437,21 @@ void pa_sink_put(pa_sink* s) {
if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes)
s->flags |= PA_SINK_FLAT_VOLUME;
+ /* 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_assert((s->flags & PA_SINK_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SINK_DECIBEL_VOLUME));
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->fixed_latency != 0));
+ 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->fixed_latency == s->fixed_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);
@@ -458,6 +469,7 @@ void pa_sink_unlink(pa_sink* s) {
pa_sink_input *i, *j = NULL;
pa_assert(s);
+ pa_assert_ctl_context();
/* Please note that pa_sink_unlink() does more than simply
* reversing pa_sink_put(). It also undoes the registrations
@@ -507,6 +519,7 @@ static void sink_free(pa_object *o) {
pa_sink_input *i;
pa_assert(s);
+ pa_assert_ctl_context();
pa_assert(pa_sink_refcnt(s) == 0);
if (PA_SINK_IS_LINKED(s->state))
@@ -547,9 +560,10 @@ static void sink_free(pa_object *o) {
pa_xfree(s);
}
-/* Called from main context */
+/* 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;
@@ -557,11 +571,32 @@ void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
pa_source_set_asyncmsgq(s->monitor_source, q);
}
-/* Called from main context */
+/* 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_assert_ref(s);
+ pa_assert_ctl_context();
+
+ if (mask == 0)
+ return;
+
+ /* For now, allow only a minimal set of flags to be changed. */
+ pa_assert((mask & ~(PA_SINK_DYNAMIC_LATENCY|PA_SINK_LATENCY)) == 0);
+
+ s->flags = (s->flags & ~mask) | (value & mask);
+
+ 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_SINK_DYNAMIC_LATENCY : 0));
+}
+
+/* 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->rtpoll = p;
+ s->thread_info.rtpoll = p;
if (s->monitor_source)
pa_source_set_rtpoll(s->monitor_source, p);
@@ -570,6 +605,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *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)
@@ -581,6 +617,7 @@ int pa_sink_update_status(pa_sink*s) {
/* Called from main context */
int pa_sink_suspend(pa_sink *s, pa_bool_t 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);
@@ -609,6 +646,7 @@ pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q) {
uint32_t idx;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
if (!q)
@@ -633,12 +671,13 @@ void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t 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_kill(i);
+ pa_sink_input_fail_move(i);
pa_sink_input_unref(i);
}
@@ -649,13 +688,13 @@ void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save) {
/* 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)))) {
- if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_OK) {
- pa_sink_input_kill(i);
- pa_sink_input_unref(i);
- }
+ pa_sink_input_fail_move(i);
+ pa_sink_input_unref(i);
}
pa_queue_free(q, NULL, NULL);
@@ -665,11 +704,15 @@ void pa_sink_move_all_fail(pa_queue *q) {
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 */
+ * 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;
@@ -682,7 +725,7 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
if (nbytes > 0)
pa_log_debug("Processing rewind...");
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
pa_sink_input_assert_ref(i);
pa_sink_input_process_rewind(i, nbytes);
}
@@ -700,6 +743,7 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns
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) {
@@ -734,18 +778,19 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns
/* Called from IO thread context */
static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
pa_sink_input *i;
- void *state = NULL;
+ 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 */
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
unsigned j;
pa_mix_info* m = NULL;
@@ -834,12 +879,11 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
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_sink_ref(s);
-
pa_assert(!s->thread_info.rewind_requested);
pa_assert(s->thread_info.rewind_nbytes == 0);
@@ -850,6 +894,8 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
return;
}
+ pa_sink_ref(s);
+
if (length <= 0)
length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
@@ -920,14 +966,13 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
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_sink_ref(s);
-
pa_assert(!s->thread_info.rewind_requested);
pa_assert(s->thread_info.rewind_nbytes == 0);
@@ -936,6 +981,8 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
return;
}
+ pa_sink_ref(s);
+
length = target->length;
block_size_max = pa_mempool_block_size_max(s->core->mempool);
if (length > block_size_max)
@@ -1003,17 +1050,23 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
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_sink_ref(s);
-
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) {
@@ -1032,91 +1085,31 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
/* Called from IO thread context */
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
- pa_mix_info info[MAX_MIX_CHANNELS];
- size_t length1st = length;
- unsigned n;
-
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_sink_ref(s);
-
pa_assert(!s->thread_info.rewind_requested);
pa_assert(s->thread_info.rewind_nbytes == 0);
- pa_assert(length > 0);
-
- n = fill_mix_info(s, &length1st, info, MAX_MIX_CHANNELS);
-
- if (n == 0) {
- pa_silence_memchunk_get(&s->core->silence_cache,
- s->core->mempool,
- result,
- &s->sample_spec,
- length1st);
- } else if (n == 1) {
- pa_cvolume volume;
-
- *result = info[0].chunk;
- pa_memblock_ref(result->memblock);
-
- if (result->length > length)
- result->length = length;
-
- pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
-
- if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
- 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 {
- pa_memchunk_make_writable(result, length);
- pa_volume_memchunk(result, &s->sample_spec, &volume);
- }
- }
- } else {
- void *ptr;
-
- result->index = 0;
- result->memblock = pa_memblock_new(s->core->mempool, length);
-
- ptr = pa_memblock_acquire(result->memblock);
-
- result->length = pa_mix(info, n,
- (uint8_t*) ptr + result->index, length1st,
- &s->sample_spec,
- &s->thread_info.soft_volume,
- s->thread_info.soft_muted);
-
- pa_memblock_release(result->memblock);
- }
+ pa_sink_ref(s);
- inputs_drop(s, info, n, result);
+ pa_sink_render(s, length, result);
if (result->length < length) {
pa_memchunk chunk;
- size_t l, d;
+
pa_memchunk_make_writable(result, length);
- l = length - result->length;
- d = result->index + result->length;
- while (l > 0) {
- chunk = *result;
- chunk.index = d;
- chunk.length = l;
+ chunk.memblock = result->memblock;
+ chunk.index = result->index + result->length;
+ chunk.length = length - result->length;
- pa_sink_render_into(s, &chunk);
+ pa_sink_render_into_full(s, &chunk);
- d += chunk.length;
- l -= chunk.length;
- }
result->length = length;
}
@@ -1128,6 +1121,7 @@ pa_usec_t pa_sink_get_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));
/* The returned value is supposed to be in the time domain of the sound card! */
@@ -1149,6 +1143,7 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
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! */
@@ -1161,7 +1156,7 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
o = PA_MSGOBJECT(s);
- /* We probably should make this a proper vtable callback instead of going through process_msg() */
+ /* FIXME: We probably should make this a proper vtable callback instead of going through process_msg() */
if (o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
return -1;
@@ -1169,108 +1164,149 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
return usec;
}
-static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volume) {
- unsigned c;
+/* Called from main context */
+static void compute_reference_ratios(pa_sink *s) {
+ uint32_t idx;
+ pa_sink_input *i;
- pa_sink_input_assert_ref(i);
- pa_assert(new_volume->channels == i->sample_spec.channels);
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
- /*
- * This basically calculates:
- *
- * i->relative_volume := i->virtual_volume / new_volume
- * i->soft_volume := i->relative_volume * i->volume_factor
- */
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ unsigned c;
+ pa_cvolume remapped;
+
+ /*
+ * Calculates the reference volume from the sink's reference
+ * volume. This basically calculates:
+ *
+ * i->reference_ratio = i->volume / s->reference_volume
+ */
- /* The new sink volume passed in here must already be remapped to
- * the sink input's channel map! */
+ remapped = s->reference_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
- i->soft_volume.channels = i->sample_spec.channels;
+ i->reference_ratio.channels = i->sample_spec.channels;
- for (c = 0; c < i->sample_spec.channels; c++)
+ for (c = 0; c < i->sample_spec.channels; c++) {
- if (new_volume->values[c] <= PA_VOLUME_MUTED)
- /* We leave i->relative_volume untouched */
- i->soft_volume.values[c] = PA_VOLUME_MUTED;
- else {
- i->relative_volume[c] =
- pa_sw_volume_to_linear(i->virtual_volume.values[c]) /
- pa_sw_volume_to_linear(new_volume->values[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(
+ i->reference_ratio.values[c],
+ remapped.values[c]) == i->volume.values[c])
+ continue;
- i->soft_volume.values[c] = pa_sw_volume_from_linear(
- i->relative_volume[c] *
- pa_sw_volume_to_linear(i->volume_factor.values[c]));
+ i->reference_ratio.values[c] = pa_sw_volume_divide(
+ i->volume.values[c],
+ remapped.values[c]);
}
+ }
+}
+
+/* Called from main context */
+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(s->flags & PA_SINK_FLAT_VOLUME);
+
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ unsigned c;
+ pa_cvolume remapped;
+
+ /*
+ * This basically calculates:
+ *
+ * i->real_ratio := i->volume / s->real_volume
+ * i->soft_volume := i->real_ratio * i->volume_factor
+ */
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ remapped = s->real_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
- /* We don't copy the soft_volume to the thread_info data
- * here. That must be done by the caller */
+ 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 */
+ }
}
/* Called from main thread */
-void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
+static void compute_real_volume(pa_sink *s) {
pa_sink_input *i;
uint32_t idx;
pa_sink_assert_ref(s);
- pa_assert(new_volume);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
- /* This is called whenever a sink input volume changes or a sink
- * input is added/removed and we might need to fix up the sink
- * volume accordingly. Please note that we don't actually update
- * the sinks volume here, we only return how it needs to be
- * updated. The caller should then call pa_sink_set_volume().*/
+ /* This determines the maximum volume of all streams and sets
+ * s->real_volume accordingly. */
if (pa_idxset_isempty(s->inputs)) {
/* In the special case that we have no sink input we leave the
* volume unmodified. */
- *new_volume = s->reference_volume;
+ s->real_volume = s->reference_volume;
return;
}
- pa_cvolume_mute(new_volume, s->channel_map.channels);
+ 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 */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- unsigned c;
- pa_cvolume remapped_volume;
-
- remapped_volume = i->virtual_volume;
- pa_cvolume_remap(&remapped_volume, &i->channel_map, &s->channel_map);
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ pa_cvolume remapped;
- for (c = 0; c < new_volume->channels; c++)
- if (remapped_volume.values[c] > new_volume->values[c])
- new_volume->values[c] = remapped_volume.values[c];
+ remapped = i->volume;
+ pa_cvolume_remap(&remapped, &i->channel_map, &s->channel_map);
+ pa_cvolume_merge(&s->real_volume, &s->real_volume, &remapped);
}
- /* Then, let's update the soft volumes of all inputs connected
- * to this sink */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- pa_cvolume remapped_new_volume;
-
- remapped_new_volume = *new_volume;
- pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
- compute_new_soft_volume(i, &remapped_new_volume);
-
- /* We don't copy soft_volume to the thread_info data here
- * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
- * want the update to be atomically with the sink volume
- * update, hence we do it within the pa_sink_set_volume() call
- * below */
- }
+ /* Then, let's update the real ratios/soft volumes of all inputs
+ * connected to this sink */
+ compute_real_ratios(s);
}
/* Called from main thread */
-void pa_sink_propagate_flat_volume(pa_sink *s) {
+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(s->flags & PA_SINK_FLAT_VOLUME);
@@ -1278,63 +1314,88 @@ void pa_sink_propagate_flat_volume(pa_sink *s) {
* caused by a sink input volume change. We need to fix up the
* sink input volumes accordingly */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- pa_cvolume sink_volume, new_virtual_volume;
- unsigned c;
-
- /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ pa_cvolume old_volume, remapped;
- sink_volume = s->virtual_volume;
- pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map);
+ old_volume = i->volume;
- for (c = 0; c < i->sample_spec.channels; c++)
- new_virtual_volume.values[c] = pa_sw_volume_from_linear(
- i->relative_volume[c] *
- pa_sw_volume_to_linear(sink_volume.values[c]));
+ /* This basically calculates:
+ *
+ * i->volume := s->reference_volume * i->reference_ratio */
- new_virtual_volume.channels = i->sample_spec.channels;
+ remapped = s->reference_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
- if (!pa_cvolume_equal(&new_virtual_volume, &i->virtual_volume)) {
- i->virtual_volume = new_virtual_volume;
+ /* The volume changed, let's tell people so */
+ if (!pa_cvolume_equal(&old_volume, &i->volume)) {
- /* Hmm, the soft volume might no longer actually match
- * what has been chosen as new virtual volume here,
- * especially when the old volume was
- * PA_VOLUME_MUTED. Hence let's recalculate the soft
- * volumes here. */
- compute_new_soft_volume(i, &sink_volume);
+ if (i->volume_changed)
+ i->volume_changed(i);
- /* The virtual volume changed, let's tell people so */
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
-
- /* If the soft_volume of any of the sink inputs got changed, let's
- * make sure the thread copies are synced up. */
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SYNC_VOLUMES, NULL, 0, NULL) == 0);
}
/* Called from main thread */
-void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save) {
- pa_bool_t virtual_volume_changed;
+void pa_sink_set_volume(
+ pa_sink *s,
+ const pa_cvolume *volume,
+ pa_bool_t sendmsg,
+ pa_bool_t save) {
+
+ pa_cvolume old_reference_volume;
+ pa_bool_t reference_changed;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
- pa_assert(volume);
- pa_assert(pa_cvolume_valid(volume));
- pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
+ pa_assert(!volume || pa_cvolume_valid(volume));
+ pa_assert(volume || (s->flags & PA_SINK_FLAT_VOLUME));
+ pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
+
+ /* As a special exception we accept mono volumes on all sinks --
+ * even on those with more complex channel maps */
+
+ /* If volume is NULL we synchronize the sink's real and reference
+ * volumes with the stream volumes. If it is not NULL we update
+ * the reference_volume with it. */
+
+ old_reference_volume = s->reference_volume;
+
+ if (volume) {
- virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
- s->virtual_volume = *volume;
- s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
+ if (pa_cvolume_compatible(volume, &s->sample_spec))
+ s->reference_volume = *volume;
+ else
+ pa_cvolume_scale(&s->reference_volume, pa_cvolume_max(volume));
+
+ if (s->flags & PA_SINK_FLAT_VOLUME) {
+ /* OK, propagate this volume change back to the inputs */
+ propagate_reference_volume(s);
+
+ /* And now recalculate the real volume */
+ compute_real_volume(s);
+ } else
+ s->real_volume = s->reference_volume;
+
+ } else {
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
- if (become_reference)
- s->reference_volume = s->virtual_volume;
+ /* Ok, let's determine the new real volume */
+ compute_real_volume(s);
- /* Propagate this volume change back to the inputs */
- if (virtual_volume_changed)
- if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
- pa_sink_propagate_flat_volume(s);
+ /* Let's 'push' the reference volume if necessary */
+ pa_cvolume_merge(&s->reference_volume, &s->reference_volume, &s->real_volume);
+
+ /* We need to fix the reference ratios of all streams now that
+ * we changed the reference volume */
+ compute_reference_ratios(s);
+ }
+
+ reference_changed = !pa_cvolume_equal(&old_reference_volume, &s->reference_volume);
+ s->save_volume = (!reference_changed && s->save_volume) || save;
if (s->set_volume) {
/* If we have a function set_volume(), then we do not apply a
@@ -1347,72 +1408,133 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
} else
/* If we have no function set_volume(), then the soft volume
* becomes the virtual volume */
- s->soft_volume = s->virtual_volume;
+ s->soft_volume = s->real_volume;
/* This tells the sink that soft and/or virtual volume changed */
if (sendmsg)
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
- if (virtual_volume_changed)
+ if (reference_changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from 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(volume);
+ pa_assert_ctl_context();
- s->soft_volume = *volume;
+ if (!volume)
+ pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+ else
+ s->soft_volume = *volume;
if (PA_SINK_IS_LINKED(s->state))
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 = *volume;
+ s->thread_info.soft_volume = s->soft_volume;
}
-/* Called from main thread */
-const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_bool_t reference) {
+static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) {
+ pa_sink_input *i;
+ uint32_t idx;
+ pa_cvolume old_reference_volume;
+
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_virtual_volume = s->virtual_volume;
+ /* 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->get_volume)
- s->get_volume(s);
+ if (pa_cvolume_equal(old_real_volume, &s->real_volume))
+ return;
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
+ old_reference_volume = s->reference_volume;
- if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
+ /* 1. Make the real volume the reference volume */
+ s->reference_volume = s->real_volume;
- s->reference_volume = s->virtual_volume;
+ if (s->flags & PA_SINK_FLAT_VOLUME) {
- if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s);
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ pa_cvolume old_volume, remapped;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ old_volume = i->volume;
+
+ /* 2. Since the sink's reference and real volumes are equal
+ * now our ratios should be too. */
+ i->reference_ratio = 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() */
+ remapped = s->reference_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
+
+ /* Notify if something changed */
+ if (!pa_cvolume_equal(&old_volume, &i->volume)) {
+
+ if (i->volume_changed)
+ i->volume_changed(i);
+
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
}
}
- return reference ? &s->reference_volume : &s->virtual_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. */
+ s->save_volume = TRUE;
+
+ if (!pa_cvolume_equal(&old_reference_volume, &s->reference_volume))
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from main thread */
-void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save) {
+const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- /* The sink implementor may call this if the volume changed to make sure everyone is notified */
- if (pa_cvolume_equal(&s->virtual_volume, new_volume)) {
- s->save_volume = s->save_volume || save;
- return;
+ if (s->refresh_volume || force_refresh) {
+ struct pa_cvolume old_real_volume;
+
+ old_real_volume = s->real_volume;
+
+ if (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);
+
+ propagate_real_volume(s, &old_real_volume);
}
- s->reference_volume = s->virtual_volume = *new_volume;
- s->save_volume = save;
+ return &s->reference_volume;
+}
- if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s);
+/* Called from main thread */
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_real_volume) {
+ pa_cvolume old_real_volume;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ /* The sink implementor may call this if the volume changed to make sure everyone is notified */
+
+ old_real_volume = s->real_volume;
+ s->real_volume = *new_real_volume;
+
+ propagate_real_volume(s, &old_real_volume);
}
/* Called from main thread */
@@ -1420,6 +1542,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t old_muted;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
old_muted = s->muted;
@@ -1439,6 +1562,8 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t 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) {
pa_bool_t old_muted = s->muted;
@@ -1449,6 +1574,8 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
if (old_muted != s->muted) {
+ s->save_muted = TRUE;
+
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
/* Make sure the soft mute status stays in sync */
@@ -1460,18 +1587,18 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
-void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save) {
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
/* The sink implementor may call this if the volume changed to make sure everyone is notified */
- if (s->muted == new_muted) {
- s->save_muted = s->save_muted || save;
+ if (s->muted == new_muted)
return;
- }
s->muted = new_muted;
- s->save_muted = save;
+ s->save_muted = TRUE;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -1479,6 +1606,7 @@ void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save) {
/* Called from main thread */
pa_bool_t 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);
@@ -1492,16 +1620,18 @@ pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist
}
/* 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 && !strcmp(old, description))
+ if (old && description && pa_streq(old, description))
return;
if (description)
@@ -1528,6 +1658,7 @@ 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);
@@ -1546,6 +1677,7 @@ 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);
@@ -1564,6 +1696,7 @@ unsigned pa_sink_check_suspend(pa_sink *s) {
uint32_t idx;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
if (!PA_SINK_IS_LINKED(s->state))
return 0;
@@ -1597,8 +1730,9 @@ static void sync_input_volumes_within_thread(pa_sink *s) {
void *state = NULL;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
- while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) {
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
continue;
@@ -1701,7 +1835,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
pa_sink_input_unref(i);
- pa_sink_invalidate_requested_latency(s);
+ pa_sink_invalidate_requested_latency(s, TRUE);
pa_sink_request_rewind(s, (size_t) -1);
/* In flat volume mode we need to update the volume as
@@ -1723,10 +1857,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
size_t sink_nbytes, total_nbytes;
/* Get the latency of the sink */
- if (!(s->flags & PA_SINK_LATENCY) ||
- PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
- usec = 0;
-
+ usec = pa_sink_get_latency_within_thread(s);
sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
@@ -1747,7 +1878,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
pa_sink_input_unref(i);
- pa_sink_invalidate_requested_latency(s);
+ pa_sink_invalidate_requested_latency(s, TRUE);
pa_log_debug("Requesting rewind due to started move");
pa_sink_request_rewind(s, (size_t) -1);
@@ -1785,10 +1916,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
size_t nbytes;
/* Get the latency of the sink */
- if (!(s->flags & PA_SINK_LATENCY) ||
- PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
- usec = 0;
-
+ usec = pa_sink_get_latency_within_thread(s);
nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
if (nbytes > 0)
@@ -1876,6 +2004,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
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;
@@ -1899,6 +2030,16 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
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;
@@ -1934,9 +2075,10 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause)
int ret = 0;
pa_core_assert_ref(c);
+ pa_assert_ctl_context();
pa_assert(cause != 0);
- for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) {
+ PA_IDXSET_FOREACH(sink, c->sinks, idx) {
int r;
if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
@@ -1949,6 +2091,7 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause)
/* Called from main thread */
void pa_sink_detach(pa_sink *s) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
@@ -1957,6 +2100,7 @@ void pa_sink_detach(pa_sink *s) {
/* Called from main thread */
void pa_sink_attach(pa_sink *s) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
@@ -1968,9 +2112,10 @@ void pa_sink_detach_within_thread(pa_sink *s) {
void *state = NULL;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
if (i->detach)
i->detach(i);
@@ -1984,9 +2129,10 @@ void pa_sink_attach_within_thread(pa_sink *s) {
void *state = NULL;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
if (i->attach)
i->attach(i);
@@ -1997,6 +2143,7 @@ void pa_sink_attach_within_thread(pa_sink *s) {
/* 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 (s->thread_info.state == PA_SINK_SUSPENDED)
@@ -2026,15 +2173,15 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
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->fixed_latency, s->thread_info.min_latency, s->thread_info.max_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;
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
-
+ 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;
@@ -2062,6 +2209,7 @@ 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)
@@ -2077,16 +2225,16 @@ void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
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)) {
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ 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);
@@ -2095,6 +2243,7 @@ void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t 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);
@@ -2107,6 +2256,7 @@ 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;
@@ -2116,7 +2266,7 @@ void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
if (PA_SINK_IS_LINKED(s->thread_info.state)) {
pa_sink_input *i;
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
pa_sink_input_update_max_request(i, s->thread_info.max_request);
}
}
@@ -2124,6 +2274,7 @@ void pa_sink_set_max_request_within_thread(pa_sink *s, size_t 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);
@@ -2132,23 +2283,24 @@ void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
}
/* Called from IO thread */
-void pa_sink_invalidate_requested_latency(pa_sink *s) {
+void pa_sink_invalidate_requested_latency(pa_sink *s, pa_bool_t 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))
+ if ((s->flags & PA_SINK_DYNAMIC_LATENCY))
+ s->thread_info.requested_latency_valid = FALSE;
+ else if (dynamic)
return;
- s->thread_info.requested_latency_valid = FALSE;
-
if (PA_SINK_IS_LINKED(s->thread_info.state)) {
if (s->update_requested_latency)
s->update_requested_latency(s);
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
if (i->update_sink_requested_latency)
i->update_sink_requested_latency(i);
}
@@ -2157,6 +2309,7 @@ void pa_sink_invalidate_requested_latency(pa_sink *s) {
/* 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
@@ -2191,6 +2344,7 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
/* 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);
@@ -2209,9 +2363,8 @@ void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *ma
/* 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) {
- void *state = NULL;
-
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);
@@ -2222,27 +2375,36 @@ void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t 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;
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &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);
+ 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, before the sink is put */
+/* 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();
- pa_assert(pa_sink_get_state(s) == PA_SINK_INIT);
+ if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
+ pa_assert(latency == 0);
+ return;
+ }
if (latency < ABSOLUTE_MIN_LATENCY)
latency = ABSOLUTE_MIN_LATENCY;
@@ -2250,14 +2412,69 @@ void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency) {
if (latency > ABSOLUTE_MAX_LATENCY)
latency = ABSOLUTE_MAX_LATENCY;
- s->fixed_latency = 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);
+ 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 */
size_t pa_sink_get_max_rewind(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_rewind;
@@ -2271,6 +2488,7 @@ size_t pa_sink_get_max_rewind(pa_sink *s) {
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;
@@ -2284,7 +2502,8 @@ size_t pa_sink_get_max_request(pa_sink *s) {
int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
pa_device_port *port;
- pa_assert(s);
+ 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);
@@ -2315,7 +2534,6 @@ int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
return 0;
}
-/* Called from main context */
pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
@@ -2430,3 +2648,48 @@ pa_bool_t pa_device_init_intended_roles(pa_proplist *p) {
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, "internal"))
+ priority += 900;
+ else if (pa_streq(s, "speaker"))
+ priority += 500;
+ else if (pa_streq(s, "headphone"))
+ priority += 400;
+ }
+
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_BUS))) {
+
+ if (pa_streq(s, "pci"))
+ priority += 50;
+ else if (pa_streq(s, "usb"))
+ priority += 40;
+ else if (pa_streq(s, "bluetooth"))
+ 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;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index d16fcc01..ba547fc3 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -42,6 +42,7 @@ typedef struct pa_device_port pa_device_port;
#include <pulsecore/rtpoll.h>
#include <pulsecore/card.h>
#include <pulsecore/queue.h>
+#include <pulsecore/thread-mq.h>
#define PA_MAX_INPUTS_PER_SINK 32
@@ -89,9 +90,10 @@ struct pa_sink {
unsigned n_volume_steps; /* shall be constant */
/* Also see http://pulseaudio.org/wiki/InternalVolumes */
- pa_cvolume virtual_volume; /* The volume clients are informed about */
- pa_cvolume reference_volume; /* The volume taken as refernce base for relative sink input 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 */
+
pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
@@ -101,15 +103,14 @@ struct pa_sink {
pa_bool_t save_muted:1;
pa_asyncmsgq *asyncmsgq;
- pa_rtpoll *rtpoll;
pa_memchunk silence;
- pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
-
pa_hashmap *ports;
pa_device_port *active_port;
+ unsigned priority;
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -155,9 +156,14 @@ struct pa_sink {
pa_sink_state_t state;
pa_hashmap *inputs;
+ pa_rtpoll *rtpoll;
+
pa_cvolume soft_volume;
pa_bool_t 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. */
pa_bool_t requested_latency_valid:1;
pa_usec_t requested_latency;
@@ -173,14 +179,21 @@ struct pa_sink {
size_t rewind_nbytes;
pa_bool_t 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 */
} thread_info;
void *userdata;
};
-PA_DECLARE_CLASS(pa_sink);
+PA_DECLARE_PUBLIC_CLASS(pa_sink);
#define PA_SINK(s) (pa_sink_cast(s))
typedef enum pa_sink_message {
@@ -200,6 +213,8 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_DETACH,
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,
@@ -267,12 +282,15 @@ void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s);
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, pa_bool_t save);
-void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save);
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume);
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted);
+
+void pa_sink_update_flags(pa_sink *s, pa_sink_flags_t mask, pa_sink_flags_t value);
pa_bool_t pa_device_init_description(pa_proplist *p);
pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink);
pa_bool_t pa_device_init_intended_roles(pa_proplist *p);
+unsigned pa_device_init_priority(pa_proplist *p);
/**** May be called by everyone, from main context */
@@ -280,6 +298,7 @@ pa_bool_t pa_device_init_intended_roles(pa_proplist *p);
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);
@@ -288,11 +307,8 @@ int pa_sink_update_status(pa_sink*s);
int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause);
int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
-void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
-void pa_sink_propagate_flat_volume(pa_sink *s);
-
-void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save);
-const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference);
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);
@@ -331,16 +347,23 @@ 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);
/*** 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);
+void pa_sink_invalidate_requested_latency(pa_sink *s, pa_bool_t dynamic);
pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s);
pa_device_port *pa_device_port_new(const char *name, const char *description, size_t extra);
void pa_device_port_free(pa_device_port *p);
+/* 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/socket-client.c b/src/pulsecore/socket-client.c
index 24535157..c9cfdbe3 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -202,8 +202,6 @@ static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event
}
static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
- int r;
-
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_assert(sa);
@@ -211,7 +209,7 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t
pa_make_fd_nonblock(c->fd);
- if ((r = connect(c->fd, sa, len)) < 0) {
+ if (connect(c->fd, sa, len) < 0) {
#ifdef OS_IS_WIN32
if (WSAGetLastError() != EWOULDBLOCK) {
pa_log_debug("connect(): %d", WSAGetLastError());
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 502e5c69..16de4923 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -64,9 +64,8 @@ enum {
FILE_STREAM_MESSAGE_UNLINK
};
-PA_DECLARE_CLASS(file_stream);
+PA_DEFINE_PRIVATE_CLASS(file_stream, pa_msgobject);
#define FILE_STREAM(o) (file_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
/* Called from main context */
static void file_stream_unlink(file_stream *u) {
@@ -312,7 +311,7 @@ int pa_play_file(
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, 0);
+ pa_sink_input_new(&u->sink_input, sink->core, &data);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
@@ -335,8 +334,7 @@ int pa_play_file(
return 0;
fail:
- if (u)
- file_stream_unref(u);
+ file_stream_unref(u);
if (fd >= 0)
pa_close(fd);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 4ba25ae4..1509807b 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -41,14 +41,14 @@
#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
-static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_source_output, pa_msgobject);
static void source_output_free(pa_object* mo);
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) {
pa_assert(data);
- memset(data, 0, sizeof(*data));
+ pa_zero(*data);
data->resample_method = PA_RESAMPLER_INVALID;
data->proplist = pa_proplist_new();
@@ -84,6 +84,7 @@ static void reset_callbacks(pa_source_output *o) {
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;
@@ -100,8 +101,7 @@ static void reset_callbacks(pa_source_output *o) {
int pa_source_output_new(
pa_source_output**_o,
pa_core *core,
- pa_source_output_new_data *data,
- pa_source_output_flags_t flags) {
+ pa_source_output_new_data *data) {
pa_source_output *o;
pa_resampler *resampler = NULL;
@@ -111,6 +111,7 @@ int pa_source_output_new(
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);
@@ -144,13 +145,13 @@ int pa_source_output_new(
pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
- if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
+ if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
data->sample_spec.format = data->source->sample_spec.format;
- if (flags & PA_SOURCE_OUTPUT_FIX_RATE)
+ if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE)
data->sample_spec.rate = data->source->sample_spec.rate;
- if (flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
+ if (data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
data->sample_spec.channels = data->source->sample_spec.channels;
data->channel_map = data->source->channel_map;
}
@@ -166,7 +167,7 @@ int pa_source_output_new(
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
return r;
- if ((flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND) &&
+ 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;
@@ -177,7 +178,7 @@ int pa_source_output_new(
return -PA_ERR_TOOLARGE;
}
- if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+ 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)) {
@@ -186,9 +187,9 @@ int pa_source_output_new(
&data->source->sample_spec, &data->source->channel_map,
&data->sample_spec, &data->channel_map,
data->resample_method,
- ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
- ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
- (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+ ((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->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -PA_ERR_NOTSUPPORTED;
@@ -201,7 +202,7 @@ int pa_source_output_new(
o->core = core;
o->state = PA_SOURCE_OUTPUT_INIT;
- o->flags = flags;
+ 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;
@@ -262,6 +263,7 @@ int pa_source_output_new(
/* 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;
@@ -275,6 +277,7 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state)
/* 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;
@@ -294,6 +297,7 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_
void pa_source_output_unlink(pa_source_output*o) {
pa_bool_t linked;
pa_assert(o);
+ pa_assert_ctl_context();
/* See pa_sink_unlink() for a couple of comments how this function
* works */
@@ -346,6 +350,7 @@ 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);
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
@@ -353,8 +358,6 @@ static void source_output_free(pa_object* mo) {
pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
- pa_assert(!o->thread_info.attached);
-
if (o->thread_info.delay_memblockq)
pa_memblockq_free(o->thread_info.delay_memblockq);
@@ -371,7 +374,9 @@ static void source_output_free(pa_object* mo) {
/* 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);
@@ -395,6 +400,7 @@ void pa_source_output_put(pa_source_output *o) {
/* 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);
@@ -405,6 +411,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_la
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);
@@ -424,6 +431,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
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));
@@ -499,8 +507,9 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
/* 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_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));
@@ -526,8 +535,17 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in so
}
/* 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));
@@ -538,15 +556,16 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i
/* 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->fixed_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);
+ pa_source_invalidate_requested_latency(o->source, TRUE);
return usec;
}
@@ -554,6 +573,7 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output
/* 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);
@@ -565,7 +585,7 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t
if (o->source) {
if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
- usec = o->source->fixed_latency;
+ usec = pa_source_get_fixed_latency(o->source);
if (usec != (pa_usec_t) -1) {
pa_usec_t min_latency, max_latency;
@@ -582,6 +602,7 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t
/* 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;
@@ -598,6 +619,7 @@ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
/* Called from main context */
void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
pa_source_output_assert_ref(o);
+ pa_assert_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);
@@ -606,6 +628,7 @@ void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
/* 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);
@@ -623,6 +646,7 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
/* Called from main context */
void pa_source_output_set_name(pa_source_output *o, const char *name) {
const char *old;
+ pa_assert_ctl_context();
pa_source_output_assert_ref(o);
if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME))
@@ -647,11 +671,12 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
/* Called from main thread */
void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
if (p)
pa_proplist_update(o->proplist, mode, p);
- if (PA_SINK_IS_LINKED(o->state)) {
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
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);
}
@@ -660,6 +685,7 @@ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode
/* 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;
}
@@ -667,6 +693,7 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
/* Called from main context */
pa_bool_t 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)
@@ -708,6 +735,7 @@ int pa_source_output_start_move(pa_source_output *o) {
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);
@@ -739,6 +767,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
pa_resampler *new_resampler;
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);
@@ -816,10 +845,29 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
}
/* 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, pa_bool_t 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);
@@ -838,6 +886,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
}
if ((r = pa_source_output_finish_move(o, dest, save)) < 0) {
+ pa_source_output_fail_move(o);
pa_source_output_unref(o);
return r;
}
@@ -850,6 +899,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
/* Called from IO thread context */
void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
pa_source_output_assert_ref(o);
+ pa_source_output_assert_io_context(o);
if (state == o->thread_info.state)
return;
@@ -906,11 +956,13 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
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)
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 9824e160..273b78fc 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -55,7 +55,8 @@ typedef enum pa_source_output_flags {
PA_SOURCE_OUTPUT_FIX_RATE = 64,
PA_SOURCE_OUTPUT_FIX_CHANNELS = 128,
PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
- PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND = 512
+ PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND = 512,
+ PA_SOURCE_OUTPUT_KILL_ON_SUSPEND = 1024
} pa_source_output_flags_t;
struct pa_source_output {
@@ -108,6 +109,10 @@ struct pa_source_output {
* from IO context. */
void (*update_source_latency_range) (pa_source_output *o); /* may be NULL */
+ /* Called whenver 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. Called from IO thread context */
void (*attach) (pa_source_output *o); /* may be NULL */
@@ -127,7 +132,9 @@ struct pa_source_output {
/* If non-NULL called whenever the source output is moved to a new
* source. Called from main context after the stream was detached
* from the old source and before it is attached to the new
- * source. */
+ * 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
@@ -175,7 +182,7 @@ struct pa_source_output {
void *userdata;
};
-PA_DECLARE_CLASS(pa_source_output);
+PA_DECLARE_PUBLIC_CLASS(pa_source_output);
#define PA_SOURCE_OUTPUT(o) pa_source_output_cast(o)
enum {
@@ -194,6 +201,8 @@ typedef struct pa_source_output_send_event_hook_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;
@@ -224,8 +233,7 @@ void pa_source_output_new_data_done(pa_source_output_new_data *data);
int pa_source_output_new(
pa_source_output**o,
pa_core *core,
- pa_source_output_new_data *data,
- pa_source_output_flags_t flags);
+ pa_source_output_new_data *data);
void pa_source_output_put(pa_source_output *o);
void pa_source_output_unlink(pa_source_output*o);
@@ -238,6 +246,8 @@ void pa_source_output_cork(pa_source_output *o, pa_bool_t b);
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
+size_t pa_source_output_get_max_rewind(pa_source_output *o);
+
/* Callable by everyone */
/* External code may request disconnection with this funcion */
@@ -260,6 +270,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
* 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, pa_bool_t save);
+void pa_source_output_fail_move(pa_source_output *o);
#define pa_source_output_get_state(o) ((o)->state)
@@ -277,4 +288,7 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
+#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
index 74f38bc5..415c54bc 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -33,6 +33,7 @@
#include <pulse/timeval.h>
#include <pulse/util.h>
+#include <pulsecore/core-util.h>
#include <pulsecore/source-output.h>
#include <pulsecore/namereg.h>
#include <pulsecore/core-subscribe.h>
@@ -45,14 +46,14 @@
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
-static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_source, pa_msgobject);
static void source_free(pa_object *o);
pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
pa_assert(data);
- memset(data, 0, sizeof(*data));
+ pa_zero(*data);
data->proplist = pa_proplist_new();
return data;
@@ -145,6 +146,7 @@ pa_source* pa_source_new(
pa_assert(core);
pa_assert(data);
pa_assert(data->name);
+ pa_assert_ctl_context();
s = pa_msgobject_new(pa_source);
@@ -179,7 +181,7 @@ pa_source* pa_source_new(
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
- pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
if (!data->muted_is_set)
data->muted = FALSE;
@@ -203,6 +205,7 @@ pa_source* pa_source_new(
s->core = core;
s->state = PA_SOURCE_INIT;
s->flags = flags;
+ s->priority = 0;
s->suspend_cause = 0;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
@@ -210,6 +213,8 @@ pa_source* pa_source_new(
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;
@@ -217,20 +222,17 @@ pa_source* pa_source_new(
s->n_corked = 0;
s->monitor_of = NULL;
- s->virtual_volume = data->volume;
+ s->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;
- s->fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
-
reset_callbacks(s);
s->userdata = NULL;
s->asyncmsgq = NULL;
- s->rtpoll = NULL;
/* As a minor optimization we just steal the list instead of
* copying it here */
@@ -263,6 +265,7 @@ pa_source* pa_source_new(
&s->sample_spec,
0);
+ s->thread_info.rtpoll = NULL;
s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
@@ -272,6 +275,7 @@ pa_source* pa_source_new(
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_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
@@ -297,6 +301,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
pa_source_state_t original_state;
pa_assert(s);
+ pa_assert_ctl_context();
if (s->state == state)
return 0;
@@ -333,27 +338,26 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
/* We're suspending or resuming, tell everyone about it */
- for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
+ PA_IDXSET_FOREACH(o, s->outputs, idx)
if (s->state == PA_SOURCE_SUSPENDED &&
- (o->flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND))
+ (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;
}
/* 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);
/* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
- pa_assert(s->rtpoll);
pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
/* Generally, flags should be initialized via pa_source_new(). As
@@ -368,7 +372,7 @@ void pa_source_put(pa_source *s) {
pa_assert((s->flags & PA_SOURCE_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SOURCE_DECIBEL_VOLUME));
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->fixed_latency != 0));
+ pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0));
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
@@ -382,6 +386,7 @@ void pa_source_unlink(pa_source *s) {
pa_source_output *o, *j = NULL;
pa_assert(s);
+ pa_assert_ctl_context();
/* See pa_sink_unlink() for a couple of comments how this function
* works. */
@@ -423,6 +428,7 @@ 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);
if (PA_SOURCE_IS_LINKED(s->state))
@@ -458,23 +464,40 @@ static void source_free(pa_object *o) {
pa_xfree(s);
}
-/* Called from main context */
+/* 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 */
+/* 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_assert_ref(s);
+ pa_assert_ctl_context();
+
+ if (mask == 0)
+ return;
+
+ /* For now, allow only a minimal set of flags to be changed. */
+ pa_assert((mask & ~(PA_SOURCE_DYNAMIC_LATENCY|PA_SOURCE_LATENCY)) == 0);
+
+ s->flags = (s->flags & ~mask) | (value & mask);
+}
+
+/* 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->rtpoll = p;
+ 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)
@@ -486,6 +509,7 @@ int pa_source_update_status(pa_source*s) {
/* Called from main context */
int pa_source_suspend(pa_source *s, pa_bool_t 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);
@@ -513,6 +537,7 @@ 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);
@@ -532,6 +557,7 @@ pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q) {
uint32_t idx;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (!q)
@@ -556,12 +582,13 @@ void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t 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_kill(o);
+ pa_source_output_fail_move(o);
pa_source_output_unref(o);
}
@@ -572,13 +599,13 @@ void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save) {
/* 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)))) {
- if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_OK) {
- pa_source_output_kill(o);
- pa_source_output_unref(o);
- }
+ pa_source_output_fail_move(o);
+ pa_source_output_unref(o);
}
pa_queue_free(q, NULL, NULL);
@@ -590,17 +617,18 @@ void pa_source_process_rewind(pa_source *s, size_t nbytes) {
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 (s->thread_info.state == PA_SOURCE_SUSPENDED)
+ if (nbytes <= 0)
return;
- if (nbytes <= 0)
+ if (s->thread_info.state == PA_SOURCE_SUSPENDED)
return;
pa_log_debug("Processing rewind...");
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
pa_source_output_assert_ref(o);
pa_source_output_process_rewind(o, nbytes);
}
@@ -612,6 +640,7 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
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);
@@ -651,6 +680,7 @@ void pa_source_post(pa_source*s, const pa_memchunk *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);
@@ -682,6 +712,7 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
pa_usec_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)
@@ -701,6 +732,7 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
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! */
@@ -722,77 +754,96 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
}
/* Called from main thread */
-void pa_source_set_volume(pa_source *s, const pa_cvolume *volume, pa_bool_t save) {
- pa_cvolume old_virtual_volume;
- pa_bool_t virtual_volume_changed;
+void pa_source_set_volume(
+ pa_source *s,
+ const pa_cvolume *volume,
+ pa_bool_t save) {
+
+ pa_bool_t real_changed;
+ pa_cvolume old_volume;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
- pa_assert(volume);
pa_assert(pa_cvolume_valid(volume));
- pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
+ pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
- old_virtual_volume = s->virtual_volume;
- s->virtual_volume = *volume;
- virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
- s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
+ old_volume = s->volume;
+
+ if (pa_cvolume_compatible(volume, &s->sample_spec))
+ s->volume = *volume;
+ else
+ pa_cvolume_scale(&s->volume, pa_cvolume_max(volume));
+
+ real_changed = !pa_cvolume_equal(&old_volume, &s->volume);
+ s->save_volume = (!real_changed && s->save_volume) || save;
if (s->set_volume) {
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
s->set_volume(s);
} else
- s->soft_volume = s->virtual_volume;
+ s->soft_volume = s->volume;
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
- if (virtual_volume_changed)
+ if (real_changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from 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(volume);
+ 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))
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 = *volume;
+ s->thread_info.soft_volume = s->soft_volume;
}
/* Called from main thread */
const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t 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) {
- pa_cvolume old_virtual_volume = s->virtual_volume;
+ pa_cvolume old_volume;
+
+ old_volume = s->volume;
if (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);
- if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume))
+ if (!pa_cvolume_equal(&old_volume, &s->volume)) {
+ s->save_volume = TRUE;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
}
- return &s->virtual_volume;
+ return &s->volume;
}
/* Called from main thread */
-void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save) {
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
/* The source implementor may call this if the volume changed to make sure everyone is notified */
- if (pa_cvolume_equal(&s->virtual_volume, new_volume)) {
- s->save_volume = s->save_volume || save;
+ if (pa_cvolume_equal(&s->volume, new_volume))
return;
- }
- s->virtual_volume = *new_volume;
- s->save_volume = save;
+ s->volume = *new_volume;
+ s->save_volume = TRUE;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -802,6 +853,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t old_muted;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
old_muted = s->muted;
@@ -820,6 +872,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) {
/* Called from main thread */
pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t 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) {
@@ -831,6 +884,8 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
if (old_muted != s->muted) {
+ s->save_muted = TRUE;
+
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
/* Make sure the soft mute status stays in sync */
@@ -842,18 +897,18 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
-void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save) {
+void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
/* The source implementor may call this if the mute state changed to make sure everyone is notified */
- if (s->muted == new_muted) {
- s->save_muted = s->save_muted || save;
+ if (s->muted == new_muted)
return;
- }
s->muted = new_muted;
- s->save_muted = save;
+ s->save_muted = TRUE;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -861,6 +916,7 @@ void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save) {
/* Called from main thread */
pa_bool_t 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);
@@ -874,16 +930,18 @@ pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_prop
}
/* 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 && !strcmp(old, description))
+ if (old && description && pa_streq(old, description))
return;
if (description)
@@ -901,6 +959,7 @@ void pa_source_set_description(pa_source *s, const char *description) {
unsigned pa_source_linked_by(pa_source *s) {
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
+ pa_assert_ctl_context();
return pa_idxset_size(s->outputs);
}
@@ -911,6 +970,7 @@ unsigned pa_source_used_by(pa_source *s) {
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
+ pa_assert_ctl_context();
ret = pa_idxset_size(s->outputs);
pa_assert(ret >= s->n_corked);
@@ -925,6 +985,7 @@ unsigned pa_source_check_suspend(pa_source *s) {
uint32_t idx;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
if (!PA_SOURCE_IS_LINKED(s->state))
return 0;
@@ -1006,7 +1067,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
pa_source_output_unref(o);
- pa_source_invalidate_requested_latency(s);
+ pa_source_invalidate_requested_latency(s, TRUE);
return 0;
}
@@ -1086,6 +1147,16 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
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;
@@ -1120,6 +1191,7 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t caus
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))) {
@@ -1138,6 +1210,7 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t caus
/* Called from main thread */
void pa_source_detach(pa_source *s) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL) == 0);
@@ -1146,6 +1219,7 @@ void pa_source_detach(pa_source *s) {
/* Called from main thread */
void pa_source_attach(pa_source *s) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
@@ -1157,9 +1231,10 @@ void pa_source_detach_within_thread(pa_source *s) {
void *state = NULL;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
if (o->detach)
o->detach(o);
}
@@ -1170,9 +1245,10 @@ void pa_source_attach_within_thread(pa_source *s) {
void *state = NULL;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
if (o->attach)
o->attach(o);
}
@@ -1184,15 +1260,15 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
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->fixed_latency, s->thread_info.min_latency, s->thread_info.max_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;
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
-
+ 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;
@@ -1214,6 +1290,7 @@ 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)
@@ -1230,21 +1307,22 @@ void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind) {
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)) {
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ 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);
@@ -1253,17 +1331,18 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
}
/* Called from IO thread */
-void pa_source_invalidate_requested_latency(pa_source *s) {
+void pa_source_invalidate_requested_latency(pa_source *s, pa_bool_t 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))
+ if ((s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ s->thread_info.requested_latency_valid = FALSE;
+ else if (dynamic)
return;
- s->thread_info.requested_latency_valid = FALSE;
-
if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
if (s->update_requested_latency)
@@ -1275,12 +1354,13 @@ void pa_source_invalidate_requested_latency(pa_source *s) {
}
if (s->monitor_of)
- pa_sink_invalidate_requested_latency(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
@@ -1315,6 +1395,7 @@ void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t
/* 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);
@@ -1333,9 +1414,8 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t
/* 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) {
- void *state = NULL;
-
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);
@@ -1347,25 +1427,34 @@ void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_laten
(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;
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &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);
+ 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();
- pa_assert(pa_source_get_state(s) == PA_SOURCE_INIT);
+ if (s->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+ pa_assert(latency == 0);
+ return;
+ }
if (latency < ABSOLUTE_MIN_LATENCY)
latency = ABSOLUTE_MIN_LATENCY;
@@ -1373,12 +1462,64 @@ void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency) {
if (latency > ABSOLUTE_MAX_LATENCY)
latency = ABSOLUTE_MAX_LATENCY;
- s->fixed_latency = 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);
+ 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 */
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))
@@ -1394,9 +1535,10 @@ int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
pa_device_port *port;
pa_assert(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);
+ pa_log_debug("set_port() operation not implemented for source %u \"%s\"", s->index, s->name);
return -PA_ERR_NOTIMPLEMENTED;
}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 7e9fd8b7..e3e56bc4 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -43,6 +43,7 @@ typedef struct pa_source pa_source;
#include <pulsecore/source-output.h>
#include <pulsecore/card.h>
#include <pulsecore/queue.h>
+#include <pulsecore/thread-mq.h>
#define PA_MAX_OUTPUTS_PER_SOURCE 32
@@ -78,7 +79,7 @@ struct pa_source {
pa_volume_t base_volume; /* shall be constant */
unsigned n_volume_steps; /* shall be constant */
- pa_cvolume virtual_volume, soft_volume;
+ pa_cvolume volume, soft_volume;
pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
@@ -89,15 +90,14 @@ struct pa_source {
pa_bool_t save_muted:1;
pa_asyncmsgq *asyncmsgq;
- pa_rtpoll *rtpoll;
pa_memchunk silence;
- pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
-
pa_hashmap *ports;
pa_device_port *active_port;
+ unsigned priority;
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -139,6 +139,8 @@ struct pa_source {
pa_source_state_t state;
pa_hashmap *outputs;
+ pa_rtpoll *rtpoll;
+
pa_cvolume soft_volume;
pa_bool_t soft_muted:1;
@@ -151,12 +153,14 @@ struct pa_source {
pa_usec_t min_latency; /* we won't go below this latency */
pa_usec_t max_latency; /* An upper limit for the latencies */
- } thread_info;
+
+ pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
+ } thread_info;
void *userdata;
};
-PA_DECLARE_CLASS(pa_source);
+PA_DECLARE_PUBLIC_CLASS(pa_source);
#define PA_SOURCE(s) pa_source_cast(s)
typedef enum pa_source_message {
@@ -173,6 +177,8 @@ typedef enum pa_source_message {
PA_SOURCE_MESSAGE_DETACH,
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_MAX
@@ -237,17 +243,20 @@ void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s);
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, pa_bool_t save);
-void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save);
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume);
+void pa_source_mute_changed(pa_source *s, pa_bool_t 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 */
/* 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);
@@ -257,6 +266,7 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t caus
void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, pa_bool_t save);
const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);
+
void pa_source_set_mute(pa_source *source, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
@@ -288,11 +298,16 @@ 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);
/*** To be called exclusively by source output drivers, from IO context */
-void pa_source_invalidate_requested_latency(pa_source *s);
+void pa_source_invalidate_requested_latency(pa_source *s, pa_bool_t dynamic);
pa_usec_t pa_source_get_latency_within_thread(pa_source *s);
+#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/start-child.c b/src/pulsecore/start-child.c
index 7774bde6..4a70aea1 100644
--- a/src/pulsecore/start-child.c
+++ b/src/pulsecore/start-child.c
@@ -68,23 +68,29 @@ int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
} else {
/* child */
- pa_reset_priority();
+ pa_reset_personality();
pa_assert_se(pa_close(pipe_fds[0]) == 0);
- pa_assert_se(dup2(pipe_fds[1], 1) == 1);
+ pa_assert_se(dup2(pipe_fds[1], STDOUT_FILENO) == STDOUT_FILENO);
- if (pipe_fds[1] != 1)
+ if (pipe_fds[1] != STDOUT_FILENO)
pa_assert_se(pa_close(pipe_fds[1]) == 0);
- pa_close(0);
- pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+ pa_close(STDIN_FILENO);
+ pa_assert_se(open("/dev/null", O_RDONLY) == STDIN_FILENO);
- pa_close(2);
- pa_assert_se(open("/dev/null", O_WRONLY) == 2);
+ 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. */
+ unsetenv("LD_BIND_NOW");
#ifdef PR_SET_PDEATHSIG
/* On Linux we can use PR_SET_PDEATHSIG to have the helper
diff --git a/src/pulsecore/svolume_arm.c b/src/pulsecore/svolume_arm.c
new file mode 100644
index 00000000..5bd1448f
--- /dev/null
+++ b/src/pulsecore/svolume_arm.c
@@ -0,0 +1,195 @@
+/***
+ 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, write to 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/timeval.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/core-util.h>
+
+#include "cpu-arm.h"
+
+#include "sample-util.h"
+#include "endianmacros.h"
+
+#if defined (__arm__)
+
+#define MOD_INC() \
+ " subs r0, r6, %2 \n\t" \
+ " addcs r0, %1 \n\t" \
+ " movcs r6, r0 \n\t"
+
+static void
+pa_volume_s16ne_arm (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ int32_t *ve;
+
+ channels = PA_MAX (4U, channels);
+ ve = volumes + channels;
+
+ __asm__ __volatile__ (
+ " mov r6, %1 \n\t"
+ " mov %3, %3, LSR #1 \n\t" /* length /= sizeof (int16_t) */
+ " tst %3, #1 \n\t" /* check for odd samples */
+ " beq 2f \n\t"
+
+ "1: \n\t"
+ " ldr r0, [r6], #4 \n\t" /* odd samples volumes */
+ " ldrh r2, [%0] \n\t"
+
+ " smulwb r0, r0, r2 \n\t"
+ " ssat r0, #16, r0 \n\t"
+
+ " strh r0, [%0], #2 \n\t"
+
+ MOD_INC()
+
+ "2: \n\t"
+ " mov %3, %3, LSR #1 \n\t"
+ " tst %3, #1 \n\t" /* check for odd samples */
+ " beq 4f \n\t"
+
+ "3: \n\t"
+ " ldrd r2, [r6], #8 \n\t" /* 2 samples at a time */
+ " ldr r0, [%0] \n\t"
+
+ " smulwt r2, r2, r0 \n\t"
+ " smulwb r3, r3, r0 \n\t"
+
+ " ssat r2, #16, r2 \n\t"
+ " ssat r3, #16, r3 \n\t"
+
+ " pkhbt r0, r3, r2, LSL #16 \n\t"
+ " str r0, [%0], #4 \n\t"
+
+ MOD_INC()
+
+ "4: \n\t"
+ " movs %3, %3, LSR #1 \n\t"
+ " beq 6f \n\t"
+
+ "5: \n\t"
+ " ldrd r2, [r6], #8 \n\t" /* 4 samples at a time */
+ " ldrd r4, [r6], #8 \n\t"
+ " ldrd r0, [%0] \n\t"
+
+ " smulwt r2, r2, r0 \n\t"
+ " smulwb r3, r3, r0 \n\t"
+ " smulwt r4, r4, r1 \n\t"
+ " smulwb r5, r5, r1 \n\t"
+
+ " ssat r2, #16, r2 \n\t"
+ " ssat r3, #16, r3 \n\t"
+ " ssat r4, #16, r4 \n\t"
+ " ssat r5, #16, r5 \n\t"
+
+ " pkhbt r0, r3, r2, LSL #16 \n\t"
+ " pkhbt r1, r5, r4, LSL #16 \n\t"
+ " strd r0, [%0], #8 \n\t"
+
+ MOD_INC()
+
+ " subs %3, %3, #1 \n\t"
+ " bne 5b \n\t"
+ "6: \n\t"
+
+ : "+r" (samples), "+r" (volumes), "+r" (ve), "+r" (length)
+ :
+ : "r6", "r5", "r4", "r3", "r2", "r1", "r0", "cc"
+ );
+}
+
+#undef RUN_TEST
+
+#ifdef RUN_TEST
+#define CHANNELS 2
+#define SAMPLES 1023
+#define TIMES 1000
+#define PADDING 16
+
+static void run_test (void) {
+ int16_t samples[SAMPLES];
+ int16_t samples_ref[SAMPLES];
+ int16_t samples_orig[SAMPLES];
+ int32_t volumes[CHANNELS + PADDING];
+ int i, j, padding;
+ pa_do_volume_func_t func;
+ pa_usec_t start, stop;
+
+ func = pa_get_volume_func (PA_SAMPLE_S16NE);
+
+ printf ("checking ARM %zd\n", sizeof (samples));
+
+ pa_random (samples, sizeof (samples));
+ memcpy (samples_ref, samples, sizeof (samples));
+ memcpy (samples_orig, samples, sizeof (samples));
+
+ for (i = 0; i < CHANNELS; i++)
+ volumes[i] = rand() >> 1;
+ for (padding = 0; padding < PADDING; padding++, i++)
+ volumes[i] = volumes[padding];
+
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ pa_volume_s16ne_arm (samples, volumes, CHANNELS, sizeof (samples));
+ for (i = 0; i < SAMPLES; i++) {
+ if (samples[i] != samples_ref[i]) {
+ printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
+ samples_orig[i], volumes[i % CHANNELS]);
+ }
+ }
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples, samples_orig, sizeof (samples));
+ pa_volume_s16ne_arm (samples, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ARM: %llu usec.", (long long unsigned int) (stop - start));
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples_ref, samples_orig, sizeof (samples));
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ref: %llu usec.", (long long unsigned int) (stop - start));
+}
+#endif
+
+#endif /* defined (__arm__) */
+
+
+void pa_volume_func_init_arm (pa_cpu_arm_flag_t flags) {
+#if defined (__arm__)
+ pa_log_info("Initialising ARM optimized functions.");
+
+#ifdef RUN_TEST
+ run_test ();
+#endif
+
+ pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_arm);
+#endif /* defined (__arm__) */
+}
diff --git a/src/pulsecore/svolume_c.c b/src/pulsecore/svolume_c.c
new file mode 100644
index 00000000..5fc052b8
--- /dev/null
+++ b/src/pulsecore/svolume_c.c
@@ -0,0 +1,335 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/core-util.h>
+
+#include "sample-util.h"
+#include "endianmacros.h"
+
+static void
+pa_volume_u8_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t) *samples - 0x80;
+ t = ((t * lo) >> 16) + (t * hi);
+ 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, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t) st_alaw2linear16(*samples);
+ t = ((t * lo) >> 16) + (t * hi);
+ 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, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t) st_ulaw2linear16(*samples);
+ t = ((t * lo) >> 16) + (t * hi);
+ 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, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (int16_t);
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ /* Multiplying the 32bit volume factor with the 16bit
+ * sample might result in an 48bit value. We want to
+ * do without 64 bit integers and hence do the
+ * multiplication independantly for the HI and LO part
+ * of the volume. */
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t)(*samples);
+ t = ((t * lo) >> 16) + (t * hi);
+ 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, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (int16_t);
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t) PA_INT16_SWAP(*samples);
+ t = ((t * lo) >> 16) + (t * hi);
+ 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, 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, float *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (float);
+
+ for (channel = 0; length; length--) {
+ float t;
+
+ t = PA_FLOAT32_SWAP(*samples);
+ t *= volumes[channel];
+ *samples++ = PA_FLOAT32_SWAP(t);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_s32ne_c (int32_t *samples, 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, 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, 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, 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, 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, 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(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return do_volume_table[f];
+}
+
+void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func) {
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ do_volume_table[f] = func;
+}
diff --git a/src/pulsecore/svolume_mmx.c b/src/pulsecore/svolume_mmx.c
new file mode 100644
index 00000000..74918e78
--- /dev/null
+++ b/src/pulsecore/svolume_mmx.c
@@ -0,0 +1,316 @@
+/***
+ 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, write to 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/timeval.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/core-util.h>
+
+#include "cpu-x86.h"
+
+#include "sample-util.h"
+#include "endianmacros.h"
+
+#if 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 calulcation 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 %%mm6, %%mm5 \n\t" /* .. | ffff | 0 | */ \
+ " pand "#v", %%mm5 \n\t" /* .. | vh | 0 | */ \
+ " por %%mm5, %%mm4 \n\t" /* .. | vh | (p0) | */ \
+ " pmulhw "#s", "#v" \n\t" /* .. | 0 | vl*p0 | */ \
+ " paddw %%mm4, "#v" \n\t" /* .. | vh | vl*p0 | vh + sign correct */ \
+ " pslld $16, "#s" \n\t" /* .. | p0 | 0 | */ \
+ " por %%mm7, "#s" \n\t" /* .. | p0 | 1 | */ \
+ " pmaddwd "#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, int32_t *volumes, unsigned channels, unsigned length)
+{
+ pa_reg_x86 channel, temp;
+
+ /* the max number of samples we process at a time, this is also the max amount
+ * we overread the volume array, which should have enough padding. */
+ channels = 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 (%1, %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 (%1, %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 (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movq 8(%1, %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" ((pa_reg_x86)channel), "=&r" (temp)
+ : "r" ((pa_reg_x86)channels)
+ : "cc"
+ );
+}
+
+static void
+pa_volume_s16re_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ pa_reg_x86 channel, temp;
+
+ /* the max number of samples we process at a time, this is also the max amount
+ * we overread the volume array, which should have enough padding. */
+ channels = 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 (%1, %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 (%1, %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 (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movq 8(%1, %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" ((pa_reg_x86)channel), "=&r" (temp)
+ : "r" ((pa_reg_x86)channels)
+ : "cc"
+ );
+}
+
+#undef RUN_TEST
+
+#ifdef RUN_TEST
+#define CHANNELS 2
+#define SAMPLES 1021
+#define TIMES 1000
+#define PADDING 16
+
+static void run_test (void) {
+ int16_t samples[SAMPLES];
+ int16_t samples_ref[SAMPLES];
+ int16_t samples_orig[SAMPLES];
+ int32_t volumes[CHANNELS + PADDING];
+ int i, j, padding;
+ pa_do_volume_func_t func;
+ pa_usec_t start, stop;
+
+ func = pa_get_volume_func (PA_SAMPLE_S16NE);
+
+ printf ("checking MMX %zd\n", sizeof (samples));
+
+ pa_random (samples, sizeof (samples));
+ memcpy (samples_ref, samples, sizeof (samples));
+ memcpy (samples_orig, samples, sizeof (samples));
+
+ for (i = 0; i < CHANNELS; i++)
+ volumes[i] = rand() >> 1;
+ for (padding = 0; padding < PADDING; padding++, i++)
+ volumes[i] = volumes[padding];
+
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ pa_volume_s16ne_mmx (samples, volumes, CHANNELS, sizeof (samples));
+ for (i = 0; i < SAMPLES; i++) {
+ if (samples[i] != samples_ref[i]) {
+ printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
+ samples_orig[i], volumes[i % CHANNELS]);
+ }
+ }
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples, samples_orig, sizeof (samples));
+ pa_volume_s16ne_mmx (samples, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("MMX: %llu usec.", (long long unsigned int)(stop - start));
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples_ref, samples_orig, sizeof (samples));
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
+}
+#endif
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+
+void pa_volume_func_init_mmx (pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+#ifdef RUN_TEST
+ run_test ();
+#endif
+
+ if (flags & PA_CPU_X86_MMX) {
+ pa_log_info("Initialising MMX optimized 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 (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/svolume_sse.c b/src/pulsecore/svolume_sse.c
new file mode 100644
index 00000000..bbd73a9b
--- /dev/null
+++ b/src/pulsecore/svolume_sse.c
@@ -0,0 +1,317 @@
+/***
+ 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, write to 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/timeval.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/core-util.h>
+
+#include "cpu-x86.h"
+
+#include "sample-util.h"
+#include "endianmacros.h"
+
+#if 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" /* .. | p0 | 0 | */ \
+ " 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 void
+pa_volume_s16ne_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ pa_reg_x86 channel, temp;
+
+ /* the max number of samples we process at a time, this is also the max amount
+ * we overread the volume array, which should have enough padding. */
+ channels = PA_MAX (8U, 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 (%1, %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 (%1, %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 (%1, %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 (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
+ " movdqu 16(%1, %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)
+ : "r" ((pa_reg_x86)channels)
+ : "cc"
+ );
+}
+
+static void
+pa_volume_s16re_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ pa_reg_x86 channel, temp;
+
+ /* the max number of samples we process at a time, this is also the max amount
+ * we overread the volume array, which should have enough padding. */
+ channels = PA_MAX (8U, 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 (%1, %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 (%1, %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 (%1, %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 (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
+ " movdqu 16(%1, %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)
+ : "r" ((pa_reg_x86)channels)
+ : "cc"
+ );
+}
+
+#undef RUN_TEST
+
+#ifdef RUN_TEST
+#define CHANNELS 2
+#define SAMPLES 1021
+#define TIMES 1000
+#define PADDING 16
+
+static void run_test (void) {
+ int16_t samples[SAMPLES];
+ int16_t samples_ref[SAMPLES];
+ int16_t samples_orig[SAMPLES];
+ int32_t volumes[CHANNELS + PADDING];
+ int i, j, padding;
+ pa_do_volume_func_t func;
+ pa_usec_t start, stop;
+
+ func = pa_get_volume_func (PA_SAMPLE_S16NE);
+
+ printf ("checking SSE %zd\n", sizeof (samples));
+
+ pa_random (samples, sizeof (samples));
+ memcpy (samples_ref, samples, sizeof (samples));
+ memcpy (samples_orig, samples, sizeof (samples));
+
+ for (i = 0; i < CHANNELS; i++)
+ volumes[i] = rand() >> 1;
+ for (padding = 0; padding < PADDING; padding++, i++)
+ volumes[i] = volumes[padding];
+
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ pa_volume_s16ne_sse (samples, volumes, CHANNELS, sizeof (samples));
+ for (i = 0; i < SAMPLES; i++) {
+ if (samples[i] != samples_ref[i]) {
+ printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
+ samples_orig[i], volumes[i % CHANNELS]);
+ }
+ }
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples, samples_orig, sizeof (samples));
+ pa_volume_s16ne_sse (samples, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("SSE: %llu usec.", (long long unsigned int)(stop - start));
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples_ref, samples_orig, sizeof (samples));
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
+}
+#endif
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+void pa_volume_func_init_sse (pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+#ifdef RUN_TEST
+ run_test ();
+#endif
+
+ if (flags & PA_CPU_X86_SSE2) {
+ pa_log_info("Initialising SSE2 optimized 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 (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
index 34f92a7e..73997a74 100644
--- a/src/pulsecore/thread-mq.c
+++ b/src/pulsecore/thread-mq.c
@@ -59,7 +59,7 @@ static void asyncmsgq_read_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io
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) {
+ while (pa_asyncmsgq_get(aq, &object, &code, &data, &offset, &chunk, 0) >= 0) {
int ret;
ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
@@ -104,6 +104,15 @@ void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rt
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 (!pa_asyncmsgq_dispatching(q->outq))
+ pa_asyncmsgq_flush(q->outq, TRUE);
+
q->mainloop->io_free(q->read_event);
q->mainloop->io_free(q->write_event);
q->read_event = q->write_event = NULL;
diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h
index 3b5e0e78..96839d25 100644
--- a/src/pulsecore/thread-mq.h
+++ b/src/pulsecore/thread-mq.h
@@ -45,4 +45,12 @@ 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/time-smoother.c b/src/pulsecore/time-smoother.c
index 9d5a0705..d6c37878 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -108,29 +108,11 @@ pa_smoother* pa_smoother_new(
s = pa_xnew(pa_smoother, 1);
s->adjust_time = adjust_time;
s->history_time = history_time;
- s->time_offset = 0;
+ s->min_history = min_history;
s->monotonic = monotonic;
-
- s->px = s->py = 0;
- s->dp = 1;
-
- s->ex = s->ey = s->ry = 0;
- s->de = 1;
-
- s->history_idx = 0;
- s->n_history = 0;
-
- s->last_y = s->last_x = 0;
-
- s->abc_valid = FALSE;
-
- s->paused = FALSE;
s->smoothing = smoothing;
- s->min_history = min_history;
-
- s->paused = paused;
- s->time_offset = s->pause_time = time_offset;
+ pa_smoother_reset(s, time_offset, paused);
return s;
}
@@ -514,9 +496,26 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay)
return (pa_usec_t) llrint((double) y_delay / nde);
}
-void pa_smoother_reset(pa_smoother *s) {
+void pa_smoother_reset(pa_smoother *s, pa_usec_t time_offset, pa_bool_t 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
index 5244a7e7..63d33e48 100644
--- a/src/pulsecore/time-smoother.h
+++ b/src/pulsecore/time-smoother.h
@@ -52,7 +52,7 @@ 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, pa_bool_t abrupt);
-void pa_smoother_reset(pa_smoother *s);
+void pa_smoother_reset(pa_smoother *s, pa_usec_t time_offset, pa_bool_t paused);
void pa_smoother_fix_now(pa_smoother *s);
diff --git a/src/pulsecore/usergroup.c b/src/pulsecore/usergroup.c
new file mode 100644
index 00000000..71b13bca
--- /dev/null
+++ b/src/pulsecore/usergroup.c
@@ -0,0 +1,372 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <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
index 00000000..1c091638
--- /dev/null
+++ b/src/pulsecore/usergroup.h
@@ -0,0 +1,51 @@
+#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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_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/vector.h b/src/pulsecore/vector.h
index 924e3cb8..9de3b8cd 100644
--- a/src/pulsecore/vector.h
+++ b/src/pulsecore/vector.h
@@ -23,7 +23,8 @@
#include <inttypes.h>
/* First, define HAVE_VECTOR if we have the gcc vector extensions at all */
-#if defined(__SSE2__) || defined(__ALTIVEC__)
+#if defined(__SSE2__)
+ /* || defined(__ALTIVEC__)*/
#define HAVE_VECTOR
diff --git a/src/tests/envelope-test.c b/src/tests/envelope-test.c
index 3af3044e..9382040b 100644
--- a/src/tests/envelope-test.c
+++ b/src/tests/envelope-test.c
@@ -34,8 +34,6 @@
#include <pulsecore/memblock.h>
#include <pulsecore/sample-util.h>
-#include <liboil/liboil.h>
-
const pa_envelope_def ramp_down = {
.n_points = 2,
.points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
@@ -202,7 +200,6 @@ int main(int argc, char *argv[]) {
.values = { PA_VOLUME_NORM, PA_VOLUME_NORM/2 }
};
- oil_init();
pa_log_set_level(PA_LOG_DEBUG);
pa_assert_se(pool = pa_mempool_new(FALSE, 0));
diff --git a/src/tests/get-binary-name-test.c b/src/tests/get-binary-name-test.c
index a34e38fd..e49f2eff 100644
--- a/src/tests/get-binary-name-test.c
+++ b/src/tests/get-binary-name-test.c
@@ -23,12 +23,33 @@
#include <limits.h>
#include <stdio.h>
+#include <string.h>
#include <pulse/util.h>
+#include <pulse/xmalloc.h>
int main(int argc, char *argv[]) {
- char exename[PATH_MAX];
+ char *exename;
+ size_t allocated = 128;
+
+ for (;;) {
+ exename = pa_xmalloc(allocated);
+
+ if (!pa_get_binary_name(exename, allocated)) {
+ printf("failed to read binary name\n");
+ pa_xfree(exename);
+ break;
+ }
+
+ if (strlen(exename) < allocated - 1) {
+ printf("%s\n", exename);
+ pa_xfree(exename);
+ break;
+ }
+
+ pa_xfree(exename);
+ allocated *= 2;
+ }
- printf("%s\n", pa_get_binary_name(exename, sizeof(exename)));
return 0;
}
diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
index 0c906d3e..007555c3 100644
--- a/src/tests/interpol-test.c
+++ b/src/tests/interpol-test.c
@@ -43,20 +43,37 @@ static pa_context *context = NULL;
static pa_stream *stream = NULL;
static pa_mainloop_api *mainloop_api = NULL;
static pa_bool_t playback = TRUE;
+static pa_usec_t latency = 0;
static void stream_write_cb(pa_stream *p, size_t nbytes, void *userdata) {
/* Just some silence */
- pa_assert_se(pa_stream_write(p, pa_xmalloc0(nbytes), nbytes, pa_xfree, 0, PA_SEEK_RELATIVE) == 0);
+
+ for (;;) {
+ void *data;
+
+ pa_assert_se((nbytes = pa_stream_writable_size(p)) != (size_t) -1);
+
+ if (nbytes <= 0)
+ break;
+
+ pa_assert_se(pa_stream_begin_write(p, &data, &nbytes) == 0);
+ pa_memzero(data, nbytes);
+ pa_assert_se(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, just drop the data */
+ /* We don't care about the data, just drop it */
- while (pa_stream_readable_size(p) > 0) {
- const void *d;
- size_t b;
+ for (;;) {
+ const void *data;
- pa_assert_se(pa_stream_peek(p, &d, &b) == 0);
+ pa_assert_se((nbytes = pa_stream_readable_size(p)) != (size_t) -1);
+
+ if (nbytes <= 0)
+ break;
+
+ pa_assert_se(pa_stream_peek(p, &data, &nbytes) == 0);
pa_assert_se(pa_stream_drop(p) == 0);
}
}
@@ -82,27 +99,36 @@ static void context_state_callback(pa_context *c, void *userdata) {
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;
+
fprintf(stderr, "Connection established.\n");
- stream = pa_stream_new(c, "interpol-test", &ss, NULL);
- assert(stream);
+ pa_assert_se(stream = pa_stream_new(c, "interpol-test", &ss, NULL));
if (playback) {
- pa_assert_se(pa_stream_connect_playback(stream, NULL, NULL, flags, NULL, NULL) == 0);
+ 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, NULL, flags) == 0);
+ pa_assert_se(pa_stream_connect_record(stream, NULL, &attr, flags) == 0);
pa_stream_set_read_callback(stream, stream_read_cb, NULL);
}
@@ -123,7 +149,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
int main(int argc, char *argv[]) {
pa_threaded_mainloop* m = NULL;
- int k, r;
+ int k;
struct timeval start, last_info = { 0, 0 };
pa_usec_t old_t = 0, old_rtc = 0;
#ifdef CORK
@@ -134,24 +160,22 @@ int main(int argc, char *argv[]) {
playback = argc <= 1 || !pa_streq(argv[1], "-r");
- /* Set up a new main loop */
- m = pa_threaded_mainloop_new();
- assert(m);
-
- mainloop_api = pa_threaded_mainloop_get_api(m);
+ latency =
+ (argc >= 2 && !pa_streq(argv[1], "-r")) ? atoi(argv[1]) :
+ (argc >= 3 ? atoi(argv[2]) : 0);
- context = pa_context_new(mainloop_api, argv[0]);
- assert(context);
+ /* Set up a new main loop */
+ pa_assert_se(m = pa_threaded_mainloop_new());
+ pa_assert_se(mainloop_api = pa_threaded_mainloop_get_api(m));
+ pa_assert_se(context = pa_context_new(mainloop_api, argv[0]));
pa_context_set_state_callback(context, context_state_callback, NULL);
- r = pa_context_connect(context, NULL, 0, NULL);
- assert(r >= 0);
+ pa_assert_se(pa_context_connect(context, NULL, 0, NULL) >= 0);
pa_gettimeofday(&start);
- r = pa_threaded_mainloop_start(m);
- assert(r >= 0);
+ pa_assert_se(pa_threaded_mainloop_start(m) >= 0);
/* #ifdef CORK */
for (k = 0; k < 20000; k++)
@@ -160,7 +184,7 @@ int main(int argc, char *argv[]) {
/* #endif */
{
pa_bool_t success = FALSE, changed = FALSE;
- pa_usec_t t, rtc;
+ pa_usec_t t, rtc, d;
struct timeval now, tv;
pa_bool_t playing = FALSE;
@@ -169,7 +193,8 @@ int main(int argc, char *argv[]) {
if (stream) {
const pa_timing_info *info;
- if (pa_stream_get_time(stream, &t) >= 0)
+ 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))) {
@@ -191,14 +216,16 @@ int main(int argc, char *argv[]) {
pa_bool_t cork_now;
#endif
rtc = pa_timeval_diff(&now, &start);
- printf("%i\t%llu\t%llu\t%llu\t%llu\t%lli\t%u\t%u\n", k,
+ printf("%i\t%llu\t%llu\t%llu\t%llu\t%lli\t%u\t%u\t%llu\t%llu\n", 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);
+ playing,
+ (unsigned long long) latency,
+ (unsigned long long) d);
fflush(stdout);
old_t = t;
diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c
index c7a30d67..457c4acd 100644
--- a/src/tests/mix-test.c
+++ b/src/tests/mix-test.c
@@ -32,8 +32,6 @@
#include <pulsecore/memblock.h>
#include <pulsecore/sample-util.h>
-#include <liboil/liboil.h>
-
static float swap_float(float a) {
uint32_t *b = (uint32_t*) &a;
*b = PA_UINT32_SWAP(*b);
@@ -69,6 +67,8 @@ static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
break;
}
+ case PA_SAMPLE_S24_32NE:
+ case PA_SAMPLE_S24_32RE:
case PA_SAMPLE_S32NE:
case PA_SAMPLE_S32RE: {
uint32_t *u = d;
@@ -83,8 +83,10 @@ static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
case PA_SAMPLE_S24RE: {
uint8_t *u = d;
- for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
- printf("0x%02x%02x%02xx ", *(u++), *(u++), *(u++));
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+ printf("0x%02x%02x%02xx ", *u, *(u+1), *(u+2));
+ u += 3;
+ }
break;
}
@@ -123,66 +125,72 @@ static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
case PA_SAMPLE_U8:
case PA_SAMPLE_ULAW:
case PA_SAMPLE_ALAW: {
- static const uint8_t u8_samples[] =
- { 0x00, 0xFF, 0x7F, 0x80, 0x9f,
- 0x3f, 0x01, 0xF0, 0x20, 0x21 };
+ static const uint8_t u8_samples[] = {
+ 0x00, 0xFF, 0x7F, 0x80, 0x9f,
+ 0x3f, 0x01, 0xF0, 0x20, 0x21
+ };
- memcpy(d, &u8_samples[0], sizeof(u8_samples));
+ memcpy(d, u8_samples, sizeof(u8_samples));
break;
}
case PA_SAMPLE_S16NE:
case PA_SAMPLE_S16RE: {
- static const uint16_t u16_samples[] =
- { 0x0000, 0xFFFF, 0x7FFF, 0x8000, 0x9fff,
- 0x3fff, 0x0001, 0xF000, 0x0020, 0x0021 };
+ static const uint16_t u16_samples[] = {
+ 0x0000, 0xFFFF, 0x7FFF, 0x8000, 0x9fff,
+ 0x3fff, 0x0001, 0xF000, 0x0020, 0x0021
+ };
- memcpy(d, &u16_samples[0], sizeof(u16_samples));
+ memcpy(d, u16_samples, sizeof(u16_samples));
break;
}
+ case PA_SAMPLE_S24_32NE:
+ case PA_SAMPLE_S24_32RE:
case PA_SAMPLE_S32NE:
case PA_SAMPLE_S32RE: {
- static const uint32_t u32_samples[] =
- { 0x00000001, 0xFFFF0002, 0x7FFF0003, 0x80000004, 0x9fff0005,
- 0x3fff0006, 0x00010007, 0xF0000008, 0x00200009, 0x0021000A };
+ static const uint32_t u32_samples[] = {
+ 0x00000001, 0xFFFF0002, 0x7FFF0003, 0x80000004, 0x9fff0005,
+ 0x3fff0006, 0x00010007, 0xF0000008, 0x00200009, 0x0021000A
+ };
- memcpy(d, &u32_samples[0], sizeof(u32_samples));
+ memcpy(d, u32_samples, sizeof(u32_samples));
break;
}
case PA_SAMPLE_S24NE:
case PA_SAMPLE_S24RE: {
- /* Need to be on a byte array because they are not aligned */
- static const uint8_t u24_samples[] =
- { 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 };
-
- memcpy(d, &u24_samples[0], sizeof(u24_samples));
+ /* Need to be on a byte array because they are not aligned */
+ static const uint8_t u24_samples[] = {
+ 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
+ };
+
+ memcpy(d, u24_samples, sizeof(u24_samples));
break;
}
case PA_SAMPLE_FLOAT32NE:
case PA_SAMPLE_FLOAT32RE: {
float *u = d;
- static const float float_samples[] =
- { 0.0f, -1.0f, 1.0f, 4711.0f, 0.222f,
- 0.33f, -.3f, 99.0f, -0.555f, -.123f };
+ static const float float_samples[] = {
+ 0.0f, -1.0f, 1.0f, 4711.0f, 0.222f,
+ 0.33f, -.3f, 99.0f, -0.555f, -.123f
+ };
if (ss->format == PA_SAMPLE_FLOAT32RE) {
for (i = 0; i < 10; i++)
u[i] = swap_float(float_samples[i]);
- } else {
- memcpy(d, &float_samples[0], sizeof(float_samples));
- }
+ } else
+ memcpy(d, float_samples, sizeof(float_samples));
break;
}
@@ -201,7 +209,6 @@ int main(int argc, char *argv[]) {
pa_sample_spec a;
pa_cvolume v;
- oil_init();
pa_log_set_level(PA_LOG_DEBUG);
pa_assert_se(pool = pa_mempool_new(FALSE, 0));
diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c
index 9d110d6b..4990bf93 100644
--- a/src/tests/remix-test.c
+++ b/src/tests/remix-test.c
@@ -32,8 +32,6 @@
#include <pulsecore/memblock.h>
#include <pulsecore/sample-util.h>
-#include <liboil/liboil.h>
-
int main(int argc, char *argv[]) {
static const pa_channel_map maps[] = {
@@ -55,7 +53,6 @@ int main(int argc, char *argv[]) {
unsigned i, j;
pa_mempool *pool;
- oil_init();
pa_log_set_level(PA_LOG_DEBUG);
pa_assert_se(pool = pa_mempool_new(FALSE, 0));
diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c
index 7236265a..82198b5e 100644
--- a/src/tests/resampler-test.c
+++ b/src/tests/resampler-test.c
@@ -32,8 +32,6 @@
#include <pulsecore/memblock.h>
#include <pulsecore/sample-util.h>
-#include <liboil/liboil.h>
-
static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
void *d;
unsigned i;
@@ -248,7 +246,6 @@ int main(int argc, char *argv[]) {
pa_sample_spec a, b;
pa_cvolume v;
- oil_init();
pa_log_set_level(PA_LOG_DEBUG);
pa_assert_se(pool = pa_mempool_new(FALSE, 0));
diff --git a/src/tests/stripnul.c b/src/tests/stripnul.c
index 1d8c4938..d677ad20 100644
--- a/src/tests/stripnul.c
+++ b/src/tests/stripnul.c
@@ -31,7 +31,7 @@
int main(int argc, char *argv[]) {
FILE *i, *o;
size_t granularity;
- pa_bool_t found;
+ pa_bool_t found = FALSE;
uint8_t *zero;
pa_assert_se(argc >= 2);
diff --git a/src/tests/usergroup-test.c b/src/tests/usergroup-test.c
new file mode 100644
index 00000000..a48b016d
--- /dev/null
+++ b/src/tests/usergroup-test.c
@@ -0,0 +1,161 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#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 <pulsecore/usergroup.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 (strcmp(a->gr_name, b->gr_name)) {
+ fprintf(stderr, "Group name mismatch: [%s] [%s]\n",
+ a->gr_name, b->gr_name);
+ return 1;
+ }
+
+ if (strcmp(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 (strcmp(*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 (strcmp(a->pw_name, b->pw_name)) {
+ fprintf(stderr, "pw_name mismatch: [%s] [%s]\n", a->pw_name, b->pw_name);
+ return 1;
+ }
+
+ if (strcmp(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 (strcmp(a->pw_gecos, b->pw_gecos)) {
+ fprintf(stderr, "pw_gecos mismatch: [%s] [%s]\n", a->pw_gecos, b->pw_gecos);
+ return 1;
+ }
+
+ if (strcmp(a->pw_dir, b->pw_dir)) {
+ fprintf(stderr, "pw_dir mismatch: [%s] [%s]\n", a->pw_dir, b->pw_dir);
+ return 1;
+ }
+
+ if (strcmp(a->pw_shell, b->pw_shell)) {
+ fprintf(stderr, "pw_shell mismatch: [%s] [%s]\n", a->pw_shell, b->pw_shell);
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ struct group *gr;
+ struct passwd *pw;
+ int err;
+ struct group *reference_group = NULL;
+ struct passwd *reference_passwd = NULL;
+
+ err = load_reference_structs(&reference_group, &reference_passwd);
+ if (err)
+ return 77;
+
+ errno = 0;
+ gr = pa_getgrgid_malloc(reference_group->gr_gid);
+ if (compare_group(reference_group, gr))
+ return 1;
+ pa_getgrgid_free(gr);
+
+ errno = 0;
+ gr = pa_getgrnam_malloc(reference_group->gr_name);
+ if (compare_group(reference_group, gr))
+ return 1;
+ pa_getgrnam_free(gr);
+
+ errno = 0;
+ pw = pa_getpwuid_malloc(reference_passwd->pw_uid);
+ if (compare_passwd(reference_passwd, pw))
+ return 1;
+ pa_getpwuid_free(pw);
+
+ errno = 0;
+ pw = pa_getpwnam_malloc(reference_passwd->pw_name);
+ if (compare_passwd(reference_passwd, pw))
+ return 1;
+ pa_getpwnam_free(pw);
+
+ return 0;
+}
diff --git a/src/tests/voltest.c b/src/tests/voltest.c
index 2dcfa53c..551f7ecd 100644
--- a/src/tests/voltest.c
+++ b/src/tests/voltest.c
@@ -1,13 +1,40 @@
+/***
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdio.h>
#include <pulse/volume.h>
#include <pulse/gccmacro.h>
+#include <pulsecore/macro.h>
+
int main(int argc, char *argv[]) {
pa_volume_t v;
pa_cvolume cv;
float b;
pa_channel_map map;
+ pa_volume_t md = 0;
+ unsigned mdn = 0;
printf("Attenuation of sample 1 against 32767: %g dB\n", 20.0*log10(1.0/32767.0));
printf("Smallest possible attenutation > 0 applied to 32767: %li\n", lrint(32767.0*pa_sw_volume_to_linear(1)));
@@ -60,5 +87,48 @@ int main(int argc, char *argv[]) {
printf("After: volume: [%s]; balance: %2.1f (intended: %2.1f) %s\n", pa_cvolume_snprint(s, sizeof(s), &r), 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;
+
+ pa_assert(k == v);
+ pa_assert(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);
+ qq = db + db2;
+ 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++;
+ }
+ }
+
+ printf("max deviation: %lu n=%lu\n", (unsigned long) md, (unsigned long) mdn);
+
+ pa_assert(md <= 1);
+ pa_assert(mdn <= 251);
+
return 0;
}
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
index f00a32eb..781bfa33 100644
--- a/src/utils/pacat.c
+++ b/src/utils/pacat.c
@@ -105,12 +105,12 @@ static void context_drain_complete(pa_context*c, void *userdata) {
static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
if (!success) {
- pa_log(_("Failed to drain stream: %s\n"), pa_strerror(pa_context_errno(context)));
+ pa_log(_("Failed to drain stream: %s"), pa_strerror(pa_context_errno(context)));
quit(1);
}
if (verbose)
- pa_log(_("Playback stream drained.\n"));
+ pa_log(_("Playback stream drained."));
pa_stream_disconnect(stream);
pa_stream_unref(stream);
@@ -120,7 +120,7 @@ static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
pa_context_disconnect(context);
else {
if (verbose)
- pa_log(_("Draining connection to server.\n"));
+ pa_log(_("Draining connection to server."));
}
}
@@ -133,7 +133,7 @@ static void start_drain(void) {
pa_stream_set_write_callback(stream, NULL, NULL);
if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
- pa_log(_("pa_stream_drain(): %s\n"), pa_strerror(pa_context_errno(context)));
+ pa_log(_("pa_stream_drain(): %s"), pa_strerror(pa_context_errno(context)));
quit(1);
return;
}
@@ -156,7 +156,7 @@ static void do_stream_write(size_t length) {
l = buffer_length;
if (pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE) < 0) {
- pa_log(_("pa_stream_write() failed: %s\n"), pa_strerror(pa_context_errno(context)));
+ pa_log(_("pa_stream_write() failed: %s"), pa_strerror(pa_context_errno(context)));
quit(1);
return;
}
@@ -193,7 +193,11 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
pa_assert(sndfile);
- data = pa_xmalloc(length);
+ if (pa_stream_begin_write(s, &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);
@@ -205,9 +209,9 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
bytes = sf_read_raw(sndfile, data, (sf_count_t) length);
if (bytes > 0)
- pa_stream_write(s, data, (size_t) bytes, pa_xfree, 0, PA_SEEK_RELATIVE);
+ pa_stream_write(s, data, (size_t) bytes, NULL, 0, PA_SEEK_RELATIVE);
else
- pa_xfree(data);
+ pa_stream_cancel_write(s);
if (bytes < (sf_count_t) length)
start_drain();
@@ -226,12 +230,11 @@ static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
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\n"), pa_strerror(pa_context_errno(context)));
+ pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
quit(1);
return;
}
@@ -249,6 +252,7 @@ static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
buffer_length = length;
buffer_index = 0;
}
+
pa_stream_drop(s);
}
@@ -260,7 +264,7 @@ static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
const void *data;
if (pa_stream_peek(s, &data, &length) < 0) {
- pa_log(_("pa_stream_peek() failed: %s\n"), pa_strerror(pa_context_errno(context)));
+ pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
quit(1);
return;
}
@@ -300,25 +304,25 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
const pa_buffer_attr *a;
char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
- pa_log(_("Stream successfully created.\n"));
+ pa_log(_("Stream successfully created."));
if (!(a = pa_stream_get_buffer_attr(s)))
- pa_log(_("pa_stream_get_buffer_attr() failed: %s\n"), pa_strerror(pa_context_errno(pa_stream_get_context(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\n"), a->maxlength, a->tlength, a->prebuf, a->minreq);
+ 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\n"), a->maxlength, a->fragsize);
+ pa_log(_("Buffer metrics: maxlength=%u, fragsize=%u"), a->maxlength, a->fragsize);
}
}
- pa_log(_("Using sample spec '%s', channel map '%s'.\n"),
+ 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 (%u, %ssuspended).\n"),
+ pa_log(_("Connected to device %s (%u, %ssuspended)."),
pa_stream_get_device_name(s),
pa_stream_get_device_index(s),
pa_stream_is_suspended(s) ? "" : "not ");
@@ -328,7 +332,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
case PA_STREAM_FAILED:
default:
- pa_log(_("Stream error: %s\n"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+ pa_log(_("Stream error: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
quit(1);
}
}
@@ -338,9 +342,9 @@ static void stream_suspended_callback(pa_stream *s, void *userdata) {
if (verbose) {
if (pa_stream_is_suspended(s))
- pa_log(_("Stream device suspended.%s \n"), CLEAR_LINE);
+ pa_log(_("Stream device suspended.%s"), CLEAR_LINE);
else
- pa_log(_("Stream device resumed.%s \n"), CLEAR_LINE);
+ pa_log(_("Stream device resumed.%s"), CLEAR_LINE);
}
}
@@ -348,35 +352,35 @@ static void stream_underflow_callback(pa_stream *s, void *userdata) {
pa_assert(s);
if (verbose)
- pa_log(_("Stream underrun.%s \n"), CLEAR_LINE);
+ 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 \n"), CLEAR_LINE);
+ 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 \n"), CLEAR_LINE);
+ 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 \n"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "), CLEAR_LINE);
+ 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 \n"), CLEAR_LINE);
+ 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) {
@@ -387,7 +391,7 @@ static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *p
pa_assert(pl);
t = pa_proplist_to_string_sep(pl, ", ");
- pa_log("Got event '%s', properties '%s'\n", name, t);
+ pa_log("Got event '%s', properties '%s'", name, t);
pa_xfree(t);
}
@@ -402,17 +406,16 @@ static void context_state_callback(pa_context *c, void *userdata) {
break;
case PA_CONTEXT_READY: {
- int r;
pa_buffer_attr buffer_attr;
pa_assert(c);
pa_assert(!stream);
if (verbose)
- pa_log(_("Connection established.%s \n"), CLEAR_LINE);
+ 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\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c)));
goto fail;
}
@@ -439,14 +442,14 @@ static void context_state_callback(pa_context *c, void *userdata) {
if (mode == PLAYBACK) {
pa_cvolume cv;
- if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL)) < 0) {
- pa_log(_("pa_stream_connect_playback() failed: %s\n"), pa_strerror(pa_context_errno(c)));
+ if (pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, 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 ((r = pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags)) < 0) {
- pa_log(_("pa_stream_connect_record() failed: %s\n"), pa_strerror(pa_context_errno(c)));
+ if (pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags) < 0) {
+ pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c)));
goto fail;
}
}
@@ -460,7 +463,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_FAILED:
default:
- pa_log(_("Connection failure: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
goto fail;
}
@@ -493,12 +496,12 @@ static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_even
if ((r = read(fd, buffer, l)) <= 0) {
if (r == 0) {
if (verbose)
- pa_log(_("Got EOF.\n"));
+ pa_log(_("Got EOF."));
start_drain();
} else {
- pa_log(_("read() failed: %s\n"), strerror(errno));
+ pa_log(_("read() failed: %s"), strerror(errno));
quit(1);
}
@@ -530,7 +533,7 @@ static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_eve
pa_assert(buffer_length);
if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
- pa_log(_("write() failed: %s\n"), strerror(errno));
+ pa_log(_("write() failed: %s"), strerror(errno));
quit(1);
mainloop_api->io_free(stdio_event);
@@ -551,7 +554,7 @@ static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_eve
/* UNIX signal to quit recieved */
static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
if (verbose)
- pa_log(_("Got signal, exiting.\n"));
+ pa_log(_("Got signal, exiting."));
quit(0);
}
@@ -565,14 +568,15 @@ static void stream_update_timing_callback(pa_stream *s, int success, void *userd
if (!success ||
pa_stream_get_time(s, &usec) < 0 ||
pa_stream_get_latency(s, &l, &negative) < 0) {
- pa_log(_("Failed to get latency: %s\n"), pa_strerror(pa_context_errno(context)));
+ pa_log(_("Failed to get latency: %s"), pa_strerror(pa_context_errno(context)));
quit(1);
return;
}
- pa_log(_("Time: %0.3f sec; Latency: %0.0f usec. \r"),
+ fprintf(stderr, _("Time: %0.3f sec; Latency: %0.0f usec."),
(float) usec / 1000000,
(float) l * (negative?-1.0f:1.0f));
+ fprintf(stderr, " \r");
}
/* Someone requested that the latency is shown */
@@ -588,7 +592,7 @@ static void time_event_callback(pa_mainloop_api *m, pa_time_event *e, const stru
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\n"), pa_strerror(pa_context_errno(context)));
+ pa_log(_("pa_stream_update_timing_info() failed: %s"), pa_strerror(pa_context_errno(context)));
else
pa_operation_unref(o);
}
@@ -753,7 +757,7 @@ int main(int argc, char *argv[]) {
if (!(t = pa_locale_to_utf8(optarg)) ||
pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
- pa_log(_("Invalid client name '%s'\n"), t ? t : optarg);
+ pa_log(_("Invalid client name '%s'"), t ? t : optarg);
pa_xfree(t);
goto quit;
}
@@ -764,12 +768,11 @@ int main(int argc, char *argv[]) {
case ARG_STREAM_NAME: {
char *t;
- t = pa_locale_to_utf8(optarg);
if (!(t = pa_locale_to_utf8(optarg)) ||
pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) {
- pa_log(_("Invalid stream name '%s'\n"), t ? t : optarg);
+ pa_log(_("Invalid stream name '%s'"), t ? t : optarg);
pa_xfree(t);
goto quit;
}
@@ -806,7 +809,7 @@ int main(int argc, char *argv[]) {
case ARG_CHANNELMAP:
if (!pa_channel_map_parse(&channel_map, optarg)) {
- pa_log(_("Invalid channel map '%s'\n"), optarg);
+ pa_log(_("Invalid channel map '%s'"), optarg);
goto quit;
}
@@ -835,14 +838,14 @@ int main(int argc, char *argv[]) {
case ARG_LATENCY:
if (((latency = (size_t) atoi(optarg))) <= 0) {
- pa_log(_("Invalid latency specification '%s'\n"), optarg);
+ 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'\n"), optarg);
+ pa_log(_("Invalid process time specification '%s'"), optarg);
goto quit;
}
break;
@@ -854,7 +857,7 @@ int main(int argc, char *argv[]) {
pa_proplist_setp(proplist, t) < 0) {
pa_xfree(t);
- pa_log(_("Invalid property '%s'\n"), optarg);
+ pa_log(_("Invalid property '%s'"), optarg);
goto quit;
}
@@ -890,7 +893,7 @@ int main(int argc, char *argv[]) {
}
if (!pa_sample_spec_valid(&sample_spec)) {
- pa_log(_("Invalid sample specification\n"));
+ pa_log(_("Invalid sample specification"));
goto quit;
}
@@ -900,19 +903,19 @@ int main(int argc, char *argv[]) {
filename = argv[optind];
if ((fd = open(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
- pa_log(_("open(): %s\n"), strerror(errno));
+ pa_log(_("open(): %s"), strerror(errno));
goto quit;
}
if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
- pa_log(_("dup2(): %s\n"), strerror(errno));
+ pa_log(_("dup2(): %s"), strerror(errno));
goto quit;
}
pa_close(fd);
} else if (optind+1 <= argc) {
- pa_log(_("Too many arguments.\n"));
+ pa_log(_("Too many arguments."));
goto quit;
}
@@ -923,7 +926,7 @@ int main(int argc, char *argv[]) {
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.\n"));
+ pa_log(_("Failed to generate sample specification for file."));
goto quit;
}
@@ -943,16 +946,16 @@ int main(int argc, char *argv[]) {
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.\n"));
+ 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.\n"));
+ 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.\n"));
+ pa_log(_("Failed to determine sample specification from file."));
goto quit;
}
sample_spec_set = TRUE;
@@ -961,7 +964,7 @@ int main(int argc, char *argv[]) {
/* 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.\n"));
+ pa_log(_("Warning: Failed to determine channel map from file."));
} else
channel_map_set = TRUE;
}
@@ -972,7 +975,7 @@ int main(int argc, char *argv[]) {
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\n"));
+ pa_log(_("Channel map doesn't match sample specification"));
goto quit;
}
@@ -983,7 +986,7 @@ int main(int argc, char *argv[]) {
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.\n"));
+ pa_log(_("Warning: failed to write channel map to file."));
writef_function = pa_sndfile_writef_function(&sample_spec);
}
@@ -998,7 +1001,7 @@ int main(int argc, char *argv[]) {
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'.\n"),
+ 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));
@@ -1025,7 +1028,7 @@ int main(int argc, char *argv[]) {
/* Set up a new main loop */
if (!(m = pa_mainloop_new())) {
- pa_log(_("pa_mainloop_new() failed.\n"));
+ pa_log(_("pa_mainloop_new() failed."));
goto quit;
}
@@ -1044,14 +1047,14 @@ int main(int argc, char *argv[]) {
mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
- pa_log(_("io_new() failed.\n"));
+ 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.\n"));
+ pa_log(_("pa_context_new() failed."));
goto quit;
}
@@ -1059,20 +1062,20 @@ int main(int argc, char *argv[]) {
/* Connect the context */
if (pa_context_connect(context, server, 0, NULL) < 0) {
- pa_log(_("pa_context_connect() failed: %s\n"), pa_strerror(pa_context_errno(context)));
+ 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.\n"));
+ 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.\n"));
+ pa_log(_("pa_mainloop_run() failed."));
goto quit;
}
diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c
index ac60a0bc..5ef57e3b 100644
--- a/src/utils/pacmd.c
+++ b/src/utils/pacmd.c
@@ -25,7 +25,7 @@
#include <assert.h>
#include <signal.h>
-#include <sys/select.h>
+#include <sys/poll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
@@ -45,6 +45,13 @@
int main(int argc, char*argv[]) {
+ enum {
+ WATCH_STDIN,
+ WATCH_STDOUT,
+ WATCH_SOCKET,
+ N_WATCH
+ };
+
pid_t pid ;
int fd = -1;
int ret = 1, i;
@@ -53,6 +60,7 @@ int main(int argc, char*argv[]) {
size_t ibuf_index, ibuf_length, obuf_index, obuf_length;
char *cli;
pa_bool_t ibuf_eof, obuf_eof, ibuf_closed, obuf_closed;
+ struct pollfd pollfd[N_WATCH];
setlocale(LC_ALL, "");
bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
@@ -108,7 +116,7 @@ int main(int argc, char*argv[]) {
size_t k;
k = PA_MIN(sizeof(ibuf) - ibuf_length, strlen(argv[i]));
- memcpy(ibuf + ibuf_length, argv[1], k);
+ memcpy(ibuf + ibuf_length, argv[i], k);
ibuf_length += k;
if (ibuf_length < sizeof(ibuf)) {
@@ -120,38 +128,45 @@ int main(int argc, char*argv[]) {
ibuf_eof = TRUE;
}
- for (;;) {
- fd_set ifds, ofds;
+ pa_zero(pollfd);
+ pollfd[WATCH_STDIN].fd = STDIN_FILENO;
+ pollfd[WATCH_STDOUT].fd = STDOUT_FILENO;
+ pollfd[WATCH_SOCKET].fd = fd;
+
+ for (;;) {
if (ibuf_eof &&
obuf_eof &&
ibuf_length <= 0 &&
obuf_length <= 0)
break;
- FD_ZERO(&ifds);
- FD_ZERO(&ofds);
+ pollfd[WATCH_STDIN].events = pollfd[WATCH_STDOUT].events = pollfd[WATCH_SOCKET].events = 0;
if (obuf_length > 0)
- FD_SET(1, &ofds);
+ pollfd[WATCH_STDOUT].events |= POLLOUT;
else if (!obuf_eof)
- FD_SET(fd, &ifds);
+ pollfd[WATCH_SOCKET].events |= POLLIN;
if (ibuf_length > 0)
- FD_SET(fd, &ofds);
+ pollfd[WATCH_SOCKET].events |= POLLOUT;
else if (!ibuf_eof)
- FD_SET(0, &ifds);
+ pollfd[WATCH_STDIN].events |= POLLIN;
- if (select(FD_SETSIZE, &ifds, &ofds, NULL, NULL) < 0) {
- pa_log(_("select(): %s"), strerror(errno));
+ if (poll(pollfd, N_WATCH, -1) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ pa_log(_("poll(): %s"), strerror(errno));
goto fail;
}
- if (FD_ISSET(0, &ifds)) {
+ if (pollfd[WATCH_STDIN].revents & POLLIN) {
ssize_t r;
pa_assert(!ibuf_length);
- if ((r = pa_read(0, ibuf, sizeof(ibuf), NULL)) <= 0) {
+ if ((r = pa_read(STDIN_FILENO, ibuf, sizeof(ibuf), NULL)) <= 0) {
if (r < 0) {
pa_log(_("read(): %s"), strerror(errno));
goto fail;
@@ -164,7 +179,7 @@ int main(int argc, char*argv[]) {
}
}
- if (FD_ISSET(fd, &ifds)) {
+ if (pollfd[WATCH_SOCKET].revents & POLLIN) {
ssize_t r;
pa_assert(!obuf_length);
@@ -181,21 +196,26 @@ int main(int argc, char*argv[]) {
}
}
- if (FD_ISSET(1, &ofds)) {
+ if (pollfd[WATCH_STDOUT].revents & POLLHUP) {
+ obuf_eof = TRUE;
+ obuf_length = 0;
+ } else if (pollfd[WATCH_STDOUT].revents & POLLOUT) {
ssize_t r;
pa_assert(obuf_length);
- if ((r = pa_write(1, obuf + obuf_index, obuf_length, NULL)) < 0) {
+ if ((r = pa_write(STDOUT_FILENO, obuf + obuf_index, obuf_length, NULL)) < 0) {
pa_log(_("write(): %s"), strerror(errno));
goto fail;
}
obuf_length -= (size_t) r;
obuf_index += obuf_index;
-
}
- if (FD_ISSET(fd, &ofds)) {
+ if (pollfd[WATCH_SOCKET].revents & POLLHUP) {
+ ibuf_eof = TRUE;
+ ibuf_length = 0;
+ } if (pollfd[WATCH_SOCKET].revents & POLLOUT) {
ssize_t r;
pa_assert(ibuf_length);
@@ -209,14 +229,14 @@ int main(int argc, char*argv[]) {
}
if (ibuf_length <= 0 && ibuf_eof && !ibuf_closed) {
- pa_close(0);
+ pa_close(STDIN_FILENO);
shutdown(fd, SHUT_WR);
ibuf_closed = TRUE;
}
if (obuf_length <= 0 && obuf_eof && !obuf_closed) {
shutdown(fd, SHUT_RD);
- pa_close(1);
+ pa_close(STDOUT_FILENO);
obuf_closed = TRUE;
}
}
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index c8c3a437..141ab5b1 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -50,7 +50,6 @@ static pa_context *context = NULL;
static pa_mainloop_api *mainloop_api = NULL;
static char
- *device = NULL,
*sample_name = NULL,
*sink_name = NULL,
*source_name = NULL,
@@ -66,6 +65,8 @@ static uint32_t
static uint32_t module_index;
static pa_bool_t suspend;
+static pa_bool_t mute;
+static pa_volume_t volume;
static pa_proplist *proplist = NULL;
@@ -74,7 +75,6 @@ static pa_stream *sample_stream = NULL;
static pa_sample_spec sample_spec;
static pa_channel_map channel_map;
static size_t sample_length = 0;
-
static int actions = 1;
static pa_bool_t nl = FALSE;
@@ -95,7 +95,13 @@ static enum {
SUSPEND_SOURCE,
SET_CARD_PROFILE,
SET_SINK_PORT,
- SET_SOURCE_PORT
+ SET_SOURCE_PORT,
+ SET_SINK_VOLUME,
+ SET_SOURCE_VOLUME,
+ SET_SINK_INPUT_VOLUME,
+ SET_SINK_MUTE,
+ SET_SOURCE_MUTE,
+ SET_SINK_INPUT_MUTE
} action = NONE;
static void quit(int ret) {
@@ -109,6 +115,7 @@ static void context_drain_complete(pa_context *c, void *userdata) {
static void drain(void) {
pa_operation *o;
+
if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
pa_context_disconnect(context);
else
@@ -123,9 +130,9 @@ static void complete_action(void) {
}
static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
- char s[128];
+ char s[PA_BYTES_SNPRINT_MAX];
if (!i) {
- pa_log(_("Failed to get statistics: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get statistics: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -146,7 +153,7 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
if (!i) {
- pa_log(_("Failed to get server information: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -195,7 +202,7 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
char *pl;
if (is_last < 0) {
- pa_log(_("Failed to get sink information: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -287,7 +294,7 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
char *pl;
if (is_last < 0) {
- pa_log(_("Failed to get source information: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -365,7 +372,7 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int
char *pl;
if (is_last < 0) {
- pa_log(_("Failed to get module information: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -402,7 +409,7 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int
char *pl;
if (is_last < 0) {
- pa_log(_("Failed to get client information: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get client information: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -437,7 +444,7 @@ static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_
char *pl;
if (is_last < 0) {
- pa_log(_("Failed to get card information: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get card information: %s"), pa_strerror(pa_context_errno(c)));
complete_action();
return;
}
@@ -486,7 +493,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
char *pl;
if (is_last < 0) {
- pa_log(_("Failed to get sink input information: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -544,7 +551,7 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
char *pl;
if (is_last < 0) {
- pa_log(_("Failed to get source output information: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -591,11 +598,11 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
}
static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
- char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char t[PA_BYTES_SNPRINT_MAX], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
char *pl;
if (is_last < 0) {
- pa_log(_("Failed to get sample information: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failed to get sample information: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -643,7 +650,7 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
static void simple_callback(pa_context *c, int success, void *userdata) {
if (!success) {
- pa_log(_("Failure: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -653,7 +660,7 @@ static void simple_callback(pa_context *c, int success, void *userdata) {
static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
if (idx == PA_INVALID_INDEX) {
- pa_log(_("Failure: %s\n"), pa_strerror(pa_context_errno(c)));
+ pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
@@ -677,7 +684,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
case PA_STREAM_FAILED:
default:
- pa_log(_("Failed to upload sample: %s\n"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+ pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
quit(1);
}
}
@@ -694,7 +701,7 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
if ((sf_readf_float(sndfile, d, l)) != l) {
pa_xfree(d);
- pa_log(_("Premature end of file\n"));
+ pa_log(_("Premature end of file"));
quit(1);
return;
}
@@ -726,7 +733,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
break;
case PLAY_SAMPLE:
- pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL));
+ pa_operation_unref(pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL));
break;
case REMOVE_SAMPLE:
@@ -800,6 +807,42 @@ static void context_state_callback(pa_context *c, void *userdata) {
pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
break;
+ case SET_SINK_MUTE:
+ pa_operation_unref(pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL));
+ break;
+
+ case SET_SOURCE_MUTE:
+ pa_operation_unref(pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL));
+ break;
+
+ case SET_SINK_INPUT_MUTE:
+ pa_operation_unref(pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL));
+ break;
+
+ case SET_SINK_VOLUME: {
+ pa_cvolume v;
+
+ pa_cvolume_set(&v, 1, volume);
+ pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL));
+ break;
+ }
+
+ case SET_SOURCE_VOLUME: {
+ pa_cvolume v;
+
+ pa_cvolume_set(&v, 1, volume);
+ pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL));
+ break;
+ }
+
+ case SET_SINK_INPUT_VOLUME: {
+ pa_cvolume v;
+
+ pa_cvolume_set(&v, 1, volume);
+ pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL));
+ break;
+ }
+
default:
pa_assert_not_reached();
}
@@ -811,13 +854,13 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_FAILED:
default:
- pa_log(_("Connection failure: %s\n"), pa_strerror(pa_context_errno(c)));
+ 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.\n"));
+ pa_log(_("Got SIGINT, exiting."));
quit(0);
}
@@ -829,20 +872,30 @@ static void help(const char *argv0) {
"%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 ID SINK\n"
- "%s [options] move-source-output ID SOURCE\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 ID\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\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"),
- argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
+ argv0, argv0, argv0, argv0, argv0,
+ argv0, argv0, argv0, argv0, argv0,
+ argv0, argv0, argv0, argv0, argv0,
+ argv0, argv0, argv0, argv0, argv0,
+ argv0);
}
enum {
@@ -897,7 +950,7 @@ int main(int argc, char *argv[]) {
if (!(t = pa_locale_to_utf8(optarg)) ||
pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
- pa_log(_("Invalid client name '%s'\n"), t ? t : optarg);
+ pa_log(_("Invalid client name '%s'"), t ? t : optarg);
pa_xfree(t);
goto quit;
}
@@ -923,7 +976,7 @@ int main(int argc, char *argv[]) {
action = UPLOAD_SAMPLE;
if (optind+1 >= argc) {
- pa_log(_("Please specify a sample file to load\n"));
+ pa_log(_("Please specify a sample file to load"));
goto quit;
}
@@ -936,19 +989,19 @@ int main(int argc, char *argv[]) {
pa_zero(sfi);
if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
- pa_log(_("Failed to open sound file.\n"));
+ 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.\n"));
+ 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.\n"));
+ pa_log(_("Warning: Failed to determine sample specification from file."));
pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
}
@@ -958,19 +1011,19 @@ int main(int argc, char *argv[]) {
} 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\n"));
+ pa_log(_("You have to specify a sample name to play"));
goto quit;
}
sample_name = pa_xstrdup(argv[optind+1]);
if (optind+2 < argc)
- device = pa_xstrdup(argv[optind+2]);
+ 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\n"));
+ pa_log(_("You have to specify a sample name to remove"));
goto quit;
}
@@ -979,7 +1032,7 @@ int main(int argc, char *argv[]) {
} 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\n"));
+ pa_log(_("You have to specify a sink input index and a sink"));
goto quit;
}
@@ -989,7 +1042,7 @@ int main(int argc, char *argv[]) {
} 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\n"));
+ pa_log(_("You have to specify a source output index and a source"));
goto quit;
}
@@ -1004,7 +1057,7 @@ int main(int argc, char *argv[]) {
action = LOAD_MODULE;
if (argc <= optind+1) {
- pa_log(_("You have to specify a module name and arguments.\n"));
+ pa_log(_("You have to specify a module name and arguments."));
goto quit;
}
@@ -1024,7 +1077,7 @@ int main(int argc, char *argv[]) {
action = UNLOAD_MODULE;
if (argc != optind+2) {
- pa_log(_("You have to specify a module index\n"));
+ pa_log(_("You have to specify a module index"));
goto quit;
}
@@ -1034,7 +1087,7 @@ int main(int argc, char *argv[]) {
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.\n"));
+ pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
goto quit;
}
@@ -1047,7 +1100,7 @@ int main(int argc, char *argv[]) {
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.\n"));
+ pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
goto quit;
}
@@ -1059,7 +1112,7 @@ int main(int argc, char *argv[]) {
action = SET_CARD_PROFILE;
if (argc != optind+3) {
- pa_log(_("You have to specify a card name/index and a profile name\n"));
+ pa_log(_("You have to specify a card name/index and a profile name"));
goto quit;
}
@@ -1070,7 +1123,7 @@ int main(int argc, char *argv[]) {
action = SET_SINK_PORT;
if (argc != optind+3) {
- pa_log(_("You have to specify a sink name/index and a port name\n"));
+ pa_log(_("You have to specify a sink name/index and a port name"));
goto quit;
}
@@ -1081,13 +1134,123 @@ int main(int argc, char *argv[]) {
action = SET_SOURCE_PORT;
if (argc != optind+3) {
- pa_log(_("You have to specify a source name/index and a port name\n"));
+ 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-sink-volume")) {
+ uint32_t v;
+ action = SET_SINK_VOLUME;
+
+ if (argc != optind+3) {
+ pa_log(_("You have to specify a sink name/index and a volume"));
+ goto quit;
+ }
+
+ if (pa_atou(argv[optind+2], &v) < 0) {
+ pa_log(_("Invalid volume specification"));
+ goto quit;
+ }
+
+ sink_name = pa_xstrdup(argv[optind+1]);
+ volume = (pa_volume_t) v;
+
+ } else if (pa_streq(argv[optind], "set-source-volume")) {
+ uint32_t v;
+ action = SET_SOURCE_VOLUME;
+
+ if (argc != optind+3) {
+ pa_log(_("You have to specify a source name/index and a volume"));
+ goto quit;
+ }
+
+ if (pa_atou(argv[optind+2], &v) < 0) {
+ pa_log(_("Invalid volume specification"));
+ goto quit;
+ }
+
+ source_name = pa_xstrdup(argv[optind+1]);
+ volume = (pa_volume_t) v;
+
+ } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
+ uint32_t v;
+ 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 (pa_atou(argv[optind+2], &v) < 0) {
+ pa_log(_("Invalid volume specification"));
+ goto quit;
+ }
+
+ volume = (pa_volume_t) v;
+
+ } else if (pa_streq(argv[optind], "set-sink-mute")) {
+ int b;
+ action = SET_SINK_MUTE;
+
+ if (argc != optind+3) {
+ pa_log(_("You have to specify a sink name/index and a mute boolean"));
+ goto quit;
+ }
+
+ if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
+ pa_log(_("Invalid volume specification"));
+ goto quit;
+ }
+
+ sink_name = pa_xstrdup(argv[optind+1]);
+ mute = b;
+
+ } else if (pa_streq(argv[optind], "set-source-mute")) {
+ int b;
+ action = SET_SOURCE_MUTE;
+
+ if (argc != optind+3) {
+ pa_log(_("You have to specify a source name/index and a mute boolean"));
+ goto quit;
+ }
+
+ if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
+ pa_log(_("Invalid volume specification"));
+ goto quit;
+ }
+
+ source_name = pa_xstrdup(argv[optind+1]);
+ mute = b;
+
+ } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
+ int b;
+ action = SET_SINK_INPUT_MUTE;
+
+ if (argc != optind+3) {
+ pa_log(_("You have to specify a sink input index and a mute boolean"));
+ goto quit;
+ }
+
+ if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
+ pa_log(_("Invalid sink input index specification"));
+ goto quit;
+ }
+
+ if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
+ pa_log(_("Invalid volume specification"));
+ goto quit;
+ }
+
+ mute = b;
+
} else if (pa_streq(argv[optind], "help")) {
help(bn);
ret = 0;
@@ -1096,12 +1259,12 @@ int main(int argc, char *argv[]) {
}
if (action == NONE) {
- pa_log(_("No valid command specified.\n"));
+ pa_log(_("No valid command specified."));
goto quit;
}
if (!(m = pa_mainloop_new())) {
- pa_log(_("pa_mainloop_new() failed.\n"));
+ pa_log(_("pa_mainloop_new() failed."));
goto quit;
}
@@ -1113,7 +1276,7 @@ int main(int argc, char *argv[]) {
pa_disable_sigpipe();
if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
- pa_log(_("pa_context_new() failed.\n"));
+ pa_log(_("pa_context_new() failed."));
goto quit;
}
@@ -1124,7 +1287,7 @@ int main(int argc, char *argv[]) {
}
if (pa_mainloop_run(m, &ret) < 0) {
- pa_log(_("pa_mainloop_run() failed.\n"));
+ pa_log(_("pa_mainloop_run() failed."));
goto quit;
}
@@ -1141,7 +1304,6 @@ quit:
}
pa_xfree(server);
- pa_xfree(device);
pa_xfree(sample_name);
pa_xfree(sink_name);
pa_xfree(source_name);
diff --git a/src/utils/padsp.c b/src/utils/padsp.c
index dfa5aac2..41bfd741 100644
--- a/src/utils/padsp.c
+++ b/src/utils/padsp.c
@@ -53,6 +53,7 @@
#include <pulse/pulseaudio.h>
#include <pulse/gccmacro.h>
#include <pulsecore/llist.h>
+#include <pulsecore/core-util.h>
/* On some systems SIOCINQ isn't defined, but FIONREAD is just an alias */
#if !defined(SIOCINQ) && defined(FIONREAD)
@@ -459,15 +460,16 @@ static void reset_params(fd_info *i) {
}
static const char *client_name(char *buf, size_t n) {
- char p[PATH_MAX];
+ char *p;
const char *e;
if ((e = getenv("PADSP_CLIENT_NAME")))
return e;
- if (pa_get_binary_name(p, sizeof(p)))
+ if ((p = pa_get_binary_name_malloc())) {
snprintf(buf, n, "OSS Emulation[%s]", p);
- else
+ pa_xfree(p);
+ } else
snprintf(buf, n, "OSS");
return buf;
@@ -1819,7 +1821,7 @@ fail:
pa_threaded_mainloop_unlock(i->mainloop);
- return 0;
+ return r;
}
static int dsp_trigger(fd_info *i) {
@@ -1862,7 +1864,7 @@ fail:
pa_threaded_mainloop_unlock(i->mainloop);
- return 0;
+ return r;
}
static int dsp_cork(fd_info *i, pa_stream *s, int b) {
@@ -1900,7 +1902,7 @@ fail:
pa_threaded_mainloop_unlock(i->mainloop);
- return 0;
+ return r;
}
static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {