diff options
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 = @@ -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(®ex, 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(¶m, 0, sizeof(param)); param.sched_priority = 12; pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) == 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; } |