summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--PROTOCOL10
-rwxr-xr-xbootstrap.sh15
-rw-r--r--configure.ac212
-rw-r--r--man/pulse-daemon.conf.5.xml.in9
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am89
-rw-r--r--src/daemon/cmdline.c6
-rw-r--r--src/daemon/daemon-conf.c28
-rw-r--r--src/daemon/daemon-conf.h13
-rw-r--r--src/daemon/daemon.conf.in2
-rwxr-xr-xsrc/daemon/default.pa.in4
-rw-r--r--src/daemon/main.c15
-rw-r--r--src/map-file16
l---------src/modules/alsa/Makefile1
-rw-r--r--src/modules/alsa/alsa-sink.c (renamed from src/modules/module-alsa-sink.c)250
-rw-r--r--src/modules/alsa/alsa-sink.h36
-rw-r--r--src/modules/alsa/alsa-source.c (renamed from src/modules/module-alsa-source.c)249
-rw-r--r--src/modules/alsa/alsa-source.h36
-rw-r--r--src/modules/alsa/alsa-util.c (renamed from src/modules/alsa-util.c)526
-rw-r--r--src/modules/alsa/alsa-util.h (renamed from src/modules/alsa-util.h)42
-rw-r--r--src/modules/alsa/module-alsa-card.c360
-rw-r--r--src/modules/alsa/module-alsa-sink.c125
-rw-r--r--src/modules/alsa/module-alsa-source.c149
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c233
-rw-r--r--src/modules/bluetooth/module-bluetooth-discover.c291
-rw-r--r--src/modules/bluetooth/module-bluetooth-proximity.c4
-rw-r--r--src/modules/dbus-util.h2
-rw-r--r--src/modules/hal-util.c127
-rw-r--r--src/modules/hal-util.h30
-rw-r--r--src/modules/module-always-sink.c10
-rw-r--r--src/modules/module-card-restore.c284
-rw-r--r--src/modules/module-combine.c5
-rw-r--r--src/modules/module-console-kit.c22
-rw-r--r--src/modules/module-default-device-restore.c22
-rw-r--r--src/modules/module-defs.h.m42
-rw-r--r--src/modules/module-detect.c2
-rw-r--r--src/modules/module-device-restore.c40
-rw-r--r--src/modules/module-esound-sink.c12
-rw-r--r--src/modules/module-flat-volume.c224
-rw-r--r--src/modules/module-hal-detect.c657
-rw-r--r--src/modules/module-jack-sink.c9
-rw-r--r--src/modules/module-jack-source.c9
-rw-r--r--src/modules/module-ladspa-sink.c15
-rw-r--r--src/modules/module-lirc.c6
-rw-r--r--src/modules/module-match.c66
-rw-r--r--src/modules/module-mmkbd-evdev.c6
-rw-r--r--src/modules/module-null-sink.c9
-rw-r--r--src/modules/module-pipe-sink.c9
-rw-r--r--src/modules/module-pipe-source.c9
-rw-r--r--src/modules/module-position-event-sounds.c42
-rw-r--r--src/modules/module-raop-sink.c44
-rw-r--r--src/modules/module-remap-sink.c15
-rw-r--r--src/modules/module-rescue-streams.c16
-rw-r--r--src/modules/module-sine-source.c22
-rw-r--r--src/modules/module-sine.c4
-rw-r--r--src/modules/module-stream-restore.c228
-rw-r--r--src/modules/module-suspend-on-idle.c68
-rw-r--r--src/modules/module-tunnel.c18
-rw-r--r--src/modules/module-volume-restore.c507
-rw-r--r--src/modules/module-x11-bell.c2
-rw-r--r--src/modules/module-x11-xsmp.c19
l---------src/modules/oss/Makefile1
-rw-r--r--src/modules/oss/module-oss.c (renamed from src/modules/module-oss.c)100
-rw-r--r--src/modules/oss/oss-util.c (renamed from src/modules/oss-util.c)17
-rw-r--r--src/modules/oss/oss-util.h (renamed from src/modules/oss-util.h)0
-rw-r--r--src/modules/raop/base64.c9
-rw-r--r--src/modules/rtp/module-rtp-recv.c4
-rw-r--r--src/modules/rtp/module-rtp-send.c2
-rw-r--r--src/modules/rtp/rtp.c4
-rw-r--r--src/modules/rtp/sap.c4
-rw-r--r--src/pulse/browser.h1
-rw-r--r--src/pulse/channelmap.c200
-rw-r--r--src/pulse/channelmap.h25
-rw-r--r--src/pulse/context.c3
-rw-r--r--src/pulse/context.h5
-rw-r--r--src/pulse/def.h135
-rw-r--r--src/pulse/error.h1
-rw-r--r--src/pulse/ext-stream-restore.h1
-rw-r--r--src/pulse/gccmacro.h4
-rw-r--r--src/pulse/glib-mainloop.h1
-rw-r--r--src/pulse/i18n.h1
-rw-r--r--src/pulse/internal.h37
-rw-r--r--src/pulse/introspect.c388
-rw-r--r--src/pulse/introspect.h100
-rw-r--r--src/pulse/mainloop-api.h1
-rw-r--r--src/pulse/mainloop-signal.h2
-rw-r--r--src/pulse/operation.h1
-rw-r--r--src/pulse/proplist.c117
-rw-r--r--src/pulse/proplist.h38
-rw-r--r--src/pulse/pulseaudio.h1
-rw-r--r--src/pulse/sample.c26
-rw-r--r--src/pulse/sample.h55
-rw-r--r--src/pulse/scache.c8
-rw-r--r--src/pulse/scache.h5
-rw-r--r--src/pulse/simple.h1
-rw-r--r--src/pulse/stream.c2
-rw-r--r--src/pulse/subscribe.h1
-rw-r--r--src/pulse/thread-mainloop.h1
-rw-r--r--src/pulse/timeval.h12
-rw-r--r--src/pulse/utf8.h1
-rw-r--r--src/pulse/util.h1
-rw-r--r--src/pulse/version.h.in12
-rw-r--r--src/pulse/volume.c119
-rw-r--r--src/pulse/volume.h30
-rw-r--r--src/pulse/xmalloc.h1
-rw-r--r--src/pulsecore/asyncq.c16
-rw-r--r--src/pulsecore/atomic.h73
-rw-r--r--src/pulsecore/autoload.c202
-rw-r--r--src/pulsecore/autoload.h58
-rw-r--r--src/pulsecore/bitset.c67
-rw-r--r--src/pulsecore/bitset.h37
-rw-r--r--src/pulsecore/card.c254
-rw-r--r--src/pulsecore/card.h100
-rw-r--r--src/pulsecore/cli-command.c374
-rw-r--r--src/pulsecore/cli-text.c264
-rw-r--r--src/pulsecore/cli-text.h3
-rw-r--r--src/pulsecore/cli.c19
-rw-r--r--src/pulsecore/client.c48
-rw-r--r--src/pulsecore/client.h17
-rw-r--r--src/pulsecore/core-scache.c42
-rw-r--r--src/pulsecore/core-scache.h3
-rw-r--r--src/pulsecore/core-util.c60
-rw-r--r--src/pulsecore/core-util.h8
-rw-r--r--src/pulsecore/core.c23
-rw-r--r--src/pulsecore/core.h41
-rw-r--r--src/pulsecore/endianmacros.h38
-rw-r--r--src/pulsecore/envelope.c1
-rw-r--r--src/pulsecore/log.c8
-rw-r--r--src/pulsecore/log.h2
-rw-r--r--src/pulsecore/module.c101
-rw-r--r--src/pulsecore/module.h14
-rw-r--r--src/pulsecore/mutex-posix.c20
-rw-r--r--src/pulsecore/mutex.h7
-rw-r--r--src/pulsecore/namereg.c120
-rw-r--r--src/pulsecore/namereg.h13
-rw-r--r--src/pulsecore/native-common.h14
-rw-r--r--src/pulsecore/pdispatch.c8
-rw-r--r--src/pulsecore/play-memblockq.c4
-rw-r--r--src/pulsecore/protocol-esound.c40
-rw-r--r--src/pulsecore/protocol-http.c10
-rw-r--r--src/pulsecore/protocol-native.c386
-rw-r--r--src/pulsecore/protocol-simple.c24
-rw-r--r--src/pulsecore/ratelimit.c75
-rw-r--r--src/pulsecore/ratelimit.h46
-rw-r--r--src/pulsecore/resampler.c32
-rw-r--r--src/pulsecore/resampler.h4
-rw-r--r--src/pulsecore/rtclock.c26
-rw-r--r--src/pulsecore/rtclock.h1
-rw-r--r--src/pulsecore/sample-util.c329
-rw-r--r--src/pulsecore/sconv-s16be.c30
-rw-r--r--src/pulsecore/sconv-s16be.h30
-rw-r--r--src/pulsecore/sconv-s16le.c225
-rw-r--r--src/pulsecore/sconv-s16le.h30
-rw-r--r--src/pulsecore/sconv.c16
-rw-r--r--src/pulsecore/shared.c9
-rw-r--r--src/pulsecore/sink-input.c391
-rw-r--r--src/pulsecore/sink-input.h78
-rw-r--r--src/pulsecore/sink.c348
-rw-r--r--src/pulsecore/sink.h64
-rw-r--r--src/pulsecore/socket-util.c8
-rw-r--r--src/pulsecore/sound-file-stream.c4
-rw-r--r--src/pulsecore/source-output.c199
-rw-r--r--src/pulsecore/source-output.h38
-rw-r--r--src/pulsecore/source.c170
-rw-r--r--src/pulsecore/source.h51
-rw-r--r--src/pulsecore/strbuf.c6
-rw-r--r--src/pulsecore/strbuf.h3
-rw-r--r--src/pulsecore/tagstruct.h4
-rw-r--r--src/pulsecore/vector.h97
-rw-r--r--src/tests/proplist-test.c11
-rw-r--r--src/tests/rtstutter.c19
-rw-r--r--src/tests/vector-test.c83
-rw-r--r--src/tests/voltest.c29
-rw-r--r--src/utils/pactl.c368
175 files changed, 8469 insertions, 3638 deletions
diff --git a/Makefile.am b/Makefile.am
index f4dd9989..8735d312 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,7 +18,7 @@
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = bootstrap.sh LICENSE GPL LGPL doxygen/Makefile.am doxygen/Makefile.in doxygen/doxygen.conf.in README todo
-SUBDIRS = libltdl src doxygen man po
+SUBDIRS = src doxygen man po
MAINTAINERCLEANFILES =
noinst_DATA =
diff --git a/PROTOCOL b/PROTOCOL
index 80a0c331..8c5937b3 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -154,8 +154,7 @@ PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end:
early_requests (bool)
-
-### v15, implemented by >= 0.9.14
+### v15, implemented by >= 0.9.15
PA_COMMAND_CREATE_PLAYBACK_STREAM
@@ -163,4 +162,9 @@ PA_COMMAND_CREATE_PLAYBACK_STREAM
PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM:
- bool dont_inhibit_auto_suspend ate the end
+ bool dont_inhibit_auto_suspend at the end
+
+PA_COMMAND_GET_MODULE_INFO_LIST
+
+ remove bool auto_unload
+ add proplist at the end
diff --git a/bootstrap.sh b/bootstrap.sh
index 4eacc5a9..e64f3426 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -40,6 +40,15 @@ run_versioned() {
set -ex
+# We check for this here, because if pkg-config is not found in the
+# system, it's likely that the pkg.m4 macro file is also not present,
+# which will make PKG_PROG_PKG_CONFIG be undefined and the generated
+# configure file faulty.
+if ! pkg-config --version &>/dev/null; then
+ echo "pkg-config is required to bootstrap this program" &>/dev/null
+ exit 1
+fi
+
if [ "x$1" = "xam" ] ; then
run_versioned automake "$VERSION" -a -c --foreign
./config.status
@@ -57,10 +66,10 @@ else
test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize
intltoolize --copy --force --automake
- "$LIBTOOLIZE" -c --force --ltdl --recursive
+ "$LIBTOOLIZE" -c --force
run_versioned aclocal "$VERSION" -I m4
- run_versioned autoconf 2.62 -Wall
- run_versioned autoheader 2.62
+ run_versioned autoconf 2.63 -Wall
+ run_versioned autoheader 2.63
run_versioned automake "$VERSION" --copy --foreign --add-missing
if test "x$NOCONFIGURE" = "x"; then
diff --git a/configure.ac b/configure.ac
index c24658b1..c02b20ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -22,18 +22,21 @@
AC_PREREQ(2.63)
-m4_define(PA_MAJOR, [0])
-m4_define(PA_MINOR, [9])
-m4_define(PA_MICRO, [14])
+m4_define(pa_major, [0])
+m4_define(pa_minor, [9])
+m4_define(pa_micro, [15])
-AC_INIT([pulseaudio],[PA_MAJOR.PA_MINOR.PA_MICRO],[mzchyfrnhqvb (at) 0pointer (dot) net])
+AC_INIT([pulseaudio],[pa_major.pa_minor.pa_micro],[mzchyfrnhqvb (at) 0pointer (dot) net])
AC_CONFIG_SRCDIR([src/daemon/main.c])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign 1.10 -Wall])
-AC_SUBST(PA_MAJORMINOR, PA_MAJOR.PA_MINOR)
-AC_SUBST(PA_MAJORMINORMICRO, PA_MAJOR.PA_MINOR.PA_MICRO)
+AC_SUBST(PA_MAJOR, pa_major)
+AC_SUBST(PA_MINOR, pa_minor)
+AC_SUBST(PA_MICRO, pa_micro)
+AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
+AC_SUBST(PA_MAJORMINORMICRO, pa_major.pa_minor.pa_micro)
AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
AC_SUBST(PA_API_VERSION, 12)
@@ -41,7 +44,7 @@ AC_SUBST(PA_PROTOCOL_VERSION, 15)
# The stable ABI for client applications, for the version info x:y:z
# always will hold y=z
-AC_SUBST(LIBPULSE_VERSION_INFO, [7:0:7])
+AC_SUBST(LIBPULSE_VERSION_INFO, [7:1:7])
# A simplified, synchronous, ABI-stable interface for client
# applications, for the version info x:y:z always will hold y=z
@@ -101,6 +104,12 @@ for flag in $DESIRED_FLAGS ; do
CC_CHECK_CFLAGS([$flag], [CFLAGS="$CFLAGS $flag"])
done
+dnl Check whether to build tests by default (as compile-test) or not
+AC_ARG_ENABLE([default-build-tests],
+ AS_HELP_STRING([--disable-default-build-tests], [Build test programs only during make check]))
+
+AM_CONDITIONAL([BUILD_TESTS_DEFAULT], [test "x$enable_default_build_tests" != "xno"])
+
# Native atomic operation support
AC_ARG_ENABLE([atomic-arm-linux-helpers],
AS_HELP_STRING([--disable-atomic-arm-linux-helpers],[use inline asm or libatomic_ops instead]),
@@ -123,16 +132,31 @@ AC_ARG_ENABLE([atomic-arm-memory-barrier],
esac
],)
+AC_ARG_ENABLE([netbsd-atomic-ops],
+ AS_HELP_STRING([--enable-netbsd-atomic-ops],[Use the native NetBSD atomic_ops implementation]),
+ [
+ case "${enableval}" in
+ yes) atomic_netbsd_helpers=yes ;;
+ no) atomic_netbsd_helpers=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-netbsd-atomic-ops) ;;
+ esac
+ ],
+ [atomic_netbsd_helpers=auto])
+
AC_MSG_CHECKING([target operating system])
case $host in
- *-*-linux*)
- AC_MSG_RESULT([linux])
- pulse_target_os=linux
- ;;
- *)
- AC_MSG_RESULT([unknown])
- pulse_target_os=unknown
- ;;
+ *-*-linux*)
+ AC_MSG_RESULT([linux])
+ pulse_target_os=linux
+ ;;
+ *-*-netbsd*)
+ AC_MSG_RESULT([netbsd])
+ pulse_target_os=netbsd
+ ;;
+ *)
+ AC_MSG_RESULT([unknown])
+ pulse_target_os=unknown
+ ;;
esac
# If everything else fails use libatomic_ops
@@ -154,42 +178,48 @@ else
AC_MSG_CHECKING([architecture for native atomic operations])
case $host_cpu in
arm*)
- AC_MSG_RESULT([arm])
- AC_MSG_CHECKING([whether we can use Linux kernel helpers])
- # The Linux kernel helper functions have been there since 2.6.16. However
- # compile time checking for kernel version in cross compile environment
- # (which is usually the case for arm cpu) is tricky (or impossible).
- if test "x$pulse_target_os" = "xlinux" && test "x$atomic_arm_linux_helpers" != "xno"; then
- AC_MSG_RESULT([yes])
- AC_DEFINE_UNQUOTED(ATOMIC_ARM_LINUX_HELPERS, 1, [special arm linux implementation])
- need_libatomic_ops=no
- else
- AC_MSG_RESULT([no])
- AC_CACHE_CHECK([compiler support for arm inline asm atomic operations],
- pulseaudio_cv_support_arm_atomic_ops,
- [AC_COMPILE_IFELSE(
- AC_LANG_PROGRAM([],
- [[volatile int a=0;
- int o=0, n=1, r;
- asm volatile ("ldrex %0, [%1]\n"
- "subs %0, %0, %2\n"
- "strexeq %0, %3, [%1]\n"
- : "=&r" (r)
- : "r" (&a), "Ir" (o), "r" (n)
- : "cc");
+ AC_MSG_RESULT([arm])
+ AC_MSG_CHECKING([whether we can use Linux kernel helpers])
+ # The Linux kernel helper functions have been there since 2.6.16. However
+ # compile time checking for kernel version in cross compile environment
+ # (which is usually the case for arm cpu) is tricky (or impossible).
+ if test "x$pulse_target_os" = "xlinux" && test "x$atomic_arm_linux_helpers" != "xno"; then
+ AC_MSG_RESULT([yes])
+ AC_DEFINE_UNQUOTED(ATOMIC_ARM_LINUX_HELPERS, 1, [special arm linux implementation])
+ need_libatomic_ops=no
+ else
+ AC_MSG_RESULT([no])
+ AC_CACHE_CHECK([compiler support for arm inline asm atomic operations],
+ pulseaudio_cv_support_arm_atomic_ops,
+ [AC_COMPILE_IFELSE(
+ AC_LANG_PROGRAM([],
+ [[volatile int a=0;
+ int o=0, n=1, r;
+ asm volatile ("ldrex %0, [%1]\n"
+ "subs %0, %0, %2\n"
+ "strexeq %0, %3, [%1]\n"
+ : "=&r" (r)
+ : "r" (&a), "Ir" (o), "r" (n)
+ : "cc");
return (a==1 ? 0 : -1);
- ]]),
- [pulseaudio_cv_support_arm_atomic_ops=yes],
- [pulseaudio_cv_support_arm_atomic_ops=no])
- ])
- AS_IF([test "$pulseaudio_cv_support_arm_atomic_ops" = "yes"], [
- AC_DEFINE([ATOMIC_ARM_INLINE_ASM], 1, [Have ARMv6 instructions.])
- need_libatomic_ops=no
- ])
- fi
- ;;
+ ]]),
+ [pulseaudio_cv_support_arm_atomic_ops=yes],
+ [pulseaudio_cv_support_arm_atomic_ops=no])
+ ])
+ AS_IF([test "$pulseaudio_cv_support_arm_atomic_ops" = "yes"], [
+ AC_DEFINE([ATOMIC_ARM_INLINE_ASM], 1, [Have ARMv6 instructions.])
+ need_libatomic_ops=no
+ ])
+ fi
+ ;;
*)
- AC_MSG_RESULT([unknown])
+ if test "x$pulse_target_os" = "xnetbsd" && test "x$atomic_netbsd_helpers" = "xyes"; then
+ AC_MSG_RESULT([yes])
+ AC_DEFINE_UNQUOTED(NETBSD_ATOMIC_OPS, 1, [netbsd implementation])
+ need_libatomic_ops=no
+ else
+ AC_MSG_RESULT([unknown])
+ fi
;;
esac
fi
@@ -210,20 +240,43 @@ AS_IF([test "$pulseaudio_cv__Bool" = "yes"], [
#### libtool stuff ####
LT_PREREQ(2.2)
-LT_CONFIG_LTDL_DIR([libltdl])
LT_INIT([dlopen win32-dll disable-static])
-LTDL_INIT([convenience recursive])
+
+dnl Unfortunately, even up to libtool 2.2.6a there is no way to know
+dnl exactly which version of libltdl is present in the system, so we
+dnl just assume that it's a working version as long as we have the
+dnl library and the header files.
+dnl
+dnl As an extra safety device, check for lt_dladvise_init() which is
+dnl only implemented in libtool 2.x, and refine as we go if we have
+dnl refined requirements.
+dnl
+dnl Check the header files first since the system may have a
+dnl libltdl.so for runtime, but no headers, and we want to bail out as
+dnl soon as possible.
+dnl
+dnl We don't need any special variable for this though, since the user
+dnl can give the proper place to find libltdl through the standard
+dnl variables like LDFLAGS and CPPFLAGS.
+
+AC_CHECK_HEADER([ltdl.h],
+ [AC_CHECK_LIB([ltdl], [lt_dladvise_init], [LIBLTDL=-lltdl], [LIBLTDL=])],
+ [LIBLTDL=])
+
+AS_IF([test "x$LIBLTDL" = "x"],
+ [AC_MSG_ERROR([Unable to find libltdl.])])
+AC_SUBST([LIBLTDL])
#### Determine build environment ####
os_is_win32=0
case "$host_os" in
- mingw*)
+ mingw*)
AC_DEFINE([OS_IS_WIN32], 1, [Build target is Windows.])
os_is_win32=1
- ;;
- esac
+ ;;
+ esac
AM_CONDITIONAL(OS_IS_WIN32, test "x$os_is_win32" = "x1")
@@ -240,16 +293,16 @@ AC_HEADER_STDC
AC_CHECK_HEADERS([arpa/inet.h glob.h grp.h netdb.h netinet/in.h \
netinet/in_systm.h netinet/tcp.h poll.h pwd.h sched.h \
sys/mman.h sys/resource.h sys/select.h sys/socket.h sys/wait.h \
- syslog.h sys/dl.h dlfcn.h linux/sockios.h])
+ sys/uio.h syslog.h sys/dl.h dlfcn.h linux/sockios.h])
AC_CHECK_HEADERS([netinet/ip.h], [], [],
- [#include <sys/types.h>
- #if HAVE_NETINET_IN_H
- # include <netinet/in.h>
- #endif
- #if HAVE_NETINET_IN_SYSTM_H
- # include <netinet/in_systm.h>
- #endif
- ])
+ [#include <sys/types.h>
+ #if HAVE_NETINET_IN_H
+ # include <netinet/in.h>
+ #endif
+ #if HAVE_NETINET_IN_SYSTM_H
+ # include <netinet/in_systm.h>
+ #endif
+ ])
AC_CHECK_HEADERS([regex.h], [HAVE_REGEX=1], [HAVE_REGEX=0])
AC_CHECK_HEADERS([sys/un.h], [HAVE_AF_UNIX=1], [HAVE_AF_UNIX=0])
@@ -269,6 +322,9 @@ AC_CHECK_HEADERS([sys/filio.h])
# Windows
AC_CHECK_HEADERS([windows.h winsock2.h ws2tcpip.h])
+# NetBSD
+AC_CHECK_HEADERS([sys/atomic.h])
+
# Other
AC_CHECK_HEADERS([sys/ioctl.h])
AC_CHECK_HEADERS([byteswap.h])
@@ -337,10 +393,10 @@ AC_CHECK_FUNCS([lrintf strtof])
AC_FUNC_FORK
AC_FUNC_GETGROUPS
AC_FUNC_SELECT_ARGTYPES
-AC_CHECK_FUNCS([chmod chown clock_gettime getaddrinfo getgrgid_r \
- getpwuid_r gettimeofday getuid inet_ntop inet_pton mlock nanosleep \
+AC_CHECK_FUNCS([chmod chown clock_gettime getaddrinfo getgrgid_r getgrnam_r \
+ getpwnam_r getpwuid_r gettimeofday getuid inet_ntop inet_pton mlock nanosleep \
pipe posix_fadvise posix_madvise posix_memalign setpgid setsid shm_open \
- sigaction sleep sysconf])
+ sigaction sleep sysconf pthread_setaffinity_np])
AC_CHECK_FUNCS([mkfifo], [HAVE_MKFIFO=1], [HAVE_MKFIFO=0])
AM_CONDITIONAL(HAVE_MKFIFO, test "x$HAVE_MKFIFO" = "x1")
@@ -413,14 +469,6 @@ AC_SUBST(pulselocaledir)
#### pkg-config ####
-# Check for pkg-config manually first, as if its not installed the
-# PKG_PROG_PKG_CONFIG macro won't be defined.
-AC_CHECK_PROG(have_pkg_config, pkg-config, yes, no)
-
-if test x"$have_pkg_config" = "xno"; then
- AC_MSG_ERROR(pkg-config is required to install this program)
-fi
-
PKG_PROG_PKG_CONFIG
#### X11 (optional) ####
@@ -870,8 +918,11 @@ AC_ARG_ENABLE([hal],
],
[hal=auto])
if test "x${hal}" != xno -a \( "x$HAVE_OSS" = "x1" -o "x$HAVE_ALSA" = "x1" \) ; then
- PKG_CHECK_MODULES(HAL, [ hal >= 0.5.7 ],
- HAVE_HAL=1,
+ PKG_CHECK_MODULES(HAL, [ hal >= 0.5.11 ],
+ [
+ HAVE_HAL=1
+ AC_DEFINE([HAVE_HAL], 1, [Have HAL.])
+ ],
[
HAVE_HAL=0
if test "x$hal" = xyes ; then
@@ -1139,12 +1190,11 @@ AC_SUBST(modlibexecdir)
AC_ARG_ENABLE(
[force-preopen],
AS_HELP_STRING([--enable-force-preopen],[Preopen modules, even when dlopen() is supported.]),
- [FORCE_PREOPEN=1], [FORCE_PREOPEN=0])
-AM_CONDITIONAL([FORCE_PREOPEN], [test "x$FORCE_PREOPEN" = "x1"])
+ [FORCE_PREOPEN=$enableval], [FORCE_PREOPEN=no])
+AM_CONDITIONAL([FORCE_PREOPEN], [test "x$FORCE_PREOPEN" = "xyes"])
AC_CONFIG_FILES([
Makefile
-libltdl/Makefile
src/Makefile
man/Makefile
libpulse.pc
@@ -1255,6 +1305,7 @@ echo "
System Config Path: ${PA_SYSTEM_CONFIG_PATH}
Compiler: ${CC}
CFLAGS: ${CFLAGS}
+
Have X11: ${ENABLE_X11}
Enable OSS: ${ENABLE_OSS}
Enable Alsa: ${ENABLE_ALSA}
@@ -1271,9 +1322,12 @@ echo "
Enable libsamplerate: ${ENABLE_LIBSAMPLERATE}
Enable PolicyKit: ${ENABLE_POLKIT}
Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
+
System User: ${PA_SYSTEM_USER}
System Group: ${PA_SYSTEM_GROUP}
Realtime Group: ${PA_REALTIME_GROUP}
Access Group: ${PA_ACCESS_GROUP}
Enable per-user EsounD socket: ${ENABLE_PER_USER_ESOUND_SOCKET}
+ Force preopen: ${FORCE_PREOPEN}
+ Preopened modules: ${PREOPEN_MODS}
"
diff --git a/man/pulse-daemon.conf.5.xml.in b/man/pulse-daemon.conf.5.xml.in
index a516ee3f..9ddcd6ae 100644
--- a/man/pulse-daemon.conf.5.xml.in
+++ b/man/pulse-daemon.conf.5.xml.in
@@ -140,7 +140,6 @@ USA.
precedence.</p>
</option>
-
<option>
<p><opt>system-instance=</opt> Run the daemon as system-wide
instance, requires root priviliges. Takes a boolean argument,
@@ -148,7 +147,6 @@ USA.
argument takes precedence.</p>
</option>
-
<option>
<p><opt>disable-shm=</opt> Disable data transfer via POSIX
shared memory. Takes a boolean argument, defaults to
@@ -165,6 +163,13 @@ USA.
memory overcommit.</p>
</option>
+ <option>
+ <p><opt>flat-volumes=</opt> Enable 'flat' volumes, i.e. where
+ possible let the sink volume equal the maximum of the volumes of
+ the inputs connected to it. Takes a boolean argument, defaults
+ to <opt>yes</opt>.</p>
+ </option>
+
</section>
<section name="Scheduling">
diff --git a/src/.gitignore b/src/.gitignore
index 72c38cc6..66738d0a 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -58,3 +58,4 @@ thread-test
utf8-test
voltest
start-pulseaudio-x11
+vector-test
diff --git a/src/Makefile.am b/src/Makefile.am
index dd9035b5..00dd53d1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,13 +47,19 @@ endif
AM_CFLAGS = \
-I$(top_srcdir)/src \
+ -I$(top_builddir)/src \
-I$(top_srcdir)/src/modules \
+ -I$(top_builddir)/src/modules \
-I$(top_srcdir)/src/modules/rtp \
+ -I$(top_builddir)/src/modules/rtp \
-I$(top_srcdir)/src/modules/gconf \
+ -I$(top_builddir)/src/modules/gconf \
-I$(top_srcdir)/src/modules/bluetooth \
+ -I$(top_builddir)/src/modules/bluetooth \
+ -I$(top_srcdir)/src/modules/alsa \
+ -I$(top_builddir)/src/modules/alsa \
-I$(top_srcdir)/src/modules/raop \
$(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS \
- $(LTDLINCL) \
$(LIBSAMPLERATE_CFLAGS) \
$(LIBSNDFILE_CFLAGS) \
$(LIBSPEEX_CFLAGS) \
@@ -226,6 +232,7 @@ TESTS = \
strlist-test \
close-test \
voltest \
+ vector-test \
memblockq-test \
channelmap-test \
thread-mainloop-test \
@@ -248,7 +255,7 @@ TESTS = \
lock-autospawn-test \
prioq-test
-noinst_PROGRAMS = \
+TESTS_BINARIES = \
mainloop-test \
mcalign-test \
pacat-simple \
@@ -256,6 +263,7 @@ noinst_PROGRAMS = \
strlist-test \
close-test \
voltest \
+ vector-test \
memblockq-test \
sync-playback \
interpol-test \
@@ -288,7 +296,7 @@ if HAVE_SIGXCPU
#TESTS += \
# cpulimit-test \
# cpulimit-test2
-noinst_PROGRAMS += \
+TESTS_BINARIES += \
cpulimit-test \
cpulimit-test2
endif
@@ -296,10 +304,16 @@ endif
if HAVE_GLIB20
TESTS += \
mainloop-test-glib
-noinst_PROGRAMS += \
+TESTS_BINARIES += \
mainloop-test-glib
endif
+if BUILD_TESTS_DEFAULT
+noinst_PROGRAMS = $(TESTS_BINARIES)
+else
+check_PROGRAMS = $(TESTS_BINARIES)
+endif
+
mainloop_test_SOURCES = tests/mainloop-test.c
mainloop_test_CFLAGS = $(AM_CFLAGS)
mainloop_test_LDADD = $(AM_LDADD) libpulse.la
@@ -395,6 +409,11 @@ voltest_CFLAGS = $(AM_CFLAGS)
voltest_LDADD = $(AM_LDADD) libpulse.la
voltest_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+vector_test_SOURCES = tests/vector-test.c
+vector_test_CFLAGS = $(AM_CFLAGS)
+vector_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
+vector_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
channelmap_test_SOURCES = tests/channelmap-test.c
channelmap_test_CFLAGS = $(AM_CFLAGS)
channelmap_test_LDADD = $(AM_LDADD) libpulse.la
@@ -514,7 +533,8 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \
pulsecore/llist.h \
pulsecore/lock-autospawn.c pulsecore/lock-autospawn.h \
pulsecore/log.c pulsecore/log.h \
- pulsecore/macro.h \
+ pulsecore/ratelimit.c pulsecore/ratelimit.h \
+ pulsecore/macro.h pulsecore/vector.h \
pulsecore/mcalign.c pulsecore/mcalign.h \
pulsecore/memblock.c pulsecore/memblock.h \
pulsecore/memblockq.c pulsecore/memblockq.h \
@@ -536,6 +556,7 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \
pulsecore/refcnt.h \
pulsecore/rtclock.c pulsecore/rtclock.h \
pulsecore/shm.c pulsecore/shm.h \
+ pulsecore/bitset.c pulsecore/bitset.h \
pulsecore/socket-client.c pulsecore/socket-client.h \
pulsecore/socket-server.c pulsecore/socket-server.h \
pulsecore/socket-util.c pulsecore/socket-util.h \
@@ -712,10 +733,10 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \
pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \
pulsecore/asyncq.c pulsecore/asyncq.h \
pulsecore/auth-cookie.c pulsecore/auth-cookie.h \
- pulsecore/autoload.c pulsecore/autoload.h \
pulsecore/cli-command.c pulsecore/cli-command.h \
pulsecore/cli-text.c pulsecore/cli-text.h \
pulsecore/client.c pulsecore/client.h \
+ pulsecore/card.c pulsecore/card.h \
pulsecore/core-scache.c pulsecore/core-scache.h \
pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
pulsecore/core.c pulsecore/core.h \
@@ -801,7 +822,7 @@ libcli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecom
libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h
libprotocol_cli_la_LDFLAGS = -avoid-version
-libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la libcli.la
libprotocol_http_la_SOURCES = pulsecore/protocol-http.c pulsecore/protocol-http.h
libprotocol_http_la_LDFLAGS = -avoid-version
@@ -842,7 +863,6 @@ libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore-@PA_MAJORMIN
###################################
modlibexec_LTLIBRARIES += \
- module-flat-volume.la \
module-cli.la \
module-cli-protocol-tcp.la \
module-simple-protocol-tcp.la \
@@ -852,6 +872,7 @@ modlibexec_LTLIBRARIES += \
module-volume-restore.la \
module-device-restore.la \
module-stream-restore.la \
+ module-card-restore.la \
module-default-device-restore.la \
module-always-sink.la \
module-rescue-streams.la \
@@ -920,7 +941,8 @@ if HAVE_ALSA
modlibexec_LTLIBRARIES += \
libalsa-util.la \
module-alsa-sink.la \
- module-alsa-source.la
+ module-alsa-source.la \
+ module-alsa-card.la
endif
if HAVE_SOLARIS
@@ -1036,9 +1058,10 @@ SYMDEF_FILES = \
modules/module-x11-bell-symdef.h \
modules/module-x11-publish-symdef.h \
modules/module-x11-xsmp-symdef.h \
- modules/module-oss-symdef.h \
- modules/module-alsa-sink-symdef.h \
- modules/module-alsa-source-symdef.h \
+ modules/oss/module-oss-symdef.h \
+ modules/alsa/module-alsa-sink-symdef.h \
+ modules/alsa/module-alsa-source-symdef.h \
+ modules/alsa/module-alsa-card-symdef.h \
modules/module-solaris-symdef.h \
modules/module-waveout-symdef.h \
modules/module-detect-symdef.h \
@@ -1049,6 +1072,7 @@ SYMDEF_FILES = \
modules/module-volume-restore-symdef.h \
modules/module-device-restore-symdef.h \
modules/module-stream-restore-symdef.h \
+ modules/module-card-restore-symdef.h \
modules/module-default-device-restore-symdef.h \
modules/module-always-sink-symdef.h \
modules/module-rescue-streams-symdef.h \
@@ -1061,25 +1085,15 @@ SYMDEF_FILES = \
modules/module-raop-discover-symdef.h \
modules/gconf/module-gconf-symdef.h \
modules/module-position-event-sounds-symdef.h \
- modules/module-console-kit-symdef.h \
- modules/module-flat-volume-symdef.h
+ modules/module-console-kit-symdef.h
EXTRA_DIST += $(SYMDEF_FILES)
BUILT_SOURCES += $(SYMDEF_FILES)
$(SYMDEF_FILES): modules/module-defs.h.m4
- $(MKDIR_P) modules
- $(MKDIR_P) modules/gconf
- $(MKDIR_P) modules/rtp
- $(MKDIR_P) modules/bluetooth
+ $(MKDIR_P) $(dir $@)
$(M4) -Dfname="$@" $< > $@
-# Flat volume
-
-module_flat_volume_la_SOURCES = modules/module-flat-volume.c
-module_flat_volume_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_flat_volume_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
-
# Simple protocol
module_simple_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
@@ -1232,31 +1246,42 @@ module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X11_LIBS) libpulsecore-@PA_MAJORMINOR
# OSS
-liboss_util_la_SOURCES = modules/oss-util.c modules/oss-util.h
+liboss_util_la_SOURCES = modules/oss/oss-util.c modules/oss/oss-util.h
liboss_util_la_LDFLAGS = -avoid-version
liboss_util_la_LIBADD = libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
-module_oss_la_SOURCES = modules/module-oss.c
+module_oss_la_SOURCES = modules/oss/module-oss.c
module_oss_la_LDFLAGS = $(MODULE_LDFLAGS)
module_oss_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# ALSA
-libalsa_util_la_SOURCES = modules/alsa-util.c modules/alsa-util.h
+libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h
libalsa_util_la_LDFLAGS = -avoid-version
libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
-module_alsa_sink_la_SOURCES = modules/module-alsa-sink.c
+if HAVE_HAL
+libalsa_util_la_SOURCES += modules/hal-util.h modules/hal-util.c
+libalsa_util_la_LIBADD += $(HAL_LIBS) libdbus-util.la
+libalsa_util_la_CFLAGS += $(HAL_CFLAGS)
+endif
+
+module_alsa_sink_la_SOURCES = modules/alsa/module-alsa-sink.c
module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
-module_alsa_source_la_SOURCES = modules/module-alsa-source.c
+module_alsa_source_la_SOURCES = modules/alsa/module-alsa-source.c
module_alsa_source_la_LDFLAGS = $(MODULE_LDFLAGS)
module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+module_alsa_card_la_SOURCES = modules/alsa/module-alsa-card.c
+module_alsa_card_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_alsa_card_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_alsa_card_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
# Solaris
module_solaris_la_SOURCES = modules/module-solaris.c
@@ -1326,6 +1351,12 @@ module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_stream_restore_la_CFLAGS = $(AM_CFLAGS)
+# Card profile restore module
+module_card_restore_la_SOURCES = modules/module-card-restore.c
+module_card_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_card_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_card_restore_la_CFLAGS = $(AM_CFLAGS)
+
# Default sink/source restore module
module_default_device_restore_la_SOURCES = modules/module-default-device-restore.c
module_default_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index cc3d714b..43a4a326 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -52,7 +52,6 @@ enum {
ARG_DISALLOW_MODULE_LOADING,
ARG_DISALLOW_EXIT,
ARG_EXIT_IDLE_TIME,
- ARG_MODULE_IDLE_TIME,
ARG_SCACHE_IDLE_TIME,
ARG_LOG_TARGET,
ARG_LOG_META,
@@ -88,7 +87,6 @@ static const struct option long_options[] = {
{"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING},
{"disallow-exit", 2, 0, ARG_DISALLOW_EXIT},
{"exit-idle-time", 2, 0, ARG_EXIT_IDLE_TIME},
- {"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME},
{"scache-idle-time", 2, 0, ARG_SCACHE_IDLE_TIME},
{"log-target", 1, 0, ARG_LOG_TARGET},
{"log-meta", 2, 0, ARG_LOG_META},
@@ -352,10 +350,6 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
conf->exit_idle_time = atoi(optarg);
break;
- case ARG_MODULE_IDLE_TIME:
- conf->module_idle_time = atoi(optarg);
- break;
-
case ARG_SCACHE_IDLE_TIME:
conf->scache_idle_time = atoi(optarg);
break;
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index d7ffc105..279fb7ba 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -64,8 +64,8 @@ static const pa_daemon_conf default_conf = {
.realtime_priority = 5, /* Half of JACK's default rtprio */
.disallow_module_loading = FALSE,
.disallow_exit = FALSE,
+ .flat_volumes = TRUE,
.exit_idle_time = 20,
- .module_idle_time = 20,
.scache_idle_time = 20,
.auto_log_target = 1,
.script_commands = NULL,
@@ -98,11 +98,15 @@ static const pa_daemon_conf default_conf = {
#ifdef RLIMIT_NPROC
,.rlimit_nproc = { .value = 0, .is_set = FALSE }
#endif
+#ifdef RLIMIT_NOFILE
,.rlimit_nofile = { .value = 256, .is_set = TRUE }
+#endif
#ifdef RLIMIT_MEMLOCK
,.rlimit_memlock = { .value = 0, .is_set = FALSE }
#endif
+#ifdef RLIMIT_AS
,.rlimit_as = { .value = 0, .is_set = FALSE }
+#endif
#ifdef RLIMIT_LOCKS
,.rlimit_locks = { .value = 0, .is_set = FALSE }
#endif
@@ -415,8 +419,8 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "system-instance", pa_config_parse_bool, NULL },
{ "no-cpu-limit", pa_config_parse_bool, NULL },
{ "disable-shm", pa_config_parse_bool, NULL },
+ { "flat-volumes", pa_config_parse_bool, NULL },
{ "exit-idle-time", pa_config_parse_int, NULL },
- { "module-idle-time", pa_config_parse_int, NULL },
{ "scache-idle-time", pa_config_parse_int, NULL },
{ "realtime-priority", parse_rtprio, NULL },
{ "dl-search-path", pa_config_parse_string, NULL },
@@ -444,8 +448,12 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "rlimit-stack", parse_rlimit, NULL },
{ "rlimit-core", parse_rlimit, NULL },
{ "rlimit-rss", parse_rlimit, NULL },
+#ifdef RLIMIT_NOFILE
{ "rlimit-nofile", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_AS
{ "rlimit-as", parse_rlimit, NULL },
+#endif
#ifdef RLIMIT_NPROC
{ "rlimit-nproc", parse_rlimit, NULL },
#endif
@@ -484,8 +492,8 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
table[i++].data = &c->system_instance;
table[i++].data = &c->no_cpu_limit;
table[i++].data = &c->disable_shm;
+ table[i++].data = &c->flat_volumes;
table[i++].data = &c->exit_idle_time;
- table[i++].data = &c->module_idle_time;
table[i++].data = &c->scache_idle_time;
table[i++].data = c;
table[i++].data = &c->dl_search_path;
@@ -511,10 +519,14 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
table[i++].data = &c->rlimit_fsize;
table[i++].data = &c->rlimit_data;
table[i++].data = &c->rlimit_stack;
- table[i++].data = &c->rlimit_as;
table[i++].data = &c->rlimit_core;
+ table[i++].data = &c->rlimit_rss;
+#ifdef RLIMIT_NOFILE
table[i++].data = &c->rlimit_nofile;
+#endif
+#ifdef RLIMIT_AS
table[i++].data = &c->rlimit_as;
+#endif
#ifdef RLIMIT_NPROC
table[i++].data = &c->rlimit_nproc;
#endif
@@ -641,8 +653,8 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
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, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes));
pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
- pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
@@ -666,12 +678,16 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
- pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
+#ifdef RLIMIT_AS
+ pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
+#endif
#ifdef RLIMIT_NPROC
pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
#endif
+#ifdef RLIMIT_NOFILE
pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
+#endif
#ifdef RLIMIT_MEMLOCK
pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
#endif
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 04a4ebe7..aa9d2981 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -70,9 +70,9 @@ typedef struct pa_daemon_conf {
load_default_script_file,
disallow_exit,
log_meta,
- log_time;
+ log_time,
+ flat_volumes;
int exit_idle_time,
- module_idle_time,
scache_idle_time,
auto_log_target,
realtime_priority,
@@ -85,7 +85,14 @@ typedef struct pa_daemon_conf {
char *config_file;
#ifdef HAVE_SYS_RESOURCE_H
- pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss, rlimit_nofile, rlimit_as;
+ pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss;
+
+#ifdef RLIMIT_NOFILE
+ pa_rlimit rlimit_nofile;
+#endif
+#ifdef RLIMIT_AS
+ pa_rlimit rlimit_as;
+#endif
#ifdef RLIMIT_NPROC
pa_rlimit rlimit_nproc;
#endif
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index 00a95932..69d17f26 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -53,6 +53,8 @@
; disable-remixing = no
; disable-lfe-remixing = yes
+; flat-volumes = yes
+
; no-cpu-limit = no
; rlimit-fsize = -1
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index 7de4c071..5d69926b 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -32,6 +32,7 @@
### Automatically restore the volume of streams and devices
load-module module-device-restore
load-module module-stream-restore
+load-module module-card-restore
### Load audio drivers statically (it's probably better to not load
### these drivers manually, but instead use module-hal-detect --
@@ -76,9 +77,6 @@ load-module module-native-protocol-unix
#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 description="RTP Multicast Sink"
#load-module module-rtp-send source=rtp.monitor
-### Enable flat volumes where possible
-load-module module-flat-volume
-
### Automatically restore the default sink/source when changed by the user during runtime
load-module module-default-device-restore
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 9c419efc..936c214d 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -298,11 +298,15 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
#ifdef RLIMIT_NPROC
set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
#endif
+#ifdef RLIMIT_NOFILE
set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
+#endif
#ifdef RLIMIT_MEMLOCK
set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
#endif
+#ifdef RLIMIT_AS
set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+#endif
#ifdef RLIMIT_LOCKS
set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
#endif
@@ -821,6 +825,8 @@ int main(int argc, char *argv[]) {
pa_log_debug(_("Running on host: %s"), s);
pa_xfree(s);
+ pa_log_debug(_("Found %u CPUs."), pa_ncpus());
+
pa_log_info(_("Page size is %lu bytes"), (unsigned long) PA_PAGE_SIZE);
#ifdef HAVE_VALGRIND_MEMCHECK_H
@@ -885,6 +891,8 @@ int main(int argc, char *argv[]) {
else
pa_log_info(_("Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!"));
+ pa_rtclock_hrtimer_enable();
+
#ifdef SIGRTMIN
/* Valgrind uses SIGRTMAX. To easy debugging we don't use it here */
pa_rtsig_configure(SIGRTMIN, SIGRTMAX-1);
@@ -901,7 +909,6 @@ int main(int argc, char *argv[]) {
c->default_n_fragments = conf->default_n_fragments;
c->default_fragment_size_msec = conf->default_fragment_size_msec;
c->exit_idle_time = conf->exit_idle_time;
- c->module_idle_time = conf->module_idle_time;
c->scache_idle_time = conf->scache_idle_time;
c->resample_method = conf->resample_method;
c->realtime_priority = conf->realtime_priority;
@@ -910,6 +917,7 @@ int main(int argc, char *argv[]) {
c->disable_lfe_remixing = !!conf->disable_lfe_remixing;
c->running_as_daemon = !!conf->daemonize;
c->disallow_exit = conf->disallow_exit;
+ c->flat_volumes = conf->flat_volumes;
pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
pa_signal_new(SIGINT, signal_callback, c);
@@ -963,11 +971,6 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) {
- pa_log_error(_("Default sink name (%s) does not exist in name register."), c->default_sink_name);
- goto finish;
- }
-
#ifdef HAVE_FORK
if (daemon_pipe[1] >= 0) {
int ok = 0;
diff --git a/src/map-file b/src/map-file
index 82b9c386..d6137590 100644
--- a/src/map-file
+++ b/src/map-file
@@ -9,6 +9,7 @@ pa_browser_unref;
pa_bytes_per_second;
pa_bytes_snprint;
pa_bytes_to_usec;
+pa_channel_map_can_balance;
pa_channel_map_compatible;
pa_channel_map_equal;
pa_channel_map_init;
@@ -18,6 +19,9 @@ pa_channel_map_init_mono;
pa_channel_map_init_stereo;
pa_channel_map_parse;
pa_channel_map_snprint;
+pa_channel_map_superset;
+pa_channel_map_to_name;
+pa_channel_map_to_pretty_name;
pa_channel_map_valid;
pa_channel_position_to_pretty_string;
pa_channel_position_to_string;
@@ -30,6 +34,9 @@ pa_context_exit_daemon;
pa_context_get_autoload_info_by_index;
pa_context_get_autoload_info_by_name;
pa_context_get_autoload_info_list;
+pa_context_get_card_info_by_index;
+pa_context_get_card_info_by_name;
+pa_context_get_card_info_list;
pa_context_get_client_info;
pa_context_get_client_info_list;
pa_context_get_index;
@@ -73,6 +80,8 @@ pa_context_ref;
pa_context_remove_autoload_by_index;
pa_context_remove_autoload_by_name;
pa_context_remove_sample;
+pa_context_set_card_profile_by_index;
+pa_context_set_card_profile_by_name;
pa_context_set_default_sink;
pa_context_set_default_source;
pa_context_set_name;
@@ -100,10 +109,13 @@ pa_cvolume_avg;
pa_cvolume_channels_equal_to;
pa_cvolume_compatible;
pa_cvolume_equal;
+pa_cvolume_get_balance;
pa_cvolume_init;
pa_cvolume_max;
pa_cvolume_remap;
+pa_cvolume_scale;
pa_cvolume_set;
+pa_cvolume_set_balance;
pa_cvolume_snprint;
pa_cvolume_valid;
pa_ext_stream_restore_delete;
@@ -148,14 +160,18 @@ pa_proplist_clear;
pa_proplist_contains;
pa_proplist_copy;
pa_proplist_free;
+pa_proplist_from_string;
pa_proplist_get;
pa_proplist_gets;
+pa_proplist_isempty;
pa_proplist_iterate;
pa_proplist_new;
pa_proplist_set;
pa_proplist_setf;
pa_proplist_sets;
+pa_proplist_size;
pa_proplist_to_string;
+pa_proplist_to_string_sep;
pa_proplist_unset;
pa_proplist_unset_many;
pa_proplist_update;
diff --git a/src/modules/alsa/Makefile b/src/modules/alsa/Makefile
new file mode 120000
index 00000000..efe5a336
--- /dev/null
+++ b/src/modules/alsa/Makefile
@@ -0,0 +1 @@
+../../pulse/Makefile \ No newline at end of file
diff --git a/src/modules/module-alsa-sink.c b/src/modules/alsa/alsa-sink.c
index f9fb9bd5..22460bb0 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -53,43 +53,7 @@
#include <pulsecore/time-smoother.h>
#include "alsa-util.h"
-#include "module-alsa-sink-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("ALSA Sink");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
-PA_MODULE_USAGE(
- "sink_name=<name for the sink> "
- "device=<ALSA device> "
- "device_id=<ALSA card index> "
- "format=<sample format> "
- "rate=<sample rate> "
- "channels=<number of channels> "
- "channel_map=<channel map> "
- "fragments=<number of fragments> "
- "fragment_size=<fragment size> "
- "mmap=<enable memory mapping?> "
- "tsched=<enable system timer based scheduling mode?> "
- "tsched_buffer_size=<buffer size when using timer based scheduling> "
- "tsched_buffer_watermark=<lower fill watermark>");
-
-static const char* const valid_modargs[] = {
- "sink_name",
- "device",
- "device_id",
- "format",
- "rate",
- "channels",
- "channel_map",
- "fragments",
- "fragment_size",
- "mmap",
- "tsched",
- "tsched_buffer_size",
- "tsched_buffer_watermark",
- NULL
-};
+#include "alsa-sink.h"
#define DEFAULT_DEVICE "default"
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
@@ -138,6 +102,8 @@ struct userdata {
snd_pcm_sframes_t hwbuf_unused_frames;
};
+static void userdata_free(struct userdata *u);
+
static void fix_tsched_watermark(struct userdata *u) {
size_t max_use;
size_t min_sleep, min_wakeup;
@@ -285,9 +251,10 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
- if (polled)
+ if (polled && pa_log_ratelimit())
pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
break;
}
@@ -407,9 +374,10 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
- if (polled)
+ if (polled && pa_log_ratelimit())
pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
break;
}
@@ -613,7 +581,7 @@ static int update_sw_params(struct userdata *u) {
pa_usec_t sleep_usec, process_usec;
hw_sleep_time(u, &sleep_usec, &process_usec);
- avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec);
+ avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec) / u->frame_size;
}
pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
@@ -747,6 +715,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -788,7 +757,7 @@ static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
}
-static int sink_get_volume_cb(pa_sink *s) {
+static void sink_get_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
unsigned i;
@@ -851,27 +820,24 @@ static int sink_get_volume_cb(pa_sink *s) {
if (!pa_cvolume_equal(&u->hardware_volume, &r)) {
- u->hardware_volume = s->volume = r;
+ s->virtual_volume = u->hardware_volume = r;
if (u->hw_dB_supported) {
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);
}
}
- return 0;
+ return;
fail:
pa_log_error("Unable to read volume: %s", snd_strerror(err));
-
- return -1;
}
-static int sink_set_volume_cb(pa_sink *s) {
+static void sink_set_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
unsigned i;
@@ -888,7 +854,7 @@ static int sink_set_volume_cb(pa_sink *s) {
long alsa_vol;
pa_volume_t vol;
- vol = s->volume.values[i];
+ vol = s->virtual_volume.values[i];
if (u->hw_dB_supported) {
@@ -902,6 +868,10 @@ static int sink_set_volume_cb(pa_sink *s) {
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
@@ -921,7 +891,7 @@ static int sink_set_volume_cb(pa_sink *s) {
pa_volume_t vol;
long alsa_vol;
- vol = pa_cvolume_max(&s->volume);
+ vol = pa_cvolume_max(&s->virtual_volume);
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
@@ -934,7 +904,11 @@ static int sink_set_volume_cb(pa_sink *s) {
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
- pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
+ pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
alsa_vol = to_alsa_volume(u, vol);
@@ -955,30 +929,26 @@ static int sink_set_volume_cb(pa_sink *s) {
char t[PA_CVOLUME_SNPRINT_MAX];
/* 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(&r, &s->volume, &r);
- pa_sink_set_soft_volume(s, &r);
-
- pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume));
+ pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_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), &r));
+ pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
} else
/* We can't match exactly what the user requested, hence let's
* at least tell the user about it */
- s->volume = r;
+ s->virtual_volume = r;
- return 0;
+ return;
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
-
- return -1;
}
-static int sink_get_mute_cb(pa_sink *s) {
+static void sink_get_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err, sw;
@@ -987,15 +957,13 @@ static int sink_get_mute_cb(pa_sink *s) {
if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) {
pa_log_error("Unable to get switch: %s", snd_strerror(err));
- return -1;
+ return;
}
s->muted = !sw;
-
- return 0;
}
-static int sink_set_mute_cb(pa_sink *s) {
+static void sink_set_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
@@ -1004,10 +972,8 @@ static int sink_set_mute_cb(pa_sink *s) {
if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) {
pa_log_error("Unable to set switch: %s", snd_strerror(err));
- return -1;
+ return;
}
-
- return 0;
}
static void sink_update_requested_latency_cb(pa_sink *s) {
@@ -1217,7 +1183,7 @@ static void thread_func(void *userdata) {
u->since_start = 0;
}
- if (revents && u->use_tsched)
+ if (revents && u->use_tsched && pa_log_ratelimit())
pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
} else
revents = 0;
@@ -1233,11 +1199,36 @@ finish:
pa_log_debug("Thread shutting down");
}
-int pa__init(pa_module*m) {
+static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) {
+ const char *n;
+ char *t;
+
+ pa_assert(data);
+ pa_assert(ma);
+ pa_assert(device_name);
+
+ if ((n = pa_modargs_get_value(ma, "sink_name", NULL))) {
+ pa_sink_new_data_set_name(data, n);
+ data->namereg_fail = TRUE;
+ return;
+ }
+
+ if ((n = pa_modargs_get_value(ma, "name", NULL)))
+ data->namereg_fail = TRUE;
+ else {
+ n = device_id ? device_id : device_name;
+ data->namereg_fail = FALSE;
+ }
+
+ t = pa_sprintf_malloc("alsa_output.%s", n);
+ pa_sink_new_data_set_name(data, t);
+ pa_xfree(t);
+}
+
+pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
- pa_modargs *ma = NULL;
struct userdata *u = NULL;
- const char *dev_id;
+ const char *dev_id = NULL;
pa_sample_spec ss;
pa_channel_map map;
uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
@@ -1245,23 +1236,14 @@ int pa__init(pa_module*m) {
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
int err;
- const char *name;
- char *name_buf = NULL;
- pa_bool_t namereg_fail;
- pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
+ pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_usec_t usec;
pa_sink_new_data data;
snd_pcm_info_alloca(&pcm_info);
pa_assert(m);
-
- pa_alsa_redirect_errors_inc();
-
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("Failed to parse module arguments");
- goto fail;
- }
+ pa_assert(ma);
ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
@@ -1300,6 +1282,11 @@ int pa__init(pa_module*m) {
goto fail;
}
+ if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+ pa_log("Failed to parse ignore_dB argument.");
+ goto fail;
+ }
+
if (use_tsched && !pa_rtclock_hrtimer()) {
pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
use_tsched = FALSE;
@@ -1308,7 +1295,6 @@ int pa__init(pa_module*m) {
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
- m->userdata = u;
u->use_mmap = use_mmap;
u->use_tsched = use_tsched;
u->first = TRUE;
@@ -1323,20 +1309,35 @@ int pa__init(pa_module*m) {
pa_smoother_set_time_offset(u->smoother, usec);
pa_smoother_pause(u->smoother, usec);
- snd_config_update_free_global();
-
b = use_mmap;
d = use_tsched;
- if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+ if (profile) {
+
+ if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+ pa_log("device_id= not set");
+ goto fail;
+ }
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile(
+ dev_id,
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_PLAYBACK,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d, profile)))
- if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+ goto fail;
+
+ } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
dev_id,
&u->device_name,
&ss, &map,
SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames,
- &b, &d)))
+ &b, &d, &profile)))
goto fail;
@@ -1348,7 +1349,7 @@ int pa__init(pa_module*m) {
&ss, &map,
SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames,
- &b, &d)))
+ &b, &d, FALSE)))
goto fail;
}
@@ -1356,6 +1357,9 @@ int pa__init(pa_module*m) {
pa_assert(u->device_name);
pa_log_info("Successfully opened device %s.", u->device_name);
+ if (profile)
+ pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
+
if (use_mmap && !b) {
pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
u->use_mmap = use_mmap = FALSE;
@@ -1394,11 +1398,11 @@ int pa__init(pa_module*m) {
if (snd_pcm_info(u->pcm_handle, info) >= 0) {
char *md;
- int card;
+ int card_idx;
- if ((card = snd_pcm_info_get_card(info)) >= 0) {
+ if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
- md = pa_sprintf_malloc("hw:%i", card);
+ md = pa_sprintf_malloc("hw:%i", card_idx);
if (strcmp(u->device_name, md))
if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
@@ -1418,30 +1422,27 @@ int pa__init(pa_module*m) {
}
}
- if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
- namereg_fail = TRUE;
- else {
- name = name_buf = pa_sprintf_malloc("alsa_output.%s", u->device_name);
- namereg_fail = FALSE;
- }
-
pa_sink_new_data_init(&data);
- data.driver = __FILE__;
+ data.driver = driver;
data.module = m;
- pa_sink_new_data_set_name(&data, name);
- data.namereg_fail = namereg_fail;
+ data.card = card;
+ set_sink_name(&data, ma, dev_id, u->device_name);
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
- pa_alsa_init_proplist(data.proplist, pcm_info);
+ pa_alsa_init_proplist_pcm(m->core, data.proplist, pcm_info);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+ if (profile) {
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
+ }
+
u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
- pa_xfree(name_buf);
if (!u->sink) {
pa_log("Failed to create sink object");
@@ -1507,8 +1508,8 @@ int pa__init(pa_module*m) {
}
if (suitable) {
- if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
- pa_log_info("Mixer doesn't support dB information.");
+ if (ignore_dB || snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
+ pa_log_info("Mixer doesn't support dB information or data is ignored.");
else {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
@@ -1545,6 +1546,8 @@ int pa__init(pa_module*m) {
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+ if (!u->hw_dB_supported)
+ u->sink->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
} else
pa_log_info("Using software volume control.");
}
@@ -1594,29 +1597,17 @@ int pa__init(pa_module*m) {
pa_sink_put(u->sink);
- pa_modargs_free(ma);
-
- return 0;
+ return u->sink;
fail:
- if (ma)
- pa_modargs_free(ma);
-
- pa__done(m);
+ userdata_free(u);
- return -1;
+ return NULL;
}
-void pa__done(pa_module*m) {
- struct userdata *u;
-
- pa_assert(m);
-
- if (!(u = m->userdata)) {
- pa_alsa_redirect_errors_dec();
- return;
- }
+static void userdata_free(struct userdata *u) {
+ pa_assert(u);
if (u->sink)
pa_sink_unlink(u->sink);
@@ -1656,8 +1647,13 @@ void pa__done(pa_module*m) {
pa_xfree(u->device_name);
pa_xfree(u);
+}
- snd_config_update_free_global();
+void pa_alsa_sink_free(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
- pa_alsa_redirect_errors_dec();
+ userdata_free(u);
}
diff --git a/src/modules/alsa/alsa-sink.h b/src/modules/alsa/alsa-sink.h
new file mode 100644
index 00000000..47ece9e0
--- /dev/null
+++ b/src/modules/alsa/alsa-sink.h
@@ -0,0 +1,36 @@
+#ifndef fooalsasinkhfoo
+#define fooalsasinkhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/sink.h>
+
+#include "alsa-util.h"
+
+pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile);
+
+void pa_alsa_sink_free(pa_sink *s);
+
+#endif
diff --git a/src/modules/module-alsa-source.c b/src/modules/alsa/alsa-source.c
index a6e4c907..0fd9838c 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -54,43 +54,7 @@
#include <pulsecore/rtclock.h>
#include "alsa-util.h"
-#include "module-alsa-source-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("ALSA Source");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
-PA_MODULE_USAGE(
- "source_name=<name for the source> "
- "device=<ALSA device> "
- "device_id=<ALSA card index> "
- "format=<sample format> "
- "rate=<sample rate> "
- "channels=<number of channels> "
- "channel_map=<channel map> "
- "fragments=<number of fragments> "
- "fragment_size=<fragment size> "
- "mmap=<enable memory mapping?> "
- "tsched=<enable system timer based scheduling mode?> "
- "tsched_buffer_size=<buffer size when using timer based scheduling> "
- "tsched_buffer_watermark=<upper fill watermark>");
-
-static const char* const valid_modargs[] = {
- "source_name",
- "device",
- "device_id",
- "format",
- "rate",
- "channels",
- "channel_map",
- "fragments",
- "fragment_size",
- "mmap",
- "tsched",
- "tsched_buffer_size",
- "tsched_buffer_watermark",
- NULL
-};
+#include "alsa-source.h"
#define DEFAULT_DEVICE "default"
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
@@ -136,6 +100,8 @@ struct userdata {
snd_pcm_sframes_t hwbuf_unused_frames;
};
+static void userdata_free(struct userdata *u);
+
static void fix_tsched_watermark(struct userdata *u) {
size_t max_use;
size_t min_sleep, min_wakeup;
@@ -272,9 +238,10 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
if (PA_UNLIKELY(n <= 0)) {
- if (polled)
+ if (polled && pa_log_ratelimit())
pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio device.");
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
break;
}
@@ -379,9 +346,10 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
if (PA_UNLIKELY(n <= 0)) {
- if (polled)
+ if (polled && pa_log_ratelimit())
pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
return work_done;
}
@@ -558,7 +526,7 @@ static int update_sw_params(struct userdata *u) {
pa_usec_t sleep_usec, process_usec;
hw_sleep_time(u, &sleep_usec, &process_usec);
- avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec);
+ avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec) / u->frame_size;
}
pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
@@ -693,6 +661,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
case PA_SOURCE_UNLINKED:
case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
;
}
@@ -734,7 +703,7 @@ static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
}
-static int source_get_volume_cb(pa_source *s) {
+static void source_get_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err;
unsigned i;
@@ -797,27 +766,24 @@ static int source_get_volume_cb(pa_source *s) {
if (!pa_cvolume_equal(&u->hardware_volume, &r)) {
- u->hardware_volume = s->volume = r;
+ s->virtual_volume = u->hardware_volume = r;
if (u->hw_dB_supported) {
pa_cvolume reset;
/* 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);
}
}
- return 0;
+ return;
fail:
pa_log_error("Unable to read volume: %s", snd_strerror(err));
-
- return -1;
}
-static int source_set_volume_cb(pa_source *s) {
+static void source_set_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err;
unsigned i;
@@ -834,7 +800,7 @@ static int source_set_volume_cb(pa_source *s) {
long alsa_vol;
pa_volume_t vol;
- vol = s->volume.values[i];
+ vol = s->virtual_volume.values[i];
if (u->hw_dB_supported) {
@@ -848,6 +814,10 @@ static int source_set_volume_cb(pa_source *s) {
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
@@ -867,7 +837,7 @@ static int source_set_volume_cb(pa_source *s) {
pa_volume_t vol;
long alsa_vol;
- vol = pa_cvolume_max(&s->volume);
+ vol = pa_cvolume_max(&s->virtual_volume);
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
@@ -880,7 +850,11 @@ static int source_set_volume_cb(pa_source *s) {
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
- pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
+ pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
alsa_vol = to_alsa_volume(u, vol);
@@ -902,29 +876,26 @@ static int source_set_volume_cb(pa_source *s) {
/* Match exactly what the user requested by software */
- pa_sw_cvolume_divide(&r, &s->volume, &r);
- pa_source_set_soft_volume(s, &r);
+ pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
- pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume));
+ pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_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), &r));
+ pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
} else
/* We can't match exactly what the user requested, hence let's
* at least tell the user about it */
- s->volume = r;
+ s->virtual_volume = r;
- return 0;
+ return;
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
-
- return -1;
}
-static int source_get_mute_cb(pa_source *s) {
+static void source_get_mute_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err, sw;
@@ -933,15 +904,13 @@ static int source_get_mute_cb(pa_source *s) {
if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) {
pa_log_error("Unable to get switch: %s", snd_strerror(err));
- return -1;
+ return;
}
s->muted = !sw;
-
- return 0;
}
-static int source_set_mute_cb(pa_source *s) {
+static void source_set_mute_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err;
@@ -950,10 +919,8 @@ static int source_set_mute_cb(pa_source *s) {
if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) {
pa_log_error("Unable to set switch: %s", snd_strerror(err));
- return -1;
+ return;
}
-
- return 0;
}
static void source_update_requested_latency_cb(pa_source *s) {
@@ -1052,7 +1019,7 @@ static void thread_func(void *userdata) {
snd_pcm_start(u->pcm_handle);
}
- if (revents && u->use_tsched)
+ if (revents && u->use_tsched && pa_log_ratelimit())
pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
} else
revents = 0;
@@ -1068,11 +1035,36 @@ finish:
pa_log_debug("Thread shutting down");
}
-int pa__init(pa_module*m) {
+static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) {
+ const char *n;
+ char *t;
+
+ pa_assert(data);
+ pa_assert(ma);
+ pa_assert(device_name);
+
+ if ((n = pa_modargs_get_value(ma, "source_name", NULL))) {
+ pa_source_new_data_set_name(data, n);
+ data->namereg_fail = TRUE;
+ return;
+ }
+
+ if ((n = pa_modargs_get_value(ma, "name", NULL)))
+ data->namereg_fail = TRUE;
+ else {
+ n = device_id ? device_id : device_name;
+ data->namereg_fail = FALSE;
+ }
+
+ t = pa_sprintf_malloc("alsa_input.%s", n);
+ pa_source_new_data_set_name(data, t);
+ pa_xfree(t);
+}
+
+pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
- pa_modargs *ma = NULL;
struct userdata *u = NULL;
- const char *dev_id;
+ const char *dev_id = NULL;
pa_sample_spec ss;
pa_channel_map map;
uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
@@ -1080,23 +1072,13 @@ int pa__init(pa_module*m) {
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
int err;
- const char *name;
- char *name_buf = NULL;
- pa_bool_t namereg_fail;
- pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
+ pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_source_new_data data;
snd_pcm_info_alloca(&pcm_info);
pa_assert(m);
- pa_alsa_redirect_errors_inc();
-
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("Failed to parse module arguments");
- goto fail;
- }
-
ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
pa_log("Failed to parse sample specification");
@@ -1134,6 +1116,11 @@ int pa__init(pa_module*m) {
goto fail;
}
+ if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+ pa_log("Failed to parse ignore_dB argument.");
+ goto fail;
+ }
+
if (use_tsched && !pa_rtclock_hrtimer()) {
pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
use_tsched = FALSE;
@@ -1142,7 +1129,6 @@ int pa__init(pa_module*m) {
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
- m->userdata = u;
u->use_mmap = use_mmap;
u->use_tsched = use_tsched;
u->rtpoll = pa_rtpoll_new();
@@ -1152,20 +1138,34 @@ int pa__init(pa_module*m) {
u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE, 5);
pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
- snd_config_update_free_global();
-
b = use_mmap;
d = use_tsched;
- if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+ if (profile) {
+
+ if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+ pa_log("device_id= not set");
+ goto fail;
+ }
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile(
+ dev_id,
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_CAPTURE,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d, profile)))
+ goto fail;
+
+ } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
- if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
dev_id,
&u->device_name,
&ss, &map,
SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames,
- &b, &d)))
+ &b, &d, &profile)))
goto fail;
} else {
@@ -1176,13 +1176,16 @@ int pa__init(pa_module*m) {
&ss, &map,
SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames,
- &b, &d)))
+ &b, &d, FALSE)))
goto fail;
}
pa_assert(u->device_name);
pa_log_info("Successfully opened device %s.", u->device_name);
+ if (profile)
+ pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
+
if (use_mmap && !b) {
pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
u->use_mmap = use_mmap = FALSE;
@@ -1221,11 +1224,11 @@ int pa__init(pa_module*m) {
if (snd_pcm_info(u->pcm_handle, info) >= 0) {
char *md;
- int card;
+ int card_idx;
- if ((card = snd_pcm_info_get_card(info)) >= 0) {
+ if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
- md = pa_sprintf_malloc("hw:%i", card);
+ md = pa_sprintf_malloc("hw:%i", card_idx);
if (strcmp(u->device_name, md))
if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
@@ -1245,30 +1248,27 @@ int pa__init(pa_module*m) {
}
}
- if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
- namereg_fail = TRUE;
- else {
- name = name_buf = pa_sprintf_malloc("alsa_input.%s", u->device_name);
- namereg_fail = FALSE;
- }
-
pa_source_new_data_init(&data);
- data.driver = __FILE__;
+ data.driver = driver;
data.module = m;
- pa_source_new_data_set_name(&data, name);
- data.namereg_fail = namereg_fail;
+ data.card = card;
+ set_source_name(&data, ma, dev_id, u->device_name);
pa_source_new_data_set_sample_spec(&data, &ss);
pa_source_new_data_set_channel_map(&data, &map);
- pa_alsa_init_proplist(data.proplist, pcm_info);
+ pa_alsa_init_proplist_pcm(m->core, data.proplist, pcm_info);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+ if (profile) {
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
+ }
+
u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
pa_source_new_data_done(&data);
- pa_xfree(name_buf);
if (!u->source) {
pa_log("Failed to create source object");
@@ -1329,8 +1329,8 @@ int pa__init(pa_module*m) {
}
if (suitable) {
- if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
- pa_log_info("Mixer doesn't support dB information.");
+ if (ignore_dB || snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
+ pa_log_info("Mixer doesn't support dB information or data is ignored.");
else {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
@@ -1367,6 +1367,9 @@ int pa__init(pa_module*m) {
u->source->set_volume = source_set_volume_cb;
u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+ if (!u->hw_dB_supported)
+ u->source->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
} else
pa_log_info("Using software volume control.");
}
@@ -1415,29 +1418,17 @@ int pa__init(pa_module*m) {
pa_source_put(u->source);
- pa_modargs_free(ma);
-
- return 0;
+ return u->source;
fail:
- if (ma)
- pa_modargs_free(ma);
+ userdata_free(u);
- pa__done(m);
-
- return -1;
+ return NULL;
}
-void pa__done(pa_module*m) {
- struct userdata *u;
-
- pa_assert(m);
-
- if (!(u = m->userdata)) {
- pa_alsa_redirect_errors_dec();
- return;
- }
+static void userdata_free(struct userdata *u) {
+ pa_assert(u);
if (u->source)
pa_source_unlink(u->source);
@@ -1474,7 +1465,13 @@ void pa__done(pa_module*m) {
pa_xfree(u->device_name);
pa_xfree(u);
+}
- snd_config_update_free_global();
- pa_alsa_redirect_errors_dec();
+void pa_alsa_source_free(pa_source *s) {
+ struct userdata *u;
+
+ pa_source_assert_ref(s);
+ pa_assert_se(u = s->userdata);
+
+ userdata_free(u);
}
diff --git a/src/modules/alsa/alsa-source.h b/src/modules/alsa/alsa-source.h
new file mode 100644
index 00000000..5fed6cc8
--- /dev/null
+++ b/src/modules/alsa/alsa-source.h
@@ -0,0 +1,36 @@
+#ifndef fooalsasourcehfoo
+#define fooalsasourcehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/source.h>
+
+#include "alsa-util.h"
+
+pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile);
+
+void pa_alsa_source_free(pa_source *s);
+
+#endif
diff --git a/src/modules/alsa-util.c b/src/modules/alsa/alsa-util.c
index ff3af19d..7e5a3503 100644
--- a/src/modules/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -39,6 +39,10 @@
#include "alsa-util.h"
+#ifdef HAVE_HAL
+#include "hal-util.h"
+#endif
+
struct pa_alsa_fdlist {
unsigned num_fds;
struct pollfd *fds;
@@ -232,6 +236,10 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
[PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
[PA_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
[PA_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
+ [PA_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
+ [PA_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
+ [PA_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE,
+ [PA_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE,
};
static const pa_sample_format_t try_order[] = {
@@ -239,6 +247,10 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
PA_SAMPLE_FLOAT32RE,
PA_SAMPLE_S32NE,
PA_SAMPLE_S32RE,
+ PA_SAMPLE_S24_32NE,
+ PA_SAMPLE_S24_32RE,
+ PA_SAMPLE_S24NE,
+ PA_SAMPLE_S24RE,
PA_SAMPLE_S16NE,
PA_SAMPLE_S16RE,
PA_SAMPLE_ALAW,
@@ -259,6 +271,14 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
*f = PA_SAMPLE_FLOAT32LE;
else if (*f == PA_SAMPLE_FLOAT32LE)
*f = PA_SAMPLE_FLOAT32BE;
+ else if (*f == PA_SAMPLE_S24BE)
+ *f = PA_SAMPLE_S24LE;
+ else if (*f == PA_SAMPLE_S24LE)
+ *f = PA_SAMPLE_S24BE;
+ else if (*f == PA_SAMPLE_S24_32BE)
+ *f = PA_SAMPLE_S24_32LE;
+ else if (*f == PA_SAMPLE_S24_32LE)
+ *f = PA_SAMPLE_S24_32BE;
else if (*f == PA_SAMPLE_S16BE)
*f = PA_SAMPLE_S16LE;
else if (*f == PA_SAMPLE_S16LE)
@@ -298,8 +318,8 @@ int pa_alsa_set_hw_params(
pa_bool_t require_exact_channel_number) {
int ret = -1;
- snd_pcm_uframes_t _period_size = *period_size;
- unsigned int _periods = *periods;
+ snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
+ unsigned int _periods = periods ? *periods : 0;
snd_pcm_uframes_t buffer_size;
unsigned int r = ss->rate;
unsigned int c = ss->channels;
@@ -311,8 +331,6 @@ int pa_alsa_set_hw_params(
pa_assert(pcm_handle);
pa_assert(ss);
- pa_assert(periods);
- pa_assert(period_size);
snd_pcm_hw_params_alloca(&hwparams);
@@ -345,10 +363,6 @@ int pa_alsa_set_hw_params(
if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0)
goto finish;
- /* Adjust the buffer sizes, if we didn't get the rate we were asking for */
- _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
- tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
-
if (require_exact_channel_number) {
if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0)
goto finish;
@@ -357,50 +371,56 @@ int pa_alsa_set_hw_params(
goto finish;
}
- if (_use_tsched) {
- _period_size = tsched_size;
- _periods = 1;
+ if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0)
+ goto finish;
- pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0);
- pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
- }
+ 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);
- buffer_size = _periods * _period_size;
+ if (_use_tsched) {
+ _period_size = tsched_size;
+ _periods = 1;
- if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0)
- goto finish;
+ pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0);
+ pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
+ }
+
+ buffer_size = _periods * _period_size;
- if (_periods > 0) {
+ 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 */
+ /* First we pass 0 as direction to get exactly what we asked
+ * for. That this is necessary is presumably a bug in ALSA */
- dir = 0;
- if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
- dir = 1;
+ dir = 0;
if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
- dir = -1;
- if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
- goto finish;
+ dir = 1;
+ if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
+ dir = -1;
+ if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
+ goto finish;
+ }
}
}
- }
- if (_period_size > 0)
- if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
- goto finish;
+ if (_period_size > 0)
+ if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
+ goto finish;
+ }
if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
goto finish;
if (ss->rate != r)
- pa_log_warn("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);
+ pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);
if (ss->channels != c)
- pa_log_warn("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);
+ pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);
if (ss->format != f)
- pa_log_warn("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));
+ 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 ((ret = snd_pcm_prepare(pcm_handle)) < 0)
goto finish;
@@ -418,8 +438,11 @@ int pa_alsa_set_hw_params(
pa_assert(_periods > 0);
pa_assert(_period_size > 0);
- *periods = _periods;
- *period_size = _period_size;
+ if (periods)
+ *periods = _periods;
+
+ if (period_size)
+ *period_size = _period_size;
if (use_mmap)
*use_mmap = _use_mmap;
@@ -472,57 +495,90 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
return 0;
}
-struct device_info {
- pa_channel_map map;
- const char *name;
-};
+static const struct pa_alsa_profile_info device_table[] = {
+ {{ 1, { PA_CHANNEL_POSITION_MONO }},
+ "hw",
+ "Analog Mono",
+ "analog-mono",
+ 1 },
+
+ {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
+ "front",
+ "Analog Stereo",
+ "analog-stereo",
+ 10 },
+
+ {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
+ "iec958",
+ "IEC958 Digital Stereo",
+ "iec958-stereo",
+ 5 },
+
+ {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
+ "hdmi",
+ "HDMI Digital Stereo",
+ "hdmi-stereo",
+ 4 },
-static const struct device_info device_table[] = {
- {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } }, "front" },
+ {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
+ "surround40",
+ "Analog Surround 4.0",
+ "analog-surround-40",
+ 7 },
{{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, "surround40" },
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
+ "a52",
+ "IEC958/AC3 Digital Surround 4.0",
+ "iec958-ac3-surround-40",
+ 2 },
{{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_LFE }}, "surround41" },
+ PA_CHANNEL_POSITION_LFE }},
+ "surround41",
+ "Analog Surround 4.1",
+ "analog-surround-41",
+ 7 },
{{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_CENTER }}, "surround50" },
+ PA_CHANNEL_POSITION_CENTER }},
+ "surround50",
+ "Analog Surround 5.0",
+ "analog-surround-50",
+ 7 },
{{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }}, "surround51" },
+ PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }},
+ "surround51",
+ "Analog Surround 5.1",
+ "analog-surround-51",
+ 8 },
+
+ {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_CENTER,
+ PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT,
+ PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE}},
+ "a52",
+ "IEC958/AC3 Digital Surround 5.1",
+ "iec958-ac3-surround-51",
+ 3 },
{{ 8, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }} , "surround71" },
+ PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }},
+ "surround71",
+ "Analog Surround 7.1",
+ "analog-surround-71",
+ 7 },
- {{ 0, { 0 }}, NULL }
+ {{ 0, { 0 }}, NULL, NULL, NULL, 0 }
};
-static pa_bool_t channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
- pa_bool_t in_a[PA_CHANNEL_POSITION_MAX];
- unsigned i;
-
- pa_assert(a);
- pa_assert(b);
-
- memset(in_a, 0, sizeof(in_a));
-
- for (i = 0; i < a->channels; i++)
- in_a[a->map[i]] = TRUE;
-
- for (i = 0; i < b->channels; i++)
- if (!in_a[b->map[i]])
- return FALSE;
-
- return TRUE;
-}
-
-snd_pcm_t *pa_alsa_open_by_device_id(
+snd_pcm_t *pa_alsa_open_by_device_id_auto(
const char *dev_id,
char **dev,
pa_sample_spec *ss,
@@ -532,11 +588,11 @@ snd_pcm_t *pa_alsa_open_by_device_id(
snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
- pa_bool_t *use_tsched) {
+ pa_bool_t *use_tsched,
+ const pa_alsa_profile_info **profile) {
int i;
int direction = 1;
- int err;
char *d;
snd_pcm_t *pcm_handle;
@@ -554,99 +610,158 @@ snd_pcm_t *pa_alsa_open_by_device_id(
* way, we iterate backwards, and check all devices that do not
* provide a superset of the requested channel map.*/
- for (i = 0;; i += direction) {
- pa_sample_spec try_ss;
- pa_bool_t reformat;
-
- if (i < 0) {
- pa_assert(direction == -1);
+ i = 0;
+ for (;;) {
- /* OK, so we iterated backwards, and now are at the
- * beginning of our list. */
+ if ((direction > 0) == pa_channel_map_superset(&device_table[i].map, map)) {
+ pa_sample_spec try_ss;
- break;
+ pa_log_debug("Checking for %s (%s)", device_table[i].name, device_table[i].alsa_name);
- } else if (!device_table[i].name) {
- pa_assert(direction == 1);
+ d = pa_sprintf_malloc("%s:%s", device_table[i].alsa_name, dev_id);
- /* OK, so we are at the end of our list. at iterated
- * forwards. */
+ try_ss.channels = device_table[i].map.channels;
+ try_ss.rate = ss->rate;
+ try_ss.format = ss->format;
- i--;
- direction = -1;
- }
+ pcm_handle = pa_alsa_open_by_device_string(
+ d,
+ dev,
+ &try_ss,
+ map,
+ mode,
+ nfrags,
+ period_size,
+ tsched_size,
+ use_mmap,
+ use_tsched,
+ TRUE);
- if ((direction > 0) == !channel_map_superset(&device_table[i].map, map))
- continue;
+ pa_xfree(d);
- d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id);
+ if (pcm_handle) {
- reformat = FALSE;
- for (;;) {
- pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
+ *ss = try_ss;
+ *map = device_table[i].map;
+ pa_assert(map->channels == ss->channels);
- /* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <=
- * 1.0.17a would then ignore the SND_PCM_NO_xxx
- * flags. Instead we enable nonblock mode afterwards via
- * snd_pcm_nonblock(). Also see
- * http://mailman.alsa-project.org/pipermail/alsa-devel/2008-August/010258.html */
+ if (profile)
+ *profile = &device_table[i];
- if ((err = snd_pcm_open(&pcm_handle, d, mode,
- /* SND_PCM_NONBLOCK| */
- SND_PCM_NO_AUTO_RESAMPLE|
- SND_PCM_NO_AUTO_CHANNELS|
- (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
- pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
- break;
+ return pcm_handle;
}
+ }
- try_ss.channels = device_table[i].map.channels;
- try_ss.rate = ss->rate;
- try_ss.format = ss->format;
+ if (direction > 0) {
+ if (!device_table[i+1].alsa_name) {
+ /* OK, so we are at the end of our list. Let's turn
+ * back. */
+ direction = -1;
+ } else {
+ /* We are not at the end of the list, so let's simply
+ * try the next entry */
+ i++;
+ }
+ }
- if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
+ if (direction < 0) {
- if (!reformat) {
- reformat = TRUE;
- snd_pcm_close(pcm_handle);
- continue;
- }
+ if (device_table[i+1].alsa_name &&
+ device_table[i].map.channels == device_table[i+1].map.channels) {
- if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
- char *t;
+ /* OK, the next entry has the same number of channels,
+ * let's try it */
+ i++;
- t = pa_sprintf_malloc("plug:%s", d);
- pa_xfree(d);
- d = t;
+ } else {
+ /* Hmm, so the next entry does not have the same
+ * number of channels, so let's go backwards until we
+ * find the next entry with a differnt number of
+ * channels */
- reformat = FALSE;
+ for (i--; i >= 0; i--)
+ if (device_table[i].map.channels != device_table[i+1].map.channels)
+ break;
- snd_pcm_close(pcm_handle);
- continue;
- }
+ /* Hmm, there is no entry with a different number of
+ * entries, then we're done */
+ if (i < 0)
+ break;
- pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err));
- snd_pcm_close(pcm_handle);
- break;
+ /* OK, now lets find go back as long as we have the same number of channels */
+ for (; i > 0; i--)
+ if (device_table[i].map.channels != device_table[i-1].map.channels)
+ break;
}
-
- *ss = try_ss;
- *map = device_table[i].map;
- pa_assert(map->channels == ss->channels);
- *dev = d;
- return pcm_handle;
}
-
- pa_xfree(d);
}
/* OK, we didn't find any good device, so let's try the raw plughw: 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);
+ pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE);
pa_xfree(d);
+ if (pcm_handle && profile)
+ *profile = NULL;
+
+ return pcm_handle;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_id_profile(
+ const char *dev_id,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched,
+ const pa_alsa_profile_info *profile) {
+
+ char *d;
+ snd_pcm_t *pcm_handle;
+ pa_sample_spec try_ss;
+
+ pa_assert(dev_id);
+ pa_assert(dev);
+ pa_assert(ss);
+ pa_assert(map);
+ pa_assert(nfrags);
+ pa_assert(period_size);
+ pa_assert(profile);
+
+ d = pa_sprintf_malloc("%s:%s", profile->alsa_name, dev_id);
+
+ try_ss.channels = profile->map.channels;
+ try_ss.rate = ss->rate;
+ try_ss.format = ss->format;
+
+ pcm_handle = pa_alsa_open_by_device_string(
+ d,
+ dev,
+ &try_ss,
+ map,
+ mode,
+ nfrags,
+ period_size,
+ tsched_size,
+ use_mmap,
+ use_tsched,
+ TRUE);
+
+ pa_xfree(d);
+
+ if (!pcm_handle)
+ return NULL;
+
+ *ss = try_ss;
+ *map = profile->map;
+ pa_assert(map->channels == ss->channels);
+
return pcm_handle;
}
@@ -660,7 +775,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
- pa_bool_t *use_tsched) {
+ pa_bool_t *use_tsched,
+ pa_bool_t require_exact_channel_number) {
int err;
char *d;
@@ -668,11 +784,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_bool_t reformat = FALSE;
pa_assert(device);
- pa_assert(dev);
pa_assert(ss);
pa_assert(map);
- pa_assert(nfrags);
- pa_assert(period_size);
d = pa_xstrdup(device);
@@ -690,12 +803,12 @@ snd_pcm_t *pa_alsa_open_by_device_string(
SND_PCM_NO_AUTO_RESAMPLE|
SND_PCM_NO_AUTO_CHANNELS|
(reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
- pa_log("Error opening PCM device %s: %s", d, snd_strerror(err));
+ pa_log_info("Error opening PCM device %s: %s", d, snd_strerror(err));
pa_xfree(d);
return NULL;
}
- if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) {
+ if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) {
if (!reformat) {
reformat = TRUE;
@@ -719,13 +832,16 @@ snd_pcm_t *pa_alsa_open_by_device_string(
continue;
}
- pa_log("Failed to set hardware parameters on %s: %s", d, snd_strerror(err));
+ pa_log_info("Failed to set hardware parameters on %s: %s", d, snd_strerror(err));
pa_xfree(d);
snd_pcm_close(pcm_handle);
return NULL;
}
- *dev = d;
+ if (dev)
+ *dev = d;
+ else
+ pa_xfree(d);
if (ss->channels != map->channels)
pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA);
@@ -734,6 +850,94 @@ snd_pcm_t *pa_alsa_open_by_device_string(
}
}
+int pa_alsa_probe_profiles(
+ const char *dev_id,
+ const pa_sample_spec *ss,
+ void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata),
+ void *userdata) {
+
+ const pa_alsa_profile_info *i;
+
+ pa_assert(dev_id);
+ pa_assert(ss);
+ pa_assert(cb);
+
+ /* We try each combination of playback/capture. We also try to
+ * open only for capture resp. only for sink. Don't get confused
+ * by the trailing entry in device_table we use for this! */
+
+ for (i = device_table; i < device_table + PA_ELEMENTSOF(device_table); i++) {
+ const pa_alsa_profile_info *j;
+ snd_pcm_t *pcm_i = NULL;
+
+ if (i->alsa_name) {
+ char *id;
+ pa_sample_spec try_ss;
+ pa_channel_map try_map;
+
+ pa_log_debug("Checking for playback on %s (%s)", i->name, i->alsa_name);
+ id = pa_sprintf_malloc("%s:%s", i->alsa_name, dev_id);
+
+ try_ss = *ss;
+ try_ss.channels = i->map.channels;
+ try_map = i->map;
+
+ pcm_i = pa_alsa_open_by_device_string(
+ id, NULL,
+ &try_ss, &try_map,
+ SND_PCM_STREAM_PLAYBACK,
+ NULL, NULL, 0, NULL, NULL,
+ TRUE);
+
+ pa_xfree(id);
+
+ if (!pcm_i)
+ continue;
+ }
+
+ for (j = device_table; j < device_table + PA_ELEMENTSOF(device_table); j++) {
+ snd_pcm_t *pcm_j = NULL;
+
+ if (j->alsa_name) {
+ char *jd;
+ pa_sample_spec try_ss;
+ pa_channel_map try_map;
+
+ pa_log_debug("Checking for capture on %s (%s)", j->name, j->alsa_name);
+ jd = pa_sprintf_malloc("%s:%s", j->alsa_name, dev_id);
+
+ try_ss = *ss;
+ try_ss.channels = j->map.channels;
+ try_map = j->map;
+
+ pcm_j = pa_alsa_open_by_device_string(
+ jd, NULL,
+ &try_ss, &try_map,
+ SND_PCM_STREAM_CAPTURE,
+ NULL, NULL, 0, NULL, NULL,
+ TRUE);
+
+ pa_xfree(jd);
+
+ if (!pcm_j)
+ continue;
+ }
+
+ if (pcm_j)
+ snd_pcm_close(pcm_j);
+
+ if (i->alsa_name || j->alsa_name)
+ cb(i->alsa_name ? i : NULL,
+ j->alsa_name ? j : NULL, userdata);
+ }
+
+ if (pcm_i)
+ snd_pcm_close(pcm_i);
+ }
+
+ return TRUE;
+}
+
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
int err;
@@ -1048,7 +1252,30 @@ void pa_alsa_redirect_errors_dec(void) {
snd_lib_error_set_handler(NULL);
}
-void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
+void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) {
+ char *cn, *lcn;
+
+ pa_assert(p);
+ pa_assert(card >= 0);
+
+ pa_proplist_setf(p, "alsa.card", "%i", card);
+
+ if (snd_card_get_name(card, &cn) >= 0) {
+ pa_proplist_sets(p, "alsa.card_name", cn);
+ free(cn);
+ }
+
+ if (snd_card_get_longname(card, &lcn) >= 0) {
+ pa_proplist_sets(p, "alsa.long_card_name", lcn);
+ free(lcn);
+ }
+
+#ifdef HAVE_HAL
+ pa_hal_get_info(c, p, card);
+#endif
+}
+
+void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info) {
static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = {
[SND_PCM_CLASS_GENERIC] = "generic",
@@ -1069,8 +1296,7 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
snd_pcm_class_t class;
snd_pcm_subclass_t subclass;
- const char *n, *id, *sdn;
- char *cn = NULL, *lcn = NULL;
+ const char *n, *id, *sdn, *cn;
int card;
pa_assert(p);
@@ -1103,13 +1329,8 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info));
if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) {
- pa_proplist_setf(p, "alsa.card", "%i", card);
-
- if (snd_card_get_name(card, &cn) >= 0)
- pa_proplist_sets(p, "alsa.card_name", cn);
-
- if (snd_card_get_longname(card, &lcn) >= 0)
- pa_proplist_sets(p, "alsa.long_card_name", lcn);
+ pa_alsa_init_proplist_card(c, p, card);
+ cn = pa_proplist_gets(p, "alsa.card_name");
}
if (cn && n)
@@ -1118,9 +1339,6 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn);
else if (n)
pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n);
-
- free(lcn);
- free(cn);
}
int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
diff --git a/src/modules/alsa-util.h b/src/modules/alsa/alsa-util.h
index 95bb983a..f2d3278b 100644
--- a/src/modules/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -32,6 +32,7 @@
#include <pulse/proplist.h>
#include <pulsecore/rtpoll.h>
+#include <pulsecore/core.h>
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
@@ -54,7 +55,16 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback);
-snd_pcm_t *pa_alsa_open_by_device_id(
+typedef struct pa_alsa_profile_info {
+ pa_channel_map map;
+ const char *alsa_name;
+ const char *description;
+ const char *name;
+ unsigned priority;
+} pa_alsa_profile_info;
+
+/* Picks a working profile based on the specified ss/map */
+snd_pcm_t *pa_alsa_open_by_device_id_auto(
const char *dev_id,
char **dev,
pa_sample_spec *ss,
@@ -64,8 +74,24 @@ snd_pcm_t *pa_alsa_open_by_device_id(
snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
- pa_bool_t *use_tsched);
+ pa_bool_t *use_tsched,
+ const pa_alsa_profile_info **profile);
+/* Uses the specified profile */
+snd_pcm_t *pa_alsa_open_by_device_id_profile(
+ const char *dev_id,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched,
+ const pa_alsa_profile_info *profile);
+
+/* Opens the explicit ALSA device */
snd_pcm_t *pa_alsa_open_by_device_string(
const char *device,
char **dev,
@@ -76,7 +102,14 @@ snd_pcm_t *pa_alsa_open_by_device_string(
snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
- pa_bool_t *use_tsched);
+ pa_bool_t *use_tsched,
+ pa_bool_t require_exact_channel_number);
+
+int pa_alsa_probe_profiles(
+ const char *dev_id,
+ const pa_sample_spec *ss,
+ void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata),
+ void *userdata);
int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback);
@@ -86,7 +119,8 @@ void pa_alsa_dump_status(snd_pcm_t *pcm);
void pa_alsa_redirect_errors_inc(void);
void pa_alsa_redirect_errors_dec(void);
-void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info);
+void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info);
+void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card);
int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
new file mode 100644
index 00000000..e63414ec
--- /dev/null
+++ b/src/modules/alsa/module-alsa-card.c
@@ -0,0 +1,360 @@
+/***
+ 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 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/queue.h>
+
+#include "alsa-util.h"
+#include "alsa-sink.h"
+#include "alsa-source.h"
+#include "module-alsa-card-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Card");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+ "name=<name for the card/sink/source, to be prefixed> "
+ "card_name=<name for card> "
+ "sink_name=<name for sink> "
+ "source_name=<name for source> "
+ "device_id=<ALSA card index> "
+ "format=<sample format> "
+ "rate=<sample rate> "
+ "fragments=<number of fragments> "
+ "fragment_size=<fragment size> "
+ "mmap=<enable memory mapping?> "
+ "tsched=<enable system timer based scheduling mode?> "
+ "tsched_buffer_size=<buffer size when using timer based scheduling> "
+ "tsched_buffer_watermark=<lower fill watermark> "
+ "profile=<profile name> "
+ "ignore_dB=<ignore dB information from the device?>");
+
+static const char* const valid_modargs[] = {
+ "name",
+ "card_name",
+ "sink_name",
+ "source_name",
+ "device_id",
+ "format",
+ "rate",
+ "fragments",
+ "fragment_size",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "profile",
+ "ignore_dB",
+ NULL
+};
+
+#define DEFAULT_DEVICE_ID "0"
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+
+ char *device_id;
+
+ pa_card *card;
+ pa_sink *sink;
+ pa_source *source;
+
+ pa_modargs *modargs;
+};
+
+struct profile_data {
+ const pa_alsa_profile_info *sink_profile, *source_profile;
+};
+
+static void enumerate_cb(
+ const pa_alsa_profile_info *sink,
+ const pa_alsa_profile_info *source,
+ void *userdata) {
+
+ pa_hashmap *profiles = (pa_hashmap *) userdata;
+ char *t, *n;
+ pa_card_profile *p;
+ struct profile_data *d;
+
+ if (sink && source) {
+ n = pa_sprintf_malloc("output-%s+input-%s", sink->name, source->name);
+ t = pa_sprintf_malloc("Output %s + Input %s", sink->description, source->description);
+ } else if (sink) {
+ n = pa_sprintf_malloc("output-%s", sink->name);
+ t = pa_sprintf_malloc("Output %s", sink->description);
+ } else {
+ pa_assert(source);
+ n = pa_sprintf_malloc("input-%s", source->name);
+ t = pa_sprintf_malloc("Input %s", source->description);
+ }
+
+ pa_log_info("Found output profile '%s'", t);
+
+ p = pa_card_profile_new(n, t, sizeof(struct profile_data));
+
+ pa_xfree(t);
+ pa_xfree(n);
+
+ p->priority = (sink ? sink->priority : 0)*100 + (source ? source->priority : 0);
+ p->n_sinks = !!sink;
+ p->n_sources = !!source;
+
+ if (sink)
+ p->max_sink_channels = sink->map.channels;
+ if (source)
+ p->max_source_channels = source->map.channels;
+
+ d = PA_CARD_PROFILE_DATA(p);
+
+ d->sink_profile = sink;
+ d->source_profile = source;
+
+ pa_hashmap_put(profiles, p->name, p);
+}
+
+static void add_disabled_profile(pa_hashmap *profiles) {
+ pa_card_profile *p;
+ struct profile_data *d;
+
+ p = pa_card_profile_new("off", "Off", sizeof(struct profile_data));
+
+ d = PA_CARD_PROFILE_DATA(p);
+ d->sink_profile = d->source_profile = NULL;
+
+ pa_hashmap_put(profiles, p->name, p);
+}
+
+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+ struct userdata *u;
+ struct profile_data *nd, *od;
+
+ pa_assert(c);
+ pa_assert(new_profile);
+ pa_assert_se(u = c->userdata);
+
+ nd = PA_CARD_PROFILE_DATA(new_profile);
+ od = PA_CARD_PROFILE_DATA(c->active_profile);
+
+ if (od->sink_profile != nd->sink_profile) {
+ pa_queue *inputs = NULL;
+
+ if (u->sink) {
+ if (nd->sink_profile)
+ inputs = pa_sink_move_all_start(u->sink);
+
+ pa_alsa_sink_free(u->sink);
+ u->sink = NULL;
+ }
+
+ if (nd->sink_profile) {
+ u->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, nd->sink_profile);
+
+ if (inputs) {
+ if (u->sink)
+ pa_sink_move_all_finish(u->sink, inputs, FALSE);
+ else
+ pa_sink_move_all_fail(inputs);
+ }
+ }
+ }
+
+ if (od->source_profile != nd->source_profile) {
+ pa_queue *outputs = NULL;
+
+ if (u->source) {
+ if (nd->source_profile)
+ outputs = pa_source_move_all_start(u->source);
+
+ pa_alsa_source_free(u->source);
+ u->source = NULL;
+ }
+
+ if (nd->source_profile) {
+ u->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, nd->source_profile);
+
+ if (outputs) {
+ if (u->source)
+ pa_source_move_all_finish(u->source, outputs, FALSE);
+ else
+ pa_source_move_all_fail(outputs);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void init_profile(struct userdata *u) {
+ struct profile_data *d;
+
+ pa_assert(u);
+
+ d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+
+ if (d->sink_profile)
+ u->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, d->sink_profile);
+
+ if (d->source_profile)
+ u->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, d->source_profile);
+}
+
+static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *device_id) {
+ char *t;
+ const char *n;
+
+ pa_assert(data);
+ pa_assert(ma);
+ pa_assert(device_id);
+
+ if ((n = pa_modargs_get_value(ma, "card_name", NULL))) {
+ pa_card_new_data_set_name(data, n);
+ data->namereg_fail = TRUE;
+ return;
+ }
+
+ if ((n = pa_modargs_get_value(ma, "name", NULL)))
+ data->namereg_fail = TRUE;
+ else {
+ n = device_id;
+ data->namereg_fail = FALSE;
+ }
+
+ t = pa_sprintf_malloc("alsa_card.%s", n);
+ pa_card_new_data_set_name(data, t);
+ pa_xfree(t);
+}
+
+int pa__init(pa_module*m) {
+ pa_card_new_data data;
+ pa_modargs *ma;
+ int alsa_card_index;
+ struct userdata *u;
+
+ pa_alsa_redirect_errors_inc();
+ snd_config_update_free_global();
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID));
+ u->card = NULL;
+ u->sink = NULL;
+ u->source = NULL;
+ u->modargs = ma;
+
+ if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
+ pa_log("Card '%s' doesn't exist: %s", u->device_id, snd_strerror(alsa_card_index));
+ goto fail;
+ }
+
+ pa_card_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_alsa_init_proplist_card(m->core, data.proplist, alsa_card_index);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id);
+ set_card_name(&data, ma, u->device_id);
+
+ data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ if (pa_alsa_probe_profiles(u->device_id, &m->core->default_sample_spec, enumerate_cb, data.profiles) < 0) {
+ pa_card_new_data_done(&data);
+ goto fail;
+ }
+
+ if (pa_hashmap_isempty(data.profiles)) {
+ pa_log("Failed to find a working profile.");
+ pa_card_new_data_done(&data);
+ goto fail;
+ }
+
+ add_disabled_profile(data.profiles);
+
+ u->card = pa_card_new(m->core, &data);
+ pa_card_new_data_done(&data);
+
+ if (!u->card)
+ goto fail;
+
+ u->card->userdata = u;
+ u->card->set_profile = card_set_profile;
+
+ init_profile(u);
+
+ return 0;
+
+fail:
+
+ pa__done(m);
+ return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return
+ (u->sink ? pa_sink_linked_by(u->sink) : 0) +
+ (u->source ? pa_source_linked_by(u->source) : 0);
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ goto finish;
+
+ if (u->sink)
+ pa_alsa_sink_free(u->sink);
+
+ if (u->source)
+ pa_alsa_source_free(u->source);
+
+ if (u->card)
+ pa_card_free(u->card);
+
+ if (u->modargs)
+ pa_modargs_free(u->modargs);
+
+ pa_xfree(u->device_id);
+ pa_xfree(u);
+
+finish:
+ snd_config_update_free_global();
+ pa_alsa_redirect_errors_dec();
+}
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
new file mode 100644
index 00000000..4f844e08
--- /dev/null
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -0,0 +1,125 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2008 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+
+#include "alsa-util.h"
+#include "alsa-sink.h"
+#include "module-alsa-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+ "name=<name of the sink, to be prefixed> "
+ "sink_name=<name for the sink> "
+ "device=<ALSA device> "
+ "device_id=<ALSA card index> "
+ "format=<sample format> "
+ "rate=<sample rate> "
+ "channels=<number of channels> "
+ "channel_map=<channel map> "
+ "fragments=<number of fragments> "
+ "fragment_size=<fragment size> "
+ "mmap=<enable memory mapping?> "
+ "tsched=<enable system timer based scheduling mode?> "
+ "tsched_buffer_size=<buffer size when using timer based scheduling> "
+ "tsched_buffer_watermark=<lower fill watermark> "
+ "ignore_dB=<ignore dB information from the device?>");
+
+static const char* const valid_modargs[] = {
+ "name",
+ "sink_name",
+ "device",
+ "device_id",
+ "format",
+ "rate",
+ "channels",
+ "channel_map",
+ "fragments",
+ "fragment_size",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "ignore_dB",
+ NULL
+};
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+
+ pa_assert(m);
+
+ pa_alsa_redirect_errors_inc();
+ snd_config_update_free_global();
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ if (!(m->userdata = pa_alsa_sink_new(m, ma, __FILE__, NULL, NULL)))
+ goto fail;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+ pa_sink *sink;
+
+ pa_assert(m);
+ pa_assert_se(sink = m->userdata);
+
+ return pa_sink_linked_by(sink);
+}
+
+void pa__done(pa_module*m) {
+ pa_sink *sink;
+
+ pa_assert(m);
+
+ if ((sink = m->userdata))
+ pa_alsa_sink_free(sink);
+
+ snd_config_update_free_global();
+ pa_alsa_redirect_errors_dec();
+}
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
new file mode 100644
index 00000000..c35936df
--- /dev/null
+++ b/src/modules/alsa/module-alsa-source.c
@@ -0,0 +1,149 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2008 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <asoundlib.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
+
+#include "alsa-util.h"
+#include "alsa-source.h"
+#include "module-alsa-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+ "name=<name for the source, to be prefixed> "
+ "source_name=<name for the source> "
+ "device=<ALSA device> "
+ "device_id=<ALSA card index> "
+ "format=<sample format> "
+ "rate=<sample rate> "
+ "channels=<number of channels> "
+ "channel_map=<channel map> "
+ "fragments=<number of fragments> "
+ "fragment_size=<fragment size> "
+ "mmap=<enable memory mapping?> "
+ "tsched=<enable system timer based scheduling mode?> "
+ "tsched_buffer_size=<buffer size when using timer based scheduling> "
+ "tsched_buffer_watermark=<upper fill watermark> "
+ "ignore_dB=<ignore dB information from the device?>");
+
+static const char* const valid_modargs[] = {
+ "name",
+ "source_name",
+ "device",
+ "device_id",
+ "format",
+ "rate",
+ "channels",
+ "channel_map",
+ "fragments",
+ "fragment_size",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "ignore_dB",
+ NULL
+};
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+
+ pa_assert(m);
+
+ pa_alsa_redirect_errors_inc();
+ snd_config_update_free_global();
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ if (!(m->userdata = pa_alsa_source_new(m, ma, __FILE__, NULL, NULL)))
+ goto fail;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+ pa_source *source;
+
+ pa_assert(m);
+ pa_assert_se(source = m->userdata);
+
+ return pa_source_linked_by(source);
+}
+
+void pa__done(pa_module*m) {
+ pa_source *source;
+
+ pa_assert(m);
+
+ if ((source = m->userdata))
+ pa_alsa_source_free(source);
+
+ snd_config_update_free_global();
+ pa_alsa_redirect_errors_dec();
+}
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index cb4746a4..e2f6d019 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -65,7 +65,10 @@ PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name of the device> "
"address=<address of the device> "
- "profile=<a2dp|hsp>");
+ "profile=<a2dp|hsp> "
+ "rate=<sample rate> "
+ "channels=<number of channels> "
+ "path=<device object path>");
struct bt_a2dp {
sbc_capabilities_t sbc_capabilities;
@@ -85,6 +88,7 @@ struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink;
+ pa_source *source;
pa_thread_mq thread_mq;
pa_rtpoll *rtpoll;
@@ -109,6 +113,8 @@ struct userdata {
pa_usec_t latency;
struct bt_a2dp a2dp;
+ char *path;
+ pa_dbus_connection *conn;
};
static const char* const valid_modargs[] = {
@@ -117,6 +123,7 @@ static const char* const valid_modargs[] = {
"profile",
"rate",
"channels",
+ "path",
NULL
};
@@ -228,9 +235,9 @@ static int bt_getcaps(struct userdata *u) {
msg.getcaps_req.h.length = sizeof(msg.getcaps_req);
strncpy(msg.getcaps_req.device, u->addr, 18);
- if (strcasecmp(u->profile, "a2dp") == 0)
+ if (pa_streq(u->profile, "a2dp"))
msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
- else if (strcasecmp(u->profile, "hsp") == 0)
+ else if (pa_streq(u->profile, "hsp"))
msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO;
else {
pa_log_error("Invalid profile argument: %s", u->profile);
@@ -448,8 +455,11 @@ static int bt_setconf(struct userdata *u) {
}
u->ss.format = PA_SAMPLE_S16LE;
}
- else
- u->ss.format = PA_SAMPLE_U8;
+ else {
+ u->ss.format = PA_SAMPLE_S16LE;
+ u->ss.channels = 1;
+ u->ss.rate = 8000;
+ }
memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
msg.setconf_req.h.type = BT_REQUEST;
@@ -597,7 +607,7 @@ static int sco_process_render(struct userdata *u) {
for (;;) {
ssize_t l;
- l = pa_loop_write(u->stream_fd, (uint8_t*) p, memchunk.length, NULL);
+ l = pa_loop_write(u->stream_fd, p, memchunk.length, NULL);
pa_log_debug("Memblock written to socket: %li bytes", (long) l);
pa_assert(l != 0);
@@ -786,6 +796,144 @@ finish:
pa_log_debug("IO thread shutting down");
}
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
+ DBusMessageIter arg_i;
+ DBusError err;
+ const char *value;
+ struct userdata *u;
+
+ pa_assert(bus);
+ pa_assert(msg);
+ pa_assert(userdata);
+ u = userdata;
+
+ pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+ dbus_message_get_interface(msg),
+ dbus_message_get_path(msg),
+ dbus_message_get_member(msg));
+
+ dbus_error_init(&err);
+
+ if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") ||
+ dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) {
+
+ struct device *d;
+ const char *profile;
+ DBusMessageIter variant_i;
+ dbus_uint16_t gain;
+
+ if (!dbus_message_iter_init(msg, &arg_i)) {
+ pa_log("dbus: message has no parameters");
+ goto done;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_STRING) {
+ pa_log("Property name not a string.");
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(&arg_i, &value);
+
+ if (!dbus_message_iter_next(&arg_i)) {
+ pa_log("Property value missing");
+ goto done;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_VARIANT) {
+ pa_log("Property value not a variant.");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&arg_i, &variant_i);
+
+ if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) {
+ dbus_message_iter_get_basic(&variant_i, &gain);
+
+ if (pa_streq(value, "SpeakerGain")) {
+ pa_log("spk gain: %d", gain);
+ pa_cvolume_set(&u->sink->volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ u->sink->virtual_volume = u->sink->volume;
+ pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
+ } else {
+ pa_log("mic gain: %d", gain);
+ if (!u->source)
+ goto done;
+
+ pa_cvolume_set(&u->source->volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ pa_subscription_post(u->source->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, u->source->index);
+ }
+ }
+ }
+
+done:
+ dbus_error_free(&err);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int sink_get_volume_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ /* refresh? */
+
+ return 0;
+}
+
+static int source_get_volume_cb(pa_source *s) {
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ /* refresh? */
+
+ return 0;
+}
+
+static int sink_set_volume_cb(pa_sink *s) {
+ DBusError e;
+ DBusMessage *m, *r;
+ DBusMessageIter it, itvar;
+ dbus_uint16_t vol;
+ const char *spkgain = "SpeakerGain";
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ dbus_error_init(&e);
+
+ vol = ((float)pa_cvolume_max(&s->volume) / PA_VOLUME_NORM) * 15;
+ pa_log_debug("set headset volume: %d", vol);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetProperty"));
+ dbus_message_iter_init_append(m, &it);
+ dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &spkgain);
+ dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT16_AS_STRING, &itvar);
+ dbus_message_iter_append_basic(&itvar, DBUS_TYPE_UINT16, &vol);
+ dbus_message_iter_close_container(&it, &itvar);
+
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+ if (r)
+ dbus_message_unref(r);
+
+ dbus_error_free(&e);
+
+ return 0;
+}
+
+static int source_set_volume_cb(pa_source *s) {
+ dbus_uint16_t vol;
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ vol = ((float)pa_cvolume_max(&s->volume) / PA_VOLUME_NORM) * 15;
+
+ pa_log_debug("set headset mic volume: %d (not implemented yet)", vol);
+
+ return 0;
+}
+
int pa__init(pa_module* m) {
int e;
pa_modargs *ma;
@@ -793,8 +941,12 @@ int pa__init(pa_module* m) {
pa_sink_new_data data;
struct pollfd *pollfd;
struct userdata *u;
+ DBusError err;
+ char *tmp;
pa_assert(m);
+ dbus_error_init(&err);
+
m->userdata = u = pa_xnew0(struct userdata, 1);
u->module = m;
u->core = m->core;
@@ -830,6 +982,7 @@ int pa__init(pa_module* m) {
pa_log_error("Failed to get rate from module arguments");
goto fail;
}
+ u->path = pa_xstrdup(pa_modargs_get_value(ma, "path", NULL));
channels = u->ss.channels;
if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0) {
@@ -907,7 +1060,50 @@ int pa__init(pa_module* m) {
goto fail;
}
pa_sink_put(u->sink);
+ if (!u->path)
+ goto end;
+
+ /* connect to the bus */
+ u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
+ if (dbus_error_is_set(&err) || (u->conn == NULL) ) {
+ pa_log("Failed to get D-Bus connection: %s", err.message);
+ goto fail;
+ }
+
+ /* monitor property changes */
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
+ pa_log_error("Failed to add filter function");
+ goto fail;
+ }
+
+ if (pa_streq(u->profile, "hsp")) {
+ tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path);
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), tmp, &err);
+ pa_xfree(tmp);
+ if (dbus_error_is_set(&err)) {
+ pa_log_error("Unable to subscribe to org.bluez.Headset signals: %s: %s", err.name, err.message);
+ goto fail;
+ }
+ } else {
+
+ tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path);
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), tmp, &err);
+ pa_xfree(tmp);
+ if (dbus_error_is_set(&err)) {
+ pa_log_error("Unable to subscribe to org.bluez.AudioSink signals: %s: %s", err.name, err.message);
+ goto fail;
+ }
+ }
+
+ u->sink->get_volume = sink_get_volume_cb;
+ u->sink->set_volume = sink_set_volume_cb;
+ if (u->source) {
+ u->source->get_volume = source_get_volume_cb;
+ u->source->set_volume = source_set_volume_cb;
+ }
+
+end:
pa_modargs_free(ma);
return 0;
@@ -926,6 +1122,31 @@ void pa__done(pa_module *m) {
if (!(u = m->userdata))
return;
+ if (u->conn) {
+ DBusError error;
+ char *tmp;
+ dbus_error_init(&error);
+
+ if (pa_streq(u->profile, "hsp")) {
+ tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path);
+ dbus_bus_remove_match(pa_dbus_connection_get(u->conn), tmp, &error);
+ pa_xfree(tmp);
+ dbus_error_free(&error);
+ } else {
+ tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path);
+ dbus_bus_remove_match(pa_dbus_connection_get(u->conn), tmp, &error);
+ pa_xfree(tmp);
+ dbus_error_free(&error);
+ }
+
+ dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u);
+
+ pa_dbus_connection_unref(u->conn);
+ }
+
+ if (u->path)
+ pa_xfree(u->path);
+
if (u->sink)
pa_sink_unlink(u->sink);
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 2fe09370..1bc05c02 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -53,6 +53,13 @@ struct uuid {
PA_LLIST_FIELDS(struct uuid);
};
+struct dbus_pending {
+ char *path;
+ char *profile;
+ DBusPendingCall *pending;
+ PA_LLIST_FIELDS(struct dbus_pending);
+};
+
struct device {
char *name;
char *object_path;
@@ -70,7 +77,9 @@ struct device {
struct userdata {
pa_module *module;
pa_dbus_connection *conn;
+ dbus_int32_t dbus_data_slot;
PA_LLIST_HEAD(struct device, device_list);
+ PA_LLIST_HEAD(struct dbus_pending, dbus_pending_list);
};
static struct module *module_new(const char *profile, pa_module *pa_m) {
@@ -118,6 +127,31 @@ static void uuid_free(struct uuid *uuid) {
pa_xfree(uuid);
}
+static struct dbus_pending *dbus_pending_new(struct userdata *u, DBusPendingCall *pending, const char *path, const char *profile) {
+ struct dbus_pending *node;
+
+ pa_assert(pending);
+
+ node = pa_xnew(struct dbus_pending, 1);
+ node->pending = pending;
+ node->path = pa_xstrdup(path);
+ node->profile = pa_xstrdup(profile);
+ PA_LLIST_INIT(struct dbus_pending, node);
+ dbus_pending_call_set_data(pending, u->dbus_data_slot, node, NULL);
+
+ return node;
+}
+
+static void dbus_pending_free(struct dbus_pending *pending) {
+ pa_assert(pending);
+
+ pa_xfree(pending->path);
+ pa_xfree(pending->profile);
+ dbus_pending_call_cancel(pending->pending);
+ dbus_pending_call_unref(pending->pending);
+ pa_xfree(pending);
+}
+
static struct device *device_new(const char *object_path) {
struct device *node;
@@ -342,7 +376,7 @@ static void load_module_for_device(struct userdata *u, struct device *d, const c
pa_assert(d);
get_device_properties(u, d);
- args = pa_sprintf_malloc("sink_name=\"%s\" address=\"%s\" profile=\"%s\"", d->name, d->address, profile);
+ args = pa_sprintf_malloc("sink_name=\"%s\" address=\"%s\" profile=\"%s\" path=\"%s\"", d->name, d->address, profile, d->object_path);
pa_m = pa_module_load(u->module->core, "module-bluetooth-device", args);
pa_xfree(args);
@@ -468,23 +502,269 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *
done:
dbus_error_free(&err);
- return DBUS_HANDLER_RESULT_HANDLED;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+
+static void get_properties_reply(DBusPendingCall *pending, void *user_data) {
+ struct userdata *u;
+ DBusMessage *r;
+ dbus_bool_t connected;
+ DBusMessageIter arg_i, element_i;
+ DBusMessageIter variant_i;
+ struct device *d;
+ struct dbus_pending *p;
+
+ pa_assert(u = user_data);
+
+ r = dbus_pending_call_steal_reply(pending);
+ if (!r)
+ goto end;
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("%s GetProperties reply has no arguments", p->profile);
+ goto end;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("%s GetProperties argument is not an array", p->profile);
+ goto end;
+ }
+
+ connected = FALSE;
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter dict_i;
+ const char *key;
+
+ dbus_message_iter_recurse(&element_i, &dict_i);
+
+ if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_STRING) {
+ pa_log("Property name not a string.");
+ goto end;
+ }
+
+ dbus_message_iter_get_basic(&dict_i, &key);
+
+ if (!dbus_message_iter_next(&dict_i)) {
+ pa_log("Property value missing");
+ goto end;
+ }
+
+ if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_VARIANT) {
+ pa_log("Property value not a variant.");
+ goto end;
+ }
+
+ dbus_message_iter_recurse(&dict_i, &variant_i);
+
+ switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+ case DBUS_TYPE_BOOLEAN: {
+
+ dbus_bool_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Connected")) {
+ connected = value;
+ goto endloop;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (!dbus_message_iter_next(&element_i))
+ break;
+ }
+
+endloop:
+ if (connected) {
+ p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+ pa_log_debug("%s: %s connected", p->path, p->profile);
+ d = device_find(u, p->path);
+
+ if (!d) {
+ d = device_new(p->path);
+ PA_LLIST_PREPEND(struct device, u->device_list, d);
+ }
+
+ load_module_for_device(u, d, p->profile);
+ }
+
+ dbus_message_unref(r);
+
+end:
+ p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+ PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_free(p);
+}
+
+static void list_devices_reply(DBusPendingCall *pending, void *user_data) {
+ DBusMessage *r, *m;
+ DBusPendingCall *call;
+ DBusError e;
+ char **paths = NULL;
+ int i, num = -1;
+ struct dbus_pending *p;
+ struct userdata *u;
+
+ pa_assert(u = user_data);
+ dbus_error_init(&e);
+
+ r = dbus_pending_call_steal_reply(pending);
+ if (!r) {
+ pa_log("Failed to get ListDevices reply");
+ goto end;
+ }
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
+ pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e.message);
+ dbus_error_free(&e);
+ } else {
+ for (i = 0; i < num; ++i) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.Headset", "GetProperties"));
+ if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+ p = dbus_pending_new(u, call, paths[i], "hsp");
+ PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_call_set_notify(call, get_properties_reply, u, NULL);
+ } else {
+ pa_log("Failed to send GetProperties");
+ }
+
+ dbus_message_unref(m);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.AudioSink", "GetProperties"));
+ if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+ p = dbus_pending_new(u, call, paths[i], "a2dp");
+ PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_call_set_notify(call, get_properties_reply, u, NULL);
+ } else {
+ pa_log("Failed to send GetProperties");
+ }
+
+ dbus_message_unref(m);
+ }
+ }
+
+ if (paths)
+ dbus_free_string_array (paths);
+ dbus_message_unref(r);
+
+end:
+ p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+ PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_free(p);
+}
+
+static void list_adapters_reply(DBusPendingCall *pending, void *user_data) {
+ DBusMessage *r, *m;
+ DBusPendingCall *call;
+ DBusError e;
+ char **paths = NULL;
+ int i, num = -1;
+ struct dbus_pending *p;
+ struct userdata *u;
+
+ pa_assert(u = user_data);
+ dbus_error_init(&e);
+
+ r = dbus_pending_call_steal_reply(pending);
+ if (!r) {
+ pa_log("Failed to get ListAdapters reply");
+ goto end;
+ }
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
+ pa_log("org.bluez.Manager.ListAdapters returned an error: '%s'\n", e.message);
+ dbus_error_free(&e);
+ } else {
+ for (i = 0; i < num; ++i) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.Adapter", "ListDevices"));
+ if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+ p = dbus_pending_new(u, call, NULL, NULL);
+ PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_call_set_notify(call, list_devices_reply, u, NULL);
+ } else {
+ pa_log("Failed to send ListDevices");
+ }
+
+ dbus_message_unref(m);
+ }
+ }
+
+ if (paths)
+ dbus_free_string_array (paths);
+ dbus_message_unref(r);
+
+end:
+ p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+ PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_free(p);
+}
+
+static void lookup_devices(struct userdata *u) {
+ DBusMessage *m;
+ DBusPendingCall *call;
+ struct dbus_pending *p;
+
+ pa_assert(u);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
+ if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+ p = dbus_pending_new(u, call, NULL, NULL);
+ PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_call_set_notify(call, list_adapters_reply, u, NULL);
+ } else {
+ pa_log("Failed to send ListAdapters");
+ }
+
+ dbus_message_unref(m);
}
void pa__done(pa_module* m) {
struct userdata *u;
struct device *i;
+ struct dbus_pending *p;
pa_assert(m);
if (!(u = m->userdata))
return;
+ while ((p = u->dbus_pending_list)) {
+ PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_free(p);
+ }
+
while ((i = u->device_list)) {
PA_LLIST_REMOVE(struct device, u->device_list, i);
device_free(i);
}
+ if (u->dbus_data_slot != -1) {
+ dbus_pending_call_free_data_slot(&u->dbus_data_slot);
+ }
+
if (u->conn) {
DBusError error;
dbus_error_init(&error);
@@ -514,8 +794,10 @@ int pa__init(pa_module* m) {
dbus_error_init(&err);
m->userdata = u = pa_xnew(struct userdata, 1);
+ u->dbus_data_slot = -1;
u->module = m;
PA_LLIST_HEAD_INIT(struct device, u->device_list);
+ PA_LLIST_HEAD_INIT(DBusPendingCall, u->dbus_pending_list);
/* connect to the bus */
u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
@@ -524,6 +806,9 @@ int pa__init(pa_module* m) {
goto fail;
}
+ if (!dbus_pending_call_allocate_data_slot(&u->dbus_data_slot))
+ goto fail;
+
/* dynamic detection of bluetooth audio devices */
if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
pa_log_error("Failed to add filter function");
@@ -548,6 +833,8 @@ int pa__init(pa_module* m) {
goto fail;
}
+ lookup_devices(u);
+
return 0;
fail:
diff --git a/src/modules/bluetooth/module-bluetooth-proximity.c b/src/modules/bluetooth/module-bluetooth-proximity.c
index 4cfaaf5b..f30d39fe 100644
--- a/src/modules/bluetooth/module-bluetooth-proximity.c
+++ b/src/modules/bluetooth/module-bluetooth-proximity.c
@@ -103,7 +103,7 @@ static void update_volume(struct userdata *u) {
u->muted = FALSE;
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
return;
}
@@ -116,7 +116,7 @@ static void update_volume(struct userdata *u) {
u->muted = TRUE;
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
return;
}
diff --git a/src/modules/dbus-util.h b/src/modules/dbus-util.h
index 2b24ac63..c4794dac 100644
--- a/src/modules/dbus-util.h
+++ b/src/modules/dbus-util.h
@@ -24,6 +24,8 @@
#include <dbus/dbus.h>
+#include <pulsecore/core.h>
+
typedef struct pa_dbus_connection pa_dbus_connection;
/* return the DBusConnection of the specified type for the given core,
diff --git a/src/modules/hal-util.c b/src/modules/hal-util.c
new file mode 100644
index 00000000..82bbc57e
--- /dev/null
+++ b/src/modules/hal-util.c
@@ -0,0 +1,127 @@
+/***
+ 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 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General 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/log.h>
+
+#include <hal/libhal.h>
+
+#include "dbus-util.h"
+#include "hal-util.h"
+
+int pa_hal_get_info(pa_core *core, pa_proplist *p, int card) {
+ pa_dbus_connection *c = NULL;
+ LibHalContext *hal = NULL;
+ DBusError error;
+ int r = -1;
+ char **udis = NULL, *t;
+ int n, i;
+
+ pa_assert(core);
+ pa_assert(p);
+ pa_assert(card >= 0);
+
+ dbus_error_init(&error);
+
+ if (!(c = pa_dbus_bus_get(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 finish;
+ }
+
+
+ if (!(hal = libhal_ctx_new())) {
+ pa_log_error("libhal_ctx_new() finished");
+ goto finish;
+ }
+
+ if (!libhal_ctx_set_dbus_connection(hal, pa_dbus_connection_get(c))) {
+ pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ if (!libhal_ctx_init(hal, &error)) {
+ pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ if (!(udis = libhal_find_device_by_capability(hal, "sound", &n, &error)) < 0) {
+ pa_log_error("Couldn't find devices: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ for (i = 0; i < n; i++) {
+ dbus_int32_t this_card;
+
+ this_card = libhal_device_get_property_int(hal, udis[i], "sound.card", &error);
+ if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
+ continue;
+ }
+
+ if (this_card == card)
+ break;
+
+ }
+
+ if (i >= n)
+ goto finish;
+
+ pa_proplist_sets(p, "hal.udi", udis[i]);
+
+ t = libhal_device_get_property_string(hal, udis[i], "info.product", &error);
+ if (dbus_error_is_set(&error))
+ dbus_error_free(&error);
+ if (t) {
+ pa_proplist_sets(p, "hal.product", t);
+ libhal_free_string(t);
+ }
+
+ t = libhal_device_get_property_string(hal, udis[i], "sound.card_id", &error);
+ if (dbus_error_is_set(&error))
+ dbus_error_free(&error);
+ if (t) {
+ pa_proplist_sets(p, "hal.card_id", t);
+ libhal_free_string(t);
+ }
+
+ r = 0;
+
+finish:
+
+ if (udis)
+ libhal_free_string_array(udis);
+
+ dbus_error_free(&error);
+
+ if (hal) {
+ libhal_ctx_shutdown(hal, &error);
+ libhal_ctx_free(hal);
+ dbus_error_free(&error);
+ }
+
+ if (c)
+ pa_dbus_connection_unref(c);
+
+ return r;
+}
diff --git a/src/modules/hal-util.h b/src/modules/hal-util.h
new file mode 100644
index 00000000..3c0e0943
--- /dev/null
+++ b/src/modules/hal-util.h
@@ -0,0 +1,30 @@
+#ifndef foohalutilhfoo
+#define foohalutilhfoo
+
+/***
+ 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 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+
+#include <pulsecore/core.h>
+
+int pa_hal_get_info(pa_core *core, pa_proplist *p, int card);
+
+#endif
diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
index cd3f3112..591695fb 100644
--- a/src/modules/module-always-sink.c
+++ b/src/modules/module-always-sink.c
@@ -100,6 +100,10 @@ static pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userd
if (u->ignore)
return PA_HOOK_OK;
+ /* There's no point in doing anything if the core is shut down anyway */
+ if (c->state == PA_CORE_SHUTDOWN)
+ return PA_HOOK_OK;
+
/* Auto-loaded null-sink not active, so ignoring newly detected sink. */
if (u->null_module == PA_INVALID_INDEX)
return PA_HOOK_OK;
@@ -130,6 +134,10 @@ static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* us
return PA_HOOK_OK;
}
+ /* There's no point in doing anything if the core is shut down anyway */
+ if (c->state == PA_CORE_SHUTDOWN)
+ return PA_HOOK_OK;
+
load_null_sink_if_needed(c, sink, u);
return PA_HOOK_OK;
@@ -172,7 +180,7 @@ void pa__done(pa_module*m) {
pa_hook_slot_free(u->put_slot);
if (u->unlink_slot)
pa_hook_slot_free(u->unlink_slot);
- if (u->null_module != PA_INVALID_INDEX)
+ if (u->null_module != PA_INVALID_INDEX && m->core->state != PA_CORE_SHUTDOWN)
pa_module_unload_request_by_index(m->core, u->null_module, TRUE);
pa_xfree(u->sink_name);
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
new file mode 100644
index 00000000..02e973c4
--- /dev/null
+++ b/src/modules/module-card-restore.c
@@ -0,0 +1,284 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006-2008 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <gdbm.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/card.h>
+#include <pulsecore/namereg.h>
+
+#include "module-card-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SAVE_INTERVAL 10
+
+static const char* const valid_modargs[] = {
+ NULL
+};
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_subscription *subscription;
+ pa_hook_slot *card_new_hook_slot;
+ pa_time_event *save_time_event;
+ GDBM_FILE gdbm_file;
+};
+
+struct entry {
+ char profile[PA_NAME_MAX];
+};
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(tv);
+ pa_assert(u);
+
+ pa_assert(e == u->save_time_event);
+ u->core->mainloop->time_free(u->save_time_event);
+ u->save_time_event = NULL;
+
+ gdbm_sync(u->gdbm_file);
+ pa_log_info("Synced.");
+}
+
+static struct entry* read_entry(struct userdata *u, const char *name) {
+ datum key, data;
+ struct entry *e;
+
+ pa_assert(u);
+ pa_assert(name);
+
+ key.dptr = (char*) name;
+ key.dsize = (int) strlen(name);
+
+ data = gdbm_fetch(u->gdbm_file, key);
+
+ if (!data.dptr)
+ goto fail;
+
+ if (data.dsize != sizeof(struct entry)) {
+ pa_log_warn("Database contains entry for card %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ goto fail;
+ }
+
+ e = (struct entry*) data.dptr;
+
+ if (!memchr(e->profile, 0, sizeof(e->profile))) {
+ pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name);
+ goto fail;
+ }
+
+ return e;
+
+fail:
+
+ pa_xfree(data.dptr);
+ return NULL;
+}
+
+static void trigger_save(struct userdata *u) {
+ struct timeval tv;
+
+ if (u->save_time_event)
+ return;
+
+ pa_gettimeofday(&tv);
+ tv.tv_sec += SAVE_INTERVAL;
+ u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ struct userdata *u = userdata;
+ struct entry entry, *old;
+ datum key, data;
+ pa_card *card;
+
+ pa_assert(c);
+ pa_assert(u);
+
+ if (t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW) &&
+ t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE))
+ return;
+
+ memset(&entry, 0, sizeof(entry));
+
+ if (!(card = pa_idxset_get_by_index(c->cards, idx)))
+ return;
+
+ pa_strlcpy(entry.profile, card->active_profile ? card->active_profile->name : "", sizeof(entry.profile));
+
+ if ((old = read_entry(u, card->name))) {
+
+ if (strncmp(old->profile, entry.profile, sizeof(entry.profile)) == 0) {
+ pa_xfree(old);
+ return;
+ }
+
+ pa_xfree(old);
+ }
+
+ key.dptr = card->name;
+ key.dsize = (int) strlen(card->name);
+
+ data.dptr = (void*) &entry;
+ data.dsize = sizeof(entry);
+
+ pa_log_info("Storing profile for card %s.", card->name);
+
+ gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
+
+ trigger_save(u);
+}
+
+static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
+ struct entry *e;
+
+ pa_assert(new_data);
+
+ if ((e = read_entry(u, new_data->name)) && e->profile) {
+
+ if (!new_data->active_profile) {
+ pa_card_new_data_set_profile(new_data, e->profile);
+ pa_log_info("Restoring profile for card %s.", new_data->name);
+ } else
+ pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
+
+ pa_xfree(e);
+ }
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ char *fname, *fn;
+ pa_card *card;
+ uint32_t idx;
+ int gdbm_cache_size;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->save_time_event = NULL;
+ u->gdbm_file = NULL;
+
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_CARD, subscribe_callback, u);
+
+ u->card_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) card_new_hook_callback, u);
+
+ /* We include the host identifier in the file name because gdbm
+ * files are CPU dependant, and we don't want things to go wrong
+ * if we are on a multiarch system. */
+
+ fn = pa_sprintf_malloc("card-database."CANONICAL_HOST".gdbm");
+ fname = pa_state_path(fn, TRUE);
+ pa_xfree(fn);
+
+ if (!fname)
+ goto fail;
+
+ if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT|GDBM_NOLOCK, 0600, NULL))) {
+ pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
+ pa_xfree(fname);
+ goto fail;
+ }
+
+ /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
+ gdbm_cache_size = 10;
+ gdbm_setopt(u->gdbm_file, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
+
+ pa_log_info("Sucessfully opened database file '%s'.", fname);
+ pa_xfree(fname);
+
+ for (card = pa_idxset_first(m->core->cards, &idx); card; card = pa_idxset_next(m->core->cards, &idx))
+ subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, card->index, u);
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+ pa__done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata* u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+
+ if (u->card_new_hook_slot)
+ pa_hook_slot_free(u->card_new_hook_slot);
+
+ if (u->save_time_event)
+ u->core->mainloop->time_free(u->save_time_event);
+
+ if (u->gdbm_file)
+ gdbm_close(u->gdbm_file);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index d61d127a..82c88711 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -504,7 +504,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
* 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);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
/* Called from thread context */
@@ -627,6 +627,7 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -1103,7 +1104,7 @@ int pa__init(pa_module*m) {
while ((n = pa_split(slaves, ",", &split_state))) {
pa_sink *slave_sink;
- if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, TRUE)) || slave_sink == u->sink) {
+ if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK)) || slave_sink == u->sink) {
pa_log("Invalid slave sink '%s'", n);
pa_xfree(n);
goto fail;
diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
index e1933c28..4f3ed8dd 100644
--- a/src/modules/module-console-kit.c
+++ b/src/modules/module-console-kit.c
@@ -63,6 +63,7 @@ struct session {
};
struct userdata {
+ pa_module *module;
pa_core *core;
pa_dbus_connection *connection;
pa_hashmap *sessions;
@@ -73,7 +74,7 @@ static void add_session(struct userdata *u, const char *id) {
DBusMessage *m = NULL, *reply = NULL;
uint32_t uid;
struct session *session;
- char *t;
+ pa_client_new_data data;
dbus_error_init (&error);
@@ -109,11 +110,19 @@ static void add_session(struct userdata *u, const char *id) {
session = pa_xnew(struct session, 1);
session->id = pa_xstrdup(id);
- t = pa_sprintf_malloc("ConsoleKit Session %s", id);
- session->client = pa_client_new(u->core, __FILE__, t);
- pa_xfree(t);
-
- pa_proplist_sets(session->client->proplist, "console-kit.session", id);
+ pa_client_new_data_init(&data);
+ data.module = u->module;
+ data.driver = __FILE__;
+ pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "ConsoleKit Session %s", id);
+ pa_proplist_sets(data.proplist, "console-kit.session", id);
+ session->client = pa_client_new(u->core, &data);
+ pa_client_new_data_done(&data);
+
+ if (!session->client) {
+ pa_xfree(session->id);
+ pa_xfree(session);
+ goto fail;
+ }
pa_hashmap_put(u->sessions, session->id, session);
@@ -295,6 +304,7 @@ int pa__init(pa_module*m) {
m->userdata = u = pa_xnew(struct userdata, 1);
u->core = m->core;
+ u->module = m;
u->connection = connection;
u->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
index d2cc24f3..d299f40b 100644
--- a/src/modules/module-default-device-restore.c
+++ b/src/modules/module-default-device-restore.c
@@ -57,10 +57,11 @@ static void load(struct userdata *u) {
/* We never overwrite manually configured settings */
- if (u->core->default_sink_name)
+ if (u->core->default_sink)
pa_log_info("Manually configured default sink, not overwriting.");
else if ((f = fopen(u->sink_filename, "r"))) {
char ln[256] = "";
+ pa_sink *s;
fgets(ln, sizeof(ln)-1, f);
pa_strip_nl(ln);
@@ -68,8 +69,8 @@ static void load(struct userdata *u) {
if (!ln[0])
pa_log_info("No previous default sink setting, ignoring.");
- else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
- pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
+ else if ((s = pa_namereg_get(u->core, ln, PA_NAMEREG_SINK))) {
+ pa_namereg_set_default_sink(u->core, s);
pa_log_info("Restored default sink '%s'.", ln);
} else
pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
@@ -77,10 +78,11 @@ static void load(struct userdata *u) {
} else if (errno != ENOENT)
pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
- if (u->core->default_source_name)
+ if (u->core->default_source)
pa_log_info("Manually configured default source, not overwriting.");
else if ((f = fopen(u->source_filename, "r"))) {
char ln[256] = "";
+ pa_source *s;
fgets(ln, sizeof(ln)-1, f);
pa_strip_nl(ln);
@@ -88,8 +90,8 @@ static void load(struct userdata *u) {
if (!ln[0])
pa_log_info("No previous default source setting, ignoring.");
- else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
- pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
+ else if ((s = pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE))) {
+ pa_namereg_set_default_source(u->core, s);
pa_log_info("Restored default source '%s'.", ln);
} else
pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
@@ -106,8 +108,8 @@ static void save(struct userdata *u) {
if (u->sink_filename) {
if ((f = fopen(u->sink_filename, "w"))) {
- const char *n = pa_namereg_get_default_sink_name(u->core);
- fprintf(f, "%s\n", pa_strempty(n));
+ pa_sink *s = pa_namereg_get_default_sink(u->core);
+ fprintf(f, "%s\n", s ? s->name : "");
fclose(f);
} else
pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
@@ -115,8 +117,8 @@ static void save(struct userdata *u) {
if (u->source_filename) {
if ((f = fopen(u->source_filename, "w"))) {
- const char *n = pa_namereg_get_default_source_name(u->core);
- fprintf(f, "%s\n", pa_strempty(n));
+ pa_source *s = pa_namereg_get_default_source(u->core);
+ fprintf(f, "%s\n", s ? s->name : "");
fclose(f);
} else
pa_log("Failed to save default source: %s", pa_cstrerror(errno));
diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4
index 64ce1928..f9924cfa 100644
--- a/src/modules/module-defs.h.m4
+++ b/src/modules/module-defs.h.m4
@@ -18,9 +18,11 @@ gen_symbol(pa__get_description)
gen_symbol(pa__get_usage)
gen_symbol(pa__get_version)
gen_symbol(pa__load_once)
+gen_symbol(pa__get_n_used)
int pa__init(pa_module*m);
void pa__done(pa_module*m);
+int pa__get_n_used(pa_module*m);
const char* pa__get_author(void);
const char* pa__get_description(void);
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index 1616d47c..9ed262db 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -239,7 +239,7 @@ int pa__init(pa_module*m) {
#ifdef HAVE_ALSA
if ((n = detect_alsa(m->core, just_one)) <= 0)
#endif
-#if HAVE_OSS
+#ifdef HAVE_OSS
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 c0cb0dc5..8e0cf92b 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -53,6 +53,9 @@ PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "restore_volume=<Save/restore volumes?> "
+ "restore_muted=<Save/restore muted states?>");
#define SAVE_INTERVAL 10
@@ -98,14 +101,14 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
pa_log_info("Synced.");
}
-static struct entry* read_entry(struct userdata *u, char *name) {
+static struct entry* read_entry(struct userdata *u, const char *name) {
datum key, data;
struct entry *e;
pa_assert(u);
pa_assert(name);
- key.dptr = name;
+ key.dptr = (char*) name;
key.dsize = (int) strlen(name);
data = gdbm_fetch(u->gdbm_file, key);
@@ -235,13 +238,22 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *
if ((e = read_entry(u, name))) {
if (u->restore_volume) {
- pa_log_info("Restoring volume for sink %s.", new_data->name);
- pa_sink_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+
+ if (!new_data->volume_is_set) {
+ pa_log_info("Restoring volume for sink %s.", new_data->name);
+ pa_sink_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+ } else
+ pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name);
+
}
if (u->restore_muted) {
- pa_log_info("Restoring mute state for sink %s.", new_data->name);
- pa_sink_new_data_set_muted(new_data, e->muted);
+
+ if (!new_data->muted_is_set) {
+ pa_log_info("Restoring mute state for sink %s.", new_data->name);
+ pa_sink_new_data_set_muted(new_data, e->muted);
+ } else
+ pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
}
pa_xfree(e);
@@ -263,13 +275,21 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
if ((e = read_entry(u, name))) {
if (u->restore_volume) {
- pa_log_info("Restoring volume for source %s.", new_data->name);
- pa_source_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+
+ if (!new_data->volume_is_set) {
+ pa_log_info("Restoring volume for source %s.", new_data->name);
+ pa_source_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+ } else
+ pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name);
}
if (u->restore_muted) {
- pa_log_info("Restoring mute state for source %s.", new_data->name);
- pa_source_new_data_set_muted(new_data, e->muted);
+
+ if (!new_data->muted_is_set) {
+ pa_log_info("Restoring mute state for source %s.", new_data->name);
+ pa_source_new_data_set_muted(new_data, e->muted);
+ } else
+ pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
}
pa_xfree(e);
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 14f1810a..552cf75e 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -156,6 +156,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -168,7 +169,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, &u->sink->sample_spec);
*((pa_usec_t*) data) = w > r ? w - r : 0;
- break;
+ return 0;
}
case SINK_MESSAGE_PASS_SOCKET: {
@@ -621,6 +622,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
pa_assert(m);
diff --git a/src/modules/module-flat-volume.c b/src/modules/module-flat-volume.c
deleted file mode 100644
index 9bc8055a..00000000
--- a/src/modules/module-flat-volume.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/***
- This file is part of PulseAudio.
-
- Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- Copyright 2004-2006, 2008 Lennart Poettering
-
- Contact: Marc-Andre Lureau <marc-andre.lureau@nokia.com>
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <regex.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/core-error.h>
-#include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/log.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/macro.h>
-
-#include "module-flat-volume-symdef.h"
-
-PA_MODULE_AUTHOR("Marc-Andre Lureau");
-PA_MODULE_DESCRIPTION("Flat volume");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE("");
-
-struct userdata {
- pa_subscription *subscription;
- pa_hook_slot *sink_input_set_volume_hook_slot;
- pa_hook_slot *sink_input_fixate_hook_slot;
-};
-
-static void process_input_volume_change(
- pa_cvolume *dest_volume,
- const pa_cvolume *dest_virtual_volume,
- pa_channel_map *dest_channel_map,
- pa_sink_input *this,
- pa_sink *sink) {
-
- pa_sink_input *i;
- uint32_t idx;
- pa_cvolume max_volume, sink_volume;
-
- pa_assert(dest_volume);
- pa_assert(dest_virtual_volume);
- pa_assert(dest_channel_map);
- pa_assert(sink);
-
- if (!(sink->flags & PA_SINK_DECIBEL_VOLUME))
- return;
-
- pa_log_debug("Sink input volume changed");
-
- max_volume = *dest_virtual_volume;
- pa_cvolume_remap(&max_volume, dest_channel_map, &sink->channel_map);
-
- for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) {
- /* skip this sink-input if we are processing a volume change request */
- if (this && this == i)
- continue;
-
- if (pa_cvolume_max(&i->virtual_volume) > pa_cvolume_max(&max_volume)) {
- max_volume = i->virtual_volume;
- pa_cvolume_remap(&max_volume, &i->channel_map, &sink->channel_map);
- }
- }
-
- /* Set the master volume, and normalize inputs */
- if (!pa_cvolume_equal(&max_volume, &sink->volume)) {
-
- pa_sink_set_volume(sink, &max_volume);
-
- pa_log_debug("sink = %.2f (changed)", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM);
-
- /* Now, normalize each of the internal volume (client sink-input volume / sink master volume) */
- for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) {
- /* skip this sink-input if we are processing a volume change request */
- if (this && this == i)
- continue;
-
- sink_volume = max_volume;
- pa_cvolume_remap(&sink_volume, &sink->channel_map, &i->channel_map);
- pa_sw_cvolume_divide(&i->volume, &i->virtual_volume, &sink_volume);
- pa_log_debug("sink input { id = %d, flat = %.2f, true = %.2f }",
- i->index,
- (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM,
- (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM);
- pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, &i->volume, 1), 0, NULL, pa_xfree);
- }
- } else
- pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM);
-
- /* and this one */
-
- sink_volume = max_volume;
- pa_cvolume_remap(&sink_volume, &sink->channel_map, dest_channel_map);
- pa_sw_cvolume_divide(dest_volume, dest_virtual_volume, &sink_volume);
- pa_log_debug("caller sink input: { id = %d, flat = %.2f, true = %.2f }",
- this ? (int)this->index : -1,
- (double)pa_cvolume_avg(dest_virtual_volume)/PA_VOLUME_NORM,
- (double)pa_cvolume_avg(dest_volume)/PA_VOLUME_NORM);
-}
-
-static pa_hook_result_t sink_input_set_volume_hook_callback(pa_core *c, pa_sink_input_set_volume_data *this, struct userdata *u) {
- pa_assert(this);
- pa_assert(this->sink_input);
-
- process_input_volume_change(&this->volume, &this->virtual_volume, &this->sink_input->channel_map,
- this->sink_input, this->sink_input->sink);
-
- return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *this, struct userdata *u) {
- pa_assert(this);
- pa_assert(this->sink);
-
- process_input_volume_change(&this->volume, &this->virtual_volume, &this->channel_map,
- NULL, this->sink);
-
- return PA_HOOK_OK;
-}
-
-static void subscribe_callback(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
- struct userdata *u = userdata;
- pa_sink *sink;
- pa_sink_input *i;
- uint32_t iidx;
- pa_cvolume sink_volume;
-
- pa_assert(core);
- pa_assert(u);
-
- if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
- t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE))
- return;
-
- if (!(sink = pa_idxset_get_by_index(core->sinks, idx)))
- return;
-
- if (!(sink->flags & PA_SINK_DECIBEL_VOLUME))
- return;
-
- pa_log_debug("Sink volume changed");
- pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)) / PA_VOLUME_NORM);
-
- sink_volume = *pa_sink_get_volume(sink, FALSE);
-
- for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &iidx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &iidx))) {
- pa_cvolume si_volume;
-
- si_volume = sink_volume;
- pa_cvolume_remap(&si_volume, &sink->channel_map, &i->channel_map);
- pa_sw_cvolume_multiply(&i->virtual_volume, &i->volume, &si_volume);
- pa_log_debug("sink input = { id = %d, flat = %.2f, true = %.2f }",
- i->index,
- (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM,
- (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- }
-}
-
-int pa__init(pa_module*m) {
- struct userdata *u;
-
- pa_assert(m);
-
- u = pa_xnew(struct userdata, 1);
- m->userdata = u;
-
- u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
- u->sink_input_set_volume_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_set_volume_hook_callback, u);
-
- u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK, subscribe_callback, u);
-
- return 0;
-}
-
-void pa__done(pa_module*m) {
- struct userdata* u;
-
- pa_assert(m);
-
- if (!(u = m->userdata))
- return;
-
- if (u->subscription)
- pa_subscription_free(u->subscription);
-
- if (u->sink_input_set_volume_hook_slot)
- pa_hook_slot_free(u->sink_input_set_volume_hook_slot);
- if (u->sink_input_fixate_hook_slot)
- pa_hook_slot_free(u->sink_input_fixate_hook_slot);
-
- pa_xfree(u);
-}
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index 8c1ab329..d3b351a4 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -66,9 +66,9 @@ PA_MODULE_USAGE("api=<oss>");
#endif
struct device {
- uint32_t index;
- char *udi;
- char *sink_name, *source_name;
+ char *udi, *originating_udi;
+ char *card_name, *sink_name, *source_name;
+ uint32_t module;
pa_bool_t acl_race_fix;
};
@@ -76,18 +76,13 @@ struct userdata {
pa_core *core;
LibHalContext *context;
pa_dbus_connection *connection;
- pa_hashmap *devices;
+ pa_hashmap *devices; /* Every entry is indexed twice in this table: by the udi we found the device with and by the originating device's udi */
const char *capability;
#ifdef HAVE_ALSA
pa_bool_t use_tsched;
#endif
};
-struct timerdata {
- struct userdata *u;
- char *udi;
-};
-
#define CAPABILITY_ALSA "alsa"
#define CAPABILITY_OSS "oss"
@@ -99,22 +94,22 @@ static const char* const valid_modargs[] = {
NULL
};
-static void hal_device_free(struct device* d) {
+static void device_free(struct device* d) {
pa_assert(d);
pa_xfree(d->udi);
+ pa_xfree(d->originating_udi);
pa_xfree(d->sink_name);
pa_xfree(d->source_name);
+ pa_xfree(d->card_name);
pa_xfree(d);
}
-static void hal_device_free_cb(void *d, void *data) {
- hal_device_free(d);
-}
-
static const char *strip_udi(const char *udi) {
const char *slash;
+ pa_assert(udi);
+
if ((slash = strrchr(udi, '/')))
return slash+1;
@@ -123,405 +118,380 @@ static const char *strip_udi(const char *udi) {
#ifdef HAVE_ALSA
-typedef enum {
- ALSA_TYPE_SINK,
- ALSA_TYPE_SOURCE,
- ALSA_TYPE_OTHER,
- ALSA_TYPE_MAX
-} alsa_type_t;
+enum alsa_type {
+ ALSA_TYPE_PLAYBACK,
+ ALSA_TYPE_CAPTURE,
+ ALSA_TYPE_OTHER
+};
-static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *udi, DBusError *error) {
+static enum alsa_type hal_alsa_device_get_type(LibHalContext *context, const char *udi) {
char *type;
- alsa_type_t t;
+ enum alsa_type t = ALSA_TYPE_OTHER;
+ DBusError error;
+
+ dbus_error_init(&error);
- if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", error)))
- return ALSA_TYPE_OTHER;
+ pa_assert(context);
+ pa_assert(udi);
- if (!strcmp(type, "playback"))
- t = ALSA_TYPE_SINK;
- else if (!strcmp(type, "capture"))
- t = ALSA_TYPE_SOURCE;
- else
- t = ALSA_TYPE_OTHER;
+ if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", &error)))
+ goto finish;
+
+ if (pa_streq(type, "playback"))
+ t = ALSA_TYPE_PLAYBACK;
+ else if (pa_streq(type, "capture"))
+ t = ALSA_TYPE_CAPTURE;
libhal_free_string(type);
+finish:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ }
+
return t;
}
-static int hal_alsa_device_is_modem(LibHalContext *context, const char *udi, DBusError *error) {
+static pa_bool_t hal_alsa_device_is_modem(LibHalContext *context, const char *udi) {
char *class;
- int r;
+ pa_bool_t r = FALSE;
+ DBusError error;
- if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", error)))
- return 0;
+ dbus_error_init(&error);
+
+ pa_assert(context);
+ pa_assert(udi);
+
+ if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", &error)))
+ goto finish;
+
+ r = pa_streq(class, "modem");
+ libhal_free_string(class);
- r = strcmp(class, "modem") == 0;
- pa_xfree(class);
+finish:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ }
return r;
}
-static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
- char *args;
- alsa_type_t type;
+static int hal_device_load_alsa(struct userdata *u, const char *udi, struct device *d) {
+ enum alsa_type type;
int device, card;
- const char *module_name;
DBusError error;
pa_module *m;
+ char *args, *originating_udi = NULL, *card_name = NULL;
dbus_error_init(&error);
pa_assert(u);
- pa_assert(sink_name);
- pa_assert(source_name);
+ pa_assert(udi);
+ pa_assert(d);
- *sink_name = *source_name = NULL;
+ /* We only care for PCM devices */
+ type = hal_alsa_device_get_type(u->context, udi);
+ if (type == ALSA_TYPE_OTHER)
+ goto fail;
- type = hal_alsa_device_get_type(u->context, udi, &error);
- if (dbus_error_is_set(&error) || type == ALSA_TYPE_OTHER)
+ /* We don't care for modems */
+ if (hal_alsa_device_is_modem(u->context, udi))
goto fail;
+ /* We only care for the main device */
device = libhal_device_get_property_int(u->context, udi, "alsa.device", &error);
if (dbus_error_is_set(&error) || device != 0)
goto fail;
- card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
- if (dbus_error_is_set(&error))
+ /* We store only one entry per card, hence we look for the originating device */
+ originating_udi = libhal_device_get_property_string(u->context, udi, "alsa.originating_device", &error);
+ if (dbus_error_is_set(&error) || !originating_udi)
goto fail;
- if (hal_alsa_device_is_modem(u->context, udi, &error))
+ /* Make sure we only load one module per card */
+ if (pa_hashmap_get(u->devices, originating_udi))
goto fail;
- if (type == ALSA_TYPE_SINK) {
- *sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
-
- module_name = "module-alsa-sink";
- args = pa_sprintf_malloc("device_id=%u sink_name=%s tsched=%i", card, *sink_name, (int) u->use_tsched);
- } else {
- *source_name = pa_sprintf_malloc("alsa_input.%s", strip_udi(udi));
-
- module_name = "module-alsa-source";
- args = pa_sprintf_malloc("device_id=%u source_name=%s tsched=%i", card, *source_name, (int) u->use_tsched);
- }
-
- pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+ /* We need the identifier */
+ card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
+ if (dbus_error_is_set(&error))
+ goto fail;
- m = pa_module_load(u->core, module_name, args);
+ card_name = pa_sprintf_malloc("alsa_card.%s", strip_udi(originating_udi));
+ args = pa_sprintf_malloc("device_id=%u name=%s card_name=%s tsched=%i", card, strip_udi(originating_udi), 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_xfree(*sink_name);
- pa_xfree(*source_name);
- *sink_name = *source_name = NULL;
- }
+ if (!m)
+ goto fail;
+
+ d->originating_udi = originating_udi;
+ d->module = m->index;
+ d->card_name = card_name;
- return m;
+ return 0;
fail:
if (dbus_error_is_set(&error)) {
- pa_log_error("D-Bus error while parsing ALSA data: %s: %s", error.name, error.message);
+ pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
dbus_error_free(&error);
}
- return NULL;
+ pa_xfree(originating_udi);
+ pa_xfree(card_name);
+
+ return -1;
}
#endif
#ifdef HAVE_OSS
-static int hal_oss_device_is_pcm(LibHalContext *context, const char *udi, DBusError *error) {
+static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi) {
char *class = NULL, *dev = NULL, *e;
int device;
- int r = 0;
+ pa_bool_t r = FALSE;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ pa_assert(context);
+ pa_assert(udi);
- class = libhal_device_get_property_string(context, udi, "oss.type", error);
- if (dbus_error_is_set(error) || !class)
+ /* We only care for PCM devices */
+ class = libhal_device_get_property_string(context, udi, "oss.type", &error);
+ if (dbus_error_is_set(&error) || !class)
goto finish;
- if (strcmp(class, "pcm"))
+ if (!pa_streq(class, "pcm"))
goto finish;
- dev = libhal_device_get_property_string(context, udi, "oss.device_file", error);
- if (dbus_error_is_set(error) || !dev)
+ /* We don't like /dev/audio */
+ dev = libhal_device_get_property_string(context, udi, "oss.device_file", &error);
+ if (dbus_error_is_set(&error) || !dev)
goto finish;
if ((e = strrchr(dev, '/')))
if (pa_startswith(e + 1, "audio"))
goto finish;
- device = libhal_device_get_property_int(context, udi, "oss.device", error);
- if (dbus_error_is_set(error) || device != 0)
+ /* 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)
goto finish;
- r = 1;
+ r = TRUE;
finish:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL OSS data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ }
+
libhal_free_string(class);
libhal_free_string(dev);
return r;
}
-static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
- char* args;
- char* device;
+static int hal_device_load_oss(struct userdata *u, const char *udi, struct device *d) {
DBusError error;
pa_module *m;
+ char *args, *originating_udi = NULL, *device, *sink_name = NULL, *source_name = NULL;
dbus_error_init(&error);
pa_assert(u);
- pa_assert(sink_name);
- pa_assert(source_name);
+ pa_assert(udi);
+ pa_assert(d);
+
+ /* We only care for OSS PCM devices */
+ if (!hal_oss_device_is_pcm(u->context, udi))
+ goto fail;
- *sink_name = *source_name = NULL;
+ /* We store only one entry per card, hence we look for the originating device */
+ originating_udi = libhal_device_get_property_string(u->context, udi, "oss.originating_device", &error);
+ if (dbus_error_is_set(&error) || !originating_udi)
+ goto fail;
- if (!hal_oss_device_is_pcm(u->context, udi, &error) || dbus_error_is_set(&error))
+ /* Make sure we only load one module per card */
+ if (pa_hashmap_get(u->devices, originating_udi))
goto fail;
+ /* We need the device file */
device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
if (!device || dbus_error_is_set(&error))
goto fail;
- *sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
- *source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+ sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
+ source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+ args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, sink_name, source_name);
- args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, *sink_name, *source_name);
libhal_free_string(device);
pa_log_debug("Loading module-oss with arguments '%s'", args);
m = pa_module_load(u->core, "module-oss", args);
pa_xfree(args);
- if (!m) {
- pa_xfree(*sink_name);
- pa_xfree(*source_name);
- *sink_name = *source_name = NULL;
- }
+ if (!m)
+ goto fail;
+
+ d->originating_udi = originating_udi;
+ d->module = m->index;
+ d->sink_name = sink_name;
+ d->source_name = source_name;
- return m;
+ return 0;
fail:
if (dbus_error_is_set(&error)) {
- pa_log_error("D-Bus error while parsing OSS data: %s: %s", error.name, error.message);
+ pa_log_error("D-Bus error while parsing OSS HAL data: %s: %s", error.name, error.message);
dbus_error_free(&error);
}
- return NULL;
+ pa_xfree(originating_udi);
+ pa_xfree(source_name);
+ pa_xfree(sink_name);
+
+ return -1;
}
#endif
static struct device* hal_device_add(struct userdata *u, const char *udi) {
- pa_module* m = NULL;
struct device *d;
- char *sink_name = NULL, *source_name = NULL;
+ int r;
pa_assert(u);
pa_assert(u->capability);
- pa_assert(!pa_hashmap_get(u->devices, udi));
+
+ d = pa_xnew(struct device, 1);
+ d->acl_race_fix = FALSE;
+ d->udi = pa_xstrdup(udi);
+ d->originating_udi = NULL;
+ d->module = PA_INVALID_INDEX;
+ d->sink_name = d->source_name = d->card_name = NULL;
#ifdef HAVE_ALSA
- if (strcmp(u->capability, CAPABILITY_ALSA) == 0)
- m = hal_device_load_alsa(u, udi, &sink_name, &source_name);
+ if (pa_streq(u->capability, CAPABILITY_ALSA))
+ r = hal_device_load_alsa(u, udi, d);
#endif
#ifdef HAVE_OSS
- if (strcmp(u->capability, CAPABILITY_OSS) == 0)
- m = hal_device_load_oss(u, udi, &sink_name, &source_name);
+ if (pa_streq(u->capability, CAPABILITY_OSS))
+ r = hal_device_load_oss(u, udi, d);
#endif
- if (!m)
+ if (r < 0) {
+ device_free(d);
return NULL;
+ }
- d = pa_xnew(struct device, 1);
- d->acl_race_fix = FALSE;
- d->udi = pa_xstrdup(udi);
- d->index = m->index;
- d->sink_name = sink_name;
- d->source_name = source_name;
pa_hashmap_put(u->devices, d->udi, d);
+ pa_hashmap_put(u->devices, d->originating_udi, d);
return d;
}
-static int hal_device_add_all(struct userdata *u, const char *capability) {
- DBusError error;
- int i, n, count = 0;
+static int hal_device_add_all(struct userdata *u) {
+ int n, count = 0;
char** udis;
-
- pa_assert(u);
+ DBusError error;
dbus_error_init(&error);
- if (u->capability && strcmp(u->capability, capability) != 0)
- return 0;
-
- pa_log_info("Trying capability %s", capability);
+ pa_assert(u);
- udis = libhal_find_device_by_capability(u->context, capability, &n, &error);
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error finding devices: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- return -1;
- }
+ udis = libhal_find_device_by_capability(u->context, u->capability, &n, &error);
+ if (dbus_error_is_set(&error) || !udis)
+ goto fail;
if (n > 0) {
- u->capability = capability;
+ int i;
for (i = 0; i < n; i++) {
struct device *d;
- if (!(d = hal_device_add(u, udis[i])))
- pa_log_debug("Not loaded device %s", udis[i]);
- else {
- if (d->sink_name)
- pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+ if ((d = hal_device_add(u, udis[i])))
count++;
- }
+ else
+ pa_log_debug("Not loaded device %s", udis[i]);
}
}
libhal_free_string_array(udis);
- return count;
-}
-
-static dbus_bool_t device_has_capability(LibHalContext *context, const char *udi, const char* cap, DBusError *error){
- dbus_bool_t has_prop;
-
- has_prop = libhal_device_property_exists(context, udi, "info.capabilities", error);
- if (!has_prop || dbus_error_is_set(error))
- return FALSE;
- return libhal_device_query_capability(context, udi, cap, error);
-}
-
-static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const struct timeval *tv, void *userdata) {
- DBusError error;
- struct timerdata *td = userdata;
-
- dbus_error_init(&error);
-
- if (!pa_hashmap_get(td->u->devices, td->udi)) {
- dbus_bool_t b;
- struct device *d;
+ return count;
- b = libhal_device_exists(td->u->context, td->udi, &error);
-
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error adding device: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- } else if (b) {
- if (!(d = hal_device_add(td->u, td->udi)))
- pa_log_debug("Not loaded device %s", td->udi);
- else {
- if (d->sink_name)
- pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
- }
- }
+fail:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
}
- pa_xfree(td->udi);
- pa_xfree(td);
- ea->time_free(ev);
+ return -1;
}
static void device_added_cb(LibHalContext *context, const char *udi) {
DBusError error;
- struct timeval tv;
- struct timerdata *t;
struct userdata *u;
pa_bool_t good = FALSE;
- pa_assert_se(u = libhal_ctx_get_user_data(context));
-
- if (pa_hashmap_get(u->devices, udi))
- return;
-
- pa_log_debug("HAL Device added: %s", udi);
-
dbus_error_init(&error);
- if (u->capability) {
-
- good = device_has_capability(context, udi, u->capability, &error);
-
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error getting capability: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- return;
- }
-
- } else {
-
-#ifdef HAVE_ALSA
- good = device_has_capability(context, udi, CAPABILITY_ALSA, &error);
-
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error getting capability: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- return;
- }
+ pa_assert(context);
+ pa_assert(udi);
- if (good)
- u->capability = CAPABILITY_ALSA;
-#endif
-#if defined(HAVE_OSS) && defined(HAVE_ALSA)
- if (!good) {
-#endif
-#ifdef HAS_OSS
- good = device_has_capability(context, udi, CAPABILITY_OSS, &error);
+ pa_assert_se(u = libhal_ctx_get_user_data(context));
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error getting capability: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- return;
- }
+ good = libhal_device_query_capability(context, udi, u->capability, &error);
+ if (dbus_error_is_set(&error) || !good)
+ goto finish;
- if (good)
- u->capability = CAPABILITY_OSS;
+ if (!hal_device_add(u, udi))
+ pa_log_debug("Not loaded device %s", udi);
-#endif
-#if defined(HAVE_OSS) && defined(HAVE_ALSA)
- }
-#endif
+finish:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
}
-
- if (!good)
- return;
-
- /* actually add the device 1/2 second later */
- t = pa_xnew(struct timerdata, 1);
- t->u = u;
- t->udi = pa_xstrdup(udi);
-
- pa_gettimeofday(&tv);
- pa_timeval_add(&tv, 500000);
- u->core->mainloop->time_new(u->core->mainloop, &tv, device_added_time_cb, t);
}
static void device_removed_cb(LibHalContext* context, const char *udi) {
struct device *d;
struct userdata *u;
+ pa_assert(context);
+ pa_assert(udi);
+
pa_assert_se(u = libhal_ctx_get_user_data(context));
- pa_log_debug("Device removed: %s", udi);
+ if (!(d = pa_hashmap_get(u->devices, udi)))
+ return;
+
+ pa_hashmap_remove(u->devices, d->originating_udi);
+ pa_hashmap_remove(u->devices, d->udi);
- if ((d = pa_hashmap_remove(u->devices, udi))) {
- pa_module_unload_request_by_index(u->core, d->index, TRUE);
- hal_device_free(d);
- }
+ pa_log_debug("Removing HAL device: %s", d->originating_udi);
+
+ pa_module_unload_request_by_index(u->core, d->module, TRUE);
+ device_free(d);
}
static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
struct userdata *u;
+ pa_assert(context);
+ pa_assert(udi);
+ pa_assert(capability);
+
pa_assert_se(u = libhal_ctx_get_user_data(context));
- if (!u->capability || strcmp(u->capability, capability) == 0)
+ if (pa_streq(u->capability, capability))
/* capability we care about, pretend it's a new device */
device_added_cb(context, udi);
}
@@ -529,20 +499,24 @@ static void new_capability_cb(LibHalContext *context, const char *udi, const cha
static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
struct userdata *u;
+ pa_assert(context);
+ pa_assert(udi);
+ pa_assert(capability);
+
pa_assert_se(u = libhal_ctx_get_user_data(context));
- if (u->capability && strcmp(u->capability, capability) == 0)
+ if (pa_streq(u->capability, capability))
/* capability we care about, pretend it was removed */
device_removed_cb(context, udi);
}
static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
- struct userdata*u = userdata;
+ struct userdata*u;
DBusError error;
pa_assert(bus);
pa_assert(message);
- pa_assert(u);
+ pa_assert_se(u = userdata);
dbus_error_init(&error);
@@ -554,13 +528,14 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
uint32_t uid;
- int suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
+ pa_bool_t suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
goto finish;
}
+ /* Check if this is about us? */
if (uid == getuid() || uid == geteuid()) {
struct device *d;
const char *udi;
@@ -569,46 +544,44 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
if ((d = pa_hashmap_get(u->devices, udi))) {
pa_bool_t send_acl_race_fix_message = FALSE;
-
d->acl_race_fix = FALSE;
if (d->sink_name) {
pa_sink *sink;
- if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
- int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
-
- if (prev_suspended && !suspend) {
- /* resume */
- if (pa_sink_suspend(sink, 0) >= 0)
- pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
- else
- d->acl_race_fix = TRUE;
-
- } else if (!prev_suspended && suspend) {
- /* suspend */
- if (pa_sink_suspend(sink, 1) >= 0)
- send_acl_race_fix_message = TRUE;
- }
+ if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
+ pa_bool_t success = pa_sink_suspend(sink, suspend) >= 0;
+
+ if (!success && !suspend)
+ d->acl_race_fix = TRUE; /* resume failed, let's try again */
+ else if (suspend)
+ send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
}
}
if (d->source_name) {
pa_source *source;
- if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
- int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+ if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
+ pa_bool_t success = pa_source_suspend(source, suspend) >= 0;
- if (prev_suspended && !suspend) {
- /* resume */
- if (pa_source_suspend(source, 0) < 0)
- d->acl_race_fix = TRUE;
+ if (!success && !suspend)
+ d->acl_race_fix = TRUE; /* resume failed, let's try again */
+ else if (suspend)
+ send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
+ }
+ }
- } else if (!prev_suspended && suspend) {
- /* suspend */
- if (pa_source_suspend(source, 0) >= 0)
- send_acl_race_fix_message = TRUE;
- }
+ if (d->card_name) {
+ pa_card *card;
+
+ if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
+ pa_bool_t success = pa_card_suspend(card, suspend) >= 0;
+
+ if (!success && !suspend)
+ d->acl_race_fix = TRUE; /* resume failed, let's try again */
+ else if (suspend)
+ send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
}
}
@@ -621,6 +594,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
} else if (!suspend)
device_added_cb(u->context, udi);
+
}
return DBUS_HANDLER_RESULT_HANDLED;
@@ -631,40 +605,36 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
sever has closed the device. We can remove this as
soon as HAL learns frevoke() */
- const char *udi;
struct device *d;
+ const char *udi;
udi = dbus_message_get_path(message);
- if ((d = pa_hashmap_get(u->devices, udi)) && d->acl_race_fix) {
- pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
-
- d->acl_race_fix = FALSE;
+ if ((d = pa_hashmap_get(u->devices, udi))) {
- if (d->sink_name) {
- pa_sink *sink;
-
- if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
+ if (d->acl_race_fix) {
+ d->acl_race_fix = FALSE;
+ pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
- int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
+ if (d->sink_name) {
+ pa_sink *sink;
- if (prev_suspended) {
- /* resume */
- if (pa_sink_suspend(sink, 0) >= 0)
- pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
- }
+ if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK)))
+ pa_sink_suspend(sink, FALSE);
}
- }
- if (d->source_name) {
- pa_source *source;
+ if (d->source_name) {
+ pa_source *source;
- if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
+ if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE)))
+ pa_source_suspend(source, FALSE);
+ }
- int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+ if (d->card_name) {
+ pa_card *card;
- if (prev_suspended)
- pa_source_suspend(source, 0);
+ if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD)))
+ pa_card_suspend(card, FALSE);
}
}
@@ -692,18 +662,20 @@ static void hal_context_free(LibHalContext* hal_context) {
dbus_error_free(&error);
}
-static LibHalContext* hal_context_new(pa_core* c, DBusConnection *conn) {
+static LibHalContext* hal_context_new(DBusConnection *connection) {
DBusError error;
LibHalContext *hal_context = NULL;
dbus_error_init(&error);
+ pa_assert(connection);
+
if (!(hal_context = libhal_ctx_new())) {
pa_log_error("libhal_ctx_new() failed");
goto fail;
}
- if (!libhal_ctx_set_dbus_connection(hal_context, conn)) {
+ if (!libhal_ctx_set_dbus_connection(hal_context, connection)) {
pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
goto fail;
}
@@ -726,13 +698,10 @@ fail:
int pa__init(pa_module*m) {
DBusError error;
- pa_dbus_connection *conn;
struct userdata *u = NULL;
- LibHalContext *hal_context = NULL;
int n = 0;
pa_modargs *ma;
const char *api;
- pa_bool_t use_tsched = TRUE;
pa_assert(m);
@@ -743,90 +712,74 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->context = NULL;
+ u->connection = NULL;
+ u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ u->capability = NULL;
+
+#ifdef HAVE_ALSA
+ u->use_tsched = TRUE;
+
+ if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) {
pa_log("Failed to parse tsched argument.");
goto fail;
}
- if ((api = pa_modargs_get_value(ma, "api", NULL))) {
- pa_bool_t good = FALSE;
+ api = pa_modargs_get_value(ma, "api", "alsa");
-#ifdef HAVE_ALSA
- if (strcmp(api, CAPABILITY_ALSA) == 0) {
- good = TRUE;
- api = CAPABILITY_ALSA;
- }
+ if (pa_streq(api, "alsa"))
+ u->capability = CAPABILITY_ALSA;
+#else
+ api = pa_modargs_get_value(ma, "api", "oss");
#endif
+
#ifdef HAVE_OSS
- if (strcmp(api, CAPABILITY_OSS) == 0) {
- good = TRUE;
- api = CAPABILITY_OSS;
- }
+ if (pa_streq(api, "oss"))
+ u->capability = CAPABILITY_OSS;
#endif
- if (!good) {
- pa_log_error("Invalid API specification.");
- goto fail;
- }
+ if (!u->capability) {
+ pa_log_error("Invalid API specification.");
+ goto fail;
}
- if (!(conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
- if (conn)
- pa_dbus_connection_unref(conn);
+ 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;
}
- if (!(hal_context = hal_context_new(m->core, pa_dbus_connection_get(conn)))) {
+ if (!(u->context = hal_context_new(pa_dbus_connection_get(u->connection)))) {
/* pa_hal_context_new() logs appropriate errors */
- pa_dbus_connection_unref(conn);
goto fail;
}
- u = pa_xnew(struct userdata, 1);
- u->core = m->core;
- u->context = hal_context;
- u->connection = conn;
- u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- u->capability = api;
-#ifdef HAVE_ALSA
- u->use_tsched = use_tsched;
-#endif
- m->userdata = u;
-
-#ifdef HAVE_ALSA
- n = hal_device_add_all(u, CAPABILITY_ALSA);
-#endif
-#if defined(HAVE_ALSA) && defined(HAVE_OSS)
- if (n <= 0)
-#endif
-#ifdef HAVE_OSS
- n += hal_device_add_all(u, CAPABILITY_OSS);
-#endif
+ n = hal_device_add_all(u);
- libhal_ctx_set_user_data(hal_context, u);
- libhal_ctx_set_device_added(hal_context, device_added_cb);
- libhal_ctx_set_device_removed(hal_context, device_removed_cb);
- libhal_ctx_set_device_new_capability(hal_context, new_capability_cb);
- libhal_ctx_set_device_lost_capability(hal_context, lost_capability_cb);
+ libhal_ctx_set_user_data(u->context, u);
+ libhal_ctx_set_device_added(u->context, device_added_cb);
+ libhal_ctx_set_device_removed(u->context, device_removed_cb);
+ libhal_ctx_set_device_new_capability(u->context, new_capability_cb);
+ libhal_ctx_set_device_lost_capability(u->context, lost_capability_cb);
- if (!libhal_device_property_watch_all(hal_context, &error)) {
+ if (!libhal_device_property_watch_all(u->context, &error)) {
pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
goto fail;
}
- if (!dbus_connection_add_filter(pa_dbus_connection_get(conn), filter_cb, u, NULL)) {
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) {
pa_log_error("Failed to add filter function");
goto fail;
}
- dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
+ dbus_bus_add_match(pa_dbus_connection_get(u->connection), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
if (dbus_error_is_set(&error)) {
pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
goto fail;
}
- dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',interface='org.pulseaudio.Server'", &error);
+ dbus_bus_add_match(pa_dbus_connection_get(u->connection), "type='signal',interface='org.pulseaudio.Server'", &error);
if (dbus_error_is_set(&error)) {
pa_log_error("Unable to subscribe to PulseAudio signals: %s: %s", error.name, error.message);
goto fail;
@@ -848,7 +801,6 @@ fail:
return -1;
}
-
void pa__done(pa_module *m) {
struct userdata *u;
@@ -860,8 +812,17 @@ void pa__done(pa_module *m) {
if (u->context)
hal_context_free(u->context);
- if (u->devices)
- pa_hashmap_free(u->devices, hal_device_free_cb, NULL);
+ if (u->devices) {
+ struct device *d;
+
+ while ((d = pa_hashmap_first(u->devices))) {
+ pa_hashmap_remove(u->devices, d->udi);
+ pa_hashmap_remove(u->devices, d->originating_udi);
+ device_free(d);
+ }
+
+ pa_hashmap_free(u->devices, NULL, NULL);
+ }
if (u->connection) {
DBusError error;
diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c
index 555cb825..b448e84e 100644
--- a/src/modules/module-jack-sink.c
+++ b/src/modules/module-jack-sink.c
@@ -430,6 +430,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c
index 9eccbbfa..0c7ee535 100644
--- a/src/modules/module-jack-source.c
+++ b/src/modules/module-jack-source.c
@@ -398,6 +398,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_source_linked_by(u->source);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
pa_assert(m);
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index a27ed712..e746f342 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -144,7 +144,7 @@ static void sink_request_rewind(pa_sink *s) {
pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */
- pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE);
+ 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 */
@@ -355,7 +355,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT) {
pa_log_debug("Requesting rewind due to state change.");
- pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
}
@@ -396,7 +396,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+ if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) {
pa_log("Master sink not found");
goto fail;
}
@@ -770,6 +770,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
unsigned c;
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
index 97e97dc7..9a782cac 100644
--- a/src/modules/module-lirc.c
+++ b/src/modules/module-lirc.c
@@ -118,7 +118,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
else {
pa_sink *s;
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+ 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;
@@ -135,7 +135,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_NORM;
}
- pa_sink_set_volume(s, &cv);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case DOWN:
@@ -146,7 +146,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MUTED;
}
- pa_sink_set_volume(s, &cv);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case MUTE:
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 769a6b59..cbf62687 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -48,7 +48,8 @@ PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Playback stream expression matching module");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE("table=<filename>");
+PA_MODULE_USAGE("table=<filename> "
+ "key=<property_key>");
#define WHITESPACE "\n\r \t"
@@ -57,17 +58,20 @@ PA_MODULE_USAGE("table=<filename>");
static const char* const valid_modargs[] = {
"table",
+ "key",
NULL,
};
struct rule {
regex_t regex;
pa_volume_t volume;
+ pa_proplist *proplist;
struct rule *next;
};
struct userdata {
struct rule *rules;
+ char *property_key;
pa_subscription *subscription;
};
@@ -95,11 +99,12 @@ static int load_rules(struct userdata *u, const char *filename) {
while (!feof(f)) {
char *d, *v;
- pa_volume_t volume;
+ pa_volume_t volume = PA_VOLUME_NORM;
uint32_t k;
regex_t regex;
char ln[256];
struct rule *rule;
+ pa_proplist *proplist = NULL;
if (!fgets(ln, sizeof(ln), f))
break;
@@ -121,14 +126,33 @@ static int load_rules(struct userdata *u, const char *filename) {
}
*d = 0;
- if (pa_atou(v, &k) < 0) {
- pa_log("[%s:%u] failed to parse volume", filename, n);
- goto finish;
+ if (pa_atou(v, &k) >= 0) {
+ volume = (pa_volume_t) k;
+ } else if (*v == '"') {
+ char *e;
+
+ e = strchr(v+1, '"');
+ if (!e) {
+ pa_log(__FILE__ ": [%s:%u] failed to parse line - missing role closing quote", filename, n);
+ goto finish;
+ }
+
+ *e = '\0';
+ e = pa_sprintf_malloc("media.role=\"%s\"", v+1);
+ proplist = pa_proplist_from_string(e);
+ pa_xfree(e);
+ } else {
+ char *e;
+
+ e = v+strspn(v, WHITESPACE);
+ if (!*e) {
+ pa_log(__FILE__ ": [%s:%u] failed to parse line - missing end of property list", filename, n);
+ goto finish;
+ }
+ *e = '\0';
+ proplist = pa_proplist_from_string(v);
}
- volume = (pa_volume_t) k;
-
-
if (regcomp(&regex, ln, REG_EXTENDED|REG_NOSUB) != 0) {
pa_log("[%s:%u] invalid regular expression", filename, n);
goto finish;
@@ -136,6 +160,7 @@ static int load_rules(struct userdata *u, const char *filename) {
rule = pa_xnew(struct rule, 1);
rule->regex = regex;
+ rule->proplist = proplist;
rule->volume = volume;
rule->next = NULL;
@@ -177,15 +202,22 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
return;
- if (!(n = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)))
+ if (!(n = pa_proplist_gets(si->proplist, u->property_key)))
return;
+ pa_log_debug("Matching with %s", n);
+
for (r = u->rules; r; r = r->next) {
if (!regexec(&r->regex, n, 0, NULL, 0)) {
- pa_cvolume cv;
- pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
- pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
- pa_sink_input_set_volume(si, &cv);
+ if (r->proplist) {
+ pa_log_debug("updating proplist of sink input '%s'", n);
+ pa_proplist_update(si->proplist, PA_UPDATE_MERGE, r->proplist);
+ } else {
+ 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);
+ }
}
}
}
@@ -201,11 +233,14 @@ int pa__init(pa_module*m) {
goto fail;
}
+
u = pa_xnew(struct userdata, 1);
u->rules = NULL;
u->subscription = NULL;
m->userdata = u;
+ u->property_key = pa_xstrdup(pa_modargs_get_value(ma, "key", PA_PROP_MEDIA_NAME));
+
if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)
goto fail;
@@ -234,10 +269,15 @@ void pa__done(pa_module*m) {
if (u->subscription)
pa_subscription_free(u->subscription);
+ if (u->property_key)
+ pa_xfree(u->property_key);
+
for (r = u->rules; r; r = n) {
n = r->next;
regfree(&r->regex);
+ if (r->proplist)
+ pa_proplist_free(r->proplist);
pa_xfree(r);
}
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
index 21f176a4..a379923a 100644
--- a/src/modules/module-mmkbd-evdev.c
+++ b/src/modules/module-mmkbd-evdev.c
@@ -109,7 +109,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
if (volchange != INVALID) {
pa_sink *s;
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+ 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;
@@ -126,7 +126,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_NORM;
}
- pa_sink_set_volume(s, &cv);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case DOWN:
@@ -137,7 +137,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MUTED;
}
- pa_sink_set_volume(s, &cv);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case MUTE_TOGGLE:
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index daf9767c..570f8be4 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -324,6 +324,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 2b55c823..03e27170 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -315,6 +315,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index 77310cab..975090c2 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -302,6 +302,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_source_linked_by(u->source);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index 90e693a3..8e4f4c32 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -58,30 +58,9 @@ struct userdata {
pa_hook_slot *sink_input_fixate_hook_slot;
};
-static pa_bool_t is_left(pa_channel_position_t p) {
- return
- p == PA_CHANNEL_POSITION_FRONT_LEFT ||
- p == PA_CHANNEL_POSITION_REAR_LEFT ||
- p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
- p == PA_CHANNEL_POSITION_SIDE_LEFT ||
- p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
- p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
-}
-
-static pa_bool_t is_right(pa_channel_position_t p) {
- return
- p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
- p == PA_CHANNEL_POSITION_REAR_RIGHT||
- p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
- p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
- p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
- p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
-}
-
static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) {
const char *hpos;
double f;
- unsigned c;
char t[PA_CVOLUME_SNPRINT_MAX];
pa_assert(data);
@@ -101,23 +80,16 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i
pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
- if (!data->volume_is_set) {
- pa_cvolume_reset(&data->volume, data->sample_spec.channels);
- data->volume_is_set = TRUE;
+ if (!data->virtual_volume_is_set) {
+ pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
+ data->virtual_volume_is_set = TRUE;
+ data->virtual_volume_is_absolute = FALSE;
}
- for (c = 0; c < data->sample_spec.channels; c++) {
-
- if (is_left(data->channel_map.map[c]))
- data->volume.values[c] =
- pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f)));
-
- if (is_right(data->channel_map.map[c]))
- data->volume.values[c] =
- pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f));
- }
+ pa_cvolume_set_balance(&data->virtual_volume, &data->channel_map, f*2.0-1.0);
+ data->save_volume = FALSE;
- pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume));
+ pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->virtual_volume));
return PA_HOOK_OK;
}
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 62f0a73c..74ee6122 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -195,6 +195,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -207,7 +208,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec);
*((pa_usec_t*) data) = w > r ? w - r : 0;
- break;
+ return 0;
}
case SINK_MESSAGE_PASS_SOCKET: {
@@ -254,20 +255,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static int sink_get_volume_cb(pa_sink *s) {
+static void sink_get_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int i;
pa_assert(u);
- for (i = 0; i < s->sample_spec.channels; i++) {
- s->volume.values[i] = u->volume;
- }
-
- return 0;
+ for (i = 0; i < s->sample_spec.channels; i++)
+ s->virtual_volume.values[i] = u->volume;
}
-static int sink_set_volume_cb(pa_sink *s) {
+static void sink_set_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int rv;
@@ -275,39 +273,34 @@ static int sink_set_volume_cb(pa_sink *s) {
/* If we're muted, we fake it */
if (u->muted)
- return 0;
+ return;
pa_assert(s->sample_spec.channels > 0);
/* Avoid pointless volume sets */
- if (u->volume == s->volume.values[0])
- return 0;
+ if (u->volume == s->virtual_volume.values[0])
+ return;
- rv = pa_raop_client_set_volume(u->raop, s->volume.values[0]);
+ rv = pa_raop_client_set_volume(u->raop, s->virtual_volume.values[0]);
if (0 == rv)
- u->volume = s->volume.values[0];
-
- return rv;
+ u->volume = s->virtual_volume.values[0];
}
-static int sink_get_mute_cb(pa_sink *s) {
+static void sink_get_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
pa_assert(u);
s->muted = u->muted;
- return 0;
}
-static int sink_set_mute_cb(pa_sink *s) {
+static void sink_set_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
- int rv;
pa_assert(u);
- rv = pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
+ pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
u->muted = s->muted;
- return rv;
}
static void thread_func(void *userdata) {
@@ -627,6 +620,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
pa_assert(m);
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 976a8ce5..e17fef03 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -119,7 +119,7 @@ static void sink_request_rewind(pa_sink *s) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
- pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE);
+ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE, FALSE);
}
/* Called from I/O thread context */
@@ -270,7 +270,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT) {
pa_log_debug("Requesting rewind due to state change.");
- pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
}
@@ -302,7 +302,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+ if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) {
pa_log("Master sink not found");
goto fail;
}
@@ -415,6 +415,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index cc6717cb..e52e39c1 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -54,12 +54,16 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
pa_assert(c);
pa_assert(sink);
+ /* There's no point in doing anything if the core is shut down anyway */
+ if (c->state == PA_CORE_SHUTDOWN)
+ return PA_HOOK_OK;
+
if (!pa_idxset_size(sink->inputs)) {
pa_log_debug("No sink inputs to move away.");
return PA_HOOK_OK;
}
- if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0)) || target == sink) {
+ if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)) || target == sink) {
uint32_t idx;
for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
@@ -73,7 +77,7 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
}
while ((i = pa_idxset_first(sink->inputs, NULL))) {
- if (pa_sink_input_move_to(i, target) < 0) {
+ if (pa_sink_input_move_to(i, target, FALSE) < 0) {
pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
@@ -92,12 +96,16 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
pa_assert(c);
pa_assert(source);
+ /* There's no point in doing anything if the core is shut down anyway */
+ if (c->state == PA_CORE_SHUTDOWN)
+ return PA_HOOK_OK;
+
if (!pa_idxset_size(source->outputs)) {
pa_log_debug("No source outputs to move away.");
return PA_HOOK_OK;
}
- if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0)) || target == source) {
+ if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE)) || target == source) {
uint32_t idx;
for (target = pa_idxset_first(c->sources, &idx); target; target = pa_idxset_next(c->sources, &idx))
@@ -113,7 +121,7 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
pa_assert(target != source);
while ((o = pa_idxset_first(source->outputs, NULL))) {
- if (pa_source_output_move_to(o, target) < 0) {
+ if (pa_source_output_move_to(o, target, FALSE) < 0) {
pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c
index be95cc39..5626c2ab 100644
--- a/src/modules/module-sine-source.c
+++ b/src/modules/module-sine-source.c
@@ -96,9 +96,9 @@ static int source_process_msg(
switch (code) {
- case PA_SINK_MESSAGE_SET_STATE:
+ case PA_SOURCE_MESSAGE_SET_STATE:
- if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
+ if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING)
u->timestamp = pa_rtclock_usec();
break;
@@ -201,15 +201,6 @@ finish:
pa_log_debug("Thread shutting down");
}
-static void calc_sine(float *f, size_t l, double freq) {
- size_t i;
-
- l /= sizeof(float);
-
- for (i = 0; i < l; i++)
- *(f++) = (float) 0.5f * sin((double) i*M_PI*2*freq / (double) l);
-}
-
int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma;
@@ -295,6 +286,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_source_linked_by(u->source);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index 3b0dc352..b0782c3c 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -115,7 +115,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
* 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);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
int pa__init(pa_module*m) {
@@ -131,7 +131,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK, TRUE))) {
+ if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) {
pa_log("No such sink.");
goto fail;
}
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index fdf69a20..0c9bd4f9 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -56,6 +56,10 @@ PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Automatically restore the volume/mute/device state of streams");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "restore_device=<Save/restore sinks/sources?> "
+ "restore_volume=<Save/restore volumes?> "
+ "restore_muted=<Save/restore muted states?>");
#define SAVE_INTERVAL 10
@@ -87,10 +91,12 @@ struct userdata {
};
struct entry {
- char device[PA_NAME_MAX];
pa_channel_map channel_map;
- pa_cvolume volume;
+ char device[PA_NAME_MAX];
+ pa_cvolume relative_volume;
+ pa_cvolume absolute_volume;
pa_bool_t muted:1;
+ pa_bool_t device_valid:1, relative_volume_valid:1, absolute_volume_valid:1, muted_valid:1;
};
@@ -137,14 +143,14 @@ static char *get_name(pa_proplist *p, const char *prefix) {
return pa_sprintf_malloc("%s-fallback:%s", prefix, r);
}
-static struct entry* read_entry(struct userdata *u, char *name) {
+static struct entry* read_entry(struct userdata *u, const char *name) {
datum key, data;
struct entry *e;
pa_assert(u);
pa_assert(name);
- key.dptr = name;
+ key.dptr = (char*) name;
key.dsize = (int) strlen(name);
data = gdbm_fetch(u->gdbm_file, key);
@@ -153,7 +159,9 @@ static struct entry* read_entry(struct userdata *u, char *name) {
goto fail;
if (data.dsize != sizeof(struct entry)) {
- pa_log_warn("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ /* This is probably just a database upgrade, hence let's not
+ * consider this more than a debug message */
+ pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
goto fail;
}
@@ -164,18 +172,19 @@ static struct entry* read_entry(struct userdata *u, char *name) {
goto fail;
}
- if (!(pa_cvolume_valid(&e->volume))) {
- pa_log_warn("Invalid volume stored in database for stream %s", name);
+ if (!(pa_channel_map_valid(&e->channel_map))) {
+ pa_log_warn("Invalid channel map stored in database for stream %s", name);
goto fail;
}
- if (!(pa_channel_map_valid(&e->channel_map))) {
- pa_log_warn("Invalid channel map stored in database for stream %s", name);
+ 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->volume.channels != e->channel_map.channels) {
- pa_log_warn("Volume and channel map don't match in database entry for stream %s", name);
+ if ((e->relative_volume_valid && (!pa_cvolume_valid(&e->relative_volume) || e->relative_volume.channels != e->channel_map.channels)) ||
+ (e->absolute_volume_valid && (!pa_cvolume_valid(&e->absolute_volume) || e->absolute_volume.channels != e->channel_map.channels))) {
+ pa_log_warn("Invalid volume stored in database for stream %s", name);
goto fail;
}
@@ -213,6 +222,33 @@ static void trigger_save(struct userdata *u) {
u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
}
+static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
+ pa_cvolume t;
+
+ pa_assert(a);
+ pa_assert(b);
+
+ if (a->device_valid != b->device_valid ||
+ (a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
+ return FALSE;
+
+ if (a->muted_valid != b->muted_valid ||
+ (a->muted && (a->muted != b->muted)))
+ return FALSE;
+
+ t = b->relative_volume;
+ if (a->relative_volume_valid != b->relative_volume_valid ||
+ (a->relative_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->relative_volume)))
+ return FALSE;
+
+ t = b->absolute_volume;
+ if (a->absolute_volume_valid != b->absolute_volume_valid ||
+ (a->absolute_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->absolute_volume)))
+ return FALSE;
+
+ return TRUE;
+}
+
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
struct userdata *u = userdata;
struct entry entry, *old;
@@ -240,9 +276,23 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
entry.channel_map = sink_input->channel_map;
- entry.volume = *pa_sink_input_get_volume(sink_input);
+
+ if (sink_input->sink->flags & PA_SINK_FLAT_VOLUME) {
+ entry.absolute_volume = *pa_sink_input_get_volume(sink_input);
+ entry.absolute_volume_valid = sink_input->save_volume;
+
+ pa_sw_cvolume_divide(&entry.relative_volume, &entry.absolute_volume, pa_sink_get_volume(sink_input->sink, FALSE));
+ entry.relative_volume_valid = sink_input->save_volume;
+ } else {
+ entry.relative_volume = *pa_sink_input_get_volume(sink_input);
+ entry.relative_volume_valid = sink_input->save_volume;
+ }
+
entry.muted = pa_sink_input_get_mute(sink_input);
+ entry.muted_valid = sink_input->save_muted;
+
pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
+ entry.device_valid = sink_input->save_sink;
} else {
pa_source_output *source_output;
@@ -255,19 +305,15 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (!(name = get_name(source_output->proplist, "source-output")))
return;
- /* The following fields are filled in to make the entry valid
- * according to read_entry(). They are otherwise useless */
entry.channel_map = source_output->channel_map;
- pa_cvolume_reset(&entry.volume, entry.channel_map.channels);
+
pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
+ entry.device_valid = source_output->save_source;
}
if ((old = read_entry(u, name))) {
- if (pa_cvolume_equal(pa_cvolume_remap(&old->volume, &old->channel_map, &entry.channel_map), &entry.volume) &&
- !old->muted == !entry.muted &&
- strcmp(old->device, entry.device) == 0) {
-
+ if (entries_equal(old, &entry)) {
pa_xfree(old);
pa_xfree(name);
return;
@@ -297,20 +343,25 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
pa_assert(new_data);
+ if (!u->restore_device)
+ return PA_HOOK_OK;
+
if (!(name = get_name(new_data->proplist, "sink-input")))
return PA_HOOK_OK;
if ((e = read_entry(u, name))) {
pa_sink *s;
- if (u->restore_device &&
- (s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK, TRUE))) {
+ if (e->device_valid) {
- if (!new_data->sink) {
- pa_log_info("Restoring device for stream %s.", name);
- new_data->sink = s;
- } else
- pa_log_info("Not restore device for stream %s, because already set.", name);
+ 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_info("Not restore device for stream %s, because already set.", name);
+ }
}
pa_xfree(e);
@@ -327,6 +378,9 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
pa_assert(new_data);
+ if (!u->restore_volume && !u->restore_muted)
+ return PA_HOOK_OK;
+
if (!(name = get_name(new_data->proplist, "sink-input")))
return PA_HOOK_OK;
@@ -334,17 +388,43 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
if (u->restore_volume) {
- if (!new_data->volume_is_set) {
- pa_log_info("Restoring volume for sink input %s.", name);
- pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+ if (!new_data->virtual_volume_is_set) {
+ pa_cvolume v;
+ pa_cvolume_init(&v);
+
+ if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) {
+
+ if (e->absolute_volume_valid &&
+ e->device_valid &&
+ pa_streq(new_data->sink->name, e->device)) {
+
+ v = e->absolute_volume;
+ new_data->virtual_volume_is_absolute = TRUE;
+ } else if (e->relative_volume_valid) {
+
+ v = e->relative_volume;
+ new_data->virtual_volume_is_absolute = FALSE;
+ }
+
+ } else if (e->relative_volume_valid) {
+ v = e->relative_volume;
+ new_data->virtual_volume_is_absolute = FALSE;
+ }
+
+ if (v.channels > 0) {
+ pa_log_info("Restoring volume for sink input %s.", name);
+ pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map));
+ new_data->save_volume = TRUE;
+ }
} else
pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
}
- if (u->restore_muted) {
+ if (u->restore_muted && e->muted_valid) {
if (!new_data->muted_is_set) {
pa_log_info("Restoring mute state for sink input %s.", name);
pa_sink_input_new_data_set_muted(new_data, e->muted);
+ new_data->save_muted = TRUE;
} else
pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
}
@@ -363,21 +443,27 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
pa_assert(new_data);
+ if (!u->restore_device)
+ return PA_HOOK_OK;
+
+ if (new_data->direct_on_input)
+ return PA_HOOK_OK;
+
if (!(name = get_name(new_data->proplist, "source-output")))
return PA_HOOK_OK;
if ((e = read_entry(u, name))) {
pa_source *s;
- if (u->restore_device &&
- !new_data->direct_on_input &&
- (s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE, TRUE))) {
-
- if (!new_data->source) {
- pa_log_info("Restoring device for stream %s.", name);
- new_data->source = s;
- } else
- pa_log_info("Not restoring device for stream %s, because already set", name);
+ 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_info("Not restoring device for stream %s, because already set", name);
+ }
}
pa_xfree(e);
@@ -431,21 +517,40 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
}
if (u->restore_volume) {
- pa_cvolume v = e->volume;
- pa_log_info("Restoring volume for sink input %s.", name);
- pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map));
+ pa_cvolume v;
+ pa_cvolume_init(&v);
+
+ if (si->sink->flags & PA_SINK_FLAT_VOLUME) {
+
+ if (e->absolute_volume_valid &&
+ e->device_valid &&
+ pa_streq(e->device, si->sink->name))
+ v = e->absolute_volume;
+ else if (e->relative_volume_valid) {
+ pa_cvolume t = si->sink->virtual_volume;
+ pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &si->sink->channel_map, &e->channel_map));
+ }
+ } else if (e->relative_volume_valid)
+ v = e->relative_volume;
+
+ if (v.channels > 0) {
+ pa_log_info("Restoring volume for sink input %s.", name);
+ pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map), TRUE);
+ }
}
- if (u->restore_muted) {
+ if (u->restore_muted &&
+ e->muted_valid) {
pa_log_info("Restoring mute state for sink input %s.", name);
- pa_sink_input_set_mute(si, e->muted);
+ pa_sink_input_set_mute(si, e->muted, TRUE);
}
if (u->restore_device &&
- (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE, TRUE))) {
+ e->device_valid &&
+ (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
pa_log_info("Restoring device for stream %s.", name);
- pa_sink_input_move_to(si, s);
+ pa_sink_input_move_to(si, s, TRUE);
}
}
@@ -462,10 +567,11 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
}
if (u->restore_device &&
- (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE, TRUE))) {
+ e->device_valid &&
+ (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
pa_log_info("Restoring device for stream %s.", name);
- pa_source_output_move_to(so, s);
+ pa_source_output_move_to(so, s, TRUE);
}
}
}
@@ -548,11 +654,13 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
pa_xfree(key.dptr);
if ((e = read_entry(u, name))) {
+ pa_cvolume r;
+
pa_tagstruct_puts(reply, name);
pa_tagstruct_put_channel_map(reply, &e->channel_map);
- pa_tagstruct_put_cvolume(reply, &e->volume);
- pa_tagstruct_puts(reply, e->device);
- pa_tagstruct_put_boolean(reply, e->muted);
+ pa_tagstruct_put_cvolume(reply, e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r));
+ pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
+ pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
pa_xfree(e);
}
@@ -592,16 +700,28 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
- pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
+ pa_tagstruct_get_cvolume(t, &entry.relative_volume) < 0 ||
pa_tagstruct_gets(t, &device) < 0 ||
pa_tagstruct_get_boolean(t, &muted) < 0)
goto fail;
- if (entry.channel_map.channels != entry.volume.channels)
+ entry.absolute_volume_valid = FALSE;
+ entry.relative_volume_valid = entry.relative_volume.channels > 0;
+
+ if (entry.relative_volume_valid &&
+ entry.channel_map.channels != entry.relative_volume.channels)
goto fail;
entry.muted = muted;
- pa_strlcpy(entry.device, device, sizeof(entry.device));
+ entry.muted_valid = TRUE;
+
+ if (device)
+ pa_strlcpy(entry.device, device, sizeof(entry.device));
+ entry.device_valid = !!entry.device[0];
+
+ if (entry.device_valid &&
+ !pa_namereg_is_valid_name(entry.device))
+ goto fail;
key.dptr = (void*) name;
key.dsize = (int) strlen(name);
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 8ab84e08..5e5e53e7 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -62,8 +62,10 @@ struct userdata {
*source_output_new_slot,
*sink_input_unlink_slot,
*source_output_unlink_slot,
- *sink_input_move_slot,
- *source_output_move_slot,
+ *sink_input_move_start_slot,
+ *source_output_move_start_slot,
+ *sink_input_move_finish_slot,
+ *source_output_move_finish_slot,
*sink_input_state_changed_slot,
*source_output_state_changed_slot;
};
@@ -181,40 +183,60 @@ static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_outpu
return PA_HOOK_OK;
}
-static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_hook_data *data, struct userdata *u) {
+static pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
struct device_info *d;
pa_assert(c);
- pa_assert(data);
+ pa_sink_input_assert_ref(s);
pa_assert(u);
- if ((d = pa_hashmap_get(u->device_infos, data->destination)))
- resume(d);
-
- if (pa_sink_check_suspend(data->sink_input->sink) <= 1)
- if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))
+ if (pa_sink_check_suspend(s->sink) <= 1)
+ if ((d = pa_hashmap_get(u->device_infos, s->sink)))
restart(d);
return PA_HOOK_OK;
}
-static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_move_hook_data *data, struct userdata *u) {
+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_assert(c);
- pa_assert(data);
+ pa_sink_input_assert_ref(s);
pa_assert(u);
- if ((d = pa_hashmap_get(u->device_infos, data->destination)))
+ if ((d = pa_hashmap_get(u->device_infos, s->sink)))
resume(d);
- if (pa_source_check_suspend(data->source_output->source) <= 1)
- if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_move_start_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_source_output_assert_ref(s);
+ pa_assert(u);
+
+ if (pa_source_check_suspend(s->source) <= 1)
+ if ((d = pa_hashmap_get(u->device_infos, s->source)))
restart(d);
return PA_HOOK_OK;
}
+static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_source_output_assert_ref(s);
+ pa_assert(u);
+
+ if ((d = pa_hashmap_get(u->device_infos, s->source)))
+ resume(d);
+
+ return PA_HOOK_OK;
+}
+
static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
struct device_info *d;
pa_sink_input_state_t state;
@@ -376,8 +398,10 @@ int pa__init(pa_module*m) {
u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u);
u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u);
- u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_hook_cb, u);
- u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_hook_cb, u);
+ u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_start_hook_cb, u);
+ u->source_output_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_start_hook_cb, u);
+ u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_finish_hook_cb, u);
+ u->source_output_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_finish_hook_cb, u);
u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
@@ -421,8 +445,10 @@ void pa__done(pa_module*m) {
pa_hook_slot_free(u->sink_input_new_slot);
if (u->sink_input_unlink_slot)
pa_hook_slot_free(u->sink_input_unlink_slot);
- if (u->sink_input_move_slot)
- pa_hook_slot_free(u->sink_input_move_slot);
+ if (u->sink_input_move_start_slot)
+ pa_hook_slot_free(u->sink_input_move_start_slot);
+ if (u->sink_input_move_finish_slot)
+ pa_hook_slot_free(u->sink_input_move_finish_slot);
if (u->sink_input_state_changed_slot)
pa_hook_slot_free(u->sink_input_state_changed_slot);
@@ -430,8 +456,10 @@ void pa__done(pa_module*m) {
pa_hook_slot_free(u->source_output_new_slot);
if (u->source_output_unlink_slot)
pa_hook_slot_free(u->source_output_unlink_slot);
- if (u->source_output_move_slot)
- pa_hook_slot_free(u->source_output_move_slot);
+ if (u->source_output_move_start_slot)
+ pa_hook_slot_free(u->source_output_move_start_slot);
+ if (u->source_output_move_finish_slot)
+ pa_hook_slot_free(u->source_output_move_finish_slot);
if (u->source_output_state_changed_slot)
pa_hook_slot_free(u->source_output_state_changed_slot);
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index a46d6e59..5c7a6e55 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -494,6 +494,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -581,6 +582,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
case PA_SOURCE_UNLINKED:
case PA_SOURCE_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -1054,10 +1056,10 @@ 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->volume))
+ pa_cvolume_equal(&volume, &u->sink->virtual_volume))
return;
- memcpy(&u->sink->volume, &volume, sizeof(pa_cvolume));
+ memcpy(&u->sink->virtual_volume, &volume, sizeof(pa_cvolume));
if (u->version >= 11)
u->sink->muted = !!mute;
@@ -1619,7 +1621,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
#ifdef TUNNEL_SINK
/* Called from main context */
-static int sink_set_volume(pa_sink *sink) {
+static void sink_set_volume(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
uint32_t tag;
@@ -1632,14 +1634,12 @@ static int sink_set_volume(pa_sink *sink) {
pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
pa_tagstruct_putu32(t, tag = u->ctag++);
pa_tagstruct_putu32(t, u->device_index);
- pa_tagstruct_put_cvolume(t, &sink->volume);
+ pa_tagstruct_put_cvolume(t, &sink->virtual_volume);
pa_pstream_send_tagstruct(u->pstream, t);
-
- return 0;
}
/* Called from main context */
-static int sink_set_mute(pa_sink *sink) {
+static void sink_set_mute(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
uint32_t tag;
@@ -1649,7 +1649,7 @@ static int sink_set_mute(pa_sink *sink) {
pa_assert(u);
if (u->version < 11)
- return -1;
+ return;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
@@ -1657,8 +1657,6 @@ static int sink_set_mute(pa_sink *sink) {
pa_tagstruct_putu32(t, u->device_index);
pa_tagstruct_put_boolean(t, !!sink->muted);
pa_pstream_send_tagstruct(u->pstream, t);
-
- return 0;
}
#endif
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
index aac0d046..21c71491 100644
--- a/src/modules/module-volume-restore.c
+++ b/src/modules/module-volume-restore.c
@@ -23,43 +23,19 @@
#include <config.h>
#endif
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-
#include <pulse/xmalloc.h>
-#include <pulse/volume.h>
-#include <pulse/timeval.h>
-#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
-#include <pulsecore/core-subscribe.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/source-output.h>
-#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
#include "module-volume-restore-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams");
+PA_MODULE_DESCRIPTION("Compatibility module");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE(
- "table=<filename> "
- "restore_device=<Restore the device for each stream?> "
- "restore_volume=<Restore the volume for each stream?>"
-);
-
-#define WHITESPACE "\n\r \t"
-#define DEFAULT_VOLUME_TABLE_FILE "volume-restore.table"
-#define SAVE_INTERVAL 10
static const char* const valid_modargs[] = {
"table",
@@ -68,413 +44,10 @@ static const char* const valid_modargs[] = {
NULL,
};
-struct rule {
- char* name;
- pa_bool_t volume_is_set;
- pa_cvolume volume;
- char *sink, *source;
-};
-
-struct userdata {
- pa_core *core;
- pa_hashmap *hashmap;
- pa_subscription *subscription;
- pa_hook_slot
- *sink_input_new_hook_slot,
- *sink_input_fixate_hook_slot,
- *source_output_new_hook_slot;
- pa_bool_t modified;
- char *table_file;
- pa_time_event *save_time_event;
-};
-
-static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
- char *p;
- long k;
- unsigned i;
-
- pa_assert(s);
- pa_assert(v);
-
- if (!isdigit(*s))
- return NULL;
-
- k = strtol(s, &p, 0);
- if (k <= 0 || k > (long) PA_CHANNELS_MAX)
- return NULL;
-
- v->channels = (uint8_t) k;
-
- for (i = 0; i < v->channels; i++) {
- p += strspn(p, WHITESPACE);
-
- if (!isdigit(*p))
- return NULL;
-
- k = strtol(p, &p, 0);
-
- if (k < (long) PA_VOLUME_MUTED)
- return NULL;
-
- v->values[i] = (pa_volume_t) k;
- }
-
- if (*p != 0)
- return NULL;
-
- return v;
-}
-
-static int load_rules(struct userdata *u) {
- FILE *f;
- int n = 0;
- int ret = -1;
- char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
- char *ln = buf_name;
-
- if (!(f = fopen(u->table_file, "r"))) {
- if (errno == ENOENT) {
- pa_log_info("Starting with empty ruleset.");
- ret = 0;
- } else
- pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
-
- goto finish;
- }
-
- pa_lock_fd(fileno(f), 1);
-
- while (!feof(f)) {
- struct rule *rule;
- pa_cvolume v;
- pa_bool_t v_is_set;
-
- if (!fgets(ln, sizeof(buf_name), f))
- break;
-
- n++;
-
- pa_strip_nl(ln);
-
- if (ln[0] == '#')
- continue;
-
- if (ln == buf_name) {
- ln = buf_volume;
- continue;
- }
-
- if (ln == buf_volume) {
- ln = buf_sink;
- continue;
- }
-
- if (ln == buf_sink) {
- ln = buf_source;
- continue;
- }
-
- pa_assert(ln == buf_source);
-
- if (buf_volume[0]) {
- if (!parse_volume(buf_volume, &v)) {
- pa_log("parse failure in %s:%u, stopping parsing", u->table_file, n);
- goto finish;
- }
-
- v_is_set = TRUE;
- } else
- v_is_set = FALSE;
-
- ln = buf_name;
-
- if (pa_hashmap_get(u->hashmap, buf_name)) {
- pa_log("double entry in %s:%u, ignoring", u->table_file, n);
- continue;
- }
-
- rule = pa_xnew(struct rule, 1);
- rule->name = pa_xstrdup(buf_name);
- if ((rule->volume_is_set = v_is_set))
- rule->volume = v;
- rule->sink = buf_sink[0] ? pa_xstrdup(buf_sink) : NULL;
- rule->source = buf_source[0] ? pa_xstrdup(buf_source) : NULL;
-
- pa_hashmap_put(u->hashmap, rule->name, rule);
- }
-
- if (ln != buf_name) {
- pa_log("invalid number of lines in %s.", u->table_file);
- goto finish;
- }
-
- ret = 0;
-
-finish:
- if (f) {
- pa_lock_fd(fileno(f), 0);
- fclose(f);
- }
-
- return ret;
-}
-
-static int save_rules(struct userdata *u) {
- FILE *f;
- int ret = -1;
- void *state = NULL;
- struct rule *rule;
-
- if (!u->modified)
- return 0;
-
- pa_log_info("Saving rules...");
-
- if (!(f = fopen(u->table_file, "w"))) {
- pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
- goto finish;
- }
-
- pa_lock_fd(fileno(f), 1);
-
- while ((rule = pa_hashmap_iterate(u->hashmap, &state, NULL))) {
- unsigned i;
-
- fprintf(f, "%s\n", rule->name);
-
- if (rule->volume_is_set) {
- fprintf(f, "%u", rule->volume.channels);
-
- for (i = 0; i < rule->volume.channels; i++)
- fprintf(f, " %u", rule->volume.values[i]);
- }
-
- fprintf(f, "\n%s\n%s\n",
- rule->sink ? rule->sink : "",
- rule->source ? rule->source : "");
- }
-
- ret = 0;
- u->modified = FALSE;
- pa_log_debug("Successfully saved rules...");
-
-finish:
- if (f) {
- pa_lock_fd(fileno(f), 0);
- fclose(f);
- }
-
- return ret;
-}
-
-static char* client_name(pa_client *c) {
- char *t, *e;
-
- if (!pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME) || !c->driver)
- return NULL;
-
- t = pa_sprintf_malloc("%s$%s", c->driver, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
- t[strcspn(t, "\n\r#")] = 0;
-
- if (!*t) {
- pa_xfree(t);
- return NULL;
- }
-
- if ((e = strrchr(t, '('))) {
- char *k = e + 1 + strspn(e + 1, "0123456789-");
-
- /* Dirty trick: truncate all trailing parens with numbers in
- * between, since they are usually used to identify multiple
- * sessions of the same application, which is something we
- * explicitly don't want. Besides other stuff this makes xmms
- * with esound work properly for us. */
-
- if (*k == ')' && *(k+1) == 0)
- *e = 0;
- }
-
- return t;
-}
-
-static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
- struct userdata *u = userdata;
-
- pa_assert(a);
- pa_assert(e);
- pa_assert(tv);
- pa_assert(u);
-
- pa_assert(e == u->save_time_event);
- u->core->mainloop->time_free(u->save_time_event);
- u->save_time_event = NULL;
-
- save_rules(u);
-}
-
-static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
- struct userdata *u = userdata;
- pa_sink_input *si = NULL;
- pa_source_output *so = NULL;
- struct rule *r;
- char *name;
-
- pa_assert(c);
- pa_assert(u);
-
- if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
- t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
- t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
- t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
- return;
-
- if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
- if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
- return;
-
- if (!si->client || !(name = client_name(si->client)))
- return;
- } else {
- pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
-
- if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
- return;
-
- if (!so->client || !(name = client_name(so->client)))
- return;
- }
-
- if ((r = pa_hashmap_get(u->hashmap, name))) {
- pa_xfree(name);
-
- if (si) {
-
- if (!r->volume_is_set || !pa_cvolume_equal(pa_sink_input_get_volume(si), &r->volume)) {
- pa_log_info("Saving volume for <%s>", r->name);
- r->volume = *pa_sink_input_get_volume(si);
- r->volume_is_set = TRUE;
- u->modified = TRUE;
- }
-
- if (!r->sink || strcmp(si->sink->name, r->sink) != 0) {
- pa_log_info("Saving sink for <%s>", r->name);
- pa_xfree(r->sink);
- r->sink = pa_xstrdup(si->sink->name);
- u->modified = TRUE;
- }
- } else {
- pa_assert(so);
-
- if (!r->source || strcmp(so->source->name, r->source) != 0) {
- pa_log_info("Saving source for <%s>", r->name);
- pa_xfree(r->source);
- r->source = pa_xstrdup(so->source->name);
- u->modified = TRUE;
- }
- }
-
- } else {
- pa_log_info("Creating new entry for <%s>", name);
-
- r = pa_xnew(struct rule, 1);
- r->name = name;
-
- if (si) {
- r->volume = *pa_sink_input_get_volume(si);
- r->volume_is_set = TRUE;
- r->sink = pa_xstrdup(si->sink->name);
- r->source = NULL;
- } else {
- pa_assert(so);
- r->volume_is_set = FALSE;
- r->sink = NULL;
- r->source = pa_xstrdup(so->source->name);
- }
-
- pa_hashmap_put(u->hashmap, r->name, r);
- u->modified = TRUE;
- }
-
- if (u->modified && !u->save_time_event) {
- struct timeval tv;
- pa_gettimeofday(&tv);
- tv.tv_sec += SAVE_INTERVAL;
- u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
- }
-}
-
-static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
- struct rule *r;
- char *name;
-
- pa_assert(data);
-
- /* In the NEW hook we only adjust the device. Adjusting the volume
- * is left for the FIXATE hook */
-
- if (!data->client || !(name = client_name(data->client)))
- return PA_HOOK_OK;
-
- if ((r = pa_hashmap_get(u->hashmap, name))) {
- if (!data->sink && r->sink) {
- if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
- pa_log_info("Restoring sink for <%s>", r->name);
- }
- }
-
- pa_xfree(name);
-
- return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
- struct rule *r;
- char *name;
-
- pa_assert(data);
-
- /* In the FIXATE hook we only adjust the volum. Adjusting the device
- * is left for the NEW hook */
-
- if (!data->client || !(name = client_name(data->client)))
- return PA_HOOK_OK;
-
- if ((r = pa_hashmap_get(u->hashmap, name))) {
-
- if (r->volume_is_set && data->sample_spec.channels == r->volume.channels) {
- pa_log_info("Restoring volume for <%s>", r->name);
- pa_sink_input_new_data_set_volume(data, &r->volume);
- }
- }
-
- pa_xfree(name);
-
- return PA_HOOK_OK;
-}
-
-static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
- struct rule *r;
- char *name;
-
- pa_assert(data);
-
- if (!data->client || !(name = client_name(data->client)))
- return PA_HOOK_OK;
-
- if ((r = pa_hashmap_get(u->hashmap, name))) {
- if (!data->source && r->source) {
- if ((data->source = pa_namereg_get(c, r->source, PA_NAMEREG_SOURCE, 1)))
- pa_log_info("Restoring source for <%s>", r->name);
- }
- }
-
- return PA_HOOK_OK;
-}
-
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
- struct userdata *u;
pa_bool_t restore_device = TRUE, restore_volume = TRUE;
+ char *t;
pa_assert(m);
@@ -483,90 +56,26 @@ int pa__init(pa_module*m) {
goto fail;
}
- u = pa_xnew(struct userdata, 1);
- u->core = m->core;
- u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- u->modified = FALSE;
- u->subscription = NULL;
- u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;
- u->save_time_event = NULL;
-
- m->userdata = u;
-
- if (!(u->table_file = pa_state_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE), TRUE)))
- goto fail;
-
if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) {
pa_log("restore_volume= and restore_device= expect boolean arguments");
goto fail;
}
- if (!(restore_device || restore_volume)) {
- pa_log("Both restrong the volume and restoring the device are disabled. There's no point in using this module at all then, failing.");
- goto fail;
- }
-
- if (load_rules(u) < 0)
- goto fail;
+ pa_log_warn("module-volume-restore is obsolete. It has been replaced by module-stream-restore. We will now load the latter but please make sure to remove module-volume-restore from your configuration.");
- u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
-
- if (restore_device) {
- u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
- u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
- }
+ 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);
+ pa_xfree(t);
- if (restore_volume)
- u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+ pa_module_unload_request(m, TRUE);
pa_modargs_free(ma);
return 0;
fail:
- pa__done(m);
if (ma)
pa_modargs_free(ma);
return -1;
}
-
-static void free_func(void *p, void *userdata) {
- struct rule *r = p;
- pa_assert(r);
-
- pa_xfree(r->name);
- pa_xfree(r->sink);
- pa_xfree(r->source);
- pa_xfree(r);
-}
-
-void pa__done(pa_module*m) {
- struct userdata* u;
-
- pa_assert(m);
-
- if (!(u = m->userdata))
- return;
-
- if (u->subscription)
- pa_subscription_free(u->subscription);
-
- if (u->sink_input_new_hook_slot)
- pa_hook_slot_free(u->sink_input_new_hook_slot);
- if (u->sink_input_fixate_hook_slot)
- pa_hook_slot_free(u->sink_input_fixate_hook_slot);
- if (u->source_output_new_hook_slot)
- pa_hook_slot_free(u->source_output_new_hook_slot);
-
- if (u->hashmap) {
- save_rules(u);
- pa_hashmap_free(u->hashmap, free_func, NULL);
- }
-
- if (u->save_time_event)
- u->core->mainloop->time_free(u->save_time_event);
-
- pa_xfree(u->table_file);
- pa_xfree(u);
-}
diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c
index e93721c1..bef02536 100644
--- a/src/modules/module-x11-bell.c
+++ b/src/modules/module-x11-bell.c
@@ -82,7 +82,7 @@ static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) {
bne = (XkbBellNotifyEvent*) e;
- if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) {
+ if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) {
pa_log_info("Ringing bell failed, reverting to X11 device bell.");
XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
}
diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c
index 57d182fd..5fc8047d 100644
--- a/src/modules/module-x11-xsmp.c
+++ b/src/modules/module-x11-xsmp.c
@@ -117,13 +117,14 @@ static void new_ice_connection(IceConn connection, IcePointer client_data, Bool
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
- char t[256], *vendor, *client_id, *k;
+ char t[256], *vendor, *client_id;
SmcCallbacks callbacks;
SmProp prop_program, prop_user;
SmProp *prop_list[2];
SmPropValue val_program, val_user;
struct userdata *u;
const char *e;
+ pa_client_new_data data;
pa_assert(m);
@@ -198,16 +199,22 @@ int pa__init(pa_module*m) {
SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list);
pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id);
- k = pa_sprintf_malloc("XSMP Session on %s as %s", vendor, client_id);
- u->client = pa_client_new(u->core, __FILE__, k);
- pa_xfree(k);
- pa_proplist_sets(u->client->proplist, "xsmp.vendor", vendor);
- pa_proplist_sets(u->client->proplist, "xsmp.client.id", client_id);
+ pa_client_new_data_init(&data);
+ data.module = m;
+ data.driver = __FILE__;
+ pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "XSMP Session on %s as %s", vendor, client_id);
+ pa_proplist_sets(data.proplist, "xsmp.vendor", vendor);
+ pa_proplist_sets(data.proplist, "xsmp.client.id", client_id);
+ u->client = pa_client_new(u->core, &data);
+ pa_client_new_data_done(&data);
free(vendor);
free(client_id);
+ if (!u->client)
+ goto fail;
+
pa_modargs_free(ma);
return 0;
diff --git a/src/modules/oss/Makefile b/src/modules/oss/Makefile
new file mode 120000
index 00000000..efe5a336
--- /dev/null
+++ b/src/modules/oss/Makefile
@@ -0,0 +1 @@
+../../pulse/Makefile \ No newline at end of file
diff --git a/src/modules/module-oss.c b/src/modules/oss/module-oss.c
index 23a32549..eac0c8e6 100644
--- a/src/modules/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -71,6 +71,11 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
+#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
+#include <sys/audioio.h>
+#include <sys/syscall.h>
+#endif
+
#include "oss-util.h"
#include "module-oss-symdef.h"
@@ -121,7 +126,7 @@ struct userdata {
int mixer_fd;
int mixer_devmask;
- int nfrags, frag_size;
+ int nfrags, frag_size, orig_frag_size;
pa_bool_t use_mmap;
unsigned out_mmap_current, in_mmap_current;
@@ -399,13 +404,27 @@ static pa_usec_t io_sink_get_latency(struct userdata *u) {
if (u->use_getodelay) {
int arg;
-
+#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
+#if defined(AUDIO_GETBUFINFO)
+ struct audio_info info;
+ if (syscall(SYS_ioctl, u->fd, AUDIO_GETBUFINFO, &info) < 0) {
+ pa_log_info("Device doesn't support AUDIO_GETBUFINFO: %s", pa_cstrerror(errno));
+ u->use_getodelay = 0;
+ } else {
+ arg = info.play.seek + info.blocksize / 2;
+ r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
+ }
+#else
+ pa_log_info("System doesn't support AUDIO_GETBUFINFO");
+ u->use_getodelay = 0;
+#endif
+#else
if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno));
u->use_getodelay = 0;
} else
r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
-
+#endif
}
if (!u->use_getodelay && u->use_getospace) {
@@ -424,7 +443,6 @@ static pa_usec_t io_sink_get_latency(struct userdata *u) {
return r;
}
-
static pa_usec_t io_source_get_latency(struct userdata *u) {
pa_usec_t r = 0;
@@ -508,9 +526,6 @@ static int suspend(struct userdata *u) {
return 0;
}
-static int sink_get_volume(pa_sink *s);
-static int source_get_volume(pa_source *s);
-
static int unsuspend(struct userdata *u) {
int m;
pa_sample_spec ss, *ss_original;
@@ -536,7 +551,7 @@ static int unsuspend(struct userdata *u) {
}
if (u->nfrags >= 2 && u->frag_size >= 1)
- if (pa_oss_set_fragments(u->fd, u->nfrags, u->frag_size) < 0) {
+ if (pa_oss_set_fragments(u->fd, u->nfrags, u->orig_frag_size) < 0) {
pa_log_warn("Resume failed, couldn't set original fragment settings.");
goto fail;
}
@@ -602,9 +617,9 @@ static int unsuspend(struct userdata *u) {
build_pollfd(u);
if (u->sink)
- sink_get_volume(u->sink);
+ pa_sink_get_volume(u->sink, TRUE);
if (u->source)
- source_get_volume(u->source);
+ pa_source_get_volume(u->source, TRUE);
pa_log_info("Resumed successfully...");
@@ -681,6 +696,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
break;
+ case PA_SINK_INVALID_STATE:
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
;
@@ -762,6 +778,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
case PA_SOURCE_UNLINKED:
case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
;
}
@@ -777,84 +794,76 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return ret;
}
-static int sink_get_volume(pa_sink *s) {
+static void sink_get_volume(pa_sink *s) {
struct userdata *u;
- int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
if (u->mixer_devmask & SOUND_MASK_VOLUME)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
if (u->mixer_devmask & SOUND_MASK_PCM)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
- return -1;
}
-static int sink_set_volume(pa_sink *s) {
+static void sink_set_volume(pa_sink *s) {
struct userdata *u;
- int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
if (u->mixer_devmask & SOUND_MASK_VOLUME)
- if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
if (u->mixer_devmask & SOUND_MASK_PCM)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
- return -1;
}
-static int source_get_volume(pa_source *s) {
+static void source_get_volume(pa_source *s) {
struct userdata *u;
- int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
if (u->mixer_devmask & SOUND_MASK_IGAIN)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
if (u->mixer_devmask & SOUND_MASK_RECLEV)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
- return -1;
}
-static int source_set_volume(pa_source *s) {
+static void source_set_volume(pa_source *s) {
struct userdata *u;
- int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
if (u->mixer_devmask & SOUND_MASK_IGAIN)
- if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
if (u->mixer_devmask & SOUND_MASK_RECLEV)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
- return -1;
}
static void thread_func(void *userdata) {
@@ -877,7 +886,7 @@ static void thread_func(void *userdata) {
/* pa_log("loop"); */
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
if (u->sink->thread_info.rewind_requested)
pa_sink_process_rewind(u->sink, 0);
@@ -1144,7 +1153,7 @@ int pa__init(pa_module*m) {
struct userdata *u = NULL;
const char *dev;
int fd = -1;
- int nfrags, frag_size;
+ int nfrags, orig_frag_size, frag_size;
int mode, caps;
pa_bool_t record = TRUE, playback = TRUE, use_mmap = TRUE;
pa_sample_spec ss;
@@ -1201,12 +1210,12 @@ int pa__init(pa_module*m) {
if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) {
pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
- use_mmap = 0;
+ use_mmap = FALSE;
}
if (use_mmap && mode == O_WRONLY) {
pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
- use_mmap = 0;
+ use_mmap = FALSE;
}
if (pa_oss_get_hw_description(dev, hwdesc, sizeof(hwdesc)) >= 0)
@@ -1216,6 +1225,7 @@ int pa__init(pa_module*m) {
pa_log_info("Device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+ orig_frag_size = frag_size;
if (nfrags >= 2 && frag_size >= 1)
if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
goto fail;
@@ -1235,6 +1245,7 @@ int pa__init(pa_module*m) {
m->userdata = u;
u->fd = fd;
u->mixer_fd = -1;
+ u->mixer_devmask = 0;
u->use_getospace = u->use_getispace = TRUE;
u->use_getodelay = TRUE;
u->mode = mode;
@@ -1242,6 +1253,7 @@ int pa__init(pa_module*m) {
u->device_name = pa_xstrdup(dev);
u->in_nfrags = u->out_nfrags = (uint32_t) (u->nfrags = nfrags);
u->out_fragment_size = u->in_fragment_size = (uint32_t) (u->frag_size = frag_size);
+ u->orig_frag_size = orig_frag_size;
u->use_mmap = use_mmap;
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
@@ -1383,7 +1395,6 @@ int pa__init(pa_module*m) {
if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
pa_bool_t do_close = TRUE;
- u->mixer_devmask = 0;
if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
@@ -1394,6 +1405,7 @@ int pa__init(pa_module*m) {
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL;
u->sink->get_volume = sink_get_volume;
u->sink->set_volume = sink_set_volume;
+ u->sink->n_volume_steps = 101;
do_close = FALSE;
}
@@ -1402,6 +1414,7 @@ int pa__init(pa_module*m) {
u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL;
u->source->get_volume = source_get_volume;
u->source->set_volume = source_set_volume;
+ u->source->n_volume_steps = 101;
do_close = FALSE;
}
}
@@ -1409,6 +1422,7 @@ int pa__init(pa_module*m) {
if (do_close) {
pa_close(u->mixer_fd);
u->mixer_fd = -1;
+ u->mixer_devmask = 0;
}
}
diff --git a/src/modules/oss-util.c b/src/modules/oss/oss-util.c
index f766030d..f04b875d 100644
--- a/src/modules/oss-util.c
+++ b/src/modules/oss/oss-util.c
@@ -45,6 +45,7 @@
int pa_oss_open(const char *device, int *mode, int* pcaps) {
int fd = -1;
int caps;
+ char *t;
pa_assert(device);
pa_assert(mode);
@@ -92,7 +93,8 @@ int pa_oss_open(const char *device, int *mode, int* pcaps) {
success:
- pa_log_debug("capabilities:%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ t = pa_sprintf_malloc(
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
*pcaps & DSP_CAP_BATCH ? " BATCH" : "",
#ifdef DSP_CAP_BIND
*pcaps & DSP_CAP_BIND ? " BIND" : "",
@@ -140,6 +142,9 @@ success:
#endif
*pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : "");
+ pa_log_debug("capabilities:%s", t);
+ pa_xfree(t);
+
pa_make_fd_cloexec(fd);
return fd;
@@ -164,6 +169,10 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
[PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
[PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */
[PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S24LE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S24BE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S24_32LE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S24_32BE] = AFMT_QUERY, /* not supported */
};
pa_assert(fd >= 0);
@@ -241,6 +250,8 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
int arg;
arg = ((int) nfrags << 16) | simple_log2(frag_size);
+ pa_log_debug("Asking for %i fragments of size %i (requested %i)", nfrags, 1 << simple_log2(frag_size), frag_size);
+
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno));
return -1;
@@ -298,7 +309,11 @@ static int get_device_number(const char *dev) {
int r;
if (!(p = rp = pa_readlink(dev))) {
+#ifdef ENOLINK
if (errno != EINVAL && errno != ENOLINK) {
+#else
+ if (errno != EINVAL) {
+#endif
r = -1;
goto finish;
}
diff --git a/src/modules/oss-util.h b/src/modules/oss/oss-util.h
index 654f7bba..654f7bba 100644
--- a/src/modules/oss-util.h
+++ b/src/modules/oss/oss-util.h
diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c
index 8918def8..059c7028 100644
--- a/src/modules/raop/base64.c
+++ b/src/modules/raop/base64.c
@@ -45,6 +45,7 @@ static int pos(char c)
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '+') return 62;
if (c == '/') return 63;
+ return -1;
}
int pa_base64_encode(const void *data, int size, char **str)
@@ -97,8 +98,12 @@ static unsigned int token_decode(const char *token)
marker++;
else if (marker > 0)
return DECODE_ERROR;
- else
- val += pos(token[i]);
+ else {
+ int lpos = pos(token[i]);
+ if (lpos < 0)
+ return DECODE_ERROR;
+ val += lpos;
+ }
}
if (marker > 2)
return DECODE_ERROR;
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index e35773cc..00d21255 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -313,7 +313,7 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
if (pa_memblockq_is_readable(s->memblockq) &&
s->sink_input->thread_info.underrun_for > 0) {
pa_log_debug("Requesting rewind due to end of underrun");
- pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE, FALSE);
}
return 1;
@@ -415,7 +415,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
goto fail;
}
- if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, TRUE))) {
+ if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
pa_log("Sink does not exist.");
goto fail;
}
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index 9c0f07f1..a6d682bb 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -196,7 +196,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE, 1))) {
+ if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
pa_log("Source does not exist.");
goto fail;
}
diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c
index 88351010..c09c321f 100644
--- a/src/modules/rtp/rtp.c
+++ b/src/modules/rtp/rtp.c
@@ -35,6 +35,10 @@
#include <sys/filio.h>
#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c
index b0c95aa5..7764f7bd 100644
--- a/src/modules/rtp/sap.c
+++ b/src/modules/rtp/sap.c
@@ -38,6 +38,10 @@
#include <sys/filio.h>
#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
diff --git a/src/pulse/browser.h b/src/pulse/browser.h
index c4e0a17e..499fae2e 100644
--- a/src/pulse/browser.h
+++ b/src/pulse/browser.h
@@ -26,6 +26,7 @@
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulse/cdecl.h>
+#include <pulse/version.h>
/** \file
* An abstract interface for Zeroconf browsing of PulseAudio servers */
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index fd313bd3..455bda1b 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -32,6 +32,7 @@
#include <pulse/i18n.h>
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
+#include <pulsecore/bitset.h>
#include "channelmap.h"
@@ -497,11 +498,58 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
pa_channel_map_init(&map);
+ /* We don't need to match against the well known channel mapping
+ * "mono" here explicitly, because that can be understood as
+ * listing with one channel called "mono". */
+
if (strcmp(s, "stereo") == 0) {
map.channels = 2;
map.map[0] = PA_CHANNEL_POSITION_LEFT;
map.map[1] = PA_CHANNEL_POSITION_RIGHT;
goto finish;
+ } else if (strcmp(s, "surround-40") == 0) {
+ map.channels = 4;
+ map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+ map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ goto finish;
+ } else if (strcmp(s, "surround-41") == 0) {
+ map.channels = 5;
+ map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+ map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ map.map[4] = PA_CHANNEL_POSITION_LFE;
+ goto finish;
+ } else if (strcmp(s, "surround-50") == 0) {
+ map.channels = 5;
+ map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+ map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ goto finish;
+ } else if (strcmp(s, "surround-51") == 0) {
+ map.channels = 6;
+ map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+ map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ map.map[5] = PA_CHANNEL_POSITION_LFE;
+ goto finish;
+ } else if (strcmp(s, "surround-71") == 0) {
+ map.channels = 8;
+ map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+ map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ map.map[5] = PA_CHANNEL_POSITION_LFE;
+ map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+ map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+ goto finish;
}
state = NULL;
@@ -577,3 +625,155 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s
return map->channels == ss->channels;
}
+
+int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
+ pa_bitset_t in_a[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
+ unsigned i;
+
+ pa_assert(a);
+ pa_assert(b);
+
+ memset(in_a, 0, sizeof(in_a));
+
+ for (i = 0; i < a->channels; i++)
+ pa_bitset_set(in_a, a->map[i], TRUE);
+
+ for (i = 0; i < b->channels; i++)
+ if (!pa_bitset_get(in_a, b->map[i]))
+ return 0;
+
+ return 1;
+}
+
+int pa_channel_map_can_balance(const pa_channel_map *map) {
+ unsigned c;
+
+ pa_assert(map);
+
+ for (c = 0; c < map->channels; c++)
+
+ switch (map->map[c]) {
+ case PA_CHANNEL_POSITION_LEFT:
+ case PA_CHANNEL_POSITION_RIGHT:
+ case PA_CHANNEL_POSITION_REAR_LEFT:
+ case PA_CHANNEL_POSITION_REAR_RIGHT:
+ case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+ case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+ case PA_CHANNEL_POSITION_SIDE_LEFT:
+ case PA_CHANNEL_POSITION_SIDE_RIGHT:
+ case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
+ case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
+ case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
+ case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
+ return 1;
+
+ default:
+ ;
+ }
+
+ return 0;
+}
+
+const char* pa_channel_map_to_name(const pa_channel_map *map) {
+ pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
+ unsigned c;
+
+ pa_assert(map);
+
+ memset(in_map, 0, sizeof(in_map));
+
+ for (c = 0; c < map->channels; c++)
+ pa_bitset_set(in_map, map->map[c], TRUE);
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_MONO, -1))
+ return "mono";
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
+ return "stereo";
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
+ return "surround-40";
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_LFE, -1))
+ return "surround-41";
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_CENTER, -1))
+ return "surround-50";
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
+ return "surround-51";
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+ PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
+ return "surround-71";
+
+ return NULL;
+}
+
+const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) {
+ pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
+ unsigned c;
+
+ pa_assert(map);
+
+ memset(in_map, 0, sizeof(in_map));
+
+ for (c = 0; c < map->channels; c++)
+ pa_bitset_set(in_map, map->map[c], TRUE);
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_MONO, -1))
+ return _("Mono");
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
+ return _("Stereo");
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
+ return _("Surround 4.0");
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_LFE, -1))
+ return _("Surround 4.1");
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_CENTER, -1))
+ return _("Surround 5.0");
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
+ return _("Surround 5.1");
+
+ if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+ PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+ PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
+ return _("Surround 7.1");
+
+ return NULL;
+}
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index d7d19d79..de2d712a 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -26,6 +26,7 @@
#include <pulse/sample.h>
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
+#include <pulse/version.h>
/** \page channelmap Channel Maps
*
@@ -214,7 +215,10 @@ const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos);
/** Make a humand readable string from the specified channel map */
char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
-/** Parse a channel position list into a channel map structure. */
+/** Parse a channel position list or well-known mapping name into a
+ * channel map structure. This turns the output of
+ * pa_channel_map_snprint() and pa_channel_map_to_name() back into a
+ * pa_channel_map */
pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s);
/** Compare two channel maps. Return 1 if both match. */
@@ -227,6 +231,25 @@ int pa_channel_map_valid(const pa_channel_map *map) PA_GCC_PURE;
* the specified sample spec. \since 0.9.12 */
int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) PA_GCC_PURE;
+/** Returns non-zero if every channel defined in b is also defined in a. \since 0.9.15 */
+int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE;
+
+/** Returns non-zero if it makes sense to apply a volume 'balance'
+ * with this mapping, i.e. if there are left/right channels
+ * available. \since 0.9.15 */
+int pa_channel_map_can_balance(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Tries to find a well-known channel mapping name for this channel
+ * mapping. I.e. "stereo", "surround-71" and so on. If the channel
+ * mapping is unknown NULL will be returned. This name can be parsed
+ * with pa_channel_map_parse() \since 0.9.15 */
+const char* pa_channel_map_to_name(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Tries to find a human readable text label for this channel
+mapping. I.e. "Stereo", "Surround 7.1" and so on. If the channel
+mapping is unknown NULL will be returned. \since 0.9.15 */
+const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) PA_GCC_PURE;
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 3145d9c8..d41e62e2 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -366,7 +366,8 @@ int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa
if (command == PA_COMMAND_ERROR) {
pa_assert(t);
- if (pa_tagstruct_getu32(t, &err) < 0) {
+ if (pa_tagstruct_getu32(t, &err) < 0 ||
+ !pa_tagstruct_eof(t)) {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
diff --git a/src/pulse/context.h b/src/pulse/context.h
index 3b513976..dfb7e4a1 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -29,6 +29,7 @@
#include <pulse/cdecl.h>
#include <pulse/operation.h>
#include <pulse/proplist.h>
+#include <pulse/version.h>
/** \page async Asynchronous API
*
@@ -232,14 +233,14 @@ uint32_t pa_context_get_protocol_version(pa_context *c);
/** Return the protocol version of the connected server. */
uint32_t pa_context_get_server_protocol_version(pa_context *c);
-/* Update the property list of the client, adding new entries. Please
+/** Update the property list of the client, adding new entries. Please
* note that it is highly recommended to set as much properties
* initially via pa_context_new_with_proplist() as possible instead a
* posteriori with this function, since that information may then be
* used to route streams of the client to the right device. \since 0.9.11 */
pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata);
-/* Update the property list of the client, remove entries. \since 0.9.11 */
+/** Update the property list of the client, remove entries. \since 0.9.11 */
pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata);
/** Return the client index this context is
diff --git a/src/pulse/def.h b/src/pulse/def.h
index ace56574..7517a7aa 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -29,6 +29,7 @@
#include <pulse/cdecl.h>
#include <pulse/sample.h>
+#include <pulse/version.h>
/** \file
* Global definitions */
@@ -46,7 +47,7 @@ typedef enum pa_context_state {
PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
} pa_context_state_t;
-/** Return non-zero if the passed state is one of the connected states */
+/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
return
x == PA_CONTEXT_CONNECTING ||
@@ -55,6 +56,10 @@ static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
x == PA_CONTEXT_READY;
}
+/** \cond fulldocs */
+#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD
+/** \endcond */
+
/** The state of a stream */
typedef enum pa_stream_state {
PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */
@@ -64,13 +69,17 @@ typedef enum pa_stream_state {
PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */
} pa_stream_state_t;
-/** Return non-zero if the passed state is one of the connected states */
+/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
return
x == PA_STREAM_CREATING ||
x == PA_STREAM_READY;
}
+/** \cond fulldocs */
+#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD
+/** \endcond */
+
/** The state of an operation */
typedef enum pa_operation_state {
PA_OPERATION_RUNNING, /**< The operation is still running */
@@ -226,13 +235,13 @@ typedef enum pa_stream_flags {
PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND = 0x8000U,
/**< If set this stream won't be taken into account when we it is
* checked whether the device this stream is connected to should
- * auto-suspend. \ since 0.9.14 */
+ * auto-suspend. \since 0.9.15 */
PA_STREAM_START_UNMUTED = 0x10000U
/**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED
* nor PA_STREAM_START_MUTED it is left to the server to decide
* whether to create the stream in muted or in unmuted
- * state. \since 0.9.14 */
+ * state. \since 0.9.15 */
} pa_stream_flags_t;
@@ -348,6 +357,7 @@ enum {
PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */
PA_ERR_UNKNOWN, /**< The error code was unknown to the client */
PA_ERR_NOEXTENSION, /**< Extension does not exist. \since 0.9.12 */
+ PA_ERR_OBSOLETE, /**< Obsolete functionality. \since 0.9.15 */
PA_ERR_MAX /**< Not really an error but the first invalid error code */
};
@@ -380,10 +390,15 @@ typedef enum pa_subscription_mask {
PA_SUBSCRIPTION_MASK_SERVER = 0x0080U,
/**< Other global server changes. */
+/** \cond fulldocs */
PA_SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U,
- /**< Autoload table events. */
+ /**< \deprecated Autoload table events. */
+/** \endcond */
+
+ PA_SUBSCRIPTION_MASK_CARD = 0x0200U,
+ /**< Card events. \since 0.9.15 */
- PA_SUBSCRIPTION_MASK_ALL = 0x01ffU
+ PA_SUBSCRIPTION_MASK_ALL = 0x02ffU
/**< Catch all events */
} pa_subscription_mask_t;
@@ -413,8 +428,13 @@ typedef enum pa_subscription_event_type {
PA_SUBSCRIPTION_EVENT_SERVER = 0x0007U,
/**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */
+/** \cond fulldocs */
PA_SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U,
- /**< Event type: Autoload table changes. */
+ /**< \deprecated Event type: Autoload table changes. */
+/** \endcond */
+
+ PA_SUBSCRIPTION_EVENT_CARD = 0x0009U,
+ /**< Event type: Card \since 0.9.15 */
PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 0x000FU,
/**< A mask to extract the event type from an event value */
@@ -428,7 +448,7 @@ typedef enum pa_subscription_event_type {
PA_SUBSCRIPTION_EVENT_REMOVE = 0x0020U,
/**< An object was removed */
- PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U,
+ PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
/**< A mask to extract the event operation from an event value */
} pa_subscription_event_type_t;
@@ -580,9 +600,13 @@ typedef enum pa_sink_flags {
PA_SINK_HW_MUTE_CTRL = 0x0010U,
/**< Supports hardware mute control \since 0.9.11 */
- PA_SINK_DECIBEL_VOLUME = 0x0020U
+ PA_SINK_DECIBEL_VOLUME = 0x0020U,
/**< Volume can be translated to dB with pa_sw_volume_to_dB()
* \since 0.9.11 */
+
+ PA_SINK_FLAT_VOLUME = 0x0040U
+ /**< This sink is in flat volume mode, i.e. always the maximum of
+ * the volume of all connected inputs. \since 0.9.15 */
} pa_sink_flags_t;
/** \cond fulldocs */
@@ -590,8 +614,52 @@ typedef enum pa_sink_flags {
#define PA_SINK_LATENCY PA_SINK_LATENCY
#define PA_SINK_HARDWARE PA_SINK_HARDWARE
#define PA_SINK_NETWORK PA_SINK_NETWORK
-#define PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
+#define PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
#define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
+#define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
+/** \endcond */
+
+/** Sink state. \since 0.9.15 */
+typedef enum pa_sink_state { /* enum serialized in u8 */
+ PA_SINK_INVALID_STATE = -1,
+ /**< This state is used when the server does not support sink state introspection \since 0.9.15 */
+
+ PA_SINK_RUNNING = 0,
+ /**< Running, sink is playing and used by at least one non-corked sink-input \since 0.9.15 */
+
+ PA_SINK_IDLE = 1,
+ /**< When idle, the sink is playing but there is no non-corked sink-input attached to it \since 0.9.15 */
+
+ PA_SINK_SUSPENDED = 2,
+ /**< When suspended, actual sink access can be closed, for instance \since 0.9.15 */
+
+/** \cond fulldocs */
+ /* PRIVATE: Server-side values -- DO NOT USE THIS ON THE CLIENT
+ * SIDE! These values are *not* considered part of the official PA
+ * API/ABI. If you use them your application might break when PA
+ * is upgraded. Also, please note that these values are not useful
+ * on the client side anyway. */
+
+ PA_SINK_INIT = -2,
+ /**< Initialization state */
+
+ PA_SINK_UNLINKED = -3
+ /**< The state when the sink is getting unregistered and removed from client access */
+/** \endcond */
+
+} pa_sink_state_t;
+
+/** Returns non-zero if sink is playing: running or idle. \since 0.9.15 */
+static inline int PA_SINK_IS_OPENED(pa_sink_state_t x) {
+ return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
+}
+
+/** \cond fulldocs */
+#define PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
+#define PA_SINK_RUNNING PA_SINK_RUNNING
+#define PA_SINK_IDLE PA_SINK_IDLE
+#define PA_SINK_SUSPENDED PA_SINK_SUSPENDED
+#define PA_SINK_IS_OPENED PA_SINK_IS_OPENED
/** \endcond */
/** Special source flags. */
@@ -607,7 +675,7 @@ typedef enum pa_source_flags {
* "virtual"/software source \since 0.9.3 */
PA_SOURCE_NETWORK = 0x0008U,
- /**< Is a networked sink of some kind. \since 0.9.7 */
+ /**< Is a networked source of some kind. \since 0.9.7 */
PA_SOURCE_HW_MUTE_CTRL = 0x0010U,
/**< Supports hardware mute control \since 0.9.11 */
@@ -622,10 +690,53 @@ typedef enum pa_source_flags {
#define PA_SOURCE_LATENCY PA_SOURCE_LATENCY
#define PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
#define PA_SOURCE_NETWORK PA_SOURCE_NETWORK
-#define PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
+#define PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
#define PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
/** \endcond */
+/** Source state. \since 0.9.15 */
+typedef enum pa_source_state {
+ PA_SOURCE_INVALID_STATE = -1,
+ /**< This state is used when the server does not support source state introspection \since 0.9.15 */
+
+ PA_SOURCE_RUNNING = 0,
+ /**< Running, source is recording and used by at least one non-corked source-output \since 0.9.15 */
+
+ PA_SOURCE_IDLE = 1,
+ /**< When idle, the source is still recording but there is no non-corked source-output \since 0.9.15 */
+
+ PA_SOURCE_SUSPENDED = 2,
+ /**< When suspended, actual source access can be closed, for instance \since 0.9.15 */
+
+/** \cond fulldocs */
+ /* PRIVATE: Server-side values -- DO NOT USE THIS ON THE CLIENT
+ * SIDE! These values are *not* considered part of the official PA
+ * API/ABI. If you use them your application might break when PA
+ * is upgraded. Also, please note that these values are not useful
+ * on the client side anyway. */
+
+ PA_SOURCE_INIT = -2,
+ /**< Initialization state */
+
+ PA_SOURCE_UNLINKED = -3
+ /**< The state when the source is getting unregistered and removed from client access */
+/** \endcond */
+
+} pa_source_state_t;
+
+/** Returns non-zero if source is recording: running or idle. \since 0.9.15 */
+static inline int PA_SOURCE_IS_OPENED(pa_source_state_t x) {
+ return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
+}
+
+/** \cond fulldocs */
+#define PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
+#define PA_SOURCE_RUNNING PA_SOURCE_RUNNING
+#define PA_SOURCE_IDLE PA_SOURCE_IDLE
+#define PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
+#define PA_SOURCE_IS_OPENED PA_SOURCE_IS_OPENED
+/** \endcond */
+
/** A generic free() like callback prototype */
typedef void (*pa_free_cb_t)(void *p);
diff --git a/src/pulse/error.h b/src/pulse/error.h
index 9f9e3d33..c30b80bd 100644
--- a/src/pulse/error.h
+++ b/src/pulse/error.h
@@ -25,6 +25,7 @@
#include <inttypes.h>
#include <pulse/cdecl.h>
+#include <pulse/version.h>
/** \file
* Error management */
diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h
index 2038eb4a..ac01d235 100644
--- a/src/pulse/ext-stream-restore.h
+++ b/src/pulse/ext-stream-restore.h
@@ -23,6 +23,7 @@
***/
#include <pulse/context.h>
+#include <pulse/version.h>
/** \file
*
diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h
index 0533b109..0b1a1a66 100644
--- a/src/pulse/gccmacro.h
+++ b/src/pulse/gccmacro.h
@@ -22,6 +22,9 @@
USA.
***/
+/** \file
+ * GCC attribute macros */
+
#ifdef __GNUC__
#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
#else
@@ -100,6 +103,7 @@
#else
/** Macro for usage of GCC's alloc_size attribute */
#define PA_GCC_ALLOC_SIZE(x)
+/** Macro for usage of GCC's alloc_size attribute */
#define PA_GCC_ALLOC_SIZE2(x,y)
#endif
#endif
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
index 60fd61a3..fd68f8ac 100644
--- a/src/pulse/glib-mainloop.h
+++ b/src/pulse/glib-mainloop.h
@@ -27,6 +27,7 @@
#include <pulse/mainloop-api.h>
#include <pulse/cdecl.h>
+#include <pulse/version.h>
/** \page glib-mainloop GLIB Main Loop Bindings
*
diff --git a/src/pulse/i18n.h b/src/pulse/i18n.h
index 4c0ef9d3..f91c0bf9 100644
--- a/src/pulse/i18n.h
+++ b/src/pulse/i18n.h
@@ -24,6 +24,7 @@
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
+#include <pulse/version.h>
PA_C_DECL_BEGIN
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 5fe4210e..9a2d6457 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -225,20 +225,37 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag);
-#define PA_CHECK_VALIDITY(context, expression, error) do { \
- if (!(expression)) \
+#define PA_CHECK_VALIDITY(context, expression, error) \
+ do { \
+ if (!(expression)) \
return -pa_context_set_error((context), (error)); \
-} while(0)
+ } while(FALSE)
-#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) do { \
- if (!(expression)) { \
- pa_context_set_error((context), (error)); \
- return value; \
- } \
-} while(0)
+#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \
+ do { \
+ if (!(expression)) { \
+ pa_context_set_error((context), (error)); \
+ return value; \
+ } \
+ } while(FALSE)
-#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
+#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) \
+ PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
+
+#define PA_FAIL(context, error) \
+ do { \
+ return -pa_context_set_error((context), (error)); \
+ } while(FALSE)
+
+#define PA_FAIL_RETURN_ANY(context, error, value) \
+ do { \
+ pa_context_set_error((context), (error)); \
+ return value; \
+ } while(FALSE)
+
+#define PA_FAIL_RETURN_NULL(context, error) \
+ PA_FAIL_RETURN_ANY(context, error, NULL)
void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 8056a5a1..1d50939c 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -28,8 +28,10 @@
#include <pulse/context.h>
#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include <pulsecore/pstream-util.h>
#include "internal.h"
@@ -60,7 +62,8 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, uint32_t t
pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 ||
pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 ||
pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 ||
- pa_tagstruct_getu32(t, &i.scache_size) < 0) {
+ pa_tagstruct_getu32(t, &i.scache_size) < 0 ||
+ !pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
@@ -146,15 +149,19 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
eol = -1;
} else {
- uint32_t flags;
while (!pa_tagstruct_eof(t)) {
pa_sink_info i;
- pa_bool_t mute = FALSE;
+ pa_bool_t mute;
+ uint32_t flags;
+ uint32_t state;
memset(&i, 0, sizeof(i));
i.proplist = pa_proplist_new();
i.base_volume = PA_VOLUME_NORM;
+ i.n_volume_steps = PA_VOLUME_NORM+1;
+ mute = FALSE;
+ state = PA_SINK_INVALID_STATE;
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -173,7 +180,9 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
(pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) ||
(o->context->version >= 15 &&
- pa_tagstruct_get_volume(t, &i.base_volume) < 0)) {
+ (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
+ pa_tagstruct_getu32(t, &state) < 0 ||
+ pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
@@ -182,6 +191,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
i.mute = (int) mute;
i.flags = (pa_sink_flags_t) flags;
+ i.state = (pa_sink_state_t) state;
if (o->callback) {
pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
@@ -273,12 +283,16 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_source_info i;
+ pa_bool_t mute;
uint32_t flags;
- pa_bool_t mute = FALSE;
+ uint32_t state;
memset(&i, 0, sizeof(i));
i.proplist = pa_proplist_new();
i.base_volume = PA_VOLUME_NORM;
+ i.n_volume_steps = PA_VOLUME_NORM+1;
+ mute = FALSE;
+ state = PA_SOURCE_INVALID_STATE;
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -297,7 +311,9 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
(pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) ||
(o->context->version >= 15 &&
- pa_tagstruct_get_volume(t, &i.base_volume) < 0)) {
+ (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
+ pa_tagstruct_getu32(t, &state) < 0 ||
+ pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
@@ -306,6 +322,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
i.mute = (int) mute;
i.flags = (pa_source_flags_t) flags;
+ i.state = (pa_source_state_t) state;
if (o->callback) {
pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
@@ -457,6 +474,201 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t
return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_cb_t) cb, userdata);
}
+/*** Card info ***/
+
+static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eol = 1;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+ goto finish;
+
+ eol = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_card_info i;
+ uint32_t j;
+ const char*ap;
+
+ memset(&i, 0, sizeof(i));
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0 ||
+ pa_tagstruct_getu32(t, &i.n_profiles) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (i.n_profiles > 0) {
+ i.profiles = pa_xnew(pa_card_profile_info, i.n_profiles+1);
+
+ for (j = 0; j < i.n_profiles; j++) {
+
+ if (pa_tagstruct_gets(t, &i.profiles[j].name) < 0 ||
+ pa_tagstruct_gets(t, &i.profiles[j].description) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_xfree(i.profiles);
+ goto finish;
+ }
+ }
+
+ /* Terminate with an extra NULL entry, just to make sure */
+ i.profiles[j].name = NULL;
+ i.profiles[j].description = NULL;
+ }
+
+ i.proplist = pa_proplist_new();
+
+ if (pa_tagstruct_gets(t, &ap) < 0 ||
+ pa_tagstruct_get_proplist(t, i.proplist) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_xfree(i.profiles);
+ pa_proplist_free(i.proplist);
+ goto finish;
+ }
+
+ if (ap) {
+ for (j = 0; j < i.n_profiles; j++)
+ if (pa_streq(i.profiles[j].name, ap)) {
+ i.active_profile = &i.profiles[j];
+ break;
+ }
+ }
+
+ if (o->callback) {
+ pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+
+ pa_proplist_free(i.proplist);
+ pa_xfree(i.profiles);
+ }
+ }
+
+ if (o->callback) {
+ pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback;
+ cb(o->context, NULL, eol, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_CARD_INFO, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_card_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char*name, pa_card_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_CARD_INFO, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_card_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_CARD_INFO_LIST, context_get_card_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_CARD_PROFILE, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_tagstruct_puts(t, profile);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char *name, const char*profile, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_CARD_PROFILE, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_puts(t, profile);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
/*** Module info ***/
static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -480,13 +692,16 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_module_info i;
pa_bool_t auto_unload = FALSE;
+
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_gets(t, &i.argument) < 0 ||
pa_tagstruct_getu32(t, &i.n_used) < 0 ||
- pa_tagstruct_get_boolean(t, &auto_unload) < 0) {
+ (o->context->version < 15 && pa_tagstruct_get_boolean(t, &auto_unload) < 0) ||
+ (o->context->version >= 15 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
@@ -497,6 +712,8 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -1167,186 +1384,59 @@ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_s
/*** Autoload stuff ***/
-static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- pa_operation *o = userdata;
- int eol = 1;
-
- pa_assert(pd);
- pa_assert(o);
- pa_assert(PA_REFCNT_VALUE(o) >= 1);
-
- if (!o->context)
- goto finish;
-
- if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
- goto finish;
-
- eol = -1;
- } else {
-
- while (!pa_tagstruct_eof(t)) {
- pa_autoload_info i;
-
- memset(&i, 0, sizeof(i));
-
- if (pa_tagstruct_getu32(t, &i.index) < 0 ||
- pa_tagstruct_gets(t, &i.name) < 0 ||
- pa_tagstruct_getu32(t, &i.type) < 0 ||
- pa_tagstruct_gets(t, &i.module) < 0 ||
- pa_tagstruct_gets(t, &i.argument) < 0) {
- pa_context_fail(o->context, PA_ERR_PROTOCOL);
- goto finish;
- }
-
- if (o->callback) {
- pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback;
- cb(o->context, &i, 0, o->userdata);
- }
- }
- }
-
- if (o->callback) {
- pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback;
- cb(o->context, NULL, eol, o->userdata);
- }
-
-finish:
- pa_operation_done(o);
- pa_operation_unref(o);
-}
-
-PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Module auto-loading no longer supported.");
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) {
- pa_tagstruct *t;
- pa_operation *o;
- uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- pa_assert(cb);
-
- PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
- PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
- t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag);
- pa_tagstruct_puts(t, name);
- pa_tagstruct_putu32(t, type);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
- return o;
+ PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
}
-PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Module auto-loading no longer supported.");
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) {
- pa_tagstruct *t;
- pa_operation *o;
- uint32_t tag;
-
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- pa_assert(cb);
-
- PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
-
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
- t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag);
- pa_tagstruct_putu32(t, idx);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
- return o;
+ PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
}
-
-PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Module auto-loading no longer supported.");
pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) {
- return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
}
-PA_WARN_REFERENCE(pa_context_add_autoload, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_add_autoload, "Module auto-loading no longer supported.");
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) {
- pa_operation *o;
- pa_tagstruct *t;
- uint32_t tag;
-
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
- PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
- PA_CHECK_VALIDITY_RETURN_NULL(c, module && *module, PA_ERR_INVALID);
-
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
- t = pa_tagstruct_command(c, PA_COMMAND_ADD_AUTOLOAD, &tag);
- pa_tagstruct_puts(t, name);
- pa_tagstruct_putu32(t, type);
- pa_tagstruct_puts(t, module);
- pa_tagstruct_puts(t, argument);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
- return o;
+ PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
}
-PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Module auto-loading no longer supported.");
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) {
- pa_operation *o;
- pa_tagstruct *t;
- uint32_t tag;
-
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
- PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
-
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
- t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag);
- pa_tagstruct_puts(t, name);
- pa_tagstruct_putu32(t, type);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
- return o;
+ PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
}
-PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Module auto-loading no longer supported.");
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) {
- pa_operation *o;
- pa_tagstruct *t;
- uint32_t tag;
-
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
-
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
- t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag);
- pa_tagstruct_putu32(t, idx);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
- return o;
+ PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
}
pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) {
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index b409cadb..badc787e 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -32,6 +32,7 @@
#include <pulse/channelmap.h>
#include <pulse/volume.h>
#include <pulse/proplist.h>
+#include <pulse/version.h>
/** \page introspect Server Query and Control
*
@@ -128,18 +129,6 @@
* pa_context_get_module_info() or pa_context_get_module_info_list(). The
* information structure is called pa_module_info.
*
- * \subsection autoload_subsec Autoload Entries
- *
- * Modules can be autoloaded as a result of a client requesting a
- * certain sink or source. Please note that autoloading is deprecated
- * in 0.9.11. and is likely to be removed from the API in a later
- * version. This mapping between sink/source names and modules can be
- * queried from the server:
- *
- * \li By index - pa_context_get_autoload_info_by_index()
- * \li By sink/source name - pa_context_get_autoload_info_by_name()
- * \li All - pa_context_get_autoload_info_list()
- *
* \subsection client_subsec Clients
*
* PulseAudio clients are also identified by index and are retrieved using
@@ -189,14 +178,6 @@
* Server modules can be remotely loaded and unloaded using
* pa_context_load_module() and pa_context_unload_module().
*
- * \subsection autoload_subsec Autoload Entries
- *
- * New module autoloading rules can be added, and existing can be removed
- * using pa_context_add_autoload() and pa_context_remove_autoload_by_index()
- * / pa_context_remove_autoload_by_name(). Please note that autoloading is deprecated
- * in 0.9.11. and is likely to be removed from the API in a later
- * version.
- *
* \subsection client_subsec Clients
*
* The only operation supported on clients, is the possibility of kicking
@@ -231,7 +212,9 @@ typedef struct pa_sink_info {
pa_sink_flags_t flags; /**< Flags */
pa_proplist *proplist; /**< Property list \since 0.9.11 */
pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */
- pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.14 */
+ pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.15 */
+ pa_sink_state_t state; /**< State \since 0.9.15 */
+ uint32_t n_volume_steps; /**< Number of volume steps for sinks which do not support arbitrary volumes. \since 0.9.15 */
} pa_sink_info;
/** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@@ -241,7 +224,7 @@ typedef void (*pa_sink_info_cb_t)(pa_context *c, const pa_sink_info *i, int eol,
pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata);
/** Get information about a sink by its index */
-pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_sink_info_cb_t cb, void *userdata);
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata);
/** Get the complete sink list */
pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata);
@@ -287,7 +270,9 @@ typedef struct pa_source_info {
pa_source_flags_t flags; /**< Flags */
pa_proplist *proplist; /**< Property list \since 0.9.11 */
pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */
- pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.14 */
+ pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */
+ pa_source_state_t state; /**< State \since 0.9.15 */
+ uint32_t n_volume_steps; /**< Number of volume steps for sources which do not support arbitrary volumes. \since 0.9.15 */
} pa_source_info;
/** Callback prototype for pa_context_get_source_info_by_name() and friends */
@@ -297,7 +282,7 @@ typedef void (*pa_source_info_cb_t)(pa_context *c, const pa_source_info *i, int
pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata);
/** Get information about a source by its index */
-pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa_source_info_cb_t cb, void *userdata);
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata);
/** Get the complete source list */
pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata);
@@ -350,7 +335,10 @@ typedef struct pa_module_info {
const char*name, /**< Name of the module */
*argument; /**< Argument string of the module */
uint32_t n_used; /**< Usage counter or PA_INVALID_INDEX */
- int auto_unload; /**< Non-zero if this is an autoloaded module */
+/** \cond fulldocs */
+ int auto_unload; /**< \deprecated Non-zero if this is an autoloaded module */
+/** \endcond */
+ pa_proplist *proplist; /**< Property list \since 0.9.15 */
} pa_module_info;
/** Callback prototype for pa_context_get_module_info() and firends*/
@@ -400,6 +388,50 @@ pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_suc
/** @} */
+/** @{ \name Cards */
+
+/** Stores information about a specific profile of a card. Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release. \since 0.9.15 */
+typedef struct pa_card_profile_info {
+ const char *name; /**< Name of this profile */
+ const char *description; /**< Description of this profile */
+} pa_card_profile_info;
+
+/** Stores information about cards. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. \since 0.9.15 */
+typedef struct pa_card_info {
+ uint32_t index; /**< Index of this card */
+ const char *name; /**< Name of this card */
+ uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */
+ const char *driver; /**< Driver name */
+ uint32_t n_profiles; /**< Number of entries in profile array */
+ pa_card_profile_info* profiles; /**< Array of available profile, or NULL. Array is terminated by an entry with name set to NULL. Number of entries is stored in n_profiles */
+ pa_card_profile_info* active_profile; /**< Pointer to active profile in the array, or NULL */
+ pa_proplist *proplist; /**< Property list */
+} pa_card_info;
+
+/** Callback prototype for pa_context_get_card_info() and firends \since 0.9.15 */
+typedef void (*pa_card_info_cb_t) (pa_context *c, const pa_card_info*i, int eol, void *userdata);
+
+/** Get information about a card by its index \since 0.9.15 */
+pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata);
+
+/** Get information about a card by its name \since 0.9.15 */
+pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata);
+
+/** Get the complete card list \since 0.9.15 */
+pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata);
+
+/** Change the profile of a card. \since 0.9.15 */
+pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata);
+
+/** Change the profile of a card. \since 0.9.15 */
+pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
/** @{ \name Sink Inputs */
/** Stores information about sink inputs. Please note that this structure
@@ -551,13 +583,13 @@ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t
/** @{ \name Autoload Entries */
-/** Type of an autoload entry. */
+/** \deprecated Type of an autoload entry. */
typedef enum pa_autoload_type {
PA_AUTOLOAD_SINK = 0,
PA_AUTOLOAD_SOURCE = 1
} pa_autoload_type_t;
-/** Stores information about autoload entries. Please note that this structure
+/** \deprecated Stores information about autoload entries. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_autoload_info {
@@ -568,25 +600,25 @@ typedef struct pa_autoload_info {
const char *argument; /**< Argument string for module */
} pa_autoload_info;
-/** Callback prototype for pa_context_get_autoload_info_by_name() and firends */
+/** \deprecated Callback prototype for pa_context_get_autoload_info_by_name() and firends */
typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);
-/** Get info about a specific autoload entry. */
+/** \deprecated Get info about a specific autoload entry. */
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
-/** Get info about a specific autoload entry. */
+/** \deprecated Get info about a specific autoload entry. */
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
-/** Get the complete list of autoload entries. */
+/** \deprecated Get the complete list of autoload entries. */
pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
-/** Add a new autoload entry. */
+/** \deprecated Add a new autoload entry. */
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata) PA_GCC_DEPRECATED;
-/** Remove an autoload entry. */
+/** \deprecated Remove an autoload entry. */
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
-/** Remove an autoload entry. */
+/** \deprecated Remove an autoload entry. */
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
/** @} */
diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h
index 53c7411e..e353ed96 100644
--- a/src/pulse/mainloop-api.h
+++ b/src/pulse/mainloop-api.h
@@ -27,6 +27,7 @@
#include <time.h>
#include <pulse/cdecl.h>
+#include <pulse/version.h>
/** \file
*
diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h
index a6c16f2f..a9e250bc 100644
--- a/src/pulse/mainloop-signal.h
+++ b/src/pulse/mainloop-signal.h
@@ -40,8 +40,10 @@ PA_C_DECL_BEGIN
/** An opaque UNIX signal event source object */
typedef struct pa_signal_event pa_signal_event;
+/** Callback prototype for signal events */
typedef void (*pa_signal_cb_t) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata);
+/** Destroy callback prototype for signal events */
typedef void (*pa_signal_destroy_cb_t) (pa_mainloop_api *api, pa_signal_event*e, void *userdata);
/** Initialize the UNIX signal subsystem and bind it to the specified main loop */
diff --git a/src/pulse/operation.h b/src/pulse/operation.h
index 188e2cb9..b68e7816 100644
--- a/src/pulse/operation.h
+++ b/src/pulse/operation.h
@@ -24,6 +24,7 @@
#include <pulse/cdecl.h>
#include <pulse/def.h>
+#include <pulse/version.h>
/** \file
* Asynchronous operations */
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index 93bc0034..282fe5cc 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -259,21 +259,24 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state) {
return prop->key;
}
-char *pa_proplist_to_string(pa_proplist *p) {
+char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) {
const char *key;
void *state = NULL;
pa_strbuf *buf;
pa_assert(p);
+ pa_assert(sep);
buf = pa_strbuf_new();
while ((key = pa_proplist_iterate(p, &state))) {
-
const char *v;
+ if (!pa_strbuf_isempty(buf))
+ pa_strbuf_puts(buf, sep);
+
if ((v = pa_proplist_gets(p, key)))
- pa_strbuf_printf(buf, "%s = \"%s\"\n", key, v);
+ pa_strbuf_printf(buf, "%s = \"%s\"", key, v);
else {
const void *value;
size_t nbytes;
@@ -283,7 +286,7 @@ char *pa_proplist_to_string(pa_proplist *p) {
c = pa_xmalloc(nbytes*2+1);
pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
- pa_strbuf_printf(buf, "%s = hex:%s\n", key, c);
+ pa_strbuf_printf(buf, "%s = hex:%s", key, c);
pa_xfree(c);
}
}
@@ -291,6 +294,100 @@ char *pa_proplist_to_string(pa_proplist *p) {
return pa_strbuf_tostring_free(buf);
}
+char *pa_proplist_to_string(pa_proplist *p) {
+ char *s, *t;
+
+ s = pa_proplist_to_string_sep(p, "\n");
+ t = pa_sprintf_malloc("%s\n", s);
+ pa_xfree(s);
+
+ return t;
+}
+
+/* Remove all whitepsapce from the beginning and the end of *s. *s may
+ * be modified. (from conf-parser.c) */
+#define WHITESPACE " \t\n"
+#define in_string(c,s) (strchr(s,c) != NULL)
+
+static char *strip(char *s) {
+ char *b = s+strspn(s, WHITESPACE);
+ char *e, *l = NULL;
+
+ for (e = b; *e; e++)
+ if (!in_string(*e, WHITESPACE))
+ l = e;
+
+ if (l)
+ *(l+1) = 0;
+
+ return b;
+}
+
+pa_proplist *pa_proplist_from_string(const char *str) {
+ pa_proplist *p;
+ char *s, *v, *k, *e;
+
+ pa_assert(str);
+ pa_assert_se(p = pa_proplist_new());
+ pa_assert_se(s = strdup(str));
+
+ for (k = s; *k; k = e) {
+ k = k+strspn(k, WHITESPACE);
+
+ if (!*k)
+ break;
+
+ if (!(v = strchr(k, '='))) {
+ pa_log("Missing '='.");
+ break;
+ }
+
+ *v++ = '\0';
+ k = strip(k);
+
+ v = v+strspn(v, WHITESPACE);
+ if (*v == '"') {
+ v++;
+ if (!(e = strchr(v, '"'))) { /* FIXME: handle escape */
+ pa_log("Missing '\"' at end of string value.");
+ break;
+ }
+ *e++ = '\0';
+ pa_proplist_sets(p, k, v);
+ } else {
+ uint8_t *blob;
+
+ if (*v++ != 'h' || *v++ != 'e' || *v++ != 'x' || *v++ != ':') {
+ pa_log("Value must be a string or \"hex:\"");
+ break;
+ }
+
+ e = v;
+ while (in_string(*e, "0123456789abcdefABCDEF"))
+ ++e;
+
+ if ((e - v) % 2) {
+ pa_log("Invalid \"hex:\" value data");
+ break;
+ }
+
+ blob = pa_xmalloc((size_t)(e-v)/2);
+ if (pa_parsehex(v, blob, (e-v)/2) != (size_t)((e-v)/2)) {
+ pa_log("Invalid \"hex:\" value data");
+ pa_xfree(blob);
+ break;
+ }
+
+ pa_proplist_set(p, k, blob, (e-v)/2);
+ pa_xfree(blob);
+ }
+ }
+
+ pa_xfree(s);
+
+ return p;
+}
+
int pa_proplist_contains(pa_proplist *p, const char *key) {
pa_assert(p);
pa_assert(key);
@@ -322,3 +419,15 @@ pa_proplist* pa_proplist_copy(pa_proplist *template) {
return p;
}
+
+unsigned pa_proplist_size(pa_proplist *p) {
+ pa_assert(p);
+
+ return pa_hashmap_size(MAKE_HASHMAP(p));
+}
+
+int pa_proplist_isempty(pa_proplist *p) {
+ pa_assert(p);
+
+ return pa_hashmap_isempty(MAKE_HASHMAP(p));
+}
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index c23ef238..203a28c5 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -26,6 +26,7 @@
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
+#include <pulse/version.h>
PA_C_DECL_BEGIN
@@ -75,8 +76,10 @@ PA_C_DECL_BEGIN
* device.connector isa, pci, usb, firewire, bluetooth
* device.access_mode mmap, mmap_rewrite, serial
* device.master_device
- * device.bufferin.buffer_size
- * device.bufferin.fragment_size
+ * device.buffering.buffer_size
+ * device.buffering.fragment_size
+ * device.profile.name analog-stereo, analog-surround-40, iec958-stereo, ...
+ * device.profile.description "Analog Stereo", ...
*/
#define PA_PROP_MEDIA_NAME "media.name"
#define PA_PROP_MEDIA_TITLE "media.title"
@@ -124,6 +127,12 @@ PA_C_DECL_BEGIN
#define PA_PROP_DEVICE_MASTER_DEVICE "device.master_device"
#define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE "device.buffering.buffer_size"
#define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size"
+#define PA_PROP_DEVICE_PROFILE_NAME "device.profile.name"
+#define PA_PROP_DEVICE_PROFILE_DESCRIPTION "device.profile.description"
+#define PA_PROP_MODULE_AUTHOR "module.author"
+#define PA_PROP_MODULE_DESCRIPTION "module.description"
+#define PA_PROP_MODULE_USAGE "module.usage"
+#define PA_PROP_MODULE_VERSION "module.version"
/** A property list object. Basically a dictionary with UTF-8 strings
* as keys and arbitrary data as values. \since 0.9.11 */
@@ -153,7 +162,7 @@ int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) P
* internal copy of the data passed is made. \since 0.9.11 */
int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes);
-/* Return a string entry for the specified key. Will return NULL if
+/** Return a string entry for the specified key. Will return NULL if
* the data is not valid UTF-8. Will return a NUL-terminated string in
* an internally allocated buffer. The caller should make a copy of
* the data before accessing the property list again. \since 0.9.11 */
@@ -209,11 +218,22 @@ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]);
* have any particular order. \since 0.9.11 */
const char *pa_proplist_iterate(pa_proplist *p, void **state);
-/** Format the property list nicely as a human readable string. Call pa_xfree() on the result. \since
- * 0.9.11 */
+/** Format the property list nicely as a human readable string. This
+ * works very much like pa_proplist_to_string_sep() and uses a newline
+ * as seperator and appends one final one. Call pa_xfree() on the
+ * result. \since 0.9.11 */
char *pa_proplist_to_string(pa_proplist *p);
-/** Returns 1 if an entry for the specified key is existant in the
+/** Format the property list nicely as a human readable string and
+ * choose the seperator. Call pa_xfree() on the result. \since
+ * 0.9.15 */
+char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep);
+
+/** Allocate a new property list and assign key/value from a human
+ * readable string. \since 0.9.15 */
+pa_proplist *pa_proplist_from_string(const char *str);
+
+ /** Returns 1 if an entry for the specified key is existant in the
* property list. \since 0.9.11 */
int pa_proplist_contains(pa_proplist *p, const char *key);
@@ -224,6 +244,12 @@ void pa_proplist_clear(pa_proplist *p);
* the specific list. \since 0.9.11 */
pa_proplist* pa_proplist_copy(pa_proplist *t);
+/** Return the number of entries on the property list. \since 0.9.15 */
+unsigned pa_proplist_size(pa_proplist *t);
+
+/** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */
+int pa_proplist_isempty(pa_proplist *t);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
index e09cacaf..5086783d 100644
--- a/src/pulse/pulseaudio.h
+++ b/src/pulse/pulseaudio.h
@@ -108,7 +108,6 @@
* modules:
*
* \li libpulse - The asynchronous API and the internal main loop implementation.
- * \li libpulse-mainloop-glib12 - GLIB 1.2 main loop bindings.
* \li libpulse-mainloop-glib - GLIB 2.x main loop bindings.
* \li libpulse-simple - The simple PulseAudio API.
*/
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 29501595..a6c77345 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -48,6 +48,10 @@ size_t pa_sample_size(const pa_sample_spec *spec) {
[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
};
pa_assert(spec);
@@ -125,6 +129,10 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) {
[PA_SAMPLE_FLOAT32BE] = "float32be",
[PA_SAMPLE_S32LE] = "s32le",
[PA_SAMPLE_S32BE] = "s32be",
+ [PA_SAMPLE_S24LE] = "s24le",
+ [PA_SAMPLE_S24BE] = "s24be",
+ [PA_SAMPLE_S24_32LE] = "s24-32le",
+ [PA_SAMPLE_S24_32BE] = "s24-32be",
};
if (f < 0 || f >= PA_SAMPLE_MAX)
@@ -195,7 +203,23 @@ pa_sample_format_t pa_parse_sample_format(const char *format) {
else if (strcasecmp(format, "s32ne") == 0 || strcasecmp(format, "s32") == 0 || strcasecmp(format, "32") == 0)
return PA_SAMPLE_S32NE;
else if (strcasecmp(format, "s32re") == 0)
- return PA_SAMPLE_S32RE;
+ return PA_SAMPLE_S24RE;
+ else if (strcasecmp(format, "s24le") == 0)
+ return PA_SAMPLE_S24LE;
+ else if (strcasecmp(format, "s24be") == 0)
+ return PA_SAMPLE_S24BE;
+ else if (strcasecmp(format, "s24ne") == 0 || strcasecmp(format, "s24") == 0 || strcasecmp(format, "24") == 0)
+ return PA_SAMPLE_S24NE;
+ else if (strcasecmp(format, "s24re") == 0)
+ return PA_SAMPLE_S24RE;
+ else if (strcasecmp(format, "s24-32le") == 0)
+ return PA_SAMPLE_S24LE;
+ else if (strcasecmp(format, "s24-32be") == 0)
+ return PA_SAMPLE_S24BE;
+ else if (strcasecmp(format, "s24-32ne") == 0 || strcasecmp(format, "s24-32") == 0)
+ return PA_SAMPLE_S24NE;
+ else if (strcasecmp(format, "s24-32re") == 0)
+ return PA_SAMPLE_S24RE;
return -1;
}
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 3c7dd0e7..45a481fe 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -30,6 +30,7 @@
#include <pulse/gccmacro.h>
#include <pulse/cdecl.h>
+#include <pulse/version.h>
/** \page sample Sample Format Specifications
*
@@ -51,6 +52,10 @@
* \li PA_SAMPLE_ULAW - 8 bit mu-Law.
* \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian.
* \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian.
+ * \li PA_SAMPLE_S24LE - Signed 24 bit integer PCM packed, little endian.
+ * \li PA_SAMPLE_S24BE - Signed 24 bit integer PCM packed, big endian.
+ * \li PA_SAMPLE_S24_32LE - Signed 24 bit integer PCM in LSB of 32 bit words, little endian.
+ * \li PA_SAMPLE_S24_32BE - Signed 24 bit integer PCM in LSB of 32 bit words, big endian.
*
* The floating point sample formats have the range from -1.0 to 1.0.
*
@@ -59,14 +64,14 @@
*
* \section rate_sec Sample Rates
*
- * PulseAudio supports any sample rate between 1 Hz and 4 GHz. There is no
+ * PulseAudio supports any sample rate between 1 Hz and 192000 Hz. There is no
* point trying to exceed the sample rate of the output device though as the
* signal will only get downsampled, consuming CPU on the machine running the
* server.
*
* \section chan_sec Channels
*
- * PulseAudio supports up to 16 individiual channels. The order of the
+ * PulseAudio supports up to 32 individiual channels. The order of the
* channels is up to the application, but they must be continous. To map
* channels to speakers, see \ref channelmap.
*
@@ -136,16 +141,28 @@ typedef enum pa_sample_format {
/**< Signed 16 Bit PCM, big endian */
PA_SAMPLE_FLOAT32LE,
- /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */
+ /**< 32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0 */
PA_SAMPLE_FLOAT32BE,
- /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */
+ /**< 32 Bit IEEE floating point, big endian, range -1.0 to 1.0 */
PA_SAMPLE_S32LE,
/**< Signed 32 Bit PCM, little endian (PC) */
PA_SAMPLE_S32BE,
- /**< Signed 32 Bit PCM, big endian (PC) */
+ /**< Signed 32 Bit PCM, big endian */
+
+ PA_SAMPLE_S24LE,
+ /**< Signed 24 Bit PCM packed, little endian (PC). \since 0.9.15 */
+
+ PA_SAMPLE_S24BE,
+ /**< Signed 24 Bit PCM packed, big endian. \since 0.9.15 */
+
+ PA_SAMPLE_S24_32LE,
+ /**< Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC). \since 0.9.15 */
+
+ PA_SAMPLE_S24_32BE,
+ /**< Signed 24 Bit PCM in LSB of 32 Bit words, big endian. \since 0.9.15 */
PA_SAMPLE_MAX,
/**< Upper limit of valid sample types */
@@ -161,12 +178,21 @@ typedef enum pa_sample_format {
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
/** Signed 32 Bit PCM, native endian */
#define PA_SAMPLE_S32NE PA_SAMPLE_S32BE
+/** Signed 24 Bit PCM packed, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24NE PA_SAMPLE_S24BE
+/** Signed 24 Bit PCM in LSB of 32 Bit words, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32BE
+
/** Signed 16 Bit PCM reverse endian */
#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
/** 32 Bit IEEE floating point, reverse endian */
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
-/** Signed 32 Bit PCM reverse endian */
+/** Signed 32 Bit PCM, reverse endian */
#define PA_SAMPLE_S32RE PA_SAMPLE_S32LE
+/** Signed 24 Bit PCM, packed reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24RE PA_SAMPLE_S24LE
+/** Signed 24 Bit PCM, in LSB of 32 Bit words, reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32LE
#else
/** Signed 16 Bit PCM, native endian */
#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
@@ -174,12 +200,21 @@ typedef enum pa_sample_format {
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
/** Signed 32 Bit PCM, native endian */
#define PA_SAMPLE_S32NE PA_SAMPLE_S32LE
-/** Signed 16 Bit PCM reverse endian */
+/** Signed 24 Bit PCM packed, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24NE PA_SAMPLE_S24LE
+/** Signed 24 Bit PCM in LSB of 32 Bit words, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32LE
+
+/** Signed 16 Bit PCM, reverse endian */
#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
/** 32 Bit IEEE floating point, reverse endian */
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
-/** Signed 32 Bit PCM reverse endian */
+/** Signed 32 Bit PCM, reverse endian */
#define PA_SAMPLE_S32RE PA_SAMPLE_S32BE
+/** Signed 24 Bit PCM, packed reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24RE PA_SAMPLE_S24BE
+/** Signed 24 Bit PCM, in LSB of 32 Bit words, reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32BE
#endif
/** A Shortcut for PA_SAMPLE_FLOAT32NE */
@@ -196,6 +231,10 @@ typedef enum pa_sample_format {
#define PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
#define PA_SAMPLE_S32LE PA_SAMPLE_S32LE
#define PA_SAMPLE_S32BE PA_SAMPLE_S32BE
+#define PA_SAMPLE_S24LE PA_SAMPLE_S24LE
+#define PA_SAMPLE_S24BE PA_SAMPLE_S24BE
+#define PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
+#define PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
/** \endcond */
/** A sample format and attribute specification */
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index fd3b9876..c96c42ad 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -188,6 +188,10 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char
t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, dev);
+
+ if (volume == (pa_volume_t) -1 && c->version < 15)
+ volume = PA_VOLUME_NORM;
+
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
@@ -225,6 +229,10 @@ pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *na
t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, dev);
+
+ if (volume == (pa_volume_t) -1 && c->version < 15)
+ volume = PA_VOLUME_NORM;
+
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
pa_tagstruct_put_proplist(t, p);
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
index f380b4e8..79fcfbc5 100644
--- a/src/pulse/scache.h
+++ b/src/pulse/scache.h
@@ -28,6 +28,7 @@
#include <pulse/context.h>
#include <pulse/stream.h>
#include <pulse/cdecl.h>
+#include <pulse/version.h>
/** \page scache Sample Cache
*
@@ -100,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 */ ,
+ 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_context_success_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
void *userdata /**< Userdata to pass to the callback */);
@@ -112,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 */ ,
+ 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_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.h b/src/pulse/simple.h
index a1380a0a..3f57a654 100644
--- a/src/pulse/simple.h
+++ b/src/pulse/simple.h
@@ -29,6 +29,7 @@
#include <pulse/channelmap.h>
#include <pulse/def.h>
#include <pulse/cdecl.h>
+#include <pulse/version.h>
/** \page simple Simple API
*
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index c0ae4ac2..5a29bd63 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -87,6 +87,8 @@ pa_stream *pa_stream_new_with_proplist(
PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h
index 0e4be8c3..2707cec5 100644
--- a/src/pulse/subscribe.h
+++ b/src/pulse/subscribe.h
@@ -28,6 +28,7 @@
#include <pulse/def.h>
#include <pulse/context.h>
#include <pulse/cdecl.h>
+#include <pulse/version.h>
/** \page subscribe Event Subscription
*
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index 521e29b0..4de338a1 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -25,6 +25,7 @@
#include <pulse/mainloop-api.h>
#include <pulse/cdecl.h>
+#include <pulse/version.h>
PA_C_DECL_BEGIN
diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h
index ee398296..2b3faf16 100644
--- a/src/pulse/timeval.h
+++ b/src/pulse/timeval.h
@@ -26,17 +26,29 @@
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
#include <pulse/sample.h>
+#include <pulse/version.h>
/** \file
* Utility functions for handling timeval calculations */
PA_C_DECL_BEGIN
+/** The number of milliseconds in a second */
#define PA_MSEC_PER_SEC ((pa_usec_t) 1000ULL)
+
+/** The number of microseconds in a second */
#define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL)
+
+/** The number of nanoseconds in a second */
#define PA_NSEC_PER_SEC ((pa_usec_t) 1000000000ULL)
+
+/** The number of microseconds in a millisecond */
#define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL)
+
+/** The number of nanoseconds in a millisecond */
#define PA_NSEC_PER_MSEC ((pa_usec_t) 1000000ULL)
+
+/** The number of nanoseconds in a microsecond */
#define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL)
struct timeval;
diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h
index 6c7e7a5b..4d751953 100644
--- a/src/pulse/utf8.h
+++ b/src/pulse/utf8.h
@@ -25,6 +25,7 @@
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
+#include <pulse/version.h>
/** \file
* UTF8 Validation functions
diff --git a/src/pulse/util.h b/src/pulse/util.h
index cf06d4fd..f6dd40cb 100644
--- a/src/pulse/util.h
+++ b/src/pulse/util.h
@@ -27,6 +27,7 @@
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
+#include <pulse/version.h>
/** \file
* Assorted utility functions */
diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in
index e6226c44..566dd55e 100644
--- a/src/pulse/version.h.in
+++ b/src/pulse/version.h.in
@@ -43,13 +43,23 @@ const char* pa_get_library_version(void);
/** The current API version. Version 6 relates to Polypaudio
* 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have
- * PA_API_VERSION undefined. */
+ * PA_API_VERSION undefined. Please note that this is only ever
+ * increased on incompatible API changes! */
#define PA_API_VERSION @PA_API_VERSION@
/** The current protocol version. Version 8 relates to Polypaudio
* 0.8/PulseAudio 0.9. */
#define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@
+/** The major version of PA. \since 0.9.15 */
+#define PA_MAJOR @PA_MAJOR@
+
+/** The minor version of PA. \since 0.9.15 */
+#define PA_MINOR @PA_MINOR@
+
+/** The micro version of PA. \since 0.9.15 */
+#define PA_MICRO @PA_MICRO@
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index ace5c4d6..3434cb18 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -341,7 +341,7 @@ static pa_bool_t on_lfe(pa_channel_position_t p) {
p == PA_CHANNEL_POSITION_LFE;
}
-pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map *to) {
+pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) {
int a, b;
pa_cvolume result;
@@ -402,3 +402,120 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) {
return v->channels == ss->channels;
}
+
+static void get_avg_lr(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r) {
+ int c;
+ pa_volume_t left = 0, right = 0;
+ unsigned n_left = 0, n_right = 0;
+
+ pa_assert(v);
+ pa_assert(map);
+ pa_assert(map->channels == v->channels);
+ pa_assert(l);
+ pa_assert(r);
+
+ for (c = 0; c < map->channels; c++) {
+ if (on_left(map->map[c])) {
+ left += v->values[c];
+ n_left++;
+ } else if (on_right(map->map[c])) {
+ right += v->values[c];
+ n_right++;
+ }
+ }
+
+ if (n_left <= 0)
+ *l = PA_VOLUME_NORM;
+ else
+ *l = left / n_left;
+
+ if (n_right <= 0)
+ *r = PA_VOLUME_NORM;
+ else
+ *r = right / n_right;
+}
+
+float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) {
+ pa_volume_t left, right;
+
+ pa_assert(v);
+ pa_assert(map);
+ pa_assert(map->channels == v->channels);
+
+ get_avg_lr(map, v, &left, &right);
+
+ if (left == right)
+ return 0.0f;
+
+ /* 1.0, 0.0 => -1.0
+ 0.0, 1.0 => 1.0
+ 0.0, 0.0 => 0.0
+ 0.5, 0.5 => 0.0
+ 1.0, 0.5 => -0.5
+ 1.0, 0.25 => -0.75
+ 0.75, 0.25 => -0.66
+ 0.5, 0.25 => -0.5 */
+
+ if (left > right)
+ return -1.0f + ((float) right / (float) left);
+ else
+ return 1.0f - ((float) left / (float) right);
+}
+
+pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) {
+ pa_volume_t left, nleft, right, nright, m;
+ unsigned c;
+
+ pa_assert(map->channels == v->channels);
+ pa_assert(map);
+ pa_assert(v);
+ pa_assert(new_balance >= -1.0f);
+ pa_assert(new_balance <= 1.0f);
+
+ get_avg_lr(map, v, &left, &right);
+
+ m = PA_MAX(left, right);
+
+ if (new_balance <= 0) {
+ nright = (new_balance + 1.0f) * m;
+ nleft = m;
+ } else {
+ nleft = (1.0f - new_balance) * m;
+ nright = m;
+ }
+
+ for (c = 0; c < map->channels; c++) {
+ if (on_left(map->map[c])) {
+ if (left == 0)
+ v->values[c] = 0;
+ else
+ v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left);
+ } else if (on_right(map->map[c])) {
+ if (right == 0)
+ v->values[c] = 0;
+ else
+ v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right);
+ }
+ }
+
+ return v;
+}
+
+pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
+ unsigned c;
+ pa_volume_t t = 0;
+
+ pa_assert(c);
+
+ for (c = 0; c < v->channels; c++)
+ if (v->values[c] > t)
+ t = v->values[c];
+
+ if (t <= 0)
+ return pa_cvolume_set(v, v->channels, max);
+
+ for (c = 0; c < v->channels; c++)
+ v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t);
+
+ return v;
+}
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 5815c906..9a883ca7 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -29,6 +29,7 @@
#include <pulse/gccmacro.h>
#include <pulse/sample.h>
#include <pulse/channelmap.h>
+#include <pulse/version.h>
/** \page volume Volume Control
*
@@ -154,20 +155,20 @@ char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c);
* pa_volume_snprint(). Please note that this value can change with
* any release without warning and without being considered API or ABI
* breakage. You should not use this definition anywhere where it
- * might become part of an ABI. \since 0.9.14 */
+ * might become part of an ABI. \since 0.9.15 */
#define PA_VOLUME_SNPRINT_MAX 10
-/** Pretty print a volume \since 0.9.14 */
+/** Pretty print a volume \since 0.9.15 */
char *pa_volume_snprint(char *s, size_t l, pa_volume_t v);
/** Maximum length of the strings returned by
* pa_volume_snprint_dB(). Please note that this value can change with
* any release without warning and without being considered API or ABI
* breakage. You should not use this definition anywhere where it
- * might become part of an ABI. \since 0.9.14 */
+ * might become part of an ABI. \since 0.9.15 */
#define PA_SW_VOLUME_SNPRINT_DB_MAX 10
-/** Pretty print a volume but show dB values. \since 0.9.14 */
+/** Pretty print a volume but show dB values. \since 0.9.15 */
char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v);
/** Return the average volume of all channels */
@@ -227,12 +228,31 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
#endif
/** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */
-pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map *to);
+pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to);
/** Return non-zero if the specified volume is compatible with
* the specified sample spec. \since 0.9.13 */
int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE;
+/** Calculate a 'balance' value for the specified volume with the
+ * specified channel map. The return value will range from -1.0f
+ * (left) to +1.0f (right) \since 0.9.15 */
+float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE;
+
+/** Adjust the 'balance' value for the specified volume with the
+ * specified channel map. v will be modified in place and
+ * returned. The balance is a value between -1.0f and +1.0f. This
+ * operation might not be reversable! Also, after this call
+ * pa_cvolume_get_balance() is not guaranteed to actually return the
+ * requested balance (e.g. when the input volume was zero anyway for
+ * all channels) \since 0.9.15 */
+pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance);
+
+/** Scale the passed pa_cvolume structure so that the maximum volume
+ * of all channels equals max. The proportions between the channel
+ * volumes are kept. \since 0.9.15 */
+pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h
index b2643588..c30d4df1 100644
--- a/src/pulse/xmalloc.h
+++ b/src/pulse/xmalloc.h
@@ -29,6 +29,7 @@
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
+#include <pulse/version.h>
/** \file
* Memory allocation functions.
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
index f64931a5..67f661fe 100644
--- a/src/pulsecore/asyncq.c
+++ b/src/pulsecore/asyncq.c
@@ -163,14 +163,14 @@ static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
return 0;
}
-static pa_bool_t flush_postq(pa_asyncq *l) {
+static pa_bool_t flush_postq(pa_asyncq *l, pa_bool_t wait) {
struct localq *q;
pa_assert(l);
while ((q = l->last_localq)) {
- if (push(l, q->data, FALSE) < 0)
+ if (push(l, q->data, wait) < 0)
return FALSE;
l->last_localq = q->prev;
@@ -187,7 +187,7 @@ static pa_bool_t flush_postq(pa_asyncq *l) {
int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) {
pa_assert(l);
- if (!flush_postq(l))
+ if (!flush_postq(l, wait))
return -1;
return push(l, p, wait);
@@ -199,13 +199,15 @@ void pa_asyncq_post(pa_asyncq*l, void *p) {
pa_assert(l);
pa_assert(p);
- if (pa_asyncq_push(l, p, FALSE) >= 0)
- return;
+ if (flush_postq(l, FALSE))
+ if (pa_asyncq_push(l, p, FALSE) >= 0)
+ return;
/* OK, we couldn't push anything in the queue. So let's queue it
* locally and push it later */
- pa_log("q overrun, queuing locally");
+ if (pa_log_ratelimit())
+ pa_log_warn("q overrun, queuing locally");
if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq))))
q = pa_xnew(struct localq, 1);
@@ -299,7 +301,7 @@ void pa_asyncq_write_before_poll(pa_asyncq *l) {
for (;;) {
- if (flush_postq(l))
+ if (flush_postq(l, FALSE))
break;
if (pa_fdsem_before_poll(l->read_fdsem) >= 0) {
diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h
index 9c58c661..6e33a0e6 100644
--- a/src/pulsecore/atomic.h
+++ b/src/pulsecore/atomic.h
@@ -107,6 +107,79 @@ static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, v
return __sync_bool_compare_and_swap(&a->value, (long) old_p, (long) new_p);
}
+#elif defined(__NetBSD__) && defined(HAVE_SYS_ATOMIC_H)
+
+/* NetBSD 5.0+ atomic_ops(3) implementation */
+
+#include <sys/atomic.h>
+
+typedef struct pa_atomic {
+ volatile unsigned int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (unsigned int) (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ membar_sync();
+ return (int) a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = (unsigned int) i;
+ membar_sync();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ int nv = (int) atomic_add_int_nv(&a->value, i);
+ return nv - i;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ int nv = (int) atomic_add_int_nv(&a->value, -i);
+ return nv + i;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ int nv = (int) atomic_inc_uint_nv(&a->value);
+ return nv - 1;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+ int nv = (int) atomic_dec_uint_nv(&a->value);
+ return nv + 1;
+}
+
+/* Returns TRUE when the operation was successful. */
+static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+ unsigned int r = atomic_cas_uint(&a->value, (unsigned int) old_i, (unsigned int) new_i);
+ return (int) r == old_i;
+}
+
+typedef struct pa_atomic_ptr {
+ volatile void *value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ membar_sync();
+ return (void *) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = p;
+ membar_sync();
+}
+
+static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+ void *r = atomic_cas_ptr(&a->value, old_p, new_p);
+ return r == old_p;
+}
+
#elif defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__))
#warn "The native atomic operations implementation for AMD64 has not been tested thoroughly. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: test the native atomic operations implementation for AMD64, fix libatomic_ops, or upgrade your GCC."
diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c
deleted file mode 100644
index 8c84cee5..00000000
--- a/src/pulsecore/autoload.c
+++ /dev/null
@@ -1,202 +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 of the License,
- or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/module.h>
-#include <pulsecore/memchunk.h>
-#include <pulsecore/sound-file.h>
-#include <pulsecore/log.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/core-scache.h>
-#include <pulsecore/core-subscribe.h>
-
-#include "autoload.h"
-
-static void entry_free(pa_autoload_entry *e) {
- pa_assert(e);
- pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX);
- pa_xfree(e->name);
- pa_xfree(e->module);
- pa_xfree(e->argument);
- pa_xfree(e);
-}
-
-static void entry_remove_and_free(pa_autoload_entry *e) {
- pa_assert(e);
- pa_assert(e->core);
-
- pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
- pa_hashmap_remove(e->core->autoload_hashmap, e->name);
- entry_free(e);
-}
-
-static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
- pa_autoload_entry *e = NULL;
-
- pa_core_assert_ref(c);
- pa_assert(name);
-
- if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name)))
- return NULL;
-
- e = pa_xnew(pa_autoload_entry, 1);
- e->core = c;
- e->name = pa_xstrdup(name);
- e->module = e->argument = NULL;
- e->in_action = 0;
-
- if (!c->autoload_hashmap)
- c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- pa_assert(c->autoload_hashmap);
-
- pa_hashmap_put(c->autoload_hashmap, e->name, e);
-
- if (!c->autoload_idxset)
- c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- pa_idxset_put(c->autoload_idxset, e, &e->index);
-
- pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index);
-
- return e;
-}
-
-int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {
- pa_autoload_entry *e = NULL;
-
- pa_assert(c);
- pa_assert(name);
- pa_assert(module);
- pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
-
- if (!(e = entry_new(c, name)))
- return -1;
-
- e->module = pa_xstrdup(module);
- e->argument = pa_xstrdup(argument);
- e->type = type;
-
- if (idx)
- *idx = e->index;
-
- return 0;
-}
-
-int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
- pa_autoload_entry *e;
-
- pa_assert(c);
- pa_assert(name);
- pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
-
- if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
- return -1;
-
- entry_remove_and_free(e);
- return 0;
-}
-
-int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
- pa_autoload_entry *e;
-
- pa_assert(c);
- pa_assert(idx != PA_IDXSET_INVALID);
-
- if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
- return -1;
-
- entry_remove_and_free(e);
- return 0;
-}
-
-void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
- pa_autoload_entry *e;
- pa_module *m;
-
- pa_assert(c);
- pa_assert(name);
-
- if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type))
- return;
-
- if (e->in_action)
- return;
-
- e->in_action = 1;
-
- if (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) {
- if ((m = pa_module_load(c, e->module, e->argument)))
- m->auto_unload = 1;
- }
-
- e->in_action = 0;
-}
-
-static void free_func(void *p, void *userdata) {
- pa_autoload_entry *e = p;
- pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
- entry_free(e);
-}
-
-void pa_autoload_free(pa_core *c) {
-
- if (c->autoload_hashmap) {
- pa_hashmap_free(c->autoload_hashmap, free_func, NULL);
- c->autoload_hashmap = NULL;
- }
-
- if (c->autoload_idxset) {
- pa_idxset_free(c->autoload_idxset, NULL, NULL);
- c->autoload_idxset = NULL;
- }
-}
-
-const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
- pa_autoload_entry *e;
-
- pa_core_assert_ref(c);
- pa_assert(name);
-
- if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
- return NULL;
-
- return e;
-}
-
-const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) {
- pa_autoload_entry *e;
-
- pa_core_assert_ref(c);
- pa_assert(idx != PA_IDXSET_INVALID);
-
- if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
- return NULL;
-
- return e;
-}
diff --git a/src/pulsecore/autoload.h b/src/pulsecore/autoload.h
deleted file mode 100644
index 3926351f..00000000
--- a/src/pulsecore/autoload.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef fooautoloadhfoo
-#define fooautoloadhfoo
-
-/***
- This file is part of PulseAudio.
-
- Copyright 2004-2006 Lennart Poettering
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-#include <pulsecore/namereg.h>
-
-/* Using the autoloading facility, modules by be loaded on-demand and
- * synchronously. The user may register a "ghost sink" or "ghost
- * source". Whenever this sink/source is requested but not available a
- * specified module is loaded. */
-
-/* An autoload entry, or "ghost" sink/source */
-typedef struct pa_autoload_entry {
- pa_core *core;
- uint32_t index;
- char *name;
- pa_namereg_type_t type; /* Type of the autoload entry */
- int in_action; /* The module is currently being loaded */
- char *module, *argument;
-} pa_autoload_entry;
-
-/* Add a new autoload entry of the given time, with the speicified
- * sink/source name, module name and argument. Return the entry's
- * index in *index */
-int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx);
-
-/* Free all autoload entries */
-void pa_autoload_free(pa_core *c);
-int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
-int pa_autoload_remove_by_index(pa_core *c, uint32_t idx);
-
-/* Request an autoload entry by its name, effectively causing a module to be loaded */
-void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type);
-
-const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
-const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx);
-
-#endif
diff --git a/src/pulsecore/bitset.c b/src/pulsecore/bitset.c
new file mode 100644
index 00000000..4beeb1cc
--- /dev/null
+++ b/src/pulsecore/bitset.c
@@ -0,0 +1,67 @@
+/***
+ 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 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include "bitset.h"
+
+void pa_bitset_set(pa_bitset_t *b, unsigned k, pa_bool_t v) {
+ pa_assert(b);
+
+ if (v)
+ b[k >> 5] |= 1 << (k & 31);
+ else
+ b[k >> 5] &= ~((uint32_t) (1 << (k & 31)));
+}
+
+pa_bool_t pa_bitset_get(const pa_bitset_t *b, unsigned k) {
+ return !!(b[k >> 5] & (1 << (k & 31)));
+}
+
+pa_bool_t pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...) {
+ va_list ap;
+ pa_bitset_t *a;
+ pa_bool_t equal;
+
+ a = pa_xnew0(pa_bitset_t, PA_BITSET_ELEMENTS(n));
+
+ va_start(ap, n);
+ for (;;) {
+ int j = va_arg(ap, int);
+
+ if (j < 0)
+ break;
+
+ pa_bitset_set(a, j, TRUE);
+ }
+ va_end(ap);
+
+ equal = memcmp(a, b, PA_BITSET_SIZE(n)) == 0;
+ pa_xfree(a);
+
+ return equal;
+}
diff --git a/src/pulsecore/bitset.h b/src/pulsecore/bitset.h
new file mode 100644
index 00000000..95f5cfce
--- /dev/null
+++ b/src/pulsecore/bitset.h
@@ -0,0 +1,37 @@
+#ifndef foopulsecorebitsethfoo
+#define foopulsecorebitsethfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <pulsecore/macro.h>
+
+#define PA_BITSET_ELEMENTS(n) (((n)+31)/32)
+#define PA_BITSET_SIZE(n) (PA_BITSET_ELEMENTS(n)*4)
+
+typedef uint32_t pa_bitset_t;
+
+void pa_bitset_set(pa_bitset_t *b, unsigned k, pa_bool_t v);
+pa_bool_t pa_bitset_get(const pa_bitset_t *b, unsigned k);
+pa_bool_t pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...);
+
+#endif
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
new file mode 100644
index 00000000..8e1ba536
--- /dev/null
+++ b/src/pulsecore/card.c
@@ -0,0 +1,254 @@
+/***
+ 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 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/namereg.h>
+
+#include "card.h"
+
+pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra) {
+ pa_card_profile *c;
+
+ pa_assert(name);
+
+ c = pa_xmalloc(PA_ALIGN(sizeof(pa_card_profile)) + extra);
+ c->name = pa_xstrdup(name);
+ c->description = pa_xstrdup(description);
+
+ c->priority = 0;
+ c->n_sinks = c->n_sources = 0;
+ c->max_sink_channels = c->max_source_channels = 0;
+
+ return c;
+}
+
+void pa_card_profile_free(pa_card_profile *c) {
+ pa_assert(c);
+
+ pa_xfree(c->name);
+ pa_xfree(c->description);
+ pa_xfree(c);
+}
+
+pa_card_new_data* pa_card_new_data_init(pa_card_new_data *data) {
+ pa_assert(data);
+
+ memset(data, 0, sizeof(*data));
+ data->proplist = pa_proplist_new();
+
+ return data;
+}
+
+void pa_card_new_data_set_name(pa_card_new_data *data, const char *name) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ data->name = pa_xstrdup(name);
+}
+
+void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile) {
+ pa_assert(data);
+
+ pa_xfree(data->active_profile);
+ data->active_profile = pa_xstrdup(profile);
+}
+
+void pa_card_new_data_done(pa_card_new_data *data) {
+
+ pa_assert(data);
+
+ pa_proplist_free(data->proplist);
+
+ if (data->profiles) {
+ pa_card_profile *c;
+
+ while ((c = pa_hashmap_steal_first(data->profiles)))
+ pa_card_profile_free(c);
+
+ pa_hashmap_free(data->profiles, NULL, NULL);
+ }
+
+ pa_xfree(data->name);
+ pa_xfree(data->active_profile);
+}
+
+pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
+ pa_card *c;
+ const char *name;
+
+ pa_core_assert_ref(core);
+ pa_assert(data);
+ pa_assert(data->name);
+
+ c = pa_xnew(pa_card, 1);
+
+ if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_CARD, c, data->namereg_fail))) {
+ pa_xfree(c);
+ return NULL;
+ }
+
+ pa_card_new_data_set_name(data, name);
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_NEW], data) < 0) {
+ pa_xfree(c);
+ pa_namereg_unregister(core, name);
+ return NULL;
+ }
+
+ c->core = core;
+ c->name = pa_xstrdup(data->name);
+ c->proplist = pa_proplist_copy(data->proplist);
+ c->driver = pa_xstrdup(pa_path_get_filename(data->driver));
+ c->module = data->module;
+
+ c->sinks = pa_idxset_new(NULL, NULL);
+ c->sources = pa_idxset_new(NULL, NULL);
+
+ /* As a minor optimization we just steal the list instead of
+ * copying it here */
+ c->profiles = data->profiles;
+ data->profiles = NULL;
+
+ c->active_profile = NULL;
+
+ if (data->active_profile && c->profiles)
+ c->active_profile = pa_hashmap_get(c->profiles, data->active_profile);
+
+ if (!c->active_profile && c->profiles) {
+ void *state = NULL;
+ pa_card_profile *p;
+
+ while ((p = pa_hashmap_iterate(c->profiles, &state, NULL))) {
+ if (!c->active_profile ||
+ p->priority > c->active_profile->priority)
+
+ c->active_profile = p;
+ }
+ }
+
+ c->userdata = NULL;
+ c->set_profile = NULL;
+
+ pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0);
+
+ pa_log_info("Created %u \"%s\"", c->index, c->name);
+ pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, c->index);
+
+ pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_PUT], c);
+ return c;
+}
+
+void pa_card_free(pa_card *c) {
+ pa_core *core;
+ pa_card_profile *profile;
+
+ pa_assert(c);
+ pa_assert(c->core);
+
+ core = c->core;
+
+ pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_UNLINK], c);
+
+ pa_namereg_unregister(core, c->name);
+
+ pa_idxset_remove_by_data(c->core->cards, c, NULL);
+
+ pa_log_info("Freed %u \"%s\"", c->index, c->name);
+
+ pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
+
+ pa_assert(pa_idxset_isempty(c->sinks));
+ pa_idxset_free(c->sinks, NULL, NULL);
+ pa_assert(pa_idxset_isempty(c->sources));
+ pa_idxset_free(c->sources, NULL, NULL);
+
+ if (c->profiles) {
+ while ((profile = pa_hashmap_steal_first(c->profiles)))
+ pa_card_profile_free(profile);
+
+ pa_hashmap_free(c->profiles, NULL, NULL);
+ }
+
+ pa_proplist_free(c->proplist);
+ pa_xfree(c->driver);
+ pa_xfree(c->name);
+ pa_xfree(c);
+}
+
+int pa_card_set_profile(pa_card *c, const char *name) {
+ pa_card_profile *profile;
+ pa_assert(c);
+
+ if (!c->set_profile) {
+ pa_log_warn("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name);
+ return -1;
+ }
+
+ if (!c->profiles)
+ return -1;
+
+ if (!(profile = pa_hashmap_get(c->profiles, name)))
+ return -1;
+
+ if (c->active_profile == profile)
+ return 0;
+
+ if (c->set_profile(c, profile) < 0)
+ return -1;
+
+ pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
+
+ pa_log_info("Successfully changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name);
+
+ c->active_profile = profile;
+
+ return 0;
+}
+
+int pa_card_suspend(pa_card *c, pa_bool_t suspend) {
+ pa_sink *sink;
+ pa_source *source;
+ uint32_t idx;
+ int ret = 0;
+
+ pa_assert(c);
+
+ for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx))
+ ret -= pa_sink_suspend(sink, suspend) < 0;
+
+ for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx))
+ ret -= pa_source_suspend(source, suspend) < 0;
+
+ return ret;
+}
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
new file mode 100644
index 00000000..b179831e
--- /dev/null
+++ b/src/pulsecore/card.h
@@ -0,0 +1,100 @@
+#ifndef foopulsecardhfoo
+#define foopulsecardhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_card pa_card;
+
+#include <pulse/proplist.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/idxset.h>
+
+typedef struct pa_card_profile {
+ char *name;
+ char *description;
+
+ unsigned priority;
+
+ /* We probably want to have different properties later on here */
+ unsigned n_sinks;
+ unsigned n_sources;
+
+ unsigned max_sink_channels;
+ unsigned max_source_channels;
+
+ /* .. followed by some implementation specific data */
+} pa_card_profile;
+
+#define PA_CARD_PROFILE_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_card_profile))))
+
+struct pa_card {
+ uint32_t index;
+ pa_core *core;
+
+ char *name;
+
+ pa_proplist *proplist;
+ pa_module *module;
+ char *driver;
+
+ pa_idxset *sinks;
+ pa_idxset *sources;
+
+ pa_hashmap *profiles;
+ pa_card_profile *active_profile;
+
+ void *userdata;
+
+ int (*set_profile)(pa_card *c, pa_card_profile *profile);
+};
+
+typedef struct pa_card_new_data {
+ char *name;
+ char *description;
+
+ pa_proplist *proplist;
+ const char *driver;
+ pa_module *module;
+
+ pa_hashmap *profiles;
+ char *active_profile;
+
+ pa_bool_t namereg_fail:1;
+} pa_card_new_data;
+
+pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra);
+void pa_card_profile_free(pa_card_profile *c);
+
+pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data);
+void pa_card_new_data_set_name(pa_card_new_data *data, const char *name);
+void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile);
+void pa_card_new_data_done(pa_card_new_data *data);
+
+pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
+void pa_card_free(pa_card *c);
+
+int pa_card_set_profile(pa_card *c, const char *name);
+
+int pa_card_suspend(pa_card *c, pa_bool_t suspend);
+
+#endif
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index b5ff98db..1df0bd63 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -47,11 +47,11 @@
#include <pulsecore/sample-util.h>
#include <pulsecore/sound-file.h>
#include <pulsecore/play-memchunk.h>
-#include <pulsecore/autoload.h>
#include <pulsecore/sound-file-stream.h>
#include <pulsecore/shared.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/modinfo.h>
#include "cli-command.h"
@@ -80,6 +80,7 @@ static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
@@ -106,9 +107,6 @@ static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
@@ -121,6 +119,11 @@ static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
/* A method table for all available commands */
@@ -133,6 +136,7 @@ static const struct command commands[] = {
{ "list-clients", pa_cli_command_clients, "List loaded clients", 1 },
{ "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 },
{ "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 },
+ { "list-cards", pa_cli_command_cards, "List cards", 1 },
{ "stat", pa_cli_command_stat, "Show memory block statistics", 1 },
{ "info", pa_cli_command_info, "Show comprehensive status", 1 },
{ "ls", pa_cli_command_info, NULL, 1 },
@@ -146,6 +150,10 @@ static const struct command commands[] = {
{ "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, bool)", 3},
{ "set-sink-input-mute", pa_cli_command_sink_input_mute, "Set the mute switch of a sink input (args: index, bool)", 3},
{ "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, bool)", 3},
+ { "update-sink-proplist", pa_cli_command_update_sink_proplist, "Update the properties of a sink (args: index|name, properties)", 3},
+ { "update-source-proplist", pa_cli_command_update_source_proplist, "Update the properties of a source (args: index|name, properties)", 3},
+ { "update-sink-input-proplist", pa_cli_command_update_sink_input_proplist, "Update the properties of a sink input (args: index, properties)", 3},
+ { "update-source-output-proplist", pa_cli_command_update_source_output_proplist, "Update the properties of a source_output (args: index, properties)", 3},
{ "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
{ "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
{ "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
@@ -158,11 +166,6 @@ static const struct command commands[] = {
{ "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3},
{ "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
{ "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
- { "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
- { "add-autoload-sink", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4},
- { "add-autoload-source", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4},
- { "remove-autoload-sink", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a sink (args: name)"*/, 2},
- { "remove-autoload-source", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a source (args: name)"*/, 2},
{ "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
{ "shared", pa_cli_command_list_shared_props, NULL, 1},
{ "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3},
@@ -171,6 +174,7 @@ static const struct command commands[] = {
{ "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3},
{ "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3},
{ "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2},
+ { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (aargs: index, name)", 3},
{ "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2},
{ "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2},
{ "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2},
@@ -246,6 +250,20 @@ static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
return 0;
}
+static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ char *s;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_card_list_to_string(c));
+ pa_strbuf_puts(buf, s);
+ pa_xfree(s);
+ return 0;
+}
+
static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
@@ -306,7 +324,8 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
char s[256];
const pa_mempool_stat *stat;
unsigned k;
- const char *def_sink, *def_source;
+ pa_sink *def_sink;
+ pa_source *def_source;
static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = {
[PA_MEMBLOCK_POOL] = "POOL",
@@ -346,12 +365,12 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_strbuf_printf(buf, "Default sample spec: %s\n",
pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec));
- def_sink = pa_namereg_get_default_sink_name(c);
- def_source = pa_namereg_get_default_source_name(c);
+ def_sink = pa_namereg_get_default_sink(c);
+ def_source = pa_namereg_get_default_source(c);
pa_strbuf_printf(buf, "Default sink name: %s\n"
"Default source name: %s\n",
- def_sink ? def_sink : "none",
- def_source ? def_source : "none");
+ def_sink ? def_sink->name : "none",
+ def_source ? def_source->name : "none");
for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++)
pa_strbuf_printf(buf,
@@ -374,10 +393,10 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_cli_command_sinks(c, t, buf, fail);
pa_cli_command_sources(c, t, buf, fail);
pa_cli_command_clients(c, t, buf, fail);
+ pa_cli_command_cards(c, t, buf, fail);
pa_cli_command_sink_inputs(c, t, buf, fail);
pa_cli_command_source_outputs(c, t, buf, fail);
pa_cli_command_scache_list(c, t, buf, fail);
-/* pa_cli_command_autoload_list(c, t, buf, fail); */
return 0;
}
@@ -494,13 +513,13 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
- pa_sink_set_volume(sink, &cvolume);
+ pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);
return 0;
}
@@ -542,7 +561,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
}
pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
- pa_sink_input_set_volume(si, &cvolume);
+ pa_sink_input_set_volume(si, &cvolume, TRUE);
return 0;
}
@@ -572,7 +591,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
return -1;
}
- if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+ if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
pa_strbuf_puts(buf, "No source found by this name or index.\n");
return -1;
}
@@ -607,7 +626,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
- if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
@@ -641,7 +660,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+ if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
@@ -650,6 +669,154 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
+static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *s;
+ pa_sink *sink;
+ pa_proplist *p;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(s = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
+ pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+ return -1;
+ }
+
+ p = pa_proplist_from_string(s);
+
+ pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
+
+ pa_proplist_free(p);
+
+ return 0;
+}
+
+static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *s;
+ pa_source *source;
+ pa_proplist *p;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(s = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+ return -1;
+ }
+
+ if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
+ pa_strbuf_puts(buf, "No source found by this name or index.\n");
+ return -1;
+ }
+
+ p = pa_proplist_from_string(s);
+
+ pa_source_update_proplist(source, PA_UPDATE_REPLACE, p);
+
+ pa_proplist_free(p);
+
+ return 0;
+}
+
+static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *s;
+ pa_sink_input *si;
+ uint32_t idx;
+ pa_proplist *p;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink input either by index.\n");
+ return -1;
+ }
+
+ if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+ pa_strbuf_puts(buf, "Failed to parse index.\n");
+ return -1;
+ }
+
+ if (!(s = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+ return -1;
+ }
+
+ if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+ pa_strbuf_puts(buf, "No sink input found with this index.\n");
+ return -1;
+ }
+
+ p = pa_proplist_from_string(s);
+
+ pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p);
+
+ pa_proplist_free(p);
+
+ return 0;
+}
+
+static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *s;
+ pa_source_output *so;
+ uint32_t idx;
+ pa_proplist *p;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+ return -1;
+ }
+
+ if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+ pa_strbuf_puts(buf, "Failed to parse index.\n");
+ return -1;
+ }
+
+ if (!(s = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+ return -1;
+ }
+
+ if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
+ pa_strbuf_puts(buf, "No source output found with this index.\n");
+ return -1;
+ }
+
+ p = pa_proplist_from_string(s);
+
+ pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p);
+
+ pa_proplist_free(p);
+
+ return 0;
+}
+
static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink_input *si;
@@ -686,12 +853,13 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- pa_sink_input_set_mute(si, mute);
+ pa_sink_input_set_mute(si, mute, TRUE);
return 0;
}
static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
+ pa_sink *s;
pa_core_assert_ref(c);
pa_assert(t);
@@ -703,12 +871,17 @@ static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return -1;
}
- pa_namereg_set_default(c, n, PA_NAMEREG_SINK);
+ if ((s = pa_namereg_get(c, n, PA_NAMEREG_SINK)))
+ pa_namereg_set_default_sink(c, s);
+ else
+ pa_strbuf_printf(buf, "Sink %s does not exist.\n", n);
+
return 0;
}
static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
+ pa_source *s;
pa_core_assert_ref(c);
pa_assert(t);
@@ -720,7 +893,10 @@ static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE);
+ if ((s = pa_namereg_get(c, n, PA_NAMEREG_SOURCE)))
+ pa_namereg_set_default_source(c, s);
+ else
+ pa_strbuf_printf(buf, "Source %s does not exist.\n", n);
return 0;
}
@@ -841,7 +1017,7 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
pa_strbuf_puts(buf, "No sink by that name.\n");
return -1;
}
@@ -937,7 +1113,7 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
- if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
pa_strbuf_puts(buf, "No sink by that name.\n");
return -1;
}
@@ -946,66 +1122,6 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return pa_play_file(sink, fname, NULL);
}
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
- const char *a, *b;
-
- pa_core_assert_ref(c);
- pa_assert(t);
- pa_assert(buf);
- pa_assert(fail);
-
- pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
-
- if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
- pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
- return -1;
- }
-
- pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL);
-
- return 0;
-}
-
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
- const char *name;
-
- pa_core_assert_ref(c);
- pa_assert(t);
- pa_assert(buf);
- pa_assert(fail);
-
- pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
-
- if (!(name = pa_tokenizer_get(t, 1))) {
- pa_strbuf_puts(buf, "You need to specify a device name\n");
- return -1;
- }
-
- if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) {
- pa_strbuf_puts(buf, "Failed to remove autload entry\n");
- return -1;
- }
-
- return 0;
-}
-
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
- char *s;
-
- pa_core_assert_ref(c);
- pa_assert(t);
- pa_assert(buf);
- pa_assert(fail);
-
- pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
-
- pa_assert_se(s = pa_autoload_list_to_string(c));
- pa_strbuf_puts(buf, s);
- pa_xfree(s);
-
- return 0;
-}
-
static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_core_assert_ref(c);
pa_assert(t);
@@ -1058,12 +1174,12 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
- if (pa_sink_input_move_to(si, sink) < 0) {
+ if (pa_sink_input_move_to(si, sink, TRUE) < 0) {
pa_strbuf_puts(buf, "Moved failed.\n");
return -1;
}
@@ -1101,12 +1217,12 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str
return -1;
}
- if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE, 1))) {
+ if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE))) {
pa_strbuf_puts(buf, "No source found by this name or index.\n");
return -1;
}
- if (pa_source_output_move_to(so, source) < 0) {
+ if (pa_source_output_move_to(so, source, TRUE) < 0) {
pa_strbuf_puts(buf, "Moved failed.\n");
return -1;
}
@@ -1138,7 +1254,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return -1;
}
- if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
@@ -1172,7 +1288,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+ if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
pa_strbuf_puts(buf, "No source found by this name or index.\n");
return -1;
}
@@ -1307,17 +1423,47 @@ static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *
return 0;
}
+static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *p;
+ pa_card *card;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a card either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(p = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
+ return -1;
+ }
+
+ if (!(card = pa_namereg_get(c, n, PA_NAMEREG_CARD))) {
+ pa_strbuf_puts(buf, "No card found by this name or index.\n");
+ return -1;
+ }
+
+ if (pa_card_set_profile(card, p) < 0) {
+ pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p);
+ return -1;
+ }
+
+ return 0;
+}
+
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
pa_sink *sink;
pa_source *source;
+ pa_card *card;
int nl;
- const char *p;
uint32_t idx;
char txt[256];
time_t now;
- void *i;
- pa_autoload_entry *a;
pa_core_assert_ref(c);
pa_assert(t);
@@ -1333,8 +1479,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
#endif
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
- if (m->auto_unload)
- continue;
pa_strbuf_printf(buf, "load-module %s", m->name);
@@ -1347,8 +1491,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
nl = 0;
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
- if (sink->module && sink->module->auto_unload)
- continue;
if (!nl) {
pa_strbuf_puts(buf, "\n");
@@ -1361,8 +1503,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
}
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
- if (source->module && source->module->auto_unload)
- continue;
if (!nl) {
pa_strbuf_puts(buf, "\n");
@@ -1374,43 +1514,33 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
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)) {
- if (c->autoload_hashmap) {
- nl = 0;
-
- i = NULL;
- while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) {
-
- if (!nl) {
- pa_strbuf_puts(buf, "\n");
- nl = 1;
- }
-
- pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module);
-
- if (a->argument)
- pa_strbuf_printf(buf, " %s", a->argument);
-
+ if (!nl) {
pa_strbuf_puts(buf, "\n");
+ nl = 1;
}
+
+ if (card->active_profile)
+ pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name);
}
nl = 0;
- if ((p = pa_namereg_get_default_sink_name(c))) {
+ if ((sink = pa_namereg_get_default_sink(c))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
- pa_strbuf_printf(buf, "set-default-sink %s\n", p);
+ pa_strbuf_printf(buf, "set-default-sink %s\n", sink->name);
}
- if ((p = pa_namereg_get_default_source_name(c))) {
+ if ((source = pa_namereg_get_default_source(c))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
- pa_strbuf_printf(buf, "set-default-source %s\n", p);
+ pa_strbuf_printf(buf, "set-default-source %s\n", source->name);
}
pa_strbuf_puts(buf, "\n### EOF\n");
@@ -1513,7 +1643,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
size_t l;
if (ifstate && *ifstate == IFSTATE_FALSE)
- return 0;
+ return 0;
l = strcspn(cs, whitespace);
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 362a9791..57129d0c 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -38,7 +38,6 @@
#include <pulsecore/strbuf.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/core-scache.h>
-#include <pulsecore/autoload.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
@@ -55,13 +54,22 @@ char *pa_module_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules));
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
+ char *t;
+
pa_strbuf_printf(s, " index: %u\n"
"\tname: <%s>\n"
"\targument: <%s>\n"
"\tused: %i\n"
- "\tauto unload: %s\n",
- m->index, m->name, m->argument ? m->argument : "", m->n_used,
- pa_yes_no(m->auto_unload));
+ "\tload once: %s\n",
+ m->index,
+ m->name,
+ pa_strempty(m->argument),
+ pa_module_get_n_used(m),
+ pa_yes_no(m->load_once));
+
+ t = pa_proplist_to_string_sep(m->proplist, "\n\t\t");
+ pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -89,25 +97,116 @@ char *pa_client_list_to_string(pa_core *c) {
if (client->module)
pa_strbuf_printf(s, "\towner module: %u\n", client->module->index);
- t = pa_proplist_to_string(client->proplist);
- pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ t = pa_proplist_to_string_sep(client->proplist, "\n\t\t");
+ pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
}
+char *pa_card_list_to_string(pa_core *c) {
+ pa_strbuf *s;
+ pa_card *card;
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_assert(c);
+
+ s = pa_strbuf_new();
+
+ pa_strbuf_printf(s, "%u card(s) available.\n", pa_idxset_size(c->cards));
+
+ for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) {
+ char *t;
+ pa_sink *sink;
+ pa_source *source;
+ uint32_t sidx;
+
+ pa_strbuf_printf(
+ s,
+ " index: %u\n"
+ "\tname: <%s>\n"
+ "\tdriver: <%s>\n",
+ card->index,
+ card->name,
+ card->driver);
+
+ if (card->module)
+ pa_strbuf_printf(s, "\towner module: %u\n", card->module->index);
+
+ t = pa_proplist_to_string_sep(card->proplist, "\n\t\t");
+ pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+ pa_xfree(t);
+
+ if (card->profiles) {
+ pa_card_profile *p;
+ void *state = NULL;
+
+ pa_strbuf_puts(s, "\tprofiles:\n");
+
+ while ((p = pa_hashmap_iterate(card->profiles, &state, NULL)))
+ pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
+ }
+
+ if (card->active_profile)
+ pa_strbuf_printf(
+ s,
+ "\tactive profile: <%s>\n",
+ card->active_profile->name);
+
+ if (!pa_idxset_isempty(card->sinks)) {
+ pa_strbuf_puts(s, "\tsinks:\n");
+ for (sink = pa_idxset_first(card->sinks, &sidx); sink; sink = pa_idxset_next(card->sinks, &sidx))
+ pa_strbuf_printf(s, "\t\t%s/#%u: %s\n", sink->name, sink->index, pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ }
+
+ if (!pa_idxset_isempty(card->sources)) {
+ pa_strbuf_puts(s, "\tsources:\n");
+ for (source = pa_idxset_first(card->sources, &sidx); source; source = pa_idxset_next(card->sources, &sidx))
+ pa_strbuf_printf(s, "\t\t%s/#%u: %s\n", source->name, source->index, pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ }
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
+
+static const char *sink_state_to_string(pa_sink_state_t state) {
+ switch (state) {
+ case PA_SINK_INIT:
+ return "INIT";
+ case PA_SINK_RUNNING:
+ return "RUNNING";
+ case PA_SINK_SUSPENDED:
+ return "SUSPENDED";
+ case PA_SINK_IDLE:
+ return "IDLE";
+ case PA_SINK_UNLINKED:
+ return "UNLINKED";
+ default:
+ return "INVALID";
+ }
+}
+
+static const char *source_state_to_string(pa_source_state_t state) {
+ switch (state) {
+ case PA_SOURCE_INIT:
+ return "INIT";
+ case PA_SOURCE_RUNNING:
+ return "RUNNING";
+ case PA_SOURCE_SUSPENDED:
+ return "SUSPENDED";
+ case PA_SOURCE_IDLE:
+ return "IDLE";
+ case PA_SOURCE_UNLINKED:
+ return "UNLINKED";
+ default:
+ return "INVALID";
+ }
+}
+
char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_sink *sink;
uint32_t idx = PA_IDXSET_INVALID;
- static const char* const state_table[] = {
- [PA_SINK_INIT] = "INIT",
- [PA_SINK_RUNNING] = "RUNNING",
- [PA_SINK_SUSPENDED] = "SUSPENDED",
- [PA_SINK_IDLE] = "IDLE",
- [PA_SINK_UNLINKED] = "UNLINKED"
- };
pa_assert(c);
s = pa_strbuf_new();
@@ -122,6 +221,9 @@ char *pa_sink_list_to_string(pa_core *c) {
vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
pa_usec_t min_latency, max_latency;
+ const char *cmn;
+
+ cmn = pa_channel_map_to_pretty_name(&sink->channel_map);
pa_sink_get_latency_range(sink, &min_latency, &max_latency);
@@ -130,10 +232,12 @@ char *pa_sink_list_to_string(pa_core *c) {
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tvolume: %s%s%s\n"
+ "\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
+ "\tvolume steps: %u\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
"\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
@@ -141,10 +245,10 @@ char *pa_sink_list_to_string(pa_core *c) {
"\tmax rewind: %lu KiB\n"
"\tmonitor source: %u\n"
"\tsample spec: %s\n"
- "\tchannel map: %s\n"
+ "\tchannel map: %s%s%s\n"
"\tused by: %u\n"
"\tlinked by: %u\n",
- c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
+ sink == c->default_sink ? '*' : ' ',
sink->index,
sink->name,
sink->driver,
@@ -154,13 +258,16 @@ char *pa_sink_list_to_string(pa_core *c) {
sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
- state_table[pa_sink_get_state(sink)],
+ sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME" : "",
+ sink_state_to_string(pa_sink_get_state(sink)),
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)) : "",
+ 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) : "",
+ sink->n_volume_steps,
pa_yes_no(pa_sink_get_mute(sink, FALSE)),
(double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC,
(double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
@@ -171,14 +278,18 @@ char *pa_sink_list_to_string(pa_core *c) {
sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
+ cmn ? "\n\t " : "",
+ cmn ? cmn : "",
pa_sink_used_by(sink),
pa_sink_linked_by(sink));
+ if (sink->card)
+ pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
if (sink->module)
pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
- t = pa_proplist_to_string(sink->proplist);
- pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t");
+ pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
}
@@ -189,13 +300,6 @@ char *pa_source_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_source *source;
uint32_t idx = PA_IDXSET_INVALID;
- static const char* const state_table[] = {
- [PA_SOURCE_INIT] = "INIT",
- [PA_SOURCE_RUNNING] = "RUNNING",
- [PA_SOURCE_SUSPENDED] = "SUSPENDED",
- [PA_SOURCE_IDLE] = "IDLE",
- [PA_SOURCE_UNLINKED] = "UNLINKED"
- };
pa_assert(c);
s = pa_strbuf_new();
@@ -210,6 +314,9 @@ char *pa_source_list_to_string(pa_core *c) {
vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
pa_usec_t min_latency, max_latency;
+ const char *cmn;
+
+ cmn = pa_channel_map_to_pretty_name(&source->channel_map);
pa_source_get_latency_range(source, &min_latency, &max_latency);
@@ -221,16 +328,18 @@ char *pa_source_list_to_string(pa_core *c) {
"\tflags: %s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tvolume: %s%s%s\n"
+ "\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
+ "\tvolume steps: %u\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
"\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
"\tmax rewind: %lu KiB\n"
"\tsample spec: %s\n"
- "\tchannel map: %s\n"
+ "\tchannel map: %s%s%s\n"
"\tused by: %u\n"
"\tlinked by: %u\n",
- c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
+ c->default_source == source ? '*' : ' ',
source->index,
source->name,
source->driver,
@@ -240,13 +349,15 @@ char *pa_source_list_to_string(pa_core *c) {
source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
- state_table[pa_source_get_state(source)],
+ source_state_to_string(pa_source_get_state(source)),
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)) : "",
+ pa_cvolume_get_balance(pa_source_get_volume(source, FALSE), &source->channel_map),
pa_volume_snprint(v, sizeof(v), source->base_volume),
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), source->base_volume) : "",
+ source->n_volume_steps,
pa_yes_no(pa_source_get_mute(source, FALSE)),
(double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
(double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
@@ -255,16 +366,20 @@ char *pa_source_list_to_string(pa_core *c) {
(unsigned long) pa_source_get_max_rewind(source) / 1024,
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
+ cmn ? "\n\t " : "",
+ cmn ? cmn : "",
pa_source_used_by(source),
pa_source_linked_by(source));
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
+ if (source->card)
+ pa_strbuf_printf(s, "\tcard: %u <%s>\n", source->card->index, source->card->name);
if (source->module)
pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
- t = pa_proplist_to_string(source->proplist);
- pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ t = pa_proplist_to_string_sep(source->proplist, "\n\t\t");
+ pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
}
@@ -291,6 +406,9 @@ char *pa_source_output_list_to_string(pa_core *c) {
for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
pa_usec_t cl;
+ const char *cmn;
+
+ cmn = pa_channel_map_to_pretty_name(&o->channel_map);
if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1)
pa_snprintf(clt, sizeof(clt), "n/a");
@@ -309,7 +427,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
"\tcurrent latency: %0.2f ms\n"
"\trequested latency: %s\n"
"\tsample spec: %s\n"
- "\tchannel map: %s\n"
+ "\tchannel map: %s%s%s\n"
"\tresample method: %s\n",
o->index,
o->driver,
@@ -327,6 +445,8 @@ char *pa_source_output_list_to_string(pa_core *c) {
clt,
pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
+ cmn ? "\n\t " : "",
+ cmn ? cmn : "",
pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
if (o->module)
pa_strbuf_printf(s, "\towner module: %u\n", o->module->index);
@@ -335,8 +455,8 @@ char *pa_source_output_list_to_string(pa_core *c) {
if (o->direct_on_input)
pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index);
- t = pa_proplist_to_string(o->proplist);
- pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ t = pa_proplist_to_string_sep(o->proplist, "\n\t\t");
+ pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
}
@@ -361,8 +481,11 @@ char *pa_sink_input_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
pa_usec_t cl;
+ const char *cmn;
+
+ cmn = pa_channel_map_to_pretty_name(&i->channel_map);
if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
pa_snprintf(clt, sizeof(clt), "n/a");
@@ -379,11 +502,13 @@ char *pa_sink_input_list_to_string(pa_core *c) {
"\tstate: %s\n"
"\tsink: %u <%s>\n"
"\tvolume: %s\n"
+ "\t %s\n"
+ "\t balance %0.2f\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
"\trequested latency: %s\n"
"\tsample spec: %s\n"
- "\tchannel map: %s\n"
+ "\tchannel map: %s%s%s\n"
"\tresample method: %s\n",
i->index,
i->driver,
@@ -398,11 +523,15 @@ char *pa_sink_input_list_to_string(pa_core *c) {
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
+ pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_input_get_volume(i)),
+ pa_cvolume_get_balance(pa_sink_input_get_volume(i), &i->channel_map),
pa_yes_no(pa_sink_input_get_mute(i)),
(double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
clt,
pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+ cmn ? "\n\t " : "",
+ cmn ? cmn : "",
pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
if (i->module)
@@ -410,8 +539,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
if (i->client)
pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME)));
- t = pa_proplist_to_string(i->proplist);
- pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ t = pa_proplist_to_string_sep(i->proplist, "\n\t\t");
+ pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
}
@@ -424,7 +553,7 @@ char *pa_scache_list_to_string(pa_core *c) {
s = pa_strbuf_new();
- pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
+ pa_strbuf_printf(s, "%u cache entrie(s) available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
if (c->scache) {
pa_scache_entry *e;
@@ -432,7 +561,10 @@ char *pa_scache_list_to_string(pa_core *c) {
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
double l = 0;
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
+ const char *cmn;
+
+ cmn = pa_channel_map_to_pretty_name(&e->channel_map);
if (e->memchunk.memblock) {
pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
@@ -445,24 +577,30 @@ char *pa_scache_list_to_string(pa_core *c) {
" name: <%s>\n"
"\tindex: %u\n"
"\tsample spec: %s\n"
- "\tchannel map: %s\n"
+ "\tchannel map: %s%s%s\n"
"\tlength: %lu\n"
"\tduration: %0.1f s\n"
"\tvolume: %s\n"
+ "\t %s\n"
+ "\t balance %0.2f\n"
"\tlazy: %s\n"
"\tfilename: <%s>\n",
e->name,
e->index,
ss,
cm,
+ cmn ? "\n\t " : "",
+ cmn ? cmn : "",
(long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
l,
- pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
+ e->volume_is_set ? pa_cvolume_snprint(cv, sizeof(cv), &e->volume) : "n/a",
+ e->volume_is_set ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &e->volume) : "n/a",
+ (e->memchunk.memblock && e->volume_is_set) ? pa_cvolume_get_balance(&e->volume, &e->channel_map) : 0.0f,
pa_yes_no(e->lazy),
e->filename ? e->filename : "n/a");
- t = pa_proplist_to_string(e->proplist);
- pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ t = pa_proplist_to_string_sep(e->proplist, "\n\t\t");
+ pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
}
}
@@ -470,38 +608,6 @@ char *pa_scache_list_to_string(pa_core *c) {
return pa_strbuf_tostring_free(s);
}
-char *pa_autoload_list_to_string(pa_core *c) {
- pa_strbuf *s;
- pa_assert(c);
-
- s = pa_strbuf_new();
-
- pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0);
-
- if (c->autoload_hashmap) {
- pa_autoload_entry *e;
- void *state = NULL;
-
- while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
- pa_strbuf_printf(
- s,
- " name: <%s>\n"
- "\ttype: %s\n"
- "\tindex: %u\n"
- "\tmodule_name: <%s>\n"
- "\targuments: <%s>\n",
- e->name,
- e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
- e->index,
- e->module,
- e->argument ? e->argument : "");
-
- }
- }
-
- return pa_strbuf_tostring_free(s);
-}
-
char *pa_full_status_string(pa_core *c) {
pa_strbuf *s;
int i;
@@ -528,13 +634,13 @@ char *pa_full_status_string(pa_core *c) {
t = pa_client_list_to_string(c);
break;
case 5:
- t = pa_module_list_to_string(c);
+ t = pa_card_list_to_string(c);
break;
case 6:
- t = pa_scache_list_to_string(c);
+ t = pa_module_list_to_string(c);
break;
case 7:
- t = pa_autoload_list_to_string(c);
+ t = pa_scache_list_to_string(c);
break;
}
diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h
index f4cb97a5..aad51648 100644
--- a/src/pulsecore/cli-text.h
+++ b/src/pulsecore/cli-text.h
@@ -31,12 +31,11 @@ char *pa_sink_input_list_to_string(pa_core *c);
char *pa_source_output_list_to_string(pa_core *c);
char *pa_sink_list_to_string(pa_core *core);
char *pa_source_list_to_string(pa_core *c);
+char *pa_card_list_to_string(pa_core *c);
char *pa_client_list_to_string(pa_core *c);
char *pa_module_list_to_string(pa_core *c);
char *pa_scache_list_to_string(pa_core *c);
-char *pa_autoload_list_to_string(pa_core *c);
char *pa_full_status_string(pa_core *c);
#endif
-
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index 67bf1e73..25a4f748 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -67,20 +67,33 @@ static void client_kill(pa_client *c);
pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
char cname[256];
pa_cli *c;
+ pa_client_new_data data;
+ pa_client *client;
+
pa_assert(io);
+ pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+
+ pa_client_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_proplist_sets(data.proplist, PA_PROP_APPLICATION_NAME, cname);
+ client = pa_client_new(core, &data);
+ pa_client_new_data_done(&data);
+
+ if (!client)
+ return NULL;
+
c = pa_xnew(pa_cli, 1);
c->core = core;
+ c->client = client;
pa_assert_se(c->line = pa_ioline_new(io));
c->userdata = NULL;
c->eof_callback = NULL;
- pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
- pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));
c->client->kill = client_kill;
c->client->userdata = c;
- c->client->module = m;
pa_ioline_set_callback(c->line, line_callback, c);
pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT);
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index ab6e5df4..18004412 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -29,6 +29,7 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
@@ -37,27 +38,49 @@
#include "client.h"
-pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
+pa_client_new_data* pa_client_new_data_init(pa_client_new_data *data) {
+ pa_assert(data);
+
+ memset(data, 0, sizeof(*data));
+ data->proplist = pa_proplist_new();
+
+ return data;
+}
+
+void pa_client_new_data_done(pa_client_new_data *data) {
+ pa_assert(data);
+
+ pa_proplist_free(data->proplist);
+}
+
+pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) {
pa_client *c;
pa_core_assert_ref(core);
+ pa_assert(data);
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_NEW], data) < 0)
+ return NULL;
c = pa_xnew(pa_client, 1);
c->core = core;
- c->proplist = pa_proplist_new();
- if (name)
- pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
- c->driver = pa_xstrdup(driver);
- c->module = NULL;
+ c->proplist = pa_proplist_copy(data->proplist);
+ c->driver = pa_xstrdup(pa_path_get_filename(data->driver));
+ c->module = data->module;
+
+ c->sink_inputs = pa_idxset_new(NULL, NULL);
+ c->source_outputs = pa_idxset_new(NULL, NULL);
- c->kill = NULL;
c->userdata = NULL;
+ c->kill = NULL;
pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
- pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name));
+ pa_log_info("Created %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
+ pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_PUT], c);
+
pa_core_check_idle(core);
return c;
@@ -70,10 +93,19 @@ void pa_client_free(pa_client *c) {
pa_assert(c->core);
core = c->core;
+
+ pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], c);
+
pa_idxset_remove_by_data(c->core->clients, c, NULL);
pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
+
+ pa_assert(pa_idxset_isempty(c->sink_inputs));
+ pa_idxset_free(c->sink_inputs, NULL, NULL);
+ pa_assert(pa_idxset_isempty(c->source_outputs));
+ pa_idxset_free(c->source_outputs, NULL, NULL);
+
pa_proplist_free(c->proplist);
pa_xfree(c->driver);
pa_xfree(c);
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
index 28d1fe5f..48e9bc7a 100644
--- a/src/pulsecore/client.h
+++ b/src/pulsecore/client.h
@@ -42,11 +42,24 @@ struct pa_client {
pa_module *module;
char *driver;
- void (*kill)(pa_client *c);
+ pa_idxset *sink_inputs;
+ pa_idxset *source_outputs;
+
void *userdata;
+
+ void (*kill)(pa_client *c);
};
-pa_client *pa_client_new(pa_core *c, const char *driver, const char *name);
+typedef struct pa_client_new_data {
+ pa_proplist *proplist;
+ const char *driver;
+ pa_module *module;
+} pa_client_new_data;
+
+pa_client_new_data *pa_client_new_data_init(pa_client_new_data *data);
+void pa_client_new_data_done(pa_client_new_data *data);
+
+pa_client *pa_client_new(pa_core *c, pa_client_new_data *data);
/* This function should be called only by the code that created the client */
void pa_client_free(pa_client *c);
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 1d080e11..6d2ae932 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -98,7 +98,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_assert(c);
pa_assert(name);
- if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE))) {
+ if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) {
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
@@ -137,6 +137,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_sample_spec_init(&e->sample_spec);
pa_channel_map_init(&e->channel_map);
pa_cvolume_init(&e->volume);
+ e->volume_is_set = FALSE;
pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
@@ -175,6 +176,7 @@ int pa_scache_add_item(
pa_sample_spec_init(&e->sample_spec);
pa_channel_map_init(&e->channel_map);
pa_cvolume_init(&e->volume);
+ e->volume_is_set = FALSE;
if (ss) {
e->sample_spec = *ss;
@@ -273,7 +275,7 @@ int pa_scache_remove_item(pa_core *c, const char *name) {
pa_assert(c);
pa_assert(name);
- if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
+ if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
return -1;
pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e);
@@ -308,12 +310,13 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
pa_scache_entry *e;
pa_cvolume r;
pa_proplist *merged;
+ pa_bool_t pass_volume;
pa_assert(c);
pa_assert(name);
pa_assert(sink);
- if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE)))
+ if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
return -1;
if (e->lazy && !e->memchunk.memblock) {
@@ -324,10 +327,12 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
- if (pa_cvolume_valid(&e->volume))
- pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map);
- else
- pa_cvolume_reset(&e->volume, e->sample_spec.channels);
+ if (e->volume_is_set) {
+ if (pa_cvolume_valid(&e->volume))
+ pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map);
+ else
+ pa_cvolume_reset(&e->volume, e->sample_spec.channels);
+ }
}
if (!e->memchunk.memblock)
@@ -335,19 +340,26 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
- pa_cvolume_set(&r, e->volume.channels, volume);
- pa_sw_cvolume_multiply(&r, &r, &e->volume);
+ pass_volume = TRUE;
- merged = pa_proplist_new();
+ if (e->volume_is_set && volume != (pa_volume_t) -1) {
+ 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)
+ pa_cvolume_set(&r, e->sample_spec.channels, volume);
+ else
+ pass_volume = FALSE;
+ merged = pa_proplist_new();
pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
-
pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
if (p)
pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
- if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) {
+ if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, sink_input_idx) < 0) {
pa_proplist_free(merged);
return -1;
}
@@ -360,13 +372,13 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
return 0;
}
-int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
pa_sink *sink;
pa_assert(c);
pa_assert(name);
- if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload)))
+ if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK)))
return -1;
return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
@@ -390,7 +402,7 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
pa_assert(c);
pa_assert(name);
- if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE)))
+ if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
return PA_IDXSET_INVALID;
return e->index;
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
index 80e0fd04..a75f8aca 100644
--- a/src/pulsecore/core-scache.h
+++ b/src/pulsecore/core-scache.h
@@ -36,6 +36,7 @@ typedef struct pa_scache_entry {
char *name;
pa_cvolume volume;
+ pa_bool_t volume_is_set;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_memchunk memchunk;
@@ -56,7 +57,7 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
int pa_scache_remove_item(pa_core *c, const char *name);
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
-int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
void pa_scache_free(pa_core *c);
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 6f315666..e65b1796 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -935,7 +935,7 @@ static int is_group(gid_t gid, const char *name) {
#else
n = -1;
#endif
- if (n < 0)
+ if (n <= 0)
n = 512;
data = pa_xmalloc((size_t) n);
@@ -959,7 +959,7 @@ finish:
* support getgrgid_r. */
errno = 0;
- if ((result = getgrgid(gid)) == NULL) {
+ if (!(result = getgrgid(gid))) {
pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno));
if (!errno)
@@ -1026,18 +1026,35 @@ int pa_uid_in_group(uid_t uid, const char *name) {
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;
- if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) {
-
+#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 (!errno)
errno = ENOENT;
-
goto finish;
}
@@ -1045,8 +1062,11 @@ int pa_uid_in_group(uid_t uid, const char *name) {
for (i = gr->gr_mem; *i; i++) {
struct passwd pwbuf, *pw;
- errno = 0;
+#ifdef HAVE_GETPWNAM_R
if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
+#else
+ if (!(pw = getpwnam(*i)))
+#endif
continue;
if (pw->pw_uid == uid) {
@@ -1069,15 +1089,25 @@ gid_t pa_get_gid_of_group(const char *name) {
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);
errno = 0;
- if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) {
-
+#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 (!errno)
errno = ENOENT;
-
goto finish;
}
@@ -2511,3 +2541,15 @@ void pa_reduce(unsigned *num, unsigned *den) {
pa_assert(pa_gcd(*num, *den) == 1);
}
+
+unsigned pa_ncpus(void) {
+ long ncpus;
+
+#ifdef _SC_NPROCESSORS_CONF
+ ncpus = sysconf(_SC_NPROCESSORS_CONF);
+#else
+ ncpus = 1;
+#endif
+
+ return ncpus <= 0 ? 1 : (unsigned) ncpus;
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index d9fad11e..18901f47 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -27,6 +27,7 @@
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
+#include <string.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
@@ -96,6 +97,10 @@ static inline const char *pa_strempty(const char *x) {
return x ? x : "";
}
+static inline const char *pa_strna(const char *x) {
+ return x ? x : "n/a";
+}
+
char *pa_split(const char *c, const char*delimiters, const char **state);
char *pa_split_spaces(const char *c, const char **state);
@@ -197,7 +202,6 @@ pa_bool_t pa_in_system_mode(void);
char *pa_machine_id(void);
char *pa_uname_string(void);
-
#ifdef HAVE_VALGRIND_MEMCHECK_H
pa_bool_t pa_in_valgrind(void);
#else
@@ -209,4 +213,6 @@ static inline pa_bool_t pa_in_valgrind(void) {
unsigned pa_gcd(unsigned a, unsigned b);
void pa_reduce(unsigned *num, unsigned *den);
+unsigned pa_ncpus(void);
+
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 5761bbc7..689fc8f8 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -37,7 +37,6 @@
#include <pulsecore/namereg.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-scache.h>
-#include <pulsecore/autoload.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/shared.h>
#include <pulsecore/random.h>
@@ -91,20 +90,21 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
c->parent.parent.free = core_free;
c->parent.process_msg = core_process_msg;
+ c->state = PA_CORE_STARTUP;
c->mainloop = m;
c->clients = pa_idxset_new(NULL, NULL);
c->sinks = pa_idxset_new(NULL, NULL);
c->sources = pa_idxset_new(NULL, NULL);
c->source_outputs = pa_idxset_new(NULL, NULL);
c->sink_inputs = pa_idxset_new(NULL, NULL);
+ c->cards = pa_idxset_new(NULL, NULL);
- c->default_source_name = c->default_sink_name = NULL;
+ c->default_source = NULL;
+ c->default_sink = NULL;
c->modules = NULL;
c->namereg = NULL;
c->scache = NULL;
- c->autoload_idxset = NULL;
- c->autoload_hashmap = NULL;
c->running_as_daemon = FALSE;
c->default_sample_spec.format = PA_SAMPLE_S16NE;
@@ -113,7 +113,6 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
c->default_n_fragments = 4;
c->default_fragment_size_msec = 25;
- c->module_auto_unload_event = NULL;
c->module_defer_unload_event = NULL;
c->scache_auto_unload_event = NULL;
@@ -128,8 +127,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
c->exit_event = NULL;
c->exit_idle_time = -1;
- c->module_idle_time = 20;
c->scache_idle_time = 20;
+ c->flat_volumes = TRUE;
c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
@@ -153,6 +152,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
pa_core_check_idle(c);
+ c->state = PA_CORE_RUNNING;
+
return c;
}
@@ -161,12 +162,17 @@ static void core_free(pa_object *o) {
int j;
pa_assert(c);
+ c->state = PA_CORE_SHUTDOWN;
+
pa_module_unload_all(c);
pa_assert(!c->modules);
pa_assert(pa_idxset_isempty(c->clients));
pa_idxset_free(c->clients, NULL, NULL);
+ pa_assert(pa_idxset_isempty(c->cards));
+ pa_idxset_free(c->cards, NULL, NULL);
+
pa_assert(pa_idxset_isempty(c->sinks));
pa_idxset_free(c->sinks, NULL, NULL);
@@ -181,14 +187,13 @@ static void core_free(pa_object *o) {
pa_scache_free(c);
pa_namereg_free(c);
- pa_autoload_free(c);
pa_subscription_free_all(c);
if (c->exit_event)
c->mainloop->time_free(c->exit_event);
- pa_xfree(c->default_source_name);
- pa_xfree(c->default_sink_name);
+ pa_assert(!c->default_source);
+ pa_assert(!c->default_sink);
pa_silence_cache_done(&c->silence_cache);
pa_mempool_free(c->mempool);
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index f796fb93..b349c6fc 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -25,6 +25,8 @@
#include <pulse/mainloop-api.h>
#include <pulse/sample.h>
+typedef struct pa_core pa_core;
+
#include <pulsecore/idxset.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/memblock.h>
@@ -34,13 +36,18 @@
#include <pulsecore/hook-list.h>
#include <pulsecore/asyncmsgq.h>
#include <pulsecore/sample-util.h>
-
-typedef struct pa_core pa_core;
-
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/msgobject.h>
+typedef enum pa_core_state {
+ PA_CORE_STARTUP,
+ PA_CORE_RUNNING,
+ PA_CORE_SHUTDOWN
+} pa_core_state_t;
+
typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_NEW,
PA_CORE_HOOK_SINK_FIXATE,
@@ -49,7 +56,6 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_UNLINK_POST,
PA_CORE_HOOK_SINK_STATE_CHANGED,
PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
- PA_CORE_HOOK_SINK_SET_VOLUME,
PA_CORE_HOOK_SOURCE_NEW,
PA_CORE_HOOK_SOURCE_FIXATE,
PA_CORE_HOOK_SOURCE_PUT,
@@ -62,8 +68,8 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_INPUT_PUT,
PA_CORE_HOOK_SINK_INPUT_UNLINK,
PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
- PA_CORE_HOOK_SINK_INPUT_MOVE,
- PA_CORE_HOOK_SINK_INPUT_MOVE_POST,
+ PA_CORE_HOOK_SINK_INPUT_MOVE_START,
+ PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH,
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
@@ -72,10 +78,16 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
- PA_CORE_HOOK_SOURCE_OUTPUT_MOVE,
- PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST,
+ PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START,
+ PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH,
PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+ PA_CORE_HOOK_CLIENT_NEW,
+ PA_CORE_HOOK_CLIENT_PUT,
+ PA_CORE_HOOK_CLIENT_UNLINK,
+ PA_CORE_HOOK_CARD_NEW,
+ PA_CORE_HOOK_CARD_PUT,
+ PA_CORE_HOOK_CARD_UNLINK,
PA_CORE_HOOK_MAX
} pa_core_hook_t;
@@ -86,6 +98,8 @@ typedef enum pa_core_hook {
struct pa_core {
pa_msgobject parent;
+ pa_core_state_t state;
+
/* A random value which may be used to identify this instance of
* PulseAudio. Not cryptographically secure in any way. */
uint32_t cookie;
@@ -93,18 +107,18 @@ struct pa_core {
pa_mainloop_api *mainloop;
/* idxset of all kinds of entities */
- pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
+ pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache;
/* Some hashmaps for all sorts of entities */
- pa_hashmap *namereg, *autoload_hashmap, *shared;
+ pa_hashmap *namereg, *shared;
/* The name of the default sink/source */
- char *default_source_name, *default_sink_name;
+ pa_source *default_source;
+ pa_sink *default_sink;
pa_sample_spec default_sample_spec;
unsigned default_n_fragments, default_fragment_size_msec;
- pa_time_event *module_auto_unload_event;
pa_defer_event *module_defer_unload_event;
pa_defer_event *subscription_defer_event;
@@ -115,7 +129,8 @@ struct pa_core {
pa_mempool *mempool;
pa_silence_cache silence_cache;
- int exit_idle_time, module_idle_time, scache_idle_time;
+ int exit_idle_time, scache_idle_time;
+ pa_bool_t flat_volumes;
pa_time_event *exit_event;
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
index 85bebd68..eea1c743 100644
--- a/src/pulsecore/endianmacros.h
+++ b/src/pulsecore/endianmacros.h
@@ -45,6 +45,32 @@
#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) (x) >> 24) | ((uint32_t) (x) << 24) | (((uint32_t) (x) & 0xFF00) << 8) | ((((uint32_t) (x)) >> 8) & 0xFF00) ) )
#endif
+static inline uint32_t PA_READ24LE(const uint8_t *p) {
+ return
+ ((uint32_t) p[0] << 16) |
+ ((uint32_t) p[1] << 8) |
+ ((uint32_t) p[2]);
+}
+
+static inline uint32_t PA_READ24BE(const uint8_t *p) {
+ return
+ ((uint32_t) p[2] << 16) |
+ ((uint32_t) p[1] << 8) |
+ ((uint32_t) p[0]);
+}
+
+static inline void PA_WRITE24LE(uint8_t *p, uint32_t u) {
+ p[0] = (uint8_t) (u >> 16);
+ p[1] = (uint8_t) (u >> 8);
+ p[2] = (uint8_t) u;
+}
+
+static inline void PA_WRITE24BE(uint8_t *p, uint32_t u) {
+ p[2] = (uint8_t) (u >> 16);
+ p[1] = (uint8_t) (u >> 8);
+ p[0] = (uint8_t) u;
+}
+
static inline float PA_FLOAT32_SWAP(float x) {
union {
float f;
@@ -91,6 +117,12 @@ static inline float PA_FLOAT32_SWAP(float x) {
#define PA_FLOAT32_TO_LE(x) PA_FLOAT32_SWAP(x)
#define PA_FLOAT32_TO_BE(x) ((float) (x))
+
+ #define PA_READ24NE(x) PA_READ24BE(x)
+ #define PA_WRITE24NE(x,y) PA_WRITE24BE((x),(y))
+
+ #define PA_READ24RE(x) PA_READ24LE(x)
+ #define PA_WRITE24RE(x,y) PA_WRITE24LE((x),(y))
#else
#define PA_INT16_FROM_LE(x) ((int16_t)(x))
#define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x)
@@ -118,6 +150,12 @@ static inline float PA_FLOAT32_SWAP(float x) {
#define PA_FLOAT32_TO_LE(x) ((float) (x))
#define PA_FLOAT32_TO_BE(x) PA_FLOAT32_SWAP(x)
+
+ #define PA_READ24NE(x) PA_READ24LE(x)
+ #define PA_WRITE24NE(x,y) PA_WRITE24LE((x),(y))
+
+ #define PA_READ24RE(x) PA_READ24BE(x)
+ #define PA_WRITE24RE(x,y) PA_WRITE24BE((x),(y))
#endif
#endif
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
index 7f2252e9..f872d347 100644
--- a/src/pulsecore/envelope.c
+++ b/src/pulsecore/envelope.c
@@ -600,7 +600,6 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
switch (e->sample_spec.format) {
-
case PA_SAMPLE_U8: {
uint8_t *t;
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index adf2f112..9a7f7cac 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -47,6 +47,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/once.h>
+#include <pulsecore/ratelimit.h>
#include "log.h"
@@ -375,3 +376,10 @@ void pa_log_level(pa_log_level_t level, const char *format, ...) {
pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
va_end(ap);
}
+
+pa_bool_t pa_log_ratelimit(void) {
+ /* Not more than 10 messages every 5s */
+ static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
+
+ return pa_ratelimit_test(&ratelimit);
+}
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index 3d66e903..77adb791 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -109,4 +109,6 @@ LOG_FUNC(error, PA_LOG_ERROR)
#define pa_log pa_log_error
+pa_bool_t pa_log_ratelimit(void);
+
#endif
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 56ed2c5d..d470bb0b 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -33,39 +33,26 @@
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
+#include <pulse/proplist.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
#include <pulsecore/ltdl-helper.h>
+#include <pulsecore/modinfo.h>
#include "module.h"
#define PA_SYMBOL_INIT "pa__init"
#define PA_SYMBOL_DONE "pa__done"
#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
-
-#define UNLOAD_POLL_TIME 2
-
-static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
- pa_core *c = PA_CORE(userdata);
- struct timeval ntv;
-
- pa_core_assert_ref(c);
- pa_assert(c->mainloop == m);
- pa_assert(c->module_auto_unload_event == e);
-
- pa_module_unload_unused(c);
-
- pa_gettimeofday(&ntv);
- pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC);
- m->time_restart(e, &ntv);
-}
+#define PA_SYMBOL_GET_N_USED "pa__get_n_used"
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
pa_bool_t (*load_once)(void);
+ pa_modinfo *mi;
pa_assert(c);
pa_assert(name);
@@ -77,6 +64,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
m->name = pa_xstrdup(name);
m->argument = pa_xstrdup(argument);
m->load_once = FALSE;
+ m->proplist = pa_proplist_new();
if (!(m->dl = lt_dlopenext(name))) {
pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());
@@ -107,10 +95,9 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
}
m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);
+ m->get_n_used = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_N_USED);
m->userdata = NULL;
m->core = c;
- m->n_used = -1;
- m->auto_unload = FALSE;
m->unload_requested = FALSE;
if (m->init(m) < 0) {
@@ -121,13 +108,6 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
if (!c->modules)
c->modules = pa_idxset_new(NULL, NULL);
- if (m->auto_unload && !c->module_auto_unload_event) {
- struct timeval ntv;
- pa_gettimeofday(&ntv);
- pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC);
- c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
- }
-
pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
pa_assert(m->index != PA_IDXSET_INVALID);
@@ -135,11 +115,28 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
+ if ((mi = pa_modinfo_get_by_handle(m->dl, name))) {
+
+ if (mi->author && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_AUTHOR))
+ pa_proplist_sets(m->proplist, PA_PROP_MODULE_AUTHOR, mi->author);
+
+ if (mi->description && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_DESCRIPTION))
+ pa_proplist_sets(m->proplist, PA_PROP_MODULE_DESCRIPTION, mi->description);
+
+ if (mi->version && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_VERSION))
+ pa_proplist_sets(m->proplist, PA_PROP_MODULE_VERSION, mi->version);
+
+ pa_modinfo_free(mi);
+ }
+
return m;
fail:
if (m) {
+ if (m->proplist)
+ pa_proplist_free(m->proplist);
+
pa_xfree(m->argument);
pa_xfree(m->name);
@@ -161,6 +158,9 @@ static void pa_module_free(pa_module *m) {
if (m->done)
m->done(m);
+ if (m->proplist)
+ pa_proplist_free(m->proplist);
+
lt_dlclose(m->dl);
pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index);
@@ -212,44 +212,12 @@ void pa_module_unload_all(pa_core *c) {
c->modules = NULL;
}
- if (c->module_auto_unload_event) {
- c->mainloop->time_free(c->module_auto_unload_event);
- c->module_auto_unload_event = NULL;
- }
-
if (c->module_defer_unload_event) {
c->mainloop->defer_free(c->module_defer_unload_event);
c->module_defer_unload_event = NULL;
}
}
-void pa_module_unload_unused(pa_core *c) {
- void *state = NULL;
- time_t now;
- pa_module *m;
-
- pa_assert(c);
-
- if (!c->modules)
- return;
-
- time(&now);
-
- while ((m = pa_idxset_iterate(c->modules, &state, NULL))) {
-
- if (m->n_used > 0)
- continue;
-
- if (!m->auto_unload)
- continue;
-
- if (m->last_used_time + m->core->module_idle_time > now)
- continue;
-
- pa_module_unload(c, m, FALSE);
- }
-}
-
static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
void *state = NULL;
pa_core *c = PA_CORE(userdata);
@@ -290,20 +258,11 @@ void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force
pa_module_unload_request(m, force);
}
-void pa_module_set_used(pa_module*m, int used) {
+int pa_module_get_n_used(pa_module*m) {
pa_assert(m);
- if (m->n_used != used)
- pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
-
- if (used == 0 && m->n_used > 0)
- time(&m->last_used_time);
-
- m->n_used = used;
-}
-
-pa_modinfo *pa_module_get_info(pa_module *m) {
- pa_assert(m);
+ if (!m->get_n_used)
+ return -1;
- return pa_modinfo_get_by_handle(m->dl, m->name);
+ return m->get_n_used(m);
}
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 661b2dd6..6ab43dcf 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -27,8 +27,9 @@
typedef struct pa_module pa_module;
+#include <pulse/proplist.h>
+
#include <pulsecore/core.h>
-#include <pulsecore/modinfo.h>
struct pa_module {
pa_core *core;
@@ -39,16 +40,14 @@ struct pa_module {
int (*init)(pa_module*m);
void (*done)(pa_module*m);
+ int (*get_n_used)(pa_module *m);
void *userdata;
- int n_used;
-
- pa_bool_t auto_unload:1;
pa_bool_t load_once:1;
pa_bool_t unload_requested:1;
- time_t last_used_time;
+ pa_proplist *proplist;
};
pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
@@ -60,9 +59,8 @@ void pa_module_unload_request(pa_module *m, pa_bool_t force);
void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force);
void pa_module_unload_all(pa_core *c);
-void pa_module_unload_unused(pa_core *c);
-void pa_module_set_used(pa_module*m, int used);
+int pa_module_get_n_used(pa_module*m);
#define PA_MODULE_AUTHOR(s) \
const char *pa__get_author(void) { return s; } \
@@ -84,6 +82,4 @@ void pa_module_set_used(pa_module*m, int used);
pa_bool_t pa__load_once(void) { return b; } \
struct __stupid_useless_struct_to_allow_trailing_semicolon
-pa_modinfo *pa_module_get_info(pa_module *m);
-
#endif
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index 35465b7b..c3ead97d 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -138,3 +138,23 @@ int pa_cond_wait(pa_cond *c, pa_mutex *m) {
return pthread_cond_wait(&c->cond, &m->mutex);
}
+
+pa_mutex* pa_static_mutex_get(pa_static_mutex *s, pa_bool_t recursive, pa_bool_t inherit_priority) {
+ pa_mutex *m;
+
+ pa_assert(s);
+
+ /* First, check if already initialized and short cut */
+ if ((m = pa_atomic_ptr_load(&s->ptr)))
+ return m;
+
+ /* OK, not initialized, so let's allocate, and fill in */
+ m = pa_mutex_new(recursive, inherit_priority);
+ if ((pa_atomic_ptr_cmpxchg(&s->ptr, NULL, m)))
+ return m;
+
+ /* Him, filling in failed, so someone else must have filled in
+ * already */
+ pa_assert_se(m = pa_atomic_ptr_load(&s->ptr));
+ return m;
+}
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
index 36e1d635..8e0b1f2e 100644
--- a/src/pulsecore/mutex.h
+++ b/src/pulsecore/mutex.h
@@ -23,6 +23,7 @@
***/
#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
typedef struct pa_mutex pa_mutex;
@@ -45,4 +46,10 @@ void pa_cond_free(pa_cond *c);
void pa_cond_signal(pa_cond *c, int broadcast);
int pa_cond_wait(pa_cond *c, pa_mutex *m);
+typedef struct pa_static_mutex {
+ pa_atomic_ptr_t ptr;
+} pa_static_mutex;
+
+pa_mutex* pa_static_mutex_get(pa_static_mutex *m, pa_bool_t recursive, pa_bool_t inherit_priority);
+
#endif
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index ecd8def8..f3d5a8f8 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -30,7 +30,6 @@
#include <pulse/xmalloc.h>
-#include <pulsecore/autoload.h>
#include <pulsecore/source.h>
#include <pulsecore/sink.h>
#include <pulsecore/core-subscribe.h>
@@ -109,7 +108,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
if (!*name)
return NULL;
- if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) &&
+ if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD) &&
!pa_namereg_is_valid_name(name)) {
if (fail)
@@ -174,59 +173,49 @@ void pa_namereg_unregister(pa_core *c, const char *name) {
pa_assert_se(e = pa_hashmap_remove(c->namereg, name));
+ if (c->default_sink == e->data)
+ pa_namereg_set_default_sink(c, NULL);
+ else if (c->default_source == e->data)
+ pa_namereg_set_default_source(c, NULL);
+
pa_xfree(e->name);
pa_xfree(e);
}
-void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload) {
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
struct namereg_entry *e;
uint32_t idx;
pa_assert(c);
- if (!name) {
+ if (type == PA_NAMEREG_SOURCE && (!name || pa_streq(name, "@DEFAULT_SOURCE@"))) {
+ pa_source *s;
- if (type == PA_NAMEREG_SOURCE)
- name = pa_namereg_get_default_source_name(c);
- else if (type == PA_NAMEREG_SINK)
- name = pa_namereg_get_default_sink_name(c);
+ if ((s = pa_namereg_get_default_source(c)))
+ return s;
- } else if (strcmp(name, "@DEFAULT_SINK@") == 0) {
- if (type == PA_NAMEREG_SINK)
- name = pa_namereg_get_default_sink_name(c);
+ } else if (type == PA_NAMEREG_SINK && (!name || pa_streq(name, "@DEFAULT_SINK@"))) {
+ pa_sink *s;
- } else if (strcmp(name, "@DEFAULT_SOURCE@") == 0) {
- if (type == PA_NAMEREG_SOURCE)
- name = pa_namereg_get_default_source_name(c);
+ if ((s = pa_namereg_get_default_sink(c)))
+ return s;
- } else if (strcmp(name, "@DEFAULT_MONITOR@") == 0) {
- if (type == PA_NAMEREG_SOURCE) {
- pa_sink *k;
+ } else if (type == PA_NAMEREG_SOURCE && name && pa_streq(name, "@DEFAULT_MONITOR@")) {
+ pa_sink *s;
- if ((k = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, autoload)))
- return k->monitor_source;
- }
- } else if (*name == '@')
- name = NULL;
+ if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)))
+ return s->monitor_source;
- if (!name)
+ }
+
+ if (*name == '@' || !name || !pa_namereg_is_valid_name(name))
return NULL;
if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
if (e->type == type)
return e->data;
- if (pa_atou(name, &idx) < 0) {
-
- if (autoload) {
- pa_autoload_request(c, name, type);
-
- if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
- if (e->type == type)
- return e->data;
- }
-
+ if (pa_atou(name, &idx) < 0)
return NULL;
- }
if (type == PA_NAMEREG_SINK)
return pa_idxset_get_by_index(c->sinks, idx);
@@ -234,66 +223,63 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bo
return pa_idxset_get_by_index(c->sources, idx);
else if (type == PA_NAMEREG_SAMPLE && c->scache)
return pa_idxset_get_by_index(c->scache, idx);
+ else if (type == PA_NAMEREG_CARD)
+ return pa_idxset_get_by_index(c->cards, idx);
return NULL;
}
-int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) {
- char **s;
-
+pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) {
pa_assert(c);
- pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
-
- s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name;
- if (!name && !*s)
- return 0;
+ if (c->default_sink != s) {
+ c->default_sink = s;
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+ }
- if (name && *s && !strcmp(name, *s))
- return 0;
+ return s;
+}
- if (!pa_namereg_is_valid_name(name))
- return -1;
+pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) {
+ pa_assert(c);
- pa_xfree(*s);
- *s = pa_xstrdup(name);
- pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+ if (c->default_source != s) {
+ c->default_source = s;
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+ }
- return 0;
+ return s;
}
-const char *pa_namereg_get_default_sink_name(pa_core *c) {
+pa_sink *pa_namereg_get_default_sink(pa_core *c) {
pa_sink *s;
pa_assert(c);
- if (c->default_sink_name)
- return c->default_sink_name;
+ if (c->default_sink)
+ return c->default_sink;
if ((s = pa_idxset_first(c->sinks, NULL)))
- pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK);
+ return pa_namereg_set_default_sink(c, s);
- return c->default_sink_name;
+ return NULL;
}
-const char *pa_namereg_get_default_source_name(pa_core *c) {
+pa_source *pa_namereg_get_default_source(pa_core *c) {
pa_source *s;
uint32_t idx;
pa_assert(c);
- if (c->default_source_name)
- return c->default_source_name;
+ if (c->default_source)
+ return c->default_source;
- for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx))
- if (!s->monitor_of) {
- pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
- break;
- }
+ 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 (!c->default_source_name)
- if ((s = pa_idxset_first(c->sources, NULL)))
- pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
+ if ((s = pa_idxset_first(c->sources, NULL)))
+ return pa_namereg_set_default_source(c, s);
- return c->default_source_name;
+ return NULL;
}
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index f4581006..b91dd52d 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -30,18 +30,21 @@
typedef enum pa_namereg_type {
PA_NAMEREG_SINK,
PA_NAMEREG_SOURCE,
- PA_NAMEREG_SAMPLE
+ PA_NAMEREG_SAMPLE,
+ PA_NAMEREG_CARD
} pa_namereg_type_t;
void pa_namereg_free(pa_core *c);
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail);
void pa_namereg_unregister(pa_core *c, const char *name);
-void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload);
-int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type);
-const char *pa_namereg_get_default_sink_name(pa_core *c);
-const char *pa_namereg_get_default_source_name(pa_core *c);
+pa_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_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);
char* pa_namereg_make_valid_name(const char *name);
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 8138b7a4..b31a5da1 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -94,10 +94,11 @@ enum {
PA_COMMAND_LOAD_MODULE,
PA_COMMAND_UNLOAD_MODULE,
- PA_COMMAND_ADD_AUTOLOAD,
- PA_COMMAND_REMOVE_AUTOLOAD,
- PA_COMMAND_GET_AUTOLOAD_INFO,
- PA_COMMAND_GET_AUTOLOAD_INFO_LIST,
+ /* Obsolete */
+ PA_COMMAND_ADD_AUTOLOAD___OBSOLETE,
+ PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE,
+ PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE,
+ PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE,
PA_COMMAND_GET_RECORD_LATENCY,
PA_COMMAND_CORK_RECORD_STREAM,
@@ -151,6 +152,11 @@ enum {
/* Supported since protocol v14 (0.9.12) */
PA_COMMAND_EXTENSION,
+ /* Supported since protocol v15 (0.9.15*/
+ PA_COMMAND_GET_CARD_INFO,
+ PA_COMMAND_GET_CARD_INFO_LIST,
+ PA_COMMAND_SET_CARD_PROFILE,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index a5b33d62..305941a3 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -110,10 +110,10 @@ static const char *command_names[PA_COMMAND_MAX] = {
[PA_COMMAND_LOAD_MODULE] = "LOAD_MODULE",
[PA_COMMAND_UNLOAD_MODULE] = "UNLOAD_MODULE",
- [PA_COMMAND_ADD_AUTOLOAD] = "ADD_AUTOLOAD",
- [PA_COMMAND_REMOVE_AUTOLOAD] = "REMOVE_AUTOLOAD",
- [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO",
- [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST",
+ [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = "ADD_AUTOLOAD (obsolete)",
+ [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = "REMOVE_AUTOLOAD (obsolete)",
+ [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = "GET_AUTOLOAD_INFO (obsolete)",
+ [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = "GET_AUTOLOAD_INFO_LIST (obsolete)",
[PA_COMMAND_GET_RECORD_LATENCY] = "GET_RECORD_LATENCY",
[PA_COMMAND_CORK_RECORD_STREAM] = "CORK_RECORD_STREAM",
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 86edfe98..eac21de5 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -109,7 +109,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
* 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);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
@@ -197,7 +197,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
- pa_sink_input_new_data_set_volume(&data, volume);
+ pa_sink_input_new_data_set_virtual_volume(&data, volume);
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
u->sink_input = pa_sink_input_new(sink->core, &data, 0);
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 460119a9..1311e678 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -403,7 +403,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification");
if (c->options->default_sink) {
- sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1);
+ sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK);
CHECK_VALIDITY(sink, "No such sink: %s", c->options->default_sink);
}
@@ -422,7 +422,6 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
sdata.module = c->options->module;
sdata.client = c->client;
sdata.sink = sink;
- pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
@@ -490,7 +489,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
if (request == ESD_PROTO_STREAM_MON) {
pa_sink* sink;
- if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) {
pa_log("no such sink.");
return -1;
}
@@ -503,7 +502,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
pa_assert(request == ESD_PROTO_STREAM_REC);
if (c->options->default_source) {
- if (!(source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE, 1))) {
+ if (!(source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE))) {
pa_log("no such source.");
return -1;
}
@@ -525,7 +524,6 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
sdata.module = c->options->module;
sdata.client = c->client;
sdata.source = source;
- pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_source_output_new_data_set_sample_spec(&sdata, &ss);
c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
@@ -569,7 +567,7 @@ static int esd_proto_get_latency(connection *c, esd_proto_t request, const void
pa_assert(!data);
pa_assert(length == 0);
- if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1)))
+ if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
latency = 0;
else {
double usec = (double) pa_sink_get_latency(sink);
@@ -590,7 +588,7 @@ static int esd_proto_server_info(connection *c, esd_proto_t request, const void
pa_assert(data);
pa_assert(length == sizeof(int32_t));
- if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) {
+ if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) {
rate = (int32_t) sink->sample_spec.rate;
format = format_native2esd(&sink->sample_spec);
}
@@ -762,7 +760,7 @@ static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *
volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
volume.channels = 2;
- pa_sink_input_set_volume(conn->sink_input, &volume);
+ pa_sink_input_set_volume(conn->sink_input, &volume, TRUE);
ok = 1;
} else
ok = 0;
@@ -865,7 +863,7 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con
if (request == ESD_PROTO_SAMPLE_PLAY) {
pa_sink *sink;
- if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1)))
+ if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
ok = (int32_t) idx + 1;
} else {
@@ -1238,7 +1236,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
pa_log_debug("Requesting rewind due to end of underrun.");
- pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE, FALSE);
}
/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
@@ -1377,7 +1375,9 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev
void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o) {
connection *c;
- char cname[256], pname[128];
+ char pname[128];
+ pa_client_new_data data;
+ pa_client *client;
pa_assert(p);
pa_assert(io);
@@ -1389,6 +1389,18 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou
return;
}
+ pa_client_new_data_init(&data);
+ data.module = o->module;
+ data.driver = __FILE__;
+ pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+ pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "EsounD client (%s)", pname);
+ pa_proplist_sets(data.proplist, "esound-protocol.peer", pname);
+ client = pa_client_new(p->core, &data);
+ pa_client_new_data_done(&data);
+
+ if (!client)
+ return;
+
c = pa_msgobject_new(connection);
c->parent.parent.free = connection_free;
c->parent.process_msg = connection_process_msg;
@@ -1396,11 +1408,7 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou
c->io = io;
pa_iochannel_set_callback(c->io, io_callback, c);
- pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
- pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
- c->client = pa_client_new(p->core, __FILE__, cname);
- pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname);
- c->client->module = o->module;
+ c->client = client;
c->client->kill = client_kill_cb;
c->client->userdata = c;
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index c89d48b6..5379a36c 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -156,6 +156,8 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
if (!strcmp(c->url, URL_ROOT)) {
char txt[256];
+ pa_sink *def_sink;
+ pa_source *def_source;
http_response(c, 200, "OK", "text/html");
pa_ioline_puts(c->line,
@@ -173,8 +175,12 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt)));
PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
- PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
- PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
+
+ def_sink = pa_namereg_get_default_sink(c->protocol->core);
+ def_source = pa_namereg_get_default_source(c->protocol->core);
+
+ PRINTF_FIELD("Default Sink:", def_sink ? def_sink->name : "n/a");
+ PRINTF_FIELD("Default Source:", def_source ? def_source->name : "n/a");
pa_ioline_puts(c->line, "</table>");
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 3c1e5761..c9621652 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -49,7 +49,6 @@
#include <pulsecore/core-scache.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
-#include <pulsecore/autoload.h>
#include <pulsecore/strlist.h>
#include <pulsecore/shared.h>
#include <pulsecore/sample-util.h>
@@ -248,10 +247,6 @@ static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t
static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -261,6 +256,7 @@ static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command
static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = NULL,
@@ -288,6 +284,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_GET_SINK_INFO] = command_get_info,
[PA_COMMAND_GET_SOURCE_INFO] = command_get_info,
[PA_COMMAND_GET_CLIENT_INFO] = command_get_info,
+ [PA_COMMAND_GET_CARD_INFO] = command_get_info,
[PA_COMMAND_GET_MODULE_INFO] = command_get_info,
[PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
[PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
@@ -296,6 +293,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
[PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
[PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list,
+ [PA_COMMAND_GET_CARD_INFO_LIST] = command_get_info_list,
[PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
[PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
[PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
@@ -330,10 +328,11 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill,
[PA_COMMAND_LOAD_MODULE] = command_load_module,
[PA_COMMAND_UNLOAD_MODULE] = command_unload_module,
- [PA_COMMAND_GET_AUTOLOAD_INFO] = command_get_autoload_info,
- [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = command_get_autoload_info_list,
- [PA_COMMAND_ADD_AUTOLOAD] = command_add_autoload,
- [PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload,
+
+ [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = NULL,
+ [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = NULL,
+ [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = NULL,
+ [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = NULL,
[PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
[PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
@@ -352,6 +351,8 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
[PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
+ [PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
+
[PA_COMMAND_EXTENSION] = command_extension
};
@@ -605,7 +606,6 @@ static record_stream* record_stream_new(
pa_source_output_new_data_init(&data);
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
- pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
data.driver = __FILE__;
data.module = c->options->module;
data.client = c->client;
@@ -1005,7 +1005,6 @@ static playback_stream* playback_stream_new(
pa_sink_input_new_data_init(&data);
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
- pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
data.driver = __FILE__;
data.module = c->options->module;
data.client = c->client;
@@ -1013,7 +1012,7 @@ static playback_stream* playback_stream_new(
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
if (volume)
- pa_sink_input_new_data_set_volume(&data, volume);
+ pa_sink_input_new_data_set_virtual_volume(&data, volume);
if (muted_set)
pa_sink_input_new_data_set_muted(&data, muted);
data.sync_base = ssync ? ssync->sink_input : NULL;
@@ -1240,7 +1239,7 @@ 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),
- FALSE, TRUE);
+ FALSE, TRUE, FALSE);
}
} else {
@@ -1253,7 +1252,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
* let's have it usk us again */
pa_log_debug("Requesting rewind due to rewrite.");
- pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), TRUE, FALSE);
+ pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), TRUE, FALSE, FALSE);
}
}
@@ -1799,7 +1798,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
} else if (sink_name) {
- if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
pa_proplist_free(p);
return;
@@ -2040,7 +2039,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
} else if (source_name) {
- if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1))) {
+ if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
pa_proplist_free(p);
return;
@@ -2315,12 +2314,12 @@ static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_
if (command == PA_COMMAND_LOOKUP_SINK) {
pa_sink *sink;
- if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1)))
+ if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK)))
idx = sink->index;
} else {
pa_source *source;
pa_assert(command == PA_COMMAND_LOOKUP_SOURCE);
- if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1)))
+ if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE)))
idx = source->index;
}
@@ -2490,7 +2489,9 @@ static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uin
p = pa_proplist_new();
- if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) {
+ if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+ !pa_tagstruct_eof(t)) {
+
protocol_error(c);
pa_proplist_free(p);
return;
@@ -2575,7 +2576,7 @@ static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag
if (sink_index != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
else
- sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
+ sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK);
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
@@ -2646,6 +2647,13 @@ static void fixup_sample_spec(pa_native_connection *c, pa_sample_spec *fixed, co
if (fixed->format == PA_SAMPLE_S32BE)
fixed->format = PA_SAMPLE_FLOAT32BE;
}
+
+ if (c->version < 15) {
+ if (fixed->format == PA_SAMPLE_S24LE || fixed->format == PA_SAMPLE_S24_32LE)
+ fixed->format = PA_SAMPLE_FLOAT32LE;
+ if (fixed->format == PA_SAMPLE_S24BE || fixed->format == PA_SAMPLE_S24_32BE)
+ fixed->format = PA_SAMPLE_FLOAT32BE;
+ }
}
static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink *sink) {
@@ -2678,8 +2686,13 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
}
- if (c->version >= 15)
+ if (c->version >= 15) {
pa_tagstruct_put_volume(t, sink->base_volume);
+ if (PA_UNLIKELY(pa_sink_get_state(sink) == PA_SINK_INVALID_STATE))
+ pa_log_error("Internal sink state is invalid.");
+ pa_tagstruct_putu32(t, pa_sink_get_state(sink));
+ pa_tagstruct_putu32(t, sink->n_volume_steps);
+ }
}
static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
@@ -2712,8 +2725,13 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
}
- if (c->version >= 15)
+ if (c->version >= 15) {
pa_tagstruct_put_volume(t, source->base_volume);
+ if (PA_UNLIKELY(pa_source_get_state(source) == PA_SOURCE_INVALID_STATE))
+ pa_log_error("Internal source state is invalid.");
+ pa_tagstruct_putu32(t, pa_source_get_state(source));
+ pa_tagstruct_putu32(t, source->n_volume_steps);
+ }
}
static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
@@ -2727,18 +2745,47 @@ static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_c
if (c->version >= 13)
pa_tagstruct_put_proplist(t, client->proplist);
+}
+
+static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_card *card) {
+ void *state = NULL;
+ pa_card_profile *p;
+
+ pa_assert(t);
+ pa_assert(card);
+
+ pa_tagstruct_putu32(t, card->index);
+ pa_tagstruct_puts(t, card->name);
+ pa_tagstruct_putu32(t, card->module ? card->module->index : PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, card->driver);
+
+ pa_tagstruct_putu32(t, card->profiles ? pa_hashmap_size(card->profiles) : 0);
+ if (card->profiles) {
+ while ((p = pa_hashmap_iterate(card->profiles, &state, NULL))) {
+ pa_tagstruct_puts(t, p->name);
+ pa_tagstruct_puts(t, p->description);
+ }
+ }
+
+ pa_tagstruct_puts(t, card->active_profile ? card->active_profile->name : NULL);
+ pa_tagstruct_put_proplist(t, card->proplist);
}
-static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
+static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) {
pa_assert(t);
pa_assert(module);
pa_tagstruct_putu32(t, module->index);
pa_tagstruct_puts(t, module->name);
pa_tagstruct_puts(t, module->argument);
- pa_tagstruct_putu32(t, (uint32_t) module->n_used);
- pa_tagstruct_put_boolean(t, module->auto_unload);
+ pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module));
+
+ if (c->version < 15)
+ pa_tagstruct_put_boolean(t, FALSE); /* autoload is obsolete */
+
+ if (c->version >= 15)
+ pa_tagstruct_put_proplist(t, module->proplist);
}
static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
@@ -2795,6 +2842,7 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *
static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
pa_sample_spec fixed_ss;
+ pa_cvolume v;
pa_assert(t);
pa_assert(e);
@@ -2806,7 +2854,13 @@ static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
pa_tagstruct_putu32(t, e->index);
pa_tagstruct_puts(t, e->name);
- pa_tagstruct_put_cvolume(t, &e->volume);
+
+ if (e->volume_is_set)
+ v = e->volume;
+ else
+ pa_cvolume_init(&v);
+
+ pa_tagstruct_put_cvolume(t, &v);
pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &e->channel_map);
@@ -2824,6 +2878,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
pa_sink *sink = NULL;
pa_source *source = NULL;
pa_client *client = NULL;
+ pa_card *card = NULL;
pa_module *module = NULL;
pa_sink_input *si = NULL;
pa_source_output *so = NULL;
@@ -2836,6 +2891,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
if (pa_tagstruct_getu32(t, &idx) < 0 ||
(command != PA_COMMAND_GET_CLIENT_INFO &&
+ command != PA_COMMAND_GET_CARD_INFO &&
command != PA_COMMAND_GET_MODULE_INFO &&
command != PA_COMMAND_GET_SINK_INPUT_INFO &&
command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO &&
@@ -2855,12 +2911,17 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
if (idx != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
else
- sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
} else if (command == PA_COMMAND_GET_SOURCE_INFO) {
if (idx != PA_INVALID_INDEX)
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
else
- source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+ } else if (command == PA_COMMAND_GET_CARD_INFO) {
+ if (idx != PA_INVALID_INDEX)
+ card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
+ else
+ card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
} else if (command == PA_COMMAND_GET_CLIENT_INFO)
client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
else if (command == PA_COMMAND_GET_MODULE_INFO)
@@ -2874,10 +2935,10 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
if (idx != PA_INVALID_INDEX)
sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
else
- sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE, 0);
+ sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE);
}
- if (!sink && !source && !client && !module && !si && !so && !sce) {
+ if (!sink && !source && !client && !card && !module && !si && !so && !sce) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
return;
}
@@ -2889,8 +2950,10 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
source_fill_tagstruct(c, reply, source);
else if (client)
client_fill_tagstruct(c, reply, client);
+ else if (client)
+ card_fill_tagstruct(c, reply, card);
else if (module)
- module_fill_tagstruct(reply, module);
+ module_fill_tagstruct(c, reply, module);
else if (si)
sink_input_fill_tagstruct(c, reply, si);
else if (so)
@@ -2925,6 +2988,8 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
i = c->protocol->core->sources;
else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
i = c->protocol->core->clients;
+ else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
+ i = c->protocol->core->cards;
else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
i = c->protocol->core->modules;
else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
@@ -2944,8 +3009,10 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
source_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
client_fill_tagstruct(c, reply, p);
+ else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
+ card_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
- module_fill_tagstruct(reply, p);
+ module_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
sink_input_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
@@ -2964,7 +3031,8 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_tagstruct *reply;
char txt[256];
- const char *n;
+ pa_sink *def_sink;
+ pa_source *def_source;
pa_sample_spec fixed_ss;
pa_native_connection_assert_ref(c);
@@ -2986,10 +3054,10 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
pa_tagstruct_put_sample_spec(reply, &fixed_ss);
- n = pa_namereg_get_default_sink_name(c->protocol->core);
- pa_tagstruct_puts(reply, n);
- n = pa_namereg_get_default_source_name(c->protocol->core);
- pa_tagstruct_puts(reply, n);
+ def_sink = pa_namereg_get_default_sink(c->protocol->core);
+ pa_tagstruct_puts(reply, def_sink ? def_sink->name : NULL);
+ def_source = pa_namereg_get_default_source(c->protocol->core);
+ pa_tagstruct_puts(reply, def_source ? def_source->name : NULL);
pa_tagstruct_putu32(reply, c->protocol->core->cookie);
@@ -3078,14 +3146,14 @@ static void command_set_volume(
if (idx != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
else
- sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
break;
case PA_COMMAND_SET_SOURCE_VOLUME:
if (idx != PA_INVALID_INDEX)
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
else
- source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
break;
case PA_COMMAND_SET_SINK_INPUT_VOLUME:
@@ -3099,11 +3167,11 @@ static void command_set_volume(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_volume(sink, &volume);
+ pa_sink_set_volume(sink, &volume, TRUE, TRUE);
else if (source)
pa_source_set_volume(source, &volume);
else if (si)
- pa_sink_input_set_volume(si, &volume);
+ pa_sink_input_set_volume(si, &volume, TRUE);
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -3148,7 +3216,7 @@ static void command_set_mute(
if (idx != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
else
- sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
break;
@@ -3156,7 +3224,7 @@ static void command_set_mute(
if (idx != PA_INVALID_INDEX)
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
else
- source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
break;
@@ -3175,7 +3243,7 @@ static void command_set_mute(
else if (source)
pa_source_set_mute(source, mute);
else if (si)
- pa_sink_input_set_mute(si, mute);
+ pa_sink_input_set_mute(si, mute, TRUE);
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -3604,7 +3672,23 @@ static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t comman
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID);
- pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK);
+ if (command == PA_COMMAND_SET_DEFAULT_SOURCE) {
+ pa_source *source;
+
+ source = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SOURCE);
+ CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ pa_namereg_set_default_source(c->protocol->core, source);
+ } else {
+ pa_sink *sink;
+ pa_assert(command == PA_COMMAND_SET_DEFAULT_SINK);
+
+ sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK);
+ CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ pa_namereg_set_default_sink(c->protocol->core, sink);
+ }
+
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -3748,143 +3832,6 @@ static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t t
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
- const char *name, *module, *argument;
- uint32_t type;
- uint32_t idx;
- pa_tagstruct *reply;
-
- pa_native_connection_assert_ref(c);
- pa_assert(t);
-
- if (pa_tagstruct_gets(t, &name) < 0 ||
- pa_tagstruct_getu32(t, &type) < 0 ||
- pa_tagstruct_gets(t, &module) < 0 ||
- pa_tagstruct_gets(t, &argument) < 0 ||
- !pa_tagstruct_eof(t)) {
- protocol_error(c);
- return;
- }
-
- CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, type == 0 || type == 1, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, module && *module && pa_utf8_valid(module), tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID);
-
- if (pa_autoload_add(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, module, argument, &idx) < 0) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
- return;
- }
-
- reply = reply_new(tag);
- pa_tagstruct_putu32(reply, idx);
- pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
- const char *name = NULL;
- uint32_t type, idx = PA_IDXSET_INVALID;
- int r;
-
- pa_native_connection_assert_ref(c);
- pa_assert(t);
-
- if ((pa_tagstruct_getu32(t, &idx) < 0 &&
- (pa_tagstruct_gets(t, &name) < 0 ||
- pa_tagstruct_getu32(t, &type) < 0)) ||
- !pa_tagstruct_eof(t)) {
- protocol_error(c);
- return;
- }
-
- CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name || idx != PA_IDXSET_INVALID, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, !name || (*name && pa_utf8_valid(name) && (type == 0 || type == 1)), tag, PA_ERR_INVALID);
-
- if (name)
- r = pa_autoload_remove_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
- else
- r = pa_autoload_remove_by_index(c->protocol->core, idx);
-
- CHECK_VALIDITY(c->pstream, r >= 0, tag, PA_ERR_NOENTITY);
-
- pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) {
- pa_assert(t && e);
-
- pa_tagstruct_putu32(t, e->index);
- pa_tagstruct_puts(t, e->name);
- pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0U : 1U);
- pa_tagstruct_puts(t, e->module);
- pa_tagstruct_puts(t, e->argument);
-}
-
-static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
- const pa_autoload_entry *a = NULL;
- uint32_t type, idx;
- const char *name;
- pa_tagstruct *reply;
-
- pa_native_connection_assert_ref(c);
- pa_assert(t);
-
- if ((pa_tagstruct_getu32(t, &idx) < 0 &&
- (pa_tagstruct_gets(t, &name) < 0 ||
- pa_tagstruct_getu32(t, &type) < 0)) ||
- !pa_tagstruct_eof(t)) {
- protocol_error(c);
- return;
- }
-
- CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name || idx != PA_IDXSET_INVALID, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, !name || (*name && (type == 0 || type == 1) && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
-
- if (name)
- a = pa_autoload_get_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
- else
- a = pa_autoload_get_by_index(c->protocol->core, idx);
-
- CHECK_VALIDITY(c->pstream, a, tag, PA_ERR_NOENTITY);
-
- reply = reply_new(tag);
- autoload_fill_tagstruct(reply, a);
- pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
- pa_tagstruct *reply;
-
- pa_native_connection_assert_ref(c);
- pa_assert(t);
-
- if (!pa_tagstruct_eof(t)) {
- protocol_error(c);
- return;
- }
-
- CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
-
- reply = reply_new(tag);
-
- if (c->protocol->core->autoload_hashmap) {
- pa_autoload_entry *a;
- void *state = NULL;
-
- while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL)))
- autoload_fill_tagstruct(reply, a);
- }
-
- pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
@@ -3918,11 +3865,11 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
if (idx_device != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device);
else
- sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK, 1);
+ sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK);
CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
- if (pa_sink_input_move_to(si, sink) < 0) {
+ if (pa_sink_input_move_to(si, sink, TRUE) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -3937,11 +3884,11 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
if (idx_device != PA_INVALID_INDEX)
source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device);
else
- source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE, 1);
+ source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE);
CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
- if (pa_source_output_move_to(so, source) < 0) {
+ if (pa_source_output_move_to(so, source, TRUE) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -3989,7 +3936,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
if (idx != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
else
- sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
@@ -4017,7 +3964,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
if (idx != PA_INVALID_INDEX)
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
else
- source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
@@ -4064,13 +4011,50 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
- cb = (pa_native_protocol_ext_cb_t) pa_hashmap_get(c->protocol->extensions, m);
+ cb = (pa_native_protocol_ext_cb_t) (unsigned long) pa_hashmap_get(c->protocol->extensions, m);
CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
if (cb(c->protocol, m, c, tag, t) < 0)
protocol_error(c);
}
+static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ uint32_t idx = PA_INVALID_INDEX;
+ const char *name = NULL, *profile = NULL;
+ pa_card *card = NULL;
+
+ pa_native_connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_gets(t, &profile) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, 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);
+
+ if (idx != PA_INVALID_INDEX)
+ card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
+ else
+ card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
+
+ CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
+
+ if (pa_card_set_profile(card, profile) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ return;
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
/*** pstream callbacks ***/
@@ -4214,7 +4198,9 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev
void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *o) {
pa_native_connection *c;
- char cname[256], pname[128];
+ char pname[128];
+ pa_client *client;
+ pa_client_new_data data;
pa_assert(p);
pa_assert(io);
@@ -4226,6 +4212,18 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
return;
}
+ pa_client_new_data_init(&data);
+ data.module = o->module;
+ data.driver = __FILE__;
+ pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+ pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Native client (%s)", pname);
+ pa_proplist_sets(data.proplist, "native-protocol.peer", pname);
+ client = pa_client_new(p->core, &data);
+ pa_client_new_data_done(&data);
+
+ if (!client)
+ return;
+
c = pa_msgobject_new(pa_native_connection);
c->parent.parent.free = native_connection_free;
c->parent.process_msg = native_connection_process_msg;
@@ -4257,13 +4255,9 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
c->is_local = pa_iochannel_socket_is_local(io);
c->version = 8;
- pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
- pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname);
- c->client = pa_client_new(p->core, __FILE__, cname);
- pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname);
+ c->client = client;
c->client->kill = client_kill_cb;
c->client->userdata = c;
- c->client->module = o->module;
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
@@ -4412,7 +4406,7 @@ int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_nativ
pa_assert(cb);
pa_assert(!pa_hashmap_get(p->extensions, m));
- pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) cb) == 0);
+ pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) (unsigned long) cb) == 0);
return 0;
}
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 743bf2ee..dd8002a3 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -323,7 +323,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
pa_log_debug("Requesting rewind due to end of underrun.");
- pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE, FALSE);
}
/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
@@ -476,7 +476,8 @@ static void io_callback(pa_iochannel*io, void *userdata) {
void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simple_options *o) {
connection *c = NULL;
- char cname[256], pname[128];
+ char pname[128];
+ pa_client_new_data client_data;
pa_assert(p);
pa_assert(io);
@@ -505,11 +506,18 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
c->playback.underrun = TRUE;
pa_atomic_store(&c->playback.missing, 0);
+ pa_client_new_data_init(&client_data);
+ client_data.module = o->module;
+ client_data.driver = __FILE__;
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
- pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname);
- pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname));
- pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname);
- c->client->module = o->module;
+ pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "Simple client (%s)", pname);
+ pa_proplist_sets(client_data.proplist, "simple-protocol.peer", pname);
+ c->client = pa_client_new(p->core, &client_data);
+ pa_client_new_data_done(&client_data);
+
+ if (!c->client)
+ goto fail;
+
c->client->kill = client_kill_cb;
c->client->userdata = c;
@@ -518,7 +526,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
size_t l;
pa_sink *sink;
- if (!(sink = pa_namereg_get(c->protocol->core, o->default_sink, PA_NAMEREG_SINK, TRUE))) {
+ if (!(sink = pa_namereg_get(c->protocol->core, o->default_sink, PA_NAMEREG_SINK))) {
pa_log("Failed to get sink.");
goto fail;
}
@@ -570,7 +578,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
size_t l;
pa_source *source;
- if (!(source = pa_namereg_get(c->protocol->core, o->default_source, PA_NAMEREG_SOURCE, TRUE))) {
+ if (!(source = pa_namereg_get(c->protocol->core, o->default_source, PA_NAMEREG_SOURCE))) {
pa_log("Failed to get source.");
goto fail;
}
diff --git a/src/pulsecore/ratelimit.c b/src/pulsecore/ratelimit.c
new file mode 100644
index 00000000..8ce78579
--- /dev/null
+++ b/src/pulsecore/ratelimit.c
@@ -0,0 +1,75 @@
+/***
+ 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 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General 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/rtclock.h>
+#include <pulsecore/log.h>
+#include <pulsecore/mutex.h>
+
+#include "ratelimit.h"
+
+static pa_static_mutex mutex;
+
+/* Modelled after Linux' lib/ratelimit.c by Dave Young
+ * <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
+
+pa_bool_t pa_ratelimit_test(pa_ratelimit *r) {
+ pa_usec_t now;
+ pa_mutex *m;
+
+ now = pa_rtclock_usec();
+
+ m = pa_static_mutex_get(&mutex, FALSE, FALSE);
+ pa_mutex_lock(m);
+
+ pa_assert(r);
+ pa_assert(r->interval > 0);
+ pa_assert(r->burst > 0);
+
+ if (r->begin <= 0 ||
+ r->begin + r->interval < now) {
+
+ if (r->n_missed > 0)
+ pa_log_warn("%u events suppressed", r->n_missed);
+
+ r->begin = now;
+
+ /* Reset counters */
+ r->n_printed = 0;
+ r->n_missed = 0;
+ goto good;
+ }
+
+ if (r->n_printed <= r->burst)
+ goto good;
+
+ r->n_missed++;
+ pa_mutex_unlock(m);
+ return FALSE;
+
+good:
+ r->n_printed++;
+ pa_mutex_unlock(m);
+ return TRUE;
+}
diff --git a/src/pulsecore/ratelimit.h b/src/pulsecore/ratelimit.h
new file mode 100644
index 00000000..e652c520
--- /dev/null
+++ b/src/pulsecore/ratelimit.h
@@ -0,0 +1,46 @@
+#ifndef foopulsecoreratelimithfoo
+#define foopulsecoreratelimithfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_ratelimit {
+ const pa_usec_t interval;
+ const unsigned burst;
+ unsigned n_printed, n_missed;
+ pa_usec_t begin;
+} pa_ratelimit;
+
+#define PA_DEFINE_RATELIMIT(_name, _interval, _burst) \
+ pa_ratelimit _name = { \
+ .interval = _interval, \
+ .burst = _burst, \
+ .n_printed = 0, \
+ .n_missed = 0, \
+ .begin = 0 \
+ }
+
+pa_bool_t pa_ratelimit_test(pa_ratelimit *r);
+
+#endif
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index f0515ebe..be390db7 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -25,7 +25,7 @@
#include <string.h>
-#if HAVE_LIBSAMPLERATE
+#ifdef HAVE_LIBSAMPLERATE
#include <samplerate.h>
#endif
@@ -257,8 +257,12 @@ pa_resampler* pa_resampler_new(
if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE ||
a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
+ a->format == PA_SAMPLE_S24NE || a->format == PA_SAMPLE_S24RE ||
+ a->format == PA_SAMPLE_S24_32NE || a->format == PA_SAMPLE_S24_32RE ||
b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE ||
- b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE)
+ b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE ||
+ b->format == PA_SAMPLE_S24NE || b->format == PA_SAMPLE_S24RE ||
+ b->format == PA_SAMPLE_S24_32NE || b->format == PA_SAMPLE_S24_32RE)
r->work_format = PA_SAMPLE_FLOAT32NE;
else
r->work_format = PA_SAMPLE_S16NE;
@@ -399,6 +403,30 @@ pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
return r->method;
}
+const pa_channel_map* pa_resampler_input_channel_map(pa_resampler *r) {
+ pa_assert(r);
+
+ return &r->i_cm;
+}
+
+const pa_sample_spec* pa_resampler_input_sample_spec(pa_resampler *r) {
+ pa_assert(r);
+
+ return &r->i_ss;
+}
+
+const pa_channel_map* pa_resampler_output_channel_map(pa_resampler *r) {
+ pa_assert(r);
+
+ return &r->o_cm;
+}
+
+const pa_sample_spec* pa_resampler_output_sample_spec(pa_resampler *r) {
+ pa_assert(r);
+
+ return &r->o_ss;
+}
+
static const char * const resample_methods[] = {
"src-sinc-best-quality",
"src-sinc-medium-quality",
diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h
index 87110cc2..54dfa559 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -99,5 +99,9 @@ const char *pa_resample_method_to_string(pa_resample_method_t m);
/* Return 1 when the specified resampling method is supported */
int pa_resample_method_supported(pa_resample_method_t m);
+const pa_channel_map* pa_resampler_input_channel_map(pa_resampler *r);
+const pa_sample_spec* pa_resampler_input_sample_spec(pa_resampler *r);
+const pa_channel_map* pa_resampler_output_channel_map(pa_resampler *r);
+const pa_sample_spec* pa_resampler_output_sample_spec(pa_resampler *r);
#endif
diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c
index f33de830..5fc6da2b 100644
--- a/src/pulsecore/rtclock.c
+++ b/src/pulsecore/rtclock.c
@@ -27,9 +27,12 @@
#include <stddef.h>
#include <time.h>
#include <sys/time.h>
+#include <sys/prctl.h>
+#include <errno.h>
#include <pulse/timeval.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
#include "rtclock.h"
@@ -89,6 +92,29 @@ pa_bool_t pa_rtclock_hrtimer(void) {
#endif
}
+void pa_rtclock_hrtimer_enable(void) {
+#ifdef PR_SET_TIMERSLACK
+ int slack_ns;
+
+ if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) {
+ pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported.");
+ return;
+ }
+
+ pa_log_debug("Timer slack set to %i us.", slack_ns/1000);
+
+ slack_ns = 500000000;
+
+ pa_log_debug("Setting timer slack to %i us.", slack_ns/1000);
+
+ if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
+ pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
+ return;
+ }
+
+#endif
+}
+
pa_usec_t pa_rtclock_usec(void) {
struct timeval tv;
diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h
index aa2cdace..281461df 100644
--- a/src/pulsecore/rtclock.h
+++ b/src/pulsecore/rtclock.h
@@ -35,6 +35,7 @@ pa_usec_t pa_rtclock_usec(void);
pa_usec_t pa_rtclock_age(const struct timeval *tv);
pa_bool_t pa_rtclock_hrtimer(void);
+void pa_rtclock_hrtimer_enable(void);
/* timer with a resolution better than this are considered high-resolution */
#define PA_HRTIMER_THRESHOLD_USEC 10
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 3be08efd..905ba5df 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -83,6 +83,10 @@ static uint8_t silence_byte(pa_sample_format_t format) {
case PA_SAMPLE_S32BE:
case PA_SAMPLE_FLOAT32LE:
case PA_SAMPLE_FLOAT32BE:
+ case PA_SAMPLE_S24LE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32BE:
return 0;
case PA_SAMPLE_ALAW:
return 0xd5;
@@ -209,13 +213,22 @@ size_t pa_mix(
for (i = 0; i < nstreams; i++) {
pa_mix_info *m = streams + i;
- int32_t v, cv = m->linear[channel].i;
+ int32_t v, lo, hi, cv = m->linear[channel].i;
if (PA_UNLIKELY(cv <= 0))
continue;
+ /* 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 = cv >> 16;
+ lo = cv & 0xFFFF;
+
v = *((int16_t*) m->ptr);
- v = (v * cv) / 0x10000;
+ v = ((v * lo) >> 16) + (v * hi);
sum += v;
m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
@@ -244,13 +257,16 @@ size_t pa_mix(
for (i = 0; i < nstreams; i++) {
pa_mix_info *m = streams + i;
- int32_t v, cv = m->linear[channel].i;
+ int32_t v, lo, hi, cv = m->linear[channel].i;
if (PA_UNLIKELY(cv <= 0))
continue;
+ hi = cv >> 16;
+ lo = cv & 0xFFFF;
+
v = PA_INT16_SWAP(*((int16_t*) m->ptr));
- v = (v * cv) / 0x10000;
+ v = ((v * lo) >> 16) + (v * hi);
sum += v;
m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
@@ -286,7 +302,7 @@ size_t pa_mix(
continue;
v = *((int32_t*) m->ptr);
- v = (v * cv) / 0x10000;
+ v = (v * cv) >> 16;
sum += v;
m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
@@ -322,7 +338,7 @@ size_t pa_mix(
continue;
v = PA_INT32_SWAP(*((int32_t*) m->ptr));
- v = (v * cv) / 0x10000;
+ v = (v * cv) >> 16;
sum += v;
m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
@@ -340,6 +356,150 @@ size_t pa_mix(
break;
}
+ case PA_SAMPLE_S24NE: {
+ unsigned channel = 0;
+
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
+
+ while (data < end) {
+ int64_t sum = 0;
+ unsigned i;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t cv = m->linear[channel].i;
+ int64_t v;
+
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+
+ v = (int32_t) (PA_READ24NE(m->ptr) << 8);
+ v = (v * cv) >> 16;
+ sum += v;
+
+ m->ptr = (uint8_t*) m->ptr + 3;
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ PA_WRITE24NE(data, ((uint32_t) sum) >> 8);
+
+ data = (uint8_t*) data + 3;
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S24RE: {
+ unsigned channel = 0;
+
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
+
+ while (data < end) {
+ int64_t sum = 0;
+ unsigned i;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t cv = m->linear[channel].i;
+ int64_t v;
+
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+
+ v = (int32_t) (PA_READ24RE(m->ptr) << 8);
+ v = (v * cv) >> 16;
+ sum += v;
+
+ m->ptr = (uint8_t*) m->ptr + 3;
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ PA_WRITE24RE(data, ((uint32_t) sum) >> 8);
+
+ data = (uint8_t*) data + 3;
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S24_32NE: {
+ unsigned channel = 0;
+
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
+
+ while (data < end) {
+ int64_t sum = 0;
+ unsigned i;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t cv = m->linear[channel].i;
+ int64_t v;
+
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+
+ v = (int32_t) (*((uint32_t*)m->ptr) << 8);
+ v = (v * cv) >> 16;
+ sum += v;
+
+ m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ *((uint32_t*) data) = ((uint32_t) (int32_t) sum) >> 8;
+
+ data = (uint8_t*) data + sizeof(uint32_t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S24_32RE: {
+ unsigned channel = 0;
+
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
+
+ while (data < end) {
+ int64_t sum = 0;
+ unsigned i;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t cv = m->linear[channel].i;
+ int64_t v;
+
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+
+ v = (int32_t) (PA_UINT32_SWAP(*((uint32_t*) m->ptr)) << 8);
+ v = (v * cv) >> 16;
+ sum += v;
+
+ m->ptr = (uint8_t*) m->ptr + 3;
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ *((uint32_t*) data) = PA_INT32_SWAP(((uint32_t) (int32_t) sum) >> 8);
+
+ data = (uint8_t*) data + sizeof(uint32_t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
case PA_SAMPLE_U8: {
unsigned channel = 0;
@@ -357,7 +517,7 @@ size_t pa_mix(
continue;
v = (int32_t) *((uint8_t*) m->ptr) - 0x80;
- v = (v * cv) / 0x10000;
+ v = (v * cv) >> 16;
sum += v;
m->ptr = (uint8_t*) m->ptr + 1;
@@ -386,13 +546,16 @@ size_t pa_mix(
for (i = 0; i < nstreams; i++) {
pa_mix_info *m = streams + i;
- int32_t v, cv = m->linear[channel].i;
+ int32_t v, hi, lo, cv = m->linear[channel].i;
if (PA_UNLIKELY(cv <= 0))
continue;
+ hi = cv >> 16;
+ lo = cv & 0xFFFF;
+
v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr));
- v = (v * cv) / 0x10000;
+ v = ((v * lo) >> 16) + (v * hi);
sum += v;
m->ptr = (uint8_t*) m->ptr + 1;
@@ -421,13 +584,16 @@ size_t pa_mix(
for (i = 0; i < nstreams; i++) {
pa_mix_info *m = streams + i;
- int32_t v, cv = m->linear[channel].i;
+ int32_t v, hi, lo, cv = m->linear[channel].i;
if (PA_UNLIKELY(cv <= 0))
continue;
+ hi = cv >> 16;
+ lo = cv & 0xFFFF;
+
v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr));
- v = (v * cv) / 0x10000;
+ v = ((v * lo) >> 16) + (v * hi);
sum += v;
m->ptr = (uint8_t*) m->ptr + 1;
@@ -562,16 +728,26 @@ void pa_volume_memchunk(
e = (int16_t*) ptr + c->length/sizeof(int16_t);
for (channel = 0, d = ptr; d < e; d++) {
- int32_t t;
+ 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 * linear[channel]) / 0x10000;
+ 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;
}
@@ -585,10 +761,13 @@ void pa_volume_memchunk(
e = (int16_t*) ptr + c->length/sizeof(int16_t);
for (channel = 0, d = ptr; d < e; d++) {
- int32_t t;
+ int32_t t, hi, lo;
+
+ hi = linear[channel] >> 16;
+ lo = linear[channel] & 0xFFFF;
t = (int32_t) PA_INT16_SWAP(*d);
- t = (t * linear[channel]) / 0x10000;
+ t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = PA_INT16_SWAP((int16_t) t);
@@ -612,7 +791,7 @@ void pa_volume_memchunk(
int64_t t;
t = (int64_t)(*d);
- t = (t * linear[channel]) / 0x10000;
+ t = (t * linear[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*d = (int32_t) t;
@@ -635,14 +814,105 @@ void pa_volume_memchunk(
int64_t t;
t = (int64_t) PA_INT32_SWAP(*d);
- t = (t * linear[channel]) / 0x10000;
+ 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/3;
+
+ for (channel = 0, d = ptr; d < e; d++) {
+ 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/3;
+
+ for (channel = 0, d = ptr; d < e; d++) {
+ 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;
}
@@ -656,10 +926,13 @@ void pa_volume_memchunk(
e = (uint8_t*) ptr + c->length;
for (channel = 0, d = ptr; d < e; d++) {
- int32_t t;
+ int32_t t, hi, lo;
+
+ hi = linear[channel] >> 16;
+ lo = linear[channel] & 0xFFFF;
t = (int32_t) *d - 0x80;
- t = (t * linear[channel]) / 0x10000;
+ t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
*d = (uint8_t) (t + 0x80);
@@ -679,10 +952,13 @@ void pa_volume_memchunk(
e = (uint8_t*) ptr + c->length;
for (channel = 0, d = ptr; d < e; d++) {
- int32_t t;
+ int32_t t, hi, lo;
+
+ hi = linear[channel] >> 16;
+ lo = linear[channel] & 0xFFFF;
t = (int32_t) st_ulaw2linear16(*d);
- t = (t * linear[channel]) / 0x10000;
+ t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
@@ -702,10 +978,13 @@ void pa_volume_memchunk(
e = (uint8_t*) ptr + c->length;
for (channel = 0, d = ptr; d < e; d++) {
- int32_t t;
+ int32_t t, hi, lo;
+
+ hi = linear[channel] >> 16;
+ lo = linear[channel] & 0xFFFF;
t = (int32_t) st_alaw2linear16(*d);
- t = (t * linear[channel]) / 0x10000;
+ t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
@@ -900,12 +1179,16 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool,
case PA_SAMPLE_S16BE:
case PA_SAMPLE_S32LE:
case PA_SAMPLE_S32BE:
+ case PA_SAMPLE_S24LE:
+ case PA_SAMPLE_S24BE:
case PA_SAMPLE_FLOAT32LE:
case PA_SAMPLE_FLOAT32BE:
cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S24LE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S24BE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
break;
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
index e7e1449a..0d5146aa 100644
--- a/src/pulsecore/sconv-s16be.c
+++ b/src/pulsecore/sconv-s16be.c
@@ -27,28 +27,52 @@
#define INT16_FROM PA_INT16_FROM_BE
#define INT16_TO PA_INT16_TO_BE
+#define UINT16_FROM PA_UINT16_FROM_BE
+#define UINT16_TO PA_UINT16_TO_BE
#define INT32_FROM PA_INT32_FROM_BE
#define INT32_TO PA_INT32_TO_BE
+#define UINT32_FROM PA_UINT32_FROM_BE
+#define UINT32_TO PA_UINT32_TO_BE
+
+#define READ24 PA_READ24BE
+#define WRITE24 PA_WRITE24BE
#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne
#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne
-
#define pa_sconv_s16le_to_float32re pa_sconv_s16be_to_float32re
#define pa_sconv_s16le_from_float32re pa_sconv_s16be_from_float32re
#define pa_sconv_s32le_to_float32ne pa_sconv_s32be_to_float32ne
#define pa_sconv_s32le_from_float32ne pa_sconv_s32be_from_float32ne
-
#define pa_sconv_s32le_to_float32re pa_sconv_s32be_to_float32re
#define pa_sconv_s32le_from_float32re pa_sconv_s32be_from_float32re
+#define pa_sconv_s24le_to_float32ne pa_sconv_s24be_to_float32ne
+#define pa_sconv_s24le_from_float32ne pa_sconv_s24be_from_float32ne
+#define pa_sconv_s24le_to_float32re pa_sconv_s24be_to_float32re
+#define pa_sconv_s24le_from_float32re pa_sconv_s24be_from_float32re
+
+#define pa_sconv_s24_32le_to_float32ne pa_sconv_s24_32be_to_float32ne
+#define pa_sconv_s24_32le_from_float32ne pa_sconv_s24_32be_from_float32ne
+#define pa_sconv_s24_32le_to_float32re pa_sconv_s24_32be_to_float32re
+#define pa_sconv_s24_32le_from_float32re pa_sconv_s24_32be_from_float32re
+
#define pa_sconv_s32le_to_s16ne pa_sconv_s32be_to_s16ne
#define pa_sconv_s32le_from_s16ne pa_sconv_s32be_from_s16ne
-
#define pa_sconv_s32le_to_s16re pa_sconv_s32be_to_s16re
#define pa_sconv_s32le_from_s16re pa_sconv_s32be_from_s16re
+#define pa_sconv_s24le_to_s16ne pa_sconv_s24be_to_s16ne
+#define pa_sconv_s24le_from_s16ne pa_sconv_s24be_from_s16ne
+#define pa_sconv_s24le_to_s16re pa_sconv_s24be_to_s16re
+#define pa_sconv_s24le_from_s16re pa_sconv_s24be_from_s16re
+
+#define pa_sconv_s24_32le_to_s16ne pa_sconv_s24_32be_to_s16ne
+#define pa_sconv_s24_32le_from_s16ne pa_sconv_s24_32be_from_s16ne
+#define pa_sconv_s24_32le_to_s16re pa_sconv_s24_32be_to_s16re
+#define pa_sconv_s24_32le_from_s16re pa_sconv_s24_32be_from_s16re
+
#ifdef WORDS_BIGENDIAN
#define SWAP_WORDS 0
#else
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
index 60580815..02633333 100644
--- a/src/pulsecore/sconv-s16be.h
+++ b/src/pulsecore/sconv-s16be.h
@@ -34,26 +34,36 @@ void pa_sconv_s32be_from_float32ne(unsigned n, const float *a, int32_t *b);
void pa_sconv_s32be_to_float32re(unsigned n, const int32_t *a, float *b);
void pa_sconv_s32be_from_float32re(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s24be_to_float32ne(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24be_from_float32ne(unsigned n, const float *a, uint8_t *b);
+void pa_sconv_s24be_to_float32re(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24be_from_float32re(unsigned n, const float *a, uint8_t *b);
+
+void pa_sconv_s24_32be_to_float32ne(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24_32be_from_float32ne(unsigned n, const float *a, uint8_t *b);
+void pa_sconv_s24_32be_to_float32re(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24_32be_from_float32re(unsigned n, const float *a, uint8_t *b);
+
void pa_sconv_s32be_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
void pa_sconv_s32be_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
void pa_sconv_s32be_to_s16re(unsigned n, const int32_t *a, int16_t *b);
void pa_sconv_s32be_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s24be_to_s16ne(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24be_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
+void pa_sconv_s24be_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24be_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
+
+void pa_sconv_s24_32be_to_s16ne(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24_32be_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
+void pa_sconv_s24_32be_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24_32be_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
+
#ifdef WORDS_BIGENDIAN
#define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne
#define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne
#define pa_sconv_float32le_to_s16ne pa_sconv_s16be_from_float32re
#define pa_sconv_float32le_from_s16ne pa_sconv_s16be_to_float32re
-
-#define pa_sconv_float32be_to_s32ne pa_sconv_s32be_from_float32ne
-#define pa_sconv_float32be_from_s32ne pa_sconv_s32be_to_float32ne
-#define pa_sconv_float32le_to_s32ne pa_sconv_s32be_from_float32re
-#define pa_sconv_float32le_from_s32ne pa_sconv_s32be_to_float32re
-
-#define pa_sconv_s16be_to_s32ne pa_sconv_s32be_from_s16ne
-#define pa_sconv_s16be_from_s32ne pa_sconv_s32be_to_s16ne
-#define pa_sconv_s16le_to_s32ne pa_sconv_s32be_from_s16re
-#define pa_sconv_s16le_from_s32ne pa_sconv_s32be_to_s16re
#endif
#endif
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 159c4655..79f0391c 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -23,7 +23,7 @@
#include <config.h>
#endif
-/* Despite the name of this file we implement S32 handling here, too. */
+/* Despite the name of this file we implement S32 and S24 handling here, too. */
#include <inttypes.h>
#include <stdio.h>
@@ -41,18 +41,37 @@
#ifndef INT16_FROM
#define INT16_FROM PA_INT16_FROM_LE
#endif
+#ifndef UINT16_FROM
+#define UINT16_FROM PA_UINT16_FROM_LE
+#endif
#ifndef INT16_TO
#define INT16_TO PA_INT16_TO_LE
#endif
+#ifndef UINT16_TO
+#define UINT16_TO PA_UINT16_TO_LE
+#endif
#ifndef INT32_FROM
#define INT32_FROM PA_INT32_FROM_LE
#endif
+#ifndef UINT32_FROM
+#define UINT32_FROM PA_UINT32_FROM_LE
+#endif
#ifndef INT32_TO
#define INT32_TO PA_INT32_TO_LE
#endif
+#ifndef UINT32_TO
+#define UINT32_TO PA_UINT32_TO_LE
+#endif
+
+#ifndef READ24
+#define READ24 PA_READ24LE
+#endif
+#ifndef WRITE24
+#define WRITE24 PA_WRITE24LE
+#endif
#ifndef SWAP_WORDS
#ifdef WORDS_BIGENDIAN
@@ -243,3 +262,207 @@ void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) {
b++;
}
}
+
+void pa_sconv_s24le_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ *b = (int16_t) (READ24(a) >> 8);
+ a += 3;
+ b++;
+ }
+}
+
+void pa_sconv_s24le_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ WRITE24(b, ((uint32_t) *a) << 8);
+ a++;
+ b += 3;
+ }
+}
+
+void pa_sconv_s24le_to_s16re(unsigned n, const uint8_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int16_t s = (int16_t) (READ24(a) >> 8);
+ *b = PA_INT16_SWAP(s);
+ a += 3;
+ b++;
+ }
+}
+
+void pa_sconv_s24le_from_s16re(unsigned n, const int16_t *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ uint32_t s = ((uint32_t) PA_INT16_SWAP(*a)) << 8;
+ WRITE24(b, s);
+ a++;
+ b += 3;
+ }
+}
+
+void pa_sconv_s24le_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = READ24(a) << 8;
+ *b = ((float) s) / 0x7FFFFFFF;
+ a += 3;
+ b ++;
+ }
+}
+
+void pa_sconv_s24le_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *a;
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+ s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+ WRITE24(b, ((uint32_t) s) >> 8);
+ a++;
+ b+=3;
+ }
+}
+
+void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = READ24(a) << 8;
+ float k = ((float) s) / 0x7FFFFFFF;
+ *b = PA_FLOAT32_SWAP(k);
+ a += 3;
+ b ++;
+ }
+}
+
+void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *a;
+ v = PA_FLOAT32_SWAP(v);
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+ s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+ WRITE24(b, ((uint32_t) s) >> 8);
+ a++;
+ b+=3;
+ }
+}
+
+void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ *b = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8) >> 16);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s24_32le_to_s16re(unsigned n, const uint32_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int16_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8) >> 16);
+ *b = PA_INT16_SWAP(s);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s24_32le_from_s16ne(unsigned n, const int16_t *a, uint32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ *b = UINT32_TO(((uint32_t) ((int32_t) *a << 16)) >> 8);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s24_32le_from_s16re(unsigned n, const int16_t *a, uint32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ uint32_t s = ((uint32_t) ((int32_t) PA_INT16_SWAP(*a) << 16)) >> 8;
+ *b = UINT32_TO(s);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8));
+ *b = ((float) s) / 0x7FFFFFFF;
+ a ++;
+ b ++;
+ }
+}
+
+void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8));
+ float k = ((float) s) / 0x7FFFFFFF;
+ *b = PA_FLOAT32_SWAP(k);
+ a ++;
+ b ++;
+ }
+}
+
+void pa_sconv_s24_32le_from_float32ne(unsigned n, const float *a, uint32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *a;
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+ s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+ *b = UINT32_TO(((uint32_t) s) >> 8);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s24_32le_from_float32re(unsigned n, const float *a, uint32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *a;
+ v = PA_FLOAT32_SWAP(v);
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+ s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+ *b = UINT32_TO(((uint32_t) s) >> 8);
+ a++;
+ b++;
+ }
+}
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
index 8d4c87c3..f7b00645 100644
--- a/src/pulsecore/sconv-s16le.h
+++ b/src/pulsecore/sconv-s16le.h
@@ -34,26 +34,36 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b);
void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b);
void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s24le_to_float32ne(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24le_from_float32ne(unsigned n, const float *a, uint8_t *b);
+void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b);
+
+void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b);
+void pa_sconv_s24_32le_from_float32ne(unsigned n, const float *a, uint32_t *b);
+void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b);
+void pa_sconv_s24_32le_from_float32re(unsigned n, const float *a, uint32_t *b);
+
void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
void pa_sconv_s32le_to_s16re(unsigned n, const int32_t *a, int16_t *b);
void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s24le_to_s16ne(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24le_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
+void pa_sconv_s24le_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24le_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
+
+void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b);
+void pa_sconv_s24_32le_from_s16ne(unsigned n, const int16_t *a, uint32_t *b);
+void pa_sconv_s24_32le_to_s16re(unsigned n, const uint32_t *a, int16_t *b);
+void pa_sconv_s24_32le_from_s16re(unsigned n, const int16_t *a, uint32_t *b);
+
#ifndef WORDS_BIGENDIAN
#define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re
#define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re
#define pa_sconv_float32le_to_s16ne pa_sconv_s16le_from_float32ne
#define pa_sconv_float32le_from_s16ne pa_sconv_s16le_to_float32ne
-
-#define pa_sconv_float32be_to_s32ne pa_sconv_s32le_from_float32re
-#define pa_sconv_float32be_from_s32ne pa_sconv_s32le_to_float32re
-#define pa_sconv_float32le_to_s32ne pa_sconv_s32le_from_float32ne
-#define pa_sconv_float32le_from_s32ne pa_sconv_s32le_to_float32ne
-
-#define pa_sconv_s16be_to_s32ne pa_sconv_s32le_from_s16re
-#define pa_sconv_s16be_from_s32ne pa_sconv_s32le_to_s16re
-#define pa_sconv_s16le_to_s32ne pa_sconv_s32le_from_s16ne
-#define pa_sconv_s16le_from_s32ne pa_sconv_s32le_to_s16ne
#endif
#endif
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index 6c4d420e..fcd0309c 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -198,6 +198,10 @@ pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
[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,
};
@@ -216,6 +220,10 @@ pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
[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,
@@ -238,6 +246,10 @@ pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
[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
};
@@ -258,6 +270,10 @@ pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
[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,
};
diff --git a/src/pulsecore/shared.c b/src/pulsecore/shared.c
index 77d919d3..d6e42dd8 100644
--- a/src/pulsecore/shared.c
+++ b/src/pulsecore/shared.c
@@ -111,7 +111,14 @@ void pa_shared_cleanup(pa_core *c) {
if (!c->shared)
return;
- pa_assert(pa_hashmap_isempty(c->shared));
+ if (!pa_hashmap_isempty(c->shared)) {
+ pa_strbuf *s = pa_strbuf_new();
+
+ pa_shared_dump(c, s);
+ pa_log_debug("%s", pa_strbuf_tostring(s));
+ pa_strbuf_free(s);
+ pa_assert(pa_hashmap_isempty(c->shared));
+ }
pa_hashmap_free(c->shared, NULL, NULL);
c->shared = NULL;
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index d25cd797..d4d11194 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -30,6 +30,7 @@
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/core-subscribe.h>
@@ -71,11 +72,18 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const
data->channel_map = *map;
}
-void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
+void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
pa_assert(data);
- if ((data->volume_is_set = !!volume))
- data->volume = data->virtual_volume = *volume;
+ if ((data->soft_volume_is_set = !!volume))
+ data->soft_volume = *volume;
+}
+
+void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
+ pa_assert(data);
+
+ if ((data->virtual_volume_is_set = !!volume))
+ data->virtual_volume = *volume;
}
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
@@ -130,8 +138,10 @@ pa_sink_input* pa_sink_input_new(
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
- if (!data->sink)
- data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, TRUE);
+ if (!data->sink) {
+ data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
+ data->save_sink = FALSE;
+ }
pa_return_null_if_fail(data->sink);
pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED);
@@ -152,17 +162,36 @@ pa_sink_input* pa_sink_input_new(
pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec));
- if (!data->volume_is_set) {
- pa_cvolume_reset(&data->volume, data->sample_spec.channels);
- pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
- }
+ if (!data->virtual_volume_is_set) {
+
+ if (data->sink->flags & PA_SINK_FLAT_VOLUME) {
+ data->virtual_volume = data->sink->virtual_volume;
+ pa_cvolume_remap(&data->virtual_volume, &data->sink->channel_map, &data->channel_map);
+ } else
+ pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
+
+ data->save_volume = FALSE;
- pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
- pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
+ } else if (!data->virtual_volume_is_absolute) {
+
+ /* When the 'absolute' bool is set then we'll treat the volume
+ * as relative to the sink volume even in flat volume mode */
+ if (data->sink->flags & PA_SINK_FLAT_VOLUME) {
+ pa_cvolume t = data->sink->virtual_volume;
+ pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map);
+ pa_sw_cvolume_multiply(&data->virtual_volume, &data->virtual_volume, &t);
+ }
+ }
pa_return_null_if_fail(pa_cvolume_valid(&data->virtual_volume));
pa_return_null_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec));
+ if (!data->soft_volume_is_set)
+ data->soft_volume = data->virtual_volume;
+
+ pa_return_null_if_fail(pa_cvolume_valid(&data->soft_volume));
+ pa_return_null_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec));
+
if (!data->muted_is_set)
data->muted = FALSE;
@@ -183,13 +212,17 @@ pa_sink_input* pa_sink_input_new(
pa_assert(pa_channel_map_valid(&data->channel_map));
/* Due to the fixing of the sample spec the volume might not match anymore */
- pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);
+ pa_cvolume_remap(&data->soft_volume, &original_cm, &data->channel_map);
+ pa_cvolume_remap(&data->virtual_volume, &original_cm, &data->channel_map);
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+ if (data->client)
+ pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
+
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
return NULL;
@@ -214,8 +247,6 @@ pa_sink_input* pa_sink_input_new(
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
-
- data->resample_method = pa_resampler_get_method(resampler);
}
i = pa_msgobject_new(pa_sink_input);
@@ -226,17 +257,21 @@ pa_sink_input* pa_sink_input_new(
i->state = PA_SINK_INPUT_INIT;
i->flags = flags;
i->proplist = pa_proplist_copy(data->proplist);
- i->driver = pa_xstrdup(data->driver);
+ i->driver = pa_xstrdup(pa_path_get_filename(data->driver));
i->module = data->module;
i->sink = data->sink;
i->client = data->client;
- i->resample_method = data->resample_method;
+ i->requested_resample_method = data->resample_method;
+ i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
i->sample_spec = data->sample_spec;
i->channel_map = data->channel_map;
i->virtual_volume = data->virtual_volume;
- i->volume = data->volume;
+ i->soft_volume = data->soft_volume;
+ i->save_volume = data->save_volume;
+ i->save_sink = data->save_sink;
+ i->save_muted = data->save_muted;
i->muted = data->muted;
@@ -260,11 +295,12 @@ pa_sink_input* pa_sink_input_new(
pa_atomic_store(&i->thread_info.drained, 1);
i->thread_info.sample_spec = i->sample_spec;
i->thread_info.resampler = resampler;
- i->thread_info.volume = i->volume;
+ i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted;
i->thread_info.requested_sink_latency = (pa_usec_t) -1;
i->thread_info.rewrite_nbytes = 0;
i->thread_info.rewrite_flush = FALSE;
+ i->thread_info.dont_rewind_render = FALSE;
i->thread_info.underrun_for = (uint64_t) -1;
i->thread_info.playing_for = 0;
i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
@@ -282,6 +318,9 @@ pa_sink_input* pa_sink_input_new(
pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0);
pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
+ if (i->client)
+ pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
+
pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s",
i->index,
pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)),
@@ -298,6 +337,9 @@ pa_sink_input* pa_sink_input_new(
static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
pa_assert(i);
+ if (!i->sink)
+ return;
+
if (i->state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED)
pa_assert_se(i->sink->n_corked -- >= 1);
else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED)
@@ -330,13 +372,13 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
}
if (state != PA_SINK_INPUT_UNLINKED) {
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev)
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
for (ssync = i->sync_next; ssync; ssync = ssync->sync_next)
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
}
pa_sink_update_status(i->sink);
@@ -358,7 +400,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
linked = PA_SINK_INPUT_IS_LINKED(i->state);
if (linked)
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
if (i->sync_prev)
i->sync_prev->sync_next = i->sync_next;
@@ -367,9 +409,14 @@ void pa_sink_input_unlink(pa_sink_input *i) {
i->sync_prev = i->sync_next = NULL;
- pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL);
- if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
- pa_sink_input_unref(i);
+ pa_idxset_remove_by_data(i->core->sink_inputs, i, NULL);
+
+ if (i->sink)
+ if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
+ pa_sink_input_unref(i);
+
+ if (i->client)
+ pa_idxset_remove_by_data(i->client->sink_inputs, i, NULL);
while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
pa_assert(o != p);
@@ -380,20 +427,30 @@ void pa_sink_input_unlink(pa_sink_input *i) {
update_n_corked(i, PA_SINK_INPUT_UNLINKED);
i->state = PA_SINK_INPUT_UNLINKED;
- if (linked)
+ 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);
+ }
+
if (i->sink->asyncmsgq)
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
+ }
reset_callbacks(i);
if (linked) {
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
}
- pa_sink_update_status(i->sink);
+ if (i->sink) {
+ pa_sink_update_status(i->sink);
+ i->sink = NULL;
+ }
- i->sink = NULL;
pa_sink_input_unref(i);
}
@@ -442,7 +499,7 @@ void pa_sink_input_put(pa_sink_input *i) {
pa_assert(i->process_rewind);
pa_assert(i->kill);
- i->thread_info.volume = i->volume;
+ i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted;
state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
@@ -450,10 +507,17 @@ void pa_sink_input_put(pa_sink_input *i) {
update_n_corked(i, state);
i->state = state;
+ /* We might need to update the sink's volume if we are in flat volume mode. */
+ if (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);
+ }
+
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i);
pa_sink_update_status(i->sink);
}
@@ -508,9 +572,9 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
block_size_max_sink_input = i->thread_info.resampler ?
pa_resampler_max_block_size(i->thread_info.resampler) :
- pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sample_spec);
+ pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sample_spec);
- block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sink->sample_spec);
+ block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sink->sample_spec);
/* Default buffer size */
if (slength <= 0)
@@ -535,7 +599,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
* it after and leave it for the sink code */
do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
- volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted;
+ volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted;
while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) {
pa_memchunk tchunk;
@@ -581,7 +645,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
if (i->thread_info.muted)
pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
else
- pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume);
+ pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.soft_volume);
}
if (!i->thread_info.resampler)
@@ -627,7 +691,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
/* We've both the same channel map, so let's have the sink do the adjustment for us*/
pa_cvolume_mute(volume, i->sink->sample_spec.channels);
else
- *volume = i->thread_info.volume;
+ *volume = i->thread_info.soft_volume;
return 0;
}
@@ -658,7 +722,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
- if (nbytes > 0) {
+ if (nbytes > 0 && !i->thread_info.dont_rewind_render) {
pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes);
}
@@ -714,6 +778,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
i->thread_info.rewrite_nbytes = 0;
i->thread_info.rewrite_flush = FALSE;
+ i->thread_info.dont_rewind_render = FALSE;
}
/* Called from thread context */
@@ -797,35 +862,43 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
}
/* Called from main context */
-void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
- pa_sink_input_set_volume_data data;
-
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save) {
pa_sink_input_assert_ref(i);
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));
- data.sink_input = i;
- data.virtual_volume = *volume;
- data.volume = *volume;
+ if (pa_cvolume_equal(volume, &i->virtual_volume))
+ return;
- /* If you change something here, consider looking into
- * module-flat-volume.c as well since it uses very similar
- * code. */
+ i->virtual_volume = *volume;
+ i->save_volume = save;
- if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], &data) < 0)
- return;
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
+ pa_cvolume new_volume;
- if (!pa_cvolume_equal(&i->volume, &data.volume)) {
- i->volume = data.volume;
- pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &data.volume, 0, NULL) == 0);
- }
+ /* We are in flat volume mode, so let's update all sink input
+ * volumes and update the flat volume of the sink */
- if (!pa_cvolume_equal(&i->virtual_volume, &data.virtual_volume)) {
- i->virtual_volume = data.virtual_volume;
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ pa_sink_update_flat_volume(i->sink, &new_volume);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE);
+
+ } else {
+
+ /* OK, we are in normal volume mode. The volume only affects
+ * ourselves */
+
+ i->soft_volume = *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);
+
+ 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 */
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
/* Called from main context */
@@ -837,7 +910,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
}
/* Called from main context */
-void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
+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(PA_SINK_INPUT_IS_LINKED(i->state));
@@ -846,9 +919,10 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
return;
i->muted = mute;
+ i->save_muted = save;
- pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
/* Called from main context */
@@ -859,6 +933,21 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
return i->muted;
}
+/* Called from main thread */
+pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
+
+ pa_sink_input_assert_ref(i);
+
+ pa_proplist_update(i->proplist, mode, p);
+
+ if (PA_SINK_IS_LINKED(i->state)) {
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
+
+ return TRUE;
+}
+
/* Called from main context */
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
pa_sink_input_assert_ref(i);
@@ -880,7 +969,7 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
return 0;
}
@@ -903,8 +992,8 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
if (PA_SINK_INPUT_IS_LINKED(i->state)) {
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
@@ -912,17 +1001,13 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
- return i->resample_method;
+ return i->actual_resample_method;
}
/* Called from main context */
-pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
+pa_bool_t pa_sink_input_may_move(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- pa_sink_assert_ref(dest);
-
- if (dest == i->sink)
- return TRUE;
if (i->flags & PA_SINK_INPUT_DONT_MOVE)
return FALSE;
@@ -932,6 +1017,21 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
return FALSE;
}
+ return TRUE;
+}
+
+/* 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(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_sink_assert_ref(dest);
+
+ if (dest == i->sink)
+ return TRUE;
+
+ if (!pa_sink_input_may_move(i))
+ return FALSE;
+
if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) {
pa_log_warn("Failed to move sink input: too many inputs per sink.");
return FALSE;
@@ -945,34 +1045,72 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
}
/* Called from main context */
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
- pa_resampler *new_resampler;
- pa_sink *origin;
- pa_sink_input_move_hook_data hook_data;
+int pa_sink_input_start_move(pa_sink_input *i) {
pa_source_output *o, *p = NULL;
+ pa_sink *origin;
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- pa_sink_assert_ref(dest);
+ pa_assert(i->sink);
- origin = i->sink;
-
- if (dest == origin)
- return 0;
+ if (!pa_sink_input_may_move(i))
+ return -1;
- if (!pa_sink_input_may_move_to(i, dest))
+ if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i) < 0)
return -1;
+ origin = i->sink;
+
/* Kill directly connected outputs */
while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
pa_assert(o != p);
pa_source_output_kill(o);
p = o;
}
+ pa_assert(pa_idxset_isempty(i->direct_outputs));
+
+ pa_idxset_remove_by_data(i->sink->inputs, i, NULL);
+
+ if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
+ pa_assert_se(i->sink->n_corked-- >= 1);
+
+ /* 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);
+ }
+
+ 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);
+ i->sink = NULL;
+
+ return 0;
+}
+
+/* Called from main context */
+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(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(!i->sink);
+ pa_sink_assert_ref(dest);
+
+ if (!pa_sink_input_may_move_to(i, dest))
+ return -1;
+
+ i->sink = dest;
+ i->save_sink = save;
+ pa_idxset_put(dest->inputs, i, NULL);
+
+ if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
+ i->sink->n_corked++;
if (i->thread_info.resampler &&
- pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
- pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
+ pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) &&
+ pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map))
/* Try to reuse the old resampler if possible */
new_resampler = i->thread_info.resampler;
@@ -984,10 +1122,10 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
/* Okey, we need a new resampler for the new sink */
if (!(new_resampler = pa_resampler_new(
- dest->core->mempool,
+ i->core->mempool,
&i->sample_spec, &i->channel_map,
&dest->sample_spec, &dest->channel_map,
- i->resample_method,
+ i->requested_resample_method,
((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
(i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
@@ -997,21 +1135,6 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
} else
new_resampler = NULL;
- hook_data.sink_input = i;
- hook_data.destination = dest;
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data);
-
- pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
-
- pa_idxset_remove_by_data(origin->inputs, i, NULL);
- pa_idxset_put(dest->inputs, i, NULL);
- i->sink = dest;
-
- if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) {
- pa_assert_se(origin->n_corked-- >= 1);
- dest->n_corked++;
- }
-
/* Replace resampler and render queue */
if (new_resampler != i->thread_info.resampler) {
@@ -1032,20 +1155,47 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
&i->sink->silence);
}
- pa_sink_update_status(origin);
pa_sink_update_status(dest);
+ /* 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);
+ }
+
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
+ pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
+
+ /* Notify everyone */
if (i->moved)
i->moved(i);
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name);
+ return 0;
+}
- /* Notify everyone */
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+/* Called from main context */
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(i->sink);
+ pa_sink_assert_ref(dest);
+
+ if (dest == i->sink)
+ return 0;
+
+ if (!pa_sink_input_may_move_to(i, dest))
+ return -1;
+
+ if (pa_sink_input_start_move(i) < 0)
+ return -1;
+
+ if (pa_sink_input_finish_move(i, dest, save) < 0)
+ return -1;
return 0;
}
@@ -1076,7 +1226,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state
/* This will tell the implementing sink input driver to rewind
* so that the unplayed already mixed data is not lost */
- pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
+ pa_sink_input_request_rewind(i, 0, TRUE, TRUE, FALSE);
} else if (uncorking) {
@@ -1087,7 +1237,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state
/* OK, we're being uncorked. Make sure we're not rewound when
* the hw buffer is remixed and request a remix. */
- pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
}
@@ -1098,14 +1248,18 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
switch (code) {
- case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
- i->thread_info.volume = *((pa_cvolume*) userdata);
- pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
+ case PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME:
+ if (!pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) {
+ i->thread_info.soft_volume = i->soft_volume;
+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
+ }
return 0;
- case PA_SINK_INPUT_MESSAGE_SET_MUTE:
- i->thread_info.muted = PA_PTR_TO_UINT(userdata);
- pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
+ case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE:
+ if (i->thread_info.muted != i->muted) {
+ i->thread_info.muted = i->muted;
+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
+ }
return 0;
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
@@ -1180,7 +1334,7 @@ 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) {
+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
@@ -1191,7 +1345,9 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
* If 'rewrite' is FALSE the sink is rewound as far as requested
* and possible and the already rendered data is dropped so that
* in the next iteration we read new data from the
- * implementor. This implies 'flush' is TRUE. */
+ * implementor. This implies 'flush' is TRUE. If
+ * dont_rewind_render is TRUE then the render memblockq is not
+ * rewound. */
pa_sink_input_assert_ref(i);
@@ -1204,6 +1360,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
return;
pa_assert(rewrite || flush);
+ pa_assert(!dont_rewind_render || !rewrite);
/* Calculate how much we can rewind locally without having to
* touch the sink */
@@ -1238,6 +1395,10 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
i->thread_info.rewrite_flush ||
(flush && i->thread_info.rewrite_nbytes != 0);
+ i->thread_info.dont_rewind_render =
+ i->thread_info.dont_rewind_render ||
+ dont_rewind_render;
+
if (nbytes != (size_t) -1) {
/* Transform to sink domain */
@@ -1258,8 +1419,8 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
pa_assert(ret);
pa_silence_memchunk_get(
- &i->sink->core->silence_cache,
- i->sink->core->mempool,
+ &i->core->silence_cache,
+ i->core->mempool,
ret,
&i->sample_spec,
i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0);
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 27125988..893d8690 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -78,7 +78,7 @@ struct pa_sink_input {
pa_module *module; /* may be NULL */
pa_client *client; /* may be NULL */
- pa_sink *sink;
+ pa_sink *sink; /* NULL while we are being moved */
/* A sink input may be connected to multiple source outputs
* directly, so that they don't get mixed data of the entire
@@ -90,12 +90,16 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
- pa_cvolume virtual_volume;
+ pa_cvolume virtual_volume, soft_volume;
+ pa_bool_t muted:1;
- pa_cvolume volume;
- pa_bool_t muted;
+ /* if TRUE then the source we are connected to and/or the volume
+ * set is worth remembering, i.e. was explicitly chosen by the
+ * user and not automatically. module-stream-restore looks for
+ * this.*/
+ pa_bool_t save_sink:1, save_volume:1, save_muted:1;
- pa_resample_method_t resample_method;
+ pa_resample_method_t requested_resample_method, actual_resample_method;
/* Returns the chunk of audio data and drops it from the
* queue. Returns -1 on failure. Called from IO thread context. If
@@ -170,7 +174,15 @@ struct pa_sink_input {
pa_sink_input_state_t state;
pa_atomic_t drained;
- pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+ pa_cvolume soft_volume;
+ pa_bool_t muted:1;
+
+ 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 */
+ pa_bool_t rewrite_flush:1, dont_rewind_render:1;
+ size_t rewrite_nbytes;
+ uint64_t underrun_for, playing_for;
pa_sample_spec sample_spec;
@@ -179,15 +191,8 @@ struct pa_sink_input {
/* We maintain a history of resampled audio data here. */
pa_memblockq *render_memblockq;
- size_t rewrite_nbytes;
- pa_bool_t rewrite_flush;
- uint64_t underrun_for, playing_for;
-
pa_sink_input *sync_prev, *sync_next;
- pa_cvolume volume;
- pa_bool_t muted;
-
/* The requested latency for the sink */
pa_usec_t requested_sink_latency;
@@ -201,8 +206,8 @@ PA_DECLARE_CLASS(pa_sink_input);
#define PA_SINK_INPUT(o) pa_sink_input_cast(o)
enum {
- PA_SINK_INPUT_MESSAGE_SET_VOLUME,
- PA_SINK_INPUT_MESSAGE_SET_MUTE,
+ PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME,
+ PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE,
PA_SINK_INPUT_MESSAGE_GET_LATENCY,
PA_SINK_INPUT_MESSAGE_SET_RATE,
PA_SINK_INPUT_MESSAGE_SET_STATE,
@@ -227,35 +232,28 @@ typedef struct pa_sink_input_new_data {
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_cvolume virtual_volume;
-
- pa_cvolume volume;
+ pa_cvolume virtual_volume, soft_volume;
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;
+
+ pa_bool_t virtual_volume_is_set:1, soft_volume_is_set:1;
pa_bool_t muted_is_set:1;
+
+ pa_bool_t virtual_volume_is_absolute:1;
+
+ pa_bool_t save_sink:1, save_volume:1, save_muted:1;
} pa_sink_input_new_data;
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
-void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
+void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
+void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
-typedef struct pa_sink_input_move_hook_data {
- pa_sink_input *sink_input;
- pa_sink *destination;
-} pa_sink_input_move_hook_data;
-
-typedef struct pa_sink_set_input_volume_data {
- pa_sink_input *sink_input;
- pa_cvolume virtual_volume;
- pa_cvolume volume;
-} pa_sink_input_set_volume_data;
-
/* To be called by the implementing module only */
pa_sink_input* pa_sink_input_new(
@@ -277,7 +275,7 @@ fully -- or at all. If the request for a rewrite was successful, the
sink driver will call ->rewind() and pass the number of bytes that
could be rewound in the HW device. This functionality is required for
implementing the "zero latency" write-through functionality. */
-void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush);
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render);
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
@@ -290,15 +288,23 @@ void pa_sink_input_kill(pa_sink_input*i);
pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
-void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume);
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save);
const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
-void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute);
+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);
+pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest);
-pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest);
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save);
+pa_bool_t pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */
+pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */
+
+/* The same as pa_sink_input_move_to() but in two seperate steps,
+ * first the detaching from the old sink, then the attaching to the
+ * new sink */
+int pa_sink_input_start_move(pa_sink_input *i);
+int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save);
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index a4d993cd..3afeadb0 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -32,6 +32,7 @@
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
+#include <pulse/util.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/namereg.h>
@@ -167,6 +168,9 @@ pa_sink* pa_sink_new(
if (!data->muted_is_set)
data->muted = FALSE;
+ if (data->card)
+ pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
+
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
pa_xfree(s);
pa_namereg_unregister(core, name);
@@ -181,8 +185,9 @@ pa_sink* pa_sink_new(
s->flags = flags;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
- s->driver = pa_xstrdup(data->driver);
+ s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
s->module = data->module;
+ s->card = data->card;
s->sample_spec = data->sample_spec;
s->channel_map = data->channel_map;
@@ -190,8 +195,10 @@ pa_sink* pa_sink_new(
s->inputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
- s->volume = data->volume;
+ s->virtual_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;
@@ -209,8 +216,8 @@ pa_sink* pa_sink_new(
0);
s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
- s->thread_info.soft_muted = FALSE;
+ s->thread_info.soft_volume = s->soft_volume;
+ s->thread_info.soft_muted = s->muted;
s->thread_info.state = s->state;
s->thread_info.rewind_nbytes = 0;
s->thread_info.rewind_requested = FALSE;
@@ -223,6 +230,9 @@ pa_sink* pa_sink_new(
pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
+ if (s->card)
+ pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0);
+
pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s",
s->index,
s->name,
@@ -235,6 +245,7 @@ pa_sink* pa_sink_new(
source_data.name = pa_sprintf_malloc("%s.monitor", name);
source_data.driver = data->driver;
source_data.module = data->module;
+ source_data.card = data->card;
dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
@@ -301,8 +312,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
i->suspend(i, state == PA_SINK_SUSPENDED);
}
- if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
+ if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
return 0;
}
@@ -322,10 +335,17 @@ void pa_sink_put(pa_sink* s) {
if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
s->flags |= PA_SINK_DECIBEL_VOLUME;
- s->thread_info.soft_volume = s->volume;
+ s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
}
+ if (s->flags & PA_SINK_DECIBEL_VOLUME)
+ s->n_volume_steps = PA_VOLUME_NORM+1;
+
+ if (s->core->flat_volumes)
+ if (s->flags & PA_SINK_DECIBEL_VOLUME)
+ s->flags |= PA_SINK_FLAT_VOLUME;
+
pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
pa_source_put(s->monitor_source);
@@ -358,6 +378,9 @@ void pa_sink_unlink(pa_sink* s) {
pa_namereg_unregister(s->core, s->name);
pa_idxset_remove_by_data(s->core->sinks, s, NULL);
+ if (s->card)
+ pa_idxset_remove_by_data(s->card->sinks, s, NULL);
+
while ((i = pa_idxset_first(s->inputs, NULL))) {
pa_assert(i != j);
pa_sink_input_kill(i);
@@ -458,6 +481,58 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
}
+/* Called from main context */
+pa_queue *pa_sink_move_all_start(pa_sink *s) {
+ pa_queue *q;
+ pa_sink_input *i, *n;
+ uint32_t idx;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ q = pa_queue_new();
+
+ for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
+ n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
+
+ if (pa_sink_input_start_move(i) >= 0)
+ pa_queue_push(q, pa_sink_input_ref(i));
+ }
+
+ return q;
+}
+
+/* Called from main context */
+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(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_unlink(i);
+
+ pa_sink_input_unref(i);
+ }
+
+ pa_queue_free(q, NULL, NULL);
+}
+
+/* Called from main context */
+void pa_sink_move_all_fail(pa_queue *q) {
+ pa_sink_input *i;
+ pa_assert(q);
+
+ while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
+ pa_sink_input_unlink(i);
+ pa_sink_input_unref(i);
+ }
+
+ pa_queue_free(q, NULL, NULL);
+}
+
/* Called from IO thread context */
void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
pa_sink_input *i;
@@ -642,7 +717,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_assert(length > 0);
- n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
+ n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
if (n == 0) {
@@ -685,8 +760,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
result->index = 0;
}
- if (s->thread_info.state == PA_SINK_RUNNING)
- inputs_drop(s, info, n, result);
+ inputs_drop(s, info, n, result);
pa_sink_unref(s);
}
@@ -716,7 +790,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_assert(length > 0);
- n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
+ n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
if (n == 0) {
if (target->length > length)
@@ -765,8 +839,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_memblock_release(target->memblock);
}
- if (s->thread_info.state == PA_SINK_RUNNING)
- inputs_drop(s, info, n, target);
+ inputs_drop(s, info, n, target);
pa_sink_unref(s);
}
@@ -845,9 +918,100 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
}
/* Called from main thread */
-void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
- pa_bool_t changed;
- pa_sink_set_volume_data data;
+void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
+ pa_sink_input *i;
+ uint32_t idx;
+
+ /* This is called whenever a sink input volume changes 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_flat_volume().*/
+
+ pa_cvolume_mute(new_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);
+
+ 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];
+ }
+
+ /* 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);
+ pa_sw_cvolume_divide(&i->soft_volume, &i->virtual_volume, &remapped_new_volume);
+
+ /* Hooks have the ability to play games with i->soft_volume */
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+
+ /* We don't issue PA_SINK_INPUT_MESSAGE_SET_VOLUME because
+ * we want the update to have atomically with the sink
+ * volume update, hence we do it within the
+ * pa_sink_set_flat_volume() call below*/
+ }
+}
+
+/* Called from main thread */
+void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
+ pa_sink_input *i;
+ uint32_t idx;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(old_volume);
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
+
+ /* This is called whenever the sink volume changes that is not
+ * caused by a sink input volume change. We need to fix up the
+ * sink input volumes accordingly */
+
+ 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_old_volume, remapped_new_volume, fixed_volume;
+ unsigned c;
+
+ remapped_new_volume = s->virtual_volume;
+ pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
+
+ remapped_old_volume = *old_volume;
+ pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map);
+
+ for (c = 0; c < i->sample_spec.channels; c++)
+
+ if (remapped_old_volume.values[c] == PA_VOLUME_MUTED)
+ fixed_volume.values[c] = PA_VOLUME_MUTED;
+ else
+ fixed_volume.values[c] = (pa_volume_t)
+ ((uint64_t) i->virtual_volume.values[c] *
+ (uint64_t) remapped_new_volume.values[c] /
+ (uint64_t) remapped_old_volume.values[c]);
+
+ fixed_volume.channels = i->virtual_volume.channels;
+
+ if (!pa_cvolume_equal(&fixed_volume, &i->virtual_volume)) {
+ i->virtual_volume = fixed_volume;
+
+ /* 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);
+ }
+ }
+}
+
+/* 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_cvolume old_virtual_volume;
+ pa_bool_t virtual_volume_changed;
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->state));
@@ -855,37 +1019,45 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
pa_assert(pa_cvolume_valid(volume));
pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
- data.sink = s;
- data.volume = *volume;
+ old_virtual_volume = s->virtual_volume;
+ s->virtual_volume = *volume;
+ virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
- changed = !pa_cvolume_equal(&data.volume, &s->volume);
+ /* 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, &old_virtual_volume);
- if (changed) {
- if (pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_SET_VOLUME], &data) < 0)
- return;
-
- changed = !pa_cvolume_equal(&data.volume, &s->volume);
- }
+ if (s->set_volume) {
+ /* If we have a function set_volume(), then we do not apply a
+ * soft volume by default. However, set_volume() is apply one
+ * to s->soft_volume */
- s->volume = data.volume;
+ pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+ s->set_volume(s);
- if (s->set_volume && s->set_volume(s) < 0)
- s->set_volume = NULL;
+ } else
+ /* If we have no function set_volume(), then the soft volume
+ * becomes the virtual volume */
+ s->soft_volume = s->virtual_volume;
- if (!s->set_volume)
- pa_sink_set_soft_volume(s, 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 (changed)
+ if (virtual_volume_changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-/* Called from main thread */
+/* 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);
+ s->soft_volume = *volume;
+
if (PA_SINK_IS_LINKED(s->state))
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL);
+ 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;
}
@@ -893,41 +1065,43 @@ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
/* Called from main thread */
const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_IS_LINKED(s->state));
if (s->refresh_volume || force_refresh) {
- struct pa_cvolume old_volume = s->volume;
+ struct pa_cvolume old_virtual_volume = s->virtual_volume;
+
+ if (s->get_volume)
+ s->get_volume(s);
- if (s->get_volume && s->get_volume(s) < 0)
- s->get_volume = NULL;
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
- if (!s->get_volume)
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+ if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
+
+ if (s->flags & PA_SINK_FLAT_VOLUME)
+ pa_sink_propagate_flat_volume(s, &old_virtual_volume);
- if (!pa_cvolume_equal(&old_volume, &s->volume))
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
}
- return &s->volume;
+ return &s->virtual_volume;
}
/* Called from main thread */
void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
- pa_bool_t changed;
+ pa_bool_t old_muted;
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->state));
- changed = s->muted != mute;
+ old_muted = s->muted;
s->muted = mute;
- if (s->set_mute && s->set_mute(s) < 0)
- s->set_mute = NULL;
+ if (s->set_mute)
+ s->set_mute(s);
- if (!s->set_mute)
- pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
- if (changed)
+ if (old_muted != s->muted)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -935,16 +1109,14 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_IS_LINKED(s->state));
if (s->refresh_muted || force_refresh) {
pa_bool_t old_muted = s->muted;
- if (s->get_mute && s->get_mute(s) < 0)
- s->get_mute = NULL;
+ if (s->get_mute)
+ s->get_mute(s);
- if (!s->get_mute)
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
+ 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)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
@@ -954,6 +1126,21 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
}
/* 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_proplist_update(s->proplist, mode, p);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
+
+ return TRUE;
+}
+
+/* Called from main thread */
void pa_sink_set_description(pa_sink *s, const char *description) {
const char *old;
pa_sink_assert_ref(s);
@@ -1053,6 +1240,22 @@ unsigned pa_sink_check_suspend(pa_sink *s) {
return ret;
}
+/* Called from the IO thread */
+static void sync_input_volumes_within_thread(pa_sink *s) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) {
+ if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
+ continue;
+
+ i->thread_info.soft_volume = i->soft_volume;
+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
+ }
+}
+
/* Called from IO thread, except when it is not */
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_sink *s = PA_SINK(o);
@@ -1107,7 +1310,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
* slow start, i.e. need some time to buffer client
* samples before beginning streaming. */
- return 0;
+ /* In flat volume mode we need to update the volume as
+ * well */
+ return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
}
case PA_SINK_MESSAGE_REMOVE_INPUT: {
@@ -1148,7 +1353,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_sink_invalidate_requested_latency(s);
pa_sink_request_rewind(s, (size_t) -1);
- return 0;
+ /* In flat volume mode we need to update the volume as
+ * well */
+ return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
}
case PA_SINK_MESSAGE_START_MOVE: {
@@ -1194,7 +1401,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_log_debug("Requesting rewind due to started move");
pa_sink_request_rewind(s, (size_t) -1);
- return 0;
+ /* In flat volume mode we need to update the volume as
+ * well */
+ return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
}
case PA_SINK_MESSAGE_FINISH_MOVE: {
@@ -1238,27 +1447,36 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_sink_request_rewind(s, nbytes);
}
- return 0;
+ /* In flat volume mode we need to update the volume as
+ * well */
+ return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
}
case PA_SINK_MESSAGE_SET_VOLUME:
- s->thread_info.soft_volume = *((pa_cvolume*) userdata);
- pa_sink_request_rewind(s, (size_t) -1);
- return 0;
+ if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
+ s->thread_info.soft_volume = s->soft_volume;
+ pa_sink_request_rewind(s, (size_t) -1);
+ }
- case PA_SINK_MESSAGE_SET_MUTE:
- s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+ if (s->flags & PA_SINK_FLAT_VOLUME)
+ sync_input_volumes_within_thread(s);
- pa_sink_request_rewind(s, (size_t) -1);
return 0;
case PA_SINK_MESSAGE_GET_VOLUME:
- *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+ return 0;
+
+ case PA_SINK_MESSAGE_SET_MUTE:
+
+ if (!s->thread_info.soft_muted != s->muted) {
+ s->thread_info.soft_muted = s->muted;
+ pa_sink_request_rewind(s, (size_t) -1);
+ }
+
return 0;
case PA_SINK_MESSAGE_GET_MUTE:
- *((pa_bool_t*) userdata) = s->thread_info.soft_muted;
return 0;
case PA_SINK_MESSAGE_SET_STATE:
@@ -1591,6 +1809,7 @@ void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t m
pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
}
+/* Called from main context */
size_t pa_sink_get_max_rewind(pa_sink *s) {
size_t r;
pa_sink_assert_ref(s);
@@ -1603,6 +1822,7 @@ size_t pa_sink_get_max_rewind(pa_sink *s) {
return r;
}
+/* Called from main context */
size_t pa_sink_get_max_request(pa_sink *s) {
size_t r;
pa_sink_assert_ref(s);
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 254be3b0..124b4e11 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -27,6 +27,7 @@ typedef struct pa_sink pa_sink;
#include <inttypes.h>
+#include <pulse/def.h>
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
@@ -38,21 +39,12 @@ typedef struct pa_sink pa_sink;
#include <pulsecore/refcnt.h>
#include <pulsecore/msgobject.h>
#include <pulsecore/rtpoll.h>
+#include <pulsecore/card.h>
+#include <pulsecore/queue.h>
#define PA_MAX_INPUTS_PER_SINK 32
-typedef enum pa_sink_state {
- PA_SINK_INIT,
- PA_SINK_RUNNING,
- PA_SINK_SUSPENDED,
- PA_SINK_IDLE,
- PA_SINK_UNLINKED
-} pa_sink_state_t;
-
-static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) {
- return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
-}
-
+/* Returns true if sink is linked: registered and accessible from client side. */
static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
}
@@ -70,6 +62,7 @@ struct pa_sink {
pa_proplist *proplist;
pa_module *module; /* may be NULL */
+ pa_card *card; /* may be NULL */
pa_sample_spec sample_spec;
pa_channel_map channel_map;
@@ -78,10 +71,11 @@ struct pa_sink {
unsigned n_corked;
pa_source *monitor_source;
- pa_cvolume volume;
- pa_bool_t muted;
+ pa_volume_t base_volume; /* shall be constant */
+ unsigned n_volume_steps; /* shall be constant */
- pa_volume_t base_volume; /* shall be constant */
+ pa_cvolume virtual_volume, soft_volume;
+ pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
pa_bool_t refresh_muted:1;
@@ -100,23 +94,23 @@ struct pa_sink {
* context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message
* will be sent to the IO thread instead. If refresh_volume is
* FALSE neither this function is called nor a message is sent. */
- int (*get_volume)(pa_sink *s); /* may be NULL */
+ void (*get_volume)(pa_sink *s); /* may be NULL */
/* Called when the volume shall be changed. Called from main loop
* context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
* will be sent to the IO thread instead. */
- int (*set_volume)(pa_sink *s); /* dito */
+ void (*set_volume)(pa_sink *s); /* dito */
/* Called when the mute setting is queried. Called from main loop
* context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message
* will be sent to the IO thread instead. If refresh_mute is
* FALSE neither this function is called nor a message is sent.*/
- int (*get_mute)(pa_sink *s); /* dito */
+ void (*get_mute)(pa_sink *s); /* dito */
/* Called when the mute setting shall be changed. Called from main
* loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
* message will be sent to the IO thread instead. */
- int (*set_mute)(pa_sink *s); /* dito */
+ void (*set_mute)(pa_sink *s); /* dito */
/* Called when a rewind request is issued. Called from IO thread
* context. */
@@ -131,6 +125,7 @@ struct pa_sink {
struct {
pa_sink_state_t state;
pa_hashmap *inputs;
+
pa_cvolume soft_volume;
pa_bool_t soft_muted:1;
@@ -186,6 +181,7 @@ typedef struct pa_sink_new_data {
const char *driver;
pa_module *module;
+ pa_card *card;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
@@ -208,12 +204,7 @@ void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volum
void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
void pa_sink_new_data_done(pa_sink_new_data *data);
-typedef struct pa_sink_set_volume_data {
- pa_sink *sink;
- pa_cvolume volume;
-} pa_sink_set_volume_data;
-
-/* To be called exclusively by the sink driver, from main context */
+/*** To be called exclusively by the sink driver, from main context */
pa_sink* pa_sink_new(
pa_core *core,
@@ -232,7 +223,9 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s);
-/* May be called by everyone, from main context */
+void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume);
+
+/**** May be called by everyone, from main context */
/* The returned value is supposed to be in the time domain of the sound card! */
pa_usec_t pa_sink_get_latency(pa_sink *s);
@@ -246,18 +239,27 @@ int pa_sink_update_status(pa_sink*s);
int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
-void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume);
-void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume);
+void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
+void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume);
+
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg);
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 pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refres);
+pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);
+
+pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_sink_get_state(s) ((s)->state)
-/* To be called exclusively by the sink driver, from IO context */
+/* Moves all inputs away, and stores them in pa_queue */
+pa_queue *pa_sink_move_all_start(pa_sink *s);
+void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save);
+void pa_sink_move_all_fail(pa_queue *q);
+
+/*** To be called exclusively by the sink driver, from IO context */
void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
@@ -278,7 +280,7 @@ void pa_sink_set_max_request(pa_sink *s, size_t max_request);
void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
-/* To be called exclusively by sink input drivers, from IO context */
+/*** To be called exclusively by sink input drivers, from IO context */
void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index f721f699..a0920025 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -202,9 +202,11 @@ void pa_make_udp_socket_low_delay(int fd) {
}
int pa_socket_set_rcvbuf(int fd, size_t l) {
+ int bufsz = (int)l;
+
pa_assert(fd >= 0);
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) {
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&bufsz, sizeof(bufsz)) < 0) {
pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno));
return -1;
}
@@ -213,9 +215,11 @@ int pa_socket_set_rcvbuf(int fd, size_t l) {
}
int pa_socket_set_sndbuf(int fd, size_t l) {
+ int bufsz = (int)l;
+
pa_assert(fd >= 0);
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) {
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&bufsz, sizeof(bufsz)) < 0) {
pa_log("SO_SNDBUF: %s", pa_cstrerror(errno));
return -1;
}
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index c30c16eb..1be421f1 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -133,7 +133,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
* 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);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
/* Called from IO thread context */
@@ -322,7 +322,7 @@ int pa_play_file(
data.sink = sink;
data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, &ss);
- pa_sink_input_new_data_set_volume(&data, volume);
+ pa_sink_input_new_data_set_virtual_volume(&data, volume);
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index c92c5ab7..1e1ac4e1 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -29,6 +29,7 @@
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/core-subscribe.h>
@@ -111,8 +112,10 @@ pa_source_output* pa_source_output_new(
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
- if (!data->source)
- data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, TRUE);
+ if (!data->source) {
+ data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
+ data->save_source = FALSE;
+ }
pa_return_null_if_fail(data->source);
pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);
@@ -153,6 +156,9 @@ pa_source_output* pa_source_output_new(
pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+ if (data->client)
+ pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
+
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0)
return NULL;
@@ -177,8 +183,6 @@ pa_source_output* pa_source_output_new(
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
-
- data->resample_method = pa_resampler_get_method(resampler);
}
o = pa_msgobject_new(pa_source_output);
@@ -189,17 +193,20 @@ pa_source_output* pa_source_output_new(
o->state = PA_SOURCE_OUTPUT_INIT;
o->flags = flags;
o->proplist = pa_proplist_copy(data->proplist);
- o->driver = pa_xstrdup(data->driver);
+ o->driver = pa_xstrdup(pa_path_get_filename(data->driver));
o->module = data->module;
o->source = data->source;
o->client = data->client;
- o->resample_method = data->resample_method;
+ o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
+ o->requested_resample_method = data->resample_method;
o->sample_spec = data->sample_spec;
o->channel_map = data->channel_map;
o->direct_on_input = data->direct_on_input;
+ o->save_source = data->save_source;
+
reset_callbacks(o);
o->userdata = NULL;
@@ -223,6 +230,9 @@ pa_source_output* pa_source_output_new(
pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
+ if (o->client)
+ pa_assert_se(pa_idxset_put(o->client->source_outputs, o, NULL) >= 0);
+
if (o->direct_on_input)
pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);
@@ -242,11 +252,13 @@ pa_source_output* pa_source_output_new(
static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) {
pa_assert(o);
+ if (!o->source)
+ return;
+
if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED)
pa_assert_se(o->source->n_corked -- >= 1);
else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
o->source->n_corked++;
-
}
/* Called from main context */
@@ -262,7 +274,7 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t
o->state = state;
if (state != PA_SOURCE_OUTPUT_UNLINKED)
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
pa_source_update_status(o->source);
@@ -282,31 +294,39 @@ void pa_source_output_unlink(pa_source_output*o) {
linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
if (linked)
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
if (o->direct_on_input)
pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
- pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL);
- if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
- pa_source_output_unref(o);
+
+ pa_idxset_remove_by_data(o->core->source_outputs, o, NULL);
+
+ if (o->source)
+ if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
+ pa_source_output_unref(o);
+
+ if (o->client)
+ pa_idxset_remove_by_data(o->client->source_outputs, o, NULL);
update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
o->state = PA_SOURCE_OUTPUT_UNLINKED;
- if (linked)
+ if (linked && o->source)
if (o->source->asyncmsgq)
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
reset_callbacks(o);
if (linked) {
- pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
}
- pa_source_update_status(o->source);
+ if (o->source) {
+ pa_source_update_status(o->source);
+ o->source = NULL;
+ }
- o->source = NULL;
pa_source_output_unref(o);
}
@@ -355,8 +375,8 @@ void pa_source_output_put(pa_source_output *o) {
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
- pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
pa_source_update_status(o->source);
}
@@ -564,7 +584,7 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
- pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
return 0;
}
@@ -587,18 +607,48 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
- pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
}
}
+/* Called from main thread */
+pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
+
+ pa_source_output_assert_ref(o);
+
+ pa_proplist_update(o->proplist, mode, p);
+
+ if (PA_SINK_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);
+ }
+
+ return TRUE;
+}
+
/* Called from main context */
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
pa_source_output_assert_ref(o);
- return o->resample_method;
+ return o->actual_resample_method;
+}
+
+/* Called from main context */
+pa_bool_t pa_source_output_may_move(pa_source_output *o) {
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+ if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
+ return FALSE;
+
+ if (o->direct_on_input)
+ return FALSE;
+
+ return TRUE;
}
+/* Called from main context */
pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
@@ -607,10 +657,7 @@ pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
if (dest == o->source)
return TRUE;
- if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
- return FALSE;
-
- if (o->direct_on_input)
+ if (!pa_source_output_may_move(o))
return FALSE;
if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
@@ -626,26 +673,56 @@ pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
}
/* Called from main context */
-int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
+int pa_source_output_start_move(pa_source_output *o) {
pa_source *origin;
- pa_resampler *new_resampler;
- pa_source_output_move_hook_data hook_data;
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
- pa_source_assert_ref(dest);
+ pa_assert(o->source);
+
+ if (!pa_source_output_may_move(o))
+ return -1;
+
+ if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o) < 0)
+ return -1;
origin = o->source;
- if (dest == origin)
- return 0;
+ pa_idxset_remove_by_data(o->source->outputs, o, NULL);
+
+ if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
+ pa_assert_se(origin->n_corked-- >= 1);
+
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+
+ pa_source_update_status(o->source);
+ o->source = NULL;
+
+ return 0;
+}
+
+/* Called from main context */
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save) {
+ pa_resampler *new_resampler;
+
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+ pa_assert(!o->source);
+ pa_source_assert_ref(dest);
if (!pa_source_output_may_move_to(o, dest))
return -1;
+ o->source = dest;
+ o->save_source = save;
+ pa_idxset_put(o->source->outputs, o, NULL);
+
+ if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
+ o->source->n_corked++;
+
if (o->thread_info.resampler &&
- pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
- pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
+ pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) &&
+ pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &dest->channel_map))
/* Try to reuse the old resampler if possible */
new_resampler = o->thread_info.resampler;
@@ -657,10 +734,10 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
/* Okey, we need a new resampler for the new source */
if (!(new_resampler = pa_resampler_new(
- dest->core->mempool,
+ o->core->mempool,
&dest->sample_spec, &dest->channel_map,
&o->sample_spec, &o->channel_map,
- o->resample_method,
+ o->requested_resample_method,
((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
(o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
@@ -670,22 +747,6 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
} else
new_resampler = NULL;
- hook_data.source_output = o;
- hook_data.destination = dest;
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data);
-
- /* Okey, let's move it */
- pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
-
- pa_idxset_remove_by_data(origin->outputs, o, NULL);
- pa_idxset_put(dest->outputs, o, NULL);
- o->source = dest;
-
- if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) {
- pa_assert_se(origin->n_corked-- >= 1);
- dest->n_corked++;
- }
-
/* Replace resampler */
if (new_resampler != o->thread_info.resampler) {
if (o->thread_info.resampler)
@@ -705,20 +766,40 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
&o->source->silence);
}
- pa_source_update_status(origin);
pa_source_update_status(dest);
-
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
+ pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);
+
+ /* Notify everyone */
if (o->moved)
o->moved(o);
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o);
+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o);
+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
- pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name);
+ return 0;
+}
- /* Notify everyone */
- pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+/* Called from main context */
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) {
+
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+ pa_assert(o->source);
+ pa_source_assert_ref(dest);
+
+ if (dest == o->source)
+ return 0;
+
+ if (!pa_source_output_may_move_to(o, dest))
+ return -1;
+
+ if (pa_source_output_start_move(o) < 0)
+ return -1;
+
+ if (pa_source_output_finish_move(o, dest, save) < 0)
+ return -1;
return 0;
}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index f011f9bd..79b4926b 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -32,6 +32,7 @@ typedef struct pa_source_output pa_source_output;
#include <pulsecore/resampler.h>
#include <pulsecore/module.h>
#include <pulsecore/client.h>
+#include <pulsecore/sink-input.h>
typedef enum pa_source_output_state {
PA_SOURCE_OUTPUT_INIT,
@@ -71,7 +72,7 @@ struct pa_source_output {
pa_module *module; /* may be NULL */
pa_client *client; /* may be NULL */
- pa_source *source;
+ pa_source *source; /* NULL while being moved */
/* A source output can monitor just a single input of a sink, in which case we find it here */
pa_sink_input *direct_on_input; /* may be NULL */
@@ -79,7 +80,12 @@ struct pa_source_output {
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_resample_method_t resample_method;
+ /* if TRUE then the source we are connected to is worth
+ * remembering, i.e. was explicitly chosen by the user and not
+ * automatically. module-stream-restore looks for this.*/
+ pa_bool_t save_source:1;
+
+ pa_resample_method_t requested_resample_method, actual_resample_method;
/* Pushes a new memchunk into the output. Called from IO thread
* context. */
@@ -139,7 +145,7 @@ struct pa_source_output {
struct {
pa_source_output_state_t state;
- pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+ pa_bool_t attached:1; /* True only between ->attach() and ->detach() calls */
pa_sample_spec sample_spec;
@@ -187,6 +193,8 @@ typedef struct pa_source_output_new_data {
pa_bool_t sample_spec_is_set:1;
pa_bool_t channel_map_is_set:1;
+
+ pa_bool_t save_source:1;
} pa_source_output_new_data;
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
@@ -194,11 +202,6 @@ void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data,
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
void pa_source_output_new_data_done(pa_source_output_new_data *data);
-typedef struct pa_source_output_move_hook_data {
- pa_source_output *source_output;
- pa_source *destination;
-} pa_source_output_move_hook_data;
-
/* To be called by the implementing module only */
pa_source_output* pa_source_output_new(
@@ -209,11 +212,11 @@ pa_source_output* pa_source_output_new(
void pa_source_output_put(pa_source_output *o);
void pa_source_output_unlink(pa_source_output*o);
-void pa_source_output_set_name(pa_source_output *i, const char *name);
+void pa_source_output_set_name(pa_source_output *o, const char *name);
-pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec);
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec);
-void pa_source_output_cork(pa_source_output *i, pa_bool_t b);
+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);
@@ -222,12 +225,21 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
/* External code may request disconnection with this funcion */
void pa_source_output_kill(pa_source_output*o);
-pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_latency);
+pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency);
+
+pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p);
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
+pa_bool_t pa_source_output_may_move(pa_source_output *o);
pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);
-int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save);
+
+/* The same as pa_source_output_move_to() but in two seperate steps,
+ * first the detaching from the old source, then the attaching to the
+ * new source */
+int pa_source_output_start_move(pa_source_output *o);
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save);
#define pa_source_output_get_state(o) ((o)->state)
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 815ec271..0152b082 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -31,6 +31,7 @@
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
+#include <pulse/util.h>
#include <pulsecore/source-output.h>
#include <pulsecore/namereg.h>
@@ -158,6 +159,9 @@ pa_source* pa_source_new(
if (!data->muted_is_set)
data->muted = FALSE;
+ if (data->card)
+ pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
+
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
pa_xfree(s);
pa_namereg_unregister(core, name);
@@ -172,8 +176,9 @@ pa_source* pa_source_new(
s->flags = flags;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
- s->driver = pa_xstrdup(data->driver);
+ s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
s->module = data->module;
+ s->card = data->card;
s->sample_spec = data->sample_spec;
s->channel_map = data->channel_map;
@@ -182,10 +187,12 @@ pa_source* pa_source_new(
s->n_corked = 0;
s->monitor_of = NULL;
- s->volume = data->volume;
+ s->virtual_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->base_volume = PA_VOLUME_NORM;
reset_callbacks(s);
s->userdata = NULL;
@@ -201,8 +208,8 @@ pa_source* pa_source_new(
0);
s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
- s->thread_info.soft_muted = FALSE;
+ s->thread_info.soft_volume = s->soft_volume;
+ s->thread_info.soft_muted = s->muted;
s->thread_info.state = s->state;
s->thread_info.max_rewind = 0;
s->thread_info.requested_latency_valid = FALSE;
@@ -212,6 +219,9 @@ pa_source* pa_source_new(
pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
+ if (s->card)
+ pa_assert_se(pa_idxset_put(s->card->sources, s, NULL) >= 0);
+
pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s",
s->index,
s->name,
@@ -285,10 +295,13 @@ void pa_source_put(pa_source *s) {
if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
s->flags |= PA_SOURCE_DECIBEL_VOLUME;
- s->thread_info.soft_volume = s->volume;
+ s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
}
+ if (s->flags & PA_SOURCE_DECIBEL_VOLUME)
+ s->n_volume_steps = PA_VOLUME_NORM+1;
+
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
@@ -314,6 +327,9 @@ void pa_source_unlink(pa_source *s) {
pa_namereg_unregister(s->core, s->name);
pa_idxset_remove_by_data(s->core->sources, s, NULL);
+ if (s->card)
+ pa_idxset_remove_by_data(s->card->sources, s, NULL);
+
while ((o = pa_idxset_first(s->outputs, NULL))) {
pa_assert(o != j);
pa_source_output_kill(o);
@@ -401,6 +417,58 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
}
+/* Called from main context */
+pa_queue *pa_source_move_all_start(pa_source *s) {
+ pa_queue *q;
+ pa_source_output *o, *n;
+ uint32_t idx;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+ q = pa_queue_new();
+
+ for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
+ n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
+
+ if (pa_source_output_start_move(o) >= 0)
+ pa_queue_push(q, pa_source_output_ref(o));
+ }
+
+ return q;
+}
+
+/* Called from main context */
+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(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_unlink(o);
+
+ pa_source_output_unref(o);
+ }
+
+ pa_queue_free(q, NULL, NULL);
+}
+
+/* Called from main context */
+void pa_source_move_all_fail(pa_queue *q) {
+ pa_source_output *o;
+ pa_assert(q);
+
+ while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
+ pa_source_output_unlink(o);
+ pa_source_output_unref(o);
+ }
+
+ pa_queue_free(q, NULL, NULL);
+}
+
/* Called from IO thread context */
void pa_source_process_rewind(pa_source *s, size_t nbytes) {
pa_source_output *o;
@@ -429,9 +497,6 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
pa_assert(chunk);
- if (s->thread_info.state != PA_SOURCE_RUNNING)
- return;
-
if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
pa_memchunk vchunk = *chunk;
@@ -470,9 +535,6 @@ void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *
pa_assert(o->thread_info.direct_on_input);
pa_assert(chunk);
- if (s->thread_info.state != PA_SOURCE_RUNNING)
- return;
-
if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
pa_memchunk vchunk = *chunk;
@@ -511,32 +573,38 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
/* Called from main thread */
void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
- pa_bool_t changed;
+ pa_cvolume old_virtual_volume;
+ pa_bool_t virtual_volume_changed;
pa_source_assert_ref(s);
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));
- changed = !pa_cvolume_equal(volume, &s->volume);
- s->volume = *volume;
+ old_virtual_volume = s->virtual_volume;
+ s->virtual_volume = *volume;
+ virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
- if (s->set_volume && s->set_volume(s) < 0)
- s->set_volume = NULL;
+ 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;
- if (!s->set_volume)
- pa_source_set_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 (changed)
+ if (virtual_volume_changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-/* Called from main thread */
+/* 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);
if (PA_SOURCE_IS_LINKED(s->state))
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL);
+ 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;
}
@@ -547,38 +615,36 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (s->refresh_volume || force_refresh) {
- pa_cvolume old_volume = s->volume;
+ pa_cvolume old_virtual_volume = s->virtual_volume;
- if (s->get_volume && s->get_volume(s) < 0)
- s->get_volume = NULL;
+ if (s->get_volume)
+ s->get_volume(s);
- if (!s->get_volume)
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+ 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_volume, &s->volume))
+ if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume))
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
- return &s->volume;
+ return &s->virtual_volume;
}
/* Called from main thread */
void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
- pa_bool_t changed;
+ pa_bool_t old_muted;
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
- changed = s->muted != mute;
+ old_muted = s->muted;
s->muted = mute;
- if (s->set_mute && s->set_mute(s) < 0)
- s->set_mute = NULL;
+ if (s->set_mute)
+ s->set_mute(s);
- if (!s->set_mute)
- pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
- if (changed)
+ if (old_muted != s->muted)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -591,11 +657,10 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
if (s->refresh_muted || force_refresh) {
pa_bool_t old_muted = s->muted;
- if (s->get_mute && s->get_mute(s) < 0)
- s->get_mute = NULL;
+ if (s->get_mute)
+ s->get_mute(s);
- if (!s->get_mute)
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
+ 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)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
@@ -605,6 +670,21 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
}
/* 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_proplist_update(s->proplist, mode, p);
+
+ if (PA_SOURCE_IS_LINKED(s->state)) {
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
+
+ return TRUE;
+}
+
+/* Called from main thread */
void pa_source_set_description(pa_source *s, const char *description) {
const char *old;
pa_source_assert_ref(s);
@@ -743,19 +823,17 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
}
case PA_SOURCE_MESSAGE_SET_VOLUME:
- s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+ s->thread_info.soft_volume = s->soft_volume;
return 0;
- case PA_SOURCE_MESSAGE_SET_MUTE:
- s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+ case PA_SOURCE_MESSAGE_GET_VOLUME:
return 0;
- case PA_SOURCE_MESSAGE_GET_VOLUME:
- *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+ case PA_SOURCE_MESSAGE_SET_MUTE:
+ s->thread_info.soft_muted = s->muted;
return 0;
case PA_SOURCE_MESSAGE_GET_MUTE:
- *((pa_bool_t*) userdata) = s->thread_info.soft_muted;
return 0;
case PA_SOURCE_MESSAGE_SET_STATE:
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index b93e4ad0..8a91016a 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -41,21 +41,12 @@ typedef struct pa_source pa_source;
#include <pulsecore/msgobject.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/source-output.h>
+#include <pulsecore/card.h>
+#include <pulsecore/queue.h>
#define PA_MAX_OUTPUTS_PER_SOURCE 32
-typedef enum pa_source_state {
- PA_SOURCE_INIT,
- PA_SOURCE_RUNNING,
- PA_SOURCE_SUSPENDED,
- PA_SOURCE_IDLE,
- PA_SOURCE_UNLINKED
-} pa_source_state_t;
-
-static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) {
- return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
-}
-
+/* Returns true if source is linked: registered and accessible from client side. */
static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
}
@@ -73,6 +64,7 @@ struct pa_source {
pa_proplist *proplist;
pa_module *module; /* may be NULL */
+ pa_card *card; /* may be NULL */
pa_sample_spec sample_spec;
pa_channel_map channel_map;
@@ -81,10 +73,11 @@ struct pa_source {
unsigned n_corked;
pa_sink *monitor_of; /* may be NULL */
- pa_cvolume volume;
- pa_bool_t muted;
-
pa_volume_t base_volume; /* shall be constant */
+ unsigned n_volume_steps; /* shall be constant */
+
+ pa_cvolume virtual_volume, soft_volume;
+ pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
pa_bool_t refresh_muted:1;
@@ -103,23 +96,23 @@ struct pa_source {
* context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
* will be sent to the IO thread instead. If refresh_volume is
* FALSE neither this function is called nor a message is sent. */
- int (*get_volume)(pa_source *s); /* dito */
+ void (*get_volume)(pa_source *s); /* dito */
/* Called when the volume shall be changed. Called from main loop
* context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message
* will be sent to the IO thread instead. */
- int (*set_volume)(pa_source *s); /* dito */
+ void (*set_volume)(pa_source *s); /* dito */
/* Called when the mute setting is queried. Called from main loop
* context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message
* will be sent to the IO thread instead. If refresh_mute is
* FALSE neither this function is called nor a message is sent.*/
- int (*get_mute)(pa_source *s); /* dito */
+ void (*get_mute)(pa_source *s); /* dito */
/* Called when the mute setting shall be changed. Called from main
* loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
* message will be sent to the IO thread instead. */
- int (*set_mute)(pa_source *s); /* dito */
+ void (*set_mute)(pa_source *s); /* dito */
/* Called when a the requested latency is changed. Called from IO
* thread context. */
@@ -130,6 +123,7 @@ struct pa_source {
struct {
pa_source_state_t state;
pa_hashmap *outputs;
+
pa_cvolume soft_volume;
pa_bool_t soft_muted:1;
@@ -174,6 +168,7 @@ typedef struct pa_source_new_data {
const char *driver;
pa_module *module;
+ pa_card *card;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
@@ -196,7 +191,7 @@ void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *v
void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute);
void pa_source_new_data_done(pa_source_new_data *data);
-/* To be called exclusively by the source driver, from main context */
+/*** To be called exclusively by the source driver, from main context */
pa_source* pa_source_new(
pa_core *core,
@@ -215,7 +210,9 @@ void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t
void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s);
-/* May be called by everyone, from main context */
+void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume);
+
+/*** 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);
@@ -229,17 +226,23 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend);
int pa_source_suspend_all(pa_core *c, pa_bool_t suspend);
void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
-void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume);
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 pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
+pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p);
+
unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
-/* To be called exclusively by the source driver, from IO context */
+/* Moves all inputs away, and stores them in pa_queue */
+pa_queue *pa_source_move_all_start(pa_source *s);
+void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save);
+void pa_source_move_all_fail(pa_queue *q);
+
+/*** To be called exclusively by the source driver, from IO context */
void pa_source_post(pa_source*s, const pa_memchunk *chunk);
void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk);
@@ -255,7 +258,7 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s);
void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
-/* To be called exclusively by source output drivers, from IO context */
+/*** To be called exclusively by source output drivers, from IO context */
void pa_source_invalidate_requested_latency(pa_source *s);
diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
index 540faef9..8b952788 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -180,3 +180,9 @@ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
size *= 2;
}
}
+
+pa_bool_t pa_strbuf_isempty(pa_strbuf *sb) {
+ pa_assert(sb);
+
+ return sb->length <= 0;
+}
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
index ac68d7be..1d2a588f 100644
--- a/src/pulsecore/strbuf.h
+++ b/src/pulsecore/strbuf.h
@@ -23,6 +23,7 @@
***/
#include <pulse/gccmacro.h>
+#include <pulsecore/macro.h>
typedef struct pa_strbuf pa_strbuf;
@@ -35,4 +36,6 @@ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_A
void pa_strbuf_puts(pa_strbuf *sb, const char *t);
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
+pa_bool_t pa_strbuf_isempty(pa_strbuf *sb);
+
#endif
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
index 19288eeb..1a99e924 100644
--- a/src/pulsecore/tagstruct.h
+++ b/src/pulsecore/tagstruct.h
@@ -37,6 +37,10 @@
typedef struct pa_tagstruct pa_tagstruct;
+/* Due to a stupid design flaw, proplists may only be at the END of a
+ * packet or not before a STRING! Don't forget that! We can't really
+ * fix this without breaking compat. */
+
enum {
PA_TAG_INVALID = 0,
PA_TAG_STRING = 't',
diff --git a/src/pulsecore/vector.h b/src/pulsecore/vector.h
new file mode 100644
index 00000000..076bd6c0
--- /dev/null
+++ b/src/pulsecore/vector.h
@@ -0,0 +1,97 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+/* First, define HAVE_VECTOR if we have the gcc vector extensions at all */
+#if defined(__SSE2__) || defined(__ALTIVEC__)
+#define HAVE_VECTOR
+
+
+/* This is supposed to be portable to different SIMD instruction
+ * sets. We define vector types for different base types: uint8_t,
+ * int16_t, int32_t, float. The vector type is a union. The fields .i,
+ * .u, .f are arrays for accessing the separate elements of a
+ * vector. .v is a gcc vector type of the right format. .m is the
+ * vector in the type the SIMD extenstion specific intrinsics API
+ * expects. PA_xxx_VECTOR_SIZE is the size of the
+ * entries. PA_xxxx_VECTOR_MAKE constructs a gcc vector variable with
+ * the same value in all elements. */
+
+#ifdef __SSE2__
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+#define PA_UINT8_VECTOR_SIZE 16
+#define PA_INT16_VECTOR_SIZE 8
+#define PA_INT32_VECTOR_SIZE 4
+#define PA_FLOAT_VECTOR_SIZE 4
+
+#define PA_UINT8_VECTOR_MAKE(x) (pa_v16qi) { x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x }
+#define PA_INT16_VECTOR_MAKE(x) (pa_v8hi) { x, x, x, x, x, x, x, x }
+#define PA_INT32_VECTOR_MAKE(x) (pa_v4si) { x, x, x, x }
+#define PA_FLOAT_VECTOR_MAKE(x) (pa_v4fi) { x, x, x, x }
+
+#endif
+
+/* uint8_t vector */
+typedef uint8_t pa_v16qi __attribute__ ((vector_size (PA_UINT8_VECTOR_SIZE * sizeof(uint8_t))));
+typedef union pa_uint8_vector {
+ uint8_t u[PA_UINT8_VECTOR_SIZE];
+ pa_v16qi v;
+#ifdef __SSE2__
+ __m128i m;
+#endif
+} pa_uint8_vector_t;
+
+/* int16_t vector*/
+typedef int16_t pa_v8hi __attribute__ ((vector_size (PA_INT16_VECTOR_SIZE * sizeof(int16_t))));
+typedef union pa_int16_vector {
+ int16_t i[PA_INT16_VECTOR_SIZE];
+ pa_v8hi v;
+#ifdef __SSE2__
+ __m128i m;
+#endif
+} pa_int16_vector_t;
+
+/* int32_t vector */
+typedef int32_t pa_v4si __attribute__ ((vector_size (PA_INT32_VECTOR_SIZE * sizeof(int32_t))));
+typedef union pa_int32_vector {
+ int32_t i[PA_INT32_VECTOR_SIZE];
+ pa_v4si v;
+#ifdef __SSE2__
+ __m128i m;
+#endif
+} pa_int32_vector_t;
+
+/* float vector */
+typedef float pa_v4sf __attribute__ ((vector_size (PA_FLOAT_VECTOR_SIZE * sizeof(float))));
+typedef union pa_float_vector {
+ float f[PA_FLOAT_VECTOR_SIZE];
+ pa_v4sf v;
+#ifdef __SSE2__
+ __m128 m;
+#endif
+} pa_float_vector_t;
+
+#endif
diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c
index 20041af6..f69fa686 100644
--- a/src/tests/proplist-test.c
+++ b/src/tests/proplist-test.c
@@ -29,8 +29,8 @@
#include <pulsecore/core-util.h>
int main(int argc, char*argv[]) {
- pa_proplist *a, *b;
- char *s, *t;
+ pa_proplist *a, *b, *c;
+ char *s, *t, *u;
a = pa_proplist_new();
pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0);
@@ -50,11 +50,18 @@ int main(int argc, char*argv[]) {
s = pa_proplist_to_string(a);
t = pa_proplist_to_string(b);
printf("---\n%s---\n%s", s, t);
+
+ c = pa_proplist_from_string(s);
+ u = pa_proplist_to_string(c);
+ pa_assert_se(pa_streq(s, u));
+
pa_xfree(s);
pa_xfree(t);
+ pa_xfree(u);
pa_proplist_free(a);
pa_proplist_free(b);
+ pa_proplist_free(c);
return 0;
}
diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c
index 6b0cb8f7..d8aff342 100644
--- a/src/tests/rtstutter.c
+++ b/src/tests/rtstutter.c
@@ -36,30 +36,35 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
static int msec_lower, msec_upper;
static void* work(void *p) PA_GCC_NORETURN;
static void* work(void *p) {
+#ifdef HAVE_PTHREAD_SETAFFINITY_NP
cpu_set_t mask;
+#endif
struct sched_param param;
- pa_log_notice("CPU%i: Created thread.", PA_PTR_TO_INT(p));
+ pa_log_notice("CPU%i: Created thread.", PA_PTR_TO_UINT(p));
memset(&param, 0, sizeof(param));
param.sched_priority = 12;
pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) == 0);
+#ifdef HAVE_PTHREAD_SETAFFINITY_NP
CPU_ZERO(&mask);
- CPU_SET((size_t) PA_PTR_TO_INT(p), &mask);
+ CPU_SET((size_t) PA_PTR_TO_UINT(p), &mask);
pa_assert_se(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0);
+#endif
for (;;) {
struct timespec now, end;
uint64_t nsec;
- pa_log_notice("CPU%i: Sleeping for 1s", PA_PTR_TO_INT(p));
+ pa_log_notice("CPU%i: Sleeping for 1s", PA_PTR_TO_UINT(p));
sleep(1);
pa_assert_se(clock_gettime(CLOCK_REALTIME, &end) == 0);
@@ -68,7 +73,7 @@ static void* work(void *p) {
(uint64_t) ((((double) rand())*(double)(msec_upper-msec_lower)*PA_NSEC_PER_MSEC)/RAND_MAX) +
(uint64_t) ((uint64_t) msec_lower*PA_NSEC_PER_MSEC);
- pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_INT(p), (int) (nsec/PA_NSEC_PER_MSEC));
+ pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_UINT(p), (int) (nsec/PA_NSEC_PER_MSEC));
end.tv_sec += (time_t) (nsec / PA_NSEC_PER_SEC);
end.tv_nsec += (long int) (nsec % PA_NSEC_PER_SEC);
@@ -86,7 +91,7 @@ static void* work(void *p) {
}
int main(int argc, char*argv[]) {
- int n;
+ unsigned n;
srand((unsigned) time(NULL));
@@ -106,9 +111,9 @@ int main(int argc, char*argv[]) {
pa_log_notice("Creating random latencies in the range of %ims to %ims.", msec_lower, msec_upper);
- for (n = 1; n < sysconf(_SC_NPROCESSORS_CONF); n++) {
+ for (n = 1; n < pa_ncpus(); n++) {
pthread_t t;
- pa_assert_se(pthread_create(&t, NULL, work, PA_INT_TO_PTR(n)) == 0);
+ pa_assert_se(pthread_create(&t, NULL, work, PA_UINT_TO_PTR(n)) == 0);
}
work(PA_INT_TO_PTR(0));
diff --git a/src/tests/vector-test.c b/src/tests/vector-test.c
new file mode 100644
index 00000000..f7344172
--- /dev/null
+++ b/src/tests/vector-test.c
@@ -0,0 +1,83 @@
+/***
+ 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 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General 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/vector.h>
+#include <pulsecore/log.h>
+
+int main(int argc, char *argv[]) {
+
+#ifdef __SSE2__
+ pa_int16_vector_t input, zero;
+ pa_int32_vector_t unpacked1, unpacked2;
+ pa_int32_vector_t volume1, volume2, volume1_hi, volume1_lo, volume2_hi, volume2_lo, reduce, mask;
+ pa_int16_vector_t output;
+
+ unsigned u;
+
+ zero.v = PA_INT16_VECTOR_MAKE(0);
+ reduce.v = PA_INT32_VECTOR_MAKE(0x10000);
+ volume1.v = volume2.v = PA_INT32_VECTOR_MAKE(0x10000*2+7);
+ mask.v = PA_INT32_VECTOR_MAKE(0xFFFF);
+
+ volume1_lo.m = _mm_and_si128(volume1.m, mask.m);
+ volume2_lo.m = _mm_and_si128(volume2.m, mask.m);
+ volume1_hi.m = _mm_srli_epi32(volume1.m, 16);
+ volume2_hi.m = _mm_srli_epi32(volume2.m, 16);
+
+ input.v = PA_INT16_VECTOR_MAKE(32000);
+
+ for (u = 0; u < PA_INT16_VECTOR_SIZE; u++)
+ pa_log("input=%i\n", input.i[u]);
+
+ unpacked1.m = _mm_unpackhi_epi16(zero.m, input.m);
+ unpacked2.m = _mm_unpacklo_epi16(zero.m, input.m);
+
+ for (u = 0; u < PA_INT32_VECTOR_SIZE; u++)
+ pa_log("unpacked1=%i\n", unpacked1.i[u]);
+
+ unpacked1.v /= reduce.v;
+ unpacked2.v /= reduce.v;
+
+ for (u = 0; u < PA_INT32_VECTOR_SIZE; u++)
+ pa_log("unpacked1=%i\n", unpacked1.i[u]);
+
+ for (u = 0; u < PA_INT32_VECTOR_SIZE; u++)
+ pa_log("volume1=%i\n", volume1.i[u]);
+
+ unpacked1.v = (unpacked1.v * volume1_lo.v) / reduce.v + unpacked1.v * volume1_hi.v;
+ unpacked2.v = (unpacked2.v * volume2_lo.v) / reduce.v + unpacked2.v * volume2_hi.v;
+
+ for (u = 0; u < PA_INT32_VECTOR_SIZE; u++)
+ pa_log("unpacked1=%i\n", unpacked1.i[u]);
+
+ output.m = _mm_packs_epi32(unpacked1.m, unpacked2.m);
+
+ for (u = 0; u < PA_INT16_VECTOR_SIZE; u++)
+ pa_log("output=%i\n", output.i[u]);
+
+#endif
+
+ return 0;
+}
diff --git a/src/tests/voltest.c b/src/tests/voltest.c
index 5bfc97e0..0c6d2ea6 100644
--- a/src/tests/voltest.c
+++ b/src/tests/voltest.c
@@ -6,6 +6,8 @@
int main(int argc, char *argv[]) {
pa_volume_t v;
pa_cvolume cv;
+ float b;
+ pa_channel_map map;
for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) {
@@ -28,5 +30,32 @@ int main(int argc, char *argv[]) {
}
+ map.channels = cv.channels = 2;
+ map.map[0] = PA_CHANNEL_POSITION_LEFT;
+ map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+
+ for (cv.values[0] = PA_VOLUME_MUTED; cv.values[0] <= PA_VOLUME_NORM*2; cv.values[0] += 4096)
+ for (cv.values[1] = PA_VOLUME_MUTED; cv.values[1] <= PA_VOLUME_NORM*2; cv.values[1] += 4096) {
+ char s[PA_CVOLUME_SNPRINT_MAX];
+
+ printf("Volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&cv, &map));
+ }
+
+ for (cv.values[0] = PA_VOLUME_MUTED+4096; cv.values[0] <= PA_VOLUME_NORM*2; cv.values[0] += 4096)
+ for (cv.values[1] = PA_VOLUME_MUTED; cv.values[1] <= PA_VOLUME_NORM*2; cv.values[1] += 4096)
+ for (b = -1.0f; b <= 1.0f; b += 0.2f) {
+ char s[PA_CVOLUME_SNPRINT_MAX];
+ pa_cvolume r;
+ float k;
+
+ printf("Before: volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&cv, &map));
+
+ r = cv;
+ pa_cvolume_set_balance(&r, &map,b);
+
+ k = pa_cvolume_get_balance(&r, &map);
+ 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" : "");
+ }
+
return 0;
}
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 2f430ca7..154e7f9c 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -45,7 +45,7 @@
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, *module_name = NULL, *module_args = NULL;
+static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL, *module_name = NULL, *module_args = NULL, *card_name = NULL, *profile_name = NULL;
static uint32_t sink_input_idx = PA_INVALID_INDEX, source_output_idx = PA_INVALID_INDEX;
static uint32_t module_index;
static int suspend;
@@ -73,6 +73,7 @@ static enum {
UNLOAD_MODULE,
SUSPEND_SINK,
SUSPEND_SOURCE,
+ SET_CARD_PROFILE
} action = NONE;
static void quit(int ret) {
@@ -153,7 +154,21 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi
}
static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
- char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ static const char *state_table[] = {
+ [1+PA_SINK_INVALID_STATE] = "n/a",
+ [1+PA_SINK_RUNNING] = "RUNNING",
+ [1+PA_SINK_IDLE] = "IDLE",
+ [1+PA_SINK_SUSPENDED] = "SUSPENDED"
+ };
+
+ char
+ s[PA_SAMPLE_SPEC_SNPRINT_MAX],
+ cv[PA_CVOLUME_SNPRINT_MAX],
+ cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
+ v[PA_VOLUME_SNPRINT_MAX],
+ vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
+ cm[PA_CHANNEL_MAP_SNPRINT_MAX];
char *pl;
if (is_last < 0) {
@@ -173,24 +188,38 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
printf("\n");
nl = 1;
- printf(_("*** Sink #%u ***\n"
- "Name: %s\n"
- "Driver: %s\n"
- "Sample Specification: %s\n"
- "Channel Map: %s\n"
- "Owner Module: %u\n"
- "Volume: %s\n"
- "Monitor Source: %s\n"
- "Latency: %0.0f usec, configured %0.0f usec\n"
- "Flags: %s%s%s%s%s%s\n"
- "Properties:\n%s"),
+ printf(_("Sink #%u\n"
+ "\tState: %s\n"
+ "\tName: %s\n"
+ "\tDescription: %s\n"
+ "\tDriver: %s\n"
+ "\tSample Specification: %s\n"
+ "\tChannel Map: %s\n"
+ "\tOwner Module: %u\n"
+ "\tMute: %s\n"
+ "\tVolume: %s%s%s\n"
+ "\t balance %0.2f\n"
+ "\tBase Volume: %s%s%s\n"
+ "\tMonitor Source: %s\n"
+ "\tLatency: %0.0f usec, configured %0.0f usec\n"
+ "\tFlags: %s%s%s%s%s%s\n"
+ "\tProperties:\n\t\t%s\n"),
i->index,
+ state_table[1+i->state],
i->name,
+ pa_strnull(i->description),
pa_strnull(i->driver),
pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
i->owner_module,
- i->mute ? _("muted") : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ pa_yes_no(i->mute),
+ pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
+ i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
+ pa_cvolume_get_balance(&i->volume, &i->channel_map),
+ pa_volume_snprint(v, sizeof(v), i->base_volume),
+ i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
+ i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
pa_strnull(i->monitor_source_name),
(double) i->latency, (double) i->configured_latency,
i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
@@ -199,13 +228,27 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
- pl = pa_proplist_to_string(i->proplist));
+ pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
pa_xfree(pl);
}
static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
- char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ static const char *state_table[] = {
+ [1+PA_SOURCE_INVALID_STATE] = "n/a",
+ [1+PA_SOURCE_RUNNING] = "RUNNING",
+ [1+PA_SOURCE_IDLE] = "IDLE",
+ [1+PA_SOURCE_SUSPENDED] = "SUSPENDED"
+ };
+
+ char
+ s[PA_SAMPLE_SPEC_SNPRINT_MAX],
+ cv[PA_CVOLUME_SNPRINT_MAX],
+ cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
+ v[PA_VOLUME_SNPRINT_MAX],
+ vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
+ cm[PA_CHANNEL_MAP_SNPRINT_MAX];
char *pl;
if (is_last < 0) {
@@ -225,24 +268,38 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
printf("\n");
nl = 1;
- printf(_("*** Source #%u ***\n"
- "Name: %s\n"
- "Driver: %s\n"
- "Sample Specification: %s\n"
- "Channel Map: %s\n"
- "Owner Module: %u\n"
- "Volume: %s\n"
- "Monitor of Sink: %s\n"
- "Latency: %0.0f usec, configured %0.0f usec\n"
- "Flags: %s%s%s%s%s%s\n"
- "Properties:\n%s"),
+ printf(_("Source #%u\n"
+ "\tState: %s\n"
+ "\tName: %s\n"
+ "\tDescription: %s\n"
+ "\tDriver: %s\n"
+ "\tSample Specification: %s\n"
+ "\tChannel Map: %s\n"
+ "\tOwner Module: %u\n"
+ "\tMute: %s\n"
+ "\tVolume: %s%s%s\n"
+ "\t balance %0.2f\n"
+ "\tBase Volume: %s%s%s\n"
+ "\tMonitor of Sink: %s\n"
+ "\tLatency: %0.0f usec, configured %0.0f usec\n"
+ "\tFlags: %s%s%s%s%s%s\n"
+ "\tProperties:\n\t\t%s\n"),
i->index,
+ state_table[1+i->state],
i->name,
+ pa_strnull(i->description),
pa_strnull(i->driver),
pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
i->owner_module,
- i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ pa_yes_no(i->mute),
+ pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
+ i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
+ pa_cvolume_get_balance(&i->volume, &i->channel_map),
+ pa_volume_snprint(v, sizeof(v), i->base_volume),
+ i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
+ i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"),
(double) i->latency, (double) i->configured_latency,
i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
@@ -251,13 +308,14 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
- pl = pa_proplist_to_string(i->proplist));
+ pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
pa_xfree(pl);
}
static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
char t[32];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, _("Failed to get module information: %s\n"), pa_strerror(pa_context_errno(c)));
@@ -278,16 +336,18 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int
snprintf(t, sizeof(t), "%u", i->n_used);
- printf(_("*** Module #%u ***\n"
- "Name: %s\n"
- "Argument: %s\n"
- "Usage counter: %s\n"
- "Auto unload: %s\n"),
+ printf(_("Module #%u\n"
+ "\tName: %s\n"
+ "\tArgument: %s\n"
+ "\tUsage counter: %s\n"
+ "\tProperties:\n\t\t%s\n"),
i->index,
i->name,
i->argument ? i->argument : "",
i->n_used != PA_INVALID_INDEX ? t : _("n/a"),
- pa_yes_no(i->auto_unload));
+ pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+ pa_xfree(pl);
}
static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
@@ -313,20 +373,69 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int
snprintf(t, sizeof(t), "%u", i->owner_module);
- printf(_("*** Client #%u ***\n"
- "Driver: %s\n"
- "Owner Module: %s\n"
- "Properties:\n%s"),
+ printf(_("Client #%u\n"
+ "\tDriver: %s\n"
+ "\tOwner Module: %s\n"
+ "\tProperties:\n\t\t%s\n"),
+ i->index,
+ pa_strnull(i->driver),
+ i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
+ pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+ pa_xfree(pl);
+}
+
+static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_last, void *userdata) {
+ char t[32];
+ char *pl;
+
+ if (is_last < 0) {
+ fprintf(stderr, _("Failed to get card information: %s\n"), pa_strerror(pa_context_errno(c)));
+ complete_action();
+ return;
+ }
+
+ if (is_last) {
+ complete_action();
+ return;
+ }
+
+ assert(i);
+
+ if (nl)
+ printf("\n");
+ nl = 1;
+
+ snprintf(t, sizeof(t), "%u", i->owner_module);
+
+ printf(_("Card #%u\n"
+ "\tName: %s\n"
+ "\tDriver: %s\n"
+ "\tOwner Module: %s\n"
+ "\tProperties:\n\t\t%s\n"),
i->index,
+ i->name,
pa_strnull(i->driver),
i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
- pl = pa_proplist_to_string(i->proplist));
+ pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+ if (i->profiles) {
+ pa_card_profile_info *p;
+
+ printf(_("\tProfiles:\n"));
+ for (p = i->profiles; p->name; p++)
+ printf("\t\t%s: %s\n", p->name, p->description);
+ }
+
+ if (i->active_profile)
+ printf(_("\tActive Profile: %s\n"),
+ i->active_profile->name);
pa_xfree(pl);
}
static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
- char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char t[32], k[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 *pl;
if (is_last < 0) {
@@ -349,18 +458,21 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
snprintf(t, sizeof(t), "%u", i->owner_module);
snprintf(k, sizeof(k), "%u", i->client);
- printf(_("*** Sink Input #%u ***\n"
- "Driver: %s\n"
- "Owner Module: %s\n"
- "Client: %s\n"
- "Sink: %u\n"
- "Sample Specification: %s\n"
- "Channel Map: %s\n"
- "Volume: %s\n"
- "Buffer Latency: %0.0f usec\n"
- "Sink Latency: %0.0f usec\n"
- "Resample method: %s\n"
- "Properties:\n%s"),
+ printf(_("Sink Input #%u\n"
+ "\tDriver: %s\n"
+ "\tOwner Module: %s\n"
+ "\tClient: %s\n"
+ "\tSink: %u\n"
+ "\tSample Specification: %s\n"
+ "\tChannel Map: %s\n"
+ "\tMute: %s\n"
+ "\tVolume: %s\n"
+ "\t %s\n"
+ "\t balance %0.2f\n"
+ "\tBuffer Latency: %0.0f usec\n"
+ "\tSink Latency: %0.0f usec\n"
+ "\tResample method: %s\n"
+ "\tProperties:\n\t\t%s\n"),
i->index,
pa_strnull(i->driver),
i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
@@ -368,11 +480,14 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
i->sink,
pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
- i->mute ? _("muted") : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ pa_yes_no(i->mute),
+ pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
+ pa_cvolume_get_balance(&i->volume, &i->channel_map),
(double) i->buffer_usec,
(double) i->sink_usec,
i->resample_method ? i->resample_method : _("n/a"),
- pl = pa_proplist_to_string(i->proplist));
+ pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
pa_xfree(pl);
}
@@ -402,17 +517,17 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
snprintf(t, sizeof(t), "%u", i->owner_module);
snprintf(k, sizeof(k), "%u", i->client);
- printf(_("*** Source Output #%u ***\n"
- "Driver: %s\n"
- "Owner Module: %s\n"
- "Client: %s\n"
- "Source: %u\n"
- "Sample Specification: %s\n"
- "Channel Map: %s\n"
- "Buffer Latency: %0.0f usec\n"
- "Source Latency: %0.0f usec\n"
- "Resample method: %s\n"
- "Properties:\n%s"),
+ printf(_("Source Output #%u\n"
+ "\tDriver: %s\n"
+ "\tOwner Module: %s\n"
+ "\tClient: %s\n"
+ "\tSource: %u\n"
+ "\tSample Specification: %s\n"
+ "\tChannel Map: %s\n"
+ "\tBuffer Latency: %0.0f usec\n"
+ "\tSource Latency: %0.0f usec\n"
+ "\tResample method: %s\n"
+ "\tProperties:\n\t\t%s\n"),
i->index,
pa_strnull(i->driver),
i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
@@ -423,13 +538,13 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
(double) i->buffer_usec,
(double) i->source_usec,
i->resample_method ? i->resample_method : _("n/a"),
- pl = pa_proplist_to_string(i->proplist));
+ pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
pa_xfree(pl);
}
static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
- char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ 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 *pl;
if (is_last < 0) {
@@ -449,63 +564,36 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
printf("\n");
nl = 1;
-
pa_bytes_snprint(t, sizeof(t), i->bytes);
- printf(_("*** Sample #%u ***\n"
- "Name: %s\n"
- "Volume: %s\n"
- "Sample Specification: %s\n"
- "Channel Map: %s\n"
- "Duration: %0.1fs\n"
- "Size: %s\n"
- "Lazy: %s\n"
- "Filename: %s\n"
- "Properties:\n%s"),
+ printf(_("Sample #%u\n"
+ "\tName: %s\n"
+ "\tSample Specification: %s\n"
+ "\tChannel Map: %s\n"
+ "\tVolume: %s\n"
+ "\t %s\n"
+ "\t balance %0.2f\n"
+ "\tDuration: %0.1fs\n"
+ "\tSize: %s\n"
+ "\tLazy: %s\n"
+ "\tFilename: %s\n"
+ "\tProperties:\n\t\t%s\n"),
i->index,
i->name,
- pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : _("n/a"),
pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"),
- (double) i->duration/1000000,
+ pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
+ pa_cvolume_get_balance(&i->volume, &i->channel_map),
+ (double) i->duration/1000000.0,
t,
pa_yes_no(i->lazy),
i->filename ? i->filename : _("n/a"),
- pl = pa_proplist_to_string(i->proplist));
+ pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
pa_xfree(pl);
}
-static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata) {
- if (is_last < 0) {
- fprintf(stderr, _("Failed to get autoload information: %s\n"), pa_strerror(pa_context_errno(c)));
- quit(1);
- return;
- }
-
- if (is_last) {
- complete_action();
- return;
- }
-
- assert(i);
-
- if (nl)
- printf("\n");
- nl = 1;
-
- printf(_("*** Autoload Entry #%u ***\n"
- "Name: %s\n"
- "Type: %s\n"
- "Module: %s\n"
- "Argument: %s\n"),
- i->index,
- i->name,
- i->type == PA_AUTOLOAD_SINK ? _("sink") : _("source"),
- i->module,
- i->argument ? i->argument : "");
-}
-
static void simple_callback(pa_context *c, int success, void *userdata) {
if (!success) {
fprintf(stderr, _("Failure: %s\n"), pa_strerror(pa_context_errno(c)));
@@ -619,7 +707,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
- pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL));
+ pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
break;
case MOVE_SINK_INPUT:
@@ -652,6 +740,10 @@ static void context_state_callback(pa_context *c, void *userdata) {
pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
break;
+ case SET_CARD_PROFILE:
+ pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
+ break;
+
default:
assert(0);
}
@@ -676,22 +768,23 @@ static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig
static void help(const char *argv0) {
printf(_("%s [options] stat\n"
- "%s [options] list\n"
- "%s [options] exit\n"
- "%s [options] upload-sample FILENAME [NAME]\n"
- "%s [options] play-sample NAME [SINK]\n"
- "%s [options] remove-sample NAME\n"
- "%s [options] move-sink-input ID SINK\n"
- "%s [options] move-source-output ID SOURCE\n"
- "%s [options] load-module NAME [ARGS ...]\n"
- "%s [options] unload-module ID\n"
- "%s [options] suspend-sink [SINK] 1|0\n"
- "%s [options] suspend-source [SOURCE] 1|0\n\n"
- " -h, --help Show this help\n"
- " --version Show version\n\n"
- " -s, --server=SERVER The name of the server to connect to\n"
- " -n, --client-name=NAME How to call this client on the server\n"),
- argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
+ "%s [options] list\n"
+ "%s [options] exit\n"
+ "%s [options] upload-sample FILENAME [NAME]\n"
+ "%s [options] play-sample NAME [SINK]\n"
+ "%s [options] remove-sample NAME\n"
+ "%s [options] move-sink-input ID SINK\n"
+ "%s [options] move-source-output ID SOURCE\n"
+ "%s [options] load-module NAME [ARGS ...]\n"
+ "%s [options] unload-module ID\n"
+ "%s [options] suspend-sink [SINK] 1|0\n"
+ "%s [options] suspend-source [SOURCE] 1|0\n"
+ "%s [options] set-card-profile [CARD] [PROFILE] \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);
}
enum { ARG_VERSION = 256 };
@@ -872,7 +965,7 @@ int main(int argc, char *argv[]) {
action = SUSPEND_SINK;
if (argc > optind+3 || optind+1 >= argc) {
- fprintf(stderr, _("You may not specify more than one sink. You have to specify at least one boolean value.\n"));
+ fprintf(stderr, _("You may not specify more than one sink. You have to specify a boolean value.\n"));
goto quit;
}
@@ -885,7 +978,7 @@ int main(int argc, char *argv[]) {
action = SUSPEND_SOURCE;
if (argc > optind+3 || optind+1 >= argc) {
- fprintf(stderr, _("You may not specify more than one source. You have to specify at least one boolean value.\n"));
+ fprintf(stderr, _("You may not specify more than one source. You have to specify a boolean value.\n"));
goto quit;
}
@@ -893,6 +986,17 @@ int main(int argc, char *argv[]) {
if (argc > optind+2)
source_name = pa_xstrdup(argv[optind+1]);
+ } else if (!strcmp(argv[optind], "set-card-profile")) {
+ action = SET_CARD_PROFILE;
+
+ if (argc != optind+3) {
+ fprintf(stderr, _("You have to specify a card name/index and a profile name\n"));
+ goto quit;
+ }
+
+ card_name = pa_xstrdup(argv[optind+1]);
+ profile_name = pa_xstrdup(argv[optind+2]);
+
} else if (!strcmp(argv[optind], "help")) {
help(bn);
ret = 0;
@@ -954,6 +1058,8 @@ quit:
pa_xfree(source_name);
pa_xfree(module_args);
pa_xfree(client_name);
+ pa_xfree(card_name);
+ pa_xfree(profile_name);
return ret;
}