From b546beef21d1918b7c08f2d1838ef6f32d7ead73 Mon Sep 17 00:00:00 2001
From: Arun Raghavan
Date: Thu, 23 Jul 2020 19:36:06 -0400
Subject: [PATCH 001/865] proplist-util: Drop deprecated G_CONST_RETURN macro
The preference in glib is now to just use the const qualifier directly.
---
src/pulsecore/proplist-util.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index f966579b4..16ea9e006 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -45,14 +45,14 @@ extern char **environ;
#if defined(HAVE_GLIB) && defined(PA_GCC_WEAKREF)
#include
-static G_CONST_RETURN gchar* _g_get_application_name(void) PA_GCC_WEAKREF(g_get_application_name);
+static const gchar* _g_get_application_name(void) PA_GCC_WEAKREF(g_get_application_name);
#endif
#if defined(HAVE_GTK) && defined(PA_GCC_WEAKREF)
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include
#include
-static G_CONST_RETURN gchar* _gtk_window_get_default_icon_name(void) PA_GCC_WEAKREF(gtk_window_get_default_icon_name);
+static const gchar* _gtk_window_get_default_icon_name(void) PA_GCC_WEAKREF(gtk_window_get_default_icon_name);
static Display *_gdk_display PA_GCC_WEAKREF(gdk_display);
#endif
From c442227c6bc64789cb4bd47dbd0b73b11e7a60e4 Mon Sep 17 00:00:00 2001
From: Arun Raghavan
Date: Thu, 23 Jul 2020 19:36:11 -0400
Subject: [PATCH 002/865] glib-mainloop: Drop deprecated g_get_current_time()
usage
This uses the year 2038-safe g_get_real_time() as recommended instead.
Bumps GLib dependency to 2.28 as a result.
---
configure.ac | 2 +-
meson.build | 2 +-
src/pulse/glib-mainloop.c | 21 +++++++++------------
3 files changed, 11 insertions(+), 14 deletions(-)
diff --git a/configure.ac b/configure.ac
index a519e63d7..caabef6ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -894,7 +894,7 @@ AC_ARG_ENABLE([glib2],
AS_HELP_STRING([--disable-glib2],[Disable optional GLib 2 support]))
AS_IF([test "x$enable_glib2" != "xno"],
- [PKG_CHECK_MODULES(GLIB20, [ glib-2.0 >= 2.4.0 ], HAVE_GLIB20=1, HAVE_GLIB20=0)],
+ [PKG_CHECK_MODULES(GLIB20, [ glib-2.0 >= 2.28.0 ], HAVE_GLIB20=1, HAVE_GLIB20=0)],
HAVE_GLIB20=0)
AS_IF([test "x$enable_glib2" = "xyes" && test "x$HAVE_GLIB20" = "x0"],
diff --git a/meson.build b/meson.build
index 658eeee57..1c671d32c 100644
--- a/meson.build
+++ b/meson.build
@@ -556,7 +556,7 @@ if gio_dep.found()
cdata.set('HAVE_GSETTINGS', 1)
endif
-glib_dep = dependency('glib-2.0', version : '>= 2.4.0', required: get_option('glib'))
+glib_dep = dependency('glib-2.0', version : '>= 2.28.0', required: get_option('glib'))
if glib_dep.found()
cdata.set('HAVE_GLIB', 1)
endif
diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c
index 1ce3cd39e..77295def8 100644
--- a/src/pulse/glib-mainloop.c
+++ b/src/pulse/glib-mainloop.c
@@ -482,16 +482,15 @@ static gboolean prepare_func(GSource *source, gint *timeout) {
return TRUE;
} else if (g->n_enabled_time_events) {
pa_time_event *t;
- GTimeVal now;
+ gint64 now;
struct timeval tvnow;
pa_usec_t usec;
t = find_next_time_event(g);
g_assert(t);
- g_get_current_time(&now);
- tvnow.tv_sec = now.tv_sec;
- tvnow.tv_usec = now.tv_usec;
+ now = g_get_real_time();
+ pa_timeval_store(&tvnow, now);
if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) {
*timeout = 0;
@@ -514,15 +513,14 @@ static gboolean check_func(GSource *source) {
return TRUE;
else if (g->n_enabled_time_events) {
pa_time_event *t;
- GTimeVal now;
+ gint64 now;
struct timeval tvnow;
t = find_next_time_event(g);
g_assert(t);
- g_get_current_time(&now);
- tvnow.tv_sec = now.tv_sec;
- tvnow.tv_usec = now.tv_usec;
+ now = g_get_real_time();
+ pa_timeval_store(&tvnow, now);
if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0)
return TRUE;
@@ -558,16 +556,15 @@ static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer us
}
if (g->n_enabled_time_events) {
- GTimeVal now;
+ gint64 now;
struct timeval tvnow;
pa_time_event *t;
t = find_next_time_event(g);
g_assert(t);
- g_get_current_time(&now);
- tvnow.tv_sec = now.tv_sec;
- tvnow.tv_usec = now.tv_usec;
+ now = g_get_real_time();
+ pa_timeval_store(&tvnow, now);
if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) {
From 9055f5baf37e379d5e3b556bff1741565442723e Mon Sep 17 00:00:00 2001
From: Arun Raghavan
Date: Thu, 23 Jul 2020 19:42:47 -0400
Subject: [PATCH 003/865] stream-restore,device-restore: Avoid unaligned access
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Newer GCC warns us that the channel_map and volume in legacy entries are
accessed via pointers, and these might be unaligned as the legacy entry
is a packed structure. For this reason, we read out those values into
local variables before accessing them as pointers.
The warnings are:
[146/433] Compiling C object src/modules/module-device-restore.so.p/module-device-restore.c.o
../src/modules/module-device-restore.c: In function ‘legacy_entry_read’:
../src/modules/module-device-restore.c:554:51: warning: taking address of packed member of ‘struct legacy_entry’ may result in an unaligned pointer value [-Waddress-of-packed-member]
554 | if (le->volume_valid && !pa_channel_map_valid(&le->channel_map)) {
| ^~~~~~~~~~~~~~~~
../src/modules/module-device-restore.c:559:48: warning: taking address of packed member of ‘struct legacy_entry’ may result in an unaligned pointer value [-Waddress-of-packed-member]
559 | if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
| ^~~~~~~~~~~
../src/modules/module-device-restore.c:559:104: warning: taking address of packed member of ‘struct legacy_entry’ may result in an unaligned pointer value [-Waddress-of-packed-member]
559 | if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
| ^~~~~~~~~~~
../src/modules/module-device-restore.c:559:117: warning: taking address of packed member of ‘struct legacy_entry’ may result in an unaligned pointer value [-Waddress-of-packed-member]
559 | if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
| ^~~~~~~~~~~~~~~~
[211/433] Compiling C object src/modules/module-stream-restore.so.p/module-stream-restore.c.o
../src/modules/module-stream-restore.c: In function ‘legacy_entry_read’:
../src/modules/module-stream-restore.c:1076:51: warning: taking address of packed member of ‘struct legacy_entry’ may result in an unaligned pointer value [-Waddress-of-packed-member]
1076 | if (le->volume_valid && !pa_channel_map_valid(&le->channel_map)) {
| ^~~~~~~~~~~~~~~~
../src/modules/module-stream-restore.c:1081:48: warning: taking address of packed member of ‘struct legacy_entry’ may result in an unaligned pointer value [-Waddress-of-packed-member]
1081 | if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
| ^~~~~~~~~~~
../src/modules/module-stream-restore.c:1081:104: warning: taking address of packed member of ‘struct legacy_entry’ may result in an unaligned pointer value [-Waddress-of-packed-member]
1081 | if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
| ^~~~~~~~~~~
../src/modules/module-stream-restore.c:1081:117: warning: taking address of packed member of ‘struct legacy_entry’ may result in an unaligned pointer value [-Waddress-of-packed-member]
1081 | if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
|
---
src/modules/module-device-restore.c | 11 +++++++++--
src/modules/module-stream-restore.c | 11 +++++++++--
2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index d15d9ffa3..684c8360d 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -528,6 +528,8 @@ static bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry *
char port[PA_NAME_MAX];
} PA_GCC_PACKED;
struct legacy_entry *le;
+ pa_channel_map channel_map;
+ pa_cvolume volume;
pa_assert(u);
pa_assert(data);
@@ -551,12 +553,17 @@ static bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry *
return false;
}
- if (le->volume_valid && !pa_channel_map_valid(&le->channel_map)) {
+ /* Read these out before accessing contents via pointers as struct legacy_entry may not be adequately aligned for these
+ * members to be accessed directly */
+ channel_map = le->channel_map;
+ volume = le->volume;
+
+ if (le->volume_valid && !pa_channel_map_valid(&channel_map)) {
pa_log_warn("Invalid channel map.");
return false;
}
- if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
+ if (le->volume_valid && (!pa_cvolume_valid(&volume) || !pa_cvolume_compatible_with_channel_map(&volume, &channel_map))) {
pa_log_warn("Volume and channel map don't match.");
return false;
}
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 7144a664b..d26543bde 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -1029,6 +1029,8 @@ static struct entry *legacy_entry_read(struct userdata *u, const char *name) {
pa_datum data;
struct legacy_entry *le;
struct entry *e;
+ pa_channel_map channel_map;
+ pa_cvolume volume;
pa_assert(u);
pa_assert(name);
@@ -1073,12 +1075,17 @@ static struct entry *legacy_entry_read(struct userdata *u, const char *name) {
goto fail;
}
- if (le->volume_valid && !pa_channel_map_valid(&le->channel_map)) {
+ /* Read these out before accessing contents via pointers as struct legacy_entry may not be adequately aligned for these
+ * members to be accessed directly */
+ channel_map = le->channel_map;
+ volume = le->volume;
+
+ if (le->volume_valid && !pa_channel_map_valid(&channel_map)) {
pa_log_warn("Invalid channel map stored in database for legacy stream");
goto fail;
}
- if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
+ if (le->volume_valid && (!pa_cvolume_valid(&volume) || !pa_cvolume_compatible_with_channel_map(&volume, &channel_map))) {
pa_log_warn("Invalid volume stored in database for legacy stream");
goto fail;
}
From 093dd2f18180f38df0304946933661b116d314dc Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Sun, 11 Oct 2020 15:47:32 +0300
Subject: [PATCH 004/865] raop-sink: Don't set device.intended_roles=music
If there are RAOP devices in the network, it doesn't necessarily mean
that the user wants to play music to them.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/993
---
src/modules/raop/raop-sink.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
index 114f6d1e2..deba8d49a 100644
--- a/src/modules/raop/raop-sink.c
+++ b/src/modules/raop/raop-sink.c
@@ -826,7 +826,6 @@ pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver) {
pa_sink_new_data_set_channel_map(&data, &map);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server);
- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "music");
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "RAOP sink '%s'", server);
if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
From 757eb264485b11ca9163a260d8772c59ac3108a1 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Thu, 24 Sep 2020 12:07:07 +0300
Subject: [PATCH 005/865] mutex-posix: Log pthread_mutex_unlock() error
This call has been seen failing on FreeBSD, and it's difficult to debug
without seeing which error the function returned.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/988
---
src/pulsecore/mutex-posix.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index a835be1b7..41309a0a3 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -25,6 +25,8 @@
#include
#include
+
+#include
#include
#include "mutex.h"
@@ -103,9 +105,14 @@ bool pa_mutex_try_lock(pa_mutex *m) {
}
void pa_mutex_unlock(pa_mutex *m) {
+ int err;
+
pa_assert(m);
- pa_assert_se(pthread_mutex_unlock(&m->mutex) == 0);
+ if ((err = pthread_mutex_unlock(&m->mutex) != 0)) {
+ pa_log("pthread_mutex_unlock() failed: %s", pa_cstrerror(err));
+ pa_assert_not_reached();
+ }
}
pa_cond *pa_cond_new(void) {
From d5d08035913be89ad0dec99b4759ab9377bc1eea Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Tue, 22 Sep 2020 14:12:37 +0300
Subject: [PATCH 006/865] main: set LC_NUMERIC to C
The webrtc backend of module-echo-cancel uses sscanf() to parse floating
point numbers from module arguments, which didn't work when the locale
used a comma for the decimal point. Setting the LC_NUMERIC locale
variable to C makes the pulseaudio process use a period as the decimal
point regardless of the user's locale configuration.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/89
---
src/daemon/main.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 59f931219..c7c434288 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -480,7 +480,13 @@ int main(int argc, char *argv[]) {
pa_unblock_sigs(-1);
pa_reset_priority();
+ /* Load locale from the environment. */
setlocale(LC_ALL, "");
+
+ /* Set LC_NUMERIC to C so that floating point strings are consistently
+ * formatted and parsed across locales. */
+ setlocale(LC_NUMERIC, "C");
+
pa_init_i18n();
conf = pa_daemon_conf_new();
From a28f2e7293a055588f7bba568b5049ff91b3aad0 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Thu, 3 Sep 2020 14:28:29 +0300
Subject: [PATCH 007/865] alsa-mixer: Add support for the Center/LFE element
We already supported the CLFE element, which should be semantically
equivalent, so I just copied all the CLFE element definitions.
The Center/LFE element is seen on Creative X-Fi with 20K1 chipset cards.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/978
---
src/modules/alsa/mixer/paths/analog-output-headphones.conf | 4 ++++
src/modules/alsa/mixer/paths/analog-output-lineout.conf | 6 ++++++
.../alsa/mixer/paths/analog-output-speaker-always.conf | 6 ++++++
src/modules/alsa/mixer/paths/analog-output-speaker.conf | 6 ++++++
src/modules/alsa/mixer/paths/analog-output.conf | 6 ++++++
5 files changed, 28 insertions(+)
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
index d4ad7777d..14fb81481 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
@@ -160,4 +160,8 @@ volume = off
switch = off
volume = off
+[Element Speaker Center/LFE]
+switch = off
+volume = off
+
.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-output-lineout.conf b/src/modules/alsa/mixer/paths/analog-output-lineout.conf
index 9a6af3ad8..61df49965 100644
--- a/src/modules/alsa/mixer/paths/analog-output-lineout.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-lineout.conf
@@ -181,6 +181,12 @@ volume = merge
override-map.1 = all-center
override-map.2 = all-center,lfe
+[Element Center/LFE]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,lfe
+
[Element Bass Speaker]
switch = off
volume = off
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf b/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
index 71f356dce..bf781c671 100644
--- a/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
@@ -174,4 +174,10 @@ volume = merge
override-map.1 = all-center
override-map.2 = all-center,lfe
+[Element Center/LFE]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,lfe
+
.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker.conf b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
index 27a3983d5..1610b9a05 100644
--- a/src/modules/alsa/mixer/paths/analog-output-speaker.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
@@ -220,6 +220,12 @@ volume = merge
override-map.1 = all-center
override-map.2 = all-center,lfe
+[Element Center/LFE]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,lfe
+
[Element Speaker CLFE]
switch = mute
volume = merge
diff --git a/src/modules/alsa/mixer/paths/analog-output.conf b/src/modules/alsa/mixer/paths/analog-output.conf
index e6ba98358..0f6b5f5a0 100644
--- a/src/modules/alsa/mixer/paths/analog-output.conf
+++ b/src/modules/alsa/mixer/paths/analog-output.conf
@@ -79,4 +79,10 @@ volume = merge
override-map.1 = all-center
override-map.2 = all-center,lfe
+[Element Center/LFE]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,lfe
+
.include analog-output.conf.common
From 45abd0b43c7901b375afcba6d9b0e1cca2f349a8 Mon Sep 17 00:00:00 2001
From: Sanchayan Maity
Date: Wed, 2 Sep 2020 19:19:07 +0530
Subject: [PATCH 008/865] Add default.pa.d folder support
The .include meta command already supports specifying a directory and
when including a directory, all files with the extension '.pa' in that
directory will be parsed in alphabetical order.
This feature can be used to add support for default.pa.d directory, so
that packages for other applications or users can just drop in a file
for configuration without changing the default.pa which is shipped.
We use the PA_DEFAULT_CONFIG_DIR for this, however, since meson quotes
this build variable, introduce an unquoted version for this purpose and
use it with .include.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/909
Signed-off-by: Sanchayan Maity
---
configure.ac | 1 +
meson.build | 1 +
src/daemon/default.pa.in | 5 +++++
src/daemon/system.pa.in | 5 +++++
4 files changed, 12 insertions(+)
diff --git a/configure.ac b/configure.ac
index caabef6ed..6e6049fec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1394,6 +1394,7 @@ AC_DEFINE(PA_SOEXT, [".so"], [Shared object extension])
AC_SUBST(pulseconfdir, ["${sysconfdir}/pulse"])
AX_DEFINE_DIR(PA_DEFAULT_CONFIG_DIR, pulseconfdir, [Location of configuration files])
+AX_DEFINE_DIR(PA_DEFAULT_CONFIG_DIR_UNQUOTED, pulseconfdir, [Location of configuration files])
#### Mac OSX specific stuff #####
diff --git a/meson.build b/meson.build
index 1c671d32c..217ba0d85 100644
--- a/meson.build
+++ b/meson.build
@@ -136,6 +136,7 @@ cdata.set_quoted('PA_SRCDIR', join_paths(meson.current_source_dir(), 'src'))
cdata.set_quoted('PA_BUILDDIR', meson.current_build_dir())
cdata.set_quoted('PA_SOEXT', '.so')
cdata.set_quoted('PA_DEFAULT_CONFIG_DIR', pulsesysconfdir)
+cdata.set('PA_DEFAULT_CONFIG_DIR_UNQUOTED', pulsesysconfdir)
cdata.set_quoted('PA_BINARY', join_paths(bindir, 'pulseaudio'))
cdata.set_quoted('PA_SYSTEM_RUNTIME_PATH', join_paths(localstatedir, 'run', 'pulse'))
cdata.set_quoted('PA_SYSTEM_CONFIG_PATH', join_paths(localstatedir, 'lib', 'pulse'))
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index 030334f36..3468a89b9 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -173,3 +173,8 @@ load-module module-filter-apply
### Make some devices default
#set-default-sink output
#set-default-source input
+
+### Allow including a default.pa.d directory, which if present, can be used
+### for additional configuration snippets.
+.nofail
+.include @PA_DEFAULT_CONFIG_DIR_UNQUOTED@/default.pa.d
diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in
index 73e39ec93..1470e2368 100755
--- a/src/daemon/system.pa.in
+++ b/src/daemon/system.pa.in
@@ -60,3 +60,8 @@ load-module module-suspend-on-idle
### Enable positioned event sounds
load-module module-position-event-sounds
+
+### Allow including a system.pa.d directory, which if present, can be used
+### for additional configuration snippets.
+.nofail
+.include @PA_DEFAULT_CONFIG_DIR_UNQUOTED@/system.pa.d
From 69ba5a2b58ad6e98c8899aa297afd8276dbbe052 Mon Sep 17 00:00:00 2001
From: Nazar Mokrynskyi
Date: Sat, 21 Dec 2019 14:01:44 +0200
Subject: [PATCH 009/865] alsa-mixer: add mono input support for Behringer
UMC22
---
src/modules/alsa/90-pulseaudio.rules | 1 +
src/modules/alsa/alsa-mixer.c | 2 +
.../mixer/profile-sets/behringer-umc22.conf | 68 +++++++++++++++++++
3 files changed, 71 insertions(+)
create mode 100644 src/modules/alsa/mixer/profile-sets/behringer-umc22.conf
diff --git a/src/modules/alsa/90-pulseaudio.rules b/src/modules/alsa/90-pulseaudio.rules
index 7bfacda09..c81f05b51 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -110,6 +110,7 @@ ATTRS{idVendor}=="0763", ATTRS{idProduct}=="2012", ENV{PULSE_PROFILE_SET}="maudi
ATTRS{idVendor}=="045e", ATTRS{idProduct}=="02bb", ENV{PULSE_PROFILE_SET}="kinect-audio.conf"
ATTRS{idVendor}=="041e", ATTRS{idProduct}=="322c", ENV{PULSE_PROFILE_SET}="sb-omni-surround-5.1.conf"
ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="4014", ENV{PULSE_PROFILE_SET}="dell-dock-tb16-usb-audio.conf"
+ATTRS{idVendor}=="08bb", ATTRS{idProduct}=="2902", ENV{PULSE_PROFILE_SET}="behringer-umc22.conf"
# ID 1038:12ad is for the 2018 refresh of the Arctis 7.
# ID 1038:1294 is for Arctis Pro Wireless (which works with the Arctis 7 configuration).
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 063179052..a05ae7e08 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -4392,6 +4392,8 @@ static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
static const struct description_map well_known_descriptions[] = {
{ "analog-mono", N_("Analog Mono") },
+ { "analog-mono-left", N_("Analog Mono (Left)") },
+ { "analog-mono-right", N_("Analog Mono (Right)") },
{ "analog-stereo", N_("Analog Stereo") },
{ "mono-fallback", N_("Mono") },
{ "stereo-fallback", N_("Stereo") },
diff --git a/src/modules/alsa/mixer/profile-sets/behringer-umc22.conf b/src/modules/alsa/mixer/profile-sets/behringer-umc22.conf
new file mode 100644
index 000000000..cc74852c3
--- /dev/null
+++ b/src/modules/alsa/mixer/profile-sets/behringer-umc22.conf
@@ -0,0 +1,68 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see .
+
+; Behringer U-Phoria UMC22
+;
+; Default mapping only allows to use stereo input and sound card has two
+; physical input channels.
+;
+; However in case of only using a single input channel (like condenser
+; microphone) only one channel will have any sound, which is often
+; inconvenient for casual use.
+;
+; This config includes mono input options which makes it much more
+; friendly in single input configuration.
+;
+; This config also removes default digital input/output mappings that do
+; not physically exist on this card.
+;
+; Added by Nazar Mokrynskyi
+
+[General]
+auto-profiles = yes
+
+[Mapping analog-stereo-input]
+device-strings = hw:%f
+channel-map = left,right
+paths-input = analog-input-mic
+direction = input
+priority = 4
+
+[Mapping analog-mono]
+device-strings = hw:%f
+channel-map = mono,mono
+paths-input = analog-input-mic
+direction = input
+priority = 3
+
+[Mapping analog-mono-left]
+device-strings = hw:%f
+channel-map = mono,aux1
+paths-input = analog-input-mic
+direction = input
+priority = 2
+
+[Mapping analog-mono-right]
+device-strings = hw:%f
+channel-map = aux1,mono
+paths-input = analog-input-mic
+direction = input
+priority = 1
+
+[Mapping analog-stereo-output]
+device-strings = front:%f
+channel-map = left,right
+paths-output = analog-output
+direction = output
From 05c373d939506f31f96e58d8390b92e737e12afc Mon Sep 17 00:00:00 2001
From: Kai-Heng Feng
Date: Wed, 2 Sep 2020 00:23:54 +0800
Subject: [PATCH 010/865] alsa-mixer: Add support for HP Thunderbolt Dock
The HP Thunderbolt Dock [1] has two separate USB cards, a headset jack
and an optional module which is a speakerphone.
This patch adds new description for them, and mark the intended-roles as
phone for the speakerphone module.
[1] https://store.hp.com/us/en/pdp/hp-thunderbolt-dock-120w-g2-with-audio
---
src/Makefile.am | 4 ++-
src/modules/alsa/90-pulseaudio.rules | 2 ++
src/modules/alsa/alsa-mixer.c | 4 +++
.../profile-sets/hp-tbt-dock-120w-g2.conf | 30 ++++++++++++++++++
.../hp-tbt-dock-audio-module.conf | 31 +++++++++++++++++++
5 files changed, 70 insertions(+), 1 deletion(-)
create mode 100644 src/modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf
create mode 100644 src/modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf
diff --git a/src/Makefile.am b/src/Makefile.am
index bd764037b..478274cce 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1367,7 +1367,9 @@ dist_alsaprofilesets_DATA = \
modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf \
modules/alsa/mixer/profile-sets/usb-gaming-headset.conf \
modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf \
- modules/alsa/mixer/profile-sets/cmedia-high-speed-true-hdaudio.conf
+ modules/alsa/mixer/profile-sets/cmedia-high-speed-true-hdaudio.conf \
+ modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf \
+ modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf
if HAVE_UDEV
dist_udevrules_DATA = \
diff --git a/src/modules/alsa/90-pulseaudio.rules b/src/modules/alsa/90-pulseaudio.rules
index c81f05b51..83b257df6 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -111,6 +111,8 @@ ATTRS{idVendor}=="045e", ATTRS{idProduct}=="02bb", ENV{PULSE_PROFILE_SET}="kinec
ATTRS{idVendor}=="041e", ATTRS{idProduct}=="322c", ENV{PULSE_PROFILE_SET}="sb-omni-surround-5.1.conf"
ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="4014", ENV{PULSE_PROFILE_SET}="dell-dock-tb16-usb-audio.conf"
ATTRS{idVendor}=="08bb", ATTRS{idProduct}=="2902", ENV{PULSE_PROFILE_SET}="behringer-umc22.conf"
+ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="0269", ENV{PULSE_PROFILE_SET}="hp-tbt-dock-120w-g2.conf"
+ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="0567", ENV{PULSE_PROFILE_SET}="hp-tbt-dock-audio-module.conf"
# ID 1038:12ad is for the 2018 refresh of the Arctis 7.
# ID 1038:1294 is for Arctis Pro Wireless (which works with the Arctis 7 configuration).
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index a05ae7e08..937771d43 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -4404,6 +4404,8 @@ static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
* multichannel-input and multichannel-output. */
{ "analog-stereo-input", N_("Analog Stereo") },
{ "analog-stereo-output", N_("Analog Stereo") },
+ { "analog-stereo-headset", N_("Headset") },
+ { "analog-stereo-speakerphone", N_("Speakerphone") },
{ "multichannel-input", N_("Multichannel") },
{ "multichannel-output", N_("Multichannel") },
{ "analog-surround-21", N_("Analog Surround 2.1") },
@@ -4560,6 +4562,8 @@ static int profile_verify(pa_alsa_profile *p) {
static const struct description_map well_known_descriptions[] = {
{ "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
{ "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
+ { "output:analog-stereo-headset+input:analog-stereo-headset", N_("Headset") },
+ { "output:analog-stereo-speakerphone+input:analog-stereo-speakerphone", N_("Speakerphone") },
{ "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
{ "output:multichannel-output+input:multichannel-input", N_("Multichannel Duplex") },
{ "output:unknown-stereo+input:unknown-stereo", N_("Stereo Duplex") },
diff --git a/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf b/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf
new file mode 100644
index 000000000..3f652816b
--- /dev/null
+++ b/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf
@@ -0,0 +1,30 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see .
+
+; HP Thunderbolt Dock Audio Headset
+;
+; This card has one stereo input and one stereo output.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-headset]
+device-strings = hw:%f,0,0
+channel-map = left,right
+
+[Profile output:analog-stereo-headset+input:analog-stereo-headset]
+output-mappings = analog-stereo-headset
+input-mappings = analog-stereo-headset
+skip-probe = yes
diff --git a/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf b/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf
new file mode 100644
index 000000000..c03275cbe
--- /dev/null
+++ b/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf
@@ -0,0 +1,31 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see .
+
+; HP Thunderbolt Dock Audio Module
+;
+; This card has one stereo input and one stereo output.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-speakerphone]
+device-strings = hw:%f,0,0
+channel-map = left,right
+intended-roles = phone
+
+[Profile output:analog-stereo-speakerphone+input:analog-stereo-speakerphone]
+output-mappings = analog-stereo-speakerphone
+input-mappings = analog-stereo-speakerphone
+skip-probe = yes
From a73ec2a3f4f03829575761dc17546cf3226861c7 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Thu, 3 Sep 2020 13:33:15 +0300
Subject: [PATCH 011/865] alsa-mixer: Expand comments in the HP Thunderbolt
Dock configuration
---
.../alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf | 9 +++++++--
.../mixer/profile-sets/hp-tbt-dock-audio-module.conf | 7 ++++++-
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf b/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf
index 3f652816b..a683a4e4e 100644
--- a/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf
+++ b/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf
@@ -13,9 +13,14 @@
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see .
-; HP Thunderbolt Dock Audio Headset
+; HP Thunderbolt Dock 120W G2
;
-; This card has one stereo input and one stereo output.
+; This dock has a 3.5mm headset connector. Both input and output are stereo.
+;
+; There's a separate speakerphone module called "HP Thunderbolt Dock Audio
+; Module", which can be attached to this dock. The module will appear in ALSA
+; as a separate USB sound card, configuration for it is in
+; hp-tbt-dock-audio-module.conf.
[General]
auto-profiles = no
diff --git a/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf b/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf
index c03275cbe..692ab8dd0 100644
--- a/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf
+++ b/src/modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf
@@ -15,7 +15,12 @@
; HP Thunderbolt Dock Audio Module
;
-; This card has one stereo input and one stereo output.
+; This device attaches to the "HP Thunderbolt Dock 120W G2" dock. The audio
+; module provides a speakerphone with echo cancellation and appears in ALSA as
+; a USB sound card with stereo input and output.
+;
+; The dock itself has a 3.5mm headset connector and appears as a separate USB
+; sound card, configuration for it is in hp-tbt-dock-120w-g2.conf.
[General]
auto-profiles = no
From 5f3717f39cc3e373a3caaebd0d1625486fdd3b31 Mon Sep 17 00:00:00 2001
From: Arun Raghavan
Date: Mon, 23 Nov 2020 17:01:01 -0500
Subject: [PATCH 012/865] mutex-posix: Fix error assignment for unlock() check
This comment was missed in 757eb264485b11ca9163a260d8772c59ac3108a1.
---
src/pulsecore/mutex-posix.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index 41309a0a3..86f029c7c 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -109,7 +109,7 @@ void pa_mutex_unlock(pa_mutex *m) {
pa_assert(m);
- if ((err = pthread_mutex_unlock(&m->mutex) != 0)) {
+ if ((err = pthread_mutex_unlock(&m->mutex)) != 0) {
pa_log("pthread_mutex_unlock() failed: %s", pa_cstrerror(err));
pa_assert_not_reached();
}
From 00bd8e1ef8e131b05ca7b52b28e4daf0e1086555 Mon Sep 17 00:00:00 2001
From: Christopher Snowhill
Date: Thu, 23 Jan 2020 21:21:59 -0800
Subject: [PATCH 013/865] virtual-surround-sink: Use FFTW3 instead of naive
approach
This replaces the original virtual surround sink with a total
rewrite, aiming to implement any number of hrir use cases,
including asymmetrical impulses as two separate left and right
output files. It uses FFTW3 FFT convolution, using the overlap-
save method, with full rewind support. It operates in steps
equal to the resampled length of the hrir, and overlaps input
blocks in increments equal to the size of the FFT block. If
using paired hrirs, it requires matched sample spec and sample
rates and channel maps. For best results, the input files should
have speaker maps, rather than expecting the sample loader to
auto detect the mapping.
Part-of:
---
src/Makefile.am | 10 +-
src/modules/meson.build | 7 +-
src/modules/module-virtual-surround-sink.c | 877 ++++++++++++++-------
3 files changed, 603 insertions(+), 291 deletions(-)
diff --git a/src/Makefile.am b/src/Makefile.am
index 478274cce..d298d85d3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1268,7 +1268,6 @@ modlibexec_LTLIBRARIES += \
module-loopback.la \
module-virtual-sink.la \
module-virtual-source.la \
- module-virtual-surround-sink.la \
module-switch-on-connect.la \
module-switch-on-port-available.la \
module-filter-apply.la \
@@ -1530,6 +1529,11 @@ endif
endif
endif
+if HAVE_FFTW
+modlibexec_LTLIBRARIES += \
+ module-virtual-surround-sink.la
+endif
+
if HAVE_DBUS
if HAVE_FFTW
modlibexec_LTLIBRARIES += \
@@ -1775,9 +1779,9 @@ module_virtual_source_la_LDFLAGS = $(MODULE_LDFLAGS)
module_virtual_source_la_LIBADD = $(MODULE_LIBADD)
module_virtual_surround_sink_la_SOURCES = modules/module-virtual-surround-sink.c
-module_virtual_surround_sink_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) -DPA_MODULE_NAME=module_virtual_surround_sink
+module_virtual_surround_sink_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(FFTW_CFLAGS) -DPA_MODULE_NAME=module_virtual_surround_sink
module_virtual_surround_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_virtual_surround_sink_la_LIBADD = $(MODULE_LIBADD)
+module_virtual_surround_sink_la_LIBADD = $(MODULE_LIBADD) $(FFTW_LIBS)
# X11
diff --git a/src/modules/meson.build b/src/modules/meson.build
index 5f0437164..9a394c3b9 100644
--- a/src/modules/meson.build
+++ b/src/modules/meson.build
@@ -61,7 +61,6 @@ all_modules = [
[ 'module-tunnel-source-new', 'module-tunnel-source-new.c' ],
[ 'module-virtual-sink', 'module-virtual-sink.c' ],
[ 'module-virtual-source', 'module-virtual-source.c' ],
- [ 'module-virtual-surround-sink', 'module-virtual-surround-sink.c' ],
[ 'module-volume-restore', 'module-volume-restore.c' ],
# [ 'module-waveout', 'module-waveout.c' ],
]
@@ -137,6 +136,12 @@ if dbus_dep.found()
]
endif
+if fftw_dep.found()
+ all_modules += [
+ [ 'module-virtual-surround-sink', 'module-virtual-surround-sink.c', [], [], [fftw_dep, libm_dep] ],
+ ]
+endif
+
if dbus_dep.found() and fftw_dep.found()
all_modules += [
[ 'module-equalizer-sink', 'module-equalizer-sink.c', [], [], [dbus_dep, fftw_dep, libm_dep] ],
diff --git a/src/modules/module-virtual-surround-sink.c b/src/modules/module-virtual-surround-sink.c
index c32107388..0506370e8 100644
--- a/src/modules/module-virtual-surround-sink.c
+++ b/src/modules/module-virtual-surround-sink.c
@@ -4,6 +4,8 @@
Copyright 2010 Intel Corporation
Contributor: Pierre-Louis Bossart
Copyright 2012 Niels Ole Salscheider
+ Contributor: Alexander E. Patrakov
+ Copyright 2020 Christopher Snowhill
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -23,6 +25,10 @@
#include
#endif
+#include
+
+#include
+
#include
#include
@@ -39,9 +45,8 @@
#include
#include
-#include
-PA_MODULE_AUTHOR("Niels Ole Salscheider");
+PA_MODULE_AUTHOR("Christopher Snowhill");
PA_MODULE_DESCRIPTION(_("Virtual surround sink"));
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(false);
@@ -57,6 +62,8 @@ PA_MODULE_USAGE(
"use_volume_sharing= "
"force_flat_volume= "
"hrir=/path/to/left_hrir.wav "
+ "hrir_left=/path/to/left_hrir.wav "
+ "hrir_right=/path/to/optional/right_hrir.wav "
"autoloaded= "
));
@@ -66,32 +73,26 @@ PA_MODULE_USAGE(
struct userdata {
pa_module *module;
- /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */
- /* bool autoloaded; */
+ bool autoloaded;
pa_sink *sink;
pa_sink_input *sink_input;
- pa_memblockq *memblockq;
+ pa_memblockq *memblockq_sink;
bool auto_desc;
- unsigned channels;
- unsigned hrir_channels;
- unsigned fs, sink_fs;
+ size_t fftlen;
+ size_t hrir_samples;
+ size_t inputs;
- unsigned *mapping_left;
- unsigned *mapping_right;
-
- unsigned hrir_samples;
- float *hrir_data;
-
- float *input_buffer;
- int input_buffer_offset;
-
- bool autoloaded;
+ fftwf_plan *p_fw, p_bw;
+ fftwf_complex *f_in, *f_out, **f_ir;
+ float *revspace, *outspace[2], **inspace;
};
+#define BLOCK_SIZE (512)
+
static const char* const valid_modargs[] = {
"sink_name",
"sink_properties",
@@ -103,11 +104,157 @@ static const char* const valid_modargs[] = {
"channel_map",
"use_volume_sharing",
"force_flat_volume",
- "hrir",
"autoloaded",
+ "hrir",
+ "hrir_left",
+ "hrir_right",
NULL
};
+/* Vector size of 4 floats */
+#define v_size 4
+static void * alloc(size_t x, size_t s) {
+ size_t f;
+ float *t;
+
+ f = PA_ROUND_UP(x*s, sizeof(float)*v_size);
+ pa_assert_se(t = fftwf_malloc(f));
+ pa_memzero(t, f);
+
+ return t;
+}
+
+static size_t sink_input_samples(size_t nbytes)
+{
+ return nbytes / 8;
+}
+
+static size_t sink_input_bytes(size_t nsamples)
+{
+ return nsamples * 8;
+}
+
+static size_t sink_samples(const struct userdata *u, size_t nbytes)
+{
+ return nbytes / (u->inputs * 4);
+}
+
+static size_t sink_bytes(const struct userdata *u, size_t nsamples)
+{
+ return nsamples * (u->inputs * 4);
+}
+
+/* Mirror channels for symmetrical impulse */
+static pa_channel_position_t mirror_channel(pa_channel_position_t channel) {
+ switch (channel) {
+ case PA_CHANNEL_POSITION_FRONT_LEFT:
+ return PA_CHANNEL_POSITION_FRONT_RIGHT;
+
+ case PA_CHANNEL_POSITION_FRONT_RIGHT:
+ return PA_CHANNEL_POSITION_FRONT_LEFT;
+
+ case PA_CHANNEL_POSITION_REAR_LEFT:
+ return PA_CHANNEL_POSITION_REAR_RIGHT;
+
+ case PA_CHANNEL_POSITION_REAR_RIGHT:
+ return PA_CHANNEL_POSITION_REAR_LEFT;
+
+ case PA_CHANNEL_POSITION_SIDE_LEFT:
+ return PA_CHANNEL_POSITION_SIDE_RIGHT;
+
+ case PA_CHANNEL_POSITION_SIDE_RIGHT:
+ return PA_CHANNEL_POSITION_SIDE_LEFT;
+
+ case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+ return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+
+ case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+ return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+
+ case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
+ return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
+
+ case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
+ return PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
+
+ case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
+ return PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+
+ case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
+ return PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+
+ default:
+ return channel;
+ }
+}
+
+/* Normalize the hrir */
+static void normalize_hrir(float * hrir_data, unsigned hrir_samples, unsigned hrir_channels) {
+ /* normalize hrir to avoid audible clipping
+ *
+ * The following heuristic tries to avoid audible clipping. It cannot avoid
+ * clipping in the worst case though, because the scaling factor would
+ * become too large resulting in a too quiet signal.
+ * The idea of the heuristic is to avoid clipping when a single click is
+ * played back on all channels. The scaling factor describes the additional
+ * factor that is necessary to avoid clipping for "normal" signals.
+ *
+ * This algorithm doesn't pretend to be perfect, it's just something that
+ * appears to work (not too quiet, no audible clipping) on the material that
+ * it has been tested on. If you find a real-world example where this
+ * algorithm results in audible clipping, please write a patch that adjusts
+ * the scaling factor constants or improves the algorithm (or if you can't
+ * write a patch, at least report the problem to the PulseAudio mailing list
+ * or bug tracker). */
+
+ const float scaling_factor = 2.5;
+
+ float hrir_sum, hrir_max;
+ unsigned i, j;
+
+ hrir_max = 0;
+ for (i = 0; i < hrir_samples; i++) {
+ hrir_sum = 0;
+ for (j = 0; j < hrir_channels; j++)
+ hrir_sum += fabs(hrir_data[i * hrir_channels + j]);
+
+ if (hrir_sum > hrir_max)
+ hrir_max = hrir_sum;
+ }
+
+ for (i = 0; i < hrir_samples; i++) {
+ for (j = 0; j < hrir_channels; j++)
+ hrir_data[i * hrir_channels + j] /= hrir_max * scaling_factor;
+ }
+}
+
+/* Normalize a stereo hrir */
+static void normalize_hrir_stereo(float * hrir_data, float * hrir_right_data, unsigned hrir_samples, unsigned hrir_channels) {
+ const float scaling_factor = 2.5;
+
+ float hrir_sum, hrir_max;
+ unsigned i, j;
+
+ hrir_max = 0;
+ for (i = 0; i < hrir_samples; i++) {
+ hrir_sum = 0;
+ for (j = 0; j < hrir_channels; j++) {
+ hrir_sum += fabs(hrir_data[i * hrir_channels + j]);
+ hrir_sum += fabs(hrir_right_data[i * hrir_channels + j]);
+ }
+
+ if (hrir_sum > hrir_max)
+ hrir_max = hrir_sum;
+ }
+
+ for (i = 0; i < hrir_samples; i++) {
+ for (j = 0; j < hrir_channels; j++) {
+ hrir_data[i * hrir_channels + j] /= hrir_max * scaling_factor;
+ hrir_right_data[i * hrir_channels + j] /= hrir_max * scaling_factor;
+ }
+ }
+}
+
/* Called from I/O thread context */
static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
@@ -121,11 +268,11 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
* sink input is first shut down, the sink second. */
if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
!PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
- *((int64_t*) data) = 0;
+ *((pa_usec_t*) data) = 0;
return 0;
}
- *((int64_t*) data) =
+ *((pa_usec_t*) data) =
/* Get the latency of the master sink */
pa_sink_get_latency_within_thread(u->sink_input->sink, true) +
@@ -174,6 +321,7 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
/* Called from I/O thread context */
static void sink_request_rewind_cb(pa_sink *s) {
struct userdata *u;
+ size_t nbytes_sink, nbytes_input;
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
@@ -182,10 +330,11 @@ static void sink_request_rewind_cb(pa_sink *s) {
!PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
return;
+ nbytes_sink = s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq_sink);
+ nbytes_input = sink_input_bytes(sink_samples(u, nbytes_sink));
+
/* Just hand this one over to the master sink */
- pa_sink_input_request_rewind(u->sink_input,
- s->thread_info.rewind_nbytes +
- pa_memblockq_get_length(u->memblockq), true, false, false);
+ pa_sink_input_request_rewind(u->sink_input, nbytes_input, true, false, false);
}
/* Called from I/O thread context */
@@ -233,136 +382,177 @@ static void sink_set_mute_cb(pa_sink *s) {
pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
}
+static size_t memblockq_missing(pa_memblockq *bq) {
+ size_t l, tlength;
+ pa_assert(bq);
+
+ tlength = pa_memblockq_get_tlength(bq);
+ if ((l = pa_memblockq_get_length(bq)) >= tlength)
+ return 0;
+
+ l = tlength - l;
+ return l >= pa_memblockq_get_minreq(bq) ? l : 0;
+}
+
/* Called from I/O thread context */
-static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes_input, pa_memchunk *chunk) {
struct userdata *u;
float *src, *dst;
- unsigned n;
+ int c, ear;
+ size_t s, bytes_missing, fftlen;
pa_memchunk tchunk;
-
- unsigned j, k, l;
- float sum_right, sum_left;
- float current_sample;
+ float fftlen_if, *revspace;
pa_sink_input_assert_ref(i);
pa_assert(chunk);
pa_assert_se(u = i->userdata);
- if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return -1;
-
/* Hmm, process any rewind request that might be queued up */
pa_sink_process_rewind(u->sink, 0);
- while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
+ while ((bytes_missing = memblockq_missing(u->memblockq_sink)) != 0) {
pa_memchunk nchunk;
- pa_sink_render(u->sink, nbytes * u->sink_fs / u->fs, &nchunk);
- pa_memblockq_push(u->memblockq, &nchunk);
+ pa_sink_render(u->sink, bytes_missing, &nchunk);
+ pa_memblockq_push(u->memblockq_sink, &nchunk);
pa_memblock_unref(nchunk.memblock);
}
- tchunk.length = PA_MIN(nbytes * u->sink_fs / u->fs, tchunk.length);
- pa_assert(tchunk.length > 0);
+ pa_memblockq_rewind(u->memblockq_sink, sink_bytes(u, u->fftlen - BLOCK_SIZE));
+ pa_memblockq_peek_fixed_size(u->memblockq_sink, sink_bytes(u, u->fftlen), &tchunk);
- n = (unsigned) (tchunk.length / u->sink_fs);
+ pa_memblockq_drop(u->memblockq_sink, tchunk.length);
- pa_assert(n > 0);
+ /* Now tchunk contains enough data to perform the FFT
+ * This should be equal to u->fftlen */
chunk->index = 0;
- chunk->length = n * u->fs;
+ chunk->length = sink_input_bytes(BLOCK_SIZE);
chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
- pa_memblockq_drop(u->memblockq, n * u->sink_fs);
-
src = pa_memblock_acquire_chunk(&tchunk);
- dst = pa_memblock_acquire(chunk->memblock);
- for (l = 0; l < n; l++) {
- memcpy(((char*) u->input_buffer) + u->input_buffer_offset * u->sink_fs, ((char *) src) + l * u->sink_fs, u->sink_fs);
-
- sum_right = 0;
- sum_left = 0;
-
- /* fold the input buffer with the impulse response */
- for (j = 0; j < u->hrir_samples; j++) {
- for (k = 0; k < u->channels; k++) {
- current_sample = u->input_buffer[((u->input_buffer_offset + j) % u->hrir_samples) * u->channels + k];
-
- sum_left += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_left[k]];
- sum_right += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_right[k]];
- }
+ for (c = 0; c < u->inputs; c++) {
+ for (s = 0, fftlen = u->fftlen; s < fftlen; s++) {
+ u->inspace[c][s] = src[s * u->inputs + c];
}
-
- dst[2 * l] = PA_CLAMP_UNLIKELY(sum_left, -1.0f, 1.0f);
- dst[2 * l + 1] = PA_CLAMP_UNLIKELY(sum_right, -1.0f, 1.0f);
-
- u->input_buffer_offset--;
- if (u->input_buffer_offset < 0)
- u->input_buffer_offset += u->hrir_samples;
}
pa_memblock_release(tchunk.memblock);
- pa_memblock_release(chunk->memblock);
-
pa_memblock_unref(tchunk.memblock);
+ fftlen_if = 1.0f / (float)u->fftlen;
+ revspace = u->revspace + u->fftlen - BLOCK_SIZE;
+
+ pa_memzero(u->outspace[0], BLOCK_SIZE * 4);
+ pa_memzero(u->outspace[1], BLOCK_SIZE * 4);
+
+ for (c = 0; c < u->inputs; c++) {
+ fftwf_complex *f_in = u->f_in;
+ fftwf_complex *f_out = u->f_out;
+
+ fftwf_execute(u->p_fw[c]);
+
+ for (ear = 0; ear < 2; ear++) {
+ fftwf_complex *f_ir = u->f_ir[c * 2 + ear];
+ float *outspace = u->outspace[ear];
+
+ for (s = 0, fftlen = u->fftlen / 2 + 1; s < fftlen; s++) {
+ float re = f_ir[s][0] * f_in[s][0] - f_ir[s][1] * f_in[s][1];
+ float im = f_ir[s][1] * f_in[s][0] + f_ir[s][0] * f_in[s][1];
+ f_out[s][0] = re;
+ f_out[s][1] = im;
+ }
+
+ fftwf_execute(u->p_bw);
+
+ for (s = 0, fftlen = BLOCK_SIZE; s < fftlen; ++s)
+ outspace[s] += revspace[s] * fftlen_if;
+ }
+ }
+
+ dst = pa_memblock_acquire_chunk(chunk);
+
+ for (s = 0, fftlen = BLOCK_SIZE; s < fftlen; s++) {
+ float output;
+ float *outspace = u->outspace[0];
+
+ output = outspace[s];
+ if (output < -1.0) output = -1.0;
+ if (output > 1.0) output = 1.0;
+ dst[s * 2 + 0] = output;
+
+ outspace = u->outspace[1];
+
+ output = outspace[s];
+ if (output < -1.0) output = -1.0;
+ if (output > 1.0) output = 1.0;
+ dst[s * 2 + 1] = output;
+ }
+
+ pa_memblock_release(chunk->memblock);
+
return 0;
}
/* Called from I/O thread context */
-static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes_input) {
struct userdata *u;
size_t amount = 0;
+ size_t nbytes_sink;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- /* If the sink is not yet linked, there is nothing to rewind */
- if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
- return;
+ nbytes_sink = sink_bytes(u, sink_input_samples(nbytes_input));
if (u->sink->thread_info.rewind_nbytes > 0) {
size_t max_rewrite;
- max_rewrite = nbytes * u->sink_fs / u->fs + pa_memblockq_get_length(u->memblockq);
- amount = PA_MIN(u->sink->thread_info.rewind_nbytes * u->sink_fs / u->fs, max_rewrite);
+ max_rewrite = nbytes_sink + pa_memblockq_get_length(u->memblockq_sink);
+ amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
u->sink->thread_info.rewind_nbytes = 0;
if (amount > 0) {
- pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, true);
-
- /* Reset the input buffer */
- memset(u->input_buffer, 0, u->hrir_samples * u->sink_fs);
- u->input_buffer_offset = 0;
+ pa_memblockq_seek(u->memblockq_sink, - (int64_t) amount, PA_SEEK_RELATIVE, true);
}
}
pa_sink_process_rewind(u->sink, amount);
- pa_memblockq_rewind(u->memblockq, nbytes * u->sink_fs / u->fs);
+
+ pa_memblockq_rewind(u->memblockq_sink, nbytes_sink);
}
/* Called from I/O thread context */
-static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes_input) {
struct userdata *u;
+ size_t nbytes_sink, nbytes_memblockq;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
+ nbytes_sink = sink_bytes(u, sink_input_samples(nbytes_input));
+ nbytes_memblockq = sink_bytes(u, sink_input_samples(nbytes_input) + u->fftlen);
+
/* FIXME: Too small max_rewind:
* https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
- pa_memblockq_set_maxrewind(u->memblockq, nbytes * u->sink_fs / u->fs);
- pa_sink_set_max_rewind_within_thread(u->sink, nbytes * u->sink_fs / u->fs);
+ pa_memblockq_set_maxrewind(u->memblockq_sink, nbytes_memblockq);
+ pa_sink_set_max_rewind_within_thread(u->sink, nbytes_sink);
}
/* Called from I/O thread context */
-static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes_input) {
struct userdata *u;
+ size_t nbytes_sink;
+
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- pa_sink_set_max_request_within_thread(u->sink, nbytes * u->sink_fs / u->fs);
+ nbytes_sink = sink_bytes(u, sink_input_samples(nbytes_input));
+
+ nbytes_sink = PA_ROUND_UP(nbytes_sink, sink_bytes(u, BLOCK_SIZE));
+ pa_sink_set_max_request_within_thread(u->sink, nbytes_sink);
}
/* Called from I/O thread context */
@@ -401,6 +591,7 @@ static void sink_input_detach_cb(pa_sink_input *i) {
/* Called from I/O thread context */
static void sink_input_attach_cb(pa_sink_input *i) {
struct userdata *u;
+ size_t max_request;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
@@ -410,14 +601,15 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
- pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i) * u->sink_fs / u->fs);
+ max_request = sink_bytes(u, sink_input_samples(pa_sink_input_get_max_request(i)));
+ max_request = PA_ROUND_UP(max_request, sink_bytes(u, BLOCK_SIZE));
+ pa_sink_set_max_request_within_thread(u->sink, max_request);
/* FIXME: Too small max_rewind:
* https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
- pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i) * u->sink_fs / u->fs);
+ pa_sink_set_max_rewind_within_thread(u->sink, sink_bytes(u, sink_input_samples(pa_sink_input_get_max_rewind(i))));
- if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
- pa_sink_attach_within_thread(u->sink);
+ pa_sink_attach_within_thread(u->sink);
}
/* Called from main context */
@@ -427,12 +619,12 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- /* The order here matters! We first kill the sink so that streams
- * can properly be moved away while the sink input is still connected
- * to the master. */
+ /* The order here matters! We first kill the sink input, followed
+ * by the sink. That means the sink callbacks must be protected
+ * against an unconnected sink input! */
pa_sink_input_cork(u->sink_input, true);
- pa_sink_unlink(u->sink);
pa_sink_input_unlink(u->sink_input);
+ pa_sink_unlink(u->sink);
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
@@ -503,119 +695,57 @@ static void sink_input_mute_changed_cb(pa_sink_input *i) {
pa_sink_mute_changed(u->sink, i->muted);
}
-static pa_channel_position_t mirror_channel(pa_channel_position_t channel) {
- switch (channel) {
- case PA_CHANNEL_POSITION_FRONT_LEFT:
- return PA_CHANNEL_POSITION_FRONT_RIGHT;
-
- case PA_CHANNEL_POSITION_FRONT_RIGHT:
- return PA_CHANNEL_POSITION_FRONT_LEFT;
-
- case PA_CHANNEL_POSITION_REAR_LEFT:
- return PA_CHANNEL_POSITION_REAR_RIGHT;
-
- case PA_CHANNEL_POSITION_REAR_RIGHT:
- return PA_CHANNEL_POSITION_REAR_LEFT;
-
- case PA_CHANNEL_POSITION_SIDE_LEFT:
- return PA_CHANNEL_POSITION_SIDE_RIGHT;
-
- case PA_CHANNEL_POSITION_SIDE_RIGHT:
- return PA_CHANNEL_POSITION_SIDE_LEFT;
-
- case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
- return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
-
- case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
- return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
-
- case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
- return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
-
- case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
- return PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
-
- case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
- return PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
-
- case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
- return PA_CHANNEL_POSITION_TOP_REAR_LEFT;
-
- default:
- return channel;
- }
-}
-
-static void normalize_hrir(struct userdata *u) {
- /* normalize hrir to avoid audible clipping
- *
- * The following heuristic tries to avoid audible clipping. It cannot avoid
- * clipping in the worst case though, because the scaling factor would
- * become too large resulting in a too quiet signal.
- * The idea of the heuristic is to avoid clipping when a single click is
- * played back on all channels. The scaling factor describes the additional
- * factor that is necessary to avoid clipping for "normal" signals.
- *
- * This algorithm doesn't pretend to be perfect, it's just something that
- * appears to work (not too quiet, no audible clipping) on the material that
- * it has been tested on. If you find a real-world example where this
- * algorithm results in audible clipping, please write a patch that adjusts
- * the scaling factor constants or improves the algorithm (or if you can't
- * write a patch, at least report the problem to the PulseAudio mailing list
- * or bug tracker). */
-
- const float scaling_factor = 2.5;
-
- float hrir_sum, hrir_max;
- unsigned i, j;
-
- hrir_max = 0;
- for (i = 0; i < u->hrir_samples; i++) {
- hrir_sum = 0;
- for (j = 0; j < u->hrir_channels; j++)
- hrir_sum += fabs(u->hrir_data[i * u->hrir_channels + j]);
-
- if (hrir_sum > hrir_max)
- hrir_max = hrir_sum;
- }
-
- for (i = 0; i < u->hrir_samples; i++) {
- for (j = 0; j < u->hrir_channels; j++)
- u->hrir_data[i * u->hrir_channels + j] /= hrir_max * scaling_factor;
- }
-}
-
int pa__init(pa_module*m) {
struct userdata *u;
- pa_sample_spec ss, sink_input_ss;
- pa_channel_map map, sink_input_map;
+ pa_sample_spec ss_input, ss_output;
+ pa_channel_map map_output;
pa_modargs *ma;
const char *master_name;
- pa_sink *master = NULL;
+ const char *hrir_left_file;
+ const char *hrir_right_file;
+ pa_sink *master=NULL;
pa_sink_input_new_data sink_input_data;
pa_sink_new_data sink_data;
bool use_volume_sharing = true;
bool force_flat_volume = false;
pa_memchunk silence;
+ const char* z;
+ unsigned i, j, ear, found_channel_left, found_channel_right;
- const char *hrir_file;
- unsigned i, j, found_channel_left, found_channel_right;
- float *hrir_data;
+ pa_sample_spec ss;
+ pa_channel_map map;
- pa_sample_spec hrir_ss;
- pa_channel_map hrir_map;
+ float *hrir_data=NULL, *hrir_right_data=NULL;
+ float *hrir_temp_data;
+ size_t hrir_samples;
+ size_t hrir_copied_length, hrir_total_length;
+ int hrir_channels;
+ int fftlen;
- pa_sample_spec hrir_temp_ss;
- pa_memchunk hrir_temp_chunk, hrir_temp_chunk_resampled;
+ float *impulse_temp=NULL;
+
+ unsigned *mapping_left=NULL;
+ unsigned *mapping_right=NULL;
+
+ fftwf_plan p;
+
+ pa_channel_map hrir_map, hrir_right_map;
+
+ pa_sample_spec hrir_left_temp_ss;
+ pa_memchunk hrir_left_temp_chunk, hrir_left_temp_chunk_resampled;
pa_resampler *resampler;
- size_t hrir_copied_length, hrir_total_length;
- hrir_temp_chunk.memblock = NULL;
- hrir_temp_chunk_resampled.memblock = NULL;
+ pa_sample_spec hrir_right_temp_ss;
+ pa_memchunk hrir_right_temp_chunk, hrir_right_temp_chunk_resampled;
pa_assert(m);
+ hrir_left_temp_chunk.memblock = NULL;
+ hrir_left_temp_chunk_resampled.memblock = NULL;
+ hrir_right_temp_chunk.memblock = NULL;
+ hrir_right_temp_chunk_resampled.memblock = NULL;
+
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
@@ -629,45 +759,61 @@ int pa__init(pa_module*m) {
"please use the 'sink_master' argument instead.");
}
- master = pa_namereg_get(m->core, master_name, PA_NAMEREG_SINK);
- if (!master) {
- pa_log("Master sink not found.");
+ if (!(master = pa_namereg_get(m->core, master_name, PA_NAMEREG_SINK))) {
+ pa_log("Master sink not found");
goto fail;
}
+ hrir_left_file = pa_modargs_get_value(ma, "hrir_left", NULL);
+ if (!hrir_left_file) {
+ hrir_left_file = pa_modargs_get_value(ma, "hrir", NULL);
+ if (!hrir_left_file) {
+ pa_log("Either the 'hrir' or 'hrir_left' module arguments are required.");
+ goto fail;
+ }
+ }
+
+ hrir_right_file = pa_modargs_get_value(ma, "hrir_right", NULL);
+
pa_assert(master);
- u = pa_xnew0(struct userdata, 1);
- u->module = m;
- m->userdata = u;
-
- /* Initialize hrir and input buffer */
- /* this is the hrir file for the left ear! */
- if (!(hrir_file = pa_modargs_get_value(ma, "hrir", NULL))) {
- pa_log("The mandatory 'hrir' module argument is missing.");
- goto fail;
- }
-
- if (pa_sound_file_load(master->core->mempool, hrir_file, &hrir_temp_ss, &hrir_map, &hrir_temp_chunk, NULL) < 0) {
+ if (pa_sound_file_load(master->core->mempool, hrir_left_file, &hrir_left_temp_ss, &hrir_map, &hrir_left_temp_chunk, NULL) < 0) {
pa_log("Cannot load hrir file.");
goto fail;
}
- /* sample spec / map of hrir */
- hrir_ss.format = PA_SAMPLE_FLOAT32;
- hrir_ss.rate = master->sample_spec.rate;
- hrir_ss.channels = hrir_temp_ss.channels;
+ if (hrir_right_file) {
+ if (pa_sound_file_load(master->core->mempool, hrir_right_file, &hrir_right_temp_ss, &hrir_right_map, &hrir_right_temp_chunk, NULL) < 0) {
+ pa_log("Cannot load hrir_right file.");
+ goto fail;
+ }
+ if (!pa_sample_spec_equal(&hrir_left_temp_ss, &hrir_right_temp_ss)) {
+ pa_log("Both hrir_left and hrir_right must have the same sample format");
+ goto fail;
+ }
+ if (!pa_channel_map_equal(&hrir_map, &hrir_right_map)) {
+ pa_log("Both hrir_left and hrir_right must have the same channel layout");
+ goto fail;
+ }
+ }
- /* sample spec of sink */
- ss = hrir_ss;
+ ss_input.format = PA_SAMPLE_FLOAT32NE;
+ ss_input.rate = master->sample_spec.rate;
+ ss_input.channels = hrir_left_temp_ss.channels;
+
+ ss = ss_input;
map = hrir_map;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
pa_log("Invalid sample format specification or channel map");
goto fail;
}
- ss.format = PA_SAMPLE_FLOAT32;
- hrir_ss.rate = ss.rate;
- u->channels = ss.channels;
+
+ ss.format = PA_SAMPLE_FLOAT32NE;
+ ss_input.rate = ss.rate;
+ ss_input.channels = ss.channels;
+
+ ss_output = ss_input;
+ ss_output.channels = 2;
if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
pa_log("use_volume_sharing= expects a boolean argument");
@@ -684,14 +830,11 @@ int pa__init(pa_module*m) {
goto fail;
}
- /* sample spec / map of sink input */
- pa_channel_map_init_stereo(&sink_input_map);
- sink_input_ss.channels = 2;
- sink_input_ss.format = PA_SAMPLE_FLOAT32;
- sink_input_ss.rate = ss.rate;
+ pa_channel_map_init_stereo(&map_output);
- u->sink_fs = pa_frame_size(&ss);
- u->fs = pa_frame_size(&sink_input_ss);
+ u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+ m->userdata = u;
/* Create sink */
pa_sink_new_data_init(&sink_data);
@@ -699,7 +842,7 @@ int pa__init(pa_module*m) {
sink_data.module = m;
if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
sink_data.name = pa_sprintf_malloc("%s.vsurroundsink", master->name);
- pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+ pa_sink_new_data_set_sample_spec(&sink_data, &ss_input);
pa_sink_new_data_set_channel_map(&sink_data, &map);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
@@ -718,8 +861,6 @@ int pa__init(pa_module*m) {
}
if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
- const char *z;
-
z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Surround Sink %s on %s", sink_data.name, z ? z : master->name);
}
@@ -743,7 +884,7 @@ int pa__init(pa_module*m) {
pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
pa_sink_enable_decibel_volume(u->sink, true);
}
- /* Normally this flag would be enabled automatically be we can force it. */
+ /* Normally this flag would be enabled automatically but we can force it. */
if (force_flat_volume)
u->sink->flags |= PA_SINK_FLAT_VOLUME;
u->sink->userdata = u;
@@ -758,9 +899,8 @@ int pa__init(pa_module*m) {
sink_input_data.origin_sink = u->sink;
pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Surround Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
- pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_input_ss);
- pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_input_map);
- sink_input_data.flags |= PA_SINK_INPUT_START_CORKED;
+ pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss_output);
+ pa_sink_input_new_data_set_channel_map(&sink_input_data, &map_output);
pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
pa_sink_input_new_data_done(&sink_input_data);
@@ -786,78 +926,107 @@ int pa__init(pa_module*m) {
u->sink->input_to_master = u->sink_input;
pa_sink_input_get_silence(u->sink_input, &silence);
- u->memblockq = pa_memblockq_new("module-virtual-surround-sink memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &silence);
- pa_memblock_unref(silence.memblock);
- /* resample hrir */
- resampler = pa_resampler_new(u->sink->core->mempool, &hrir_temp_ss, &hrir_map, &hrir_ss, &hrir_map, u->sink->core->lfe_crossover_freq,
+ resampler = pa_resampler_new(u->sink->core->mempool, &hrir_left_temp_ss, &hrir_map, &ss_input, &hrir_map, u->sink->core->lfe_crossover_freq,
PA_RESAMPLER_SRC_SINC_BEST_QUALITY, PA_RESAMPLER_NO_REMAP);
- u->hrir_samples = hrir_temp_chunk.length / pa_frame_size(&hrir_temp_ss) * hrir_ss.rate / hrir_temp_ss.rate;
- if (u->hrir_samples > 64) {
- u->hrir_samples = 64;
- pa_log("The (resampled) hrir contains more than 64 samples. Only the first 64 samples will be used to limit processor usage.");
- }
+ hrir_samples = hrir_left_temp_chunk.length / pa_frame_size(&hrir_left_temp_ss) * ss_input.rate / hrir_left_temp_ss.rate;
- hrir_total_length = u->hrir_samples * pa_frame_size(&hrir_ss);
- u->hrir_channels = hrir_ss.channels;
+ hrir_total_length = hrir_samples * pa_frame_size(&ss_input);
+ hrir_channels = ss_input.channels;
- u->hrir_data = (float *) pa_xmalloc(hrir_total_length);
+ hrir_data = (float *) pa_xmalloc(hrir_total_length);
hrir_copied_length = 0;
+ u->hrir_samples = hrir_samples;
+ u->inputs = hrir_channels;
+
/* add silence to the hrir until we get enough samples out of the resampler */
while (hrir_copied_length < hrir_total_length) {
- pa_resampler_run(resampler, &hrir_temp_chunk, &hrir_temp_chunk_resampled);
- if (hrir_temp_chunk.memblock != hrir_temp_chunk_resampled.memblock) {
+ pa_resampler_run(resampler, &hrir_left_temp_chunk, &hrir_left_temp_chunk_resampled);
+ if (hrir_left_temp_chunk.memblock != hrir_left_temp_chunk_resampled.memblock) {
/* Silence input block */
- pa_silence_memblock(hrir_temp_chunk.memblock, &hrir_temp_ss);
+ pa_silence_memblock(hrir_left_temp_chunk.memblock, &hrir_left_temp_ss);
}
- if (hrir_temp_chunk_resampled.memblock) {
+ if (hrir_left_temp_chunk_resampled.memblock) {
/* Copy hrir data */
- hrir_data = (float *) pa_memblock_acquire(hrir_temp_chunk_resampled.memblock);
+ hrir_temp_data = (float *) pa_memblock_acquire(hrir_left_temp_chunk_resampled.memblock);
- if (hrir_total_length - hrir_copied_length >= hrir_temp_chunk_resampled.length) {
- memcpy(u->hrir_data + hrir_copied_length, hrir_data, hrir_temp_chunk_resampled.length);
- hrir_copied_length += hrir_temp_chunk_resampled.length;
+ if (hrir_total_length - hrir_copied_length >= hrir_left_temp_chunk_resampled.length) {
+ memcpy(hrir_data + hrir_copied_length, hrir_temp_data, hrir_left_temp_chunk_resampled.length);
+ hrir_copied_length += hrir_left_temp_chunk_resampled.length;
} else {
- memcpy(u->hrir_data + hrir_copied_length, hrir_data, hrir_total_length - hrir_copied_length);
+ memcpy(hrir_data + hrir_copied_length, hrir_temp_data, hrir_total_length - hrir_copied_length);
hrir_copied_length = hrir_total_length;
}
- pa_memblock_release(hrir_temp_chunk_resampled.memblock);
- pa_memblock_unref(hrir_temp_chunk_resampled.memblock);
- hrir_temp_chunk_resampled.memblock = NULL;
+ pa_memblock_release(hrir_left_temp_chunk_resampled.memblock);
+ pa_memblock_unref(hrir_left_temp_chunk_resampled.memblock);
+ hrir_left_temp_chunk_resampled.memblock = NULL;
}
}
+ pa_memblock_unref(hrir_left_temp_chunk.memblock);
+ hrir_left_temp_chunk.memblock = NULL;
+
+ if (hrir_right_file) {
+ pa_resampler_reset(resampler);
+
+ hrir_right_data = (float *) pa_xmalloc(hrir_total_length);
+ hrir_copied_length = 0;
+
+ while (hrir_copied_length < hrir_total_length) {
+ pa_resampler_run(resampler, &hrir_right_temp_chunk, &hrir_right_temp_chunk_resampled);
+ if (hrir_right_temp_chunk.memblock != hrir_right_temp_chunk_resampled.memblock) {
+ /* Silence input block */
+ pa_silence_memblock(hrir_right_temp_chunk.memblock, &hrir_right_temp_ss);
+ }
+
+ if (hrir_right_temp_chunk_resampled.memblock) {
+ /* Copy hrir data */
+ hrir_temp_data = (float *) pa_memblock_acquire(hrir_right_temp_chunk_resampled.memblock);
+
+ if (hrir_total_length - hrir_copied_length >= hrir_right_temp_chunk_resampled.length) {
+ memcpy(hrir_right_data + hrir_copied_length, hrir_temp_data, hrir_right_temp_chunk_resampled.length);
+ hrir_copied_length += hrir_right_temp_chunk_resampled.length;
+ } else {
+ memcpy(hrir_right_data + hrir_copied_length, hrir_temp_data, hrir_total_length - hrir_copied_length);
+ hrir_copied_length = hrir_total_length;
+ }
+
+ pa_memblock_release(hrir_right_temp_chunk_resampled.memblock);
+ pa_memblock_unref(hrir_right_temp_chunk_resampled.memblock);
+ hrir_right_temp_chunk_resampled.memblock = NULL;
+ }
+ }
+
+ pa_memblock_unref(hrir_right_temp_chunk.memblock);
+ hrir_right_temp_chunk.memblock = NULL;
+ }
+
pa_resampler_free(resampler);
- pa_memblock_unref(hrir_temp_chunk.memblock);
- hrir_temp_chunk.memblock = NULL;
-
- if (hrir_map.channels < map.channels) {
- pa_log("hrir file does not have enough channels!");
- goto fail;
- }
-
- normalize_hrir(u);
+ if (hrir_right_data)
+ normalize_hrir_stereo(hrir_data, hrir_right_data, hrir_samples, hrir_channels);
+ else
+ normalize_hrir(hrir_data, hrir_samples, hrir_channels);
/* create mapping between hrir and input */
- u->mapping_left = (unsigned *) pa_xnew0(unsigned, u->channels);
- u->mapping_right = (unsigned *) pa_xnew0(unsigned, u->channels);
+ mapping_left = (unsigned *) pa_xnew0(unsigned, hrir_channels);
+ mapping_right = (unsigned *) pa_xnew0(unsigned, hrir_channels);
for (i = 0; i < map.channels; i++) {
found_channel_left = 0;
found_channel_right = 0;
for (j = 0; j < hrir_map.channels; j++) {
if (hrir_map.map[j] == map.map[i]) {
- u->mapping_left[i] = j;
+ mapping_left[i] = j;
found_channel_left = 1;
}
if (hrir_map.map[j] == mirror_channel(map.map[i])) {
- u->mapping_right[i] = j;
+ mapping_right[i] = j;
found_channel_right = 1;
}
}
@@ -872,25 +1041,130 @@ int pa__init(pa_module*m) {
}
}
- u->input_buffer = pa_xmalloc0(u->hrir_samples * u->sink_fs);
- u->input_buffer_offset = 0;
+ fftlen = (hrir_samples + BLOCK_SIZE + 1); /* Grow a bit for overlap */
+ {
+ /* Round up to a power of two */
+ int pow = 1;
+ while (fftlen > 2) { pow++; fftlen /= 2; }
+ fftlen = 2 << pow;
+ }
+
+ u->fftlen = fftlen;
+
+ u->f_in = (fftwf_complex*) alloc(sizeof(fftwf_complex), (fftlen/2+1));
+ u->f_out = (fftwf_complex*) alloc(sizeof(fftwf_complex), (fftlen/2+1));
+
+ u->f_ir = (fftwf_complex**) alloc(sizeof(fftwf_complex*), (hrir_channels*2));
+ for (i = 0, j = hrir_channels*2; i < j; i++)
+ u->f_ir[i] = (fftwf_complex*) alloc(sizeof(fftwf_complex), (fftlen/2+1));
+
+ u->revspace = (float*) alloc(sizeof(float), fftlen);
+
+ u->outspace[0] = (float*) alloc(sizeof(float), BLOCK_SIZE);
+ u->outspace[1] = (float*) alloc(sizeof(float), BLOCK_SIZE);
+
+ u->inspace = (float**) alloc(sizeof(float*), hrir_channels);
+ for (i = 0; i < hrir_channels; i++)
+ u->inspace[i] = (float*) alloc(sizeof(float), fftlen);
+
+ u->p_fw = (fftwf_plan*) alloc(sizeof(fftwf_plan), hrir_channels);
+ for (i = 0; i < hrir_channels; i++)
+ pa_assert_se(u->p_fw[i] = fftwf_plan_dft_r2c_1d(fftlen, u->inspace[i], u->f_in, FFTW_ESTIMATE));
+
+ pa_assert_se(u->p_bw = fftwf_plan_dft_c2r_1d(fftlen, u->f_out, u->revspace, FFTW_ESTIMATE));
+
+ impulse_temp = (float*) alloc(sizeof(float), fftlen);
+
+ if (hrir_right_data) {
+ for (i = 0; i < hrir_channels; i++) {
+ for (ear = 0; ear < 2; ear++) {
+ size_t index = i * 2 + ear;
+ size_t impulse_index = mapping_left[i];
+ float *impulse = (ear == 0) ? hrir_data : hrir_right_data;
+ for (j = 0; j < hrir_samples; j++) {
+ impulse_temp[j] = impulse[j * hrir_channels + impulse_index];
+ }
+
+ p = fftwf_plan_dft_r2c_1d(fftlen, impulse_temp, u->f_ir[index], FFTW_ESTIMATE);
+ if (p) {
+ fftwf_execute(p);
+ fftwf_destroy_plan(p);
+ } else {
+ pa_log("fftw plan creation failed for %s ear speaker index %d", (ear == 0) ? "left" : "right", i);
+ goto fail;
+ }
+ }
+ }
+ } else {
+ for (i = 0; i < hrir_channels; i++) {
+ for (ear = 0; ear < 2; ear++) {
+ size_t index = i * 2 + ear;
+ size_t impulse_index = (ear == 0) ? mapping_left[i] : mapping_right[i];
+ for (j = 0; j < hrir_samples; j++) {
+ impulse_temp[j] = hrir_data[j * hrir_channels + impulse_index];
+ }
+
+ p = fftwf_plan_dft_r2c_1d(fftlen, impulse_temp, u->f_ir[index], FFTW_ESTIMATE);
+ if (p) {
+ fftwf_execute(p);
+ fftwf_destroy_plan(p);
+ } else {
+ pa_log("fftw plan creation failed for %s ear speaker index %d", (ear == 0) ? "left" : "right", i);
+ goto fail;
+ }
+ }
+ }
+ }
+
+ pa_xfree(impulse_temp);
+
+ pa_xfree(hrir_data);
+ if (hrir_right_data)
+ pa_xfree(hrir_right_data);
+
+ pa_xfree(mapping_left);
+ pa_xfree(mapping_right);
+
+ u->memblockq_sink = pa_memblockq_new("module-virtual-surround-sink memblockq (input)", 0, MEMBLOCKQ_MAXLENGTH, sink_bytes(u, BLOCK_SIZE), &ss_input, 0, 0, sink_bytes(u, u->fftlen), &silence);
+ pa_memblock_unref(silence.memblock);
+
+ pa_memblockq_seek(u->memblockq_sink, sink_bytes(u, u->fftlen - BLOCK_SIZE), PA_SEEK_RELATIVE, false);
+ pa_memblockq_flush_read(u->memblockq_sink);
- /* The order here is important. The input must be put first,
- * otherwise streams might attach to the sink before the sink
- * input is attached to the master. */
- pa_sink_input_put(u->sink_input);
pa_sink_put(u->sink);
- pa_sink_input_cork(u->sink_input, false);
+ pa_sink_input_put(u->sink_input);
pa_modargs_free(ma);
+
return 0;
fail:
- if (hrir_temp_chunk.memblock)
- pa_memblock_unref(hrir_temp_chunk.memblock);
+ if (impulse_temp)
+ pa_xfree(impulse_temp);
- if (hrir_temp_chunk_resampled.memblock)
- pa_memblock_unref(hrir_temp_chunk_resampled.memblock);
+ if (mapping_left)
+ pa_xfree(mapping_left);
+
+ if (mapping_right)
+ pa_xfree(mapping_right);
+
+ if (hrir_data)
+ pa_xfree(hrir_data);
+
+ if (hrir_right_data)
+ pa_xfree(hrir_right_data);
+
+ if (hrir_left_temp_chunk.memblock)
+ pa_memblock_unref(hrir_left_temp_chunk.memblock);
+
+ if (hrir_left_temp_chunk_resampled.memblock)
+ pa_memblock_unref(hrir_left_temp_chunk_resampled.memblock);
+
+ if (hrir_right_temp_chunk.memblock)
+ pa_memblock_unref(hrir_right_temp_chunk.memblock);
+
+ if (hrir_right_temp_chunk_resampled.memblock)
+ pa_memblock_unref(hrir_right_temp_chunk_resampled.memblock);
if (ma)
pa_modargs_free(ma);
@@ -910,6 +1184,7 @@ int pa__get_n_used(pa_module *m) {
}
void pa__done(pa_module*m) {
+ size_t i, j;
struct userdata *u;
pa_assert(m);
@@ -921,32 +1196,60 @@ void pa__done(pa_module*m) {
* destruction order! */
if (u->sink_input)
- pa_sink_input_cork(u->sink_input, true);
+ pa_sink_input_unlink(u->sink_input);
if (u->sink)
pa_sink_unlink(u->sink);
- if (u->sink_input) {
- pa_sink_input_unlink(u->sink_input);
+ if (u->sink_input)
pa_sink_input_unref(u->sink_input);
- }
if (u->sink)
pa_sink_unref(u->sink);
- if (u->memblockq)
- pa_memblockq_free(u->memblockq);
+ if (u->memblockq_sink)
+ pa_memblockq_free(u->memblockq_sink);
- if (u->hrir_data)
- pa_xfree(u->hrir_data);
+ if (u->p_fw) {
+ for (i = 0, j = u->inputs; i < j; i++) {
+ if (u->p_fw[i])
+ fftwf_destroy_plan(u->p_fw[i]);
+ }
+ fftwf_free(u->p_fw);
+ }
- if (u->input_buffer)
- pa_xfree(u->input_buffer);
+ if (u->p_bw)
+ fftwf_destroy_plan(u->p_bw);
- if (u->mapping_left)
- pa_xfree(u->mapping_left);
- if (u->mapping_right)
- pa_xfree(u->mapping_right);
+ if (u->f_ir) {
+ for (i = 0, j = u->inputs * 2; i < j; i++) {
+ if (u->f_ir[i])
+ fftwf_free(u->f_ir[i]);
+ }
+ fftwf_free(u->f_ir);
+ }
+
+ if (u->f_out)
+ fftwf_free(u->f_out);
+
+ if (u->f_in)
+ fftwf_free(u->f_in);
+
+ if (u->revspace)
+ fftwf_free(u->revspace);
+
+ if (u->outspace[0])
+ fftwf_free(u->outspace[0]);
+ if (u->outspace[1])
+ fftwf_free(u->outspace[1]);
+
+ if (u->inspace) {
+ for (i = 0, j = u->inputs; i < j; i++) {
+ if (u->inspace[i])
+ fftwf_free(u->inspace[i]);
+ }
+ fftwf_free(u->inspace);
+ }
pa_xfree(u);
}
From b8c656b6645a1ec7d8bef0f1cef557eaba4eaea8 Mon Sep 17 00:00:00 2001
From: Laurent Bigonville
Date: Thu, 27 Aug 2020 15:58:55 +0200
Subject: [PATCH 014/865] tests: Fix incompatible pointer type on Debian
kfreebsd
Debian kfreebsd uses the GNU libc that uses cpu_set_t instead of cpuset_t
Also do not include unnecessary headers on this platform
Fixes: #851
Part-of:
---
src/tests/once-test.c | 4 +---
src/tests/rtstutter.c | 4 +---
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/src/tests/once-test.c b/src/tests/once-test.c
index cb5618707..c4d4b4be6 100644
--- a/src/tests/once-test.c
+++ b/src/tests/once-test.c
@@ -22,10 +22,8 @@
#ifdef HAVE_PTHREAD
#include
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#ifdef __FreeBSD__
#include
-#endif
#include
#include
#endif
@@ -63,7 +61,7 @@ static void thread_func(void *data) {
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
static pa_atomic_t i_cpu = PA_ATOMIC_INIT(0);
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#ifdef __FreeBSD__
cpuset_t mask;
#else
cpu_set_t mask;
diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c
index 56b5146ca..9d74855a3 100644
--- a/src/tests/rtstutter.c
+++ b/src/tests/rtstutter.c
@@ -29,10 +29,8 @@
#ifdef HAVE_PTHREAD
#include
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#ifdef __FreeBSD__
#include
-#endif
#include
#include
#endif
@@ -61,7 +59,7 @@ static void work(void *p) {
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
{
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#ifdef __FreeBSD__
cpuset_t mask;
#else
cpu_set_t mask;
From 0fc54f9e7a9f441a33c87baec39a32ac8efe6e2c Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Wed, 25 Nov 2020 19:35:04 +0300
Subject: [PATCH 015/865] pa-info: look for alsa-info.sh in /usr/sbin as well
At least Gentoo and OpenSUSE install alsa-info.sh in /usr/sbin, look there too.
Part-of:
---
src/utils/pa-info | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/utils/pa-info b/src/utils/pa-info
index 1b1cc29b9..7bee1d8df 100755
--- a/src/utils/pa-info
+++ b/src/utils/pa-info
@@ -40,6 +40,7 @@ function jacks_do {
function alsa_info_do {
alsa_info=$(which alsa-info.sh)
[ $alsa_info ] || alsa_info=$(which alsa-info)
+ [ $alsa_info ] || alsa_info='/usr/sbin/alsa-info.sh'
[ $alsa_info ] || alsa_info='/usr/share/alsa-base/alsa-info.sh'
[ -f $alsa_info ] && {
$alsa_info --stdout
From 1e7adb4fdbc7db172482dab23e4509191fe5f45f Mon Sep 17 00:00:00 2001
From: Jaechul Lee
Date: Fri, 6 Nov 2020 10:52:33 +0900
Subject: [PATCH 016/865] null-sink: Change block size to 50 msec when
norewinds is set
playing sound through null sink takes almost 2 seconds at first time
playback when norewinds is set. Because block_usec is set 2 seconds at
initializing time. The value will be changed 50 msec after calling
update_request_latency callback.
Part-of:
---
src/modules/module-null-sink.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index bbbf83435..6865663da 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -57,8 +57,8 @@ PA_MODULE_USAGE(
"norewinds=");
#define DEFAULT_SINK_NAME "null"
-#define BLOCK_USEC (PA_USEC_PER_SEC * 2)
-#define NOREWINDS_MAX_LATENCY_USEC (50*PA_USEC_PER_MSEC)
+#define BLOCK_USEC (2 * PA_USEC_PER_SEC)
+#define BLOCK_USEC_NOREWINDS (50 * PA_USEC_PER_MSEC)
struct userdata {
pa_core *core;
@@ -318,6 +318,7 @@ int pa__init(pa_module*m) {
u->core = m->core;
u->module = m;
u->rtpoll = pa_rtpoll_new();
+ u->block_usec = BLOCK_USEC;
if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
pa_log("pa_thread_mq_init() failed.");
@@ -381,13 +382,15 @@ int pa__init(pa_module*m) {
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
- u->block_usec = BLOCK_USEC;
- nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
-
if(pa_modargs_get_value_boolean(ma, "norewinds", &u->norewinds) < 0){
pa_log("Invalid argument, norewinds expects a boolean value.");
}
+ if (u->norewinds)
+ u->block_usec = BLOCK_USEC_NOREWINDS;
+
+ nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
if(u->norewinds){
pa_sink_set_max_rewind(u->sink, 0);
} else {
@@ -401,11 +404,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if(u->norewinds){
- pa_sink_set_latency_range(u->sink, 0, NOREWINDS_MAX_LATENCY_USEC);
- } else {
- pa_sink_set_latency_range(u->sink, 0, BLOCK_USEC);
- }
+ pa_sink_set_latency_range(u->sink, 0, u->block_usec);
pa_sink_put(u->sink);
From ecd597995a587705752796e3b8e229430ee6e847 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Fri, 27 Nov 2020 23:49:17 +0300
Subject: [PATCH 017/865] build-sys: meson: add oss-output option for OSS
output support
Restore an option to disable OSS output available with autotools.
---
meson.build | 6 +++---
meson_options.txt | 3 +++
src/modules/meson.build | 2 +-
src/utils/meson.build | 2 +-
4 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/meson.build b/meson.build
index 217ba0d85..4dae29808 100644
--- a/meson.build
+++ b/meson.build
@@ -613,8 +613,8 @@ if x11_dep.found()
endif
# Module dependencies
-
-if cc.has_header('sys/soundcard.h')
+if get_option('oss-output')
+ assert(cc.has_header('sys/soundcard.h'), 'Need header file for OSS support')
cdata.set('HAVE_OSS_OUTPUT', 1)
cdata.set('HAVE_OSS_WRAPPER', 1)
cdata.set('PULSEDSP_LOCATION', pulsedsp_location)
@@ -808,7 +808,7 @@ summary = [
'',
'Enable memfd shared memory: @0@'.format(cdata.has('HAVE_MEMFD')),
'Enable X11: @0@'.format(x11_dep.found()),
-# 'Enable OSS Output: @0@'.format(${ENABLE_OSS_OUTPUT}),
+ 'Enable OSS Output: @0@'.format(get_option('oss-output')),
# 'Enable OSS Wrapper: @0@'.format(${ENABLE_OSS_WRAPPER}),
# 'Enable EsounD: @0@'.format(${ENABLE_ESOUND}),
'Enable Alsa: @0@'.format(alsa_dep.found()),
diff --git a/meson_options.txt b/meson_options.txt
index 824f24e08..5283a6d98 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -123,6 +123,9 @@ option('openssl',
option('orc',
type : 'feature', value : 'auto',
description : 'Optimized Inner Loop Runtime Compiler')
+option('oss-output',
+ type : 'boolean',
+ description : 'Optional OSS output support')
option('samplerate',
type : 'feature', value : 'disabled',
description : 'Optional libsamplerate support (DEPRECATED)')
diff --git a/src/modules/meson.build b/src/modules/meson.build
index 9a394c3b9..9df6d0e61 100644
--- a/src/modules/meson.build
+++ b/src/modules/meson.build
@@ -73,7 +73,7 @@ if cc.has_header('linux/input.h')
]
endif
-if cc.has_header('sys/soundcard.h')
+if get_option('oss-output')
subdir('oss')
all_modules += [
[ 'module-oss', 'oss/module-oss.c', [], [], [], liboss_util ],
diff --git a/src/utils/meson.build b/src/utils/meson.build
index dedf4e404..934abb850 100644
--- a/src/utils/meson.build
+++ b/src/utils/meson.build
@@ -84,7 +84,7 @@ if x11_dep.found()
)
endif
-if cc.has_header('sys/soundcard.h')
+if get_option('oss-output')
libpulsecommon_sources = [
'padsp.c',
]
From cb3d12377cb1131fb3627ece66b0b70c2c5e0479 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Mon, 30 Nov 2020 13:31:19 +0300
Subject: [PATCH 018/865] build-sys: meson: use target_machine.cpu_family() for
CANONICAL_HOST
target_machine provides information about the machine on which the compiled
binary's output will run.
cpu_family() returns CPU family name (such as x86_64, not more specific amd64)
Part-of:
---
meson.build | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/meson.build b/meson.build
index 4dae29808..683f40e1f 100644
--- a/meson.build
+++ b/meson.build
@@ -125,7 +125,7 @@ cdata = configuration_data()
cdata.set_quoted('PACKAGE', 'pulseaudio')
cdata.set_quoted('PACKAGE_NAME', 'pulseaudio')
cdata.set_quoted('PACKAGE_VERSION', pa_version_str)
-cdata.set_quoted('CANONICAL_HOST', host_machine.cpu())
+cdata.set_quoted('CANONICAL_HOST', target_machine.cpu_family())
cdata.set('PA_MAJOR', pa_version_major)
cdata.set('PA_MINOR', pa_version_minor)
cdata.set('PA_API_VERSION', pa_api_version)
From 4cdc0053c01f83b4b1c55042e594e1951a084c4f Mon Sep 17 00:00:00 2001
From: Georg Chini
Date: Tue, 14 Jan 2020 10:15:36 +0100
Subject: [PATCH 019/865] protocol-native: add message sending capability
This patch adds the PA_COMMAND_SEND_OBJECT_MESSAGE command to protocol-native
so that clients can use the messaging feature introduced in the previous patch.
Sending messages can in effect replace the extension system for modules. The
approach is more flexible than the extension interface because a generic string
format is used to exchange information. Furthermore the messaging system can be
used for any object, not only for modules, and is easier to implement than
extensions.
Part-of:
---
PROTOCOL | 17 +++++++++
configure.ac | 2 +-
meson.build | 2 +-
src/map-file | 1 +
src/pulse/introspect.c | 64 +++++++++++++++++++++++++++++++++
src/pulse/introspect.h | 16 +++++++++
src/pulsecore/native-common.h | 3 ++
src/pulsecore/pdispatch.c | 3 ++
src/pulsecore/protocol-native.c | 52 +++++++++++++++++++++++++++
9 files changed, 158 insertions(+), 2 deletions(-)
diff --git a/PROTOCOL b/PROTOCOL
index 4307898c2..72d3af3c0 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -435,6 +435,23 @@ sink, source and card ports):
string availability_group
uint32 type
+## v35, implemented by >= 15.0
+
+Added new command for communication with objects.
+
+PA_COMMAND_SEND_OBJECT_MESSAGE:
+sends a message to an object identified by an object path
+
+parameters:
+ string object_path - unique path identifying the object
+ string message - message name
+ string message_parameters - additional parameters if required (may be
+ NULL, which should be treated the same as an
+ empty string)
+
+The command returns a string, which may be empty or NULL (NULL should be
+treated the same as an empty string).
+
#### If you just changed the protocol, read this
## module-tunnel depends on the sink/source/sink-input/source-input protocol
## internals, so if you changed these, you might have broken module-tunnel.
diff --git a/configure.ac b/configure.ac
index 6e6049fec..fb1b518e9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,7 +42,7 @@ AC_SUBST(PA_MINOR, pa_minor)
AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 34)
+AC_SUBST(PA_PROTOCOL_VERSION, 35)
# The stable ABI for client applications, for the version info x:y:z
# always will hold x=z
diff --git a/meson.build b/meson.build
index 683f40e1f..06c44d748 100644
--- a/meson.build
+++ b/meson.build
@@ -19,7 +19,7 @@ endif
pa_version_major_minor = pa_version_major + '.' + pa_version_minor
pa_api_version = 12
-pa_protocol_version = 34
+pa_protocol_version = 35
# The stable ABI for client applications, for the version info x:y:z
# always will hold x=z
diff --git a/src/map-file b/src/map-file
index b0cd1bfc4..ea3f70268 100644
--- a/src/map-file
+++ b/src/map-file
@@ -87,6 +87,7 @@ pa_context_remove_autoload_by_name;
pa_context_remove_sample;
pa_context_rttime_new;
pa_context_rttime_restart;
+pa_context_send_message_to_object;
pa_context_set_card_profile_by_index;
pa_context_set_card_profile_by_name;
pa_context_set_default_sink;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 3027f38ed..7fefb9ce0 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -2205,3 +2205,67 @@ pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, in
return o;
}
+
+/** Object response string processing **/
+
+static void context_string_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ const char *response;
+ int success = 1;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, false) < 0)
+ goto finish;
+
+ success = 0;
+ response = "";
+ } else if (pa_tagstruct_gets(t, &response) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!response)
+ response = "";
+
+ if (o->callback) {
+ pa_context_string_cb_t cb = (pa_context_string_cb_t) o->callback;
+ cb(o->context, success, response, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_send_message_to_object(pa_context *c, const char *object_path, const char *message, const char *message_parameters, pa_context_string_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SEND_OBJECT_MESSAGE, &tag);
+
+ pa_tagstruct_puts(t, object_path);
+ pa_tagstruct_puts(t, message);
+ pa_tagstruct_puts(t, message_parameters);
+
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_string_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index c547bde09..fae44a7d1 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -204,6 +204,12 @@
* Server modules can be remotely loaded and unloaded using
* pa_context_load_module() and pa_context_unload_module().
*
+ * \subsection message_subsec Messages
+ *
+ * Server objects like sinks, sink inputs or modules can register a message
+ * handler to communicate with clients. A message can be sent to a named
+ * message handler using pa_context_send_message_to_object().
+ *
* \subsection client_subsec Clients
*
* The only operation supported on clients is the possibility of kicking
@@ -489,6 +495,16 @@ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_s
/** @} */
+/** @{ \name Messages */
+
+/** Callback prototype for pa_context_send_message_to_object() \since 15.0 */
+typedef void (*pa_context_string_cb_t)(pa_context *c, int success, const char *response, void *userdata);
+
+/** Send a message to an object that registered a message handler. \since 15.0 */
+pa_operation* pa_context_send_message_to_object(pa_context *c, const char *recipient_name, const char *message, const char *message_parameters, pa_context_string_cb_t cb, void *userdata);
+
+/** @} */
+
/** @{ \name Clients */
/** Stores information about clients. Please note that this structure
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 70338b9f3..3de960def 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -187,6 +187,9 @@ enum {
* BOTH DIRECTIONS */
PA_COMMAND_REGISTER_MEMFD_SHMID,
+ /* Supported since protocol v34 (14.0) */
+ PA_COMMAND_SEND_OBJECT_MESSAGE,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index ab632a5ab..c2d26e63e 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -199,6 +199,9 @@ static const char *command_names[PA_COMMAND_MAX] = {
/* Supported since protocol v31 (9.0) */
/* BOTH DIRECTIONS */
[PA_COMMAND_REGISTER_MEMFD_SHMID] = "REGISTER_MEMFD_SHMID",
+
+ /* Supported since protocol v35 (15.0) */
+ [PA_COMMAND_SEND_OBJECT_MESSAGE] = "SEND_OBJECT_MESSAGE",
};
#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index e8559b239..f8dad57d6 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -47,6 +47,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -4721,6 +4722,55 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
protocol_error(c);
}
+/* Send message to an object which registered a handler. Result must be returned as string. */
+static void command_send_object_message(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ const char *object_path = NULL;
+ const char *message = NULL;
+ const char *message_parameters = NULL;
+ const char *client_name;
+ char *response = NULL;
+ int ret;
+ pa_tagstruct *reply;
+
+ pa_native_connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_gets(t, &object_path) < 0 ||
+ pa_tagstruct_gets(t, &message) < 0 ||
+ pa_tagstruct_gets(t, &message_parameters) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, object_path != NULL, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, pa_utf8_valid(object_path), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, message != NULL, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, pa_utf8_valid(message), tag, PA_ERR_INVALID);
+ if (message_parameters)
+ CHECK_VALIDITY(c->pstream, pa_utf8_valid(message_parameters), tag, PA_ERR_INVALID);
+
+ client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
+ pa_log_debug("Client %s sent message %s to path %s", client_name, message, object_path);
+ if (message_parameters)
+ pa_log_debug("Message parameters: %s", message_parameters);
+
+ ret = pa_message_handler_send_message(c->protocol->core, object_path, message, message_parameters, &response);
+
+ if (ret < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
+ return;
+ }
+
+ reply = reply_new(tag);
+ pa_tagstruct_puts(reply, response);
+ pa_xfree(response);
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx = PA_INVALID_INDEX;
@@ -4972,6 +5022,8 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_REGISTER_MEMFD_SHMID] = command_register_memfd_shmid,
+ [PA_COMMAND_SEND_OBJECT_MESSAGE] = command_send_object_message,
+
[PA_COMMAND_EXTENSION] = command_extension
};
From 68f2f1395d635576dae2a8aff34d38f06511d153 Mon Sep 17 00:00:00 2001
From: Georg Chini
Date: Tue, 14 Jan 2020 10:24:36 +0100
Subject: [PATCH 020/865] pactl, pacmd, cli-command: Add send-message command
Part-of:
---
doc/messaging_api.txt | 15 +++++++++++
man/pactl.1.xml.in | 7 +++++
man/pulse-cli-syntax.5.xml.in | 7 +++++
shell-completion/bash/pulseaudio | 4 +--
shell-completion/zsh/_pulseaudio | 2 ++
src/pulse/introspect.h | 3 ++-
src/pulsecore/cli-command.c | 44 ++++++++++++++++++++++++++++++++
src/utils/pacmd.c | 1 +
src/utils/pactl.c | 43 ++++++++++++++++++++++++++++++-
9 files changed, 122 insertions(+), 4 deletions(-)
create mode 100644 doc/messaging_api.txt
diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt
new file mode 100644
index 000000000..cbc06e75a
--- /dev/null
+++ b/doc/messaging_api.txt
@@ -0,0 +1,15 @@
+Message API reference
+
+The message API allows any object within pulseaudio to register a message
+handler. A message handler is a function that can be called by clients using
+PA_COMMAND_SEND_OBJECT_MESSAGE. A message consists at least of an object path
+and a message command, both specified as strings. Additional parameters can
+be specified using a single string, but are not mandatory. The message handler
+returns an error number as defined in def.h and also returns a string in
+the "response" variable. The following reference lists available messages,
+their parameters and return values.
+
+Recipient:
+Message:
+Parameters:
+Return value:
diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
index cff628f34..9f4b7abd3 100644
--- a/man/pactl.1.xml.in
+++ b/man/pactl.1.xml.in
@@ -253,6 +253,13 @@ License along with PulseAudio; if not, see .
for possible encodings.
+
+
+
+
diff --git a/shell-completion/bash/pulseaudio b/shell-completion/bash/pulseaudio
index 1ca0e40b8..6c2635f65 100644
--- a/shell-completion/bash/pulseaudio
+++ b/shell-completion/bash/pulseaudio
@@ -113,7 +113,7 @@ _pactl() {
local comps
local flags='-h --help --version -s --server= --client-name='
local list_types='short sinks sources sink-inputs source-outputs cards
- modules samples clients'
+ modules samples clients message-handlers'
local commands=(stat info list exit upload-sample play-sample remove-sample
load-module unload-module move-sink-input move-source-output
suspend-sink suspend-source set-card-profile set-default-sink
diff --git a/shell-completion/zsh/_pulseaudio b/shell-completion/zsh/_pulseaudio
index 7668386fc..2a1f18eaa 100644
--- a/shell-completion/zsh/_pulseaudio
+++ b/shell-completion/zsh/_pulseaudio
@@ -285,6 +285,7 @@ _pactl_completion() {
'clients: list connected clients'
'samples: list samples'
'cards: list available cards'
+ 'message-handlers: list available message-handlers'
)
if ((CURRENT == 2)); then
diff --git a/src/Makefile.am b/src/Makefile.am
index d298d85d3..19f100f11 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -929,6 +929,7 @@ libpulse_la_SOURCES = \
pulse/timeval.c pulse/timeval.h \
pulse/utf8.c pulse/utf8.h \
pulse/util.c pulse/util.h \
+ pulse/message-params.c pulse/message-params.h \
pulse/volume.c pulse/volume.h \
pulse/xmalloc.c pulse/xmalloc.h
diff --git a/src/map-file b/src/map-file
index ea3f70268..0acbf05b5 100644
--- a/src/map-file
+++ b/src/map-file
@@ -229,6 +229,8 @@ pa_mainloop_quit;
pa_mainloop_run;
pa_mainloop_set_poll_func;
pa_mainloop_wakeup;
+pa_message_params_read_raw;
+pa_message_params_read_string;
pa_msleep;
pa_thread_make_realtime;
pa_operation_cancel;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 7fefb9ce0..0671fa163 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -2236,8 +2236,15 @@ static void context_string_callback(pa_pdispatch *pd, uint32_t command, uint32_t
response = "";
if (o->callback) {
- pa_context_string_cb_t cb = (pa_context_string_cb_t) o->callback;
- cb(o->context, success, response, o->userdata);
+ char *response_copy;
+ pa_context_string_cb_t cb;
+
+ response_copy = pa_xstrdup(response);
+
+ cb = (pa_context_string_cb_t) o->callback;
+ cb(o->context, success, response_copy, o->userdata);
+
+ pa_xfree(response_copy);
}
finish:
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index d8cdc3c8c..2c3c4acf4 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -498,7 +498,7 @@ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_s
/** @{ \name Messages */
/** Callback prototype for pa_context_send_message_to_object() \since 15.0 */
-typedef void (*pa_context_string_cb_t)(pa_context *c, int success, const char *response, void *userdata);
+typedef void (*pa_context_string_cb_t)(pa_context *c, int success, char *response, void *userdata);
/** Send a message to an object that registered a message handler. For more information
* see https://cgit.freedesktop.org/pulseaudio/pulseaudio/tree/doc/messaging_api.txt. \since 15.0 */
diff --git a/src/pulse/meson.build b/src/pulse/meson.build
index aaebff53e..abc1e7921 100644
--- a/src/pulse/meson.build
+++ b/src/pulse/meson.build
@@ -19,6 +19,7 @@ libpulse_sources = [
'mainloop-api.c',
'mainloop-signal.c',
'mainloop.c',
+ 'message-params.c',
'operation.c',
'proplist.c',
'rtclock.c',
@@ -50,6 +51,7 @@ libpulse_headers = [
'mainloop-api.h',
'mainloop-signal.h',
'mainloop.h',
+ 'message-params.h',
'operation.h',
'proplist.h',
'pulseaudio.h',
diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c
new file mode 100644
index 000000000..0afda4f38
--- /dev/null
+++ b/src/pulse/message-params.c
@@ -0,0 +1,118 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see .
+***/
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+#include "message-params.h"
+
+/* Split the specified string into elements. An element is defined as
+ * a sub-string between curly braces. The function is needed to parse
+ * the parameters of messages. Each time it is called it returns the
+ * position of the current element in result and the state pointer is
+ * advanced to the next list element.
+ *
+ * The variable state points to, should be initialized to NULL before
+ * the first call. The function returns 1 on success, 0 if end of string
+ * is encountered and -1 on parse error.
+ *
+ * result is set to NULL on end of string or parse error. */
+static int split_list(char *c, char **result, void **state) {
+ char *current = *state ? *state : c;
+ uint32_t open_braces;
+
+ pa_assert(result);
+
+ *result = NULL;
+
+ /* Empty or no string */
+ if (!current || *current == 0)
+ return 0;
+
+ /* Find opening brace */
+ while (*current != 0) {
+
+ if (*current == '{')
+ break;
+
+ /* unexpected closing brace, parse error */
+ if (*current == '}')
+ return -1;
+
+ current++;
+ }
+
+ /* No opening brace found, end of string */
+ if (*current == 0)
+ return 0;
+
+ *result = current + 1;
+ open_braces = 1;
+
+ while (open_braces != 0 && *current != 0) {
+ current++;
+ if (*current == '{')
+ open_braces++;
+ if (*current == '}')
+ open_braces--;
+ }
+
+ /* Parse error, closing brace missing */
+ if (open_braces != 0) {
+ *result = NULL;
+ return -1;
+ }
+
+ /* Replace } with 0 */
+ *current = 0;
+
+ *state = current + 1;
+
+ return 1;
+}
+
+/* Read a string from the parameter list. The state pointer is
+ * advanced to the next element of the list. Returns a pointer
+ * to a sub-string within c. The result must not be freed. */
+int pa_message_params_read_string(char *c, const char **result, void **state) {
+ char *start_pos;
+ int r;
+
+ pa_assert(result);
+
+ if ((r = split_list(c, &start_pos, state)) == 1)
+ *result = start_pos;
+
+ return r;
+}
+
+/* Another wrapper for split_list() to distinguish between reading
+ * pure string data and raw data which may contain further lists. */
+int pa_message_params_read_raw(char *c, char **result, void **state) {
+ return split_list(c, result, state);
+}
diff --git a/src/pulse/message-params.h b/src/pulse/message-params.h
new file mode 100644
index 000000000..5c9bc1a6e
--- /dev/null
+++ b/src/pulse/message-params.h
@@ -0,0 +1,42 @@
+#ifndef foomessagehelperhfoo
+#define foomessagehelperhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see .
+***/
+
+#include
+#include
+#include
+
+#include
+#include
+
+/** \file
+ * Utility functions for reading and writing message parameters */
+
+PA_C_DECL_BEGIN
+
+/** Read raw data from a parameter list. Used to split a message parameter
+ * string into list elements \since 15.0 */
+int pa_message_params_read_raw(char *c, char **result, void **state);
+
+/** Read a string from a parameter list. \since 15.0 */
+int pa_message_params_read_string(char *c, const char **result, void **state);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index cb824ed17..da8b3b3da 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -86,7 +86,7 @@ static char *message_handler_list(pa_core *c) {
return pa_strbuf_to_string_free(buf);
}
-static int core_message_handler(const char *object_path, const char *message, const char *message_parameters, char **response, void *userdata) {
+static int core_message_handler(const char *object_path, const char *message, char *message_parameters, char **response, void *userdata) {
pa_core *c;
pa_assert(c = (pa_core *) userdata);
diff --git a/src/pulsecore/message-handler.c b/src/pulsecore/message-handler.c
index 18c62fc2d..427186dbd 100644
--- a/src/pulsecore/message-handler.c
+++ b/src/pulsecore/message-handler.c
@@ -90,6 +90,8 @@ void pa_message_handler_unregister(pa_core *c, const char *object_path) {
/* Send a message to an object identified by object_path */
int pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) {
struct pa_message_handler *handler;
+ int ret;
+ char *parameter_copy;
pa_assert(c);
pa_assert(object_path);
@@ -101,9 +103,14 @@ int pa_message_handler_send_message(pa_core *c, const char *object_path, const c
if (!(handler = pa_hashmap_get(c->message_handlers, object_path)))
return -PA_ERR_NOENTITY;
+ parameter_copy = pa_xstrdup(message_parameters);
+
/* The handler is expected to return an error code and may also
return an error string in response */
- return handler->callback(handler->object_path, message, message_parameters, response, handler->userdata);
+ ret = handler->callback(handler->object_path, message, parameter_copy, response, handler->userdata);
+
+ pa_xfree(parameter_copy);
+ return ret;
}
/* Set handler description */
diff --git a/src/pulsecore/message-handler.h b/src/pulsecore/message-handler.h
index be94510f0..38b24e1b2 100644
--- a/src/pulsecore/message-handler.h
+++ b/src/pulsecore/message-handler.h
@@ -26,7 +26,7 @@
typedef int (*pa_message_handler_cb_t)(
const char *object_path,
const char *message,
- const char *message_parameters,
+ char *message_parameters,
char **response,
void *userdata);
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 26b85dfc3..bc1c5265a 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -35,6 +35,7 @@
#include
#include
+#include
#include
#include
@@ -882,7 +883,7 @@ static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
complete_action();
}
-static void send_message_callback(pa_context *c, int success, const char *response, void *userdata) {
+static void send_message_callback(pa_context *c, int success, char *response, void *userdata) {
if (!success) {
pa_log(_("Send message failed: %s"), pa_strerror(pa_context_errno(c)));
@@ -895,6 +896,62 @@ static void send_message_callback(pa_context *c, int success, const char *respon
complete_action();
}
+static void list_handlers_callback(pa_context *c, int success, char *response, void *userdata) {
+ void *state = NULL;
+ char *handler_list;
+ char *handler_struct;
+ int err;
+
+ if (!success) {
+ pa_log(_("list-handlers message failed: %s"), pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (pa_message_params_read_raw(response, &handler_list, &state) <= 0) {
+ pa_log(_("list-handlers message response could not be parsed correctly"));
+ quit(1);
+ return;
+ }
+
+ state = NULL;
+ while ((err = pa_message_params_read_raw(handler_list, &handler_struct, &state)) > 0) {
+ void *state2 = NULL;
+ const char *path;
+ const char *description;
+
+ if (pa_message_params_read_string(handler_struct, &path, &state2) <= 0) {
+ err = -1;
+ break;
+ }
+ if (pa_message_params_read_string(handler_struct, &description, &state2) <= 0) {
+ err = -1;
+ break;
+ }
+
+ if (short_list_format)
+ printf("%s\n", path);
+ else {
+ if (nl)
+ printf("\n");
+ nl = true;
+
+ printf("Message Handler %s\n"
+ "\tDescription: %s\n",
+ path,
+ description);
+ }
+ }
+
+ if (err < 0) {
+ pa_log(_("list-handlers message response could not be parsed correctly"));
+ quit(1);
+ return;
+ }
+
+ complete_action();
+}
+
static void volume_relative_adjust(pa_cvolume *cv) {
pa_assert(volume_flags & VOL_RELATIVE);
@@ -1308,6 +1365,8 @@ static void context_state_callback(pa_context *c, void *userdata) {
o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
else if (pa_streq(list_type, "cards"))
o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
+ else if (pa_streq(list_type, "message-handlers"))
+ o = pa_context_send_message_to_object(c, "/core", "list-handlers", NULL, list_handlers_callback, NULL);
else
pa_assert_not_reached();
} else {
@@ -1744,12 +1803,13 @@ int main(int argc, char *argv[]) {
if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") ||
pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
- pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) {
+ pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") ||
+ pa_streq(argv[i], "message-handlers")) {
list_type = pa_xstrdup(argv[i]);
} else if (pa_streq(argv[i], "short")) {
short_list_format = true;
} else {
- pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards");
+ pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards, message-handlers");
goto quit;
}
}
From 590fd1ca698031b77ddcf6fc44d3729211760dac Mon Sep 17 00:00:00 2001
From: Georg Chini
Date: Tue, 14 Jan 2020 13:24:16 +0100
Subject: [PATCH 023/865] message-params: Allow parameter strings to contain
escaped curly braces
The patch adds the possibility to escape curly braces within parameter strings
and introduces several new functions that can be used for writing parameters.
For writing, the structure pa_message_params, which is a wrapper for pa_strbuf
has been created. Following new write functions are available:
pa_message_params_new() - creates a new pa_message_params structure
pa_message_params_free() - frees a pa_message_params structure
pa_message_param_to_string_free() - converts a pa_message_param to string and
frees the structure
pa_message_params_begin_list() - starts a list
pa_message_params_end_list() - ends a list
pa_message_params_write_string() - writes a string to a pa_message_params structure
pa_message_params_write_raw() - writes a raw string to a pa_message_params structure
For string parameters that contain curly braces or backslashes, those characters
will be escaped when using pa_message_params_write_string(), while write_raw() will
put the string into the buffer without any changes.
For reading, pa_message_params_read_string() reverts the changes that
pa_message_params_write_string() might have introduced.
The patch also adds more restrictions on the object path name. Now only
alphanumeric characters and one of "_", ".", "-" and "/" are allowed.
The path name may not end with a / or contain a double slash. If the user
specifies a trailing / when sending a message, it will be silently removed.
Part-of:
---
doc/messaging_api.txt | 7 +-
src/Makefile.am | 3 +-
src/map-file | 7 ++
src/meson.build | 2 +
src/pulse/message-params.c | 158 +++++++++++++++++++++++++++++---
src/pulse/message-params.h | 39 +++++++-
src/pulsecore/core.c | 22 +++--
src/pulsecore/message-handler.c | 58 ++++++++----
8 files changed, 249 insertions(+), 47 deletions(-)
diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt
index 431a5df27..e0a921da4 100644
--- a/doc/messaging_api.txt
+++ b/doc/messaging_api.txt
@@ -14,10 +14,15 @@ look like that:
{{Integer} {{1st float} {2nd float} ...}}{...}
Any characters that are not enclosed in curly braces are ignored (all characters
between { and {, between } and } and between } and {). The same syntax is used
-to specify message parameters. The following reference lists available messages,
+to specify message parameters. The reference further down lists available messages,
their parameters and return values. If a return value is enclosed in {}, this
means that multiple elements of the same type may be returned.
+For string parameters that contain curly braces or backslashes, those characters
+must be escaped by adding a "\" before them.
+
+Reference:
+
Object path: /core
Message: list-handlers
Parameters: None
diff --git a/src/Makefile.am b/src/Makefile.am
index 19f100f11..df912a8b3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -711,6 +711,7 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = \
pulse/timeval.c pulse/timeval.h \
pulse/rtclock.c pulse/rtclock.h \
pulse/volume.c pulse/volume.h \
+ pulse/message-params.c pulse/message-params.h \
pulsecore/atomic.h \
pulsecore/authkey.c pulsecore/authkey.h \
pulsecore/conf-parser.c pulsecore/conf-parser.h \
@@ -917,6 +918,7 @@ libpulse_la_SOURCES = \
pulse/mainloop-api.c pulse/mainloop-api.h \
pulse/mainloop-signal.c pulse/mainloop-signal.h \
pulse/mainloop.c pulse/mainloop.h \
+ pulse/message-params.c pulse/message-params.h \
pulse/operation.c pulse/operation.h \
pulse/proplist.c pulse/proplist.h \
pulse/pulseaudio.h \
@@ -929,7 +931,6 @@ libpulse_la_SOURCES = \
pulse/timeval.c pulse/timeval.h \
pulse/utf8.c pulse/utf8.h \
pulse/util.c pulse/util.h \
- pulse/message-params.c pulse/message-params.h \
pulse/volume.c pulse/volume.h \
pulse/xmalloc.c pulse/xmalloc.h
diff --git a/src/map-file b/src/map-file
index 0acbf05b5..4d196c11d 100644
--- a/src/map-file
+++ b/src/map-file
@@ -229,8 +229,15 @@ pa_mainloop_quit;
pa_mainloop_run;
pa_mainloop_set_poll_func;
pa_mainloop_wakeup;
+pa_message_params_begin_list;
+pa_message_params_end_list;
+pa_message_params_free;
+pa_message_params_new;
pa_message_params_read_raw;
pa_message_params_read_string;
+pa_message_params_to_string_free;
+pa_message_params_write_raw;
+pa_message_params_write_string;
pa_msleep;
pa_thread_make_realtime;
pa_operation_cancel;
diff --git a/src/meson.build b/src/meson.build
index 8d74a3164..b84112eee 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -5,6 +5,7 @@ libpulsecommon_sources = [
'pulse/format.c',
'pulse/json.c',
'pulse/mainloop-api.c',
+ 'pulse/message-params.c',
'pulse/xmalloc.c',
'pulse/proplist.c',
'pulse/utf8.c',
@@ -78,6 +79,7 @@ libpulsecommon_headers = [
'pulse/format.h',
'pulse/json.h',
'pulse/mainloop-api.h',
+ 'pulse/message-params.h',
'pulse/xmalloc.h',
'pulse/proplist.h',
'pulse/utf8.h',
diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c
index 0afda4f38..236800bca 100644
--- a/src/pulse/message-params.c
+++ b/src/pulse/message-params.c
@@ -28,23 +28,33 @@
#include
#include
+#include
+#include
#include "message-params.h"
+/* Message parameter structure, a wrapper for pa_strbuf */
+struct pa_message_params {
+ pa_strbuf *buffer;
+};
+
/* Split the specified string into elements. An element is defined as
* a sub-string between curly braces. The function is needed to parse
* the parameters of messages. Each time it is called it returns the
* position of the current element in result and the state pointer is
- * advanced to the next list element.
+ * advanced to the next list element. On return, the parameter
+ * *is_unpacked indicates if the string is plain text or contains a
+ * sub-list. is_unpacked may be NULL.
*
* The variable state points to, should be initialized to NULL before
* the first call. The function returns 1 on success, 0 if end of string
* is encountered and -1 on parse error.
*
* result is set to NULL on end of string or parse error. */
-static int split_list(char *c, char **result, void **state) {
+static int split_list(char *c, char **result, bool *is_unpacked, void **state) {
char *current = *state ? *state : c;
uint32_t open_braces;
+ bool found_backslash = false;
pa_assert(result);
@@ -57,29 +67,52 @@ static int split_list(char *c, char **result, void **state) {
/* Find opening brace */
while (*current != 0) {
- if (*current == '{')
+ /* Skip escaped curly braces. */
+ if (*current == '\\' && !found_backslash) {
+ found_backslash = true;
+ current++;
+ continue;
+ }
+
+ if (*current == '{' && !found_backslash)
break;
/* unexpected closing brace, parse error */
- if (*current == '}')
+ if (*current == '}' && !found_backslash)
return -1;
+ found_backslash = false;
current++;
}
/* No opening brace found, end of string */
if (*current == 0)
- return 0;
+ return 0;
+ if (is_unpacked)
+ *is_unpacked = true;
*result = current + 1;
+ found_backslash = false;
open_braces = 1;
while (open_braces != 0 && *current != 0) {
current++;
- if (*current == '{')
+
+ /* Skip escaped curly braces. */
+ if (*current == '\\' && !found_backslash) {
+ found_backslash = true;
+ continue;
+ }
+
+ if (*current == '{' && !found_backslash) {
open_braces++;
- if (*current == '}')
+ if (is_unpacked)
+ *is_unpacked = false;
+ }
+ if (*current == '}' && !found_backslash)
open_braces--;
+
+ found_backslash = false;
}
/* Parse error, closing brace missing */
@@ -96,23 +129,122 @@ static int split_list(char *c, char **result, void **state) {
return 1;
}
+/* Read functions */
+
/* Read a string from the parameter list. The state pointer is
* advanced to the next element of the list. Returns a pointer
- * to a sub-string within c. The result must not be freed. */
+ * to a sub-string within c. Escape characters will be removed
+ * from the string. The result must not be freed. */
int pa_message_params_read_string(char *c, const char **result, void **state) {
char *start_pos;
+ char *value = NULL;
int r;
+ bool is_unpacked = true;
pa_assert(result);
- if ((r = split_list(c, &start_pos, state)) == 1)
- *result = start_pos;
+ if ((r = split_list(c, &start_pos, &is_unpacked, state)) == 1)
+ value = start_pos;
+
+ /* Check if we got a plain string not containing further lists */
+ if (!is_unpacked) {
+ /* Parse error */
+ r = -1;
+ value = NULL;
+ }
+
+ if (value)
+ *result = pa_unescape(value);
return r;
}
-/* Another wrapper for split_list() to distinguish between reading
- * pure string data and raw data which may contain further lists. */
+/* A wrapper for split_list() to distinguish between reading pure
+ * string data and raw data which may contain further lists. */
int pa_message_params_read_raw(char *c, char **result, void **state) {
- return split_list(c, result, state);
+ return split_list(c, result, NULL, state);
+}
+
+/* Write functions. The functions are wrapper functions around pa_strbuf,
+ * so that the client does not need to use pa_strbuf directly. */
+
+/* Creates a new pa_message_param structure */
+pa_message_params *pa_message_params_new(void) {
+ pa_message_params *params;
+
+ params = pa_xnew(pa_message_params, 1);
+ params->buffer = pa_strbuf_new();
+
+ return params;
+}
+
+/* Frees a pa_message_params structure */
+void pa_message_params_free(pa_message_params *params) {
+ pa_assert(params);
+
+ pa_strbuf_free(params->buffer);
+ pa_xfree(params);
+}
+
+/* Converts a pa_message_param structure to string and frees the structure.
+ * The returned string needs to be freed with pa_xree(). */
+char *pa_message_params_to_string_free(pa_message_params *params) {
+ char *result;
+
+ pa_assert(params);
+
+ result = pa_strbuf_to_string_free(params->buffer);
+
+ pa_xfree(params);
+ return result;
+}
+
+/* Writes an opening curly brace */
+void pa_message_params_begin_list(pa_message_params *params) {
+
+ pa_assert(params);
+
+ pa_strbuf_putc(params->buffer, '{');
+}
+
+/* Writes a closing curly brace */
+void pa_message_params_end_list(pa_message_params *params) {
+
+ pa_assert(params);
+
+ pa_strbuf_putc(params->buffer, '}');
+}
+
+/* Writes a string to a message_params structure, adding curly braces
+ * around the string and escaping curly braces within the string. */
+void pa_message_params_write_string(pa_message_params *params, const char *value) {
+ char *output;
+
+ pa_assert(params);
+
+ /* Null value is written as empty element */
+ if (!value)
+ value = "";
+
+ output = pa_escape(value, "{}");
+ pa_strbuf_printf(params->buffer, "{%s}", output);
+
+ pa_xfree(output);
+}
+
+/* Writes a raw string to a message_params structure, adding curly braces
+ * around the string if add_braces is true. This function can be used to
+ * write parts of a string or whole parameter lists that have been prepared
+ * elsewhere (for example an array). */
+void pa_message_params_write_raw(pa_message_params *params, const char *value, bool add_braces) {
+ pa_assert(params);
+
+ /* Null value is written as empty element */
+ if (!value)
+ value = "";
+
+ if (add_braces)
+ pa_strbuf_printf(params->buffer, "{%s}", value);
+ else
+ pa_strbuf_puts(params->buffer, value);
}
diff --git a/src/pulse/message-params.h b/src/pulse/message-params.h
index 5c9bc1a6e..f30164ff9 100644
--- a/src/pulse/message-params.h
+++ b/src/pulse/message-params.h
@@ -30,13 +30,48 @@
PA_C_DECL_BEGIN
+/** Structure which holds a parameter list. Wrapper for pa_strbuf \since 15.0 */
+typedef struct pa_message_params pa_message_params;
+
+/** @{ \name Read functions */
+
/** Read raw data from a parameter list. Used to split a message parameter
- * string into list elements \since 15.0 */
+ * string into list elements. The string returned in *result must not be freed. \since 15.0 */
int pa_message_params_read_raw(char *c, char **result, void **state);
-/** Read a string from a parameter list. \since 15.0 */
+/** Read a string from a parameter list. Escaped curly braces and backslashes
+ * will be unescaped. \since 15.0 */
int pa_message_params_read_string(char *c, const char **result, void **state);
+/** @} */
+
+/** @{ \name Write functions */
+
+/** Create a new pa_message_params structure \since 15.0 */
+pa_message_params *pa_message_params_new(void);
+
+/** Free a pa_message_params structure. \since 15.0 */
+void pa_message_params_free(pa_message_params *params);
+
+/** Convert pa_message_params to string, free pa_message_params structure. \since 15.0 */
+char *pa_message_params_to_string_free(pa_message_params *params);
+
+/** Start a list by writing an opening brace. \since 15.0 */
+void pa_message_params_begin_list(pa_message_params *params);
+
+/** End a list by writing a closing brace. \since 15.0 */
+void pa_message_params_end_list(pa_message_params *params);
+
+/** Append string to parameter list. Curly braces and backslashes will be escaped. \since 15.0 */
+void pa_message_params_write_string(pa_message_params *params, const char *value);
+
+/** Append raw string to parameter list. Used to write incomplete strings
+ * or complete parameter lists (for example arrays). Adds curly braces around
+ * the string if add_braces is true. \since 15.0 */
+void pa_message_params_write_raw(pa_message_params *params, const char *value, bool add_braces);
+
+/** @} */
+
PA_C_DECL_END
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index da8b3b3da..8b830199f 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include
#include
@@ -65,25 +66,26 @@ static void core_free(pa_object *o);
/* Returns a list of handlers. */
static char *message_handler_list(pa_core *c) {
- pa_strbuf *buf;
+ pa_message_params *param;
void *state = NULL;
struct pa_message_handler *handler;
- buf = pa_strbuf_new();
+ param = pa_message_params_new();
- pa_strbuf_putc(buf, '{');
+ pa_message_params_begin_list(param);
PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
- pa_strbuf_putc(buf, '{');
+ pa_message_params_begin_list(param);
- pa_strbuf_printf(buf, "{%s} {", handler->object_path);
- if (handler->description)
- pa_strbuf_puts(buf, handler->description);
+ /* object_path cannot contain characters that need escaping, therefore
+ * pa_message_params_write_raw() can safely be used here. */
+ pa_message_params_write_raw(param, handler->object_path, true);
+ pa_message_params_write_string(param, handler->description);
- pa_strbuf_puts(buf, "}}");
+ pa_message_params_end_list(param);
}
- pa_strbuf_putc(buf, '}');
+ pa_message_params_end_list(param);
- return pa_strbuf_to_string_free(buf);
+ return pa_message_params_to_string_free(param);
}
static int core_message_handler(const char *object_path, const char *message, char *message_parameters, char **response, void *userdata) {
diff --git a/src/pulsecore/message-handler.c b/src/pulsecore/message-handler.c
index 427186dbd..40644554a 100644
--- a/src/pulsecore/message-handler.c
+++ b/src/pulsecore/message-handler.c
@@ -31,17 +31,36 @@
#include "message-handler.h"
-/* Check if a string does not contain control characters. Currently these are
- * only "{" and "}". */
-static bool string_is_valid(const char *test_string) {
+/* Check if a path string starts with a / and only contains valid characters.
+ * Also reject double slashes. */
+static bool object_path_is_valid(const char *test_string) {
uint32_t i;
+ if (!test_string)
+ return false;
+
+ /* Make sure the string starts with a / */
+ if (test_string[0] != '/')
+ return false;
+
for (i = 0; test_string[i]; i++) {
- if (test_string[i] == '{' ||
- test_string[i] == '}')
- return false;
+
+ if ((test_string[i] >= 'a' && test_string[i] <= 'z') ||
+ (test_string[i] >= 'A' && test_string[i] <= 'Z') ||
+ (test_string[i] >= '0' && test_string[i] <= '9') ||
+ test_string[i] == '.' ||
+ test_string[i] == '_' ||
+ test_string[i] == '-' ||
+ (test_string[i] == '/' && test_string[i + 1] != '/'))
+ continue;
+
+ return false;
}
+ /* Make sure the string does not end with a / */
+ if (test_string[i - 1] == '/')
+ return false;
+
return true;
}
@@ -56,13 +75,8 @@ void pa_message_handler_register(pa_core *c, const char *object_path, const char
pa_assert(cb);
pa_assert(userdata);
- /* Ensure that the object path is not empty and starts with "/". */
- pa_assert(object_path[0] == '/');
-
- /* Ensure that object path and description are valid strings */
- pa_assert(string_is_valid(object_path));
- if (description)
- pa_assert(string_is_valid(description));
+ /* Ensure that object path is valid */
+ pa_assert(object_path_is_valid(object_path));
handler = pa_xnew0(struct pa_message_handler, 1);
handler->userdata = userdata;
@@ -91,7 +105,7 @@ void pa_message_handler_unregister(pa_core *c, const char *object_path) {
int pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) {
struct pa_message_handler *handler;
int ret;
- char *parameter_copy;
+ char *parameter_copy, *path_copy;
pa_assert(c);
pa_assert(object_path);
@@ -100,8 +114,16 @@ int pa_message_handler_send_message(pa_core *c, const char *object_path, const c
*response = NULL;
- if (!(handler = pa_hashmap_get(c->message_handlers, object_path)))
+ path_copy = pa_xstrdup(object_path);
+
+ /* Remove trailing / from path name if present */
+ if (path_copy[strlen(path_copy) - 1] == '/')
+ path_copy[strlen(path_copy) - 1] = 0;
+
+ if (!(handler = pa_hashmap_get(c->message_handlers, path_copy))) {
+ pa_xfree(path_copy);
return -PA_ERR_NOENTITY;
+ }
parameter_copy = pa_xstrdup(message_parameters);
@@ -110,6 +132,7 @@ int pa_message_handler_send_message(pa_core *c, const char *object_path, const c
ret = handler->callback(handler->object_path, message, parameter_copy, response, handler->userdata);
pa_xfree(parameter_copy);
+ pa_xfree(path_copy);
return ret;
}
@@ -123,11 +146,6 @@ int pa_message_handler_set_description(pa_core *c, const char *object_path, cons
if (!(handler = pa_hashmap_get(c->message_handlers, object_path)))
return -PA_ERR_NOENTITY;
- if (description) {
- if (!string_is_valid(description))
- return -PA_ERR_INVALID;
- }
-
pa_xfree(handler->description);
handler->description = pa_xstrdup(description);
From ca6638860805366148ab8bbd6d731a1df8ce8bbb Mon Sep 17 00:00:00 2001
From: Georg Chini
Date: Tue, 14 Jan 2020 14:04:11 +0100
Subject: [PATCH 024/865] core-util: Add pa_atoi64() and pa_atou64() functions
Part-of:
---
src/pulsecore/core-util.c | 87 +++++++++++++++++++++++++++++++++++++++
src/pulsecore/core-util.h | 2 +
2 files changed, 89 insertions(+)
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 601b1d1df..d421a7572 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2243,6 +2243,50 @@ int pa_atou(const char *s, uint32_t *ret_u) {
return 0;
}
+/* Convert the string s to an unsigned 64 bit integer in *ret_u */
+int pa_atou64(const char *s, uint64_t *ret_u) {
+ char *x = NULL;
+ unsigned long long l;
+
+ pa_assert(s);
+ pa_assert(ret_u);
+
+ /* strtoull() ignores leading spaces. We don't. */
+ if (isspace((unsigned char)*s)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* strtoull() accepts strings that start with a minus sign. In that case the
+ * original negative number gets negated, and strtoull() returns the negated
+ * result. We don't want that kind of behaviour. strtoull() also allows a
+ * leading plus sign, which is also a thing that we don't want. */
+ if (*s == '-' || *s == '+') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ errno = 0;
+ l = strtoull(s, &x, 0);
+
+ /* If x doesn't point to the end of s, there was some trailing garbage in
+ * the string. If x points to s, no conversion was done (empty string). */
+ if (!x || *x || x == s || errno) {
+ if (!errno)
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((uint64_t) l != l) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ *ret_u = (uint64_t) l;
+
+ return 0;
+}
+
/* Convert the string s to a signed long integer in *ret_l. */
int pa_atol(const char *s, long *ret_l) {
char *x = NULL;
@@ -2281,6 +2325,49 @@ int pa_atol(const char *s, long *ret_l) {
return 0;
}
+/* Convert the string s to a signed 64 bit integer in *ret_l. */
+int pa_atoi64(const char *s, int64_t *ret_l) {
+ char *x = NULL;
+ long long l;
+
+ pa_assert(s);
+ pa_assert(ret_l);
+
+ /* strtoll() ignores leading spaces. We don't. */
+ if (isspace((unsigned char)*s)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* strtoll() accepts leading plus signs, but that's ugly, so we don't allow
+ * that. */
+ if (*s == '+') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ errno = 0;
+ l = strtoll(s, &x, 0);
+
+ /* If x doesn't point to the end of s, there was some trailing garbage in
+ * the string. If x points to s, no conversion was done (at least an empty
+ * string can trigger this). */
+ if (!x || *x || x == s || errno) {
+ if (!errno)
+ errno = EINVAL;
+ return -1;
+ }
+
+ *ret_l = l;
+
+ if ((int64_t) l != l) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ return 0;
+}
+
#ifdef HAVE_STRTOD_L
static locale_t c_locale = NULL;
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 9440af917..3117df882 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -151,6 +151,8 @@ int pa_atoi(const char *s, int32_t *ret_i);
int pa_atou(const char *s, uint32_t *ret_u);
int pa_atol(const char *s, long *ret_l);
int pa_atod(const char *s, double *ret_d);
+int pa_atoi64(const char *s, int64_t *ret_l);
+int pa_atou64(const char *s, uint64_t *ret_u);
size_t pa_snprintf(char *str, size_t size, const char *format, ...);
size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
From 8140932afdccbe29e7b03456f965b28a0abf24bb Mon Sep 17 00:00:00 2001
From: Georg Chini
Date: Tue, 14 Jan 2020 16:41:59 +0100
Subject: [PATCH 025/865] message-params: Add read/write functions for various
simple data types
The following functions have been added:
pa_message_params_write_double() - writes a double to a pa_message_params structure
pa_message_params_write_int64() - writes an integer to a pa_message_params structure
pa_message_params_write_uint64() - writes an unsigned to a pa_message_params structure
pa_message_params_write_bool() - writes a boolean to a pa_message_params structure
pa_message_params_read_double() - read a double from a parameter list
pa_message_params_read_int64() - read an integer from a parameter list
pa_message_params_read_uint64() - read an unsigned from a parameter list
pa_message_params_read_bool() - read a boolean from a parameter list
The patch also improves the doxygen documentation im message-params.h
Part-of:
---
src/map-file | 8 ++
src/pulse/message-params.c | 192 +++++++++++++++++++++++++++++++++++--
src/pulse/message-params.h | 62 +++++++++++-
3 files changed, 250 insertions(+), 12 deletions(-)
diff --git a/src/map-file b/src/map-file
index 4d196c11d..172fd441d 100644
--- a/src/map-file
+++ b/src/map-file
@@ -233,11 +233,19 @@ pa_message_params_begin_list;
pa_message_params_end_list;
pa_message_params_free;
pa_message_params_new;
+pa_message_params_read_bool;
+pa_message_params_read_double;
+pa_message_params_read_int64;
pa_message_params_read_raw;
pa_message_params_read_string;
+pa_message_params_read_uint64;
pa_message_params_to_string_free;
+pa_message_params_write_bool;
+pa_message_params_write_double;
+pa_message_params_write_int64;
pa_message_params_write_raw;
pa_message_params_write_string;
+pa_message_params_write_uint64;
pa_msleep;
pa_thread_make_realtime;
pa_operation_cancel;
diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c
index 236800bca..8885f95e8 100644
--- a/src/pulse/message-params.c
+++ b/src/pulse/message-params.c
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
@@ -62,7 +63,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) {
/* Empty or no string */
if (!current || *current == 0)
- return 0;
+ return PA_MESSAGE_PARAMS_LIST_END;
/* Find opening brace */
while (*current != 0) {
@@ -79,7 +80,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) {
/* unexpected closing brace, parse error */
if (*current == '}' && !found_backslash)
- return -1;
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
found_backslash = false;
current++;
@@ -87,7 +88,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) {
/* No opening brace found, end of string */
if (*current == 0)
- return 0;
+ return PA_MESSAGE_PARAMS_LIST_END;
if (is_unpacked)
*is_unpacked = true;
@@ -118,7 +119,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) {
/* Parse error, closing brace missing */
if (open_braces != 0) {
*result = NULL;
- return -1;
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
}
/* Replace } with 0 */
@@ -126,7 +127,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) {
*state = current + 1;
- return 1;
+ return PA_MESSAGE_PARAMS_OK;
}
/* Read functions */
@@ -143,13 +144,13 @@ int pa_message_params_read_string(char *c, const char **result, void **state) {
pa_assert(result);
- if ((r = split_list(c, &start_pos, &is_unpacked, state)) == 1)
+ if ((r = split_list(c, &start_pos, &is_unpacked, state)) == PA_MESSAGE_PARAMS_OK)
value = start_pos;
/* Check if we got a plain string not containing further lists */
if (!is_unpacked) {
/* Parse error */
- r = -1;
+ r = PA_MESSAGE_PARAMS_PARSE_ERROR;
value = NULL;
}
@@ -165,6 +166,129 @@ int pa_message_params_read_raw(char *c, char **result, void **state) {
return split_list(c, result, NULL, state);
}
+/* Read a double from the parameter list. The state pointer is
+ * advanced to the next element of the list. */
+int pa_message_params_read_double(char *c, double *result, void **state) {
+ char *start_pos, *end_pos, *s;
+ int err;
+ struct lconv *locale;
+ double value;
+ bool is_unpacked = true;
+
+ pa_assert(result);
+
+ if ((err = split_list(c, &start_pos, &is_unpacked, state)) != PA_MESSAGE_PARAMS_OK)
+ return err;
+
+ /* Empty element */
+ if (!*start_pos)
+ return PA_MESSAGE_PARAMS_IS_NULL;
+
+ /* Check if we got a plain string not containing further lists */
+ if (!is_unpacked)
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+
+ /* Convert to double */
+ locale = localeconv();
+
+ /* Replace decimal point with the correct character for the
+ * current locale. This assumes that no thousand separator
+ * is used. */
+ for (s = start_pos; *s; s++) {
+ if (*s == '.' || *s == ',')
+ *s = *locale->decimal_point;
+ }
+
+ /* Convert to double */
+ errno = 0;
+ value = strtod(start_pos, &end_pos);
+
+ /* Conversion error or string contains invalid characters. If the
+ * whole string was used for conversion, end_pos should point to
+ * the end of the string. */
+ if (errno != 0 || *end_pos != 0 || end_pos == start_pos)
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+
+ *result = value;
+ return PA_MESSAGE_PARAMS_OK;
+}
+
+/* Read an integer from the parameter list. The state pointer is
+ * advanced to the next element of the list. */
+int pa_message_params_read_int64(char *c, int64_t *result, void **state) {
+ char *start_pos;
+ int err;
+ int64_t value;
+ bool is_unpacked = true;
+
+ pa_assert(result);
+
+ if ((err = split_list(c, &start_pos, &is_unpacked, state)) != PA_MESSAGE_PARAMS_OK)
+ return err;
+
+ /* Empty element */
+ if (!*start_pos)
+ return PA_MESSAGE_PARAMS_IS_NULL;
+
+ /* Check if we got a plain string not containing further lists */
+ if (!is_unpacked)
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+
+ /* Convert to int64 */
+ if (pa_atoi64(start_pos, &value) < 0)
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+
+ *result = value;
+ return PA_MESSAGE_PARAMS_OK;
+}
+
+/* Read an unsigned integer from the parameter list. The state pointer is
+ * advanced to the next element of the list. */
+int pa_message_params_read_uint64(char *c, uint64_t *result, void **state) {
+ char *start_pos;
+ int err;
+ uint64_t value;
+ bool is_unpacked = true;
+
+ pa_assert(result);
+
+ if ((err = split_list(c, &start_pos, &is_unpacked, state)) != PA_MESSAGE_PARAMS_OK)
+ return err;
+
+ /* Empty element */
+ if (!*start_pos)
+ return PA_MESSAGE_PARAMS_IS_NULL;
+
+ /* Check if we got a plain string not containing further lists */
+ if (!is_unpacked)
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+
+ /* Convert to int64 */
+ if (pa_atou64(start_pos, &value) < 0)
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+
+ *result = value;
+ return PA_MESSAGE_PARAMS_OK;
+}
+
+/* Read a boolean from the parameter list. The state pointer is
+ * advanced to the next element of the list. */
+int pa_message_params_read_bool(char *c, bool *result, void **state) {
+ int err;
+ uint64_t value;
+
+ pa_assert(result);
+
+ if ((err = pa_message_params_read_uint64(c, &value, state)) != PA_MESSAGE_PARAMS_OK)
+ return err;
+
+ *result = false;
+ if (value)
+ *result = true;
+
+ return PA_MESSAGE_PARAMS_OK;
+}
+
/* Write functions. The functions are wrapper functions around pa_strbuf,
* so that the client does not need to use pa_strbuf directly. */
@@ -239,7 +363,8 @@ void pa_message_params_write_string(pa_message_params *params, const char *value
void pa_message_params_write_raw(pa_message_params *params, const char *value, bool add_braces) {
pa_assert(params);
- /* Null value is written as empty element */
+ /* Null value is written as empty element if add_braces is true.
+ * Otherwise nothing is written. */
if (!value)
value = "";
@@ -248,3 +373,54 @@ void pa_message_params_write_raw(pa_message_params *params, const char *value, b
else
pa_strbuf_puts(params->buffer, value);
}
+
+/* Writes a double to a message_params structure, adding curly braces.
+ * precision gives the number of significant digits, not digits after
+ * the decimal point. */
+void pa_message_params_write_double(pa_message_params *params, double value, int precision) {
+ char *buf, *s;
+
+ pa_assert(params);
+
+ /* We do not care about locale because we do not know which locale is
+ * used on the server side. If the decimal separator is a comma, we
+ * replace it with a dot to achieve consistent output on all locales. */
+ buf = pa_sprintf_malloc("{%.*g}", precision, value);
+ for (s = buf; *s; s++) {
+ if (*s == ',') {
+ *s = '.';
+ break;
+ }
+ }
+
+ pa_strbuf_puts(params->buffer, buf);
+
+ pa_xfree(buf);
+}
+
+/* Writes an integer to a message_param structure, adding curly braces. */
+void pa_message_params_write_int64(pa_message_params *params, int64_t value) {
+
+ pa_assert(params);
+
+ pa_strbuf_printf(params->buffer, "{%lli}", (long long)value);
+}
+
+/* Writes an unsigned integer to a message_params structure, adding curly braces. */
+void pa_message_params_write_uint64(pa_message_params *params, uint64_t value) {
+
+ pa_assert(params);
+
+ pa_strbuf_printf(params->buffer, "{%llu}", (unsigned long long)value);
+}
+
+/* Writes a boolean to a message_params structure, adding curly braces. */
+void pa_message_params_write_bool(pa_message_params *params, bool value) {
+
+ pa_assert(params);
+
+ if (value)
+ pa_strbuf_puts(params->buffer, "{1}");
+ else
+ pa_strbuf_puts(params->buffer, "{0}");
+}
diff --git a/src/pulse/message-params.h b/src/pulse/message-params.h
index f30164ff9..a2e0f9e8d 100644
--- a/src/pulse/message-params.h
+++ b/src/pulse/message-params.h
@@ -26,28 +26,68 @@
#include
/** \file
- * Utility functions for reading and writing message parameters */
+ * Utility functions for reading and writing message parameters.
+ * All read functions return a value from pa_message_params_error_code
+ * and the read value in result (or *result for string functions).
+ * The string read functions read_string() and read_raw() return a pointer
+ * to a sub-string within the parameter list in *result, therefore the
+ * string in *result must not be freed and is only valid within the
+ * message handler callback function. If the string is needed outside
+ * the callback, it must be copied using pa_xstrdup().
+ * When a read function is called, the state pointer is advanced to the
+ * next list element. The variable state points to should be initialized
+ * to NULL before the first call.\n
+ * Write functions operate on a pa_message_params structure which is a
+ * wrapper for pa_strbuf. A parameter list or sub-list is started by a
+ * call to begin_list() and ended by a call to end_list().
+ * A pa_message_params structure must be converted to a string using
+ * pa_message_params_to_string_free() before it can be passed to a
+ * message handler. */
PA_C_DECL_BEGIN
/** Structure which holds a parameter list. Wrapper for pa_strbuf \since 15.0 */
typedef struct pa_message_params pa_message_params;
+/** Read function return values \since 15.0 */
+enum pa_message_params_error_code {
+ /** No value (empty element) found for numeric or boolean value */
+ PA_MESSAGE_PARAMS_IS_NULL = -2,
+ /** Error encountered while parsing a value */
+ PA_MESSAGE_PARAMS_PARSE_ERROR = -1,
+ /** End of parameter list reached */
+ PA_MESSAGE_PARAMS_LIST_END = 0,
+ /** Parsing successful */
+ PA_MESSAGE_PARAMS_OK = 1,
+};
+
/** @{ \name Read functions */
-/** Read raw data from a parameter list. Used to split a message parameter
+/** Read a boolean from parameter list in c. \since 15.0 */
+int pa_message_params_read_bool(char *c, bool *result, void **state);
+
+/** Read a double from parameter list in c. \since 15.0 */
+int pa_message_params_read_double(char *c, double *result, void **state);
+
+/** Read an integer from parameter list in c. \since 15.0 */
+int pa_message_params_read_int64(char *c, int64_t *result, void **state);
+
+/** Read raw data from parameter list in c. Used to split a message parameter
* string into list elements. The string returned in *result must not be freed. \since 15.0 */
int pa_message_params_read_raw(char *c, char **result, void **state);
-/** Read a string from a parameter list. Escaped curly braces and backslashes
+/** Read a string from a parameter list in c. Escaped curly braces and backslashes
* will be unescaped. \since 15.0 */
int pa_message_params_read_string(char *c, const char **result, void **state);
+/** Read an unsigned integer from parameter list in c. \since 15.0 */
+int pa_message_params_read_uint64(char *c, uint64_t *result, void **state);
+
/** @} */
/** @{ \name Write functions */
-/** Create a new pa_message_params structure \since 15.0 */
+/** Create a new pa_message_params structure. \since 15.0 */
pa_message_params *pa_message_params_new(void);
/** Free a pa_message_params structure. \since 15.0 */
@@ -62,6 +102,17 @@ void pa_message_params_begin_list(pa_message_params *params);
/** End a list by writing a closing brace. \since 15.0 */
void pa_message_params_end_list(pa_message_params *params);
+/** Append a boolean to parameter list. \since 15.0 */
+void pa_message_params_write_bool(pa_message_params *params, bool value);
+
+/** Append a double to parameter list. Precision gives the number of
+ * significant digits. The decimal separator will always be written as
+ * dot, regardless which locale is used. \since 15.0 */
+void pa_message_params_write_double(pa_message_params *params, double value, int precision);
+
+/** Append an integer to parameter list. \since 15.0 */
+void pa_message_params_write_int64(pa_message_params *params, int64_t value);
+
/** Append string to parameter list. Curly braces and backslashes will be escaped. \since 15.0 */
void pa_message_params_write_string(pa_message_params *params, const char *value);
@@ -70,6 +121,9 @@ void pa_message_params_write_string(pa_message_params *params, const char *value
* the string if add_braces is true. \since 15.0 */
void pa_message_params_write_raw(pa_message_params *params, const char *value, bool add_braces);
+/** Append an unsigned integer to parameter list. \since 15.0 */
+void pa_message_params_write_uint64(pa_message_params *params, uint64_t value);
+
/** @} */
PA_C_DECL_END
From ff64defc137f61f1376653f523b5aa17f139ae2c Mon Sep 17 00:00:00 2001
From: Georg Chini
Date: Tue, 14 Jan 2020 20:50:24 +0100
Subject: [PATCH 026/865] message-params: Add read functions for arrays
The following new functions have been added:
pa_message_params_read_double_array() - read an array of double from list
pa_message_params_read_int64_array() - read an array of int64 from list
pa_message_params_read_uint64_array() - read an array of uint64 from list
pa_message_params_read_string_array() - read an array of strings from list
Part-of:
---
src/map-file | 4 +
src/pulse/message-params.c | 173 ++++++++++++++++++++++++++++++++++++-
src/pulse/message-params.h | 29 ++++++-
3 files changed, 204 insertions(+), 2 deletions(-)
diff --git a/src/map-file b/src/map-file
index 172fd441d..7b1ed5206 100644
--- a/src/map-file
+++ b/src/map-file
@@ -235,10 +235,14 @@ pa_message_params_free;
pa_message_params_new;
pa_message_params_read_bool;
pa_message_params_read_double;
+pa_message_params_read_double_array;
pa_message_params_read_int64;
+pa_message_params_read_int64_array;
pa_message_params_read_raw;
pa_message_params_read_string;
+pa_message_params_read_string_array;
pa_message_params_read_uint64;
+pa_message_params_read_uint64_array;
pa_message_params_to_string_free;
pa_message_params_write_bool;
pa_message_params_write_double;
diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c
index 8885f95e8..7d2dd8020 100644
--- a/src/pulse/message-params.c
+++ b/src/pulse/message-params.c
@@ -39,6 +39,61 @@ struct pa_message_params {
pa_strbuf *buffer;
};
+/* Helper functions */
+
+/* Count number of top level elements in parameter list */
+static int count_elements(const char *c) {
+ const char *s;
+ uint32_t element_count;
+ bool found_element, found_backslash;
+ int open_braces;
+
+ if (!c || *c == 0)
+ return PA_MESSAGE_PARAMS_LIST_END;
+
+ element_count = 0;
+ open_braces = 0;
+ found_element = false;
+ found_backslash = false;
+ s = c;
+
+ /* Count elements in list */
+ while (*s != 0) {
+
+ /* Skip escaped curly braces. */
+ if (*s == '\\' && !found_backslash) {
+ found_backslash = true;
+ s++;
+ continue;
+ }
+
+ if (*s == '{' && !found_backslash) {
+ found_element = true;
+ open_braces++;
+ }
+ if (*s == '}' && !found_backslash)
+ open_braces--;
+
+ /* unexpected closing brace, parse error */
+ if (open_braces < 0)
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+
+ if (open_braces == 0 && found_element) {
+ element_count++;
+ found_element = false;
+ }
+
+ found_backslash = false;
+ s++;
+ }
+
+ /* missing closing brace, parse error */
+ if (open_braces > 0)
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+
+ return element_count;
+}
+
/* Split the specified string into elements. An element is defined as
* a sub-string between curly braces. The function is needed to parse
* the parameters of messages. Each time it is called it returns the
@@ -188,7 +243,7 @@ int pa_message_params_read_double(char *c, double *result, void **state) {
if (!is_unpacked)
return PA_MESSAGE_PARAMS_PARSE_ERROR;
- /* Convert to double */
+ /* Get decimal separator for current locale */
locale = localeconv();
/* Replace decimal point with the correct character for the
@@ -289,6 +344,122 @@ int pa_message_params_read_bool(char *c, bool *result, void **state) {
return PA_MESSAGE_PARAMS_OK;
}
+/* Converts a parameter list to a string array. Escaping is removed from
+ * the strings. Returns an array of pointers to sub-strings within c in
+ * *results. The returned array must be freed, but not the strings
+ * within the array. The function returns the number of strings in the
+ * array. */
+int pa_message_params_read_string_array(char *c, const char ***results) {
+ void *state = NULL;
+ uint32_t element_count, i;
+ int err;
+ const char **values;
+
+ pa_assert(results);
+
+ /* Count elements, return if no element was found or parse error. */
+ if ((element_count = count_elements(c)) <= 0)
+ return element_count;
+
+ /* Allocate array */
+ values = pa_xmalloc0(element_count * sizeof(char *));
+
+ for (i = 0; (err = pa_message_params_read_string(c, &(values[i]), &state)) > 0; i++)
+ ;
+
+ if (err < 0) {
+ pa_xfree(values);
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+ }
+
+ *results = values;
+ return element_count;
+}
+
+/* Converts a parameter list to a double array. */
+int pa_message_params_read_double_array(char *c, double **results) {
+ double *values;
+ void *state = NULL;
+ uint32_t element_count, i;
+ int err;
+
+ pa_assert(results);
+
+ /* Count elements, return if no element was found or parse error. */
+ if ((element_count = count_elements(c)) <= 0)
+ return element_count;
+
+ /* Allocate array */
+ values = pa_xmalloc0(element_count * sizeof(double));
+
+ for (i = 0; (err = pa_message_params_read_double(c, &(values[i]), &state)) > 0; i++)
+ ;
+
+ if (err < 0) {
+ pa_xfree(values);
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+ }
+
+ *results = values;
+ return element_count;
+}
+
+/* Converts a parameter list to an int64 array. */
+int pa_message_params_read_int64_array(char *c, int64_t **results) {
+ int64_t *values;
+ void *state = NULL;
+ uint32_t element_count, i;
+ int err;
+
+ pa_assert(results);
+
+ /* Count elements, return if no element was found or parse error. */
+ if ((element_count = count_elements(c)) <= 0)
+ return element_count;
+
+ /* Allocate array */
+ values = pa_xmalloc0(element_count * sizeof(int64_t));
+
+ for (i = 0; (err = pa_message_params_read_int64(c, &(values[i]), &state)) > 0; i++)
+ ;
+
+ if (err < 0) {
+ pa_xfree(values);
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+ }
+
+ *results = values;
+ return element_count;
+}
+
+/* Converts a parameter list to an uint64 array. */
+int pa_message_params_read_uint64_array(char *c, uint64_t **results) {
+ uint64_t *values;
+ void *state = NULL;
+ uint32_t element_count, i;
+ int err;
+
+ pa_assert(results);
+
+ /* Count elements, return if no element was found or parse error. */
+ if ((element_count = count_elements(c)) <= 0)
+ return element_count;
+
+ /* Allocate array */
+ values = pa_xmalloc0(element_count * sizeof(uint64_t));
+
+ for (i = 0; (err = pa_message_params_read_uint64(c, &(values[i]), &state)) > 0; i++)
+ ;
+
+ if (err < 0) {
+ pa_xfree(values);
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+ }
+
+ *results = values;
+ return element_count;
+}
+
/* Write functions. The functions are wrapper functions around pa_strbuf,
* so that the client does not need to use pa_strbuf directly. */
diff --git a/src/pulse/message-params.h b/src/pulse/message-params.h
index a2e0f9e8d..3d90eac3c 100644
--- a/src/pulse/message-params.h
+++ b/src/pulse/message-params.h
@@ -36,7 +36,12 @@
* the callback, it must be copied using pa_xstrdup().
* When a read function is called, the state pointer is advanced to the
* next list element. The variable state points to should be initialized
- * to NULL before the first call.\n
+ * to NULL before the first call.
+ * All read functions except read_raw() preserve a default value passed
+ * in result if the call fails. For the array functions, results must be
+ * initialized prior to the call either to NULL or to an array with default
+ * values. If the function succeeds, the default array will be freed and
+ * the number of elements in the result array is returned.\n\n
* Write functions operate on a pa_message_params structure which is a
* wrapper for pa_strbuf. A parameter list or sub-list is started by a
* call to begin_list() and ended by a call to end_list().
@@ -69,9 +74,19 @@ int pa_message_params_read_bool(char *c, bool *result, void **state);
/** Read a double from parameter list in c. \since 15.0 */
int pa_message_params_read_double(char *c, double *result, void **state);
+/** Converts a parameter list to a double array. Empty elements in the parameter
+ * list are treated as error. Before the call, results must be initialized, either
+ * to NULL or to an array with default values. \since 15.0 */
+int pa_message_params_read_double_array(char *c, double **results);
+
/** Read an integer from parameter list in c. \since 15.0 */
int pa_message_params_read_int64(char *c, int64_t *result, void **state);
+/** Converts a parameter list to an int64 array. Empty elements in the parameter
+ * list are treated as error. Before the call, results must be initialized, either
+ * to NULL or to an array with default values. \since 15.0 */
+int pa_message_params_read_int64_array(char *c, int64_t **results);
+
/** Read raw data from parameter list in c. Used to split a message parameter
* string into list elements. The string returned in *result must not be freed. \since 15.0 */
int pa_message_params_read_raw(char *c, char **result, void **state);
@@ -80,9 +95,21 @@ int pa_message_params_read_raw(char *c, char **result, void **state);
* will be unescaped. \since 15.0 */
int pa_message_params_read_string(char *c, const char **result, void **state);
+/** Convert a parameter list to a string array. Escaping is removed from
+ * the strings. Returns an array of pointers to sub-strings within c in
+ * *results. The returned array must be freed, but not the strings
+ * within the array. Before the call, results must be initialized, either
+ * to NULL or to an array with default values. \since 15.0 */
+int pa_message_params_read_string_array(char *c, const char ***results);
+
/** Read an unsigned integer from parameter list in c. \since 15.0 */
int pa_message_params_read_uint64(char *c, uint64_t *result, void **state);
+/** Converts a parameter list to an uint64 array. Empty elements in the parameter
+ * list are treated as error. Before the call, results must be initialized, either
+ * to NULL or to an array with default values. \since 15.0 */
+int pa_message_params_read_uint64_array(char *c, uint64_t **results);
+
/** @} */
/** @{ \name Write functions */
From 6722b2d8c422493829c4c57ca5ec1919c7c74231 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Tue, 9 Jun 2020 07:56:17 +0300
Subject: [PATCH 027/865] core-util: Make range checks easier to read
It wasn't immediately obvious to me what these checks are supposed to
do. Explicitly checking against the min/max values should make the code
easier to understand.
Part-of:
---
src/pulsecore/core-util.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index d421a7572..a6473c14a 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2189,7 +2189,7 @@ int pa_atoi(const char *s, int32_t *ret_i) {
if (pa_atol(s, &l) < 0)
return -1;
- if ((int32_t) l != l) {
+ if (l < INT32_MIN || l > INT32_MAX) {
errno = ERANGE;
return -1;
}
@@ -2233,7 +2233,7 @@ int pa_atou(const char *s, uint32_t *ret_u) {
return -1;
}
- if ((uint32_t) l != l) {
+ if (l > UINT32_MAX) {
errno = ERANGE;
return -1;
}
@@ -2277,7 +2277,7 @@ int pa_atou64(const char *s, uint64_t *ret_u) {
return -1;
}
- if ((uint64_t) l != l) {
+ if (l > UINT64_MAX) {
errno = ERANGE;
return -1;
}
@@ -2360,7 +2360,7 @@ int pa_atoi64(const char *s, int64_t *ret_l) {
*ret_l = l;
- if ((int64_t) l != l) {
+ if (l < INT64_MIN || l > INT64_MAX) {
errno = ERANGE;
return -1;
}
From 5d022a07a635568f7964e24b326cebaf8d64b7da Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Sat, 11 Jul 2020 13:58:36 +0300
Subject: [PATCH 028/865] introspect: Add version check to
pa_context_send_message_to_object()
If an application calls the function when the server doesn't support the
feature, the result should be just an error from the function. Without
the check the whole connection gets terminated due to protocol error.
Part-of:
---
src/pulse/introspect.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 0671fa163..daae4cac4 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -2262,6 +2262,7 @@ pa_operation* pa_context_send_message_to_object(pa_context *c, const char *objec
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 35, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
From d50145a9160ffcba49c1af0eec571d18f318b30e Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Wed, 22 Jul 2020 16:06:23 +0300
Subject: [PATCH 029/865] core-util: Reduce repetition in number parsing
functions
Part-of:
---
src/pulsecore/core-util.c | 80 +++++++++++++++------------------------
1 file changed, 31 insertions(+), 49 deletions(-)
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index a6473c14a..5b6062f61 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2199,6 +2199,32 @@ int pa_atoi(const char *s, int32_t *ret_i) {
return 0;
}
+enum numtype {
+ NUMTYPE_UINT,
+ NUMTYPE_INT,
+ NUMTYPE_DOUBLE,
+};
+
+/* A helper function for pa_atou() and friends. This does some common checks,
+ * because our number parsing is more strict than the strtoX functions. */
+static int prepare_number_string(const char *s, enum numtype type) {
+ /* The strtoX functions ignore leading spaces, we don't. */
+ if (isspace((unsigned char) s[0]))
+ return -1;
+
+ /* The strtoX functions accept a plus sign, we don't. */
+ if (s[0] == '+')
+ return -1;
+
+ /* The strtoul and strtoull functions allow a minus sign even though they
+ * parse an unsigned number. In case of a minus sign the original negative
+ * number gets negated. We don't want that kind of behviour. */
+ if (type == NUMTYPE_UINT && s[0] == '-')
+ return -1;
+
+ return 0;
+}
+
/* Convert the string s to an unsigned integer in *ret_u */
int pa_atou(const char *s, uint32_t *ret_u) {
char *x = NULL;
@@ -2207,17 +2233,7 @@ int pa_atou(const char *s, uint32_t *ret_u) {
pa_assert(s);
pa_assert(ret_u);
- /* strtoul() ignores leading spaces. We don't. */
- if (isspace((unsigned char)*s)) {
- errno = EINVAL;
- return -1;
- }
-
- /* strtoul() accepts strings that start with a minus sign. In that case the
- * original negative number gets negated, and strtoul() returns the negated
- * result. We don't want that kind of behaviour. strtoul() also allows a
- * leading plus sign, which is also a thing that we don't want. */
- if (*s == '-' || *s == '+') {
+ if (prepare_number_string(s, NUMTYPE_UINT) < 0) {
errno = EINVAL;
return -1;
}
@@ -2251,17 +2267,7 @@ int pa_atou64(const char *s, uint64_t *ret_u) {
pa_assert(s);
pa_assert(ret_u);
- /* strtoull() ignores leading spaces. We don't. */
- if (isspace((unsigned char)*s)) {
- errno = EINVAL;
- return -1;
- }
-
- /* strtoull() accepts strings that start with a minus sign. In that case the
- * original negative number gets negated, and strtoull() returns the negated
- * result. We don't want that kind of behaviour. strtoull() also allows a
- * leading plus sign, which is also a thing that we don't want. */
- if (*s == '-' || *s == '+') {
+ if (prepare_number_string(s, NUMTYPE_UINT) < 0) {
errno = EINVAL;
return -1;
}
@@ -2295,15 +2301,7 @@ int pa_atol(const char *s, long *ret_l) {
pa_assert(s);
pa_assert(ret_l);
- /* strtol() ignores leading spaces. We don't. */
- if (isspace((unsigned char)*s)) {
- errno = EINVAL;
- return -1;
- }
-
- /* strtol() accepts leading plus signs, but that's ugly, so we don't allow
- * that. */
- if (*s == '+') {
+ if (prepare_number_string(s, NUMTYPE_INT) < 0) {
errno = EINVAL;
return -1;
}
@@ -2333,15 +2331,7 @@ int pa_atoi64(const char *s, int64_t *ret_l) {
pa_assert(s);
pa_assert(ret_l);
- /* strtoll() ignores leading spaces. We don't. */
- if (isspace((unsigned char)*s)) {
- errno = EINVAL;
- return -1;
- }
-
- /* strtoll() accepts leading plus signs, but that's ugly, so we don't allow
- * that. */
- if (*s == '+') {
+ if (prepare_number_string(s, NUMTYPE_INT) < 0) {
errno = EINVAL;
return -1;
}
@@ -2383,15 +2373,7 @@ int pa_atod(const char *s, double *ret_d) {
pa_assert(s);
pa_assert(ret_d);
- /* strtod() ignores leading spaces. We don't. */
- if (isspace((unsigned char)*s)) {
- errno = EINVAL;
- return -1;
- }
-
- /* strtod() accepts leading plus signs, but that's ugly, so we don't allow
- * that. */
- if (*s == '+') {
+ if (prepare_number_string(s, NUMTYPE_DOUBLE) < 0) {
errno = EINVAL;
return -1;
}
From 6bc00720f3213c80d4c7b8d7cd8ebcee8b43dcae Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Sat, 18 Jul 2020 10:13:26 +0300
Subject: [PATCH 030/865] core-util: Never parse integers as octal
I believe nobody needs to pass octal numbers to PulseAudio, and if we
encounter integer strings starting with zeros, the intention is to use
them in base 10. Hexadecimal numbers are more common, and they can't be
interpreted in base 10 anyway, so they are still supported.
Part-of:
---
src/pulsecore/core-util.c | 82 +++++++++++++++++++++++++++++++++++----
1 file changed, 74 insertions(+), 8 deletions(-)
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 5b6062f61..25498c5a0 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2206,9 +2206,29 @@ enum numtype {
};
/* A helper function for pa_atou() and friends. This does some common checks,
- * because our number parsing is more strict than the strtoX functions. */
-static int prepare_number_string(const char *s, enum numtype type) {
- /* The strtoX functions ignore leading spaces, we don't. */
+ * because our number parsing is more strict than the strtoX functions.
+ *
+ * Leading zeros are stripped from integers so that they don't get parsed as
+ * octal (but "0x" is preserved for hexadecimal numbers). For NUMTYPE_INT the
+ * zero stripping may involve allocating a new string, in which case it's
+ * stored in tmp. Otherwise tmp is set to NULL. The caller needs to free tmp
+ * after they're done with ret. When parsing other types than NUMTYPE_INT the
+ * caller can pass NULL as tmp.
+ *
+ * The final string to parse is returned in ret. ret will point either inside
+ * s or to tmp. */
+static int prepare_number_string(const char *s, enum numtype type, char **tmp, const char **ret) {
+ const char *original = s;
+ bool negative = false;
+
+ pa_assert(s);
+ pa_assert(type != NUMTYPE_INT || tmp);
+ pa_assert(ret);
+
+ if (tmp)
+ *tmp = NULL;
+
+ /* The strtoX functions accept leading spaces, we don't. */
if (isspace((unsigned char) s[0]))
return -1;
@@ -2222,6 +2242,44 @@ static int prepare_number_string(const char *s, enum numtype type) {
if (type == NUMTYPE_UINT && s[0] == '-')
return -1;
+ /* The strtoX functions interpret the number as octal if it starts with
+ * a zero. We prefer to use base 10, so we strip all leading zeros (if the
+ * string starts with "0x", strtoul() interprets it as hexadecimal, which
+ * is fine, because it's unambiguous unlike octal).
+ *
+ * While stripping the leading zeros, we have to remember to also handle
+ * the case where the number is negative, which makes the zero skipping
+ * code somewhat complex. */
+
+ /* Doubles don't need zero stripping, we can finish now. */
+ if (type == NUMTYPE_DOUBLE)
+ goto finish;
+
+ if (s[0] == '-') {
+ negative = true;
+ s++; /* Skip the minus sign. */
+ }
+
+ /* Don't skip zeros if the string starts with "0x". */
+ if (s[0] == '0' && s[1] != 'x') {
+ while (s[0] == '0' && s[1])
+ s++; /* Skip zeros. */
+ }
+
+ if (negative) {
+ s--; /* Go back one step, we need the minus sign back. */
+
+ /* If s != original, then we have skipped some zeros and we need to replace
+ * the last skipped zero with a minus sign. */
+ if (s != original) {
+ *tmp = pa_xstrdup(s);
+ *tmp[0] = '-';
+ s = *tmp;
+ }
+ }
+
+finish:
+ *ret = s;
return 0;
}
@@ -2233,7 +2291,7 @@ int pa_atou(const char *s, uint32_t *ret_u) {
pa_assert(s);
pa_assert(ret_u);
- if (prepare_number_string(s, NUMTYPE_UINT) < 0) {
+ if (prepare_number_string(s, NUMTYPE_UINT, NULL, &s) < 0) {
errno = EINVAL;
return -1;
}
@@ -2267,7 +2325,7 @@ int pa_atou64(const char *s, uint64_t *ret_u) {
pa_assert(s);
pa_assert(ret_u);
- if (prepare_number_string(s, NUMTYPE_UINT) < 0) {
+ if (prepare_number_string(s, NUMTYPE_UINT, NULL, &s) < 0) {
errno = EINVAL;
return -1;
}
@@ -2295,13 +2353,14 @@ int pa_atou64(const char *s, uint64_t *ret_u) {
/* Convert the string s to a signed long integer in *ret_l. */
int pa_atol(const char *s, long *ret_l) {
+ char *tmp;
char *x = NULL;
long l;
pa_assert(s);
pa_assert(ret_l);
- if (prepare_number_string(s, NUMTYPE_INT) < 0) {
+ if (prepare_number_string(s, NUMTYPE_INT, &tmp, &s) < 0) {
errno = EINVAL;
return -1;
}
@@ -2315,9 +2374,12 @@ int pa_atol(const char *s, long *ret_l) {
if (!x || *x || x == s || errno) {
if (!errno)
errno = EINVAL;
+ pa_xfree(tmp);
return -1;
}
+ pa_xfree(tmp);
+
*ret_l = l;
return 0;
@@ -2325,13 +2387,14 @@ int pa_atol(const char *s, long *ret_l) {
/* Convert the string s to a signed 64 bit integer in *ret_l. */
int pa_atoi64(const char *s, int64_t *ret_l) {
+ char *tmp;
char *x = NULL;
long long l;
pa_assert(s);
pa_assert(ret_l);
- if (prepare_number_string(s, NUMTYPE_INT) < 0) {
+ if (prepare_number_string(s, NUMTYPE_INT, &tmp, &s) < 0) {
errno = EINVAL;
return -1;
}
@@ -2345,9 +2408,12 @@ int pa_atoi64(const char *s, int64_t *ret_l) {
if (!x || *x || x == s || errno) {
if (!errno)
errno = EINVAL;
+ pa_xfree(tmp);
return -1;
}
+ pa_xfree(tmp);
+
*ret_l = l;
if (l < INT64_MIN || l > INT64_MAX) {
@@ -2373,7 +2439,7 @@ int pa_atod(const char *s, double *ret_d) {
pa_assert(s);
pa_assert(ret_d);
- if (prepare_number_string(s, NUMTYPE_DOUBLE) < 0) {
+ if (prepare_number_string(s, NUMTYPE_DOUBLE, NULL, &s) < 0) {
errno = EINVAL;
return -1;
}
From b76964e480812466c9f528a7fb7a589debefa2e1 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Thu, 23 Jul 2020 09:39:25 +0300
Subject: [PATCH 031/865] core-util-test: Test parsing integer strings with
leading zeros
Part-of:
---
src/tests/core-util-test.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/tests/core-util-test.c b/src/tests/core-util-test.c
index 8d1db0c07..1a9603ac2 100644
--- a/src/tests/core-util-test.c
+++ b/src/tests/core-util-test.c
@@ -100,6 +100,10 @@ START_TEST (modargs_test_atoi) {
ck_assert_int_eq(value, 100000);
ck_assert_int_eq(pa_atoi("-100000", &value), 0);
ck_assert_int_eq(value, -100000);
+ ck_assert_int_eq(pa_atoi("010", &value), 0);
+ ck_assert_int_eq(value, 10);
+ ck_assert_int_eq(pa_atoi("-010", &value), 0);
+ ck_assert_int_eq(value, -10);
// hexadecimal
ck_assert_int_eq(pa_atoi("0x100000", &value), 0);
@@ -120,6 +124,8 @@ START_TEST (modargs_test_atou) {
// decimal
ck_assert_int_eq(pa_atou("100000", &value), 0);
ck_assert_int_eq(value, 100000);
+ ck_assert_int_eq(pa_atou("010", &value), 0);
+ ck_assert_int_eq(value, 10);
// hexadecimal
ck_assert_int_eq(pa_atou("0x100000", &value), 0);
@@ -142,6 +148,10 @@ START_TEST (modargs_test_atol) {
ck_assert_int_eq(value, 100000l);
ck_assert_int_eq(pa_atol("-100000", &value), 0);
ck_assert_int_eq(value, -100000l);
+ ck_assert_int_eq(pa_atol("010", &value), 0);
+ ck_assert_int_eq(value, 10);
+ ck_assert_int_eq(pa_atol("-010", &value), 0);
+ ck_assert_int_eq(value, -10);
// hexadecimal
ck_assert_int_eq(pa_atol("0x100000", &value), 0);
From ef5a2f15541cfe5e325be9136c84a7945ac54404 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Thu, 23 Jul 2020 09:49:12 +0300
Subject: [PATCH 032/865] core-util-test: Drop "modargs" from test function
names
These tests aren't directly related to modargs. I suppose these were
first written for the modargs.h API and renaming was forgotten.
Part-of:
---
src/tests/core-util-test.c | 52 +++++++++++++++++++-------------------
1 file changed, 26 insertions(+), 26 deletions(-)
diff --git a/src/tests/core-util-test.c b/src/tests/core-util-test.c
index 1a9603ac2..e48cb3807 100644
--- a/src/tests/core-util-test.c
+++ b/src/tests/core-util-test.c
@@ -26,7 +26,7 @@
#include
#include
-START_TEST (modargs_test_parse_boolean) {
+START_TEST (test_parse_boolean) {
ck_assert_int_eq(pa_parse_boolean("true"), true);
ck_assert_int_eq(pa_parse_boolean("yes"), true);
ck_assert_int_eq(pa_parse_boolean("1"), true);
@@ -40,7 +40,7 @@ START_TEST (modargs_test_parse_boolean) {
}
END_TEST
-START_TEST (modargs_test_parse_volume) {
+START_TEST (test_parse_volume) {
pa_volume_t value;
// dB volumes
@@ -92,7 +92,7 @@ START_TEST (modargs_test_parse_volume) {
}
END_TEST
-START_TEST (modargs_test_atoi) {
+START_TEST (test_atoi) {
int32_t value;
// decimal
@@ -118,7 +118,7 @@ START_TEST (modargs_test_atoi) {
}
END_TEST
-START_TEST (modargs_test_atou) {
+START_TEST (test_atou) {
uint32_t value;
// decimal
@@ -140,7 +140,7 @@ START_TEST (modargs_test_atou) {
}
END_TEST
-START_TEST (modargs_test_atol) {
+START_TEST (test_atol) {
long value;
// decimal
@@ -166,7 +166,7 @@ START_TEST (modargs_test_atol) {
}
END_TEST
-START_TEST (modargs_test_atod) {
+START_TEST (test_atod) {
double value;
double epsilon = 0.001;
@@ -187,7 +187,7 @@ START_TEST (modargs_test_atod) {
}
END_TEST
-START_TEST (modargs_test_replace) {
+START_TEST (test_replace) {
char* value;
value = pa_replace("abcde", "bcd", "XYZ");
@@ -208,22 +208,22 @@ START_TEST (modargs_test_replace) {
}
END_TEST
-START_TEST (modargs_test_replace_fail_1) {
+START_TEST (test_replace_fail_1) {
pa_replace(NULL, "b", "bab");
}
END_TEST
-START_TEST (modargs_test_replace_fail_2) {
+START_TEST (test_replace_fail_2) {
pa_replace("abe", NULL, "bab");
}
END_TEST
-START_TEST (modargs_test_replace_fail_3) {
+START_TEST (test_replace_fail_3) {
pa_replace("abcde", "b", NULL);
}
END_TEST
-START_TEST (modargs_test_escape) {
+START_TEST (test_escape) {
char* value;
value = pa_escape("abcde", "bcd");
@@ -240,12 +240,12 @@ START_TEST (modargs_test_escape) {
}
END_TEST
-START_TEST (modargs_test_replace_fail_4) {
+START_TEST (test_replace_fail_4) {
pa_replace("abe", "", "bab");
}
END_TEST
-START_TEST (modargs_test_unescape) {
+START_TEST (test_unescape) {
char* value;
value = pa_unescape(pa_xstrdup("a\\b\\c\\de"));
@@ -271,19 +271,19 @@ int main(int argc, char *argv[]) {
tc = tcase_create("core-util");
suite_add_tcase(s, tc);
- tcase_add_test(tc, modargs_test_parse_boolean);
- tcase_add_test(tc, modargs_test_parse_volume);
- tcase_add_test(tc, modargs_test_atoi);
- tcase_add_test(tc, modargs_test_atou);
- tcase_add_test(tc, modargs_test_atol);
- tcase_add_test(tc, modargs_test_atod);
- tcase_add_test(tc, modargs_test_replace);
- tcase_add_test_raise_signal(tc, modargs_test_replace_fail_1, SIGABRT);
- tcase_add_test_raise_signal(tc, modargs_test_replace_fail_2, SIGABRT);
- tcase_add_test_raise_signal(tc, modargs_test_replace_fail_3, SIGABRT);
- tcase_add_test_raise_signal(tc, modargs_test_replace_fail_4, SIGABRT);
- tcase_add_test(tc, modargs_test_escape);
- tcase_add_test(tc, modargs_test_unescape);
+ tcase_add_test(tc, test_parse_boolean);
+ tcase_add_test(tc, test_parse_volume);
+ tcase_add_test(tc, test_atoi);
+ tcase_add_test(tc, test_atou);
+ tcase_add_test(tc, test_atol);
+ tcase_add_test(tc, test_atod);
+ tcase_add_test(tc, test_replace);
+ tcase_add_test_raise_signal(tc, test_replace_fail_1, SIGABRT);
+ tcase_add_test_raise_signal(tc, test_replace_fail_2, SIGABRT);
+ tcase_add_test_raise_signal(tc, test_replace_fail_3, SIGABRT);
+ tcase_add_test_raise_signal(tc, test_replace_fail_4, SIGABRT);
+ tcase_add_test(tc, test_escape);
+ tcase_add_test(tc, test_unescape);
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
From fe162e0bbda7efcee489db10bd76c8853297d96e Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Thu, 23 Jul 2020 10:01:49 +0300
Subject: [PATCH 033/865] core-util-test: Test pa_atou64() and pa_atoi64()
Part-of:
---
src/tests/core-util-test.c | 56 ++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/src/tests/core-util-test.c b/src/tests/core-util-test.c
index e48cb3807..49aa4abcc 100644
--- a/src/tests/core-util-test.c
+++ b/src/tests/core-util-test.c
@@ -115,6 +115,7 @@ START_TEST (test_atoi) {
ck_assert_int_lt(pa_atoi("3.14", &value), 0);
ck_assert_int_lt(pa_atoi("7*8", &value), 0);
ck_assert_int_lt(pa_atoi("false", &value), 0);
+ ck_assert_int_lt(pa_atoi("10000000000", &value), 0);
}
END_TEST
@@ -137,6 +138,31 @@ START_TEST (test_atou) {
ck_assert_int_lt(pa_atou("3.14", &value), 0);
ck_assert_int_lt(pa_atou("7*8", &value), 0);
ck_assert_int_lt(pa_atou("false", &value), 0);
+ ck_assert_int_lt(pa_atou("10000000000", &value), 0);
+}
+END_TEST
+
+START_TEST (test_atou64) {
+ uint64_t value;
+
+ // decimal
+ ck_assert_int_eq(pa_atou64("100000", &value), 0);
+ ck_assert_int_eq(value, 100000);
+ ck_assert_int_eq(pa_atou64("010", &value), 0);
+ ck_assert_int_eq(value, 10);
+ ck_assert_int_eq(pa_atou64("10000000000", &value), 0);
+ ck_assert_int_eq(value, 10000000000);
+
+ // hexadecimal
+ ck_assert_int_eq(pa_atou64("0x100000", &value), 0);
+ ck_assert_int_eq(value, 0x100000);
+
+ // invalid values
+ ck_assert_int_lt(pa_atou64("-100000", &value), 0);
+ ck_assert_int_lt(pa_atou64("-0x100000", &value), 0);
+ ck_assert_int_lt(pa_atou64("3.14", &value), 0);
+ ck_assert_int_lt(pa_atou64("7*8", &value), 0);
+ ck_assert_int_lt(pa_atou64("false", &value), 0);
}
END_TEST
@@ -166,6 +192,34 @@ START_TEST (test_atol) {
}
END_TEST
+START_TEST (test_atoi64) {
+ int64_t value;
+
+ // decimal
+ ck_assert_int_eq(pa_atoi64("100000", &value), 0);
+ ck_assert_int_eq(value, 100000);
+ ck_assert_int_eq(pa_atoi64("-100000", &value), 0);
+ ck_assert_int_eq(value, -100000);
+ ck_assert_int_eq(pa_atoi64("010", &value), 0);
+ ck_assert_int_eq(value, 10);
+ ck_assert_int_eq(pa_atoi64("-010", &value), 0);
+ ck_assert_int_eq(value, -10);
+ ck_assert_int_eq(pa_atoi64("10000000000", &value), 0);
+ ck_assert_int_eq(value, 10000000000);
+
+ // hexadecimal
+ ck_assert_int_eq(pa_atoi64("0x100000", &value), 0);
+ ck_assert_int_eq(value, 0x100000);
+ ck_assert_int_eq(pa_atoi64("-0x100000", &value), 0);
+ ck_assert_int_eq(value, -0x100000);
+
+ // invalid values
+ ck_assert_int_lt(pa_atoi64("3.14", &value), 0);
+ ck_assert_int_lt(pa_atoi64("7*8", &value), 0);
+ ck_assert_int_lt(pa_atoi64("false", &value), 0);
+}
+END_TEST
+
START_TEST (test_atod) {
double value;
double epsilon = 0.001;
@@ -275,7 +329,9 @@ int main(int argc, char *argv[]) {
tcase_add_test(tc, test_parse_volume);
tcase_add_test(tc, test_atoi);
tcase_add_test(tc, test_atou);
+ tcase_add_test(tc, test_atou64);
tcase_add_test(tc, test_atol);
+ tcase_add_test(tc, test_atoi64);
tcase_add_test(tc, test_atod);
tcase_add_test(tc, test_replace);
tcase_add_test_raise_signal(tc, test_replace_fail_1, SIGABRT);
From 818a87deb280aadb6cb127df99012548e9198485 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Mon, 30 Nov 2020 22:39:23 +0300
Subject: [PATCH 034/865] message-params: add read length param reference to
array read methods
Part-of:
---
src/pulse/message-params.c | 70 ++++++++++++++++++++++++++------------
src/pulse/message-params.h | 27 +++++++--------
2 files changed, 62 insertions(+), 35 deletions(-)
diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c
index 7d2dd8020..d5cafab3b 100644
--- a/src/pulse/message-params.c
+++ b/src/pulse/message-params.c
@@ -344,22 +344,24 @@ int pa_message_params_read_bool(char *c, bool *result, void **state) {
return PA_MESSAGE_PARAMS_OK;
}
-/* Converts a parameter list to a string array. Escaping is removed from
- * the strings. Returns an array of pointers to sub-strings within c in
- * *results. The returned array must be freed, but not the strings
- * within the array. The function returns the number of strings in the
- * array. */
-int pa_message_params_read_string_array(char *c, const char ***results) {
+/* Converts a parameter list to a string array. */
+int pa_message_params_read_string_array(char *c, const char ***results, int *length) {
void *state = NULL;
uint32_t element_count, i;
int err;
const char **values;
pa_assert(results);
+ pa_assert(length);
/* Count elements, return if no element was found or parse error. */
- if ((element_count = count_elements(c)) <= 0)
- return element_count;
+ element_count = count_elements(c);
+ if (element_count < 0) {
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+ } else if (element_count == 0) {
+ *length = 0;
+ return PA_MESSAGE_PARAMS_OK;
+ }
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(char *));
@@ -373,21 +375,29 @@ int pa_message_params_read_string_array(char *c, const char ***results) {
}
*results = values;
- return element_count;
+ *length = element_count;
+
+ return PA_MESSAGE_PARAMS_OK;
}
/* Converts a parameter list to a double array. */
-int pa_message_params_read_double_array(char *c, double **results) {
+int pa_message_params_read_double_array(char *c, double **results, int *length) {
double *values;
void *state = NULL;
uint32_t element_count, i;
int err;
pa_assert(results);
+ pa_assert(length);
/* Count elements, return if no element was found or parse error. */
- if ((element_count = count_elements(c)) <= 0)
- return element_count;
+ element_count = count_elements(c);
+ if (element_count < 0) {
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+ } else if (element_count == 0) {
+ *length = 0;
+ return PA_MESSAGE_PARAMS_OK;
+ }
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(double));
@@ -401,21 +411,29 @@ int pa_message_params_read_double_array(char *c, double **results) {
}
*results = values;
- return element_count;
+ *length = element_count;
+
+ return PA_MESSAGE_PARAMS_OK;
}
/* Converts a parameter list to an int64 array. */
-int pa_message_params_read_int64_array(char *c, int64_t **results) {
+int pa_message_params_read_int64_array(char *c, int64_t **results, int *length) {
int64_t *values;
void *state = NULL;
uint32_t element_count, i;
int err;
pa_assert(results);
+ pa_assert(length);
/* Count elements, return if no element was found or parse error. */
- if ((element_count = count_elements(c)) <= 0)
- return element_count;
+ element_count = count_elements(c);
+ if (element_count < 0) {
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+ } else if (element_count == 0) {
+ *length = 0;
+ return PA_MESSAGE_PARAMS_OK;
+ }
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(int64_t));
@@ -429,21 +447,29 @@ int pa_message_params_read_int64_array(char *c, int64_t **results) {
}
*results = values;
- return element_count;
+ *length = element_count;
+
+ return PA_MESSAGE_PARAMS_OK;
}
/* Converts a parameter list to an uint64 array. */
-int pa_message_params_read_uint64_array(char *c, uint64_t **results) {
+int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length) {
uint64_t *values;
void *state = NULL;
uint32_t element_count, i;
int err;
pa_assert(results);
+ pa_assert(length);
/* Count elements, return if no element was found or parse error. */
- if ((element_count = count_elements(c)) <= 0)
- return element_count;
+ element_count = count_elements(c);
+ if (element_count < 0) {
+ return PA_MESSAGE_PARAMS_PARSE_ERROR;
+ } else if (element_count == 0) {
+ *length = 0;
+ return PA_MESSAGE_PARAMS_OK;
+ }
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(uint64_t));
@@ -457,7 +483,9 @@ int pa_message_params_read_uint64_array(char *c, uint64_t **results) {
}
*results = values;
- return element_count;
+ *length = element_count;
+
+ return PA_MESSAGE_PARAMS_OK;
}
/* Write functions. The functions are wrapper functions around pa_strbuf,
diff --git a/src/pulse/message-params.h b/src/pulse/message-params.h
index 3d90eac3c..18addee96 100644
--- a/src/pulse/message-params.h
+++ b/src/pulse/message-params.h
@@ -75,17 +75,17 @@ int pa_message_params_read_bool(char *c, bool *result, void **state);
int pa_message_params_read_double(char *c, double *result, void **state);
/** Converts a parameter list to a double array. Empty elements in the parameter
- * list are treated as error. Before the call, results must be initialized, either
- * to NULL or to an array with default values. \since 15.0 */
-int pa_message_params_read_double_array(char *c, double **results);
+ * list are treated as error. Returns allocated array in *results and array size in *length.
+ * The returned array must be freed with pa_xfree(). \since 15.0 */
+int pa_message_params_read_double_array(char *c, double **results, int *length);
/** Read an integer from parameter list in c. \since 15.0 */
int pa_message_params_read_int64(char *c, int64_t *result, void **state);
/** Converts a parameter list to an int64 array. Empty elements in the parameter
- * list are treated as error. Before the call, results must be initialized, either
- * to NULL or to an array with default values. \since 15.0 */
-int pa_message_params_read_int64_array(char *c, int64_t **results);
+ * list are treated as error. Returns allocated array in *results and array size in *length.
+ * The returned array must be freed with pa_xfree(). \since 15.0 */
+int pa_message_params_read_int64_array(char *c, int64_t **results, int *length);
/** Read raw data from parameter list in c. Used to split a message parameter
* string into list elements. The string returned in *result must not be freed. \since 15.0 */
@@ -96,19 +96,18 @@ int pa_message_params_read_raw(char *c, char **result, void **state);
int pa_message_params_read_string(char *c, const char **result, void **state);
/** Convert a parameter list to a string array. Escaping is removed from
- * the strings. Returns an array of pointers to sub-strings within c in
- * *results. The returned array must be freed, but not the strings
- * within the array. Before the call, results must be initialized, either
- * to NULL or to an array with default values. \since 15.0 */
-int pa_message_params_read_string_array(char *c, const char ***results);
+ * the strings. Returns allocated array of pointers to sub-strings within c in
+ * *results and stores array size in *length. The returned array must be
+ * freed with pa_xfree(), but not the strings within the array. \since 15.0 */
+int pa_message_params_read_string_array(char *c, const char ***results, int *length);
/** Read an unsigned integer from parameter list in c. \since 15.0 */
int pa_message_params_read_uint64(char *c, uint64_t *result, void **state);
/** Converts a parameter list to an uint64 array. Empty elements in the parameter
- * list are treated as error. Before the call, results must be initialized, either
- * to NULL or to an array with default values. \since 15.0 */
-int pa_message_params_read_uint64_array(char *c, uint64_t **results);
+ * list are treated as error. Returns allocated array in *results and array size in *length.
+ * The returned array must be freed with pa_xfree(). \since 15.0 */
+int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length);
/** @} */
From c1ac76014e89e995f107a0f569a8cf7fe661b307 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Mon, 30 Nov 2020 22:25:16 +0300
Subject: [PATCH 035/865] message-params: consume array enclosing {} by array
read methods
Part-of:
---
src/pulse/message-params.c | 36 ++++++++++++++++++++++++++++--------
1 file changed, 28 insertions(+), 8 deletions(-)
diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c
index d5cafab3b..ae2ed8b68 100644
--- a/src/pulse/message-params.c
+++ b/src/pulse/message-params.c
@@ -350,12 +350,16 @@ int pa_message_params_read_string_array(char *c, const char ***results, int *len
uint32_t element_count, i;
int err;
const char **values;
+ char *start_pos;
pa_assert(results);
pa_assert(length);
+ if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
+ return err;
+
/* Count elements, return if no element was found or parse error. */
- element_count = count_elements(c);
+ element_count = count_elements(start_pos);
if (element_count < 0) {
return PA_MESSAGE_PARAMS_PARSE_ERROR;
} else if (element_count == 0) {
@@ -366,7 +370,8 @@ int pa_message_params_read_string_array(char *c, const char ***results, int *len
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(char *));
- for (i = 0; (err = pa_message_params_read_string(c, &(values[i]), &state)) > 0; i++)
+ state = NULL;
+ for (i = 0; (err = pa_message_params_read_string(start_pos, &(values[i]), &state)) > 0; i++)
;
if (err < 0) {
@@ -386,12 +391,16 @@ int pa_message_params_read_double_array(char *c, double **results, int *length)
void *state = NULL;
uint32_t element_count, i;
int err;
+ char *start_pos;
pa_assert(results);
pa_assert(length);
+ if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
+ return err;
+
/* Count elements, return if no element was found or parse error. */
- element_count = count_elements(c);
+ element_count = count_elements(start_pos);
if (element_count < 0) {
return PA_MESSAGE_PARAMS_PARSE_ERROR;
} else if (element_count == 0) {
@@ -402,7 +411,8 @@ int pa_message_params_read_double_array(char *c, double **results, int *length)
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(double));
- for (i = 0; (err = pa_message_params_read_double(c, &(values[i]), &state)) > 0; i++)
+ state = NULL;
+ for (i = 0; (err = pa_message_params_read_double(start_pos, &(values[i]), &state)) > 0; i++)
;
if (err < 0) {
@@ -422,12 +432,16 @@ int pa_message_params_read_int64_array(char *c, int64_t **results, int *length)
void *state = NULL;
uint32_t element_count, i;
int err;
+ char *start_pos;
pa_assert(results);
pa_assert(length);
+ if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
+ return err;
+
/* Count elements, return if no element was found or parse error. */
- element_count = count_elements(c);
+ element_count = count_elements(start_pos);
if (element_count < 0) {
return PA_MESSAGE_PARAMS_PARSE_ERROR;
} else if (element_count == 0) {
@@ -438,7 +452,8 @@ int pa_message_params_read_int64_array(char *c, int64_t **results, int *length)
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(int64_t));
- for (i = 0; (err = pa_message_params_read_int64(c, &(values[i]), &state)) > 0; i++)
+ state = NULL;
+ for (i = 0; (err = pa_message_params_read_int64(start_pos, &(values[i]), &state)) > 0; i++)
;
if (err < 0) {
@@ -458,12 +473,16 @@ int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length
void *state = NULL;
uint32_t element_count, i;
int err;
+ char *start_pos;
pa_assert(results);
pa_assert(length);
+ if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
+ return err;
+
/* Count elements, return if no element was found or parse error. */
- element_count = count_elements(c);
+ element_count = count_elements(start_pos);
if (element_count < 0) {
return PA_MESSAGE_PARAMS_PARSE_ERROR;
} else if (element_count == 0) {
@@ -474,7 +493,8 @@ int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(uint64_t));
- for (i = 0; (err = pa_message_params_read_uint64(c, &(values[i]), &state)) > 0; i++)
+ state = NULL;
+ for (i = 0; (err = pa_message_params_read_uint64(start_pos, &(values[i]), &state)) > 0; i++)
;
if (err < 0) {
From bd8ef73b913f050792017ce8772a99bc198ecff5 Mon Sep 17 00:00:00 2001
From: Jaechul Lee
Date: Mon, 16 Nov 2020 10:14:56 +0900
Subject: [PATCH 036/865] null-source: Add max_latency_msec argument
It takes much time when starting to capture because max latency is set
to 2 seconds as a initial value. null-source latency need to be set a
lower value than initial value to improve latency.
---
src/modules/module-null-source.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/modules/module-null-source.c b/src/modules/module-null-source.c
index 8d3796e9a..38e3c823e 100644
--- a/src/modules/module-null-source.c
+++ b/src/modules/module-null-source.c
@@ -52,6 +52,7 @@ PA_MODULE_USAGE(
"rate= "
"source_name= "
"channel_map= "
+ "max_latency_msec= "
"description= ");
#define DEFAULT_SOURCE_NAME "source.null"
@@ -79,6 +80,7 @@ static const char* const valid_modargs[] = {
"channels",
"source_name",
"channel_map",
+ "max_latency_msec",
"description",
NULL
};
@@ -123,6 +125,7 @@ static void source_update_requested_latency_cb(pa_source *s) {
pa_assert(u);
u->block_usec = pa_source_get_requested_latency_within_thread(s);
+
if (u->block_usec == (pa_usec_t)-1)
u->block_usec = u->source->thread_info.max_latency;
}
@@ -197,6 +200,8 @@ int pa__init(pa_module*m) {
pa_channel_map map;
pa_modargs *ma = NULL;
pa_source_new_data data;
+ pa_usec_t max_latency = MAX_LATENCY_USEC;
+ uint32_t max_latency_msec;
pa_assert(m);
@@ -247,7 +252,11 @@ int pa__init(pa_module*m) {
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
- pa_source_set_latency_range(u->source, MIN_LATENCY_USEC, MAX_LATENCY_USEC);
+ if (pa_modargs_get_value_u32(ma, "max_latency_msec", &max_latency_msec))
+ max_latency = max_latency_msec * PA_USEC_PER_MSEC;
+
+ pa_source_set_latency_range(u->source, MIN_LATENCY_USEC, max_latency);
+
u->block_usec = u->source->thread_info.max_latency;
u->source->thread_info.max_rewind =
From afc1a0e45b01fac363fee727256a2556aaf30e79 Mon Sep 17 00:00:00 2001
From: Jason Nader
Date: Mon, 7 Dec 2020 19:56:39 +0900
Subject: [PATCH 037/865] man: clarify pactl command arguments
---
man/pactl.1.xml.in | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
index 1f094df42..197df7dee 100644
--- a/man/pactl.1.xml.in
+++ b/man/pactl.1.xml.in
@@ -135,7 +135,7 @@ License along with PulseAudio; if not, see .
suspend-sinkSINKtrue|false
Suspend or resume the specified sink (which may be
- specified either by its name or index), depending whether true
+ specified either by its symbolic name or numerical index), depending whether true
(suspend) or false (resume) is passed as last argument. Suspending
a sink will pause all playback. Depending on the module implementing
the sink this might have the effect that the underlying device is
@@ -147,7 +147,7 @@ License along with PulseAudio; if not, see .
suspend-sourceSOURCEtrue|false
Suspend or resume the specified source (which may be
- specified either by its name or index), depending whether true
+ specified either by its symbolic name or numerical index), depending whether true
(suspend) or false (resume) is passed as last argument. Suspending
a source will pause all capturing. Depending on the module implementing
the source this might have the effect that the underlying device is
@@ -163,7 +163,7 @@ License along with PulseAudio; if not, see .
set-default-sinkSINK
-
Make the specified sink (identified by its symbolic name) the default sink.
+
Make the specified sink (identified by its symbolic name or numerical index) the default sink.
@@ -173,7 +173,7 @@ License along with PulseAudio; if not, see .
set-default-sourceSOURCE
-
Make the specified source (identified by its symbolic name) the default source.
+
Make the specified source (identified by its symbolic name or numerical index) the default source.
From 71aa4720a4194e9602271634d9042b8e56a8217f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?=
Date: Tue, 24 Nov 2020 18:57:41 +0200
Subject: [PATCH 038/865] alsa-mixer: Add OnePlus Bullets udev rule
---
src/modules/alsa/90-pulseaudio.rules | 3 ++
.../profile-sets/simple-headphones-mic.conf | 42 +++++++++++++++++++
2 files changed, 45 insertions(+)
create mode 100644 src/modules/alsa/mixer/profile-sets/simple-headphones-mic.conf
diff --git a/src/modules/alsa/90-pulseaudio.rules b/src/modules/alsa/90-pulseaudio.rules
index 83b257df6..4de40127e 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -147,6 +147,9 @@ ATTRS{idVendor}=="0951", ATTRS{idProduct}=="16ff", ENV{ID_ID}="usb-HyperX_Cloud_
ATTRS{idVendor}=="0951", ATTRS{idProduct}=="1702", ENV{ID_ID}="usb-HyperX_Cloud_Orbit_S_Hi-Res_2Ch-$env{ID_USB_INTERFACE_NUM}"
ATTRS{idVendor}=="0951", ATTRS{idProduct}=="1703", ENV{ID_ID}="usb-HyperX_Cloud_Orbit_S_3D_8Ch-$env{ID_USB_INTERFACE_NUM}"
+# OnePlus Type-C Bullets (ED117)
+ATTRS{idVendor}=="2a70", ATTRS{idProduct}=="1881", ENV{PULSE_PROFILE_SET}="simple-headphones-mic.conf"
+
GOTO="pulseaudio_end"
LABEL="pulseaudio_check_pci"
diff --git a/src/modules/alsa/mixer/profile-sets/simple-headphones-mic.conf b/src/modules/alsa/mixer/profile-sets/simple-headphones-mic.conf
new file mode 100644
index 000000000..809d01587
--- /dev/null
+++ b/src/modules/alsa/mixer/profile-sets/simple-headphones-mic.conf
@@ -0,0 +1,42 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see .
+
+; This is a profile meant for simple (stereo + mic) headphones.
+; default.conf also works but using this one will hide some profiles
+; that don't make sense like IEC958 and multichannel inputs.
+
+[General]
+auto-profiles = yes
+
+[Mapping analog-stereo]
+device-strings = front:%f
+channel-map = left,right
+paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
+priority = 15
+
+# If everything else fails, try to use hw:0 as a stereo device...
+[Mapping stereo-fallback]
+device-strings = hw:%f
+fallback = yes
+channel-map = front-left,front-right
+paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
+priority = 1
+
+[Mapping analog-mono]
+device-strings = hw:%f,0,0
+channel-map = mono
+direction = input
From ea69288b76d94e61c1d2492d87d969f05357cec2 Mon Sep 17 00:00:00 2001
From: Sanchayan Maity
Date: Thu, 23 Jul 2020 20:33:36 +0530
Subject: [PATCH 039/865] module-match: Allow loading module more than once
As long as modules use different keys, multiple loading of this module
should be safe. Even in case of same keys, either the pa_proplist_update
should correctly update the property list based on the mode or for the
volume writable case, the last rule should win.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/942
Part-of:
---
src/modules/module-match.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 76f71256c..9b5e76cd7 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -47,7 +47,7 @@
PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Playback stream expression matching module");
PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_LOAD_ONCE(false);
PA_MODULE_USAGE("table= "
"key=");
From bbac2e6e2b6af61672200a7ca39a89886eaf5dd0 Mon Sep 17 00:00:00 2001
From: Dusan Kazik
Date: Tue, 24 Nov 2020 08:32:06 +0000
Subject: [PATCH 040/865] Translated using Weblate (Slovak)
Currently translated at 76.0% (400 of 526 strings)
Translation: pulseaudio/pulseaudio
Translate-URL: https://translate.fedoraproject.org/projects/pulseaudio/pulseaudio/sk/
Part-of:
---
po/sk.po | 111 +++++++++++++++++++++++++++++++++++--------------------
1 file changed, 71 insertions(+), 40 deletions(-)
diff --git a/po/sk.po b/po/sk.po
index 99f898351..a7b096d91 100644
--- a/po/sk.po
+++ b/po/sk.po
@@ -9,7 +9,7 @@ msgstr ""
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/"
"issues/new\n"
"POT-Creation-Date: 2016-06-22 13:54+0000\n"
-"PO-Revision-Date: 2020-10-07 02:48+0000\n"
+"PO-Revision-Date: 2020-11-25 08:35+0000\n"
"Last-Translator: Dusan Kazik \n"
"Language-Team: Slovak \n"
@@ -18,7 +18,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
-"X-Generator: Weblate 4.2.2\n"
+"X-Generator: Weblate 4.3.2\n"
#: ../src/daemon/cmdline.c:113
#, c-format
@@ -95,11 +95,11 @@ msgstr ""
#: ../src/daemon/cmdline.c:246
msgid "--daemonize expects boolean argument"
-msgstr ""
+msgstr "Voľba --daemonize očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:254
msgid "--fail expects boolean argument"
-msgstr ""
+msgstr "Voľba --fail očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:265
msgid ""
@@ -109,23 +109,23 @@ msgstr ""
#: ../src/daemon/cmdline.c:277
msgid "--high-priority expects boolean argument"
-msgstr ""
+msgstr "Voľba --high-priority očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:285
msgid "--realtime expects boolean argument"
-msgstr ""
+msgstr "Voľba --realtime očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:293
msgid "--disallow-module-loading expects boolean argument"
-msgstr ""
+msgstr "Voľba --disallow-module-loading očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:301
msgid "--disallow-exit expects boolean argument"
-msgstr ""
+msgstr "Voľba --disallow-exit očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:309
msgid "--use-pid-file expects boolean argument"
-msgstr ""
+msgstr "Voľba --use-pid-file očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:328
msgid ""
@@ -141,11 +141,11 @@ msgstr ""
#: ../src/daemon/cmdline.c:338
msgid "--log-time expects boolean argument"
-msgstr ""
+msgstr "Voľba --log-time očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:346
msgid "--log-meta expects boolean argument"
-msgstr ""
+msgstr "Voľba --log-meta očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:366
#, c-format
@@ -158,11 +158,11 @@ msgstr "Voľba --system očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:381
msgid "--no-cpu-limit expects boolean argument"
-msgstr ""
+msgstr "Voľba --no-cpu-limit očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:389
msgid "--disable-shm expects boolean argument"
-msgstr ""
+msgstr "Voľba --disable-shm očakáva booleovský parameter"
#: ../src/daemon/cmdline.c:397
msgid "--enable-memfd expects boolean argument"
@@ -350,7 +350,7 @@ msgstr "Zlyhalo zmenenie UID: %s"
#: ../src/daemon/main.c:255
msgid "System wide mode unsupported on this platform."
-msgstr ""
+msgstr "Systémový režim nie je podporovaný na tejto platforme."
#: ../src/daemon/main.c:484
msgid "Failed to parse command line."
@@ -395,10 +395,12 @@ msgstr ""
#: ../src/daemon/main.c:712
msgid "Running in system mode, but --disallow-exit not set."
msgstr ""
+"Spustené v systémovom režime, ale voľba --disallow-exit nie je nastavená."
#: ../src/daemon/main.c:715
msgid "Running in system mode, but --disallow-module-loading not set."
msgstr ""
+"Spustené v systémovom režime, ale voľba --disallow-exit nie je nastavená."
#: ../src/daemon/main.c:718
msgid "Running in system mode, forcibly disabling SHM mode."
@@ -407,10 +409,11 @@ msgstr ""
#: ../src/daemon/main.c:723
msgid "Running in system mode, forcibly disabling exit idle time."
msgstr ""
+"Spustené v systémovom režime. Ukončenie pri nečinnosti je vynútene zakázané."
#: ../src/daemon/main.c:756
msgid "Failed to acquire stdio."
-msgstr ""
+msgstr "Zlyhalo získanie štandardného vstupu/výstupu."
#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
#, c-format
@@ -866,11 +869,11 @@ msgstr "Vstup cez Bluetooth"
#: ../src/modules/bluetooth/module-bluez5-device.c:1786
msgid "High Fidelity Playback (A2DP Sink)"
-msgstr ""
+msgstr "HiFi prehrávanie (cieľ A2DP)"
#: ../src/modules/bluetooth/module-bluez5-device.c:1797
msgid "High Fidelity Capture (A2DP Source)"
-msgstr ""
+msgstr "HiFi zaznamenávanie (zdroj A2DP)"
#: ../src/modules/bluetooth/module-bluez5-device.c:1808
msgid "Headset Head Unit (HSP/HFP)"
@@ -926,7 +929,7 @@ msgstr ""
#: ../src/modules/module-ladspa-sink.c:51
msgid "Virtual LADSPA sink"
-msgstr ""
+msgstr "Virtuálny cieľ LADSPA"
#: ../src/modules/module-ladspa-sink.c:55
msgid ""
@@ -979,7 +982,7 @@ msgstr "Tunel do %s/%s"
#: ../src/modules/module-virtual-surround-sink.c:47
msgid "Virtual surround sink"
-msgstr ""
+msgstr "Virtuálny priestorový cieľ"
#: ../src/modules/module-virtual-surround-sink.c:51
msgid ""
@@ -1028,11 +1031,11 @@ msgstr "Basový reproduktor"
#: ../src/pulse/channelmap.c:115
msgid "Front Left-of-center"
-msgstr ""
+msgstr "Predný ľavý stredový"
#: ../src/pulse/channelmap.c:116
msgid "Front Right-of-center"
-msgstr ""
+msgstr "Predný pravý stredový"
#: ../src/pulse/channelmap.c:118
msgid "Side Left"
@@ -1553,7 +1556,7 @@ msgstr ""
#: ../src/utils/pacat.c:416
#, c-format
msgid "Stream buffer attributes changed.%s"
-msgstr ""
+msgstr "Atribúty zásobníka prúdu boli zmenené. %s"
#: ../src/utils/pacat.c:431
msgid "Cork request stack is empty: corking stream"
@@ -1759,7 +1762,7 @@ msgstr "Neznámy formát súboru %s."
#: ../src/utils/pacat.c:995
msgid "Failed to parse the argument for --monitor-stream"
-msgstr "Zlyhala analyzovanie argumentu pre --monitor-stream"
+msgstr "Zlyhala analyzovanie parametra pre voľbu --monitor-stream"
#: ../src/utils/pacat.c:1006
msgid "Invalid sample specification"
@@ -1856,7 +1859,7 @@ msgstr ""
#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
msgid "NAME [ARGS ...]"
-msgstr "NÁZOV [ARGUMENTY ...]"
+msgstr "NÁZOV [PARAMETRE ...]"
#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
msgid "NAME|#N"
@@ -1897,15 +1900,15 @@ msgstr "Č."
#: ../src/utils/pacmd.c:62
msgid "NAME SINK|#N"
-msgstr ""
+msgstr "NÁZOV CIEĽA|Č."
#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
msgid "NAME FILENAME"
-msgstr "NÁZOV NÁZOV-SÚBORU"
+msgstr "NÁZOV NÁZOV_SÚBORU"
#: ../src/utils/pacmd.c:66
msgid "PATHNAME"
-msgstr "NÁZOV-CESTY"
+msgstr "NÁZOV_CESTY"
#: ../src/utils/pacmd.c:67
msgid "FILENAME SINK|#N"
@@ -1913,7 +1916,7 @@ msgstr ""
#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
msgid "#N SINK|SOURCE"
-msgstr ""
+msgstr "Č. CIEĽU|ZDROJ"
#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
msgid "1|0"
@@ -1929,7 +1932,7 @@ msgstr "NÁZOV|Č. PORTU"
#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
msgid "CARD-NAME|CARD-#N PORT OFFSET"
-msgstr "NÁZOV-KARTY|KARTA-Č. PORT POSUNUTIE"
+msgstr "NÁZOV_KARTY|KARTA-Č. PORT POSUNUTIE"
#: ../src/utils/pacmd.c:75
msgid "TARGET"
@@ -2053,11 +2056,20 @@ msgid ""
"Default Source: %s\n"
"Cookie: %04x:%04x\n"
msgstr ""
+"Používateľské meno: %s\n"
+"Názov hostiteľa: %s\n"
+"Názov servera: %s\n"
+"Verzia servera: %s\n"
+"Predvolená špecifikácia snímky: %s\n"
+"Predvolená mapa kanálov: %s\n"
+"Predvolený cieľ: %s\n"
+"Predvolený zdroj: %s\n"
+"Cookie: %04x:%04x\n"
#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
#, c-format
msgid "Failed to get sink information: %s"
-msgstr ""
+msgstr "Zlyhalo získanie informácií o cieli: %s"
#: ../src/utils/pactl.c:281
#, c-format
@@ -2148,7 +2160,7 @@ msgid ""
msgstr ""
"Modul #%u\n"
"\tNázov: %s\n"
-"\tArgument: %s\n"
+"\tParameter: %s\n"
"\tPočítadlo použití: %s\n"
"\tVlastnosti:\n"
"\t\t%s\n"
@@ -2203,7 +2215,7 @@ msgstr "\tProfily:\n"
#: ../src/utils/pactl.c:581
#, c-format
msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
-msgstr ""
+msgstr "\t\t%s: %s (ciele: %u, zdroje: %u, priorita: %u, dostupné: %s)\n"
#: ../src/utils/pactl.c:586
#, c-format
@@ -2250,6 +2262,23 @@ msgid ""
"\tProperties:\n"
"\t\t%s\n"
msgstr ""
+"Vstup cieľa č. %u\n"
+"\tOvládač: %s\n"
+"\tModul vlastníka: %s\n"
+"\tKlient: %s\n"
+"\tCieľ: %u\n"
+"\tŠpecifikácia snímky: %s\n"
+"\tMapa kanálov: %s\n"
+"\tFormát: %s\n"
+"\tBlokovanie: %s\n"
+"\tStlmenie: %s\n"
+"\tHlasitosť: %s\n"
+"\t vyváženie %0.2f\n"
+"\tOneskorenie zásobníka: %0.0f usek\n"
+"\tOneskorenie cieľa: %0.0f usek\n"
+"\tMetóda presnímkovania: %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
#, c-format
@@ -2308,7 +2337,7 @@ msgstr "Zlyhanie: %s"
#: ../src/utils/pactl.c:866
#, c-format
msgid "Failed to unload module: Module %s not loaded"
-msgstr ""
+msgstr "Zlyhalo uvoľnenie modulu: Modul %s nie je načítaný"
#: ../src/utils/pactl.c:884
#, c-format
@@ -2351,7 +2380,7 @@ msgstr "neznámy"
#: ../src/utils/pactl.c:1147
msgid "sink"
-msgstr ""
+msgstr "cieľ"
#: ../src/utils/pactl.c:1150
msgid "source"
@@ -2430,11 +2459,11 @@ msgstr "[TYP]"
#: ../src/utils/pactl.c:1567
msgid "FILENAME [NAME]"
-msgstr "NÁZOV-SÚBORU [NÁZOV]"
+msgstr "NÁZOV_SÚBORU [NÁZOV]"
#: ../src/utils/pactl.c:1568
msgid "NAME [SINK]"
-msgstr ""
+msgstr "NÁZOV [CIEĽ]"
#: ../src/utils/pactl.c:1577
msgid "NAME|#N VOLUME [VOLUME ...]"
@@ -2527,11 +2556,11 @@ msgstr "Musíte určiť číslo výstupu zdroja a zdroj"
#: ../src/utils/pactl.c:1775
msgid "You have to specify a module name and arguments."
-msgstr "Musíte určiť názov modulu a argumenty."
+msgstr "Musíte určiť názov modulu a parametre."
#: ../src/utils/pactl.c:1795
msgid "You have to specify a module index or name"
-msgstr ""
+msgstr "Musíte určiť číslo modulu alebo názov"
#: ../src/utils/pactl.c:1808
msgid ""
@@ -2558,7 +2587,7 @@ msgstr ""
#: ../src/utils/pactl.c:1867
msgid "You have to specify a sink name"
-msgstr ""
+msgstr "Musíte určiť názov cieľa"
#: ../src/utils/pactl.c:1877
msgid "You have to specify a source name/index and a port name"
@@ -2631,6 +2660,8 @@ msgid ""
"You have to specify a sink index and a semicolon-separated list of supported "
"formats"
msgstr ""
+"Musíte určiť číslo cieľa a bodkočiarkami oddelený zoznam podporovaných "
+"formátov"
#: ../src/utils/pactl.c:2038
msgid "You have to specify a card name/index, a port name and a latency offset"
@@ -2755,7 +2786,7 @@ msgstr "Zdroj: %s\n"
#: ../src/utils/pax11publish.c:114
#, c-format
msgid "Sink: %s\n"
-msgstr ""
+msgstr "Cieľ: %s\n"
#: ../src/utils/pax11publish.c:116
#, c-format
From 6bf178d794d1bca62b54a01a86a7c6bfc8c12813 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Tue, 8 Dec 2020 13:52:39 +0200
Subject: [PATCH 041/865] i18n: Update pulseaudio.pot
Part-of:
---
po/pulseaudio.pot | 515 +++++++++++++++++++++++++---------------------
1 file changed, 279 insertions(+), 236 deletions(-)
diff --git a/po/pulseaudio.pot b/po/pulseaudio.pot
index b02286acb..7e2f604dd 100644
--- a/po/pulseaudio.pot
+++ b/po/pulseaudio.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: pulseaudio\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/new\n"
-"POT-Creation-Date: 2020-11-21 16:47+0300\n"
+"POT-Creation-Date: 2020-12-08 13:51+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -337,91 +337,91 @@ msgstr ""
msgid "System wide mode unsupported on this platform."
msgstr ""
-#: src/daemon/main.c:495
+#: src/daemon/main.c:501
msgid "Failed to parse command line."
msgstr ""
-#: src/daemon/main.c:534
+#: src/daemon/main.c:540
msgid ""
"System mode refused for non-root user. Only starting the D-Bus server lookup service."
msgstr ""
-#: src/daemon/main.c:633
+#: src/daemon/main.c:639
#, c-format
msgid "Failed to kill daemon: %s"
msgstr ""
-#: src/daemon/main.c:662
+#: src/daemon/main.c:668
msgid "This program is not intended to be run as root (unless --system is specified)."
msgstr ""
-#: src/daemon/main.c:665
+#: src/daemon/main.c:671
msgid "Root privileges required."
msgstr ""
-#: src/daemon/main.c:672
+#: src/daemon/main.c:678
msgid "--start not supported for system instances."
msgstr ""
-#: src/daemon/main.c:712
-#, c-format
-msgid "User-configured server at %s, refusing to start/autospawn."
-msgstr ""
-
#: src/daemon/main.c:718
#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: src/daemon/main.c:724
+#, c-format
msgid "User-configured server at %s, which appears to be local. Probing deeper."
msgstr ""
-#: src/daemon/main.c:723
+#: src/daemon/main.c:729
msgid "Running in system mode, but --disallow-exit not set."
msgstr ""
-#: src/daemon/main.c:726
+#: src/daemon/main.c:732
msgid "Running in system mode, but --disallow-module-loading not set."
msgstr ""
-#: src/daemon/main.c:729
+#: src/daemon/main.c:735
msgid "Running in system mode, forcibly disabling SHM mode."
msgstr ""
-#: src/daemon/main.c:734
+#: src/daemon/main.c:740
msgid "Running in system mode, forcibly disabling exit idle time."
msgstr ""
-#: src/daemon/main.c:767
+#: src/daemon/main.c:773
msgid "Failed to acquire stdio."
msgstr ""
-#: src/daemon/main.c:773 src/daemon/main.c:844
+#: src/daemon/main.c:779 src/daemon/main.c:850
#, c-format
msgid "pipe() failed: %s"
msgstr ""
-#: src/daemon/main.c:778 src/daemon/main.c:849
+#: src/daemon/main.c:784 src/daemon/main.c:855
#, c-format
msgid "fork() failed: %s"
msgstr ""
-#: src/daemon/main.c:793 src/daemon/main.c:864 src/utils/pacat.c:562
+#: src/daemon/main.c:799 src/daemon/main.c:870 src/utils/pacat.c:562
#, c-format
msgid "read() failed: %s"
msgstr ""
-#: src/daemon/main.c:799
+#: src/daemon/main.c:805
msgid "Daemon startup failed."
msgstr ""
-#: src/daemon/main.c:832
+#: src/daemon/main.c:838
#, c-format
msgid "setsid() failed: %s"
msgstr ""
-#: src/daemon/main.c:965
+#: src/daemon/main.c:971
msgid "Failed to get machine ID"
msgstr ""
-#: src/daemon/main.c:991
+#: src/daemon/main.c:997
msgid ""
"OK, so you are running PA in system mode. Please make sure that you actually do want to "
"do that.\n"
@@ -429,26 +429,26 @@ msgid ""
"WhatIsWrongWithSystemWide/ for an explanation why system mode is usually a bad idea."
msgstr ""
-#: src/daemon/main.c:1007
+#: src/daemon/main.c:1013
msgid "pa_pid_file_create() failed."
msgstr ""
-#: src/daemon/main.c:1039
+#: src/daemon/main.c:1045
msgid "pa_core_new() failed."
msgstr ""
-#: src/daemon/main.c:1114
+#: src/daemon/main.c:1120
msgid "command line arguments"
msgstr ""
-#: src/daemon/main.c:1121
+#: src/daemon/main.c:1127
#, c-format
msgid ""
"Failed to initialize daemon due to errors while executing startup commands. Source of "
"commands: %s"
msgstr ""
-#: src/daemon/main.c:1126
+#: src/daemon/main.c:1132
msgid "Daemon startup without any loaded modules, refusing to work."
msgstr ""
@@ -502,12 +502,12 @@ msgid "Internal Microphone"
msgstr ""
#: src/modules/alsa/alsa-mixer.c:2631 src/modules/alsa/alsa-mixer.c:2717
-#: src/utils/pactl.c:260
+#: src/utils/pactl.c:265
msgid "Radio"
msgstr ""
#: src/modules/alsa/alsa-mixer.c:2632 src/modules/alsa/alsa-mixer.c:2718
-#: src/utils/pactl.c:261
+#: src/utils/pactl.c:266
msgid "Video"
msgstr ""
@@ -544,12 +544,12 @@ msgid "No Bass Boost"
msgstr ""
#: src/modules/alsa/alsa-mixer.c:2641 src/modules/bluetooth/module-bluez5-device.c:1800
-#: src/utils/pactl.c:250
+#: src/utils/pactl.c:255
msgid "Speaker"
msgstr ""
#: src/modules/alsa/alsa-mixer.c:2642 src/modules/alsa/alsa-mixer.c:2720
-#: src/utils/pactl.c:251
+#: src/utils/pactl.c:256
msgid "Headphones"
msgstr ""
@@ -617,136 +617,153 @@ msgstr ""
msgid "Analog Mono"
msgstr ""
+#: src/modules/alsa/alsa-mixer.c:4395
+msgid "Analog Mono (Left)"
+msgstr ""
+
+#: src/modules/alsa/alsa-mixer.c:4396
+msgid "Analog Mono (Right)"
+msgstr ""
+
#. Note: Not translated to "Analog Stereo Input", because the source
#. * name gets "Input" appended to it automatically, so adding "Input"
#. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output.
-#: src/modules/alsa/alsa-mixer.c:4395 src/modules/alsa/alsa-mixer.c:4403
-#: src/modules/alsa/alsa-mixer.c:4404
+#: src/modules/alsa/alsa-mixer.c:4397 src/modules/alsa/alsa-mixer.c:4405
+#: src/modules/alsa/alsa-mixer.c:4406
msgid "Analog Stereo"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4396 src/pulse/channelmap.c:103
+#: src/modules/alsa/alsa-mixer.c:4398 src/pulse/channelmap.c:103
#: src/pulse/channelmap.c:771
msgid "Mono"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4397 src/pulse/channelmap.c:775
+#: src/modules/alsa/alsa-mixer.c:4399 src/pulse/channelmap.c:775
msgid "Stereo"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4405 src/modules/alsa/alsa-mixer.c:4406
+#: src/modules/alsa/alsa-mixer.c:4407 src/modules/alsa/alsa-mixer.c:4565
+#: src/modules/bluetooth/module-bluez5-device.c:1780 src/utils/pactl.c:259
+msgid "Headset"
+msgstr ""
+
+#: src/modules/alsa/alsa-mixer.c:4408 src/modules/alsa/alsa-mixer.c:4566
+msgid "Speakerphone"
+msgstr ""
+
+#: src/modules/alsa/alsa-mixer.c:4409 src/modules/alsa/alsa-mixer.c:4410
msgid "Multichannel"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4407
+#: src/modules/alsa/alsa-mixer.c:4411
msgid "Analog Surround 2.1"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4408
+#: src/modules/alsa/alsa-mixer.c:4412
msgid "Analog Surround 3.0"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4409
+#: src/modules/alsa/alsa-mixer.c:4413
msgid "Analog Surround 3.1"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4410
+#: src/modules/alsa/alsa-mixer.c:4414
msgid "Analog Surround 4.0"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4411
+#: src/modules/alsa/alsa-mixer.c:4415
msgid "Analog Surround 4.1"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4412
+#: src/modules/alsa/alsa-mixer.c:4416
msgid "Analog Surround 5.0"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4413
+#: src/modules/alsa/alsa-mixer.c:4417
msgid "Analog Surround 5.1"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4414
+#: src/modules/alsa/alsa-mixer.c:4418
msgid "Analog Surround 6.0"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4415
+#: src/modules/alsa/alsa-mixer.c:4419
msgid "Analog Surround 6.1"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4416
+#: src/modules/alsa/alsa-mixer.c:4420
msgid "Analog Surround 7.0"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4417
+#: src/modules/alsa/alsa-mixer.c:4421
msgid "Analog Surround 7.1"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4418
+#: src/modules/alsa/alsa-mixer.c:4422
msgid "Digital Stereo (IEC958)"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4419
+#: src/modules/alsa/alsa-mixer.c:4423
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4420
+#: src/modules/alsa/alsa-mixer.c:4424
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4421
+#: src/modules/alsa/alsa-mixer.c:4425
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4422
+#: src/modules/alsa/alsa-mixer.c:4426
msgid "Digital Stereo (HDMI)"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4423
+#: src/modules/alsa/alsa-mixer.c:4427
msgid "Digital Surround 5.1 (HDMI)"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4424
+#: src/modules/alsa/alsa-mixer.c:4428
msgid "Chat"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4425
+#: src/modules/alsa/alsa-mixer.c:4429
msgid "Game"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4559
+#: src/modules/alsa/alsa-mixer.c:4563
msgid "Analog Mono Duplex"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4560
+#: src/modules/alsa/alsa-mixer.c:4564
msgid "Analog Stereo Duplex"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4561
+#: src/modules/alsa/alsa-mixer.c:4567
msgid "Digital Stereo Duplex (IEC958)"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4562
+#: src/modules/alsa/alsa-mixer.c:4568
msgid "Multichannel Duplex"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4563
+#: src/modules/alsa/alsa-mixer.c:4569
msgid "Stereo Duplex"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4564 src/modules/alsa/module-alsa-card.c:188
+#: src/modules/alsa/alsa-mixer.c:4570 src/modules/alsa/module-alsa-card.c:188
#: src/modules/bluetooth/module-bluez5-device.c:2053
msgid "Off"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4664
+#: src/modules/alsa/alsa-mixer.c:4670
#, c-format
msgid "%s Output"
msgstr ""
-#: src/modules/alsa/alsa-mixer.c:4672
+#: src/modules/alsa/alsa-mixer.c:4678
#, c-format
msgid "%s Input"
msgstr ""
@@ -831,11 +848,7 @@ msgstr ""
msgid "Bluetooth Output"
msgstr ""
-#: src/modules/bluetooth/module-bluez5-device.c:1780 src/utils/pactl.c:254
-msgid "Headset"
-msgstr ""
-
-#: src/modules/bluetooth/module-bluez5-device.c:1786 src/utils/pactl.c:265
+#: src/modules/bluetooth/module-bluez5-device.c:1786 src/utils/pactl.c:270
msgid "Handsfree"
msgstr ""
@@ -843,19 +856,19 @@ msgstr ""
msgid "Headphone"
msgstr ""
-#: src/modules/bluetooth/module-bluez5-device.c:1813 src/utils/pactl.c:264
+#: src/modules/bluetooth/module-bluez5-device.c:1813 src/utils/pactl.c:269
msgid "Portable"
msgstr ""
-#: src/modules/bluetooth/module-bluez5-device.c:1819 src/utils/pactl.c:266
+#: src/modules/bluetooth/module-bluez5-device.c:1819 src/utils/pactl.c:271
msgid "Car"
msgstr ""
-#: src/modules/bluetooth/module-bluez5-device.c:1825 src/utils/pactl.c:267
+#: src/modules/bluetooth/module-bluez5-device.c:1825 src/utils/pactl.c:272
msgid "HiFi"
msgstr ""
-#: src/modules/bluetooth/module-bluez5-device.c:1831 src/utils/pactl.c:268
+#: src/modules/bluetooth/module-bluez5-device.c:1831 src/utils/pactl.c:273
msgid "Phone"
msgstr ""
@@ -946,11 +959,11 @@ msgstr ""
msgid "Clocked NULL sink"
msgstr ""
-#: src/modules/module-null-sink.c:333
+#: src/modules/module-null-sink.c:334
msgid "Null Output"
msgstr ""
-#: src/modules/module-null-sink.c:345 src/utils/pactl.c:1096
+#: src/modules/module-null-sink.c:346 src/utils/pactl.c:1170
#, c-format
msgid "Failed to set format: invalid format string %s"
msgstr ""
@@ -981,17 +994,18 @@ msgstr ""
msgid "Tunnel to %s/%s"
msgstr ""
-#: src/modules/module-virtual-surround-sink.c:45
+#: src/modules/module-virtual-surround-sink.c:50
msgid "Virtual surround sink"
msgstr ""
-#: src/modules/module-virtual-surround-sink.c:49
+#: src/modules/module-virtual-surround-sink.c:54
msgid ""
"sink_name= sink_properties= master= sink_master= format= "
"rate= channels= channel_map= "
"use_volume_sharing= force_flat_volume= hrir=/path/to/left_hrir."
-"wav autoloaded= "
+"wav hrir_left=/path/to/left_hrir.wav hrir_right=/path/to/optional/right_hrir.wav "
+"autoloaded= "
msgstr ""
#: src/modules/raop/module-raop-discover.c:295
@@ -1588,7 +1602,7 @@ msgstr ""
msgid "pa_stream_connect_record() failed: %s"
msgstr ""
-#: src/utils/pacat.c:514 src/utils/pactl.c:1492
+#: src/utils/pacat.c:514 src/utils/pactl.c:1572
#, c-format
msgid "Connection failure: %s"
msgstr ""
@@ -1709,7 +1723,7 @@ msgid ""
"Linked with libpulse %s\n"
msgstr ""
-#: src/utils/pacat.c:852 src/utils/pactl.c:1694
+#: src/utils/pacat.c:852 src/utils/pactl.c:1775
#, c-format
msgid "Invalid client name '%s'"
msgstr ""
@@ -1780,7 +1794,7 @@ msgid ""
"file."
msgstr ""
-#: src/utils/pacat.c:1079 src/utils/pactl.c:1758
+#: src/utils/pacat.c:1079 src/utils/pactl.c:1840
msgid "Failed to determine sample specification from file."
msgstr ""
@@ -1813,7 +1827,7 @@ msgstr ""
msgid "Failed to set media name."
msgstr ""
-#: src/utils/pacat.c:1160 src/utils/pactl.c:2108
+#: src/utils/pacat.c:1160 src/utils/pactl.c:2206
msgid "pa_mainloop_new() failed."
msgstr ""
@@ -1821,11 +1835,11 @@ msgstr ""
msgid "io_new() failed."
msgstr ""
-#: src/utils/pacat.c:1190 src/utils/pactl.c:2120
+#: src/utils/pacat.c:1190 src/utils/pactl.c:2218
msgid "pa_context_new() failed."
msgstr ""
-#: src/utils/pacat.c:1198 src/utils/pactl.c:2126
+#: src/utils/pacat.c:1198 src/utils/pactl.c:2224
#, c-format
msgid "pa_context_connect() failed: %s"
msgstr ""
@@ -1834,19 +1848,19 @@ msgstr ""
msgid "pa_context_rttime_new() failed."
msgstr ""
-#: src/utils/pacat.c:1211 src/utils/pactl.c:2131
+#: src/utils/pacat.c:1211 src/utils/pactl.c:2229
msgid "pa_mainloop_run() failed."
msgstr ""
-#: src/utils/pacmd.c:51 src/utils/pactl.c:1616
+#: src/utils/pacmd.c:51 src/utils/pactl.c:1696
msgid "NAME [ARGS ...]"
msgstr ""
-#: src/utils/pacmd.c:52 src/utils/pacmd.c:60 src/utils/pactl.c:1617
+#: src/utils/pacmd.c:52 src/utils/pacmd.c:60 src/utils/pactl.c:1697
msgid "NAME|#N"
msgstr ""
-#: src/utils/pacmd.c:53 src/utils/pacmd.c:63 src/utils/pactl.c:1615 src/utils/pactl.c:1621
+#: src/utils/pacmd.c:53 src/utils/pacmd.c:63 src/utils/pactl.c:1695 src/utils/pactl.c:1701
msgid "NAME"
msgstr ""
@@ -1858,7 +1872,7 @@ msgstr ""
msgid "#N VOLUME"
msgstr ""
-#: src/utils/pacmd.c:56 src/utils/pacmd.c:70 src/utils/pactl.c:1619
+#: src/utils/pacmd.c:56 src/utils/pacmd.c:70 src/utils/pactl.c:1699
msgid "NAME|#N 1|0"
msgstr ""
@@ -1894,7 +1908,7 @@ msgstr ""
msgid "FILENAME SINK|#N"
msgstr ""
-#: src/utils/pacmd.c:69 src/utils/pactl.c:1618
+#: src/utils/pacmd.c:69 src/utils/pactl.c:1698
msgid "#N SINK|SOURCE"
msgstr ""
@@ -1902,15 +1916,15 @@ msgstr ""
msgid "1|0"
msgstr ""
-#: src/utils/pacmd.c:72 src/utils/pactl.c:1620
+#: src/utils/pacmd.c:72 src/utils/pactl.c:1700
msgid "CARD PROFILE"
msgstr ""
-#: src/utils/pacmd.c:73 src/utils/pactl.c:1622
+#: src/utils/pacmd.c:73 src/utils/pactl.c:1702
msgid "NAME|#N PORT"
msgstr ""
-#: src/utils/pacmd.c:74 src/utils/pactl.c:1628
+#: src/utils/pacmd.c:74 src/utils/pactl.c:1708
msgid "CARD-NAME|CARD-#N PORT OFFSET"
msgstr ""
@@ -1926,7 +1940,11 @@ msgstr ""
msgid "FRAMES"
msgstr ""
-#: src/utils/pacmd.c:81
+#: src/utils/pacmd.c:80 src/utils/pactl.c:1709
+msgid "RECIPIENT MESSAGE [MESSAGE_PARAMETERS]"
+msgstr ""
+
+#: src/utils/pacmd.c:82
#, c-format
msgid ""
"\n"
@@ -1935,7 +1953,7 @@ msgid ""
"When no command is given pacmd starts in the interactive mode.\n"
msgstr ""
-#: src/utils/pacmd.c:128
+#: src/utils/pacmd.c:129
#, c-format
msgid ""
"pacmd %s\n"
@@ -1943,73 +1961,73 @@ msgid ""
"Linked with libpulse %s\n"
msgstr ""
-#: src/utils/pacmd.c:142
+#: src/utils/pacmd.c:143
msgid "No PulseAudio daemon running, or not running as session daemon."
msgstr ""
-#: src/utils/pacmd.c:147
+#: src/utils/pacmd.c:148
#, c-format
msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
msgstr ""
-#: src/utils/pacmd.c:164
+#: src/utils/pacmd.c:165
#, c-format
msgid "connect(): %s"
msgstr ""
-#: src/utils/pacmd.c:172
+#: src/utils/pacmd.c:173
msgid "Failed to kill PulseAudio daemon."
msgstr ""
-#: src/utils/pacmd.c:180
+#: src/utils/pacmd.c:181
msgid "Daemon not responding."
msgstr ""
-#: src/utils/pacmd.c:212 src/utils/pacmd.c:321 src/utils/pacmd.c:339
+#: src/utils/pacmd.c:213 src/utils/pacmd.c:322 src/utils/pacmd.c:340
#, c-format
msgid "write(): %s"
msgstr ""
-#: src/utils/pacmd.c:268
+#: src/utils/pacmd.c:269
#, c-format
msgid "poll(): %s"
msgstr ""
-#: src/utils/pacmd.c:279 src/utils/pacmd.c:299
+#: src/utils/pacmd.c:280 src/utils/pacmd.c:300
#, c-format
msgid "read(): %s"
msgstr ""
-#: src/utils/pactl.c:164
+#: src/utils/pactl.c:169
#, c-format
msgid "Failed to get statistics: %s"
msgstr ""
-#: src/utils/pactl.c:170
+#: src/utils/pactl.c:175
#, c-format
msgid "Currently in use: %u block containing %s bytes total.\n"
msgid_plural "Currently in use: %u blocks containing %s bytes total.\n"
msgstr[0] ""
msgstr[1] ""
-#: src/utils/pactl.c:176
+#: src/utils/pactl.c:181
#, c-format
msgid "Allocated during whole lifetime: %u block containing %s bytes total.\n"
msgid_plural "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
msgstr[0] ""
msgstr[1] ""
-#: src/utils/pactl.c:182
+#: src/utils/pactl.c:187
#, c-format
msgid "Sample cache size: %s\n"
msgstr ""
-#: src/utils/pactl.c:191
+#: src/utils/pactl.c:196
#, c-format
msgid "Failed to get server information: %s"
msgstr ""
-#: src/utils/pactl.c:196
+#: src/utils/pactl.c:201
#, c-format
msgid ""
"Server String: %s\n"
@@ -2020,7 +2038,7 @@ msgid ""
"Tile Size: %zu\n"
msgstr ""
-#: src/utils/pactl.c:212
+#: src/utils/pactl.c:217
#, c-format
msgid ""
"User Name: %s\n"
@@ -2034,76 +2052,76 @@ msgid ""
"Cookie: %04x:%04x\n"
msgstr ""
-#: src/utils/pactl.c:237
+#: src/utils/pactl.c:242
msgid "availability unknown"
msgstr ""
-#: src/utils/pactl.c:238
+#: src/utils/pactl.c:243
msgid "available"
msgstr ""
-#: src/utils/pactl.c:239
+#: src/utils/pactl.c:244
msgid "not available"
msgstr ""
-#: src/utils/pactl.c:248 src/utils/pactl.c:272
+#: src/utils/pactl.c:253 src/utils/pactl.c:277
msgid "Unknown"
msgstr ""
-#: src/utils/pactl.c:249
+#: src/utils/pactl.c:254
msgid "Aux"
msgstr ""
-#: src/utils/pactl.c:252
+#: src/utils/pactl.c:257
msgid "Line"
msgstr ""
-#: src/utils/pactl.c:253
+#: src/utils/pactl.c:258
msgid "Mic"
msgstr ""
-#: src/utils/pactl.c:255
+#: src/utils/pactl.c:260
msgid "Handset"
msgstr ""
-#: src/utils/pactl.c:256
+#: src/utils/pactl.c:261
msgid "Earpiece"
msgstr ""
-#: src/utils/pactl.c:257
+#: src/utils/pactl.c:262
msgid "SPDIF"
msgstr ""
-#: src/utils/pactl.c:258
+#: src/utils/pactl.c:263
msgid "HDMI"
msgstr ""
-#: src/utils/pactl.c:259
+#: src/utils/pactl.c:264
msgid "TV"
msgstr ""
-#: src/utils/pactl.c:262
+#: src/utils/pactl.c:267
msgid "USB"
msgstr ""
-#: src/utils/pactl.c:263
+#: src/utils/pactl.c:268
msgid "Bluetooth"
msgstr ""
-#: src/utils/pactl.c:269
+#: src/utils/pactl.c:274
msgid "Network"
msgstr ""
-#: src/utils/pactl.c:270
+#: src/utils/pactl.c:275
msgid "Analog"
msgstr ""
-#: src/utils/pactl.c:294 src/utils/pactl.c:946 src/utils/pactl.c:1024
+#: src/utils/pactl.c:299 src/utils/pactl.c:1020 src/utils/pactl.c:1098
#, c-format
msgid "Failed to get sink information: %s"
msgstr ""
-#: src/utils/pactl.c:320
+#: src/utils/pactl.c:325
#, c-format
msgid ""
"Sink #%u\n"
@@ -2125,36 +2143,36 @@ msgid ""
"\t\t%s\n"
msgstr ""
-#: src/utils/pactl.c:364 src/utils/pactl.c:472 src/utils/pactl.c:635
+#: src/utils/pactl.c:369 src/utils/pactl.c:477 src/utils/pactl.c:640
#, c-format
msgid "\tPorts:\n"
msgstr ""
-#: src/utils/pactl.c:366 src/utils/pactl.c:474
+#: src/utils/pactl.c:371 src/utils/pactl.c:479
#, c-format
msgid "\t\t%s: %s (type: %s, priority: %u%s%s, %s)\n"
msgstr ""
-#: src/utils/pactl.c:368 src/utils/pactl.c:476 src/utils/pactl.c:640
+#: src/utils/pactl.c:373 src/utils/pactl.c:481 src/utils/pactl.c:645
msgid ", availability group: "
msgstr ""
-#: src/utils/pactl.c:373 src/utils/pactl.c:481
+#: src/utils/pactl.c:378 src/utils/pactl.c:486
#, c-format
msgid "\tActive Port: %s\n"
msgstr ""
-#: src/utils/pactl.c:379 src/utils/pactl.c:487
+#: src/utils/pactl.c:384 src/utils/pactl.c:492
#, c-format
msgid "\tFormats:\n"
msgstr ""
-#: src/utils/pactl.c:403 src/utils/pactl.c:966 src/utils/pactl.c:1039
+#: src/utils/pactl.c:408 src/utils/pactl.c:1040 src/utils/pactl.c:1113
#, c-format
msgid "Failed to get source information: %s"
msgstr ""
-#: src/utils/pactl.c:429
+#: src/utils/pactl.c:434
#, c-format
msgid ""
"Source #%u\n"
@@ -2176,19 +2194,19 @@ msgid ""
"\t\t%s\n"
msgstr ""
-#: src/utils/pactl.c:457 src/utils/pactl.c:529 src/utils/pactl.c:572 src/utils/pactl.c:614
-#: src/utils/pactl.c:713 src/utils/pactl.c:714 src/utils/pactl.c:725 src/utils/pactl.c:783
-#: src/utils/pactl.c:784 src/utils/pactl.c:795 src/utils/pactl.c:846 src/utils/pactl.c:847
-#: src/utils/pactl.c:853
+#: src/utils/pactl.c:462 src/utils/pactl.c:534 src/utils/pactl.c:577 src/utils/pactl.c:619
+#: src/utils/pactl.c:718 src/utils/pactl.c:719 src/utils/pactl.c:730 src/utils/pactl.c:788
+#: src/utils/pactl.c:789 src/utils/pactl.c:800 src/utils/pactl.c:851 src/utils/pactl.c:852
+#: src/utils/pactl.c:858
msgid "n/a"
msgstr ""
-#: src/utils/pactl.c:498 src/utils/pactl.c:903
+#: src/utils/pactl.c:503 src/utils/pactl.c:977
#, c-format
msgid "Failed to get module information: %s"
msgstr ""
-#: src/utils/pactl.c:521
+#: src/utils/pactl.c:526
#, c-format
msgid ""
"Module #%u\n"
@@ -2199,12 +2217,12 @@ msgid ""
"\t\t%s\n"
msgstr ""
-#: src/utils/pactl.c:540
+#: src/utils/pactl.c:545
#, c-format
msgid "Failed to get client information: %s"
msgstr ""
-#: src/utils/pactl.c:566
+#: src/utils/pactl.c:571
#, c-format
msgid ""
"Client #%u\n"
@@ -2214,12 +2232,12 @@ msgid ""
"\t\t%s\n"
msgstr ""
-#: src/utils/pactl.c:583
+#: src/utils/pactl.c:588
#, c-format
msgid "Failed to get card information: %s"
msgstr ""
-#: src/utils/pactl.c:606
+#: src/utils/pactl.c:611
#, c-format
msgid ""
"Card #%u\n"
@@ -2230,44 +2248,44 @@ msgid ""
"\t\t%s\n"
msgstr ""
-#: src/utils/pactl.c:622
+#: src/utils/pactl.c:627
#, c-format
msgid "\tProfiles:\n"
msgstr ""
-#: src/utils/pactl.c:624
+#: src/utils/pactl.c:629
#, c-format
msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
msgstr ""
-#: src/utils/pactl.c:629
+#: src/utils/pactl.c:634
#, c-format
msgid "\tActive Profile: %s\n"
msgstr ""
-#: src/utils/pactl.c:638
+#: src/utils/pactl.c:643
#, c-format
msgid "\t\t%s: %s (type: %s, priority: %u, latency offset: % usec%s%s, %s)\n"
msgstr ""
-#: src/utils/pactl.c:644
+#: src/utils/pactl.c:649
#, c-format
msgid ""
"\t\t\tProperties:\n"
"\t\t\t\t%s\n"
msgstr ""
-#: src/utils/pactl.c:649
+#: src/utils/pactl.c:654
#, c-format
msgid "\t\t\tPart of profile(s): %s"
msgstr ""
-#: src/utils/pactl.c:666 src/utils/pactl.c:986 src/utils/pactl.c:1054
+#: src/utils/pactl.c:671 src/utils/pactl.c:1060 src/utils/pactl.c:1128
#, c-format
msgid "Failed to get sink input information: %s"
msgstr ""
-#: src/utils/pactl.c:695
+#: src/utils/pactl.c:700
#, c-format
msgid ""
"Sink Input #%u\n"
@@ -2289,12 +2307,12 @@ msgid ""
"\t\t%s\n"
msgstr ""
-#: src/utils/pactl.c:736 src/utils/pactl.c:1006 src/utils/pactl.c:1069
+#: src/utils/pactl.c:741 src/utils/pactl.c:1080 src/utils/pactl.c:1143
#, c-format
msgid "Failed to get source output information: %s"
msgstr ""
-#: src/utils/pactl.c:765
+#: src/utils/pactl.c:770
#, c-format
msgid ""
"Source Output #%u\n"
@@ -2316,12 +2334,12 @@ msgid ""
"\t\t%s\n"
msgstr ""
-#: src/utils/pactl.c:806
+#: src/utils/pactl.c:811
#, c-format
msgid "Failed to get sample information: %s"
msgstr ""
-#: src/utils/pactl.c:833
+#: src/utils/pactl.c:838
#, c-format
msgid ""
"Sample #%u\n"
@@ -2338,17 +2356,31 @@ msgid ""
"\t\t%s\n"
msgstr ""
-#: src/utils/pactl.c:861 src/utils/pactl.c:871
+#: src/utils/pactl.c:866 src/utils/pactl.c:876
#, c-format
msgid "Failure: %s"
msgstr ""
-#: src/utils/pactl.c:910
+#: src/utils/pactl.c:889
+#, c-format
+msgid "Send message failed: %s"
+msgstr ""
+
+#: src/utils/pactl.c:906
+#, c-format
+msgid "list-handlers message failed: %s"
+msgstr ""
+
+#: src/utils/pactl.c:912 src/utils/pactl.c:947
+msgid "list-handlers message response could not be parsed correctly"
+msgstr ""
+
+#: src/utils/pactl.c:984
#, c-format
msgid "Failed to unload module: Module %s not loaded"
msgstr ""
-#: src/utils/pactl.c:928
+#: src/utils/pactl.c:1002
#, c-format
msgid ""
"Failed to set volume: You tried to set volumes for %d channel, whereas channel(s) "
@@ -2359,135 +2391,136 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: src/utils/pactl.c:1139
+#: src/utils/pactl.c:1213
#, c-format
msgid "Failed to upload sample: %s"
msgstr ""
-#: src/utils/pactl.c:1156
+#: src/utils/pactl.c:1230
msgid "Premature end of file"
msgstr ""
-#: src/utils/pactl.c:1176
+#: src/utils/pactl.c:1250
msgid "new"
msgstr ""
-#: src/utils/pactl.c:1179
+#: src/utils/pactl.c:1253
msgid "change"
msgstr ""
-#: src/utils/pactl.c:1182
+#: src/utils/pactl.c:1256
msgid "remove"
msgstr ""
-#: src/utils/pactl.c:1185 src/utils/pactl.c:1220
+#: src/utils/pactl.c:1259 src/utils/pactl.c:1294
msgid "unknown"
msgstr ""
-#: src/utils/pactl.c:1193
+#: src/utils/pactl.c:1267
msgid "sink"
msgstr ""
-#: src/utils/pactl.c:1196
+#: src/utils/pactl.c:1270
msgid "source"
msgstr ""
-#: src/utils/pactl.c:1199
+#: src/utils/pactl.c:1273
msgid "sink-input"
msgstr ""
-#: src/utils/pactl.c:1202
+#: src/utils/pactl.c:1276
msgid "source-output"
msgstr ""
-#: src/utils/pactl.c:1205
+#: src/utils/pactl.c:1279
msgid "module"
msgstr ""
-#: src/utils/pactl.c:1208
+#: src/utils/pactl.c:1282
msgid "client"
msgstr ""
-#: src/utils/pactl.c:1211
+#: src/utils/pactl.c:1285
msgid "sample-cache"
msgstr ""
-#: src/utils/pactl.c:1214
+#: src/utils/pactl.c:1288
msgid "server"
msgstr ""
-#: src/utils/pactl.c:1217
+#: src/utils/pactl.c:1291
msgid "card"
msgstr ""
-#: src/utils/pactl.c:1226
+#: src/utils/pactl.c:1300
#, c-format
msgid "Event '%s' on %s #%u\n"
msgstr ""
-#: src/utils/pactl.c:1498
+#: src/utils/pactl.c:1578
msgid "Got SIGINT, exiting."
msgstr ""
-#: src/utils/pactl.c:1531
+#: src/utils/pactl.c:1611
msgid "Invalid volume specification"
msgstr ""
-#: src/utils/pactl.c:1554
+#: src/utils/pactl.c:1634
msgid "Volume outside permissible range.\n"
msgstr ""
-#: src/utils/pactl.c:1567
+#: src/utils/pactl.c:1647
msgid "Invalid number of volume specifications.\n"
msgstr ""
-#: src/utils/pactl.c:1579
+#: src/utils/pactl.c:1659
msgid "Inconsistent volume specification.\n"
msgstr ""
-#: src/utils/pactl.c:1609 src/utils/pactl.c:1610 src/utils/pactl.c:1611
-#: src/utils/pactl.c:1612 src/utils/pactl.c:1613 src/utils/pactl.c:1614
-#: src/utils/pactl.c:1615 src/utils/pactl.c:1616 src/utils/pactl.c:1617
-#: src/utils/pactl.c:1618 src/utils/pactl.c:1619 src/utils/pactl.c:1620
-#: src/utils/pactl.c:1621 src/utils/pactl.c:1622 src/utils/pactl.c:1623
-#: src/utils/pactl.c:1624 src/utils/pactl.c:1625 src/utils/pactl.c:1626
-#: src/utils/pactl.c:1627 src/utils/pactl.c:1628 src/utils/pactl.c:1629
+#: src/utils/pactl.c:1689 src/utils/pactl.c:1690 src/utils/pactl.c:1691
+#: src/utils/pactl.c:1692 src/utils/pactl.c:1693 src/utils/pactl.c:1694
+#: src/utils/pactl.c:1695 src/utils/pactl.c:1696 src/utils/pactl.c:1697
+#: src/utils/pactl.c:1698 src/utils/pactl.c:1699 src/utils/pactl.c:1700
+#: src/utils/pactl.c:1701 src/utils/pactl.c:1702 src/utils/pactl.c:1703
+#: src/utils/pactl.c:1704 src/utils/pactl.c:1705 src/utils/pactl.c:1706
+#: src/utils/pactl.c:1707 src/utils/pactl.c:1708 src/utils/pactl.c:1709
+#: src/utils/pactl.c:1710
msgid "[options]"
msgstr ""
-#: src/utils/pactl.c:1611
+#: src/utils/pactl.c:1691
msgid "[TYPE]"
msgstr ""
-#: src/utils/pactl.c:1613
+#: src/utils/pactl.c:1693
msgid "FILENAME [NAME]"
msgstr ""
-#: src/utils/pactl.c:1614
+#: src/utils/pactl.c:1694
msgid "NAME [SINK]"
msgstr ""
-#: src/utils/pactl.c:1623
+#: src/utils/pactl.c:1703
msgid "NAME|#N VOLUME [VOLUME ...]"
msgstr ""
-#: src/utils/pactl.c:1624
+#: src/utils/pactl.c:1704
msgid "#N VOLUME [VOLUME ...]"
msgstr ""
-#: src/utils/pactl.c:1625
+#: src/utils/pactl.c:1705
msgid "NAME|#N 1|0|toggle"
msgstr ""
-#: src/utils/pactl.c:1626
+#: src/utils/pactl.c:1706
msgid "#N 1|0|toggle"
msgstr ""
-#: src/utils/pactl.c:1627
+#: src/utils/pactl.c:1707
msgid "#N FORMATS"
msgstr ""
-#: src/utils/pactl.c:1630
+#: src/utils/pactl.c:1711
#, c-format
msgid ""
"\n"
@@ -2495,7 +2528,7 @@ msgid ""
"can be used to specify the default sink, source and monitor.\n"
msgstr ""
-#: src/utils/pactl.c:1633
+#: src/utils/pactl.c:1714
#, c-format
msgid ""
"\n"
@@ -2506,7 +2539,7 @@ msgid ""
" -n, --client-name=NAME How to call this client on the server\n"
msgstr ""
-#: src/utils/pactl.c:1674
+#: src/utils/pactl.c:1755
#, c-format
msgid ""
"pactl %s\n"
@@ -2514,146 +2547,156 @@ msgid ""
"Linked with libpulse %s\n"
msgstr ""
-#: src/utils/pactl.c:1730
+#: src/utils/pactl.c:1812
#, c-format
msgid "Specify nothing, or one of: %s"
msgstr ""
-#: src/utils/pactl.c:1740
+#: src/utils/pactl.c:1822
msgid "Please specify a sample file to load"
msgstr ""
-#: src/utils/pactl.c:1753
+#: src/utils/pactl.c:1835
msgid "Failed to open sound file."
msgstr ""
-#: src/utils/pactl.c:1765
+#: src/utils/pactl.c:1847
msgid "Warning: Failed to determine sample specification from file."
msgstr ""
-#: src/utils/pactl.c:1775
+#: src/utils/pactl.c:1857
msgid "You have to specify a sample name to play"
msgstr ""
-#: src/utils/pactl.c:1787
+#: src/utils/pactl.c:1869
msgid "You have to specify a sample name to remove"
msgstr ""
-#: src/utils/pactl.c:1796
+#: src/utils/pactl.c:1878
msgid "You have to specify a sink input index and a sink"
msgstr ""
-#: src/utils/pactl.c:1806
+#: src/utils/pactl.c:1888
msgid "You have to specify a source output index and a source"
msgstr ""
-#: src/utils/pactl.c:1821
+#: src/utils/pactl.c:1903
msgid "You have to specify a module name and arguments."
msgstr ""
-#: src/utils/pactl.c:1841
+#: src/utils/pactl.c:1923
msgid "You have to specify a module index or name"
msgstr ""
-#: src/utils/pactl.c:1854
+#: src/utils/pactl.c:1936
msgid "You may not specify more than one sink. You have to specify a boolean value."
msgstr ""
-#: src/utils/pactl.c:1859 src/utils/pactl.c:1879
+#: src/utils/pactl.c:1941 src/utils/pactl.c:1961
msgid "Invalid suspend specification."
msgstr ""
-#: src/utils/pactl.c:1874
+#: src/utils/pactl.c:1956
msgid "You may not specify more than one source. You have to specify a boolean value."
msgstr ""
-#: src/utils/pactl.c:1891
+#: src/utils/pactl.c:1973
msgid "You have to specify a card name/index and a profile name"
msgstr ""
-#: src/utils/pactl.c:1902
+#: src/utils/pactl.c:1984
msgid "You have to specify a sink name/index and a port name"
msgstr ""
-#: src/utils/pactl.c:1913
+#: src/utils/pactl.c:1995
msgid "You have to specify a sink name"
msgstr ""
-#: src/utils/pactl.c:1923
+#: src/utils/pactl.c:2005
msgid "You have to specify a source name/index and a port name"
msgstr ""
-#: src/utils/pactl.c:1934
+#: src/utils/pactl.c:2016
msgid "You have to specify a source name"
msgstr ""
-#: src/utils/pactl.c:1944
+#: src/utils/pactl.c:2026
msgid "You have to specify a sink name/index and a volume"
msgstr ""
-#: src/utils/pactl.c:1957
+#: src/utils/pactl.c:2039
msgid "You have to specify a source name/index and a volume"
msgstr ""
-#: src/utils/pactl.c:1970
+#: src/utils/pactl.c:2052
msgid "You have to specify a sink input index and a volume"
msgstr ""
-#: src/utils/pactl.c:1975
+#: src/utils/pactl.c:2057
msgid "Invalid sink input index"
msgstr ""
-#: src/utils/pactl.c:1986
+#: src/utils/pactl.c:2068
msgid "You have to specify a source output index and a volume"
msgstr ""
-#: src/utils/pactl.c:1991
+#: src/utils/pactl.c:2073
msgid "Invalid source output index"
msgstr ""
-#: src/utils/pactl.c:2002
+#: src/utils/pactl.c:2084
msgid "You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
msgstr ""
-#: src/utils/pactl.c:2007 src/utils/pactl.c:2022 src/utils/pactl.c:2042
-#: src/utils/pactl.c:2060
+#: src/utils/pactl.c:2089 src/utils/pactl.c:2104 src/utils/pactl.c:2124
+#: src/utils/pactl.c:2142
msgid "Invalid mute specification"
msgstr ""
-#: src/utils/pactl.c:2017
+#: src/utils/pactl.c:2099
msgid "You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
msgstr ""
-#: src/utils/pactl.c:2032
+#: src/utils/pactl.c:2114
msgid "You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
msgstr ""
-#: src/utils/pactl.c:2037
+#: src/utils/pactl.c:2119
msgid "Invalid sink input index specification"
msgstr ""
-#: src/utils/pactl.c:2050
+#: src/utils/pactl.c:2132
msgid "You have to specify a source output index and a mute action (0, 1, or 'toggle')"
msgstr ""
-#: src/utils/pactl.c:2055
+#: src/utils/pactl.c:2137
msgid "Invalid source output index specification"
msgstr ""
-#: src/utils/pactl.c:2072
+#: src/utils/pactl.c:2150
+msgid "You have to specify at least an object path and a message name"
+msgstr ""
+
+#: src/utils/pactl.c:2160
+msgid ""
+"Excess arguments given, they will be ignored. Note that all message parameters must be "
+"given as a single string."
+msgstr ""
+
+#: src/utils/pactl.c:2170
msgid ""
"You have to specify a sink index and a semicolon-separated list of supported formats"
msgstr ""
-#: src/utils/pactl.c:2084
+#: src/utils/pactl.c:2182
msgid "You have to specify a card name/index, a port name and a latency offset"
msgstr ""
-#: src/utils/pactl.c:2091
+#: src/utils/pactl.c:2189
msgid "Could not parse latency offset"
msgstr ""
-#: src/utils/pactl.c:2103
+#: src/utils/pactl.c:2201
msgid "No valid command specified."
msgstr ""
From a17d00c3d2e306e782524eccc9b5a516be3d2be7 Mon Sep 17 00:00:00 2001
From: Jaechul Lee
Date: Tue, 8 Dec 2020 11:20:14 +0900
Subject: [PATCH 042/865] null-source: Fix max_latency_msec argument is not
changed
This module uses 2 seconds for default max latency. If max_latency_msec
is set, max_latency value will be changed.
Part-of:
---
src/modules/module-null-source.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/modules/module-null-source.c b/src/modules/module-null-source.c
index 38e3c823e..3aefbf2c5 100644
--- a/src/modules/module-null-source.c
+++ b/src/modules/module-null-source.c
@@ -200,7 +200,6 @@ int pa__init(pa_module*m) {
pa_channel_map map;
pa_modargs *ma = NULL;
pa_source_new_data data;
- pa_usec_t max_latency = MAX_LATENCY_USEC;
uint32_t max_latency_msec;
pa_assert(m);
@@ -252,10 +251,13 @@ int pa__init(pa_module*m) {
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
- if (pa_modargs_get_value_u32(ma, "max_latency_msec", &max_latency_msec))
- max_latency = max_latency_msec * PA_USEC_PER_MSEC;
+ max_latency_msec = MAX_LATENCY_USEC / PA_USEC_PER_MSEC;
+ if (pa_modargs_get_value_u32(ma, "max_latency_msec", &max_latency_msec) < 0) {
+ pa_log("Failed to get max_latency_msec.");
+ goto fail;
+ }
- pa_source_set_latency_range(u->source, MIN_LATENCY_USEC, max_latency);
+ pa_source_set_latency_range(u->source, MIN_LATENCY_USEC, max_latency_msec * PA_USEC_PER_MSEC);
u->block_usec = u->source->thread_info.max_latency;
From 8342c1233977504f5e3d2d61b1bdb4c7884b1d8c Mon Sep 17 00:00:00 2001
From: Ben Buchwald
Date: Mon, 6 Jan 2020 18:03:42 -0500
Subject: [PATCH 043/865] jackdbus-detect: Passthrough more arguments to sink
and source
module-jackdbus-detect now accepts sink_name, sink_properties,
sink_client_name, sink_channel_map, source_name, source_properties,
source_client_name, and source_channel_map arguments that will be passed
through to module-jack-source and module-jack-sink (without the sink and
source prefixes, except where needed).
---
src/modules/jack/module-jackdbus-detect.c | 169 +++++++++++++++++++---
1 file changed, 148 insertions(+), 21 deletions(-)
diff --git a/src/modules/jack/module-jackdbus-detect.c b/src/modules/jack/module-jackdbus-detect.c
index 63c430748..0a9f24c96 100644
--- a/src/modules/jack/module-jackdbus-detect.c
+++ b/src/modules/jack/module-jackdbus-detect.c
@@ -25,12 +25,14 @@
#include
#endif
+#include
#include
#include
#include
#include
#include
+#include
PA_MODULE_AUTHOR("David Henningsson");
PA_MODULE_DESCRIPTION("Adds JACK sink/source ports when JACK is started");
@@ -38,8 +40,16 @@ PA_MODULE_LOAD_ONCE(true);
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_USAGE(
"channels= "
- "source_channels= "
+ "sink_name= "
+ "sink_properties= "
+ "sink_client_name= "
"sink_channels= "
+ "sink_channel_map= "
+ "source_name= "
+ "source_properties= "
+ "source_client_name= "
+ "source_channels= "
+ "source_channel_map= "
"connect=");
#define JACK_SERVICE_NAME "org.jackaudio.service"
@@ -61,8 +71,16 @@ PA_MODULE_USAGE(
static const char* const valid_modargs[] = {
"channels",
- "source_channels",
+ "sink_name",
+ "sink_properties",
+ "sink_client_name",
"sink_channels",
+ "sink_channel_map",
+ "source_name",
+ "source_properties",
+ "source_client_name",
+ "source_channels",
+ "source_channel_map",
"connect",
NULL
};
@@ -76,6 +94,19 @@ static const char* const modnames[JACK_SS_COUNT] = {
"module-jack-source"
};
+static const char* const modtypes[JACK_SS_COUNT] = {
+ "sink",
+ "source"
+};
+
+struct moddata {
+ char *name;
+ pa_proplist *proplist;
+ char *client_name;
+ uint32_t channels;
+ pa_channel_map channel_map;
+};
+
struct userdata {
pa_module *module;
pa_core *core;
@@ -83,13 +114,13 @@ struct userdata {
bool filter_added, match_added;
bool is_service_started;
bool autoconnect_ports;
- uint32_t channels[JACK_SS_COUNT];
+ struct moddata mod_args[JACK_SS_COUNT];
/* Using index here protects us from module unloading without us knowing */
int jack_module_index[JACK_SS_COUNT];
};
static void ensure_ports_stopped(struct userdata* u) {
- int i;
+ unsigned i;
pa_assert(u);
for (i = 0; i < JACK_SS_COUNT; i++)
@@ -100,19 +131,81 @@ static void ensure_ports_stopped(struct userdata* u) {
}
}
+static char* proplist_to_arg(pa_proplist *p) {
+ const char *key;
+ void *state = NULL;
+ pa_strbuf *buf;
+
+ pa_assert(p);
+
+ buf = pa_strbuf_new();
+
+ while ((key = pa_proplist_iterate(p, &state))) {
+ const char *v;
+ char *escaped;
+
+ if (!pa_strbuf_isempty(buf))
+ pa_strbuf_puts(buf, " ");
+
+ if ((v = pa_proplist_gets(p, key))) {
+ pa_strbuf_printf(buf, "%s=\"", key);
+
+ escaped = pa_escape(v, "\"'");
+ pa_strbuf_puts(buf, escaped);
+ pa_xfree(escaped);
+
+ pa_strbuf_puts(buf, "\"");
+ } else {
+ const void *value;
+ size_t nbytes;
+ char *c;
+
+ pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
+ c = pa_xmalloc(nbytes*2+1);
+ pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
+
+ pa_strbuf_printf(buf, "%s=hex:%s", key, c);
+ pa_xfree(c);
+ }
+ }
+
+ return pa_strbuf_to_string_free(buf);
+}
+
static void ensure_ports_started(struct userdata* u) {
- int i;
+ unsigned i;
+ char *escaped;
pa_assert(u);
for (i = 0; i < JACK_SS_COUNT; i++)
if (!u->jack_module_index[i]) {
- char* args;
- pa_module* m;
- if (u->channels[i] > 0) {
- args = pa_sprintf_malloc("connect=%s channels=%" PRIu32, pa_yes_no(u->autoconnect_ports), u->channels[i]);
- } else {
- args = pa_sprintf_malloc("connect=%s", pa_yes_no(u->autoconnect_ports));
+ pa_strbuf *args_buf = pa_strbuf_new();
+ char *args;
+ pa_module *m;
+ pa_strbuf_printf(args_buf, "connect=%s", pa_yes_no(u->autoconnect_ports));
+ if (u->mod_args[i].name) {
+ escaped = pa_escape(u->mod_args[i].name, "'");
+ pa_strbuf_printf(args_buf, " %s_name='%s'", modtypes[i], escaped);
+ pa_xfree(escaped);
}
+ if (!pa_proplist_isempty(u->mod_args[i].proplist)) {
+ escaped = proplist_to_arg(u->mod_args[i].proplist);
+ pa_strbuf_printf(args_buf, " %s_properties='%s'", modtypes[i], escaped);
+ pa_xfree(escaped);
+ }
+ if (u->mod_args[i].client_name) {
+ escaped = pa_escape(u->mod_args[i].client_name, "'");
+ pa_strbuf_printf(args_buf, " client_name='%s'", escaped);
+ pa_xfree(escaped);
+ }
+ if (u->mod_args[i].channels > 0)
+ pa_strbuf_printf(args_buf, " channels=%" PRIu32, u->mod_args[i].channels);
+ if (u->mod_args[i].channel_map.channels > 0) {
+ char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ pa_channel_map_snprint(cm, sizeof(cm), &u->mod_args[i].channel_map);
+ pa_strbuf_printf(args_buf, " channel_map='%s'", cm);
+ }
+ args = pa_strbuf_to_string_free(args_buf);
pa_module_load(&m, u->core, modnames[i], args);
pa_xfree(args);
@@ -218,7 +311,9 @@ int pa__init(pa_module *m) {
struct userdata *u = NULL;
pa_modargs *ma;
uint32_t channels = 0;
- int i;
+ unsigned i;
+ char argname[32];
+ const char *name;
pa_assert(m);
@@ -243,18 +338,40 @@ int pa__init(pa_module *m) {
pa_log("Failed to parse channels= argument.");
goto fail;
}
+
for (i = 0; i < JACK_SS_COUNT; i++) {
- u->channels[i] = channels;
- }
+ pa_snprintf(argname, sizeof(argname), "%s_name", modtypes[i]);
+ name = pa_modargs_get_value(ma, argname, NULL);
+ u->mod_args[i].name = pa_xstrdup(name);
- if (pa_modargs_get_value_u32(ma, "source_channels", &u->channels[JACK_SS_SOURCE]) < 0 || (u->channels[JACK_SS_SOURCE] > 0 && !pa_channels_valid(u->channels[JACK_SS_SOURCE]))) {
- pa_log("Failed to parse source_channels= argument.");
- goto fail;
- }
+ u->mod_args[i].proplist = pa_proplist_new();
+ pa_snprintf(argname, sizeof(argname), "%s_properties", modtypes[i]);
+ if (pa_modargs_get_proplist(ma, argname, u->mod_args[i].proplist, PA_UPDATE_REPLACE) < 0) {
+ pa_log("Invalid %s properties", modtypes[i]);
+ goto fail;
+ }
- if (pa_modargs_get_value_u32(ma, "sink_channels", &u->channels[JACK_SS_SINK]) < 0 || (u->channels[JACK_SS_SINK] > 0 && !pa_channels_valid(u->channels[JACK_SS_SINK]))) {
- pa_log("Failed to parse sink_channels= argument.");
- goto fail;
+ pa_snprintf(argname, sizeof(argname), "%s_client_name", modtypes[i]);
+ name = pa_modargs_get_value(ma, argname, NULL);
+ u->mod_args[i].client_name = pa_xstrdup(name);
+
+ u->mod_args[i].channels = channels;
+ pa_snprintf(argname, sizeof(argname), "%s_channels", modtypes[i]);
+ if (pa_modargs_get_value_u32(ma, argname, &u->mod_args[i].channels) < 0
+ || (u->mod_args[i].channels > 0 && !pa_channels_valid(u->mod_args[i].channels))) {
+ pa_log("Failed to parse %s= argument.", argname);
+ goto fail;
+ }
+
+ pa_channel_map_init(&u->mod_args[i].channel_map);
+ pa_snprintf(argname, sizeof(argname), "%s_channel_map", modtypes[i]);
+ if (pa_modargs_get_value(ma, argname, NULL)) {
+ if (pa_modargs_get_channel_map(ma, argname, &u->mod_args[i].channel_map) < 0
+ || (u->mod_args[i].channels > 0 && u->mod_args[i].channel_map.channels != u->mod_args[i].channels)) {
+ pa_log("Failed to parse %s= argument.", argname);
+ goto fail;
+ }
+ }
}
if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
@@ -298,6 +415,7 @@ fail:
void pa__done(pa_module *m) {
struct userdata *u;
+ unsigned i;
pa_assert(m);
@@ -320,5 +438,14 @@ void pa__done(pa_module *m) {
pa_dbus_connection_unref(u->connection);
}
+ for (i = 0; i < JACK_SS_COUNT; i++) {
+ pa_xfree(u->mod_args[i].name);
+
+ if (u->mod_args[i].proplist)
+ pa_proplist_free(u->mod_args[i].proplist);
+
+ pa_xfree(u->mod_args[i].client_name);
+ }
+
pa_xfree(u);
}
From 05f3e8bf9ae7adeb4db8b77c0a92dbb5350d189a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Danis?=
Date: Tue, 28 May 2019 16:49:19 +0200
Subject: [PATCH 044/865] core, modules: Remove useless EINTR tests
Since commit ad447d14682 (in 2009) pa_read and pa_write take care of
handling EINTR error.
So, pa_read, pa_write, pa_iochannel_read and pa_iochannel_write can not
exit with errno set to EINTR, and testing it is useless.
---
src/modules/bluetooth/module-bluez5-device.c | 10 +---
src/modules/module-esound-sink.c | 4 +-
src/modules/module-pipe-sink.c | 17 +++----
src/modules/module-pipe-source.c | 4 +-
src/modules/module-solaris.c | 4 +-
src/modules/oss/module-oss.c | 10 +---
src/pulsecore/fdsem.c | 52 +++++---------------
src/pulsecore/iochannel.c | 2 +-
src/pulsecore/protocol-esound.c | 8 +--
src/pulsecore/protocol-simple.c | 2 +-
10 files changed, 31 insertions(+), 82 deletions(-)
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
index 402053a59..87a8809ff 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -279,10 +279,6 @@ static int sco_process_render(struct userdata *u) {
saved_errno = errno;
- if (saved_errno == EINTR)
- /* Retry right away if we got interrupted */
- continue;
-
pa_memblock_unref(memchunk.memblock);
if (saved_errno == EAGAIN) {
@@ -462,11 +458,7 @@ static int a2dp_write_buffer(struct userdata *u, size_t nbytes) {
if (l < 0) {
- if (errno == EINTR)
- /* Retry right away if we got interrupted */
- continue;
-
- else if (errno == EAGAIN) {
+ if (errno == EAGAIN) {
/* Hmm, apparently the socket was not writable, give up for now */
pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss.");
break;
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 5ff04516a..f46dc3889 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -249,9 +249,7 @@ static void thread_func(void *userdata) {
if (l < 0) {
- if (errno == EINTR)
- continue;
- else if (errno == EAGAIN) {
+ if (errno == EAGAIN) {
/* OK, we filled all socket buffers up
* now. */
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 213924fdc..43595420f 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -199,14 +199,13 @@ static ssize_t pipe_sink_write(struct userdata *u, pa_memchunk *pchunk) {
if (l < 0) {
if (errno == EAGAIN)
break;
- else if (errno != EINTR) {
- if (!u->fifo_error) {
- pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- u->fifo_error = true;
- }
- count = -1 - count;
- break;
+
+ if (!u->fifo_error) {
+ pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ u->fifo_error = true;
}
+ count = -1 - count;
+ break;
} else {
if (u->fifo_error) {
pa_log_debug("Recovered from FIFO error");
@@ -288,9 +287,7 @@ static int process_render(struct userdata *u) {
if (l < 0) {
- if (errno == EINTR)
- continue;
- else if (errno == EAGAIN)
+ if (errno == EAGAIN)
return 0;
else {
pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index 74ec0551a..32b35c163 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -155,9 +155,7 @@ static void thread_func(void *userdata) {
if (l < 0) {
- if (errno == EINTR)
- continue;
- else if (errno != EAGAIN) {
+ if (errno != EAGAIN) {
pa_log("Failed to read data from FIFO: %s", pa_cstrerror(errno));
goto fail;
}
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index 038aca114..ec9eb875f 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -714,9 +714,7 @@ static void thread_func(void *userdata) {
pa_memblock_release(u->memchunk.memblock);
if (w <= 0) {
- if (errno == EINTR) {
- continue;
- } else if (errno == EAGAIN) {
+ if (errno == EAGAIN) {
/* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */
pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes);
break;
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index ed124cab4..6eb025489 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -980,10 +980,7 @@ static void thread_func(void *userdata) {
if (t < 0) {
- if (errno == EINTR)
- continue;
-
- else if (errno == EAGAIN) {
+ if (errno == EAGAIN) {
pa_log_debug("EAGAIN");
revents &= ~POLLOUT;
@@ -1087,10 +1084,7 @@ static void thread_func(void *userdata) {
if (t < 0) {
pa_memblock_unref(memchunk.memblock);
- if (errno == EINTR)
- continue;
-
- else if (errno == EAGAIN) {
+ if (errno == EAGAIN) {
pa_log_debug("EAGAIN");
revents &= ~POLLIN;
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
index a7fbf95d2..5fc22975a 100644
--- a/src/pulsecore/fdsem.c
+++ b/src/pulsecore/fdsem.c
@@ -151,26 +151,16 @@ static void flush(pa_fdsem *f) {
uint64_t u;
if ((r = pa_read(f->efd, &u, sizeof(u), NULL)) != sizeof(u)) {
-
- if (r >= 0 || errno != EINTR) {
- pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
- pa_assert_not_reached();
- }
-
- continue;
+ pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+ pa_assert_not_reached();
}
r = (ssize_t) u;
} else
#endif
if ((r = pa_read(f->fds[0], &x, sizeof(x), NULL)) <= 0) {
-
- if (r >= 0 || errno != EINTR) {
- pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
- pa_assert_not_reached();
- }
-
- continue;
+ pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+ pa_assert_not_reached();
}
} while (pa_atomic_sub(&f->data->in_pipe, (int) r) > (int) r);
@@ -194,23 +184,15 @@ void pa_fdsem_post(pa_fdsem *f) {
uint64_t u = 1;
if ((r = pa_write(f->efd, &u, sizeof(u), &f->write_type)) != sizeof(u)) {
- if (r >= 0 || errno != EINTR) {
- pa_log_error("Invalid write to eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
- pa_assert_not_reached();
- }
-
- continue;
+ pa_log_error("Invalid write to eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+ pa_assert_not_reached();
}
} else
#endif
if ((r = pa_write(f->fds[1], &x, 1, &f->write_type)) != 1) {
- if (r >= 0 || errno != EINTR) {
- pa_log_error("Invalid write to pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
- pa_assert_not_reached();
- }
-
- continue;
+ pa_log_error("Invalid write to pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+ pa_assert_not_reached();
}
break;
@@ -238,13 +220,8 @@ void pa_fdsem_wait(pa_fdsem *f) {
uint64_t u;
if ((r = pa_read(f->efd, &u, sizeof(u), NULL)) != sizeof(u)) {
-
- if (r >= 0 || errno != EINTR) {
- pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
- pa_assert_not_reached();
- }
-
- continue;
+ pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+ pa_assert_not_reached();
}
r = (ssize_t) u;
@@ -252,13 +229,8 @@ void pa_fdsem_wait(pa_fdsem *f) {
#endif
if ((r = pa_read(f->fds[0], &x, sizeof(x), NULL)) <= 0) {
-
- if (r >= 0 || errno != EINTR) {
- pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
- pa_assert_not_reached();
- }
-
- continue;
+ pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+ pa_assert_not_reached();
}
pa_atomic_sub(&f->data->in_pipe, (int) r);
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
index e25824b78..eb93176ec 100644
--- a/src/pulsecore/iochannel.c
+++ b/src/pulsecore/iochannel.c
@@ -227,7 +227,7 @@ ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
return r; /* Fast path - we almost always successfully write everything */
if (r < 0) {
- if (errno == EINTR || errno == EAGAIN)
+ if (errno == EAGAIN)
r = 0;
else
return r;
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index d54c7f845..cf0fe4fdf 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -1010,7 +1010,7 @@ static int do_read(connection *c) {
((uint8_t*) &c->request) + c->read_data_length,
sizeof(c->request) - c->read_data_length)) <= 0) {
- if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ if (r < 0 && errno == EAGAIN)
return 0;
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
@@ -1066,7 +1066,7 @@ static int do_read(connection *c) {
(uint8_t*) c->read_data + c->read_data_length,
handler->data_length - c->read_data_length)) <= 0) {
- if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ if (r < 0 && errno == EAGAIN)
return 0;
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
@@ -1097,7 +1097,7 @@ static int do_read(connection *c) {
pa_memblock_release(c->scache.memchunk.memblock);
if (r <= 0) {
- if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ if (r < 0 && errno == EAGAIN)
return 0;
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
@@ -1165,7 +1165,7 @@ static int do_read(connection *c) {
if (r <= 0) {
- if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ if (r < 0 && errno == EAGAIN)
return 0;
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 77d05398c..ed6a402e7 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -183,7 +183,7 @@ static int do_read(connection *c) {
if (r <= 0) {
- if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ if (r < 0 && errno == EAGAIN)
return 0;
pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));
From e43ca00d5255d81d2004bf33f30dab3843e8aa1d Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Tue, 11 Feb 2020 09:40:49 +0200
Subject: [PATCH 045/865] module: Check version before loading a module
Since there's no stable API for modules, all modules need to be compiled
together with the server. This version check tries to ensure that if
a version mismatch happens, there will be an informative error message
rather than a random crash.
---
src/pulsecore/module.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 15a54b6ec..040886d2d 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -45,6 +45,7 @@
#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
#define PA_SYMBOL_GET_N_USED "pa__get_n_used"
#define PA_SYMBOL_GET_DEPRECATE "pa__get_deprecated"
+#define PA_SYMBOL_GET_VERSION "pa__get_version"
bool pa_module_exists(const char *name) {
const char *paths, *state = NULL;
@@ -113,6 +114,7 @@ void pa_module_hook_connect(pa_module *m, pa_hook *hook, pa_hook_priority_t prio
int pa_module_load(pa_module** module, pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
+ const char *(*get_version)(void);
bool (*load_once)(void);
const char* (*get_deprecated)(void);
pa_modinfo *mi;
@@ -147,6 +149,21 @@ int pa_module_load(pa_module** module, pa_core *c, const char *name, const char
goto fail;
}
+ if ((get_version = (const char *(*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_VERSION))) {
+ const char *version = get_version();
+
+ if (!pa_safe_streq(version, PACKAGE_VERSION)) {
+ pa_log("Module \"%s\" version (%s) doesn't match the expected version (%s).",
+ name, pa_strnull(version), PACKAGE_VERSION);
+ errcode = -PA_ERR_IO;
+ goto fail;
+ }
+ } else {
+ pa_log("Symbol \"%s\" not found in module \"%s\".", PA_SYMBOL_GET_VERSION, name);
+ errcode = -PA_ERR_IO;
+ goto fail;
+ }
+
if ((load_once = (bool (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
m->load_once = load_once();
From dc79f304ddc4a2bfcaf7b2d69fa5f1c28dc95ce8 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Fri, 14 Feb 2020 07:29:33 +0200
Subject: [PATCH 046/865] build-sys: Add an option for enabling/disabling
Valgrind
In OpenEmbedded the PulseAudio recipe currently disables Valgrind
support by passing "ac_cv_header_valgrind_memcheck_h=no" to the
configure script (this was added to make it deterministic whether
Valgrdind support gets enabled or not). I'm converting the PulseAudio
recipe to use Meson, and I needed an option to disable Valgrind.
---
meson.build | 6 +++++-
meson_options.txt | 3 +++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/meson.build b/meson.build
index 06c44d748..3adb876ff 100644
--- a/meson.build
+++ b/meson.build
@@ -221,7 +221,6 @@ check_headers = [
'sys/un.h',
'sys/wait.h',
'syslog.h',
- 'valgrind/memcheck.h',
'xlocale.h',
]
@@ -232,6 +231,10 @@ foreach h : check_headers
endif
endforeach
+if cc.has_header('valgrind/memcheck.h', required: get_option('valgrind'))
+ cdata.set('HAVE_VALGRIND_MEMCHECK_H', 1)
+endif
+
# FIXME: move this to the above set
if cc.has_header('pthread.h')
cdata.set('HAVE_PTHREAD', 1)
@@ -842,6 +845,7 @@ summary = [
'Enable SoXR (resampler): @0@'.format(soxr_dep.found()),
'Enable WebRTC echo canceller: @0@'.format(webrtc_dep.found()),
'Enable Gcov coverage: @0@'.format(get_option('gcov')),
+ 'Enable Valgrind: @0@'.format(cdata.has('HAVE_VALGRIND_MEMCHECK_H')),
'Enable man pages: @0@'.format(get_option('man')),
'Enable unit tests: @0@'.format(get_option('tests')),
'',
diff --git a/meson_options.txt b/meson_options.txt
index 5283a6d98..247f26a78 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -141,6 +141,9 @@ option('systemd',
option('udev',
type : 'feature', value : 'auto',
description : 'Optional udev support')
+option('valgrind',
+ type : 'feature', value : 'auto',
+ description : 'Optional Valgrind support')
option('x11',
type : 'feature', value : 'auto',
description : 'Optional X11 support')
From a8cb4edf7591bf3038769ea1cd2eae264c67ea51 Mon Sep 17 00:00:00 2001
From: Pierre Ossman
Date: Tue, 7 Jul 2015 13:01:22 +0200
Subject: [PATCH 047/865] tunnel-sink-new: log buffer events
Aids in tracking down exactly where in the chain underruns are
happening.
Signed-off-by: Pierre Ossman
---
src/modules/module-tunnel-sink-new.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/src/modules/module-tunnel-sink-new.c b/src/modules/module-tunnel-sink-new.c
index 802e6a59a..66a178717 100644
--- a/src/modules/module-tunnel-sink-new.c
+++ b/src/modules/module-tunnel-sink-new.c
@@ -280,6 +280,9 @@ static void stream_changed_buffer_attr_cb(pa_stream *stream, void *userdata) {
bufferattr = pa_stream_get_buffer_attr(u->stream);
pa_sink_set_max_request_within_thread(u->sink, bufferattr->tlength);
+
+ pa_log_debug("Server reports buffer attrs changed. tlength now at %lu.",
+ (unsigned long) bufferattr->tlength);
}
/* called after we requested a change of the stream buffer_attr */
@@ -287,6 +290,16 @@ static void stream_set_buffer_attr_cb(pa_stream *stream, int success, void *user
stream_changed_buffer_attr_cb(stream, userdata);
}
+/* called when the server experiences an underrun of our buffer */
+static void stream_underflow_callback(pa_stream *stream, void *userdata) {
+ pa_log_info("Server signalled buffer underrun.");
+}
+
+/* called when the server experiences an overrun of our buffer */
+static void stream_overflow_callback(pa_stream *stream, void *userdata) {
+ pa_log_info("Server signalled buffer overrun.");
+}
+
static void context_state_cb(pa_context *c, void *userdata) {
struct userdata *u = userdata;
pa_assert(u);
@@ -333,8 +346,12 @@ static void context_state_cb(pa_context *c, void *userdata) {
reset_bufferattr(&bufferattr);
bufferattr.tlength = pa_usec_to_bytes(requested_latency, &u->sink->sample_spec);
+ pa_log_debug("tlength requested at %lu.", (unsigned long) bufferattr.tlength);
+
pa_stream_set_state_callback(u->stream, stream_state_cb, userdata);
pa_stream_set_buffer_attr_callback(u->stream, stream_changed_buffer_attr_cb, userdata);
+ pa_stream_set_underflow_callback(u->stream, stream_underflow_callback, userdata);
+ pa_stream_set_overflow_callback(u->stream, stream_overflow_callback, userdata);
if (pa_stream_connect_playback(u->stream,
u->remote_sink_name,
&bufferattr,
@@ -383,6 +400,9 @@ static void sink_update_requested_latency_cb(pa_sink *s) {
if (pa_stream_get_buffer_attr(u->stream)->tlength == nbytes)
break;
+ pa_log_debug("Requesting new buffer attrs. tlength requested at %lu.",
+ (unsigned long) nbytes);
+
reset_bufferattr(&bufferattr);
bufferattr.tlength = nbytes;
if ((operation = pa_stream_set_buffer_attr(u->stream, &bufferattr, stream_set_buffer_attr_cb, u)))
From 7ec6ee4725a72b42ddcb38550efc968a06e6be2f Mon Sep 17 00:00:00 2001
From: Patrick McLean
Date: Wed, 26 Feb 2020 00:26:12 -0800
Subject: [PATCH 048/865] alsa-mixer: add support for Sennheiser GSX 1000
gaming DAC
This same profile should also work for the GSX 1200, but I don't know the USB id
for that.
Part-of:
---
src/Makefile.am | 7 ++-
src/modules/alsa/90-pulseaudio.rules | 7 +++
src/modules/alsa/alsa-mixer.c | 4 ++
.../alsa/mixer/paths/analog-output-chat.conf | 5 ++
.../mixer/paths/virtual-surround-7.1.conf | 5 ++
.../mixer/profile-sets/sennheiser-gsx.conf | 58 +++++++++++++++++++
6 files changed, 84 insertions(+), 2 deletions(-)
create mode 100644 src/modules/alsa/mixer/paths/analog-output-chat.conf
create mode 100644 src/modules/alsa/mixer/paths/virtual-surround-7.1.conf
create mode 100644 src/modules/alsa/mixer/profile-sets/sennheiser-gsx.conf
diff --git a/src/Makefile.am b/src/Makefile.am
index df912a8b3..eeec40b55 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1370,7 +1370,8 @@ dist_alsaprofilesets_DATA = \
modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf \
modules/alsa/mixer/profile-sets/cmedia-high-speed-true-hdaudio.conf \
modules/alsa/mixer/profile-sets/hp-tbt-dock-120w-g2.conf \
- modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf
+ modules/alsa/mixer/profile-sets/hp-tbt-dock-audio-module.conf \
+ modules/alsa/mixer/profile-sets/sennheiser-gsx.conf
if HAVE_UDEV
dist_udevrules_DATA = \
@@ -1417,7 +1418,9 @@ dist_alsapaths_DATA = \
modules/alsa/mixer/paths/steelseries-arctis-output-game-common.conf \
modules/alsa/mixer/paths/usb-gaming-headset-input.conf \
modules/alsa/mixer/paths/usb-gaming-headset-output-mono.conf \
- modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
+ modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf \
+ modules/alsa/mixer/paths/analog-output-chat.conf \
+ modules/alsa/mixer/paths/virtual-surround-7.1.conf
endif
diff --git a/src/modules/alsa/90-pulseaudio.rules b/src/modules/alsa/90-pulseaudio.rules
index 4de40127e..cc5a3597a 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -150,6 +150,13 @@ ATTRS{idVendor}=="0951", ATTRS{idProduct}=="1703", ENV{ID_ID}="usb-HyperX_Cloud_
# OnePlus Type-C Bullets (ED117)
ATTRS{idVendor}=="2a70", ATTRS{idProduct}=="1881", ENV{PULSE_PROFILE_SET}="simple-headphones-mic.conf"
+# ID 1395:005e is for Sennheiser GSX 1000
+# ID 1395:00a0 is for Sennheiser GSX 1000
+# ID 1395:005f is for Sennheiser GSX 1200
+ATTRS{idVendor}=="1395", ATTRS{idProduct}=="005e", ENV{PULSE_PROFILE_SET}="sennheiser-gsx.conf"
+ATTRS{idVendor}=="1395", ATTRS{idProduct}=="00a0", ENV{PULSE_PROFILE_SET}="sennheiser-gsx.conf"
+ATTRS{idVendor}=="1395", ATTRS{idProduct}=="005f", ENV{PULSE_PROFILE_SET}="sennheiser-gsx.conf"
+
GOTO="pulseaudio_end"
LABEL="pulseaudio_check_pci"
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 937771d43..f978f71c3 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -2729,6 +2729,9 @@ static int path_verify(pa_alsa_path *p) {
{ "multichannel-output", N_("Multichannel Output"), PA_DEVICE_PORT_TYPE_LINE },
{ "steelseries-arctis-output-game-common", N_("Game Output"), PA_DEVICE_PORT_TYPE_HEADSET },
{ "steelseries-arctis-output-chat-common", N_("Chat Output"), PA_DEVICE_PORT_TYPE_HEADSET },
+ { "analog-chat-output", N_("Chat Output"), PA_DEVICE_PORT_TYPE_HEADSET },
+ { "analog-chat-input", N_("Chat Input"), PA_DEVICE_PORT_TYPE_HEADSET },
+ { "virtual-surround-7.1", N_("Virtual Surround 7.1"), PA_DEVICE_PORT_TYPE_HEADPHONES },
};
pa_alsa_element *e;
@@ -4567,6 +4570,7 @@ static int profile_verify(pa_alsa_profile *p) {
{ "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
{ "output:multichannel-output+input:multichannel-input", N_("Multichannel Duplex") },
{ "output:unknown-stereo+input:unknown-stereo", N_("Stereo Duplex") },
+ { "output:analog-output-surround71+output:analog-output-chat+input:analog-input", N_("Mono Chat + 7.1 Surround") },
{ "off", N_("Off") }
};
const char *description_key = p->description_key ? p->description_key : p->name;
diff --git a/src/modules/alsa/mixer/paths/analog-output-chat.conf b/src/modules/alsa/mixer/paths/analog-output-chat.conf
new file mode 100644
index 000000000..360a1fceb
--- /dev/null
+++ b/src/modules/alsa/mixer/paths/analog-output-chat.conf
@@ -0,0 +1,5 @@
+; Some gaming devices have a separate "chat" device, this is for voice chat
+; while playing games. This device is just a fairly standard analog mono
+; device, but it's nicer to make it clear that this is the "chat" device
+; as is mentioned in the marketing info and manual.
+.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/virtual-surround-7.1.conf b/src/modules/alsa/mixer/paths/virtual-surround-7.1.conf
new file mode 100644
index 000000000..7f111f276
--- /dev/null
+++ b/src/modules/alsa/mixer/paths/virtual-surround-7.1.conf
@@ -0,0 +1,5 @@
+[Element PCM,1]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
diff --git a/src/modules/alsa/mixer/profile-sets/sennheiser-gsx.conf b/src/modules/alsa/mixer/profile-sets/sennheiser-gsx.conf
new file mode 100644
index 000000000..0ac157685
--- /dev/null
+++ b/src/modules/alsa/mixer/profile-sets/sennheiser-gsx.conf
@@ -0,0 +1,58 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see .
+
+; USB Gaming DAC.
+; These devices have two output devices. The first one is mono, meant for
+; voice audio, and the second one is 7.1 surround, meant for everything
+; else. The 7.1 surround is mapped to headphones within the device.
+; The purpose of the mono/7.1 design is to provide separate volume
+; controls for voice and other audio, which can be useful in gaming.
+;
+; Works with:
+; Sennheiser GSX 1000
+; Sennheiser GSX 1200
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-chat-output]
+device-strings = hw:%f,0
+channel-map = mono
+paths-output = analog-chat-output
+direction = output
+priority = 4000
+intended-roles = phone
+
+[Mapping analog-output-surround71]
+device-strings = hw:%f,1
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+paths-output = virtual-surround-7.1
+priority = 4100
+direction = output
+
+[Mapping analog-chat-input]
+device-strings = hw:%f,0
+channel-map = mono
+paths-input = analog-chat-input
+priority = 4100
+direction = input
+
+[Profile output:analog-output-surround71+output:analog-output-chat+input:analog-input]
+output-mappings = analog-output-surround71 analog-chat-output
+input-mappings = analog-chat-input
+priority = 5100
+skip-probe = yes
From 3afb7f8c08ab6ce8e47075f7484f470c8c561a11 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Sat, 14 Mar 2020 08:22:44 +0200
Subject: [PATCH 049/865] pstream: Log "Cannot send block reference..." only
once per pstream
Despite the ratelimiting, this error gets logged too much.
I fixed a typo (Fallig -> Falling) while at it.
Related: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/824
Part-of:
---
src/pulsecore/pstream.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index eb7050828..7147b776a 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -154,6 +154,7 @@ struct pa_pstream {
* @registered_memfd_ids: registered memfd pools SHM IDs. Check
* pa_pstream_register_memfd_mempool() for more information. */
bool use_shm, use_memfd;
+ bool non_registered_memfd_id_error_logged;
pa_idxset *registered_memfd_ids;
pa_memimport *import;
@@ -677,9 +678,11 @@ static void prepare_next_write_item(pa_pstream *p) {
flags |= PA_FLAG_SHMDATA_MEMFD_BLOCK;
send_payload = false;
} else {
- if (pa_log_ratelimit(PA_LOG_ERROR)) {
+ if (!p->non_registered_memfd_id_error_logged) {
pa_log("Cannot send block reference with non-registered memfd ID = %u", shm_id);
- pa_log("Fallig back to copying full block data over socket");
+ pa_log("Falling back to copying full block data over socket");
+ pa_log("There's a bug report about this: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/824");
+ p->non_registered_memfd_id_error_logged = true;
}
}
}
From 6b04e2a89e2a0a8b4f6aecb38fd00d82914f7050 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Sat, 14 Mar 2020 10:42:44 +0200
Subject: [PATCH 050/865] sink: Reduce chat sink priority
Some gaming sound cards have custom profiles with analog-game and
analog-chat mappings that exist simultaneously. The game sink should
have higher priority than the chat sink, but currently there's no way to
affect the sink priorities from the profile-set configuration (the
mapping priority is not propagated to the sink priority).
I first thought about adding the mapping priority to the sink priority,
but that could mess up the prioritization system in
pa_device_init_priority(). I ended up checking for the intended roles
property to reduce the chat sink priority. I also reduced the iec958
priority so that the chat and iec958 sinks don't end up with the same
priority.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/818
Part-of:
---
src/pulsecore/sink.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index e89b59655..8bb67f6d4 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -3609,10 +3609,18 @@ unsigned pa_device_init_priority(pa_proplist *p) {
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
- if (pa_startswith(s, "analog-"))
+ if (pa_startswith(s, "analog-")) {
priority += 9;
+
+ /* If an analog device has an intended role of "phone", it probably
+ * co-exists with another device that is meant for everything else,
+ * and that other device should have higher priority than the phone
+ * device. */
+ if (pa_str_in_list_spaces(pa_proplist_gets(p, PA_PROP_DEVICE_INTENDED_ROLES), "phone"))
+ priority -= 1;
+ }
else if (pa_startswith(s, "iec958-"))
- priority += 8;
+ priority += 7;
}
return priority;
From 836ba89c4a3cd15efa33f9e70ed8e4764bcb3793 Mon Sep 17 00:00:00 2001
From: Mattias Jernberg
Date: Thu, 26 Mar 2020 19:53:08 +0100
Subject: [PATCH 051/865] alsa-mixer: Enable volume control of SteelSeries
Arctis stereo output
Since there is now support for specifying the index of an Element, add the
same config as is used for the output-mono variant, as they behave the same:
One volume control with no support for adjustments to the left and right
channels.
Part-of:
---
.../mixer/paths/usb-gaming-headset-output-stereo.conf | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
index e3f91cd6c..1a1e7944e 100644
--- a/src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
+++ b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
@@ -23,10 +23,12 @@
; Steelseries Arctis 7
; Steelseries Arctis Pro Wireless.
; Lucidsound LS31
-;
-; This path doesn't provide hardware volume control, because the stereo
-; output is controlled by the PCM element with index 1, and currently
-; PulseAudio only supports elements with index 0.
[General]
description-key = analog-output-headphones
+
+[Element PCM,1]
+volume = merge
+switch = mute
+override-map.1 = all
+override-map.2 = all-left,all-right
From 19e34d8d5bb9380ed70607b3f661c26df6d4836c Mon Sep 17 00:00:00 2001
From: Hui Wang
Date: Thu, 10 Dec 2020 15:36:40 +0800
Subject: [PATCH 052/865] alsa-mixer: disable the Auto-Mute once the system has
speaker
With the Auto-Mute enabled, if the headphone jack is plugged, the
alsa hda driver will mute the speaker and set pinctl of the speaker
to Hi-Z state, after this happens, even the pulseaudio unmute the
speaker, the speaker still couldn't output sound because the pinctl
is in Hi-Z state.
We found this issue on a Dell machine which has multi-function audio
jack, after the headphone is plugged in, the speaker's availability is
still unknown, users could select speaker from gnome-sound-setting,
but even the speaker is selected to be the active device, it couldn't
output sound.
The Auto-Mute is not useful if the pulseaudio is running since pa
could mute/unmute devices according to active port change, the ucm
for sof+hda already disabled the Auto-Mute, let us disable it for
hda audio if the machine has the internal speaker.
Signed-off-by: Hui Wang
Part-of:
---
src/modules/alsa/mixer/paths/analog-output-speaker.conf | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker.conf b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
index 1610b9a05..91a4bc163 100644
--- a/src/modules/alsa/mixer/paths/analog-output-speaker.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
@@ -88,6 +88,13 @@ override-map.2 = all-left,all-right
switch = off
volume = off
+; Make sure the internal speakers are not auto-muted once the system has speakers
+[Element Auto-Mute Mode]
+enumeration = select
+
+[Option Auto-Mute Mode:Disabled]
+name = analog-output-speaker
+
; This profile path is intended to control the speaker, let's mute headphones
; else there will be a spike when plugging in headphones
[Element Headphone]
From d15b31d751d90274fee303a87a4c8493f1a118eb Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Sun, 13 Dec 2020 13:09:19 +0300
Subject: [PATCH 053/865] module-alsa-card: handle udev PULSE_MODARGS
Allow adding module arguments using udev PULSE_MODARGS environment variable and
fail module loading if there is a problem with PULSE_MODARGS
This helps setting e.g. 'tsched=0' for specific devices without a need to create
full load module entry in default.pa.
Part-of:
---
src/modules/alsa/module-alsa-card.c | 44 +++++++++++++++++++++++++++++
src/pulsecore/modargs.c | 26 +++++++++++++++++
src/pulsecore/modargs.h | 7 +++++
3 files changed, 77 insertions(+)
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index de2fe9cc4..f2f50382a 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -104,6 +104,8 @@ static const char* const valid_modargs[] = {
#define DEFAULT_DEVICE_ID "0"
+#define PULSE_MODARGS "PULSE_MODARGS"
+
struct userdata {
pa_core *core;
pa_module *module;
@@ -820,6 +822,7 @@ int pa__init(pa_module *m) {
const char *description;
const char *profile_str = NULL;
char *fn = NULL;
+ char *udev_args = NULL;
bool namereg_fail = false;
int err = -PA_MODULE_ERR_UNSPECIFIED, rval;
@@ -849,6 +852,47 @@ int pa__init(pa_module *m) {
goto fail;
}
+#ifdef HAVE_UDEV
+ udev_args = pa_udev_get_property(u->alsa_card_index, PULSE_MODARGS);
+#endif
+
+ if (udev_args) {
+ bool udev_modargs_success = true;
+ pa_modargs *temp_ma = pa_modargs_new(udev_args, valid_modargs);
+
+ if (temp_ma) {
+ /* do not try to replace device_id */
+
+ if (pa_modargs_remove_key(temp_ma, "device_id") == 0) {
+ pa_log_warn("Unexpected 'device_id' module argument override ignored from udev " PULSE_MODARGS "='%s'", udev_args);
+ }
+
+ /* Implement modargs override by copying original module arguments
+ * over udev entry arguments ignoring duplicates. */
+
+ if (pa_modargs_merge_missing(temp_ma, u->modargs, valid_modargs) == 0) {
+ /* swap module arguments */
+ pa_modargs *old_ma = u->modargs;
+ u->modargs = temp_ma;
+ temp_ma = old_ma;
+
+ pa_log_info("Applied module arguments override from udev " PULSE_MODARGS "='%s'", udev_args);
+ } else {
+ pa_log("Failed to apply module arguments override from udev " PULSE_MODARGS "='%s'", udev_args);
+ udev_modargs_success = false;
+ }
+
+ pa_modargs_free(temp_ma);
+ } else {
+ pa_log("Failed to parse module arguments from udev " PULSE_MODARGS "='%s'", udev_args);
+ udev_modargs_success = false;
+ }
+ pa_xfree(udev_args);
+
+ if (!udev_modargs_success)
+ goto fail;
+ }
+
if (pa_modargs_get_value_boolean(u->modargs, "ignore_dB", &ignore_dB) < 0) {
pa_log("Failed to parse ignore_dB argument.");
goto fail;
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index bce5891db..a707f6c8d 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -272,6 +272,15 @@ int pa_modargs_append(pa_modargs *ma, const char *args, const char* const* valid
return parse(ma, args, valid_keys, true);
}
+int pa_modargs_remove_key(pa_modargs *ma, const char *key) {
+ if (pa_hashmap_remove_and_free(ma->unescaped, key) == 0) {
+ pa_hashmap_remove_and_free(ma->raw, key);
+ return 0;
+ }
+
+ return -1;
+}
+
void pa_modargs_free(pa_modargs*ma) {
pa_assert(ma);
@@ -544,3 +553,20 @@ const char *pa_modargs_iterate(pa_modargs *ma, void **state) {
return e->key;
}
+
+int pa_modargs_merge_missing(pa_modargs *dst, pa_modargs *src, const char* const valid_keys[]) {
+ void *state;
+ const char *key, *value;
+ int ret = 0;
+
+ for (state = NULL, key = pa_modargs_iterate(src, &state); key; key = pa_modargs_iterate(src, &state)) {
+ value = pa_modargs_get_value(src, key, NULL);
+ if (value && add_key_value(dst, pa_xstrdup(key), pa_xstrdup(value), valid_keys, true) < 0) {
+ pa_log_warn("Failed to add module argument '%s=%s'", key, value);
+ ret = -1;
+ /* continue to gather all errors */
+ }
+ }
+
+ return ret;
+}
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index 96132a3aa..abb16747d 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -95,4 +95,11 @@ int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa
* have any particular order. */
const char *pa_modargs_iterate(pa_modargs *ma, void **state);
+/* Remove entry by key. Returns 0 if successful, -1 otherwise */
+int pa_modargs_remove_key(pa_modargs *ma, const char *key);
+
+/* Add all key/value pairs from src that are is not already present in dst, to dst.
+ * Returns 0 if there were no errors, -1 otherwise. */
+int pa_modargs_merge_missing(pa_modargs *dst, pa_modargs *src, const char* const valid_keys[]);
+
#endif
From cb91d7a12e6be0e2c720d47d2ceca322a7b21d2c Mon Sep 17 00:00:00 2001
From: Samuel Thibault
Date: Sun, 26 Apr 2020 19:55:08 +0200
Subject: [PATCH 054/865] alsa-sink: increase watermark when there is nothing
to rewind
If we do not manage to rewind at all because there is nothing to rewind
any more, it means the latency is too small, and we let audio escape our
control. We should thus increase the watermark to fix this.
Fixes #871
Part-of:
---
src/modules/alsa/alsa-sink.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index f7fef8a7e..bd8377ced 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1877,8 +1877,11 @@ static int process_rewind(struct userdata *u) {
u->after_rewind = true;
return 0;
}
- } else
+ } else {
pa_log_debug("Mhmm, actually there is nothing to rewind.");
+ if (u->use_tsched)
+ increase_watermark(u);
+ }
rewind_done:
pa_sink_process_rewind(u->sink, 0);
From 9b0ae8327d990584bb9a966d8d7bee6badbdb8c0 Mon Sep 17 00:00:00 2001
From: SimonP
Date: Thu, 7 May 2020 16:13:27 +0100
Subject: [PATCH 055/865] alsa-mixer: Respect XDG base directory spec when
loading path configs
Try $XDG_DATA_HOME, then $XDG_DATA_DIRS, and finally fall back to old
behaviour (prefix-defined directory).
core-util: Ignore non-absolute XDG base dirs
These are invalid per the spec.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/862
Part-of:
---
src/modules/alsa/alsa-mixer.c | 68 ++++++++++++++++++++++++++++----
src/pulsecore/core-util.c | 64 ++++++++++++++++++++++++++++++
src/pulsecore/core-util.h | 3 ++
src/tests/alsa-mixer-path-test.c | 1 -
4 files changed, 127 insertions(+), 9 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index f978f71c3..c40629712 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -2763,13 +2763,66 @@ static int path_verify(pa_alsa_path *p) {
return 0;
}
-static const char *get_default_paths_dir(void) {
+static char *get_path_config_path(const char *paths_dir, const char *fname) {
+ char *path_config_path;
+ char *dir;
+ char *data_home;
+ pa_dynarray *data_dirs;
+
+ if (paths_dir) {
+ path_config_path = pa_maybe_prefix_path(fname, paths_dir);
+ if (access(path_config_path, R_OK) == 0)
+ return path_config_path;
+ else
+ pa_xfree(path_config_path);
+ }
+
#ifdef HAVE_RUNNING_FROM_BUILD_TREE
- if (pa_run_from_build_tree())
- return PA_SRCDIR "/modules/alsa/mixer/paths/";
- else
+ if (pa_run_from_build_tree()) {
+ path_config_path = pa_maybe_prefix_path(fname, PA_SRCDIR "/modules/alsa/mixer/paths/");
+ if (access(path_config_path, R_OK) == 0)
+ return path_config_path;
+ else
+ pa_xfree(path_config_path);
+ }
#endif
- return PA_ALSA_PATHS_DIR;
+
+ if (pa_get_data_home_dir(&data_home) == 0) {
+ dir = pa_sprintf_malloc("%s" PA_PATH_SEP "alsa-mixer" PA_PATH_SEP "paths", data_home);
+ pa_xfree(data_home);
+
+ path_config_path = pa_maybe_prefix_path(fname, dir);
+ pa_xfree(dir);
+
+ if (access(path_config_path, R_OK) == 0)
+ return path_config_path;
+ else
+ pa_xfree(path_config_path);
+ }
+
+ if (pa_get_data_dirs(&data_dirs) == 0) {
+ int idx;
+ const char *n;
+
+ PA_DYNARRAY_FOREACH(n, data_dirs, idx) {
+ dir = pa_sprintf_malloc("%s" PA_PATH_SEP "alsa-mixer" PA_PATH_SEP "paths", n);
+ path_config_path = pa_maybe_prefix_path(fname, dir);
+ pa_xfree(dir);
+
+ if (access(path_config_path, R_OK) == 0) {
+ pa_dynarray_free(data_dirs);
+ return path_config_path;
+ }
+ else {
+ pa_xfree(path_config_path);
+ }
+ }
+
+ pa_dynarray_free(data_dirs);
+ }
+
+ path_config_path = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR);
+ return path_config_path;
}
pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
@@ -2827,10 +2880,9 @@ pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa
items[2].data = &p->description;
items[3].data = &mute_during_activation;
- if (!paths_dir)
- paths_dir = get_default_paths_dir();
+ fn = get_path_config_path(paths_dir, fname);
- fn = pa_maybe_prefix_path(fname, paths_dir);
+ pa_log_info("Loading path config: %s", fn);
r = pa_config_parse(fn, NULL, items, p->proplist, false, p);
pa_xfree(fn);
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 25498c5a0..0da5c093e 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -1568,6 +1568,70 @@ int pa_get_config_home_dir(char **_r) {
return 0;
}
+int pa_get_data_home_dir(char **_r) {
+ const char *e;
+ char *home_dir;
+
+ pa_assert(_r);
+
+ e = getenv("XDG_DATA_HOME");
+ if (e && *e) {
+ if (pa_is_path_absolute(e)) {
+ *_r = pa_sprintf_malloc("%s" PA_PATH_SEP "pulseaudio", e);
+ return 0;
+ }
+ else
+ pa_log_warn("Ignored non-absolute XDG_DATA_HOME value '%s'", e);
+ }
+
+ home_dir = pa_get_home_dir_malloc();
+ if (!home_dir)
+ return -PA_ERR_NOENTITY;
+
+ *_r = pa_sprintf_malloc("%s" PA_PATH_SEP ".local" PA_PATH_SEP "share" PA_PATH_SEP "pulseaudio", home_dir);
+ pa_xfree(home_dir);
+ return 0;
+}
+
+int pa_get_data_dirs(pa_dynarray **_r) {
+ const char *e;
+ const char *def = "/usr/local/share/:/usr/share/";
+ const char *p;
+ const char *split_state = NULL;
+ char *n;
+ pa_dynarray *paths;
+
+ pa_assert(_r);
+
+ e = getenv("XDG_DATA_DIRS");
+ p = e && *e ? e : def;
+
+ paths = pa_dynarray_new((pa_free_cb_t) pa_xfree);
+
+ while ((n = pa_split(p, ":", &split_state))) {
+ char *path;
+
+ if (!pa_is_path_absolute(n)) {
+ pa_log_warn("Ignored non-absolute path '%s' in XDG_DATA_DIRS", n);
+ pa_xfree(n);
+ continue;
+ }
+
+ path = pa_sprintf_malloc("%s" PA_PATH_SEP "pulseaudio", n);
+ pa_xfree(n);
+ pa_dynarray_append(paths, path);
+ }
+
+ if (pa_dynarray_size(paths) == 0) {
+ pa_log_warn("XDG_DATA_DIRS contains no valid paths");
+ pa_dynarray_free(paths);
+ return -PA_ERR_INVALID;
+ }
+
+ *_r = paths;
+ return 0;
+}
+
int pa_append_to_config_home_dir(const char *path, char **_r) {
int r;
char *config_home_dir;
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 3117df882..ed123c796 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -37,6 +37,7 @@
#include
#include
#include
+#include
#ifndef PACKAGE
#error "Please include config.h before including this file!"
@@ -142,6 +143,8 @@ char *pa_get_state_dir(void);
char *pa_get_home_dir_malloc(void);
int pa_append_to_home_dir(const char *path, char **_r);
int pa_get_config_home_dir(char **_r);
+int pa_get_data_home_dir(char **_r);
+int pa_get_data_dirs(pa_dynarray **_r);
int pa_append_to_config_home_dir(const char *path, char **_r);
char *pa_get_binary_name_malloc(void);
char *pa_runtime_path(const char *fn);
diff --git a/src/tests/alsa-mixer-path-test.c b/src/tests/alsa-mixer-path-test.c
index 75cf08613..91e4d0dfa 100644
--- a/src/tests/alsa-mixer-path-test.c
+++ b/src/tests/alsa-mixer-path-test.c
@@ -17,7 +17,6 @@
* Meson. */
#ifndef MESON_BUILD
-/* This function was copied from alsa-mixer.c */
static const char *get_default_paths_dir(void) {
if (pa_run_from_build_tree())
return PA_SRCDIR "/modules/alsa/mixer/paths/";
From e3f2c25face8f964827f75dbc8fd766fc02aaf6d Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Sat, 6 Jun 2020 17:00:01 +0300
Subject: [PATCH 056/865] alsa-mixer: Pick at most one fallback mapping
The old behaviour was such that if none of the normal mappings worked,
we would probe ALL fallbacks. I don't think that makes sense, and it
caused concrete issues: let's say we have a regular stereo mic device,
but there's no "front" PCM defined for it. In this situation we would
probe the stereo-fallback mapping (which uses "hw" instead of "front"),
and it would work, but then we'd also probe the "multichannel-input"
mapping, which would also work, so we end up with two mappings that
don't have any difference in behaviour.
I think it's better to simply pick the first working fallback and ignore
the rest.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/901
(issue is marked as confidential due to unreleased hardware)
Part-of:
---
src/modules/alsa/alsa-mixer.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index c40629712..8b9ccfbf6 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -5155,14 +5155,14 @@ void pa_alsa_profile_set_probe(
if (p->output_mappings)
PA_IDXSET_FOREACH(m, p->output_mappings, idx)
if (m->output_pcm) {
- found_output |= !p->fallback_output;
+ found_output = true;
mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths, mixers);
}
if (p->input_mappings)
PA_IDXSET_FOREACH(m, p->input_mappings, idx)
if (m->input_pcm) {
- found_input |= !p->fallback_input;
+ found_input = true;
mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths, mixers);
}
}
From d285b00a168e830c1e837513b7442a48c69d9937 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Mon, 14 Dec 2020 08:48:11 +0300
Subject: [PATCH 057/865] build-sys: meson: change oss-output boolean to Meson
feature
Part-of:
---
meson.build | 7 +++----
meson_options.txt | 2 +-
src/modules/meson.build | 2 +-
src/utils/meson.build | 2 +-
4 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/meson.build b/meson.build
index 3adb876ff..437fc802c 100644
--- a/meson.build
+++ b/meson.build
@@ -616,8 +616,7 @@ if x11_dep.found()
endif
# Module dependencies
-if get_option('oss-output')
- assert(cc.has_header('sys/soundcard.h'), 'Need header file for OSS support')
+if cc.has_header('sys/soundcard.h', required: get_option('oss-output'))
cdata.set('HAVE_OSS_OUTPUT', 1)
cdata.set('HAVE_OSS_WRAPPER', 1)
cdata.set('PULSEDSP_LOCATION', pulsedsp_location)
@@ -811,8 +810,8 @@ summary = [
'',
'Enable memfd shared memory: @0@'.format(cdata.has('HAVE_MEMFD')),
'Enable X11: @0@'.format(x11_dep.found()),
- 'Enable OSS Output: @0@'.format(get_option('oss-output')),
-# 'Enable OSS Wrapper: @0@'.format(${ENABLE_OSS_WRAPPER}),
+ 'Enable OSS Output: @0@'.format(cdata.has('HAVE_OSS_OUTPUT')),
+ 'Enable OSS Wrapper: @0@'.format(cdata.has('HAVE_OSS_WRAPPER')),
# 'Enable EsounD: @0@'.format(${ENABLE_ESOUND}),
'Enable Alsa: @0@'.format(alsa_dep.found()),
# 'Enable CoreAudio: @0@'.format(${ENABLE_COREAUDIO}),
diff --git a/meson_options.txt b/meson_options.txt
index 247f26a78..ccfa2f7eb 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -124,7 +124,7 @@ option('orc',
type : 'feature', value : 'auto',
description : 'Optimized Inner Loop Runtime Compiler')
option('oss-output',
- type : 'boolean',
+ type : 'feature', value : 'auto',
description : 'Optional OSS output support')
option('samplerate',
type : 'feature', value : 'disabled',
diff --git a/src/modules/meson.build b/src/modules/meson.build
index 9df6d0e61..a4da633bb 100644
--- a/src/modules/meson.build
+++ b/src/modules/meson.build
@@ -73,7 +73,7 @@ if cc.has_header('linux/input.h')
]
endif
-if get_option('oss-output')
+if cdata.has('HAVE_OSS_OUTPUT')
subdir('oss')
all_modules += [
[ 'module-oss', 'oss/module-oss.c', [], [], [], liboss_util ],
diff --git a/src/utils/meson.build b/src/utils/meson.build
index 934abb850..a0701987b 100644
--- a/src/utils/meson.build
+++ b/src/utils/meson.build
@@ -84,7 +84,7 @@ if x11_dep.found()
)
endif
-if get_option('oss-output')
+if cdata.has('HAVE_OSS_WRAPPER')
libpulsecommon_sources = [
'padsp.c',
]
From c0779b28b84640736807cd562553d9f517ea0a72 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Thu, 17 Sep 2020 17:36:37 +0300
Subject: [PATCH 058/865] sink: Increase JACK device priority
The JACK sink and source priority was 0, but I believe the common case
is that when a JACK sink or source is loaded, the user wants to make it
the default device.
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/983
Part-of:
---
src/pulsecore/sink.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 8bb67f6d4..e8aeb17fa 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -3577,6 +3577,14 @@ unsigned pa_device_init_priority(pa_proplist *p) {
pa_assert(p);
+ /* JACK sinks and sources get very high priority so that we'll switch the
+ * default devices automatically when jackd starts and
+ * module-jackdbus-detect creates the jack sink and source. */
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_API))) {
+ if (pa_streq(s, "jack"))
+ priority += 10000;
+ }
+
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) {
if (pa_streq(s, "sound"))
From 4552fe15b3578219d7e21cb77018a5f9425cd593 Mon Sep 17 00:00:00 2001
From: Sebastian Krzyszkowiak
Date: Thu, 29 Oct 2020 02:11:22 +0100
Subject: [PATCH 059/865] filter-apply: Look for filter parameters also in
device properties
Some filters take parameters that effectively describe the hardware
they're being applied to (like echo-cancel allowing to specify the
mic array parameters for better noise filtering). This allows system
integrators to set default parameters for such modules per-device,
which will get used when the stream doesn't specify their own.
Part-of:
---
src/modules/module-filter-apply.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c
index c9f3f3932..1c1278218 100644
--- a/src/modules/module-filter-apply.c
+++ b/src/modules/module-filter-apply.c
@@ -146,15 +146,20 @@ static const char* get_filter_name(pa_object *o, bool is_sink_input) {
static const char* get_filter_parameters(pa_object *o, const char *want, bool is_sink_input) {
const char *parameters;
char *prop_parameters;
- pa_proplist *pl;
+ pa_proplist *pl, *device_pl;
- if (is_sink_input)
+ if (is_sink_input) {
pl = PA_SINK_INPUT(o)->proplist;
- else
+ device_pl = PA_SINK_INPUT(o)->sink->proplist;
+ } else {
pl = PA_SOURCE_OUTPUT(o)->proplist;
+ device_pl = PA_SOURCE_OUTPUT(o)->source->proplist;
+ }
prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, want);
parameters = pa_proplist_gets(pl, prop_parameters);
+ if (!parameters)
+ parameters = pa_proplist_gets(device_pl, prop_parameters);
pa_xfree(prop_parameters);
return parameters;
From 9c774c62950928b62cf60c697909842708393378 Mon Sep 17 00:00:00 2001
From: Edward Lee
Date: Thu, 1 Oct 2020 09:39:54 -0400
Subject: [PATCH 060/865] win32: Handle (WSA)EWOULDBLOCK as EAGAIN.
Part-of:
---
src/pulsecore/core-util.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 0da5c093e..1c9746dbf 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -407,6 +407,8 @@ ssize_t pa_read(int fd, void *buf, size_t count, int *type) {
if (WSAGetLastError() != WSAENOTSOCK) {
errno = WSAGetLastError();
+ if (errno == WSAEWOULDBLOCK)
+ errno = EAGAIN;
return r;
}
@@ -448,6 +450,8 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
#ifdef OS_IS_WIN32
if (WSAGetLastError() != WSAENOTSOCK) {
errno = WSAGetLastError();
+ if (errno == WSAEWOULDBLOCK)
+ errno = EAGAIN;
return r;
}
#else
From 68cb06b5fd8f7201153c47dc566bd2e33f2cf484 Mon Sep 17 00:00:00 2001
From: Edward Lee
Date: Thu, 1 Oct 2020 09:56:45 -0400
Subject: [PATCH 061/865] win32: Fix (interim) esound paths in
module-protocol-stub.c
Part-of:
---
src/modules/module-protocol-stub.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
index a228208dc..5dd3fe079 100644
--- a/src/modules/module-protocol-stub.c
+++ b/src/modules/module-protocol-stub.c
@@ -300,7 +300,9 @@ int pa__init(pa_module*m) {
# if defined(USE_PROTOCOL_ESOUND)
-# if defined(USE_PER_USER_ESOUND_SOCKET)
+ /* Windows doesn't support getuid(), so we ignore the per-user Esound socket compile flag.
+ * Moreover, Esound Unix sockets haven't been supported on Windows historically. */
+# if defined(USE_PER_USER_ESOUND_SOCKET) && !defined(OS_IS_WIN32)
u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
# else
u->socket_path = pa_xstrdup("/tmp/.esd/socket");
From 4e5786ffdd5eb8eaf3934d5f8b372151787e4a6c Mon Sep 17 00:00:00 2001
From: Edward Lee
Date: Thu, 1 Oct 2020 10:10:19 -0400
Subject: [PATCH 062/865] win32: Check WSAGetLastError() in
pa_is_unix_socket_stale.
Part-of:
---
src/pulsecore/socket-util.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index e389ef203..83d4c9c6c 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -239,8 +239,13 @@ int pa_unix_socket_is_stale(const char *fn) {
sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
+#if !defined(OS_IS_WIN32)
if (errno == ECONNREFUSED)
ret = 1;
+#else
+ if (WSAGetLastError() == WSAECONNREFUSED)
+ ret = 1;
+#endif
} else
ret = 0;
From 4b3cc2df8d4b099d8804d76b18c2998701e3e3e4 Mon Sep 17 00:00:00 2001
From: Edward Lee
Date: Thu, 1 Oct 2020 10:11:09 -0400
Subject: [PATCH 063/865] win32: (Temporarily) Add sockaddr_un definition from
This is a temporary addition, until ships with the
MinGW toolchain.
Part-of:
---
src/pulsecore/socket.h | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/src/pulsecore/socket.h b/src/pulsecore/socket.h
index 72f222818..982901b0e 100644
--- a/src/pulsecore/socket.h
+++ b/src/pulsecore/socket.h
@@ -11,6 +11,31 @@
typedef long suseconds_t;
+/** Windows 10 supports AF_UNIX as of build 17603, with
+ support provided in the header file . However,
+ only the latest Windows SDK provides this file; older SDKs and
+ MinGW do not.
+
+ Hence we define SOCKADDR_UN here. We do not expect this definition to change
+ as Windows has some pretty good binary backwards-compatibility guarantees.
+
+ This shouldn't pose a problem for older versions of Windows; we expect them to
+ fail with an error whenever we try to make a socket of type AF_UNIX. */
+#define UNIX_PATH_MAX 108
+
+typedef struct sockaddr_un
+{
+ ADDRESS_FAMILY sun_family; /* AF_UNIX */
+ char sun_path[UNIX_PATH_MAX]; /* pathname */
+} SOCKADDR_UN, *PSOCKADDR_UN;
+
+#ifndef SUN_LEN
+#define SUN_LEN(ptr) \
+ ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
+#endif
+
+#define HAVE_SYS_UN_H
+
#endif
#ifdef HAVE_WS2TCPIP_H
From 3c51930b6165c2cd48e8f04e01c267f34f1942f2 Mon Sep 17 00:00:00 2001
From: Edward Lee
Date: Thu, 1 Oct 2020 10:14:08 -0400
Subject: [PATCH 064/865] win32: Add support for Unix sockets in autoconf
build.
Part-of:
---
configure.ac | 3 +++
src/Makefile.am | 3 +++
2 files changed, 6 insertions(+)
diff --git a/configure.ac b/configure.ac
index fb1b518e9..357b6d7f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -434,6 +434,9 @@ AC_CHECK_HEADERS([netinet/ip.h], [], [],
AC_CHECK_HEADERS([sys/resource.h], [HAVE_SYS_RESOURCE_H=1], [HAVE_SYS_RESOURCE_H=0])
AC_SUBST(HAVE_SYS_RESOURCE_H)
AC_CHECK_HEADERS([sys/un.h], [HAVE_AF_UNIX=1], [HAVE_AF_UNIX=0])
+# In principle we would test for , but that doesn't ship (yet) with the
+# MinGW headers.
+AC_CHECK_HEADERS([winsock2.h], [HAVE_AF_UNIX=1], [])
AM_CONDITIONAL(HAVE_AF_UNIX, test "x$HAVE_AF_UNIX" = "x1")
AC_SUBST(HAVE_AF_UNIX)
diff --git a/src/Makefile.am b/src/Makefile.am
index eeec40b55..97c9dce9f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -206,8 +206,11 @@ bin_PROGRAMS += pasuspender
endif
if HAVE_AF_UNIX
+if !OS_IS_WIN32
+# pacmd relies on Unix signals, which are not present on Windows.
bin_PROGRAMS += pacmd
endif
+endif
if HAVE_X11
bin_PROGRAMS += pax11publish
From 7bc559fe26633c0d97557ca192bceea084d3a540 Mon Sep 17 00:00:00 2001
From: Edward Lee
Date: Thu, 1 Oct 2020 10:22:20 -0400
Subject: [PATCH 065/865] win32: Change linkage of lt_preloaded_symbols to
match definition in libtool in daemon/main.c.
Part-of:
---
src/daemon/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/daemon/main.c b/src/daemon/main.c
index c7c434288..1032390ca 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -101,7 +101,7 @@
#ifdef DISABLE_LIBTOOL_PRELOAD
/* FIXME: work around a libtool bug by making sure we have 2 elements. Bug has
* been reported: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=29576 */
-const lt_dlsymlist lt_preloaded_symbols[] = {
+LT_DLSYM_CONST lt_dlsymlist lt_preloaded_symbols[] = {
{ "@PROGRAM@", NULL },
{ NULL, NULL }
};
From b892e327a960e46ac4a377a95d274a5dc75cdf99 Mon Sep 17 00:00:00 2001
From: Edward Lee
Date: Thu, 1 Oct 2020 10:24:40 -0400
Subject: [PATCH 066/865] win32: Fix meson build system for Windows.
Part-of:
---
meson.build | 47 +++++++++++++++++++++++++---
src/daemon/meson.build | 2 +-
src/meson.build | 18 +++++++++--
src/modules/meson.build | 35 +++++++++++++++------
src/pulse/meson.build | 2 +-
src/pulsecore/meson.build | 14 +++++----
src/utils/meson.build | 64 +++++++++++++++++++++------------------
7 files changed, 127 insertions(+), 55 deletions(-)
diff --git a/meson.build b/meson.build
index 437fc802c..cd617c602 100644
--- a/meson.build
+++ b/meson.build
@@ -158,6 +158,10 @@ cdata.set('top_srcdir', meson.source_root())
# First some defaults to keep config file generation happy
cdata.set('HAVE_COREAUDIO', 0)
cdata.set('HAVE_WAVEOUT', 0)
+
+platform_socket_dep = []
+platform_dep = []
+
# FIXME: This was not tested. Maybe some flags should better be CFLAGS,
# rather than ending up in the config.h file?
if host_machine.system() == 'darwin'
@@ -165,7 +169,20 @@ if host_machine.system() == 'darwin'
cdata.set('_DARWIN_C_SOURCE', '200112L') # Needed to get NSIG on Mac OS
elif host_machine.system() == 'windows'
cdata.set('OS_IS_WIN32', 1)
+ cdata.set('HAVE_WINDOWS_H', 1)
+ cdata.set('HAVE_WAVEOUT', 1)
+ cdata.set('HAVE_WINSOCK2_H', 1)
+ cdata.set('HAVE_WS2TCPIP_H', 1)
cdata.set('WIN32_LEAN_AND_MEAN', 1) # Needed to avoid including unnecessary headers on Windows
+ cdata.set('gid_t', 'int')
+ cdata.set('uid_t', 'int')
+ ws2_32_dep = meson.get_compiler('c').find_library('ws2_32')
+ winsock_dep = meson.get_compiler('c').find_library('wsock32')
+ ole32_dep = meson.get_compiler('c').find_library('ole32')
+ ssp_dep = meson.get_compiler('c').find_library('ssp')
+ pcreposix_dep = meson.get_compiler('c').find_library('pcreposix')
+ platform_socket_dep = [ws2_32_dep, winsock_dep]
+ platform_dep = [ole32_dep, ssp_dep, pcreposix_dep]
#elif host_machine.system() == 'solaris'
# # Apparently meson has no solaris support?
# # Needed to get declarations for msg_control and msg_controllen on Solaris
@@ -236,8 +253,10 @@ if cc.has_header('valgrind/memcheck.h', required: get_option('valgrind'))
endif
# FIXME: move this to the above set
-if cc.has_header('pthread.h')
- cdata.set('HAVE_PTHREAD', 1)
+if host_machine.system() != 'windows'
+ if cc.has_header('pthread.h')
+ cdata.set('HAVE_PTHREAD', 1)
+ endif
endif
if cc.has_header_symbol('pthread.h', 'PTHREAD_PRIO_INHERIT')
@@ -297,7 +316,16 @@ check_functions = [
foreach f : check_functions
if cc.has_function(f)
define = 'HAVE_' + f.underscorify().to_upper()
- cdata.set(define, 1)
+
+ if f == 'posix_memalign' and host_machine.system() == 'windows'
+ message('Win32/mingw32 does not properly define posix_memalign.')
+ elif f == 'fork' and host_machine.system() == 'windows'
+ # __builtin_fork is defined and compiles properly, but calling __builtin_fork() does not.
+ # This causes Meson to think that Windows has a fork() which causes a link error...
+ message('Win32/mingw32 does not properly define fork.')
+ else
+ cdata.set(define, 1)
+ endif
endif
endforeach
@@ -306,7 +334,11 @@ if cc.has_header_symbol('sys/syscall.h', 'SYS_memfd_create')
endif
if cc.has_function('dgettext')
- libintl_dep = []
+ if host_machine.system() != 'windows'
+ libintl_dep = []
+ else
+ libintl_dep = cc.find_library('intl')
+ endif
else
libintl_dep = cc.find_library('intl')
endif
@@ -357,7 +389,12 @@ cdata.set('MESON_BUILD', 1)
# On ELF systems we don't want the libraries to be unloaded since we don't clean them up properly,
# so we request the nodelete flag to be enabled.
# On other systems, we don't really know how to do that, but it's welcome if somebody can tell.
-nodelete_link_args = ['-Wl,-z,nodelete']
+# Windows doesn't support this flag.
+if host_machine.system() != 'windows'
+ nodelete_link_args = ['-Wl,-z,nodelete']
+else
+ nodelete_link_args = []
+endif
# Code coverage
diff --git a/src/daemon/meson.build b/src/daemon/meson.build
index 9c9f807e7..4a57d7baa 100644
--- a/src/daemon/meson.build
+++ b/src/daemon/meson.build
@@ -31,7 +31,7 @@ executable('pulseaudio',
include_directories : [configinc, topinc],
link_args : ['-ffast-math'],
link_with : [libpulsecore, libpulsecommon, libpulse],
- dependencies : [ltdl_dep, cap_dep, dbus_dep, libsystemd_dep, dl_dep, libintl_dep],
+ dependencies : [ltdl_dep, cap_dep, dbus_dep, libsystemd_dep, dl_dep, libintl_dep, platform_dep, platform_socket_dep],
c_args : pa_c_args,
)
diff --git a/src/meson.build b/src/meson.build
index b84112eee..0842db297 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -39,7 +39,6 @@ libpulsecommon_sources = [
'pulsecore/memblock.c',
'pulsecore/memblockq.c',
'pulsecore/memchunk.c',
- 'pulsecore/mutex-posix.c',
'pulsecore/native-common.c',
'pulsecore/once.c',
'pulsecore/packet.c',
@@ -56,7 +55,6 @@ libpulsecommon_sources = [
'pulsecore/random.c',
'pulsecore/srbchannel.c',
'pulsecore/sample-util.c',
- 'pulsecore/semaphore-posix.c',
'pulsecore/shm.c',
'pulsecore/bitset.c',
'pulsecore/socket-client.c',
@@ -65,7 +63,6 @@ libpulsecommon_sources = [
'pulsecore/strbuf.c',
'pulsecore/strlist.c',
'pulsecore/tagstruct.c',
- 'pulsecore/thread-posix.c',
'pulsecore/time-smoother.c',
'pulsecore/tokenizer.c',
'pulsecore/usergroup.c',
@@ -177,6 +174,20 @@ if x11_dep.found()
endif
# FIXME: Do non-POSIX thread things
+if host_machine.system() == 'windows'
+ libpulsecommon_sources += [
+ 'pulsecore/mutex-win32.c',
+ 'pulsecore/poll-win32.c',
+ 'pulsecore/semaphore-win32.c',
+ 'pulsecore/thread-win32.c',
+ ]
+else
+ libpulsecommon_sources += [
+ 'pulsecore/mutex-posix.c',
+ 'pulsecore/semaphore-posix.c',
+ 'pulsecore/thread-posix.c'
+ ]
+endif
# FIXME: Do SIMD things
libpulsecommon = shared_library('pulsecommon-' + pa_version_major_minor,
@@ -190,6 +201,7 @@ libpulsecommon = shared_library('pulsecommon-' + pa_version_major_minor,
dependencies : [
libm_dep, thread_dep, dl_dep, shm_dep, iconv_dep, sndfile_dep, dbus_dep,
x11_dep, libsystemd_dep, glib_dep, gtk_dep, asyncns_dep, libintl_dep,
+ platform_dep, platform_socket_dep,
],
implicit_include_directories : false)
diff --git a/src/modules/meson.build b/src/modules/meson.build
index a4da633bb..dcfc432d8 100644
--- a/src/modules/meson.build
+++ b/src/modules/meson.build
@@ -1,4 +1,6 @@
-subdir('rtp')
+if host_machine.system() != 'windows'
+ subdir('rtp')
+endif
# module name, sources, [headers, extra flags, extra deps, extra libs]
all_modules = [
@@ -44,8 +46,6 @@ all_modules = [
[ 'module-rescue-streams', 'module-rescue-streams.c' ],
[ 'module-role-cork', ['module-role-cork.c', 'stream-interaction.c'], 'stream-interaction.h' ],
[ 'module-role-ducking', ['module-role-ducking.c', 'stream-interaction.c'], 'stream-interaction.h' ],
- [ 'module-rtp-recv', 'rtp/module-rtp-recv.c', [], [], [], librtp ],
- [ 'module-rtp-send', 'rtp/module-rtp-send.c' , [], [], [], librtp ],
[ 'module-simple-protocol-tcp', 'module-protocol-stub.c', [], ['-DUSE_PROTOCOL_SIMPLE', '-DUSE_TCP_SOCKETS'], [], libprotocol_simple ],
[ 'module-simple-protocol-unix', 'module-protocol-stub.c', [], ['-DUSE_PROTOCOL_SIMPLE', '-DUSE_UNIX_SOCKETS'], [], libprotocol_simple ],
[ 'module-sine', 'module-sine.c' ],
@@ -62,9 +62,23 @@ all_modules = [
[ 'module-virtual-sink', 'module-virtual-sink.c' ],
[ 'module-virtual-source', 'module-virtual-source.c' ],
[ 'module-volume-restore', 'module-volume-restore.c' ],
-# [ 'module-waveout', 'module-waveout.c' ],
]
+if host_machine.system() == 'windows'
+ winmm_dep = meson.get_compiler('c').find_library('winmm')
+ ksuser_dep = meson.get_compiler('c').find_library('ksuser')
+ all_modules += [
+ [ 'module-waveout', 'module-waveout.c', [], [], [winmm_dep, ksuser_dep] ],
+ ]
+endif
+
+if host_machine.system() != 'windows'
+ all_modules += [
+ [ 'module-rtp-recv', 'rtp/module-rtp-recv.c', [], [], [], librtp ],
+ [ 'module-rtp-send', 'rtp/module-rtp-send.c' , [], [], [], librtp ],
+ ]
+endif
+
# Modules enabled by headers
if cc.has_header('linux/input.h')
@@ -176,10 +190,13 @@ if lirc_dep.found()
endif
if openssl_dep.found()
- subdir('raop')
- all_modules += [
- [ 'module-raop-sink', 'raop/module-raop-sink.c', [], [], [], libraop ],
- ]
+ if host_machine.system() != 'windows'
+ subdir('raop')
+ all_modules += [
+ [ 'module-raop-sink', 'raop/module-raop-sink.c', [], [], [], libraop ],
+ ]
+ endif
+
if avahi_dep.found()
all_modules += [
[ 'module-raop-discover', 'raop/module-raop-discover.c', [], [], [avahi_dep], libavahi_wrap ],
@@ -289,7 +306,7 @@ foreach m : all_modules
install : true,
install_rpath : rpath_dirs,
install_dir : modlibexecdir,
- dependencies : [thread_dep, libpulse_dep, libpulsecommon_dep, libpulsecore_dep, libintl_dep] + extra_deps,
+ dependencies : [thread_dep, libpulse_dep, libpulsecommon_dep, libpulsecore_dep, libintl_dep, platform_dep, platform_socket_dep] + extra_deps,
link_args : [nodelete_link_args, '-Wl,--no-undefined' ],
link_with : extra_libs,
name_prefix : '',
diff --git a/src/pulse/meson.build b/src/pulse/meson.build
index abc1e7921..8341bfcf0 100644
--- a/src/pulse/meson.build
+++ b/src/pulse/meson.build
@@ -83,7 +83,7 @@ libpulse = shared_library('pulse',
link_args : [nodelete_link_args, versioning_link_args],
install : true,
install_rpath : privlibdir,
- dependencies : [libm_dep, thread_dep, libpulsecommon_dep, dbus_dep, dl_dep, iconv_dep, libintl_dep],
+ dependencies : [libm_dep, thread_dep, libpulsecommon_dep, dbus_dep, dl_dep, iconv_dep, libintl_dep, platform_dep, platform_socket_dep],
implicit_include_directories : false)
libpulse_dep = declare_dependency(link_with: libpulse)
diff --git a/src/pulsecore/meson.build b/src/pulsecore/meson.build
index 5f78be012..e8ce2931c 100644
--- a/src/pulsecore/meson.build
+++ b/src/pulsecore/meson.build
@@ -182,11 +182,13 @@ libpulsecore_simd = simd.check('libpulsecore_simd',
libpulsecore_simd_lib = libpulsecore_simd[0]
cdata.merge_from(libpulsecore_simd[1])
-# FIXME: Implement Windows support
-#'mutex-win32.c',
-#'poll-win32.c',
-#'semaphore-win32.c',
-#'thread-win32.c',
+if host_machine.system() == 'windows'
+ libpulsecore_sources += ['mutex-win32.c',
+ 'poll-win32.c',
+ 'semaphore-win32.c',
+ 'thread-win32.c',
+ ]
+endif
libpulsecore = shared_library('pulsecore-' + pa_version_major_minor,
libpulsecore_sources, libpulsecore_headers,
@@ -198,7 +200,7 @@ libpulsecore = shared_library('pulsecore-' + pa_version_major_minor,
install_rpath : privlibdir,
install_dir : privlibdir,
link_with : libpulsecore_simd_lib,
- dependencies : [libm_dep, libpulsecommon_dep, ltdl_dep, shm_dep, sndfile_dep, database_dep, dbus_dep, libatomic_ops_dep, orc_dep, samplerate_dep, soxr_dep, speex_dep, x11_dep, libintl_dep],
+ dependencies : [libm_dep, libpulsecommon_dep, ltdl_dep, shm_dep, sndfile_dep, database_dep, dbus_dep, libatomic_ops_dep, orc_dep, samplerate_dep, soxr_dep, speex_dep, x11_dep, libintl_dep, platform_dep, platform_socket_dep,],
implicit_include_directories : false)
libpulsecore_dep = declare_dependency(link_with: libpulsecore)
diff --git a/src/utils/meson.build b/src/utils/meson.build
index a0701987b..f40eaff95 100644
--- a/src/utils/meson.build
+++ b/src/utils/meson.build
@@ -19,12 +19,14 @@ executable('pacat',
c_args : pa_c_args,
)
-foreach alias : pacat_aliases
- # FIXME How to handle extension (.exe on windows)?
- dst = join_paths(bindir, alias)
- cmd = 'ln -fs @0@ $DESTDIR@1@'.format('pacat', dst)
- meson.add_install_script('sh', '-c', cmd)
-endforeach
+# Windows doesn't support symbolic links.
+if host_machine.system() != 'windows'
+ foreach alias : pacat_aliases
+ dst = join_paths(bindir, alias)
+ cmd = 'ln -fs @0@ $DESTDIR@1@'.format('pacat', dst)
+ meson.add_install_script('sh', '-c', cmd)
+ endforeach
+endif
pactl_sources = [
'pactl.c',
@@ -40,33 +42,35 @@ executable('pactl',
c_args : pa_c_args,
)
-pasuspender_sources = [
- 'pasuspender.c',
-]
+if host_machine.system() != 'windows'
+ pasuspender_sources = [
+ 'pasuspender.c',
+ ]
-executable('pasuspender',
- pasuspender_sources,
- install: true,
- install_rpath : privlibdir,
- include_directories : [configinc, topinc],
- link_with : [libpulsecommon, libpulse],
- dependencies: [libintl_dep],
- c_args : pa_c_args,
-)
+ executable('pasuspender',
+ pasuspender_sources,
+ install: true,
+ install_rpath : privlibdir,
+ include_directories : [configinc, topinc],
+ link_with : [libpulsecommon, libpulse],
+ dependencies: [libintl_dep],
+ c_args : pa_c_args,
+ )
-pacmd_sources = [
- 'pacmd.c',
-]
+ pacmd_sources = [
+ 'pacmd.c',
+ ]
-executable('pacmd',
- pacmd_sources,
- install: true,
- install_rpath : privlibdir,
- include_directories : [configinc, topinc],
- link_with : [libpulsecommon, libpulse],
- dependencies: [libintl_dep],
- c_args : pa_c_args,
-)
+ executable('pacmd',
+ pacmd_sources,
+ install: true,
+ install_rpath : privlibdir,
+ include_directories : [configinc, topinc],
+ link_with : [libpulsecommon, libpulse],
+ dependencies: [libintl_dep],
+ c_args : pa_c_args,
+ )
+endif
if x11_dep.found()
pax11publish_sources = [
From e4e507802ccfa7cb13183b53bcdaec772846432d Mon Sep 17 00:00:00 2001
From: David
Date: Sun, 20 Dec 2020 11:32:02 +0000
Subject: [PATCH 067/865] Capitalize jack in module-jack-source.c
Part-of:
---
src/modules/jack/module-jack-source.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/modules/jack/module-jack-source.c b/src/modules/jack/module-jack-source.c
index eaf2cd81c..c4541913c 100644
--- a/src/modules/jack/module-jack-source.c
+++ b/src/modules/jack/module-jack-source.c
@@ -342,7 +342,7 @@ int pa__init(pa_module*m) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
if (server_name)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack source (%s)", jack_get_client_name(u->client));
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "JACK source (%s)", jack_get_client_name(u->client));
pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
From ee1391860fa597c26eb6d2ee27b09c0eac659eb7 Mon Sep 17 00:00:00 2001
From: David
Date: Sun, 20 Dec 2020 11:35:30 +0000
Subject: [PATCH 068/865] Capitalize jack in module-jack-sink.c
Part-of:
---
src/modules/jack/module-jack-sink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/modules/jack/module-jack-sink.c b/src/modules/jack/module-jack-sink.c
index effa0dd01..a7c723073 100644
--- a/src/modules/jack/module-jack-sink.c
+++ b/src/modules/jack/module-jack-sink.c
@@ -400,7 +400,7 @@ int pa__init(pa_module*m) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
if (server_name)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client));
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "JACK sink (%s)", jack_get_client_name(u->client));
pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
From e67af95830a5f1b9930ecfdd4e32e0027b7a902f Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela
Date: Sat, 24 Oct 2020 18:20:59 +0200
Subject: [PATCH 069/865] alsa: mixer - use safe dB range values when the
override mask is unset
Use safe values for the min_dB and max_dB fields when the position mask
is unset to avoid breakage for the upper levels.
If the range is incorrect, the volume range shown in pavucontrol shows
strange values.
(Thanks to Wim Taymans for the idea.)
Signed-off-by: Jaroslav Kysela
Part-of:
---
src/modules/alsa/alsa-mixer.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 8b9ccfbf6..3a2913245 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -3072,6 +3072,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
pa_channel_position_t t;
pa_channel_position_mask_t path_volume_channels = 0;
+ bool min_dB_set, max_dB_set;
char buf[64];
pa_assert(p);
@@ -3102,7 +3103,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
pa_log_debug("Probe of element %s failed.", buf);
return -1;
}
- pa_log_debug("Probe of element %s succeeded (volume=%d, switch=%d, enumeration=%d).", buf, e->volume_use, e->switch_use, e->enumeration_use);
+ pa_log_debug("Probe of element %s succeeded (volume=%d, switch=%d, enumeration=%d, has_dB=%d).", buf, e->volume_use, e->switch_use, e->enumeration_use, e->has_dB);
if (ignore_dB)
e->has_dB = false;
@@ -3166,18 +3167,30 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
p->supported = true;
p->min_dB = INFINITY;
+ min_dB_set = false;
p->max_dB = -INFINITY;
+ max_dB_set = false;
for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
- if (p->min_dB > min_dB[t])
+ if (p->min_dB > min_dB[t]) {
p->min_dB = min_dB[t];
+ min_dB_set = true;
+ }
- if (p->max_dB < max_dB[t])
+ if (p->max_dB < max_dB[t]) {
p->max_dB = max_dB[t];
+ max_dB_set = true;
+ }
}
}
+ /* this is probably a wrong prediction, but it should be safe */
+ if (!min_dB_set)
+ p->min_dB = -INFINITY;
+ if (!max_dB_set)
+ p->max_dB = 0;
+
return 0;
}
From b530aa4681087bcb315890df31ec91b4eb3cd4cc Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela
Date: Wed, 14 Oct 2020 22:20:37 +0200
Subject: [PATCH 070/865] alsa: mixer - add support up 8 mixer channels
We have at least one USB hardware which supports the 8
channels in one mixer element:
https://github.com/alsa-project/alsa-ucm-conf/pull/25
POSITION_MASK_CHANNELS define was added for the future extensions.
The override_map variable was changed from bool to mask (unsigned int).
The channel map override settings is handled for channels up to eight now.
Also added missing override-map.3 .. override-map.8 to the configuration
parser array.
The driver channel position was added to the override mask arguments
(syntax is driver:pulseaudio like left:all-left). If ommited, the ALSA's
channel positions are guessed by index.
Link: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/292
Signed-off-by: Jaroslav Kysela
Part-of:
---
src/modules/alsa/alsa-mixer.c | 118 ++++++++++++++++++++++++++++------
src/modules/alsa/alsa-mixer.h | 6 +-
2 files changed, 103 insertions(+), 21 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 3a2913245..8cd17cc35 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -689,6 +689,20 @@ static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_M
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
};
+static snd_mixer_selem_channel_id_t alsa_channel_positions[POSITION_MASK_CHANNELS] = {
+ SND_MIXER_SCHN_FRONT_LEFT,
+ SND_MIXER_SCHN_FRONT_RIGHT,
+ SND_MIXER_SCHN_REAR_LEFT,
+ SND_MIXER_SCHN_REAR_RIGHT,
+ SND_MIXER_SCHN_FRONT_CENTER,
+ SND_MIXER_SCHN_WOOFER,
+ SND_MIXER_SCHN_SIDE_LEFT,
+ SND_MIXER_SCHN_SIDE_RIGHT,
+#if POSITION_MASK_CHANNELS > 8
+#error "Extend alsa_channel_positions[] array (9+)"
+#endif
+};
+
static void setting_free(pa_alsa_setting *s) {
pa_assert(s);
@@ -1767,7 +1781,11 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
if (is_mono) {
e->n_channels = 1;
- if (!e->override_map) {
+ if ((e->override_map & (1 << (e->n_channels-1))) && e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] == 0) {
+ pa_log_warn("Override map for mono element %s is invalid, ignoring override map", e->path->name);
+ e->override_map &= ~(1 << (e->n_channels-1));
+ }
+ if (!(e->override_map & (1 << (e->n_channels-1)))) {
for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
continue;
@@ -1794,24 +1812,25 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Volume element %s with no channels?", buf);
return false;
- } else if (e->n_channels > 2) {
+ } else if (e->n_channels > POSITION_MASK_CHANNELS) {
/* FIXME: In some places code like this is used:
*
* e->masks[alsa_channel_ids[p]][e->n_channels-1]
*
* The definition of e->masks is
*
- * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
+ * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS];
*
- * Since the array size is fixed at 2, we obviously
- * don't support elements with more than two
+ * Since the array size is fixed at POSITION_MASK_CHANNELS, we obviously
+ * don't support elements with more than POSITION_MASK_CHANNELS
* channels... */
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", buf, e->n_channels);
return false;
}
- if (!e->override_map) {
+retry:
+ if (!(e->override_map & (1 << (e->n_channels-1)))) {
for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
bool has_channel;
@@ -1834,6 +1853,17 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
}
+
+ if (e->merged_mask == 0) {
+ if (!(e->override_map & (1 << (e->n_channels-1)))) {
+ pa_log_warn("Channel map for element %s is invalid", e->path->name);
+ return false;
+ }
+ pa_log_warn("Override map for element %s has empty result, ignoring override map", e->path->name);
+ e->override_map &= ~(1 << (e->n_channels-1));
+ goto retry;
+ }
+
return true;
}
@@ -2427,6 +2457,16 @@ static int element_parse_volume_limit(pa_config_parser_state *state) {
return 0;
}
+static unsigned int parse_channel_position(const char *m)
+{
+ pa_channel_position_t p;
+
+ if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
+ return SND_MIXER_SCHN_UNKNOWN;
+
+ return alsa_channel_ids[p];
+}
+
static pa_channel_position_mask_t parse_mask(const char *m) {
pa_channel_position_mask_t v;
@@ -2464,7 +2504,9 @@ static int element_parse_override_map(pa_config_parser_state *state) {
pa_alsa_path *p;
pa_alsa_element *e;
const char *split_state = NULL;
+ char *s;
unsigned i = 0;
+ int channel_count = 0;
char *n;
pa_assert(state);
@@ -2476,31 +2518,60 @@ static int element_parse_override_map(pa_config_parser_state *state) {
return -1;
}
+ s = strstr(state->lvalue, ".");
+ if (s) {
+ pa_atoi(s + 1, &channel_count);
+ if (channel_count < 1 || channel_count > POSITION_MASK_CHANNELS) {
+ pa_log("[%s:%u] Override map index '%s' invalid in '%s'", state->filename, state->lineno, state->lvalue, state->section);
+ return 0;
+ }
+ } else {
+ pa_log("[%s:%u] Invalid override map syntax '%s' in '%s'", state->filename, state->lineno, state->lvalue, state->section);
+ return -1;
+ }
+
while ((n = pa_split(state->rvalue, ",", &split_state))) {
pa_channel_position_mask_t m;
+ snd_mixer_selem_channel_id_t channel_position;
+
+ if (i >= (unsigned)channel_count) {
+ pa_log("[%s:%u] Invalid override map size (>%d) in '%s'", state->filename, state->lineno, channel_count, state->section);
+ return -1;
+ }
+ channel_position = alsa_channel_positions[i];
if (!*n)
m = 0;
else {
- if ((m = parse_mask(n)) == 0) {
- pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
+ s = strstr(n, ":");
+ if (s) {
+ *s = '\0';
+ s++;
+ channel_position = parse_channel_position(n);
+ if (channel_position == SND_MIXER_SCHN_UNKNOWN) {
+ pa_log("[%s:%u] Override map position '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
+ pa_xfree(n);
+ return -1;
+ }
+ }
+ if ((m = parse_mask(s ? s : n)) == 0) {
+ pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, s ? s : n, state->section);
pa_xfree(n);
return -1;
}
}
- if (pa_streq(state->lvalue, "override-map.1"))
- e->masks[i++][0] = m;
- else
- e->masks[i++][1] = m;
-
- /* Later on we might add override-map.3 and so on here ... */
-
+ if (e->masks[channel_position][channel_count-1]) {
+ pa_log("[%s:%u] Override map '%s' duplicate position '%s' in '%s'", state->filename, state->lineno, s ? s : n, snd_mixer_selem_channel_name(channel_position), state->section);
+ pa_xfree(n);
+ return -1;
+ }
+ e->override_map |= (1 << (channel_count - 1));
+ e->masks[channel_position][channel_count-1] = m;
pa_xfree(n);
+ i++;
}
- e->override_map = true;
-
return 0;
}
@@ -2856,6 +2927,15 @@ pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa
{ "enumeration", element_parse_enumeration, NULL, NULL },
{ "override-map.1", element_parse_override_map, NULL, NULL },
{ "override-map.2", element_parse_override_map, NULL, NULL },
+ { "override-map.3", element_parse_override_map, NULL, NULL },
+ { "override-map.4", element_parse_override_map, NULL, NULL },
+ { "override-map.5", element_parse_override_map, NULL, NULL },
+ { "override-map.6", element_parse_override_map, NULL, NULL },
+ { "override-map.7", element_parse_override_map, NULL, NULL },
+ { "override-map.8", element_parse_override_map, NULL, NULL },
+#if POSITION_MASK_CHANNELS > 8
+#error "Add override-map.9+ definitions"
+#endif
/* ... later on we might add override-map.3 and so on here ... */
{ "required", element_parse_required, NULL, NULL },
{ "required-any", element_parse_required, NULL, NULL },
@@ -3227,7 +3307,7 @@ void pa_alsa_element_dump(pa_alsa_element *e) {
pa_assert(e);
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
- pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
+ pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%02x",
buf,
e->direction,
e->switch_use,
@@ -3239,7 +3319,7 @@ void pa_alsa_element_dump(pa_alsa_element *e) {
e->required_absent,
(long long unsigned) e->merged_mask,
e->n_channels,
- pa_yes_no(e->override_map));
+ e->override_map);
PA_LLIST_FOREACH(o, e->options)
pa_alsa_option_dump(o);
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 905e3128b..81519551d 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -50,6 +50,8 @@ typedef struct pa_alsa_port_data pa_alsa_port_data;
#include "alsa-util.h"
#include "alsa-ucm.h"
+#define POSITION_MASK_CHANNELS 8
+
typedef enum pa_alsa_switch_use {
PA_ALSA_SWITCH_IGNORE,
PA_ALSA_SWITCH_MUTE, /* make this switch follow mute status */
@@ -152,7 +154,7 @@ struct pa_alsa_element {
long constant_volume;
- bool override_map:1;
+ unsigned int override_map;
bool direction_try_other:1;
bool has_dB:1;
@@ -160,7 +162,7 @@ struct pa_alsa_element {
long volume_limit; /* -1 for no configured limit */
double min_dB, max_dB;
- pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
+ pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS];
unsigned n_channels;
pa_channel_position_mask_t merged_mask;
From 8f6029077a0c18f4d12105e9bd8c093c25ad7b38 Mon Sep 17 00:00:00 2001
From: Hui Wang
Date: Mon, 23 Nov 2020 11:25:51 +0800
Subject: [PATCH 071/865] switch-on-port-available: Switch to headset or mic on
unknown avail
On some Dell AIO machines, there is no internal mic, only a multi
function audio jack, so the only input devices are headphone-mic and
headset-mic, and they share the Jack with headphone.
When there is no headset plugged in that Jack, the headphone-mic
and headset-mic are off. And since there is no available port under
the analog input source, this source is unlinked (if there is
internal mic, the source will not be unlinked). so the only pa-source
left in the PA is analog-stereo-monitor.
After the headset is plugged, we need to let switch_to_port() handle
headset-mic and headphone-mic conditionally, this will guarantee the
source will be created if it is unlinked before plugging, and then the
input profile could be selected correctly.
Signed-off-by: Hui Wang
Part-of:
---
src/modules/module-switch-on-port-available.c | 32 ++++++++++++-------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c
index 84b856659..f450004ca 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -228,9 +228,7 @@ static struct port_pointers find_port_pointers(pa_device_port *port) {
}
/* Switches to a port, switching profiles if necessary or preferred */
-static void switch_to_port(pa_device_port *port) {
- struct port_pointers pp = find_port_pointers(port);
-
+static void switch_to_port(pa_device_port *port, struct port_pointers pp) {
if (pp.is_port_active)
return; /* Already selected */
@@ -252,8 +250,7 @@ static void switch_to_port(pa_device_port *port) {
}
/* Switches away from a port, switching profiles if necessary or preferred */
-static void switch_from_port(pa_device_port *port) {
- struct port_pointers pp = find_port_pointers(port);
+static void switch_from_port(pa_device_port *port, struct port_pointers pp) {
pa_device_port *p, *best_port = NULL;
void *state;
@@ -282,12 +279,12 @@ static void switch_from_port(pa_device_port *port) {
* PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED callback, as at this point
* the profile availability hasn't been updated yet. */
if (best_port)
- switch_to_port(best_port);
+ switch_to_port(best_port, pp);
}
static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void* userdata) {
- pa_assert(port);
+ struct port_pointers pp = find_port_pointers(port);
if (!port->card) {
pa_log_warn("Port %s does not have a card", port->name);
@@ -314,6 +311,15 @@ static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port
* functionality for setups that can't trigger this kind of
* interaction.
*
+ * For headset or microphone, if they are part of some availability group
+ * and they become unknown from off, it needs to check if their source is
+ * unlinked or not, if their source is unlinked, let switch_to_port()
+ * process them, then with the running of pa_card_set_profile(), their
+ * source will be created, otherwise the headset or microphone can't be used
+ * to record sound since there is no source for these 2 ports. This issue
+ * is observed on Dell machines which have multi-function audio jack but no
+ * internal mic.
+ *
* We should make this configurable so that users can optionally
* override the default to a headset or mic. */
@@ -323,20 +329,22 @@ static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port
break;
}
- /* For no we only switch the headphone port */
- if (port->direction != PA_DIRECTION_OUTPUT) {
+ /* Switch the headphone port, the input ports without source and the
+ * input ports their source->active_port is part of a group of ports.
+ */
+ if (port->direction == PA_DIRECTION_INPUT && pp.source && !pp.source->active_port->availability_group) {
pa_log_debug("Not switching to input port %s, its availability is unknown.", port->name);
break;
}
- switch_to_port(port);
+ switch_to_port(port, pp);
break;
case PA_AVAILABLE_YES:
- switch_to_port(port);
+ switch_to_port(port, pp);
break;
case PA_AVAILABLE_NO:
- switch_from_port(port);
+ switch_from_port(port, pp);
break;
default:
break;
From dd7593b83669d84d3aa02597a61e37c74123da50 Mon Sep 17 00:00:00 2001
From: morrishoresh
Date: Thu, 24 Dec 2020 23:58:54 +0200
Subject: [PATCH 072/865] bluetooth: correct possible segmentation fault
Part-of:
---
src/modules/bluetooth/backend-ofono.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c
index 0e5bbe8b7..d7a13efd0 100644
--- a/src/modules/bluetooth/backend-ofono.c
+++ b/src/modules/bluetooth/backend-ofono.c
@@ -627,8 +627,6 @@ static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage
card = pa_hashmap_get(backend->cards, path);
- card->connecting = false;
-
if (!card || codec != HFP_AUDIO_CODEC_CVSD || card->fd >= 0) {
pa_log_warn("New audio connection invalid arguments (path=%s fd=%d, codec=%d)", path, fd, codec);
pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call"));
@@ -639,6 +637,7 @@ static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage
pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", path, fd, codec);
+ card->connecting = false;
card->fd = fd;
card->transport->codec = codec;
From 4874f8a607b62b496aa0a1f6c1f0c3e8570195ae Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Wed, 30 Dec 2020 00:00:50 +0300
Subject: [PATCH 073/865] bluetooth: align max renderable block size to frame
size
When bluez5 device I/O thread detects it is too slow sending data, allow it to
skip up to max rendered block size bytes which must be aligned to frame size.
Part-of:
---
src/modules/bluetooth/module-bluez5-device.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
index 87a8809ff..9440084a6 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -1484,7 +1484,7 @@ static void thread_func(void *userdata) {
if (bytes_to_send > 2 * u->write_block_size) {
uint64_t skip_bytes;
pa_memchunk tmp;
- size_t mempool_max_block_size = pa_mempool_block_size_max(u->core->mempool);
+ size_t max_render_size = pa_frame_align(pa_mempool_block_size_max(u->core->mempool), &u->encoder_sample_spec);
pa_usec_t skip_usec;
skip_bytes = bytes_to_send - 2 * u->write_block_size;
@@ -1497,8 +1497,8 @@ static void thread_func(void *userdata) {
while (skip_bytes > 0) {
size_t bytes_to_render;
- if (skip_bytes > mempool_max_block_size)
- bytes_to_render = mempool_max_block_size;
+ if (skip_bytes > max_render_size)
+ bytes_to_render = max_render_size;
else
bytes_to_render = skip_bytes;
From 3ecea410d79d398126b54579b422e2f16baede5f Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Tue, 29 Dec 2020 00:03:41 +0300
Subject: [PATCH 074/865] context: add property to forcefully disable shared
memory channels
If application wants last bits of virtual memory, allow it to forcefully disable
shared memory communication channels via PA_PROP_CONTEXT_FORCE_DISABLE_SHM=yes
Part-of:
---
src/pulse/context.c | 11 +++++++++++
src/pulse/proplist.h | 3 +++
2 files changed, 14 insertions(+)
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 1d1bb9ee8..05b6633aa 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -128,6 +128,7 @@ static void reset_callbacks(pa_context *c) {
pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, const pa_proplist *p) {
pa_context *c;
pa_mem_type_t type;
+ const char *force_disable_shm_str;
pa_assert(mainloop);
@@ -173,6 +174,16 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
c->conf = pa_client_conf_new();
pa_client_conf_load(c->conf, true, true);
+ force_disable_shm_str = pa_proplist_gets(c->proplist, PA_PROP_CONTEXT_FORCE_DISABLE_SHM);
+ if (force_disable_shm_str) {
+ int b = pa_parse_boolean(force_disable_shm_str);
+ if (b < 0) {
+ pa_log_warn("Ignored invalid value for '%s' property: %s", PA_PROP_CONTEXT_FORCE_DISABLE_SHM, force_disable_shm_str);
+ } else if (b) {
+ c->conf->disable_shm = true;
+ }
+ }
+
c->srb_template.readfd = -1;
c->srb_template.writefd = -1;
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index e50518b37..5da01664b 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -267,6 +267,9 @@ PA_C_DECL_BEGIN
/** For PCM formats: the channel map of the stream as returned by pa_channel_map_snprint() \since 1.0 */
#define PA_PROP_FORMAT_CHANNEL_MAP "format.channel_map"
+/** For context: whether to forcefully disable data transfer via POSIX or memfd shared memory. This property overrides any other client configuration which would otherwise enable SHM communication channels. \since 15.0 */
+#define PA_PROP_CONTEXT_FORCE_DISABLE_SHM "context.force.disable.shm"
+
/** 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;
From aad2fca0c90ef72c6f5d72e16073238eba9051a8 Mon Sep 17 00:00:00 2001
From: Kai-Heng Feng
Date: Tue, 22 Dec 2020 13:39:05 +0800
Subject: [PATCH 075/865] alsa-mixer: Handle the index for ALSA mixer jack
identifiers
Some systems have two jacks with same name but different index, we need
to take index into consideration to use both jacks.
Part-of:
---
src/modules/alsa/alsa-mixer.c | 109 +++++++++++++++-------------
src/modules/alsa/alsa-mixer.h | 6 +-
src/modules/alsa/alsa-ucm.c | 4 +-
src/modules/alsa/alsa-util.c | 4 +-
src/modules/alsa/alsa-util.h | 2 +-
src/modules/alsa/module-alsa-card.c | 6 +-
6 files changed, 73 insertions(+), 58 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 8cd17cc35..58c33bf48 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -113,7 +113,7 @@ struct description2_map {
pa_device_port_type_t type;
};
-static char *alsa_id_str(char *dst, size_t dst_len, pa_alsa_mixer_id *id) {
+char *pa_alsa_mixer_id_to_string(char *dst, size_t dst_len, pa_alsa_mixer_id *id) {
if (id->index > 0) {
snprintf(dst, dst_len, "'%s',%d", id->name, id->index);
} else {
@@ -153,7 +153,7 @@ static int alsa_id_decode(const char *src, char *name, int *index) {
return 0;
}
-pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name) {
+pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name, int index) {
pa_alsa_jack *jack;
pa_assert(name);
@@ -162,7 +162,8 @@ pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name
jack->path = path;
jack->mixer_device_name = pa_xstrdup(mixer_device_name);
jack->name = pa_xstrdup(name);
- jack->alsa_name = pa_sprintf_malloc("%s Jack", name);
+ jack->alsa_id.name = pa_sprintf_malloc("%s Jack", name);
+ jack->alsa_id.index = index;
jack->state_unplugged = PA_AVAILABLE_NO;
jack->state_plugged = PA_AVAILABLE_YES;
jack->ucm_devices = pa_dynarray_new(NULL);
@@ -177,7 +178,7 @@ void pa_alsa_jack_free(pa_alsa_jack *jack) {
pa_dynarray_free(jack->ucm_hw_mute_devices);
pa_dynarray_free(jack->ucm_devices);
- pa_xfree(jack->alsa_name);
+ pa_xfree(jack->alsa_id.name);
pa_xfree(jack->name);
pa_xfree(jack->mixer_device_name);
pa_xfree(jack);
@@ -835,7 +836,7 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -861,14 +862,14 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
if (value < e->db_fix->min_step) {
value = e->db_fix->min_step;
snd_mixer_selem_set_playback_volume(me, c, value);
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
"Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
} else if (value > e->db_fix->max_step) {
value = e->db_fix->max_step;
snd_mixer_selem_set_playback_volume(me, c, value);
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
"Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
@@ -891,14 +892,14 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
if (value < e->db_fix->min_step) {
value = e->db_fix->min_step;
snd_mixer_selem_set_capture_volume(me, c, value);
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
"Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
} else if (value > e->db_fix->max_step) {
value = e->db_fix->max_step;
snd_mixer_selem_set_capture_volume(me, c, value);
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
"Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
@@ -1006,7 +1007,7 @@ static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, bool *b) {
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -1172,7 +1173,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -1364,7 +1365,7 @@ static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -1375,7 +1376,7 @@ static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
r = snd_mixer_selem_set_capture_switch_all(me, b);
if (r < 0) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
}
@@ -1419,7 +1420,7 @@ static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -1464,7 +1465,7 @@ static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
}
if (r < 0) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to set volume of %s: %s", buf, pa_alsa_strerror(errno));
}
@@ -1670,19 +1671,19 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
if (r < 0) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to get volume range of %s: %s", buf, pa_alsa_strerror(r));
return false;
}
if (e->min_volume >= e->max_volume) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Your kernel driver is broken for element %s: it reports a volume range from %li to %li which makes no sense.",
buf, e->min_volume, e->max_volume);
return false;
}
if (e->volume_use == PA_ALSA_VOLUME_CONSTANT && (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
e->constant_volume, buf, e->min_volume, e->max_volume);
return false;
@@ -1690,7 +1691,7 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
if (e->db_fix && ((e->min_volume > e->db_fix->min_step) || (e->max_volume < e->db_fix->max_step))) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
"real hardware range (%li-%li). Disabling the decibel fix.", buf,
e->db_fix->min_step, e->db_fix->max_step, e->min_volume, e->max_volume);
@@ -1717,19 +1718,19 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
long max_dB_checked = 0;
if (element_ask_vol_dB(me, e->direction, e->min_volume, &min_dB_checked) < 0) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->min_volume);
return false;
}
if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB_checked) < 0) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->max_volume);
return false;
}
if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
"doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
"%0.2f dB at level %li.", buf, min_dB / 100.0, max_dB / 100.0,
@@ -1752,7 +1753,7 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
if (e->volume_limit >= 0) {
if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
"%li-%li. The volume limit is ignored.",
buf, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
@@ -1764,7 +1765,7 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
e->db_fix->max_step = e->max_volume;
e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
} else if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB) < 0) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to get dB value of %s: %s", buf, pa_alsa_strerror(r));
e->has_dB = false;
} else
@@ -1809,7 +1810,7 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
}
if (e->n_channels <= 0) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Volume element %s with no channels?", buf);
return false;
} else if (e->n_channels > POSITION_MASK_CHANNELS) {
@@ -1824,7 +1825,7 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
* Since the array size is fixed at POSITION_MASK_CHANNELS, we obviously
* don't support elements with more than POSITION_MASK_CHANNELS
* channels... */
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", buf, e->n_channels);
return false;
}
@@ -1973,12 +1974,12 @@ static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m)
}
new_name = pa_sprintf_malloc("%s,pcm=%i Jack", j->name, mapping->hw_device_index);
- pa_xfree(j->alsa_name);
- j->alsa_name = new_name;
+ pa_xfree(j->alsa_id.name);
+ j->alsa_id.name = new_name;
j->append_pcm_to_name = false;
}
- has_control = pa_alsa_mixer_find_card(m, j->alsa_name, 0) != NULL;
+ has_control = pa_alsa_mixer_find_card(m, &j->alsa_id, 0) != NULL;
pa_alsa_jack_set_has_control(j, has_control);
if (j->has_control) {
@@ -2041,19 +2042,26 @@ finish:
static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
pa_alsa_jack *j;
+ char *name;
+ int index;
if (!pa_startswith(section, "Jack "))
return NULL;
section += 5;
- if (p->last_jack && pa_streq(p->last_jack->name, section))
+ name = alloca(strlen(section) + 1);
+ if (alsa_id_decode(section, name, &index))
+ return NULL;
+
+ if (p->last_jack && pa_streq(p->last_jack->name, name) &&
+ p->last_jack->alsa_id.index == index)
return p->last_jack;
PA_LLIST_FOREACH(j, p->jacks)
- if (pa_streq(j->name, section))
+ if (pa_streq(j->name, name) && j->alsa_id.index == index)
goto finish;
- j = pa_alsa_jack_new(p, NULL, section);
+ j = pa_alsa_jack_new(p, NULL, name, index);
PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
finish:
@@ -2645,7 +2653,7 @@ static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx)
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -2658,7 +2666,7 @@ static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx)
r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
if (r < 0) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
}
@@ -2666,7 +2674,7 @@ static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx)
pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to set enumeration of %s: %s", buf, pa_alsa_strerror(errno));
}
}
@@ -2723,7 +2731,7 @@ static int option_verify(pa_alsa_option *o) {
if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
- alsa_id_str(buf, sizeof(buf), &o->element->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
pa_log("Element %s of option %s not set for select.", buf, o->name);
return -1;
}
@@ -2731,7 +2739,7 @@ static int option_verify(pa_alsa_option *o) {
if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
!pa_streq(o->alsa_name, "on") &&
!pa_streq(o->alsa_name, "off")) {
- alsa_id_str(buf, sizeof(buf), &o->element->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
pa_log("Switch %s options need be named off or on ", buf);
return -1;
}
@@ -2757,13 +2765,13 @@ static int element_verify(pa_alsa_element *e) {
(e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
(e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
(e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log("Element %s cannot be required and absent at the same time.", buf);
return -1;
}
if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log("Element %s cannot set select for both switch and enumeration.", buf);
return -1;
}
@@ -3168,16 +3176,17 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
pa_log_debug("Probing path '%s'", p->name);
PA_LLIST_FOREACH(j, p->jacks) {
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &j->alsa_id);
if (jack_probe(j, mapping, m) < 0) {
p->supported = false;
- pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
+ pa_log_debug("Probe of jack %s failed.", buf);
return -1;
}
- pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
+ pa_log_debug("Probe of jack %s succeeded (%s)", buf, j->has_control ? "found!" : "not found");
}
PA_LLIST_FOREACH(e, p->elements) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
if (element_probe(e, m) < 0) {
p->supported = false;
pa_log_debug("Probe of element %s failed.", buf);
@@ -3286,7 +3295,7 @@ void pa_alsa_setting_dump(pa_alsa_setting *s) {
void pa_alsa_jack_dump(pa_alsa_jack *j) {
pa_assert(j);
- pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
+ pa_log_debug("Jack %s, alsa_name='%s', index='%d', detection %s", j->name, j->alsa_id.name, j->alsa_id.index, j->has_control ? "possible" : "unavailable");
}
void pa_alsa_option_dump(pa_alsa_option *o) {
@@ -3306,7 +3315,7 @@ void pa_alsa_element_dump(pa_alsa_element *e) {
pa_alsa_option *o;
pa_assert(e);
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%02x",
buf,
e->direction,
@@ -3366,7 +3375,7 @@ static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_e
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return;
}
@@ -3661,7 +3670,7 @@ static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_
SELEM_INIT(sid, &a->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- alsa_id_str(buf, sizeof(buf), &a->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return false;
}
@@ -3692,7 +3701,7 @@ static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_
return false;
for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
- alsa_id_str(buf, sizeof(buf), &a->alsa_id);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
buf, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
return false;
@@ -3769,7 +3778,8 @@ static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
continue;
PA_LLIST_FOREACH(jb, p2->jacks) {
- if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
+ if (jb->has_control && pa_streq(ja->alsa_id.name, jb->alsa_id.name) &&
+ (ja->alsa_id.index == jb->alsa_id.index) &&
(ja->state_plugged == jb->state_plugged) &&
(ja->state_unplugged == jb->state_unplugged)) {
exists = true;
@@ -4459,7 +4469,8 @@ static void profile_set_set_availability_groups(pa_alsa_profile_set *ps) {
PA_LLIST_FOREACH(j2, p2->jacks) {
if (!j2->has_control || j2->state_plugged == PA_AVAILABLE_NO)
continue;
- if (pa_streq(j->alsa_name, j2->alsa_name)) {
+ if (pa_streq(j->alsa_id.name, j2->alsa_id.name) &&
+ j->alsa_id.index == j2->alsa_id.index) {
j->state_plugged = PA_AVAILABLE_UNKNOWN;
j2->state_plugged = PA_AVAILABLE_UNKNOWN;
found = p2->availability_group;
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 81519551d..db8310258 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -115,6 +115,8 @@ struct pa_alsa_mixer_id {
int index;
};
+char *pa_alsa_mixer_id_to_string(char *dst, size_t dst_len, pa_alsa_mixer_id *id);
+
/* An option belongs to an element and refers to one enumeration item
* of the element is an enumeration item, or a switch status if the
* element is a switch item. */
@@ -179,8 +181,8 @@ struct pa_alsa_jack {
snd_mixer_t *mixer_handle;
char *mixer_device_name;
+ struct pa_alsa_mixer_id alsa_id;
char *name; /* E g "Headphone" */
- char *alsa_name; /* E g "Headphone Jack" */
bool has_control; /* is the jack itself present? */
bool plugged_in; /* is this jack currently plugged in? */
snd_mixer_elem_t *melem; /* Jack detection handle */
@@ -196,7 +198,7 @@ struct pa_alsa_jack {
bool append_pcm_to_name;
};
-pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name);
+pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name, int index);
void pa_alsa_jack_free(pa_alsa_jack *jack);
void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control);
void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in);
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 18925b792..d9cea6105 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1719,7 +1719,7 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *d
pa_log("[%s] No mixer device name for JackControl \"%s\"", device_name, jack_control);
return NULL;
}
- j = pa_alsa_jack_new(NULL, mixer_device_name, name);
+ j = pa_alsa_jack_new(NULL, mixer_device_name, name, 0);
PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
finish:
@@ -1953,7 +1953,7 @@ static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) {
continue;
}
- has_control = pa_alsa_mixer_find_card(mixer_handle, dev->jack->alsa_name, 0) != NULL;
+ has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
pa_alsa_jack_set_has_control(dev->jack, has_control);
pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
}
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index bf35b6112..172a7bb51 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1635,8 +1635,8 @@ static snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer,
return NULL;
}
-snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char *name, unsigned int device) {
- return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, name, 0, device);
+snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, struct pa_alsa_mixer_id *alsa_id, unsigned int device) {
+ return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device);
}
snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device) {
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index cb8be22fb..2eed3eac3 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -148,7 +148,7 @@ const char* pa_alsa_strerror(int errnum);
bool pa_alsa_may_tsched(bool want);
-snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char *name, unsigned int device);
+snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, struct pa_alsa_mixer_id *alsa_id, unsigned int device);
snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device);
snd_mixer_t *pa_alsa_open_mixer(pa_hashmap *mixers, int alsa_card_index, bool probe);
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index f2f50382a..7ff82395f 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -623,6 +623,7 @@ static void init_jacks(struct userdata *u) {
void *state;
pa_alsa_path* path;
pa_alsa_jack* jack;
+ char buf[64];
u->jacks = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
@@ -665,9 +666,10 @@ static void init_jacks(struct userdata *u) {
}
}
pa_alsa_mixer_set_fdlist(u->mixers, jack->mixer_handle, u->core->mainloop);
- jack->melem = pa_alsa_mixer_find_card(jack->mixer_handle, jack->alsa_name, 0);
+ jack->melem = pa_alsa_mixer_find_card(jack->mixer_handle, &jack->alsa_id, 0);
if (!jack->melem) {
- pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name);
+ pa_alsa_mixer_id_to_string(buf, sizeof(buf), &jack->alsa_id);
+ pa_log_warn("Jack %s seems to have disappeared.", buf);
pa_alsa_jack_set_has_control(jack, false);
continue;
}
From 2a0fa78d18895ed283e9a36ba9af5a6102cceb4f Mon Sep 17 00:00:00 2001
From: Kai-Heng Feng
Date: Wed, 25 Mar 2020 18:08:56 +0800
Subject: [PATCH 076/865] alsa-mixer: Support dual Front Headphone Jack
There are dual Front Headphone Jacks with different indices or with
different names, they can be found on HP EliteDesk 800 SFF and HP
EliteDesk 800 DM, respectively.
Part-of:
---
.../alsa/mixer/paths/analog-output-headphones.conf | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
index 14fb81481..d5a9a327b 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
@@ -35,6 +35,18 @@ state.unplugged = unknown
[Jack Front Headphone]
required-any = any
+; HP EliteDesk 800 SFF Headphone
+[Jack Front Headphone,1]
+required-any = any
+
+; HP EliteDesk 800 DM Headset
+[Jack Front Headphone Front]
+required-any = any
+
+; HP EliteDesk 800 DM Headphone
+[Jack Front Headphone Surround]
+required-any = any
+
[Jack Front Headphone Phantom]
required-any = any
state.plugged = unknown
From d51870d4f0f559102cb3860f32522bf163729419 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Sat, 26 Dec 2020 22:15:20 +0200
Subject: [PATCH 077/865] alsa-mixer: Use unambiguous descriptions with dual
headphone outputs
Previously both paths had description "Headphones", which I assume can
cause confusion with users who see two ports with identical names. I
don't have this kind of hardware myself nor have I heard complaints from
users, this is just something I noticed while reading the configuration
files.
Part-of:
---
src/modules/alsa/alsa-mixer.c | 1 +
src/modules/alsa/mixer/paths/analog-output-headphones-2.conf | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 58c33bf48..a6e6672ad 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -2797,6 +2797,7 @@ static int path_verify(pa_alsa_path *p) {
{ "analog-input-video", N_("Video"), PA_DEVICE_PORT_TYPE_VIDEO },
{ "analog-output", N_("Analog Output"), PA_DEVICE_PORT_TYPE_ANALOG },
{ "analog-output-headphones", N_("Headphones"), PA_DEVICE_PORT_TYPE_HEADPHONES },
+ { "analog-output-headphones-2", N_("Headphones 2"), PA_DEVICE_PORT_TYPE_HEADPHONES },
{ "analog-output-headphones-mono", N_("Headphones Mono Output"), PA_DEVICE_PORT_TYPE_HEADPHONES },
{ "analog-output-lineout", N_("Line Out"), PA_DEVICE_PORT_TYPE_LINE },
{ "analog-output-mono", N_("Analog Mono Output"), PA_DEVICE_PORT_TYPE_ANALOG },
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf b/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
index 30815d0a8..33f1cfd2e 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
@@ -19,7 +19,6 @@
[General]
priority = 98
-description-key = analog-output-headphones
[Properties]
device.icon_name = audio-headphones
From 0c14f89b8403c78ff6938359c9885f0a27f071ec Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Sat, 26 Dec 2020 22:22:06 +0200
Subject: [PATCH 078/865] alsa-mixer: Move HP EliteDesk 800 SFF/DM second
headphone output to path analog-output-headphones-2
The two headphone outputs should be handled in separate paths so that
volume control can be implemented properly for both outputs.
Part-of:
---
.../alsa/mixer/paths/analog-output-headphones-2.conf | 12 ++++++++++--
.../alsa/mixer/paths/analog-output-headphones.conf | 8 --------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf b/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
index 33f1cfd2e..f88f8a0b0 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
@@ -13,7 +13,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see .
-; Path for mixers that have a 'Headphone2' control
+; Path for the second headphone output on dual-headphone machines.
;
; See analog-output.conf.common for an explanation on the directives
@@ -23,6 +23,14 @@ priority = 98
[Properties]
device.icon_name = audio-headphones
+; HP EliteDesk 800 SFF Headphone
+[Jack Front Headphone,1]
+required-any = any
+
+; HP EliteDesk 800 DM Headphone
+[Jack Front Headphone Surround]
+required-any = any
+
[Element Hardware Master]
switch = mute
volume = merge
@@ -55,7 +63,7 @@ switch = off
volume = off
[Element Headphone2]
-required = any
+required-any = any
switch = mute
volume = merge
override-map.1 = all
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
index d5a9a327b..1c7611fc1 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
@@ -35,18 +35,10 @@ state.unplugged = unknown
[Jack Front Headphone]
required-any = any
-; HP EliteDesk 800 SFF Headphone
-[Jack Front Headphone,1]
-required-any = any
-
; HP EliteDesk 800 DM Headset
[Jack Front Headphone Front]
required-any = any
-; HP EliteDesk 800 DM Headphone
-[Jack Front Headphone Surround]
-required-any = any
-
[Jack Front Headphone Phantom]
required-any = any
state.plugged = unknown
From 21a531041ab0b5e52dbde1e3ed4ab8fb49198fda Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen
Date: Sat, 26 Dec 2020 22:29:06 +0200
Subject: [PATCH 079/865] alsa-mixer: Add support for the Headphone,1 element
This is seen at least on HP EliteDesk 800 DM and HP EliteDesk 800 SFF.
This is used by the analog-output-headphones-2 path, but all other paths
on the same sink need to handle the element too. The existing
configuration is inconsistent between files regarding whether headphone
outputs should be muted or not when not using them. I chose to be
consistent within files, which means that Headphone,1 handling is
inconsistent between files in the same way that the existing Headphone
and Headphone2 handling is. (My opinion is that unused paths should be
always muted, but I didn't want to do that policy change in this patch.)
Part-of:
---
.../alsa/mixer/paths/analog-output-headphones-2.conf | 7 +++++++
src/modules/alsa/mixer/paths/analog-output-headphones.conf | 7 +++++++
src/modules/alsa/mixer/paths/analog-output-lineout.conf | 4 ++++
src/modules/alsa/mixer/paths/analog-output-mono.conf | 4 ++++
.../alsa/mixer/paths/analog-output-speaker-always.conf | 4 ++++
src/modules/alsa/mixer/paths/analog-output-speaker.conf | 4 ++++
6 files changed, 30 insertions(+)
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf b/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
index f88f8a0b0..178999088 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
@@ -54,6 +54,13 @@ volume = off
switch = mute
volume = zero
+[Element Headphone,1]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
[Element Headphone+LO]
switch = mute
volume = zero
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
index 1c7611fc1..c808e19e1 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
@@ -93,6 +93,13 @@ volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
+; This path is intended to control the first headphones, not
+; the second headphones. But it should not hurt if we leave the second
+; headphone jack enabled nonetheless.
+[Element Headphone,1]
+switch = mute
+volume = zero
+
[Element Headset]
required-any = any
switch = mute
diff --git a/src/modules/alsa/mixer/paths/analog-output-lineout.conf b/src/modules/alsa/mixer/paths/analog-output-lineout.conf
index 61df49965..1ffce2225 100644
--- a/src/modules/alsa/mixer/paths/analog-output-lineout.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-lineout.conf
@@ -127,6 +127,10 @@ required-any = any
switch = off
volume = off
+[Element Headphone,1]
+switch = off
+volume = off
+
[Element Headphone2]
switch = off
volume = off
diff --git a/src/modules/alsa/mixer/paths/analog-output-mono.conf b/src/modules/alsa/mixer/paths/analog-output-mono.conf
index 989654334..5e4940598 100644
--- a/src/modules/alsa/mixer/paths/analog-output-mono.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-mono.conf
@@ -44,6 +44,10 @@ override-map.2 = all-left,all-right
switch = mute
volume = zero
+[Element Headphone,1]
+switch = mute
+volume = zero
+
[Element Headphone+LO]
switch = mute
volume = zero
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf b/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
index bf781c671..756afa954 100644
--- a/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
@@ -76,6 +76,10 @@ volume = off
switch = mute
volume = zero
+[Element Headphone,1]
+switch = mute
+volume = zero
+
[Element Headphone2]
switch = mute
volume = zero
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker.conf b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
index 91a4bc163..72f928fd9 100644
--- a/src/modules/alsa/mixer/paths/analog-output-speaker.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
@@ -101,6 +101,10 @@ name = analog-output-speaker
switch = off
volume = off
+[Element Headphone,1]
+switch = off
+volume = off
+
[Element Headphone2]
switch = off
volume = off
From 81ebd8ba3f644e95a7f858ede9fc65d340ab06e2 Mon Sep 17 00:00:00 2001
From: Georg Chini
Date: Fri, 1 Jan 2021 21:35:41 +0100
Subject: [PATCH 080/865] message-params: Fix array reading functions
The array read functions need the state pointer as an additional argument because the
array may be in the middle of a parameter list and the state pointer must be advanced
to the element after the array.
Additionally fixes some compiler warnings.
Part-of:
---
src/pulse/message-params.c | 36 ++++++++++++++++--------------------
src/pulse/message-params.h | 8 ++++----
2 files changed, 20 insertions(+), 24 deletions(-)
diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c
index ae2ed8b68..99402ebc2 100644
--- a/src/pulse/message-params.c
+++ b/src/pulse/message-params.c
@@ -345,9 +345,9 @@ int pa_message_params_read_bool(char *c, bool *result, void **state) {
}
/* Converts a parameter list to a string array. */
-int pa_message_params_read_string_array(char *c, const char ***results, int *length) {
- void *state = NULL;
- uint32_t element_count, i;
+int pa_message_params_read_string_array(char *c, const char ***results, int *length, void **state) {
+ void *state1 = NULL;
+ int element_count, i;
int err;
const char **values;
char *start_pos;
@@ -370,8 +370,7 @@ int pa_message_params_read_string_array(char *c, const char ***results, int *len
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(char *));
- state = NULL;
- for (i = 0; (err = pa_message_params_read_string(start_pos, &(values[i]), &state)) > 0; i++)
+ for (i = 0; (err = pa_message_params_read_string(start_pos, &(values[i]), &state1)) > 0; i++)
;
if (err < 0) {
@@ -386,10 +385,10 @@ int pa_message_params_read_string_array(char *c, const char ***results, int *len
}
/* Converts a parameter list to a double array. */
-int pa_message_params_read_double_array(char *c, double **results, int *length) {
+int pa_message_params_read_double_array(char *c, double **results, int *length, void **state) {
double *values;
- void *state = NULL;
- uint32_t element_count, i;
+ void *state1 = NULL;
+ int element_count, i;
int err;
char *start_pos;
@@ -411,8 +410,7 @@ int pa_message_params_read_double_array(char *c, double **results, int *length)
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(double));
- state = NULL;
- for (i = 0; (err = pa_message_params_read_double(start_pos, &(values[i]), &state)) > 0; i++)
+ for (i = 0; (err = pa_message_params_read_double(start_pos, &(values[i]), &state1)) > 0; i++)
;
if (err < 0) {
@@ -427,10 +425,10 @@ int pa_message_params_read_double_array(char *c, double **results, int *length)
}
/* Converts a parameter list to an int64 array. */
-int pa_message_params_read_int64_array(char *c, int64_t **results, int *length) {
+int pa_message_params_read_int64_array(char *c, int64_t **results, int *length, void **state) {
int64_t *values;
- void *state = NULL;
- uint32_t element_count, i;
+ void *state1 = NULL;
+ int element_count, i;
int err;
char *start_pos;
@@ -452,8 +450,7 @@ int pa_message_params_read_int64_array(char *c, int64_t **results, int *length)
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(int64_t));
- state = NULL;
- for (i = 0; (err = pa_message_params_read_int64(start_pos, &(values[i]), &state)) > 0; i++)
+ for (i = 0; (err = pa_message_params_read_int64(start_pos, &(values[i]), &state1)) > 0; i++)
;
if (err < 0) {
@@ -468,10 +465,10 @@ int pa_message_params_read_int64_array(char *c, int64_t **results, int *length)
}
/* Converts a parameter list to an uint64 array. */
-int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length) {
+int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length, void **state) {
uint64_t *values;
- void *state = NULL;
- uint32_t element_count, i;
+ void *state1 = NULL;
+ int element_count, i;
int err;
char *start_pos;
@@ -493,8 +490,7 @@ int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length
/* Allocate array */
values = pa_xmalloc0(element_count * sizeof(uint64_t));
- state = NULL;
- for (i = 0; (err = pa_message_params_read_uint64(start_pos, &(values[i]), &state)) > 0; i++)
+ for (i = 0; (err = pa_message_params_read_uint64(start_pos, &(values[i]), &state1)) > 0; i++)
;
if (err < 0) {
diff --git a/src/pulse/message-params.h b/src/pulse/message-params.h
index 18addee96..3e53c1e84 100644
--- a/src/pulse/message-params.h
+++ b/src/pulse/message-params.h
@@ -77,7 +77,7 @@ int pa_message_params_read_double(char *c, double *result, void **state);
/** Converts a parameter list to a double array. Empty elements in the parameter
* list are treated as error. Returns allocated array in *results and array size in *length.
* The returned array must be freed with pa_xfree(). \since 15.0 */
-int pa_message_params_read_double_array(char *c, double **results, int *length);
+int pa_message_params_read_double_array(char *c, double **results, int *length, void **state);
/** Read an integer from parameter list in c. \since 15.0 */
int pa_message_params_read_int64(char *c, int64_t *result, void **state);
@@ -85,7 +85,7 @@ int pa_message_params_read_int64(char *c, int64_t *result, void **state);
/** Converts a parameter list to an int64 array. Empty elements in the parameter
* list are treated as error. Returns allocated array in *results and array size in *length.
* The returned array must be freed with pa_xfree(). \since 15.0 */
-int pa_message_params_read_int64_array(char *c, int64_t **results, int *length);
+int pa_message_params_read_int64_array(char *c, int64_t **results, int *length, void **state);
/** Read raw data from parameter list in c. Used to split a message parameter
* string into list elements. The string returned in *result must not be freed. \since 15.0 */
@@ -99,7 +99,7 @@ int pa_message_params_read_string(char *c, const char **result, void **state);
* the strings. Returns allocated array of pointers to sub-strings within c in
* *results and stores array size in *length. The returned array must be
* freed with pa_xfree(), but not the strings within the array. \since 15.0 */
-int pa_message_params_read_string_array(char *c, const char ***results, int *length);
+int pa_message_params_read_string_array(char *c, const char ***results, int *length, void **state);
/** Read an unsigned integer from parameter list in c. \since 15.0 */
int pa_message_params_read_uint64(char *c, uint64_t *result, void **state);
@@ -107,7 +107,7 @@ int pa_message_params_read_uint64(char *c, uint64_t *result, void **state);
/** Converts a parameter list to an uint64 array. Empty elements in the parameter
* list are treated as error. Returns allocated array in *results and array size in *length.
* The returned array must be freed with pa_xfree(). \since 15.0 */
-int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length);
+int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length, void **state);
/** @} */
From b76d835e590b8aa10d724c857ea7d135ba53d94e Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Sun, 27 Dec 2020 15:25:40 +0300
Subject: [PATCH 081/865] ladspa-sink: do not call pa_sink_suspend if
PA_SINK_IS_LINKED is false
While module-ladspa-sink is still being loaded and before pa_sink_put() is
called there may be an attempt to reconfigure master sink when avoid-resampling
is true. This breaks attempting to suspend ladspa-sink which is still in INIT
state.
Fix this by skipping pa_sink_suspend if PA_SINK_IS_LINKED is false.
Part-of:
---
src/modules/module-ladspa-sink.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index f4fed6d64..5726d0818 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -714,6 +714,9 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_sink_state_t old_state, p
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
+ if (!PA_SINK_IS_LINKED(u->sink->state))
+ return;
+
if (i->sink->state != PA_SINK_SUSPENDED || i->sink->suspend_cause == PA_SUSPEND_IDLE)
pa_sink_suspend(u->sink, false, PA_SUSPEND_UNAVAILABLE);
else
From 28f646fd87e824ba5e89377cd9aedde74e6be96c Mon Sep 17 00:00:00 2001
From: Arun Raghavan
Date: Fri, 25 Dec 2020 11:10:30 -0500
Subject: [PATCH 082/865] doc: Add some contributor guidelines
Document some things that should be helpful to at least new
contributors. Since we don't have a way to show this when people are
creating MRs, also copy over the next to a merge request template so
that creates a dropdown that folks might look at when creating an MR.
Part-of:
---
.gitlab/merge_request_templates/Guidelines.md | 67 +++++++++++++++++++
CONTRIBUTING.md | 67 +++++++++++++++++++
2 files changed, 134 insertions(+)
create mode 100644 .gitlab/merge_request_templates/Guidelines.md
create mode 100644 CONTRIBUTING.md
diff --git a/.gitlab/merge_request_templates/Guidelines.md b/.gitlab/merge_request_templates/Guidelines.md
new file mode 100644
index 000000000..2fb9a626b
--- /dev/null
+++ b/.gitlab/merge_request_templates/Guidelines.md
@@ -0,0 +1,67 @@
+# Contribution Guidelines
+
+## A note for contributors
+
+Thank you for your contribution!
+
+Please make sure you tick the box labelled `Allow commits from members who can
+merge to the target branch`. This allows us to make minor edits ourselves, and
+then automatically rebase and merge your changes.
+
+PulseAudio is currently maintained by three volunteer developers in their free
+time (probably amounting to less than one full time developer), which is not
+really enough, given the project size and scope. For this reason bug reports
+and patch submissions are sometimes handled very slowly.
+
+For non-trivial patches, we meet biweekly on IRC to discuss and prioritise
+outstanding MRs. If you haven't heard from us a few days after you create the
+MR, please take a look at [the patch status
+page](https://www.freedesktop.org/wiki/Software/PulseAudio/PatchStatus/).
+
+If you don't see your MR in that list either, please don't hesitate to drop a
+comment pinging us, and we'll try to at least respond and make sure your
+request is tracked on that list.
+
+## Coding Style
+
+Please take a look at the [coding style
+documentation](https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/CodingStyle/)
+on our wiki.
+
+## Commit messagse
+
+We follow the standard git commit message format of a summary on the first line
+(<=50 characterss for preference, <=72 characters otherwise), followed by a new
+line, followed by a detailed commit message. An additional line at the end may
+link to an issue being fixed by this MR.
+
+The first line is usually a short description of "what" your commit does, and
+the rest of the message describes the "why", along with any additional
+information that readers might need to understand the rationale for the change.
+If in doubt, more verbose is better than less.
+
+If you need to describe the "how" of the commit, that is usually best
+documented along with the code itself.
+
+Commit messages are prefixed with the subsystem being affected. Your best bet
+to figure out what is appropriate is to look at previous commit messages. An
+example:
+
+```
+sink: Reduce chat sink priority
+
+Some gaming sound cards have custom profiles with analog-game and
+analog-chat mappings that exist simultaneously. The game sink should
+have higher priority than the chat sink, but currently there's no way to
+affect the sink priorities from the profile-set configuration (the
+mapping priority is not propagated to the sink priority).
+
+I first thought about adding the mapping priority to the sink priority,
+but that could mess up the prioritization system in
+pa_device_init_priority(). I ended up checking for the intended roles
+property to reduce the chat sink priority. I also reduced the iec958
+priority so that the chat and iec958 sinks don't end up with the same
+priority.
+
+Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/818
+```
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..2fb9a626b
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,67 @@
+# Contribution Guidelines
+
+## A note for contributors
+
+Thank you for your contribution!
+
+Please make sure you tick the box labelled `Allow commits from members who can
+merge to the target branch`. This allows us to make minor edits ourselves, and
+then automatically rebase and merge your changes.
+
+PulseAudio is currently maintained by three volunteer developers in their free
+time (probably amounting to less than one full time developer), which is not
+really enough, given the project size and scope. For this reason bug reports
+and patch submissions are sometimes handled very slowly.
+
+For non-trivial patches, we meet biweekly on IRC to discuss and prioritise
+outstanding MRs. If you haven't heard from us a few days after you create the
+MR, please take a look at [the patch status
+page](https://www.freedesktop.org/wiki/Software/PulseAudio/PatchStatus/).
+
+If you don't see your MR in that list either, please don't hesitate to drop a
+comment pinging us, and we'll try to at least respond and make sure your
+request is tracked on that list.
+
+## Coding Style
+
+Please take a look at the [coding style
+documentation](https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/CodingStyle/)
+on our wiki.
+
+## Commit messagse
+
+We follow the standard git commit message format of a summary on the first line
+(<=50 characterss for preference, <=72 characters otherwise), followed by a new
+line, followed by a detailed commit message. An additional line at the end may
+link to an issue being fixed by this MR.
+
+The first line is usually a short description of "what" your commit does, and
+the rest of the message describes the "why", along with any additional
+information that readers might need to understand the rationale for the change.
+If in doubt, more verbose is better than less.
+
+If you need to describe the "how" of the commit, that is usually best
+documented along with the code itself.
+
+Commit messages are prefixed with the subsystem being affected. Your best bet
+to figure out what is appropriate is to look at previous commit messages. An
+example:
+
+```
+sink: Reduce chat sink priority
+
+Some gaming sound cards have custom profiles with analog-game and
+analog-chat mappings that exist simultaneously. The game sink should
+have higher priority than the chat sink, but currently there's no way to
+affect the sink priorities from the profile-set configuration (the
+mapping priority is not propagated to the sink priority).
+
+I first thought about adding the mapping priority to the sink priority,
+but that could mess up the prioritization system in
+pa_device_init_priority(). I ended up checking for the intended roles
+property to reduce the chat sink priority. I also reduced the iec958
+priority so that the chat and iec958 sinks don't end up with the same
+priority.
+
+Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/818
+```
From b6396dbe9c97be844553dd748e7cf9c3efdc0086 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Tue, 22 Dec 2020 20:54:40 +0300
Subject: [PATCH 083/865] x11: gracefully handle X11 connection error
Perform X11 connection recovery via XSetIOErrorExitHandler mechanism.
Implementation is largely inspired by this change to GNOME/mutter
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1447
Part-of:
---
configure.ac | 12 ++++-
meson.build | 4 ++
src/modules/x11/module-x11-bell.c | 2 +
src/modules/x11/module-x11-cork-request.c | 2 +
src/modules/x11/module-x11-publish.c | 2 +
src/modules/x11/module-x11-xsmp.c | 2 +-
src/pulsecore/x11wrap.c | 59 ++++++++++++++++++++++-
src/pulsecore/x11wrap.h | 4 +-
8 files changed, 81 insertions(+), 6 deletions(-)
diff --git a/configure.ac b/configure.ac
index 357b6d7f7..bd3a53367 100644
--- a/configure.ac
+++ b/configure.ac
@@ -633,8 +633,14 @@ AC_ARG_ENABLE([x11],
AS_HELP_STRING([--disable-x11],[Disable optional X11 support]))
AS_IF([test "x$enable_x11" != "xno"],
- [PKG_CHECK_MODULES(X11, [ x11-xcb xcb >= 1.6 ice sm xtst ], HAVE_X11=1, HAVE_X11=0)],
- HAVE_X11=0)
+ [
+ PKG_CHECK_MODULES(X11, [ x11-xcb xcb >= 1.6 ice sm xtst ], HAVE_X11=1, HAVE_X11=0)
+ AC_CHECK_LIB(X11, XSetIOErrorExitHandler, [HAVE_XSETIOERROREXITHANDLER=yes], [HAVE_XSETIOERROREXITHANDLER=no])
+ ],
+ [
+ HAVE_X11=0
+ HAVE_XSETIOERROREXITHANDLER=no
+ ])
AS_IF([test "x$enable_x11" = "xyes" && test "x$HAVE_X11" = "x0"],
[AC_MSG_ERROR([*** X11 not found])])
@@ -642,6 +648,7 @@ AS_IF([test "x$enable_x11" = "xyes" && test "x$HAVE_X11" = "x0"],
AC_SUBST(HAVE_X11)
AM_CONDITIONAL([HAVE_X11], [test "x$HAVE_X11" = x1])
AS_IF([test "x$HAVE_X11" = "x1"], AC_DEFINE([HAVE_X11], 1, [Have X11?]))
+AS_IF([test "x$HAVE_XSETIOERROREXITHANDLER" = "xyes"], AC_DEFINE([HAVE_XSETIOERROREXITHANDLER], 1, [Have XSetIOErrorExitHandler function.]))
#### Capabilities (optional) ####
@@ -1687,6 +1694,7 @@ echo "
Enable memfd shared memory: ${ENABLE_MEMFD}
Enable X11: ${ENABLE_X11}
+ Safe X11 I/O errors: ${HAVE_XSETIOERROREXITHANDLER}
Enable OSS Output: ${ENABLE_OSS_OUTPUT}
Enable OSS Wrapper: ${ENABLE_OSS_WRAPPER}
Enable EsounD: ${ENABLE_ESOUND}
diff --git a/meson.build b/meson.build
index cd617c602..23e04ef46 100644
--- a/meson.build
+++ b/meson.build
@@ -650,6 +650,9 @@ if x11_dep.found()
sm_dep = dependency('sm', required : true)
xtst_dep = dependency('xtst', required : true)
cdata.set('HAVE_X11', 1)
+ if cc.has_function('XSetIOErrorExitHandler', dependencies: x11_dep)
+ cdata.set('HAVE_XSETIOERROREXITHANDLER', 1)
+ endif
endif
# Module dependencies
@@ -847,6 +850,7 @@ summary = [
'',
'Enable memfd shared memory: @0@'.format(cdata.has('HAVE_MEMFD')),
'Enable X11: @0@'.format(x11_dep.found()),
+ ' Safe X11 I/O errors: @0@'.format(cdata.has('HAVE_XSETIOERROREXITHANDLER')),
'Enable OSS Output: @0@'.format(cdata.has('HAVE_OSS_OUTPUT')),
'Enable OSS Wrapper: @0@'.format(cdata.has('HAVE_OSS_WRAPPER')),
# 'Enable EsounD: @0@'.format(${ENABLE_ESOUND}),
diff --git a/src/modules/x11/module-x11-bell.c b/src/modules/x11/module-x11-bell.c
index eab1e6c3a..058a93316 100644
--- a/src/modules/x11/module-x11-bell.c
+++ b/src/modules/x11/module-x11-bell.c
@@ -93,6 +93,8 @@ static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
pa_assert(u);
pa_assert(u->x11_wrapper == w);
+ pa_log_debug("X11 client kill callback called");
+
if (u->x11_client)
pa_x11_client_free(u->x11_client);
diff --git a/src/modules/x11/module-x11-cork-request.c b/src/modules/x11/module-x11-cork-request.c
index 6b1a86b9f..b9378f568 100644
--- a/src/modules/x11/module-x11-cork-request.c
+++ b/src/modules/x11/module-x11-cork-request.c
@@ -66,6 +66,8 @@ static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
pa_assert(u);
pa_assert(u->x11_wrapper == w);
+ pa_log_debug("X11 client kill callback called");
+
if (u->x11_client) {
pa_x11_client_free(u->x11_client);
u->x11_client = NULL;
diff --git a/src/modules/x11/module-x11-publish.c b/src/modules/x11/module-x11-publish.c
index 68adf1574..da619f83c 100644
--- a/src/modules/x11/module-x11-publish.c
+++ b/src/modules/x11/module-x11-publish.c
@@ -116,6 +116,8 @@ static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
pa_assert(u);
pa_assert(u->x11_wrapper == w);
+ pa_log_debug("X11 client kill callback called");
+
if (u->x11_client)
pa_x11_client_free(u->x11_client);
diff --git a/src/modules/x11/module-x11-xsmp.c b/src/modules/x11/module-x11-xsmp.c
index 459da1302..eb18b4312 100644
--- a/src/modules/x11/module-x11-xsmp.c
+++ b/src/modules/x11/module-x11-xsmp.c
@@ -64,7 +64,7 @@ static void die_cb(SmcConn connection, SmPointer client_data) {
pa_log_debug("Got die message from XSMP.");
- pa_x11_wrapper_kill(u->x11);
+ pa_x11_wrapper_kill_deferred(u->x11);
pa_x11_wrapper_unref(u->x11);
u->x11 = NULL;
diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c
index 0c040cf57..635d21e00 100644
--- a/src/pulsecore/x11wrap.c
+++ b/src/pulsecore/x11wrap.c
@@ -33,6 +33,8 @@
#include "x11wrap.h"
+#include
+
typedef struct pa_x11_internal pa_x11_internal;
struct pa_x11_internal {
@@ -51,6 +53,7 @@ struct pa_x11_wrapper {
pa_defer_event* defer_event;
pa_io_event* io_event;
+ pa_defer_event* cleanup_event;
PA_LLIST_HEAD(pa_x11_client, clients);
PA_LLIST_HEAD(pa_x11_internal, internals);
@@ -64,6 +67,8 @@ struct pa_x11_client {
void *userdata;
};
+static void x11_wrapper_kill(pa_x11_wrapper *w);
+
/* Dispatch all pending X11 events */
static void work(pa_x11_wrapper *w) {
pa_assert(w);
@@ -167,6 +172,38 @@ static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening,
x11_internal_remove(w, (pa_x11_internal*) *watch_data);
}
+static int x11_error_handler(Display* display, XErrorEvent* error_event) {
+ pa_log_warn("X11 error handler called");
+ return 0;
+}
+
+static int x11_io_error_handler(Display* display) {
+ pa_log_warn("X11 I/O error handler called");
+ return 0;
+}
+
+static void deferred_x11_teardown(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+ pa_x11_wrapper *w = userdata;
+
+ m->defer_enable(e, 0);
+
+ pa_log_debug("Start tearing down X11 modules after X11 I/O error");
+
+ x11_wrapper_kill(w);
+
+ pa_log_debug("Done tearing down X11 modules after X11 I/O error");
+}
+
+#ifdef HAVE_XSETIOERROREXITHANDLER
+static void x11_io_error_exit_handler(Display* display, void *userdata) {
+ pa_x11_wrapper *w = userdata;
+
+ pa_log_warn("X11 I/O error exit handler called, preparing to tear down X11 modules");
+
+ pa_x11_wrapper_kill_deferred(w);
+}
+#endif
+
static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
pa_x11_wrapper*w;
Display *d;
@@ -187,11 +224,20 @@ static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char
w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w);
w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
+ w->cleanup_event = c->mainloop->defer_new(c->mainloop, deferred_x11_teardown, w);
+ w->core->mainloop->defer_enable(w->cleanup_event, 0);
+ XSetErrorHandler(x11_error_handler);
+ XSetIOErrorHandler(x11_io_error_handler);
+#ifdef HAVE_XSETIOERROREXITHANDLER
+ XSetIOErrorExitHandler(d, x11_io_error_exit_handler, w);
+#endif
XAddConnectionWatch(d, x11_watch, (XPointer) w);
pa_assert_se(pa_shared_set(c, w->property_name, w) >= 0);
+ pa_log_debug("Created X11 connection wrapper '%s'", w->property_name);
+
return w;
}
@@ -202,9 +248,12 @@ static void x11_wrapper_free(pa_x11_wrapper*w) {
pa_assert(!w->clients);
+ pa_log_debug("Destroying X11 connection wrapper '%s'", w->property_name);
+
XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
XCloseDisplay(w->display);
+ w->core->mainloop->defer_free(w->cleanup_event);
w->core->mainloop->io_free(w->io_event);
w->core->mainloop->defer_free(w->defer_event);
@@ -261,7 +310,15 @@ xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w) {
return XGetXCBConnection(pa_x11_wrapper_get_display(w));
}
-void pa_x11_wrapper_kill(pa_x11_wrapper *w) {
+void pa_x11_wrapper_kill_deferred(pa_x11_wrapper *w) {
+ pa_assert(w);
+
+ /* schedule X11 display teardown */
+ w->core->mainloop->defer_enable(w->cleanup_event, 1);
+}
+
+/* Kill the connection to the X11 display */
+static void x11_wrapper_kill(pa_x11_wrapper *w) {
pa_x11_client *c, *n;
pa_assert(w);
diff --git a/src/pulsecore/x11wrap.h b/src/pulsecore/x11wrap.h
index 0539303c2..2d81705c2 100644
--- a/src/pulsecore/x11wrap.h
+++ b/src/pulsecore/x11wrap.h
@@ -48,8 +48,8 @@ Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w);
/* Return the XCB connection object for this connection */
xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w);
-/* Kill the connection to the X11 display */
-void pa_x11_wrapper_kill(pa_x11_wrapper *w);
+/* Initiate X11 connection teardown. */
+void pa_x11_wrapper_kill_deferred(pa_x11_wrapper *w);
/* Register an X11 client, that is called for each X11 event */
pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata);
From 6bfd9c809e1a679043c6c4bfdd1e117ed7b5fc2a Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Wed, 23 Dec 2020 10:31:18 +0300
Subject: [PATCH 084/865] x11: gracefully handle ICE connection error
Implementation is largely inspired by GNOME/mutter ICE session handling.
Part-of:
---
src/modules/x11/module-x11-xsmp.c | 82 ++++++++++++++++++++++++++-----
1 file changed, 69 insertions(+), 13 deletions(-)
diff --git a/src/modules/x11/module-x11-xsmp.c b/src/modules/x11/module-x11-xsmp.c
index eb18b4312..1ff6cc770 100644
--- a/src/modules/x11/module-x11-xsmp.c
+++ b/src/modules/x11/module-x11-xsmp.c
@@ -55,21 +55,50 @@ struct userdata {
pa_module *module;
pa_client *client;
SmcConn connection;
- pa_x11_wrapper *x11;
+
+ pa_x11_wrapper *x11_wrapper;
+ pa_x11_client *x11_client;
};
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(w);
+ pa_assert(u);
+ pa_assert(u->x11_wrapper == w);
+
+ pa_log_debug("X11 client kill callback called");
+
+ if (u->connection) {
+ SmcCloseConnection(u->connection, 0, NULL);
+ u->connection = NULL;
+ }
+
+ if (u->x11_client) {
+ pa_x11_client_free(u->x11_client);
+ u->x11_client = NULL;
+ }
+
+ if (u->x11_wrapper) {
+ pa_x11_wrapper_unref(u->x11_wrapper);
+ u->x11_wrapper = NULL;
+ }
+
+ pa_module_unload_request(u->module, true);
+}
+
static void die_cb(SmcConn connection, SmPointer client_data) {
struct userdata *u = client_data;
pa_assert(u);
pa_log_debug("Got die message from XSMP.");
- pa_x11_wrapper_kill_deferred(u->x11);
+ if (u->connection) {
+ SmcCloseConnection(u->connection, 0, NULL);
+ u->connection = NULL;
+ }
- pa_x11_wrapper_unref(u->x11);
- u->x11 = NULL;
-
- pa_module_unload_request(u->module, true);
+ pa_x11_wrapper_kill_deferred(u->x11_wrapper);
}
static void save_complete_cb(SmcConn connection, SmPointer client_data) {
@@ -87,6 +116,7 @@ static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_fla
IceConn connection = userdata;
if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) {
+ pa_log_debug("IceProcessMessages: I/O error, closing ICE connection");
IceSetShutdownNegotiation(connection, False);
IceCloseConnection(connection);
}
@@ -106,6 +136,17 @@ static void new_ice_connection(IceConn connection, IcePointer client_data, Bool
c->mainloop->io_free(*watch_data);
}
+static IceIOErrorHandler ice_installed_handler;
+
+/* We call any handler installed before (or after) module is loaded but
+ avoid calling the default libICE handler which does an exit() */
+
+static void ice_io_error_handler(IceConn iceConn) {
+ pa_log_warn("ICE I/O error handler called");
+ if (ice_installed_handler)
+ (*ice_installed_handler) (iceConn);
+}
+
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
@@ -123,17 +164,27 @@ int pa__init(pa_module*m) {
if (ice_in_use) {
pa_log("module-x11-xsmp may not be loaded twice.");
return -1;
- }
+ } else {
+ IceIOErrorHandler default_handler;
- IceAddConnectionWatch(new_ice_connection, m->core);
- ice_in_use = true;
+ ice_installed_handler = IceSetIOErrorHandler (NULL);
+ default_handler = IceSetIOErrorHandler (ice_io_error_handler);
+
+ if (ice_installed_handler == default_handler)
+ ice_installed_handler = NULL;
+
+ IceSetIOErrorHandler(ice_io_error_handler);
+
+ IceAddConnectionWatch(new_ice_connection, m->core);
+ ice_in_use = true;
+ }
m->userdata = u = pa_xnew(struct userdata, 1);
u->core = m->core;
u->module = m;
u->client = NULL;
u->connection = NULL;
- u->x11 = NULL;
+ u->x11_wrapper = NULL;
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@@ -147,9 +198,11 @@ int pa__init(pa_module*m) {
}
}
- if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+ if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
goto fail;
+ u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u);
+
e = pa_modargs_get_value(ma, "session_manager", NULL);
if (!e && !getenv("SESSION_MANAGER")) {
@@ -253,8 +306,11 @@ void pa__done(pa_module*m) {
if (u->client)
pa_client_free(u->client);
- if (u->x11)
- pa_x11_wrapper_unref(u->x11);
+ if (u->x11_client)
+ pa_x11_client_free(u->x11_client);
+
+ if (u->x11_wrapper)
+ pa_x11_wrapper_unref(u->x11_wrapper);
pa_xfree(u);
}
From f18f55bb5ee4391370e59528ff0aa93068299252 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Sun, 27 Dec 2020 12:40:18 +0300
Subject: [PATCH 085/865] daemon: fix default daemon.conf when running from
build tree
Part-of:
---
src/daemon/daemon-conf.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index bcf7329f1..ffef554be 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -665,9 +665,21 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
pa_xfree(c->config_file);
c->config_file = NULL;
+ const char *default_config_file = DEFAULT_CONFIG_FILE;
+#ifdef HAVE_RUNNING_FROM_BUILD_TREE
+ if (pa_run_from_build_tree()) {
+ pa_log_notice("Detected that we are run from the build tree, fixing default daemon.conf file path.");
+#ifdef MESON_BUILD
+ default_config_file = PA_BUILDDIR PA_PATH_SEP "src" PA_PATH_SEP "daemon" PA_PATH_SEP "daemon.conf";
+#else
+ default_config_file = PA_BUILDDIR PA_PATH_SEP "daemon.conf";
+#endif // Endof #ifdef MESON_BUILD
+ }
+#endif // Endof #ifdef HAVE_RUNNING_FROM_BUILD_TREE
+
f = filename ?
pa_fopen_cloexec(c->config_file = pa_xstrdup(filename), "r") :
- pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
+ pa_open_config_file(default_config_file, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
if (!f && errno != ENOENT) {
pa_log_warn(_("Failed to open configuration file: %s"), pa_cstrerror(errno));
From 0efae0488ccd4536926f5fa2b4cf524e63da9095 Mon Sep 17 00:00:00 2001
From: Georg Chini
Date: Mon, 4 Jan 2021 21:08:05 +0100
Subject: [PATCH 086/865] loopback: Fix sink latency calculation if source is
alsa source with fixed latency
When an alsa source with fixed latency is used, the actual latency of the source
will only be one fragment size. This is not taken into account when the required
sink latency is calculated.
This patch fixes the issue.
Part-of:
---
src/modules/module-loopback.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 871f01166..4f8ceec46 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -1016,6 +1016,14 @@ static void set_sink_input_latency(struct userdata *u, pa_sink *sink) {
if (u->min_source_latency > requested_latency) {
latency = PA_MAX(u->latency, u->minimum_latency);
requested_latency = (latency - u->min_source_latency) / 2;
+ /* In the case of a fixed alsa source, u->minimum_latency is calculated from
+ * the default fragment size while u->min_source_latency is the reported minimum
+ * of the source latency (nr_of_fragments * fragment_size). This can lead to a
+ * situation where u->minimum_latency < u->min_source_latency. We only fall
+ * back to use the fragment size instead of min_source_latency if the calculation
+ * above does not deliver a usable result. */
+ if (u->fixed_alsa_source && u->min_source_latency >= latency)
+ requested_latency = (latency - u->core->default_fragment_size_msec * PA_USEC_PER_MSEC) / 2;
}
latency = PA_CLAMP(requested_latency , u->min_sink_latency, u->max_sink_latency);
From 0ac6b16787b201025498063d3b48db4a86f48d2b Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Sun, 29 Nov 2020 11:21:29 +0300
Subject: [PATCH 087/865] database: extract common method to handle machine id
and architecture
Part-of:
---
src/Makefile.am | 2 +-
src/modules/module-card-restore.c | 12 ++---
src/modules/module-device-manager.c | 12 ++---
src/modules/module-device-restore.c | 12 ++---
src/modules/module-equalizer-sink.c | 24 +++++-----
src/modules/module-stream-restore.c | 12 ++---
src/pulsecore/database-gdbm.c | 25 ++++++----
src/pulsecore/database-simple.c | 17 ++++---
src/pulsecore/database-tdb.c | 17 ++++---
src/pulsecore/database.c | 72 +++++++++++++++++++++++++++++
src/pulsecore/database.h | 13 +++++-
src/pulsecore/meson.build | 1 +
12 files changed, 154 insertions(+), 65 deletions(-)
create mode 100644 src/pulsecore/database.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 97c9dce9f..14c59566f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1042,7 +1042,7 @@ libpulsecore_@PA_MAJORMINOR@_la_SOURCES = \
pulsecore/source.c pulsecore/source.h \
pulsecore/start-child.c pulsecore/start-child.h \
pulsecore/thread-mq.c pulsecore/thread-mq.h \
- pulsecore/database.h
+ pulsecore/database.c pulsecore/database.h
libpulsecore_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(LIBSNDFILE_CFLAGS) $(WINSOCK_CFLAGS)
libpulsecore_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index 80506cd04..b35cf3e7e 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -618,7 +618,7 @@ static pa_hook_result_t card_preferred_port_changed_callback(pa_core *core, pa_c
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
- char *fname;
+ char *state_path;
bool restore_bluetooth_profile;
pa_assert(m);
@@ -648,17 +648,15 @@ int pa__init(pa_module*m) {
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_added_callback, u);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) port_offset_change_callback, u);
- if (!(fname = pa_state_path("card-database", true)))
+ if (!(state_path = pa_state_path(NULL, true)))
goto fail;
- if (!(u->database = pa_database_open(fname, true))) {
- pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
- pa_xfree(fname);
+ if (!(u->database = pa_database_open(state_path, "card-database", true, true))) {
+ pa_xfree(state_path);
goto fail;
}
- pa_log_info("Successfully opened database file '%s'.", fname);
- pa_xfree(fname);
+ pa_xfree(state_path);
pa_modargs_free(ma);
return 0;
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 308ef0b57..b51b6c8a0 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -1544,7 +1544,7 @@ struct prioritised_indexes {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
- char *fname;
+ char *state_path;
pa_sink *sink;
pa_source *source;
uint32_t idx;
@@ -1601,17 +1601,15 @@ int pa__init(pa_module*m) {
u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+5, (pa_hook_cb_t) source_unlink_hook_callback, u);
}
- if (!(fname = pa_state_path("device-manager", true)))
+ if (!(state_path = pa_state_path(NULL, true)))
goto fail;
- if (!(u->database = pa_database_open(fname, true))) {
- pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
- pa_xfree(fname);
+ if (!(u->database = pa_database_open(state_path, "device-manager", true, true))) {
+ pa_xfree(state_path);
goto fail;
}
- pa_log_info("Successfully opened database file '%s'.", fname);
- pa_xfree(fname);
+ pa_xfree(state_path);
/* Attempt to inject the devices into the list in priority order */
total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 684c8360d..9ca2a7cd5 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -1202,7 +1202,7 @@ static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_nati
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
- char *fname;
+ char *state_path;
pa_sink *sink;
pa_source *source;
uint32_t idx;
@@ -1259,17 +1259,15 @@ int pa__init(pa_module*m) {
if (restore_formats)
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) sink_put_hook_callback, u);
- if (!(fname = pa_state_path("device-volumes", true)))
+ if (!(state_path = pa_state_path(NULL, true)))
goto fail;
- if (!(u->database = pa_database_open(fname, true))) {
- pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
- pa_xfree(fname);
+ if (!(u->database = pa_database_open(state_path, "device-volumes", true, true))) {
+ pa_xfree(state_path);
goto fail;
}
- pa_log_info("Successfully opened database file '%s'.", fname);
- pa_xfree(fname);
+ pa_xfree(state_path);
PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 4cfe3ed48..f8a9e9514 100644
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -946,7 +946,7 @@ static void save_state(struct userdata *u) {
float *H;
pa_datum key, data;
pa_database *database;
- char *dbname;
+ char *state_path;
char *packed;
size_t packed_length;
@@ -969,9 +969,9 @@ static void save_state(struct userdata *u) {
data.data = state;
data.size = filter_state_size + packed_length;
//thread safety for 0.9.17?
- pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, false));
- pa_assert_se(database = pa_database_open(dbname, true));
- pa_xfree(dbname);
+ pa_assert_se(state_path = pa_state_path(NULL, false));
+ pa_assert_se(database = pa_database_open(state_path, EQ_STATE_DB, false, true));
+ pa_xfree(state_path);
pa_database_set(database, &key, &data, true);
pa_database_sync(database);
@@ -1020,10 +1020,10 @@ static void load_state(struct userdata *u) {
float *H;
pa_datum key, value;
pa_database *database;
- char *dbname;
- pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, false));
- database = pa_database_open(dbname, false);
- pa_xfree(dbname);
+ char *state_path;
+ pa_assert_se(state_path = pa_state_path(NULL, false));
+ database = pa_database_open(state_path, EQ_STATE_DB, false, false);
+ pa_xfree(state_path);
if (!database) {
pa_log("No resume state");
return;
@@ -1626,12 +1626,12 @@ void dbus_init(struct userdata *u) {
sink_list = pa_shared_get(u->sink->core, SINKLIST);
u->database = pa_shared_get(u->sink->core, EQDB);
if (sink_list == NULL) {
- char *dbname;
+ char *state_path;
sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func);
pa_shared_set(u->sink->core, SINKLIST, sink_list);
- pa_assert_se(dbname = pa_state_path("equalizer-presets", false));
- pa_assert_se(u->database = pa_database_open(dbname, true));
- pa_xfree(dbname);
+ pa_assert_se(state_path = pa_state_path(NULL, false));
+ pa_assert_se(u->database = pa_database_open(state_path, "equalizer-presets", false, true));
+ pa_xfree(state_path);
pa_shared_set(u->sink->core, EQDB, u->database);
pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core);
pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index d26543bde..315360740 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -2266,7 +2266,7 @@ static void clean_up_db(struct userdata *u) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
- char *fname;
+ char *state_path;
pa_sink_input *si;
pa_source_output *so;
uint32_t idx;
@@ -2324,17 +2324,15 @@ int pa__init(pa_module*m) {
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_fixate_hook_callback, u);
}
- if (!(fname = pa_state_path("stream-volumes", true)))
+ if (!(state_path = pa_state_path(NULL, true)))
goto fail;
- if (!(u->database = pa_database_open(fname, true))) {
- pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
- pa_xfree(fname);
+ if (!(u->database = pa_database_open(state_path, "stream-volumes", true, true))) {
+ pa_xfree(state_path);
goto fail;
}
- pa_log_info("Successfully opened database file '%s'.", fname);
- pa_xfree(fname);
+ pa_xfree(state_path);
clean_up_db(u);
diff --git a/src/pulsecore/database-gdbm.c b/src/pulsecore/database-gdbm.c
index b1da9df9f..cd03405f5 100644
--- a/src/pulsecore/database-gdbm.c
+++ b/src/pulsecore/database-gdbm.c
@@ -59,17 +59,24 @@ void pa_datum_free(pa_datum *d) {
pa_zero(d);
}
-pa_database* pa_database_open(const char *fn, bool for_write) {
- GDBM_FILE f;
- int gdbm_cache_size;
- char *path;
-
- pa_assert(fn);
-
+const char* pa_database_get_arch_suffix(void) {
/* We include the host identifier in the file name because gdbm
* files are CPU dependent, and we don't want things to go wrong
* if we are on a multiarch system. */
- path = pa_sprintf_malloc("%s."CANONICAL_HOST".gdbm", fn);
+
+ return CANONICAL_HOST;
+}
+
+const char* pa_database_get_filename_suffix(void) {
+ return ".gdbm";
+}
+
+pa_database* pa_database_open_internal(const char *path, bool for_write) {
+ GDBM_FILE f;
+ int gdbm_cache_size;
+
+ pa_assert(path);
+
errno = 0;
/* We need to set the block size explicitly here, since otherwise
@@ -80,8 +87,6 @@ pa_database* pa_database_open(const char *fn, bool for_write) {
if (f)
pa_log_debug("Opened GDBM database '%s'", path);
- pa_xfree(path);
-
if (!f) {
if (errno == 0)
errno = EIO;
diff --git a/src/pulsecore/database-simple.c b/src/pulsecore/database-simple.c
index 387648769..ebfbe4c61 100644
--- a/src/pulsecore/database-simple.c
+++ b/src/pulsecore/database-simple.c
@@ -222,14 +222,21 @@ static int fill_data(simple_data *db, FILE *f) {
return pa_hashmap_size(db->map);
}
-pa_database* pa_database_open(const char *fn, bool for_write) {
+const char* pa_database_get_arch_suffix(void) {
+ /* Simple database binary file format is CPU dependent. */
+ return CANONICAL_HOST;
+}
+
+const char* pa_database_get_filename_suffix(void) {
+ return ".simple";
+}
+
+pa_database* pa_database_open_internal(const char *path, bool for_write) {
FILE *f;
- char *path;
simple_data *db;
- pa_assert(fn);
+ pa_assert(path);
- path = pa_sprintf_malloc("%s."CANONICAL_HOST".simple", fn);
errno = 0;
f = pa_fopen_cloexec(path, "r");
@@ -251,8 +258,6 @@ pa_database* pa_database_open(const char *fn, bool for_write) {
db = NULL;
}
- pa_xfree(path);
-
return (pa_database*) db;
}
diff --git a/src/pulsecore/database-tdb.c b/src/pulsecore/database-tdb.c
index 282f58061..5e7315901 100644
--- a/src/pulsecore/database-tdb.c
+++ b/src/pulsecore/database-tdb.c
@@ -97,18 +97,23 @@ finish:
return c;
}
-pa_database* pa_database_open(const char *fn, bool for_write) {
+const char* pa_database_get_arch_suffix(void) {
+ /* TDB binary file format is not dependent on system architecture */
+ return NULL;
+}
+
+const char* pa_database_get_filename_suffix(void) {
+ return ".tdb";
+}
+
+pa_database* pa_database_open_internal(const char *path, bool for_write) {
struct tdb_context *c;
- char *path;
- pa_assert(fn);
+ pa_assert(path);
- path = pa_sprintf_malloc("%s.tdb", fn);
if ((c = tdb_open_cloexec(path, 0, TDB_NOSYNC|TDB_NOLOCK, (for_write ? O_RDWR|O_CREAT : O_RDONLY), 0644)))
pa_log_debug("Opened TDB database '%s'", path);
- pa_xfree(path);
-
if (!c) {
if (errno == 0)
errno = EIO;
diff --git a/src/pulsecore/database.c b/src/pulsecore/database.c
new file mode 100644
index 000000000..11f3d03c9
--- /dev/null
+++ b/src/pulsecore/database.c
@@ -0,0 +1,72 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2020 Igor V. Kovalenko
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see .
+***/
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+
+#include
+#include
+#include
+
+#include "database.h"
+#include "core-error.h"
+
+pa_database* pa_database_open(const char *path, const char *fn, bool prependmid, bool for_write) {
+
+ const char *arch_suffix = pa_database_get_arch_suffix();
+ const char *filename_suffix = pa_database_get_filename_suffix();
+
+ char *machine_id = NULL, *filename_prefix, *full_path;
+
+ pa_database *f;
+
+ pa_assert(!arch_suffix || arch_suffix[0]);
+ pa_assert(filename_suffix && filename_suffix[0]);
+
+ if (prependmid && !(machine_id = pa_machine_id())) {
+ return NULL;
+ }
+
+ /* We include the host identifier in the file name because some database files are
+ * CPU dependent, and we don't want things to go wrong if we are on a multiarch system. */
+ filename_prefix = pa_sprintf_malloc("%s%s%s%s%s",
+ machine_id?:"", machine_id?"-":"",
+ fn,
+ arch_suffix?".":"", arch_suffix?:"");
+
+ full_path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s%s", path, filename_prefix, filename_suffix);
+
+ f = pa_database_open_internal(full_path, for_write);
+
+ if (f)
+ pa_log_info("Successfully opened '%s' database file '%s'.", fn, full_path);
+ else
+ pa_log("Failed to open '%s' database file '%s': %s", fn, full_path, pa_cstrerror(errno));
+
+ pa_xfree(full_path);
+ pa_xfree(filename_prefix);
+
+ /* deallocate machine_id if it was used to construct file name */
+ pa_xfree(machine_id);
+
+ return f;
+}
diff --git a/src/pulsecore/database.h b/src/pulsecore/database.h
index 3a1c7ceaf..fe2890577 100644
--- a/src/pulsecore/database.h
+++ b/src/pulsecore/database.h
@@ -38,8 +38,17 @@ typedef struct pa_datum {
void pa_datum_free(pa_datum *d);
-/* This will append a suffix to the filename */
-pa_database* pa_database_open(const char *fn, bool for_write);
+/* Database implementation; returns non-empty system architecture name string if database file format depends on system architecture, or NULL otherwise. */
+const char* pa_database_get_arch_suffix(void);
+/* Database implementation; returns non-empty database filename extension string */
+const char* pa_database_get_filename_suffix(void);
+
+/* This will attempt opening database file matching compiled CANONICAL_HOST identifier.
+ * If prependmid is true, file name is augmented with machine id prefix. */
+pa_database* pa_database_open(const char *path, const char *fn, bool prependmid, bool for_write);
+
+/* Database implementation; opens specified database file using provided path. */
+pa_database* pa_database_open_internal(const char *path, bool for_write);
void pa_database_close(pa_database *db);
pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data);
diff --git a/src/pulsecore/meson.build b/src/pulsecore/meson.build
index e8ce2931c..608273a17 100644
--- a/src/pulsecore/meson.build
+++ b/src/pulsecore/meson.build
@@ -14,6 +14,7 @@ libpulsecore_sources = [
'cpu-orc.c',
'cpu-x86.c',
'device-port.c',
+ 'database.c',
'ffmpeg/resample2.c',
'filter/biquad.c',
'filter/crossover.c',
From ae9d0cf307e731a02cdde38fb1632b20322f9f68 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Sun, 29 Nov 2020 15:16:26 +0300
Subject: [PATCH 088/865] database: use existing database matching same
architecture prefix
State database binary file format may depend on system architecture,
for instance gdbm binary format depends on architecture word size,
making x86 and x64 gdbm files incompatible.
If this is the case, it is handled by adding system architecture name to
database file name using automatically configured CANONICAL_HOST string.
Meson build define CANONICAL_HOST to be system architecture name, while
autotools build extends this with vendor and and operating system components.
Switch autotools build to use host_cpu for CANONICAL_HOST to match Meson
configuration. For backwards compatibility always use existing database file
matching CANONICAL_HOST prefix if it exists.
Part-of:
---
configure.ac | 2 +-
src/pulsecore/database.c | 38 ++++++++++++++++++++++++++++++++++++++
src/pulsecore/database.h | 3 ++-
3 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/configure.ac b/configure.ac
index bd3a53367..2bc4b9002 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,7 +57,7 @@ AC_SUBST(LIBPULSE_SIMPLE_VERSION_INFO, [1:1:1])
AC_SUBST(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO, [0:6:0])
AC_CANONICAL_HOST
-AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.])
+AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host_cpu", [Canonical host system architecture string.])
AC_CHECK_PROG([STOW], [stow], [yes], [no])
diff --git a/src/pulsecore/database.c b/src/pulsecore/database.c
index 11f3d03c9..43af6fc76 100644
--- a/src/pulsecore/database.c
+++ b/src/pulsecore/database.c
@@ -22,6 +22,7 @@
#endif
#include
+#include
#include
#include
@@ -37,6 +38,9 @@ pa_database* pa_database_open(const char *path, const char *fn, bool prependmid,
char *machine_id = NULL, *filename_prefix, *full_path;
+ DIR *database_dir = NULL;
+ struct dirent *de;
+
pa_database *f;
pa_assert(!arch_suffix || arch_suffix[0]);
@@ -53,6 +57,40 @@ pa_database* pa_database_open(const char *path, const char *fn, bool prependmid,
fn,
arch_suffix?".":"", arch_suffix?:"");
+ /* Search for existing database directory entry name matching architecture suffix and filename suffix. */
+ database_dir = opendir(path);
+
+ if (database_dir) {
+ for (;;) {
+ errno = 0;
+ de = readdir(database_dir);
+ if (!de) {
+ if (errno) {
+ pa_log_warn("Unable to search for compatible database file, readdir() failed: %s", pa_cstrerror(errno));
+ /* can continue as if there is no matching database file candidate */
+ }
+
+ break;
+ }
+
+ if (pa_startswith(de->d_name, filename_prefix) && pa_endswith(de->d_name + strlen(filename_prefix), filename_suffix)) {
+ /* candidate filename found, replace filename_prefix with this one if match is not exact */
+
+ if (strlen(de->d_name) != strlen(filename_prefix) + strlen(filename_suffix)) {
+ pa_log_debug("Found compatible database file '%s/%s', using it", path, de->d_name);
+ pa_xfree(filename_prefix);
+ filename_prefix = pa_xstrndup(de->d_name, strlen(de->d_name) - strlen(filename_suffix));
+ }
+
+ break;
+ }
+ }
+
+ closedir(database_dir);
+ } else {
+ pa_log_warn("Unable to search for compatible database file, failed to open directory %s: %s", path, pa_cstrerror(errno));
+ }
+
full_path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s%s", path, filename_prefix, filename_suffix);
f = pa_database_open_internal(full_path, for_write);
diff --git a/src/pulsecore/database.h b/src/pulsecore/database.h
index fe2890577..cc16e535d 100644
--- a/src/pulsecore/database.h
+++ b/src/pulsecore/database.h
@@ -43,7 +43,8 @@ const char* pa_database_get_arch_suffix(void);
/* Database implementation; returns non-empty database filename extension string */
const char* pa_database_get_filename_suffix(void);
-/* This will attempt opening database file matching compiled CANONICAL_HOST identifier.
+/* This will attempt opening database file matching compiled CANONICAL_HOST implementation architecture name prefix,
+ * or new database file will be created and opened with implementation architecture name suffix if required.
* If prependmid is true, file name is augmented with machine id prefix. */
pa_database* pa_database_open(const char *path, const char *fn, bool prependmid, bool for_write);
From 4ca8997aa0a7116cc60cf375a198e3e4dad7a4e6 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Sun, 20 Dec 2020 21:36:21 +0300
Subject: [PATCH 089/865] database: drop arch from newly created database file
name
Part-of:
---
src/pulsecore/database.c | 56 +++++++++++++++++++++-------------------
1 file changed, 29 insertions(+), 27 deletions(-)
diff --git a/src/pulsecore/database.c b/src/pulsecore/database.c
index 43af6fc76..3e6f102f4 100644
--- a/src/pulsecore/database.c
+++ b/src/pulsecore/database.c
@@ -50,45 +50,47 @@ pa_database* pa_database_open(const char *path, const char *fn, bool prependmid,
return NULL;
}
- /* We include the host identifier in the file name because some database files are
- * CPU dependent, and we don't want things to go wrong if we are on a multiarch system. */
- filename_prefix = pa_sprintf_malloc("%s%s%s%s%s",
- machine_id?:"", machine_id?"-":"",
- fn,
- arch_suffix?".":"", arch_suffix?:"");
+ /* Database file name starts with ${machine_id}-${fn} */
+ if (machine_id)
+ filename_prefix = pa_sprintf_malloc("%s-%s", machine_id, fn);
+ else
+ filename_prefix = pa_xstrdup(fn);
- /* Search for existing database directory entry name matching architecture suffix and filename suffix. */
- database_dir = opendir(path);
+ if (arch_suffix) {
+ /* Search for existing database directory entry name matching architecture suffix and filename suffix. */
+ database_dir = opendir(path);
- if (database_dir) {
- for (;;) {
- errno = 0;
- de = readdir(database_dir);
- if (!de) {
- if (errno) {
- pa_log_warn("Unable to search for compatible database file, readdir() failed: %s", pa_cstrerror(errno));
- /* can continue as if there is no matching database file candidate */
+ if (database_dir) {
+ for (;;) {
+ errno = 0;
+ de = readdir(database_dir);
+ if (!de) {
+ if (errno) {
+ pa_log_warn("Unable to search for compatible database file, readdir() failed: %s", pa_cstrerror(errno));
+ /* can continue as if there is no matching database file candidate */
+ }
+
+ break;
}
- break;
- }
+ if (pa_startswith(de->d_name, filename_prefix)
+ && de->d_name[strlen(filename_prefix)] == '.'
+ && pa_startswith(de->d_name + strlen(filename_prefix) + 1, arch_suffix)
+ && pa_endswith(de->d_name + strlen(filename_prefix) + 1 + strlen(arch_suffix), filename_suffix)) {
- if (pa_startswith(de->d_name, filename_prefix) && pa_endswith(de->d_name + strlen(filename_prefix), filename_suffix)) {
- /* candidate filename found, replace filename_prefix with this one if match is not exact */
+ /* candidate filename found, replace filename_prefix with this one */
- if (strlen(de->d_name) != strlen(filename_prefix) + strlen(filename_suffix)) {
pa_log_debug("Found compatible database file '%s/%s', using it", path, de->d_name);
pa_xfree(filename_prefix);
filename_prefix = pa_xstrndup(de->d_name, strlen(de->d_name) - strlen(filename_suffix));
+ break;
}
-
- break;
}
- }
- closedir(database_dir);
- } else {
- pa_log_warn("Unable to search for compatible database file, failed to open directory %s: %s", path, pa_cstrerror(errno));
+ closedir(database_dir);
+ } else {
+ pa_log_warn("Unable to search for compatible database file, failed to open directory %s: %s", path, pa_cstrerror(errno));
+ }
}
full_path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s%s", path, filename_prefix, filename_suffix);
From 7115023c510690e473c03f51e3e5eaff9c73f8b7 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Mon, 21 Dec 2020 19:27:53 +0300
Subject: [PATCH 090/865] database: pick old database file from any arch
Part-of:
---
src/pulsecore/database-gdbm.c | 8 -----
src/pulsecore/database-simple.c | 5 ---
src/pulsecore/database-tdb.c | 5 ---
src/pulsecore/database.c | 57 +++++++++++++++------------------
src/pulsecore/database.h | 21 +++++++++---
5 files changed, 42 insertions(+), 54 deletions(-)
diff --git a/src/pulsecore/database-gdbm.c b/src/pulsecore/database-gdbm.c
index cd03405f5..b39f7dee2 100644
--- a/src/pulsecore/database-gdbm.c
+++ b/src/pulsecore/database-gdbm.c
@@ -59,14 +59,6 @@ void pa_datum_free(pa_datum *d) {
pa_zero(d);
}
-const char* pa_database_get_arch_suffix(void) {
- /* We include the host identifier in the file name because gdbm
- * files are CPU dependent, and we don't want things to go wrong
- * if we are on a multiarch system. */
-
- return CANONICAL_HOST;
-}
-
const char* pa_database_get_filename_suffix(void) {
return ".gdbm";
}
diff --git a/src/pulsecore/database-simple.c b/src/pulsecore/database-simple.c
index ebfbe4c61..96af8e098 100644
--- a/src/pulsecore/database-simple.c
+++ b/src/pulsecore/database-simple.c
@@ -222,11 +222,6 @@ static int fill_data(simple_data *db, FILE *f) {
return pa_hashmap_size(db->map);
}
-const char* pa_database_get_arch_suffix(void) {
- /* Simple database binary file format is CPU dependent. */
- return CANONICAL_HOST;
-}
-
const char* pa_database_get_filename_suffix(void) {
return ".simple";
}
diff --git a/src/pulsecore/database-tdb.c b/src/pulsecore/database-tdb.c
index 5e7315901..6605ed8fc 100644
--- a/src/pulsecore/database-tdb.c
+++ b/src/pulsecore/database-tdb.c
@@ -97,11 +97,6 @@ finish:
return c;
}
-const char* pa_database_get_arch_suffix(void) {
- /* TDB binary file format is not dependent on system architecture */
- return NULL;
-}
-
const char* pa_database_get_filename_suffix(void) {
return ".tdb";
}
diff --git a/src/pulsecore/database.c b/src/pulsecore/database.c
index 3e6f102f4..8b3b89c56 100644
--- a/src/pulsecore/database.c
+++ b/src/pulsecore/database.c
@@ -33,7 +33,6 @@
pa_database* pa_database_open(const char *path, const char *fn, bool prependmid, bool for_write) {
- const char *arch_suffix = pa_database_get_arch_suffix();
const char *filename_suffix = pa_database_get_filename_suffix();
char *machine_id = NULL, *filename_prefix, *full_path;
@@ -43,7 +42,6 @@ pa_database* pa_database_open(const char *path, const char *fn, bool prependmid,
pa_database *f;
- pa_assert(!arch_suffix || arch_suffix[0]);
pa_assert(filename_suffix && filename_suffix[0]);
if (prependmid && !(machine_id = pa_machine_id())) {
@@ -56,41 +54,38 @@ pa_database* pa_database_open(const char *path, const char *fn, bool prependmid,
else
filename_prefix = pa_xstrdup(fn);
- if (arch_suffix) {
- /* Search for existing database directory entry name matching architecture suffix and filename suffix. */
- database_dir = opendir(path);
+ /* Search for existing database directory entry name matching architecture suffix and filename suffix. */
+ database_dir = opendir(path);
- if (database_dir) {
- for (;;) {
- errno = 0;
- de = readdir(database_dir);
- if (!de) {
- if (errno) {
- pa_log_warn("Unable to search for compatible database file, readdir() failed: %s", pa_cstrerror(errno));
- /* can continue as if there is no matching database file candidate */
- }
-
- break;
+ if (database_dir) {
+ for (;;) {
+ errno = 0;
+ de = readdir(database_dir);
+ if (!de) {
+ if (errno) {
+ pa_log_warn("Unable to search for existing database file, readdir() failed: %s", pa_cstrerror(errno));
+ /* can continue as if there is no matching database file candidate */
}
- if (pa_startswith(de->d_name, filename_prefix)
- && de->d_name[strlen(filename_prefix)] == '.'
- && pa_startswith(de->d_name + strlen(filename_prefix) + 1, arch_suffix)
- && pa_endswith(de->d_name + strlen(filename_prefix) + 1 + strlen(arch_suffix), filename_suffix)) {
-
- /* candidate filename found, replace filename_prefix with this one */
-
- pa_log_debug("Found compatible database file '%s/%s', using it", path, de->d_name);
- pa_xfree(filename_prefix);
- filename_prefix = pa_xstrndup(de->d_name, strlen(de->d_name) - strlen(filename_suffix));
- break;
- }
+ break;
}
- closedir(database_dir);
- } else {
- pa_log_warn("Unable to search for compatible database file, failed to open directory %s: %s", path, pa_cstrerror(errno));
+ if (pa_startswith(de->d_name, filename_prefix)
+ && de->d_name[strlen(filename_prefix)] == '.'
+ && pa_endswith(de->d_name + strlen(filename_prefix) + 1, filename_suffix)) {
+
+ /* candidate filename found, replace filename_prefix with this one */
+
+ pa_log_debug("Found existing database file '%s/%s', using it", path, de->d_name);
+ pa_xfree(filename_prefix);
+ filename_prefix = pa_xstrndup(de->d_name, strlen(de->d_name) - strlen(filename_suffix));
+ break;
+ }
}
+
+ closedir(database_dir);
+ } else {
+ pa_log_warn("Unable to search for existing database file, failed to open directory %s: %s", path, pa_cstrerror(errno));
}
full_path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s%s", path, filename_prefix, filename_suffix);
diff --git a/src/pulsecore/database.h b/src/pulsecore/database.h
index cc16e535d..7fa489c67 100644
--- a/src/pulsecore/database.h
+++ b/src/pulsecore/database.h
@@ -38,14 +38,25 @@ typedef struct pa_datum {
void pa_datum_free(pa_datum *d);
-/* Database implementation; returns non-empty system architecture name string if database file format depends on system architecture, or NULL otherwise. */
-const char* pa_database_get_arch_suffix(void);
/* Database implementation; returns non-empty database filename extension string */
const char* pa_database_get_filename_suffix(void);
-/* This will attempt opening database file matching compiled CANONICAL_HOST implementation architecture name prefix,
- * or new database file will be created and opened with implementation architecture name suffix if required.
- * If prependmid is true, file name is augmented with machine id prefix. */
+/* Opens a database file. The file is loaded from the directory indicated by
+ * path. The file name is constructed by using fn as the base and then adding
+ * several parts:
+ * 1) If prependmid is true, the machine id is prepended to the file name.
+ * 2) The database implementation specific suffix is added.
+ * 3) Older versions of PulseAudio in some cases added the CPU architecture
+ * to the file name, which was later deemed unnecessary, but for
+ * compatibility reasons we still need to look for those files, so we scan
+ * the directory for files that match the prefix (possible machine id plus
+ * fn) and the suffix, and if any matches are found, we use the first one.
+ *
+ * When no existing file is found, we create a new file for the database
+ * (without the CPU architecture part in the name).
+ *
+ * For a read-only database, set for_write to false. */
+
pa_database* pa_database_open(const char *path, const char *fn, bool prependmid, bool for_write);
/* Database implementation; opens specified database file using provided path. */
From c2ae56a73cac813c95973e534a011d38a6a9e891 Mon Sep 17 00:00:00 2001
From: "Igor V. Kovalenko"
Date: Mon, 21 Dec 2020 19:55:00 +0300
Subject: [PATCH 091/865] database: clean up remaining references to
CANONICAL_HOST
Part-of:
---
configure.ac | 3 ---
meson.build | 1 -
src/daemon/main.c | 1 -
3 files changed, 5 deletions(-)
diff --git a/configure.ac b/configure.ac
index 2bc4b9002..155e5653e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -56,9 +56,6 @@ AC_SUBST(LIBPULSE_SIMPLE_VERSION_INFO, [1:1:1])
# info x:y:z always will hold x=z
AC_SUBST(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO, [0:6:0])
-AC_CANONICAL_HOST
-AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host_cpu", [Canonical host system architecture string.])
-
AC_CHECK_PROG([STOW], [stow], [yes], [no])
AS_IF([test "x$STOW" = "xyes" && test -d /usr/local/stow], [
diff --git a/meson.build b/meson.build
index 23e04ef46..bff13cc16 100644
--- a/meson.build
+++ b/meson.build
@@ -125,7 +125,6 @@ cdata = configuration_data()
cdata.set_quoted('PACKAGE', 'pulseaudio')
cdata.set_quoted('PACKAGE_NAME', 'pulseaudio')
cdata.set_quoted('PACKAGE_VERSION', pa_version_str)
-cdata.set_quoted('CANONICAL_HOST', target_machine.cpu_family())
cdata.set('PA_MAJOR', pa_version_major)
cdata.set('PA_MINOR', pa_version_minor)
cdata.set('PA_API_VERSION', pa_api_version)
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 1032390ca..a424d9ef9 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -922,7 +922,6 @@ int main(int argc, char *argv[]) {
pa_set_env_and_record("PULSE_SYSTEM", conf->system_instance ? "1" : "0");
pa_log_info("This is PulseAudio %s", PACKAGE_VERSION);
- pa_log_debug("Compilation host: %s", CANONICAL_HOST);
pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS);
#ifdef HAVE_LIBSAMPLERATE
From b1bdd27a6c7828ed4c055986b89fb17e90e02c2a Mon Sep 17 00:00:00 2001
From: Patrick Gaskin
Date: Sun, 3 Jan 2021 03:29:35 -0500
Subject: [PATCH 092/865] cli: Fix crash when using .include with an empty
directory
This would previously fail the size > 0 assertion in pa_xmalloc.
Part-of:
---
src/pulsecore/cli-command.c | 44 ++++++++++++++++++-------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index c5a21ed93..5dc2ac6b9 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -2118,34 +2118,34 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
}
closedir(d);
+ if ((count = pa_dynarray_size(files))) {
+ sorted_files = pa_xnew(char*, count);
+ for (i = 0; i < count; ++i)
+ sorted_files[i] = pa_dynarray_get(files, i);
+ pa_dynarray_free(files);
- count = pa_dynarray_size(files);
- sorted_files = pa_xnew(char*, count);
- for (i = 0; i < count; ++i)
- sorted_files[i] = pa_dynarray_get(files, i);
- pa_dynarray_free(files);
-
- for (i = 0; i < count; ++i) {
- for (unsigned j = 0; j < count; ++j) {
- if (strcmp(sorted_files[i], sorted_files[j]) < 0) {
- char *tmp = sorted_files[i];
- sorted_files[i] = sorted_files[j];
- sorted_files[j] = tmp;
+ for (i = 0; i < count; ++i) {
+ for (unsigned j = 0; j < count; ++j) {
+ if (strcmp(sorted_files[i], sorted_files[j]) < 0) {
+ char *tmp = sorted_files[i];
+ sorted_files[i] = sorted_files[j];
+ sorted_files[j] = tmp;
+ }
}
}
- }
- for (i = 0; i < count; ++i) {
- if (!failed) {
- if (pa_cli_command_execute_file(c, sorted_files[i], buf, fail) < 0 && *fail)
- failed = true;
+ for (i = 0; i < count; ++i) {
+ if (!failed) {
+ if (pa_cli_command_execute_file(c, sorted_files[i], buf, fail) < 0 && *fail)
+ failed = true;
+ }
+
+ pa_xfree(sorted_files[i]);
}
-
- pa_xfree(sorted_files[i]);
+ pa_xfree(sorted_files);
+ if (failed)
+ return -1;
}
- pa_xfree(sorted_files);
- if (failed)
- return -1;
}
} else if (pa_cli_command_execute_file(c, filename, buf, fail) < 0 && *fail) {
return -1;
From 5d42d861e320c2e4c1f9bca7556cddc16250f5f0 Mon Sep 17 00:00:00 2001
From: Fabian Affolter
Date: Sat, 12 Dec 2020 16:44:16 +0000
Subject: [PATCH 093/865] Translated using Weblate (German)
Currently translated at 100.0% (526 of 526 strings)
Translation: pulseaudio/pulseaudio
Translate-URL: https://translate.fedoraproject.org/projects/pulseaudio/pulseaudio/de/
Part-of:
---
po/de.po | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/po/de.po b/po/de.po
index b02a4a0fe..87a7535fb 100644
--- a/po/de.po
+++ b/po/de.po
@@ -12,17 +12,19 @@
msgid ""
msgstr ""
"Project-Id-Version: pulseaudio.master-tx.de\n"
-"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/new\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/"
+"issues/new\n"
"POT-Creation-Date: 2016-09-24 21:48+0200\n"
-"PO-Revision-Date: 2016-09-29 12:52+0200\n"
-"Last-Translator: Mario Blättermann \n"
-"Language-Team: \n"
+"PO-Revision-Date: 2020-12-13 17:35+0000\n"
+"Last-Translator: Fabian Affolter \n"
+"Language-Team: German \n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Poedit 1.8.9\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.3.2\n"
#: ../src/daemon/cmdline.c:113
#, c-format
@@ -986,7 +988,7 @@ msgstr "Kopfhörer"
#: ../src/modules/bluetooth/module-bluez5-device.c:1728
msgid "Portable"
-msgstr ""
+msgstr "Tragbar"
#: ../src/modules/bluetooth/module-bluez5-device.c:1733
msgid "Car"
@@ -1015,19 +1017,19 @@ msgstr "Bluetooth-Eingabe"
#: ../src/modules/bluetooth/module-bluez5-device.c:1790
msgid "High Fidelity Playback (A2DP Sink)"
-msgstr "High Fidelity Playback (A2DP-Ziel)"
+msgstr "High Fidelity Wiedergabe (A2DP-Ziel)"
#: ../src/modules/bluetooth/module-bluez5-device.c:1802
msgid "High Fidelity Capture (A2DP Source)"
-msgstr "High Fidelity Capture (A2DP-Quelle)"
+msgstr "High Fidelity Aufnahme (A2DP-Quelle)"
#: ../src/modules/bluetooth/module-bluez5-device.c:1814
msgid "Headset Head Unit (HSP/HFP)"
-msgstr ""
+msgstr "Kopfhörer Kopf-Einheit (HSP/HFP)"
#: ../src/modules/bluetooth/module-bluez5-device.c:1827
msgid "Headset Audio Gateway (HSP/HFP)"
-msgstr ""
+msgstr "Kopfhörer Audio Gateway (HSP/HFP)"
#: ../src/modules/echo-cancel/module-echo-cancel.c:61
msgid ""
From f1589c08e5a92867da35ce5ed1c4441afdb7ce1d Mon Sep 17 00:00:00 2001
From: Julien Humbert
Date: Sat, 12 Dec 2020 07:00:58 +0000
Subject: [PATCH 094/865] Translated using Weblate (French)
Currently translated at 100.0% (517 of 517 strings)
Translation: pulseaudio/pulseaudio
Translate-URL: https://translate.fedoraproject.org/projects/pulseaudio/pulseaudio/fr/
Part-of:
---
po/fr.po | 495 +++++++++++++++++++++++++++----------------------------
1 file changed, 245 insertions(+), 250 deletions(-)
diff --git a/po/fr.po b/po/fr.po
index aa570fa1d..f5613ed72 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -16,8 +16,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-10-06 16:57+0200\n"
-"PO-Revision-Date: 2020-07-06 08:27+0000\n"
-"Last-Translator: Jean-Baptiste Holcroft \n"
+"PO-Revision-Date: 2020-12-13 17:35+0000\n"
+"Last-Translator: Julien Humbert \n"
"Language-Team: French \n"
"Language: fr\n"
@@ -25,7 +25,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.1.1\n"
+"X-Generator: Weblate 4.3.2\n"
#: ../src/daemon/cmdline.c:111
#, c-format
@@ -109,14 +109,15 @@ msgstr ""
"disponibles\n"
" --cleanup-shm Effacer les segments usés de la "
"mémoire partagée\n"
-" --start Lancer le démon s'il n'est pas en "
-"cours d'exécution\n"
-" -k --kill Kill un démon en cours d'exécution\n"
-" --check Rechercher un démon en cours "
-"d'exécution (retourne uniquement un code de sortie)\n"
+" --start Lancer le démon s’il n’est pas en "
+"cours d’exécution\n"
+" -k --kill Tuer un démon en cours d’exécution\n"
+" --check Rechercher un démon en cours d’"
+"exécution (retourne uniquement un code de sortie)\n"
"\n"
"OPTIONS :\n"
-" --system[=BOOL] Exécuter en tant qu'instance globale\n"
+" --system[=BOOL] Exécuter en tant qu’instance "
+"globale\n"
" -D, --daemonize[=BOOL] Démoniser après le lancement\n"
" --fail[=BOOL] Quitter si le lancement échoue\n"
" --high-priority[=BOOL] Tenter de définir un niveau nice "
@@ -124,31 +125,31 @@ msgstr ""
" (uniquement disponible en tant que "
"root, lorsque SUID ou\n"
" avec RLIMIT_NICE élevé)\n"
-" --realtime[=BOOL] Tenter d'activer la planification "
+" --realtime[=BOOL] Tenter d’activer la planification "
"realtime\n"
" (uniquement disponible en tant que "
"root, lorsque SUID ou\n"
" avec RLIMIT_RTPRIO élevé)\n"
" --disallow-module-loading[=BOOL] Interdire le chargement ou "
"déchargement de modules\n"
-" requis par l'utilisateur de module "
+" requis par l’utilisateur de module "
"après le lancement\n"
-" --disallow-exit[=BOOL] Interdire la sortie requise par "
-"l'utilisateur\n"
-" --exit-idle-time=SECS Quitter le démon lorsqu'inactif et "
+" --disallow-exit[=BOOL] Interdire la sortie requise par l’"
+"utilisateur\n"
+" --exit-idle-time=SECS Quitter le démon lorsqu’inactif et "
"que cette\n"
-" période s'est écoulée\n"
+" période s’est écoulée\n"
" --scache-idle-time=SECS Décharger les samples chargés "
-"automatiquement lorsqu'inactif et que cette\n"
-" période s'est écoulée\n"
+"automatiquement lorsqu’inactif et que cette\n"
+" période s’est écoulée\n"
" --log-level[=LEVEL] Augmenter ou définir le niveau de "
"détail\n"
" -v --verbose Augmenter le niveau de détail\n"
" --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
" Indiquer la cible du journal\n"
-" --log-meta[=BOOL] Inclure l'emplacement du code dans "
+" --log-meta[=BOOL] Inclure l’emplacement du code dans "
"les messages journaux\n"
-" --log-time[=BOOL] Inclure l'horodatage dans les "
+" --log-time[=BOOL] Inclure l’horodatage dans les "
"messages journaux\n"
" --log-backtrace=FRAMES Inclure un backtrace dans les "
"messages journaux\n"
@@ -170,10 +171,10 @@ msgstr ""
"SCRIPT DE LANCEMENT :\n"
" -L, --load=\"MODULE ARGUMENTS\" Charger le module du greffon "
"spécifié avec\n"
-" l'argument spécifié\n"
+" l’argument spécifié\n"
" -F, --file=FILENAME Lancer le script spécifié\n"
" -C Ouvrir une ligne de commande sur le "
-"TTY en cours d'exécution\n"
+"TTY en cours d’exécution\n"
" après le lancement\n"
"\n"
" -n Ne pas charger le fichier du script "
@@ -280,17 +281,17 @@ msgstr "[%s:%u] rlimit « %s » invalide."
#: ../src/daemon/daemon-conf.c:330
#, c-format
msgid "[%s:%u] Invalid sample format '%s'."
-msgstr "[%s:%u] Format d'échantillon « %s » invalide."
+msgstr "[%s:%u] Format d’échantillon « %s » invalide."
#: ../src/daemon/daemon-conf.c:347 ../src/daemon/daemon-conf.c:364
#, c-format
msgid "[%s:%u] Invalid sample rate '%s'."
-msgstr "[%s:%u] Taux d'échantillonnage « %s » invalide."
+msgstr "[%s:%u] Taux d’échantillonnage « %s » invalide."
#: ../src/daemon/daemon-conf.c:387
#, c-format
msgid "[%s:%u] Invalid sample channels '%s'."
-msgstr "[%s:%u] Canaux d'échantillonnage « %s » invalide."
+msgstr "[%s:%u] Canaux d’échantillonnage « %s » invalides."
#: ../src/daemon/daemon-conf.c:404
#, c-format
@@ -320,7 +321,7 @@ msgstr "[%s:%u] Type de serveur « %s » invalide."
#: ../src/daemon/daemon-conf.c:611
#, c-format
msgid "Failed to open configuration file: %s"
-msgstr "Échec lors de l'ouverture du fichier de configuration : %s"
+msgstr "Échec lors de l’ouverture du fichier de configuration : %s"
#: ../src/daemon/daemon-conf.c:627
msgid ""
@@ -373,7 +374,7 @@ msgstr "Chargement unique : %s\n"
#: ../src/daemon/dumpmodules.c:72
#, c-format
msgid "DEPRECATION WARNING: %s\n"
-msgstr "AVERTISSEMENT D'OBSOLESCENCE : %s\n"
+msgstr "AVERTISSEMENT D’OBSOLESCENCE : %s\n"
#: ../src/daemon/dumpmodules.c:76
#, c-format
@@ -383,7 +384,7 @@ msgstr "Chemin : %s\n"
#: ../src/daemon/ltdl-bind-now.c:75
#, c-format
msgid "Failed to open module %s: %s"
-msgstr "Échec d'ouverture du module %s : %s"
+msgstr "Échec d’ouverture du module %s : %s"
#: ../src/daemon/ltdl-bind-now.c:126
msgid "Failed to find original lt_dlopen loader."
@@ -391,16 +392,16 @@ msgstr "Échec lors de la recherche du chargeur lt_dlopen original."
#: ../src/daemon/ltdl-bind-now.c:131
msgid "Failed to allocate new dl loader."
-msgstr "Échec lors de l'allocation du nouveau chargeur dl."
+msgstr "Échec lors de l’allocation du nouveau chargeur dl."
#: ../src/daemon/ltdl-bind-now.c:144
msgid "Failed to add bind-now-loader."
-msgstr "Échec lors de l'ajout du chargeur bind-now."
+msgstr "Échec lors de l’ajout du chargeur bind-now."
#: ../src/daemon/main.c:160
#, c-format
msgid "Failed to find user '%s'."
-msgstr "Impossible de trouver l'utilisateur « %s »."
+msgstr "Impossible de trouver l’utilisateur « %s »."
#: ../src/daemon/main.c:165
#, c-format
@@ -411,13 +412,12 @@ msgstr "Impossible de trouver le groupe « %s »."
#, c-format
msgid "GID of user '%s' and of group '%s' don't match."
msgstr ""
-"Le GID de l'utilisateur « %s » et du groupe « %s » ne sont pas identiques."
+"Le GID de l’utilisateur « %s » et du groupe « %s » ne sont pas identiques."
#: ../src/daemon/main.c:179
#, c-format
msgid "Home directory of user '%s' is not '%s', ignoring."
-msgstr ""
-"Le dossier personnel de l'utilisateur « %s » n'est pas « %s », ignoré."
+msgstr "Le dossier personnel de l’utilisateur « %s » n’est pas « %s », ignoré."
#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
#, c-format
@@ -437,7 +437,7 @@ msgstr "Échec lors du changement de GID : %s"
#: ../src/daemon/main.c:226
#, c-format
msgid "Failed to change UID: %s"
-msgstr "Échec lors du changement d'UID : %s"
+msgstr "Échec lors du changement d’UID : %s"
#: ../src/daemon/main.c:255
msgid "System wide mode unsupported on this platform."
@@ -445,7 +445,7 @@ msgstr "Mode système étendu non pris en charge sur cette plateforme."
#: ../src/daemon/main.c:484
msgid "Failed to parse command line."
-msgstr "Échec lors de l'analyse de la ligne de commande."
+msgstr "Échec lors de l’analyse de la ligne de commande."
#: ../src/daemon/main.c:523
msgid ""
@@ -465,7 +465,7 @@ msgid ""
"This program is not intended to be run as root (unless --system is "
"specified)."
msgstr ""
-"Le programme n'est pas conçu pour être lancé en tant que root (sauf si --"
+"Le programme n’est pas conçu pour être lancé en tant que root (sauf si --"
"system est renseigné)."
#: ../src/daemon/main.c:654
@@ -474,13 +474,13 @@ msgstr "Les privilèges root sont nécessaires."
#: ../src/daemon/main.c:661
msgid "--start not supported for system instances."
-msgstr "--start n'est pas pris en charge pour les instances système."
+msgstr "--start n’est pas pris en charge pour les instances système."
#: ../src/daemon/main.c:701
#, c-format
msgid "User-configured server at %s, refusing to start/autospawn."
msgstr ""
-"Serveur configuré par l'utilisateur sur %s, refus du lancement start/"
+"Serveur configuré par l’utilisateur sur %s, refus du lancement start/"
"autospawn."
#: ../src/daemon/main.c:707
@@ -488,33 +488,33 @@ msgstr ""
msgid ""
"User-configured server at %s, which appears to be local. Probing deeper."
msgstr ""
-"Serveur configuré par l'utilisateur sur %s, qui semble être local. Analyse "
+"Serveur configuré par l’utilisateur sur %s, qui semble être local. Analyse "
"plus précise en cours."
#: ../src/daemon/main.c:712
msgid "Running in system mode, but --disallow-exit not set!"
msgstr ""
-"Le démon s'exécute en mode système, mais --disallow-exit n'est pas défini !"
+"Le démon s’exécute en mode système, mais --disallow-exit n’est pas défini !"
#: ../src/daemon/main.c:715
msgid "Running in system mode, but --disallow-module-loading not set!"
msgstr ""
-"Le démon s'exécute en mode système, mais --disallow-module-loading n'est pas "
-"défini !"
+"Le démon s’exécute en mode système, mais --disallow-module-loading n’est pas "
+"défini !"
#: ../src/daemon/main.c:718
msgid "Running in system mode, forcibly disabling SHM mode!"
-msgstr "Le démon s'exécute en mode système, désactivation forcée du mode SHM !"
+msgstr "Le démon s’exécute en mode système, désactivation forcée du mode SHM !"
#: ../src/daemon/main.c:723
msgid "Running in system mode, forcibly disabling exit idle time!"
msgstr ""
-"Le démon s'exécute en mode système, désactivation forcée de la fermeture "
-"après délai d'inactivité !"
+"Le démon s’exécute en mode système, désactivation forcée de la fermeture "
+"après délai d’inactivité !"
#: ../src/daemon/main.c:756
msgid "Failed to acquire stdio."
-msgstr "Échec lors de l'acquisition de stdio."
+msgstr "Échec lors de l’acquisition de stdio."
#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
#, c-format
@@ -542,7 +542,7 @@ msgstr "Échec de setsid() : %s"
#: ../src/daemon/main.c:948
msgid "Failed to get machine ID"
-msgstr "Échec lors de l'obtention de l'ID de la machine"
+msgstr "Échec lors de l’obtention de l’ID de la machine"
#: ../src/daemon/main.c:974
msgid ""
@@ -572,7 +572,7 @@ msgstr "Échec de pa_core_new()."
#: ../src/daemon/main.c:1088
msgid "Failed to initialize daemon."
-msgstr "Échec lors de l'initialisation du démon."
+msgstr "Échec lors de l’initialisation du démon."
#: ../src/daemon/main.c:1093
msgid "Daemon startup without any loaded modules, refusing to work."
@@ -592,15 +592,15 @@ msgstr "Entrée"
#: ../src/modules/alsa/alsa-mixer.c:2292
msgid "Docking Station Input"
-msgstr "Entrée de la station d'accueil"
+msgstr "Entrée de la station d’accueil"
#: ../src/modules/alsa/alsa-mixer.c:2293
msgid "Docking Station Microphone"
-msgstr "Microphone de la station d'accueil"
+msgstr "Microphone de la station d’accueil"
#: ../src/modules/alsa/alsa-mixer.c:2294
msgid "Docking Station Line In"
-msgstr "Entrée ligne de la station d'accueil"
+msgstr "Entrée ligne de la station d’accueil"
#: ../src/modules/alsa/alsa-mixer.c:2295 ../src/modules/alsa/alsa-mixer.c:2380
msgid "Line In"
@@ -657,7 +657,7 @@ msgstr "Amplificateur"
#: ../src/modules/alsa/alsa-mixer.c:2308
msgid "No Amplifier"
-msgstr "Pas d'amplificateur"
+msgstr "Pas d’amplificateur"
#: ../src/modules/alsa/alsa-mixer.c:2309
msgid "Bass Boost"
@@ -682,7 +682,7 @@ msgstr "Entrée analogique"
#: ../src/modules/alsa/alsa-mixer.c:2377
msgid "Dock Microphone"
-msgstr "Microphone de la station d'accueil"
+msgstr "Microphone de la station d’accueil"
#: ../src/modules/alsa/alsa-mixer.c:2379
msgid "Headset Microphone"
@@ -846,10 +846,10 @@ msgid ""
"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
"returned 0 or another value < min_avail."
msgstr ""
-"ALSA nous a réveillé pour écrire de nouvelles données à partir du "
-"périphérique, mais il n'y avait en fait rien à écrire !\n"
-"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
-"rapporter ce problème aux développeurs d'ALSA.\n"
+"ALSA nous a réveillés pour écrire de nouvelles données à partir du "
+"périphérique, mais il n’y avait en fait rien à écrire !\n"
+"Il s’agit très probablement d’un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d’ALSA.\n"
"Nous avons été réveillés avec POLLOUT actif, cependant un snd_pcm_avail() "
"ultérieur a retourné 0 ou une autre valeur < min_avail."
@@ -863,10 +863,10 @@ msgid ""
"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
"returned 0 or another value < min_avail."
msgstr ""
-"ALSA nous a réveillé pour lire de nouvelles données à partir du "
-"périphérique, mais il n'y avait en fait rien à lire !\n"
-"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
-"rapporter ce problème aux développeurs d'ALSA.\n"
+"ALSA nous a réveillés pour lire de nouvelles données à partir du "
+"périphérique, mais il n’y avait en fait rien à lire !\n"
+"Il s’agit très probablement d’un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d’ALSA.\n"
"Nous avons été réveillés avec POLLIN actif, cependant un snd_pcm_avail() "
"ultérieur a retourné 0 ou une autre valeur < min_avail."
@@ -880,8 +880,8 @@ msgid ""
msgstr ""
"snd_pcm_avail() a retourné une valeur qui est exceptionnellement large : %lu "
"octets (%lu ms).\n"
-"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
-"rapporter ce problème aux développeurs d'ALSA."
+"Il s’agit très probablement d’un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d’ALSA."
#: ../src/modules/alsa/alsa-util.c:1184
#, c-format
@@ -893,8 +893,8 @@ msgid ""
msgstr ""
"snd_pcm_delay() a retourné une valeur qui est exceptionnellement large : %li "
"octets (%s%lu ms).\n"
-"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
-"rapporter ce problème aux développeurs d'ALSA."
+"Il s’agit très probablement d’un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d’ALSA."
#: ../src/modules/alsa/alsa-util.c:1225
#, c-format
@@ -906,8 +906,8 @@ msgid ""
msgstr ""
"snd_pcm_avail_delay() a retourné des valeurs inhabituelles : le délai %lu "
"est inférieur au %lu disponible.\n"
-"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
-"rapporter ce problème aux développeurs d'ALSA."
+"Il s’agit très probablement d’un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d’ALSA."
#: ../src/modules/alsa/alsa-util.c:1268
#, c-format
@@ -919,8 +919,8 @@ msgid ""
msgstr ""
"snd_pcm_mmap_begin() a retourné une valeur qui est exceptionnellement large :"
" %lu octets (%lu·ms).\n"
-"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
-"rapporter ce problème aux développeurs d'ALSA."
+"Il s’agit très probablement d’un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d’ALSA."
#: ../src/modules/bluetooth/module-bluez5-device.c:1700
msgid "Headset"
@@ -996,7 +996,7 @@ msgstr ""
"sink_properties= sink_master= adjust_time=