mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-01 22:58:47 -04:00
Compare commits
21 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a487c5244 | ||
|
|
a9bd710596 | ||
|
|
d6c8c9ce37 | ||
|
|
0a8b93f4cd | ||
|
|
d15a6e543f | ||
|
|
e68aa03f29 | ||
|
|
c6b7837df3 | ||
|
|
f8653fa5c1 | ||
|
|
c8d851b4b7 | ||
|
|
f031cc28f4 | ||
|
|
374d937752 | ||
|
|
80474384f5 | ||
|
|
72b6d24adb | ||
|
|
1804c9de1d | ||
|
|
fdf91a035d | ||
|
|
eb2c757d2d | ||
|
|
8903d2ccc0 | ||
|
|
0f89157261 | ||
|
|
254cb4bd1f | ||
|
|
b3e6e2e0df | ||
|
|
de9299c7b7 |
32 changed files with 619 additions and 191 deletions
151
NEWS
151
NEWS
|
|
@ -1,3 +1,154 @@
|
|||
PulseAudio 14.2
|
||||
|
||||
A bug fix release.
|
||||
|
||||
* Fix port switching when unplugging headphones
|
||||
|
||||
Contributors
|
||||
|
||||
Tanu Kaskinen
|
||||
|
||||
|
||||
PulseAudio 14.1
|
||||
|
||||
A bug fix release.
|
||||
|
||||
* Support upto 8 mixer channels on ALSA devices
|
||||
* Handle ALSA jacks with the same name but different index values
|
||||
* Switch to plugged-in headset when mic availability is unknown
|
||||
* Fix a potential segfault in the Bluetooth oFono HFP backend
|
||||
* Fix a problem with module-ladspa-sink when avoid-resampling=true
|
||||
* Fix database names containing canonical host for meson builds
|
||||
|
||||
Contributors
|
||||
|
||||
Arun Raghavan
|
||||
Hui Wang
|
||||
Igor V. Kovalenko
|
||||
Jaroslav Kysela
|
||||
Kai-Heng Feng
|
||||
Patrick Gaskin
|
||||
Tanu Kaskinen
|
||||
morrishoresh
|
||||
|
||||
|
||||
PulseAudio 14.0
|
||||
|
||||
Changes at a glance:
|
||||
|
||||
* Notes for end users
|
||||
* Significant routing changes to default sinks/sources
|
||||
* Changing the default sink moves streams from the old default sink to the new
|
||||
* Moving a stream to the default sink removes the "manually routed" status of the stream
|
||||
* If a sink changes status to available, streams that prefer that sink move there automatically
|
||||
* The same changes have been applied to the source stream routing
|
||||
* Workaround for GNOME Sound Settings' stream routing behaviour
|
||||
* module-rescue-streams is deprecated, functionality moved to the core
|
||||
* New rescue-streams option in daemon.conf
|
||||
* Automatic switching to HDMI is now disabled by default
|
||||
* Better support for some USB gaming headsets
|
||||
* Flat volumes are now disabled by default
|
||||
* The RAOP sink can be configured to automatically reconnect on connection failures
|
||||
* Separate sink_channels and source_channels for module-jackdbus-detect
|
||||
* Improved support for ALSA UCM
|
||||
* Support for ALSA mixer controls with non-zero index
|
||||
* It's now possible to set intended roles for devices in the ALSA profile configuration
|
||||
* Ports now have a type associated with them
|
||||
* Mappings have a new "description-key" option in the ALSA profile configuration
|
||||
* New xauthority argument for X11 modules
|
||||
* module-null-sink's compressed format support can now be configured at run-time
|
||||
* The enable-lfe-remixing option in daemon.conf was split into remixing-produce-lfe and remixing-consume-lfe
|
||||
* New channel_map argument for module-raop-sink
|
||||
* Notes for application developers
|
||||
* pa_mainloop_prepare interprets the timeout argument as microseconds again
|
||||
* New availability_group and type fields in the port info structs
|
||||
* New macros: PA_LIKELY(), PA_UNLIKELY(), PA_CLAMP() and PA_CLAMP_UNLIKELY()
|
||||
* Notes for packagers
|
||||
* New GStreamer-based RTP implementation
|
||||
* qpaeq switched from Python 2 to Python 3
|
||||
* Compile-time option to forget pre-14.0 stream routing
|
||||
* The install path of the ALSA configuration files is now configurable
|
||||
* GNU gettext minimum version requirement bumped from 0.19.3 to 0.19.8
|
||||
* Heads-up: dropping autotools build system
|
||||
* Heads-up: dropping EsounD support is considered, tell us if you still need it
|
||||
* Heads-up: dropping GConf support
|
||||
|
||||
Detailed change log:
|
||||
|
||||
https://www.freedesktop.org/wiki/Software/PulseAudio/Notes/14.0/
|
||||
|
||||
Contributors
|
||||
|
||||
Alexander E. Patrakov
|
||||
Arun Raghavan
|
||||
Balázs Meskó
|
||||
Baurzhan Muftakhidinov
|
||||
Ben Buchwald
|
||||
Daniil Kovalev
|
||||
Dave Chiluk
|
||||
David Heidelberg
|
||||
Dusan Kazik
|
||||
Eero Nurkkala
|
||||
Emanuil Novachev
|
||||
Emilio Herrera
|
||||
Felipe Sateler
|
||||
Felix Yan
|
||||
Geert Warrink
|
||||
Georg Chini
|
||||
Göran Uddeborg
|
||||
Hugo Osvaldo Barrera
|
||||
Hui Wang
|
||||
Igor V. Kovalenko
|
||||
Jan Alexander Steffens
|
||||
Jarno Suni
|
||||
Jaroslav Kysela
|
||||
Jaska Uimonen
|
||||
Jean-Baptiste Holcroft
|
||||
Josh
|
||||
Juliano de Souza Camargo
|
||||
Kai-Heng Feng
|
||||
Karl Ove Hufthammer
|
||||
Khem Raj
|
||||
Krzysztof Stasiowski
|
||||
Laurent Bigonville
|
||||
Libin Yang
|
||||
Marc Ranolfi
|
||||
Michael Pivonka
|
||||
Milo Casagrande
|
||||
Milo Ivir
|
||||
Nick Moriarty
|
||||
Oğuz Ersen
|
||||
Pali Rohár
|
||||
Peter Levine
|
||||
Peter Meerwald
|
||||
Philip Withnall
|
||||
Piotr Drąg
|
||||
RODRIGUEZ Christophe
|
||||
Rafael Fontenelle
|
||||
Ralph Seichter
|
||||
Rasmus Thomsen
|
||||
Rickie Schroeder
|
||||
Rosen Penev
|
||||
Ryszard Knop
|
||||
Sanchayan Maity
|
||||
Sebastian Dröge
|
||||
Sebastien
|
||||
StefanBruens
|
||||
Taahir Ahmed
|
||||
Tanu Kaskinen
|
||||
Timo Gurr
|
||||
Tom Yan
|
||||
Tomasz Kontusz
|
||||
Vasilis Tsiligiannis
|
||||
Wim Taymans
|
||||
Yi-Jyun Pan
|
||||
Yuri Chornoivan
|
||||
itsthem
|
||||
muzena
|
||||
roshal
|
||||
zhaochengyi
|
||||
|
||||
|
||||
PulseAudio 13.0
|
||||
|
||||
Changes at a glance:
|
||||
|
|
|
|||
|
|
@ -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", [Canonical host string.])
|
||||
|
||||
AC_CHECK_PROG([STOW], [stow], [yes], [no])
|
||||
|
||||
AS_IF([test "x$STOW" = "xyes" && test -d /usr/local/stow], [
|
||||
|
|
|
|||
|
|
@ -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', host_machine.cpu())
|
||||
cdata.set('PA_MAJOR', pa_version_major)
|
||||
cdata.set('PA_MINOR', pa_version_minor)
|
||||
cdata.set('PA_API_VERSION', pa_api_version)
|
||||
|
|
|
|||
|
|
@ -1037,7 +1037,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
|
||||
|
|
|
|||
|
|
@ -916,7 +916,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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -689,6 +690,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);
|
||||
|
||||
|
|
@ -821,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;
|
||||
}
|
||||
|
|
@ -847,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);
|
||||
|
|
@ -877,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);
|
||||
|
|
@ -992,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;
|
||||
}
|
||||
|
|
@ -1158,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;
|
||||
}
|
||||
|
|
@ -1350,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;
|
||||
}
|
||||
|
|
@ -1361,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));
|
||||
}
|
||||
|
||||
|
|
@ -1405,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;
|
||||
}
|
||||
|
|
@ -1450,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));
|
||||
}
|
||||
|
||||
|
|
@ -1656,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;
|
||||
|
|
@ -1676,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);
|
||||
|
|
@ -1703,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,
|
||||
|
|
@ -1738,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);
|
||||
|
|
@ -1750,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
|
||||
|
|
@ -1767,7 +1782,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;
|
||||
|
|
@ -1791,27 +1810,28 @@ 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 > 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_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;
|
||||
}
|
||||
|
||||
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 +1854,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;
|
||||
}
|
||||
|
||||
|
|
@ -1943,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) {
|
||||
|
|
@ -2011,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:
|
||||
|
|
@ -2427,6 +2465,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 +2512,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 +2526,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;
|
||||
}
|
||||
|
||||
|
|
@ -2574,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;
|
||||
}
|
||||
|
|
@ -2587,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));
|
||||
}
|
||||
|
||||
|
|
@ -2595,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));
|
||||
}
|
||||
}
|
||||
|
|
@ -2652,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;
|
||||
}
|
||||
|
|
@ -2660,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;
|
||||
}
|
||||
|
|
@ -2686,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;
|
||||
}
|
||||
|
|
@ -2718,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 },
|
||||
|
|
@ -2800,6 +2880,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 },
|
||||
|
|
@ -3017,6 +3106,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);
|
||||
|
|
@ -3032,22 +3122,23 @@ 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);
|
||||
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;
|
||||
|
|
@ -3111,18 +3202,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;
|
||||
}
|
||||
|
||||
|
|
@ -3138,7 +3241,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) {
|
||||
|
|
@ -3158,8 +3261,8 @@ 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_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_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,
|
||||
e->switch_use,
|
||||
|
|
@ -3171,7 +3274,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);
|
||||
|
|
@ -3218,7 +3321,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;
|
||||
}
|
||||
|
|
@ -3513,7 +3616,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;
|
||||
}
|
||||
|
|
@ -3544,7 +3647,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;
|
||||
|
|
@ -3621,7 +3724,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;
|
||||
|
|
@ -4311,7 +4415,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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -113,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. */
|
||||
|
|
@ -152,7 +156,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 +164,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;
|
||||
|
|
@ -177,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 */
|
||||
|
|
@ -194,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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -13,17 +13,24 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
; 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
|
||||
|
||||
[General]
|
||||
priority = 98
|
||||
description-key = analog-output-headphones
|
||||
|
||||
[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
|
||||
|
|
@ -47,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
|
||||
|
|
@ -56,7 +70,7 @@ switch = off
|
|||
volume = off
|
||||
|
||||
[Element Headphone2]
|
||||
required = any
|
||||
required-any = any
|
||||
switch = mute
|
||||
volume = merge
|
||||
override-map.1 = all
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ state.unplugged = unknown
|
|||
[Jack Front Headphone]
|
||||
required-any = any
|
||||
|
||||
; HP EliteDesk 800 DM Headset
|
||||
[Jack Front Headphone Front]
|
||||
required-any = any
|
||||
|
||||
[Jack Front Headphone Phantom]
|
||||
required-any = any
|
||||
state.plugged = unknown
|
||||
|
|
@ -89,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
|
||||
|
|
|
|||
|
|
@ -127,6 +127,10 @@ required-any = any
|
|||
switch = off
|
||||
volume = off
|
||||
|
||||
[Element Headphone,1]
|
||||
switch = off
|
||||
volume = off
|
||||
|
||||
[Element Headphone2]
|
||||
switch = off
|
||||
volume = off
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ volume = off
|
|||
switch = mute
|
||||
volume = zero
|
||||
|
||||
[Element Headphone,1]
|
||||
switch = mute
|
||||
volume = zero
|
||||
|
||||
[Element Headphone2]
|
||||
switch = mute
|
||||
volume = zero
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ volume = off
|
|||
switch = off
|
||||
volume = off
|
||||
|
||||
[Element Headphone,1]
|
||||
switch = off
|
||||
volume = off
|
||||
|
||||
[Element Headphone2]
|
||||
switch = off
|
||||
volume = off
|
||||
|
|
|
|||
|
|
@ -621,6 +621,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);
|
||||
|
||||
|
|
@ -663,9 +664,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -1195,7 +1195,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;
|
||||
|
|
@ -1252,17 +1252,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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2259,7 +2259,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;
|
||||
|
|
@ -2317,17 +2317,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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -281,13 +278,15 @@ static void switch_from_port(pa_device_port *port) {
|
|||
* profile is still available in the
|
||||
* 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);
|
||||
if (best_port) {
|
||||
struct port_pointers best_pp = find_port_pointers(best_port);
|
||||
switch_to_port(best_port, best_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 +313,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 +331,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;
|
||||
|
|
|
|||
|
|
@ -2074,34 +2074,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;
|
||||
|
|
|
|||
|
|
@ -59,17 +59,16 @@ void pa_datum_free(pa_datum *d) {
|
|||
pa_zero(d);
|
||||
}
|
||||
|
||||
pa_database* pa_database_open(const char *fn, bool for_write) {
|
||||
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;
|
||||
char *path;
|
||||
|
||||
pa_assert(fn);
|
||||
pa_assert(path);
|
||||
|
||||
/* 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);
|
||||
errno = 0;
|
||||
|
||||
/* We need to set the block size explicitly here, since otherwise
|
||||
|
|
@ -80,8 +79,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;
|
||||
|
|
|
|||
|
|
@ -222,14 +222,16 @@ 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_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 +253,6 @@ pa_database* pa_database_open(const char *fn, bool for_write) {
|
|||
db = NULL;
|
||||
}
|
||||
|
||||
pa_xfree(path);
|
||||
|
||||
return (pa_database*) db;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,18 +97,18 @@ finish:
|
|||
return c;
|
||||
}
|
||||
|
||||
pa_database* pa_database_open(const char *fn, bool for_write) {
|
||||
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;
|
||||
|
|
|
|||
107
src/pulsecore/database.c
Normal file
107
src/pulsecore/database.c
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2020 Igor V. Kovalenko <igor.v.kovalenko@gmail.com>
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.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 <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/log.h>
|
||||
|
||||
#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 *filename_suffix = pa_database_get_filename_suffix();
|
||||
|
||||
char *machine_id = NULL, *filename_prefix, *full_path;
|
||||
|
||||
DIR *database_dir = NULL;
|
||||
struct dirent *de;
|
||||
|
||||
pa_database *f;
|
||||
|
||||
pa_assert(filename_suffix && filename_suffix[0]);
|
||||
|
||||
if (prependmid && !(machine_id = pa_machine_id())) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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 (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 */
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -38,8 +38,29 @@ 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 database filename extension string */
|
||||
const char* pa_database_get_filename_suffix(void);
|
||||
|
||||
/* 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. */
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue