po/POTFILES.in | 1 src/Makefile.am | 17 src/daemon/main.c | 10 src/modules/alsa/alsa-mixer.c | 3 src/modules/alsa/mixer/paths/iec958-stereo-input.conf | 20 + src/modules/alsa/mixer/paths/steelseries-arctis-input.conf | 25 + src/modules/alsa/mixer/paths/steelseries-arctis-output-mono.conf | 29 + src/modules/alsa/mixer/paths/steelseries-arctis-output-stereo.conf | 27 + src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules | 2 src/modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf | 55 ++ src/modules/alsa/mixer/profile-sets/steelseries-arctis-usb-audio.conf | 43 ++ src/modules/bluetooth/backend-native.c | 2 src/modules/bluetooth/bluez5-util.c | 1 src/modules/bluetooth/bluez5-util.h | 11 src/modules/bluetooth/module-bluetooth-discover.c | 4 src/modules/bluetooth/module-bluetooth-policy.c | 6 src/modules/bluetooth/module-bluez4-discover.c | 2 src/modules/bluetooth/module-bluez5-device.c | 22 + src/modules/bluetooth/module-bluez5-discover.c | 2 src/modules/dbus/iface-core.c | 2 src/modules/gconf/module-gconf.c | 2 src/modules/jack/module-jackdbus-detect.c | 2 src/modules/macosx/module-coreaudio-detect.c | 2 src/modules/module-allow-passthrough.c | 2 src/modules/module-always-sink.c | 2 src/modules/module-always-source.c | 189 ++++++++++ src/modules/module-combine.c | 2 src/modules/module-detect.c | 14 src/modules/module-equalizer-sink.c | 26 + src/modules/module-filter-apply.c | 132 +++++- src/modules/module-hal-detect-compat.c | 2 src/modules/module-loopback.c | 49 +- src/modules/module-switch-on-port-available.c | 5 src/modules/module-udev-detect.c | 2 src/modules/module-volume-restore.c | 2 src/modules/module-zeroconf-discover.c | 2 src/modules/raop/module-raop-discover.c | 2 src/modules/raop/raop-sink.c | 7 src/pulsecore/cli-command.c | 16 src/pulsecore/device-port.c | 2 src/pulsecore/module.c | 20 - src/pulsecore/module.h | 2 src/pulsecore/protocol-native.c | 2 src/pulsecore/sink.c | 24 + src/pulsecore/sink.h | 1 src/utils/padsp.c | 15 46 files changed, 709 insertions(+), 101 deletions(-)
New commits: commit 703d95fd00aff96fc66c5f960345945dd04e8980 Author: Arun Raghavan <[email protected]> Date: Sun Sep 3 13:26:14 2017 +0530 always-source: Fix pa_module_load() usage The API changed slightly since the original patch was written. diff --git a/src/modules/module-always-source.c b/src/modules/module-always-source.c index 09ac7aa5..5c1f22aa 100644 --- a/src/modules/module-always-source.c +++ b/src/modules/module-always-source.c @@ -80,7 +80,7 @@ static void load_null_source_if_needed(pa_core *c, pa_source *source, struct use u->ignore = true; t = pa_sprintf_malloc("source_name=%s", u->source_name); - m = pa_module_load(c, "module-null-source", t); + pa_module_load(&m, c, "module-null-source", t); u->null_module = m ? m->index : PA_INVALID_INDEX; pa_xfree(t); commit 15d28f21b4c00bd38a3797461b28bb6f90709aae Author: Pierre-Louis Bossart <[email protected]> Date: Mon Aug 28 17:49:16 2017 -0500 sink: force suspend/resume on passthrough transitions A race condition prevents the AES non-audio bit from being set when enabling IEC61937 passthrough on resume with no sink-input connected (pa_sink_is_passthrough returns false). The non-audio bit should really be set when opening the sink. Force the sink to suspend/resume when actually entering passthrough mode, and likewise force a suspend-resume on leaving passthrough mode. Tested with E-AC3 streams which do need the AES bit set for my Onkyon receiver to detect the format instead of playing it as PCM. Signed-off-by: Pierre-Louis Bossart <[email protected]> diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index a8b4cd3d..0cc76514 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1626,6 +1626,11 @@ bool pa_sink_is_passthrough(pa_sink *s) { void pa_sink_enter_passthrough(pa_sink *s) { pa_cvolume volume; + if (s->is_passthrough_set) { + pa_log_debug("Sink %s is already in passthrough mode, nothing to do", s->name); + return; + } + /* disable the monitor in passthrough mode */ if (s->monitor_source) { pa_log_debug("Suspending monitor source %s, because the sink is entering the passthrough mode.", s->monitor_source->name); @@ -1638,10 +1643,23 @@ void pa_sink_enter_passthrough(pa_sink *s) { pa_cvolume_set(&volume, s->sample_spec.channels, PA_MIN(s->base_volume, PA_VOLUME_NORM)); pa_sink_set_volume(s, &volume, true, false); + + pa_log_debug("Suspending/Restarting sink %s to enter passthrough mode", s->name); + + /* force sink to be resumed in passthrough mode */ + pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL); + s->is_passthrough_set = true; + pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL); } /* Called from main context */ void pa_sink_leave_passthrough(pa_sink *s) { + + if (!s->is_passthrough_set) { + pa_log_debug("Sink %s is not in passthrough mode, nothing to do", s->name); + return; + } + /* Unsuspend monitor */ if (s->monitor_source) { pa_log_debug("Resuming monitor source %s, because the sink is leaving the passthrough mode.", s->monitor_source->name); @@ -1653,6 +1671,12 @@ void pa_sink_leave_passthrough(pa_sink *s) { pa_cvolume_init(&s->saved_volume); s->saved_save_volume = false; + + /* force sink to be resumed in non-passthrough mode */ + pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL); + s->is_passthrough_set = false; + pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL); + } /* Called from main context. */ diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 0e79cf36..7deafdd8 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -108,6 +108,7 @@ struct pa_sink { /* Saved volume state while we're in passthrough mode */ pa_cvolume saved_volume; bool saved_save_volume:1; + bool is_passthrough_set:1; pa_asyncmsgq *asyncmsgq; commit b4d2443249c2ab31f58ed97a968db41c1fe61b49 Author: Sebastian Dröge <[email protected]> Date: Fri Jun 30 00:57:36 2017 +0300 Implement module-always-source This is basically a copy of module-always-sink but doing the same for sources. Whenever no source is available, a module-null-source is loaded and whenever a new source is available again, module-null-source is unloaded. By this, anything using a source will automatically be switched to the null source when the actual source disappears, and back to the actual source if it appears again. diff --git a/po/POTFILES.in b/po/POTFILES.in index e2d6d118..d4c78cf5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -24,6 +24,7 @@ src/modules/jack/module-jack-source.c src/modules/macosx/module-coreaudio-device.c src/modules/module-allow-passthrough.c src/modules/module-always-sink.c +src/modules/module-always-source.c src/modules/module-cli.c src/modules/module-combine.c src/modules/module-console-kit.c diff --git a/src/Makefile.am b/src/Makefile.am index ba2ea97e..d7a551e2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1193,6 +1193,7 @@ modlibexec_LTLIBRARIES += \ module-card-restore.la \ module-default-device-restore.la \ module-always-sink.la \ + module-always-source.la \ module-rescue-streams.la \ module-intended-roles.la \ module-suspend-on-idle.la \ @@ -1534,6 +1535,7 @@ SYMDEF_FILES = \ module-card-restore-symdef.h \ module-default-device-restore-symdef.h \ module-always-sink-symdef.h \ + module-always-source-symdef.h \ module-rescue-streams-symdef.h \ module-intended-roles-symdef.h \ module-suspend-on-idle-symdef.h \ @@ -2021,6 +2023,12 @@ module_always_sink_la_LDFLAGS = $(MODULE_LDFLAGS) module_always_sink_la_LIBADD = $(MODULE_LIBADD) module_always_sink_la_CFLAGS = $(AM_CFLAGS) +# Always Source module +module_always_source_la_SOURCES = modules/module-always-source.c +module_always_source_la_LDFLAGS = $(MODULE_LDFLAGS) +module_always_source_la_LIBADD = $(MODULE_LIBADD) +module_always_source_la_CFLAGS = $(AM_CFLAGS) + # Rescue streams module module_rescue_streams_la_SOURCES = modules/module-rescue-streams.c module_rescue_streams_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/src/modules/module-always-source.c b/src/modules/module-always-source.c new file mode 100644 index 00000000..09ac7aa5 --- /dev/null +++ b/src/modules/module-always-source.c @@ -0,0 +1,189 @@ +/*** + This file is part of PulseAudio. + + Copyright 2008 Colin Guthrie + Copyright 2017 Sebastian Dröge <[email protected]> + + 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 <http://www.gnu.org/licenses/>. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulse/xmalloc.h> + +#include <pulsecore/core.h> +#include <pulsecore/core-util.h> +#include <pulsecore/i18n.h> +#include <pulsecore/source.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> + +#include "module-always-source-symdef.h" + +PA_MODULE_AUTHOR("Sebastian Dröge"); +PA_MODULE_DESCRIPTION(_("Always keeps at least one source loaded even if it's a null one")); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(true); +PA_MODULE_USAGE( + "source_name=<name of source>"); + +#define DEFAULT_SOURCE_NAME "auto_null" + +static const char* const valid_modargs[] = { + "source_name", + NULL, +}; + +struct userdata { + uint32_t null_module; + bool ignore; + char *source_name; +}; + +static void load_null_source_if_needed(pa_core *c, pa_source *source, struct userdata* u) { + pa_source *target; + uint32_t idx; + char *t; + pa_module *m; + + pa_assert(c); + pa_assert(u); + + if (u->null_module != PA_INVALID_INDEX) + return; /* We've already got a null-source loaded */ + + /* Loop through all sources and check to see if we have *any* + * sources. Ignore the source passed in (if it's not null), and + * don't count filter or monitor sources. */ + PA_IDXSET_FOREACH(target, c->sources, idx) + if (!source || ((target != source) && !pa_source_is_filter(target) && target->monitor_of == NULL)) + break; + + if (target) + return; + + pa_log_debug("Autoloading null-source as no other sources detected."); + + u->ignore = true; + + t = pa_sprintf_malloc("source_name=%s", u->source_name); + m = pa_module_load(c, "module-null-source", t); + u->null_module = m ? m->index : PA_INVALID_INDEX; + pa_xfree(t); + + u->ignore = false; + + if (!m) + pa_log_warn("Unable to load module-null-source"); +} + +static pa_hook_result_t put_hook_callback(pa_core *c, pa_source *source, void* userdata) { + struct userdata *u = userdata; + + pa_assert(c); + pa_assert(source); + pa_assert(u); + + /* This is us detecting ourselves on load... just ignore this. */ + if (u->ignore) + return PA_HOOK_OK; + + /* There's no point in doing anything if the core is shut down anyway */ + if (c->state == PA_CORE_SHUTDOWN) + return PA_HOOK_OK; + + /* Auto-loaded null-source not active, so ignoring newly detected source. */ + if (u->null_module == PA_INVALID_INDEX) + return PA_HOOK_OK; + + /* This is us detecting ourselves on load in a different way... just ignore this too. */ + if (source->module && source->module->index == u->null_module) + return PA_HOOK_OK; + + /* We don't count filter or monitor sources since they need a real source */ + if (pa_source_is_filter(source) || source->monitor_of != NULL) + return PA_HOOK_OK; + + pa_log_info("A new source has been discovered. Unloading null-source."); + + pa_module_unload_request_by_index(c, u->null_module, true); + u->null_module = PA_INVALID_INDEX; + + return PA_HOOK_OK; +} + +static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_source *source, void* userdata) { + struct userdata *u = userdata; + + pa_assert(c); + pa_assert(source); + pa_assert(u); + + /* First check to see if it's our own null-source that's been removed... */ + if (u->null_module != PA_INVALID_INDEX && source->module && source->module->index == u->null_module) { + pa_log_debug("Autoloaded null-source removed"); + u->null_module = PA_INVALID_INDEX; + return PA_HOOK_OK; + } + + /* There's no point in doing anything if the core is shut down anyway */ + if (c->state == PA_CORE_SHUTDOWN) + return PA_HOOK_OK; + + load_null_source_if_needed(c, source, u); + + return PA_HOOK_OK; +} + +int pa__init(pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + return -1; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); + pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) put_hook_callback, u); + pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) unlink_hook_callback, u); + u->null_module = PA_INVALID_INDEX; + u->ignore = false; + + pa_modargs_free(ma); + + load_null_source_if_needed(m->core, NULL, u); + + return 0; +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->null_module != PA_INVALID_INDEX && m->core->state != PA_CORE_SHUTDOWN) + pa_module_unload_request_by_index(m->core, u->null_module, true); + + pa_xfree(u->source_name); + pa_xfree(u); +} commit c7fe78c9f73ded2c3428666722ec9c1af4b82812 Author: Tanu Kaskinen <[email protected]> Date: Sat Sep 2 18:23:12 2017 +0300 build-sys: add the Arctis configuration diff --git a/src/Makefile.am b/src/Makefile.am index 1d974037..ba2ea97e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1310,7 +1310,8 @@ dist_alsaprofilesets_DATA = \ modules/alsa/mixer/profile-sets/native-instruments-traktorkontrol-s4.conf \ modules/alsa/mixer/profile-sets/native-instruments-korecontroller.conf \ modules/alsa/mixer/profile-sets/kinect-audio.conf \ - modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf + modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf \ + modules/alsa/mixer/profile-sets/steelseries-arctis-usb-audio.conf if HAVE_UDEV dist_udevrules_DATA = \ @@ -1352,7 +1353,10 @@ dist_alsapaths_DATA = \ modules/alsa/mixer/paths/hdmi-output-4.conf \ modules/alsa/mixer/paths/hdmi-output-5.conf \ modules/alsa/mixer/paths/hdmi-output-6.conf \ - modules/alsa/mixer/paths/hdmi-output-7.conf + modules/alsa/mixer/paths/hdmi-output-7.conf \ + modules/alsa/mixer/paths/steelseries-arctis-input.conf \ + modules/alsa/mixer/paths/steelseries-arctis-output-mono.conf \ + modules/alsa/mixer/paths/steelseries-arctis-output-stereo.conf endif commit 9c7a9be7cd907262683c720f4f9afae3f056a4f1 Author: Tanu Kaskinen <[email protected]> Date: Sat Sep 2 15:44:58 2017 +0300 bluetooth: recognize another HSP HS UUID There are actually two HSP HS UUIDs. My theory is that the second one was added, because someone was not happy with the old UUID being used for identifying two different things (the HSP profile as a whole, and the HS role within the HSP profile). Some headsets only use the new UUID, and those headsets won't work if we don't recognize the new UUID. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=93898 diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c index 6eb4e168..1ed436f2 100644 --- a/src/modules/bluetooth/backend-native.c +++ b/src/modules/bluetooth/backend-native.c @@ -335,7 +335,7 @@ static void register_profile(pa_bluetooth_backend *b, const char *profile, const pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &uuid)); dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d); - if (pa_streq (uuid, PA_BLUETOOTH_UUID_HSP_HS)) { + if (pa_bluetooth_uuid_is_hsp_hs(uuid)) { /* In the headset role, the connection will only be initiated from the remote side */ autoconnect = 0; pa_dbus_append_basic_variant_dict_entry(&d, "AutoConnect", DBUS_TYPE_BOOLEAN, &autoconnect); diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index c9283232..304a26e8 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -176,6 +176,7 @@ static bool device_supports_profile(pa_bluetooth_device *device, pa_bluetooth_pr return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE); case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS) + || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS_ALT) || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF); case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG) diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index a3e7bf3d..ad30708f 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -24,7 +24,14 @@ #define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb" #define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb" + +/* There are two HSP HS UUIDs. The first one (older?) is used both as the HSP + * profile identifier and as the HS role identifier, while the second one is + * only used to identify the role. As far as PulseAudio is concerned, the two + * UUIDs mean exactly the same thing. */ #define PA_BLUETOOTH_UUID_HSP_HS "00001108-0000-1000-8000-00805f9b34fb" +#define PA_BLUETOOTH_UUID_HSP_HS_ALT "00001131-0000-1000-8000-00805f9b34fb" + #define PA_BLUETOOTH_UUID_HSP_AG "00001112-0000-1000-8000-00805f9b34fb" #define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb" #define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb" @@ -157,6 +164,10 @@ pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hoo const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile); +static inline bool pa_bluetooth_uuid_is_hsp_hs(const char *uuid) { + return pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS_ALT); +} + #define HEADSET_BACKEND_OFONO 0 #define HEADSET_BACKEND_NATIVE 1 #define HEADSET_BACKEND_AUTO 2 diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 7b5d313b..ebe7c774 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -1960,7 +1960,7 @@ static int uuid_to_profile(const char *uuid, pa_bluetooth_profile_t *_r) { *_r = PA_BLUETOOTH_PROFILE_A2DP_SINK; else if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE)) *_r = PA_BLUETOOTH_PROFILE_A2DP_SOURCE; - else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF)) + else if (pa_bluetooth_uuid_is_hsp_hs(uuid) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF)) *_r = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT; else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG)) *_r = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY; commit 15386a710c1500f70085a6312fb4d84be4d254c9 Author: Johan Heikkilä <[email protected]> Date: Sun Aug 27 16:29:37 2017 +0300 alsa-mixer: add support for Steelseries Arctis 7 headset diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index aeaf12c4..08ea45d3 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -2469,6 +2469,7 @@ static int path_verify(pa_alsa_path *p) { { "analog-input-video", N_("Video") }, { "analog-output", N_("Analog Output") }, { "analog-output-headphones", N_("Headphones") }, + { "analog-output-headphones-mono", N_("Headphones Mono Output") }, { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") }, { "analog-output-lineout", N_("Line Out") }, { "analog-output-mono", N_("Analog Mono Output") }, diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-input.conf b/src/modules/alsa/mixer/paths/steelseries-arctis-input.conf new file mode 100644 index 00000000..f3115ba6 --- /dev/null +++ b/src/modules/alsa/mixer/paths/steelseries-arctis-input.conf @@ -0,0 +1,25 @@ +# 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 <http://www.gnu.org/licenses/>. + +; Steelseries Arctis 7 USB headset microphone path. + +[General] +description-key = analog-input-microphone-headset + +[Element Headset] +volume = merge +switch = mute +override-map.1 = all +override-map.2 = all-left,all-right diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-output-mono.conf b/src/modules/alsa/mixer/paths/steelseries-arctis-output-mono.conf new file mode 100644 index 00000000..67950618 --- /dev/null +++ b/src/modules/alsa/mixer/paths/steelseries-arctis-output-mono.conf @@ -0,0 +1,29 @@ +# 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 <http://www.gnu.org/licenses/>. + +; Steelseries Arctis 7 USB headset mono output path. The headset has two +; output devices. The first one is mono, meant for voice audio, and the +; second one is stereo, meant for everything else. The purpose of this +; unusual design is to provide separate volume controls for voice and +; other audio, which can be useful in gaming. + +[General] +description-key = analog-output-headphones-mono + +[Element PCM] +volume = merge +switch = mute +override-map.1 = all +override-map.2 = all-left,all-right diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-output-stereo.conf b/src/modules/alsa/mixer/paths/steelseries-arctis-output-stereo.conf new file mode 100644 index 00000000..4e10c800 --- /dev/null +++ b/src/modules/alsa/mixer/paths/steelseries-arctis-output-stereo.conf @@ -0,0 +1,27 @@ +# 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 <http://www.gnu.org/licenses/>. + +; Steelseries Arctis 7 USB headset stereo output path. The headset has two +; output devices. The first one is mono, meant for voice audio, and the +; second one is stereo, meant for everything else. The purpose of this +; unusual design is to provide separate volume controls for voice and +; other audio, which can be useful in gaming. +; +; 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 diff --git a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules index 805a05b2..2392ca50 100644 --- a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules +++ b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules @@ -99,5 +99,6 @@ 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}=="1038", ATTRS{idProduct}=="1260", ENV{PULSE_PROFILE_SET}="steelseries-arctis-usb-audio.conf" LABEL="pulseaudio_end" diff --git a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-usb-audio.conf b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-usb-audio.conf new file mode 100644 index 00000000..d3563a16 --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-usb-audio.conf @@ -0,0 +1,43 @@ +# 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 <http://www.gnu.org/licenses/>. + +; Steelseries Arctis 7 USB headset. The headset has a microphone and two output +; devices. The first output device is mono, meant for voice audio, and the +; second one is stereo, meant for everything else. The purpose of this unusual +; design is to provide separate volume controls for voice and other audio, +; which can be useful in gaming. +; +; See default.conf for an explanation on the directives used here. + +[General] +auto-profiles = yes + +[Mapping analog-mono] +device-strings = hw:%f,0,0 +channel-map = mono +paths-output = steelseries-arctis-output-mono +paths-input = steelseries-arctis-input + +[Mapping analog-stereo] +device-strings = hw:%f,1,0 +channel-map = left,right +paths-output = steelseries-arctis-output-stereo +direction = output + +[Profile output:analog-mono+output:analog-stereo+input:analog-mono] +output-mappings = analog-mono analog-stereo +input-mappings = analog-mono +priority = 5100 +skip-probe = yes commit 739a4b3d2318f05eb7101c2baa861e5c636125a5 Author: Ian Ray <[email protected]> Date: Wed Aug 30 11:09:48 2017 +0300 alsa-mixer: round, not truncate, in to_alsa_dB to_alsa_dB() returns a result rounded to two decimal places (instead of using integer truncation) to avoid small errors when converting between dB and volume. Consider playback at -22 dB (which is supported by ALSA) but results in the higher level of -21 dB plus software attenuation. pa_sw_volume_from_dB(-22) = 28172 pa_sw_volume_to_dB(28172) = -21.9997351 to_alsa_dB(-21.9997351) = -2199 ALSA value 106 = -2200 ALSA value 107 = -2100 ... rounding = +1 /* "accurate or first above" */ snd_mixer_selem_ask_playback_dB_vol(me, -2199, rounding, &alsa_val) alsa_val = -2100 Signed-off-by: Ian Ray <[email protected]> diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index f59cad39..aeaf12c4 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -700,7 +700,7 @@ void pa_alsa_path_set_free(pa_alsa_path_set *ps) { } static long to_alsa_dB(pa_volume_t v) { - return (long) (pa_sw_volume_to_dB(v) * 100.0); + return lround(pa_sw_volume_to_dB(v) * 100.0); } static pa_volume_t from_alsa_dB(long v) { commit f0dfddead3cf1ff7af4c9c09a8027fde26065003 Author: Colin Leroy <[email protected]> Date: Sat Aug 26 11:21:15 2017 +0200 cli-command: don't exit on "module already loaded" errors Some modules may only be loaded once, and trying to load them twice from default.pa makes PulseAudio startup fail. While that could be considered a user error, it's nicer to not be so strict. It's not necessarily easy to figure what went wrong, if for example the user plays with RAOP and adds module-raop-discover to default.pa, which first works fine, but suddenly stops working when the user at some point enables RAOP support in paprefs. Enabling RAOP in paprefs makes module-gconf load the module too, so the module gets loaded twice. This patch adds a way to differentiate module load errors, and make cli-command ignore the error when the module is already loaded. diff --git a/src/daemon/main.c b/src/daemon/main.c index 9d99b8fe..55af4eca 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -111,19 +111,21 @@ int deny_severity = LOG_WARNING; int __padsp_disabled__ = 7; #endif -static void signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { +static void signal_callback(pa_mainloop_api* m, pa_signal_event *e, int sig, void *userdata) { + pa_module *module = NULL; + pa_log_info("Got signal %s.", pa_sig2str(sig)); switch (sig) { #ifdef SIGUSR1 case SIGUSR1: - pa_module_load(userdata, "module-cli", NULL); + pa_module_load(&module, userdata, "module-cli", NULL); break; #endif #ifdef SIGUSR2 case SIGUSR2: - pa_module_load(userdata, "module-cli-protocol-unix", NULL); + pa_module_load(&module, userdata, "module-cli-protocol-unix", NULL); break; #endif diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c index d69a77f1..509db443 100644 --- a/src/modules/bluetooth/module-bluetooth-discover.c +++ b/src/modules/bluetooth/module-bluetooth-discover.c @@ -52,13 +52,13 @@ int pa__init(pa_module* m) { u->bluez4_module_idx = PA_INVALID_INDEX; if (pa_module_exists("module-bluez5-discover")) { - mm = pa_module_load(m->core, "module-bluez5-discover", m->argument); + pa_module_load(&mm, m->core, "module-bluez5-discover", m->argument); if (mm) u->bluez5_module_idx = mm->index; } if (pa_module_exists("module-bluez4-discover")) { - mm = pa_module_load(m->core, "module-bluez4-discover", NULL); + pa_module_load(&mm, m->core, "module-bluez4-discover", NULL); if (mm) u->bluez4_module_idx = mm->index; } diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c index 316b9a82..24930450 100644 --- a/src/modules/bluetooth/module-bluetooth-policy.c +++ b/src/modules/bluetooth/module-bluetooth-policy.c @@ -71,6 +71,7 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, const char *s; const char *role; char *args; + pa_module *m = NULL; pa_assert(c); pa_assert(source); @@ -100,7 +101,7 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, /* Load module-loopback */ args = pa_sprintf_malloc("source=\"%s\" source_dont_move=\"true\" sink_input_properties=\"media.role=%s\"", source->name, role); - (void) pa_module_load(c, "module-loopback", args); + (void) pa_module_load(&m, c, "module-loopback", args); pa_xfree(args); return PA_HOOK_OK; @@ -112,6 +113,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void * const char *s; const char *role; char *args; + pa_module *m = NULL; pa_assert(c); pa_assert(sink); @@ -139,7 +141,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void * /* Load module-loopback */ args = pa_sprintf_malloc("sink=\"%s\" sink_dont_move=\"true\" source_output_properties=\"media.role=%s\"", sink->name, role); - (void) pa_module_load(c, "module-loopback", args); + (void) pa_module_load(&m, c, "module-loopback", args); pa_xfree(args); return PA_HOOK_OK; diff --git a/src/modules/bluetooth/module-bluez4-discover.c b/src/modules/bluetooth/module-bluez4-discover.c index cac75106..a4eaee3c 100644 --- a/src/modules/bluetooth/module-bluez4-discover.c +++ b/src/modules/bluetooth/module-bluez4-discover.c @@ -93,7 +93,7 @@ static pa_hook_result_t load_module_for_device(pa_bluez4_discovery *y, const pa_ } pa_log_debug("Loading module-bluez4-device %s", args); - m = pa_module_load(u->module->core, "module-bluez4-device", args); + pa_module_load(&m, u->module->core, "module-bluez4-device", args); pa_xfree(args); if (m) { diff --git a/src/modules/bluetooth/module-bluez5-discover.c b/src/modules/bluetooth/module-bluez5-discover.c index 97ff9435..6dbf24ea 100644 --- a/src/modules/bluetooth/module-bluez5-discover.c +++ b/src/modules/bluetooth/module-bluez5-discover.c @@ -76,7 +76,7 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i", d->path, (int)u->autodetect_mtu); pa_log_debug("Loading module-bluez5-device %s", args); - m = pa_module_load(u->module->core, "module-bluez5-device", args); + pa_module_load(&m, u->module->core, "module-bluez5-device", args); pa_xfree(args); if (m) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 7177455b..5229c046 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1506,7 +1506,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use arg_string = pa_strbuf_to_string(arg_buffer); - if (!(module = pa_module_load(c->core, name, arg_string))) { + if (pa_module_load(&module, c->core, name, arg_string) < 0) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Failed to load module."); goto finish; } diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c index 1e1b8555..7a2b975c 100644 --- a/src/modules/gconf/module-gconf.c +++ b/src/modules/gconf/module-gconf.c @@ -190,7 +190,7 @@ static void load_module( m->items[i].args = pa_xstrdup(args); m->items[i].index = PA_INVALID_INDEX; - if (!(mod = pa_module_load(u->core, name, args))) { + if (pa_module_load(&mod, u->core, name, args) < 0) { pa_log("pa_module_load() failed"); return; } diff --git a/src/modules/jack/module-jackdbus-detect.c b/src/modules/jack/module-jackdbus-detect.c index 26910b43..20db2184 100644 --- a/src/modules/jack/module-jackdbus-detect.c +++ b/src/modules/jack/module-jackdbus-detect.c @@ -111,7 +111,7 @@ static void ensure_ports_started(struct userdata* u) { } else { args = pa_sprintf_malloc("connect=%s", pa_yes_no(u->autoconnect_ports)); } - m = pa_module_load(u->core, modnames[i], args); + pa_module_load(&m, u->core, modnames[i], args); pa_xfree(args); if (m) { diff --git a/src/modules/macosx/module-coreaudio-detect.c b/src/modules/macosx/module-coreaudio-detect.c index d9c09da5..81067ce7 100644 --- a/src/modules/macosx/module-coreaudio-detect.c +++ b/src/modules/macosx/module-coreaudio-detect.c @@ -92,7 +92,7 @@ static int ca_device_added(struct pa_module *m, AudioObjectID id) { args = pa_sprintf_malloc("object_id=%d", (int) id); pa_log_debug("Loading %s with arguments '%s'", DEVICE_MODULE_NAME, args); - mod = pa_module_load(m->core, DEVICE_MODULE_NAME, args); + pa_module_load(&mod, m->core, DEVICE_MODULE_NAME, args); pa_xfree(args); if (!mod) { diff --git a/src/modules/module-allow-passthrough.c b/src/modules/module-allow-passthrough.c index 31ff2707..63b621fb 100644 --- a/src/modules/module-allow-passthrough.c +++ b/src/modules/module-allow-passthrough.c @@ -71,7 +71,7 @@ static pa_sink *ensure_null_sink_for_sink(struct userdata *u, pa_sink *s, pa_cor t = pa_sprintf_malloc("sink_name=allow_passthrough_null_%s sink_properties='device.description=\"%s\"'", name ? name : "", _("Dummy Output")); - m = pa_module_load(c, "module-null-sink", t); + pa_module_load(&m, c, "module-null-sink", t); pa_xfree(t); if (m == NULL) diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c index 12f63f75..246cb94c 100644 --- a/src/modules/module-always-sink.c +++ b/src/modules/module-always-sink.c @@ -80,7 +80,7 @@ static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata* t = pa_sprintf_malloc("sink_name=%s sink_properties='device.description=\"%s\"'", u->sink_name, _("Dummy Output")); - m = pa_module_load(c, "module-null-sink", t); + pa_module_load(&m, c, "module-null-sink", t); u->null_module = m ? m->index : PA_INVALID_INDEX; pa_xfree(t); diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 5a12a135..0c56071b 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -48,7 +48,7 @@ int pa__init(pa_module*m) { pa_log_warn("We will now load module-combine-sink. Please make sure to remove module-combine from your configuration."); - module = pa_module_load(m->core, "module-combine-sink", m->argument); + pa_module_load(&module, m->core, "module-combine-sink", m->argument); u->module_index = module ? module->index : PA_INVALID_INDEX; return module ? 0 : -1; diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c index d6c6b76d..91951640 100644 --- a/src/modules/module-detect.c +++ b/src/modules/module-detect.c @@ -74,6 +74,7 @@ static int detect_alsa(pa_core *c, int just_one) { char line[64], args[64]; unsigned device, subdevice; int is_sink; + pa_module *m = NULL; if (!fgets(line, sizeof(line), f)) break; @@ -101,7 +102,7 @@ static int detect_alsa(pa_core *c, int just_one) { continue; pa_snprintf(args, sizeof(args), "device_id=%u", device); - if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args)) + if (pa_module_load(&m, c, is_sink ? "module-alsa-sink" : "module-alsa-source", args) < 0) continue; n++; @@ -136,6 +137,7 @@ static int detect_oss(pa_core *c, int just_one) { while (!feof(f)) { char line[256], args[64]; unsigned device; + pa_module *m = NULL; if (!fgets(line, sizeof(line), f)) break; @@ -156,14 +158,14 @@ static int detect_oss(pa_core *c, int just_one) { else pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device); - if (!pa_module_load(c, "module-oss", args)) + if (pa_module_load(&m, c, "module-oss", args) < 0) continue; } else if (sscanf(line, "pcm%u: ", &device) == 1) { /* FreeBSD support, the devices are named /dev/dsp0.0, dsp0.1 and so on */ pa_snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device); - if (!pa_module_load(c, "module-oss", args)) + if (pa_module_load(&m, c, "module-oss", args) < 0) continue; } @@ -183,6 +185,7 @@ static int detect_solaris(pa_core *c, int just_one) { struct stat s; const char *dev; char args[64]; + pa_module *m = NULL; dev = getenv("AUDIODEV"); if (!dev) @@ -199,7 +202,7 @@ static int detect_solaris(pa_core *c, int just_one) { pa_snprintf(args, sizeof(args), "device=%s", dev); - if (!pa_module_load(c, "module-solaris", args)) + if (pa_module_load(&m, c, "module-solaris", args) < 0) return 0; return 1; @@ -208,11 +211,12 @@ static int detect_solaris(pa_core *c, int just_one) { #ifdef OS_IS_WIN32 static int detect_waveout(pa_core *c, int just_one) { + pa_module *m = NULL; /* * FIXME: No point in enumerating devices until the plugin supports * selecting anything but the first. */ - if (!pa_module_load(c, "module-waveout", "")) + if (pa_module_load(&m, c, "module-waveout", "") < 0) return 0; return 1; diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c index 6ca062ba..6d4475dd 100644 --- a/src/modules/module-filter-apply.c +++ b/src/modules/module-filter-apply.c @@ -574,7 +574,7 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i pa_log_debug("Loading %s with arguments '%s'", module_name, args); - if ((m = pa_module_load(u->core, module_name, args))) { + if (pa_module_load(&m, u->core, module_name, args) >= 0) { find_filters_for_module(u, m, want, parameters); filter = pa_hashmap_get(u->filters, fltr); done_something = true; diff --git a/src/modules/module-hal-detect-compat.c b/src/modules/module-hal-detect-compat.c index 719e357c..e326340c 100644 --- a/src/modules/module-hal-detect-compat.c +++ b/src/modules/module-hal-detect-compat.c @@ -64,7 +64,7 @@ int pa__init(pa_module*m) { pa_log_warn("We will now load module-udev-detect. Please make sure to remove module-hal-detect from your configuration."); t = pa_sprintf_malloc("tsched=%s", pa_yes_no(tsched)); - n = pa_module_load(m->core, "module-udev-detect", t); + pa_module_load(&n, m->core, "module-udev-detect", t); pa_xfree(t); if (n) diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 3d7064f1..6ea6034a 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -332,7 +332,7 @@ static void verify_access(struct userdata *u, struct device *d) { if (pa_ratelimit_test(&d->ratelimit, PA_LOG_DEBUG)) { pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args); - m = pa_module_load(u->core, "module-alsa-card", d->args); + pa_module_load(&m, u->core, "module-alsa-card", d->args); if (m) { d->module = m->index; diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c index e7dbe94c..e2b95cad 100644 --- a/src/modules/module-volume-restore.c +++ b/src/modules/module-volume-restore.c @@ -65,7 +65,7 @@ int pa__init(pa_module*m) { pa_log_warn("We will now load module-stream-restore. Please make sure to remove module-volume-restore from your configuration."); t = pa_sprintf_malloc("restore_volume=%s restore_device=%s", pa_yes_no(restore_volume), pa_yes_no(restore_device)); - n = pa_module_load(m->core, "module-stream-restore", t); + pa_module_load(&n, m->core, "module-stream-restore", t); pa_xfree(t); if (n) diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c index 96476b7a..a170380c 100644 --- a/src/modules/module-zeroconf-discover.c +++ b/src/modules/module-zeroconf-discover.c @@ -242,7 +242,7 @@ static void resolver_cb( pa_log_debug("Loading %s with arguments '%s'", module_name, args); - if ((m = pa_module_load(u->core, module_name, args))) { + if (pa_module_load(&m, u->core, module_name, args) >= 0) { tnl->module_index = m->index; pa_hashmap_put(u->tunnels, tnl, tnl); tnl = NULL; diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c index 9c7ac3cd..d65615b2 100644 --- a/src/modules/raop/module-raop-discover.c +++ b/src/modules/raop/module-raop-discover.c @@ -310,7 +310,7 @@ static void resolver_cb( pa_log_debug("Loading module-raop-sink with arguments '%s'", args); - if ((m = pa_module_load(u->core, "module-raop-sink", args))) { + if (pa_module_load(&m, u->core, "module-raop-sink", args) >= 0) { tnl->module_index = m->index; pa_hashmap_put(u->tunnels, tnl, tnl); tnl = NULL; diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 0d56ba94..44795b0d 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -421,6 +421,8 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) { const char *name; + pa_error_code_t err; + pa_module *m = NULL; pa_core_assert_ref(c); pa_assert(t); @@ -432,9 +434,13 @@ static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool return -1; } - if (!pa_module_load(c, name, pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "Module load failed.\n"); - return -1; + if ((err = pa_module_load(&m, c, name, pa_tokenizer_get(t, 2))) < 0) { + if (err == PA_ERR_EXIST) { + pa_strbuf_puts(buf, "Module already loaded; ignoring.\n"); + } else { + pa_strbuf_puts(buf, "Module load failed.\n"); + return -1; + } } return 0; diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c index ac158159..0478b6fb 100644 --- a/src/pulsecore/module.c +++ b/src/pulsecore/module.c @@ -107,17 +107,21 @@ void pa_module_hook_connect(pa_module *m, pa_hook *hook, pa_hook_priority_t prio pa_dynarray_append(m->hooks, pa_hook_connect(hook, prio, cb, data)); } -pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { +int pa_module_load(pa_module** module, pa_core *c, const char *name, const char *argument) { pa_module *m = NULL; bool (*load_once)(void); const char* (*get_deprecated)(void); pa_modinfo *mi; + int errcode; + pa_assert(module); pa_assert(c); pa_assert(name); - if (c->disallow_module_loading) + if (c->disallow_module_loading) { + errcode = -PA_ERR_ACCESS; goto fail; + } m = pa_xnew(pa_module, 1); m->name = pa_xstrdup(name); @@ -135,6 +139,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { * loader, which never finds anything, and therefore says "file not * found". */ pa_log("Failed to open module \"%s\".", name); + errcode = -PA_ERR_IO; goto fail; } @@ -150,6 +155,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { PA_IDXSET_FOREACH(i, c->modules, idx) { if (pa_streq(name, i->name)) { pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name); + errcode = -PA_ERR_EXIST; goto fail; } } @@ -165,6 +171,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) { pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name); + errcode = -PA_ERR_IO; goto fail; } @@ -179,6 +186,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { if (m->init(m) < 0) { pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : ""); + errcode = -PA_ERR_IO; goto fail; } @@ -202,7 +210,9 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_NEW], m); - return m; + *module = m; + + return 0; fail: @@ -225,7 +235,9 @@ fail: pa_xfree(m); } - return NULL; + *module = NULL; + + return errcode; } static void postponed_dlclose(pa_mainloop_api *api, void *userdata) { diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h index 41e2189c..ec2de0b9 100644 --- a/src/pulsecore/module.h +++ b/src/pulsecore/module.h @@ -52,7 +52,7 @@ struct pa_module { bool pa_module_exists(const char *name); -pa_module* pa_module_load(pa_core *c, const char *name, const char *argument); +int pa_module_load(pa_module** m, pa_core *c, const char *name, const char *argument); void pa_module_unload(pa_module *m, bool force); void pa_module_unload_by_index(pa_core *c, uint32_t idx, bool force); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 866e2c64..6ff5ed40 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -4468,7 +4468,7 @@ static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name) && !strchr(name, '/'), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID); - if (!(m = pa_module_load(c->protocol->core, name, argument))) { + if (pa_module_load(&m, c->protocol->core, name, argument) < 0) { pa_pstream_send_error(c->pstream, tag, PA_ERR_MODINITFAILED); return; } commit 1a66715320a254ed5d45d4c555d9da55ea3f8682 Author: Tanu Kaskinen <[email protected]> Date: Thu Aug 17 20:24:39 2017 +0300 main: set umask to 077 instead of 022 It was reported that PulseAudio weakens the umask to 022 if it's initially set to 077. That's not as big problem as it might seem, but it's still a problem. The umask affects the permissions of the state files, and those aren't readable by other users anyway in the per-user mode, because PulseAudio puts them in directories that aren't accessible to other users. In the system mode the state files will be readable by everyone, though, even by those users that don't otherwise have access to PulseAudio. The state files are slightly privacy-sensitive, because they contain e.g. history of applications that have used PulseAudio. I can't think of any use cases where access to the state files by other users would be necessary, either in the per-user mode or in the system mode, so let's use umask 077. This doesn't prevent access to any sockets in the system mode, because all directories that PulseAudio creates in the system mode will have permissions 755 regardless of the umask, and the sockets themselves always have permissions 777. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=102060 diff --git a/src/daemon/main.c b/src/daemon/main.c index f35252d0..9d99b8fe 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -888,7 +888,7 @@ int main(int argc, char *argv[]) { pa_set_env_and_record("PULSE_INTERNAL", "1"); pa_assert_se(chdir("/") == 0); - umask(0022); + umask(0077); #ifdef HAVE_SYS_RESOURCE_H set_all_rlimits(conf); commit 95404ce3f398a6882a73c09264a44cc8a9e394ec Author: Tanu Kaskinen <[email protected]> Date: Fri Aug 18 10:07:27 2017 +0300 cli-command: unload modules synchronously Users may want to change the parameters of some load-once modules in ~/.config/pulse/default.pa. That should be possible by including /etc/pulse/default.pa from the per-user configuration file, and then unloading a module and reloading it with different parameters. However, that doesn't work, because the unload-module command will not unload the module immediately, so the subsequent load-module command will fail when the module can be loaded only once. This patch makes the module unloading synchronous. "pacmd unload-module module-cli-protocol-unix" is something that might not like this change, since the command will unload the code that is processing the command, but I tested it and it works fine. When pa_module_unload() is called, that won't yet remove the module code from memory, the lt_dlclose() call is postponed until it's safe to remove the code from memory. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=102205 diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 01fea475..0d56ba94 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -462,13 +462,13 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bo return -1; } - pa_module_unload_request(m, false); + pa_module_unload(m, false); } else { PA_IDXSET_FOREACH(m, c->modules, idx) if (pa_streq(i, m->name)) { unloaded = true; - pa_module_unload_request(m, false); + pa_module_unload(m, false); } if (unloaded == false) { commit 5f27c2ec2fb0477f213e6794113182eaee1c43ba Author: Tanu Kaskinen <[email protected]> Date: Mon Jul 31 09:42:31 2017 +0300 device-port, switch-on-port-available: fix automatic profile changing when current profile is off module-switch-on-port-available didn't do anything when a port changes its status if the card didn't have any sinks or sources. This was to avoid bad things during card initialization, but the if condition also prevented any profile switches away from the "off" profile, because the card has no sinks or sources when the "off" profile is active. pa_card nowadays has the "linked" flag that module-switch-on-port-available could have checked instead, but since it doesn't make sense to emit port status change events before the card has been initialized, I added the check in pa_device_port_set_available() instead. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=101794 diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c index b9a0f3b3..4020987f 100644 --- a/src/modules/module-switch-on-port-available.c +++ b/src/modules/module-switch-on-port-available.c @@ -281,11 +281,6 @@ static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port return PA_HOOK_OK; } - if (pa_idxset_size(port->card->sinks) == 0 && pa_idxset_size(port->card->sources) == 0) - /* This card is not initialized yet. We'll handle it in - sink_new / source_new callbacks later. */ - return PA_HOOK_OK; - switch (port->available) { case PA_AVAILABLE_YES: switch_to_port(port); diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c index 76a7e80a..5cf4ac63 100644 --- a/src/pulsecore/device-port.c +++ b/src/pulsecore/device-port.c @@ -92,7 +92,7 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) { * before the card object has been created. The card object should probably * be created before port objects, and then p->card could be non-NULL for * the whole lifecycle of pa_device_port. */ - if (p->card) { + if (p->card && p->card->linked) { /* A sink or source whose active port is unavailable can't be the * default sink/source, so port availability changes may affect the * default sink/source choice. */ commit 4e6d9e321400cbb660b4373db6b50bea71707641 Author: Tanu Kaskinen <[email protected]> Date: Fri Aug 4 16:43:02 2017 +0300 build-sys: add iec958-stereo-input.conf to dist_alsapaths_DATA diff --git a/src/Makefile.am b/src/Makefile.am index 3ff1139f..1d974037 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1343,6 +1343,7 @@ dist_alsapaths_DATA = \ modules/alsa/mixer/paths/analog-output-headphones-2.conf \ modules/alsa/mixer/paths/analog-output-lineout.conf \ modules/alsa/mixer/paths/analog-output-mono.conf \ + modules/alsa/mixer/paths/iec958-stereo-input.conf \ modules/alsa/mixer/paths/iec958-stereo-output.conf \ modules/alsa/mixer/paths/hdmi-output-0.conf \ modules/alsa/mixer/paths/hdmi-output-1.conf \ commit ec325304cdca5e2a46f5a84f93c8b614a204d87f Author: Tanu Kaskinen <[email protected]> Date: Fri Aug 4 11:14:43 2017 +0300 alsa-mixer: set PCM Capture Source for iec958 input It was reported that on a certain USB card, identified as "0d8c:0102 C-Media Electronics, Inc. CM106 Like Sound Device", the "PCM Capture Source" element had to be set to "IEC958 In" before the iec958 input would work. The iec958-stereo-input.conf file didn't exist before, although the path was referenced in the default.conf profile configuration file. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=101973 diff --git a/src/modules/alsa/mixer/paths/iec958-stereo-input.conf b/src/modules/alsa/mixer/paths/iec958-stereo-input.conf new file mode 100644 index 00000000..babc8398 --- /dev/null +++ b/src/modules/alsa/mixer/paths/iec958-stereo-input.conf @@ -0,0 +1,20 @@ +# 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 <http://www.gnu.org/licenses/>. + +[Element PCM Capture Source] +enumeration = select + +[Option PCM Capture Source:IEC958 In] +name = iec958-input commit 295d4db8cfeca8d257f73fa29821e4a662790268 Author: Tanu Kaskinen <[email protected]> Date: Fri Jul 28 04:07:49 2017 +0300 raop: silence a Coverity complaint CID: 1398155 diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c index e5d219e8..4d13927f 100644 --- a/src/modules/raop/raop-sink.c +++ b/src/modules/raop/raop-sink.c @@ -391,6 +391,13 @@ static void thread_func(void *userdata) { if (!pa_raop_client_can_stream(u->raop)) continue; + /* This assertion is meant to silence a complaint from Coverity about + * pollfd being possibly NULL when we access it later. That's a false + * positive, because we check pa_raop_client_can_stream() above, and if + * that returns true, it means that the connection is up, and when the + * connection is up, pollfd will be non-NULL. */ + pa_assert(pollfd); + if (u->memchunk.length <= 0) { if (u->memchunk.memblock) pa_memblock_unref(u->memchunk.memblock); commit e03e01b0891debe691267dd74bff30c0a4938831 Author: Georg Chini <[email protected]> Date: Sat Jul 22 10:47:07 2017 +0200 bluez5-device: Set transport state correctly for AG role When connecting a headset via the native backend, the transport state was not updated correctly. This patch sets the state to PLAYING in transport_acquire() if necessary. diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index c0e681b1..7b5d313b 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -77,6 +77,7 @@ static const char* const valid_modargs[] = { enum { BLUETOOTH_MESSAGE_IO_THREAD_FAILED, BLUETOOTH_MESSAGE_STREAM_FD_HUP, + BLUETOOTH_MESSAGE_SET_TRANSPORT_PLAYING, BLUETOOTH_MESSAGE_MAX }; @@ -755,9 +756,18 @@ static int transport_acquire(struct userdata *u, bool optional) { if (u->stream_fd < 0) return u->stream_fd; + /* transport_acquired must be set before calling + * pa_bluetooth_transport_set_state() */ u->transport_acquired = true; pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd); + if (u->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_IDLE) { + if (pa_thread_mq_get() != NULL) + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_SET_TRANSPORT_PLAYING, NULL, 0, NULL, NULL); + else + pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING); + } + return 0; } @@ -2217,6 +2227,16 @@ static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t o if (u->transport->state > PA_BLUETOOTH_TRANSPORT_STATE_IDLE) pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_IDLE); break; + case BLUETOOTH_MESSAGE_SET_TRANSPORT_PLAYING: + /* transport_acquired needs to be checked here, because a message could have been + * pending when the profile was switched. If the new transport has been acquired + * correctly, the call below will have no effect because the transport state is + * already PLAYING. If transport_acquire() failed for the new profile, the transport + * state should not be changed. If the transport has been released for other reasons + * (I/O thread shutdown), transport_acquired will also be false. */ + if (u->transport_acquired) + pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING); + break; } return 0; commit 9e725ac3dbe024e6a4f10575f8cf1b80e8223dbe Author: Tanu Kaskinen <[email protected]> Date: Wed Jul 12 22:29:56 2017 +0300 equalizer-sink: update sink description when moving If the description is not updated when moving, the old automatically generated description will refer to the old master sink after the move, which is not nice. diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 280ca25f..489062fc 100644 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -127,6 +127,8 @@ struct userdata { pa_database *database; char **base_profiles; + + bool automatic_description; }; static const char* const valid_modargs[] = { @@ -1080,6 +1082,17 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { if (dest) { pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); + + if (u->automatic_description) { + const char *master_description; + char *new_description; + + master_description = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION); + new_description = pa_sprintf_malloc(_("FFT based equalizer on %s"), + master_description ? master_description : dest->name); + pa_sink_set_description(u->sink, new_description); + pa_xfree(new_description); + } } else pa_sink_set_asyncmsgq(u->sink, NULL); } @@ -1089,7 +1102,6 @@ int pa__init(pa_module*m) { pa_sample_spec ss; pa_channel_map map; pa_modargs *ma; - const char *z; pa_sink *master; pa_sink_input_new_data sink_input_data; pa_sink_new_data sink_data; @@ -1185,9 +1197,6 @@ int pa__init(pa_module*m) { pa_sink_new_data_set_sample_spec(&sink_data, &ss); pa_sink_new_data_set_channel_map(&sink_data, &map); - z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); - pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer on %s", z ? z : master->name); - pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); @@ -1197,6 +1206,15 @@ int pa__init(pa_module*m) { goto fail; } + if (!pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION)) { + const char *master_description; + + master_description = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, + _("FFT based equalizer on %s"), master_description ? master_description : master->name); + u->automatic_description = true; + } + u->autoloaded = DEFAULT_AUTOLOADED; if (pa_modargs_get_value_boolean(ma, "autoloaded", &u->autoloaded) < 0) { pa_log("Failed to parse autoloaded value"); commit 1d29247ac7f58fe1a766fa7c0ea3856f6820e15e Author: Georg Chini <[email protected]> Date: Mon Apr 17 21:37:43 2017 +0200 loopback: Use new allow_negative flag of pa_{source, sink}_get_latency_within_thread() Setting the allow_negative flag of pa_{source,sink}_get_latency_within_thread() to true leads to improved end to end latency estimation and to correct handling of negative port latency offsets. diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index ded59e1a..ab38c292 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -110,12 +110,12 @@ struct userdata { /* Used for sink input and source output snapshots */ struct { int64_t send_counter; - pa_usec_t source_latency; + int64_t source_latency; pa_usec_t source_timestamp; int64_t recv_counter; size_t loopback_memblockq_length; - pa_usec_t sink_latency; + int64_t sink_latency; pa_usec_t sink_timestamp; } latency_snapshot; @@ -125,9 +125,9 @@ struct userdata { /* Output thread variables */ struct { int64_t recv_counter; + pa_usec_t effective_source_latency; /* Copied from main thread */ - pa_usec_t effective_source_latency; pa_usec_t minimum_latency; /* Various booleans */ @@ -305,7 +305,8 @@ static void adjust_rates(struct userdata *u) { size_t buffer; uint32_t old_rate, base_rate, new_rate, run_hours; int32_t latency_difference; - pa_usec_t current_buffer_latency, snapshot_delay, current_source_sink_latency, current_latency, latency_at_optimum_rate; + pa_usec_t current_buffer_latency, snapshot_delay; + int64_t current_source_sink_latency, current_latency, latency_at_optimum_rate; pa_usec_t final_latency; pa_assert(u); @@ -352,7 +353,7 @@ static void adjust_rates(struct userdata *u) { latency_at_optimum_rate = current_source_sink_latency + current_buffer_latency * old_rate / base_rate; final_latency = PA_MAX(u->latency, u->minimum_latency); - latency_difference = (int32_t)((int64_t)latency_at_optimum_rate - final_latency); + latency_difference = (int32_t)(latency_at_optimum_rate - final_latency); pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms", (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC, @@ -466,12 +467,23 @@ static void update_latency_boundaries(struct userdata *u, pa_source *source, pa_ /* Called from output context * Sets the memblockq to the configured latency corrected by latency_offset_usec */ -static void memblockq_adjust(struct userdata *u, pa_usec_t latency_offset_usec, bool allow_push) { +static void memblockq_adjust(struct userdata *u, int64_t latency_offset_usec, bool allow_push) { size_t current_memblockq_length, requested_memblockq_length, buffer_correction; - pa_usec_t requested_buffer_latency, final_latency; + int64_t requested_buffer_latency; + pa_usec_t final_latency, requested_sink_latency; final_latency = PA_MAX(u->latency, u->output_thread_info.minimum_latency); - requested_buffer_latency = PA_CLIP_SUB(final_latency, latency_offset_usec); + + /* If source or sink have some large negative latency offset, we might want to + * hold more than final_latency in the memblockq */ + requested_buffer_latency = (int64_t)final_latency - latency_offset_usec; + + /* Keep at least one sink latency in the queue to make sure that the sink + * never underruns initially */ + requested_sink_latency = pa_sink_get_requested_latency_within_thread(u->sink_input->sink); + if (requested_buffer_latency < (int64_t)requested_sink_latency) + requested_buffer_latency = requested_sink_latency; + requested_memblockq_length = pa_usec_to_bytes(requested_buffer_latency, &u->sink_input->sample_spec); current_memblockq_length = pa_memblockq_get_length(u->memblockq); @@ -492,7 +504,8 @@ static void memblockq_adjust(struct userdata *u, pa_usec_t latency_offset_usec, /* Called from input thread context */ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { struct userdata *u; - pa_usec_t push_time, current_source_latency; + pa_usec_t push_time; + int64_t current_source_latency; pa_source_output_assert_ref(o); pa_source_output_assert_io_context(o); @@ -500,9 +513,9 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) /* Send current source latency and timestamp with the message */ push_time = pa_rtclock_now(); - current_source_latency = pa_source_get_latency_within_thread(u->source_output->source, false); + current_source_latency = pa_source_get_latency_within_thread(u->source_output->source, true); - pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_POST, PA_UINT_TO_PTR(current_source_latency), push_time, chunk, NULL); + pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_POST, PA_INT_TO_PTR(current_source_latency), push_time, chunk, NULL); u->send_counter += (int64_t) chunk->length; } @@ -531,7 +544,7 @@ static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, u->latency_snapshot.send_counter = u->send_counter; /* Add content of delay memblockq to the source latency */ - u->latency_snapshot.source_latency = pa_source_get_latency_within_thread(u->source_output->source, false) + + u->latency_snapshot.source_latency = pa_source_get_latency_within_thread(u->source_output->source, true) + pa_bytes_to_usec(length, &u->source_output->source->sample_spec); u->latency_snapshot.source_timestamp = pa_rtclock_now(); @@ -813,14 +826,14 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in * are enabled. Disable them on first push and correct the memblockq. If pop * has not been called yet, wait until the pop_cb() requests the adjustment */ if (u->output_thread_info.pop_called && (!u->output_thread_info.push_called || u->output_thread_info.pop_adjust)) { - pa_usec_t time_delta; + int64_t time_delta; /* This is the source latency at the time push was called */ - time_delta = PA_PTR_TO_UINT(data); + time_delta = PA_PTR_TO_INT(data); /* Add the time between push and post */ time_delta += pa_rtclock_now() - (pa_usec_t) offset; /* Add the sink latency */ - time_delta += pa_sink_get_latency_within_thread(u->sink_input->sink, false); + time_delta += pa_sink_get_latency_within_thread(u->sink_input->sink, true); /* The source latency report includes the audio in the chunk, * but since we already pushed the chunk to the memblockq, we need @@ -840,9 +853,9 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in * next push also contains too much data, and in that case the * resulting latency will be wrong. */ if (pa_bytes_to_usec(chunk->length, &u->sink_input->sample_spec) > u->output_thread_info.effective_source_latency) - time_delta = PA_CLIP_SUB(time_delta, u->output_thread_info.effective_source_latency); + time_delta -= (int64_t)u->output_thread_info.effective_source_latency; else - time_delta = PA_CLIP_SUB(time_delta, pa_bytes_to_usec(chunk->length, &u->sink_input->sample_spec)); + time_delta -= (int64_t)pa_bytes_to_usec(chunk->length, &u->sink_input->sample_spec); /* FIXME: We allow pushing silence here to fix up the latency. This * might lead to a gap in the stream */ @@ -895,7 +908,7 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in u->latency_snapshot.recv_counter = u->output_thread_info.recv_counter; u->latency_snapshot.loopback_memblockq_length = pa_memblockq_get_length(u->memblockq); /* Add content of render memblockq to sink latency */ - u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink, false) + + u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink, true) + pa_bytes_to_usec(length, &u->sink_input->sink->sample_spec); u->latency_snapshot.sink_timestamp = pa_rtclock_now(); commit 60c0edd5286dbb731c671ad3e6886c1e3e1eb067 Author: Hui Wang <[email protected]> Date: Fri May 26 15:42:40 2017 +0800 alsa-mixer: Add support for usb audio in the Dell dock TB16 There are one headset jack on the front panel of TB16, through this jack, we have one stereo headphone output (hw:%f,0,0) and one mono headset-mic input (hw:%f,0,0); and there is one speaker output jack (hw:%f,1,0) on the rear panel of TB16. The detail information of the Dell dock TB16: http://www.dell.com/support/article/sg/en/sgbsdt1/SLN301105 Signed-off-by: Hui Wang <[email protected]> diff --git a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules index 70e34e6f..805a05b2 100644 --- a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules +++ b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules @@ -98,5 +98,6 @@ ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1021", ENV{PULSE_PROFILE_SET}="nativ ATTRS{idVendor}=="0763", ATTRS{idProduct}=="2012", ENV{PULSE_PROFILE_SET}="maudio-fasttrack-pro.conf" 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" LABEL="pulseaudio_end" diff --git a/src/modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf b/src/modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf new file mode 100644 index 00000000..11865524 --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf @@ -0,0 +1,55 @@ +# 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 <http://www.gnu.org/licenses/>. + +; Dell Dock TB16 USB audio +; +; This card has two stereo pairs of output, One Mono input. +; +; See default.conf for an explanation on the directives used here. + +[General] +auto-profiles = no + +[Mapping analog-stereo-headphone] +description = Headphone +device-strings = hw:%f,0,0 +channel-map = left,right +direction = output + +[Mapping analog-stereo-speaker] +description = Speaker +device-strings = hw:%f,1,0 +channel-map = left,right +direction = output + +[Mapping analog-stereo-mic] +description = Headset-Mic +device-strings = hw:%f,0,0 +channel-map = left,right +direction = input + + +[Profile output:analog-stereo-speaker] +description = Speaker +output-mappings = analog-stereo-speaker +priority = 60 +skip-probe = yes + +[Profile output:analog-stereo-headphone+input:analog-stereo-mic] +description = Headset +output-mappings = analog-stereo-headphone +input-mappings = analog-stereo-mic +priority = 80 +skip-probe = yes commit 66885ad633db0f371693475c72133e91f1e09ee5 Author: Khem Raj <[email protected]> Date: Mon Apr 6 21:56:31 2015 -0700 padsp: Make it compile on musl break assumptions on glibc and there is no stat64 on non glibc C libraries See pulseaudio bug https://bugs.freedesktop.org/show_bug.cgi?id=85319 Signed-off-by: Khem Raj <[email protected]> diff --git a/src/utils/padsp.c b/src/utils/padsp.c index 251f2011..a53b161c 100644 --- a/src/utils/padsp.c +++ b/src/utils/padsp.c @@ -2394,7 +2394,7 @@ fail: return ret; } -#ifdef sun +#ifndef __GLIBC__ int ioctl(int fd, int request, ...) { #else int ioctl(int fd, unsigned long request, ...) { @@ -2534,10 +2534,13 @@ int stat(const char *pathname, struct stat *buf) { return 0; } - #ifdef HAVE_OPEN64 - +#undef stat64 +#ifdef __GLIBC__ int stat64(const char *pathname, struct stat64 *buf) { +#else +int stat64(const char *pathname, struct stat *buf) { +#endif struct stat oldbuf; int ret; @@ -2570,7 +2573,7 @@ int stat64(const char *pathname, struct stat64 *buf) { return 0; } - +#undef open64 int open64(const char *filename, int flags, ...) { va_list args; mode_t mode = 0; @@ -2696,8 +2699,8 @@ FILE* fopen(const char *filename, const char *mode) { } #ifdef HAVE_OPEN64 - -FILE *fopen64(const char *filename, const char *mode) { +#undef fopen64 +FILE *fopen64(const char *__restrict filename, const char *__restrict mode) { debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen64(%s)\n", filename?filename:"NULL"); commit 46fb1b8c5dfeb9611ba9035ff7a09dbb620c54ea Author: KimJeongYeon <[email protected]> Date: Sat Apr 29 11:32:23 2017 +0200 filter-apply: Do not re-route stream to master sink/source when manually moved to filter Currently, if a stream is manually moved to a filter sink or source managed by module-filter-apply, the stream will be silently re-routed to the master sink or source, because the filter.apply property is not set on that stream. We can assume, that the users intention however was to have the stream filtered. Therefore this patch changes the logic, so that the stream will not be moved to the master but remains on the filter sink or source. To handle the change of a property correctly, the filter.apply property must be set temporarily. An additional property filter.apply.set_by_mfa was introduced to mark those streams, so that filter.apply can be removed again when the stream moves away from the filter. diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c index e91fb37d..6ca062ba 100644 --- a/src/modules/module-filter-apply.c +++ b/src/modules/module-filter-apply.c @@ -39,6 +39,7 @@ #define PA_PROP_FILTER_APPLY_PARAMETERS PA_PROP_FILTER_APPLY".%s.parameters" #define PA_PROP_FILTER_APPLY_MOVING "filter.apply.moving" +#define PA_PROP_FILTER_APPLY_SET_BY_MFA "filter.apply.set_by_mfa" #define PA_PROP_MDM_AUTO_FILTERED "module-device-manager.auto_filtered" PA_MODULE_AUTHOR("Colin Guthrie"); @@ -161,6 +162,67 @@ static const char* get_filter_parameters(pa_object *o, const char *want, bool is return parameters; } +/* This function is used to set or unset the filter related stream properties. This is necessary + * if a stream does not have filter.apply set and is manually moved to a filter sink or source. + * In this case, the properies must be temporarily set and removed when the stream is moved away + * from the filter. */ +static void set_filter_properties(pa_proplist *pl, struct filter *filter, bool set_properties) { + char *prop_parameters; + + if (set_properties) { + pa_assert(filter); + + pa_proplist_sets(pl, PA_PROP_FILTER_APPLY, filter->name); + + if (filter->parameters) { + prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, filter->name); + pa_proplist_sets(pl, prop_parameters, filter->parameters); + pa_xfree(prop_parameters); + } + + pa_proplist_sets(pl, PA_PROP_FILTER_APPLY_SET_BY_MFA, "1"); + + } else { + const char *old_filter_name = NULL; + + if (filter) + old_filter_name = filter->name; + else + old_filter_name = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY); + + /* If the filter name cannot be determined, properties cannot be removed. */ + if (!old_filter_name) + return; + + prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, old_filter_name); + pa_proplist_unset(pl, prop_parameters); + pa_xfree(prop_parameters); + + pa_proplist_unset(pl, PA_PROP_FILTER_APPLY); + pa_proplist_unset(pl, PA_PROP_FILTER_APPLY_SET_BY_MFA); + } +} + +static struct filter* get_filter_for_object(struct userdata *u, pa_object *o, bool is_sink_input) { + pa_sink *sink = NULL; + pa_source *source = NULL; + struct filter *filter = NULL; + void *state; + + if (is_sink_input) + sink = PA_SINK_INPUT(o)->sink; + else + source = PA_SOURCE_OUTPUT(o)->source; + + PA_HASHMAP_FOREACH(filter, u->filters, state) { + if ((is_sink_input && sink == filter->sink) || (!is_sink_input && source == filter->source)) { + return filter; + } + } + + return NULL; +} + static bool should_group_filter(struct filter *filter) { return pa_streq(filter->name, "echo-cancel"); } @@ -309,7 +371,7 @@ static int do_move(struct userdata *u, pa_object *obj, pa_object *parent, bool i } } -static void move_object_for_filter(struct userdata *u, pa_object *o, struct filter* filter, bool restore, bool is_sink_input) { +static void move_object_for_filter(struct userdata *u, pa_object *o, struct filter *filter, bool restore, bool is_sink_input) { pa_object *parent; pa_proplist *pl; const char *name; @@ -343,7 +405,7 @@ static void move_object_for_filter(struct userdata *u, pa_object *o, struct filt pa_proplist_unset(pl, PA_PROP_FILTER_APPLY_MOVING); } -static void move_objects_for_filter(struct userdata *u, pa_object *o, struct filter* filter, bool restore, +static void move_objects_for_filter(struct userdata *u, pa_object *o, struct filter *filter, bool restore, bool is_sink_input) { if (!should_group_filter(filter)) @@ -431,7 +493,7 @@ static bool can_unload_module(struct userdata *u, uint32_t idx) { return true; } -static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_input) { +static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_input, bool is_property_change) { const char *want; const char *parameters; bool done_something = false; @@ -440,24 +502,23 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i pa_module *module = NULL; char *module_name = NULL; struct filter *fltr = NULL, *filter = NULL; + pa_proplist *pl; if (is_sink_input) { - sink = PA_SINK_INPUT(o)->sink; - - if (sink) + if ((sink = PA_SINK_INPUT(o)->sink)) module = sink->module; + pl = PA_SINK_INPUT(o)->proplist; } else { - source = PA_SOURCE_OUTPUT(o)->source; - - if (source) + if ((source = PA_SOURCE_OUTPUT(o)->source)) module = source->module; + pl = PA_SOURCE_OUTPUT(o)->proplist; } /* If there is no sink/source yet, we can't do much */ if ((is_sink_input && !sink) || (!is_sink_input && !source)) goto done; - /* If the stream doesn't what any filter, then let it be. */ + /* If the stream doesn't want any filter, then let it be. */ if ((want = get_filter_name(o, is_sink_input))) { /* We need to ensure the SI is playing on a sink of this type * attached to the sink it's "officially" playing on */ @@ -471,6 +532,24 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i goto done; } + /* If the stream originally did not have the filter.apply property set and is + * manually moved away from the filter, remove the filter properties from the + * stream */ + if (pa_proplist_gets(pl, PA_PROP_FILTER_APPLY_SET_BY_MFA)) { + + set_filter_properties(pl, NULL, false); + + /* If the new sink/source is also a filter, the stream has been moved from + * one filter to another, so add the properties for the new filter. */ + if ((filter = get_filter_for_object(u, o, is_sink_input))) + set_filter_properties(pl, filter, true); + + done_something = true; + goto done; + } + + /* The stream needs be moved to a filter. */ + /* Some filter modules might require parameters by default. * (e.g 'plugin', 'label', 'control' of module-ladspa-sink) */ parameters = get_filter_parameters(o, want, is_sink_input); @@ -515,23 +594,28 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i done_something = true; } } else { - void *state; + /* The filter.apply property is not set. If the stream is nevertheless using a + * filter sink/source, it either has been moved to the filter manually or the + * user just removed the filter.apply property. */ - /* We do not want to filter... but are we already filtered? - * This can happen if an input's proplist changes */ - PA_HASHMAP_FOREACH(filter, u->filters, state) { - if ((is_sink_input && sink == filter->sink) || (!is_sink_input && source == filter->source)) { + if ((filter = get_filter_for_object(u, o, is_sink_input))) { + if (is_property_change) { + /* 'filter.apply' has been manually unset. Do restore. */ move_objects_for_filter(u, o, filter, true, is_sink_input); + set_filter_properties(pl, filter, false); done_something = true; - break; + } else { + /* Stream has been manually moved to a filter sink/source + * without 'filter.apply' set. Leave sink as it is. */ + set_filter_properties(pl, filter, true); } } } +done: if (done_something) trigger_housekeeping(u); -done: pa_xfree(module_name); filter_free(fltr); @@ -542,7 +626,7 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struc pa_core_assert_ref(core); pa_sink_input_assert_ref(i); - return process(u, PA_OBJECT(i), true); + return process(u, PA_OBJECT(i), true, false); } static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { @@ -555,14 +639,14 @@ static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input * /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */ pa_hashmap_remove(u->mdm_ignored_inputs, i); - return process(u, PA_OBJECT(i), true); + return process(u, PA_OBJECT(i), true, false); } static pa_hook_result_t sink_input_proplist_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { pa_core_assert_ref(core); pa_sink_input_assert_ref(i); - return process(u, PA_OBJECT(i), true); + return process(u, PA_OBJECT(i), true, true); } static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { @@ -619,7 +703,7 @@ static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *o, pa_core_assert_ref(core); pa_source_output_assert_ref(o); - return process(u, PA_OBJECT(o), false); + return process(u, PA_OBJECT(o), false, false); } static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *o, struct userdata *u) { @@ -632,14 +716,14 @@ static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_ou /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */ pa_hashmap_remove(u->mdm_ignored_outputs, o); - return process(u, PA_OBJECT(o), false); + return process(u, PA_OBJECT(o), false, false); } static pa_hook_result_t source_output_proplist_cb(pa_core *core, pa_source_output *o, struct userdata *u) { pa_core_assert_ref(core); pa_source_output_assert_ref(o); - return process(u, PA_OBJECT(o), false); + return process(u, PA_OBJECT(o), false, true); } static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output *o, struct userdata *u) {
_______________________________________________ pulseaudio-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/pulseaudio-commits
