summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--po/LINGUAS1
-rw-r--r--po/ca.po1174
-rw-r--r--src/Makefile.am43
-rwxr-xr-xsrc/daemon/default.pa.in18
-rw-r--r--src/map-file5
-rw-r--r--src/modules/bluetooth/bluetooth-util.c864
-rw-r--r--src/modules/bluetooth/bluetooth-util.h81
-rw-r--r--src/modules/bluetooth/ipc.c2
-rw-r--r--src/modules/bluetooth/ipc.h7
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c1829
-rw-r--r--src/modules/bluetooth/module-bluetooth-discover.c839
-rw-r--r--src/modules/bluetooth/rtp.h46
-rw-r--r--src/modules/bluetooth/sbc.c375
-rw-r--r--src/modules/bluetooth/sbc.h1
-rw-r--r--src/modules/bluetooth/sbc_math.h2
-rw-r--r--src/modules/bluetooth/sbc_primitives.c469
-rw-r--r--src/modules/bluetooth/sbc_primitives.h74
-rw-r--r--src/modules/bluetooth/sbc_primitives_mmx.c319
-rw-r--r--src/modules/bluetooth/sbc_primitives_mmx.h40
-rw-r--r--src/modules/bluetooth/sbc_primitives_neon.c245
-rw-r--r--src/modules/bluetooth/sbc_primitives_neon.h40
-rw-r--r--src/modules/bluetooth/sbc_tables.h436
-rw-r--r--src/modules/module-card-restore.c13
-rw-r--r--src/modules/module-combine.c2
-rw-r--r--src/modules/module-device-restore.c15
-rw-r--r--src/modules/module-ladspa-sink.c2
-rw-r--r--src/modules/module-pipe-sink.c4
-rw-r--r--src/modules/module-position-event-sounds.c12
-rw-r--r--src/modules/module-raop-sink.c65
-rw-r--r--src/modules/module-remap-sink.c2
-rw-r--r--src/modules/module-sine.c2
-rw-r--r--src/modules/module-stream-restore.c73
-rw-r--r--src/modules/rtp/module-rtp-recv.c2
-rw-r--r--src/modules/rtp/module-rtp-send.c2
-rw-r--r--src/pulse/channelmap.c106
-rw-r--r--src/pulse/channelmap.h5
-rw-r--r--src/pulse/def.h37
-rw-r--r--src/pulse/error.c4
-rw-r--r--src/pulse/ext-stream-restore.c48
-rw-r--r--src/pulse/ext-stream-restore.h8
-rw-r--r--src/pulse/operation.c1
-rw-r--r--src/pulse/proplist.c384
-rw-r--r--src/pulse/proplist.h6
-rw-r--r--src/pulse/sample.c38
-rw-r--r--src/pulse/sample.h4
-rw-r--r--src/pulse/stream.c8
-rw-r--r--src/pulse/stream.h2
-rw-r--r--src/pulse/utf8.c26
-rw-r--r--src/pulse/utf8.h6
-rw-r--r--src/pulse/volume.c77
-rw-r--r--src/pulse/volume.h29
-rw-r--r--src/pulsecore/cli-text.c8
-rw-r--r--src/pulsecore/core-util.c47
-rw-r--r--src/pulsecore/core-util.h4
-rw-r--r--src/pulsecore/envelope.c8
-rw-r--r--src/pulsecore/idxset.c42
-rw-r--r--src/pulsecore/ltdl-helper.c6
-rw-r--r--src/pulsecore/macro.h16
-rw-r--r--src/pulsecore/modargs.c179
-rw-r--r--src/pulsecore/namereg.c2
-rw-r--r--src/pulsecore/play-memblockq.c4
-rw-r--r--src/pulsecore/proplist-util.c63
-rw-r--r--src/pulsecore/protocol-esound.c4
-rw-r--r--src/pulsecore/protocol-native.c51
-rw-r--r--src/pulsecore/protocol-simple.c4
-rw-r--r--src/pulsecore/resampler.c12
-rw-r--r--src/pulsecore/sink-input.c179
-rw-r--r--src/pulsecore/sink-input.h15
-rw-r--r--src/pulsecore/sink.c80
-rw-r--r--src/pulsecore/sound-file-stream.c4
-rw-r--r--src/pulsecore/source-output.c88
-rw-r--r--src/pulsecore/source-output.h6
-rw-r--r--src/pulsecore/source.c33
-rw-r--r--src/tests/proplist-test.c19
-rw-r--r--src/tests/volume-ui.py76
76 files changed, 5934 insertions, 2911 deletions
diff --git a/configure.ac b/configure.ac
index 3d9cfcd8..779732b9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -98,7 +98,7 @@ if test "x$M4" = xno ; then
fi
dnl Compiler flags
-DESIRED_FLAGS="-Wall -W -Wextra -pedantic -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wpacked -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option"
+DESIRED_FLAGS="-Wall -W -Wextra -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option"
for flag in $DESIRED_FLAGS ; do
CC_CHECK_CFLAGS([$flag], [CFLAGS="$CFLAGS $flag"])
diff --git a/po/LINGUAS b/po/LINGUAS
index a32bf825..b07b017b 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -8,3 +8,4 @@ fr
pl
pt_BR
sv
+zh_CN
diff --git a/po/ca.po b/po/ca.po
index d7f143c0..fa4cb224 100644
--- a/po/ca.po
+++ b/po/ca.po
@@ -19,395 +19,399 @@ msgid ""
msgstr ""
"Project-Id-Version: pulseaudio\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-01-28 04:30+0100\n"
-"PO-Revision-Date: 1008-11-08 HO:MI+ZONE\n"
-"Last-Translator: Xavier Conde Rueda <xavi.conde@gmail.com>\n"
+"POT-Creation-Date: 2008-11-08 01:48+0000\n"
+"PO-Revision-Date: 2009-01-31 06:12+0100\n"
+"Last-Translator: Agustí Grau <fletxa@gmail.com>\n"
"Language-Team: Catalan <fedora@softcatala.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: ../src/daemon/ltdl-bind-now.c:124
-#, fuzzy
-msgid "Failed to find original lt_dlopen loader."
-msgstr "No s'ha trobat el carregador dlopen original."
+#: ../src/daemon/ltdl-bind-now.c:177
+#: ../src/daemon/ltdl-bind-now.c:197
+msgid "Failed to add bind-now-loader."
+msgstr "S'ha produït un error en afegir bind-now-loader."
-#: ../src/daemon/ltdl-bind-now.c:129
-#, fuzzy
-msgid "Failed to allocate new dl loader."
+#: ../src/daemon/ltdl-bind-now.c:184
+msgid "Failed to find original dlopen loader."
msgstr "No s'ha trobat el carregador dlopen original."
-#: ../src/daemon/ltdl-bind-now.c:142
-msgid "Failed to add bind-now-loader."
-msgstr ""
-
#: ../src/daemon/polkit.c:55
#, c-format
msgid "Cannot connect to system bus: %s"
-msgstr ""
+msgstr "No s'ha pogut connectar al bus del sistema: %s"
#: ../src/daemon/polkit.c:65
#, c-format
msgid "Cannot get caller from PID: %s"
-msgstr ""
+msgstr "No s'ha pogut obtenir el cridador amb el PID: %s"
#: ../src/daemon/polkit.c:77
msgid "Cannot set UID on caller object."
-msgstr ""
+msgstr "No s'ha pogut especificar l'UID en l'objecte cridador."
+# CK: ConsoleKit
#: ../src/daemon/polkit.c:82
msgid "Failed to get CK session."
-msgstr ""
+msgstr "S'ha produït un error en obtenir la sessió del CK."
#: ../src/daemon/polkit.c:90
msgid "Cannot set UID on session object."
-msgstr ""
+msgstr "No s'ha pogut especificar l'UID en l'objecte sessió."
#: ../src/daemon/polkit.c:95
msgid "Cannot allocate PolKitAction."
-msgstr ""
+msgstr "No s'ha pogut allotjar PolKitAction"
#: ../src/daemon/polkit.c:100
msgid "Cannot set action_id"
-msgstr ""
+msgstr "No s'ha pogut especificar action_id"
#: ../src/daemon/polkit.c:105
msgid "Cannot allocate PolKitContext."
-msgstr ""
+msgstr "No s'ha pogut allotjar PolKitContext"
#: ../src/daemon/polkit.c:110
#, c-format
msgid "Cannot initialize PolKitContext: %s"
-msgstr ""
+msgstr "No s'ha pogut inicialitzar PolKitContext: %s"
#: ../src/daemon/polkit.c:119
#, c-format
msgid "Could not determine whether caller is authorized: %s"
-msgstr ""
+msgstr "S'ha produït un error en determinar si el cridador està autoritzada: %s"
#: ../src/daemon/polkit.c:139
#, c-format
msgid "Cannot obtain auth: %s"
-msgstr ""
+msgstr "No s'ha pogut obtenir autorització: %s"
#: ../src/daemon/polkit.c:148
#, c-format
msgid "PolicyKit responded with '%s'"
-msgstr ""
+msgstr "PolicyKit ha respost '%s'"
#: ../src/daemon/main.c:134
#, c-format
msgid "Got signal %s."
-msgstr ""
+msgstr "S'ha obtingut la senyal %s."
#: ../src/daemon/main.c:161
msgid "Exiting."
-msgstr ""
+msgstr "S'està sortint."
#: ../src/daemon/main.c:179
#, c-format
msgid "Failed to find user '%s'."
-msgstr ""
+msgstr "S'ha produït un error en trobar l'usuari '%s'."
#: ../src/daemon/main.c:184
#, c-format
msgid "Failed to find group '%s'."
-msgstr ""
+msgstr "S'ha produït un error en trobar el grup '%s'."
#: ../src/daemon/main.c:188
#, c-format
msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
-msgstr ""
+msgstr "S'han trobat l'usuari '%s' (UID %lu) i el grup '%s' (GID %lu)."
#: ../src/daemon/main.c:193
#, c-format
msgid "GID of user '%s' and of group '%s' don't match."
-msgstr ""
+msgstr "El GID de l'usuari '%s' i del grup '%s' no coincideixen"
#: ../src/daemon/main.c:198
#, c-format
msgid "Home directory of user '%s' is not '%s', ignoring."
-msgstr ""
+msgstr "El directori d'inici de l'usuari '%s' no és '%s', s'ignorarà."
-#: ../src/daemon/main.c:201 ../src/daemon/main.c:206
+#: ../src/daemon/main.c:201
+#: ../src/daemon/main.c:206
#, c-format
msgid "Failed to create '%s': %s"
-msgstr ""
+msgstr "S'ha produït un error durant la creació de '%s': %s"
#: ../src/daemon/main.c:213
#, c-format
msgid "Failed to change group list: %s"
-msgstr ""
+msgstr "S'ha produït un error en canviar la llista del grup :%s"
#: ../src/daemon/main.c:229
#, c-format
msgid "Failed to change GID: %s"
-msgstr ""
+msgstr "S'ha produït un error en canviar el GID: %s"
#: ../src/daemon/main.c:245
#, c-format
msgid "Failed to change UID: %s"
-msgstr ""
+msgstr "S'ha produït un error en canviar l'UID: %s"
#: ../src/daemon/main.c:259
msgid "Successfully dropped root privileges."
-msgstr ""
+msgstr "S'han lliberat els permissos de root amb éxit."
#: ../src/daemon/main.c:267
msgid "System wide mode unsupported on this platform."
-msgstr ""
+msgstr "El mode de sistema no està disponible en aquesta plataforma."
#: ../src/daemon/main.c:285
#, c-format
msgid "setrlimit(%s, (%u, %u)) failed: %s"
-msgstr ""
+msgstr "S'ha produït un error en setrlimit(%s, (%u, %u)): %s"
-#: ../src/daemon/main.c:429
+#: ../src/daemon/main.c:425
msgid "Failed to parse command line."
-msgstr ""
+msgstr "S'ha produït un error en interpretar la línia d'ordres."
-#: ../src/daemon/main.c:451
+#: ../src/daemon/main.c:441
#, c-format
msgid "We're in the group '%s', allowing high-priority scheduling."
-msgstr ""
+msgstr "Aquesta aplicació està en el grup '%s', s'està establint la planificació amb prioritat alta."
-#: ../src/daemon/main.c:458
+#: ../src/daemon/main.c:448
#, c-format
msgid "We're in the group '%s', allowing real-time scheduling."
-msgstr ""
+msgstr "Aquesta aplicació està en el grup '%s', s'està establint la planificació amb prioritat en temps real."
-#: ../src/daemon/main.c:466
+#: ../src/daemon/main.c:456
msgid "PolicyKit grants us acquire-high-priority privilege."
-msgstr ""
+msgstr "PolicyKit ha permés el permís acquire-high-priority."
-#: ../src/daemon/main.c:469
+#: ../src/daemon/main.c:459
msgid "PolicyKit refuses acquire-high-priority privilege."
-msgstr ""
+msgstr "PolicyKit ha rebutjat el permís acquire-high-priority."
-#: ../src/daemon/main.c:474
+#: ../src/daemon/main.c:464
msgid "PolicyKit grants us acquire-real-time privilege."
-msgstr ""
+msgstr "PolicyKit ha permés el permís acquire-real-time."
-#: ../src/daemon/main.c:477
+#: ../src/daemon/main.c:467
msgid "PolicyKit refuses acquire-real-time privilege."
-msgstr ""
+msgstr "PolicyKit ha rebutjat el permís acquire-real-time."
-#: ../src/daemon/main.c:506
+#: ../src/daemon/main.c:479
msgid ""
-"Called SUID root and real-time and/or high-priority scheduling was requested "
-"in the configuration. However, we lack the necessary privileges:\n"
+"Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n"
"We are not in group '"
msgstr ""
+"S'han especificat la crida SUID root i la planificació amb prioritat alta/temps real en la configuració, però no té els permissos necessaris:\n"
+"No es pertany al grup '"
-#: ../src/daemon/main.c:530
+#: ../src/daemon/main.c:480
msgid ""
-"High-priority scheduling enabled in configuration but not allowed by policy."
+"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
+"For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"
msgstr ""
+"' i PolicyKit ha denegat els permissos. S'està lliberant SUID. \n"
+"Per habilitar la prioritat en temps real, s'ha d'adquirir els permissos de PolicyKit, o pertanyer a '"
+
+#: ../src/daemon/main.c:481
+msgid "', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."
+msgstr "', o incrementar els límits de recursos RLIMIT_NICE/RLIMIT_RTPRIO per a aquest usuari."
+
+#: ../src/daemon/main.c:497
+msgid "High-priority scheduling enabled in configuration but not allowed by policy."
+msgstr "La prioritat alta està habilitada en la configuració però no està permesa per la política."
-#: ../src/daemon/main.c:559
+#: ../src/daemon/main.c:522
msgid "Successfully increased RLIMIT_RTPRIO"
-msgstr ""
+msgstr "S'ha incrementat el valor de RLIMIT_RTPRIO amb éxit."
-#: ../src/daemon/main.c:562
+#: ../src/daemon/main.c:525
#, c-format
msgid "RLIMIT_RTPRIO failed: %s"
-msgstr ""
+msgstr "S'ha produït un error amb RLIMIT_RTPRIO: %s"
-#: ../src/daemon/main.c:569
+#: ../src/daemon/main.c:532
msgid "Giving up CAP_NICE"
-msgstr ""
+msgstr "No es continuarà amb CAP_NICE"
-#: ../src/daemon/main.c:576
-msgid ""
-"Real-time scheduling enabled in configuration but not allowed by policy."
-msgstr ""
+#: ../src/daemon/main.c:539
+msgid "Real-time scheduling enabled in configuration but not allowed by policy."
+msgstr "La prioritat de planificació de temps real està habilitada en la configuració però no està permesa per la política."
-#: ../src/daemon/main.c:637
+#: ../src/daemon/main.c:597
msgid "Daemon not running"
-msgstr ""
+msgstr "El dimoni no s'està executant."
-#: ../src/daemon/main.c:639
+#: ../src/daemon/main.c:599
#, c-format
msgid "Daemon running as PID %u"
-msgstr ""
+msgstr "El dimoni s'està executant amb PID %u"
-#: ../src/daemon/main.c:649
+#: ../src/daemon/main.c:609
#, c-format
msgid "Failed to kill daemon: %s"
-msgstr ""
+msgstr "S'ha produït un error en matar el dimoni: %s"
-#: ../src/daemon/main.c:667
-msgid ""
-"This program is not intended to be run as root (unless --system is "
-"specified)."
-msgstr ""
+#: ../src/daemon/main.c:627
+msgid "This program is not intended to be run as root (unless --system is specified)."
+msgstr "No és necessari executar aquesta aplicació com a root (excepte si s'especifica --system)"
-#: ../src/daemon/main.c:669
-msgid "Root privileges required."
-msgstr ""
+#: ../src/daemon/main.c:629
+msgid "Root priviliges required."
+msgstr "Es requereixen permisos de root."
-#: ../src/daemon/main.c:674
+#: ../src/daemon/main.c:634
msgid "--start not supported for system instances."
-msgstr ""
+msgstr "L'opció --start no està disponible per a instàncies de sistema."
-#: ../src/daemon/main.c:679
+#: ../src/daemon/main.c:639
msgid "Running in system mode, but --disallow-exit not set!"
-msgstr ""
+msgstr "S'està executant en mode sistema, però no s'ha especificat l'opció --disallow-exit."
-#: ../src/daemon/main.c:682
+#: ../src/daemon/main.c:642
msgid "Running in system mode, but --disallow-module-loading not set!"
-msgstr ""
+msgstr "S'està executant en mode sistema, però no s'ha especificat l'opció --disallow-module-loading."
-#: ../src/daemon/main.c:685
+#: ../src/daemon/main.c:645
msgid "Running in system mode, forcibly disabling SHM mode!"
-msgstr ""
+msgstr "S'està executant en mode sistema, es forçarà la inhabilitació del mode SHM"
-#: ../src/daemon/main.c:690
+#: ../src/daemon/main.c:650
msgid "Running in system mode, forcibly disabling exit idle time!"
-msgstr ""
+msgstr "S'està executant en mode sistema, la sortida per temps d'inactivitat es deshabilita."
-#: ../src/daemon/main.c:717
+#: ../src/daemon/main.c:677
msgid "Failed to acquire stdio."
-msgstr ""
+msgstr "S'ha produït un error en adquirir stdio."
-#: ../src/daemon/main.c:723
+#: ../src/daemon/main.c:683
#, c-format
msgid "pipe failed: %s"
-msgstr ""
+msgstr "Ha fallat la canonada: %s"
-#: ../src/daemon/main.c:728
+#: ../src/daemon/main.c:688
#, c-format
msgid "fork() failed: %s"
-msgstr ""
+msgstr "Ha fallat fork(): %s"
-#: ../src/daemon/main.c:742
+#: ../src/daemon/main.c:702
#, c-format
msgid "read() failed: %s"
-msgstr ""
+msgstr "Ha fallat read(): %s"
-#: ../src/daemon/main.c:748
+#: ../src/daemon/main.c:708
msgid "Daemon startup failed."
-msgstr ""
+msgstr "S'ha produït un error en iniciar el dimoni."
-#: ../src/daemon/main.c:750
+#: ../src/daemon/main.c:710
msgid "Daemon startup successful."
-msgstr ""
+msgstr "S'ha iniciat el dimoni."
-#: ../src/daemon/main.c:820
+#: ../src/daemon/main.c:780
#, c-format
msgid "This is PulseAudio %s"
-msgstr ""
+msgstr "Aquest és el PulseAudio %s"
-#: ../src/daemon/main.c:821
+#: ../src/daemon/main.c:781
#, c-format
msgid "Compilation host: %s"
-msgstr ""
+msgstr "Host de compilació: %s"
-#: ../src/daemon/main.c:822
+#: ../src/daemon/main.c:782
#, c-format
msgid "Compilation CFLAGS: %s"
-msgstr ""
+msgstr "CFLAGS de compilació: %s"
-#: ../src/daemon/main.c:825
+#: ../src/daemon/main.c:785
#, c-format
msgid "Running on host: %s"
-msgstr ""
+msgstr "S'està executant en el host: %s"
-#: ../src/daemon/main.c:828
-#, c-format
-msgid "Found %u CPUs."
-msgstr ""
-
-#: ../src/daemon/main.c:830
+#: ../src/daemon/main.c:788
#, c-format
msgid "Page size is %lu bytes"
-msgstr ""
+msgstr "La mida de pàgina és de %lu bytes."
-#: ../src/daemon/main.c:833
+#: ../src/daemon/main.c:791
msgid "Compiled with Valgrind support: yes"
-msgstr ""
+msgstr "Compilat amb suport per a Valgrind: sí"
-#: ../src/daemon/main.c:835
+#: ../src/daemon/main.c:793
msgid "Compiled with Valgrind support: no"
-msgstr ""
+msgstr "Compilat amb suport per a Valgrind: no"
-#: ../src/daemon/main.c:838
+#: ../src/daemon/main.c:796
#, c-format
msgid "Running in valgrind mode: %s"
-msgstr ""
+msgstr "S'està executant amb el mode valgrind: %s"
-#: ../src/daemon/main.c:841
+#: ../src/daemon/main.c:799
msgid "Optimized build: yes"
-msgstr ""
+msgstr "Construcció optimitzada: sí"
-#: ../src/daemon/main.c:843
+#: ../src/daemon/main.c:801
msgid "Optimized build: no"
-msgstr ""
+msgstr "Construcció optmitzada: no"
-#: ../src/daemon/main.c:847
+#: ../src/daemon/main.c:805
msgid "Failed to get machine ID"
-msgstr ""
+msgstr "S'ha produït un error en obtenir l'ID de la màquina"
-#: ../src/daemon/main.c:850
+#: ../src/daemon/main.c:808
#, c-format
msgid "Machine ID is %s."
-msgstr ""
+msgstr "La ID de la màquina és %s."
-#: ../src/daemon/main.c:855
+#: ../src/daemon/main.c:813
#, c-format
msgid "Using runtime directory %s."
-msgstr ""
+msgstr "S'esta utilitzant el directori d'execució %s."
-#: ../src/daemon/main.c:860
+#: ../src/daemon/main.c:818
#, c-format
msgid "Using state directory %s."
-msgstr ""
+msgstr "S'està utilitzant el directori d'estat %s."
-#: ../src/daemon/main.c:863
+#: ../src/daemon/main.c:821
#, c-format
msgid "Running in system mode: %s"
-msgstr ""
+msgstr "S'està executant en mode sistema: %s"
-#: ../src/daemon/main.c:878
+#: ../src/daemon/main.c:836
msgid "pa_pid_file_create() failed."
-msgstr ""
+msgstr "S'ha produït un error en pa_pid_file_create()."
-#: ../src/daemon/main.c:890
+#: ../src/daemon/main.c:848
msgid "Fresh high-resolution timers available! Bon appetit!"
-msgstr ""
+msgstr "Estan disponibles els temporitzadors d'alta resolució."
-#: ../src/daemon/main.c:892
-msgid ""
-"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
-"resolution timers enabled!"
-msgstr ""
+#: ../src/daemon/main.c:850
+msgid "Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!"
+msgstr "Es recomana la utilització d'un nucli amb els temporitzadors d'alta resolució habilitats."
-#: ../src/daemon/main.c:904
+#: ../src/daemon/main.c:860
msgid "pa_core_new() failed."
-msgstr ""
+msgstr "S'ha produït un error en pa_core_new()."
-#: ../src/daemon/main.c:965
+#: ../src/daemon/main.c:921
msgid "Failed to initialize daemon."
-msgstr ""
+msgstr "S'ha produït un error en inicialitzar el dimoni."
-#: ../src/daemon/main.c:970
+#: ../src/daemon/main.c:926
msgid "Daemon startup without any loaded modules, refusing to work."
-msgstr ""
+msgstr "El dimoni s'ha iniciat sense cap mòdul carregat, no funcionarà."
+
+#: ../src/daemon/main.c:931
+#, c-format
+msgid "Default sink name (%s) does not exist in name register."
+msgstr "El nom per omissió del conducte (%s) no existeix en el registre de noms."
-#: ../src/daemon/main.c:983
+#: ../src/daemon/main.c:944
msgid "Daemon startup complete."
-msgstr ""
+msgstr "S'ha completat la inicialització del dimoni."
-#: ../src/daemon/main.c:989
+#: ../src/daemon/main.c:950
msgid "Daemon shutdown initiated."
-msgstr ""
+msgstr "S'ha iniciat l'aturada del dimoni."
-#: ../src/daemon/main.c:1010
+#: ../src/daemon/main.c:971
msgid "Daemon terminated."
-msgstr ""
+msgstr "S'ha aturat el dimoni."
-#: ../src/daemon/cmdline.c:121
-#, c-format
+#: ../src/daemon/cmdline.c:117
+#, fuzzy, c-format
msgid ""
"%s [options]\n"
"\n"
@@ -417,48 +421,34 @@ msgid ""
" --dump-conf Dump default configuration\n"
" --dump-modules Dump list of available modules\n"
" --dump-resample-methods Dump available resample methods\n"
-" --cleanup-shm Cleanup stale shared memory "
-"segments\n"
-" --start Start the daemon if it is not "
-"running\n"
+" --cleanup-shm Cleanup stale shared memory segments\n"
+" --start Start the daemon if it is not running\n"
" -k --kill Kill a running daemon\n"
-" --check Check for a running daemon (only "
-"returns exit code)\n"
+" --check Check for a running daemon\n"
"\n"
"OPTIONS:\n"
" --system[=BOOL] Run as system-wide instance\n"
" -D, --daemonize[=BOOL] Daemonize after startup\n"
" --fail[=BOOL] Quit when startup fails\n"
" --high-priority[=BOOL] Try to set high nice level\n"
-" (only available as root, when SUID "
-"or\n"
+" (only available as root, when SUID or\n"
" with elevated RLIMIT_NICE)\n"
" --realtime[=BOOL] Try to enable realtime scheduling\n"
-" (only available as root, when SUID "
-"or\n"
+" (only available as root, when SUID or\n"
" with elevated RLIMIT_RTPRIO)\n"
-" --disallow-module-loading[=BOOL] Disallow module user requested "
-"module\n"
+" --disallow-module-loading[=BOOL] Disallow module user requested module\n"
" loading/unloading after startup\n"
" --disallow-exit[=BOOL] Disallow user requested exit\n"
-" --exit-idle-time=SECS Terminate the daemon when idle and "
-"this\n"
+" --exit-idle-time=SECS Terminate the daemon when idle and this\n"
" time passed\n"
-" --module-idle-time=SECS Unload autoloaded modules when idle "
-"and\n"
+" --module-idle-time=SECS Unload autoloaded modules when idle and\n"
" this time passed\n"
-" --scache-idle-time=SECS Unload autoloaded samples when idle "
-"and\n"
+" --scache-idle-time=SECS Unload autoloaded samples when idle and\n"
" this time passed\n"
" --log-level[=LEVEL] Increase or set verbosity level\n"
" -v Increase the verbosity level\n"
" --log-target={auto,syslog,stderr} Specify the log target\n"
-" --log-meta[=BOOL] Include code location in log "
-"messages\n"
-" --log-time[=BOOL] Include timestamps in log messages\n"
-" --log-backtrace=FRAMES Include a backtrace in log messages\n"
-" -p, --dl-search-path=PATH Set the search path for dynamic "
-"shared\n"
+" -p, --dl-search-path=PATH Set the search path for dynamic shared\n"
" objects (plugins)\n"
" --resample-method=METHOD Use the specified resampling method\n"
" (See --dump-resample-methods for\n"
@@ -469,79 +459,120 @@ msgid ""
" --disable-shm[=BOOL] Disable shared memory support.\n"
"\n"
"STARTUP SCRIPT:\n"
-" -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module "
-"with\n"
+" -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with\n"
" the specified argument\n"
" -F, --file=FILENAME Run the specified script\n"
-" -C Open a command line on the running "
-"TTY\n"
+" -C Open a command line on the running TTY\n"
" after startup\n"
"\n"
" -n Don't load default script file\n"
msgstr ""
+"%s [opcions]\n"
+"\n"
+"ORDRES:\n"
+" -h, --help Mostra aquesta ajuda\n"
+" --version Mostra la versió\n"
+" --dump-conf Volca la configuració per omissió\n"
+" --dump-modules Volca la llista de mòduls\n"
+" --dump-resample-methods Volca els mètodes disponibles de remostratge\n"
+" --cleanup-shm Neteja els segments de memòria compartida no emprats\n"
+" --start Inicia el dimoni si no està corrent\n"
+" -k --kill Mata el dimoni en execució\n"
+" --check Comprova l'execució del dimoni\n"
+"\n"
+"OPCIONS:\n"
+" --system[=BOOL] Executa com a instància de sistema\n"
+" -D, --daemonize[=BOOL] Converteix en dimoni després de la inicialització\n"
+" --fail[=BOOL] Surt quan falli la inicialització\n"
+" --high-priority[=BOOL] Prova d'establir un nivell\n"
+" de prioritat alt (només disponible com a root,\n"
+" amb SUID o amb un elevat RLIMIT_NICE)\n"
+" --realtime[=BOOL] Try to enable realtime scheduling\n"
+" (only available as root, when SUID or\n"
+" with elevated RLIMIT_RTPRIO)\n"
+" --disallow-module-loading[=BOOL] Disallow module user requested module\n"
+" loading/unloading after startup\n"
+" --disallow-exit[=BOOL] Disallow user requested exit\n"
+" --exit-idle-time=SECS Terminate the daemon when idle and this\n"
+" time passed\n"
+" --module-idle-time=SECS Unload autoloaded modules when idle and\n"
+" this time passed\n"
+" --scache-idle-time=SECS Unload autoloaded samples when idle and\n"
+" this time passed\n"
+" --log-level[=LEVEL] Increase or set verbosity level\n"
+" -v Increase the verbosity level\n"
+" --log-target={auto,syslog,stderr} Specify the log target\n"
+" -p, --dl-search-path=PATH Set the search path for dynamic shared\n"
+" objects (plugins)\n"
+" --resample-method=METHOD Use the specified resampling method\n"
+" (See --dump-resample-methods for\n"
+" possible values)\n"
+" --use-pid-file[=BOOL] Create a PID file\n"
+" --no-cpu-limit[=BOOL] Do not install CPU load limiter on\n"
+" platforms that support it.\n"
+" --disable-shm[=BOOL] Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+" -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with\n"
+" the specified argument\n"
+" -F, --file=FILENAME Run the specified script\n"
+" -C Open a command line on the running TTY\n"
+" after startup\n"
+"\n"
+" -n Don't load default script file\n"
-#: ../src/daemon/cmdline.c:252
+#: ../src/daemon/cmdline.c:245
msgid "--daemonize expects boolean argument"
-msgstr ""
+msgstr "--daemonize necessita un argument booleà"
-#: ../src/daemon/cmdline.c:259
+#: ../src/daemon/cmdline.c:252
msgid "--fail expects boolean argument"
-msgstr ""
+msgstr "--fail necessita un argument booleà"
-#: ../src/daemon/cmdline.c:269
-msgid ""
-"--log-level expects log level argument (either numeric in range 0..4 or one "
-"of debug, info, notice, warn, error)."
-msgstr ""
+#: ../src/daemon/cmdline.c:262
+msgid "--log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error)."
+msgstr "--log-level necessita un argument de nivell de log (valor númeric 0..4 o 'debug', 'info', 'notice', 'warn', 'error')."
-#: ../src/daemon/cmdline.c:281
+#: ../src/daemon/cmdline.c:274
msgid "--high-priority expects boolean argument"
-msgstr ""
+msgstr "--high-priority necessita un argument booleà"
-#: ../src/daemon/cmdline.c:288
+#: ../src/daemon/cmdline.c:281
msgid "--realtime expects boolean argument"
-msgstr ""
+msgstr "--realtime necessita un argument booleà"
-#: ../src/daemon/cmdline.c:295
+#: ../src/daemon/cmdline.c:288
msgid "--disallow-module-loading expects boolean argument"
-msgstr ""
+msgstr "--disallow-module-loading necessita un argument booleà"
-#: ../src/daemon/cmdline.c:302
+#: ../src/daemon/cmdline.c:295
msgid "--disallow-exit boolean argument"
-msgstr ""
+msgstr "--disallow-exit necessita un argument booleà"
-#: ../src/daemon/cmdline.c:309
+#: ../src/daemon/cmdline.c:302
msgid "--use-pid-file expects boolean argument"
-msgstr ""
+msgstr "--use-pid-file necessita un argument booleà"
-#: ../src/daemon/cmdline.c:326
+#: ../src/daemon/cmdline.c:319
msgid "Invalid log target: use either 'syslog', 'stderr' or 'auto'."
-msgstr ""
-
-#: ../src/daemon/cmdline.c:333
-msgid "--log-time boolean argument"
-msgstr ""
-
-#: ../src/daemon/cmdline.c:340
-msgid "--log-meta boolean argument"
-msgstr ""
+msgstr "Objectiu de registre invàlid: utilitzeu 'syslog', 'stderr' o 'auto'."
-#: ../src/daemon/cmdline.c:359
+#: ../src/daemon/cmdline.c:338
#, c-format
msgid "Invalid resample method '%s'."
-msgstr ""
+msgstr "Mètode de remostratge invàlid '%s'."
-#: ../src/daemon/cmdline.c:366
+#: ../src/daemon/cmdline.c:345
msgid "--system expects boolean argument"
-msgstr ""
+msgstr "--system necessita un argument booleà"
-#: ../src/daemon/cmdline.c:373
+#: ../src/daemon/cmdline.c:352
msgid "--no-cpu-limit expects boolean argument"
-msgstr ""
+msgstr "--no-cpu-limit necessita un argument booleà"
-#: ../src/daemon/cmdline.c:380
+#: ../src/daemon/cmdline.c:359
msgid "--disable-shm expects boolean argument"
-msgstr ""
+msgstr "--disable-shm necessita un argument booleà"
#: ../src/daemon/dumpmodules.c:60
#, c-format
@@ -583,313 +614,290 @@ msgstr ""
msgid "Path: %s\n"
msgstr ""
-#: ../src/daemon/daemon-conf.c:212
+#: ../src/daemon/daemon-conf.c:205
#, c-format
msgid "[%s:%u] Invalid log target '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:228
+#: ../src/daemon/daemon-conf.c:221
#, c-format
msgid "[%s:%u] Invalid log level '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:244
+#: ../src/daemon/daemon-conf.c:237
#, c-format
msgid "[%s:%u] Invalid resample method '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:267
+#: ../src/daemon/daemon-conf.c:260
#, c-format
msgid "[%s:%u] Invalid rlimit '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:274
+#: ../src/daemon/daemon-conf.c:267
#, c-format
msgid "[%s:%u] rlimit not supported on this platform."
msgstr ""
-#: ../src/daemon/daemon-conf.c:290
+#: ../src/daemon/daemon-conf.c:283
#, c-format
msgid "[%s:%u] Invalid sample format '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:308
+#: ../src/daemon/daemon-conf.c:301
#, c-format
msgid "[%s:%u] Invalid sample rate '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:326
+#: ../src/daemon/daemon-conf.c:319
#, c-format
msgid "[%s:%u] Invalid sample channels '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:344
+#: ../src/daemon/daemon-conf.c:337
#, c-format
msgid "[%s:%u] Invalid number of fragments '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:362
+#: ../src/daemon/daemon-conf.c:355
#, c-format
msgid "[%s:%u] Invalid fragment size '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:380
+#: ../src/daemon/daemon-conf.c:373
#, c-format
msgid "[%s:%u] Invalid nice level '%s'."
msgstr ""
-#: ../src/daemon/daemon-conf.c:566
+#: ../src/daemon/daemon-conf.c:570
#, c-format
msgid "Failed to open configuration file: %s"
msgstr ""
-#: ../src/daemon/daemon-conf.c:640
+#: ../src/daemon/daemon-conf.c:644
#, c-format
msgid "### Read from configuration file: %s ###\n"
msgstr ""
#: ../src/daemon/caps.c:63
-msgid "Dropping root privileges."
+msgid "Dropping root priviliges."
msgstr ""
#: ../src/daemon/caps.c:103
msgid "Limited capabilities successfully to CAP_SYS_NICE."
msgstr ""
-#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:742
+#: ../src/pulse/channelmap.c:102
msgid "Mono"
msgstr ""
-#: ../src/pulse/channelmap.c:105
+#: ../src/pulse/channelmap.c:104
msgid "Front Center"
msgstr ""
-#: ../src/pulse/channelmap.c:106
+#: ../src/pulse/channelmap.c:105
msgid "Front Left"
msgstr ""
-#: ../src/pulse/channelmap.c:107
+#: ../src/pulse/channelmap.c:106
msgid "Front Right"
msgstr ""
-#: ../src/pulse/channelmap.c:109
+#: ../src/pulse/channelmap.c:108
msgid "Rear Center"
msgstr ""
-#: ../src/pulse/channelmap.c:110
+#: ../src/pulse/channelmap.c:109
msgid "Rear Left"
msgstr ""
-#: ../src/pulse/channelmap.c:111
+#: ../src/pulse/channelmap.c:110
msgid "Rear Right"
msgstr ""
-#: ../src/pulse/channelmap.c:113
+#: ../src/pulse/channelmap.c:112
msgid "Low Frequency Emmiter"
msgstr ""
-#: ../src/pulse/channelmap.c:115
+#: ../src/pulse/channelmap.c:114
msgid "Front Left-of-center"
msgstr ""
-#: ../src/pulse/channelmap.c:116
+#: ../src/pulse/channelmap.c:115
msgid "Front Right-of-center"
msgstr ""
-#: ../src/pulse/channelmap.c:118
+#: ../src/pulse/channelmap.c:117
msgid "Side Left"
msgstr ""
-#: ../src/pulse/channelmap.c:119
+#: ../src/pulse/channelmap.c:118
msgid "Side Right"
msgstr ""
-#: ../src/pulse/channelmap.c:121
+#: ../src/pulse/channelmap.c:120
msgid "Auxiliary 0"
msgstr ""
-#: ../src/pulse/channelmap.c:122
+#: ../src/pulse/channelmap.c:121
msgid "Auxiliary 1"
msgstr ""
-#: ../src/pulse/channelmap.c:123
+#: ../src/pulse/channelmap.c:122
msgid "Auxiliary 2"
msgstr ""
-#: ../src/pulse/channelmap.c:124
+#: ../src/pulse/channelmap.c:123
msgid "Auxiliary 3"
msgstr ""
-#: ../src/pulse/channelmap.c:125
+#: ../src/pulse/channelmap.c:124
msgid "Auxiliary 4"
msgstr ""
-#: ../src/pulse/channelmap.c:126
+#: ../src/pulse/channelmap.c:125
msgid "Auxiliary 5"
msgstr ""
-#: ../src/pulse/channelmap.c:127
+#: ../src/pulse/channelmap.c:126
msgid "Auxiliary 6"
msgstr ""
-#: ../src/pulse/channelmap.c:128
+#: ../src/pulse/channelmap.c:127
msgid "Auxiliary 7"
msgstr ""
-#: ../src/pulse/channelmap.c:129
+#: ../src/pulse/channelmap.c:128
msgid "Auxiliary 8"
msgstr ""
-#: ../src/pulse/channelmap.c:130
+#: ../src/pulse/channelmap.c:129
msgid "Auxiliary 9"
msgstr ""
-#: ../src/pulse/channelmap.c:131
+#: ../src/pulse/channelmap.c:130
msgid "Auxiliary 10"
msgstr ""
-#: ../src/pulse/channelmap.c:132
+#: ../src/pulse/channelmap.c:131
msgid "Auxiliary 11"
msgstr ""
-#: ../src/pulse/channelmap.c:133
+#: ../src/pulse/channelmap.c:132
msgid "Auxiliary 12"
msgstr ""
-#: ../src/pulse/channelmap.c:134
+#: ../src/pulse/channelmap.c:133
msgid "Auxiliary 13"
msgstr ""
-#: ../src/pulse/channelmap.c:135
+#: ../src/pulse/channelmap.c:134
msgid "Auxiliary 14"
msgstr ""
-#: ../src/pulse/channelmap.c:136
+#: ../src/pulse/channelmap.c:135
msgid "Auxiliary 15"
msgstr ""
-#: ../src/pulse/channelmap.c:137
+#: ../src/pulse/channelmap.c:136
msgid "Auxiliary 16"
msgstr ""
-#: ../src/pulse/channelmap.c:138
+#: ../src/pulse/channelmap.c:137
msgid "Auxiliary 17"
msgstr ""
-#: ../src/pulse/channelmap.c:139
+#: ../src/pulse/channelmap.c:138
msgid "Auxiliary 18"
msgstr ""
-#: ../src/pulse/channelmap.c:140
+#: ../src/pulse/channelmap.c:139
msgid "Auxiliary 19"
msgstr ""
-#: ../src/pulse/channelmap.c:141
+#: ../src/pulse/channelmap.c:140
msgid "Auxiliary 20"
msgstr ""
-#: ../src/pulse/channelmap.c:142
+#: ../src/pulse/channelmap.c:141
msgid "Auxiliary 21"
msgstr ""
-#: ../src/pulse/channelmap.c:143
+#: ../src/pulse/channelmap.c:142
msgid "Auxiliary 22"
msgstr ""
-#: ../src/pulse/channelmap.c:144
+#: ../src/pulse/channelmap.c:143
msgid "Auxiliary 23"
msgstr ""
-#: ../src/pulse/channelmap.c:145
+#: ../src/pulse/channelmap.c:144
msgid "Auxiliary 24"
msgstr ""
-#: ../src/pulse/channelmap.c:146
+#: ../src/pulse/channelmap.c:145
msgid "Auxiliary 25"
msgstr ""
-#: ../src/pulse/channelmap.c:147
+#: ../src/pulse/channelmap.c:146
msgid "Auxiliary 26"
msgstr ""
-#: ../src/pulse/channelmap.c:148
+#: ../src/pulse/channelmap.c:147
msgid "Auxiliary 27"
msgstr ""
-#: ../src/pulse/channelmap.c:149
+#: ../src/pulse/channelmap.c:148
msgid "Auxiliary 28"
msgstr ""
-#: ../src/pulse/channelmap.c:150
+#: ../src/pulse/channelmap.c:149
msgid "Auxiliary 29"
msgstr ""
-#: ../src/pulse/channelmap.c:151
+#: ../src/pulse/channelmap.c:150
msgid "Auxiliary 30"
msgstr ""
-#: ../src/pulse/channelmap.c:152
+#: ../src/pulse/channelmap.c:151
msgid "Auxiliary 31"
msgstr ""
-#: ../src/pulse/channelmap.c:154
+#: ../src/pulse/channelmap.c:153
msgid "Top Center"
msgstr ""
-#: ../src/pulse/channelmap.c:156
+#: ../src/pulse/channelmap.c:155
msgid "Top Front Center"
msgstr ""
-#: ../src/pulse/channelmap.c:157
+#: ../src/pulse/channelmap.c:156
msgid "Top Front Left"
msgstr ""
-#: ../src/pulse/channelmap.c:158
+#: ../src/pulse/channelmap.c:157
msgid "Top Front Right"
msgstr ""
-#: ../src/pulse/channelmap.c:160
+#: ../src/pulse/channelmap.c:159
msgid "Top Rear Center"
msgstr ""
-#: ../src/pulse/channelmap.c:161
+#: ../src/pulse/channelmap.c:160
msgid "Top Rear Left"
msgstr ""
-#: ../src/pulse/channelmap.c:162
+#: ../src/pulse/channelmap.c:161
msgid "Top Rear Right"
msgstr ""
-#: ../src/pulse/channelmap.c:473 ../src/pulse/sample.c:152
-#: ../src/pulse/volume.c:163 ../src/pulse/volume.c:189
-#: ../src/pulse/volume.c:209 ../src/pulse/volume.c:239
+#: ../src/pulse/channelmap.c:472
+#: ../src/pulse/sample.c:144
+#: ../src/pulse/volume.c:163
+#: ../src/pulse/volume.c:194
msgid "(invalid)"
msgstr ""
-#: ../src/pulse/channelmap.c:746
-msgid "Stereo"
-msgstr ""
-
-#: ../src/pulse/channelmap.c:751
-msgid "Surround 4.0"
-msgstr ""
-
-#: ../src/pulse/channelmap.c:757
-msgid "Surround 4.1"
-msgstr ""
-
-#: ../src/pulse/channelmap.c:763
-msgid "Surround 5.0"
-msgstr ""
-
-#: ../src/pulse/channelmap.c:769
-msgid "Surround 5.1"
-msgstr ""
-
-#: ../src/pulse/channelmap.c:776
-msgid "Surround 7.1"
-msgstr ""
-
#: ../src/pulse/error.c:43
msgid "OK"
msgstr ""
@@ -978,7 +986,8 @@ msgstr ""
msgid "No such extension"
msgstr ""
-#: ../src/pulse/client-conf-x11.c:55 ../src/utils/pax11publish.c:100
+#: ../src/pulse/client-conf-x11.c:55
+#: ../src/utils/pax11publish.c:100
msgid "XOpenDisplay() failed"
msgstr ""
@@ -991,35 +1000,45 @@ msgstr ""
msgid "Failed to open configuration file '%s': %s"
msgstr ""
-#: ../src/pulse/context.c:517
+#: ../src/pulse/context.c:516
msgid "No cookie loaded. Attempting to connect without."
msgstr ""
-#: ../src/pulse/context.c:643
+#: ../src/pulse/context.c:642
#, c-format
msgid "fork(): %s"
msgstr ""
-#: ../src/pulse/context.c:696
+#: ../src/pulse/context.c:695
#, c-format
msgid "waitpid(): %s"
msgstr ""
-#: ../src/pulse/context.c:1257
+#: ../src/pulse/context.c:1256
#, c-format
msgid "Received message for unknown extension '%s'"
msgstr ""
-#: ../src/utils/pacat.c:94
+#: ../src/utils/pacat.c:93
#, c-format
msgid "pa_stream_write() failed: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:133
+#: ../src/utils/pacat.c:132
#, c-format
msgid "pa_stream_peek() failed: %s\n"
msgstr ""
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "Buffer overrun, dropping incoming data\n"
+msgstr ""
+
+#: ../src/utils/pacat.c:143
+#, c-format
+msgid "pa_stream_drop() failed: %s\n"
+msgstr ""
+
#: ../src/utils/pacat.c:169
#, c-format
msgid "Stream successfully created.\n"
@@ -1099,78 +1118,83 @@ msgstr ""
msgid "pa_stream_new() failed: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:288
+#: ../src/utils/pacat.c:287
#, c-format
msgid "pa_stream_connect_playback() failed: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:294
+#: ../src/utils/pacat.c:293
#, c-format
msgid "pa_stream_connect_record() failed: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:308 ../src/utils/pasuspender.c:159
-#: ../src/utils/pactl.c:758 ../src/utils/paplay.c:183
+#: ../src/utils/pacat.c:307
+#: ../src/utils/pasuspender.c:159
+#: ../src/utils/pactl.c:666
+#: ../src/utils/paplay.c:183
#, c-format
msgid "Connection failure: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:329 ../src/utils/paplay.c:75
+#: ../src/utils/pacat.c:328
+#: ../src/utils/paplay.c:75
#, c-format
msgid "Failed to drain stream: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:334 ../src/utils/paplay.c:80
+#: ../src/utils/pacat.c:333
+#: ../src/utils/paplay.c:80
#, c-format
msgid "Playback stream drained.\n"
msgstr ""
-#: ../src/utils/pacat.c:344 ../src/utils/paplay.c:92
+#: ../src/utils/pacat.c:343
+#: ../src/utils/paplay.c:92
#, c-format
msgid "Draining connection to server.\n"
msgstr ""
-#: ../src/utils/pacat.c:370
+#: ../src/utils/pacat.c:369
#, c-format
msgid "Got EOF.\n"
msgstr ""
-#: ../src/utils/pacat.c:376
+#: ../src/utils/pacat.c:375
#, c-format
msgid "pa_stream_drain(): %s\n"
msgstr ""
-#: ../src/utils/pacat.c:386
+#: ../src/utils/pacat.c:385
#, c-format
msgid "read() failed: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:418
+#: ../src/utils/pacat.c:417
#, c-format
msgid "write() failed: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:439
+#: ../src/utils/pacat.c:438
#, c-format
msgid "Got signal, exiting.\n"
msgstr ""
-#: ../src/utils/pacat.c:453
+#: ../src/utils/pacat.c:452
#, c-format
msgid "Failed to get latency: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:458
+#: ../src/utils/pacat.c:457
#, c-format
msgid "Time: %0.3f sec; Latency: %0.0f usec. \r"
msgstr ""
-#: ../src/utils/pacat.c:478
+#: ../src/utils/pacat.c:477
#, c-format
msgid "pa_stream_update_timing_info() failed: %s\n"
msgstr ""
-#: ../src/utils/pacat.c:491
+#: ../src/utils/pacat.c:490
#, c-format
msgid ""
"%s [options]\n"
@@ -1183,47 +1207,30 @@ msgid ""
"\n"
" -v, --verbose Enable verbose operations\n"
"\n"
-" -s, --server=SERVER The name of the server to connect "
-"to\n"
-" -d, --device=DEVICE The name of the sink/source to "
-"connect to\n"
-" -n, --client-name=NAME How to call this client on the "
-"server\n"
-" --stream-name=NAME How to call this stream on the "
-"server\n"
-" --volume=VOLUME Specify the initial (linear) volume "
-"in range 0...65536\n"
-" --rate=SAMPLERATE The sample rate in Hz (defaults to "
-"44100)\n"
-" --format=SAMPLEFORMAT The sample type, one of s16le, "
-"s16be, u8, float32le,\n"
-" float32be, ulaw, alaw, s32le, s32be "
-"(defaults to s16ne)\n"
-" --channels=CHANNELS The number of channels, 1 for mono, "
-"2 for stereo\n"
+" -s, --server=SERVER The name of the server to connect to\n"
+" -d, --device=DEVICE The name of the sink/source to connect to\n"
+" -n, --client-name=NAME How to call this client on the server\n"
+" --stream-name=NAME How to call this stream on the server\n"
+" --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
+" --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
+" --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
+" float32be, ulaw, alaw, s32le, s32be (defaults to s16ne)\n"
+" --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
" (defaults to 2)\n"
-" --channel-map=CHANNELMAP Channel map to use instead of the "
-"default\n"
-" --fix-format Take the sample format from the sink "
-"the stream is\n"
+" --channel-map=CHANNELMAP Channel map to use instead of the default\n"
+" --fix-format Take the sample format from the sink the stream is\n"
" being connected to.\n"
-" --fix-rate Take the sampling rate from the sink "
-"the stream is\n"
+" --fix-rate Take the sampling rate from the sink the stream is\n"
" being connected to.\n"
-" --fix-channels Take the number of channels and the "
-"channel map\n"
-" from the sink the stream is being "
-"connected to.\n"
+" --fix-channels Take the number of channels and the channel map\n"
+" from the sink the stream is being connected to.\n"
" --no-remix Don't upmix or downmix channels.\n"
-" --no-remap Map channels by index instead of "
-"name.\n"
-" --latency=BYTES Request the specified latency in "
-"bytes.\n"
-" --process-time=BYTES Request the specified process time "
-"per request in bytes.\n"
+" --no-remap Map channels by index instead of name.\n"
+" --latency=BYTES Request the specified latency in bytes.\n"
+" --process-time=BYTES Request the specified process time per request in bytes.\n"
msgstr ""
-#: ../src/utils/pacat.c:592
+#: ../src/utils/pacat.c:591
#, c-format
msgid ""
"pacat %s\n"
@@ -1231,88 +1238,94 @@ msgid ""
"Linked with libpulse %s\n"
msgstr ""
-#: ../src/utils/pacat.c:649
+#: ../src/utils/pacat.c:647
#, c-format
msgid "Invalid channel map '%s'\n"
msgstr ""
-#: ../src/utils/pacat.c:678
+#: ../src/utils/pacat.c:676
#, c-format
msgid "Invalid latency specification '%s'\n"
msgstr ""
-#: ../src/utils/pacat.c:685
+#: ../src/utils/pacat.c:683
#, c-format
msgid "Invalid process time specification '%s'\n"
msgstr ""
-#: ../src/utils/pacat.c:696
+#: ../src/utils/pacat.c:694
#, c-format
msgid "Invalid sample specification\n"
msgstr ""
-#: ../src/utils/pacat.c:701
+#: ../src/utils/pacat.c:699
#, c-format
msgid "Channel map doesn't match sample specification\n"
msgstr ""
-#: ../src/utils/pacat.c:708
+#: ../src/utils/pacat.c:706
#, c-format
msgid "Opening a %s stream with sample specification '%s'.\n"
msgstr ""
-#: ../src/utils/pacat.c:708
+#: ../src/utils/pacat.c:706
msgid "recording"
msgstr ""
-#: ../src/utils/pacat.c:708
+#: ../src/utils/pacat.c:706
msgid "playback"
msgstr ""
-#: ../src/utils/pacat.c:716
+#: ../src/utils/pacat.c:714
#, c-format
msgid "open(): %s\n"
msgstr ""
-#: ../src/utils/pacat.c:721
+#: ../src/utils/pacat.c:719
#, c-format
msgid "dup2(): %s\n"
msgstr ""
-#: ../src/utils/pacat.c:731
+#: ../src/utils/pacat.c:729
#, c-format
msgid "Too many arguments.\n"
msgstr ""
-#: ../src/utils/pacat.c:744 ../src/utils/pasuspender.c:280
-#: ../src/utils/pactl.c:1013 ../src/utils/paplay.c:381
+#: ../src/utils/pacat.c:742
+#: ../src/utils/pasuspender.c:280
+#: ../src/utils/pactl.c:909
+#: ../src/utils/paplay.c:381
#, c-format
msgid "pa_mainloop_new() failed.\n"
msgstr ""
-#: ../src/utils/pacat.c:765
+#: ../src/utils/pacat.c:763
#, c-format
msgid "io_new() failed.\n"
msgstr ""
-#: ../src/utils/pacat.c:771 ../src/utils/pasuspender.c:293
-#: ../src/utils/pactl.c:1027 ../src/utils/paplay.c:396
+#: ../src/utils/pacat.c:769
+#: ../src/utils/pasuspender.c:293
+#: ../src/utils/pactl.c:923
+#: ../src/utils/paplay.c:396
#, c-format
msgid "pa_context_new() failed.\n"
msgstr ""
-#: ../src/utils/pacat.c:779
+#: ../src/utils/pacat.c:777
#, c-format
msgid "pa_context_connect() failed: %s"
msgstr ""
-#: ../src/utils/pacat.c:790
+#: ../src/utils/pacat.c:788
#, c-format
msgid "time_new() failed.\n"
msgstr ""
-#: ../src/utils/pacat.c:797 ../src/utils/pasuspender.c:301
-#: ../src/utils/pactl.c:1035 ../src/utils/paplay.c:407
+#: ../src/utils/pacat.c:795
+#: ../src/utils/pasuspender.c:301
+#: ../src/utils/pactl.c:931
+#: ../src/utils/paplay.c:407
#, c-format
msgid "pa_mainloop_run() failed.\n"
msgstr ""
@@ -1342,7 +1355,8 @@ msgstr ""
msgid "WARNING: Sound server is not local, not suspending.\n"
msgstr ""
-#: ../src/utils/pasuspender.c:176 ../src/utils/pactl.c:764
+#: ../src/utils/pasuspender.c:176
+#: ../src/utils/pactl.c:672
#: ../src/utils/paplay.c:191
#, c-format
msgid "Got SIGINT, exiting.\n"
@@ -1360,8 +1374,7 @@ msgid ""
"\n"
" -h, --help Show this help\n"
" --version Show version\n"
-" -s, --server=SERVER The name of the server to connect "
-"to\n"
+" -s, --server=SERVER The name of the server to connect to\n"
"\n"
msgstr ""
@@ -1373,32 +1386,32 @@ msgid ""
"Linked with libpulse %s\n"
msgstr ""
-#: ../src/utils/pactl.c:108
+#: ../src/utils/pactl.c:107
#, c-format
msgid "Failed to get statistics: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:114
+#: ../src/utils/pactl.c:113
#, c-format
msgid "Currently in use: %u blocks containing %s bytes total.\n"
msgstr ""
-#: ../src/utils/pactl.c:117
+#: ../src/utils/pactl.c:116
#, c-format
msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
msgstr ""
-#: ../src/utils/pactl.c:120
+#: ../src/utils/pactl.c:119
#, c-format
msgid "Sample cache size: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:129
+#: ../src/utils/pactl.c:128
#, c-format
msgid "Failed to get server information: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:136
+#: ../src/utils/pactl.c:135
#, c-format
msgid ""
"User name: %s\n"
@@ -1411,212 +1424,206 @@ msgid ""
"Cookie: %08x\n"
msgstr ""
-#: ../src/utils/pactl.c:175
+#: ../src/utils/pactl.c:160
#, c-format
msgid "Failed to get sink information: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:191
+#: ../src/utils/pactl.c:176
#, c-format
msgid ""
-"Sink #%u\n"
-"\tState: %s\n"
-"\tName: %s\n"
-"\tDescription: %s\n"
-"\tDriver: %s\n"
-"\tSample Specification: %s\n"
-"\tChannel Map: %s\n"
-"\tOwner Module: %u\n"
-"\tMute: %s\n"
-"\tVolume: %s%s%s\n"
-"\t balance %0.2f\n"
-"\tBase Volume: %s%s%s\n"
-"\tMonitor Source: %s\n"
-"\tLatency: %0.0f usec, configured %0.0f usec\n"
-"\tFlags: %s%s%s%s%s%s\n"
-"\tProperties:\n"
-"\t\t%s\n"
-msgstr ""
-
-#: ../src/utils/pactl.c:255
+"*** 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"
+msgstr ""
+
+#: ../src/utils/pactl.c:193
+#: ../src/utils/pactl.c:371
+msgid "muted"
+msgstr ""
+
+#: ../src/utils/pactl.c:212
#, c-format
msgid "Failed to get source information: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:271
+#: ../src/utils/pactl.c:228
#, c-format
msgid ""
-"Source #%u\n"
-"\tState: %s\n"
-"\tName: %s\n"
-"\tDescription: %s\n"
-"\tDriver: %s\n"
-"\tSample Specification: %s\n"
-"\tChannel Map: %s\n"
-"\tOwner Module: %u\n"
-"\tMute: %s\n"
-"\tVolume: %s%s%s\n"
-"\t balance %0.2f\n"
-"\tBase Volume: %s%s%s\n"
-"\tMonitor of Sink: %s\n"
-"\tLatency: %0.0f usec, configured %0.0f usec\n"
-"\tFlags: %s%s%s%s%s%s\n"
-"\tProperties:\n"
-"\t\t%s\n"
-msgstr ""
-
-#: ../src/utils/pactl.c:303 ../src/utils/pactl.c:347 ../src/utils/pactl.c:382
-#: ../src/utils/pactl.c:419 ../src/utils/pactl.c:478 ../src/utils/pactl.c:479
-#: ../src/utils/pactl.c:489 ../src/utils/pactl.c:533 ../src/utils/pactl.c:534
-#: ../src/utils/pactl.c:540 ../src/utils/pactl.c:583 ../src/utils/pactl.c:584
-#: ../src/utils/pactl.c:591
+"*** 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"
+msgstr ""
+
+#: ../src/utils/pactl.c:246
+#: ../src/utils/pactl.c:289
+#: ../src/utils/pactl.c:322
+#: ../src/utils/pactl.c:366
+#: ../src/utils/pactl.c:367
+#: ../src/utils/pactl.c:374
+#: ../src/utils/pactl.c:418
+#: ../src/utils/pactl.c:419
+#: ../src/utils/pactl.c:425
+#: ../src/utils/pactl.c:468
+#: ../src/utils/pactl.c:469
+#: ../src/utils/pactl.c:473
msgid "n/a"
msgstr ""
-#: ../src/utils/pactl.c:321
+#: ../src/utils/pactl.c:263
#, c-format
msgid "Failed to get module information: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:339
+#: ../src/utils/pactl.c:281
#, c-format
msgid ""
-"Module #%u\n"
-"\tName: %s\n"
-"\tArgument: %s\n"
-"\tUsage counter: %s\n"
-"\tProperties:\n"
-"\t\t%s\n"
+"*** Module #%u ***\n"
+"Name: %s\n"
+"Argument: %s\n"
+"Usage counter: %s\n"
+"Auto unload: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:358
+#: ../src/utils/pactl.c:298
#, c-format
msgid "Failed to get client information: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:376
+#: ../src/utils/pactl.c:316
#, c-format
msgid ""
-"Client #%u\n"
-"\tDriver: %s\n"
-"\tOwner Module: %s\n"
-"\tProperties:\n"
-"\t\t%s\n"
+"*** Client #%u ***\n"
+"Driver: %s\n"
+"Owner Module: %s\n"
+"Properties:\n"
+"%s"
msgstr ""
-#: ../src/utils/pactl.c:393
+#: ../src/utils/pactl.c:333
#, c-format
-msgid "Failed to get card information: %s\n"
+msgid "Failed to get sink input information: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:411
+#: ../src/utils/pactl.c:352
#, c-format
msgid ""
-"Card #%u\n"
-"\tName: %s\n"
-"\tDriver: %s\n"
-"\tOwner Module: %s\n"
-"\tProperties:\n"
-"\t\t%s\n"
+"*** 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"
+msgstr ""
+
+#: ../src/utils/pactl.c:385
+#, c-format
+msgid "Failed to get source output information: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:425
+#: ../src/utils/pactl.c:405
#, c-format
-msgid "\tProfiles:\n"
+msgid ""
+"*** Source Output #%u ***\n"
+"Driver: %s\n"
+"Owner Module: %s\n"
+"Client: %s\n"
+"Source: %u\n"
+"Sample Specification: %s\n"
+"Channel Map: %s\n"
+"Buffer Latency: %0.0f usec\n"
+"Source Latency: %0.0f usec\n"
+"Resample method: %s\n"
+"Properties:\n"
+"%s"
+msgstr ""
+
+#: ../src/utils/pactl.c:436
+#, c-format
+msgid "Failed to get sample information: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:431
+#: ../src/utils/pactl.c:455
#, c-format
-msgid "\tActive Profile: %s\n"
+msgid ""
+"*** 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"
msgstr ""
-#: ../src/utils/pactl.c:442
+#: ../src/utils/pactl.c:481
#, c-format
-msgid "Failed to get sink input information: %s\n"
+msgid "Failed to get autoload information: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:461
+#: ../src/utils/pactl.c:497
#, c-format
msgid ""
-"Sink Input #%u\n"
-"\tDriver: %s\n"
-"\tOwner Module: %s\n"
-"\tClient: %s\n"
-"\tSink: %u\n"
-"\tSample Specification: %s\n"
-"\tChannel Map: %s\n"
-"\tMute: %s\n"
-"\tVolume: %s\n"
-"\t %s\n"
-"\t balance %0.2f\n"
-"\tBuffer Latency: %0.0f usec\n"
-"\tSink Latency: %0.0f usec\n"
-"\tResample method: %s\n"
-"\tProperties:\n"
-"\t\t%s\n"
-msgstr ""
-
-#: ../src/utils/pactl.c:500
-#, c-format
-msgid "Failed to get source output information: %s\n"
+"*** Autoload Entry #%u ***\n"
+"Name: %s\n"
+"Type: %s\n"
+"Module: %s\n"
+"Argument: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:520
-#, c-format
-msgid ""
-"Source Output #%u\n"
-"\tDriver: %s\n"
-"\tOwner Module: %s\n"
-"\tClient: %s\n"
-"\tSource: %u\n"
-"\tSample Specification: %s\n"
-"\tChannel Map: %s\n"
-"\tBuffer Latency: %0.0f usec\n"
-"\tSource Latency: %0.0f usec\n"
-"\tResample method: %s\n"
-"\tProperties:\n"
-"\t\t%s\n"
-msgstr ""
-
-#: ../src/utils/pactl.c:551
-#, c-format
-msgid "Failed to get sample information: %s\n"
+#: ../src/utils/pactl.c:504
+msgid "sink"
msgstr ""
-#: ../src/utils/pactl.c:569
-#, c-format
-msgid ""
-"Sample #%u\n"
-"\tName: %s\n"
-"\tSample Specification: %s\n"
-"\tChannel Map: %s\n"
-"\tVolume: %s\n"
-"\t %s\n"
-"\t balance %0.2f\n"
-"\tDuration: %0.1fs\n"
-"\tSize: %s\n"
-"\tLazy: %s\n"
-"\tFilename: %s\n"
-"\tProperties:\n"
-"\t\t%s\n"
-msgstr ""
-
-#: ../src/utils/pactl.c:599 ../src/utils/pactl.c:609
+#: ../src/utils/pactl.c:504
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:511
+#: ../src/utils/pactl.c:521
#, c-format
msgid "Failure: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:633
+#: ../src/utils/pactl.c:545
#, c-format
msgid "Failed to upload sample: %s\n"
msgstr ""
-#: ../src/utils/pactl.c:650
+#: ../src/utils/pactl.c:562
#, c-format
msgid "Premature end of file\n"
msgstr ""
-#: ../src/utils/pactl.c:770
+#: ../src/utils/pactl.c:678
#, c-format
msgid ""
"%s [options] stat\n"
@@ -1631,18 +1638,15 @@ msgid ""
"%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"
+" -s, --server=SERVER The name of the server to connect to\n"
+" -n, --client-name=NAME How to call this client on the server\n"
msgstr ""
-#: ../src/utils/pactl.c:822
+#: ../src/utils/pactl.c:729
#, c-format
msgid ""
"pactl %s\n"
@@ -1650,66 +1654,57 @@ msgid ""
"Linked with libpulse %s\n"
msgstr ""
-#: ../src/utils/pactl.c:861
+#: ../src/utils/pactl.c:768
#, c-format
msgid "Please specify a sample file to load\n"
msgstr ""
-#: ../src/utils/pactl.c:883
+#: ../src/utils/pactl.c:790
#, c-format
msgid "Failed to open sound file.\n"
msgstr ""
-#: ../src/utils/pactl.c:895
+#: ../src/utils/pactl.c:802
#, c-format
msgid "You have to specify a sample name to play\n"
msgstr ""
-#: ../src/utils/pactl.c:907
+#: ../src/utils/pactl.c:814
#, c-format
msgid "You have to specify a sample name to remove\n"
msgstr ""
-#: ../src/utils/pactl.c:915
+#: ../src/utils/pactl.c:822
#, c-format
msgid "You have to specify a sink input index and a sink\n"
msgstr ""
-#: ../src/utils/pactl.c:924
+#: ../src/utils/pactl.c:831
#, c-format
msgid "You have to specify a source output index and a source\n"
msgstr ""
-#: ../src/utils/pactl.c:938
+#: ../src/utils/pactl.c:845
#, c-format
msgid "You have to specify a module name and arguments.\n"
msgstr ""
-#: ../src/utils/pactl.c:958
+#: ../src/utils/pactl.c:865
#, c-format
msgid "You have to specify a module index\n"
msgstr ""
-#: ../src/utils/pactl.c:968
+#: ../src/utils/pactl.c:875
#, c-format
-msgid ""
-"You may not specify more than one sink. You have to specify a boolean "
-"value.\n"
+msgid "You may not specify more than one sink. You have to specify at least one boolean value.\n"
msgstr ""
-#: ../src/utils/pactl.c:981
+#: ../src/utils/pactl.c:888
#, c-format
-msgid ""
-"You may not specify more than one source. You have to specify a boolean "
-"value.\n"
+msgid "You may not specify more than one source. You have to specify at least one boolean value.\n"
msgstr ""
-#: ../src/utils/pactl.c:993
-#, c-format
-msgid "You have to specify a card name/index and a profile name\n"
-msgstr ""
-
-#: ../src/utils/pactl.c:1008
+#: ../src/utils/pactl.c:904
#, c-format
msgid "No valid command specified.\n"
msgstr ""
@@ -1721,8 +1716,7 @@ msgid ""
"\n"
" -d Show current PulseAudio data attached to X11 display (default)\n"
" -e Export local PulseAudio data to X11 display\n"
-" -i Import PulseAudio data from X11 display to local environment "
-"variables and cookie file.\n"
+" -i Import PulseAudio data from X11 display to local environment variables and cookie file.\n"
" -r Remove PulseAudio data from X11 display\n"
msgstr ""
@@ -1809,12 +1803,14 @@ msgstr ""
msgid "select(): %s"
msgstr "select(): %s"
-#: ../src/utils/pacmd.c:124 ../src/utils/pacmd.c:140
+#: ../src/utils/pacmd.c:124
+#: ../src/utils/pacmd.c:140
#, c-format
msgid "read(): %s"
msgstr "read(): %s"
-#: ../src/utils/pacmd.c:153 ../src/utils/pacmd.c:167
+#: ../src/utils/pacmd.c:153
+#: ../src/utils/pacmd.c:167
#, c-format
msgid "write(): %s"
msgstr "write(): %s"
@@ -1844,15 +1840,11 @@ msgid ""
"\n"
" -v, --verbose Enable verbose operation\n"
"\n"
-" -s, --server=SERVER The name of the server to connect "
-"to\n"
+" -s, --server=SERVER The name of the server to connect to\n"
" -d, --device=DEVICE The name of the sink to connect to\n"
-" -n, --client-name=NAME How to call this client on the "
-"server\n"
-" --stream-name=NAME How to call this stream on the "
-"server\n"
-" --volume=VOLUME Specify the initial (linear) volume "
-"in range 0...65536\n"
+" -n, --client-name=NAME How to call this client on the server\n"
+" --stream-name=NAME How to call this stream on the server\n"
+" --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
" --channel-map=CHANNELMAP Set the channel map to the use\n"
msgstr ""
@@ -1884,6 +1876,8 @@ msgstr ""
msgid "Using sample spec '%s'\n"
msgstr ""
-#: ../src/pulsecore/lock-autospawn.c:126 ../src/pulsecore/lock-autospawn.c:207
+#: ../src/pulsecore/lock-autospawn.c:126
+#: ../src/pulsecore/lock-autospawn.c:207
msgid "Cannot access autospawn lock."
msgstr ""
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 00dd53d1..1910a82e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -862,6 +862,13 @@ libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore-@PA_MAJORMIN
# Plug-in libraries #
###################################
+if HAVE_DBUS
+# Serveral module (e.g. libalsa-util.la)
+modlibexec_LTLIBRARIES += \
+ libdbus-util.la \
+ module-console-kit.la
+endif
+
modlibexec_LTLIBRARIES += \
module-cli.la \
module-cli-protocol-tcp.la \
@@ -989,18 +996,12 @@ endif
if HAVE_HAL
modlibexec_LTLIBRARIES += \
- libdbus-util.la \
module-hal-detect.la
endif
-if HAVE_DBUS
-modlibexec_LTLIBRARIES += \
- libdbus-util.la \
- module-console-kit.la
-endif
-
if HAVE_BLUEZ
modlibexec_LTLIBRARIES += \
+ libbluetooth-util.la \
module-bluetooth-proximity.la \
module-bluetooth-discover.la \
libbluetooth-ipc.la \
@@ -1021,9 +1022,7 @@ modlibexec_LTLIBRARIES += \
endif
endif
-
-# These are generated by a M4 script
-
+# These are generated by an M4 script
SYMDEF_FILES = \
modules/module-cli-symdef.h \
modules/module-cli-protocol-tcp-symdef.h \
@@ -1445,24 +1444,30 @@ proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
# Bluetooth sink / source
module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c
module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h
+libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h modules/bluetooth/sbc_primitives.h modules/bluetooth/sbc_primitives.c modules/bluetooth/sbc_primitives_mmx.h modules/bluetooth/sbc_primitives_neon.h modules/bluetooth/sbc_primitives_mmx.c modules/bluetooth/sbc_primitives_neon.c
libbluetooth_sbc_la_LDFLAGS = -avoid-version
libbluetooth_sbc_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libbluetooth_sbc_la_CFLAGS = $(AM_CFLAGS)
-SBC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_sbc_la_SOURCES))
+BLUETOOTH_SBC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_sbc_la_SOURCES))
libbluetooth_ipc_la_SOURCES = modules/bluetooth/ipc.c modules/bluetooth/ipc.h
libbluetooth_ipc_la_LDFLAGS = -avoid-version
libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
-libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS) -w
+libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS)
+BLUETOOTH_IPC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_ipc_la_SOURCES)) rtp.h
+
+libbluetooth_util_la_SOURCES = modules/bluetooth/bluetooth-util.c modules/bluetooth/bluetooth-util.h
+libbluetooth_util_la_LDFLAGS = -avoid-version
+libbluetooth_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la libdbus-util.la
+libbluetooth_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c modules/bluetooth/rtp.h
module_bluetooth_device_la_LDFLAGS = $(MODULE_LDFLAGS)
module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
-module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -w
+module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
# Apple Airtunes/RAOP
module_raop_sink_la_SOURCES = modules/module-raop-sink.c
@@ -1538,10 +1543,16 @@ massif: pulseaudio
update-ffmpeg:
wget -O pulsecore/ffmpeg/resample2.c http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/resample2.c?view=co
+# We get things twice here, because sometimes gitweb will us just give a "Generating..." otherwise.
update-sbc:
- for i in $(SBC_FILES); do \
+ for i in $(BLUETOOTH_SBC_FILES) ; do \
+ wget -O /dev/null http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=sbc/$$i ; \
wget -O modules/bluetooth/$$i http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=sbc/$$i ; \
done
+ for i in $(BLUETOOTH_IPC_FILES); do \
+ wget -O /dev/null http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=audio/$$i ; \
+ wget -O modules/bluetooth/$$i http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=audio/$$i ; \
+ done
# Automatically generate linker version script. We use the same one for all public .sos
update-map-file:
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index 5d69926b..91a1c614 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -77,6 +77,15 @@ 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
+### Load additional modules from GConf settings. This can be configured with the paprefs tool.
+### Please keep in mind that the modules configured by paprefs might conflict with manually
+### loaded modules.
+.ifexists module-gconf@PA_SOEXT@
+.nofail
+load-module module-gconf
+.fail
+.endif
+
### Automatically restore the default sink/source when changed by the user during runtime
load-module module-default-device-restore
@@ -97,15 +106,6 @@ load-module module-console-kit
### Enable positioned event sounds
load-module module-position-event-sounds
-### Load additional modules from GConf settings. This can be configured with the paprefs tool.
-### Please keep in mind that the modules configured by paprefs might conflict with manually
-### loaded modules.
-.ifexists module-gconf@PA_SOEXT@
-.nofail
-load-module module-gconf
-.fail
-.endif
-
# X11 modules should not be started from default.pa so that one daemon
# can be shared by multiple sessions.
diff --git a/src/map-file b/src/map-file
index e076a435..ca4e7a72 100644
--- a/src/map-file
+++ b/src/map-file
@@ -1,5 +1,7 @@
PULSE_0 {
global:
+pa_ascii_filter;
+pa_ascii_valid;
pa_browser_new;
pa_browser_new_full;
pa_browser_ref;
@@ -10,6 +12,7 @@ pa_bytes_per_second;
pa_bytes_snprint;
pa_bytes_to_usec;
pa_channel_map_can_balance;
+pa_channel_map_can_fade;
pa_channel_map_compatible;
pa_channel_map_equal;
pa_channel_map_init;
@@ -108,6 +111,7 @@ pa_context_unref;
pa_cvolume_avg;
pa_cvolume_channels_equal_to;
pa_cvolume_compatible;
+pa_cvolume_compatible_with_channel_map;
pa_cvolume_equal;
pa_cvolume_get_balance;
pa_cvolume_get_fade;
@@ -179,6 +183,7 @@ pa_proplist_unset_many;
pa_proplist_update;
pa_sample_format_to_string;
pa_sample_size;
+pa_sample_size_of_format;
pa_sample_spec_equal;
pa_sample_spec_init;
pa_sample_spec_snprint;
diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
new file mode 100644
index 00000000..7855c2ef
--- /dev/null
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -0,0 +1,864 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Joao Paulo Rechi Vita
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ 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-util.h>
+#include <modules/dbus-util.h>
+
+#include "bluetooth-util.h"
+
+enum mode {
+ MODE_FIND,
+ MODE_GET,
+ MODE_DISCOVER
+};
+
+struct pa_bluetooth_discovery {
+ DBusConnection *connection;
+ PA_LLIST_HEAD(pa_dbus_pending, pending);
+
+ enum mode mode;
+
+ /* If mode == MODE_FIND look for a specific device by its address.
+ If mode == MODE_GET look for a specific device by its path. */
+ const char *looking_for;
+ pa_bluetooth_device *found_device;
+
+ /* If looking_for is NULL we do long-time discovery */
+ pa_hashmap *devices;
+ pa_bluetooth_device_callback_t callback;
+ struct userdata *userdata;
+};
+
+static pa_bluetooth_uuid *uuid_new(const char *uuid) {
+ pa_bluetooth_uuid *u;
+
+ u = pa_xnew(pa_bluetooth_uuid, 1);
+ u->uuid = pa_xstrdup(uuid);
+ PA_LLIST_INIT(pa_bluetooth_uuid, u);
+
+ return u;
+}
+
+static void uuid_free(pa_bluetooth_uuid *u) {
+ pa_assert(u);
+
+ pa_xfree(u->uuid);
+ pa_xfree(u);
+}
+
+static pa_bluetooth_device* device_new(const char *path) {
+ pa_bluetooth_device *d;
+
+ d = pa_xnew(pa_bluetooth_device, 1);
+
+ d->device_info_valid = d->audio_sink_info_valid = d->headset_info_valid = 0;
+
+ d->data = NULL;
+
+ d->name = NULL;
+ d->path = pa_xstrdup(path);
+ d->paired = -1;
+ d->alias = NULL;
+ d->device_connected = -1;
+ PA_LLIST_HEAD_INIT(pa_bluetooth_uuid, d->uuids);
+ d->address = NULL;
+ d->class = -1;
+ d->trusted = -1;
+
+ d->audio_sink_connected = -1;
+
+ d->headset_connected = -1;
+
+ return d;
+}
+
+void pa_bluetooth_device_free(pa_bluetooth_device *d) {
+ pa_bluetooth_uuid *u;
+
+ pa_assert(d);
+
+ while ((u = d->uuids)) {
+ PA_LLIST_REMOVE(pa_bluetooth_uuid, d->uuids, u);
+ uuid_free(u);
+ }
+
+ pa_xfree(d->name);
+ pa_xfree(d->path);
+ pa_xfree(d->alias);
+ pa_xfree(d->address);
+ pa_xfree(d);
+}
+
+static pa_bool_t device_is_loaded(pa_bluetooth_device *d) {
+ pa_assert(d);
+
+ return d->device_info_valid && d->audio_sink_info_valid && d->headset_info_valid;
+}
+
+static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
+ pa_assert(d);
+
+ pa_assert(d->device_info_valid);
+ pa_assert(d->audio_sink_info_valid);
+ pa_assert(d->headset_info_valid);
+
+ return d->device_info_valid > 0 &&
+ (d->audio_sink_info_valid > 0 || d->headset_info_valid > 0);
+}
+
+static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
+ const char *key;
+ DBusMessageIter variant_i;
+
+ pa_assert(y);
+ pa_assert(d);
+ pa_assert(i);
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+ pa_log("Property name not a string.");
+ return -1;
+ }
+
+ dbus_message_iter_get_basic(i, &key);
+
+ if (!dbus_message_iter_next(i)) {
+ pa_log("Property value missing");
+ return -1;
+ }
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+ pa_log("Property value not a variant.");
+ return -1;
+ }
+
+ dbus_message_iter_recurse(i, &variant_i);
+
+/* pa_log_debug("Parsing property org.bluez.Device.%s", key); */
+
+ switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+ case DBUS_TYPE_STRING: {
+
+ const char *value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Name")) {
+ pa_xfree(d->name);
+ d->name = pa_xstrdup(value);
+ } else if (pa_streq(key, "Alias")) {
+ pa_xfree(d->alias);
+ d->alias = pa_xstrdup(value);
+ } else if (pa_streq(key, "Address")) {
+ pa_xfree(d->address);
+ d->address = pa_xstrdup(value);
+ }
+
+/* pa_log_debug("Value %s", value); */
+
+ break;
+ }
+
+ case DBUS_TYPE_BOOLEAN: {
+
+ dbus_bool_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Paired"))
+ d->paired = !!value;
+ else if (pa_streq(key, "Connected"))
+ d->device_connected = !!value;
+ else if (pa_streq(key, "Trusted"))
+ d->trusted = !!value;
+
+/* pa_log_debug("Value %s", pa_yes_no(value)); */
+
+ break;
+ }
+
+ case DBUS_TYPE_UINT32: {
+
+ uint32_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Class"))
+ d->class = (int) value;
+
+/* pa_log_debug("Value %u", (unsigned) value); */
+
+ break;
+ }
+
+ case DBUS_TYPE_ARRAY: {
+
+ DBusMessageIter ai;
+ dbus_message_iter_recurse(&variant_i, &ai);
+
+ if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING &&
+ pa_streq(key, "UUIDs")) {
+
+ while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+ pa_bluetooth_uuid *node;
+ const char *value;
+
+ dbus_message_iter_get_basic(&ai, &value);
+ node = uuid_new(value);
+ PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
+
+ if (!dbus_message_iter_next(&ai))
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusMessageIter *i) {
+ const char *key;
+ DBusMessageIter variant_i;
+
+ pa_assert(u);
+ pa_assert(connected);
+ pa_assert(i);
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+ pa_log("Property name not a string.");
+ return -1;
+ }
+
+ dbus_message_iter_get_basic(i, &key);
+
+ if (!dbus_message_iter_next(i)) {
+ pa_log("Property value missing");
+ return -1;
+ }
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+ pa_log("Property value not a variant.");
+ return -1;
+ }
+
+ dbus_message_iter_recurse(i, &variant_i);
+
+/* pa_log_debug("Parsing property org.bluez.{AudioSink|Headset}.%s", key); */
+
+ 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;
+
+/* pa_log_debug("Value %s", pa_yes_no(value)); */
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_bool_t good) {
+ pa_assert(y);
+ pa_assert(d);
+
+ if (y->mode != MODE_DISCOVER)
+ return;
+
+ if (!device_is_loaded(d))
+ return;
+
+ if (!device_is_audio(d))
+ return;
+
+ y->callback(y->userdata, d, good);
+
+}
+
+static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
+ DBusMessage *r;
+ DBusMessageIter arg_i, element_i;
+ pa_dbus_pending *p;
+ pa_bluetooth_device *d;
+ pa_bluetooth_discovery *y;
+ int valid;
+
+ pa_assert_se(p = userdata);
+ pa_assert_se(y = p->context_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+/* pa_log_debug("Got %s.GetProperties response for %s", */
+/* dbus_message_get_interface(p->message), */
+/* dbus_message_get_path(p->message)); */
+
+ d = p->call_data;
+
+ valid = dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR ? -1 : 1;
+
+ if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
+ d->device_info_valid = valid;
+ else if (dbus_message_is_method_call(p->message, "org.bluez.Headset", "GetProperties"))
+ d->headset_info_valid = valid;
+ else if (dbus_message_is_method_call(p->message, "org.bluez.AudioSink", "GetProperties"))
+ d->audio_sink_info_valid = valid;
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+
+ if (!dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD))
+ pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r));
+
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("GetProperties reply has no arguments.");
+ goto finish;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("GetProperties argument is not an array.");
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter dict_i;
+
+ dbus_message_iter_recurse(&element_i, &dict_i);
+
+ if (dbus_message_has_interface(p->message, "org.bluez.Device")) {
+ if (parse_device_property(y, d, &dict_i) < 0)
+ goto finish;
+
+ } else if (dbus_message_has_interface(p->message, "org.bluez.Headset")) {
+ if (parse_audio_property(y, &d->headset_connected, &dict_i) < 0)
+ goto finish;
+
+ } else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) {
+ if (parse_audio_property(y, &d->audio_sink_connected, &dict_i) < 0)
+ goto finish;
+ }
+ }
+
+ if (!dbus_message_iter_next(&element_i))
+ break;
+ }
+
+finish:
+ run_callback(y, d, TRUE);
+
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessage *m, DBusPendingCallNotifyFunction func) {
+ pa_dbus_pending *p;
+ DBusPendingCall *call;
+
+ pa_assert(y);
+ pa_assert(m);
+
+ pa_assert_se(dbus_connection_send_with_reply(y->connection, m, &call, -1));
+
+ p = pa_dbus_pending_new(m, call, y, d);
+ PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
+ dbus_pending_call_set_notify(call, func, p, NULL);
+
+ return p;
+}
+
+static void found_device(pa_bluetooth_discovery *y, const char* path) {
+ DBusMessage *m;
+ pa_bluetooth_device *d;
+
+ pa_assert(y);
+ pa_assert(path);
+
+ d = device_new(path);
+
+ if (y->mode == MODE_DISCOVER) {
+ pa_assert(y->devices);
+ pa_hashmap_put(y->devices, d->path, d);
+ } else {
+ pa_assert(!y->found_device);
+ y->found_device = d;
+ }
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Headset", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.AudioSink", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+}
+
+static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
+ DBusError e;
+ DBusMessage *r;
+ char **paths = NULL;
+ int num = -1;
+ pa_dbus_pending *p;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(pending);
+
+ dbus_error_init(&e);
+
+ pa_assert_se(p = userdata);
+ pa_assert_se(y = p->context_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+ if (dbus_message_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 {
+ int i;
+
+ for (i = 0; i < num; ++i)
+ found_device(y, paths[i]);
+ }
+
+end:
+ if (paths)
+ dbus_free_string_array (paths);
+
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static void find_device_reply(DBusPendingCall *pending, void *userdata) {
+ DBusError e;
+ DBusMessage *r;
+ char *path = NULL;
+ pa_dbus_pending *p;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(pending);
+
+ dbus_error_init(&e);
+
+ pa_assert_se(p = userdata);
+ pa_assert_se(y = p->context_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from FindDevice reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_get_args(r, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log("org.bluez.Adapter.FindDevice returned an error: '%s'\n", e.message);
+ dbus_error_free(&e);
+ } else
+ found_device(y, path);
+
+end:
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static void found_adapter(pa_bluetooth_discovery *y, const char *path) {
+ DBusMessage *m;
+
+ if (y->mode == MODE_FIND) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "FindDevice"));
+
+ pa_assert_se(dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &y->looking_for,
+ DBUS_TYPE_INVALID));
+
+ send_and_add_to_pending(y, NULL, m, find_device_reply);
+
+ } else {
+ pa_assert(y->mode == MODE_DISCOVER);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "ListDevices"));
+ send_and_add_to_pending(y, NULL, m, list_devices_reply);
+ }
+}
+
+static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
+ DBusError e;
+ DBusMessage *r;
+ char **paths = NULL;
+ int num = -1;
+ pa_dbus_pending *p;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(pending);
+
+ dbus_error_init(&e);
+
+ pa_assert_se(p = userdata);
+ pa_assert_se(y = p->context_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+ if (dbus_message_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", e.message);
+ dbus_error_free(&e);
+ } else {
+ int i;
+
+ for (i = 0; i < num; ++i)
+ found_adapter(y, paths[i]);
+ }
+
+end:
+ if (paths)
+ dbus_free_string_array (paths);
+
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static void list_adapters(pa_bluetooth_discovery *y) {
+ DBusMessage *m;
+ pa_assert(y);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
+ send_and_add_to_pending(y, NULL, m, list_adapters_reply);
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
+ DBusError err;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(bus);
+ pa_assert(m);
+
+ pa_assert_se(y = userdata);
+
+ dbus_error_init(&err);
+
+ pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+ dbus_message_get_interface(m),
+ dbus_message_get_path(m),
+ dbus_message_get_member(m));
+
+ if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceRemoved")) {
+ const char *path;
+ pa_bluetooth_device *d;
+
+ if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log("Failed to parse org.bluez.Adapter.DeviceRemoved: %s", err.message);
+ goto fail;
+ }
+
+ pa_log_debug("Device %s removed", path);
+
+ if ((d = pa_hashmap_remove(y->devices, path))) {
+
+ pa_assert_se(y->mode == MODE_DISCOVER);
+ run_callback(y, d, FALSE);
+
+ pa_bluetooth_device_free(d);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceCreated")) {
+ const char *path;
+
+ if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log("Failed to parse org.bluez.Adapter.DeviceCreated: %s", err.message);
+ goto fail;
+ }
+
+ pa_log_debug("Device %s created", path);
+
+ found_device(y, path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.bluez.Manager", "AdapterAdded")) {
+ const char *path;
+
+ if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log("Failed to parse org.bluez.Manager.AdapterAdded: %s", err.message);
+ goto fail;
+ }
+
+ pa_log_debug("Adapter %s created", path);
+
+ found_adapter(y, path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
+ dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") ||
+ dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) {
+
+ pa_bluetooth_device *d;
+
+ if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
+ DBusMessageIter arg_i;
+
+ if (!dbus_message_iter_init(m, &arg_i)) {
+ pa_log("Failed to parse PropertyChanged: %s", err.message);
+ goto fail;
+ }
+
+ if (dbus_message_has_interface(m, "org.bluez.Device")) {
+ if (parse_device_property(y, d, &arg_i) < 0)
+ goto fail;
+
+ } else if (dbus_message_has_interface(m, "org.bluez.Headset")) {
+ if (parse_audio_property(y, &d->headset_connected, &arg_i) < 0)
+ goto fail;
+
+ } else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) {
+ if (parse_audio_property(y, &d->audio_sink_connected, &arg_i) < 0)
+ goto fail;
+ }
+
+ pa_assert_se(y->mode == MODE_DISCOVER);
+ run_callback(y, d, TRUE);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+fail:
+ dbus_error_free(&err);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+pa_bluetooth_device* pa_bluetooth_find_device(DBusConnection *c, const char* address) {
+ pa_bluetooth_discovery y;
+
+ memset(&y, 0, sizeof(y));
+ y.mode = MODE_FIND;
+ y.looking_for = address;
+ y.connection = c;
+ PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending);
+
+ list_adapters(&y);
+
+ pa_dbus_sync_pending_list(&y.pending);
+ pa_assert(!y.pending);
+
+ if (y.found_device) {
+ pa_assert(device_is_loaded(y.found_device));
+
+ if (!device_is_audio(y.found_device)) {
+ pa_bluetooth_device_free(y.found_device);
+ return NULL;
+ }
+ }
+
+ return y.found_device;
+}
+
+pa_bluetooth_device* pa_bluetooth_get_device(DBusConnection *c, const char* path) {
+ pa_bluetooth_discovery y;
+
+ memset(&y, 0, sizeof(y));
+ y.mode = MODE_GET;
+ y.connection = c;
+ PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending);
+
+ found_device(&y, path);
+
+ pa_dbus_sync_pending_list(&y.pending);
+ pa_assert(!y.pending);
+
+ if (y.found_device) {
+ pa_assert(device_is_loaded(y.found_device));
+
+ if (!device_is_audio(y.found_device)) {
+ pa_bluetooth_device_free(y.found_device);
+ return NULL;
+ }
+ }
+
+ return y.found_device;
+}
+
+pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetooth_device_callback_t cb, struct userdata *u) {
+ DBusError err;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(c);
+ pa_assert(cb);
+
+ dbus_error_init(&err);
+
+ y = pa_xnew0(pa_bluetooth_discovery, 1);
+ y->mode = MODE_DISCOVER;
+ y->connection = c;
+ y->callback = cb;
+ y->userdata = u;
+ y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
+
+ /* dynamic detection of bluetooth audio devices */
+ if (!dbus_connection_add_filter(c, filter_cb, y, NULL)) {
+ pa_log_error("Failed to add filter function");
+ goto fail;
+ }
+
+ if (pa_dbus_add_matches(
+ c, &err,
+ "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL) < 0) {
+ pa_log("Failed to add D-Bus matches: %s", err.message);
+ goto fail;
+ }
+
+ list_adapters(y);
+
+ return y;
+
+fail:
+ dbus_error_free(&err);
+ return NULL;
+}
+
+void pa_bluetooth_discovery_free(pa_bluetooth_discovery *y) {
+ pa_bluetooth_device *d;
+
+ pa_assert(y);
+
+ pa_dbus_free_pending_list(&y->pending);
+
+ if (y->devices) {
+ while ((d = pa_hashmap_steal_first(y->devices))) {
+ run_callback(y, d, FALSE);
+ pa_bluetooth_device_free(d);
+ }
+
+ pa_hashmap_free(y->devices, NULL, NULL);
+ }
+
+ if (y->connection) {
+ pa_dbus_remove_matches(y->connection,
+ "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL);
+
+ dbus_connection_remove_filter(y->connection, filter_cb, y);
+ }
+}
+
+void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) {
+ pa_assert(y);
+
+ pa_dbus_sync_pending_list(&y->pending);
+}
+
+const char*pa_bluetooth_get_form_factor(uint32_t class) {
+ unsigned i;
+ const char *r;
+
+ static const char * const table[] = {
+ [1] = "headset",
+ [2] = "hands-free",
+ [4] = "microphone",
+ [5] = "external-speakers",
+ [6] = "headphones",
+ [7] = "portable",
+ [8] = "car",
+ [10] = "hifi"
+ };
+
+ if (((class >> 8) & 31) != 4)
+ return NULL;
+
+ if ((i = (class >> 2) & 63) > PA_ELEMENTSOF(table))
+ r = NULL;
+ else
+ r = table[i];
+
+ if (!r)
+ pa_log_debug("Unknown Bluetooth minor device class %u", i);
+
+ return r;
+}
+
+char *pa_bluetooth_cleanup_name(const char *name) {
+ char *t, *s, *d;
+ pa_bool_t space = FALSE;
+
+ pa_assert(name);
+
+ while ((*name >= 1 && *name <= 32) || *name >= 127)
+ name++;
+
+ t = pa_xstrdup(name);
+
+ for (s = d = t; *s; s++) {
+
+ if (*s <= 32 || *s >= 127 || *s == '_') {
+ space = TRUE;
+ continue;
+ }
+
+ if (space) {
+ *(d++) = ' ';
+ space = FALSE;
+ }
+
+ *(d++) = *s;
+ }
+
+ *d = 0;
+
+ return t;
+}
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
new file mode 100644
index 00000000..2c3ec649
--- /dev/null
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -0,0 +1,81 @@
+#ifndef foobluetoothutilhfoo
+#define foobluetoothutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Joao Paulo Rechi Vita
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ 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 <dbus/dbus.h>
+
+#include <pulsecore/llist.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+typedef struct pa_bluetooth_uuid pa_bluetooth_uuid;
+typedef struct pa_bluetooth_device pa_bluetooth_device;
+typedef struct pa_bluetooth_discovery pa_bluetooth_discovery;
+
+struct userdata;
+
+struct pa_bluetooth_uuid {
+ char *uuid;
+ PA_LLIST_FIELDS(pa_bluetooth_uuid);
+};
+
+struct pa_bluetooth_device {
+ void *data; /* arbitrary information for the one owning the discovery object */
+
+ int device_info_valid; /* 0: no results yet; 1: good results; -1: bad results ... */
+ int audio_sink_info_valid; /* ... same here ... */
+ int headset_info_valid; /* ... and here */
+
+ /* Device information */
+ char *name;
+ char *path;
+ int paired;
+ char *alias;
+ int device_connected;
+ PA_LLIST_HEAD(pa_bluetooth_uuid, uuids);
+ char *address;
+ int class;
+ int trusted;
+
+ /* AudioSink information */
+ int audio_sink_connected;
+
+ /* Headset information */
+ int headset_connected;
+};
+
+void pa_bluetooth_device_free(pa_bluetooth_device *d);
+
+pa_bluetooth_device* pa_bluetooth_get_device(DBusConnection *c, const char* path);
+pa_bluetooth_device* pa_bluetooth_find_device(DBusConnection *c, const char* address);
+
+typedef void (*pa_bluetooth_device_callback_t)(struct userdata *u, pa_bluetooth_device *d, pa_bool_t good);
+pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetooth_device_callback_t cb, struct userdata *u);
+void pa_bluetooth_discovery_free(pa_bluetooth_discovery *d);
+void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *d);
+
+const char*pa_bluetooth_get_form_factor(uint32_t class);
+
+char *pa_bluetooth_cleanup_name(const char *name);
+
+#endif
diff --git a/src/modules/bluetooth/ipc.c b/src/modules/bluetooth/ipc.c
index 67785309..f14c92c4 100644
--- a/src/modules/bluetooth/ipc.c
+++ b/src/modules/bluetooth/ipc.c
@@ -2,7 +2,7 @@
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/src/modules/bluetooth/ipc.h b/src/modules/bluetooth/ipc.h
index 0e985c3a..4203150b 100644
--- a/src/modules/bluetooth/ipc.h
+++ b/src/modules/bluetooth/ipc.h
@@ -2,7 +2,7 @@
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -98,8 +98,6 @@ typedef struct {
#define BT_NEW_STREAM 2
#define BT_START_STREAM 3
#define BT_STOP_STREAM 4
-#define BT_SUSPEND_STREAM 5
-#define BT_RESUME_STREAM 6
#define BT_CONTROL 7
#define BT_CAPABILITIES_TRANSPORT_A2DP 0
@@ -120,7 +118,7 @@ struct bt_get_capabilities_req {
} __attribute__ ((packed));
/**
- * SBC Codec parameters as per A2DP profile 1.0 § 4.3
+ * SBC Codec parameters as per A2DP profile 1.0 § 4.3
*/
#define BT_A2DP_CODEC_SBC 0x00
@@ -198,7 +196,6 @@ typedef struct {
uint16_t sampling_rate;
} __attribute__ ((packed)) pcm_capabilities_t;
-
struct bt_get_capabilities_rsp {
bt_audio_msg_header_t h;
uint8_t data[0]; /* First codec_capabilities_t */
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 11ed7d33..d909d70d 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1,22 +1,22 @@
/***
- This file is part of PulseAudio.
+ This file is part of PulseAudio.
- Copyright 2008 Joao Paulo Rechi Vita
+ Copyright 2008 Joao Paulo Rechi Vita
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
+ PulseAudio is free software; you can redistribute it 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.
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ 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.
+ 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
@@ -44,14 +44,14 @@
#include <pulsecore/time-smoother.h>
#include <pulsecore/rtclock.h>
-#include "../dbus-util.h"
+#include <modules/dbus-util.h>
+
#include "module-bluetooth-device-symdef.h"
#include "ipc.h"
#include "sbc.h"
#include "rtp.h"
+#include "bluetooth-util.h"
-#define DEFAULT_SINK_NAME "bluetooth_sink"
-#define BUFFER_SIZE 2048
#define MAX_BITPOOL 64
#define MIN_BITPOOL 2U
#define SOL_SCO 17
@@ -63,30 +63,52 @@ PA_MODULE_DESCRIPTION("Bluetooth audio sink and source");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
- "sink_name=<name of the device> "
+ "name=<name for the card/sink/source, to be prefixed> "
+ "card_name=<name for the card> "
+ "sink_name=<name for the sink> "
+ "source_name=<name for the source> "
"address=<address of the device> "
"profile=<a2dp|hsp> "
"rate=<sample rate> "
"channels=<number of channels> "
"path=<device object path>");
-struct bt_a2dp {
+static const char* const valid_modargs[] = {
+ "name",
+ "card_name",
+ "sink_name",
+ "source_name",
+ "address",
+ "profile",
+ "rate",
+ "channels",
+ "path",
+ NULL
+};
+
+struct a2dp_info {
sbc_capabilities_t sbc_capabilities;
sbc_t sbc; /* Codec data */
pa_bool_t sbc_initialized; /* Keep track if the encoder is initialized */
size_t codesize; /* SBC codesize */
- unsigned samples; /* Number of encoded samples */
- uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
- size_t count; /* Codec transfer buffer counter */
- unsigned total_samples; /* Cumulative number of codec samples */
+ void* buffer; /* Codec transfer buffer */
+ size_t buffer_size; /* Size of the buffer */
+
uint16_t seq_num; /* Cumulative packet sequence */
- unsigned frame_count; /* Current frames in buffer*/
+};
+
+enum profile {
+ PROFILE_A2DP,
+ PROFILE_HSP,
+ PROFILE_OFF
};
struct userdata {
pa_core *core;
pa_module *module;
+
+ pa_card *card;
pa_sink *sink;
pa_source *source;
@@ -95,113 +117,145 @@ struct userdata {
pa_rtpoll_item *rtpoll_item;
pa_thread *thread;
- uint64_t offset;
- pa_smoother *smoother;
+ uint64_t read_index, write_index;
+ pa_usec_t started_at;
+ pa_smoother *read_smoother;
+
+ pa_memchunk write_memchunk;
- char *name;
- char *addr;
- char *profile;
- pa_sample_spec ss;
+ pa_sample_spec sample_spec;
- int audioservice_fd;
+ int service_fd;
int stream_fd;
- uint8_t transport;
- char *strtransport;
size_t link_mtu;
size_t block_size;
- pa_usec_t latency;
- struct bt_a2dp a2dp;
- char *path;
- pa_dbus_connection *conn;
-};
+ struct a2dp_info a2dp;
+ pa_dbus_connection *connection;
-static const char* const valid_modargs[] = {
- "sink_name",
- "address",
- "profile",
- "rate",
- "channels",
- "path",
- NULL
+ enum profile profile;
+
+ pa_modargs *modargs;
+
+ pa_bluetooth_device *device;
+
+ int write_type, read_type;
};
-static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
- int e;
- const char *type, *name;
- uint16_t length;
+static int service_send(int fd, const bt_audio_msg_header_t *msg) {
+ size_t length;
+ ssize_t r;
+
+ pa_assert(fd >= 0);
+ pa_assert(msg);
length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
- type = bt_audio_strtype(msg->type);
- name = bt_audio_strname(msg->name);
- pa_log_debug("sending: %s -> %s", type, name);
- if (send(sk, msg, length, 0) > 0)
- e = 0;
- else {
- e = -errno;
- pa_log_error("Error sending data to audio service: %s(%d)", pa_cstrerror(errno), errno);
- }
- return e;
+
+ pa_log_debug("Sending %s -> %s",
+ pa_strnull(bt_audio_strtype(msg->type)),
+ pa_strnull(bt_audio_strname(msg->name)));
+
+ if ((r = send(fd, msg, length, 0)) == (ssize_t) length)
+ return 0;
+
+ if (r < 0)
+ pa_log_error("Error sending data to audio service: %s", pa_cstrerror(errno));
+ else
+ pa_log_error("Short send()");
+
+ return -1;
}
-static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg, uint16_t expected_length) {
- int e;
- const char *type, *name;
- uint16_t length;
+static int service_recv(int fd, bt_audio_msg_header_t *msg, size_t expected_length) {
+ size_t length;
+ ssize_t r;
+
+ pa_assert(fd >= 0);
+ pa_assert(msg);
length = expected_length ? expected_length : BT_SUGGESTED_BUFFER_SIZE;
- pa_log_debug("trying to receive msg from audio service...");
- if (recv(sk, inmsg, length, 0) > 0) {
- type = bt_audio_strtype(inmsg->type);
- name = bt_audio_strname(inmsg->name);
- if (type && name) {
- pa_log_debug("Received: %s <- %s", type, name);
- e = 0;
+ pa_log_debug("Trying to receive message from audio service...");
+
+ r = recv(fd, msg, length, 0);
+
+ if (r > 0 && (r == (ssize_t) length || expected_length <= 0)) {
+
+ if ((size_t) r < sizeof(*msg)) {
+ pa_log_error("Packet read too small.");
+ return -1;
}
- else {
- e = -EINVAL;
- pa_log_error("Bogus message type %d name %d received from audio service",
- inmsg->type, inmsg->name);
+
+ if (r != msg->length) {
+ pa_log_error("Size read doesn't match header size.");
+ return -1;
}
+
+ pa_log_debug("Received %s <- %s",
+ pa_strnull(bt_audio_strtype(msg->type)),
+ pa_strnull(bt_audio_strname(msg->name)));
+ return 0;
}
- else {
- e = -errno;
- pa_log_error("Error receiving data from audio service: %s(%d)", pa_cstrerror(errno), errno);
- }
- return e;
+ if (r < 0)
+ pa_log_error("Error receiving data from audio service: %s", pa_cstrerror(errno));
+ else
+ pa_log_error("Short recv()");
+
+ return -1;
}
-static int bt_audioservice_expect(int sk, bt_audio_msg_header_t *rsp, uint8_t expected_name, uint16_t expected_length) {
- int e = bt_audioservice_recv(sk, rsp, expected_length);
+static int service_expect(int fd, bt_audio_msg_header_t *rsp, uint8_t expected_name, size_t expected_length) {
+ int r;
- if (e < 0) {
- if (rsp->name != expected_name) {
- e = -EINVAL;
- pa_log_error("Bogus message %s received while %s was expected",
- bt_audio_strname(rsp->name),
- bt_audio_strname(expected_name));
- }
+ pa_assert(fd >= 0);
+ pa_assert(rsp);
+
+ if ((r = service_recv(fd, rsp, expected_length)) < 0)
+ return r;
+
+ if (rsp->name != expected_name) {
+ pa_log_error("Bogus message %s received while %s was expected",
+ pa_strnull(bt_audio_strname(rsp->name)),
+ pa_strnull(bt_audio_strname(expected_name)));
+ return -1;
}
if (rsp->type == BT_ERROR) {
- bt_audio_error_t *error = (void *) rsp;
- pa_log_error("%s failed : %s(%d)", bt_audio_strname(rsp->name), pa_cstrerror(error->posix_errno), error->posix_errno);
- return -error->posix_errno;
+ bt_audio_error_t *error = (bt_audio_error_t *) rsp;
+ pa_log_error("%s failed: %s", pa_strnull(bt_audio_strname(rsp->name)), pa_cstrerror(error->posix_errno));
+ return -1;
}
- return e;
+ return 0;
}
-static int bt_parsecaps(struct userdata *u, struct bt_get_capabilities_rsp *rsp) {
- uint16_t bytes_left = rsp->h.length - sizeof(*rsp);
- codec_capabilities_t *codec = (void *) rsp->data;
+static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *rsp) {
+ uint16_t bytes_left;
+ const codec_capabilities_t *codec;
+
+ pa_assert(u);
+ pa_assert(rsp);
- u->transport = codec->transport;
+ bytes_left = rsp->h.length - sizeof(*rsp);
- if (codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP)
+ if (bytes_left < sizeof(codec_capabilities_t)) {
+ pa_log_error("Packet too small to store codec information.");
+ return -1;
+ }
+
+ codec = (codec_capabilities_t *) rsp->data; /** ALIGNMENT? **/
+
+ pa_log_debug("Payload size is %lu %lu", (unsigned long) bytes_left, (unsigned long) sizeof(*codec));
+
+ if ((u->profile == PROFILE_A2DP && codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP) ||
+ (u->profile == PROFILE_HSP && codec->transport != BT_CAPABILITIES_TRANSPORT_SCO)) {
+ pa_log_error("Got capabilities for wrong codec.");
+ return -1;
+ }
+
+ if (u->profile != PROFILE_A2DP)
return 0;
while (bytes_left > 0) {
@@ -209,96 +263,98 @@ static int bt_parsecaps(struct userdata *u, struct bt_get_capabilities_rsp *rsp)
break;
bytes_left -= codec->length;
- codec = (void *) (codec + codec->length);
+ codec = (const codec_capabilities_t*) ((const uint8_t*) codec + codec->length);
}
if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities))
- return -EINVAL;
+ return -1;
- memcpy(&u->a2dp.sbc_capabilities, codec, codec->length);
+ pa_assert(codec->type == BT_A2DP_CODEC_SBC);
+ memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities));
return 0;
}
-static int bt_getcaps(struct userdata *u) {
- int e;
+static int get_caps(struct userdata *u) {
union {
- bt_audio_msg_header_t rsp;
struct bt_get_capabilities_req getcaps_req;
struct bt_get_capabilities_rsp getcaps_rsp;
uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
- memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+ pa_assert(u);
+
+ memset(&msg, 0, sizeof(msg));
msg.getcaps_req.h.type = BT_REQUEST;
msg.getcaps_req.h.name = BT_GET_CAPABILITIES;
msg.getcaps_req.h.length = sizeof(msg.getcaps_req);
- strncpy(msg.getcaps_req.device, u->addr, 18);
- if (pa_streq(u->profile, "a2dp"))
+ pa_strlcpy(msg.getcaps_req.device, u->device->address, sizeof(msg.getcaps_req.device));
+ if (u->profile == PROFILE_A2DP)
msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
- 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);
- return -1;
+ pa_assert(u->profile == PROFILE_HSP);
+ msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO;
}
msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT;
- e = bt_audioservice_send(u->audioservice_fd, &msg.getcaps_req.h);
- if (e < 0) {
- pa_log_error("Failed to send GETCAPABILITIES_REQ");
- return e;
- }
+ if (service_send(u->service_fd, &msg.getcaps_req.h) < 0)
+ return -1;
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_GET_CAPABILITIES, 0);
- if (e < 0) {
- pa_log_error("Failed to expect for GETCAPABILITIES_RSP");
- return e;
- }
+ if (service_expect(u->service_fd, &msg.getcaps_rsp.h, BT_GET_CAPABILITIES, 0) < 0)
+ return -1;
- return bt_parsecaps(u, &msg.getcaps_rsp);
+ return parse_caps(u, &msg.getcaps_rsp);
}
-static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {
+static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
+
switch (freq) {
case BT_SBC_SAMPLING_FREQ_16000:
case BT_SBC_SAMPLING_FREQ_32000:
return 53;
+
case BT_SBC_SAMPLING_FREQ_44100:
+
switch (mode) {
case BT_A2DP_CHANNEL_MODE_MONO:
case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
return 31;
+
case BT_A2DP_CHANNEL_MODE_STEREO:
case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
return 53;
+
default:
pa_log_warn("Invalid channel mode %u", mode);
return 53;
}
+
case BT_SBC_SAMPLING_FREQ_48000:
+
switch (mode) {
case BT_A2DP_CHANNEL_MODE_MONO:
case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
return 29;
+
case BT_A2DP_CHANNEL_MODE_STEREO:
case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
return 51;
+
default:
pa_log_warn("Invalid channel mode %u", mode);
return 51;
}
+
default:
pa_log_warn("Invalid sampling freq %u", freq);
return 53;
}
}
-static int bt_a2dp_init(struct userdata *u) {
- sbc_capabilities_t *cap = &u->a2dp.sbc_capabilities;
- uint8_t max_bitpool, min_bitpool;
- unsigned i;
+static int setup_a2dp(struct userdata *u) {
+ sbc_capabilities_t *cap;
+ int i;
static const struct {
uint32_t rate;
@@ -310,32 +366,59 @@ static int bt_a2dp_init(struct userdata *u) {
{ 48000U, BT_SBC_SAMPLING_FREQ_48000 }
};
+ pa_assert(u);
+ pa_assert(u->profile == PROFILE_A2DP);
+
+ cap = &u->a2dp.sbc_capabilities;
+
/* Find the lowest freq that is at least as high as the requested
* sampling rate */
- for (i = 0; i < PA_ELEMENTSOF(freq_table); i++)
- if (freq_table[i].rate >= u->ss.rate || i == PA_ELEMENTSOF(freq_table)-1 ) {
- u->ss.rate = freq_table[i].rate;
+ for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
+ if (freq_table[i].rate >= u->sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
+ u->sample_spec.rate = freq_table[i].rate;
cap->frequency = freq_table[i].cap;
break;
}
- if (u->ss.channels >= 2) {
+ if ((unsigned) i >= PA_ELEMENTSOF(freq_table)) {
+ for (; i >= 0; i--) {
+ if (cap->frequency & freq_table[i].cap) {
+ u->sample_spec.rate = freq_table[i].rate;
+ cap->frequency = freq_table[i].cap;
+ break;
+ }
+ }
+
+ if (i < 0) {
+ pa_log("Not suitable sample rate");
+ return -1;
+ }
+ }
+
+ if (u->sample_spec.channels <= 1) {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ u->sample_spec.channels = 1;
+ } else
+ u->sample_spec.channels = 2;
+ }
+
+ if (u->sample_spec.channels >= 2) {
+ u->sample_spec.channels = 2;
+
if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
-
- u->ss.channels = 2;
- } else {
- if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
- }
-
- if (!cap->channel_mode) {
- pa_log_error("No supported channel modes");
- return -1;
+ u->sample_spec.channels = 1;
+ } else {
+ pa_log("No supported channel modes");
+ return -1;
+ }
}
if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
@@ -365,17 +448,18 @@ static int bt_a2dp_init(struct userdata *u) {
else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
- min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
- max_bitpool = (uint8_t) PA_MIN(default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
-
- cap->min_bitpool = (uint8_t) min_bitpool;
- cap->max_bitpool = (uint8_t) max_bitpool;
+ cap->min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
+ cap->max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
return 0;
}
-static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
- sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities;
+static void setup_sbc(struct a2dp_info *a2dp) {
+ sbc_capabilities_t *active_capabilities;
+
+ pa_assert(a2dp);
+
+ active_capabilities = &a2dp->sbc_capabilities;
if (a2dp->sbc_initialized)
sbc_reinit(&a2dp->sbc, 0);
@@ -383,42 +467,63 @@ static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
sbc_init(&a2dp->sbc, 0);
a2dp->sbc_initialized = TRUE;
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000)
- a2dp->sbc.frequency = SBC_FREQ_16000;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000)
- a2dp->sbc.frequency = SBC_FREQ_32000;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100)
- a2dp->sbc.frequency = SBC_FREQ_44100;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000)
- a2dp->sbc.frequency = SBC_FREQ_48000;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
- a2dp->sbc.mode = SBC_MODE_MONO;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
- a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
- a2dp->sbc.mode = SBC_MODE_STEREO;
+ switch (active_capabilities->frequency) {
+ case BT_SBC_SAMPLING_FREQ_16000:
+ a2dp->sbc.frequency = SBC_FREQ_16000;
+ break;
+ case BT_SBC_SAMPLING_FREQ_32000:
+ a2dp->sbc.frequency = SBC_FREQ_32000;
+ break;
+ case BT_SBC_SAMPLING_FREQ_44100:
+ a2dp->sbc.frequency = SBC_FREQ_44100;
+ break;
+ case BT_SBC_SAMPLING_FREQ_48000:
+ a2dp->sbc.frequency = SBC_FREQ_48000;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
- a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+ switch (active_capabilities->channel_mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ a2dp->sbc.mode = SBC_MODE_MONO;
+ break;
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+ break;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ a2dp->sbc.mode = SBC_MODE_STEREO;
+ break;
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
- a2dp->sbc.allocation = (uint8_t) (active_capabilities.allocation_method == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR : SBC_AM_LOUDNESS);
+ switch (active_capabilities->allocation_method) {
+ case BT_A2DP_ALLOCATION_SNR:
+ a2dp->sbc.allocation = SBC_AM_SNR;
+ break;
+ case BT_A2DP_ALLOCATION_LOUDNESS:
+ a2dp->sbc.allocation = SBC_AM_LOUDNESS;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
- switch (active_capabilities.subbands) {
+ switch (active_capabilities->subbands) {
case BT_A2DP_SUBBANDS_4:
a2dp->sbc.subbands = SBC_SB_4;
break;
case BT_A2DP_SUBBANDS_8:
a2dp->sbc.subbands = SBC_SB_8;
break;
+ default:
+ pa_assert_not_reached();
}
- switch (active_capabilities.block_length) {
+ switch (active_capabilities->block_length) {
case BT_A2DP_BLOCK_LENGTH_4:
a2dp->sbc.blocks = SBC_BLK_4;
break;
@@ -431,83 +536,82 @@ static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
case BT_A2DP_BLOCK_LENGTH_16:
a2dp->sbc.blocks = SBC_BLK_16;
break;
+ default:
+ pa_assert_not_reached();
}
- a2dp->sbc.bitpool = active_capabilities.max_bitpool;
+ a2dp->sbc.bitpool = active_capabilities->max_bitpool;
a2dp->codesize = (uint16_t) sbc_get_codesize(&a2dp->sbc);
- a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
}
-static int bt_setconf(struct userdata *u) {
- int e;
+static int set_conf(struct userdata *u) {
union {
- bt_audio_msg_header_t rsp;
struct bt_set_configuration_req setconf_req;
struct bt_set_configuration_rsp setconf_rsp;
uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- e = bt_a2dp_init(u);
- if (e < 0) {
- pa_log_error("a2dp_init error");
- return e;
- }
- u->ss.format = PA_SAMPLE_S16LE;
- }
- else {
- u->ss.format = PA_SAMPLE_S16LE;
- u->ss.channels = 1;
- u->ss.rate = 8000;
+ if (u->profile == PROFILE_A2DP ) {
+ u->sample_spec.format = PA_SAMPLE_S16LE;
+
+ if (setup_a2dp(u) < 0)
+ return -1;
+ } else {
+ pa_assert(u->profile == PROFILE_HSP);
+
+ u->sample_spec.format = PA_SAMPLE_S16LE;
+ u->sample_spec.channels = 1;
+ u->sample_spec.rate = 8000;
}
- memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+ memset(&msg, 0, sizeof(msg));
msg.setconf_req.h.type = BT_REQUEST;
msg.setconf_req.h.name = BT_SET_CONFIGURATION;
msg.setconf_req.h.length = sizeof(msg.setconf_req);
- strncpy(msg.setconf_req.device, u->addr, 18);
- msg.setconf_req.codec.transport = u->transport;
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities,
- sizeof(u->a2dp.sbc_capabilities));
- msg.setconf_req.h.length += msg.setconf_req.codec.length
- - sizeof(msg.setconf_req.codec);
+ pa_strlcpy(msg.setconf_req.device, u->device->address, sizeof(msg.setconf_req.device));
+ msg.setconf_req.access_mode = u->profile == PROFILE_A2DP ? BT_CAPABILITIES_ACCESS_MODE_WRITE : BT_CAPABILITIES_ACCESS_MODE_READWRITE;
+
+ msg.setconf_req.codec.transport = u->profile == PROFILE_A2DP ? BT_CAPABILITIES_TRANSPORT_A2DP : BT_CAPABILITIES_TRANSPORT_SCO;
+
+ if (u->profile == PROFILE_A2DP) {
+ memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities, sizeof(u->a2dp.sbc_capabilities));
+ msg.setconf_req.h.length += msg.setconf_req.codec.length - sizeof(msg.setconf_req.codec);
}
- msg.setconf_req.access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
- e = bt_audioservice_send(u->audioservice_fd, &msg.setconf_req.h);
- if (e < 0) {
- pa_log_error("Failed to send BT_SETCONFIGURATION_REQ");
- return e;
+ if (service_send(u->service_fd, &msg.setconf_req.h) < 0)
+ return -1;
+
+ if (service_expect(u->service_fd, &msg.setconf_rsp.h, BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp)) < 0)
+ return -1;
+
+ if ((u->profile == PROFILE_A2DP && msg.setconf_rsp.transport != BT_CAPABILITIES_TRANSPORT_A2DP) ||
+ (u->profile == PROFILE_HSP && msg.setconf_rsp.transport != BT_CAPABILITIES_TRANSPORT_SCO)) {
+ pa_log("Transport doesn't match what we requested.");
+ return -1;
}
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp));
- if (e < 0) {
- pa_log_error("Failed to expect BT_SETCONFIGURATION_RSP");
- return e;
+ if ((u->profile == PROFILE_A2DP && msg.setconf_rsp.access_mode != BT_CAPABILITIES_ACCESS_MODE_WRITE) ||
+ (u->profile == PROFILE_HSP && msg.setconf_rsp.access_mode != BT_CAPABILITIES_ACCESS_MODE_READWRITE)) {
+ pa_log("Access mode doesn't match what we requested.");
+ return -1;
}
- u->transport = msg.setconf_rsp.transport;
- u->strtransport = (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ? pa_xstrdup("A2DP") : pa_xstrdup("SCO"));
u->link_mtu = msg.setconf_rsp.link_mtu;
/* setup SBC encoder now we agree on parameters */
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- bt_a2dp_setup(&u->a2dp);
+ if (u->profile == PROFILE_A2DP) {
+ setup_sbc(&u->a2dp);
u->block_size = u->a2dp.codesize;
- pa_log_info("sbc parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
- u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
- }
- else
+ pa_log_info("SBC parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+ u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
+ } else
u->block_size = u->link_mtu;
return 0;
}
-static int bt_getstreamfd(struct userdata *u) {
- int e;
-// uint32_t period_count = io->buffer_size / io->period_size;
+static int setup_stream_fd(struct userdata *u) {
union {
bt_audio_msg_header_t rsp;
struct bt_start_stream_req start_req;
@@ -516,77 +620,68 @@ static int bt_getstreamfd(struct userdata *u) {
uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
+ pa_assert(u);
+ pa_assert(u->stream_fd < 0);
+
memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
msg.start_req.h.type = BT_REQUEST;
msg.start_req.h.name = BT_START_STREAM;
msg.start_req.h.length = sizeof(msg.start_req);
- e = bt_audioservice_send(u->audioservice_fd, &msg.start_req.h);
- if (e < 0) {
- pa_log_error("Failed to send BT_STREAMSTART_REQ");
- return e;
- }
-
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_START_STREAM, sizeof(msg.start_rsp));
- if (e < 0) {
- pa_log_error("Failed to expect BT_STREAMSTART_RSP");
- return e;
- }
+ if (service_send(u->service_fd, &msg.start_req.h) < 0)
+ return -1;
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_NEW_STREAM, sizeof(msg.streamfd_ind));
- if (e < 0) {
- pa_log_error("Failed to expect BT_STREAMFD_IND");
- return e;
- }
+ if (service_expect(u->service_fd, &msg.rsp, BT_START_STREAM, sizeof(msg.start_rsp)) < 0)
+ return -1;
- if (u->stream_fd >= 0)
- pa_close(u->stream_fd);
+ if (service_expect(u->service_fd, &msg.rsp, BT_NEW_STREAM, sizeof(msg.streamfd_ind)) < 0)
+ return -1;
- u->stream_fd = bt_audio_service_get_data_fd(u->audioservice_fd);
- if (u->stream_fd < 0) {
- pa_log_error("Failed to get data fd: %s (%d)",pa_cstrerror(errno), errno);
- return -errno;
+ if ((u->stream_fd = bt_audio_service_get_data_fd(u->service_fd)) < 0) {
+ pa_log("Failed to get stream fd from audio service.");
+ return -1;
}
-// if (setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)) == 0)
-// return 0;
-// if (setsockopt(u->stream_fd, SOL_SCO, SO_SNDBUF, &period_count, sizeof(period_count)) == 0)
-// return 0;
-// /* FIXME : handle error codes */
+/* setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)); */
+/* setsockopt(u->stream_fd, SOL_SCO, SCO_SNDBUF, &period_count, sizeof(period_count)); */
+
pa_make_fd_nonblock(u->stream_fd);
-// pa_make_socket_low_delay(u->stream_fd);
+ pa_make_socket_low_delay(u->stream_fd);
return 0;
}
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
+ pa_assert(u->sink == PA_SINK(o));
pa_log_debug("got message: %d", code);
switch (code) {
case PA_SINK_MESSAGE_SET_STATE:
+
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
case PA_SINK_SUSPENDED:
pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
- pa_smoother_pause(u->smoother, pa_rtclock_usec());
break;
+
case PA_SINK_IDLE:
case PA_SINK_RUNNING:
- if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
- pa_smoother_resume(u->smoother, pa_rtclock_usec());
+ if (!PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ u->started_at = pa_rtclock_usec();
+
break;
+
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
- pa_usec_t w, r;
-/* r = pa_smoother_get(u->smoother, pa_rtclock_usec()); */
-/* /\* w = pa_bytes_to_usec(u->offset + (uint64_t) u->memchunk.length, &u->sink->sample_spec); *\/ */
- *((pa_usec_t*) data) = /*w > r ? w - r :*/ 0;
+ *((pa_usec_t*) data) = 0;
return 0;
}
@@ -595,129 +690,254 @@ 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 sco_process_render(struct userdata *u) {
- void *p;
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SOURCE(o)->userdata;
+
+ pa_assert(u->source == PA_SOURCE(o));
+
+ pa_log_debug("got message: %d", code);
+ switch (code) {
+
+ case PA_SOURCE_MESSAGE_SET_STATE:
+
+ switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+
+ case PA_SOURCE_SUSPENDED:
+ pa_smoother_pause(u->read_smoother, pa_rtclock_usec());
+ break;
+
+ case PA_SOURCE_IDLE:
+ case PA_SOURCE_RUNNING:
+ if (u->source->thread_info.state == PA_SOURCE_SUSPENDED)
+ pa_smoother_resume(u->read_smoother, pa_rtclock_usec());
+ break;
+
+ case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
+ ;
+ }
+ break;
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ *((pa_usec_t*) data) = 0;
+ return 0;
+ }
+
+ }
+
+ return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static int hsp_process_render(struct userdata *u) {
int ret = 0;
pa_memchunk memchunk;
- pa_sink_render_full(u->sink, u->block_size, &memchunk);
+ pa_assert(u);
+ pa_assert(u->profile == PROFILE_HSP);
+ pa_assert(u->sink);
- p = pa_memblock_acquire(memchunk.memblock);
+ pa_sink_render_full(u->sink, u->block_size, &memchunk);
for (;;) {
ssize_t l;
+ const void *p;
- l = pa_loop_write(u->stream_fd, p, memchunk.length, NULL);
- pa_log_debug("Memblock written to socket: %li bytes", (long) l);
+ p = (const uint8_t*) pa_memblock_acquire(memchunk.memblock) + memchunk.index;
+ l = pa_write(u->stream_fd, p, memchunk.length, &u->write_type);
+ pa_memblock_release(memchunk.memblock);
+
+ pa_log_debug("Memblock written to socket: %lli bytes", (long long) l);
pa_assert(l != 0);
- if (l > 0) {
- u->offset += (uint64_t) l;
- break;
+ if (l < 0) {
+ if (errno == EINTR)
+ continue;
+ else {
+ pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno));
+ ret = -1;
+ break;
+ }
+ } else {
+ pa_assert((size_t) l <= memchunk.length);
+
+ memchunk.index += (size_t) l;
+ memchunk.length -= (size_t) l;
+
+ u->write_index += (uint64_t) l;
+
+ if (memchunk.length <= 0)
+ break;
}
+ }
+
+ pa_memblock_unref(memchunk.memblock);
+
+ return ret;
+}
- if (errno == EINTR)
- pa_log_debug("EINTR");
- else if (errno == EAGAIN)
- pa_log_debug("EAGAIN");
- else {
- pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- ret = -1;
+static int hsp_process_push(struct userdata *u) {
+ int ret = 0;
+ pa_memchunk memchunk;
+
+ pa_assert(u);
+ pa_assert(u->profile == PROFILE_HSP);
+ pa_assert(u->source);
+
+ memchunk.memblock = pa_memblock_new(u->core->mempool, u->block_size);
+ memchunk.index = memchunk.length = 0;
+
+ for (;;) {
+ ssize_t l;
+ void *p;
+
+ p = pa_memblock_acquire(memchunk.memblock);
+ l = pa_read(u->stream_fd, p, pa_memblock_get_length(memchunk.memblock), &u->read_type);
+ pa_memblock_release(memchunk.memblock);
+
+ if (l <= 0) {
+ if (l < 0 && errno == EINTR)
+ continue;
+ else {
+ pa_log_error("Failed to read data from SCO socket: %s", ret < 0 ? pa_cstrerror(errno) : "EOF");
+ ret = -1;
+ break;
+ }
+ } else {
+ memchunk.length = (size_t) l;
+ u->read_index += (uint64_t) l;
+
+ pa_source_post(u->source, &memchunk);
break;
}
}
- pa_memblock_release(memchunk.memblock);
pa_memblock_unref(memchunk.memblock);
return ret;
}
static int a2dp_process_render(struct userdata *u) {
+ size_t frame_size;
+ struct a2dp_info *a2dp;
+ struct rtp_header *header;
+ struct rtp_payload *payload;
+ size_t left;
+ void *d;
+ const void *p;
+ unsigned frame_count;
int written;
-
- struct bt_a2dp *a2dp = &u->a2dp;
- struct rtp_header *header = (void *) a2dp->buffer;
- struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
+ uint64_t writing_at;
pa_assert(u);
+ pa_assert(u->profile == PROFILE_A2DP);
+ pa_assert(u->sink);
- do {
- /* Render some data */
- int frame_size, encoded;
- void *p;
- pa_memchunk memchunk;
+ a2dp = &u->a2dp;
- pa_sink_render_full(u->sink, u->block_size, &memchunk);
+ if (a2dp->buffer_size < u->link_mtu) {
+ a2dp->buffer_size = 2*u->link_mtu;
+ pa_xfree(a2dp->buffer);
+ a2dp->buffer = pa_xmalloc(a2dp->buffer_size);
+ }
- p = pa_memblock_acquire(memchunk.memblock);
+ header = (struct rtp_header*) a2dp->buffer;
+ payload = (struct rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header));
+ d = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload);
+ left = a2dp->buffer_size - sizeof(*header) - sizeof(*payload);
- frame_size = (uint16_t) sbc_get_frame_length(&a2dp->sbc);
- pa_log_debug("SBC frame_size: %d", frame_size);
+ frame_size = sbc_get_frame_length(&a2dp->sbc);
+ frame_count = 0;
- encoded = sbc_encode(&a2dp->sbc, p, (int) a2dp->codesize, a2dp->buffer + a2dp->count,
- (int) (sizeof(a2dp->buffer) - a2dp->count), &written);
- pa_log_debug("SBC: encoded: %d; written: %d", encoded, written);
+ writing_at = u->write_index;
- pa_memblock_release(memchunk.memblock);
- pa_memblock_unref(memchunk.memblock);
+ do {
+ int encoded;
+
+ if (!u->write_memchunk.memblock)
+ pa_sink_render_full(u->sink, u->block_size, &u->write_memchunk);
+
+ p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index;
+ encoded = sbc_encode(&a2dp->sbc,
+ (void*) p, u->write_memchunk.length,
+ d, left,
+ &written);
+ pa_memblock_release(u->write_memchunk.memblock);
if (encoded <= 0) {
pa_log_error("SBC encoding error (%d)", encoded);
return -1;
}
- a2dp->count += (size_t) written;
- a2dp->frame_count++;
- a2dp->samples += (unsigned) encoded / frame_size;
- a2dp->total_samples += (unsigned) encoded / frame_size;
+ pa_assert(written >= 0);
- } while (a2dp->count + (size_t) written <= u->link_mtu);
+ pa_assert((size_t) encoded <= u->write_memchunk.length);
+ pa_assert((size_t) written <= left);
+
+/* pa_log_debug("SBC: encoded: %d; written: %d", encoded, written); */
+
+ u->write_memchunk.index += encoded;
+ u->write_memchunk.length -= encoded;
+
+ if (u->write_memchunk.length <= 0) {
+ pa_memblock_unref(u->write_memchunk.memblock);
+ pa_memchunk_reset(&u->write_memchunk);
+ }
+
+ u->write_index += encoded;
+
+ d = (uint8_t*) d + written;
+ left -= written;
+
+ frame_count++;
+
+ } while ((uint8_t*) d - (uint8_t*) a2dp->buffer + written < (ptrdiff_t) u->link_mtu);
/* write it to the fifo */
memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
- payload->frame_count = a2dp->frame_count;
+ payload->frame_count = frame_count;
header->v = 2;
header->pt = 1;
- header->sequence_number = htons(a2dp->seq_num);
- header->timestamp = htonl(a2dp->total_samples);
+ header->sequence_number = htons(a2dp->seq_num++);
+ header->timestamp = htonl(writing_at / frame_size);
header->ssrc = htonl(1);
+ p = a2dp->buffer;
+ left = (uint8_t*) d - (uint8_t*) a2dp->buffer;
+
for (;;) {
ssize_t l;
- l = pa_loop_write(u->stream_fd, a2dp->buffer, a2dp->count, NULL);
- pa_log_debug("avdtp_write: requested %lu bytes; written %li bytes", (unsigned long) a2dp->count, (long) l);
+ l = pa_write(u->stream_fd, p, left, &u->write_type);
+/* pa_log_debug("write: requested %lu bytes; written %li bytes; mtu=%li", (unsigned long) left, (long) l, (unsigned long) u->link_mtu); */
pa_assert(l != 0);
- if (l > 0)
- break;
+ if (l < 0) {
+ if (errno == EINTR)
+ continue;
+ else {
+ pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno));
+ return -1;
+ }
+ } else {
+ pa_assert((size_t) l <= left);
- if (errno == EINTR)
- pa_log_debug("EINTR");
- else if (errno == EAGAIN)
- pa_log_debug("EAGAIN");
- else {
- pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- return -1;
+ d = (uint8_t*) d + l;
+ left -= l;
+
+ if (left <= 0)
+ break;
}
}
- u->offset += a2dp->codesize*a2dp->frame_count;
-
- /* Reset buffer of data to send */
- a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
- a2dp->frame_count = 0;
- a2dp->samples = 0;
- a2dp->seq_num++;
-
return 0;
}
static void thread_func(void *userdata) {
struct userdata *u = userdata;
+ pa_bool_t do_write = FALSE, writable = FALSE;
pa_assert(u);
@@ -729,59 +949,93 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
- pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+ pa_smoother_set_time_offset(u->read_smoother, pa_rtclock_usec());
for (;;) {
- int ret, l;
struct pollfd *pollfd;
- uint64_t n;
- pa_usec_t usec;
-
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
- if (u->sink->thread_info.rewind_requested)
- pa_sink_process_rewind(u->sink, 0);
+ int ret;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- if ((l = a2dp_process_render(u)) < 0)
- goto fail;
- } else {
- if ((l = sco_process_render(u)) < 0)
+ if (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state)) {
+
+ if (pollfd->revents & POLLIN) {
+
+ if (hsp_process_push(u) < 0)
goto fail;
+
+ /* We just read something, so we are supposed to write something, too */
+ do_write = TRUE;
}
- pollfd->revents = 0;
-
- /* feed the time smoother */
- n = u->offset;
- if (ioctl(u->stream_fd, SIOCOUTQ, &l) >= 0 && l > 0)
- n -= (uint64_t) l;
- usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
- if (usec > u->latency)
- usec -= u->latency;
- else
- usec = 0;
- pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
}
+ if (u->sink && PA_SINK_IS_LINKED(u->sink->thread_info.state)) {
+
+ if (u->sink->thread_info.rewind_requested)
+ pa_sink_process_rewind(u->sink, 0);
+
+ if (pollfd->revents & POLLOUT)
+ writable = TRUE;
+
+ if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && !do_write && writable) {
+ pa_usec_t time_passed;
+ uint64_t should_have_written;
+
+ /* Hmm, there is no input stream we could synchronize
+ * to. So let's do things by time */
+
+ time_passed = pa_rtclock_usec() - u->started_at;
+ should_have_written = pa_usec_to_bytes(time_passed, &u->sink->sample_spec);
+
+ do_write = u->write_index <= should_have_written ;
+/* pa_log_debug("Time has come: %s", pa_yes_no(do_write)); */
+ }
+
+ if (writable && do_write) {
+
+ if (u->profile == PROFILE_A2DP) {
+ if (a2dp_process_render(u) < 0)
+ goto fail;
+ } else {
+ if (hsp_process_render(u) < 0)
+ goto fail;
+ }
+
+ do_write = FALSE;
+ writable = FALSE;
+ }
+
+ if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && !do_write) {
+ pa_usec_t time_passed, next_write_at, sleep_for;
+
+ /* Hmm, there is no input stream we could synchronize
+ * to. So let's estimate when we need to wake up the latest */
+
+ time_passed = pa_rtclock_usec() - u->started_at;
+ next_write_at = pa_bytes_to_usec(u->write_index, &u->sink->sample_spec);
+ sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0;
+
+/* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */
+
+ pa_rtpoll_set_timer_relative(u->rtpoll, sleep_for);
+ }
+ } else
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
+
/* Hmm, nothing to do. Let's sleep */
- pa_log_debug("IO thread going to sleep");
- pollfd->events = (short) (PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0);
- if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
- pa_log_error("rtpoll_run < 0");
+ pollfd->events = (short) (((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && !writable) ? POLLOUT : 0) |
+ (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) ? POLLIN : 0));
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
- }
- pa_log_debug("IO thread waking up");
- if (ret == 0) {
- pa_log_debug("rtpoll_run == 0");
+ if (ret == 0)
goto finish;
- }
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- if (pollfd->revents & ~POLLOUT) {
- pa_log_error("FIFO shutdown.");
+
+ if (pollfd->revents & ~(POLLOUT|POLLIN)) {
+ pa_log_error("FD error.");
goto fail;
}
}
@@ -796,324 +1050,654 @@ 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;
+/* 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_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); */
- 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));
+/* if (!dbus_message_has_path(msg, u->path)) */
+/* goto done; */
+
+/* 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; */
- dbus_error_init(&err);
+/* if (!dbus_message_iter_init(msg, &arg_i)) { */
+/* pa_log("dbus: message has no parameters"); */
+/* goto done; */
+/* } */
- if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") ||
- dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) {
+/* if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_STRING) { */
+/* pa_log("Property name not a string."); */
+/* goto done; */
+/* } */
- struct device *d;
- const char *profile;
- DBusMessageIter variant_i;
- dbus_uint16_t gain;
+/* dbus_message_iter_get_basic(&arg_i, &value); */
- if (!dbus_message_iter_init(msg, &arg_i)) {
- pa_log("dbus: message has no parameters");
- goto done;
- }
+/* 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_STRING) {
- pa_log("Property name not a string.");
- 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_get_basic(&arg_i, &value);
+/* dbus_message_iter_recurse(&arg_i, &variant_i); */
- if (!dbus_message_iter_next(&arg_i)) {
- pa_log("Property value missing");
- goto done;
- }
+/* if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) { */
+/* dbus_message_iter_get_basic(&variant_i, &gain); */
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_VARIANT) {
- pa_log("Property value not a variant.");
- goto done;
- }
+/* if (pa_streq(value, "SpeakerGain")) { */
+/* pa_log("spk gain: %d", gain); */
+/* pa_cvolume_set(&u->sink->virtual_volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); */
+/* 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; */
- dbus_message_iter_recurse(&arg_i, &variant_i);
+/* pa_cvolume_set(&u->source->virtual_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); */
+/* } */
+/* } */
+/* } */
- if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) {
- dbus_message_iter_get_basic(&variant_i, &gain);
+/* done: */
+/* dbus_error_free(&err); */
+/* return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; */
+/* } */
- if (pa_streq(value, "SpeakerGain")) {
- pa_log("spk gain: %d", gain);
- pa_cvolume_set(&u->sink->virtual_volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
- 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;
+/* static int sink_get_volume_cb(pa_sink *s) { */
+/* struct userdata *u = s->userdata; */
+/* pa_assert(u); */
- pa_cvolume_set(&u->source->virtual_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);
- }
- }
+/* /\* 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->virtual_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->virtual_volume) / PA_VOLUME_NORM) * 15; */
+
+/* pa_log_debug("set headset mic volume: %d (not implemented yet)", vol); */
+
+/* return 0; */
+/* } */
+
+static char *get_name(const char *type, pa_modargs *ma, const char *device_id, pa_bool_t *namereg_fail) {
+ char *t;
+ const char *n;
+
+ pa_assert(type);
+ pa_assert(ma);
+ pa_assert(device_id);
+ pa_assert(namereg_fail);
+
+ t = pa_sprintf_malloc("%s_name", type);
+ n = pa_modargs_get_value(ma, t, NULL);
+ pa_xfree(t);
+
+ if (n) {
+ *namereg_fail = TRUE;
+ return pa_xstrdup(n);
}
-done:
- dbus_error_free(&err);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ if ((n = pa_modargs_get_value(ma, "name", NULL)))
+ *namereg_fail = TRUE;
+ else {
+ n = device_id;
+ *namereg_fail = FALSE;
+ }
+
+ return pa_sprintf_malloc("bluez_%s.%s", type, n);
}
-static int sink_get_volume_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
+static int add_sink(struct userdata *u) {
+ pa_sink_new_data data;
+ pa_bool_t b;
+
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = u->module;
+ pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
+ pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
+ data.card = u->card;
+ data.name = get_name("sink", u->modargs, u->device->address, &b);
+ data.namereg_fail = b;
+
+ u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log_error("Failed to create sink");
+ return -1;
+ }
+
+ u->sink->userdata = u;
+ u->sink->parent.process_msg = sink_process_msg;
+/* u->sink->get_volume = sink_get_volume_cb; */
+/* u->sink->set_volume = sink_set_volume_cb; */
+
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+ return 0;
+}
+
+static int add_source(struct userdata *u) {
+ pa_source_new_data data;
+ pa_bool_t b;
+
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = u->module;
+ pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
+ pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
+ data.card = u->card;
+ data.name = get_name("source", u->modargs, u->device->address, &b);
+ data.namereg_fail = b;
+
+ u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&data);
+
+ if (!u->source) {
+ pa_log_error("Failed to create source");
+ return -1;
+ }
+
+ u->source->userdata = u;
+ u->source->parent.process_msg = source_process_msg;
+/* u->source->get_volume = source_get_volume_cb; */
+/* u->source->set_volume = source_set_volume_cb; */
+
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
+
+ return 0;
+}
+
+static int init_bt(struct userdata *u) {
pa_assert(u);
- /* refresh? */
+ /* connect to the bluez audio service */
+ if ((u->service_fd = bt_audio_service_open()) < 0) {
+ pa_log_error("Couldn't connect to bluetooth audio service");
+ return -1;
+ }
+ pa_log_debug("Connected to the bluetooth audio service");
return 0;
}
-static int source_get_volume_cb(pa_source *s) {
- struct userdata *u = s->userdata;
+static void shutdown_bt(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->stream_fd <= 0) {
+ pa_close(u->stream_fd);
+ u->stream_fd = -1;
+ }
+
+ u->write_type = u->read_type = 0;
+}
+
+static int setup_bt(struct userdata *u) {
pa_assert(u);
- /* refresh? */
+ if (get_caps(u) < 0)
+ return -1;
+
+ pa_log_debug("Got device capabilities");
+
+ if (set_conf(u) < 0)
+ return -1;
+
+ pa_log_debug("Connection to the device configured");
+
+ if (setup_stream_fd(u) < 0)
+ return -1;
+
+ pa_log_debug("Got the stream socket");
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;
+static int init_profile(struct userdata *u) {
+ int r = 0;
pa_assert(u);
- dbus_error_init(&e);
+ if (setup_bt(u) < 0)
+ return -1;
- vol = ((float) pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15;
- pa_log_debug("set headset volume: %d", vol);
+ if (u->profile == PROFILE_A2DP ||
+ u->profile == PROFILE_HSP)
+ if (add_sink(u) < 0)
+ r = -1;
- 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);
+ if (u->profile == PROFILE_HSP)
+ if (add_source(u) < 0)
+ r = -1;
- r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ return r;
+}
-finish:
- if (m)
- dbus_message_unref(m);
- if (r)
- dbus_message_unref(r);
+static void stop_thread(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ u->thread = NULL;
+ }
+
+ if (u->rtpoll_item) {
+ pa_rtpoll_item_free(u->rtpoll_item);
+ u->rtpoll_item = NULL;
+ }
- dbus_error_free(&e);
+ if (u->sink) {
+ pa_sink_unref(u->sink);
+ u->sink = NULL;
+ }
+
+ if (u->source) {
+ pa_source_unref(u->source);
+ u->source = NULL;
+ }
+}
+
+static int start_thread(struct userdata *u) {
+ struct pollfd *pollfd;
+
+ pa_assert(u);
+ pa_assert(!u->thread);
+ pa_assert(!u->rtpoll_item);
+
+ u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ pollfd->fd = u->stream_fd;
+ pollfd->events = pollfd->revents = 0;
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log_error("Failed to create IO thread");
+ stop_thread(u);
+ return -1;
+ }
+
+ if (u->sink)
+ pa_sink_put(u->sink);
+
+ if (u->source)
+ pa_source_put(u->source);
return 0;
}
-static int source_set_volume_cb(pa_source *s) {
- dbus_uint16_t vol;
- struct userdata *u = s->userdata;
+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+ struct userdata *u;
+ enum profile *d;
+ pa_queue *inputs = NULL, *outputs = NULL;
+
+ pa_assert(c);
+ pa_assert(new_profile);
+ pa_assert_se(u = c->userdata);
+
+ d = PA_CARD_PROFILE_DATA(new_profile);
+
+ if (u->sink) {
+ inputs = pa_sink_move_all_start(u->sink);
+ pa_sink_unlink(u->sink);
+ }
+
+ if (u->source) {
+ outputs = pa_source_move_all_start(u->source);
+ pa_source_unlink(u->source);
+ }
+
+ stop_thread(u);
+ shutdown_bt(u);
+
+ if (u->write_memchunk.memblock) {
+ pa_memblock_unref(u->write_memchunk.memblock);
+ pa_memchunk_reset(&u->write_memchunk);
+ }
+
+ u->profile = *d;
+ init_profile(u);
+
+ if (u->sink || u->source)
+ start_thread(u);
+
+ if (inputs) {
+ if (u->sink)
+ pa_sink_move_all_finish(u->sink, inputs, FALSE);
+ else
+ pa_sink_move_all_fail(inputs);
+ }
+
+ if (outputs) {
+ if (u->source)
+ pa_source_move_all_finish(u->source, outputs, FALSE);
+ else
+ pa_source_move_all_fail(outputs);
+ }
+
+ return 0;
+}
+
+static int add_card(struct userdata *u, const char * default_profile) {
+ pa_card_new_data data;
+ pa_bool_t b;
+ pa_card_profile *p;
+ enum profile *d;
+ const char *ff;
+ char *n;
+
+ pa_card_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = u->module;
+
+ n = pa_bluetooth_cleanup_name(u->device->name);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, n);
+ pa_xfree(n);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device->address);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "bluez");
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CONNECTOR, "bluetooth");
+ if ((ff = pa_bluetooth_get_form_factor(u->device->class)))
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, ff);
+ pa_proplist_sets(data.proplist, "bluez.path", u->device->path);
+ pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", (unsigned) u->device->class);
+ pa_proplist_sets(data.proplist, "bluez.name", u->device->name);
+ data.name = get_name("card", u->modargs, u->device->address, &b);
+ data.namereg_fail = b;
+
+ data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ if (u->device->audio_sink_info_valid > 0) {
+ p = pa_card_profile_new("a2dp", "A2DP Advanced Audio Distribution Profile", sizeof(enum profile));
+ p->priority = 10;
+ p->n_sinks = 1;
+ p->n_sources = 0;
+ p->max_sink_channels = 2;
+ p->max_source_channels = 0;
+
+ d = PA_CARD_PROFILE_DATA(p);
+ *d = PROFILE_A2DP;
+
+ pa_hashmap_put(data.profiles, p->name, p);
+ }
+
+ if (u->device->headset_info_valid > 0) {
+ p = pa_card_profile_new("hsp", "HSP/HFP Headset/Hands-Free Profile", sizeof(enum profile));
+ p->priority = 20;
+ p->n_sinks = 1;
+ p->n_sources = 1;
+ p->max_sink_channels = 1;
+ p->max_source_channels = 1;
+
+ d = PA_CARD_PROFILE_DATA(p);
+ *d = PROFILE_HSP;
+
+ pa_hashmap_put(data.profiles, p->name, p);
+ }
+
+ pa_assert(!pa_hashmap_isempty(data.profiles));
+
+ p = pa_card_profile_new("off", "Off", sizeof(enum profile));
+ d = PA_CARD_PROFILE_DATA(p);
+ *d = PROFILE_OFF;
+ pa_hashmap_put(data.profiles, p->name, p);
+
+ if (default_profile) {
+ if (pa_hashmap_get(data.profiles, default_profile))
+ pa_card_new_data_set_profile(&data, default_profile);
+ else
+ pa_log_warn("Profile '%s' not valid or not supported by device.", default_profile);
+ }
+
+ u->card = pa_card_new(u->core, &data);
+ pa_card_new_data_done(&data);
+
+ if (!u->card) {
+ pa_log("Failed to allocate card.");
+ return -1;
+ }
+
+ u->card->userdata = u;
+ u->card->set_profile = card_set_profile;
+
+ d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+ u->profile = *d;
+
+ return 0;
+}
+
+static int setup_dbus(struct userdata *u) {
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ u->connection = pa_dbus_bus_get(u->core, DBUS_BUS_SYSTEM, &error);
+ if (dbus_error_is_set(&error) || (!u->connection)) {
+ pa_log("Failed to get D-Bus connection: %s", error.message);
+ dbus_error_free(&error);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int find_device(struct userdata *u, const char *address, const char *path) {
pa_assert(u);
- vol = ((float)pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15;
+ if (!address && !path) {
+ pa_log_error("Failed to get device address/path from module arguments.");
+ return -1;
+ }
+
+ if (path) {
+ if (!(u->device = pa_bluetooth_get_device(pa_dbus_connection_get(u->connection), path))) {
+ pa_log_error("%s is not a valid BlueZ audio device.", path);
+ return -1;
+ }
- pa_log_debug("set headset mic volume: %d (not implemented yet)", vol);
+ if (address && !(pa_streq(u->device->address, address))) {
+ pa_log_error("Passed path %s and address %s don't match.", path, address);
+ return -1;
+ }
+ } else {
+ if (!(u->device = pa_bluetooth_find_device(pa_dbus_connection_get(u->connection), address))) {
+ pa_log_error("%s is not known.", address);
+ return -1;
+ }
+ }
return 0;
}
int pa__init(pa_module* m) {
- int e;
pa_modargs *ma;
uint32_t channels;
- pa_sink_new_data data;
- struct pollfd *pollfd;
struct userdata *u;
- DBusError err;
- char *tmp;
+ const char *address, *path;
pa_assert(m);
- dbus_error_init(&err);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log_error("Failed to parse module arguments");
+ goto fail;
+ }
m->userdata = u = pa_xnew0(struct userdata, 1);
u->module = m;
u->core = m->core;
- u->audioservice_fd = -1;
+ u->service_fd = -1;
u->stream_fd = -1;
- u->transport = (uint8_t) -1;
- u->offset = 0;
- u->latency = 0;
- u->a2dp.sbc_initialized = FALSE;
- u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+ u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
- u->rtpoll_item = NULL;
- u->ss = m->core->default_sample_spec;
+ u->sample_spec = m->core->default_sample_spec;
+ u->modargs = ma;
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log_error("Failed to parse module arguments");
- goto fail;
- }
- if (!(u->name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)))) {
- pa_log_error("Failed to get device name from module arguments");
- goto fail;
- }
- if (!(u->addr = pa_xstrdup(pa_modargs_get_value(ma, "address", NULL)))) {
- pa_log_error("Failed to get device address from module arguments");
- goto fail;
- }
- if (!(u->profile = pa_xstrdup(pa_modargs_get_value(ma, "profile", NULL)))) {
- pa_log_error("Failed to get profile from module arguments");
- goto fail;
- }
- if (pa_modargs_get_value_u32(ma, "rate", &u->ss.rate) < 0) {
+ if (pa_modargs_get_value_u32(ma, "rate", &u->sample_spec.rate) < 0 ||
+ u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) {
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) {
+ channels = u->sample_spec.channels;
+ if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 ||
+ channels <= 0 || channels > PA_CHANNELS_MAX) {
pa_log_error("Failed to get channels from module arguments");
goto fail;
}
- u->ss.channels = (uint8_t) channels;
+ u->sample_spec.channels = (uint8_t) channels;
- /* connect to the bluez audio service */
- u->audioservice_fd = bt_audio_service_open();
- if (u->audioservice_fd <= 0) {
- pa_log_error("Couldn't connect to bluetooth audio service");
+ if (setup_dbus(u) < 0)
goto fail;
- }
- pa_log_debug("Connected to the bluetooth audio service");
- /* queries device capabilities */
- e = bt_getcaps(u);
- if (e < 0) {
- pa_log_error("Failed to get device capabilities");
- goto fail;
- }
- pa_log_debug("Got device capabilities");
+ address = pa_modargs_get_value(ma, "address", NULL);
+ path = pa_modargs_get_value(ma, "path", NULL);
- /* configures the connection */
- e = bt_setconf(u);
- if (e < 0) {
- pa_log_error("Failed to set config");
+ if (find_device(u, address, path) < 0)
goto fail;
- }
- pa_log_debug("Connection to the device configured");
- /* gets the device socket */
- e = bt_getstreamfd(u);
- if (e < 0) {
- pa_log_error("Failed to get stream fd (%d)", e);
- goto fail;
- }
- pa_log_debug("Got the device socket");
+ pa_assert(u->device);
- /* create sink */
- pa_sink_new_data_init(&data);
- data.driver = __FILE__;
- data.module = m;
- pa_sink_new_data_set_name(&data, u->name);
- pa_sink_new_data_set_sample_spec(&data, &u->ss);
- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->name);
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s '%s' (%s)", u->strtransport, u->name, u->addr);
- pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile);
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_API, "bluez");
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CONNECTOR, "bluetooth");
-/* pa_proplist_setf(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "headset"); /\*FIXME*\/ */
-/* pa_proplist_setf(data.proplist, PA_PROP_DEVICE_VENDOR_PRODUCT_ID, "product_id"); /\*FIXME*\/ */
-/* pa_proplist_setf(data.proplist, PA_PROP_DEVICE_SERIAL, "serial"); /\*FIXME*\/ */
- u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
- pa_sink_new_data_done(&data);
- if (!u->sink) {
- pa_log_error("Failed to create sink");
+ /* Add the card structure. This will also initialize the default profile */
+ if (add_card(u, pa_modargs_get_value(ma, "profile", NULL)) < 0)
goto fail;
- }
- u->sink->userdata = u;
- u->sink->parent.process_msg = sink_process_msg;
- pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
- pa_sink_set_rtpoll(u->sink, u->rtpoll);
- u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
- pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- pollfd->fd = u->stream_fd;
- pollfd->events = pollfd->revents = 0;
-
- /* start rt thread */
- if (!(u->thread = pa_thread_new(thread_func, u))) {
- pa_log_error("Failed to create IO thread");
- 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);
+ /* Connect to the BT service and query capabilities */
+ if (init_bt(u) < 0)
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");
+ if (init_profile(u) < 0)
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;
- }
+/* if (u->path) { */
+/* DBusError err; */
+/* dbus_error_init(&err); */
+/* char *t; */
+
+
+/* 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 (u->transport == BT_CAPABILITIES_TRANSPORT_SCO || */
+/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */
+/* t = 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), t, &err); */
+/* pa_xfree(t); */
+
+/* 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; */
+/* } */
+/* } */
+
+/* if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP || */
+/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */
+/* t = 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), t, &err); */
+/* pa_xfree(t); */
+
+/* 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; */
+/* } */
+/* } */
+/* } */
+
+ if (start_thread(u) < 0)
+ goto fail;
-end:
- pa_modargs_free(ma);
return 0;
fail:
- if (ma)
- pa_modargs_free(ma);
-
pa__done(m);
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return
+ (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);
@@ -1121,63 +1705,72 @@ void pa__done(pa_module *m) {
if (!(u = m->userdata))
return;
- if (u->conn) {
- DBusError error;
- char *tmp;
- dbus_error_init(&error);
+ if (u->sink)
+ pa_sink_unlink(u->sink);
- 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);
- }
+ if (u->source)
+ pa_source_unlink(u->source);
- dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u);
+ stop_thread(u);
- pa_dbus_connection_unref(u->conn);
- }
+ if (u->connection) {
+/* DBusError error; */
+/* char *t; */
- if (u->path)
- pa_xfree(u->path);
+/* if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO || */
+/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */
- if (u->sink)
- pa_sink_unlink(u->sink);
+/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path); */
+/* dbus_error_init(&error); */
+/* dbus_bus_remove_match(pa_dbus_connection_get(u->conn), t, &error); */
+/* dbus_error_free(&error); */
+/* pa_xfree(t); */
+/* } */
- if (u->thread) {
- pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
- pa_thread_free(u->thread);
+/* if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP || */
+/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */
+
+/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path); */
+/* dbus_error_init(&error); */
+/* dbus_bus_remove_match(pa_dbus_connection_get(u->conn), t, &error); */
+/* dbus_error_free(&error); */
+/* pa_xfree(t); */
+/* } */
+
+/* dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u); */
+ pa_dbus_connection_unref(u->connection);
}
- if (u->sink)
- pa_sink_unref(u->sink);
+ if (u->card)
+ pa_card_free(u->card);
pa_thread_mq_done(&u->thread_mq);
- if (u->rtpoll_item)
- pa_rtpoll_item_free(u->rtpoll_item);
-
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
- if (u->smoother)
- pa_smoother_free(u->smoother);
-
- pa_xfree(u->name);
- pa_xfree(u->addr);
- pa_xfree(u->profile);
- pa_xfree(u->strtransport);
+ if (u->read_smoother)
+ pa_smoother_free(u->read_smoother);
if (u->stream_fd >= 0)
pa_close(u->stream_fd);
- if (u->audioservice_fd >= 0)
- pa_close(u->audioservice_fd);
+ if (u->service_fd >= 0)
+ pa_close(u->service_fd);
+
+ if (u->device)
+ pa_bluetooth_device_free(u->device);
+
+ if (u->write_memchunk.memblock)
+ pa_memblock_unref(u->write_memchunk.memblock);
+
+ if (u->a2dp.buffer)
+ pa_xfree(u->a2dp.buffer);
+
+ sbc_finish(&u->a2dp.sbc);
+
+ if (u->modargs)
+ pa_modargs_free(u->modargs);
pa_xfree(u);
}
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 1bc05c02..f9540b32 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -1,22 +1,22 @@
/***
- This file is part of PulseAudio.
+ This file is part of PulseAudio.
- Copyright 2008 Joao Paulo Rechi Vita
+ Copyright 2008 Joao Paulo Rechi Vita
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
+ PulseAudio is free software; you can redistribute it 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.
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ 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.
+ 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
@@ -29,817 +29,138 @@
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/macro.h>
#include <pulsecore/llist.h>
#include <pulsecore/core-util.h>
+#include <modules/dbus-util.h>
-#include "../dbus-util.h"
#include "module-bluetooth-discover-symdef.h"
+#include "bluetooth-util.h"
PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers");
PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_USAGE("");
-
-struct module {
- char *profile;
- uint32_t index;
- PA_LLIST_FIELDS(struct module);
-};
-
-struct uuid {
- char *uuid;
- PA_LLIST_FIELDS(struct uuid);
-};
+PA_MODULE_USAGE("async=<Asynchronous initialization?>");
-struct dbus_pending {
- char *path;
- char *profile;
- DBusPendingCall *pending;
- PA_LLIST_FIELDS(struct dbus_pending);
-};
-
-struct device {
- char *name;
- char *object_path;
- int paired;
- char *alias;
- int connected;
- PA_LLIST_HEAD(struct uuid, uuid_list);
- char *address;
- int class;
- int trusted;
- PA_LLIST_HEAD(struct module, module_list);
- PA_LLIST_FIELDS(struct device);
+static const char* const valid_modargs[] = {
+ "async",
+ NULL
};
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);
+ pa_core *core;
+ pa_dbus_connection *connection;
+ pa_bluetooth_discovery *discovery;
};
-static struct module *module_new(const char *profile, pa_module *pa_m) {
- struct module *m;
-
- m = pa_xnew(struct module, 1);
- m->profile = pa_xstrdup(profile);
- m->index = pa_m->index;
- PA_LLIST_INIT(struct module, m);
-
- return m;
-}
-
-static void module_free(struct module *m) {
- pa_assert(m);
-
- pa_xfree(m->profile);
- pa_xfree(m);
-}
-
-static struct module* module_find(struct device *d, const char *profile) {
- struct module *m;
-
- for (m = d->module_list; m; m = m->next)
- if (pa_streq(m->profile, profile))
- return m;
-
- return NULL;
-}
-
-static struct uuid *uuid_new(const char *uuid) {
- struct uuid *node;
-
- node = pa_xnew(struct uuid, 1);
- node->uuid = pa_xstrdup(uuid);
- PA_LLIST_INIT(struct uuid, node);
-
- return node;
-}
-
-static void uuid_free(struct uuid *uuid) {
- pa_assert(uuid);
-
- pa_xfree(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;
-
- node = pa_xnew(struct device, 1);
- node->name = NULL;
- node->object_path = pa_xstrdup(object_path);
- node->paired = -1;
- node->alias = NULL;
- node->connected = -1;
- PA_LLIST_HEAD_INIT(struct uuid, node->uuid_list);
- node->address = NULL;
- node->class = -1;
- node->trusted = -1;
- PA_LLIST_HEAD_INIT(struct module, node->module_list);
- PA_LLIST_INIT(struct device, node);
-
- return node;
-}
-
-static void device_free(struct device *device) {
- struct module *m;
- struct uuid *i;
-
- pa_assert(device);
-
- while ((m = device->module_list)) {
- PA_LLIST_REMOVE(struct module, device->module_list, m);
- module_free(m);
- }
-
- while ((i = device->uuid_list)) {
- PA_LLIST_REMOVE(struct uuid, device->uuid_list, i);
- uuid_free(i);
- }
-
- pa_xfree(device->name);
- pa_xfree(device->object_path);
- pa_xfree(device->alias);
- pa_xfree(device->address);
- pa_xfree(device);
-}
-
-static struct device* device_find(struct userdata *u, const char *path) {
- struct device *i;
-
- for (i = u->device_list; i; i = i->next)
- if (pa_streq(i->object_path, path))
- return i;
-
- return NULL;
-}
-
-static int parse_device_property(struct userdata *u, struct device *d, DBusMessageIter *i) {
- const char *key;
- DBusMessageIter variant_i;
-
+static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, pa_bool_t good) {
pa_assert(u);
pa_assert(d);
- pa_assert(i);
- if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
- pa_log("Property name not a string.");
- return -1;
- }
-
- dbus_message_iter_get_basic(i, &key);
-
- if (!dbus_message_iter_next(i)) {
- pa_log("Property value missing");
- return -1;
- }
-
- if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
- pa_log("Property value not a variant.");
- return -1;
- }
-
- dbus_message_iter_recurse(i, &variant_i);
-
- pa_log_debug("Parsing device property %s", key);
-
- switch (dbus_message_iter_get_arg_type(&variant_i)) {
-
- case DBUS_TYPE_STRING: {
-
- const char *value;
- dbus_message_iter_get_basic(&variant_i, &value);
-
- if (pa_streq(key, "Name")) {
- pa_xfree(d->name);
- d->name = pa_xstrdup(value);
- } else if (pa_streq(key, "Alias")) {
- pa_xfree(d->alias);
- d->alias = pa_xstrdup(value);
- } else if (pa_streq(key, "Address")) {
- pa_xfree(d->address);
- d->address = pa_xstrdup(value);
- }
-
- break;
- }
+ if (good &&
+ d->device_connected > 0 &&
+ (d->audio_sink_connected > 0 || d->headset_connected > 0)) {
- case DBUS_TYPE_BOOLEAN: {
+ if (((uint32_t) PA_PTR_TO_UINT(d->data))-1 == PA_INVALID_INDEX) {
+ pa_module *m = NULL;
+ char *args;
- dbus_bool_t value;
- dbus_message_iter_get_basic(&variant_i, &value);
+ /* Oh, awesome, a new device has shown up and been connected! */
- if (pa_streq(key, "Paired"))
- d->paired = !!value;
- else if (pa_streq(key, "Connected"))
- d->connected = !!value;
- else if (pa_streq(key, "Trusted"))
- d->trusted = !!value;
+ args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
+ pa_log_debug("Loading module-bluetooth-device %s", args);
+ m = pa_module_load(u->module->core, "module-bluetooth-device", args);
+ pa_xfree(args);
- break;
+ if (m)
+ d->data = PA_UINT_TO_PTR((uint32_t) (m->index+1));
+ else
+ pa_log_debug("Failed to load module for device %s", d->path);
}
- case DBUS_TYPE_UINT32: {
-
- uint32_t value;
- dbus_message_iter_get_basic(&variant_i, &value);
-
- if (pa_streq(key, "Class"))
- d->class = (int) value;
-
- break;
- }
-
- case DBUS_TYPE_ARRAY: {
-
- DBusMessageIter ai;
- dbus_message_iter_recurse(&variant_i, &ai);
-
- if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING &&
- pa_streq(key, "UUIDs")) {
-
- while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
- struct uuid *node;
- const char *value;
+ } else {
- dbus_message_iter_get_basic(&ai, &value);
- node = uuid_new(value);
- PA_LLIST_PREPEND(struct uuid, d->uuid_list, node);
+ if (((uint32_t) PA_PTR_TO_UINT(d->data))-1 != PA_INVALID_INDEX) {
- if (!dbus_message_iter_next(&ai))
- break;
- }
- }
+ /* Hmm, disconnection? Then let's unload our module */
- break;
+ pa_log_debug("Unloading module for %s", d->path);
+ pa_module_unload_request_by_index(u->core, (uint32_t) (PA_PTR_TO_UINT(d->data))-1, TRUE);
+ d->data = NULL;
}
}
-
- return 0;
}
-static int get_device_properties(struct userdata *u, struct device *d) {
- DBusError e;
- DBusMessage *m = NULL, *r = NULL;
- DBusMessageIter arg_i, element_i;
- int ret = -1;
-
- pa_assert(u);
- pa_assert(d);
-
- dbus_error_init(&e);
-
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->object_path, "org.bluez.Device", "GetProperties"));
-
- r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
-
- if (!r) {
- pa_log("org.bluez.Device.GetProperties failed: %s", e.message);
- goto finish;
- }
-
- if (!dbus_message_iter_init(r, &arg_i)) {
- pa_log("org.bluez.Device.GetProperties reply has no arguments");
- goto finish;
- }
-
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
- pa_log("org.bluez.Device.GetProperties argument is not an array");
- goto finish;
- }
-
- dbus_message_iter_recurse(&arg_i, &element_i);
- while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
-
- if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
- DBusMessageIter dict_i;
-
- dbus_message_iter_recurse(&element_i, &dict_i);
-
- if (parse_device_property(u, d, &dict_i) < 0)
- goto finish;
- }
-
- if (!dbus_message_iter_next(&element_i))
- break;
- }
-
- ret = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
- if (r)
- dbus_message_unref(r);
-
- dbus_error_free(&e);
-
- return ret;
-}
-
-static void load_module_for_device(struct userdata *u, struct device *d, const char *profile) {
- char *args;
- pa_module *pa_m;
- struct module *m;
-
- pa_assert(u);
- pa_assert(d);
-
- get_device_properties(u, d);
- 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);
-
- if (!pa_m) {
- pa_log_debug("Failed to load module for device %s", d->object_path);
- return;
- }
-
- m = module_new(profile, pa_m);
- PA_LLIST_PREPEND(struct module, d->module_list, m);
-}
-
-static void unload_module_for_device(struct userdata *u, struct device *d, const char *profile) {
- struct module *m;
-
- pa_assert(u);
- pa_assert(d);
-
- if (!(m = module_find(d, profile)))
- return;
-
- pa_module_unload_request_by_index(u->module->core, m->index, TRUE);
-
- PA_LLIST_REMOVE(struct module, d->module_list, m);
- module_free(m);
-}
-
-static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
- DBusMessageIter arg_i;
+static int setup_dbus(struct userdata *u) {
DBusError err;
- const char *value;
- struct userdata *u;
-
- pa_assert(bus);
- pa_assert(msg);
- pa_assert(userdata);
- u = userdata;
dbus_error_init(&err);
- 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));
-
- if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceRemoved")) {
-
- if (!dbus_message_iter_init(msg, &arg_i))
- pa_log("dbus: message has no parameters");
- else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
- pa_log("dbus: argument is not object path");
- else {
- struct device *d;
-
- dbus_message_iter_get_basic(&arg_i, &value);
- pa_log_debug("hcid: device %s removed", value);
-
- if ((d = device_find(u, value))) {
- PA_LLIST_REMOVE(struct device, u->device_list, d);
- device_free(d);
- }
- }
-
- } else 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_bool_t connected;
-
- 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 (!pa_streq(value, "Connected"))
- goto done;
-
- 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_BOOLEAN) {
- pa_log("Property value not a boolean.");
- goto done;
- }
-
- dbus_message_iter_get_basic(&variant_i, &connected);
-
- if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged"))
- profile = "hsp";
- else
- profile = "a2dp";
-
- d = device_find(u, dbus_message_get_path(msg));
-
- if (connected) {
- if (!d) {
- d = device_new(dbus_message_get_path(msg));
- PA_LLIST_PREPEND(struct device, u->device_list, d);
- }
-
- load_module_for_device(u, d, profile);
- } else if (d)
- unload_module_for_device(u, d, profile);
- }
-
-done:
- dbus_error_free(&err);
- 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;
- }
+ u->connection = pa_dbus_bus_get(u->core, DBUS_BUS_SYSTEM, &err);
- 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 (dbus_error_is_set(&err) || !u->connection) {
+ pa_log("Failed to get D-Bus connection: %s", err.message);
+ dbus_error_free(&err);
+ return -1;
}
- 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);
+ return 0;
}
-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;
+int pa__init(pa_module* m) {
struct userdata *u;
+ pa_modargs *ma;
+ pa_bool_t async = FALSE;
- pa_assert(u = user_data);
- dbus_error_init(&e);
+ pa_assert(m);
- r = dbus_pending_call_steal_reply(pending);
- if (!r) {
- pa_log("Failed to get ListAdapters reply");
- goto end;
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
}
- 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 (pa_modargs_get_value_boolean(ma, "async", &async) < 0) {
+ pa_log("Failed to parse tsched argument.");
+ goto fail;
}
- 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);
- }
- }
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+ u->core = m->core;
- if (paths)
- dbus_free_string_array (paths);
- dbus_message_unref(r);
+ if (setup_dbus(u) < 0)
+ goto fail;
-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);
-}
+ if (!(u->discovery = pa_bluetooth_discovery_new(pa_dbus_connection_get(u->connection), load_module_for_device, u)))
+ goto fail;
-static void lookup_devices(struct userdata *u) {
- DBusMessage *m;
- DBusPendingCall *call;
- struct dbus_pending *p;
+ if (!async)
+ pa_bluetooth_discovery_sync(u->discovery);
- pa_assert(u);
+ return 0;
- 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");
- }
+fail:
+ pa__done(m);
- dbus_message_unref(m);
+ return -1;
}
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);
-
- dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", &error);
- dbus_error_free(&error);
+ if (u->discovery)
+ pa_bluetooth_discovery_free(u->discovery);
- dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", &error);
- dbus_error_free(&error);
-
- dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", &error);
- 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->connection)
+ pa_dbus_connection_unref(u->connection);
pa_xfree(u);
}
-
-int pa__init(pa_module* m) {
- DBusError err;
- struct userdata *u;
-
- pa_assert(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);
- if (dbus_error_is_set(&err) || (u->conn == NULL) ) {
- pa_log("Failed to get D-Bus connection: %s", err.message);
- 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");
- goto fail;
- }
-
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", &err);
- if (dbus_error_is_set(&err)) {
- pa_log_error("Unable to subscribe to org.bluez.Adapter signals: %s: %s", err.name, err.message);
- goto fail;
- }
-
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", &err);
- 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;
- }
-
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", &err);
- 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;
- }
-
- lookup_devices(u);
-
- return 0;
-
-fail:
- dbus_error_free(&err);
- pa__done(m);
-
- return -1;
-}
diff --git a/src/modules/bluetooth/rtp.h b/src/modules/bluetooth/rtp.h
index 690bd43a..14573628 100644
--- a/src/modules/bluetooth/rtp.h
+++ b/src/modules/bluetooth/rtp.h
@@ -2,7 +2,7 @@
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
*
* This library is free software; you can redistribute it and/or
@@ -24,13 +24,13 @@
#if __BYTE_ORDER == __LITTLE_ENDIAN
struct rtp_header {
- uint8_t cc:4;
- uint8_t x:1;
- uint8_t p:1;
- uint8_t v:2;
+ unsigned cc:4;
+ unsigned x:1;
+ unsigned p:1;
+ unsigned v:2;
- uint8_t pt:7;
- uint8_t m:1;
+ unsigned pt:7;
+ unsigned m:1;
uint16_t sequence_number;
uint32_t timestamp;
@@ -39,23 +39,23 @@ struct rtp_header {
} __attribute__ ((packed));
struct rtp_payload {
- uint8_t frame_count:4;
- uint8_t rfa0:1;
- uint8_t is_last_fragment:1;
- uint8_t is_first_fragment:1;
- uint8_t is_fragmented:1;
+ unsigned frame_count:4;
+ unsigned rfa0:1;
+ unsigned is_last_fragment:1;
+ unsigned is_first_fragment:1;
+ unsigned is_fragmented:1;
} __attribute__ ((packed));
#elif __BYTE_ORDER == __BIG_ENDIAN
struct rtp_header {
- uint8_t v:2;
- uint8_t p:1;
- uint8_t x:1;
- uint8_t cc:4;
+ unsigned v:2;
+ unsigned p:1;
+ unsigned x:1;
+ unsigned cc:4;
- uint8_t m:1;
- uint8_t pt:7;
+ unsigned m:1;
+ unsigned pt:7;
uint16_t sequence_number;
uint32_t timestamp;
@@ -64,11 +64,11 @@ struct rtp_header {
} __attribute__ ((packed));
struct rtp_payload {
- uint8_t is_fragmented:1;
- uint8_t is_first_fragment:1;
- uint8_t is_last_fragment:1;
- uint8_t rfa0:1;
- uint8_t frame_count:4;
+ unsigned is_fragmented:1;
+ unsigned is_first_fragment:1;
+ unsigned is_last_fragment:1;
+ unsigned rfa0:1;
+ unsigned frame_count:4;
} __attribute__ ((packed));
#else
diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc.c
index 651981fa..29258d05 100644
--- a/src/modules/bluetooth/sbc.c
+++ b/src/modules/bluetooth/sbc.c
@@ -46,6 +46,7 @@
#include "sbc_tables.h"
#include "sbc.h"
+#include "sbc_primitives.h"
#define SBC_SYNCWORD 0x9C
@@ -76,13 +77,16 @@ struct sbc_frame {
uint8_t joint;
/* only the lower 4 bits of every element are to be used */
- uint8_t scale_factor[2][8];
+ uint32_t scale_factor[2][8];
/* raw integer subband samples in the frame */
+ int32_t SBC_ALIGNED sb_sample_f[16][2][8];
- int32_t sb_sample_f[16][2][8];
- int32_t sb_sample[16][2][8]; /* modified subband samples */
- int16_t pcm_sample[2][16*8]; /* original pcm audio samples */
+ /* modified subband samples */
+ int32_t SBC_ALIGNED sb_sample[16][2][8];
+
+ /* original pcm audio samples */
+ int16_t SBC_ALIGNED pcm_sample[2][16*8];
};
struct sbc_decoder_state {
@@ -91,16 +95,6 @@ struct sbc_decoder_state {
int offset[2][16];
};
-struct sbc_encoder_state {
- int subbands;
- int position[2];
- int16_t X[2][256];
- void (*sbc_analyze_4b_4s)(int16_t *pcm, int16_t *x,
- int32_t *out, int out_stride);
- void (*sbc_analyze_4b_8s)(int16_t *pcm, int16_t *x,
- int32_t *out, int out_stride);
-};
-
/*
* Calculates the CRC-8 of the first len bits in data
*/
@@ -368,7 +362,7 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8])
static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
size_t len)
{
- int consumed;
+ unsigned int consumed;
/* Will copy the parts of the header that are relevant to crc
* calculation here */
uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -653,180 +647,41 @@ static int sbc_synthesize_audio(struct sbc_decoder_state *state,
}
}
-static inline void _sbc_analyze_four(const int16_t *in, int32_t *out)
-{
- FIXED_A t1[4];
- FIXED_T t2[4];
- int i = 0, hop = 0;
-
- /* rounding coefficient */
- t1[0] = t1[1] = t1[2] = t1[3] =
- (FIXED_A) 1 << (SBC_PROTO_FIXED4_SCALE - 1);
-
- /* low pass polyphase filter */
- for (hop = 0; hop < 40; hop += 8) {
- t1[0] += (FIXED_A) in[hop] * _sbc_proto_fixed4[hop];
- t1[1] += (FIXED_A) in[hop + 1] * _sbc_proto_fixed4[hop + 1];
- t1[2] += (FIXED_A) in[hop + 2] * _sbc_proto_fixed4[hop + 2];
- t1[1] += (FIXED_A) in[hop + 3] * _sbc_proto_fixed4[hop + 3];
- t1[0] += (FIXED_A) in[hop + 4] * _sbc_proto_fixed4[hop + 4];
- t1[3] += (FIXED_A) in[hop + 5] * _sbc_proto_fixed4[hop + 5];
- t1[3] += (FIXED_A) in[hop + 7] * _sbc_proto_fixed4[hop + 7];
- }
-
- /* scaling */
- t2[0] = t1[0] >> SBC_PROTO_FIXED4_SCALE;
- t2[1] = t1[1] >> SBC_PROTO_FIXED4_SCALE;
- t2[2] = t1[2] >> SBC_PROTO_FIXED4_SCALE;
- t2[3] = t1[3] >> SBC_PROTO_FIXED4_SCALE;
-
- /* do the cos transform */
- for (i = 0, hop = 0; i < 4; hop += 8, i++) {
- out[i] = ((FIXED_A) t2[0] * cos_table_fixed_4[0 + hop] +
- (FIXED_A) t2[1] * cos_table_fixed_4[1 + hop] +
- (FIXED_A) t2[2] * cos_table_fixed_4[2 + hop] +
- (FIXED_A) t2[3] * cos_table_fixed_4[5 + hop]) >>
- (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
- }
-}
-
-static void sbc_analyze_4b_4s(int16_t *pcm, int16_t *x,
- int32_t *out, int out_stride)
-{
- int i;
-
- /* Input 4 x 4 Audio Samples */
- for (i = 0; i < 16; i += 4) {
- x[64 + i] = x[0 + i] = pcm[15 - i];
- x[65 + i] = x[1 + i] = pcm[14 - i];
- x[66 + i] = x[2 + i] = pcm[13 - i];
- x[67 + i] = x[3 + i] = pcm[12 - i];
- }
-
- /* Analyze four blocks */
- _sbc_analyze_four(x + 12, out);
- out += out_stride;
- _sbc_analyze_four(x + 8, out);
- out += out_stride;
- _sbc_analyze_four(x + 4, out);
- out += out_stride;
- _sbc_analyze_four(x, out);
-}
-
-static inline void _sbc_analyze_eight(const int16_t *in, int32_t *out)
-{
- FIXED_A t1[8];
- FIXED_T t2[8];
- int i, hop;
-
- /* rounding coefficient */
- t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] =
- (FIXED_A) 1 << (SBC_PROTO_FIXED8_SCALE-1);
-
- /* low pass polyphase filter */
- for (hop = 0; hop < 80; hop += 16) {
- t1[0] += (FIXED_A) in[hop] * _sbc_proto_fixed8[hop];
- t1[1] += (FIXED_A) in[hop + 1] * _sbc_proto_fixed8[hop + 1];
- t1[2] += (FIXED_A) in[hop + 2] * _sbc_proto_fixed8[hop + 2];
- t1[3] += (FIXED_A) in[hop + 3] * _sbc_proto_fixed8[hop + 3];
- t1[4] += (FIXED_A) in[hop + 4] * _sbc_proto_fixed8[hop + 4];
- t1[3] += (FIXED_A) in[hop + 5] * _sbc_proto_fixed8[hop + 5];
- t1[2] += (FIXED_A) in[hop + 6] * _sbc_proto_fixed8[hop + 6];
- t1[1] += (FIXED_A) in[hop + 7] * _sbc_proto_fixed8[hop + 7];
- t1[0] += (FIXED_A) in[hop + 8] * _sbc_proto_fixed8[hop + 8];
- t1[5] += (FIXED_A) in[hop + 9] * _sbc_proto_fixed8[hop + 9];
- t1[6] += (FIXED_A) in[hop + 10] * _sbc_proto_fixed8[hop + 10];
- t1[7] += (FIXED_A) in[hop + 11] * _sbc_proto_fixed8[hop + 11];
- t1[7] += (FIXED_A) in[hop + 13] * _sbc_proto_fixed8[hop + 13];
- t1[6] += (FIXED_A) in[hop + 14] * _sbc_proto_fixed8[hop + 14];
- t1[5] += (FIXED_A) in[hop + 15] * _sbc_proto_fixed8[hop + 15];
- }
-
- /* scaling */
- t2[0] = t1[0] >> SBC_PROTO_FIXED8_SCALE;
- t2[1] = t1[1] >> SBC_PROTO_FIXED8_SCALE;
- t2[2] = t1[2] >> SBC_PROTO_FIXED8_SCALE;
- t2[3] = t1[3] >> SBC_PROTO_FIXED8_SCALE;
- t2[4] = t1[4] >> SBC_PROTO_FIXED8_SCALE;
- t2[5] = t1[5] >> SBC_PROTO_FIXED8_SCALE;
- t2[6] = t1[6] >> SBC_PROTO_FIXED8_SCALE;
- t2[7] = t1[7] >> SBC_PROTO_FIXED8_SCALE;
-
- /* do the cos transform */
- for (i = 0, hop = 0; i < 8; hop += 16, i++) {
- out[i] = ((FIXED_A) t2[0] * cos_table_fixed_8[0 + hop] +
- (FIXED_A) t2[1] * cos_table_fixed_8[1 + hop] +
- (FIXED_A) t2[2] * cos_table_fixed_8[2 + hop] +
- (FIXED_A) t2[3] * cos_table_fixed_8[3 + hop] +
- (FIXED_A) t2[4] * cos_table_fixed_8[4 + hop] +
- (FIXED_A) t2[5] * cos_table_fixed_8[9 + hop] +
- (FIXED_A) t2[6] * cos_table_fixed_8[10 + hop] +
- (FIXED_A) t2[7] * cos_table_fixed_8[11 + hop]) >>
- (SBC_COS_TABLE_FIXED8_SCALE - SCALE_OUT_BITS);
- }
-}
-
-static void sbc_analyze_4b_8s(int16_t *pcm, int16_t *x,
- int32_t *out, int out_stride)
-{
- int i;
-
- /* Input 4 x 8 Audio Samples */
- for (i = 0; i < 32; i += 8) {
- x[128 + i] = x[0 + i] = pcm[31 - i];
- x[129 + i] = x[1 + i] = pcm[30 - i];
- x[130 + i] = x[2 + i] = pcm[29 - i];
- x[131 + i] = x[3 + i] = pcm[28 - i];
- x[132 + i] = x[4 + i] = pcm[27 - i];
- x[133 + i] = x[5 + i] = pcm[26 - i];
- x[134 + i] = x[6 + i] = pcm[25 - i];
- x[135 + i] = x[7 + i] = pcm[24 - i];
- }
-
- /* Analyze four blocks */
- _sbc_analyze_eight(x + 24, out);
- out += out_stride;
- _sbc_analyze_eight(x + 16, out);
- out += out_stride;
- _sbc_analyze_eight(x + 8, out);
- out += out_stride;
- _sbc_analyze_eight(x, out);
-}
-
static int sbc_analyze_audio(struct sbc_encoder_state *state,
struct sbc_frame *frame)
{
int ch, blk;
+ int16_t *x;
switch (frame->subbands) {
case 4:
- for (ch = 0; ch < frame->channels; ch++)
+ for (ch = 0; ch < frame->channels; ch++) {
+ x = &state->X[ch][state->position - 16 +
+ frame->blocks * 4];
for (blk = 0; blk < frame->blocks; blk += 4) {
state->sbc_analyze_4b_4s(
- &frame->pcm_sample[ch][blk * 4],
- &state->X[ch][state->position[ch]],
+ x,
frame->sb_sample_f[blk][ch],
frame->sb_sample_f[blk + 1][ch] -
frame->sb_sample_f[blk][ch]);
- state->position[ch] -= 16;
- if (state->position[ch] < 0)
- state->position[ch] = 64 - 16;
+ x -= 16;
}
+ }
return frame->blocks * 4;
case 8:
- for (ch = 0; ch < frame->channels; ch++)
+ for (ch = 0; ch < frame->channels; ch++) {
+ x = &state->X[ch][state->position - 32 +
+ frame->blocks * 8];
for (blk = 0; blk < frame->blocks; blk += 4) {
state->sbc_analyze_4b_8s(
- &frame->pcm_sample[ch][blk * 8],
- &state->X[ch][state->position[ch]],
+ x,
frame->sb_sample_f[blk][ch],
frame->sb_sample_f[blk + 1][ch] -
frame->sb_sample_f[blk][ch]);
- state->position[ch] -= 32;
- if (state->position[ch] < 0)
- state->position[ch] = 128 - 32;
+ x -= 32;
}
+ }
return frame->blocks * 8;
default:
@@ -836,23 +691,31 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
/* Supplementary bitstream writing macros for 'sbc_pack_frame' */
-#define PUT_BITS(v, n)\
- bits_cache = (v) | (bits_cache << (n));\
- bits_count += (n);\
- if (bits_count >= 16) {\
- bits_count -= 8;\
- *data_ptr++ = (uint8_t) (bits_cache >> bits_count);\
- bits_count -= 8;\
- *data_ptr++ = (uint8_t) (bits_cache >> bits_count);\
- }\
-
-#define FLUSH_BITS()\
- while (bits_count >= 8) {\
- bits_count -= 8;\
- *data_ptr++ = (uint8_t) (bits_cache >> bits_count);\
- }\
- if (bits_count > 0)\
- *data_ptr++ = (uint8_t) (bits_cache << (8 - bits_count));\
+#define PUT_BITS(data_ptr, bits_cache, bits_count, v, n) \
+ do { \
+ bits_cache = (v) | (bits_cache << (n)); \
+ bits_count += (n); \
+ if (bits_count >= 16) { \
+ bits_count -= 8; \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache >> bits_count); \
+ bits_count -= 8; \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache >> bits_count); \
+ } \
+ } while (0)
+
+#define FLUSH_BITS(data_ptr, bits_cache, bits_count) \
+ do { \
+ while (bits_count >= 8) { \
+ bits_count -= 8; \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache >> bits_count); \
+ } \
+ if (bits_count > 0) \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache << (8 - bits_count)); \
+ } while (0)
/*
* Packs the SBC frame from frame into the memory at data. At most len
@@ -869,7 +732,9 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
* -99 not implemented
*/
-static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
+static SBC_ALWAYS_INLINE int sbc_pack_frame_internal(
+ uint8_t *data, struct sbc_frame *frame, size_t len,
+ int frame_subbands, int frame_channels)
{
/* Bitstream writer starts from the fourth byte */
uint8_t *data_ptr = data + 4;
@@ -887,8 +752,6 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
uint32_t levels[2][8]; /* levels are derived from that */
uint32_t sb_sample_delta[2][8];
- u_int32_t scalefactor[2][8]; /* derived from frame->scale_factor */
-
data[0] = SBC_SYNCWORD;
data[1] = (frame->frequency & 0x03) << 6;
@@ -899,7 +762,7 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
data[1] |= (frame->allocation & 0x01) << 1;
- switch (frame->subbands) {
+ switch (frame_subbands) {
case 4:
/* Nothing to do */
break;
@@ -914,11 +777,11 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
data[2] = frame->bitpool;
if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
- frame->bitpool > frame->subbands << 4)
+ frame->bitpool > frame_subbands << 4)
return -5;
if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
- frame->bitpool > frame->subbands << 5)
+ frame->bitpool > frame_subbands << 5)
return -5;
/* Can't fill in crc yet */
@@ -927,36 +790,24 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
crc_header[1] = data[2];
crc_pos = 16;
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- frame->scale_factor[ch][sb] = 0;
- scalefactor[ch][sb] = 2 << SCALE_OUT_BITS;
- for (blk = 0; blk < frame->blocks; blk++) {
- while (scalefactor[ch][sb] < fabs(frame->sb_sample_f[blk][ch][sb])) {
- frame->scale_factor[ch][sb]++;
- scalefactor[ch][sb] *= 2;
- }
- }
- }
- }
-
if (frame->mode == JOINT_STEREO) {
/* like frame->sb_sample but joint stereo */
int32_t sb_sample_j[16][2];
/* scalefactor and scale_factor in joint case */
- u_int32_t scalefactor_j[2];
+ uint32_t scalefactor_j[2];
uint8_t scale_factor_j[2];
uint8_t joint = 0;
frame->joint = 0;
- for (sb = 0; sb < frame->subbands - 1; sb++) {
+ for (sb = 0; sb < frame_subbands - 1; sb++) {
scale_factor_j[0] = 0;
scalefactor_j[0] = 2 << SCALE_OUT_BITS;
scale_factor_j[1] = 0;
scalefactor_j[1] = 2 << SCALE_OUT_BITS;
for (blk = 0; blk < frame->blocks; blk++) {
+ uint32_t tmp;
/* Calculate joint stereo signal */
sb_sample_j[blk][0] =
ASR(frame->sb_sample_f[blk][0][sb], 1) +
@@ -966,11 +817,13 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
ASR(frame->sb_sample_f[blk][1][sb], 1);
/* calculate scale_factor_j and scalefactor_j for joint case */
- while (scalefactor_j[0] < fabs(sb_sample_j[blk][0])) {
+ tmp = fabs(sb_sample_j[blk][0]);
+ while (scalefactor_j[0] < tmp) {
scale_factor_j[0]++;
scalefactor_j[0] *= 2;
}
- while (scalefactor_j[1] < fabs(sb_sample_j[blk][1])) {
+ tmp = fabs(sb_sample_j[blk][1]);
+ while (scalefactor_j[1] < tmp) {
scale_factor_j[1]++;
scalefactor_j[1] *= 2;
}
@@ -982,7 +835,7 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
(scale_factor_j[0] +
scale_factor_j[1])) {
/* use joint stereo for this subband */
- joint |= 1 << (frame->subbands - 1 - sb);
+ joint |= 1 << (frame_subbands - 1 - sb);
frame->joint |= 1 << sb;
frame->scale_factor[0][sb] = scale_factor_j[0];
frame->scale_factor[1][sb] = scale_factor_j[1];
@@ -995,14 +848,16 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
}
}
- PUT_BITS(joint, frame->subbands);
+ PUT_BITS(data_ptr, bits_cache, bits_count,
+ joint, frame_subbands);
crc_header[crc_pos >> 3] = joint;
- crc_pos += frame->subbands;
+ crc_pos += frame_subbands;
}
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- PUT_BITS(frame->scale_factor[ch][sb] & 0x0F, 4);
+ for (ch = 0; ch < frame_channels; ch++) {
+ for (sb = 0; sb < frame_subbands; sb++) {
+ PUT_BITS(data_ptr, bits_cache, bits_count,
+ frame->scale_factor[ch][sb] & 0x0F, 4);
crc_header[crc_pos >> 3] <<= 4;
crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
crc_pos += 4;
@@ -1017,8 +872,8 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
sbc_calculate_bits(frame, bits);
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
+ for (ch = 0; ch < frame_channels; ch++) {
+ for (sb = 0; sb < frame_subbands; sb++) {
levels[ch][sb] = ((1 << bits[ch][sb]) - 1) <<
(32 - (frame->scale_factor[ch][sb] +
SCALE_OUT_BITS + 2));
@@ -1029,8 +884,8 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
}
for (blk = 0; blk < frame->blocks; blk++) {
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
+ for (ch = 0; ch < frame_channels; ch++) {
+ for (sb = 0; sb < frame_subbands; sb++) {
if (bits[ch][sb] == 0)
continue;
@@ -1039,33 +894,46 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
(sb_sample_delta[ch][sb] +
frame->sb_sample_f[blk][ch][sb])) >> 32;
- PUT_BITS(audio_sample, bits[ch][sb]);
+ PUT_BITS(data_ptr, bits_cache, bits_count,
+ audio_sample, bits[ch][sb]);
}
}
}
- FLUSH_BITS();
+ FLUSH_BITS(data_ptr, bits_cache, bits_count);
return data_ptr - data;
}
+static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
+{
+ if (frame->subbands == 4) {
+ if (frame->channels == 1)
+ return sbc_pack_frame_internal(data, frame, len, 4, 1);
+ else
+ return sbc_pack_frame_internal(data, frame, len, 4, 2);
+ } else {
+ if (frame->channels == 1)
+ return sbc_pack_frame_internal(data, frame, len, 8, 1);
+ else
+ return sbc_pack_frame_internal(data, frame, len, 8, 2);
+ }
+}
+
static void sbc_encoder_init(struct sbc_encoder_state *state,
const struct sbc_frame *frame)
{
memset(&state->X, 0, sizeof(state->X));
- state->subbands = frame->subbands;
- state->position[0] = state->position[1] = 12 * frame->subbands;
+ state->position = SBC_X_BUFFER_SIZE - frame->subbands * 9;
- /* Default implementation for analyze function */
- state->sbc_analyze_4b_4s = sbc_analyze_4b_4s;
- state->sbc_analyze_4b_8s = sbc_analyze_4b_8s;
+ sbc_init_primitives(state);
}
struct sbc_priv {
int init;
- struct sbc_frame frame;
- struct sbc_decoder_state dec_state;
- struct sbc_encoder_state enc_state;
+ struct SBC_ALIGNED sbc_frame frame;
+ struct SBC_ALIGNED sbc_decoder_state dec_state;
+ struct SBC_ALIGNED sbc_encoder_state enc_state;
};
static void sbc_set_defaults(sbc_t *sbc, unsigned long flags)
@@ -1091,10 +959,13 @@ int sbc_init(sbc_t *sbc, unsigned long flags)
memset(sbc, 0, sizeof(sbc_t));
- sbc->priv = malloc(sizeof(struct sbc_priv));
- if (!sbc->priv)
+ sbc->priv_alloc_base = malloc(sizeof(struct sbc_priv) + SBC_ALIGN_MASK);
+ if (!sbc->priv_alloc_base)
return -ENOMEM;
+ sbc->priv = (void *) (((uintptr_t) sbc->priv_alloc_base +
+ SBC_ALIGN_MASK) & ~((uintptr_t) SBC_ALIGN_MASK));
+
memset(sbc->priv, 0, sizeof(struct sbc_priv));
sbc_set_defaults(sbc, flags);
@@ -1177,8 +1048,10 @@ int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
int output_len, int *written)
{
struct sbc_priv *priv;
- char *ptr;
- int i, ch, framelen, samples;
+ int framelen, samples;
+ int (*sbc_enc_process_input)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
if (!sbc && !input)
return -EIO;
@@ -1213,22 +1086,34 @@ int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
if (!output || output_len < priv->frame.length)
return -ENOSPC;
- ptr = input;
-
- for (i = 0; i < priv->frame.subbands * priv->frame.blocks; i++) {
- for (ch = 0; ch < priv->frame.channels; ch++) {
- int16_t s;
- if (sbc->endian == SBC_BE)
- s = (ptr[0] & 0xff) << 8 | (ptr[1] & 0xff);
- else
- s = (ptr[0] & 0xff) | (ptr[1] & 0xff) << 8;
- ptr += 2;
- priv->frame.pcm_sample[ch][i] = s;
- }
+ /* Select the needed input data processing function and call it */
+ if (priv->frame.subbands == 8) {
+ if (sbc->endian == SBC_BE)
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_8s_be;
+ else
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_8s_le;
+ } else {
+ if (sbc->endian == SBC_BE)
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_4s_be;
+ else
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_4s_le;
}
+ priv->enc_state.position = sbc_enc_process_input(
+ priv->enc_state.position, (const uint8_t *) input,
+ priv->enc_state.X, priv->frame.subbands * priv->frame.blocks,
+ priv->frame.channels);
+
samples = sbc_analyze_audio(&priv->enc_state, &priv->frame);
+ priv->enc_state.sbc_calc_scalefactors(
+ priv->frame.sb_sample_f, priv->frame.scale_factor,
+ priv->frame.blocks, priv->frame.channels, priv->frame.subbands);
+
framelen = sbc_pack_frame(output, &priv->frame, output_len);
if (written)
@@ -1242,8 +1127,8 @@ void sbc_finish(sbc_t *sbc)
if (!sbc)
return;
- if (sbc->priv)
- free(sbc->priv);
+ if (sbc->priv_alloc_base)
+ free(sbc->priv_alloc_base);
memset(sbc, 0, sizeof(sbc_t));
}
diff --git a/src/modules/bluetooth/sbc.h b/src/modules/bluetooth/sbc.h
index 8ac59309..b0a14888 100644
--- a/src/modules/bluetooth/sbc.h
+++ b/src/modules/bluetooth/sbc.h
@@ -74,6 +74,7 @@ struct sbc_struct {
uint8_t endian;
void *priv;
+ void *priv_alloc_base;
};
typedef struct sbc_struct sbc_t;
diff --git a/src/modules/bluetooth/sbc_math.h b/src/modules/bluetooth/sbc_math.h
index 6ca4f526..b87bc81c 100644
--- a/src/modules/bluetooth/sbc_math.h
+++ b/src/modules/bluetooth/sbc_math.h
@@ -29,8 +29,6 @@
#define ASR(val, bits) ((-2 >> 1 == -1) ? \
((int32_t)(val)) >> (bits) : ((int32_t) (val)) / (1 << (bits)))
-#define SCALE_OUT_BITS 15
-
#define SCALE_SPROTO4_TBL 12
#define SCALE_SPROTO8_TBL 14
#define SCALE_NPROTO4_TBL 11
diff --git a/src/modules/bluetooth/sbc_primitives.c b/src/modules/bluetooth/sbc_primitives.c
new file mode 100644
index 00000000..303f3fee
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives.c
@@ -0,0 +1,469 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives.h"
+#include "sbc_primitives_mmx.h"
+#include "sbc_primitives_neon.h"
+
+/*
+ * A reference C code of analysis filter with SIMD-friendly tables
+ * reordering and code layout. This code can be used to develop platform
+ * specific SIMD optimizations. Also it may be used as some kind of test
+ * for compiler autovectorization capabilities (who knows, if the compiler
+ * is very good at this stuff, hand optimized assembly may be not strictly
+ * needed for some platform).
+ *
+ * Note: It is also possible to make a simple variant of analysis filter,
+ * which needs only a single constants table without taking care about
+ * even/odd cases. This simple variant of filter can be implemented without
+ * input data permutation. The only thing that would be lost is the
+ * possibility to use pairwise SIMD multiplications. But for some simple
+ * CPU cores without SIMD extensions it can be useful. If anybody is
+ * interested in implementing such variant of a filter, sourcecode from
+ * bluez versions 4.26/4.27 can be used as a reference and the history of
+ * the changes in git repository done around that time may be worth checking.
+ */
+
+static inline void sbc_analyze_four_simd(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ FIXED_A t1[4];
+ FIXED_T t2[4];
+ int hop = 0;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED4_SCALE - 1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 40; hop += 8) {
+ t1[0] += (FIXED_A) in[hop] * consts[hop];
+ t1[0] += (FIXED_A) in[hop + 1] * consts[hop + 1];
+ t1[1] += (FIXED_A) in[hop + 2] * consts[hop + 2];
+ t1[1] += (FIXED_A) in[hop + 3] * consts[hop + 3];
+ t1[2] += (FIXED_A) in[hop + 4] * consts[hop + 4];
+ t1[2] += (FIXED_A) in[hop + 5] * consts[hop + 5];
+ t1[3] += (FIXED_A) in[hop + 6] * consts[hop + 6];
+ t1[3] += (FIXED_A) in[hop + 7] * consts[hop + 7];
+ }
+
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED4_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED4_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED4_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED4_SCALE;
+
+ /* do the cos transform */
+ t1[0] = (FIXED_A) t2[0] * consts[40 + 0];
+ t1[0] += (FIXED_A) t2[1] * consts[40 + 1];
+ t1[1] = (FIXED_A) t2[0] * consts[40 + 2];
+ t1[1] += (FIXED_A) t2[1] * consts[40 + 3];
+ t1[2] = (FIXED_A) t2[0] * consts[40 + 4];
+ t1[2] += (FIXED_A) t2[1] * consts[40 + 5];
+ t1[3] = (FIXED_A) t2[0] * consts[40 + 6];
+ t1[3] += (FIXED_A) t2[1] * consts[40 + 7];
+
+ t1[0] += (FIXED_A) t2[2] * consts[40 + 8];
+ t1[0] += (FIXED_A) t2[3] * consts[40 + 9];
+ t1[1] += (FIXED_A) t2[2] * consts[40 + 10];
+ t1[1] += (FIXED_A) t2[3] * consts[40 + 11];
+ t1[2] += (FIXED_A) t2[2] * consts[40 + 12];
+ t1[2] += (FIXED_A) t2[3] * consts[40 + 13];
+ t1[3] += (FIXED_A) t2[2] * consts[40 + 14];
+ t1[3] += (FIXED_A) t2[3] * consts[40 + 15];
+
+ out[0] = t1[0] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ out[1] = t1[1] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ out[2] = t1[2] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ out[3] = t1[3] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+}
+
+static inline void sbc_analyze_eight_simd(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ FIXED_A t1[8];
+ FIXED_T t2[8];
+ int i, hop;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED8_SCALE-1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 80; hop += 16) {
+ t1[0] += (FIXED_A) in[hop] * consts[hop];
+ t1[0] += (FIXED_A) in[hop + 1] * consts[hop + 1];
+ t1[1] += (FIXED_A) in[hop + 2] * consts[hop + 2];
+ t1[1] += (FIXED_A) in[hop + 3] * consts[hop + 3];
+ t1[2] += (FIXED_A) in[hop + 4] * consts[hop + 4];
+ t1[2] += (FIXED_A) in[hop + 5] * consts[hop + 5];
+ t1[3] += (FIXED_A) in[hop + 6] * consts[hop + 6];
+ t1[3] += (FIXED_A) in[hop + 7] * consts[hop + 7];
+ t1[4] += (FIXED_A) in[hop + 8] * consts[hop + 8];
+ t1[4] += (FIXED_A) in[hop + 9] * consts[hop + 9];
+ t1[5] += (FIXED_A) in[hop + 10] * consts[hop + 10];
+ t1[5] += (FIXED_A) in[hop + 11] * consts[hop + 11];
+ t1[6] += (FIXED_A) in[hop + 12] * consts[hop + 12];
+ t1[6] += (FIXED_A) in[hop + 13] * consts[hop + 13];
+ t1[7] += (FIXED_A) in[hop + 14] * consts[hop + 14];
+ t1[7] += (FIXED_A) in[hop + 15] * consts[hop + 15];
+ }
+
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED8_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED8_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED8_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED8_SCALE;
+ t2[4] = t1[4] >> SBC_PROTO_FIXED8_SCALE;
+ t2[5] = t1[5] >> SBC_PROTO_FIXED8_SCALE;
+ t2[6] = t1[6] >> SBC_PROTO_FIXED8_SCALE;
+ t2[7] = t1[7] >> SBC_PROTO_FIXED8_SCALE;
+
+
+ /* do the cos transform */
+ t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] = 0;
+
+ for (i = 0; i < 4; i++) {
+ t1[0] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 0];
+ t1[0] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 1];
+ t1[1] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 2];
+ t1[1] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 3];
+ t1[2] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 4];
+ t1[2] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 5];
+ t1[3] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 6];
+ t1[3] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 7];
+ t1[4] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 8];
+ t1[4] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 9];
+ t1[5] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 10];
+ t1[5] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 11];
+ t1[6] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 12];
+ t1[6] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 13];
+ t1[7] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 14];
+ t1[7] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 15];
+ }
+
+ for (i = 0; i < 8; i++)
+ out[i] = t1[i] >>
+ (SBC_COS_TABLE_FIXED8_SCALE - SCALE_OUT_BITS);
+}
+
+static inline void sbc_analyze_4b_4s_simd(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_four_simd(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_simd(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ sbc_analyze_four_simd(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_simd(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_simd(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_eight_simd(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_simd(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ sbc_analyze_eight_simd(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_simd(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+static inline int16_t unaligned16_be(const uint8_t *ptr)
+{
+ return (int16_t) ((ptr[0] << 8) | ptr[1]);
+}
+
+static inline int16_t unaligned16_le(const uint8_t *ptr)
+{
+ return (int16_t) (ptr[0] | (ptr[1] << 8));
+}
+
+/*
+ * Internal helper functions for input data processing. In order to get
+ * optimal performance, it is important to have "nsamples", "nchannels"
+ * and "big_endian" arguments used with this inline function as compile
+ * time constants.
+ */
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s4_internal(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ if (nchannels > 0)
+ memcpy(&X[0][SBC_X_BUFFER_SIZE - 36], &X[0][position],
+ 36 * sizeof(int16_t));
+ if (nchannels > 1)
+ memcpy(&X[1][SBC_X_BUFFER_SIZE - 36], &X[1][position],
+ 36 * sizeof(int16_t));
+ position = SBC_X_BUFFER_SIZE - 36;
+ }
+
+ #define PCM(i) (big_endian ? \
+ unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+ /* copy/permutate audio samples */
+ while ((nsamples -= 8) >= 0) {
+ position -= 8;
+ if (nchannels > 0) {
+ int16_t *x = &X[0][position];
+ x[0] = PCM(0 + 7 * nchannels);
+ x[1] = PCM(0 + 3 * nchannels);
+ x[2] = PCM(0 + 6 * nchannels);
+ x[3] = PCM(0 + 4 * nchannels);
+ x[4] = PCM(0 + 0 * nchannels);
+ x[5] = PCM(0 + 2 * nchannels);
+ x[6] = PCM(0 + 1 * nchannels);
+ x[7] = PCM(0 + 5 * nchannels);
+ }
+ if (nchannels > 1) {
+ int16_t *x = &X[1][position];
+ x[0] = PCM(1 + 7 * nchannels);
+ x[1] = PCM(1 + 3 * nchannels);
+ x[2] = PCM(1 + 6 * nchannels);
+ x[3] = PCM(1 + 4 * nchannels);
+ x[4] = PCM(1 + 0 * nchannels);
+ x[5] = PCM(1 + 2 * nchannels);
+ x[6] = PCM(1 + 1 * nchannels);
+ x[7] = PCM(1 + 5 * nchannels);
+ }
+ pcm += 16 * nchannels;
+ }
+ #undef PCM
+
+ return position;
+}
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s8_internal(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ if (nchannels > 0)
+ memcpy(&X[0][SBC_X_BUFFER_SIZE - 72], &X[0][position],
+ 72 * sizeof(int16_t));
+ if (nchannels > 1)
+ memcpy(&X[1][SBC_X_BUFFER_SIZE - 72], &X[1][position],
+ 72 * sizeof(int16_t));
+ position = SBC_X_BUFFER_SIZE - 72;
+ }
+
+ #define PCM(i) (big_endian ? \
+ unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+ /* copy/permutate audio samples */
+ while ((nsamples -= 16) >= 0) {
+ position -= 16;
+ if (nchannels > 0) {
+ int16_t *x = &X[0][position];
+ x[0] = PCM(0 + 15 * nchannels);
+ x[1] = PCM(0 + 7 * nchannels);
+ x[2] = PCM(0 + 14 * nchannels);
+ x[3] = PCM(0 + 8 * nchannels);
+ x[4] = PCM(0 + 13 * nchannels);
+ x[5] = PCM(0 + 9 * nchannels);
+ x[6] = PCM(0 + 12 * nchannels);
+ x[7] = PCM(0 + 10 * nchannels);
+ x[8] = PCM(0 + 11 * nchannels);
+ x[9] = PCM(0 + 3 * nchannels);
+ x[10] = PCM(0 + 6 * nchannels);
+ x[11] = PCM(0 + 0 * nchannels);
+ x[12] = PCM(0 + 5 * nchannels);
+ x[13] = PCM(0 + 1 * nchannels);
+ x[14] = PCM(0 + 4 * nchannels);
+ x[15] = PCM(0 + 2 * nchannels);
+ }
+ if (nchannels > 1) {
+ int16_t *x = &X[1][position];
+ x[0] = PCM(1 + 15 * nchannels);
+ x[1] = PCM(1 + 7 * nchannels);
+ x[2] = PCM(1 + 14 * nchannels);
+ x[3] = PCM(1 + 8 * nchannels);
+ x[4] = PCM(1 + 13 * nchannels);
+ x[5] = PCM(1 + 9 * nchannels);
+ x[6] = PCM(1 + 12 * nchannels);
+ x[7] = PCM(1 + 10 * nchannels);
+ x[8] = PCM(1 + 11 * nchannels);
+ x[9] = PCM(1 + 3 * nchannels);
+ x[10] = PCM(1 + 6 * nchannels);
+ x[11] = PCM(1 + 0 * nchannels);
+ x[12] = PCM(1 + 5 * nchannels);
+ x[13] = PCM(1 + 1 * nchannels);
+ x[14] = PCM(1 + 4 * nchannels);
+ x[15] = PCM(1 + 2 * nchannels);
+ }
+ pcm += 32 * nchannels;
+ }
+ #undef PCM
+
+ return position;
+}
+
+/*
+ * Input data processing functions. The data is endian converted if needed,
+ * channels are deintrleaved and audio samples are reordered for use in
+ * SIMD-friendly analysis filter function. The results are put into "X"
+ * array, getting appended to the previous data (or it is better to say
+ * prepended, as the buffer is filled from top to bottom). Old data is
+ * discarded when neededed, but availability of (10 * nrof_subbands)
+ * contiguous samples is always guaranteed for the input to the analysis
+ * filter. This is achieved by copying a sufficient part of old data
+ * to the top of the buffer on buffer wraparound.
+ */
+
+static int sbc_enc_process_input_4s_le(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 2, 0);
+ else
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_4s_be(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 2, 1);
+ else
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 1, 1);
+}
+
+static int sbc_enc_process_input_8s_le(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 2, 0);
+ else
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_8s_be(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 2, 1);
+ else
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 1, 1);
+}
+
+/* Supplementary function to count the number of leading zeros */
+
+static inline int sbc_clz(uint32_t x)
+{
+#ifdef __GNUC__
+ return __builtin_clz(x);
+#else
+ /* TODO: this should be replaced with something better if good
+ * performance is wanted when using compilers other than gcc */
+ int cnt = 0;
+ while (x) {
+ cnt++;
+ x >>= 1;
+ }
+ return 32 - cnt;
+#endif
+}
+
+static void sbc_calc_scalefactors(
+ int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int channels, int subbands)
+{
+ int ch, sb, blk;
+ for (ch = 0; ch < channels; ch++) {
+ for (sb = 0; sb < subbands; sb++) {
+ uint32_t x = 1 << SCALE_OUT_BITS;
+ for (blk = 0; blk < blocks; blk++) {
+ int32_t tmp = fabs(sb_sample_f[blk][ch][sb]);
+ if (tmp != 0)
+ x |= tmp - 1;
+ }
+ scale_factor[ch][sb] = (31 - SCALE_OUT_BITS) -
+ sbc_clz(x);
+ }
+ }
+}
+
+/*
+ * Detect CPU features and setup function pointers
+ */
+void sbc_init_primitives(struct sbc_encoder_state *state)
+{
+ /* Default implementation for analyze functions */
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_simd;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_simd;
+
+ /* Default implementation for input reordering / deinterleaving */
+ state->sbc_enc_process_input_4s_le = sbc_enc_process_input_4s_le;
+ state->sbc_enc_process_input_4s_be = sbc_enc_process_input_4s_be;
+ state->sbc_enc_process_input_8s_le = sbc_enc_process_input_8s_le;
+ state->sbc_enc_process_input_8s_be = sbc_enc_process_input_8s_be;
+
+ /* Default implementation for scale factors calculation */
+ state->sbc_calc_scalefactors = sbc_calc_scalefactors;
+
+ /* X86/AMD64 optimizations */
+#ifdef SBC_BUILD_WITH_MMX_SUPPORT
+ sbc_init_primitives_mmx(state);
+#endif
+
+ /* ARM optimizations */
+#ifdef SBC_BUILD_WITH_NEON_SUPPORT
+ sbc_init_primitives_neon(state);
+#endif
+}
diff --git a/src/modules/bluetooth/sbc_primitives.h b/src/modules/bluetooth/sbc_primitives.h
new file mode 100644
index 00000000..2708c829
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_H
+#define __SBC_PRIMITIVES_H
+
+#define SCALE_OUT_BITS 15
+#define SBC_X_BUFFER_SIZE 328
+
+#ifdef __GNUC__
+#define SBC_ALWAYS_INLINE __attribute__((always_inline))
+#else
+#define SBC_ALWAYS_INLINE inline
+#endif
+
+struct sbc_encoder_state {
+ int position;
+ int16_t SBC_ALIGNED X[2][SBC_X_BUFFER_SIZE];
+ /* Polyphase analysis filter for 4 subbands configuration,
+ * it handles 4 blocks at once */
+ void (*sbc_analyze_4b_4s)(int16_t *x, int32_t *out, int out_stride);
+ /* Polyphase analysis filter for 8 subbands configuration,
+ * it handles 4 blocks at once */
+ void (*sbc_analyze_4b_8s)(int16_t *x, int32_t *out, int out_stride);
+ /* Process input data (deinterleave, endian conversion, reordering),
+ * depending on the number of subbands and input data byte order */
+ int (*sbc_enc_process_input_4s_le)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ int (*sbc_enc_process_input_4s_be)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ int (*sbc_enc_process_input_8s_le)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ int (*sbc_enc_process_input_8s_be)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ /* Scale factors calculation */
+ void (*sbc_calc_scalefactors)(int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int channels, int subbands);
+};
+
+/*
+ * Initialize pointers to the functions which are the basic "building bricks"
+ * of SBC codec. Best implementation is selected based on target CPU
+ * capabilities.
+ */
+void sbc_init_primitives(struct sbc_encoder_state *encoder_state);
+
+#endif
diff --git a/src/modules/bluetooth/sbc_primitives_mmx.c b/src/modules/bluetooth/sbc_primitives_mmx.c
new file mode 100644
index 00000000..1870a9ba
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives_mmx.c
@@ -0,0 +1,319 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_mmx.h"
+
+/*
+ * MMX optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_MMX_SUPPORT
+
+static inline void sbc_analyze_four_mmx(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ static const SBC_ALIGNED int32_t round_c[2] = {
+ 1 << (SBC_PROTO_FIXED4_SCALE - 1),
+ 1 << (SBC_PROTO_FIXED4_SCALE - 1),
+ };
+ asm volatile (
+ "movq (%0), %%mm0\n"
+ "movq 8(%0), %%mm1\n"
+ "pmaddwd (%1), %%mm0\n"
+ "pmaddwd 8(%1), %%mm1\n"
+ "paddd (%2), %%mm0\n"
+ "paddd (%2), %%mm1\n"
+ "\n"
+ "movq 16(%0), %%mm2\n"
+ "movq 24(%0), %%mm3\n"
+ "pmaddwd 16(%1), %%mm2\n"
+ "pmaddwd 24(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "movq 32(%0), %%mm2\n"
+ "movq 40(%0), %%mm3\n"
+ "pmaddwd 32(%1), %%mm2\n"
+ "pmaddwd 40(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "movq 48(%0), %%mm2\n"
+ "movq 56(%0), %%mm3\n"
+ "pmaddwd 48(%1), %%mm2\n"
+ "pmaddwd 56(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "movq 64(%0), %%mm2\n"
+ "movq 72(%0), %%mm3\n"
+ "pmaddwd 64(%1), %%mm2\n"
+ "pmaddwd 72(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "psrad %4, %%mm0\n"
+ "psrad %4, %%mm1\n"
+ "packssdw %%mm0, %%mm0\n"
+ "packssdw %%mm1, %%mm1\n"
+ "\n"
+ "movq %%mm0, %%mm2\n"
+ "pmaddwd 80(%1), %%mm0\n"
+ "pmaddwd 88(%1), %%mm2\n"
+ "\n"
+ "movq %%mm1, %%mm3\n"
+ "pmaddwd 96(%1), %%mm1\n"
+ "pmaddwd 104(%1), %%mm3\n"
+ "paddd %%mm1, %%mm0\n"
+ "paddd %%mm3, %%mm2\n"
+ "\n"
+ "movq %%mm0, (%3)\n"
+ "movq %%mm2, 8(%3)\n"
+ :
+ : "r" (in), "r" (consts), "r" (&round_c), "r" (out),
+ "i" (SBC_PROTO_FIXED4_SCALE)
+ : "memory");
+}
+
+static inline void sbc_analyze_eight_mmx(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ static const SBC_ALIGNED int32_t round_c[2] = {
+ 1 << (SBC_PROTO_FIXED8_SCALE - 1),
+ 1 << (SBC_PROTO_FIXED8_SCALE - 1),
+ };
+ asm volatile (
+ "movq (%0), %%mm0\n"
+ "movq 8(%0), %%mm1\n"
+ "movq 16(%0), %%mm2\n"
+ "movq 24(%0), %%mm3\n"
+ "pmaddwd (%1), %%mm0\n"
+ "pmaddwd 8(%1), %%mm1\n"
+ "pmaddwd 16(%1), %%mm2\n"
+ "pmaddwd 24(%1), %%mm3\n"
+ "paddd (%2), %%mm0\n"
+ "paddd (%2), %%mm1\n"
+ "paddd (%2), %%mm2\n"
+ "paddd (%2), %%mm3\n"
+ "\n"
+ "movq 32(%0), %%mm4\n"
+ "movq 40(%0), %%mm5\n"
+ "movq 48(%0), %%mm6\n"
+ "movq 56(%0), %%mm7\n"
+ "pmaddwd 32(%1), %%mm4\n"
+ "pmaddwd 40(%1), %%mm5\n"
+ "pmaddwd 48(%1), %%mm6\n"
+ "pmaddwd 56(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "movq 64(%0), %%mm4\n"
+ "movq 72(%0), %%mm5\n"
+ "movq 80(%0), %%mm6\n"
+ "movq 88(%0), %%mm7\n"
+ "pmaddwd 64(%1), %%mm4\n"
+ "pmaddwd 72(%1), %%mm5\n"
+ "pmaddwd 80(%1), %%mm6\n"
+ "pmaddwd 88(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "movq 96(%0), %%mm4\n"
+ "movq 104(%0), %%mm5\n"
+ "movq 112(%0), %%mm6\n"
+ "movq 120(%0), %%mm7\n"
+ "pmaddwd 96(%1), %%mm4\n"
+ "pmaddwd 104(%1), %%mm5\n"
+ "pmaddwd 112(%1), %%mm6\n"
+ "pmaddwd 120(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "movq 128(%0), %%mm4\n"
+ "movq 136(%0), %%mm5\n"
+ "movq 144(%0), %%mm6\n"
+ "movq 152(%0), %%mm7\n"
+ "pmaddwd 128(%1), %%mm4\n"
+ "pmaddwd 136(%1), %%mm5\n"
+ "pmaddwd 144(%1), %%mm6\n"
+ "pmaddwd 152(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "psrad %4, %%mm0\n"
+ "psrad %4, %%mm1\n"
+ "psrad %4, %%mm2\n"
+ "psrad %4, %%mm3\n"
+ "\n"
+ "packssdw %%mm0, %%mm0\n"
+ "packssdw %%mm1, %%mm1\n"
+ "packssdw %%mm2, %%mm2\n"
+ "packssdw %%mm3, %%mm3\n"
+ "\n"
+ "movq %%mm0, %%mm4\n"
+ "movq %%mm0, %%mm5\n"
+ "pmaddwd 160(%1), %%mm4\n"
+ "pmaddwd 168(%1), %%mm5\n"
+ "\n"
+ "movq %%mm1, %%mm6\n"
+ "movq %%mm1, %%mm7\n"
+ "pmaddwd 192(%1), %%mm6\n"
+ "pmaddwd 200(%1), %%mm7\n"
+ "paddd %%mm6, %%mm4\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm2, %%mm6\n"
+ "movq %%mm2, %%mm7\n"
+ "pmaddwd 224(%1), %%mm6\n"
+ "pmaddwd 232(%1), %%mm7\n"
+ "paddd %%mm6, %%mm4\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm3, %%mm6\n"
+ "movq %%mm3, %%mm7\n"
+ "pmaddwd 256(%1), %%mm6\n"
+ "pmaddwd 264(%1), %%mm7\n"
+ "paddd %%mm6, %%mm4\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm4, (%3)\n"
+ "movq %%mm5, 8(%3)\n"
+ "\n"
+ "movq %%mm0, %%mm5\n"
+ "pmaddwd 176(%1), %%mm0\n"
+ "pmaddwd 184(%1), %%mm5\n"
+ "\n"
+ "movq %%mm1, %%mm7\n"
+ "pmaddwd 208(%1), %%mm1\n"
+ "pmaddwd 216(%1), %%mm7\n"
+ "paddd %%mm1, %%mm0\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm2, %%mm7\n"
+ "pmaddwd 240(%1), %%mm2\n"
+ "pmaddwd 248(%1), %%mm7\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm3, %%mm7\n"
+ "pmaddwd 272(%1), %%mm3\n"
+ "pmaddwd 280(%1), %%mm7\n"
+ "paddd %%mm3, %%mm0\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm0, 16(%3)\n"
+ "movq %%mm5, 24(%3)\n"
+ :
+ : "r" (in), "r" (consts), "r" (&round_c), "r" (out),
+ "i" (SBC_PROTO_FIXED8_SCALE)
+ : "memory");
+}
+
+static inline void sbc_analyze_4b_4s_mmx(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_four_mmx(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_mmx(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ sbc_analyze_four_mmx(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_mmx(x + 0, out, analysis_consts_fixed4_simd_even);
+
+ asm volatile ("emms\n");
+}
+
+static inline void sbc_analyze_4b_8s_mmx(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_eight_mmx(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_mmx(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ sbc_analyze_eight_mmx(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_mmx(x + 0, out, analysis_consts_fixed8_simd_even);
+
+ asm volatile ("emms\n");
+}
+
+static int check_mmx_support(void)
+{
+#ifdef __amd64__
+ return 1; /* We assume that all 64-bit processors have MMX support */
+#else
+ int cpuid_feature_information;
+ asm volatile (
+ /* According to Intel manual, CPUID instruction is supported
+ * if the value of ID bit (bit 21) in EFLAGS can be modified */
+ "pushf\n"
+ "movl (%%esp), %0\n"
+ "xorl $0x200000, (%%esp)\n" /* try to modify ID bit */
+ "popf\n"
+ "pushf\n"
+ "xorl (%%esp), %0\n" /* check if ID bit changed */
+ "jz 1f\n"
+ "push %%eax\n"
+ "push %%ebx\n"
+ "push %%ecx\n"
+ "mov $1, %%eax\n"
+ "cpuid\n"
+ "pop %%ecx\n"
+ "pop %%ebx\n"
+ "pop %%eax\n"
+ "1:\n"
+ "popf\n"
+ : "=d" (cpuid_feature_information)
+ :
+ : "cc");
+ return cpuid_feature_information & (1 << 23);
+#endif
+}
+
+void sbc_init_primitives_mmx(struct sbc_encoder_state *state)
+{
+ if (check_mmx_support()) {
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_mmx;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_mmx;
+ }
+}
+
+#endif
diff --git a/src/modules/bluetooth/sbc_primitives_mmx.h b/src/modules/bluetooth/sbc_primitives_mmx.h
new file mode 100644
index 00000000..c1e44a5d
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives_mmx.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_MMX_H
+#define __SBC_PRIMITIVES_MMX_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__)) && \
+ !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_MMX_SUPPORT
+
+void sbc_init_primitives_mmx(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/src/modules/bluetooth/sbc_primitives_neon.c b/src/modules/bluetooth/sbc_primitives_neon.c
new file mode 100644
index 00000000..d9c12f9e
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives_neon.c
@@ -0,0 +1,245 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_neon.h"
+
+/*
+ * ARM NEON optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_NEON_SUPPORT
+
+static inline void _sbc_analyze_four_neon(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ /* TODO: merge even and odd cases (or even merge all four calls to this
+ * function) in order to have only aligned reads from 'in' array
+ * and reduce number of load instructions */
+ asm volatile (
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmull.s16 q0, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmull.s16 q1, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q1, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q1, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q1, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d4, d8\n"
+ "vmlal.s16 q1, d5, d9\n"
+
+ "vpadd.s32 d0, d0, d1\n"
+ "vpadd.s32 d1, d2, d3\n"
+
+ "vrshrn.s32 d0, q0, %3\n"
+
+ "vld1.16 {d2, d3, d4, d5}, [%1, :128]!\n"
+
+ "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */
+ "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */
+
+ "vmull.s16 q3, d2, d0\n"
+ "vmull.s16 q4, d3, d0\n"
+ "vmlal.s16 q3, d4, d1\n"
+ "vmlal.s16 q4, d5, d1\n"
+
+ "vpadd.s32 d0, d6, d7\n" /* TODO: can be eliminated */
+ "vpadd.s32 d1, d8, d9\n" /* TODO: can be eliminated */
+
+ "vst1.32 {d0, d1}, [%2, :128]\n"
+ : "+r" (in), "+r" (consts)
+ : "r" (out),
+ "i" (SBC_PROTO_FIXED4_SCALE)
+ : "memory",
+ "d0", "d1", "d2", "d3", "d4", "d5",
+ "d6", "d7", "d8", "d9", "d10", "d11");
+}
+
+static inline void _sbc_analyze_eight_neon(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ /* TODO: merge even and odd cases (or even merge all four calls to this
+ * function) in order to have only aligned reads from 'in' array
+ * and reduce number of load instructions */
+ asm volatile (
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmull.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmull.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmull.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmull.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmlal.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmlal.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmlal.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+
+ "vmlal.s16 q8, d6, d10\n"
+ "vmlal.s16 q9, d7, d11\n"
+
+ "vpadd.s32 d0, d12, d13\n"
+ "vpadd.s32 d1, d14, d15\n"
+ "vpadd.s32 d2, d16, d17\n"
+ "vpadd.s32 d3, d18, d19\n"
+
+ "vrshr.s32 q0, q0, %3\n"
+ "vrshr.s32 q1, q1, %3\n"
+ "vmovn.s32 d0, q0\n"
+ "vmovn.s32 d1, q1\n"
+
+ "vdup.i32 d3, d1[1]\n" /* TODO: can be eliminated */
+ "vdup.i32 d2, d1[0]\n" /* TODO: can be eliminated */
+ "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */
+ "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmull.s16 q6, d4, d0\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmull.s16 q7, d5, d0\n"
+ "vmull.s16 q8, d6, d0\n"
+ "vmull.s16 q9, d7, d0\n"
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmlal.s16 q6, d4, d1\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmlal.s16 q7, d5, d1\n"
+ "vmlal.s16 q8, d6, d1\n"
+ "vmlal.s16 q9, d7, d1\n"
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmlal.s16 q6, d4, d2\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmlal.s16 q7, d5, d2\n"
+ "vmlal.s16 q8, d6, d2\n"
+ "vmlal.s16 q9, d7, d2\n"
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmlal.s16 q6, d4, d3\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmlal.s16 q7, d5, d3\n"
+ "vmlal.s16 q8, d6, d3\n"
+ "vmlal.s16 q9, d7, d3\n"
+
+ "vpadd.s32 d0, d12, d13\n" /* TODO: can be eliminated */
+ "vpadd.s32 d1, d14, d15\n" /* TODO: can be eliminated */
+ "vpadd.s32 d2, d16, d17\n" /* TODO: can be eliminated */
+ "vpadd.s32 d3, d18, d19\n" /* TODO: can be eliminated */
+
+ "vst1.32 {d0, d1, d2, d3}, [%2, :128]\n"
+ : "+r" (in), "+r" (consts)
+ : "r" (out),
+ "i" (SBC_PROTO_FIXED8_SCALE)
+ : "memory",
+ "d0", "d1", "d2", "d3", "d4", "d5",
+ "d6", "d7", "d8", "d9", "d10", "d11",
+ "d12", "d13", "d14", "d15", "d16", "d17",
+ "d18", "d19");
+}
+
+static inline void sbc_analyze_4b_4s_neon(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ _sbc_analyze_four_neon(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ _sbc_analyze_four_neon(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ _sbc_analyze_four_neon(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ _sbc_analyze_four_neon(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_neon(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ _sbc_analyze_eight_neon(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ _sbc_analyze_eight_neon(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ _sbc_analyze_eight_neon(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ _sbc_analyze_eight_neon(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+void sbc_init_primitives_neon(struct sbc_encoder_state *state)
+{
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_neon;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_neon;
+}
+
+#endif
diff --git a/src/modules/bluetooth/sbc_primitives_neon.h b/src/modules/bluetooth/sbc_primitives_neon.h
new file mode 100644
index 00000000..30766ed8
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives_neon.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_NEON_H
+#define __SBC_PRIMITIVES_NEON_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && defined(__ARM_NEON__) && \
+ !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_NEON_SUPPORT
+
+void sbc_init_primitives_neon(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/src/modules/bluetooth/sbc_tables.h b/src/modules/bluetooth/sbc_tables.h
index f1dfe6c0..0057c73f 100644
--- a/src/modules/bluetooth/sbc_tables.h
+++ b/src/modules/bluetooth/sbc_tables.h
@@ -157,33 +157,34 @@ static const int32_t synmatrix8[16][8] = {
*/
#define SBC_PROTO_FIXED4_SCALE \
((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 1)
-#define F(x) (FIXED_A) ((x * 2) * \
+#define F_PROTO4(x) (FIXED_A) ((x * 2) * \
((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_PROTO4(x)
static const FIXED_T _sbc_proto_fixed4[40] = {
- F(0.00000000E+00), F(5.36548976E-04),
+ F(0.00000000E+00), F(5.36548976E-04),
-F(1.49188357E-03), F(2.73370904E-03),
- F(3.83720193E-03), F(3.89205149E-03),
- F(1.86581691E-03), F(3.06012286E-03),
+ F(3.83720193E-03), F(3.89205149E-03),
+ F(1.86581691E-03), F(3.06012286E-03),
- F(1.09137620E-02), F(2.04385087E-02),
+ F(1.09137620E-02), F(2.04385087E-02),
-F(2.88757392E-02), F(3.21939290E-02),
- F(2.58767811E-02), F(6.13245186E-03),
+ F(2.58767811E-02), F(6.13245186E-03),
-F(2.88217274E-02), F(7.76463494E-02),
- F(1.35593274E-01), F(1.94987841E-01),
+ F(1.35593274E-01), F(1.94987841E-01),
-F(2.46636662E-01), F(2.81828203E-01),
- F(2.94315332E-01), F(2.81828203E-01),
- F(2.46636662E-01), -F(1.94987841E-01),
+ F(2.94315332E-01), F(2.81828203E-01),
+ F(2.46636662E-01), -F(1.94987841E-01),
-F(1.35593274E-01), -F(7.76463494E-02),
- F(2.88217274E-02), F(6.13245186E-03),
- F(2.58767811E-02), F(3.21939290E-02),
- F(2.88757392E-02), -F(2.04385087E-02),
+ F(2.88217274E-02), F(6.13245186E-03),
+ F(2.58767811E-02), F(3.21939290E-02),
+ F(2.88757392E-02), -F(2.04385087E-02),
-F(1.09137620E-02), -F(3.06012286E-03),
-F(1.86581691E-03), F(3.89205149E-03),
- F(3.83720193E-03), F(2.73370904E-03),
- F(1.49188357E-03), -F(5.36548976E-04),
+ F(3.83720193E-03), F(2.73370904E-03),
+ F(1.49188357E-03), -F(5.36548976E-04),
};
#undef F
@@ -206,11 +207,12 @@ static const FIXED_T _sbc_proto_fixed4[40] = {
*/
#define SBC_COS_TABLE_FIXED4_SCALE \
((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
-#define F(x) (FIXED_A) ((x) * \
+#define F_COS4(x) (FIXED_A) ((x) * \
((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_COS4(x)
static const FIXED_T cos_table_fixed_4[32] = {
- F(0.7071067812), F(0.9238795325), -F(1.0000000000), F(0.9238795325),
- F(0.7071067812), F(0.3826834324), F(0.0000000000), F(0.3826834324),
+ F(0.7071067812), F(0.9238795325), -F(1.0000000000), F(0.9238795325),
+ F(0.7071067812), F(0.3826834324), F(0.0000000000), F(0.3826834324),
-F(0.7071067812), F(0.3826834324), -F(1.0000000000), F(0.3826834324),
-F(0.7071067812), -F(0.9238795325), -F(0.0000000000), -F(0.9238795325),
@@ -218,8 +220,8 @@ static const FIXED_T cos_table_fixed_4[32] = {
-F(0.7071067812), -F(0.3826834324), -F(1.0000000000), -F(0.3826834324),
-F(0.7071067812), F(0.9238795325), F(0.0000000000), F(0.9238795325),
- F(0.7071067812), -F(0.9238795325), -F(1.0000000000), -F(0.9238795325),
- F(0.7071067812), -F(0.3826834324), -F(0.0000000000), -F(0.3826834324),
+ F(0.7071067812), -F(0.9238795325), -F(1.0000000000), -F(0.9238795325),
+ F(0.7071067812), -F(0.3826834324), -F(0.0000000000), -F(0.3826834324),
};
#undef F
@@ -232,53 +234,54 @@ static const FIXED_T cos_table_fixed_4[32] = {
* in order to compensate the same change applied to cos_table_fixed_8
*/
#define SBC_PROTO_FIXED8_SCALE \
- ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 2)
-#define F(x) (FIXED_A) ((x * 4) * \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 1)
+#define F_PROTO8(x) (FIXED_A) ((x * 2) * \
((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_PROTO8(x)
static const FIXED_T _sbc_proto_fixed8[80] = {
- F(0.00000000E+00), F(1.56575398E-04),
- F(3.43256425E-04), F(5.54620202E-04),
+ F(0.00000000E+00), F(1.56575398E-04),
+ F(3.43256425E-04), F(5.54620202E-04),
-F(8.23919506E-04), F(1.13992507E-03),
- F(1.47640169E-03), F(1.78371725E-03),
- F(2.01182542E-03), F(2.10371989E-03),
- F(1.99454554E-03), F(1.61656283E-03),
- F(9.02154502E-04), F(1.78805361E-04),
- F(1.64973098E-03), F(3.49717454E-03),
-
- F(5.65949473E-03), F(8.02941163E-03),
- F(1.04584443E-02), F(1.27472335E-02),
+ F(1.47640169E-03), F(1.78371725E-03),
+ F(2.01182542E-03), F(2.10371989E-03),
+ F(1.99454554E-03), F(1.61656283E-03),
+ F(9.02154502E-04), F(1.78805361E-04),
+ F(1.64973098E-03), F(3.49717454E-03),
+
+ F(5.65949473E-03), F(8.02941163E-03),
+ F(1.04584443E-02), F(1.27472335E-02),
-F(1.46525263E-02), F(1.59045603E-02),
- F(1.62208471E-02), F(1.53184106E-02),
- F(1.29371806E-02), F(8.85757540E-03),
- F(2.92408442E-03), -F(4.91578024E-03),
+ F(1.62208471E-02), F(1.53184106E-02),
+ F(1.29371806E-02), F(8.85757540E-03),
+ F(2.92408442E-03), -F(4.91578024E-03),
-F(1.46404076E-02), F(2.61098752E-02),
- F(3.90751381E-02), F(5.31873032E-02),
+ F(3.90751381E-02), F(5.31873032E-02),
- F(6.79989431E-02), F(8.29847578E-02),
- F(9.75753918E-02), F(1.11196689E-01),
+ F(6.79989431E-02), F(8.29847578E-02),
+ F(9.75753918E-02), F(1.11196689E-01),
-F(1.23264548E-01), F(1.33264415E-01),
- F(1.40753505E-01), F(1.45389847E-01),
- F(1.46955068E-01), F(1.45389847E-01),
- F(1.40753505E-01), F(1.33264415E-01),
- F(1.23264548E-01), -F(1.11196689E-01),
+ F(1.40753505E-01), F(1.45389847E-01),
+ F(1.46955068E-01), F(1.45389847E-01),
+ F(1.40753505E-01), F(1.33264415E-01),
+ F(1.23264548E-01), -F(1.11196689E-01),
-F(9.75753918E-02), -F(8.29847578E-02),
-F(6.79989431E-02), -F(5.31873032E-02),
-F(3.90751381E-02), -F(2.61098752E-02),
- F(1.46404076E-02), -F(4.91578024E-03),
- F(2.92408442E-03), F(8.85757540E-03),
- F(1.29371806E-02), F(1.53184106E-02),
- F(1.62208471E-02), F(1.59045603E-02),
- F(1.46525263E-02), -F(1.27472335E-02),
+ F(1.46404076E-02), -F(4.91578024E-03),
+ F(2.92408442E-03), F(8.85757540E-03),
+ F(1.29371806E-02), F(1.53184106E-02),
+ F(1.62208471E-02), F(1.59045603E-02),
+ F(1.46525263E-02), -F(1.27472335E-02),
-F(1.04584443E-02), -F(8.02941163E-03),
-F(5.65949473E-03), -F(3.49717454E-03),
-F(1.64973098E-03), -F(1.78805361E-04),
-F(9.02154502E-04), F(1.61656283E-03),
- F(1.99454554E-03), F(2.10371989E-03),
- F(2.01182542E-03), F(1.78371725E-03),
- F(1.47640169E-03), F(1.13992507E-03),
- F(8.23919506E-04), -F(5.54620202E-04),
+ F(1.99454554E-03), F(2.10371989E-03),
+ F(2.01182542E-03), F(1.78371725E-03),
+ F(1.47640169E-03), F(1.13992507E-03),
+ F(8.23919506E-04), -F(5.54620202E-04),
-F(3.43256425E-04), -F(1.56575398E-04),
};
#undef F
@@ -301,13 +304,14 @@ static const FIXED_T _sbc_proto_fixed8[80] = {
*/
#define SBC_COS_TABLE_FIXED8_SCALE \
((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
-#define F(x) (FIXED_A) ((x) * \
+#define F_COS8(x) (FIXED_A) ((x) * \
((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_COS8(x)
static const FIXED_T cos_table_fixed_8[128] = {
- F(0.7071067812), F(0.8314696123), F(0.9238795325), F(0.9807852804),
+ F(0.7071067812), F(0.8314696123), F(0.9238795325), F(0.9807852804),
-F(1.0000000000), F(0.9807852804), F(0.9238795325), F(0.8314696123),
- F(0.7071067812), F(0.5555702330), F(0.3826834324), F(0.1950903220),
- F(0.0000000000), F(0.1950903220), F(0.3826834324), F(0.5555702330),
+ F(0.7071067812), F(0.5555702330), F(0.3826834324), F(0.1950903220),
+ F(0.0000000000), F(0.1950903220), F(0.3826834324), F(0.5555702330),
-F(0.7071067812), -F(0.1950903220), F(0.3826834324), F(0.8314696123),
-F(1.0000000000), F(0.8314696123), F(0.3826834324), -F(0.1950903220),
@@ -317,17 +321,17 @@ static const FIXED_T cos_table_fixed_8[128] = {
-F(0.7071067812), -F(0.9807852804), -F(0.3826834324), F(0.5555702330),
-F(1.0000000000), F(0.5555702330), -F(0.3826834324), -F(0.9807852804),
-F(0.7071067812), F(0.1950903220), F(0.9238795325), F(0.8314696123),
- F(0.0000000000), F(0.8314696123), F(0.9238795325), F(0.1950903220),
+ F(0.0000000000), F(0.8314696123), F(0.9238795325), F(0.1950903220),
- F(0.7071067812), -F(0.5555702330), -F(0.9238795325), F(0.1950903220),
+ F(0.7071067812), -F(0.5555702330), -F(0.9238795325), F(0.1950903220),
-F(1.0000000000), F(0.1950903220), -F(0.9238795325), -F(0.5555702330),
- F(0.7071067812), F(0.8314696123), -F(0.3826834324), -F(0.9807852804),
+ F(0.7071067812), F(0.8314696123), -F(0.3826834324), -F(0.9807852804),
-F(0.0000000000), -F(0.9807852804), -F(0.3826834324), F(0.8314696123),
- F(0.7071067812), F(0.5555702330), -F(0.9238795325), -F(0.1950903220),
+ F(0.7071067812), F(0.5555702330), -F(0.9238795325), -F(0.1950903220),
-F(1.0000000000), -F(0.1950903220), -F(0.9238795325), F(0.5555702330),
- F(0.7071067812), -F(0.8314696123), -F(0.3826834324), F(0.9807852804),
- F(0.0000000000), F(0.9807852804), -F(0.3826834324), -F(0.8314696123),
+ F(0.7071067812), -F(0.8314696123), -F(0.3826834324), F(0.9807852804),
+ F(0.0000000000), F(0.9807852804), -F(0.3826834324), -F(0.8314696123),
-F(0.7071067812), F(0.9807852804), -F(0.3826834324), -F(0.5555702330),
-F(1.0000000000), -F(0.5555702330), -F(0.3826834324), F(0.9807852804),
@@ -339,9 +343,317 @@ static const FIXED_T cos_table_fixed_8[128] = {
-F(0.7071067812), F(0.9807852804), -F(0.9238795325), F(0.5555702330),
-F(0.0000000000), F(0.5555702330), -F(0.9238795325), F(0.9807852804),
- F(0.7071067812), -F(0.8314696123), F(0.9238795325), -F(0.9807852804),
+ F(0.7071067812), -F(0.8314696123), F(0.9238795325), -F(0.9807852804),
-F(1.0000000000), -F(0.9807852804), F(0.9238795325), -F(0.8314696123),
- F(0.7071067812), -F(0.5555702330), F(0.3826834324), -F(0.1950903220),
+ F(0.7071067812), -F(0.5555702330), F(0.3826834324), -F(0.1950903220),
-F(0.0000000000), -F(0.1950903220), F(0.3826834324), -F(0.5555702330),
};
#undef F
+
+/*
+ * Enforce 16 byte alignment for the data, which is supposed to be used
+ * with SIMD optimized code.
+ */
+
+#define SBC_ALIGN_BITS 4
+#define SBC_ALIGN_MASK ((1 << (SBC_ALIGN_BITS)) - 1)
+
+#ifdef __GNUC__
+#define SBC_ALIGNED __attribute__((aligned(1 << (SBC_ALIGN_BITS))))
+#else
+#define SBC_ALIGNED
+#endif
+
+/*
+ * Constant tables for the use in SIMD optimized analysis filters
+ * Each table consists of two parts:
+ * 1. reordered "proto" table
+ * 2. reordered "cos" table
+ *
+ * Due to non-symmetrical reordering, separate tables for "even"
+ * and "odd" cases are needed
+ */
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed4_simd_even[40 + 16] = {
+#define C0 1.0932568993
+#define C1 1.3056875580
+#define C2 1.3056875580
+#define C3 1.6772280856
+
+#define F(x) F_PROTO4(x)
+ F(0.00000000E+00 * C0), F(3.83720193E-03 * C0),
+ F(5.36548976E-04 * C1), F(2.73370904E-03 * C1),
+ F(3.06012286E-03 * C2), F(3.89205149E-03 * C2),
+ F(0.00000000E+00 * C3), -F(1.49188357E-03 * C3),
+ F(1.09137620E-02 * C0), F(2.58767811E-02 * C0),
+ F(2.04385087E-02 * C1), F(3.21939290E-02 * C1),
+ F(7.76463494E-02 * C2), F(6.13245186E-03 * C2),
+ F(0.00000000E+00 * C3), -F(2.88757392E-02 * C3),
+ F(1.35593274E-01 * C0), F(2.94315332E-01 * C0),
+ F(1.94987841E-01 * C1), F(2.81828203E-01 * C1),
+ -F(1.94987841E-01 * C2), F(2.81828203E-01 * C2),
+ F(0.00000000E+00 * C3), -F(2.46636662E-01 * C3),
+ -F(1.35593274E-01 * C0), F(2.58767811E-02 * C0),
+ -F(7.76463494E-02 * C1), F(6.13245186E-03 * C1),
+ -F(2.04385087E-02 * C2), F(3.21939290E-02 * C2),
+ F(0.00000000E+00 * C3), F(2.88217274E-02 * C3),
+ -F(1.09137620E-02 * C0), F(3.83720193E-03 * C0),
+ -F(3.06012286E-03 * C1), F(3.89205149E-03 * C1),
+ -F(5.36548976E-04 * C2), F(2.73370904E-03 * C2),
+ F(0.00000000E+00 * C3), -F(1.86581691E-03 * C3),
+#undef F
+#define F(x) F_COS4(x)
+ F(0.7071067812 / C0), F(0.9238795325 / C1),
+ -F(0.7071067812 / C0), F(0.3826834324 / C1),
+ -F(0.7071067812 / C0), -F(0.3826834324 / C1),
+ F(0.7071067812 / C0), -F(0.9238795325 / C1),
+ F(0.3826834324 / C2), -F(1.0000000000 / C3),
+ -F(0.9238795325 / C2), -F(1.0000000000 / C3),
+ F(0.9238795325 / C2), -F(1.0000000000 / C3),
+ -F(0.3826834324 / C2), -F(1.0000000000 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed4_simd_odd[40 + 16] = {
+#define C0 1.3056875580
+#define C1 1.6772280856
+#define C2 1.0932568993
+#define C3 1.3056875580
+
+#define F(x) F_PROTO4(x)
+ F(2.73370904E-03 * C0), F(5.36548976E-04 * C0),
+ -F(1.49188357E-03 * C1), F(0.00000000E+00 * C1),
+ F(3.83720193E-03 * C2), F(1.09137620E-02 * C2),
+ F(3.89205149E-03 * C3), F(3.06012286E-03 * C3),
+ F(3.21939290E-02 * C0), F(2.04385087E-02 * C0),
+ -F(2.88757392E-02 * C1), F(0.00000000E+00 * C1),
+ F(2.58767811E-02 * C2), F(1.35593274E-01 * C2),
+ F(6.13245186E-03 * C3), F(7.76463494E-02 * C3),
+ F(2.81828203E-01 * C0), F(1.94987841E-01 * C0),
+ -F(2.46636662E-01 * C1), F(0.00000000E+00 * C1),
+ F(2.94315332E-01 * C2), -F(1.35593274E-01 * C2),
+ F(2.81828203E-01 * C3), -F(1.94987841E-01 * C3),
+ F(6.13245186E-03 * C0), -F(7.76463494E-02 * C0),
+ F(2.88217274E-02 * C1), F(0.00000000E+00 * C1),
+ F(2.58767811E-02 * C2), -F(1.09137620E-02 * C2),
+ F(3.21939290E-02 * C3), -F(2.04385087E-02 * C3),
+ F(3.89205149E-03 * C0), -F(3.06012286E-03 * C0),
+ -F(1.86581691E-03 * C1), F(0.00000000E+00 * C1),
+ F(3.83720193E-03 * C2), F(0.00000000E+00 * C2),
+ F(2.73370904E-03 * C3), -F(5.36548976E-04 * C3),
+#undef F
+#define F(x) F_COS4(x)
+ F(0.9238795325 / C0), -F(1.0000000000 / C1),
+ F(0.3826834324 / C0), -F(1.0000000000 / C1),
+ -F(0.3826834324 / C0), -F(1.0000000000 / C1),
+ -F(0.9238795325 / C0), -F(1.0000000000 / C1),
+ F(0.7071067812 / C2), F(0.3826834324 / C3),
+ -F(0.7071067812 / C2), -F(0.9238795325 / C3),
+ -F(0.7071067812 / C2), F(0.9238795325 / C3),
+ F(0.7071067812 / C2), -F(0.3826834324 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed8_simd_even[80 + 64] = {
+#define C0 2.7906148894
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.5377944043
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO8(x)
+ F(0.00000000E+00 * C0), F(2.01182542E-03 * C0),
+ F(1.56575398E-04 * C1), F(1.78371725E-03 * C1),
+ F(3.43256425E-04 * C2), F(1.47640169E-03 * C2),
+ F(5.54620202E-04 * C3), F(1.13992507E-03 * C3),
+ -F(8.23919506E-04 * C4), F(0.00000000E+00 * C4),
+ F(2.10371989E-03 * C5), F(3.49717454E-03 * C5),
+ F(1.99454554E-03 * C6), F(1.64973098E-03 * C6),
+ F(1.61656283E-03 * C7), F(1.78805361E-04 * C7),
+ F(5.65949473E-03 * C0), F(1.29371806E-02 * C0),
+ F(8.02941163E-03 * C1), F(1.53184106E-02 * C1),
+ F(1.04584443E-02 * C2), F(1.62208471E-02 * C2),
+ F(1.27472335E-02 * C3), F(1.59045603E-02 * C3),
+ -F(1.46525263E-02 * C4), F(0.00000000E+00 * C4),
+ F(8.85757540E-03 * C5), F(5.31873032E-02 * C5),
+ F(2.92408442E-03 * C6), F(3.90751381E-02 * C6),
+ -F(4.91578024E-03 * C7), F(2.61098752E-02 * C7),
+ F(6.79989431E-02 * C0), F(1.46955068E-01 * C0),
+ F(8.29847578E-02 * C1), F(1.45389847E-01 * C1),
+ F(9.75753918E-02 * C2), F(1.40753505E-01 * C2),
+ F(1.11196689E-01 * C3), F(1.33264415E-01 * C3),
+ -F(1.23264548E-01 * C4), F(0.00000000E+00 * C4),
+ F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+ F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+ F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+ -F(6.79989431E-02 * C0), F(1.29371806E-02 * C0),
+ -F(5.31873032E-02 * C1), F(8.85757540E-03 * C1),
+ -F(3.90751381E-02 * C2), F(2.92408442E-03 * C2),
+ -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+ F(1.46404076E-02 * C4), F(0.00000000E+00 * C4),
+ F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+ F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+ F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+ -F(5.65949473E-03 * C0), F(2.01182542E-03 * C0),
+ -F(3.49717454E-03 * C1), F(2.10371989E-03 * C1),
+ -F(1.64973098E-03 * C2), F(1.99454554E-03 * C2),
+ -F(1.78805361E-04 * C3), F(1.61656283E-03 * C3),
+ -F(9.02154502E-04 * C4), F(0.00000000E+00 * C4),
+ F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+ F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+ F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS8(x)
+ F(0.7071067812 / C0), F(0.8314696123 / C1),
+ -F(0.7071067812 / C0), -F(0.1950903220 / C1),
+ -F(0.7071067812 / C0), -F(0.9807852804 / C1),
+ F(0.7071067812 / C0), -F(0.5555702330 / C1),
+ F(0.7071067812 / C0), F(0.5555702330 / C1),
+ -F(0.7071067812 / C0), F(0.9807852804 / C1),
+ -F(0.7071067812 / C0), F(0.1950903220 / C1),
+ F(0.7071067812 / C0), -F(0.8314696123 / C1),
+ F(0.9238795325 / C2), F(0.9807852804 / C3),
+ F(0.3826834324 / C2), F(0.8314696123 / C3),
+ -F(0.3826834324 / C2), F(0.5555702330 / C3),
+ -F(0.9238795325 / C2), F(0.1950903220 / C3),
+ -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+ -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+ F(0.3826834324 / C2), -F(0.8314696123 / C3),
+ F(0.9238795325 / C2), -F(0.9807852804 / C3),
+ -F(1.0000000000 / C4), F(0.5555702330 / C5),
+ -F(1.0000000000 / C4), -F(0.9807852804 / C5),
+ -F(1.0000000000 / C4), F(0.1950903220 / C5),
+ -F(1.0000000000 / C4), F(0.8314696123 / C5),
+ -F(1.0000000000 / C4), -F(0.8314696123 / C5),
+ -F(1.0000000000 / C4), -F(0.1950903220 / C5),
+ -F(1.0000000000 / C4), F(0.9807852804 / C5),
+ -F(1.0000000000 / C4), -F(0.5555702330 / C5),
+ F(0.3826834324 / C6), F(0.1950903220 / C7),
+ -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+ F(0.9238795325 / C6), F(0.8314696123 / C7),
+ -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+ -F(0.3826834324 / C6), F(0.9807852804 / C7),
+ F(0.9238795325 / C6), -F(0.8314696123 / C7),
+ -F(0.9238795325 / C6), F(0.5555702330 / C7),
+ F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed8_simd_odd[80 + 64] = {
+#define C0 2.5377944043
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.7906148894
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO8(x)
+ F(0.00000000E+00 * C0), -F(8.23919506E-04 * C0),
+ F(1.56575398E-04 * C1), F(1.78371725E-03 * C1),
+ F(3.43256425E-04 * C2), F(1.47640169E-03 * C2),
+ F(5.54620202E-04 * C3), F(1.13992507E-03 * C3),
+ F(2.01182542E-03 * C4), F(5.65949473E-03 * C4),
+ F(2.10371989E-03 * C5), F(3.49717454E-03 * C5),
+ F(1.99454554E-03 * C6), F(1.64973098E-03 * C6),
+ F(1.61656283E-03 * C7), F(1.78805361E-04 * C7),
+ F(0.00000000E+00 * C0), -F(1.46525263E-02 * C0),
+ F(8.02941163E-03 * C1), F(1.53184106E-02 * C1),
+ F(1.04584443E-02 * C2), F(1.62208471E-02 * C2),
+ F(1.27472335E-02 * C3), F(1.59045603E-02 * C3),
+ F(1.29371806E-02 * C4), F(6.79989431E-02 * C4),
+ F(8.85757540E-03 * C5), F(5.31873032E-02 * C5),
+ F(2.92408442E-03 * C6), F(3.90751381E-02 * C6),
+ -F(4.91578024E-03 * C7), F(2.61098752E-02 * C7),
+ F(0.00000000E+00 * C0), -F(1.23264548E-01 * C0),
+ F(8.29847578E-02 * C1), F(1.45389847E-01 * C1),
+ F(9.75753918E-02 * C2), F(1.40753505E-01 * C2),
+ F(1.11196689E-01 * C3), F(1.33264415E-01 * C3),
+ F(1.46955068E-01 * C4), -F(6.79989431E-02 * C4),
+ F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+ F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+ F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+ F(0.00000000E+00 * C0), F(1.46404076E-02 * C0),
+ -F(5.31873032E-02 * C1), F(8.85757540E-03 * C1),
+ -F(3.90751381E-02 * C2), F(2.92408442E-03 * C2),
+ -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+ F(1.29371806E-02 * C4), -F(5.65949473E-03 * C4),
+ F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+ F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+ F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+ F(0.00000000E+00 * C0), -F(9.02154502E-04 * C0),
+ -F(3.49717454E-03 * C1), F(2.10371989E-03 * C1),
+ -F(1.64973098E-03 * C2), F(1.99454554E-03 * C2),
+ -F(1.78805361E-04 * C3), F(1.61656283E-03 * C3),
+ F(2.01182542E-03 * C4), F(0.00000000E+00 * C4),
+ F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+ F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+ F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS8(x)
+ -F(1.0000000000 / C0), F(0.8314696123 / C1),
+ -F(1.0000000000 / C0), -F(0.1950903220 / C1),
+ -F(1.0000000000 / C0), -F(0.9807852804 / C1),
+ -F(1.0000000000 / C0), -F(0.5555702330 / C1),
+ -F(1.0000000000 / C0), F(0.5555702330 / C1),
+ -F(1.0000000000 / C0), F(0.9807852804 / C1),
+ -F(1.0000000000 / C0), F(0.1950903220 / C1),
+ -F(1.0000000000 / C0), -F(0.8314696123 / C1),
+ F(0.9238795325 / C2), F(0.9807852804 / C3),
+ F(0.3826834324 / C2), F(0.8314696123 / C3),
+ -F(0.3826834324 / C2), F(0.5555702330 / C3),
+ -F(0.9238795325 / C2), F(0.1950903220 / C3),
+ -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+ -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+ F(0.3826834324 / C2), -F(0.8314696123 / C3),
+ F(0.9238795325 / C2), -F(0.9807852804 / C3),
+ F(0.7071067812 / C4), F(0.5555702330 / C5),
+ -F(0.7071067812 / C4), -F(0.9807852804 / C5),
+ -F(0.7071067812 / C4), F(0.1950903220 / C5),
+ F(0.7071067812 / C4), F(0.8314696123 / C5),
+ F(0.7071067812 / C4), -F(0.8314696123 / C5),
+ -F(0.7071067812 / C4), -F(0.1950903220 / C5),
+ -F(0.7071067812 / C4), F(0.9807852804 / C5),
+ F(0.7071067812 / C4), -F(0.5555702330 / C5),
+ F(0.3826834324 / C6), F(0.1950903220 / C7),
+ -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+ F(0.9238795325 / C6), F(0.8314696123 / C7),
+ -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+ -F(0.3826834324 / C6), F(0.9807852804 / C7),
+ F(0.9238795325 / C6), -F(0.8314696123 / C7),
+ -F(0.9238795325 / C6), F(0.5555702330 / C7),
+ F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index 02e973c4..4dffd365 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -68,7 +68,10 @@ struct userdata {
GDBM_FILE gdbm_file;
};
-struct entry {
+#define ENTRY_VERSION 1
+
+struct entry PA_GCC_PACKED {
+ uint8_t version;
char profile[PA_NAME_MAX];
};
@@ -104,12 +107,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
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));
+ pa_log_debug("Database contains entry for card %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
goto fail;
}
e = (struct entry*) data.dptr;
+ if (e->version != ENTRY_VERSION) {
+ pa_log_debug("Version of database entry for card %s doesn't match our version. Probably due to upgrade, ignoring.", name);
+ goto fail;
+ }
+
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;
@@ -148,6 +156,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
memset(&entry, 0, sizeof(entry));
+ entry.version = ENTRY_VERSION;
if (!(card = pa_idxset_get_by_index(c->cards, idx)))
return;
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index 82c88711..7f1ef24c 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -799,7 +799,7 @@ static int output_create_sink_input(struct output *o) {
data.module = o->userdata->module;
data.resample_method = o->userdata->resample_method;
- o->sink_input = pa_sink_input_new(o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
+ pa_sink_input_new(&o->sink_input, o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
pa_sink_input_new_data_done(&data);
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 8e0cf92b..7c56c240 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -79,10 +79,13 @@ struct userdata {
pa_bool_t restore_muted:1;
};
-struct entry {
+#define ENTRY_VERSION 1
+
+struct entry PA_GCC_PACKED {
+ uint8_t version;
+ pa_bool_t muted:1;
pa_channel_map channel_map;
pa_cvolume volume;
- pa_bool_t muted:1;
};
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
@@ -117,12 +120,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
goto fail;
if (data.dsize != sizeof(struct entry)) {
- pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
goto fail;
}
e = (struct entry*) data.dptr;
+ if (e->version != ENTRY_VERSION) {
+ pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
+ goto fail;
+ }
+
if (!(pa_cvolume_valid(&e->volume))) {
pa_log_warn("Invalid volume stored in database for device %s", name);
goto fail;
@@ -173,6 +181,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
memset(&entry, 0, sizeof(entry));
+ entry.version = ENTRY_VERSION;
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
pa_sink *sink;
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index e746f342..a4007b1a 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -732,7 +732,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
- u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
+ pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
pa_sink_input_new_data_done(&sink_input_data);
if (!u->sink_input)
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 03e27170..7dd44098 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -138,7 +138,9 @@ static int process_render(struct userdata *u) {
if (errno == EINTR)
continue;
- else if (errno != EAGAIN) {
+ else if (errno == EAGAIN)
+ return 0;
+ else {
pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
return -1;
}
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index 8e4f4c32..e17cbe80 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -80,16 +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->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;
+ if (!data->volume_is_set) {
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+ data->volume_is_set = TRUE;
+ data->volume_is_absolute = FALSE;
}
- pa_cvolume_set_balance(&data->virtual_volume, &data->channel_map, f*2.0-1.0);
+ pa_cvolume_set_balance(&data->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->virtual_volume));
+ pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume));
return PA_HOOK_OK;
}
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 74ee6122..1784b2cc 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -100,9 +100,6 @@ struct userdata {
pa_usec_t latency;
- pa_volume_t volume;
- pa_bool_t muted;
-
/*esd_format_t format;*/
int32_t rate;
@@ -133,6 +130,9 @@ enum {
SINK_MESSAGE_RIP_SOCKET
};
+/* Forward declaration */
+static void sink_set_volume_cb(pa_sink *);
+
static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
struct userdata *u = userdata;
pa_assert(u);
@@ -141,7 +141,7 @@ static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
u->fd = fd;
/* Set the initial volume */
- pa_raop_client_set_volume(u->raop, u->volume);
+ sink_set_volume_cb(u->sink);
pa_log_debug("Connection authenticated, handing fd to IO thread...");
@@ -255,43 +255,36 @@ 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 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->virtual_volume.values[i] = u->volume;
-}
-
static void sink_set_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
- int rv;
+ pa_cvolume hw;
+ pa_volume_t v;
+ char t[PA_CVOLUME_SNPRINT_MAX];
pa_assert(u);
- /* If we're muted, we fake it */
- if (u->muted)
+ /* If we're muted we don't need to do anything */
+ if (s->muted)
return;
- pa_assert(s->sample_spec.channels > 0);
+ /* Calculate the max volume of all channels.
+ We'll use this as our (single) volume on the APEX device and emulate
+ any variation in channel volumes in software */
+ v = pa_cvolume_max(&s->virtual_volume);
- /* Avoid pointless volume sets */
- if (u->volume == s->virtual_volume.values[0])
- return;
+ /* Create a pa_cvolume version of our single value */
+ pa_cvolume_set(&hw, s->sample_spec.channels, v);
- rv = pa_raop_client_set_volume(u->raop, s->virtual_volume.values[0]);
- if (0 == rv)
- u->volume = s->virtual_volume.values[0];
-}
+ /* Perform any software manipulation of the volume needed */
+ pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &hw);
-static void sink_get_mute_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
+ 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), &hw));
+ pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
- pa_assert(u);
-
- s->muted = u->muted;
+ /* Any necessary software volume manipulateion is done so set
+ our hw volume (or v as a single value) on the device */
+ pa_raop_client_set_volume(u->raop, v);
}
static void sink_set_mute_cb(pa_sink *s) {
@@ -299,8 +292,11 @@ static void sink_set_mute_cb(pa_sink *s) {
pa_assert(u);
- pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
- u->muted = s->muted;
+ if (s->muted) {
+ pa_raop_client_set_volume(u->raop, PA_VOLUME_MUTED);
+ } else {
+ sink_set_volume_cb(s);
+ }
}
static void thread_func(void *userdata) {
@@ -541,9 +537,6 @@ int pa__init(pa_module*m) {
u->next_encoding_overhead = 0;
u->encoding_ratio = 1.0;
- u->volume = roundf(0.7 * PA_VOLUME_NORM);
- u->muted = FALSE;
-
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->rtpoll_item = NULL;
@@ -583,9 +576,7 @@ int pa__init(pa_module*m) {
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- u->sink->get_volume = sink_get_volume_cb;
u->sink->set_volume = sink_set_volume_cb;
- u->sink->get_mute = sink_get_mute_cb;
u->sink->set_mute = sink_set_mute_cb;
u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL;
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index e17fef03..89ddf953 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -381,7 +381,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
- u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
+ pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
pa_sink_input_new_data_done(&sink_input_data);
if (!u->sink_input)
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index b0782c3c..ce08c01d 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -163,7 +163,7 @@ int pa__init(pa_module*m) {
pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
pa_sink_input_new_data_set_sample_spec(&data, &ss);
- u->sink_input = pa_sink_input_new(m->core, &data, 0);
+ pa_sink_input_new(&u->sink_input, m->core, &data, 0);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 0c9bd4f9..b1630fe1 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -90,16 +90,18 @@ struct userdata {
pa_idxset *subscribed;
};
-struct entry {
+#define ENTRY_VERSION 1
+
+struct entry PA_GCC_PACKED {
+ uint8_t version;
+ pa_bool_t muted_valid:1, relative_volume_valid:1, absolute_volume_valid:1, device_valid:1;
+ pa_bool_t muted:1;
pa_channel_map channel_map;
- 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;
+ char device[PA_NAME_MAX];
};
-
enum {
SUBCOMMAND_TEST,
SUBCOMMAND_READ,
@@ -161,19 +163,19 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
if (data.dsize != 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));
+ pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
goto fail;
}
e = (struct entry*) data.dptr;
- if (!memchr(e->device, 0, sizeof(e->device))) {
- pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name);
+ if (e->version != ENTRY_VERSION) {
+ pa_log_debug("Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring.", 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 (!memchr(e->device, 0, sizeof(e->device))) {
+ pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name);
goto fail;
}
@@ -182,6 +184,11 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
goto fail;
}
+ if ((e->relative_volume_valid || e->absolute_volume_valid) && !(pa_channel_map_valid(&e->channel_map))) {
+ pa_log_warn("Invalid channel map stored in database for stream %s", name);
+ goto fail;
+ }
+
if ((e->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);
@@ -233,7 +240,7 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
return FALSE;
if (a->muted_valid != b->muted_valid ||
- (a->muted && (a->muted != b->muted)))
+ (a->muted_valid && (a->muted != b->muted)))
return FALSE;
t = b->relative_volume;
@@ -265,6 +272,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
memset(&entry, 0, sizeof(entry));
+ entry.version = ENTRY_VERSION;
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
pa_sink_input *sink_input;
@@ -388,32 +396,33 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
if (u->restore_volume) {
- if (!new_data->virtual_volume_is_set) {
+ if (!new_data->volume_is_set) {
pa_cvolume v;
pa_cvolume_init(&v);
if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) {
+ /* We don't check for e->device_valid here because
+ that bit marks whether it is a good choice for
+ restoring, not just if the data is filled in. */
if (e->absolute_volume_valid &&
- e->device_valid &&
- pa_streq(new_data->sink->name, e->device)) {
+ (e->device[0] == 0 || pa_streq(new_data->sink->name, e->device))) {
v = e->absolute_volume;
- new_data->virtual_volume_is_absolute = TRUE;
+ new_data->volume_is_absolute = TRUE;
} else if (e->relative_volume_valid) {
-
v = e->relative_volume;
- new_data->virtual_volume_is_absolute = FALSE;
+ new_data->volume_is_absolute = FALSE;
}
} else if (e->relative_volume_valid) {
v = e->relative_volume;
- new_data->virtual_volume_is_absolute = FALSE;
+ new_data->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));
+ pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map));
new_data->save_volume = TRUE;
}
} else
@@ -523,11 +532,10 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
if (si->sink->flags & PA_SINK_FLAT_VOLUME) {
if (e->absolute_volume_valid &&
- e->device_valid &&
- pa_streq(e->device, si->sink->name))
+ (e->device[0] == 0 || 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_cvolume t = *pa_sink_get_volume(si->sink, FALSE);
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)
@@ -655,10 +663,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
if ((e = read_entry(u, name))) {
pa_cvolume r;
+ pa_channel_map cm;
pa_tagstruct_puts(reply, name);
- pa_tagstruct_put_channel_map(reply, &e->channel_map);
- pa_tagstruct_put_cvolume(reply, e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r));
+ pa_tagstruct_put_channel_map(reply, (e->relative_volume_valid || e->absolute_volume_valid) ? &e->channel_map : pa_channel_map_init(&cm));
+ pa_tagstruct_put_cvolume(reply, e->absolute_volume_valid ? &e->absolute_volume : (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);
@@ -697,21 +706,25 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
int k;
memset(&entry, 0, sizeof(entry));
+ entry.version = ENTRY_VERSION;
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
- pa_tagstruct_get_cvolume(t, &entry.relative_volume) < 0 ||
+ pa_tagstruct_get_cvolume(t, &entry.absolute_volume) < 0 ||
pa_tagstruct_gets(t, &device) < 0 ||
pa_tagstruct_get_boolean(t, &muted) < 0)
goto fail;
- 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)
+ if (!name || !*name)
goto fail;
+ entry.relative_volume = entry.absolute_volume;
+ entry.absolute_volume_valid = entry.relative_volume_valid = entry.relative_volume.channels > 0;
+
+ if (entry.relative_volume_valid)
+ if (!pa_cvolume_compatible_with_channel_map(&entry.relative_volume, &entry.channel_map))
+ goto fail;
+
entry.muted = muted;
entry.muted_valid = TRUE;
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 00d21255..baf3532f 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -453,7 +453,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
data.module = u->module;
pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec);
- s->sink_input = pa_sink_input_new(u->module->core, &data, 0);
+ pa_sink_input_new(&s->sink_input, u->module->core, &data, 0);
pa_sink_input_new_data_done(&data);
if (!s->sink_input) {
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index a6d682bb..762cdc1e 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -321,7 +321,7 @@ int pa__init(pa_module*m) {
pa_source_output_new_data_set_sample_spec(&data, &ss);
pa_source_output_new_data_set_channel_map(&data, &cm);
- o = pa_source_output_new(m->core, &data, PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND);
+ pa_source_output_new(&o, m->core, &data, PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND);
pa_source_output_new_data_done(&data);
if (!o) {
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index 455bda1b..983b8977 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -434,11 +434,11 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) {
const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
- pa_init_i18n();
-
if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
return NULL;
+ pa_init_i18n();
+
return _(pretty_table[pos]);
}
@@ -448,6 +448,9 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+ pa_return_val_if_fail(pa_channel_map_valid(b), 0);
+
if (a->channels != b->channels)
return 0;
@@ -502,19 +505,19 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
* "mono" here explicitly, because that can be understood as
* listing with one channel called "mono". */
- if (strcmp(s, "stereo") == 0) {
+ if (pa_streq(s, "stereo")) {
map.channels = 2;
map.map[0] = PA_CHANNEL_POSITION_LEFT;
map.map[1] = PA_CHANNEL_POSITION_RIGHT;
goto finish;
- } else if (strcmp(s, "surround-40") == 0) {
+ } else if (pa_streq(s, "surround-40")) {
map.channels = 4;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
goto finish;
- } else if (strcmp(s, "surround-41") == 0) {
+ } else if (pa_streq(s, "surround-41")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
@@ -522,7 +525,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_LFE;
goto finish;
- } else if (strcmp(s, "surround-50") == 0) {
+ } else if (pa_streq(s, "surround-50")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
@@ -530,7 +533,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
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) {
+ } else if (pa_streq(s, "surround-51")) {
map.channels = 6;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
@@ -539,7 +542,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
map.map[5] = PA_CHANNEL_POSITION_LFE;
goto finish;
- } else if (strcmp(s, "surround-71") == 0) {
+ } else if (pa_streq(s, "surround-71")) {
map.channels = 8;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
@@ -563,13 +566,13 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
}
/* Some special aliases */
- if (strcmp(p, "left") == 0)
+ if (pa_streq(p, "left"))
map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT;
- else if (strcmp(p, "right") == 0)
+ else if (pa_streq(p, "right"))
map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT;
- else if (strcmp(p, "center") == 0)
+ else if (pa_streq(p, "center"))
map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER;
- else if (strcmp(p, "subwoofer") == 0)
+ else if (pa_streq(p, "subwoofer"))
map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER;
else {
pa_channel_position_t i;
@@ -617,11 +620,8 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s
pa_assert(map);
pa_assert(ss);
- if (!pa_channel_map_valid(map))
- return 0;
-
- if (!pa_sample_spec_valid(ss))
- return 0;
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+ pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
return map->channels == ss->channels;
}
@@ -633,6 +633,9 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+ pa_return_val_if_fail(pa_channel_map_valid(b), 0);
+
memset(in_a, 0, sizeof(in_a));
for (i = 0; i < a->channels; i++)
@@ -647,30 +650,83 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
int pa_channel_map_can_balance(const pa_channel_map *map) {
unsigned c;
+ pa_bool_t left = FALSE, right = FALSE;
pa_assert(map);
- for (c = 0; c < map->channels; c++)
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
+ 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_TOP_FRONT_LEFT:
+ case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
+ left = TRUE;
+ break;
+
+ case PA_CHANNEL_POSITION_RIGHT:
+ case PA_CHANNEL_POSITION_REAR_RIGHT:
+ case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
case PA_CHANNEL_POSITION_SIDE_RIGHT:
+ case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
+ case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
+ right = TRUE;
+ break;
+
+ default:
+ ;
+ }
+
+ if (left && right)
+ return 1;
+ }
+
+ return 0;
+}
+
+int pa_channel_map_can_fade(const pa_channel_map *map) {
+ unsigned c;
+ pa_bool_t front = FALSE, rear = FALSE;
+
+ pa_assert(map);
+
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
+ for (c = 0; c < map->channels; c++) {
+
+ switch (map->map[c]) {
+ case PA_CHANNEL_POSITION_FRONT_LEFT:
+ case PA_CHANNEL_POSITION_FRONT_RIGHT:
+ case PA_CHANNEL_POSITION_FRONT_CENTER:
+ case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+ case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
+ case PA_CHANNEL_POSITION_TOP_FRONT_CENTER:
+ front = TRUE;
+ break;
+
+ case PA_CHANNEL_POSITION_REAR_LEFT:
+ case PA_CHANNEL_POSITION_REAR_RIGHT:
+ case PA_CHANNEL_POSITION_REAR_CENTER:
case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
- return 1;
+ case PA_CHANNEL_POSITION_TOP_REAR_CENTER:
+ rear = TRUE;
+ break;
default:
;
}
+ if (front && rear)
+ return 1;
+ }
+
return 0;
}
@@ -680,6 +736,8 @@ const char* pa_channel_map_to_name(const pa_channel_map *map) {
pa_assert(map);
+ pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+
memset(in_map, 0, sizeof(in_map));
for (c = 0; c < map->channels; c++)
@@ -732,11 +790,15 @@ const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) {
pa_assert(map);
+ pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+
memset(in_map, 0, sizeof(in_map));
for (c = 0; c < map->channels; c++)
pa_bitset_set(in_map, map->map[c], TRUE);
+ pa_init_i18n();
+
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_MONO, -1))
return _("Mono");
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index de2d712a..f9124b2e 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -239,6 +239,11 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) PA
* available. \since 0.9.15 */
int pa_channel_map_can_balance(const pa_channel_map *map) PA_GCC_PURE;
+/** Returns non-zero if it makes sense to apply a volume 'fade'
+ * (i.e. 'balance' between front and rear) with this mapping, i.e. if
+ * there are front/rear channels available. \since 0.9.15 */
+int pa_channel_map_can_fade(const pa_channel_map *map) PA_GCC_PURE;
+
/** 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
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 7517a7aa..61498883 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -237,12 +237,17 @@ typedef enum pa_stream_flags {
* checked whether the device this stream is connected to should
* auto-suspend. \since 0.9.15 */
- PA_STREAM_START_UNMUTED = 0x10000U
+ PA_STREAM_START_UNMUTED = 0x10000U,
/**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED
* nor PA_STREAM_START_MUTED it is left to the server to decide
* whether to create the stream in muted or in unmuted
* state. \since 0.9.15 */
+ PA_STREAM_FAIL_ON_SUSPEND = 0x20000U
+ /**< If the sink/source this stream is connected to is suspended
+ * during the creation of this stream, cause it to fail. If the
+ * sink/source is being suspended during creation of this stream,
+ * make sure this stream is terminated. \since 0.9.15 */
} pa_stream_flags_t;
/** \cond fulldocs */
@@ -268,6 +273,7 @@ typedef enum pa_stream_flags {
#define PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
#define PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
#define PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
+#define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
/** \endcond */
@@ -358,9 +364,38 @@ enum {
PA_ERR_UNKNOWN, /**< The error code was unknown to the client */
PA_ERR_NOEXTENSION, /**< Extension does not exist. \since 0.9.12 */
PA_ERR_OBSOLETE, /**< Obsolete functionality. \since 0.9.15 */
+ PA_ERR_NOTIMPLEMENTED, /**< Missing implementation. \since 0.9.15 */
PA_ERR_MAX /**< Not really an error but the first invalid error code */
};
+/** \cond fulldocs */
+#define PA_OK PA_OK
+#define PA_ERR_ACCESS PA_ERR_ACCESS
+#define PA_ERR_COMMAND PA_ERR_COMMAND
+#define PA_ERR_INVALID PA_ERR_INVALID
+#define PA_ERR_EXIST PA_ERR_EXIST
+#define PA_ERR_NOENTITY PA_ERR_NOENTITY
+#define PA_ERR_CONNECTIONREFUSED PA_ERR_CONNECTIONREFUSED
+#define PA_ERR_PROTOCOL PA_ERR_PROTOCOL
+#define PA_ERR_TIMEOUT PA_ERR_TIMEOUT
+#define PA_ERR_AUTHKEY PA_ERR_AUTHKEY
+#define PA_ERR_INTERNAL PA_ERR_INTERNAL
+#define PA_ERR_CONNECTIONTERMINATED PA_ERR_CONNECTIONTERMINATED
+#define PA_ERR_KILLED PA_ERR_KILLED
+#define PA_ERR_INVALIDSERVER PA_ERR_INVALIDSERVER
+#define PA_ERR_MODINITFAILED PA_ERR_MODINITFAILED
+#define PA_ERR_BADSTATE PA_ERR_BADSTATE
+#define PA_ERR_NODATA PA_ERR_NODATA
+#define PA_ERR_VERSION PA_ERR_VERSION
+#define PA_ERR_TOOLARGE PA_ERR_TOOLARGE
+#define PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
+#define PA_ERR_UNKNOWN PA_ERR_UNKNOWN
+#define PA_ERR_NOEXTENSION PA_ERR_NOEXTENSION
+#define PA_ERR_OBSOLETE PA_ERR_OBSOLETE
+#define PA_ERR_NOTIMPLEMENTED PA_ERR_NOTIMPLEMENTED
+#define PA_ERR_MAX PA_ERR_MAX
+/** \endcond */
+
/** Subscription event mask, as used by pa_context_subscribe() */
typedef enum pa_subscription_mask {
PA_SUBSCRIPTION_MASK_NULL = 0x0000U,
diff --git a/src/pulse/error.c b/src/pulse/error.c
index d9d0a8c6..d37084bb 100644
--- a/src/pulse/error.c
+++ b/src/pulse/error.c
@@ -61,7 +61,9 @@ const char*pa_strerror(int error) {
[PA_ERR_TOOLARGE] = N_("Too large"),
[PA_ERR_NOTSUPPORTED] = N_("Not supported"),
[PA_ERR_UNKNOWN] = N_("Unknown error code"),
- [PA_ERR_NOEXTENSION] = N_("No such extension")
+ [PA_ERR_NOEXTENSION] = N_("No such extension"),
+ [PA_ERR_OBSOLETE] = N_("Obsolete functionality"),
+ [PA_ERR_NOTIMPLEMENTED] = N_("Missing implementation")
};
pa_init_i18n();
diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c
index 703179c5..469c822a 100644
--- a/src/pulse/ext-stream-restore.c
+++ b/src/pulse/ext-stream-restore.c
@@ -30,6 +30,7 @@
#include <pulsecore/pstream-util.h>
#include "internal.h"
+#include "operation.h"
#include "ext-stream-restore.h"
@@ -191,8 +192,8 @@ pa_operation *pa_ext_stream_restore_write(
void *userdata) {
uint32_t tag;
- pa_operation *o;
- pa_tagstruct *t;
+ pa_operation *o = NULL;
+ pa_tagstruct *t = NULL;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -213,7 +214,15 @@ pa_operation *pa_ext_stream_restore_write(
pa_tagstruct_put_boolean(t, apply_immediately);
for (; n > 0; n--, data++) {
+ if (!data->name || !*data->name)
+ goto fail;
+
pa_tagstruct_puts(t, data->name);
+
+ if (data->volume.channels > 0 &&
+ !pa_cvolume_compatible_with_channel_map(&data->volume, &data->channel_map))
+ goto fail;
+
pa_tagstruct_put_channel_map(t, &data->channel_map);
pa_tagstruct_put_cvolume(t, &data->volume);
pa_tagstruct_puts(t, data->device);
@@ -224,6 +233,18 @@ pa_operation *pa_ext_stream_restore_write(
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
+
+fail:
+ if (o) {
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
+ }
+
+ if (t)
+ pa_tagstruct_free(t);
+
+ pa_context_set_error(c, PA_ERR_INVALID);
+ return NULL;
}
pa_operation *pa_ext_stream_restore_delete(
@@ -233,8 +254,8 @@ pa_operation *pa_ext_stream_restore_delete(
void *userdata) {
uint32_t tag;
- pa_operation *o;
- pa_tagstruct *t;
+ pa_operation *o = NULL;
+ pa_tagstruct *t = NULL;
const char *const *k;
pa_assert(c);
@@ -251,13 +272,29 @@ pa_operation *pa_ext_stream_restore_delete(
pa_tagstruct_puts(t, "module-stream-restore");
pa_tagstruct_putu32(t, SUBCOMMAND_DELETE);
- for (k = s; *k; k++)
+ for (k = s; *k; k++) {
+ if (!*k || !**k)
+ goto fail;
+
pa_tagstruct_puts(t, *k);
+ }
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
+
+fail:
+ if (o) {
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
+ }
+
+ if (t)
+ pa_tagstruct_free(t);
+
+ pa_context_set_error(c, PA_ERR_INVALID);
+ return NULL;
}
pa_operation *pa_ext_stream_restore_subscribe(
@@ -322,5 +359,4 @@ void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t)
if (c->ext_stream_restore.callback)
c->ext_stream_restore.callback(c, c->ext_stream_restore.userdata);
-
}
diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h
index ac01d235..cf9f4ccc 100644
--- a/src/pulse/ext-stream-restore.h
+++ b/src/pulse/ext-stream-restore.h
@@ -36,10 +36,10 @@ PA_C_DECL_BEGIN
* maintained by module-stream-restore. \since 0.9.12 */
typedef struct pa_ext_stream_restore_info {
const char *name; /**< Identifier string of the stream. A string like "sink-input-by-role:" or similar followed by some arbitrary property value. */
- pa_channel_map channel_map; /**< The channel map for the volume field */
- pa_cvolume volume; /**< The volume of the stream when it was seen last, if applicable */
- const char *device; /**< The sink/source of the stream when it was last seen */
- int mute; /**< The boolean mute state of the stream when it was last seen, if applicable */
+ pa_channel_map channel_map; /**< The channel map for the volume field, if applicable */
+ pa_cvolume volume; /**< The volume of the stream when it was seen last, if applicable and saved */
+ const char *device; /**< The sink/source of the stream when it was last seen, if applicable and saved */
+ int mute; /**< The boolean mute state of the stream when it was last seen, if applicable and saved */
} pa_ext_stream_restore_info;
/** Callback prototype for pa_ext_stream_restore_test(). \since 0.9.12 */
diff --git a/src/pulse/operation.c b/src/pulse/operation.c
index 13b470a8..aa2bbc05 100644
--- a/src/pulse/operation.c
+++ b/src/pulse/operation.c
@@ -62,6 +62,7 @@ pa_operation *pa_operation_ref(pa_operation *o) {
PA_REFCNT_INC(o);
return o;
}
+
void pa_operation_unref(pa_operation *o) {
pa_assert(o);
pa_assert(PA_REFCNT_VALUE(o) >= 1);
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index 282fe5cc..db4c9344 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -24,6 +24,7 @@
#endif
#include <string.h>
+#include <ctype.h>
#include <pulse/xmalloc.h>
#include <pulse/utf8.h>
@@ -46,7 +47,7 @@ struct property {
static pa_bool_t property_name_valid(const char *key) {
- if (!pa_utf8_valid(key))
+ if (!pa_ascii_valid(key))
return FALSE;
if (strlen(key) <= 0)
@@ -64,8 +65,6 @@ static void property_free(struct property *prop) {
}
pa_proplist* pa_proplist_new(void) {
- pa_init_i18n();
-
return MAKE_PROPLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func));
}
@@ -83,6 +82,7 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
pa_assert(p);
pa_assert(key);
+ pa_assert(value);
if (!property_name_valid(key) || !pa_utf8_valid(value))
return -1;
@@ -104,25 +104,130 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
}
/** Will accept only valid UTF-8 */
+static int proplist_setn(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
+ char *k, *v;
+
+ pa_assert(p);
+ pa_assert(key);
+ pa_assert(value);
+
+ k = pa_xstrndup(key, key_length);
+ v = pa_xstrndup(value, value_length);
+
+ if (!property_name_valid(k) || !pa_utf8_valid(v)) {
+ pa_xfree(k);
+ pa_xfree(v);
+ return -1;
+ }
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = k;
+ add = TRUE;
+ } else {
+ pa_xfree(prop->value);
+ pa_xfree(k);
+ }
+
+ prop->value = v;
+ prop->nbytes = strlen(v)+1;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+}
+
+static int proplist_sethex(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
+ char *k, *v;
+ uint8_t *d;
+ size_t dn;
+
+ pa_assert(p);
+ pa_assert(key);
+ pa_assert(value);
+
+ k = pa_xstrndup(key, key_length);
+
+ if (!property_name_valid(k)) {
+ pa_xfree(k);
+ return -1;
+ }
+
+ v = pa_xstrndup(value, value_length);
+ d = pa_xmalloc(value_length*2+1);
+
+ if ((dn = pa_parsehex(v, d, value_length*2)) == (size_t) -1) {
+ pa_xfree(k);
+ pa_xfree(v);
+ pa_xfree(d);
+ return -1;
+ }
+
+ pa_xfree(v);
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = k;
+ add = TRUE;
+ } else {
+ pa_xfree(prop->value);
+ pa_xfree(k);
+ }
+
+ d[dn] = 0;
+ prop->value = d;
+ prop->nbytes = dn;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+}
+
+/** Will accept only valid UTF-8 */
int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
va_list ap;
- int r;
- char *t;
+ char *v;
pa_assert(p);
pa_assert(key);
+ pa_assert(format);
if (!property_name_valid(key) || !pa_utf8_valid(format))
return -1;
va_start(ap, format);
- t = pa_vsprintf_malloc(format, ap);
+ v = pa_vsprintf_malloc(format, ap);
va_end(ap);
- r = pa_proplist_sets(p, key, t);
+ if (!pa_utf8_valid(v))
+ goto fail;
- pa_xfree(t);
- return r;
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = pa_xstrdup(key);
+ add = TRUE;
+ } else
+ pa_xfree(prop->value);
+
+ prop->value = v;
+ prop->nbytes = strlen(v)+1;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+
+fail:
+ pa_xfree(v);
+ return -1;
}
int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
@@ -131,6 +236,7 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb
pa_assert(p);
pa_assert(key);
+ pa_assert(data);
if (!property_name_valid(key))
return -1;
@@ -142,7 +248,9 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb
} else
pa_xfree(prop->value);
- prop->value = pa_xmemdup(data, nbytes);
+ prop->value = pa_xmalloc(nbytes+1);
+ memcpy(prop->value, data, nbytes);
+ ((char*) prop->value)[nbytes] = 0;
prop->nbytes = nbytes;
if (add)
@@ -183,6 +291,8 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *
pa_assert(p);
pa_assert(key);
+ pa_assert(data);
+ pa_assert(nbytes);
if (!property_name_valid(key))
return -1;
@@ -275,9 +385,32 @@ char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) {
if (!pa_strbuf_isempty(buf))
pa_strbuf_puts(buf, sep);
- if ((v = pa_proplist_gets(p, key)))
- pa_strbuf_printf(buf, "%s = \"%s\"", key, v);
- else {
+ if ((v = pa_proplist_gets(p, key))) {
+ const char *t;
+
+ pa_strbuf_printf(buf, "%s = \"", key);
+
+ for (t = v;;) {
+ size_t h;
+
+ h = strcspn(t, "\"");
+
+ if (h > 0)
+ pa_strbuf_putsn(buf, t, h);
+
+ t += h;
+
+ if (*t == 0)
+ break;
+
+ pa_assert(*t == '"');
+ pa_strbuf_puts(buf, "\\\"");
+
+ t++;
+ }
+
+ pa_strbuf_puts(buf, "\"");
+ } else {
const void *value;
size_t nbytes;
char *c;
@@ -304,88 +437,189 @@ char *pa_proplist_to_string(pa_proplist *p) {
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;
+pa_proplist *pa_proplist_from_string(const char *s) {
+ enum {
+ WHITESPACE,
+ KEY,
+ AFTER_KEY,
+ VALUE_START,
+ VALUE_SIMPLE,
+ VALUE_DOUBLE_QUOTES,
+ VALUE_DOUBLE_QUOTES_ESCAPE,
+ VALUE_TICKS,
+ VALUE_TICKS_ESCAPED,
+ VALUE_HEX
+ } state;
+
+ pa_proplist *pl;
+ const char *p, *key = NULL, *value = NULL;
+ size_t key_len = 0, value_len = 0;
+
+ pa_assert(s);
+
+ pl = pa_proplist_new();
+
+ state = WHITESPACE;
+
+ for (p = s;; p++) {
+ switch (state) {
+
+ case WHITESPACE:
+ if (*p == 0)
+ goto success;
+ else if (*p == '=')
+ goto fail;
+ else if (!isspace(*p)) {
+ key = p;
+ state = KEY;
+ key_len = 1;
+ }
+ break;
- if (l)
- *(l+1) = 0;
+ case KEY:
+ if (*p == 0)
+ goto fail;
+ else if (*p == '=')
+ state = VALUE_START;
+ else if (isspace(*p))
+ state = AFTER_KEY;
+ else
+ key_len++;
+ break;
- return b;
-}
+ case AFTER_KEY:
+ if (*p == 0)
+ goto fail;
+ else if (*p == '=')
+ state = VALUE_START;
+ else if (!isspace(*p))
+ goto fail;
+ break;
-pa_proplist *pa_proplist_from_string(const char *str) {
- pa_proplist *p;
- char *s, *v, *k, *e;
+ case VALUE_START:
+ if (*p == 0)
+ goto fail;
+ else if (strncmp(p, "hex:", 4) == 0) {
+ state = VALUE_HEX;
+ value = p+4;
+ value_len = 0;
+ p += 3;
+ } else if (*p == '\'') {
+ state = VALUE_TICKS;
+ value = p+1;
+ value_len = 0;
+ } else if (*p == '"') {
+ state = VALUE_DOUBLE_QUOTES;
+ value = p+1;
+ value_len = 0;
+ } else if (!isspace(*p)) {
+ state = VALUE_SIMPLE;
+ value = p;
+ value_len = 1;
+ }
+ break;
- pa_assert(str);
- pa_assert_se(p = pa_proplist_new());
- pa_assert_se(s = strdup(str));
+ case VALUE_SIMPLE:
+ if (*p == 0 || isspace(*p)) {
+ if (proplist_setn(pl, key, key_len, value, value_len) < 0)
+ goto fail;
- for (k = s; *k; k = e) {
- k = k+strspn(k, WHITESPACE);
+ if (*p == 0)
+ goto success;
- if (!*k)
- break;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
- if (!(v = strchr(k, '='))) {
- pa_log("Missing '='.");
- break;
- }
+ case VALUE_DOUBLE_QUOTES:
+ if (*p == 0)
+ goto fail;
+ else if (*p == '"') {
+ char *v;
+
+ v = pa_unescape(pa_xstrndup(value, value_len));
+
+ if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) {
+ pa_xfree(v);
+ goto fail;
+ }
+
+ pa_xfree(v);
+ state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_DOUBLE_QUOTES_ESCAPE;
+ value_len++;
+ } else
+ value_len++;
+ break;
- *v++ = '\0';
- k = strip(k);
+ case VALUE_DOUBLE_QUOTES_ESCAPE:
+ if (*p == 0)
+ goto fail;
+ else {
+ state = VALUE_DOUBLE_QUOTES;
+ value_len++;
+ }
+ break;
- v = v+strspn(v, WHITESPACE);
- if (*v == '"') {
- v++;
- if (!(e = strchr(v, '"'))) { /* FIXME: handle escape */
- pa_log("Missing '\"' at end of string value.");
+ case VALUE_TICKS:
+ if (*p == 0)
+ goto fail;
+ else if (*p == '\'') {
+ char *v;
+
+ v = pa_unescape(pa_xstrndup(value, value_len));
+
+ if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) {
+ pa_xfree(v);
+ goto fail;
+ }
+
+ pa_xfree(v);
+ state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_TICKS_ESCAPED;
+ value_len++;
+ } else
+ value_len++;
break;
- }
- *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:\"");
+ case VALUE_TICKS_ESCAPED:
+ if (*p == 0)
+ goto fail;
+ else {
+ state = VALUE_TICKS;
+ value_len++;
+ }
break;
- }
- e = v;
- while (in_string(*e, "0123456789abcdefABCDEF"))
- ++e;
+ case VALUE_HEX:
+ if ((*p >= '0' && *p <= '9') ||
+ (*p >= 'A' && *p <= 'F') ||
+ (*p >= 'a' && *p <= 'f')) {
+ value_len++;
+ } else if (*p == 0 || isspace(*p)) {
- if ((e - v) % 2) {
- pa_log("Invalid \"hex:\" value data");
- break;
- }
+ if (proplist_sethex(pl, key, key_len, value, value_len) < 0)
+ goto fail;
- 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;
- }
+ if (*p == 0)
+ goto success;
- pa_proplist_set(p, k, blob, (e-v)/2);
- pa_xfree(blob);
+ state = WHITESPACE;
+ } else
+ goto fail;
+ break;
}
}
- pa_xfree(s);
+success:
+ return MAKE_PROPLIST(pl);
- return p;
+fail:
+ pa_proplist_free(pl);
+ return NULL;
}
int pa_proplist_contains(pa_proplist *p, const char *key) {
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 203a28c5..d6114062 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -65,6 +65,7 @@ PA_C_DECL_BEGIN
* application.process.binary
* application.process.user
* application.process.host
+ * application.process.machine_id D-Bus machine ID
* device.string
* device.api oss, alsa, sunaudio
* device.description
@@ -72,7 +73,7 @@ PA_C_DECL_BEGIN
* device.serial
* device.vendor_product_id
* device.class sound, modem, monitor, filter, abstract
- * device.form_factor laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset
+ * device.form_factor laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset, headphones, hands-free, car, hifi, computer, portable
* device.connector isa, pci, usb, firewire, bluetooth
* device.access_mode mmap, mmap_rewrite, serial
* device.master_device
@@ -114,6 +115,7 @@ PA_C_DECL_BEGIN
#define PA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary"
#define PA_PROP_APPLICATION_PROCESS_USER "application.process.user"
#define PA_PROP_APPLICATION_PROCESS_HOST "application.process.host"
+#define PA_PROP_APPLICATION_PROCESS_MACHINE_ID "application.process.machine_id"
#define PA_PROP_DEVICE_STRING "device.string"
#define PA_PROP_DEVICE_API "device.api"
#define PA_PROP_DEVICE_DESCRIPTION "device.description"
@@ -134,7 +136,7 @@ PA_C_DECL_BEGIN
#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
+/** A property list object. Basically a dictionary with ASCII strings
* as keys and arbitrary data as values. \since 0.9.11 */
typedef struct pa_proplist pa_proplist;
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index a6c77345..4b13a337 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -36,7 +36,7 @@
#include "sample.h"
-size_t pa_sample_size(const pa_sample_spec *spec) {
+size_t pa_sample_size_of_format(pa_sample_format_t f) {
static const size_t table[] = {
[PA_SAMPLE_U8] = 1,
@@ -54,32 +54,44 @@ size_t pa_sample_size(const pa_sample_spec *spec) {
[PA_SAMPLE_S24_32BE] = 4
};
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return table[f];
+}
+
+size_t pa_sample_size(const pa_sample_spec *spec) {
+
pa_assert(spec);
- pa_assert(spec->format >= 0);
- pa_assert(spec->format < PA_SAMPLE_MAX);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
- return table[spec->format];
+ return pa_sample_size_of_format(spec->format);
}
size_t pa_frame_size(const pa_sample_spec *spec) {
pa_assert(spec);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return pa_sample_size(spec) * spec->channels;
}
size_t pa_bytes_per_second(const pa_sample_spec *spec) {
pa_assert(spec);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
+
return spec->rate*pa_frame_size(spec);
}
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
pa_assert(spec);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate);
}
size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
pa_assert(spec);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec);
}
@@ -112,6 +124,9 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_sample_spec_valid(a), 0);
+ pa_return_val_if_fail(pa_sample_spec_valid(b), 0);
+
return
(a->format == b->format) &&
(a->rate == b->rate) &&
@@ -143,7 +158,7 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) {
char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
pa_assert(s);
- pa_assert(l);
+ pa_assert(l > 0);
pa_assert(spec);
pa_init_i18n();
@@ -151,22 +166,25 @@ char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
if (!pa_sample_spec_valid(spec))
pa_snprintf(s, l, _("(invalid)"));
else
- pa_snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
+ pa_snprintf(s, l, _("%s %uch %uHz"), pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
return s;
}
char* pa_bytes_snprint(char *s, size_t l, unsigned v) {
pa_assert(s);
+ pa_assert(l > 0);
+
+ pa_init_i18n();
if (v >= ((unsigned) 1024)*1024*1024)
- pa_snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024);
+ pa_snprintf(s, l, _("%0.1f GiB"), ((double) v)/1024/1024/1024);
else if (v >= ((unsigned) 1024)*1024)
- pa_snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024);
+ pa_snprintf(s, l, _("%0.1f MiB"), ((double) v)/1024/1024);
else if (v >= (unsigned) 1024)
- pa_snprintf(s, l, "%0.1f KiB", ((double) v)/1024);
+ pa_snprintf(s, l, _("%0.1f KiB"), ((double) v)/1024);
else
- pa_snprintf(s, l, "%u B", (unsigned) v);
+ pa_snprintf(s, l, _("%u B"), (unsigned) v);
return s;
}
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 45a481fe..3c05b54a 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -261,6 +261,10 @@ size_t pa_frame_size(const pa_sample_spec *spec) PA_GCC_PURE;
/** Return the size of a sample with the specific sample type */
size_t pa_sample_size(const pa_sample_spec *spec) PA_GCC_PURE;
+/** Similar to pa_sample_size() but take a sample format instead of a
+ * full sample spec. \since 0.9.15 */
+size_t pa_sample_size_of_format(pa_sample_format_t f) PA_GCC_PURE;
+
/** Calculate the time the specified bytes take to play with the
* specified sample type. The return value will always be rounded
* down for non-integral return values. */
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 5a29bd63..fe2514d9 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -877,7 +877,7 @@ static int create_stream(
PA_CHECK_VALIDITY(s->context, s->direct_on_input == PA_INVALID_INDEX || direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|
PA_STREAM_INTERPOLATE_TIMING|
- PA_STREAM_NOT_MONOTONOUS|
+ PA_STREAM_NOT_MONOTONIC|
PA_STREAM_AUTO_TIMING_UPDATE|
PA_STREAM_NO_REMAP_CHANNELS|
PA_STREAM_NO_REMIX_CHANNELS|
@@ -891,7 +891,8 @@ static int create_stream(
PA_STREAM_ADJUST_LATENCY|
PA_STREAM_EARLY_REQUESTS|
PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND|
- PA_STREAM_START_UNMUTED)), PA_ERR_INVALID);
+ PA_STREAM_START_UNMUTED|
+ PA_STREAM_FAIL_ON_SUSPEND)), PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
@@ -1017,6 +1018,7 @@ static int create_stream(
pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
pa_tagstruct_put_boolean(t, flags & PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND);
+ pa_tagstruct_put_boolean(t, flags & PA_STREAM_FAIL_ON_SUSPEND);
}
pa_pstream_send_tagstruct(s->context->pstream, t);
@@ -1902,7 +1904,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
usec = calc_time(s, FALSE);
/* Make sure the time runs monotonically */
- if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
+ if (!(s->flags & PA_STREAM_NOT_MONOTONIC)) {
if (usec < s->previous_time)
usec = s->previous_time;
else
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 6cb363c8..1bec1ebf 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -532,7 +532,7 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe
* value returned by this function is guaranteed to increase
* monotonically. (that means: the returned value is always greater or
* equal to the value returned on the last call) This behaviour can
- * be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be
+ * be disabled by using PA_STREAM_NOT_MONOTONIC. This may be
* desirable to deal better with bad estimations of transport
* latencies, but may have strange effects if the application is not
* able to deal with time going 'backwards'. */
diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c
index 7671be46..6b58bde3 100644
--- a/src/pulse/utf8.c
+++ b/src/pulse/utf8.c
@@ -263,3 +263,29 @@ char* pa_locale_to_utf8 (const char *str) {
}
#endif
+
+char *pa_ascii_valid(const char *str) {
+ const char *p;
+ pa_assert(str);
+
+ for (p = str; *p; p++)
+ if ((unsigned char) *p >= 128)
+ return NULL;
+
+ return (char*) str;
+}
+
+char *pa_ascii_filter(const char *str) {
+ char *r, *s, *d;
+ pa_assert(str);
+
+ r = pa_xstrdup(str);
+
+ for (s = r, d = r; *s; s++)
+ if ((unsigned char) *s < 128)
+ *(d++) = *s;
+
+ *d = 0;
+
+ return r;
+}
diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h
index 4d751953..b9f74952 100644
--- a/src/pulse/utf8.h
+++ b/src/pulse/utf8.h
@@ -36,9 +36,15 @@ PA_C_DECL_BEGIN
/** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */
char *pa_utf8_valid(const char *str) PA_GCC_PURE;
+/** Test if the specified strings qualifies as valid 7-bit ASCII. Return the string if so, otherwise NULL. \since 0.9.15 */
+char *pa_ascii_valid(const char *str) PA_GCC_PURE;
+
/** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */
char *pa_utf8_filter(const char *str);
+/** Filter all invalid ASCII characters from the specified string, returning a new fully ASCII valid string. Don't forget to free the returned string with pa_xfree(). \since 0.9.15 */
+char *pa_ascii_filter(const char *str);
+
/** Convert a UTF-8 string to the current locale. Free the string using pa_xfree(). */
char* pa_utf8_to_locale (const char *str);
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index a6abce2a..54838e89 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -37,6 +37,9 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_cvolume_valid(a), 0);
+ pa_return_val_if_fail(pa_cvolume_valid(b), 0);
+
if (a->channels != b->channels)
return 0;
@@ -78,7 +81,9 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
uint64_t sum = 0;
int i;
+
pa_assert(a);
+ pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (i = 0; i < a->channels; i++)
sum += a->values[i];
@@ -91,7 +96,9 @@ pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
pa_volume_t pa_cvolume_max(const pa_cvolume *a) {
pa_volume_t m = 0;
int i;
+
pa_assert(a);
+ pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (i = 0; i < a->channels; i++)
if (a->values[i] > m)
@@ -247,11 +254,12 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) {
return s;
}
-/** Return non-zero if the volume of all channels is equal to the specified value */
int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
unsigned c;
pa_assert(a);
+ pa_return_val_if_fail(pa_cvolume_valid(a), 0);
+
for (c = 0; c < a->channels; c++)
if (a->values[c] != v)
return 0;
@@ -266,6 +274,9 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+ pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
+
for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]);
@@ -281,6 +292,9 @@ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+ pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
+
for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]);
@@ -337,11 +351,13 @@ static pa_bool_t on_center(pa_channel_position_t p) {
}
static pa_bool_t on_lfe(pa_channel_position_t p) {
+
return
p == PA_CHANNEL_POSITION_LFE;
}
static pa_bool_t on_front(pa_channel_position_t p) {
+
return
p == PA_CHANNEL_POSITION_FRONT_LEFT ||
p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
@@ -354,6 +370,7 @@ static pa_bool_t on_front(pa_channel_position_t p) {
}
static pa_bool_t on_rear(pa_channel_position_t p) {
+
return
p == PA_CHANNEL_POSITION_REAR_LEFT ||
p == PA_CHANNEL_POSITION_REAR_RIGHT ||
@@ -370,7 +387,11 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa
pa_assert(v);
pa_assert(from);
pa_assert(to);
- pa_assert(v->channels == from->channels);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(pa_channel_map_valid(from), NULL);
+ pa_return_val_if_fail(pa_channel_map_valid(to), NULL);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL);
if (pa_channel_map_equal(from, to))
return v;
@@ -416,15 +437,22 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) {
pa_assert(v);
pa_assert(ss);
- if (!pa_cvolume_valid(v))
- return 0;
-
- if (!pa_sample_spec_valid(ss))
- return 0;
+ pa_return_val_if_fail(pa_cvolume_valid(v), 0);
+ pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
return v->channels == ss->channels;
}
+int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) {
+ pa_assert(v);
+ pa_assert(cm);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), 0);
+ pa_return_val_if_fail(pa_channel_map_valid(cm), 0);
+
+ return v->channels == cm->channels;
+}
+
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;
@@ -462,7 +490,13 @@ float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) {
pa_assert(v);
pa_assert(map);
- pa_assert(map->channels == v->channels);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f);
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
+
+ if (!pa_channel_map_can_balance(map))
+ return 0.0f;
get_avg_lr(map, v, &left, &right);
@@ -488,12 +522,18 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo
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);
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
+
+ if (!pa_channel_map_can_balance(map))
+ return v;
+
get_avg_lr(map, v, &left, &right);
m = PA_MAX(left, right);
@@ -529,6 +569,9 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
pa_assert(v);
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(max != (pa_volume_t) -1, NULL);
+
for (c = 0; c < v->channels; c++)
if (v->values[c] > t)
t = v->values[c];
@@ -579,7 +622,13 @@ float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) {
pa_assert(v);
pa_assert(map);
- pa_assert(map->channels == v->channels);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f);
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
+
+ if (!pa_channel_map_can_fade(map))
+ return 0.0f;
get_avg_fr(map, v, &front, &rear);
@@ -596,12 +645,18 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float
pa_volume_t front, nfront, rear, nrear, m;
unsigned c;
- pa_assert(map->channels == v->channels);
pa_assert(map);
pa_assert(v);
pa_assert(new_fade >= -1.0f);
pa_assert(new_fade <= 1.0f);
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
+
+ if (!pa_channel_map_can_fade(map))
+ return v;
+
get_avg_fr(map, v, &front, &rear);
m = PA_MAX(front, rear);
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 8eef467f..8bfd0687 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -230,13 +230,19 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
/** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */
pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to);
-/** Return non-zero if the specified volume is compatible with
- * the specified sample spec. \since 0.9.13 */
+/** Return non-zero if the specified volume is compatible with the
+ * specified sample spec. \since 0.9.13 */
int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE;
+/** Return non-zero if the specified volume is compatible with the
+ * specified sample spec. \since 0.9.15 */
+int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) PA_GCC_PURE;
+
/** Calculate a 'balance' value for the specified volume with the
* specified channel map. The return value will range from -1.0f
- * (left) to +1.0f (right) \since 0.9.15 */
+ * (left) to +1.0f (right). If no balance value is applicable to this
+ * channel map the return value will always be 0.0f. See
+ * pa_channel_map_can_balance(). \since 0.9.15 */
float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE;
/** Adjust the 'balance' value for the specified volume with the
@@ -245,22 +251,27 @@ float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) PA_
* operation might not be reversible! Also, after this call
* pa_cvolume_get_balance() is not guaranteed to actually return the
* requested balance value (e.g. when the input volume was zero anyway for
- * all channels) \since 0.9.15 */
+ * all channels). If no balance value is applicable to
+ * this channel map the volume will not be modified. See
+ * pa_channel_map_can_balance(). \since 0.9.15 */
pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance);
/** Calculate a 'fade' value (i.e. 'balance' between front and rear)
* for the specified volume with the specified channel map. The return
- * value will range from -1.0f (rear) to +1.0f (left) \since
- * 0.9.15 */
+ * value will range from -1.0f (rear) to +1.0f (left). If no fade
+ * value is applicable to this channel map the return value will
+ * always be 0.0f. See pa_channel_map_can_fade(). \since 0.9.15 */
float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE;
/** Adjust the 'fade' value (i.e. 'balance' between front and rear)
* for the specified volume with the specified channel map. v will be
* modified in place and returned. The balance is a value between
* -1.0f and +1.0f. This operation might not be reversible! Also,
- * after this call pa_cvolume_get_fade() is not guaranteed to
- * actually return the requested fade value (e.g. when the input volume
- * was zero anyway for all channels) \since 0.9.15 */
+ * after this call pa_cvolume_get_fade() is not guaranteed to actually
+ * return the requested fade value (e.g. when the input volume was
+ * zero anyway for all channels). If no fade value is applicable to
+ * this channel map the volume will not be modified. See
+ * pa_channel_map_can_fade(). \since 0.9.15 */
pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade);
/** Scale the passed pa_cvolume structure so that the maximum volume
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 57129d0c..8ca8f2d9 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -421,7 +421,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
s,
" index: %u\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsource: %u <%s>\n"
"\tcurrent latency: %0.2f ms\n"
@@ -439,6 +439,8 @@ char *pa_source_output_list_to_string(pa_core *c) {
o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+ o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
+ o->flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
(double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
@@ -498,7 +500,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
s,
" index: %u\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsink: %u <%s>\n"
"\tvolume: %s\n"
@@ -520,6 +522,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+ i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
+ i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
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)),
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index e65b1796..b7ebdeb7 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -97,6 +97,7 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/thread.h>
+#include <pulsecore/strbuf.h>
#include "core-util.h"
@@ -2553,3 +2554,49 @@ unsigned pa_ncpus(void) {
return ncpus <= 0 ? 1 : (unsigned) ncpus;
}
+
+char *pa_replace(const char*s, const char*a, const char *b) {
+ pa_strbuf *sb;
+ size_t an;
+
+ pa_assert(s);
+ pa_assert(a);
+ pa_assert(b);
+
+ an = strlen(a);
+ sb = pa_strbuf_new();
+
+ for (;;) {
+ const char *p;
+
+ if (!(p = strstr(s, a)))
+ break;
+
+ pa_strbuf_putsn(sb, s, p-s);
+ pa_strbuf_puts(sb, b);
+ s = p + an;
+ }
+
+ pa_strbuf_puts(sb, s);
+
+ return pa_strbuf_tostring_free(sb);
+}
+
+char *pa_unescape(char *p) {
+ char *s, *d;
+ pa_bool_t escaped = FALSE;
+
+ for (s = p, d = p; *s; s++) {
+ if (!escaped && *s == '\\') {
+ escaped = TRUE;
+ continue;
+ }
+
+ *(d++) = *s;
+ escaped = FALSE;
+ }
+
+ *d = 0;
+
+ return p;
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 18901f47..442815f1 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -215,4 +215,8 @@ void pa_reduce(unsigned *num, unsigned *den);
unsigned pa_ncpus(void);
+char *pa_replace(const char*s, const char*a, const char *b);
+
+char *pa_unescape(char *p);
+
#endif
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
index f872d347..fd6a9487 100644
--- a/src/pulsecore/envelope.c
+++ b/src/pulsecore/envelope.c
@@ -599,7 +599,6 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
switch (e->sample_spec.format) {
-
case PA_SAMPLE_U8: {
uint8_t *t;
@@ -745,6 +744,13 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
break;
}
+ case PA_SAMPLE_S24LE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32BE:
+ /* FIXME */
+ pa_assert_not_reached();
+
case PA_SAMPLE_MAX:
case PA_SAMPLE_INVALID:
pa_assert_not_reached();
diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
index 24a28db7..352ac977 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -386,8 +386,11 @@ void* pa_idxset_steal_first(pa_idxset *s, uint32_t *idx) {
void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
pa_assert(s);
- if (!s->iterate_list_head)
+ if (!s->iterate_list_head) {
+ if (idx)
+ *idx = PA_IDXSET_INVALID;
return NULL;
+ }
if (idx)
*idx = s->iterate_list_head->idx;
@@ -402,20 +405,41 @@ void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
pa_assert(s);
pa_assert(idx);
+ if (*idx == PA_IDXSET_INVALID)
+ return NULL;
+
hash = *idx % NBUCKETS;
- if (!(e = index_scan(s, hash, *idx)))
- return NULL;
+ if ((e = index_scan(s, hash, *idx))) {
+
+ e = e->iterate_next;
+
+ if (e) {
+ *idx = e->idx;
+ return e->data;
+ } else {
+ *idx = PA_IDXSET_INVALID;
+ return NULL;
+ }
+
+ } else {
+
+ /* If the entry passed doesn't exist anymore we try to find
+ * the next following */
+
+ for ((*idx)++; *idx < s->current_index; (*idx)++) {
+
+ hash = *idx % NBUCKETS;
+
+ if ((e = index_scan(s, hash, *idx))) {
+ *idx = e->idx;
+ return e->data;
+ }
+ }
- if (!e->iterate_next) {
*idx = PA_IDXSET_INVALID;
return NULL;
}
-
- e = e->iterate_next;
-
- *idx = e->idx;
- return e->data;
}
unsigned pa_idxset_size(pa_idxset*s) {
diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
index 0d4c22f8..ed0b63af 100644
--- a/src/pulsecore/ltdl-helper.c
+++ b/src/pulsecore/ltdl-helper.c
@@ -42,7 +42,9 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
pa_assert(handle);
pa_assert(symbol);
- if ((f = ((pa_void_func_t) (size_t) lt_dlsym(handle, symbol))))
+ *(void**) &f = lt_dlsym(handle, symbol);
+
+ if (f)
return f;
if (!module)
@@ -57,7 +59,7 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
if (!isalnum(*c))
*c = '_';
- f = (pa_void_func_t) (size_t) lt_dlsym(handle, sn);
+ *(void**) &f = lt_dlsym(handle, sn);
pa_xfree(sn);
return f;
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index f9ce949a..59460012 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -187,17 +187,17 @@ typedef int pa_bool_t;
abort(); \
} while (FALSE)
-#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
-#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
+#define PA_PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define PA_UINT_TO_PTR(u) ((void*) ((uintptr_t) (u)))
-#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p))
-#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR((uint32_t) u)
+#define PA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
+#define PA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
-#define PA_PTR_TO_INT(p) ((int) PA_PTR_TO_UINT(p))
-#define PA_INT_TO_PTR(u) PA_UINT_TO_PTR((int) u)
+#define PA_PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define PA_INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
-#define PA_PTR_TO_INT32(p) ((int32_t) PA_PTR_TO_UINT(p))
-#define PA_INT32_TO_PTR(u) PA_UINT_TO_PTR((int32_t) u)
+#define PA_PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p)))
+#define PA_INT32_TO_PTR(u) ((void*) ((intptr_t) (u)))
#ifdef OS_IS_WIN32
#define PA_PATH_SEP "\\"
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 9e60125e..866e6e0c 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -79,106 +79,111 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co
}
pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
- pa_hashmap *map = NULL;
+ enum {
+ WHITESPACE,
+ KEY,
+ VALUE_START,
+ VALUE_SIMPLE,
+ VALUE_DOUBLE_QUOTES,
+ VALUE_TICKS
+ } state;
+
+ const char *p, *key = NULL, *value = NULL;
+ size_t key_len = 0, value_len = 0;
+ pa_hashmap *map;
map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- if (args) {
- enum {
- WHITESPACE,
- KEY,
- VALUE_START,
- VALUE_SIMPLE,
- VALUE_DOUBLE_QUOTES,
- VALUE_TICKS
- } state;
-
- const char *p, *key, *value;
- size_t key_len = 0, value_len = 0;
-
- key = value = NULL;
- state = WHITESPACE;
- for (p = args; *p; p++) {
- switch (state) {
- case WHITESPACE:
- if (*p == '=')
+ if (!args)
+ return (pa_modargs*) map;
+
+ state = WHITESPACE;
+
+ for (p = args; *p; p++) {
+ switch (state) {
+
+ case WHITESPACE:
+ if (*p == '=')
+ goto fail;
+ else if (!isspace(*p)) {
+ key = p;
+ state = KEY;
+ key_len = 1;
+ }
+ break;
+
+ case KEY:
+ if (*p == '=')
+ state = VALUE_START;
+ else if (isspace(*p))
+ goto fail;
+ else
+ key_len++;
+ break;
+
+ case VALUE_START:
+ if (*p == '\'') {
+ state = VALUE_TICKS;
+ value = p+1;
+ value_len = 0;
+ } else if (*p == '"') {
+ state = VALUE_DOUBLE_QUOTES;
+ value = p+1;
+ value_len = 0;
+ } else if (isspace(*p)) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
goto fail;
- else if (!isspace(*p)) {
- key = p;
- state = KEY;
- key_len = 1;
- }
- break;
- case KEY:
- if (*p == '=')
- state = VALUE_START;
- else if (isspace(*p))
+ state = WHITESPACE;
+ } else {
+ state = VALUE_SIMPLE;
+ value = p;
+ value_len = 1;
+ }
+ break;
+
+ case VALUE_SIMPLE:
+ if (isspace(*p)) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
goto fail;
- else
- key_len++;
- break;
- case VALUE_START:
- if (*p == '\'') {
- state = VALUE_TICKS;
- value = p+1;
- value_len = 0;
- } else if (*p == '"') {
- state = VALUE_DOUBLE_QUOTES;
- value = p+1;
- value_len = 0;
- } else if (isspace(*p)) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
- goto fail;
- state = WHITESPACE;
- } else {
- state = VALUE_SIMPLE;
- value = p;
- value_len = 1;
- }
- break;
- case VALUE_SIMPLE:
- if (isspace(*p)) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
- goto fail;
- state = WHITESPACE;
- } else
- value_len++;
- break;
- case VALUE_DOUBLE_QUOTES:
- if (*p == '"') {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
- goto fail;
- state = WHITESPACE;
- } else
- value_len++;
- break;
- case VALUE_TICKS:
- if (*p == '\'') {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
- goto fail;
- state = WHITESPACE;
- } else
- value_len++;
- break;
- }
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
+
+ case VALUE_DOUBLE_QUOTES:
+ if (*p == '"') {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
+
+ case VALUE_TICKS:
+ if (*p == '\'') {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
}
+ }
- if (state == VALUE_START) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
- goto fail;
- } else if (state == VALUE_SIMPLE) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0)
- goto fail;
- } else if (state != WHITESPACE)
+ if (state == VALUE_START) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
goto fail;
- }
+ } else if (state == VALUE_SIMPLE) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0)
+ goto fail;
+ } else if (state != WHITESPACE)
+ goto fail;
return (pa_modargs*) map;
fail:
- if (map)
- pa_modargs_free((pa_modargs*) map);
+ pa_modargs_free((pa_modargs*) map);
return NULL;
}
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index ac456ac7..86bcef74 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -194,7 +194,7 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
}
- if (*name == '@' || !name || !pa_namereg_is_valid_name(name))
+ if (!name || *name == '@' || !pa_namereg_is_valid_name(name))
return NULL;
if ((e = pa_hashmap_get(c->namereg, name)))
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index eac21de5..44aa6bf0 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -197,10 +197,10 @@ 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_virtual_volume(&data, volume);
+ pa_sink_input_new_data_set_volume(&data, volume);
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
- u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+ pa_sink_input_new(&u->sink_input, sink->core, &data, 0);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index 35c9985a..522c7afe 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -25,6 +25,7 @@
#include <string.h>
#include <locale.h>
+#include <dlfcn.h>
#include <pulse/proplist.h>
#include <pulse/utf8.h>
@@ -36,11 +37,11 @@
#include "proplist-util.h"
void pa_init_proplist(pa_proplist *p) {
- int a, b;
#if !HAVE_DECL_ENVIRON
extern char **environ;
#endif
char **e;
+ const char *pp;
pa_assert(p);
@@ -75,6 +76,15 @@ void pa_init_proplist(pa_proplist *p) {
}
}
+ if ((pp = getenv("PULSE_PROP"))) {
+ pa_proplist *t;
+
+ if ((t = pa_proplist_from_string(pp))) {
+ pa_proplist_update(p, PA_UPDATE_MERGE, t);
+ pa_proplist_free(t);
+ }
+ }
+
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) {
char t[32];
pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid());
@@ -99,22 +109,36 @@ void pa_init_proplist(pa_proplist *p) {
}
}
- a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY);
- b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME);
-
- if (!a || !b) {
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY)) {
char t[PATH_MAX];
if (pa_get_binary_name(t, sizeof(t))) {
char *c = pa_utf8_filter(t);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
+ pa_xfree(c);
+ }
+ }
- if (!a)
- pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
- if (!b)
- pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, c);
+#ifdef RTLD_NOLOAD
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_NAME)) {
+ void *dl;
- pa_xfree(c);
+ if ((dl = dlopen("libglib-2.0", RTLD_NOLOAD))) {
+ const char *(*_g_get_application_name)(void);
+
+ if ((*(void**) &_g_get_application_name = dlsym(dl, "g_get_application_name")))
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, _g_get_application_name());
+
+ dlclose(dl);
}
}
+#endif
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_NAME)) {
+ const char *t;
+
+ if ((t = pa_proplist_gets(p, PA_PROP_APPLICATION_PROCESS_BINARY)))
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, t);
+ }
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_LANGUAGE)) {
const char *l;
@@ -122,4 +146,23 @@ void pa_init_proplist(pa_proplist *p) {
if ((l = setlocale(LC_MESSAGES, NULL)))
pa_proplist_sets(p, PA_PROP_APPLICATION_LANGUAGE, l);
}
+
+ if (!pa_proplist_contains(p, PA_PROP_WINDOW_X11_DISPLAY)) {
+ const char *t;
+
+ if ((t = getenv("DISPLAY"))) {
+ char *c = pa_utf8_filter(t);
+ pa_proplist_sets(p, PA_PROP_WINDOW_X11_DISPLAY, c);
+ pa_xfree(c);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_MACHINE_ID)) {
+ char *m;
+
+ if ((m = pa_machine_id())) {
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_MACHINE_ID, m);
+ pa_xfree(m);
+ }
+ }
}
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 1311e678..e1643cbb 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -424,7 +424,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
sdata.sink = sink;
pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
- c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
+ pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata, 0);
pa_sink_input_new_data_done(&sdata);
CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
@@ -526,7 +526,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
sdata.source = source;
pa_source_output_new_data_set_sample_spec(&sdata, &ss);
- c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
+ pa_source_output_new(&c->source_output, c->protocol->core, &sdata, 0);
pa_source_output_new_data_done(&sdata);
CHECK_VALIDITY(c->source_output, "Failed to create source output.");
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index c9621652..39c834d8 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -591,10 +591,11 @@ static record_stream* record_stream_new(
pa_proplist *p,
pa_bool_t adjust_latency,
pa_sink_input *direct_on_input,
- pa_bool_t early_requests) {
+ pa_bool_t early_requests,
+ int *ret) {
record_stream *s;
- pa_source_output *source_output;
+ pa_source_output *source_output = NULL;
size_t base;
pa_source_output_new_data data;
@@ -602,6 +603,7 @@ static record_stream* record_stream_new(
pa_assert(ss);
pa_assert(maxlength);
pa_assert(p);
+ pa_assert(ret);
pa_source_output_new_data_init(&data);
@@ -616,7 +618,7 @@ static record_stream* record_stream_new(
if (peak_detect)
data.resample_method = PA_RESAMPLER_PEAKS;
- source_output = pa_source_output_new(c->protocol->core, &data, flags);
+ *ret = -pa_source_output_new(&source_output, c->protocol->core, &data, flags);
pa_source_output_new_data_done(&data);
@@ -965,10 +967,11 @@ static playback_stream* playback_stream_new(
pa_sink_input_flags_t flags,
pa_proplist *p,
pa_bool_t adjust_latency,
- pa_bool_t early_requests) {
+ pa_bool_t early_requests,
+ int *ret) {
playback_stream *s, *ssync;
- pa_sink_input *sink_input;
+ pa_sink_input *sink_input = NULL;
pa_memchunk silence;
uint32_t idx;
int64_t start_index;
@@ -982,6 +985,7 @@ static playback_stream* playback_stream_new(
pa_assert(minreq);
pa_assert(missing);
pa_assert(p);
+ pa_assert(ret);
/* Find syncid group */
for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
@@ -998,8 +1002,10 @@ static playback_stream* playback_stream_new(
if (!sink)
sink = ssync->sink_input->sink;
- else if (sink != ssync->sink_input->sink)
+ else if (sink != ssync->sink_input->sink) {
+ *ret = PA_ERR_INVALID;
return NULL;
+ }
}
pa_sink_input_new_data_init(&data);
@@ -1012,12 +1018,12 @@ 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_virtual_volume(&data, volume);
+ pa_sink_input_new_data_set_volume(&data, volume);
if (muted_set)
pa_sink_input_new_data_set_muted(&data, muted);
data.sync_base = ssync ? ssync->sink_input : NULL;
- sink_input = pa_sink_input_new(c->protocol->core, &data, flags);
+ *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data, flags);
pa_sink_input_new_data_done(&data);
@@ -1691,11 +1697,12 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
adjust_latency = FALSE,
early_requests = FALSE,
dont_inhibit_auto_suspend = FALSE,
- muted_set = FALSE;
-
+ muted_set = FALSE,
+ fail_on_suspend = FALSE;
pa_sink_input_flags_t flags = 0;
pa_proplist *p;
pa_bool_t volume_set = TRUE;
+ int ret = PA_ERR_INVALID;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -1775,7 +1782,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (c->version >= 15) {
if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
- pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) {
+ pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
+ pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
protocol_error(c);
pa_proplist_free(p);
return;
@@ -1814,16 +1822,17 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
(fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) |
(no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) |
- (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0);
+ (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
+ (fail_on_suspend ? PA_SINK_INPUT_FAIL_ON_SUSPEND : 0);
/* Only since protocol version 15 there's a seperate muted_set
* flag. For older versions we synthesize it here */
muted_set = muted_set || muted;
- s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests);
+ s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret);
pa_proplist_free(p);
- CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, s, tag, ret);
reply = reply_new(tag);
pa_tagstruct_putu32(reply, s->index);
@@ -1942,11 +1951,13 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
adjust_latency = FALSE,
peak_detect = FALSE,
early_requests = FALSE,
- dont_inhibit_auto_suspend = FALSE;
+ dont_inhibit_auto_suspend = FALSE,
+ fail_on_suspend = FALSE;
pa_source_output_flags_t flags = 0;
pa_proplist *p;
uint32_t direct_on_input_idx = PA_INVALID_INDEX;
pa_sink_input *direct_on_input = NULL;
+ int ret = PA_ERR_INVALID;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -2016,7 +2027,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
if (c->version >= 15) {
- if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) {
+ if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
+ pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
protocol_error(c);
pa_proplist_free(p);
return;
@@ -2064,12 +2076,13 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
(fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
(no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
- (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0);
+ (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
+ (fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0);
- s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests);
+ s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
pa_proplist_free(p);
- CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, s, tag, ret);
reply = reply_new(tag);
pa_tagstruct_putu32(reply, s->index);
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index dd8002a3..e149c40d 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -539,7 +539,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec);
- c->sink_input = pa_sink_input_new(p->core, &data, 0);
+ pa_sink_input_new(&c->sink_input, p->core, &data, 0);
pa_sink_input_new_data_done(&data);
if (!c->sink_input) {
@@ -591,7 +591,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_source_output_new_data_set_sample_spec(&data, &o->sample_spec);
- c->source_output = pa_source_output_new(p->core, &data, 0);
+ pa_source_output_new(&c->source_output, p->core, &data, 0);
pa_source_output_new_data_done(&data);
if (!c->source_output) {
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index be390db7..78ad5530 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -156,16 +156,6 @@ static int (* const init_table[])(pa_resampler*r) = {
[PA_RESAMPLER_PEAKS] = peaks_init,
};
-static inline size_t sample_size(pa_sample_format_t f) {
- pa_sample_spec ss = {
- .format = f,
- .rate = 0,
- .channels = 1
- };
-
- return pa_sample_size(&ss);
-}
-
pa_resampler* pa_resampler_new(
pa_mempool *pool,
const pa_sample_spec *a,
@@ -275,7 +265,7 @@ pa_resampler* pa_resampler_new(
pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format));
- r->w_sz = sample_size(r->work_format);
+ r->w_sz = pa_sample_size_of_format(r->work_format);
if (r->i_ss.format == r->work_format)
r->to_work_format_func = NULL;
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index d4d11194..df42cae8 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -72,18 +72,11 @@ 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_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
+void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
pa_assert(data);
- 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;
+ if ((data->volume_is_set = !!volume))
+ data->volume = *volume;
}
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
@@ -120,7 +113,8 @@ static void reset_callbacks(pa_sink_input *i) {
}
/* Called from main context */
-pa_sink_input* pa_sink_input_new(
+int pa_sink_input_new(
+ pa_sink_input **_i,
pa_core *core,
pa_sink_input_new_data *data,
pa_sink_input_flags_t flags) {
@@ -129,28 +123,34 @@ pa_sink_input* pa_sink_input_new(
pa_resampler *resampler = NULL;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_channel_map original_cm;
+ int r;
+ pa_assert(_i);
pa_assert(core);
pa_assert(data);
- if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data) < 0)
- return NULL;
+ if (data->client)
+ pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
- pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+ if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
+ return r;
+
+ pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
if (!data->sink) {
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);
- pa_return_null_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED));
+ pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY);
+ pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
+ pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID);
+ pa_return_val_if_fail(!(flags & PA_SINK_INPUT_FAIL_ON_SUSPEND) || pa_sink_get_state(data->sink) != PA_SINK_SUSPENDED, -PA_ERR_BADSTATE);
if (!data->sample_spec_is_set)
data->sample_spec = data->sink->sample_spec;
- pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
+ pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID);
if (!data->channel_map_is_set) {
if (pa_channel_map_compatible(&data->sink->channel_map, &data->sample_spec))
@@ -159,38 +159,23 @@ pa_sink_input* pa_sink_input_new(
pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
}
- pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
- pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec));
+ pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
+ pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
- if (!data->virtual_volume_is_set) {
+ if (!data->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);
+ data->volume = *pa_sink_get_volume(data->sink, FALSE);
+ pa_cvolume_remap(&data->volume, &data->sink->channel_map, &data->channel_map);
} else
- pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
data->save_volume = FALSE;
- } 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));
+ pa_return_val_if_fail(pa_cvolume_valid(&data->volume), -PA_ERR_INVALID);
+ pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
if (!data->muted_is_set)
data->muted = FALSE;
@@ -212,23 +197,19 @@ 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->soft_volume, &original_cm, &data->channel_map);
- pa_cvolume_remap(&data->virtual_volume, &original_cm, &data->channel_map);
+ pa_cvolume_remap(&data->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);
+ pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);
- if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
- return NULL;
+ if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
+ return r;
if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
pa_log_warn("Failed to create sink input: too many inputs per sink.");
- return NULL;
+ return -PA_ERR_TOOLARGE;
}
if ((flags & PA_SINK_INPUT_VARIABLE_RATE) ||
@@ -245,7 +226,7 @@ pa_sink_input* pa_sink_input_new(
(core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
(core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
pa_log_warn("Unsupported resampling operation.");
- return NULL;
+ return -PA_ERR_NOTSUPPORTED;
}
}
@@ -267,8 +248,18 @@ pa_sink_input* pa_sink_input_new(
i->sample_spec = data->sample_spec;
i->channel_map = data->channel_map;
- i->virtual_volume = data->virtual_volume;
- i->soft_volume = data->soft_volume;
+ if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !data->volume_is_absolute) {
+ /* When the 'absolute' bool is not set then we'll treat the volume
+ * as relative to the sink volume even in flat volume mode */
+
+ pa_cvolume t = *pa_sink_get_volume(data->sink, FALSE);
+ pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map);
+
+ pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &t);
+ } else
+ i->virtual_volume = data->volume;
+
+ pa_cvolume_init(&i->soft_volume);
i->save_volume = data->save_volume;
i->save_sink = data->save_sink;
i->save_muted = data->save_muted;
@@ -330,7 +321,8 @@ pa_sink_input* pa_sink_input_new(
/* Don't forget to call pa_sink_input_put! */
- return i;
+ *_i = i;
+ return 0;
}
/* Called from main context */
@@ -347,7 +339,7 @@ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
}
/* Called from main context */
-static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
+static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
pa_sink_input *ssync;
pa_assert(i);
@@ -355,7 +347,7 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
state = PA_SINK_INPUT_RUNNING;
if (i->state == state)
- return 0;
+ return;
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
@@ -382,8 +374,6 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
}
pa_sink_update_status(i->sink);
-
- return 0;
}
/* Called from main context */
@@ -499,9 +489,6 @@ void pa_sink_input_put(pa_sink_input *i) {
pa_assert(i->process_rewind);
pa_assert(i->kill);
- 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;
update_n_corked(i, state);
@@ -512,7 +499,11 @@ void pa_sink_input_put(pa_sink_input *i) {
pa_cvolume new_volume;
pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
- }
+ } else
+ i->soft_volume = i->virtual_volume;
+
+ i->thread_info.soft_volume = i->soft_volume;
+ i->thread_info.muted = i->muted;
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
@@ -549,7 +540,7 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
}
/* Called from thread context */
-int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
+void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
pa_bool_t do_volume_adj_here;
pa_bool_t volume_is_norm;
size_t block_size_max_sink, block_size_max_sink_input;
@@ -563,9 +554,6 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
/* pa_log_debug("peek"); */
- if (!i->pop)
- return -1;
-
pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING ||
i->thread_info.state == PA_SINK_INPUT_CORKED ||
i->thread_info.state == PA_SINK_INPUT_DRAINED);
@@ -692,8 +680,6 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
pa_cvolume_mute(volume, i->sink->sample_spec.channels);
else
*volume = i->thread_info.soft_volume;
-
- return 0;
}
/* Called from thread context */
@@ -960,7 +946,7 @@ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- pa_return_val_if_fail(i->thread_info.resampler, -1);
+ pa_return_val_if_fail(i->thread_info.resampler, -PA_ERR_BADSTATE);
if (i->sample_spec.rate == rate)
return 0;
@@ -1048,16 +1034,17 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
int pa_sink_input_start_move(pa_sink_input *i) {
pa_source_output *o, *p = NULL;
pa_sink *origin;
+ int r;
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(i->sink);
if (!pa_sink_input_may_move(i))
- return -1;
+ return -PA_ERR_NOTSUPPORTED;
- if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i) < 0)
- return -1;
+ if ((r = pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i)) < 0)
+ return r;
origin = i->sink;
@@ -1074,9 +1061,15 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
pa_assert_se(i->sink->n_corked-- >= 1);
- /* 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;
+
+ /* Make the absolute volume relative */
+ i->virtual_volume = i->soft_volume;
+ pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels);
+
+ /* We might need to update the sink's volume if we are in flat
+ * volume mode. */
pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
}
@@ -1099,14 +1092,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
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++;
+ return -PA_ERR_NOTSUPPORTED;
if (i->thread_info.resampler &&
pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) &&
@@ -1130,11 +1116,18 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
((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)))) {
pa_log_warn("Unsupported resampling operation.");
- return -1;
+ return -PA_ERR_NOTSUPPORTED;
}
} else
new_resampler = NULL;
+ 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++;
+
/* Replace resampler and render queue */
if (new_resampler != i->thread_info.resampler) {
@@ -1157,9 +1150,15 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
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;
+
+ /* Make relative volume absolute again */
+ pa_cvolume t = dest->virtual_volume;
+ pa_cvolume_remap(&t, &dest->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->virtual_volume, &i->virtual_volume, &t);
+
+ /* We might need to update the sink's volume if we are in flat volume mode. */
pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
}
@@ -1180,6 +1179,8 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
/* Called from main context */
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
+ int r;
+
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(i->sink);
@@ -1189,13 +1190,13 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
return 0;
if (!pa_sink_input_may_move_to(i, dest))
- return -1;
+ return -PA_ERR_NOTSUPPORTED;
- if (pa_sink_input_start_move(i) < 0)
- return -1;
+ if ((r = pa_sink_input_start_move(i)) < 0)
+ return r;
- if (pa_sink_input_finish_move(i, dest, save) < 0)
- return -1;
+ if ((r = pa_sink_input_finish_move(i, dest, save)) < 0)
+ return r;
return 0;
}
@@ -1310,7 +1311,7 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
}
}
- return -1;
+ return -PA_ERR_NOTIMPLEMENTED;
}
/* Called from main thread */
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 893d8690..f6c5aa1d 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -58,6 +58,7 @@ typedef enum pa_sink_input_flags {
PA_SINK_INPUT_FIX_RATE = 64,
PA_SINK_INPUT_FIX_CHANNELS = 128,
PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
+ PA_SINK_INPUT_FAIL_ON_SUSPEND = 512
} pa_sink_input_flags_t;
struct pa_sink_input {
@@ -232,16 +233,16 @@ typedef struct pa_sink_input_new_data {
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_cvolume virtual_volume, soft_volume;
+ pa_cvolume 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 virtual_volume_is_set:1, soft_volume_is_set:1;
+ pa_bool_t volume_is_set:1;
pa_bool_t muted_is_set:1;
- pa_bool_t virtual_volume_is_absolute:1;
+ pa_bool_t volume_is_absolute:1;
pa_bool_t save_sink:1, save_volume:1, save_muted:1;
} pa_sink_input_new_data;
@@ -249,14 +250,14 @@ typedef struct 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_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_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);
/* To be called by the implementing module only */
-pa_sink_input* pa_sink_input_new(
+int pa_sink_input_new(
+ pa_sink_input **i,
pa_core *core,
pa_sink_input_new_data *data,
pa_sink_input_flags_t flags);
@@ -312,7 +313,7 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i);
/* To be used exclusively by the sink driver IO thread */
-int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume);
+void pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume);
void pa_sink_input_drop(pa_sink_input *i, size_t length);
void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 61be86a1..0c297ec3 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -305,6 +305,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
s->state = state;
+ 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);
+ }
+
if (suspend_change) {
pa_sink_input *i;
uint32_t idx;
@@ -312,15 +317,13 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
/* We're suspending or resuming, tell everyone about it */
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
- if (i->suspend)
+ if (s->state == PA_SINK_SUSPENDED &&
+ (i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND))
+ pa_sink_input_kill(i);
+ else if (i->suspend)
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 */
- 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;
}
@@ -544,9 +547,17 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+ /* If nobody requested this and this is actually no real rewind
+ * then we can short cut this */
+ if (!s->thread_info.rewind_requested && nbytes <= 0)
+ return;
+
s->thread_info.rewind_nbytes = 0;
s->thread_info.rewind_requested = FALSE;
+ if (s->thread_info.state == PA_SINK_SUSPENDED)
+ return;
+
if (nbytes > 0)
pa_log_debug("Processing rewind...");
@@ -556,7 +567,7 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
}
if (nbytes > 0)
- if (s->monitor_source && PA_SOURCE_IS_OPENED(s->monitor_source->thread_info.state))
+ if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
pa_source_process_rewind(s->monitor_source, nbytes);
}
@@ -573,8 +584,7 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
pa_sink_input_assert_ref(i);
- if (pa_sink_input_peek(i, *length, &info->chunk, &info->volume) < 0)
- continue;
+ pa_sink_input_peek(i, *length, &info->chunk, &info->volume);
if (mixlength == 0 || info->chunk.length < mixlength)
mixlength = info->chunk.length;
@@ -636,7 +646,7 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *
/* Drop read data */
pa_sink_input_drop(i, result->length);
- if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) {
+ if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state)) {
if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
void *ostate = NULL;
@@ -692,7 +702,7 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *
}
}
- if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
+ if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
pa_source_post(s->monitor_source, result);
}
@@ -703,7 +713,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
size_t block_size_max;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
@@ -712,6 +722,13 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_assert(!s->thread_info.rewind_requested);
pa_assert(s->thread_info.rewind_nbytes == 0);
+ if (s->thread_info.state == PA_SINK_SUSPENDED) {
+ result->memblock = pa_memblock_ref(s->silence.memblock);
+ result->index = s->silence.index;
+ result->length = PA_MIN(s->silence.length, length);
+ return;
+ }
+
if (length <= 0)
length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
@@ -776,7 +793,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
size_t length, block_size_max;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
@@ -787,6 +804,11 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_assert(!s->thread_info.rewind_requested);
pa_assert(s->thread_info.rewind_nbytes == 0);
+ if (s->thread_info.state == PA_SINK_SUSPENDED) {
+ pa_silence_memchunk(target, &s->sample_spec);
+ return;
+ }
+
length = target->length;
block_size_max = pa_mempool_block_size_max(s->core->mempool);
if (length > block_size_max)
@@ -854,7 +876,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
size_t l, d;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
@@ -884,7 +906,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
/* Called from IO thread context */
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
pa_assert(length > 0);
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
@@ -910,7 +932,7 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
/* The returned value is supposed to be in the time domain of the sound card! */
- if (!PA_SINK_IS_OPENED(s->state))
+ if (s->state == PA_SINK_SUSPENDED)
return 0;
if (!(s->flags & PA_SINK_LATENCY))
@@ -926,12 +948,24 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
pa_sink_input *i;
uint32_t idx;
+ pa_sink_assert_ref(s);
+ pa_assert(new_volume);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
+
/* This is called whenever a sink input volume changes 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().*/
+ if (pa_idxset_isempty(s->inputs)) {
+ /* In the special case that we have no sink input we leave the
+ * volume unmodified. */
+ *new_volume = s->virtual_volume;
+ return;
+ }
+
pa_cvolume_mute(new_volume, s->channel_map.channels);
/* First let's determine the new maximum volume of all inputs
@@ -973,8 +1007,8 @@ void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
uint32_t idx;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(old_volume);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
/* This is called whenever the sink volume changes that is not
@@ -1133,6 +1167,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
pa_sink_assert_ref(s);
+ pa_assert(p);
pa_proplist_update(s->proplist, mode, p);
@@ -1473,7 +1508,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_SET_MUTE:
- if (!s->thread_info.soft_muted != s->muted) {
+ if (s->thread_info.soft_muted != s->muted) {
s->thread_info.soft_muted = s->muted;
pa_sink_request_rewind(s, (size_t) -1);
}
@@ -1486,6 +1521,10 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_SET_STATE:
s->thread_info.state = PA_PTR_TO_UINT(userdata);
+
+ if (s->thread_info.state == PA_SINK_SUSPENDED)
+ s->thread_info.rewind_requested = FALSE;
+
return 0;
case PA_SINK_MESSAGE_DETACH:
@@ -1613,6 +1652,9 @@ void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+ if (s->thread_info.state == PA_SINK_SUSPENDED)
+ return;
+
if (nbytes == (size_t) -1)
nbytes = s->thread_info.max_rewind;
@@ -1674,7 +1716,7 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->state));
- if (!PA_SINK_IS_OPENED(s->state))
+ if (s->state == PA_SINK_SUSPENDED)
return 0;
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 1be421f1..c3de3067 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -322,11 +322,11 @@ 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_virtual_volume(&data, volume);
+ pa_sink_input_new_data_set_volume(&data, volume);
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
- u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+ pa_sink_input_new(&u->sink_input, sink->core, &data, 0);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 1e1ac4e1..63d56d5a 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -95,7 +95,8 @@ static void reset_callbacks(pa_source_output *o) {
}
/* Called from main context */
-pa_source_output* pa_source_output_new(
+int pa_source_output_new(
+ pa_source_output**_o,
pa_core *core,
pa_source_output_new_data *data,
pa_source_output_flags_t flags) {
@@ -103,29 +104,34 @@ pa_source_output* pa_source_output_new(
pa_source_output *o;
pa_resampler *resampler = NULL;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ int r;
+ pa_assert(_o);
pa_assert(core);
pa_assert(data);
- if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data) < 0)
- return NULL;
+ if (data->client)
+ pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
- pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+ if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0)
+ return r;
+
+ pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
if (!data->source) {
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);
-
- pa_return_null_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of);
+ pa_return_val_if_fail(data->source, -PA_ERR_NOENTITY);
+ pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE);
+ pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID);
+ pa_return_val_if_fail(!(flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND) || pa_source_get_state(data->source) != PA_SOURCE_SUSPENDED, -PA_ERR_BADSTATE);
if (!data->sample_spec_is_set)
data->sample_spec = data->source->sample_spec;
- pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
+ pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID);
if (!data->channel_map_is_set) {
if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec))
@@ -134,8 +140,8 @@ pa_source_output* pa_source_output_new(
pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
}
- pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
- pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec));
+ pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
+ pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
data->sample_spec.format = data->source->sample_spec.format;
@@ -154,17 +160,14 @@ pa_source_output* pa_source_output_new(
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
- pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+ pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);
- 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;
+ if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
+ return r;
if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
pa_log("Failed to create source output: too many outputs per source.");
- return NULL;
+ return -PA_ERR_TOOLARGE;
}
if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
@@ -181,7 +184,7 @@ pa_source_output* pa_source_output_new(
(core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
(core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
pa_log_warn("Unsupported resampling operation.");
- return NULL;
+ return -PA_ERR_NOTSUPPORTED;
}
}
@@ -245,7 +248,8 @@ pa_source_output* pa_source_output_new(
/* Don't forget to call pa_source_output_put! */
- return o;
+ *_o = o;
+ return 0;
}
/* Called from main context */
@@ -262,11 +266,11 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state)
}
/* Called from main context */
-static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
+static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
pa_assert(o);
if (o->state == state)
- return 0;
+ return;
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
@@ -277,8 +281,6 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t
pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
pa_source_update_status(o->source);
-
- return 0;
}
/* Called from main context */
@@ -575,7 +577,7 @@ 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) {
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
- pa_return_val_if_fail(o->thread_info.resampler, -1);
+ pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE);
if (o->sample_spec.rate == rate)
return 0;
@@ -675,16 +677,17 @@ pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
/* Called from main context */
int pa_source_output_start_move(pa_source_output *o) {
pa_source *origin;
+ int r;
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_assert(o->source);
if (!pa_source_output_may_move(o))
- return -1;
+ return -PA_ERR_NOTSUPPORTED;
- if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o) < 0)
- return -1;
+ if ((r = pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o)) < 0)
+ return r;
origin = o->source;
@@ -713,13 +716,6 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
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(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))
@@ -742,11 +738,18 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
((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)))) {
pa_log_warn("Unsupported resampling operation.");
- return -1;
+ return -PA_ERR_NOTSUPPORTED;
}
} else
new_resampler = NULL;
+ 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++;
+
/* Replace resampler */
if (new_resampler != o->thread_info.resampler) {
if (o->thread_info.resampler)
@@ -783,6 +786,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
/* Called from main context */
int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) {
+ int r;
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
@@ -793,13 +797,13 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
return 0;
if (!pa_source_output_may_move_to(o, dest))
- return -1;
+ return -PA_ERR_NOTSUPPORTED;
- if (pa_source_output_start_move(o) < 0)
- return -1;
+ if ((r = pa_source_output_start_move(o)) < 0)
+ return r;
- if (pa_source_output_finish_move(o, dest, save) < 0)
- return -1;
+ if ((r = pa_source_output_finish_move(o, dest, save)) < 0)
+ return r;
return 0;
}
@@ -863,5 +867,5 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
}
}
- return -1;
+ return -PA_ERR_NOTIMPLEMENTED;
}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 79b4926b..91f28f94 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -54,7 +54,8 @@ typedef enum pa_source_output_flags {
PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
PA_SOURCE_OUTPUT_FIX_RATE = 64,
PA_SOURCE_OUTPUT_FIX_CHANNELS = 128,
- PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256
+ PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
+ PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND = 512
} pa_source_output_flags_t;
struct pa_source_output {
@@ -204,7 +205,8 @@ void pa_source_output_new_data_done(pa_source_output_new_data *data);
/* To be called by the implementing module only */
-pa_source_output* pa_source_output_new(
+int pa_source_output_new(
+ pa_source_output**o,
pa_core *core,
pa_source_output_new_data *data,
pa_source_output_flags_t flags);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 38f8e531..04c7f8b7 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -267,6 +267,11 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
s->state = state;
+ if (state != PA_SOURCE_UNLINKED) { /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
+
if (suspend_change) {
pa_source_output *o;
uint32_t idx;
@@ -274,12 +279,13 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
/* We're suspending or resuming, tell everyone about it */
for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
- if (o->suspend)
+ if (s->state == PA_SOURCE_SUSPENDED &&
+ (o->flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND))
+ pa_source_output_kill(o);
+ else if (o->suspend)
o->suspend(o, state == PA_SOURCE_SUSPENDED);
}
- if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
return 0;
}
@@ -479,7 +485,10 @@ void pa_source_process_rewind(pa_source *s, size_t nbytes) {
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+ if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+ return;
if (nbytes <= 0)
return;
@@ -498,9 +507,12 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
pa_assert(chunk);
+ if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+ return;
+
if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
pa_memchunk vchunk = *chunk;
@@ -534,11 +546,14 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
/* Called from IO thread context */
void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
pa_source_output_assert_ref(o);
pa_assert(o->thread_info.direct_on_input);
pa_assert(chunk);
+ if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+ return;
+
if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
pa_memchunk vchunk = *chunk;
@@ -564,7 +579,7 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (!PA_SOURCE_IS_OPENED(s->state))
+ if (s->state == PA_SOURCE_SUSPENDED)
return 0;
if (!(s->flags & PA_SOURCE_LATENCY))
@@ -675,8 +690,8 @@ 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_assert(p);
pa_proplist_update(s->proplist, mode, p);
@@ -1000,7 +1015,7 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) {
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (!PA_SOURCE_IS_OPENED(s->state))
+ if (s->state == PA_SOURCE_SUSPENDED)
return 0;
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c
index f69fa686..5526bb7e 100644
--- a/src/tests/proplist-test.c
+++ b/src/tests/proplist-test.c
@@ -29,8 +29,9 @@
#include <pulsecore/core-util.h>
int main(int argc, char*argv[]) {
- pa_proplist *a, *b, *c;
- char *s, *t, *u;
+ pa_proplist *a, *b, *c, *d;
+ char *s, *t, *u, *v;
+ const char *text;
a = pa_proplist_new();
pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0);
@@ -63,5 +64,19 @@ int main(int argc, char*argv[]) {
pa_proplist_free(b);
pa_proplist_free(c);
+ text = " eins = zwei drei = \"\\\"vier\\\"\" fuenf=sechs sieben ='\\a\\c\\h\\t\\'\\\"' neun= hex:0123456789abCDef ";
+
+ printf("%s\n", text);
+ d = pa_proplist_from_string(text);
+ v = pa_proplist_to_string(d);
+ pa_proplist_free(d);
+ printf("%s\n", v);
+ d = pa_proplist_from_string(v);
+ pa_xfree(v);
+ v = pa_proplist_to_string(d);
+ pa_proplist_free(d);
+ printf("%s\n", v);
+ pa_xfree(v);
+
return 0;
}
diff --git a/src/tests/volume-ui.py b/src/tests/volume-ui.py
index a2b756e6..6dc1c47d 100644
--- a/src/tests/volume-ui.py
+++ b/src/tests/volume-ui.py
@@ -1,9 +1,15 @@
#!/usr/bin/python
-import pygtk, gtk
+import pygtk, gtk, sys
from ctypes import *
-libpulse = cdll.LoadLibrary("../.libs/libpulse.so")
+try:
+ libpulse = cdll.LoadLibrary("../.libs/libpulse.so")
+except OSError:
+ try:
+ libpulse = cdll.LoadLibrary(".libs/libpulse.so")
+ except OSError:
+ libpulse = cdll.LoadLibrary("libpulse.so")
class ChannelMap(Structure):
_fields_ = [("channels", c_ubyte),
@@ -29,6 +35,18 @@ class ChannelMap(Structure):
_position_to_pretty_string.restype = c_char_p
_position_to_pretty_string.argtypes = [c_uint]
+ _can_balance = libpulse.pa_channel_map_can_balance
+ _can_balance.restype = c_int
+ _can_balance.argtypes = [c_void_p]
+
+ _can_fade = libpulse.pa_channel_map_can_fade
+ _can_fade.restype = c_int
+ _can_fade.argtypes = [c_void_p]
+
+ _parse = libpulse.pa_channel_map_parse
+ _parse.restype = c_void_p
+ _parse.argtypes = [c_void_p, c_char_p]
+
def to_name(this):
return this._to_name(byref(this))
@@ -42,7 +60,7 @@ class ChannelMap(Structure):
if r is None:
return None
else:
- return s.raw
+ return s.value
def position_to_string(this, pos):
return this._position_to_string(pos)
@@ -50,11 +68,21 @@ class ChannelMap(Structure):
def position_to_pretty_string(this, pos):
return this._position_to_pretty_string(pos)
+ def can_balance(this):
+ return bool(this._can_balance(byref(this)))
+
+ def can_fade(this):
+ return bool(this._can_fade(byref(this)))
+
+ def parse(this, s):
+ if this._parse(byref(this), s) is None:
+ raise Exception("Parse failure")
+
+
class CVolume(Structure):
_fields_ = [("channels", c_ubyte),
("values", c_uint32 * 32)]
-
_snprint = libpulse.pa_cvolume_snprint
_snprint.restype = c_char_p
_snprint.argtypes = [c_char_p, c_ulong, c_void_p]
@@ -110,19 +138,12 @@ class CVolume(Structure):
def set_fade(this, cm, f):
return this._set_fade(byref(this), byref(cm), f)
-
-
cm = ChannelMap()
-cm.channels = 6
-cm.map[0] = 1
-cm.map[1] = 2
-cm.map[2] = 3
-cm.map[3] = 5
-cm.map[4] = 6
-cm.map[5] = 7
-print "Channel map name: %s" % cm.to_name()
-print "Channel map mapping: %s" % cm.snprint()
+if len(sys.argv) > 1:
+ cm.parse(sys.argv[1])
+else:
+ cm.parse("surround-51")
v = CVolume()
v.channels = cm.channels
@@ -130,13 +151,12 @@ v.channels = cm.channels
for i in range(cm.channels):
v.values[i] = 65536/2
-print v.max()
-print v.snprint()
-print v.get_balance(cm)
-print v.get_fade(cm)
+title = cm.to_pretty_name()
+if title is None:
+ title = cm.snprint()
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-window.set_title(cm.to_pretty_name())
+window.set_title(unicode(title))
window.set_border_width(12)
vbox = gtk.VBox(spacing=6)
@@ -213,7 +233,7 @@ fade_scale.set_digits(2)
vbox.pack_start(fade_scale, expand=False, fill=True)
window.add(vbox)
-window.set_default_size(600, 400)
+window.set_default_size(600, 50)
update_volume()
@@ -223,5 +243,17 @@ fade_scale.connect("value_changed", fade_value_changed)
balance_scale.connect("value_changed", balance_value_changed)
value_scale.connect("value_changed", value_value_changed)
-window.show_all()
+vbox.show_all()
+
+if not cm.can_balance():
+ balance_label.hide()
+ balance_scale.hide()
+
+if not cm.can_fade():
+ fade_label.hide()
+ fade_scale.hide()
+
+
+window.show()
+
gtk.main()