Merge tag 'upstream/14.2.93' into feature/bookworm/upgrade-14.2.93

This commit is contained in:
Eugenio Paolantonio (g7) 2022-01-30 19:10:35 +01:00
commit 0886d509fc
10 changed files with 600 additions and 313 deletions

View file

@ -2,20 +2,21 @@ PulseAudio Droid modules
========================
Building of droid modules is split to two packages
* common (and common-devel) which contains shared library code for use in
PulseAudio modules in this package and for inclusion in other projects
* droid with actual PulseAudio modules
* **common** (and **common-devel**) which contains shared library code for use in
PulseAudio modules in this package and for inclusion in other projects
* **droid** with actual PulseAudio modules
Supported Android versions:
* 4.1.x with Qualcomm extensions (tested with 4.1.2)
* 4.2.x
* 4.4.x
* 5.x
* 6.0.x
* 7.x
* 8.x
* 9.x
* 4.1.x with Qualcomm extensions (tested with 4.1.2)
* 4.2.x
* 4.4.x
* 5.x
* 6.0.x
* 7.x
* 8.x
* 9.x
* 10.x
Headers for defining devices and strings for different droid versions are in
src/common/droid-util-audio.h (legacy headers for Jolla 1 in droid-util-41qc.h).
@ -26,11 +27,11 @@ and FANCY_ENTRY_IF_FOO if enum FOO exists in HAL audio.h.
For example:
# configure.ac:
# configure.ac:
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_IP])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_OTHER_NEW])
# and then in droid-util-audio.h add macros to proper tables:
# and then in droid-util-audio.h add macros to proper tables:
/* string_conversion_table_output_device[] */
STRING_ENTRY_IF_OUT_IP
STRING_ENTRY_IF_OUT_OTHER_NEW
@ -141,18 +142,18 @@ routes audio to internal handsfree (ihf - "handsfree speaker"):
(Before starting, droid_card.primary is using profile primary-primary and
sink.primary port output-speaker)
pactl set-card-profile droid_card.primary voicecall
pactl set-sink-port sink.primary output-parking
pactl set-sink-port sink.primary output-speaker
pactl set-card-profile droid_card.primary voicecall
pactl set-sink-port sink.primary output-parking
pactl set-sink-port sink.primary output-speaker
After this, when there is an active voicecall (created by ofono for example),
voice audio starts to flow between modem and audio chip.
To disable voicecall and return to media audio:
pactl set-card-profile droid_card.primary primary-primary
pactl set-sink-port sink.primary output-parking
pactl set-sink-port sink.primary output-speaker
pactl set-card-profile droid_card.primary primary-primary
pactl set-sink-port sink.primary output-parking
pactl set-sink-port sink.primary output-speaker
With this example sequence sinks and sources are the ones from primary-primary
card profile, and they are maintained for the whole duration of the voicecall
@ -165,10 +166,10 @@ active port is a no-op (output/input-parking doesn't do any real routing
changes).
Current virtual profiles are:
* voicecall
* voicecall-record
* communication
* ringtone
* voicecall
* voicecall-record
* communication
* ringtone
Communication profile is used for VoIP-like applications, to enable some
voicecall related algorithms without being in voicecall. Ringtone profile
@ -201,7 +202,7 @@ to PulseAudio card profiles. For example configuration with
}
}
Would map to card profiles (<output>-<input>) primary-primary and lpa-primary.
Would map to card profiles ([output]-[input]) primary-primary and lpa-primary.
module-droid-sink and module-droid-source
-----------------------------------------
@ -211,7 +212,8 @@ hand, but droid-card loads appropriate modules based on the active card
profile.
Output and input ports for droid-sink and droid-source are generated from the
audio_policy.conf, where each device generates (usually) one port, for example:
audio_policy_configuration.xml (where available) or audio_policy.conf (legacy),
where each device generates (usually) one port, for example:
audio_hw_modules {
primary {
@ -245,7 +247,7 @@ droid-util-XXX.h
Changing output routing is then as simple as
pactl set-sink-port sink.primary output-wired_headphone
pactl set-sink-port sink.primary output-wired_headphone
Sink or source do not track possible headphone/other wired accessory plugging,
but this needs to be handled elsewhere and then that other entity needs to
@ -273,20 +275,20 @@ to read from the source but through resampler.
For example,
1) source-output 44100Hz, stereo connects (so1)
a) source is configured with 44100Hz, stereo
b) so1 connects to the source without resampler
2) source-output 16000Hz, mono connects (so2)
a) so1 is detached from the source
b) source is configured with 16000Hz, mono
c) so2 connects to the source without resampler
d) resampler is created for so1, 16000Hz, mono -> 44100Hz stereo
f) so1 is re-attached to the source through resampler
3) source-output 16000Hz, mono connects (so3)
a) so1 and so2 are detached from the source
b) so3 connects to the source without resampler
c) so1 is re-attached to the source through resampler
d) so2 is attached to the source
1) source-output 44100Hz, stereo connects (so1)
1) source is configured with 44100Hz, stereo
2) so1 connects to the source without resampler
2) source-output 16000Hz, mono connects (so2)
1) so1 is detached from the source
2) source is configured with 16000Hz, mono
3) so2 connects to the source without resampler
4) resampler is created for so1, 16000Hz, mono -> 44100Hz stereo
5) so1 is re-attached to the source through resampler
3) source-output 16000Hz, mono connects (so3)
1) so1 and so2 are detached from the source
2) so3 connects to the source without resampler
3) so1 is re-attached to the source through resampler
4) so2 is attached to the source
Classifying sinks and sources
-----------------------------
@ -296,14 +298,14 @@ functionality to ease device classification.
Currently following properties are set:
* For droid sinks
* droid.output.primary
* droid.output.low_latency
* droid.output.media_latency
* droid.output.offload
* For droid sources
* droid.input.builtin
* droid.input.external
* For droid sinks
* droid.output.primary
* droid.output.low_latency
* droid.output.media_latency
* droid.output.offload
* For droid sources
* droid.input.builtin
* droid.input.external
If the property is set and with value "true", the sink or source should be
used for the property type. If the property is not defined or contains
@ -312,11 +314,11 @@ value "false" it shouldn't be used for the property type.
For example, we might have sink.primary and sink.low_latency with following
properties:
* sink.primary
* droid.output.primary "true"
* droid.output.media_latency "true"
* sink.low_latency
* droid.output.low_latency "true"
* sink.primary
* droid.output.primary "true"
* droid.output.media_latency "true"
* sink.low_latency
* droid.output.low_latency "true"
There also may be just one sink, with all the properties defined as "true"
and so on.
@ -333,53 +335,73 @@ are enabled by default with some adaptations etc.
Currently there are following quirks:
* input_atoi
* Enabled by default with Android versions 5 and up.
* Due to how atoi works in bionic vs libc we need to pass the input
route a bit funny. If input routing doesn't work switch this on or off.
* set_parameters
* Disabled by default.
* Some adaptations need to use hw module's generic set_parameters call
to change input routing. If input routing doesn't work switch this
on or off. (mostly just older adaptations)
* close_input
* Enabled by default.
* Close input stream when not in use instead of suspending the stream.
Cannot be changed when multiple inputs are merged to single source.
* unload_no_close
* Disabled by default.
* Don't call audio_hw_device_close() for the hw module when unloading.
Mostly useful for tracking module unload issues.
* no_hw_volume
* Disabled by default.
* Some broken implementations are incorrectly probed for supporting hw
volume control. This is manifested by always full volume with volume
control not affecting volume level. To fix this enable this quirk.
* output_make_writable
* Disabled by default.
* Some implementations modify write buffer in-place when this should
not be done. This can result in random segfaults when playing audio.
As a workaround make the buffer memchunk writable before passing to
audio HAL.
* realcall
* Disabled by default.
* Some vendors apply custom realcall parameter to HAL device when
doing voicecall routing. If there is no voicecall audio you can
try enabling this quirk so that the realcall parameter is applied
when switching to voicecall profile.
* unload_call_exit
* Disabled by default.
* Some HAL module implementations get stuck in mutex or segfault when
trying to unload the module. To avoid confusing segfaults call
exit(0) instead of calling unload for the module.
* output_fast
* Enabled by default.
* Create separate sink if AUDIO_OUTPUT_FLAG_FAST is found. If this sink
is misbehaving try disabling this quirk.
* output_deep_buffer
* Enabled by default.
* Create separate sink if AUDIO_OUTPUT_FLAG_DEEP_BUFFER is found. If
this sink is misbehaving try disabling this quirk.
* input_atoi
* Enabled by default with Android versions 5 and up.
* Due to how atoi works in bionic vs libc we need to pass the input
route a bit funny. If input routing doesn't work switch this on or off.
* set_parameters
* Disabled by default.
* Some adaptations need to use hw module's generic set_parameters call
to change input routing. If input routing doesn't work switch this
on or off. (mostly just older adaptations)
* close_input
* Enabled by default.
* Close input stream when not in use instead of suspending the stream.
Cannot be changed when multiple inputs are merged to single source.
* unload_no_close
* Disabled by default.
* Don't call audio_hw_device_close() for the hw module when unloading.
Mostly useful for tracking module unload issues.
* no_hw_volume
* Disabled by default.
* Some broken implementations are incorrectly probed for supporting hw
volume control. This is manifested by always full volume with volume
control not affecting volume level. To fix this enable this quirk.
* output_make_writable
* Disabled by default.
* Some implementations modify write buffer in-place when this should
not be done. This can result in random segfaults when playing audio.
As a workaround make the buffer memchunk writable before passing to
audio HAL.
* realcall
* Disabled by default.
* Some vendors apply custom realcall parameter to HAL device when
doing voicecall routing. If there is no voicecall audio you can
try enabling this quirk so that the realcall parameter is applied
when switching to voicecall profile.
* unload_call_exit
* Disabled by default.
* Some HAL module implementations get stuck in mutex or segfault when
trying to unload the module. To avoid confusing segfaults call
exit(0) instead of calling unload for the module.
* output_fast
* Enabled by default.
* Create separate sink if AUDIO_OUTPUT_FLAG_FAST is found. If this sink
is misbehaving try disabling this quirk.
* output_deep_buffer
* Enabled by default.
* Create separate sink if AUDIO_OUTPUT_FLAG_DEEP_BUFFER is found. If
this sink is misbehaving try disabling this quirk.
* audio_cal_wait
* Disabled by default.
* Certain devices do audio calibration during hw module open and
writing audio too early will break the calibration. In these cases
this quirk can be enabled and 10 seconds of sleep is added after
opening hw module.
* standby_set_route
* Disabled by default.
* Some devices don't like to receive set_parameters() call while they
are in write(), even if it seems the mutexes are correctly in place.
Standby is another synchronization point which seems to work better.
If there are hiccups like long delays when setting route during
voice call start try enabling this quirk.
* speaker_before_voice
* Disabled by default.
* Set route to speaker before changing audio mode to AUDIO_MODE_IN_CALL.
Some devices don't get routing right if the route is something else
(like AUDIO_DEVICE_OUT_WIRED_HEADSET) before calling set_mode().
If routing is wrong when call starts with wired accessory connected
try enabling this quirk.
For example, to disable input_atoi and enable close_input quirks, use module
argument
@ -409,7 +431,7 @@ droid.device.additional-route connects to droid-sink, this extra route is set
For example, if droid-sink has active port output-wired_headphone:
paplay --property=droid.device.additional-route=AUDIO_DEVICE_OUT_SPEAKER a.wav
paplay --property=droid.device.additional-route=AUDIO_DEVICE_OUT_SPEAKER a.wav
As long as the new stream is connected to droid-sink, output routing is
SPEAKER.

View file

@ -6,7 +6,7 @@
Name: pulseaudio-modules-droid-%{device}
Summary: PulseAudio Droid HAL modules
Version: %{pulsemajorminor}.86
Version: %{pulsemajorminor}.93
Release: 1
License: LGPLv2+
URL: https://github.com/mer-hybris/pulseaudio-modules-droid

View file

@ -5,7 +5,7 @@
Name: pulseaudio-modules-droid
Summary: PulseAudio Droid HAL modules
Version: %{pulsemajorminor}.86
Version: %{pulsemajorminor}.93
Release: 1
License: LGPLv2+
URL: https://github.com/mer-hybris/pulseaudio-modules-droid

View file

@ -140,6 +140,40 @@ bool pa_string_convert_num_to_str(pa_conversion_string_t type, uint32_t value, c
case CONV_STRING_INPUT_FLAG:
return string_convert_num_to_str(string_conversion_table_input_flag, value, to_str);
case CONV_STRING_AUDIO_SOURCE_FANCY:
return string_convert_num_to_str(string_conversion_table_audio_source_fancy, value, to_str);
}
pa_assert_not_reached();
return false;
}
bool pa_string_convert_str_to_num(pa_conversion_string_t type, const char *str, uint32_t *to_value) {
switch (type) {
case CONV_STRING_FORMAT:
return string_convert_str_to_num(string_conversion_table_format, str, to_value);
case CONV_STRING_OUTPUT_CHANNELS:
return string_convert_str_to_num(string_conversion_table_output_channels, str, to_value);
case CONV_STRING_INPUT_CHANNELS:
return string_convert_str_to_num(string_conversion_table_input_channels, str, to_value);
case CONV_STRING_OUTPUT_DEVICE:
return string_convert_str_to_num(string_conversion_table_output_device, str, to_value);
case CONV_STRING_INPUT_DEVICE:
return string_convert_str_to_num(string_conversion_table_input_device, str, to_value);
case CONV_STRING_OUTPUT_FLAG:
return string_convert_str_to_num(string_conversion_table_output_flag, str, to_value);
case CONV_STRING_INPUT_FLAG:
return string_convert_str_to_num(string_conversion_table_input_flag, str, to_value);
case CONV_STRING_AUDIO_SOURCE_FANCY:
return string_convert_str_to_num(string_conversion_table_audio_source_fancy, str, to_value);
}
pa_assert_not_reached();
@ -210,10 +244,6 @@ bool pa_droid_input_port_name(audio_devices_t value, const char **to_str) {
return string_convert_num_to_str(string_conversion_table_input_device_fancy, (uint32_t) value, to_str);
}
bool pa_droid_audio_source_name(audio_source_t value, const char **to_str) {
return string_convert_num_to_str(string_conversion_table_audio_source_fancy, (uint32_t) value, to_str);
}
static int parse_list(const struct string_conversion *table,
const char *separator,
const char *str,
@ -280,6 +310,10 @@ int pa_conversion_parse_list(pa_conversion_string_t type, const char *separator,
case CONV_STRING_INPUT_FLAG:
return parse_list(string_conversion_table_input_flag, separator, str, dst, unknown_entries);
/* Not handled in this context */
case CONV_STRING_AUDIO_SOURCE_FANCY:
return 0;
}
pa_assert_not_reached();

View file

@ -27,6 +27,10 @@
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <grp.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
@ -37,6 +41,7 @@
#include <pulse/volume.h>
#include <pulse/xmalloc.h>
#include <pulse/direction.h>
#include <pulse/util.h>
#include <pulsecore/core.h>
#include <pulsecore/core-error.h>
@ -82,16 +87,36 @@ struct droid_quirk valid_quirks[] = {
{ "unload_call_exit", QUIRK_UNLOAD_CALL_EXIT },
{ "output_fast", QUIRK_OUTPUT_FAST },
{ "output_deep_buffer", QUIRK_OUTPUT_DEEP_BUFFER },
{ "audio_cal_wait", QUIRK_AUDIO_CAL_WAIT },
{ "standby_set_route", QUIRK_STANDBY_SET_ROUTE },
{ "speaker_before_voice", QUIRK_SPEAKER_BEFORE_VOICE },
};
#define QUIRK_AUDIO_CAL_WAIT_S (10)
#define QUIRK_AUDIO_CAL_FILE "/data/vendor/audio/cirrus_sony.cal"
#define QUIRK_AUDIO_CAL_GROUP "audio"
#define QUIRK_AUDIO_CAL_MODE (0664)
#define DEFAULT_PRIORITY (100)
#define DEFAULT_AUDIO_FORMAT (AUDIO_FORMAT_PCM_16_BIT)
#ifndef AUDIO_PARAMETER_VALUE_ON
#define AUDIO_PARAMETER_VALUE_ON "on"
#endif
#ifndef AUDIO_PARAMETER_VALUE_OFF
#define AUDIO_PARAMETER_VALUE_OFF "off"
#endif
#define AUDIO_PARAMETER_BT_SCO_ON "BT_SCO=" AUDIO_PARAMETER_VALUE_ON
#define AUDIO_PARAMETER_BT_SCO_OFF "BT_SCO=" AUDIO_PARAMETER_VALUE_OFF
static void droid_port_free(pa_droid_port *p);
static int input_stream_set_route(pa_droid_hw_module *hw_module, pa_droid_stream *s);
static int droid_set_parameters(pa_droid_hw_module *hw, const char *parameters);
static bool droid_set_audio_source(pa_droid_hw_module *hw_module, audio_source_t audio_source);
static pa_droid_profile *profile_new(pa_droid_profile_set *ps,
const pa_droid_config_hw_module *module,
@ -685,30 +710,20 @@ void pa_droid_quirk_log(pa_droid_hw_module *hw) {
pa_assert(hw);
if (hw->quirks) {
for (i = 0; i < sizeof(valid_quirks) / sizeof(struct droid_quirk); i++) {
if (hw->quirks->enabled[i]) {
pa_log_debug("Enabled quirks:");
for (i = 0; i < sizeof(valid_quirks) / sizeof(struct droid_quirk); i++)
if (hw->quirks->enabled[i])
pa_log_debug(" %s", valid_quirks[i].name);
return;
}
for (i = 0; i < sizeof(valid_quirks) / sizeof(struct droid_quirk); i++) {
if (hw->quirks.enabled[i]) {
pa_log_debug("Enabled quirks:");
for (i = 0; i < sizeof(valid_quirks) / sizeof(struct droid_quirk); i++)
if (hw->quirks.enabled[i])
pa_log_debug(" %s", valid_quirks[i].name);
return;
}
}
}
static pa_droid_quirks *get_quirks(pa_droid_quirks *q) {
if (!q)
q = pa_xnew0(pa_droid_quirks, 1);
return q;
}
static pa_droid_quirks *set_default_quirks(pa_droid_quirks *q) {
q = NULL;
pa_assert(q);
q = get_quirks(q);
q->enabled[QUIRK_CLOSE_INPUT] = true;
q->enabled[QUIRK_OUTPUT_FAST] = true;
q->enabled[QUIRK_OUTPUT_DEEP_BUFFER] = true;
@ -729,19 +744,23 @@ static pa_droid_quirks *set_default_quirks(pa_droid_quirks *q) {
return q;
}
bool pa_droid_quirk_parse(pa_droid_hw_module *hw, const char *quirks) {
bool pa_droid_quirk_parse(pa_droid_quirks *quirks, const char *quirks_def) {
char *quirk = NULL;
char *d;
const char *state = NULL;
pa_assert(hw);
pa_assert(quirks);
hw->quirks = get_quirks(hw->quirks);
memset(quirks, 0, sizeof(*quirks));
set_default_quirks(quirks);
while ((quirk = pa_split(quirks, ",", &state))) {
if (!quirks_def)
return true;
while ((quirk = pa_split(quirks_def, ",", &state))) {
uint32_t i;
bool enable = false;
bool found = false;
if (strlen(quirk) < 2)
goto error;
@ -756,17 +775,22 @@ bool pa_droid_quirk_parse(pa_droid_hw_module *hw, const char *quirks) {
goto error;
for (i = 0; i < sizeof(valid_quirks) / sizeof(struct droid_quirk); i++) {
if (pa_streq(valid_quirks[i].name, d))
hw->quirks->enabled[valid_quirks[i].value] = enable;
if (pa_streq(valid_quirks[i].name, d)) {
quirks->enabled[valid_quirks[i].value] = enable;
found = true;
}
}
if (!found)
goto error;
pa_xfree(quirk);
}
return true;
error:
pa_log("Incorrect quirk definition \"%s\" (\"%s\")", quirk ? quirk : "<null>", quirks);
pa_log("Incorrect quirk definition \"%s\" (\"%s\")", quirk ? quirk : "<null>", quirks_def);
pa_xfree(quirk);
return false;
@ -859,7 +883,68 @@ static char *shared_name_get(const char *module_id) {
return pa_sprintf_malloc("droid-hardware-module-%s", module_id);
}
static pa_droid_hw_module *droid_hw_module_open(pa_core *core, const pa_droid_config_audio *config, const char *module_id) {
static void quirk_audio_cal(pa_droid_hw_module *hw, uint32_t flags) {
struct group *grp;
pa_assert(hw);
if (!pa_droid_quirk(hw, QUIRK_AUDIO_CAL_WAIT))
return;
if (access(QUIRK_AUDIO_CAL_FILE, F_OK) == 0) {
if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) {
pa_log_info("Waiting for audio calibration to load.");
/* 1 second is enough, so let's double that. */
pa_msleep(2 * PA_MSEC_PER_SEC);
}
return;
}
pa_log_info("Waiting for audio calibration to finish... (%d seconds)", QUIRK_AUDIO_CAL_WAIT_S);
/* First wait until the calibration file appears on file system. */
for (int i = 0; i < QUIRK_AUDIO_CAL_WAIT_S; i++) {
pa_log_debug("%d...", QUIRK_AUDIO_CAL_WAIT_S - i);
pa_msleep(PA_MSEC_PER_SEC);
if (access(QUIRK_AUDIO_CAL_FILE, F_OK) == 0) {
pa_log_debug("Calibration file " QUIRK_AUDIO_CAL_FILE " appeared, wait one second more.");
/* Then wait for a bit more. */
pa_msleep(PA_MSEC_PER_SEC);
break;
}
}
if (access(QUIRK_AUDIO_CAL_FILE, F_OK) != 0)
goto fail;
if (!(grp = getgrnam(QUIRK_AUDIO_CAL_GROUP))) {
pa_log("couldn't get gid for " QUIRK_AUDIO_CAL_GROUP);
goto fail;
}
if (chown(QUIRK_AUDIO_CAL_FILE, getuid(), grp->gr_gid) < 0) {
pa_log("chown failed for " QUIRK_AUDIO_CAL_FILE);
goto fail;
}
if (chmod(QUIRK_AUDIO_CAL_FILE, QUIRK_AUDIO_CAL_MODE) < 0) {
pa_log("chmod failed for " QUIRK_AUDIO_CAL_FILE);
goto fail;
}
pa_log_info("Done waiting for audio calibration.");
return;
fail:
if (access(QUIRK_AUDIO_CAL_FILE, F_OK) == 0)
unlink(QUIRK_AUDIO_CAL_FILE);
pa_log("Audio calibration file generation failed! (" QUIRK_AUDIO_CAL_FILE " doesn't exist)");
}
static pa_droid_hw_module *droid_hw_module_open(pa_core *core, const pa_droid_config_audio *config,
const char *module_id, const pa_droid_quirks *quirks) {
const pa_droid_config_hw_module *module;
pa_droid_hw_module *hw = NULL;
struct hw_module_t *hwmod = NULL;
@ -920,7 +1005,10 @@ static pa_droid_hw_module *droid_hw_module_open(pa_core *core, const pa_droid_co
hw->shared_name = shared_name_get(hw->module_id);
hw->outputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
hw->inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
hw->quirks = set_default_quirks(hw->quirks);
if (quirks)
memcpy(&hw->quirks, quirks, sizeof(*quirks));
else
set_default_quirks(&hw->quirks);
hw->sink_put_hook_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_EARLY-10,
sink_put_hook_cb, hw);
@ -941,20 +1029,59 @@ fail:
return NULL;
}
pa_droid_hw_module *pa_droid_hw_module_get(pa_core *core, const pa_droid_config_audio *config, const char *module_id) {
pa_droid_hw_module *hw;
static pa_droid_hw_module *droid_hw_module_shared_get(pa_core *core, const char *module_id) {
pa_droid_hw_module *hw = NULL;
char *shared_name;
pa_assert(core);
pa_assert(module_id);
shared_name = shared_name_get(module_id);
if ((hw = pa_shared_get(core, shared_name)))
hw = pa_droid_hw_module_ref(hw);
else
hw = droid_hw_module_open(core, config, module_id);
pa_xfree(shared_name);
return hw;
}
pa_droid_hw_module *pa_droid_hw_module_get2(pa_core *core, pa_modargs *ma, const char *module_id) {
pa_droid_hw_module *hw = NULL;
pa_droid_config_audio *config = NULL;
pa_droid_quirks quirks;
pa_assert(core);
pa_assert(ma);
pa_assert(module_id);
/* First let's find out if hw module has already been opened. */
if ((hw = droid_hw_module_shared_get(core, module_id)))
return hw;
/* No hw module object in shared object db, let's parse quirks and config and
* open the module now. */
if (!pa_droid_quirk_parse(&quirks, pa_modargs_get_value(ma, "quirks", NULL)))
return NULL;
if (!(config = pa_droid_config_load(ma)))
return NULL;
hw = droid_hw_module_open(core, config, module_id, &quirks);
pa_droid_config_free(config);
return hw;
}
pa_droid_hw_module *pa_droid_hw_module_get(pa_core *core, const pa_droid_config_audio *config, const char *module_id) {
pa_droid_hw_module *hw;
if (!(hw = droid_hw_module_shared_get(core, module_id)))
hw = droid_hw_module_open(core, config, module_id, NULL);
return hw;
}
@ -1008,8 +1135,6 @@ static void droid_hw_module_close(pa_droid_hw_module *hw) {
pa_idxset_free(hw->inputs, NULL);
}
pa_xfree(hw->quirks);
pa_xfree(hw);
}
@ -1267,6 +1392,8 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
goto fail;
}
pa_log_info("Open output stream %s", module_output_name);
if (!stream_config_fill(module_output, devices, &sample_spec, &channel_map, &config_out))
goto fail;
@ -1288,7 +1415,7 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
#if AUDIO_API_VERSION_MAJ >= 3
/* Go with empty address, should work
* with most devices for now. */
, NULL
, ""
#endif
);
pa_droid_hw_module_unlock(module);
@ -1298,6 +1425,8 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
goto fail;
}
quirk_audio_cal(module, module_output->flags);
s = droid_stream_new(module, module_output);
s->output = output = droid_output_stream_new();
output->stream = stream;
@ -1313,7 +1442,7 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
s->buffer_size = output->stream->common.get_buffer_size(&output->stream->common);
pa_log_info("Opened droid output stream %p with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u) buffer size: %u (%llu usec)",
pa_log_info("Opened droid output stream %p with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u) buffer size: %zu (%" PRIu64 " usec)",
(void *) s,
devices,
output->flags,
@ -1510,7 +1639,7 @@ static int input_stream_open(pa_droid_stream *s, bool resume_from_suspend) {
&input->stream
#if AUDIO_API_VERSION_MAJ >= 3
, 0
, NULL /* Don't define address */
, "" /* Use empty address. */
, hw_module->state.audio_source
#endif
);
@ -1624,7 +1753,11 @@ static void input_stream_close(pa_droid_stream *s) {
bool pa_droid_stream_reconfigure_input(pa_droid_stream *s,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map) {
const pa_channel_map *requested_channel_map,
const pa_proplist *proplist) {
/* Use default audio source by default */
audio_source_t audio_source = AUDIO_SOURCE_DEFAULT;
pa_assert(s);
pa_assert(s->input);
pa_assert(requested_sample_spec);
@ -1635,6 +1768,16 @@ bool pa_droid_stream_reconfigure_input(pa_droid_stream *s,
s->input->req_sample_spec = *requested_sample_spec;
s->input->req_channel_map = *requested_channel_map;
if (proplist) {
const char *source;
/* If audio source is defined in source-output proplist use that instead. */
if ((source = pa_proplist_gets(proplist, PROP_DROID_INPUT_AUDIO_SOURCE)))
pa_string_convert_str_to_num(CONV_STRING_AUDIO_SOURCE_FANCY, source, &audio_source);
}
/* Update audio source */
droid_set_audio_source(s->module, audio_source);
input_stream_close(s);
if (input_stream_open(s, false) < 0) {
@ -1650,6 +1793,50 @@ bool pa_droid_stream_reconfigure_input(pa_droid_stream *s,
return true;
}
bool pa_droid_stream_reconfigure_input_needed(pa_droid_stream *s,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map,
const pa_proplist *proplist) {
bool reconfigure_needed = false;
pa_assert(s);
pa_assert(s->input);
if (requested_sample_spec && !pa_sample_spec_equal(&s->input->sample_spec, requested_sample_spec)) {
reconfigure_needed = true;
pa_log_debug("input reconfigure needed: sample specs not equal");
}
if (requested_channel_map && !pa_channel_map_equal(&s->input->channel_map, requested_channel_map)) {
reconfigure_needed = true;
pa_log_debug("input reconfigure needed: channel maps not equal");
}
if (proplist) {
const char *source;
audio_source_t audio_source;
/* If audio source is defined in source-output proplist use that instead. */
if ((source = pa_proplist_gets(proplist, PROP_DROID_INPUT_AUDIO_SOURCE))) {
if (pa_string_convert_str_to_num(CONV_STRING_AUDIO_SOURCE_FANCY, source, &audio_source) &&
s->module->state.audio_source != audio_source) {
reconfigure_needed = true;
pa_log_debug("input reconfigure needed: " PROP_DROID_INPUT_AUDIO_SOURCE " changes");
}
} else {
if (pa_input_device_default_audio_source(s->module->state.input_device, &audio_source) &&
s->module->state.audio_source != audio_source) {
reconfigure_needed = true;
pa_log_debug("input reconfigure needed: audio source changes");
}
}
}
return reconfigure_needed;
}
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *hw_module,
const pa_sample_spec *default_sample_spec,
const pa_channel_map *default_channel_map) {
@ -1671,7 +1858,7 @@ pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *hw_module,
s->input->default_sample_spec = *default_sample_spec;
s->input->default_channel_map = *default_channel_map;
if (!pa_droid_stream_reconfigure_input(s, default_sample_spec, default_channel_map)) {
if (!pa_droid_stream_reconfigure_input(s, default_sample_spec, default_channel_map, NULL)) {
pa_droid_stream_unref(s);
s = NULL;
} else
@ -1747,11 +1934,30 @@ static int droid_output_stream_set_route(pa_droid_stream *s, audio_devices_t dev
pa_mutex_lock(s->module->output_mutex);
if (output->flags & AUDIO_OUTPUT_FLAG_PRIMARY || pa_droid_hw_primary_output_stream(s->module) == NULL) {
int set_bt_sco = -1;
parameters = pa_sprintf_malloc("%s=%u;", AUDIO_PARAMETER_STREAM_ROUTING, device);
/* Set BT_SCO parameter for Bluetooth voice/voip call routes. */
if (device != output->device &&
(device | output->device) & (AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)) {
set_bt_sco = (device & (AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)) ? 1 : 0;
}
if (set_bt_sco == 1)
droid_set_parameters(s->module, AUDIO_PARAMETER_BT_SCO_ON);
pa_log_debug("output stream %p set_parameters(%s) %#010x", (void *) s, parameters, device);
ret = output->stream->common.set_parameters(&output->stream->common, parameters);
if (set_bt_sco == 0)
droid_set_parameters(s->module, AUDIO_PARAMETER_BT_SCO_OFF);
if (ret < 0) {
if (ret == -ENOSYS)
pa_log_warn("output set_parameters(%s) not allowed while stream is active", parameters);
@ -1770,6 +1976,13 @@ static int droid_output_stream_set_route(pa_droid_stream *s, audio_devices_t dev
if (slave == s)
continue;
if (pa_droid_quirk(s->module, QUIRK_STANDBY_SET_ROUTE)) {
/* Some devices don't like to receive set_parameters() call while they
* are in write(), even if it seems the mutexes are correctly in place.
* Standby is another synchronization point which seems to work better. */
slave->output->stream->common.standby(&slave->output->stream->common);
}
pa_log_debug("slave output stream %p set_parameters(%s)", (void *) slave, parameters);
ret = slave->output->stream->common.set_parameters(&slave->output->stream->common, parameters);
@ -1888,16 +2101,14 @@ int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters) {
return ret;
}
int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters) {
static int droid_set_parameters(pa_droid_hw_module *hw, const char *parameters) {
int ret;
pa_assert(hw);
pa_assert(parameters);
pa_log_debug("hw %p set_parameters(%s)", (void *) hw, parameters);
pa_mutex_lock(hw->hw_mutex);
ret = hw->device->set_parameters(hw->device, parameters);
pa_mutex_unlock(hw->hw_mutex);
if (ret < 0)
pa_log("hw module %p set_parameters(%s) failed: %d", (void *) hw, parameters, ret);
@ -1905,6 +2116,19 @@ int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters) {
return ret;
}
int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters) {
int ret;
pa_assert(hw);
pa_assert(parameters);
pa_mutex_lock(hw->hw_mutex);
ret = droid_set_parameters(hw, parameters);
pa_mutex_unlock(hw->hw_mutex);
return ret;
}
bool pa_droid_stream_is_primary(pa_droid_stream *s) {
pa_assert(s);
pa_assert(s->output || s->input);
@ -2056,6 +2280,17 @@ bool pa_droid_hw_set_mode(pa_droid_hw_module *hw_module, audio_mode_t mode) {
pa_log_info("Set mode to %s.", audio_mode_to_string(mode));
if (pa_droid_quirk(hw_module, QUIRK_SPEAKER_BEFORE_VOICE) &&
hw_module->state.mode != mode && mode == AUDIO_MODE_IN_CALL) {
pa_droid_stream *primary_output;
/* Set route to speaker before changing audio mode to AUDIO_MODE_IN_CALL.
* Some devices don't get routing right if the route is something else
* (like AUDIO_DEVICE_OUT_WIRED_HEADSET) before calling set_mode().*/
if ((primary_output = pa_droid_hw_primary_output_stream(hw_module)))
pa_droid_stream_set_route(primary_output, AUDIO_DEVICE_OUT_SPEAKER);
}
pa_droid_hw_module_lock(hw_module);
if (hw_module->device->set_mode(hw_module->device, mode) < 0) {
ret = false;
@ -2081,26 +2316,14 @@ bool pa_droid_hw_set_mode(pa_droid_hw_module *hw_module, audio_mode_t mode) {
return ret;
}
bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
audio_devices_t device) {
audio_source_t audio_source = AUDIO_SOURCE_DEFAULT;
/* Return true if audio source changes */
static bool droid_set_audio_source(pa_droid_hw_module *hw_module, audio_source_t audio_source) {
audio_source_t audio_source_override = AUDIO_SOURCE_DEFAULT;
bool device_changed = false;
bool source_changed = false;
pa_assert(hw_module);
if (hw_module->state.input_device != device) {
const char *name = NULL;
pa_log_debug("Set global input to %s (%#010x)",
pa_string_convert_input_device_num_to_str(device, &name)
? name : "<unknown>",
device);
hw_module->state.input_device = device;
device_changed = true;
}
pa_input_device_default_audio_source(hw_module->state.input_device, &audio_source);
if (audio_source == AUDIO_SOURCE_DEFAULT)
pa_input_device_default_audio_source(hw_module->state.input_device, &audio_source);
/* Override audio source based on mode. */
switch (hw_module->state.mode) {
@ -2116,9 +2339,9 @@ bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
}
if (audio_source != audio_source_override) {
const char *from, *to;
pa_droid_audio_source_name(audio_source, &from);
pa_droid_audio_source_name(audio_source_override, &to);
const char *from = NULL, *to = NULL;
pa_string_convert_num_to_str(CONV_STRING_AUDIO_SOURCE_FANCY, audio_source, &from);
pa_string_convert_num_to_str(CONV_STRING_AUDIO_SOURCE_FANCY, audio_source, &to);
pa_log_info("Audio mode %s, overriding audio source %s with %s",
audio_mode_to_string(hw_module->state.mode),
from ? from : "<unknown>",
@ -2128,14 +2351,39 @@ bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
if (audio_source != hw_module->state.audio_source) {
const char *name = NULL;
pa_log_debug("set global audio source to %s (%#010x)",
pa_droid_audio_source_name(audio_source, &name)
pa_log_debug("Set global audio source to %s (%#010x)",
pa_string_convert_num_to_str(CONV_STRING_AUDIO_SOURCE_FANCY, audio_source, &name)
? name : "<unknown>",
audio_source);
hw_module->state.audio_source = audio_source;
source_changed = true;
/* audio source changed */
return true;
}
/* audio source did not change */
return false;
}
bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
audio_devices_t device) {
bool device_changed = false;
bool source_changed = false;
pa_assert(hw_module);
if (hw_module->state.input_device != device) {
const char *name = NULL;
pa_log_debug("Set global input to %s (%#010x)",
pa_string_convert_input_device_num_to_str(device, &name)
? name : "<unknown>",
device);
hw_module->state.input_device = device;
device_changed = true;
}
source_changed = droid_set_audio_source(hw_module, hw_module->state.audio_source);
if (hw_module->state.active_input && (device_changed || source_changed))
input_stream_set_route(hw_module, NULL);

View file

@ -52,10 +52,12 @@ typedef enum {
CONV_STRING_OUTPUT_DEVICE,
CONV_STRING_INPUT_DEVICE,
CONV_STRING_OUTPUT_FLAG,
CONV_STRING_INPUT_FLAG
CONV_STRING_INPUT_FLAG,
CONV_STRING_AUDIO_SOURCE_FANCY,
} pa_conversion_string_t;
bool pa_string_convert_num_to_str(pa_conversion_string_t type, uint32_t value, const char **to_str);
bool pa_string_convert_str_to_num(pa_conversion_string_t type, const char *str, uint32_t *to_value);
bool pa_convert_output_channel(uint32_t value, pa_conversion_field_t from, uint32_t *to_value);
bool pa_convert_input_channel(uint32_t value, pa_conversion_field_t from, uint32_t *to_value);
@ -81,9 +83,6 @@ bool pa_input_device_default_audio_source(audio_devices_t input_device, audio_so
bool pa_droid_output_port_name(audio_devices_t value, const char **to_str);
bool pa_droid_input_port_name(audio_devices_t value, const char **to_str);
/* Pretty audio source names */
bool pa_droid_audio_source_name(audio_source_t value, const char **to_str);
int pa_conversion_parse_list(pa_conversion_string_t type, const char *separator,
const char *str, uint32_t *dst, char **unknown_entries);

View file

@ -30,6 +30,7 @@
#include <pulsecore/mutex.h>
#include <pulsecore/strlist.h>
#include <pulsecore/atomic.h>
#include <pulsecore/modargs.h>
#include <droid/version.h>
#include <droid/droid-config.h>
@ -49,6 +50,7 @@
#define PROP_DROID_OUTPUT_OFFLOAD "droid.output.offload"
#define PROP_DROID_INPUT_BUILTIN "droid.input.builtin"
#define PROP_DROID_INPUT_EXTERNAL "droid.input.external"
#define PROP_DROID_INPUT_AUDIO_SOURCE "droid.input.source"
#define PA_DROID_PRIMARY_DEVICE "primary"
@ -67,6 +69,26 @@ typedef enum pa_droid_hook {
PA_DROID_HOOK_MAX
} pa_droid_hook_t;
enum pa_droid_quirk_type {
QUIRK_INPUT_ATOI,
QUIRK_SET_PARAMETERS,
QUIRK_CLOSE_INPUT,
QUIRK_UNLOAD_NO_CLOSE,
QUIRK_NO_HW_VOLUME,
QUIRK_OUTPUT_MAKE_WRITABLE,
QUIRK_REALCALL,
QUIRK_UNLOAD_CALL_EXIT,
QUIRK_OUTPUT_FAST,
QUIRK_OUTPUT_DEEP_BUFFER,
QUIRK_AUDIO_CAL_WAIT,
QUIRK_STANDBY_SET_ROUTE,
QUIRK_SPEAKER_BEFORE_VOICE,
QUIRK_COUNT
};
struct pa_droid_quirks {
bool enabled[QUIRK_COUNT];
};
struct pa_droid_hw_module {
PA_REFCNT_DECLARE;
@ -95,7 +117,7 @@ struct pa_droid_hw_module {
pa_atomic_t active_outputs;
pa_droid_quirks *quirks;
pa_droid_quirks quirks;
/* Mode and input control */
struct _state {
@ -217,27 +239,12 @@ struct pa_droid_profile_set {
#define PA_DROID_OUTPUT_PARKING "output-parking"
#define PA_DROID_INPUT_PARKING "input-parking"
enum pa_droid_quirk_type {
QUIRK_INPUT_ATOI,
QUIRK_SET_PARAMETERS,
QUIRK_CLOSE_INPUT,
QUIRK_UNLOAD_NO_CLOSE,
QUIRK_NO_HW_VOLUME,
QUIRK_OUTPUT_MAKE_WRITABLE,
QUIRK_REALCALL,
QUIRK_UNLOAD_CALL_EXIT,
QUIRK_OUTPUT_FAST,
QUIRK_OUTPUT_DEEP_BUFFER,
QUIRK_COUNT
};
struct pa_droid_quirks {
bool enabled[QUIRK_COUNT];
};
/* Open hardware module */
/* 'config' can be NULL if it is assumed that hw module with module_id already is open. */
pa_droid_hw_module *pa_droid_hw_module_get(pa_core *core, const pa_droid_config_audio *config, const char *module_id);
/* First try to get already open hw module and if none found parse config and quirks from modargs
* and do initial open. */
pa_droid_hw_module *pa_droid_hw_module_get2(pa_core *core, pa_modargs *ma, const char *module_id);
pa_droid_hw_module *pa_droid_hw_module_ref(pa_droid_hw_module *hw);
void pa_droid_hw_module_unref(pa_droid_hw_module *hw);
@ -245,11 +252,11 @@ void pa_droid_hw_module_lock(pa_droid_hw_module *hw);
bool pa_droid_hw_module_try_lock(pa_droid_hw_module *hw);
void pa_droid_hw_module_unlock(pa_droid_hw_module *hw);
bool pa_droid_quirk_parse(pa_droid_hw_module *hw, const char *quirks);
bool pa_droid_quirk_parse(pa_droid_quirks *quirks, const char *quirks_def);
void pa_droid_quirk_log(pa_droid_hw_module *hw);
static inline bool pa_droid_quirk(pa_droid_hw_module *hw, enum pa_droid_quirk_type quirk) {
return hw->quirks && hw->quirks->enabled[quirk];
return hw && hw->quirks.enabled[quirk];
}
bool pa_droid_hw_set_mode(pa_droid_hw_module *hw_module, audio_mode_t mode);
@ -312,9 +319,15 @@ int pa_droid_stream_set_route(pa_droid_stream *s, audio_devices_t device);
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *hw_module,
const pa_sample_spec *default_sample_spec,
const pa_channel_map *default_channel_map);
/* Test if reconfiguring of input stream is needed */
bool pa_droid_stream_reconfigure_input_needed(pa_droid_stream *s,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map,
const pa_proplist *proplist);
bool pa_droid_stream_reconfigure_input(pa_droid_stream *s,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map);
const pa_channel_map *requested_channel_map,
const pa_proplist *proplist);
bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
audio_devices_t device);

View file

@ -81,6 +81,7 @@ struct userdata {
pa_usec_t write_time;
pa_usec_t write_threshold;
audio_devices_t prewrite_devices;
bool prewrite_always;
uint32_t prewrite_silence;
pa_hook_slot *sink_put_hook_slot;
pa_hook_slot *sink_unlink_hook_slot;
@ -98,7 +99,6 @@ struct userdata {
char *voice_property_key;
char *voice_property_value;
pa_sink_input *voice_virtual_sink_input;
pa_sink_input *voice_control_sink_input;
pa_hook_slot *sink_input_volume_changed_hook_slot;
pa_hook_slot *sink_input_put_hook_slot;
@ -133,6 +133,8 @@ typedef struct droid_parameter_mapping {
static void parameter_free(droid_parameter_mapping *m);
static void userdata_free(struct userdata *u);
static void set_voice_volume(struct userdata *u, pa_sink_input *i);
static void apply_volume(pa_sink *s);
static pa_sink_input *find_volume_control_sink_input(struct userdata *u);
static void set_primary_devices(struct userdata *u, audio_devices_t devices) {
pa_assert(u);
@ -293,7 +295,7 @@ static int thread_write(struct userdata *u) {
pa_memblockq_drop(u->memblockq, c.length);
pa_memblock_unref(c.memblock);
u->write_time = 0;
pa_log("failed to write stream (%d)", wrote);
pa_log("failed to write stream (%zd)", wrote);
return -1;
}
@ -395,6 +397,9 @@ static void thread_func(void *userdata) {
if (pa_rtpoll_timer_elapsed(u->rtpoll)) {
pa_usec_t sleept = 0;
if (u->use_hw_volume)
pa_sink_volume_change_apply(u->sink, NULL);
thread_render(u);
thread_write(u);
@ -402,6 +407,9 @@ static void thread_func(void *userdata) {
sleept = u->buffer_time;
pa_rtpoll_set_timer_relative(u->rtpoll, sleept);
if (u->use_hw_volume)
pa_sink_volume_change_apply(u->sink, NULL);
}
} else
pa_rtpoll_set_timer_disabled(u->rtpoll);
@ -459,9 +467,11 @@ static int unsuspend(struct userdata *u) {
pa_log_info("Resuming...");
apply_volume(u->sink);
if (u->prewrite_silence &&
(u->primary_devices | u->extra_devices) & u->prewrite_devices &&
pa_droid_output_stream_any_active(u->stream) == 0) {
(u->prewrite_always || pa_droid_output_stream_any_active(u->stream) == 0)) {
for (i = 0; i < u->prewrite_silence; i++)
thread_write_silence(u);
}
@ -557,30 +567,39 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
return 0;
}
static void sink_set_volume_cb(pa_sink *s) {
static void apply_volume(pa_sink *s) {
struct userdata *u = s->userdata;
pa_cvolume r;
float val;
if (u->use_voice_volume)
return;
if (!u->use_hw_volume)
return;
/* Shift up by the base volume */
pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume);
if (r.channels == 1) {
float val = pa_sw_volume_to_linear(r.values[0]);
pa_log_debug("Set %s hw volume %f", s->name, val);
pa_droid_hw_module_lock(u->hw_module);
if (u->stream->output->stream->set_volume(u->stream->output->stream, val, val) < 0)
pa_log_warn("Failed to set hw volume.");
pa_droid_hw_module_unlock(u->hw_module);
} else if (r.channels == 2) {
float val[2];
for (unsigned i = 0; i < 2; i++)
val[i] = pa_sw_volume_to_linear(r.values[i]);
pa_log_debug("Set %s hw volume %f : %f", s->name, val[0], val[1]);
pa_droid_hw_module_lock(u->hw_module);
if (u->stream->output->stream->set_volume(u->stream->output->stream, val[0], val[1]) < 0)
pa_log_warn("Failed to set hw volume.");
pa_droid_hw_module_unlock(u->hw_module);
}
/* So far every hal implementation doing volume control expects
* both channels to have equal value, so we can just average the value
* from all channels. */
val = pa_sw_volume_to_linear(pa_cvolume_avg(&r));
pa_log_debug("Set %s volume -> %f", s->name, val);
pa_droid_hw_module_lock(u->hw_module);
if (u->stream->output->stream->set_volume(u->stream->output->stream, val, val) < 0)
pa_log_warn("Failed to set volume.");
pa_droid_hw_module_unlock(u->hw_module);
}
static void sink_set_volume_cb(pa_sink *s) {
(void) s;
/* noop */
}
static void sink_write_volume_cb(pa_sink *s) {
apply_volume(s);
}
/* Called from main thread */
@ -627,8 +646,10 @@ static void update_volumes(struct userdata *u) {
u->use_hw_volume ? "hardware" : "software", u->sink->name);
}
if (u->use_hw_volume)
if (u->use_hw_volume) {
pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
pa_sink_set_write_volume_callback(u->sink, sink_write_volume_cb);
}
}
static void set_sink_name(pa_modargs *ma, pa_sink_new_data *data, const char *module_id) {
@ -692,13 +713,8 @@ static pa_hook_result_t sink_input_volume_changed_hook_cb(pa_core *c, pa_sink_in
if (!u->use_voice_volume)
return PA_HOOK_OK;
if (!u->voice_control_sink_input && sink_input_is_voice_control(u, sink_input))
u->voice_control_sink_input = sink_input;
if (u->voice_control_sink_input != sink_input)
return PA_HOOK_OK;
set_voice_volume(u, sink_input);
if (sink_input_is_voice_control(u, sink_input))
set_voice_volume(u, sink_input);
return PA_HOOK_OK;
}
@ -801,14 +817,10 @@ void pa_droid_sink_set_voice_control(pa_sink* sink, bool enable) {
if (u->voice_virtual_stream)
create_voice_virtual_stream(u);
if (u->use_hw_volume)
pa_sink_set_set_volume_callback(u->sink, NULL);
u->sink_input_volume_changed_hook_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED],
PA_HOOK_LATE+10, (pa_hook_cb_t) sink_input_volume_changed_hook_cb, u);
if ((i = find_volume_control_sink_input(u))) {
u->voice_control_sink_input = i;
set_voice_volume(u, i);
}
@ -818,15 +830,11 @@ void pa_droid_sink_set_voice_control(pa_sink* sink, bool enable) {
if (u->voice_virtual_stream)
destroy_voice_virtual_stream(u);
u->voice_control_sink_input = NULL;
pa_hook_slot_free(u->sink_input_volume_changed_hook_slot);
u->sink_input_volume_changed_hook_slot = NULL;
pa_log_debug("Using %s volume control with %s",
u->use_hw_volume ? "hardware" : "software", u->sink->name);
if (u->use_hw_volume)
pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
}
}
@ -837,8 +845,7 @@ static pa_hook_result_t sink_input_put_hook_cb(pa_core *c, pa_sink_input *sink_i
const char *media_str;
audio_devices_t devices;
if (u->use_voice_volume && !u->voice_control_sink_input && sink_input_is_voice_control(u, sink_input)) {
u->voice_control_sink_input = sink_input;
if (u->use_voice_volume && sink_input_is_voice_control(u, sink_input)) {
set_voice_volume(u, sink_input);
}
@ -873,9 +880,6 @@ static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *sin
const char *media_str;
audio_devices_t devices;
if (u->voice_control_sink_input == sink_input)
u->voice_control_sink_input = NULL;
/* Dynamic routing changes do not apply during active voice call. */
if (u->use_voice_volume)
return PA_HOOK_OK;
@ -1061,6 +1065,9 @@ static bool parse_prewrite_on_resume(struct userdata *u, const char *prewrite_re
/* Argument is string of for example "deep_buffer=AUDIO_DEVICE_OUT_SPEAKER:1,primary=FOO:5" */
while ((entry = pa_split(prewrite_resume, ",", &state))) {
audio_devices_t prewrite_devices = 0;
bool prewrite_always = false;
char *tmp;
entry_len = strlen(entry);
devices_index = strcspn(entry, "=");
@ -1081,17 +1088,23 @@ static bool parse_prewrite_on_resume(struct userdata *u, const char *prewrite_re
devices[value_index] = '\0';
value = devices + value_index + 1;
if (!parse_device_list(devices, &u->prewrite_devices)) {
u->prewrite_devices = 0;
if (!parse_device_list(devices, &prewrite_devices))
goto error;
if ((tmp = strstr(value, "/always"))) {
prewrite_always = true;
*tmp = '\0';
}
if (strlen(value) == 0 || pa_atou(value, &b) < 0)
goto error;
if (pa_streq(stream, name)) {
pa_log_info("Using requested prewrite size for %s: %u (%u * %u).",
pa_log_info("Using requested prewrite%s size for %s: %zu (%u * %zu).",
prewrite_always ? "_always" : "",
name, u->buffer_size * b, b, u->buffer_size);
u->prewrite_devices = prewrite_devices;
u->prewrite_always = prewrite_always;
u->prewrite_silence = b;
pa_xfree(entry);
return true;
@ -1131,7 +1144,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
pa_channel_map channel_map;
bool namereg_fail = false;
pa_usec_t latency;
pa_droid_config_audio *config = NULL; /* Only used when sink is created without card */
uint32_t sink_buffer = 0;
const char *prewrite_resume = NULL;
bool mix_route = false;
@ -1244,21 +1256,10 @@ pa_sink *pa_droid_sink_new(pa_module *m,
}
/* Sink wasn't created from inside card module, so we'll need to open
* hw module ourself.
*
* First let's find out if hw module has already been opened, or if we need to
* do it ourself. */
if (!(u->hw_module = pa_droid_hw_module_get(u->core, NULL, module_id))) {
/* No hw module object in shared object db, let's open the module now. */
if (!(config = pa_droid_config_load(ma)))
goto fail;
* hw module ourself. */
if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id)))
goto fail;
pa_droid_config_free(config);
config = NULL;
}
if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id)))
goto fail;
}
/* Default routing */
@ -1286,9 +1287,9 @@ pa_sink *pa_droid_sink_new(pa_module *m,
u->buffer_size = pa_droid_stream_buffer_size(u->stream);
if (sink_buffer) {
u->buffer_size = pa_droid_buffer_size_round_up(sink_buffer, u->buffer_size);
pa_log_info("Using buffer size %u (requested %u).", u->buffer_size, sink_buffer);
pa_log_info("Using buffer size %zu (requested %u).", u->buffer_size, sink_buffer);
} else
pa_log_info("Using buffer size %u.", u->buffer_size);
pa_log_info("Using buffer size %zu.", u->buffer_size);
if ((prewrite_resume = pa_modargs_get_value(ma, "prewrite_on_resume", NULL))) {
if (!parse_prewrite_on_resume(u, prewrite_resume, output->name)) {
@ -1383,7 +1384,7 @@ pa_sink *pa_droid_sink_new(pa_module *m,
/* HAL latencies are in milliseconds. */
latency = pa_droid_stream_get_latency(u->stream);
pa_sink_set_fixed_latency(u->sink, latency);
pa_log_debug("Set fixed latency %llu usec", latency);
pa_log_debug("Set fixed latency %" PRIu64 " usec", latency);
pa_sink_set_max_request(u->sink, u->buffer_size);
if (u->sink->active_port)
@ -1410,7 +1411,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
return u->sink;
fail:
pa_droid_config_free(config);
pa_xfree(thread_name);
if (u)

View file

@ -94,6 +94,7 @@ static void unsuspend(struct userdata *u);
static void source_reconfigure(struct userdata *u,
const pa_sample_spec *reconfigure_sample_spec,
const pa_channel_map *reconfigure_channel_map,
const pa_proplist *proplist,
audio_devices_t update_device);
/* Our droid source may be left in a state of not having an input stream
@ -174,7 +175,7 @@ static int thread_read(struct userdata *u) {
pa_memblock_release(chunk.memblock);
if (readd < 0) {
pa_log("Failed to read from stream. (err %i)", readd);
pa_log("Failed to read from stream. (err %zd)", readd);
goto end;
}
@ -347,7 +348,7 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) {
if (!PA_SOURCE_IS_OPENED(u->source->state))
do_routing(u, data->device);
else
source_reconfigure(u, NULL, NULL, data->device);
source_reconfigure(u, NULL, NULL, NULL, data->device);
return 0;
}
@ -414,9 +415,9 @@ static void update_latency(struct userdata *u) {
if (u->source_buffer_size) {
u->buffer_size = pa_droid_buffer_size_round_up(u->source_buffer_size, u->buffer_size);
pa_log_info("Using buffer size %u (requested %u).", u->buffer_size, u->source_buffer_size);
pa_log_info("Using buffer size %zu (requested %zu).", u->buffer_size, u->source_buffer_size);
} else
pa_log_info("Using buffer size %u.", u->buffer_size);
pa_log_info("Using buffer size %zu.", u->buffer_size);
if (pa_thread_mq_get())
pa_source_set_fixed_latency_within_thread(u->source, pa_bytes_to_usec(u->buffer_size, pa_droid_stream_sample_spec(u->stream)));
@ -429,6 +430,7 @@ static void update_latency(struct userdata *u) {
static void source_reconfigure(struct userdata *u,
const pa_sample_spec *reconfigure_sample_spec,
const pa_channel_map *reconfigure_channel_map,
const pa_proplist *proplist,
audio_devices_t update_device) {
pa_channel_map old_channel_map;
pa_sample_spec old_sample_spec;
@ -453,7 +455,7 @@ static void source_reconfigure(struct userdata *u,
if (update_device)
do_routing(u, update_device);
if (pa_droid_stream_reconfigure_input(u->stream, &new_sample_spec, &new_channel_map))
if (pa_droid_stream_reconfigure_input(u->stream, &new_sample_spec, &new_channel_map, proplist))
pa_log_info("Source reconfigured.");
else
pa_log_info("Failed to reconfigure input stream, no worries, using defaults.");
@ -487,8 +489,10 @@ static pa_hook_result_t source_output_new_hook_callback(void *hook_data,
if (new_data->source != u->source)
return PA_HOOK_OK;
if (pa_sample_spec_equal(&new_data->sample_spec, pa_droid_stream_sample_spec(u->stream)) &&
pa_channel_map_equal(&new_data->channel_map, pa_droid_stream_channel_map(u->stream)))
if (!pa_droid_stream_reconfigure_input_needed(u->stream,
&new_data->sample_spec,
&new_data->channel_map,
new_data->proplist))
return PA_HOOK_OK;
pa_log_info("New source-output connecting and our source needs to be reconfigured.");
@ -500,10 +504,11 @@ static pa_hook_result_t source_output_new_hook_callback(void *hook_data,
source_reconfigure(u,
pa_droid_stream_sample_spec(primary_output),
pa_droid_stream_channel_map(primary_output),
new_data->proplist,
0);
} else
source_reconfigure(u, &new_data->sample_spec, &new_data->channel_map, 0);
source_reconfigure(u, &new_data->sample_spec, &new_data->channel_map, new_data->proplist, 0);
return PA_HOOK_OK;
}
@ -523,12 +528,12 @@ static void source_reconfigure_after_changes(struct userdata *u) {
so = so_i;
}
if (so) {
if (!pa_sample_spec_equal(&so->sample_spec, pa_droid_stream_sample_spec(u->stream)) ||
!pa_channel_map_equal(&so->channel_map, pa_droid_stream_channel_map(u->stream))) {
pa_log_info("Source-output disconnected and our source needs to be reconfigured.");
source_reconfigure(u, &so->sample_spec, &so->channel_map, 0);
}
if (so && pa_droid_stream_reconfigure_input_needed(u->stream,
&so->sample_spec,
&so->channel_map,
so->proplist)) {
pa_log_info("Source-output disconnected and our source needs to be reconfigured.");
source_reconfigure(u, &so->sample_spec, &so->channel_map, so->proplist, 0);
}
}
@ -558,7 +563,6 @@ pa_source *pa_droid_source_new(pa_module *m,
pa_channel_map channel_map;
const char *format;
bool namereg_fail = false;
pa_droid_config_audio *config = NULL; /* Only used when source is created without card */
uint32_t source_buffer = 0;
pa_assert(m);
@ -634,20 +638,10 @@ pa_source *pa_droid_source_new(pa_module *m,
pa_assert_se((u->hw_module = pa_droid_hw_module_get(u->core, NULL, card_data->module_id)));
} else {
/* Source wasn't created from inside card module, so we'll need to open
* hw module ourself.
*
* First let's find out if hw module has already been opened, or if we need to
* do it ourself. */
if (!(u->hw_module = pa_droid_hw_module_get(u->core, NULL, module_id))) {
if (!(config = pa_droid_config_load(ma)))
goto fail;
* hw module ourself. */
if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id)))
goto fail;
pa_droid_config_free(config);
config = NULL;
}
if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id)))
goto fail;
}
/* Default routing */
@ -770,7 +764,6 @@ pa_source *pa_droid_source_new(pa_module *m,
return u->source;
fail:
pa_droid_config_free(config);
pa_xfree(thread_name);
if (u)

View file

@ -729,11 +729,9 @@ int pa__init(pa_module *m) {
struct userdata *u = NULL;
pa_modargs *ma = NULL;
pa_card_new_data data;
pa_droid_config_audio *config = NULL;
const char *module_id;
bool namereg_fail = false;
bool default_profile = true;
const char *quirks;
pa_card_profile *voicecall = NULL;
pa_assert(m);
@ -756,26 +754,8 @@ int pa__init(pa_module *m) {
module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
/* First let's find out if hw module has already been opened, or if we need to
* do it ourself. */
if (!(u->hw_module = pa_droid_hw_module_get(u->core, NULL, module_id))) {
/* No hw module object in shared object db, let's open the module now. */
if (!(config = pa_droid_config_load(ma)))
goto fail;
if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id)))
goto fail;
pa_droid_config_free(config);
config = NULL;
}
if ((quirks = pa_modargs_get_value(ma, "quirks", NULL))) {
if (!pa_droid_quirk_parse(u->hw_module, quirks)) {
pa_log("Failed to parse quirks.");
goto fail;
}
}
if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id)))
goto fail;
pa_droid_quirk_log(u->hw_module);
@ -884,8 +864,6 @@ int pa__init(pa_module *m) {
return 0;
fail:
pa_droid_config_free(config);
if (ma)
pa_modargs_free(ma);