411 lines
15 KiB
Text
411 lines
15 KiB
Text
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
|
|
|
|
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
|
|
|
|
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).
|
|
|
|
When new devices with relevant new enums appear, add enum check to configure.ac.
|
|
CC_CHECK_DROID_ENUM macro will create macros HAVE_ENUM_FOO, STRING_ENTRY_IF_FOO
|
|
and FANCY_ENTRY_IF_FOO if enum FOO exists in HAL audio.h.
|
|
|
|
For example:
|
|
|
|
# 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:
|
|
/* string_conversion_table_output_device[] */
|
|
STRING_ENTRY_IF_OUT_IP
|
|
STRING_ENTRY_IF_OUT_OTHER_NEW
|
|
|
|
/* string_conversion_table_output_device_fancy[] */
|
|
FANCY_ENTRY_IF_OUT_IP("output-ip")
|
|
FANCY_ENTRY_IF_OUT_OTHER_NEW("output-other_new")
|
|
|
|
In addition to the above macros there are also now defines
|
|
HAVE_ENUM_AUDIO_DEVICE_OUT_IP and HAVE_ENUM_AUDIO_DEVICE_OUT_OTHER_NEW.
|
|
|
|
The purpose of droid-modules is to "replace AudioFlinger". Many hardware
|
|
adaptations use ALSA as the kernel interface, but there is no saying that
|
|
someday vendor would create and use something proprietary or otherwise
|
|
different from ALSA. Also the ALSA implementation in droid devices may contain
|
|
funny ways to achieve things (notable example is voicecall) which might be
|
|
difficult to do if interfacing directly with ALSA to replace AudioFlinger.
|
|
Also using ALSA directly would mean that the whole HAL adaptation would need to
|
|
be ported for each new device adaptation. With droid-modules this is much more
|
|
simpler, with somewhat stable HAL (HALv3 as of now, also different vendors add
|
|
their own incompatible extensions) API. In best scenarios using droid-modules
|
|
with new device is just compiling against target.
|
|
|
|
Components
|
|
==========
|
|
|
|
common
|
|
------
|
|
|
|
The common part of PulseAudio Droid modules contains library for handling
|
|
most operations towards audio HAL.
|
|
|
|
### Audio policy configuration parsing
|
|
|
|
To populate our configuration structs there exists two parsers, legacy parser
|
|
for old .conf format present in Android versions 7.0 and older and new xml
|
|
format present from version 7.0 upwards. The legacy format is obsoleted in
|
|
version 7.0 but by default still in use and most 7.0 adaptations probably
|
|
contain the legacy format. But 8.0 adaptations and up start to include only
|
|
the new style xml format configuration files.
|
|
|
|
### Configuration files
|
|
|
|
By default new style xml format is tried first and if it is not found old
|
|
config is read next. If the configuration is in non-default location for
|
|
some reason "config" module argument (available for all modules, card, sink,
|
|
and source) can be used to point to the configuration file location.
|
|
|
|
By default files are tried in following order,
|
|
|
|
/vendor/etc/audio_policy_configuration.xml (new xml format)
|
|
/vendor/etc/audio_policy.conf (legacy format)
|
|
/system/etc/audio_policy_configuration.xml (new xml format)
|
|
/system/etc/audio_policy.conf (legacy format)
|
|
|
|
module-droid-card
|
|
-----------------
|
|
|
|
Ideally only module-droid-card is loaded and then droid-card loads
|
|
configuration, creates profiles and loads sinks and sources based on the
|
|
selected profile.
|
|
|
|
default profile
|
|
---------------
|
|
|
|
When module-droid-card is loaded with default arguments, droid-card will try
|
|
to create a default profile (called surprisingly "default"). The default
|
|
profile will try to merge useful output and input streams to one profile,
|
|
to allow use of possible low latency or deep buffer outputs.
|
|
|
|
For example configuration with
|
|
|
|
audio_hw_modules {
|
|
primary {
|
|
outputs {
|
|
primary {}
|
|
deep_buffer {}
|
|
}
|
|
inputs {
|
|
primary {}
|
|
voice_rx {}
|
|
}
|
|
}
|
|
other {
|
|
...
|
|
}
|
|
}
|
|
|
|
The default profile would contain two sinks, sink.primary and sink.deep_buffer
|
|
and one source, source.droid.
|
|
|
|
Usually this default profile is everything that is needed in normal use, and
|
|
additional profiles created should be needed only for testing things out etc.
|
|
|
|
virtual profiles
|
|
----------------
|
|
|
|
In addition to aforementioned card profiles, droid-card creates some additional
|
|
virtual profiles. These virtual profiles are used when enabling voicecall
|
|
routings etc. When virtual profile is enabled, possible sinks and sources
|
|
previously active profile had are not removed.
|
|
|
|
As an illustration, following command line sequence enables voicecall mode and
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
and after.
|
|
|
|
This sequence follows the droid HAL idea that when changing audio mode the mode
|
|
change is done when next routing change happens. output-parking and
|
|
input-parking ports are just convenience for PulseAudio, where setting already
|
|
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
|
|
|
|
Communication profile is used for VoIP-like applications, to enable some
|
|
voicecall related algorithms without being in voicecall. Ringtone profile
|
|
should be used when ringtone is playing, to again enable possible loudness
|
|
related optimizations etc. Voicecall-record profile can be enabled when
|
|
voicecall profile is active.
|
|
|
|
debugging profiles
|
|
------------------
|
|
|
|
If needed in favour of default profile one can opt to creating combinations
|
|
of all output and input definitions in a module definition. This can be
|
|
done by passing "default=false" to module-droid-card. Module argument
|
|
module_id will then defines which module to load (by default "primary").
|
|
Without default profile all input and output definitions are translated
|
|
to PulseAudio card profiles. For example configuration with
|
|
|
|
audio_hw_modules {
|
|
primary {
|
|
outputs {
|
|
primary {}
|
|
lpa {}
|
|
}
|
|
inputs {
|
|
primary {}
|
|
}
|
|
}
|
|
other {
|
|
...
|
|
}
|
|
}
|
|
|
|
Would map to card profiles (<output>-<input>) primary-primary and lpa-primary.
|
|
|
|
module-droid-sink and module-droid-source
|
|
-----------------------------------------
|
|
|
|
Normally user should not need to load droid-sink or droid-source modules by
|
|
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_hw_modules {
|
|
primary {
|
|
outputs {
|
|
primary {
|
|
devices = AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_EARPIECE|AUDIO_DEVICE_OUT_WIRED_HEADPHONE
|
|
}
|
|
lpa {}
|
|
}
|
|
inputs {
|
|
primary {
|
|
devices = AUDIO_DEVICE_IN_BUILTIN_MIC
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Would create following ports for sink.primary:
|
|
* output-speaker
|
|
* output-earpiece
|
|
* output-wired_headphone
|
|
* output-speaker+wired_headphone
|
|
|
|
And for source.primary:
|
|
* input-builtin_mic
|
|
|
|
Only exception to one device one port rule is if output device list has both
|
|
OUT_SPEAKER and OUT_WIRED_HEADPHONE, then one additional combination port is
|
|
generated. How the devices are called in sink and source ports are defined in
|
|
droid-util-XXX.h
|
|
|
|
Changing output routing is then as simple as
|
|
|
|
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
|
|
control sinks and sources. (For example in SailfishOS this entity is OHM with
|
|
accessory-plugin and pulseaudio-policy-enforcement module for actually making
|
|
the port switching)
|
|
|
|
Droid source automatic reconfiguration
|
|
--------------------------------------
|
|
|
|
As droid HAL makes assumptions on (input) routing based on what the parameters
|
|
for the stream are (device, sample rate, channels, format, etc.) normal
|
|
PulseAudio sources are a bit inflexible as only sample rate can change after
|
|
source creation and even then there are restrictions based on alternative
|
|
sample rate value.
|
|
|
|
To overcome this and to allow some more variables affecting the stream being
|
|
passed to the input stream droid source is modified to reconfigure itself
|
|
with the source-output that connects to it. This means, that just looking at
|
|
inactive source from "pactl list" listing doesn't tell the whole story.
|
|
|
|
Droid source is always reconfigured with the *last* source-output that
|
|
connects to it, possibly already connected source-outputs will continue
|
|
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
|
|
|
|
Classifying sinks and sources
|
|
-----------------------------
|
|
|
|
Certain property values are set to all active sinks and sources based on their
|
|
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
|
|
|
|
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
|
|
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"
|
|
|
|
There also may be just one sink, with all the properties defined as "true"
|
|
and so on.
|
|
|
|
Right now there exists only one source (input device) which will always have
|
|
both properties as true.
|
|
|
|
Quirks
|
|
------
|
|
|
|
There are some adaptations that require hacks to get things working. These
|
|
hacks can be enabled or disabled with module argument "quirks". Some quirks
|
|
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.
|
|
|
|
For example, to disable input_atoi and enable close_input quirks, use module
|
|
argument
|
|
|
|
quirks=-input_atoi,+close_input
|
|
|
|
Volume control during voicecall
|
|
-------------------------------
|
|
|
|
When voicecall virtual profile is enabled, active droid-sink is internally
|
|
switched to voicecall volume control mode. What this means is changing the sink
|
|
volume or volume of normal streams connected to the sink do not change active
|
|
voicecall volume. Special stream is needed to control the voicecall volume
|
|
level. By default this stream is identified by stream property media.role,
|
|
with value "phone". This can be changed by providing module arguments
|
|
voice_property_key and voice_property_value to module-droid-card.
|
|
|
|
Usually droid HAL has 6 volume levels for voicecall.
|
|
|
|
Temporary sink audio routing
|
|
----------------------------
|
|
|
|
It is possible to add temporary route to sink audio routing with specific
|
|
stream property. When stream with property key
|
|
droid.device.additional-route connects to droid-sink, this extra route is set
|
|
(if possible) as the enabled route for the duration of the stream.
|
|
|
|
For example, if droid-sink has active port output-wired_headphone:
|
|
|
|
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.
|
|
|
|
module-droid-keepalive
|
|
----------------------
|
|
|
|
Keepalive module is MCE (https://github.com/nemomobile/mce) specific module
|
|
tracking sink/source activity and keeping a WAKELOCK when there are active
|
|
streams.
|