Compare commits

...
Sign in to create a new pull request.

119 commits

Author SHA1 Message Date
Penelope Gwen
c0e8232a36 version bump 2026-03-29 18:17:19 -07:00
Penelope Gwen
18e6cd030e correct config file paths 2026-03-29 18:00:33 -07:00
Penelope Gwen
e689a5abc9 add config files 2026-03-29 14:10:35 -07:00
Adam Boardman
974ae573ad Various upstream (sfos) fixes and avoid an assert on internal structure confusion caused by move to new upstream Pulseaudio that appears to do things differently, this will probably need revisiting 2023-05-05 10:16:14 +01:00
Adam Boardman
f28da7fddf Build fixes post numbering fix 2023-05-04 15:35:11 +01:00
Adam Boardman
caf6773723 Updated version number 2023-04-23 10:02:48 +01:00
Adam Boardman
5c0ca5b077 Update changelog and possible build fixes 2023-04-04 16:05:33 +01:00
Adam Boardman
f1fb79c3e0 Merge remote-tracking branch 'upstream/bookworm' into bookworm 2023-04-04 15:10:10 +01:00
Eugenio Paolantonio (g7)
112843b765 [skip ci] [ci] Use the checkout step provided by the droidian-buildd orb
Signed-off-by: Eugenio Paolantonio (g7) <me@medesimo.eu>
2023-01-07 01:43:51 +01:00
Eugenio Paolantonio (g7)
10599fe519 [skip ci] Replace Drone with CircleCI
Signed-off-by: Eugenio Paolantonio (g7) <me@medesimo.eu>
2022-10-09 13:26:56 +02:00
Eugenio Paolantonio (g7)
85813e293a [packaging] Provide the virtual packages pulseaudio-modules-droid-apispecific{,-dev}
Signed-off-by: Eugenio Paolantonio (g7) <me@medesimo.eu>
2022-03-06 20:57:49 +01:00
Eugenio Paolantonio (g7)
145e49611a [packaging] Move to the -modern variant
Signed-off-by: Eugenio Paolantonio (g7) <me@medesimo.eu>
2022-02-28 00:59:48 +01:00
Eugenio Paolantonio (g7)
d3253e5175 Merge tag 'upstream/14.2.97' into feature/bookworm/upgrade-14.2.97-modern 2022-02-28 00:57:12 +01:00
Juho Hämäläinen
cef8f78746 Bump packaging version. 2022-02-25 13:11:05 +02:00
Juho Hämäläinen
04b14b8aac
Merge pull request #114 from jusa/cal_wait
Enable audio_cal_wait properly.
2022-02-25 13:10:20 +02:00
Juho Hämäläinen
0adc96eb56 common: Whitespace fix. 2022-02-25 13:05:27 +02:00
Juho Hämäläinen
47a043d8dc common: Enable audio_cal_wait properly.
Actual call to the function was missing.

[common] Enable audio_cal_wait properly. JB#55832
2022-02-25 13:05:10 +02:00
Juho Hämäläinen
a995948689 Bump packaging version. 2022-02-24 15:36:40 +02:00
Juho Hämäläinen
c4be583fbf
Merge pull request #113 from jusa/droid11
Update implementation for Android 11, drop old support.
2022-02-24 15:35:18 +02:00
Juho Hämäläinen
51d85423d5 common: Force mono 16kHz with AUDIO_OUTPUT_FLAG_VOIP_RX. 2022-02-24 15:05:08 +02:00
Juho Hämäläinen
ffe02f26b1 packaging: Update spec.
[pulseaudio-modules-droid] Update implementation for Android 11. Fixes JB#55832
2022-02-24 15:05:08 +02:00
Juho Hämäläinen
8b1bc6cbce packaging: Remove old sbj spec. 2022-02-24 15:05:08 +02:00
Juho Hämäläinen
1c1d82dfb9 README: Update content. 2022-02-24 15:05:06 +02:00
Juho Hämäläinen
5c53ef736f build: Update build flags. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
77b1e60df0 common: Always flag voip rx if enabled. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
385ccb8e4c options: voip rx is always enabled 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
a37363a46c card: Create voip_rx sink in communication profile. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
e0d66cf4ad card: Update card with new common. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
2d1a50d450 card: Use VSID profiles only for debugging. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
c895311db5 source: Update source with new common. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
5238bfd192 sink: Update sink with new common. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
d4a8167c11 common: Update version requirements. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
b08cc4d219 common: Implement stream handling using device and mix ports. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
0b1221a047 card: Remove set_parameters callback. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
9ffca4ecd5 common: Remove set_parameters callback from card data. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
c359126658 common: Remove unused enums. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
73f93f1967 common: Remove _VSID define. 2022-02-24 14:48:12 +02:00
Juho Hämäläinen
5d4a5af1f5 common: Rename quirks to options and use normal modargs for parsing. 2022-02-24 14:48:10 +02:00
Juho Hämäläinen
532f6c55bf common: Update audio enum header.
Use current droid version 11 as base.
2022-02-24 09:08:28 +02:00
Juho Hämäläinen
1ac75ed3b0 common: Remove legacy from converter, parse channels as values. 2022-02-24 09:08:26 +02:00
Juho Hämäläinen
c02f917ebb common: Parser uses new config structures. 2022-02-24 09:08:21 +02:00
Juho Hämäläinen
4ca8716bd1 common: Use replace in place from utils in parser. 2022-02-24 09:08:21 +02:00
Juho Hämäläinen
67eeb83105 common: Parser supports new xml attributes. 2022-02-24 09:08:21 +02:00
Juho Hämäläinen
9e4c6fa8f1 common: Update config structures.
Match xml format style.
2022-02-24 09:08:17 +02:00
Juho Hämäläinen
40c320bc07 common: Add two string utility functions. 2022-02-24 09:08:14 +02:00
Juho Hämäläinen
eefdb31f57 common: Simple list implementation. 2022-02-24 09:08:11 +02:00
Juho Hämäläinen
b4fb20eafe common: Always use expat implementation for parser. 2022-02-23 10:27:49 +02:00
Juho Hämäläinen
fdd22b6949 common: Remove legacy conversion header. 2022-02-23 10:22:22 +02:00
Juho Hämäläinen
17fcb9e074 common: Remove legacy parser. 2022-02-23 10:21:03 +02:00
Juho Hämäläinen
1e992b6954 build: Update flag checks for current version. 2022-02-23 09:23:27 +02:00
Juho Hämäläinen
1daab94724 build: Expat is mandatory. 2022-02-23 09:23:03 +02:00
Juho Hämäläinen
0898217179 Bump packaging version. 2022-02-21 09:34:20 +02:00
Juho Hämäläinen
dbc7d678bb
Merge pull request #112 from jusa/new-api
New API for calling HAL functions.
2022-02-21 09:31:03 +02:00
Juho Hämäläinen
ef56dd4964 README: Add section about pa_shared based API. 2022-02-18 15:45:32 +02:00
Juho Hämäläinen
1cc2a0822e common: New API for calling HAL functions.
New pa_shared based API for calling HAL functions so that other modules
needing these functions don't need to link with libdroid.

[common] New API for calling HAL functions. JB#55832
2022-02-18 15:45:32 +02:00
Juho Hämäläinen
8df3961333
Merge pull request #111 from g7/for-upstream/mer-hybris/no-channels
common: Handle profiles with no supported channels
2022-02-18 15:10:19 +02:00
Eugenio Paolantonio (g7)
b23e20db5c common: Handle profiles with no supported channels
When a profile doesn't have any supported channels, pa_conversion_parse_*_channels
returns false even though the parsing itself succeeded.
This in turn makes the parsing to be aborted altogether.

Handle this case by properly ignoring the profile.

This commit changes the pa_conversion_parse_*_channels functions so that
they return the channel count, or -1 if the parsing has been unsuccessful.

[common] Handle profiles with no supported channels

Signed-off-by: Eugenio Paolantonio (g7) <me@medesimo.eu>
2022-02-18 13:15:21 +01:00
Eugenio Paolantonio (g7)
0886d509fc Merge tag 'upstream/14.2.93' into feature/bookworm/upgrade-14.2.93 2022-01-30 19:10:35 +01:00
Eugenio Paolantonio (g7)
80015ede82 [pulseaudio-modules-droid] Move to bookworm
Signed-off-by: Eugenio Paolantonio (g7) <me@medesimo.eu>
2021-12-08 16:19:04 +01:00
Juho Hämäläinen
9c6842e7d9 Bump packaging version. 2021-10-08 15:43:09 +03:00
Juho Hämäläinen
0d739ca587
Merge pull request #109 from jusa/jb55711
Use more generic stream property for audio source.
2021-10-08 15:42:31 +03:00
Juho Hämäläinen
1781dab3b8 common: Use more generic stream property for audio source.
[common] Use more generic stream property for audio source. JB#55711
2021-10-08 15:20:58 +03:00
Juho Hämäläinen
98d2c7358a Bump packaging version. 2021-10-08 08:32:35 +03:00
Juho Hämäläinen
11a09b005d
Merge pull request #108 from jusa/jb55711
Enable custom audio sources with input streams.
2021-10-08 08:31:12 +03:00
Juho Hämäläinen
d9e2ddfd72 common: Fix segfault with adaptations which assume empty adress is not NULL.
Some adaptations don't check for NULL before using the address which
leads to a crash. Let's just use empty string for address.

[common] Fix segfault with adaptations which assume empty adress is not NULL. Fixes JB#55831
2021-10-08 08:30:12 +03:00
Juho Hämäläinen
baecfc7cdd source: Use source-output proplist when reconfiguring input stream.
Use updated functions from common which utilize proplist when reconfiguring
input stream.

[source] Use source-output proplist when reconfiguring input stream. JB#55711
2021-10-07 12:06:31 +03:00
Juho Hämäläinen
2d7ee841ed common: Allow custom audio source based on property.
When reconfiguring input stream set custom audio source if one
is defined in droid.input.source property. Allowed values are
audio source fancy names listed in
string_conversion_table_audio_source_fancy.

[common] Allow custom audio source based on property. JB#55711
2021-10-07 12:04:52 +03:00
Juho Hämäläinen
4103c31aec common: Add generic converter for fancy audio sources.
[common] Add generic converter for fancy audio sources.
2021-10-07 12:00:59 +03:00
Eugenio Paolantonio (g7)
c6da10943a [ci] Updated to match new name [CI SKIP]
Signed-off-by: Eugenio Paolantonio (g7) <me@medesimo.eu>
2021-03-24 00:39:36 +01:00
Eugenio Paolantonio (g7)
74c17e4dab [packaging] pulseaudio-modules-droid-dev: fix dependency on pulseaudio-modules-droid
Signed-off-by: Eugenio Paolantonio (g7) <me@medesimo.eu>
2021-03-13 19:18:47 +01:00
r3vn
88d0b2954a [ci] build using Drone CI 2021-03-02 23:50:32 +01:00
r3vn
3ab49846d2 Merge remote-tracking branch 'mer-hybris/master' into feature/bullseye/14.2.88 2021-03-02 23:49:27 +01:00
r3vn
fe0f517719 [packaging] fix missing files 2020-11-02 16:05:09 +01:00
r3vn
2ccbf14739 Merge branch 'upstream' into feature/bullseye/12.2.86 2020-10-30 00:11:03 +01:00
r3vn
7052e809f9 [packaging] fixed package name 2020-10-13 00:31:20 +02:00
Eugenio Paolantonio (g7)
2f5ddab8f4 [packaging] Build-depend on pulseaudio-pulsecore-dev
Signed-off-by: Eugenio Paolantonio (g7) <me@medesimo.eu>
2020-10-11 00:24:48 +02:00
r3vn
08afdc0932 build using travis-ci 2020-10-09 23:17:10 +02:00
Ratchanan Srirattanamet
bc67f05782 add libevdev-dev as a build dependency 2020-06-03 15:01:49 +02:00
Ratchanan Srirattanamet
5cab2af137 card: read headphone availability from input device
Recent Android devices start to provide headphone availability via input
device instead of h2w switch. This renders droid-extcon useless.

This commit introduce droid-extevdev, a simple code that will read
headphone availability from the input device, using libevdev as an
abstraction layer. This means the code now depends on libevdev (but it
can be made optional later on if needed).

This should make headphone availability works on newer Android devices
without having to resort to h2w kernel driver.
2020-06-03 13:51:08 +02:00
Ratchanan Srirattanamet
81504b9ec4 Add virtual_voice_stream
Adding option to create voice virtual stream when voicecall is active
2020-06-03 13:49:09 +02:00
Alfred Neumayer
0730b77e39 src: Add h2w detection code from pulseaudio-packaging
This applies changes from pulseaudio-packaging for detecting
headset & headphones via the h2w virtual switch file and udev.

Compared to pulseaudio-packaging this now forces udev as a requirement
rather than providing a compile-time switch.
2020-06-03 13:49:09 +02:00
NeKit
e5ba918cdb New upstream release 12.2.84 2020-06-03 01:55:32 +02:00
NeKit
0e120cf797 Merge remote-tracking branch 'mer/master' into buster 2020-06-03 01:54:22 +02:00
NeKit
fa0e8a5535 Add libexpat1-dev as build dependency (for XML config parsing) 2019-11-20 00:24:54 +01:00
TheKit
1787c87407 Branch packaging for Gemian 2019-11-13 22:29:18 +01:00
Jonah Brüchert
58d1e45090 Detect pulseaudio version from the system, don't assume it's always the same as the droid module's one 2019-11-13 13:12:02 +01:00
Marius Gripsgard
c5cca5314f Fix pkgconf for debian based systems.
Since debian based systems uses lib/{arch}/ as libdir and /usr/lib/pulse-* for modules this causes -L to search for -ldroid-utils in lib/{arch}/
2019-11-13 13:10:06 +01:00
TheKit
5edb6d17e8 Merge remote-tracking branch 'debian-pm/master' 2019-11-13 13:05:53 +01:00
Jonah Brüchert
b5972a7dcc
Fix pulseaudio droid module version 2019-11-12 12:35:59 +01:00
Jonah Brüchert
940476a773
Release 12.2.79-2 2019-11-12 12:06:58 +01:00
Jonah Brüchert
2b9ac12840
Fix generating the pkgconfig files with proper pathes 2019-11-12 12:05:02 +01:00
Jonah Brüchert
6803ef07e7
Release new upstream version 12.2.79-1 2019-10-06 15:23:59 +02:00
Jonah Brüchert
ec3260a88f
Release 12.2.78-1 2019-04-25 22:37:48 +02:00
Jonah Brüchert
a61a670284
New upstream release 12.2.78-1 2019-04-25 22:37:32 +02:00
Jonah Brüchert
ad620b712a
New upstream release 11.1.76 2018-12-03 21:39:37 +01:00
Jonah Brüchert
8ef0e32e18
New upstream release 11.1.75 2018-11-13 21:18:48 +01:00
Jonah Brüchert
05a5925baf
Update Vcs information 2018-11-02 19:14:50 +01:00
Jonah Brüchert
f293111905
Move .gitlab-ci.yml into debian folder 2018-10-24 18:17:10 +02:00
Jonah Brüchert
addd9f1adc
New upstream release (11.1.74) 2018-10-13 20:53:09 +02:00
Jonah Brüchert
6355803c66
Make sure we install to the correct module path 2018-10-13 20:52:37 +02:00
Jonah Brüchert
ed595dec5b
Add GitLab ci configuration 2018-10-07 00:52:19 +02:00
JBBgameich
9d09908b82 New upstrem release (11.1.73) 2018-08-28 18:57:12 +02:00
JBBgameich
36c03408c1 New upstream release 2018-08-28 18:24:35 +02:00
JBBgameich
8ad1d210a8 New upstream release 2018-08-28 18:24:35 +02:00
Bhushan Shah
ec084f07a2 Revert "rules: Use $(overridden_command)"
This reverts commit 95860ffe29.
2018-08-27 09:40:14 +05:30
JBBgameich
5573d62ebe
Remove unused parts according to lintian 2018-07-04 14:21:03 +02:00
JBBgameich
88c2be2333 Run wrap-and-sort 2018-06-24 16:19:11 +00:00
JBBgameich
ffee6f91fc Update git repository url 2018-06-22 19:22:01 +00:00
JBBgameich
95860ffe29 rules: Use $(overridden_command) 2018-06-19 19:43:40 +02:00
JBBgameich
12185c8601 pulseaudio-module-droid-dev: update dependency name 2018-06-08 17:20:50 +00:00
JBBgameich
32edc3fea0 Add workaround for proper path detection 2018-06-06 15:01:08 +00:00
JBBgameich
736a82a7a1 New upstream release 11.1.68 2018-06-06 14:06:27 +00:00
JBBgameich
9e5b1fa04a Fix rules 2018-06-03 10:33:56 +00:00
JBBgameich
1a7bf9de98 Rename to pulseaudio-module-droid 2018-06-03 09:08:01 +00:00
JBBgameich
c31ce84c0f Remove fail-missing 2018-05-27 00:10:39 +02:00
JBBgameich
672f6a9455 Fix clean step 2018-05-26 22:31:21 +02:00
JBBgameich
91dbb2459e Fix dependency name 2018-05-26 22:03:45 +02:00
JBBgameich
48756afc70 Add basic copyright 2018-05-26 20:58:02 +02:00
JBBgameich
4edf08d933 Initial commit 2018-05-26 20:51:54 +02:00
38 changed files with 2938 additions and 3171 deletions

3
.gitignore vendored
View file

@ -19,3 +19,6 @@ config.status
libtool
*.pc
stamp-*
# Added by Droidian
!.circleci/

238
README.md
View file

@ -1,25 +1,23 @@
PulseAudio Droid modules
========================
For adapdations for Android versions 4 to 10,
see [pulseaudio-modules-droid-jb2q](https://github.com/mer-hybris/pulseaudio-modules-droid-jb2q)
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
Linking to libdroid is **not encouraged**, usually only HAL functions are needed
which can be accessed using the pulsecore shared API (see below).
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
* 10.x
* 11.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).
src/common/droid-util-audio.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
@ -66,28 +64,19 @@ 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 parser reads audio policy xml 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.
If the configuration is in non-default location for some reason "config"
module argument can be used to point to the configuration file location.
By default files are tried in following order,
/odm/etc/audio_policy_configuration.xml (new xml format)
/vendor/etc/audio/audio_policy_configuration.xml (new xml format)
/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)
/odm/etc/audio_policy_configuration.xml
/vendor/etc/audio/audio_policy_configuration.xml
/vendor/etc/audio_policy_configuration.xml
/system/etc/audio_policy_configuration.xml
module-droid-card
-----------------
@ -99,39 +88,15 @@ 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,
When module-droid-card is loaded with default arguments, droid-card will
create a default profile (called unsurprisingly "default"). The default
profile will merge supported 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
In addition to aforementioned card profile, 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.
@ -139,9 +104,6 @@ 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
@ -151,11 +113,11 @@ 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-card-profile droid_card.primary default
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
With this example sequence sinks and sources are the ones from default
card profile, and they are maintained for the whole duration of the voicecall
and after.
@ -177,32 +139,9 @@ 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.
If mix port with flag AUDIO_OUTPUT_FLAG_VOIP_RX exists when communication
virtual profile is enabled additional droid-sink is created with the config
defined in the mix port. Voip audio should then be played to this new sink.
module-droid-sink and module-droid-source
-----------------------------------------
@ -211,49 +150,15 @@ 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_configuration.xml (where available) or audio_policy.conf (legacy),
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
Changing output routing is 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)
Sinks or sources 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
--------------------------------------
@ -303,6 +208,7 @@ Currently following properties are set:
* droid.output.low_latency
* droid.output.media_latency
* droid.output.offload
* droid.output.voip
* For droid sources
* droid.input.builtin
* droid.input.external
@ -326,24 +232,20 @@ and so on.
Right now there exists only one source (input device) which will always have
both properties as true.
Quirks
------
Options
-------
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.
hacks can be enabled or disabled with module argument "options". Some options
are enabled by default with some adaptations etc. There are also some more
generic options.
Currently there are following quirks:
Currently there are following options:
* 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.
@ -352,22 +254,16 @@ Currently there are following quirks:
* 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.
* hw_volume
* Enabled 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.
control not affecting volume level. To fix this disable this option.
* 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
try enabling this option so that the realcall parameter is applied
when switching to voicecall profile.
* unload_call_exit
* Disabled by default.
@ -377,36 +273,39 @@ Currently there are following quirks:
* output_fast
* Enabled by default.
* Create separate sink if AUDIO_OUTPUT_FLAG_FAST is found. If this sink
is misbehaving try disabling this quirk.
is misbehaving try disabling this option.
* 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.
this sink is misbehaving try disabling this option.
* 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
this option 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.
try enabling this option.
* output_voip_rx
* Enabled by default.
* When audio configuration has AUDIO_OUTPUT_FLAG_VOIP_RX special voip
sink is created when AUDIO_MODE_IN_COMMUNICATION is active and the
sink is classified as droid.output.voip. If this is not desired then
by disabling this option the voip sink is not classified but is still
created normally.
* record_voice_16k
* Disabled by default.
* When enabled voice call recording source is forced to sample rate
of 16kHz.
For example, to disable input_atoi and enable close_input quirks, use module
argument
Options can be enabled or disabled normally as module arguments, for example:
quirks=-input_atoi,+close_input
load-module module-droid-card hw_volume=false record_voice_16k=true
Volume control during voicecall
-------------------------------
@ -436,7 +335,22 @@ For example, if droid-sink has active port output-wired_headphone:
As long as the new stream is connected to droid-sink, output routing is
SPEAKER.
module-droid-keepalive
----------------------
HAL API
-------
Module relocated to its own package pulseaudio-module-keepalive.
If there is need to call HAL directly from other modules it can be done with
function pointer API stored in PulseAudio shared map.
Once the function pointers are acquired when called they will work the same
way as defined in Android audio.h. For example:
void *handle;
int (*set_parameters)(void *handle, const char *key_value_pairs);
char* (*get_parameters)(void *handle, const char *keys);
handle = pa_shared_get(core, "droid.handle.v1");
set_parameters = pa_shared_get(core, "droid.set_parameters.v1");
get_parameters = pa_shared_get(core, "droid.get_parameters.v1");
set_parameters(handle, "route=2;");
char *value = get_parameters(handle, "connected");

View file

@ -1,6 +1,6 @@
AC_PREREQ(2.60)
AC_INIT([pulseaudio-modules-droid], [m4_esyscmd(./git-version-gen .tarball-version)], [mer-general@lists.merproject.org])
AC_INIT([pulseaudio-modules-droid-modern], [m4_esyscmd(./git-version-gen .tarball-version)], [mer-general@lists.merproject.org])
AC_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE([foreign -Wall silent-rules])
AC_CONFIG_MACRO_DIR(m4)
@ -28,7 +28,7 @@ AC_SUBST(PA_MAJOR, pa_major)
AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
AC_SUBST(PA_MODULE_VERSION, pa_module_version)
DESIRED_FLAGS="-Wall -W -Wextra -pedantic -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wpacked -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option" # PulseAudio 0.9.15 usess same + -Wcast-align -Wdeclaration-after-statement
DESIRED_FLAGS="-std=gnu11 -Wall -W -Wextra -pipe -Wno-long-long -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing -Wwrite-strings -Wno-unused-parameter -fno-common -fdiagnostics-show-option -fdiagnostics-color=auto"
for flag in $DESIRED_FLAGS ; do
CC_CHECK_CFLAGS([$flag], [CFLAGS="$CFLAGS $flag"])
@ -170,7 +170,7 @@ AS_IF([test "$pulseaudio_cv__Bool" = "yes"], [
#LT_INIT([dlopen win32-dll disable-static])
AC_PROG_LIBTOOL
PKG_CHECK_MODULES([PULSEAUDIO], [libpulse >= 5.0 pulsecore >= 5.0])
PKG_CHECK_MODULES([PULSEAUDIO], [libpulse >= 14.2 pulsecore >= 14.2])
AC_SUBST(PULSEAUDIO_CFLAGS)
AC_SUBST(PULSEAUDIO_LIBS)
@ -187,88 +187,26 @@ PKG_CHECK_MODULES([EVDEV], [libevdev >= 1.0])
AC_SUBST(EVDEV_CFLAGS)
AC_SUBST(EVDEV_LIBS)
#### expat (for xml config format parsing) (optional) ####
#### expat (for xml config format parsing) ####
AC_ARG_ENABLE([xml],
AS_HELP_STRING([--disable-xml],[Disable optional xml config support]))
AS_IF([test "x$enable_xml" != "xno"],
[PKG_CHECK_MODULES(EXPAT, [ expat >= 2.1 ], HAVE_EXPAT=1, HAVE_EXPAT=0)],
HAVE_EXPAT=0)
AS_IF([test "x$enable_xml" = "xyes" && test "x$HAVE_EXPAT" = "x0"],
[AC_MSG_ERROR([*** expat not found])])
AC_SUBST(HAVE_EXPAT)
PKG_CHECK_MODULES([EXPAT], [expat >= 2.1])
AC_SUBST(EXPAT_CFLAGS)
AC_SUBST(EXPAT_LIBS)
AM_CONDITIONAL([HAVE_EXPAT], [test "x$HAVE_EXPAT" = x1])
AS_IF([test "x$HAVE_EXPAT" = "x1"], AC_DEFINE([HAVE_EXPAT], 1, [Have expat?]))
AS_IF([test "x$HAVE_EXPAT" = "x1"], ENABLE_XML="yes (expat)", ENABLE_XML=no)
# Output devices
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_HDMI])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_HDMI_ARC])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_TELEPHONY_TX])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_LINE])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_SPDIF])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_AUX_LINE])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_SPEAKER_SAFE])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_FM])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_FM_TX])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_ANC_HEADSET])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_ANC_HEADPHONE])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_PROXY])
# Added in 6.0
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_IP])
# Input devices
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_HDMI])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_TELEPHONY_RX])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_FM_TUNER])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_TV_TUNER])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_LINE])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_SPDIF])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_BLUETOOTH_A2DP])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_LOOPBACK])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_PROXY])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_FM_RX])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_FM_RX_A2DP])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_ALL_USB])
# Added in 6.0
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_IN_IP])
# Audio sources
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_SOURCE_ECHO_REFERENCE])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_SOURCE_FM_TUNER])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_SOURCE_FM_RX])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_SOURCE_FM_RX_A2DP])
# Output flags
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_NON_BLOCKING])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_HW_AV_SYNC])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_VOIP_RX])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_INCALL_MUSIC])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH])
# Added in 6.0
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_TTS])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_RAW])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_SYNC])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO])
# Input flags
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_NONE])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_FAST])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_HW_HOTWORD])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_RAW])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_SYNC])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_MMAP_NOIRQ])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_VOIP_TX])
# Channels
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_CHANNEL_OUT_SURROUND])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_CHANNEL_OUT_5POINT1_BACK])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_CHANNEL_OUT_5POINT1_SIDE])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_CHANNEL_IN_VOICE_CALL_MONO])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO])
@ -336,5 +274,4 @@ echo "
modules directory: ${modlibexecdir}
Droid device: ${droiddevice}
XML config support: ${ENABLE_XML}
"

View file

@ -1,3 +0,0 @@
include: 'https://gitlab.com/debian-pm/tools/build/raw/master/docker/gitlab-ci-base.yml'
image: jbbgameich/build

13
debian/changelog vendored
View file

@ -1,3 +1,16 @@
pulseaudio-modules-droid-modern (16.1.101+gemian11+nmu1) bookworm; urgency=medium
* Non-maintainer upload.
* add config files
-- Penelope Gwen <support@pogmom.me> Sun, 29 Mar 2026 14:04:19 -0700
pulseaudio-modules-droid-modern (16.1.101+gemian1) unstable; urgency=medium
* New upstream nabbed from droidian
-- Adam Boardman <adamboardman@gmail.com> Tue, 04 Apr 2023 16:02:47 +0100
pulseaudio-modules-droid (12.2.84+gemian) buster; urgency=medium
* New upstream release

31
debian/control vendored
View file

@ -1,7 +1,7 @@
Source: pulseaudio-modules-droid
Source: pulseaudio-modules-droid-modern
Section: sound
Priority: optional
Build-Depends: android-headers (>= 23),
Build-Depends: android-headers,
check,
debhelper (>= 11),
dh-exec,
@ -15,18 +15,29 @@ Build-Depends: android-headers (>= 23),
libexpat1-dev,
libevdev-dev,
libudev-dev
Maintainer: Marius Gripsgard <marius@ubports.com>
Maintainer: Adam Boardman <adamboardman@gmail.com>
Standards-Version: 4.3.0
Homepage: https://github.com/mer-hybris/pulseaudio-modules-droid
Vcs-Git: https://gitlab.com/debian-pm/halium/pulseaudio-modules-droid.git
Vcs-Browser: https://gitlab.com/debian-pm/halium/pulseaudio-modules-droid
Vcs-Git: https://github.com/gemian/pulseaudio-modules-droid-modern.git
Vcs-Browser: https://github.com/gemian/pulseaudio-modules-droid-modern
Package: pulseaudio-module-droid
Package: pulseaudio-modules-droid-modern
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: PulseAudio Droid HAL module.
Provides: pulseaudio-modules-droid-apispecific
Conflicts: pulseaudio-modules-droid-apispecific
Description: PulseAudio Droid HAL module
Pulseaudio modules to interact with the Android HAL.
.
This package contains the actual modules, for Android 11+.
Package: pulseaudio-module-droid-dev
Package: pulseaudio-modules-droid-modern-dev
Architecture: any
Depends: pulseaudio-module-droid, ${misc:Depends}
Description: PulseAudio Droid HAL module development headers.
Depends: pulseaudio-modules-droid-modern (= ${binary:Version}),
${misc:Depends},
Provides: pulseaudio-modules-droid-apispecific-dev
Conflicts: pulseaudio-modules-droid-apispecific-dev
Description: PulseAudio Droid HAL module - development headers
Pulseaudio modules to interact with the Android HAL.
.
This package contains the development headers.

26
debian/copyright vendored
View file

@ -1,11 +1,10 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: pulseaudio-modules-droid
Source: https://github.com/mer-hybris/pulseaudio-modules-droid
Upstream-Name: pulseaudio-modules-droid-modern
Source: https://github.com/mer-hybris/pulseaudio-modules-droid-modern
Files: *
Copyright: 1991, 1999, Free Software Foundation, Inc.
License: UNKNOWN
Please fill license UNKNOWN from header of *
Copyright: 2013-2022, Jolla Ltd.
License: LGPL-2.1
Files: debian/*
Copyright: 2018, Jonah Brüchert
@ -17,23 +16,6 @@ Copyright: 2006, 2007, xine project
2006, 2007, Diego Pettenò <flameeyes@gmail.com>
License: GPL-2+
Files: src/*
Copyright: 2013-2018, Jolla Ltd.
License: LGPL-2.1
Files: src/Makefile.am
Copyright: 2013, 2017, Jolla Ltd.
License: LGPL-2.1
Files: src/common/Makefile.am
src/common/libdroid-util.pc.in
Copyright: 2013, 2017, Jolla Ltd.
License: LGPL-2.1
Files: src/droid/Makefile.am
Copyright: 2013, 2017, Jolla Ltd.
License: LGPL-2.1
License: GPL-2+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

4
debian/preinst vendored Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
rm /etc/pulse/default.pa.gemian || true
rm /etc/pulse/arm_droid_card_custom.pa || true

View file

@ -4,3 +4,4 @@ usr/lib/pulse-*/modules/libdroid-util*.so
usr/lib/pulse-*/modules/module-droid-card*.so
usr/lib/pulse-*/modules/module-droid-sink*.so
usr/lib/pulse-*/modules/module-droid-source*.so
etc/*

3
debian/watch vendored
View file

@ -1,3 +0,0 @@
version=4
opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/pulseaudio-modules-droid-$1\.tar\.gz/ \
https://github.com/mer-hybris/pulseaudio-modules-droid/tags .*/v?(\d\S+)\.tar\.gz

View file

@ -0,0 +1 @@
load-module module-droid-card

142
etc/pulse/default.pa.gemian Normal file
View file

@ -0,0 +1,142 @@
#!/usr/bin/pulseaudio -nF
#
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PulseAudio is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
# This startup script is used only if PulseAudio is started per-user
# (i.e. not in system mode)
.nofail
.fail
load-module module-droid-keepalive
### Automatically augment property information from .desktop files
### stored in /usr/share/application
load-module module-augment-properties
load-module module-null-sink sink_name=sink.null rate=48000
load-module module-stream-restore
### Switch when connected by default (ubports)
load-module module-switch-on-connect ignore_virtual=yes
### Should be after module-*-restore but before module-*-detect (sfos)
load-module module-switch-on-port-available
### If droid-card needs other arguments than the default, have the new
### load-module line in /etc/pulse/arm_droid_card_custom.pa
.ifexists /etc/pulse/arm_droid_card_custom.pa
.include /etc/pulse/arm_droid_card_custom.pa
.else
load-module module-droid-card rate=48000
.endif
### Needed on many new devices. HADK guide explains how to implement this fully
.ifexists module-droid-glue.so
.nofail
load-module module-droid-glue
.fail
.endif
.ifexists module-droid-hidl-28.so
.nofail
load-module module-droid-hidl-28
.fail
.endif
load-module module-null-sink sink_name=sink.fake.sco rate=8000 channels=1
load-module module-null-source source_name=source.fake.sco rate=8000 channels=1
#load-module module-bluetooth-discover bluez4_args="sco_sink=sink.fake.sco sco_source=source.fake.sco" bluez5_args="headset=droid"
load-module module-bluetooth-discover
load-module module-bluetooth-policy
#load-module module-policy-enforcement
load-module module-role-ducking trigger_roles=alarm,notification,warning ducking_roles=x-maemo volume=-200dB
### Load several protocols
.ifexists module-esound-protocol-unix.so
load-module module-esound-protocol-unix
.endif
load-module module-native-protocol-unix
### Network access (may be configured with paprefs, so leave this commented
### here if you plan to use paprefs)
#load-module module-esound-protocol-tcp
#load-module module-native-protocol-tcp
#load-module module-zeroconf-publish
### Load the RTP receiver module (also configured via paprefs, see above)
#load-module module-rtp-recv
### Load the RTP sender module (also configured via paprefs, see above)
#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
#load-module module-rtp-send source=rtp.monitor
### Load additional modules from GSettings. This can be configured with the paprefs tool.
### Please keep in mind that the modules configured by paprefs might conflict with manually
### loaded modules.
.ifexists module-gsettings.so
.nofail
load-module module-gsettings
.fail
.endif
### Make sure we always have a sink around, even if it is a null sink.
load-module module-always-sink
### Honour intended role device property
load-module module-intended-roles
### Automatically suspend sinks/sources that become idle for too long
load-module module-suspend-on-idle timeout=1
### If autoexit on idle is enabled we want to make sure we only quit
### when no local session needs us anymore.
.ifexists module-console-kit.so
load-module module-console-kit
.endif
.ifexists module-systemd-login.so
load-module module-systemd-login
.endif
### Load DBus protocol
.ifexists module-dbus-protocol.so
load-module module-dbus-protocol
.endif
### Move orphan streams to placeholder sinks or sources so that playback doesn't get
### interrupted. Policy enforcement module then moves the streams to new appropriate
### sinks or sources.
#load-module module-rescue-streams sink_name=sink.null source_name=sink.null.monitor
### Enable positioned event sounds
load-module module-position-event-sounds
### Modules to allow auto-loading of filters (such as echo cancellation)
### on demand. module-filter-heuristics tries to determine what filters
### make sense, and module-filter-apply does the heavy-lifting of
### loading modules and rerouting streams.
load-module module-filter-heuristics
load-module module-filter-apply
### Make some devices default
set-default-sink sink.primary_output
set-default-source source.droid

View file

@ -1,75 +0,0 @@
%define device sbj
%define pulseversion %{expand:%(rpm -q --qf '[%%{version}]' pulseaudio)}
%define pulsemajorminor %{expand:%(echo '%{pulseversion}' | cut -d+ -f1)}
%define moduleversion %{pulsemajorminor}.%{expand:%(echo '%{version}' | cut -d. -f3)}
Name: pulseaudio-modules-droid-%{device}
Summary: PulseAudio Droid HAL modules
Version: %{pulsemajorminor}.92
Release: 1
License: LGPLv2+
URL: https://github.com/mer-hybris/pulseaudio-modules-droid
Source0: %{name}-%{version}.tar.bz2
Requires: pulseaudio >= %{pulseversion}
Requires: %{name}-common = %{version}-%{release}
Requires: pulseaudio-module-keepalive >= 1.0.0
BuildRequires: automake
BuildRequires: libtool
BuildRequires: libtool-ltdl-devel
BuildRequires: pkgconfig(pulsecore) >= %{pulsemajorminor}
BuildRequires: pkgconfig(android-headers)
BuildRequires: pkgconfig(libhardware)
Provides: pulseaudio-modules-droid
%description
PulseAudio Droid HAL modules.
%package common
Summary: Common libs for the PulseAudio droid modules
Requires: pulseaudio >= %{pulseversion}
%description common
This contains common libs for the PulseAudio droid modules.
%package devel
Summary: Development files for PulseAudio droid modules
Requires: %{name}-common = %{version}-%{release}
Requires: pulseaudio >= %{pulseversion}
%description devel
This contains development files for PulseAudio droid modules.
%prep
%setup -q -n %{name}-%{version}
%build
echo "%{moduleversion}" > .tarball-version
%reconfigure --disable-static --with-droid-device=%{device} --disable-xml
make %{?jobs:-j%jobs}
%install
rm -rf %{buildroot}
%make_install
%files
%defattr(-,root,root,-)
%{_libdir}/pulse-%{pulsemajorminor}/modules/libdroid-sink.so
%{_libdir}/pulse-%{pulsemajorminor}/modules/libdroid-source.so
%{_libdir}/pulse-%{pulsemajorminor}/modules/module-droid-sink.so
%{_libdir}/pulse-%{pulsemajorminor}/modules/module-droid-source.so
%{_libdir}/pulse-%{pulsemajorminor}/modules/module-droid-card.so
%license COPYING
%files common
%defattr(-,root,root,-)
%{_libdir}/pulse-%{pulsemajorminor}/modules/libdroid-util.so
%files devel
%defattr(-,root,root,-)
%dir %{_prefix}/include/pulsecore/modules/droid
%{_prefix}/include/pulsecore/modules/droid/version.h
%{_prefix}/include/pulsecore/modules/droid/conversion.h
%{_prefix}/include/pulsecore/modules/droid/droid-config.h
%{_prefix}/include/pulsecore/modules/droid/droid-util.h
%{_libdir}/pkgconfig/*.pc

View file

@ -5,7 +5,7 @@
Name: pulseaudio-modules-droid
Summary: PulseAudio Droid HAL modules
Version: %{pulsemajorminor}.92
Version: %{pulsemajorminor}.101
Release: 1
License: LGPLv2+
URL: https://github.com/mer-hybris/pulseaudio-modules-droid
@ -40,7 +40,7 @@ Requires: pulseaudio >= %{pulseversion}
This contains development files for PulseAudio droid modules.
%prep
%setup -q -n %{name}-%{version}
%autosetup -n %{name}-%{version}
%build
echo "%{moduleversion}" > .tarball-version
@ -51,10 +51,9 @@ else
. %{_libdir}/droid-devel/hw-release.vars
fi
%reconfigure --disable-static --with-droid-device=$MER_HA_DEVICE
make %{?jobs:-j%jobs}
%make_build
%install
rm -rf %{buildroot}
%make_install
%files
@ -72,9 +71,11 @@ rm -rf %{buildroot}
%files devel
%defattr(-,root,root,-)
%dir %{_prefix}/include/pulsecore/modules/droid
%{_prefix}/include/pulsecore/modules/droid/version.h
%{_prefix}/include/pulsecore/modules/droid/conversion.h
%{_prefix}/include/pulsecore/modules/droid/droid-config.h
%{_prefix}/include/pulsecore/modules/droid/droid-util.h
%dir %{_includedir}/pulsecore/modules/droid
%{_includedir}/pulsecore/modules/droid/conversion.h
%{_includedir}/pulsecore/modules/droid/droid-config.h
%{_includedir}/pulsecore/modules/droid/droid-util.h
%{_includedir}/pulsecore/modules/droid/sllist.h
%{_includedir}/pulsecore/modules/droid/utils.h
%{_includedir}/pulsecore/modules/droid/version.h
%{_libdir}/pkgconfig/*.pc

View file

@ -18,7 +18,9 @@ includedir = @includedir@/pulsecore/modules/droid
include_HEADERS = include/droid/version.h \
include/droid/conversion.h \
include/droid/droid-config.h \
include/droid/droid-util.h
include/droid/droid-util.h \
include/droid/sllist.h \
include/droid/utils.h
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libdroid-util.pc
@ -26,10 +28,11 @@ pkgconfig_DATA = libdroid-util.pc
libdroid_util_la_SOURCES = droid-util.c \
droid-config.c \
conversion.c \
config-parser-legacy.c \
config-parser-xml.c \
droid-util-audio.h \
droid-util-41qc.h
config-parser-xml.h \
sllist.c \
utils.c \
droid-util-audio.h
libdroid_util_la_LDFLAGS = -avoid-version -Wl,-z,noexecstack -lhybris-common $(SBJ_DEVICE_LDFLAGS)
libdroid_util_la_LIBADD = $(AM_LIBADD)
libdroid_util_la_CFLAGS = $(AM_CFLAGS)

View file

@ -1,436 +0,0 @@
/*
* Copyright (C) 2018 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
* These PulseAudio Modules are 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
* version 2.1 of the License.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdbool.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulse/xmalloc.h>
#include <hardware_legacy/audio_policy_conf.h>
#include "droid/version.h"
#include "droid/droid-config.h"
#include "droid/conversion.h"
#include "droid/sllist.h"
/* Section defining custom global configuration variables. */
#define GLOBAL_CONFIG_EXT_TAG "custom_properties"
#define GAIN_TAG_PREFIX "gain_"
#define MAX_LINE_LENGTH (1024)
#define WHITESPACE "\n\r \t"
static void log_parse_error(const char *fn, const unsigned ln, const char *section, const char *v) {
pa_log("[%s:%u] failed to parse line in section %s: unknown section (%s)", fn, ln, section, v);
}
pa_droid_config_audio *pa_parse_droid_audio_config_legacy(const char *filename) {
pa_droid_config_audio *config = NULL;
FILE *f;
unsigned n = 0;
bool ret = true;
char *full_line = NULL;
uint32_t hw_module_count = 0;
enum config_loc {
IN_ROOT = 0,
IN_GLOBAL = 1,
IN_GLOBAL_EXT = 2,
IN_HW_MODULES = 3,
IN_MODULE = 4,
IN_OUTPUT_INPUT = 5,
IN_CONFIG = 6,
IN_MODULE_GLOBAL = 10,
IN_DEVICES = 20,
IN_DEVICES_DEVICE = 21,
IN_GAINS = 22,
IN_GAIN_N = 23
} loc = IN_ROOT;
bool in_output = true;
pa_droid_config_hw_module *module = NULL;
pa_droid_config_device *output = NULL;
pa_droid_config_device *input = NULL;
pa_assert(filename);
f = fopen(filename, "r");
if (!f) {
pa_log_info("Failed to open config file (%s): %s", filename, pa_cstrerror(errno));
ret = false;
goto finish;
}
config = pa_xnew0(pa_droid_config_audio, 1);
config->global_config = pa_xnew0(pa_droid_config_global, 1);
pa_lock_fd(fileno(f), 1);
full_line = pa_xmalloc0(sizeof(char) * MAX_LINE_LENGTH);
while (!feof(f)) {
char *ln, *d, *v, *value;
if (!fgets(full_line, MAX_LINE_LENGTH, f))
break;
n++;
pa_strip_nl(full_line);
if (!*full_line)
continue;
ln = full_line + strspn(full_line, WHITESPACE);
if (ln[0] == '#')
continue;
v = ln;
d = v + strcspn(v, WHITESPACE);
value = d + strspn(d, WHITESPACE);
d[0] = '\0';
d = value + strcspn(value, WHITESPACE);
d[0] = '\0';
/* Enter section */
if (pa_streq(value, "{")) {
if (!*v) {
pa_log("[%s:%u] failed to parse line - too few words", filename, n);
goto finish;
}
switch (loc) {
case IN_ROOT:
if (pa_streq(v, GLOBAL_CONFIG_TAG)) {
loc = IN_GLOBAL;
}
else if (pa_streq(v, AUDIO_HW_MODULE_TAG))
loc = IN_HW_MODULES;
else {
log_parse_error(filename, n, "<root>", v);
ret = false;
goto finish;
}
break;
case IN_GLOBAL:
if (pa_streq(v, GLOBAL_CONFIG_EXT_TAG))
loc = IN_GLOBAL_EXT;
else {
log_parse_error(filename, n, GLOBAL_CONFIG_TAG, v);
ret = false;
goto finish;
}
break;
case IN_HW_MODULES:
pa_assert(!module);
module = pa_droid_config_hw_module_new(config, v);
SLLIST_APPEND(pa_droid_config_hw_module, config->hw_modules, module);
hw_module_count++;
loc = IN_MODULE;
pa_log_debug("config: New module: %s", module->name);
break;
case IN_MODULE:
pa_assert(module);
if (pa_streq(v, OUTPUTS_TAG)) {
loc = IN_OUTPUT_INPUT;
in_output = true;
} else if (pa_streq(v, INPUTS_TAG)) {
loc = IN_OUTPUT_INPUT;
in_output = false;
} else if (pa_streq(v, GLOBAL_CONFIG_TAG)) {
loc = IN_MODULE_GLOBAL;
} else if (pa_streq(v, DEVICES_TAG)) {
loc = IN_DEVICES;
} else {
log_parse_error(filename, n, module->name, v);
ret = false;
goto finish;
}
break;
case IN_OUTPUT_INPUT:
pa_assert(module);
if (in_output) {
output = pa_droid_config_device_new(module, PA_DIRECTION_OUTPUT, v);
SLLIST_APPEND(pa_droid_config_device, module->outputs, output);
loc = IN_CONFIG;
pa_log_debug("config: %s: New output: %s", module->name, output->name);
} else {
input = pa_droid_config_device_new(module, PA_DIRECTION_INPUT, v);
SLLIST_APPEND(pa_droid_config_device, module->inputs, input);
loc = IN_CONFIG;
pa_log_debug("config: %s: New input: %s", module->name, input->name);
}
break;
case IN_DEVICES:
/* TODO Missing implementation of parsing the module/devices section.
* As of now there is no need for the information, fix this when that
* changes. */
loc = IN_DEVICES_DEVICE;
break;
case IN_DEVICES_DEVICE:
if (pa_streq(v, GAINS_TAG))
loc = IN_GAINS;
else {
log_parse_error(filename, n, DEVICES_TAG, v);
ret = false;
goto finish;
}
break;
case IN_GAINS:
/* TODO Missing implementation of parsing the gain_n section.
* As of now there is no need for the information, fix this when that
* changes. */
if (pa_startswith(v, GAIN_TAG_PREFIX))
loc = IN_GAIN_N;
else {
log_parse_error(filename, n, GAINS_TAG, v);
ret = false;
goto finish;
}
break;
case IN_CONFIG:
if (pa_streq(v, GAINS_TAG)) {
loc = IN_GAINS;
} else {
log_parse_error(filename, n, in_output ? output->name : input->name, v);
ret = false;
goto finish;
}
break;
default:
pa_log("[%s:%u] failed to parse line: unknown section (%s)", filename, n, v);
ret = false;
goto finish;
}
continue;
}
/* Exit section */
if (pa_streq(v, "}")) {
switch (loc) {
case IN_ROOT:
pa_log("[%s:%u] failed to parse line - extra closing bracket", filename, n);
ret = false;
goto finish;
case IN_HW_MODULES:
/* fall through */
case IN_GLOBAL:
loc = IN_ROOT;
break;
case IN_MODULE:
module = NULL;
loc = IN_HW_MODULES;
break;
case IN_DEVICES:
/* fall through */
case IN_MODULE_GLOBAL:
loc = IN_MODULE;
break;
case IN_GAINS:
if (output || input)
loc = IN_CONFIG;
else
loc = IN_DEVICES_DEVICE;
break;
case IN_OUTPUT_INPUT:
if (in_output)
output = NULL;
else
input = NULL;
/* fall through */
case IN_GAIN_N:
/* fall through */
case IN_DEVICES_DEVICE:
/* fall through */
case IN_CONFIG:
/* fall through */
case IN_GLOBAL_EXT:
loc--;
break;
}
continue;
}
/* Parsing of values */
if (loc == IN_GLOBAL ||
loc == IN_GLOBAL_EXT ||
loc == IN_MODULE_GLOBAL ||
loc == IN_CONFIG ||
loc == IN_DEVICES_DEVICE ||
loc == IN_GAIN_N) {
bool success = false;
if (loc == IN_GLOBAL || loc == IN_MODULE_GLOBAL) {
pa_droid_config_global *global_config = NULL;
if (loc == IN_MODULE_GLOBAL) {
pa_assert(module);
if (!module->global_config)
module->global_config = pa_xnew0(pa_droid_config_global, 1);
global_config = module->global_config;
} else
global_config = config->global_config;
pa_assert(global_config);
/* Parse global configuration */
if (pa_streq(v, ATTACHED_OUTPUT_DEVICES_TAG))
success = pa_conversion_parse_output_devices(filename, n, value, true, true,
&global_config->attached_output_devices);
else if (pa_streq(v, DEFAULT_OUTPUT_DEVICE_TAG))
success = pa_conversion_parse_output_devices(filename, n, value, true, true,
&global_config->default_output_device);
else if (pa_streq(v, ATTACHED_INPUT_DEVICES_TAG))
success = pa_conversion_parse_input_devices(filename, n, value, true, false,
&global_config->attached_input_devices);
else if (pa_streq(v, AUDIO_HAL_VERSION_TAG))
success = pa_conversion_parse_version(filename, n, value,
&global_config->audio_hal_version);
#ifdef SPEAKER_DRC_ENABLED_TAG
// SPEAKER_DRC_ENABLED_TAG is only from Android v4.4
else if (pa_streq(v, SPEAKER_DRC_ENABLED_TAG))
/* TODO - Add support for dynamic range control */
success = true; /* Do not fail while parsing speaker_drc_enabled entry */
#endif
else {
pa_log("[%s:%u] failed to parse line - unknown config entry %s", filename, n, v);
success = false;
}
} else if (loc == IN_GLOBAL_EXT) {
/* Parse custom global configuration
* For now just log all custom variables, don't do
* anything with the values.
* TODO: Store custom values somehow */
pa_log_debug("[%s:%u] TODO custom variable: %s = %s", filename, n, v, value);
success = true;
} else if (loc == IN_CONFIG) {
/* Parse per-output or per-input configuration */
if ((in_output && !output) || (!in_output && !input)) {
pa_log("[%s:%u] failed to parse line", filename, n);
ret = false;
goto finish;
}
if (pa_streq(v, SAMPLING_RATES_TAG))
success = pa_conversion_parse_sampling_rates(filename, n, value, true,
in_output ? output->sampling_rates : input->sampling_rates);
else if (pa_streq(v, FORMATS_TAG))
success = pa_conversion_parse_formats(filename, n, value, true,
in_output ? &output->formats : &input->formats);
else if (pa_streq(v, CHANNELS_TAG)) {
if (in_output)
success = pa_conversion_parse_output_channels(filename, n, value, true, &output->channel_masks);
else
success = pa_conversion_parse_input_channels(filename, n, value, true, &input->channel_masks);
} else if (pa_streq(v, DEVICES_TAG)) {
if (in_output)
success = pa_conversion_parse_output_devices(filename, n, value, true, false, &output->devices);
else
success = pa_conversion_parse_input_devices(filename, n, value, true, false, &input->devices);
} else if (pa_streq(v, FLAGS_TAG)) {
if (in_output)
success = pa_conversion_parse_output_flags(filename, n, value, &output->flags);
else {
#if AUDIO_API_VERSION_MAJ >= 3
success = pa_conversion_parse_input_flags(filename, n, value, &input->flags);
#else
pa_log("[%s:%u] failed to parse line - output flags inside input definition", filename, n);
success = false;
#endif
}
} else {
pa_log("[%s:%u] failed to parse line - unknown config entry %s", filename, n, v);
success = false;
}
} else if (loc == IN_DEVICES_DEVICE) {
/* TODO Missing implementation of parsing the module/devices section.
* As of now there is no need for the information, fix this when that
* changes. */
success = true;
} else if (loc == IN_GAIN_N) {
/* TODO Missing implementation of parsing the gain_n section.
* As of now there is no need for the information, fix this when that
* changes. */
success = true;
} else
pa_assert_not_reached();
if (!success) {
ret = false;
goto finish;
}
}
}
pa_log_info("Parsed config file (%s): %u modules.", filename, hw_module_count);
finish:
if (f) {
pa_lock_fd(fileno(f), 0);
fclose(f);
}
pa_xfree(full_line);
if (!ret)
pa_droid_config_free(config), config = NULL;
return config;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -26,15 +26,6 @@
#include "droid/droid-config.h"
#ifndef HAVE_EXPAT
#include <unistd.h>
pa_droid_config_audio *pa_parse_droid_audio_config_xml(const char *filename) {
if (access(filename, F_OK) == 0)
pa_log_warn("Could not parse %s, xml configuration parsing support not compiled in", filename);
return NULL;
}
#else
#include <stdarg.h>
#include <string.h>
#include <expat.h>
@ -46,6 +37,9 @@ pa_droid_config_audio *pa_parse_droid_audio_config_xml(const char *filename) {
#include "droid/conversion.h"
#include "droid/sllist.h"
#include "droid/utils.h"
#include "droid/droid-config.h"
#include "config-parser-xml.h"
#ifdef XML_UNICODE_WCHAR_T
# include <wchar.h>
@ -86,6 +80,9 @@ pa_droid_config_audio *pa_parse_droid_audio_config_xml(const char *filename) {
#define ATTRIBUTE_sources "sources"
#define ATTRIBUTE_type "type"
#define ATTRIBUTE_href "href"
#define ATTRIBUTE_maxOpenCount "maxOpenCount"
#define ATTRIBUTE_maxActiveCount "maxActiveCount"
#define ATTRIBUTE_address "address"
#define PORT_TYPE_sink "sink"
#define PORT_TYPE_source "source"
@ -218,6 +215,12 @@ static const struct element_parser element_parse_attached_devices = {
&element_parse_item
};
/* Entries like
* <modules>
* <module name="primary"> <xi:include href="other.xml"/> </module>
* </modules>
* Where other.xml contains module elements
*/
static const struct element_parser element_parse_module_include = {
ELEMENT_include,
parse_module_include,
@ -226,11 +229,25 @@ static const struct element_parser element_parse_module_include = {
NULL
};
/* Entries like
* <modules>
* <xi:include href="other.xml"/>
* </modules>
* Where other.xml contains <module name="primary">...
*/
static const struct element_parser element_parse_modules_include = {
ELEMENT_include,
parse_module_include,
NULL,
NULL,
NULL
};
static const struct element_parser element_parse_module = {
ELEMENT_module,
parse_module,
NULL,
NULL,
&element_parse_modules_include,
&element_parse_module_include
};
@ -282,7 +299,7 @@ struct profile {
char *name;
audio_format_t format;
uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES];
audio_channel_mask_t channel_masks;
audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS];
struct profile *next;
};
@ -290,6 +307,8 @@ struct mix_port {
char *name;
char *role;
uint32_t flags;
int max_open_count;
int max_active_count;
struct profile *profiles;
struct mix_port *next;
};
@ -298,6 +317,7 @@ struct device_port {
char *tag_name;
audio_devices_t type;
char *role;
char *address;
struct profile *profiles;
struct device_port *next;
};
@ -403,6 +423,7 @@ static void device_port_list_free(struct device_port *list) {
profile_list_free(p->profiles);
pa_xfree(p->tag_name);
pa_xfree(p->role);
pa_xfree(p->address);
pa_xfree(p);
}
}
@ -697,6 +718,8 @@ static bool parse_mix_port(struct parser_data *data, const char *element_name, c
struct mix_port *p;
bool parsed = false;
char *flags = NULL;
char *max_open_count = NULL;
char *max_active_count = NULL;
p = pa_xmalloc0(sizeof(*p));
@ -714,6 +737,14 @@ static bool parse_mix_port(struct parser_data *data, const char *element_name, c
goto done;
}
/* maxOpenCount is not mandatory element attribute */
if (get_element_attr(data, attributes, false, ATTRIBUTE_maxOpenCount, &max_open_count))
pa_atoi(max_open_count, &p->max_open_count);
/* maxActiveCount is not mandatory element attribute */
if (get_element_attr(data, attributes, false, ATTRIBUTE_maxActiveCount, &max_active_count))
pa_atoi(max_active_count, &p->max_active_count);
parsed = true;
done:
pa_xfree(flags);
@ -729,20 +760,9 @@ done:
return parsed;
}
static void replace_in_place(char **string, const char *a, const char *b) {
char *tmp;
pa_assert(*string);
pa_assert(a);
pa_assert(b);
tmp = pa_replace(*string, a, b);
pa_xfree(*string);
*string = tmp;
}
static bool parse_profile(struct parser_data *data, const char *element_name, const XML_Char **attributes) {
struct profile *p;
int channel_count = -1;
bool parsed = false, unknown_format = false, output = true;
char *samplingRates = NULL, *channelMasks = NULL, *format = NULL;
@ -775,24 +795,24 @@ static bool parse_profile(struct parser_data *data, const char *element_name, co
if (output && pa_startswith(channelMasks, "AUDIO_CHANNEL_IN_")) {
pa_log_info("[%s:%u] Output has wrong direction channel mask (%s), reversing.",
data->fn, data->lineno, channelMasks);
replace_in_place(&channelMasks, "AUDIO_CHANNEL_IN_", "AUDIO_CHANNEL_OUT_");
dm_replace_in_place(&channelMasks, "AUDIO_CHANNEL_IN_", "AUDIO_CHANNEL_OUT_");
}
else if (!output && pa_startswith(channelMasks, "AUDIO_CHANNEL_OUT_")) {
pa_log_info("[%s:%u] Input has wrong direction channel mask (%s), reversing.",
data->fn, data->lineno, channelMasks);
replace_in_place(&channelMasks, "AUDIO_CHANNEL_OUT_", "AUDIO_CHANNEL_IN_");
dm_replace_in_place(&channelMasks, "AUDIO_CHANNEL_OUT_", "AUDIO_CHANNEL_IN_");
}
}
if (!pa_conversion_parse_sampling_rates(data->fn, data->lineno, samplingRates, false, p->sampling_rates))
if (!pa_conversion_parse_sampling_rates(data->fn, data->lineno, samplingRates, p->sampling_rates))
goto done;
if (!pa_conversion_parse_formats(data->fn, data->lineno, format, false, &p->format))
if (!pa_conversion_parse_formats(data->fn, data->lineno, format, &p->format))
unknown_format = true;
if (!unknown_format && channelMasks && !(output ?
pa_conversion_parse_output_channels(data->fn, data->lineno, channelMasks, false, &p->channel_masks)
: pa_conversion_parse_input_channels(data->fn, data->lineno, channelMasks, false, &p->channel_masks)))
if (!unknown_format && channelMasks && (channel_count = output ?
pa_conversion_parse_output_channels(data->fn, data->lineno, channelMasks, p->channel_masks)
: pa_conversion_parse_input_channels(data->fn, data->lineno, channelMasks, p->channel_masks)) == -1)
goto done;
parsed = true;
@ -801,7 +821,10 @@ done:
pa_xfree(channelMasks);
pa_xfree(format);
if (!parsed) {
if (channel_count == 0) {
pa_log_info("[%s:%u] Ignore profile with no supported channels.", data->fn, data->lineno);
profile_list_free(p);
} else if (!parsed) {
pa_log_error("[%s:%u] Failed to parse element <" ELEMENT_profile ">", data->fn, data->lineno);
profile_list_free(p);
} else if (unknown_format) {
@ -836,10 +859,13 @@ static bool parse_device_port(struct parser_data *data, const char *element_name
goto done;
if (!(pa_streq(d->role, ATTRIBUTE_sink) ?
pa_conversion_parse_output_devices(data->fn, data->lineno, type, false, false, &d->type)
: pa_conversion_parse_input_devices(data->fn, data->lineno, type, false, false, &d->type)))
pa_conversion_parse_output_devices(data->fn, data->lineno, type, false, &d->type)
: pa_conversion_parse_input_devices(data->fn, data->lineno, type, false, &d->type)))
unknown_device = true;
/* address is not mandatory element attribute */
get_element_attr(data, attributes, false, ATTRIBUTE_address, &d->address);
parsed = true;
done:
pa_xfree(type);
@ -949,133 +975,205 @@ done:
return ret;
}
static struct device_port *find_device_port(struct module *module, const char *name) {
struct device_port *port;
SLLIST_FOREACH(port, module->device_ports) {
if (pa_streq(port->tag_name, name))
return port;
}
return NULL;
}
static bool device_in_list(struct device *list, const char *name) {
struct device *dev;
pa_assert(name);
SLLIST_FOREACH(dev, list) {
if (pa_streq(name, dev->name))
return true;
}
return false;
}
static void add_output(struct module *module, struct mix_port *mix_port, pa_droid_config_hw_module *hw_module) {
pa_droid_config_device *output;
static void generate_config_profiles(struct profile *profiles, dm_list *list) {
struct profile *profile;
struct route *route;
struct device_port *device_port;
output = pa_droid_config_device_new(hw_module, PA_DIRECTION_OUTPUT, mix_port->name);
output->flags = mix_port->flags;
SLLIST_FOREACH(profile, mix_port->profiles) {
memcpy(output->sampling_rates, profile->sampling_rates, sizeof(output->sampling_rates));
output->channel_masks |= profile->channel_masks;
output->formats |= profile->format;
SLLIST_FOREACH(profile, profiles) {
dm_config_profile *c_profile = pa_xnew0(dm_config_profile, 1);
c_profile->name = pa_xstrdup(profile->name ? profile->name : "");
c_profile->format = profile->format;
memcpy(c_profile->sampling_rates,
profile->sampling_rates,
sizeof(c_profile->sampling_rates));
memcpy(c_profile->channel_masks,
profile->channel_masks,
sizeof(c_profile->channel_masks));
dm_list_push_back(list, c_profile);
}
SLLIST_FOREACH(route, module->routes) {
struct device *source;
SLLIST_FOREACH(source, route->sources) {
if (pa_streq(source->name, mix_port->name)) {
if ((device_port = find_device_port(module, route->sink))) {
output->devices |= device_port->type;
if (device_in_list(module->attached_devices, device_port->tag_name))
hw_module->global_config->attached_output_devices |= device_port->type;
if (device_in_list(module->default_output, device_port->tag_name))
hw_module->global_config->default_output_device |= device_port->type;
break;
} else
pa_log_info("Couldn't find matching <" ELEMENT_devicePort " tagName=%s> for <" ELEMENT_mixPort " name=%s>",
route->sink, source->name);
}
}
}
pa_log_debug("config: %s: New output: %s", hw_module->name, output->name);
SLLIST_APPEND(pa_droid_config_device, hw_module->outputs, output);
}
static void add_input(struct module *module, struct mix_port *mix_port, pa_droid_config_hw_module *hw_module) {
pa_droid_config_device *input;
struct profile *profile;
struct route *route;
struct device_port *device_port;
static dm_config_port *config_device_port_new(dm_config_module *module,
struct device_port *device_port) {
dm_config_port *c_device_port = pa_xnew0(dm_config_port, 1);
input = pa_droid_config_device_new(hw_module, PA_DIRECTION_INPUT, mix_port->name);
input->flags = mix_port->flags;
SLLIST_FOREACH(profile, mix_port->profiles) {
memcpy(input->sampling_rates, profile->sampling_rates, sizeof(input->sampling_rates));
input->channel_masks |= profile->channel_masks;
input->formats |= profile->format;
}
c_device_port->module = module;
c_device_port->port_type = DM_CONFIG_TYPE_DEVICE_PORT;
c_device_port->name = pa_xstrdup(device_port->tag_name);
c_device_port->type = device_port->type;
c_device_port->role = pa_safe_streq(device_port->role, "sink") ? DM_CONFIG_ROLE_SINK : DM_CONFIG_ROLE_SOURCE;
c_device_port->address = pa_xstrdup(device_port->address ? device_port->address : "");
c_device_port->profiles = dm_list_new();
if (device_port->profiles->next)
pa_log("More than 1 profile for devicePort %s, ignoring extra profiles.", device_port->tag_name);
generate_config_profiles(device_port->profiles, c_device_port->profiles);
SLLIST_FOREACH(route, module->routes) {
if (pa_streq(route->sink, mix_port->name)) {
struct device *source;
SLLIST_FOREACH(source, route->sources) {
if ((device_port = find_device_port(module, source->name))) {
input->devices |= device_port->type;
if (device_in_list(module->attached_devices, device_port->tag_name))
hw_module->global_config->attached_input_devices |= device_port->type;
} else
pa_log_info("Couldn't find matching <" ELEMENT_mixPort " name=%s> for <" ELEMENT_devicePort " tagName=%s>",
source->name, route->sink);
}
}
}
pa_log_debug("config: %s: New input: %s", hw_module->name, input->name);
SLLIST_APPEND(pa_droid_config_device, hw_module->inputs, input);
return c_device_port;
}
static void generate_config_for_module(struct module *module, pa_droid_config_audio *config) {
pa_droid_config_hw_module *hw_module;
static dm_config_port *config_mix_port_new(dm_config_module *module,
struct mix_port *mix_port) {
dm_config_port *c_mix_port = pa_xnew0(dm_config_port, 1);
c_mix_port->module = module;
c_mix_port->port_type = DM_CONFIG_TYPE_MIX_PORT;
c_mix_port->name = pa_xstrdup(mix_port->name);
c_mix_port->role = pa_safe_streq(mix_port->role, "sink") ? DM_CONFIG_ROLE_SINK : DM_CONFIG_ROLE_SOURCE;
c_mix_port->flags = mix_port->flags;
c_mix_port->max_open_count = mix_port->max_open_count;
c_mix_port->max_active_count = mix_port->max_active_count;
c_mix_port->profiles = dm_list_new();
generate_config_profiles(mix_port->profiles, c_mix_port->profiles);
return c_mix_port;
}
/* If a devicePort doesn't have any profiles defined let's just make something
* up that could work. */
static struct profile *default_profile(const char *role) {
struct profile *p;
bool output;
output = pa_safe_streq(role, PORT_TYPE_sink);
p = pa_xmalloc0(sizeof(*p));
p->name = pa_sprintf_malloc("generated-default");
pa_assert(pa_string_convert_str_to_num(CONV_STRING_FORMAT, "AUDIO_FORMAT_PCM_16_BIT", &p->format));
p->sampling_rates[0] = 48000;
pa_assert(pa_string_convert_str_to_num(output ? CONV_STRING_OUTPUT_CHANNELS : CONV_STRING_INPUT_CHANNELS,
output ? "AUDIO_CHANNEL_OUT_STEREO" : "AUDIO_CHANNEL_IN_STEREO",
&p->channel_masks[0]));
p->next = NULL;
return p;
}
static void generate_config_for_module(struct module *module, dm_config_device *config) {
dm_config_module *c_module;
struct mix_port *mix_port;
struct device_port *device_port;
struct device *device;
struct route *route;
pa_assert(module);
pa_assert(config);
pa_assert(config->global_config);
hw_module = pa_droid_config_hw_module_new(config, module->name);
if (module->attached_devices || module->default_output)
hw_module->global_config = pa_xnew0(pa_droid_config_global, 1);
SLLIST_APPEND(pa_droid_config_hw_module, config->hw_modules, hw_module);
c_module = pa_xnew0(dm_config_module, 1);
c_module->config = config;
c_module->name = pa_xstrdup(module->name);
c_module->version_major = 0; /* Not used */
c_module->version_minor = 0; /* Not used */
c_module->attached_devices = dm_list_new();
c_module->mix_ports = dm_list_new();
c_module->device_ports = dm_list_new();
c_module->ports = dm_list_new();
c_module->routes = dm_list_new();
/* Device ports */
SLLIST_FOREACH(device_port, module->device_ports) {
dm_config_port *c_device_port;
if (!device_port->profiles) {
pa_log_info("No profile defined for devicePort %s, generating default.", device_port->tag_name);
SLLIST_APPEND(struct profile, device_port->profiles, default_profile(device_port->role));
}
c_device_port = config_device_port_new(c_module, device_port);
dm_list_push_back(c_module->ports, c_device_port);
dm_list_push_back(c_module->device_ports, c_device_port);
}
/* Attached devices */
SLLIST_FOREACH(device, module->attached_devices) {
dm_config_port *c_device_port;
void *state;
DM_LIST_FOREACH_DATA(c_device_port, c_module->device_ports, state) {
if (pa_safe_streq(c_device_port->name, device->name)) {
dm_list_push_back(c_module->attached_devices, c_device_port);
break;
}
}
}
/* Default output device */
if (module->default_output) {
dm_config_port *c_device_port;
void *state;
DM_LIST_FOREACH_DATA(c_device_port, c_module->device_ports, state) {
if (pa_safe_streq(c_device_port->name, module->default_output->name)) {
c_module->default_output_device = c_device_port;
break;
}
}
}
/* Mix ports */
SLLIST_FOREACH(mix_port, module->mix_ports) {
if (pa_streq(mix_port->role, PORT_TYPE_source))
add_output(module, mix_port, hw_module);
else if (pa_streq(mix_port->role, ATTRIBUTE_sink))
add_input(module, mix_port, hw_module);
else
pa_log_warn("Unknown <" ELEMENT_mixPort "> role \"%s\"", mix_port->role);
dm_config_port *c_mix_port = config_mix_port_new(c_module, mix_port);
dm_list_push_back(c_module->ports, c_mix_port);
dm_list_push_back(c_module->mix_ports, c_mix_port);
}
/* Routes */
SLLIST_FOREACH(route, module->routes) {
dm_config_route *c_route = pa_xnew0(dm_config_route, 1);
dm_config_port *c_port;
void *state;
c_route->sources = dm_list_new();
if (!pa_safe_streq(route->type, "mix"))
pa_log("Unknown route type %s.", route->type);
c_route->type = DM_CONFIG_TYPE_MIX;
DM_LIST_FOREACH_DATA(c_port, c_module->ports, state) {
if (pa_safe_streq(route->sink, c_port->name)) {
c_route->sink = c_port;
break;
}
}
SLLIST_FOREACH(device, route->sources) {
DM_LIST_FOREACH_DATA(c_port, c_module->ports, state) {
if (pa_safe_streq(device->name, c_port->name)) {
dm_list_push_back(c_route->sources, c_port);
break;
}
}
}
dm_list_push_back(c_module->routes, c_route);
}
dm_list_push_back(config->modules, c_module);
}
static pa_droid_config_audio *convert_config(struct audio_policy_configuration *source) {
pa_droid_config_audio *config = NULL;
static dm_config_device *process_config(struct audio_policy_configuration *source) {
dm_config_device *config = NULL;
struct global_configuration *global_config;
struct module *module;
pa_assert(source);
config = pa_xnew0(pa_droid_config_audio, 1);
config->global_config = pa_xnew0(pa_droid_config_global, 1);
config = pa_xnew0(dm_config_device, 1);
config->global_config = dm_list_new();
config->modules = dm_list_new();
pa_log_debug("Process configuration ...");
SLLIST_FOREACH(global_config, source->global) {
dm_config_global *c_global = pa_xnew0(dm_config_global, 1);
c_global->key = pa_xstrdup(global_config->key);
c_global->value = pa_xstrdup(global_config->value);
dm_list_push_back(config->global_config, c_global);
};
pa_log_debug("Convert configuration ...");
SLLIST_FOREACH(module, source->modules)
generate_config_for_module(module, config);
@ -1107,8 +1205,8 @@ static char *build_path(const char *base_file, const char *filename) {
return fn;
}
pa_droid_config_audio *pa_parse_droid_audio_config_xml(const char *filename) {
pa_droid_config_audio *config = NULL;
dm_config_device *pa_parse_droid_audio_config_xml(const char *filename) {
dm_config_device *config = NULL;
struct parser_data data;
bool ret = true;
@ -1124,8 +1222,8 @@ pa_droid_config_audio *pa_parse_droid_audio_config_xml(const char *filename) {
SLLIST_FOREACH(data.current_include, data.conf->includes) {
char *fn = NULL;
if (!data.current_include->module)
continue;
// if (!data.current_include->module)
// continue;
if (data.current_include->href[0] != '/')
fn = build_path(filename, data.current_include->href);
@ -1140,7 +1238,7 @@ pa_droid_config_audio *pa_parse_droid_audio_config_xml(const char *filename) {
}
}
config = convert_config(data.conf);
config = process_config(data.conf);
done:
if (data.conf)
@ -1148,5 +1246,3 @@ done:
return config;
}
#endif

View file

@ -0,0 +1,33 @@
#ifndef foodroidconfigparserxmlfoo
#define foodroidconfigparserxmlfoo
/*
* Copyright (C) 2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
* These PulseAudio Modules are 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
* version 2.1 of the License.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <droid/droid-config.h>
dm_config_device *pa_parse_droid_audio_config_xml(const char *filename);
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2018 Jolla Ltd.
* Copyright (C) 2013-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -55,7 +55,7 @@ CONVERT_FUNC(format);
CONVERT_FUNC(output_channel);
CONVERT_FUNC(input_channel);
#define value_separator(legacy) (legacy ? "|" : ",")
#define VALUE_SEPARATOR ","
static bool string_convert_num_to_str(const struct string_conversion *list, const uint32_t value, const char **to_str) {
pa_assert(list);
@ -88,18 +88,7 @@ static char *list_string(struct string_conversion *list, uint32_t flags) {
char *str = NULL;
char *tmp;
#if AUDIO_API_VERSION_MAJ >= 2
if (flags & AUDIO_DEVICE_BIT_IN)
flags &= ~AUDIO_DEVICE_BIT_IN;
#endif
for (unsigned int i = 0; list[i].str; i++) {
#if AUDIO_API_VERSION_MAJ >= 2
if (list[i].value & AUDIO_DEVICE_BIT_IN) {
if (popcount(list[i].value & ~AUDIO_DEVICE_BIT_IN) != 1)
continue;
} else
#endif
if (popcount(list[i].value) != 1)
continue;
@ -140,6 +129,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();
@ -155,10 +178,6 @@ bool pa_string_convert_output_device_str_to_num(const char *str, audio_devices_t
return string_convert_str_to_num(string_conversion_table_output_device, str, (uint32_t*) to_value);
}
char *pa_list_string_output_device(audio_devices_t devices) {
return list_string(string_conversion_table_output_device, devices);
}
/* Input device */
bool pa_string_convert_input_device_num_to_str(audio_devices_t value, const char **to_str) {
return string_convert_num_to_str(string_conversion_table_input_device, (uint32_t) value, to_str);
@ -168,10 +187,6 @@ bool pa_string_convert_input_device_str_to_num(const char *str, audio_devices_t
return string_convert_str_to_num(string_conversion_table_input_device, str, (uint32_t*) to_value);
}
char *pa_list_string_input_device(audio_devices_t devices) {
return list_string(string_conversion_table_input_device, devices);
}
/* Flags */
bool pa_string_convert_flag_num_to_str(audio_output_flags_t value, const char **to_str) {
return string_convert_num_to_str(string_conversion_table_output_flag, (uint32_t) value, to_str);
@ -187,13 +202,9 @@ char *pa_list_string_flags(audio_output_flags_t flags) {
bool pa_input_device_default_audio_source(audio_devices_t input_device, audio_source_t *default_source)
{
#if AUDIO_API_VERSION_MAJ >= 2
input_device &= ~AUDIO_DEVICE_BIT_IN;
#endif
/* Note converting HAL values to different HAL values! */
for (unsigned int i = 0; i < sizeof(conversion_table_default_audio_source) / (sizeof(uint32_t) * 2); i++) {
if (conversion_table_default_audio_source[i][0] & input_device) {
if (conversion_table_default_audio_source[i][0] == input_device) {
*default_source = conversion_table_default_audio_source[i][1];
return true;
}
@ -210,10 +221,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 +287,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();
@ -287,7 +298,7 @@ int pa_conversion_parse_list(pa_conversion_string_t type, const char *separator,
}
bool pa_conversion_parse_sampling_rates(const char *fn, const unsigned ln,
const char *str, bool legacy,
const char *str,
uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]) {
pa_assert(fn);
pa_assert(str);
@ -296,16 +307,14 @@ bool pa_conversion_parse_sampling_rates(const char *fn, const unsigned ln,
const char *state = NULL;
uint32_t pos = 0;
while ((entry = pa_split(str, value_separator(legacy), &state))) {
while ((entry = pa_split(str, VALUE_SEPARATOR, &state))) {
int32_t val;
#if AUDIO_API_VERSION_MAJ >= 3
if (pos == 0 && pa_streq(entry, "dynamic")) {
sampling_rates[pos++] = (uint32_t) -1;
pa_xfree(entry);
break;
}
#endif
if (pos == AUDIO_MAX_SAMPLING_RATES) {
pa_log("[%s:%u] Too many sample rate entries (> %d)", fn, ln, AUDIO_MAX_SAMPLING_RATES);
@ -355,7 +364,7 @@ static bool check_and_log(const char *fn, const unsigned ln, const char *field,
}
bool pa_conversion_parse_formats(const char *fn, const unsigned ln,
const char *str, bool legacy,
const char *str,
audio_format_t *formats) {
int count;
char *unknown = NULL;
@ -364,18 +373,13 @@ bool pa_conversion_parse_formats(const char *fn, const unsigned ln,
pa_assert(str);
pa_assert(formats);
#if AUDIO_API_VERSION_MAJ >= 3
/* Needs to be probed later */
if (pa_streq(str, "dynamic")) {
*formats = 0;
return true;
}
#endif
count = pa_conversion_parse_list(CONV_STRING_FORMAT, value_separator(legacy), str, formats, &unknown);
if (legacy)
return check_and_log(fn, ln, "formats", count, str, unknown, false);
count = pa_conversion_parse_list(CONV_STRING_FORMAT, VALUE_SEPARATOR, str, formats, &unknown);
/* As the new XML configuration lists formats as one per profile, unknown
* formats will cause the parser to quit. As a workaround for non-legacy
@ -384,44 +388,70 @@ bool pa_conversion_parse_formats(const char *fn, const unsigned ln,
return count > 0;
}
static bool parse_channels(const char *fn, const unsigned ln,
const char *str, bool in_output,
bool legacy, audio_channel_mask_t *channels) {
int count;
static int parse_channels(const char *fn, const unsigned ln,
const char *str, bool in_output,
audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]) {
bool success;
int count = 0;
char *unknown = NULL;
char *entry;
const char *state = NULL;
pa_assert(fn);
pa_assert(str);
pa_assert(channels);
/* Needs to be probed later */
if (pa_streq(str, "dynamic")) {
*channels = 0;
return true;
channel_masks[0] = 0;
return 1;
}
count = pa_conversion_parse_list(in_output ? CONV_STRING_OUTPUT_CHANNELS : CONV_STRING_INPUT_CHANNELS,
value_separator(legacy), str, channels, &unknown);
while ((entry = pa_split(str, VALUE_SEPARATOR, &state))) {
uint32_t val;
return check_and_log(fn, ln, in_output ? "output channel_masks" : "input channel_masks",
count, str, unknown, false);
if (count == AUDIO_MAX_CHANNEL_MASKS) {
pa_log("[%s:%u] Too many channel mask entries (> %d)", fn, ln, AUDIO_MAX_CHANNEL_MASKS);
pa_xfree(entry);
return false;
}
if (!string_convert_str_to_num(in_output ? string_conversion_table_output_channels
: string_conversion_table_input_channels,
entry,
&val)) {
pa_log_debug("[%s:%u] Ignore unknown channel mask value %s", fn, ln, entry);
pa_xfree(entry);
continue;
}
channel_masks[count++] = val;
pa_xfree(entry);
}
channel_masks[count] = 0;
/* Avoid aborting parsing when no supported channel is found */
success = check_and_log(fn, ln, in_output ? "output channel_masks" : "input channel_masks",
count == 0 ? 1 : count, str, unknown, false);
return success ? count : -1;
}
bool pa_conversion_parse_output_channels(const char *fn, const unsigned ln,
const char *str, bool legacy,
audio_channel_mask_t *channels) {
return parse_channels(fn, ln, str, true, legacy, channels);
int pa_conversion_parse_output_channels(const char *fn, const unsigned ln,
const char *str,
audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]) {
return parse_channels(fn, ln, str, true, channel_masks);
}
bool pa_conversion_parse_input_channels(const char *fn, const unsigned ln,
const char *str, bool legacy,
audio_channel_mask_t *channels) {
return parse_channels(fn, ln, str, false, legacy, channels);
int pa_conversion_parse_input_channels(const char *fn, const unsigned ln,
const char *str,
audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]) {
return parse_channels(fn, ln, str, false, channel_masks);
}
static bool parse_devices(const char *fn, const unsigned ln,
const char *str, bool in_output,
bool legacy, bool must_recognize_all,
bool must_recognize_all,
audio_devices_t *devices) {
int count;
char *unknown = NULL;
@ -431,11 +461,7 @@ static bool parse_devices(const char *fn, const unsigned ln,
pa_assert(devices);
count = pa_conversion_parse_list(in_output ? CONV_STRING_OUTPUT_DEVICE : CONV_STRING_INPUT_DEVICE,
value_separator(legacy), str, devices, &unknown);
if (legacy)
return check_and_log(fn, ln, in_output ? "output devices" : "input devices",
count, str, unknown, must_recognize_all);
VALUE_SEPARATOR, str, devices, &unknown);
/* As the new XML configuration lists devices as one per devicePort, unknown
* devices will cause the parser to quit. As a workaround for non-legacy
@ -446,15 +472,15 @@ static bool parse_devices(const char *fn, const unsigned ln,
}
bool pa_conversion_parse_output_devices(const char *fn, const unsigned ln,
char *str, bool legacy, bool must_recognize_all,
char *str, bool must_recognize_all,
audio_devices_t *devices) {
return parse_devices(fn, ln, str, true, legacy, must_recognize_all, devices);
return parse_devices(fn, ln, str, true, must_recognize_all, devices);
}
bool pa_conversion_parse_input_devices(const char *fn, const unsigned ln,
char *str, bool legacy, bool must_recognize_all,
char *str, bool must_recognize_all,
audio_devices_t *devices) {
return parse_devices(fn, ln, str, false, legacy, must_recognize_all, devices);
return parse_devices(fn, ln, str, false, must_recognize_all, devices);
}
bool pa_conversion_parse_output_flags(const char *fn, const unsigned ln,

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2018 Jolla Ltd.
* Copyright (C) 2013-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -26,6 +26,7 @@
#include "droid/version.h"
#include "droid/droid-config.h"
#include "droid/sllist.h"
#include "config-parser-xml.h"
#include <signal.h>
#include <stdio.h>
@ -36,52 +37,28 @@
#include <valgrind/memcheck.h>
#endif
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/volume.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core.h>
#include <pulsecore/core-error.h>
#include <pulsecore/i18n.h>
#include <pulsecore/module.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
#include <pulsecore/modargs.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/refcnt.h>
#include <pulsecore/shared.h>
#include <pulsecore/mutex.h>
#include <pulsecore/strlist.h>
#include <pulsecore/atomic.h>
#include <pulsecore/modargs.h>
#include <hardware/audio.h>
#include <hardware_legacy/audio_policy_conf.h>
#define ODM_AUDIO_POLICY_CONFIG_XML_FILE "/odm/etc/audio_policy_configuration.xml"
#define VENDOR_AUDIO_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio/audio_policy_configuration.xml"
#define VENDOR_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio_policy_configuration.xml"
#define SYSTEM_AUDIO_POLICY_CONFIG_XML_FILE "/system/etc/audio_policy_configuration.xml"
#define ODM_AUDIO_POLICY_CONFIG_XML_FILE "/odm/etc/audio_policy_configuration.xml"
#define VENDOR_AUDIO_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio/audio_policy_configuration.xml"
#define VENDOR_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio_policy_configuration.xml"
#define SYSTEM_AUDIO_POLICY_CONFIG_XML_FILE "/system/etc/audio_policy_configuration.xml"
pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma) {
pa_droid_config_audio *config = NULL;
dm_config_device *dm_config_load(pa_modargs *ma) {
dm_config_device *config = NULL;
const char *manual_config;
const char *config_location[] = {
ODM_AUDIO_POLICY_CONFIG_XML_FILE,
VENDOR_AUDIO_AUDIO_POLICY_CONFIG_XML_FILE,
VENDOR_AUDIO_POLICY_CONFIG_XML_FILE,
AUDIO_POLICY_VENDOR_CONFIG_FILE,
SYSTEM_AUDIO_POLICY_CONFIG_XML_FILE,
AUDIO_POLICY_CONFIG_FILE,
NULL};
pa_assert(ma);
@ -106,92 +83,210 @@ pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma) {
return config;
}
pa_droid_config_audio *pa_droid_config_dup(const pa_droid_config_audio *config) {
pa_droid_config_audio *config_copy;
pa_droid_config_hw_module *module, *module_copy;
pa_droid_config_device *device, *device_copy;
static dm_config_profile *config_profile_dup(const dm_config_profile *profile) {
dm_config_profile *copy = pa_xnew0(dm_config_profile, 1);
copy->name = pa_xstrdup(profile->name);
copy->format = profile->format;
memcpy(copy->sampling_rates,
profile->sampling_rates,
sizeof(profile->sampling_rates));
memcpy(copy->channel_masks,
profile->channel_masks,
sizeof(profile->channel_masks));
return copy;
}
static dm_config_port *config_port_dup(const dm_config_port *port, dm_config_module *module) {
dm_config_port *copy = pa_xnew0(dm_config_port, 1);
const dm_list_entry *i;
copy->module = module;
copy->port_type = port->port_type;
copy->name = pa_xstrdup(port->name);
copy->role = port->role;
copy->profiles = dm_list_new();
DM_LIST_FOREACH(i, port->profiles)
dm_list_push_back(copy->profiles, config_profile_dup(i->data));
if (port->port_type == DM_CONFIG_TYPE_DEVICE_PORT) {
copy->type = port->type;
copy->address = pa_xstrdup(port->address);
}
if (port->port_type == DM_CONFIG_TYPE_MIX_PORT) {
copy->flags = port->flags;
copy->max_open_count = port->max_open_count;
copy->max_active_count = port->max_active_count;
}
return copy;
}
static dm_config_route *config_route_dup(const dm_config_route *route, dm_list *ports) {
dm_config_route *copy = pa_xnew0(dm_config_route, 1);
dm_config_port *port_copy, *port;
void *state, *state2;
copy->type = route->type;
copy->sources = dm_list_new();
DM_LIST_FOREACH_DATA(port, route->sources, state) {
DM_LIST_FOREACH_DATA(port_copy, ports, state2) {
if (dm_config_port_equal(port, port_copy)) {
dm_list_push_back(copy->sources, port_copy);
break;
}
}
}
DM_LIST_FOREACH_DATA(port_copy, ports, state) {
if (dm_config_port_equal(port_copy, route->sink)) {
copy->sink = port_copy;
break;
}
}
return copy;
}
static dm_config_module *config_module_dup(const dm_config_module *module) {
dm_config_module *copy = pa_xnew0(dm_config_module, 1);
dm_config_port *device_port, *attached_device, *mix_port;
dm_config_route *route;
void *state, *state2;
copy = pa_xnew0(dm_config_module, 1);
copy->name = pa_xstrdup(module->name);
copy->version_major = module->version_major;
copy->version_minor = module->version_minor;
copy->attached_devices = dm_list_new();
copy->default_output_device = NULL;
copy->mix_ports = dm_list_new();
copy->device_ports = dm_list_new();
copy->ports = dm_list_new();
copy->routes = dm_list_new();
DM_LIST_FOREACH_DATA(device_port, module->device_ports, state) {
dm_config_port *device_port_copy = config_port_dup(device_port, copy);
dm_list_push_back(copy->device_ports, device_port_copy);
dm_list_push_back(copy->ports, device_port_copy);
if (module->default_output_device == device_port)
copy->default_output_device = device_port_copy;
DM_LIST_FOREACH_DATA(attached_device, module->attached_devices, state2) {
if (attached_device == device_port) {
dm_list_push_back(copy->attached_devices, device_port_copy);
break;
}
}
}
DM_LIST_FOREACH_DATA(mix_port, module->mix_ports, state) {
dm_config_port *mix_port_copy = config_port_dup(mix_port, copy);
dm_list_push_back(copy->mix_ports, mix_port_copy);
dm_list_push_back(copy->ports, mix_port_copy);
}
DM_LIST_FOREACH_DATA(route, module->routes, state)
dm_list_push_back(copy->routes, config_route_dup(route, copy->ports));
return copy;
}
dm_config_device *dm_config_dup(const dm_config_device *config) {
dm_config_device *copy;
dm_config_module *module;
void *state;
pa_assert(config);
config_copy = pa_xnew0(pa_droid_config_audio, 1);
copy = pa_xnew0(dm_config_device, 1);
copy->global_config = dm_list_new();
copy->modules = dm_list_new();
if (config->global_config)
config_copy->global_config = pa_xmemdup(config->global_config, sizeof(*config->global_config));
if (config->global_config) {
dm_config_global *global, *global_copy;
SLLIST_FOREACH(module, config->hw_modules) {
module_copy = pa_droid_config_hw_module_new(config_copy, module->name);
if (module->global_config)
module_copy->global_config = pa_xmemdup(module->global_config, sizeof(*module->global_config));
SLLIST_FOREACH(device, module->outputs) {
device_copy = pa_xmemdup(device, sizeof(*device));
device_copy->module = module_copy;
device_copy->name = pa_xstrdup(device->name);
SLLIST_APPEND(pa_droid_config_device, module_copy->outputs, device_copy);
DM_LIST_FOREACH_DATA(global, config->global_config, state) {
global_copy = pa_xnew0(dm_config_global, 1);
global_copy->key = pa_xstrdup(global->key);
global_copy->value = pa_xstrdup(global->value);
dm_list_push_back(copy->global_config, global_copy);
}
SLLIST_FOREACH(device, module->inputs) {
device_copy = pa_xmemdup(device, sizeof(*device));
device_copy->module = module_copy;
device_copy->name = pa_xstrdup(device->name);
SLLIST_APPEND(pa_droid_config_device, module_copy->inputs, device_copy);
}
SLLIST_APPEND(pa_droid_config_hw_module, config_copy->hw_modules, module_copy);
}
return config_copy;
DM_LIST_FOREACH_DATA(module, config->modules, state)
dm_list_push_back(copy->modules, config_module_dup(module));
return copy;
}
pa_droid_config_audio *pa_parse_droid_audio_config(const char *filename) {
const char *suffix;
pa_assert(filename);
if ((suffix = rindex(filename, '.'))) {
if (strlen(suffix) == 4 && pa_streq(suffix, ".xml"))
return pa_parse_droid_audio_config_xml(filename);
else if (strlen(suffix) == 5 && pa_streq(suffix, ".conf"))
return pa_parse_droid_audio_config_legacy(filename);
}
return NULL;
dm_config_device *pa_parse_droid_audio_config(const char *filename) {
return pa_parse_droid_audio_config_xml(filename);
}
void pa_droid_config_free(pa_droid_config_audio *config) {
pa_droid_config_hw_module *module;
pa_droid_config_device *device;
static void config_global_free(void *data) {
dm_config_global *global = data;
pa_xfree(global->key);
pa_xfree(global->value);
pa_xfree(global);
}
static void config_profile_free(void *data) {
dm_config_profile *profile = data;
pa_xfree(profile->name);
pa_xfree(profile);
}
static void config_port_free(void *data) {
dm_config_port *port = data;
pa_xfree(port->name);
pa_xfree(port->address);
dm_list_free(port->profiles, config_profile_free);
pa_xfree(port);
}
static void config_route_free(void *data) {
dm_config_route *route = data;
dm_list_free(route->sources, NULL);
pa_xfree(route);
}
static void config_module_free(void *data) {
dm_config_module *module = data;
pa_xfree(module->name);
dm_list_free(module->attached_devices, NULL);
dm_list_free(module->ports, config_port_free);
dm_list_free(module->device_ports, NULL);
dm_list_free(module->mix_ports, NULL);
dm_list_free(module->routes, config_route_free);
pa_xfree(module);
}
void dm_config_free(dm_config_device *config) {
if (!config)
return;
while (config->hw_modules) {
SLLIST_STEAL_FIRST(module, config->hw_modules);
while (module->outputs) {
SLLIST_STEAL_FIRST(device, module->outputs);
pa_droid_config_device_free(device);
}
while (module->inputs) {
SLLIST_STEAL_FIRST(device, module->inputs);
pa_droid_config_device_free(device);
}
pa_droid_config_hw_module_free(module);
}
pa_xfree(config->global_config);
dm_list_free(config->global_config, config_global_free);
dm_list_free(config->modules, config_module_free);
pa_xfree(config);
}
const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_config_audio *config, const char* module_id) {
pa_droid_config_hw_module *module;
dm_config_module *dm_config_find_module(dm_config_device *config, const char* module_id) {
dm_config_module *module;
void *state;
pa_assert(config);
pa_assert(module_id);
SLLIST_FOREACH(module, config->hw_modules) {
DM_LIST_FOREACH_DATA(module, config->modules, state) {
if (pa_streq(module_id, module->name))
return module;
}
@ -199,71 +294,73 @@ const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_conf
return NULL;
}
static const pa_droid_config_device *find_device(const pa_droid_config_hw_module *module, bool output, const char* device_name) {
pa_droid_config_device *device;
dm_config_port *dm_config_find_port(dm_config_module *module, const char* name) {
dm_config_port *port;
void *state;
pa_assert(module);
pa_assert(device_name);
pa_assert(name);
SLLIST_FOREACH(device, output ? module->outputs : module->inputs) {
if (pa_streq(device_name, device->name))
return device;
DM_LIST_FOREACH_DATA(port, module->ports, state) {
if (pa_streq(name, port->name))
return port;
}
return NULL;
}
const pa_droid_config_device *pa_droid_config_find_output(const pa_droid_config_hw_module *module, const char* output_name) {
return find_device(module, true, output_name);
}
const pa_droid_config_device *pa_droid_config_find_input(const pa_droid_config_hw_module *module, const char* input_name) {
return find_device(module, false, input_name);
}
pa_droid_config_hw_module *pa_droid_config_hw_module_new(const pa_droid_config_audio *config, const char *name) {
pa_droid_config_hw_module *hw_module;
pa_assert(config);
pa_assert(name);
hw_module = pa_xnew0(pa_droid_config_hw_module, 1);
hw_module->config = config;
hw_module->name = pa_xstrndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN);
return hw_module;
}
void pa_droid_config_hw_module_free(pa_droid_config_hw_module *hw_module) {
if (!hw_module)
return;
pa_xfree(hw_module->name);
pa_xfree(hw_module->global_config);
pa_xfree(hw_module);
}
pa_droid_config_device *pa_droid_config_device_new(const pa_droid_config_hw_module *module,
pa_direction_t direction,
const char *name) {
pa_droid_config_device *device;
dm_config_port *dm_config_default_output_device(dm_config_module *module) {
pa_assert(module);
pa_assert(direction == PA_DIRECTION_OUTPUT || direction == PA_DIRECTION_INPUT);
pa_assert(name);
device = pa_xnew0(pa_droid_config_device, 1);
device->module = module;
device->direction = direction;
device->name = pa_replace(name, " ", "_");
return device;
if (module->default_output_device)
return module->default_output_device;
else {
pa_log("Module %s doesn't have default output device.", module->name);
return 0;
}
}
void pa_droid_config_device_free(pa_droid_config_device *device) {
if (!device)
return;
char *dm_config_escape_string(const char *string) {
if (!string)
return NULL;
pa_xfree(device->name);
pa_xfree(device);
/* Just replace whitespace with underscores for now. */
return pa_replace(string, " ", "_");
}
dm_config_port *dm_config_find_device_port(dm_config_port *port, audio_devices_t device) {
dm_config_port *device_port;
void *state;
pa_assert(port);
DM_LIST_FOREACH_DATA(device_port, port->module->device_ports, state) {
if (device_port->type == device)
return device_port;
}
return NULL;
}
bool dm_config_port_equal(const dm_config_port *a, const dm_config_port *b) {
if ((!a && b) || (a && !b))
return false;
else if (!a && !b)
return true;
return (pa_streq(a->name, b->name) && a->type == b->type);
}
dm_config_port *dm_config_find_mix_port(dm_config_module *module, const char *name) {
dm_config_port *mix_port = NULL;
void *state;
DM_LIST_FOREACH_DATA(mix_port, module->mix_ports, state) {
if (pa_streq(mix_port->name, name))
return mix_port;
}
return NULL;
}

View file

@ -1,311 +0,0 @@
/*
* Copyright (C) 2013 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
* These PulseAudio Modules are 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
* version 2.1 of the License.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#ifndef _ANDROID_UTIL_V412_H_
#define _ANDROID_UTIL_V412_H_
#ifdef DROID_DEVICE_SBJ
#define QCOM_HARDWARE
#endif
#include <hardware/audio.h>
#include <hardware_legacy/audio_policy_conf.h>
#include <pulse/channelmap.h>
// PulseAudio value - Android value
uint32_t conversion_table_output_channel[][2] = {
{ PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_OUT_MONO },
{ PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_OUT_FRONT_LEFT },
{ PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_OUT_FRONT_RIGHT},
{ PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_OUT_FRONT_CENTER },
{ PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_OUT_LOW_FREQUENCY },
{ PA_CHANNEL_POSITION_REAR_LEFT, AUDIO_CHANNEL_OUT_BACK_LEFT },
{ PA_CHANNEL_POSITION_REAR_RIGHT, AUDIO_CHANNEL_OUT_BACK_RIGHT },
{ PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER },
{ PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER },
{ PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_OUT_BACK_CENTER },
{ PA_CHANNEL_POSITION_SIDE_LEFT, AUDIO_CHANNEL_OUT_SIDE_LEFT },
{ PA_CHANNEL_POSITION_SIDE_RIGHT, AUDIO_CHANNEL_OUT_SIDE_RIGHT },
{ PA_CHANNEL_POSITION_TOP_CENTER, AUDIO_CHANNEL_OUT_TOP_CENTER },
{ PA_CHANNEL_POSITION_TOP_FRONT_LEFT, AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT },
{ PA_CHANNEL_POSITION_TOP_FRONT_CENTER, AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER },
{ PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT },
{ PA_CHANNEL_POSITION_TOP_REAR_LEFT, AUDIO_CHANNEL_OUT_TOP_BACK_LEFT },
{ PA_CHANNEL_POSITION_TOP_REAR_CENTER, AUDIO_CHANNEL_OUT_TOP_BACK_CENTER },
{ PA_CHANNEL_POSITION_TOP_REAR_RIGHT, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT }
};
uint32_t conversion_table_input_channel[][2] = {
{ PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_MONO },
{ PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT },
{ PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT},
{ PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT },
{ PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK },
/* Following are missing suitable counterparts on PulseAudio side. */
{ AUDIO_CHANNEL_IN_LEFT_PROCESSED, AUDIO_CHANNEL_IN_LEFT_PROCESSED },
{ AUDIO_CHANNEL_IN_RIGHT_PROCESSED, AUDIO_CHANNEL_IN_RIGHT_PROCESSED },
{ AUDIO_CHANNEL_IN_FRONT_PROCESSED, AUDIO_CHANNEL_IN_FRONT_PROCESSED },
{ AUDIO_CHANNEL_IN_BACK_PROCESSED, AUDIO_CHANNEL_IN_BACK_PROCESSED },
{ AUDIO_CHANNEL_IN_PRESSURE, AUDIO_CHANNEL_IN_PRESSURE },
{ AUDIO_CHANNEL_IN_X_AXIS, AUDIO_CHANNEL_IN_X_AXIS },
{ AUDIO_CHANNEL_IN_Y_AXIS, AUDIO_CHANNEL_IN_Y_AXIS },
{ AUDIO_CHANNEL_IN_Z_AXIS, AUDIO_CHANNEL_IN_Z_AXIS },
{ AUDIO_CHANNEL_IN_VOICE_UPLINK, AUDIO_CHANNEL_IN_VOICE_UPLINK },
{ AUDIO_CHANNEL_IN_VOICE_DNLINK, AUDIO_CHANNEL_IN_VOICE_DNLINK }
};
uint32_t conversion_table_format[][2] = {
{ PA_SAMPLE_U8, AUDIO_FORMAT_PCM_8_BIT },
{ PA_SAMPLE_S16LE, AUDIO_FORMAT_PCM_16_BIT },
{ PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT },
{ PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_8_24_BIT }
};
uint32_t conversion_table_default_audio_source[][2] = {
{ AUDIO_DEVICE_IN_ALL, AUDIO_SOURCE_DEFAULT }
};
struct string_conversion {
uint32_t value;
const char *str;
};
#if defined(STRING_ENTRY) || defined(STRING_ENTRY)
#error STRING_ENTRY already defined somewhere, fix this lib.
#endif
#define STRING_ENTRY(str) { str, #str }
/* Output devices */
struct string_conversion string_conversion_table_output_device[] = {
STRING_ENTRY(AUDIO_DEVICE_OUT_EARPIECE),
STRING_ENTRY(AUDIO_DEVICE_OUT_SPEAKER),
STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADSET),
STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO),
STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET),
STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP),
STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES),
STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
STRING_ENTRY(AUDIO_DEVICE_OUT_AUX_DIGITAL),
STRING_ENTRY(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET),
STRING_ENTRY(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET),
STRING_ENTRY(AUDIO_DEVICE_OUT_USB_ACCESSORY),
STRING_ENTRY(AUDIO_DEVICE_OUT_USB_DEVICE),
STRING_ENTRY(AUDIO_DEVICE_OUT_FM),
STRING_ENTRY(AUDIO_DEVICE_OUT_FM_TX),
STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADSET),
STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADPHONE),
STRING_ENTRY(AUDIO_DEVICE_OUT_PROXY),
STRING_ENTRY(AUDIO_DEVICE_OUT_ALL),
STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_A2DP),
STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_SCO),
STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_USB),
{ 0, NULL }
};
struct string_conversion string_conversion_table_output_device_fancy[] = {
{ AUDIO_DEVICE_OUT_EARPIECE, "output-earpiece" },
{ AUDIO_DEVICE_OUT_SPEAKER, "output-speaker" },
{ AUDIO_DEVICE_OUT_SPEAKER
| AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-speaker+wired_headphone" },
{ AUDIO_DEVICE_OUT_WIRED_HEADSET, "output-wired_headset" },
{ AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-wired_headphone" },
{ AUDIO_DEVICE_OUT_BLUETOOTH_SCO, "output-bluetooth_sco" },
{ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, "output-sco_headset" },
{ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, "output-sco_carkit" },
{ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "output-a2dp" },
{ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "output-a2dp_headphones" },
{ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "output-a2dp_speaker" },
{ AUDIO_DEVICE_OUT_AUX_DIGITAL, "output-aux_digital" },
{ AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, "output-analog_dock_headset" },
{ AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, "output-digital_dock_headset" },
{ AUDIO_DEVICE_OUT_USB_ACCESSORY, "output-usb_accessory" },
{ AUDIO_DEVICE_OUT_USB_DEVICE, "output-usb_device" },
{ AUDIO_DEVICE_OUT_FM, "output-fm" },
{ AUDIO_DEVICE_OUT_FM_TX, "output-fm_tx" },
{ AUDIO_DEVICE_OUT_ANC_HEADSET, "output-anc_headset" },
{ AUDIO_DEVICE_OUT_ANC_HEADPHONE, "output-anc_headphone" },
{ AUDIO_DEVICE_OUT_PROXY, "output-proxy" },
{ 0, NULL }
};
/* Input devices */
struct string_conversion string_conversion_table_input_device[] = {
STRING_ENTRY(AUDIO_DEVICE_IN_COMMUNICATION),
STRING_ENTRY(AUDIO_DEVICE_IN_AMBIENT),
STRING_ENTRY(AUDIO_DEVICE_IN_BUILTIN_MIC),
STRING_ENTRY(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
STRING_ENTRY(AUDIO_DEVICE_IN_WIRED_HEADSET),
STRING_ENTRY(AUDIO_DEVICE_IN_AUX_DIGITAL),
STRING_ENTRY(AUDIO_DEVICE_IN_VOICE_CALL),
STRING_ENTRY(AUDIO_DEVICE_IN_BACK_MIC),
STRING_ENTRY(AUDIO_DEVICE_IN_ANC_HEADSET),
STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX),
STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX_A2DP),
STRING_ENTRY(AUDIO_DEVICE_IN_PROXY),
STRING_ENTRY(AUDIO_DEVICE_IN_DEFAULT),
/* Combination entries consisting of multiple devices defined above.
* These don't require counterpart in string_conversion_table_input_device_fancy. */
STRING_ENTRY(AUDIO_DEVICE_IN_ALL),
STRING_ENTRY(AUDIO_DEVICE_IN_ALL_SCO),
{ 0, NULL }
};
struct string_conversion string_conversion_table_input_device_fancy[] = {
{ AUDIO_DEVICE_IN_COMMUNICATION, "input-communication" },
{ AUDIO_DEVICE_IN_AMBIENT, "input-ambient" },
{ AUDIO_DEVICE_IN_BUILTIN_MIC, "input-builtin_mic" },
{ AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, "input-bluetooth_sco_headset" },
{ AUDIO_DEVICE_IN_WIRED_HEADSET, "input-wired_headset" },
{ AUDIO_DEVICE_IN_AUX_DIGITAL, "input-aux_digital" },
{ AUDIO_DEVICE_IN_VOICE_CALL, "input-voice_call" },
{ AUDIO_DEVICE_IN_BACK_MIC, "input-back_mic" },
{ AUDIO_DEVICE_IN_ANC_HEADSET, "input-anc_headset" },
{ AUDIO_DEVICE_IN_FM_RX, "input-fm_rx" },
{ AUDIO_DEVICE_IN_FM_RX_A2DP, "input-fm_rx_a2dp" },
{ AUDIO_DEVICE_IN_PROXY, "input-in_proxy" },
{ AUDIO_DEVICE_IN_DEFAULT, "input-default" },
{ 0, NULL }
};
struct string_conversion string_conversion_table_audio_source_fancy[] = {
{ AUDIO_SOURCE_DEFAULT, "default" },
{ AUDIO_SOURCE_MIC, "mic" },
{ AUDIO_SOURCE_VOICE_UPLINK, "voice uplink" },
{ AUDIO_SOURCE_VOICE_DOWNLINK, "voice downlink" },
{ AUDIO_SOURCE_VOICE_CALL, "voice call" },
{ AUDIO_SOURCE_CAMCORDER, "camcorder" },
{ AUDIO_SOURCE_VOICE_RECOGNITION, "voice recognition" },
{ AUDIO_SOURCE_VOICE_COMMUNICATION, "voice communication" },
{ AUDIO_SOURCE_FM_RX, "fm rx" },
{ AUDIO_SOURCE_FM_RX_A2DP, "fm rx a2dp" },
{ (uint32_t)-1, NULL }
};
/* Flags */
struct string_conversion string_conversion_table_output_flag[] = {
STRING_ENTRY(AUDIO_OUTPUT_FLAG_NONE),
STRING_ENTRY(AUDIO_OUTPUT_FLAG_DIRECT),
STRING_ENTRY(AUDIO_OUTPUT_FLAG_PRIMARY),
STRING_ENTRY(AUDIO_OUTPUT_FLAG_FAST),
STRING_ENTRY(AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
/* Qualcomm flags */
STRING_ENTRY(AUDIO_OUTPUT_FLAG_LPA),
STRING_ENTRY(AUDIO_OUTPUT_FLAG_TUNNEL),
STRING_ENTRY(AUDIO_OUTPUT_FLAG_VOIP_RX),
{ 0, NULL }
};
struct string_conversion string_conversion_table_input_flag[] = {
{ 0, NULL }
};
/* Channels */
struct string_conversion string_conversion_table_output_channels[] = {
STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_CENTER),
STRING_ENTRY(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_LEFT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_RIGHT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_CENTER),
STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_LEFT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_CENTER),
STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
STRING_ENTRY(AUDIO_CHANNEL_OUT_MONO),
STRING_ENTRY(AUDIO_CHANNEL_OUT_STEREO),
STRING_ENTRY(AUDIO_CHANNEL_OUT_QUAD),
STRING_ENTRY(AUDIO_CHANNEL_OUT_SURROUND),
STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1),
STRING_ENTRY(AUDIO_CHANNEL_OUT_7POINT1),
STRING_ENTRY(AUDIO_CHANNEL_OUT_ALL),
{ 0, NULL }
};
struct string_conversion string_conversion_table_input_channels[] = {
STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT),
STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT),
STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT),
STRING_ENTRY(AUDIO_CHANNEL_IN_BACK),
STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT_PROCESSED),
STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT_PROCESSED),
STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_PROCESSED),
STRING_ENTRY(AUDIO_CHANNEL_IN_BACK_PROCESSED),
STRING_ENTRY(AUDIO_CHANNEL_IN_PRESSURE),
STRING_ENTRY(AUDIO_CHANNEL_IN_X_AXIS),
STRING_ENTRY(AUDIO_CHANNEL_IN_Y_AXIS),
STRING_ENTRY(AUDIO_CHANNEL_IN_Z_AXIS),
STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK),
STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK),
STRING_ENTRY(AUDIO_CHANNEL_IN_MONO),
STRING_ENTRY(AUDIO_CHANNEL_IN_STEREO),
STRING_ENTRY(AUDIO_CHANNEL_IN_5POINT1),
STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO),
STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO),
STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_CALL_MONO),
STRING_ENTRY(AUDIO_CHANNEL_IN_ALL),
{ 0, NULL }
};
/* Formats */
struct string_conversion string_conversion_table_format[] = {
STRING_ENTRY(AUDIO_FORMAT_DEFAULT),
STRING_ENTRY(AUDIO_FORMAT_PCM),
STRING_ENTRY(AUDIO_FORMAT_MP3),
STRING_ENTRY(AUDIO_FORMAT_AMR_NB),
STRING_ENTRY(AUDIO_FORMAT_AMR_WB),
STRING_ENTRY(AUDIO_FORMAT_AAC),
STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V1),
STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V2),
STRING_ENTRY(AUDIO_FORMAT_VORBIS),
STRING_ENTRY(AUDIO_FORMAT_EVRC),
STRING_ENTRY(AUDIO_FORMAT_QCELP),
STRING_ENTRY(AUDIO_FORMAT_AC3),
STRING_ENTRY(AUDIO_FORMAT_AC3_PLUS),
STRING_ENTRY(AUDIO_FORMAT_DTS),
STRING_ENTRY(AUDIO_FORMAT_WMA),
STRING_ENTRY(AUDIO_FORMAT_WMA_PRO),
STRING_ENTRY(AUDIO_FORMAT_AAC_ADIF),
STRING_ENTRY(AUDIO_FORMAT_EVRCB),
STRING_ENTRY(AUDIO_FORMAT_EVRCWB),
STRING_ENTRY(AUDIO_FORMAT_EAC3),
STRING_ENTRY(AUDIO_FORMAT_DTS_LBR),
STRING_ENTRY(AUDIO_FORMAT_AMR_WB_PLUS),
/* Currently we support only PCM formats, but keep all formats
* here so audio_policy.conf can be parsed. */
STRING_ENTRY(AUDIO_FORMAT_PCM_16_BIT),
STRING_ENTRY(AUDIO_FORMAT_PCM_8_BIT),
STRING_ENTRY(AUDIO_FORMAT_PCM_32_BIT),
STRING_ENTRY(AUDIO_FORMAT_PCM_8_24_BIT),
{ 0, NULL }
};
#undef STRING_ENTRY
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Jolla Ltd.
* Copyright (C) 2017-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -28,7 +28,6 @@
#endif
#include <hardware/audio.h>
#include <hardware_legacy/audio_policy_conf.h>
#include <pulse/channelmap.h>
@ -90,8 +89,9 @@ uint32_t conversion_table_input_channel[][2] = {
uint32_t conversion_table_format[][2] = {
{ PA_SAMPLE_U8, AUDIO_FORMAT_PCM_8_BIT },
{ PA_SAMPLE_S16LE, AUDIO_FORMAT_PCM_16_BIT },
{ PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT },
{ PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_8_24_BIT }
{ PA_SAMPLE_S24_32LE, AUDIO_FORMAT_PCM_8_24_BIT },
{ PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_24_BIT_PACKED },
{ PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT }
};
uint32_t conversion_table_default_audio_source[][2] = {
@ -102,18 +102,33 @@ uint32_t conversion_table_default_audio_source[][2] = {
{ AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_AUX_DIGITAL, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_VOICE_CALL, AUDIO_SOURCE_VOICE_CALL },
{ AUDIO_DEVICE_IN_TELEPHONY_RX, AUDIO_SOURCE_VOICE_CALL },
{ AUDIO_DEVICE_IN_BACK_MIC, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_SOURCE_REMOTE_SUBMIX },
{ AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_USB_ACCESSORY, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_FM_TUNER, AUDIO_SOURCE_FM_TUNER },
{ AUDIO_DEVICE_IN_TV_TUNER, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_LINE, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_SPDIF, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_BLUETOOTH_A2DP, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_LOOPBACK, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_IP, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_BUS, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_PROXY, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_USB_HEADSET, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_BLUETOOTH_BLE, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_HDMI_ARC, AUDIO_SOURCE_MIC },
{ AUDIO_DEVICE_IN_ECHO_REFERENCE, AUDIO_SOURCE_MIC },
#if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_RX) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_RX)
{ AUDIO_DEVICE_IN_FM_RX, AUDIO_SOURCE_FM_RX },
#endif
#if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_TUNER) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_TUNER)
{ AUDIO_DEVICE_IN_FM_TUNER, AUDIO_SOURCE_FM_TUNER },
#endif
#if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_RX_A2DP) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_RX_A2DP)
{ AUDIO_DEVICE_IN_FM_RX_A2DP, AUDIO_SOURCE_FM_RX_A2DP },
#endif
{ AUDIO_DEVICE_IN_ALL, AUDIO_SOURCE_DEFAULT }
};
/* Output devices */
@ -136,30 +151,30 @@ struct string_conversion string_conversion_table_output_device[] = {
STRING_ENTRY( AUDIO_DEVICE_OUT_USB_ACCESSORY ),
STRING_ENTRY( AUDIO_DEVICE_OUT_USB_DEVICE ),
STRING_ENTRY( AUDIO_DEVICE_OUT_REMOTE_SUBMIX ),
STRING_ENTRY( AUDIO_DEVICE_OUT_TELEPHONY_TX ),
STRING_ENTRY( AUDIO_DEVICE_OUT_LINE ),
STRING_ENTRY( AUDIO_DEVICE_OUT_HDMI_ARC ),
STRING_ENTRY( AUDIO_DEVICE_OUT_SPDIF ),
STRING_ENTRY( AUDIO_DEVICE_OUT_FM ),
STRING_ENTRY( AUDIO_DEVICE_OUT_AUX_LINE ),
STRING_ENTRY( AUDIO_DEVICE_OUT_SPEAKER_SAFE ),
STRING_ENTRY( AUDIO_DEVICE_OUT_IP ),
STRING_ENTRY( AUDIO_DEVICE_OUT_BUS ),
STRING_ENTRY( AUDIO_DEVICE_OUT_PROXY ),
STRING_ENTRY( AUDIO_DEVICE_OUT_USB_HEADSET ),
STRING_ENTRY( AUDIO_DEVICE_OUT_HEARING_AID ),
STRING_ENTRY( AUDIO_DEVICE_OUT_ECHO_CANCELLER ),
STRING_ENTRY( AUDIO_DEVICE_OUT_DEFAULT ),
/* Devices which may or may not be defined for all devices,
* update configure.ac CC_CHECK_DROID_ENUM list if you encounter new ones. */
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_HDMI
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_HDMI_ARC
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_TELEPHONY_TX
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_LINE
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_SPDIF
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_AUX_LINE
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_SPEAKER_SAFE
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_FM
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_FM_TX
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_ANC_HEADSET
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_ANC_HEADPHONE
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_PROXY
STRING_ENTRY_IF_AUDIO_DEVICE_OUT_IP
{ 0, NULL }
};
/* Combination entries consisting of multiple devices defined above.
* These don't require counterpart in string_conversion_table_output_device_fancy. */
STRING_ENTRY( AUDIO_DEVICE_OUT_ALL ),
STRING_ENTRY( AUDIO_DEVICE_OUT_ALL_A2DP ),
STRING_ENTRY( AUDIO_DEVICE_OUT_ALL_SCO ),
STRING_ENTRY( AUDIO_DEVICE_OUT_ALL_USB ),
struct string_conversion string_conversion_table_audio_mode_fancy[] = {
{ AUDIO_MODE_NORMAL, "normal" },
{ AUDIO_MODE_RINGTONE, "ringtone" },
{ AUDIO_MODE_IN_CALL, "in call" },
{ AUDIO_MODE_IN_COMMUNICATION, "in communication" },
{ AUDIO_MODE_CALL_SCREEN, "call screen" },
{ 0, NULL }
};
@ -167,8 +182,6 @@ struct string_conversion string_conversion_table_output_device[] = {
struct string_conversion string_conversion_table_output_device_fancy[] = {
{ AUDIO_DEVICE_OUT_EARPIECE, "output-earpiece" },
{ AUDIO_DEVICE_OUT_SPEAKER, "output-speaker" },
{ AUDIO_DEVICE_OUT_SPEAKER
| AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-speaker+wired_headphone" },
{ AUDIO_DEVICE_OUT_WIRED_HEADSET, "output-wired_headset" },
{ AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-wired_headphone" },
{ AUDIO_DEVICE_OUT_BLUETOOTH_SCO, "output-bluetooth_sco" },
@ -183,23 +196,21 @@ struct string_conversion string_conversion_table_output_device_fancy[] = {
{ AUDIO_DEVICE_OUT_USB_ACCESSORY, "output-usb_accessory" },
{ AUDIO_DEVICE_OUT_USB_DEVICE, "output-usb_device" },
{ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "output-remote_submix" },
{ AUDIO_DEVICE_OUT_TELEPHONY_TX, "output-telephony_tx" },
{ AUDIO_DEVICE_OUT_LINE, "output-line" },
{ AUDIO_DEVICE_OUT_HDMI_ARC, "output-hdmi_arc" },
{ AUDIO_DEVICE_OUT_SPDIF, "output-spdif" },
{ AUDIO_DEVICE_OUT_FM, "output-fm" },
{ AUDIO_DEVICE_OUT_AUX_LINE, "output-aux_line" },
{ AUDIO_DEVICE_OUT_SPEAKER_SAFE, "output-speaker_safe" },
{ AUDIO_DEVICE_OUT_IP, "output-ip" },
{ AUDIO_DEVICE_OUT_BUS, "output-bus" },
{ AUDIO_DEVICE_OUT_PROXY, "output-proxy" },
{ AUDIO_DEVICE_OUT_USB_HEADSET, "output-usb_headset" },
{ AUDIO_DEVICE_OUT_HEARING_AID, "output-hearing_aid" },
{ AUDIO_DEVICE_OUT_ECHO_CANCELLER, "output-echo_canceller" },
{ AUDIO_DEVICE_OUT_DEFAULT, "output-default" },
/* Devices which may or may not be defined for all devices, */
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_HDMI ( "output-hdmi" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_HDMI_ARC ( "output-hdmi_arc" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_TELEPHONY_TX ( "output-telephony_tx" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_LINE ( "output-line" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_SPDIF ( "output-spdif" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_AUX_LINE ( "output-aux_line" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_SPEAKER_SAFE ( "output-speaker_safe" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_FM ( "output-fm" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_FM_TX ( "output-fm_tx" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_ANC_HEADSET ( "output-and_headset" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_ANC_HEADPHONE ( "output-anc_headphone" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_PROXY ( "output-proxy" )
FANCY_ENTRY_IF_AUDIO_DEVICE_OUT_IP ( "output-ip" )
{ 0, NULL }
};
@ -213,39 +224,33 @@ struct string_conversion string_conversion_table_input_device[] = {
STRING_ENTRY( AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET ),
STRING_ENTRY( AUDIO_DEVICE_IN_WIRED_HEADSET ),
STRING_ENTRY( AUDIO_DEVICE_IN_AUX_DIGITAL ),
STRING_ENTRY( AUDIO_DEVICE_IN_HDMI ), /* Same as AUDIO_DEVICE_IN_AUX_DIGITAL */
STRING_ENTRY( AUDIO_DEVICE_IN_VOICE_CALL ),
STRING_ENTRY( AUDIO_DEVICE_IN_TELEPHONY_RX ), /* Same as AUDIO_DEVICE_IN_VOICE_CALL */
STRING_ENTRY( AUDIO_DEVICE_IN_BACK_MIC ),
STRING_ENTRY( AUDIO_DEVICE_IN_REMOTE_SUBMIX ),
STRING_ENTRY( AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET ),
STRING_ENTRY( AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET ),
STRING_ENTRY( AUDIO_DEVICE_IN_USB_ACCESSORY ),
STRING_ENTRY( AUDIO_DEVICE_IN_USB_DEVICE ),
STRING_ENTRY( AUDIO_DEVICE_IN_FM_TUNER ),
STRING_ENTRY( AUDIO_DEVICE_IN_TV_TUNER ),
STRING_ENTRY( AUDIO_DEVICE_IN_LINE ),
STRING_ENTRY( AUDIO_DEVICE_IN_SPDIF ),
STRING_ENTRY( AUDIO_DEVICE_IN_BLUETOOTH_A2DP ),
STRING_ENTRY( AUDIO_DEVICE_IN_LOOPBACK ),
STRING_ENTRY( AUDIO_DEVICE_IN_IP ),
STRING_ENTRY( AUDIO_DEVICE_IN_BUS ),
STRING_ENTRY( AUDIO_DEVICE_IN_PROXY ),
STRING_ENTRY( AUDIO_DEVICE_IN_USB_HEADSET ),
STRING_ENTRY( AUDIO_DEVICE_IN_BLUETOOTH_BLE ),
STRING_ENTRY( AUDIO_DEVICE_IN_HDMI_ARC ),
STRING_ENTRY( AUDIO_DEVICE_IN_ECHO_REFERENCE ),
STRING_ENTRY( AUDIO_DEVICE_IN_DEFAULT ),
/* Devices which may or may not be defined for all devices,
* update configure.ac CC_CHECK_DROID_ENUM list if you encounter new ones. */
STRING_ENTRY_IF_AUDIO_DEVICE_IN_HDMI
STRING_ENTRY_IF_AUDIO_DEVICE_IN_TELEPHONY_RX
STRING_ENTRY_IF_AUDIO_DEVICE_IN_FM_TUNER
STRING_ENTRY_IF_AUDIO_DEVICE_IN_TV_TUNER
STRING_ENTRY_IF_AUDIO_DEVICE_IN_LINE
STRING_ENTRY_IF_AUDIO_DEVICE_IN_SPDIF
STRING_ENTRY_IF_AUDIO_DEVICE_IN_BLUETOOTH_A2DP
STRING_ENTRY_IF_AUDIO_DEVICE_IN_LOOPBACK
STRING_ENTRY_IF_AUDIO_DEVICE_IN_PROXY
/* Devices which may or may not be defined for all devices. */
STRING_ENTRY_IF_AUDIO_DEVICE_IN_FM_RX
STRING_ENTRY_IF_AUDIO_DEVICE_IN_FM_RX_A2DP
STRING_ENTRY_IF_AUDIO_DEVICE_IN_IP
#ifdef DROID_AUDIO_HAL_SECONDARY_MIC
STRING_ENTRY( AUDIO_DEVICE_IN_SECONDARY_MIC ),
#endif
/* Combination entries consisting of multiple devices defined above.
* These don't require counterpart in string_conversion_table_input_device_fancy. */
STRING_ENTRY( AUDIO_DEVICE_IN_ALL ),
STRING_ENTRY( AUDIO_DEVICE_IN_ALL_SCO ),
STRING_ENTRY_IF_AUDIO_DEVICE_IN_ALL_USB
{ 0, NULL }
};
@ -258,31 +263,31 @@ struct string_conversion string_conversion_table_input_device_fancy[] = {
{ AUDIO_DEVICE_IN_WIRED_HEADSET, "input-wired_headset" },
{ AUDIO_DEVICE_IN_AUX_DIGITAL, "input-aux_digital" },
{ AUDIO_DEVICE_IN_VOICE_CALL, "input-voice_call" },
{ AUDIO_DEVICE_IN_TELEPHONY_RX, "input-telephony_rx", },
{ AUDIO_DEVICE_IN_BACK_MIC, "input-back_mic" },
{ AUDIO_DEVICE_IN_REMOTE_SUBMIX, "input-remote_submix" },
{ AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, "input-analog_dock_headset" },
{ AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, "input-digital_dock_headset" },
{ AUDIO_DEVICE_IN_USB_ACCESSORY, "input-usb_accessory" },
{ AUDIO_DEVICE_IN_USB_DEVICE, "input-usb_device" },
{ AUDIO_DEVICE_IN_FM_TUNER, "input-fm_tuner" },
{ AUDIO_DEVICE_IN_TV_TUNER, "input-tv_tuner" },
{ AUDIO_DEVICE_IN_LINE, "input-line" },
{ AUDIO_DEVICE_IN_SPDIF, "input-spdif" },
{ AUDIO_DEVICE_IN_BLUETOOTH_A2DP, "input-bluetooth_a2dp" },
{ AUDIO_DEVICE_IN_LOOPBACK, "input-loopback" },
{ AUDIO_DEVICE_IN_IP, "input-ip" },
{ AUDIO_DEVICE_IN_BUS, "input-bus" },
{ AUDIO_DEVICE_IN_PROXY, "input-proxy" },
{ AUDIO_DEVICE_IN_USB_HEADSET, "input-usb_headset" },
{ AUDIO_DEVICE_IN_BLUETOOTH_BLE, "input-bluetooth_ble" },
{ AUDIO_DEVICE_IN_HDMI_ARC, "input-hdmi_arc" },
{ AUDIO_DEVICE_IN_ECHO_REFERENCE, "input-echo_reference" },
{ AUDIO_DEVICE_IN_DEFAULT, "input-default" },
/* Devices which may or may not be defined for all devices, */
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_HDMI ( "input-hdmi" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_TELEPHONY_RX ( "input-telephony_rx" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_FM_TUNER ( "input-fm_tuner" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_TV_TUNER ( "input-tv_tuner" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_LINE ( "input-line" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_SPDIF ( "input-spdif" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_BLUETOOTH_A2DP ( "input-bluetooth_a2dp" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_LOOPBACK ( "input-loopback" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_PROXY ( "input-proxy" )
/* Devices which may or may not be defined for all devices. */
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_FM_RX ( "input-fm_rx" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_FM_RX_A2DP ( "input-fm_rx_a2dp" )
FANCY_ENTRY_IF_AUDIO_DEVICE_IN_IP ( "input-ip" )
#ifdef DROID_AUDIO_HAL_SECONDARY_MIC
{ AUDIO_DEVICE_IN_SECONDARY_MIC, "input-secondary_mic" },
#endif
{ 0, NULL }
};
@ -298,8 +303,11 @@ struct string_conversion string_conversion_table_audio_source_fancy[] = {
{ AUDIO_SOURCE_VOICE_RECOGNITION, "voice recognition" },
{ AUDIO_SOURCE_VOICE_COMMUNICATION, "voice communication" },
{ AUDIO_SOURCE_REMOTE_SUBMIX, "remote submix" },
{ AUDIO_SOURCE_UNPROCESSED, "unprocessed" },
{ AUDIO_SOURCE_VOICE_PERFORMANCE, "voice performance" },
/* Audio sources which may or may not be defined for all devices, */
/* Audio sources which may or may not be defined for all devices. */
FANCY_ENTRY_IF_AUDIO_SOURCE_ECHO_REFERENCE ( "echo reference" )
FANCY_ENTRY_IF_AUDIO_SOURCE_FM_TUNER ( "fm tuner" )
FANCY_ENTRY_IF_AUDIO_SOURCE_FM_RX ( "fm rx" )
FANCY_ENTRY_IF_AUDIO_SOURCE_FM_RX_A2DP ( "fm rx a2dp" )
@ -314,33 +322,34 @@ struct string_conversion string_conversion_table_output_flag[] = {
STRING_ENTRY( AUDIO_OUTPUT_FLAG_PRIMARY ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_FAST ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_DEEP_BUFFER ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_NON_BLOCKING ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_HW_AV_SYNC ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_TTS ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_RAW ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_SYNC ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_DIRECT_PCM ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_MMAP_NOIRQ ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_VOIP_RX ),
STRING_ENTRY( AUDIO_OUTPUT_FLAG_INCALL_MUSIC ),
/* Audio output flags which may or may not be defined for all devices,
* update configure.ac CC_CHECK_DROID_ENUM list if you encounter new ones. */
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_NON_BLOCKING
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_HW_AV_SYNC
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_VOIP_RX
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_INCALL_MUSIC
/* Audio output flags which may or may not be defined for all devices. */
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_TTS
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_RAW
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_SYNC
STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO
{ 0, NULL }
};
struct string_conversion string_conversion_table_input_flag[] = {
/* Audio output flags which may or may not be defined for all devices,
* update configure.ac CC_CHECK_DROID_ENUM list if you encounter new ones. */
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_NONE
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_FAST
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_HW_HOTWORD
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_RAW
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_SYNC
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_MMAP_NOIRQ
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_VOIP_TX
STRING_ENTRY( AUDIO_INPUT_FLAG_NONE ),
STRING_ENTRY( AUDIO_INPUT_FLAG_FAST ),
STRING_ENTRY( AUDIO_INPUT_FLAG_HW_HOTWORD ),
STRING_ENTRY( AUDIO_INPUT_FLAG_RAW ),
STRING_ENTRY( AUDIO_INPUT_FLAG_SYNC ),
STRING_ENTRY( AUDIO_INPUT_FLAG_MMAP_NOIRQ ),
STRING_ENTRY( AUDIO_INPUT_FLAG_VOIP_TX ),
STRING_ENTRY( AUDIO_INPUT_FLAG_HW_AV_SYNC ),
STRING_ENTRY( AUDIO_INPUT_FLAG_DIRECT ),
{ 0, NULL }
};
@ -369,13 +378,6 @@ struct string_conversion string_conversion_table_output_channels[] = {
STRING_ENTRY( AUDIO_CHANNEL_OUT_STEREO ),
STRING_ENTRY( AUDIO_CHANNEL_OUT_QUAD ),
STRING_ENTRY_IF_AUDIO_CHANNEL_OUT_SURROUND
STRING_ENTRY( AUDIO_CHANNEL_OUT_5POINT1 ),
STRING_ENTRY_IF_AUDIO_CHANNEL_OUT_5POINT1_BACK
STRING_ENTRY_IF_AUDIO_CHANNEL_OUT_5POINT1_SIDE
STRING_ENTRY(AUDIO_CHANNEL_OUT_7POINT1 ),
STRING_ENTRY(AUDIO_CHANNEL_OUT_ALL ),
{ 0, NULL }
};
@ -397,7 +399,6 @@ struct string_conversion string_conversion_table_input_channels[] = {
STRING_ENTRY( AUDIO_CHANNEL_IN_MONO ),
STRING_ENTRY( AUDIO_CHANNEL_IN_STEREO ),
STRING_ENTRY( AUDIO_CHANNEL_IN_FRONT_BACK ),
STRING_ENTRY( AUDIO_CHANNEL_IN_ALL ),
STRING_ENTRY_IF_AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO
STRING_ENTRY_IF_AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO
STRING_ENTRY_IF_AUDIO_CHANNEL_IN_VOICE_CALL_MONO
@ -409,14 +410,15 @@ struct string_conversion string_conversion_table_input_channels[] = {
struct string_conversion string_conversion_table_format[] = {
/* Omit most formats as we aren't usually interested in
* other than the pcm formats anyway. */
STRING_ENTRY( AUDIO_FORMAT_INVALID ),
STRING_ENTRY( AUDIO_FORMAT_DEFAULT ),
STRING_ENTRY( AUDIO_FORMAT_PCM ),
STRING_ENTRY( AUDIO_FORMAT_MP3 ),
STRING_ENTRY( AUDIO_FORMAT_AMR_NB ),
STRING_ENTRY( AUDIO_FORMAT_AMR_WB ),
STRING_ENTRY( AUDIO_FORMAT_AAC ),
STRING_ENTRY( AUDIO_FORMAT_HE_AAC_V1 ),
STRING_ENTRY( AUDIO_FORMAT_HE_AAC_V2 ),
STRING_ENTRY( AUDIO_FORMAT_FLAC ),
STRING_ENTRY( AUDIO_FORMAT_MP3 ),
STRING_ENTRY( AUDIO_FORMAT_OPUS ),
STRING_ENTRY( AUDIO_FORMAT_SBC ),
STRING_ENTRY( AUDIO_FORMAT_VORBIS ),
STRING_ENTRY( AUDIO_FORMAT_PCM_16_BIT ),
@ -424,12 +426,6 @@ struct string_conversion string_conversion_table_format[] = {
STRING_ENTRY( AUDIO_FORMAT_PCM_32_BIT ),
STRING_ENTRY( AUDIO_FORMAT_PCM_8_24_BIT ),
/* Audio formats which may or may not be defined for all devices,
* update configure.ac CC_CHECK_DROID_ENUM list if you encounter new ones. */
STRING_ENTRY_IF_AUDIO_FORMAT_PCM_OFFLOAD
STRING_ENTRY_IF_AUDIO_FORMAT_FLAC
STRING_ENTRY_IF_AUDIO_FORMAT_OPUS
{ 0, NULL }
};

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
#define foodroidconversionfoo
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -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);
@ -69,8 +71,6 @@ bool pa_string_convert_input_device_str_to_num(const char *str, audio_devices_t
bool pa_string_convert_flag_num_to_str(audio_output_flags_t value, const char **to_str);
bool pa_string_convert_flag_str_to_num(const char *str, audio_output_flags_t *to_value);
char *pa_list_string_output_device(audio_devices_t devices);
char *pa_list_string_input_device(audio_devices_t devices);
char *pa_list_string_flags(audio_output_flags_t flags);
/* Get default audio source associated with input device.
@ -81,29 +81,26 @@ 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);
bool pa_conversion_parse_sampling_rates(const char *fn, const unsigned ln,
const char *str, bool legacy,
const char *str,
uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]);
bool pa_conversion_parse_formats(const char *fn, const unsigned ln,
const char *str, bool legacy,
const char *str,
audio_format_t *formats);
bool pa_conversion_parse_output_channels(const char *fn, const unsigned ln,
const char *str, bool legacy,
audio_channel_mask_t *channels);
bool pa_conversion_parse_input_channels(const char *fn, const unsigned ln,
const char *str, bool legacy,
audio_channel_mask_t *channels);
int pa_conversion_parse_output_channels(const char *fn, const unsigned ln,
const char *str,
audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]);
int pa_conversion_parse_input_channels(const char *fn, const unsigned ln,
const char *str,
audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]);
bool pa_conversion_parse_output_devices(const char *fn, const unsigned ln,
char *str, bool legacy, bool must_recognize_all,
char *str, bool must_recognize_all,
audio_devices_t *devices);
bool pa_conversion_parse_input_devices(const char *fn, const unsigned ln,
char *str, bool legacy, bool must_recognize_all,
char *str, bool must_recognize_all,
audio_devices_t *devices);
bool pa_conversion_parse_output_flags(const char *fn, const unsigned ln,
const char *str, audio_output_flags_t *flags);

View file

@ -2,7 +2,7 @@
#define foodroidconfigfoo
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -30,75 +30,106 @@
#include <android-config.h>
#include <hardware/audio.h>
#include <droid/sllist.h>
#include <droid/version.h>
typedef struct pa_droid_config_audio pa_droid_config_audio;
typedef struct pa_droid_config_hw_module pa_droid_config_hw_module;
#define AUDIO_MAX_SAMPLING_RATES (32)
#define AUDIO_MAX_CHANNEL_MASKS (32)
#define AUDIO_MAX_SAMPLING_RATES (32)
typedef struct dm_config_global dm_config_global;
typedef struct dm_config_port dm_config_port;
typedef struct dm_config_route dm_config_route;
typedef struct dm_config_module dm_config_module;
typedef struct dm_config_device dm_config_device;
typedef struct dm_config_profile dm_config_profile;
typedef struct pa_droid_config_global {
uint32_t audio_hal_version;
audio_devices_t attached_output_devices;
audio_devices_t default_output_device;
audio_devices_t attached_input_devices;
} pa_droid_config_global;
typedef struct pa_droid_config_device {
const pa_droid_config_hw_module *module;
char *name;
uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; /* (uint32_t) -1 -> dynamic */
audio_channel_mask_t channel_masks; /* 0 -> dynamic */
audio_format_t formats; /* 0 -> dynamic */
audio_devices_t devices;
/* Instead of using audio_output_flags_t and audio_input_flags_t
* unify the flags as uint32_t so that we can have single struct for both
* output and input configurations.
* audio_input_flags_t was introduced in APIs 2 & 3, depending on adaptation,
* so having input flags as uint32_t is simpler from input implementation
* point of view as well. */
uint32_t flags;
pa_direction_t direction;
struct pa_droid_config_device *next;
} pa_droid_config_device;
struct pa_droid_config_hw_module {
const pa_droid_config_audio *config;
char *name;
/* If global config is not defined for module, use root global config. */
pa_droid_config_global *global_config;
pa_droid_config_device *outputs;
pa_droid_config_device *inputs;
struct pa_droid_config_hw_module *next;
struct dm_config_global {
char *key;
char *value;
};
struct pa_droid_config_audio {
pa_droid_config_global *global_config;
pa_droid_config_hw_module *hw_modules;
struct dm_config_profile {
char *name;
audio_format_t format; /* 0 -> dynamic TODO check that this is still true */
uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; /* sampling_rates[0] == 0 -> dynamic, otherwise 0 terminates list */
audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]; /* channel_masks[0] == 0 -> dynamic */
};
typedef enum dm_config_role {
DM_CONFIG_ROLE_SINK,
DM_CONFIG_ROLE_SOURCE,
} dm_config_role_t;
typedef enum dm_config_type {
DM_CONFIG_TYPE_MIX,
DM_CONFIG_TYPE_DEVICE_PORT,
DM_CONFIG_TYPE_MIX_PORT,
} dm_config_type_t;
struct dm_config_port {
dm_config_module *module;
/* common values */
dm_config_type_t port_type; /* either mixPort or devicePort */
char *name;
dm_config_role_t role;
dm_list *profiles; /* dm_config_profile* */
/* devicePort specific values */
audio_devices_t type;
char *address;
/* mixPort specific values */
uint32_t flags; /* audio_output_flag_t or audio_input_flag_t */
int max_open_count; /* 0 == not defined */
int max_active_count; /* 0 == not defined */
};
struct dm_config_route {
dm_config_type_t type;
dm_config_port *sink;
dm_list *sources; /* dm_config_port* */
};
struct dm_config_module {
dm_config_device *config;
char *name;
int version_major;
int version_minor;
dm_list *attached_devices; /* dm_config_port* owned by device_ports list below */
dm_config_port *default_output_device; /* owned by device_ports list below */
dm_list *ports; /* dm_config_port* - for convenience port types are filtered to two lists below: */
dm_list *mix_ports; /* dm_config_port* */
dm_list *device_ports; /* dm_config_port* */
dm_list *routes; /* dm_config_route* */
};
struct dm_config_device {
dm_list *global_config; /* dm_config_global* */
dm_list *modules; /* dm_config_module* */
};
/* Config parser */
pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma);
pa_droid_config_audio *pa_droid_config_dup(const pa_droid_config_audio *config);
void pa_droid_config_free(pa_droid_config_audio *config);
pa_droid_config_audio *pa_parse_droid_audio_config_legacy(const char *filename);
pa_droid_config_audio *pa_parse_droid_audio_config_xml(const char *filename);
dm_config_device *dm_config_load(pa_modargs *ma);
dm_config_device *dm_config_dup(const dm_config_device *config);
void dm_config_free(dm_config_device *config);
/* autodetect config type from filename and parse */
pa_droid_config_audio *pa_parse_droid_audio_config(const char *filename);
dm_config_device *pa_parse_droid_audio_config(const char *filename);
const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_config_audio *config, const char* module_id);
const pa_droid_config_device *pa_droid_config_find_output(const pa_droid_config_hw_module *module, const char* output_name);
const pa_droid_config_device *pa_droid_config_find_input(const pa_droid_config_hw_module *module, const char* input_name);
dm_config_module *dm_config_find_module(dm_config_device *config, const char* module_id);
dm_config_port *dm_config_find_port(dm_config_module *module, const char* name);
dm_config_port *dm_config_default_output_device(dm_config_module *module);
dm_config_port *dm_config_find_device_port(dm_config_port *port, audio_devices_t device);
char *dm_config_escape_string(const char *string);
pa_droid_config_hw_module *pa_droid_config_hw_module_new(const pa_droid_config_audio *config, const char *name);
void pa_droid_config_hw_module_free(pa_droid_config_hw_module *hw_module);
pa_droid_config_device *pa_droid_config_device_new(const pa_droid_config_hw_module *module,
pa_direction_t direction,
const char *name);
void pa_droid_config_device_free(pa_droid_config_device *device);
bool dm_config_port_equal(const dm_config_port *a, const dm_config_port *b);
dm_config_port *dm_config_find_mix_port(dm_config_module *module, const char *name);
#endif

View file

@ -2,7 +2,7 @@
#define foodroidutilfoo
/*
* Copyright (C) 2013-2018 Jolla Ltd.
* Copyright (C) 2013-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -35,10 +35,6 @@
#include <droid/version.h>
#include <droid/droid-config.h>
#if defined(QCOM_BSP) && (AUDIO_API_VERSION_MAJ >= 3)
#define DROID_AUDIO_HAL_USE_VSID
#endif
#define PROP_DROID_DEVICES "droid.devices"
#define PROP_DROID_FLAGS "droid.flags"
#define PROP_DROID_HW_MODULE "droid.hw_module"
@ -48,8 +44,12 @@
#define PROP_DROID_OUTPUT_LOW_LATENCY "droid.output.low_latency"
#define PROP_DROID_OUTPUT_MEDIA_LATENCY "droid.output.media_latency"
#define PROP_DROID_OUTPUT_OFFLOAD "droid.output.offload"
#define PROP_DROID_OUTPUT_VOIP "droid.output.voip"
#define PROP_DROID_INPUT_BUILTIN "droid.input.builtin"
#define PROP_DROID_INPUT_EXTERNAL "droid.input.external"
#define PROP_DROID_INPUT_VOIP "droid.input.voip"
#define EXT_PROP_AUDIO_SOURCE "audio.source"
#define PA_DROID_PRIMARY_DEVICE "primary"
@ -58,35 +58,27 @@ typedef struct pa_droid_stream pa_droid_stream;
typedef struct pa_droid_output_stream pa_droid_output_stream;
typedef struct pa_droid_input_stream pa_droid_input_stream;
typedef struct pa_droid_card_data pa_droid_card_data;
typedef int (*common_set_parameters_cb_t)(pa_droid_card_data *card_data, const char *str);
typedef struct pa_droid_quirks pa_droid_quirks;
typedef struct pa_droid_options pa_droid_options;
typedef enum pa_droid_hook {
PA_DROID_HOOK_INPUT_CHANNEL_MAP_CHANGED, /* Call data: pa_droid_stream */
PA_DROID_HOOK_INPUT_BUFFER_SIZE_CHANGED, /* Call data: pa_droid_stream */
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
enum pa_droid_option_type {
DM_OPTION_INPUT_ATOI,
DM_OPTION_CLOSE_INPUT,
DM_OPTION_UNLOAD_NO_CLOSE,
DM_OPTION_HW_VOLUME,
DM_OPTION_REALCALL,
DM_OPTION_UNLOAD_CALL_EXIT,
DM_OPTION_OUTPUT_FAST,
DM_OPTION_OUTPUT_DEEP_BUFFER,
DM_OPTION_AUDIO_CAL_WAIT,
DM_OPTION_SPEAKER_BEFORE_VOICE,
DM_OPTION_OUTPUT_VOIP_RX,
DM_OPTION_RECORD_VOICE_16K,
DM_OPTION_COUNT
};
struct pa_droid_quirks {
bool enabled[QUIRK_COUNT];
struct pa_droid_options {
bool enabled[DM_OPTION_COUNT];
};
struct pa_droid_hw_module {
@ -95,8 +87,8 @@ struct pa_droid_hw_module {
pa_core *core;
char *shared_name;
pa_droid_config_audio *config;
const pa_droid_config_hw_module *enabled_module;
dm_config_device *config;
dm_config_module *enabled_module;
pa_mutex *hw_mutex;
pa_mutex *output_mutex;
pa_mutex *input_mutex;
@ -106,8 +98,8 @@ struct pa_droid_hw_module {
const char *module_id;
uint32_t stream_out_id;
uint32_t stream_in_id;
uint32_t stream_id;
bool bt_sco_enabled;
pa_idxset *outputs;
pa_idxset *inputs;
@ -116,14 +108,11 @@ struct pa_droid_hw_module {
pa_atomic_t active_outputs;
pa_droid_quirks quirks;
pa_droid_options options;
/* Mode and input control */
struct _state {
audio_mode_t mode;
audio_devices_t input_device;
audio_source_t audio_source;
pa_droid_stream *active_input;
} state;
};
@ -131,8 +120,6 @@ struct pa_droid_output_stream {
struct audio_stream_out *stream;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
uint32_t flags;
uint32_t device;
};
struct pa_droid_input_stream {
@ -143,6 +130,12 @@ struct pa_droid_input_stream {
pa_channel_map channel_map;
pa_sample_spec req_sample_spec;
pa_channel_map req_channel_map;
audio_source_t audio_source;
dm_config_port *default_mix_port;
dm_config_port *input_port;
pa_droid_stream *active_input;
uint32_t flags;
uint32_t device;
bool first;
@ -152,19 +145,21 @@ struct pa_droid_stream {
PA_REFCNT_DECLARE;
pa_droid_hw_module *module;
const pa_droid_config_device *device_def;
dm_config_port *mix_port;
size_t buffer_size;
void *data;
audio_io_handle_t io_handle;
audio_patch_handle_t audio_patch;
const dm_config_port *active_device_port;
pa_droid_output_stream *output;
pa_droid_input_stream *input;
};
struct pa_droid_card_data {
void *userdata;
/* General functions */
char *module_id;
common_set_parameters_cb_t set_parameters;
};
@ -174,13 +169,13 @@ typedef struct pa_droid_profile_set pa_droid_profile_set;
typedef struct pa_droid_mapping pa_droid_mapping;
typedef struct pa_droid_port_data {
audio_devices_t device;
dm_config_port *device_port;
} pa_droid_port_data;
typedef struct pa_droid_port {
pa_droid_mapping *mapping;
audio_devices_t device;
dm_config_port *device_port;
char *name;
char *description;
unsigned priority;
@ -189,9 +184,9 @@ typedef struct pa_droid_port {
struct pa_droid_mapping {
pa_droid_profile_set *profile_set;
const pa_droid_config_device *output;
/* Use all devices in one input */
const pa_droid_config_device *inputs;
dm_config_module *module;
dm_config_port *mix_port;
dm_list *device_ports;
char *name;
char *description;
@ -210,7 +205,7 @@ struct pa_droid_mapping {
typedef struct pa_droid_profile {
pa_droid_profile_set *profile_set;
const pa_droid_config_hw_module *module;
dm_config_module *module;
char *name;
char *description;
@ -222,12 +217,13 @@ typedef struct pa_droid_profile {
* hashmaps. */
pa_idxset *output_mappings;
/* Only one input */
pa_idxset *input_mappings;
pa_droid_mapping *input_mapping;
} pa_droid_profile;
struct pa_droid_profile_set {
const pa_droid_config_audio *config;
dm_config_device *config;
pa_hashmap *all_ports;
pa_hashmap *output_mappings;
@ -240,8 +236,8 @@ struct pa_droid_profile_set {
/* 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
pa_droid_hw_module *pa_droid_hw_module_get(pa_core *core, dm_config_device *config, const char *module_id);
/* First try to get already open hw module and if none found parse config and options 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);
@ -251,11 +247,10 @@ 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_quirks *quirks, const char *quirks_def);
void pa_droid_quirk_log(pa_droid_hw_module *hw);
void pa_droid_options_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 && hw->quirks.enabled[quirk];
static inline bool pa_droid_option(pa_droid_hw_module *hw, enum pa_droid_option_type option) {
return hw && hw->options.enabled[option];
}
bool pa_droid_hw_set_mode(pa_droid_hw_module *hw_module, audio_mode_t mode);
@ -264,14 +259,11 @@ int pa_droid_hw_mic_get_mute(pa_droid_hw_module *hw_module, bool *muted);
void pa_droid_hw_mic_set_mute(pa_droid_hw_module *hw_module, bool muted);
/* Profiles */
pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module);
pa_droid_profile_set *pa_droid_profile_set_default_new(const pa_droid_config_hw_module *module);
pa_droid_profile_set *pa_droid_profile_set_default_new(dm_config_module *module);
void pa_droid_profile_set_free(pa_droid_profile_set *ps);
void pa_droid_profile_add_mapping(pa_droid_profile *p, pa_droid_mapping *am);
void pa_droid_profile_free(pa_droid_profile *p);
pa_droid_mapping *pa_droid_mapping_get(pa_droid_profile_set *ps, const pa_droid_config_device *device);
bool pa_droid_mapping_is_primary(pa_droid_mapping *am);
/* Go through idxset containing pa_droid_mapping objects and if primary output or input
* mapping is found, return pointer to that mapping. */
@ -299,8 +291,8 @@ int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters);
pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
const pa_sample_spec *spec,
const pa_channel_map *map,
const char *module_output_name,
audio_devices_t devices);
dm_config_port *mix_port,
dm_config_port *device_port);
/* Set routing to the input or output stream, with following side-effects:
* Output:
@ -311,18 +303,25 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
* Input:
* - buffer size or channel count may change
*/
int pa_droid_stream_set_route(pa_droid_stream *s, audio_devices_t device);
int pa_droid_stream_set_route(pa_droid_stream *s, dm_config_port *device_port);
/* Open input stream with currently active routing, sample_spec and channel_map
* are requests and may change when opening the stream. */
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);
const pa_channel_map *default_channel_map,
const char *mix_port_name);
/* 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);
bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
audio_devices_t device);
const pa_channel_map *requested_channel_map,
const pa_proplist *proplist);
bool pa_droid_hw_set_input_device(pa_droid_stream *s,
dm_config_port *device_port);
const pa_sample_spec *pa_droid_stream_sample_spec(pa_droid_stream *stream);
const pa_channel_map *pa_droid_stream_channel_map(pa_droid_stream *stream);
@ -351,6 +350,8 @@ void *pa_droid_stream_get_data(pa_droid_stream *s);
bool pa_sink_is_droid_sink(pa_sink *sink);
bool pa_source_is_droid_source(pa_source *source);
pa_modargs *pa_droid_modargs_new(const char *args, const char* const keys[]);
/* Misc */
size_t pa_droid_buffer_size_round_up(size_t buffer_size, size_t block_size);

View file

@ -2,7 +2,7 @@
#define foosllistfoo
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -22,6 +22,12 @@
* USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdbool.h>
#include <pulse/def.h>
#define SLLIST_APPEND(t, head, item) \
do { \
item->next = NULL; \
@ -46,4 +52,43 @@
i = NULL; \
} while (0)
typedef struct dm_list_entry dm_list_entry;
typedef struct dm_list dm_list;
struct dm_list_entry {
struct dm_list_entry *next;
struct dm_list_entry *prev;
void *data;
};
struct dm_list {
struct dm_list_entry *head;
struct dm_list_entry *tail;
ssize_t size;
};
dm_list *dm_list_new(void);
void dm_list_free(dm_list *list, pa_free_cb_t free_cb);
bool dm_list_remove(dm_list *list, dm_list_entry *entry);
void dm_list_prepend(dm_list *list, void *data);
void dm_list_push_back(dm_list *list, void *data);
dm_list_entry *dm_list_last(dm_list *list);
void *dm_list_steal_first(dm_list *list);
ssize_t dm_list_size(dm_list *list);
void *dm_list_first_data(dm_list *list, void **state);
void *dm_list_next_data(dm_list *list, void **state);
/* For example
* dm_list *list;
* void *state;
* my_data *data;
* DM_LIST_FOREACH_DATA(data, list, state) {
* do_something_with_my(data);
* }
*/
#define DM_LIST_FOREACH_DATA(i, list, state) \
for (i = dm_list_first_data(list, &(state)); state; i = dm_list_next_data(list, &(state)))
/* Access i->data */
#define DM_LIST_FOREACH(i, list) \
for (i = list->head; i; i = i->next)
#endif

View file

@ -0,0 +1,32 @@
#ifndef foodroidcommonutilsfoo
#define foodroidcommonutilsfoo
/*
* Copyright (C) 2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
* These PulseAudio Modules are 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
* version 2.1 of the License.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
void dm_replace_in_place(char **string, const char *a, const char *b);
bool dm_strcasestr(const char *haystack, const char *needle);
#endif

View file

@ -2,7 +2,7 @@
#define foodroidversionfoo
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -34,8 +34,8 @@
#error "ANDROID_VERSION_* not defined. Did you get your headers via extract-headers.sh?"
#endif
/* We currently support API version up-to 3.0 */
#define DROID_API_VERSION_SUPPORT HARDWARE_DEVICE_API_VERSION(3, 0)
/* We currently support API version up to 3.1 */
#define DROID_API_VERSION_SUPPORT HARDWARE_DEVICE_API_VERSION(3, 1)
#if AUDIO_DEVICE_API_VERSION_CURRENT > DROID_API_VERSION_SUPPORT
#warning Compiling against higher audio device API version than currently supported!
@ -48,4 +48,8 @@
#define AUDIO_API_VERSION_GET_MAJ(x) ((x >> 8) & 0xff)
#define AUDIO_API_VERSION_GET_MIN(x) (x & 0xff)
#if AUDIO_API_VERSION_MAJ < 3
#error This module only supports audio API version 3 and upwards.
#endif
#endif

172
src/common/sllist.c Normal file
View file

@ -0,0 +1,172 @@
/*
* Copyright (C) 2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
* These PulseAudio Modules are 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
* version 2.1 of the License.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <pulse/xmalloc.h>
#include <pulsecore/macro.h>
#include "droid/sllist.h"
dm_list *dm_list_new(void) {
return pa_xnew0(dm_list, 1);
}
void dm_list_free(dm_list *list, pa_free_cb_t free_cb) {
pa_assert(list);
while (list->head) {
void *data = dm_list_steal_first(list);
if (free_cb)
free_cb(data);
}
pa_xfree(list);
}
bool dm_list_remove(dm_list *list, dm_list_entry *entry) {
dm_list_entry *i;
bool removed = false;
for (i = list->head; i; i = i->next) {
if (i == entry) {
removed = true;
if (list->head == entry)
list->head = entry->next;
if (list->tail == entry)
list->tail = entry->prev;
if (entry->next)
entry->next->prev = entry->prev;
if (entry->prev)
entry->prev->next = entry->next;
pa_xfree(entry);
break;
}
}
return removed;
}
void dm_list_prepend(dm_list *list, void *data) {
dm_list_entry *entry;
pa_assert(list);
entry = pa_xnew0(dm_list_entry, 1);
entry->data = data;
if (!list->tail)
list->tail = entry;
if (list->head) {
entry->next = list->head;
list->head->prev = entry;
}
list->head = entry;
list->size++;
}
void dm_list_push_back(dm_list *list, void *data) {
dm_list_entry *entry;
pa_assert(list);
entry = pa_xnew0(dm_list_entry, 1);
entry->data = data;
if (!list->head)
list->head = entry;
if (list->tail) {
list->tail->next = entry;
entry->prev = list->tail;
}
list->tail = entry;
list->size++;
}
dm_list_entry *dm_list_last(dm_list *list) {
pa_assert(list);
return list->tail;
}
void *dm_list_steal_first(dm_list *list) {
dm_list_entry *entry;
void *data = NULL;
pa_assert(list);
if (list->head) {
data = list->head->data;
entry = list->head;
if (list->head == list->tail) {
list->head = NULL;
list->tail = NULL;
} else {
list->head->next->prev = NULL;
list->head = list->head->next;
}
pa_xfree(entry);
list->size--;
}
return data;
}
ssize_t dm_list_size(dm_list *list) {
pa_assert(list);
return list->size;
}
/* For iteration */
void *dm_list_first_data(dm_list *list, void **state) {
pa_assert(list);
pa_assert(state);
*state = list->head;
if (list->head)
return list->head->data;
else
return NULL;
}
void *dm_list_next_data(dm_list *list, void **state) {
dm_list_entry *entry;
pa_assert(list);
pa_assert(state);
entry = *state;
*state = entry->next;
if (entry->next)
return entry->next->data;
else
return NULL;
}

64
src/common/utils.c Normal file
View file

@ -0,0 +1,64 @@
/*
* Copyright (C) 2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
* These PulseAudio Modules are 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
* version 2.1 of the License.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <strings.h>
#include <pulsecore/core-util.h>
#include <pulse/xmalloc.h>
#include "droid/utils.h"
void dm_replace_in_place(char **string, const char *a, const char *b) {
char *tmp;
pa_assert(*string);
pa_assert(a);
pa_assert(b);
tmp = pa_replace(*string, a, b);
pa_xfree(*string);
*string = tmp;
}
/* Simple strcasestr replacement. */
bool dm_strcasestr(const char *haystack, const char *needle) {
size_t len_haystack, len_needle;
len_haystack = strlen(haystack);
len_needle = strlen(needle);
if (len_needle > len_haystack)
return false;
for (size_t i = 0; i < len_haystack; i++) {
if (len_needle > len_haystack - i)
return false;
if (strncasecmp(haystack + i, needle, len_needle) == 0)
return true;
}
return false;
}

View file

@ -136,19 +136,19 @@ static void notify_ports(pa_droid_extevdev *u) {
for (i=0; i < N_ELEMENTS(headphone_ports); i++) {
pa_device_port *p = pa_hashmap_get(u->card->ports, headphone_ports[i]);
pa_log_debug("headphone device port %d, %d", i, p);
pa_log_debug("headphone device port %d, %p", i, p);
if (p)
pa_device_port_set_available(p, has_headphone);
}
pa_available_t has_headset =
u->sw_microphone_insert ? PA_AVAILABLE_YES : PA_AVAILABLE_NO;
((u->sw_headphone_insert || u->sw_lineout_insert)
&& u->sw_microphone_insert) ? PA_AVAILABLE_YES : PA_AVAILABLE_NO;
pa_log_debug("has_headset: %d", has_headset);
for (i=0; i < N_ELEMENTS(headset_ports); i++) {
pa_device_port *p = pa_hashmap_get(u->card->ports, headset_ports[i]);
pa_log_debug("headset device port %d, %d", i, p);
if (p)
pa_device_port_set_available(p, has_headset);
}
@ -207,6 +207,7 @@ static void evdev_cb(pa_mainloop_api *a, pa_io_event *e, int fd,
break;
default:
pa_log_debug("Unknown switch %d", ev.code);
/* Ignore unknown switch. */
break;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2018 Jolla Ltd.
* Copyright (C) 2013-2022 Jolla Ltd.
* Copyright (C) 2010 Nokia Corporation.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
@ -61,6 +61,7 @@
#include "droid-sink.h"
#include <droid/droid-util.h>
#include <droid/conversion.h>
#include <droid/sllist.h>
struct userdata {
pa_core *core;
@ -80,18 +81,10 @@ struct userdata {
pa_usec_t buffer_time;
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;
pa_hook_slot *sink_port_changed_hook_slot;
pa_sink *primary_stream_sink;
audio_devices_t primary_devices;
audio_devices_t extra_devices;
pa_hashmap *extra_devices_map;
bool mix_route;
dm_config_port *active_device_port;
dm_config_port *override_device_port;
dm_list *extra_devices_stack;
bool use_hw_volume;
bool use_voice_volume;
@ -99,7 +92,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;
@ -137,87 +129,84 @@ 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);
pa_assert(devices);
u->primary_devices = devices;
}
static bool add_extra_devices(struct userdata *u, audio_devices_t devices) {
void *value;
uint32_t count;
bool need_update = false;
static bool add_extra_devices(struct userdata *u, audio_devices_t device) {
dm_list_entry *prev;
dm_config_port *device_port;
pa_assert(u);
pa_assert(u->extra_devices_map);
pa_assert(devices);
pa_assert(u->extra_devices_stack);
if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) {
count = PA_PTR_TO_UINT(value);
count++;
pa_hashmap_remove(u->extra_devices_map, PA_UINT_TO_PTR(devices));
pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(count));
/* added extra device already exists in hashmap, so no need to update route. */
need_update = false;
} else {
pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(1));
u->extra_devices |= devices;
need_update = true;
if (!(device_port = dm_config_find_device_port(u->active_device_port, device))) {
pa_log("Unknown device port %u", device);
return false;
}
return need_update;
prev = dm_list_last(u->extra_devices_stack);
dm_list_push_back(u->extra_devices_stack, device_port);
if (prev) {
dm_config_port *last_port = prev->data;
if (dm_config_port_equal(last_port, device_port))
return false;
}
u->override_device_port = device_port;
return true;
}
static bool remove_extra_devices(struct userdata *u, audio_devices_t devices) {
void *value;
uint32_t count;
static bool remove_extra_devices(struct userdata *u, audio_devices_t device) {
dm_config_port *device_port;
dm_list_entry *remove = NULL, *i = NULL;
bool need_update = false;
pa_assert(u);
pa_assert(u->extra_devices_map);
pa_assert(devices);
pa_assert(u->extra_devices_stack);
if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) {
pa_hashmap_remove(u->extra_devices_map, PA_UINT_TO_PTR(devices));
count = PA_PTR_TO_UINT(value);
count--;
if (count == 0) {
u->extra_devices &= ~devices;
need_update = true;
} else {
/* added extra devices still exists in hashmap, so no need to update route. */
pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(count));
need_update = false;
if (!(device_port = dm_config_find_device_port(u->active_device_port, device))) {
pa_log("Unknown device port %u", device);
return false;
}
DM_LIST_FOREACH(i, u->extra_devices_stack) {
if (dm_config_port_equal(i->data, device_port)) {
remove = i;
break;
}
}
if (remove && dm_list_last(u->extra_devices_stack) == remove)
need_update = true;
if (remove)
dm_list_remove(u->extra_devices_stack, remove);
return need_update;
}
static void clear_extra_devices(struct userdata *u) {
pa_assert(u);
pa_assert(u->extra_devices_map);
pa_assert(u->extra_devices_stack);
pa_hashmap_remove_all(u->extra_devices_map);
u->extra_devices = 0;
while (dm_list_steal_first(u->extra_devices_stack));
u->override_device_port = NULL;
}
/* Called from main context during voice calls, and from IO context during media operation. */
static void do_routing(struct userdata *u) {
audio_devices_t routing;
dm_config_port *routing = NULL;
pa_assert(u);
pa_assert(u->stream);
if (u->use_voice_volume && u->extra_devices)
if (u->use_voice_volume && u->override_device_port)
clear_extra_devices(u);
if (!u->mix_route && u->extra_devices)
routing = u->extra_devices;
if (u->override_device_port)
routing = u->override_device_port;
else
routing = u->primary_devices | u->extra_devices;
routing = u->active_device_port;
pa_droid_stream_set_route(u->stream, routing);
}
@ -248,30 +237,6 @@ static bool parse_device_list(const char *str, audio_devices_t *dst) {
return true;
}
static int thread_write_silence(struct userdata *u) {
const void *p;
ssize_t wrote;
/* Drop our rendered audio and write silence to HAL. */
pa_memblockq_drop(u->memblockq, u->buffer_size);
u->write_time = pa_rtclock_now();
/* We should be able to write everything in one go as long as memblock size
* is multiples of buffer_size. Even if we don't write whole buffer size
* here it's okay, as long as mute time isn't configured too strictly. */
p = pa_memblock_acquire_chunk(&u->silence);
wrote = pa_droid_stream_write(u->stream, p, u->silence.length);
pa_memblock_release(u->silence.memblock);
u->write_time = pa_rtclock_now() - u->write_time;
if (wrote < 0)
return -1;
return 0;
}
static int thread_write(struct userdata *u) {
pa_memchunk c;
const void *p;
@ -285,9 +250,6 @@ static int thread_write(struct userdata *u) {
u->write_time = pa_rtclock_now();
for (;;) {
if (pa_droid_quirk(u->hw_module, QUIRK_OUTPUT_MAKE_WRITABLE))
pa_memchunk_make_writable(&c, c.length);
p = pa_memblock_acquire_chunk(&c);
wrote = pa_droid_stream_write(u->stream, p, c.length);
pa_memblock_release(c.memblock);
@ -458,8 +420,6 @@ static int suspend(struct userdata *u) {
/* Called from IO context */
static int unsuspend(struct userdata *u) {
uint32_t i;
pa_assert(u);
pa_assert(u->sink);
@ -470,13 +430,6 @@ static int unsuspend(struct userdata *u) {
apply_volume(u->sink);
if (u->prewrite_silence &&
(u->primary_devices | u->extra_devices) & u->prewrite_devices &&
(u->prewrite_always || pa_droid_output_stream_any_active(u->stream) == 0)) {
for (i = 0; i < u->prewrite_silence; i++)
thread_write_silence(u);
}
pa_droid_stream_suspend(u->stream, false);
return 0;
@ -552,7 +505,7 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
data = PA_DEVICE_PORT_DATA(p);
if (!data->device) {
if (!data->device_port) {
/* If there is no device defined, just return 0 to say everything is ok.
* Then next port change can be whatever sink port, even the one enabled
* before parking. */
@ -560,9 +513,9 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
return 0;
}
pa_log_debug("Sink set port %u", data->device);
pa_log_debug("Sink set port %#010x (%s)", data->device_port->type, data->device_port->name);
set_primary_devices(u, data->device);
u->active_device_port = data->device_port;
do_routing(u);
return 0;
@ -637,9 +590,9 @@ static void update_volumes(struct userdata *u) {
u->use_hw_volume = (ret == 0);
if (u->use_hw_volume &&
#if defined(HAVE_ENUM_AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
!(u->stream->output->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
!(u->stream->mix_port->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
#endif
pa_droid_quirk(u->hw_module, QUIRK_NO_HW_VOLUME)) {
!pa_droid_option(u->hw_module, DM_OPTION_HW_VOLUME)) {
pa_log_info("Forcing software volume control with %s", u->sink->name);
u->use_hw_volume = false;
} else {
@ -653,7 +606,7 @@ static void update_volumes(struct userdata *u) {
}
}
static void set_sink_name(pa_modargs *ma, pa_sink_new_data *data, const char *module_id) {
static void set_sink_name(pa_modargs *ma, pa_sink_new_data *data, pa_droid_mapping *am, const char *name) {
const char *tmp;
pa_assert(ma);
@ -664,13 +617,14 @@ static void set_sink_name(pa_modargs *ma, pa_sink_new_data *data, const char *mo
data->namereg_fail = true;
pa_proplist_sets(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink");
} else {
char *tt;
pa_assert(module_id);
tt = pa_sprintf_malloc("sink.%s", module_id);
pa_sink_new_data_set_name(data, tt);
pa_xfree(tt);
char *full_name;
pa_assert(name);
pa_assert(am);
full_name = pa_sprintf_malloc("sink.%s", name);
pa_sink_new_data_set_name(data, full_name);
pa_xfree(full_name);
data->namereg_fail = false;
pa_proplist_setf(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink %s", module_id);
pa_proplist_setf(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink %s", am->name);
}
}
@ -714,13 +668,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;
}
@ -823,14 +772,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);
}
@ -840,15 +785,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);
}
}
@ -859,8 +800,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);
}
@ -878,7 +818,7 @@ static pa_hook_result_t sink_input_put_hook_cb(pa_core *c, pa_sink_input *sink_i
if (parse_device_list(dev_str, &devices) && devices) {
pa_log_debug("Add extra route %s (%u).", dev_str, devices);
pa_log_debug("%s: Add extra route %s (%u).", u->sink->name, dev_str, devices);
/* if this device was not routed to previously post routing change */
if (add_extra_devices(u, devices))
@ -895,9 +835,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;
@ -977,167 +914,6 @@ static pa_hook_result_t sink_proplist_changed_hook_cb(pa_core *c, pa_sink *sink,
return PA_HOOK_OK;
}
static pa_hook_result_t sink_port_changed_hook_cb(pa_core *c, pa_sink *sink, struct userdata *u) {
pa_device_port *port;
pa_assert(c);
pa_assert(sink);
pa_assert(u);
if (sink != u->primary_stream_sink)
return PA_HOOK_OK;
port = sink->active_port;
pa_log_info("Set slave sink port to %s", port->name);
pa_sink_set_port(u->sink, port->name, false);
return PA_HOOK_OK;
}
static void unset_primary_stream_sink(struct userdata *u) {
pa_assert(u);
pa_assert(u->primary_stream_sink);
pa_assert(u->sink_port_changed_hook_slot);
pa_hook_slot_free(u->sink_port_changed_hook_slot);
u->sink_port_changed_hook_slot = NULL;
u->primary_stream_sink = NULL;
}
static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *sink, struct userdata *u) {
pa_assert(c);
pa_assert(sink);
pa_assert(u);
if (sink != u->primary_stream_sink)
return PA_HOOK_OK;
pa_log_info("Primary stream sink disappeared.");
unset_primary_stream_sink(u);
return PA_HOOK_OK;
}
static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *sink, struct userdata *u) {
struct userdata *sink_u;
pa_assert(c);
pa_assert(sink);
pa_assert(u);
if (!pa_sink_is_droid_sink(sink))
return PA_HOOK_OK;
sink_u = sink->userdata;
if (!pa_droid_stream_is_primary(sink_u->stream))
return PA_HOOK_OK;
u->primary_stream_sink = sink;
pa_assert(!u->sink_port_changed_hook_slot);
u->sink_port_changed_hook_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], PA_HOOK_NORMAL,
(pa_hook_cb_t) sink_port_changed_hook_cb, u);
pa_log_info("Primary stream sink setup for slave.");
sink_port_changed_hook_cb(c, sink, u);
return PA_HOOK_OK;
}
static void setup_track_primary(struct userdata *u) {
pa_sink *sink;
struct userdata *sink_u;
uint32_t idx;
pa_assert(u);
u->sink_put_hook_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL,
(pa_hook_cb_t) sink_put_hook_cb, u);
u->sink_unlink_hook_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_NORMAL,
(pa_hook_cb_t) sink_unlink_hook_cb, u);
PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
if (pa_sink_is_droid_sink(sink)) {
sink_u = sink->userdata;
if (pa_droid_stream_is_primary(sink_u->stream)) {
sink_put_hook_cb(u->core, sink, u);
break;
}
}
}
}
static bool parse_prewrite_on_resume(struct userdata *u, const char *prewrite_resume, const char *name) {
const char *state = NULL;
char *entry = NULL;
char *devices, *stream, *value;
uint32_t devices_len, devices_index, value_index, entry_len;
uint32_t b;
pa_assert(u);
pa_assert(prewrite_resume);
pa_assert(name);
/* 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, "=");
if (devices_index == 0 || devices_index >= entry_len - 1)
goto error;
entry[devices_index] = '\0';
devices = entry + devices_index + 1;
stream = entry;
devices_len = strlen(devices);
value_index = strcspn(devices, ":");
if (value_index == 0 || value_index >= devices_len - 1)
goto error;
devices[value_index] = '\0';
value = devices + value_index + 1;
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%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;
}
pa_xfree(entry);
}
return true;
error:
pa_xfree(entry);
return false;
}
pa_sink *pa_droid_sink_new(pa_module *m,
pa_modargs *ma,
const char *driver,
@ -1147,24 +923,22 @@ pa_sink *pa_droid_sink_new(pa_module *m,
pa_card *card) {
struct userdata *u = NULL;
const pa_droid_config_device *output = NULL;
dm_config_port *mix_port = NULL;
dm_config_port *device_port = NULL;
bool deferred_volume = false;
bool voice_virtual_stream = false;
char *thread_name = NULL;
pa_sink_new_data data;
const char *module_id = NULL;
const char *tmp;
char *list = NULL;
uint32_t alternate_sample_rate;
const char *format;
audio_devices_t dev_out;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
bool namereg_fail = false;
pa_usec_t latency;
uint32_t sink_buffer = 0;
const char *prewrite_resume = NULL;
bool mix_route = false;
char *sink_name = NULL;
pa_assert(m);
pa_assert(ma);
@ -1179,8 +953,8 @@ pa_sink *pa_droid_sink_new(pa_module *m,
}
if (card && am) {
output = am->output;
module_id = output->module->name;
mix_port = am->mix_port;
module_id = mix_port->name;
} else
module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
@ -1189,18 +963,22 @@ pa_sink *pa_droid_sink_new(pa_module *m,
/* First parse both sample spec and channel map, then see if sink_* override some
* of the values. */
if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) {
pa_log("Failed to parse sink sample specification and channel map.");
if (pa_modargs_get_sample_spec(ma, &sample_spec) < 0) {
pa_log("Failed to parse sink sample specification.");
goto fail;
}
if (pa_modargs_get_value(ma, "sink_channel_map", NULL)) {
if (pa_modargs_get_channel_map(ma, "sink_channel_map", &channel_map) < 0) {
pa_log("Failed to parse sink channel map.");
goto fail;
}
if (pa_modargs_get_channel_map(ma, NULL, &channel_map) < 0) {
pa_log("Failed to parse sink channel map.");
goto fail;
}
sample_spec.channels = channel_map.channels;
/* Possible overrides. */
if (pa_modargs_get_channel_map(ma, "sink_channel_map", &channel_map) < 0) {
pa_log("Failed to parse sink channel map.");
goto fail;
}
if ((format = pa_modargs_get_value(ma, "sink_format", NULL))) {
@ -1210,6 +988,11 @@ pa_sink *pa_droid_sink_new(pa_module *m,
}
}
if (pa_modargs_get_value_u32(ma, "rate", &sample_spec.rate) < 0) {
pa_log("Failed to parse sink samplerate");
goto fail;
}
if (pa_modargs_get_value_u32(ma, "sink_rate", &sample_spec.rate) < 0) {
pa_log("Failed to parse sink samplerate");
goto fail;
@ -1231,11 +1014,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "sink_mix_route", &mix_route) < 0) {
pa_log("Failed to parse sink_mix_route, expects boolean argument.");
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "voice_virtual_stream", &voice_virtual_stream) < 0) {
pa_log("Failed to parse voice_virtual_stream. Needs to be a boolean argument.");
goto fail;
@ -1253,8 +1031,7 @@ pa_sink *pa_droid_sink_new(pa_module *m,
u->voice_virtual_stream = voice_virtual_stream;
u->voice_property_key = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_key", DEFAULT_VOICE_CONTROL_PROPERTY_KEY));
u->voice_property_value = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_value", DEFAULT_VOICE_CONTROL_PROPERTY_VALUE));
u->extra_devices_map = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
u->mix_route = mix_route;
u->extra_devices_stack = dm_list_new();
if (card_data) {
u->card_data = card_data;
@ -1268,34 +1045,25 @@ pa_sink *pa_droid_sink_new(pa_module *m,
goto fail;
}
if (!(output = pa_droid_config_find_output(u->hw_module->enabled_module, output_name))) {
pa_log("Could not find output %s from module %s.", output_name, u->hw_module->enabled_module->name);
goto fail;
}
/* Sink wasn't created from inside card module, so we'll need to open
* hw module ourself. */
if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id)))
goto fail;
if (!(mix_port = dm_config_find_port(u->hw_module->enabled_module, output_name)) ||
mix_port->port_type != DM_CONFIG_TYPE_MIX_PORT) {
pa_log("Could not find output %s from module %s.", output_name, u->hw_module->enabled_module->name);
goto fail;
}
}
/* Default routing */
dev_out = output->module->global_config ? output->module->global_config->default_output_device
: u->hw_module->config->global_config->default_output_device;
pa_assert(mix_port);
if ((tmp = pa_modargs_get_value(ma, "output_devices", NULL))) {
audio_devices_t tmp_dev;
/* Start with default output device */
device_port = dm_config_default_output_device(mix_port->module);
if (parse_device_list(tmp, &tmp_dev) && tmp_dev)
dev_out = tmp_dev;
pa_log_debug("Set initial devices %s", tmp);
}
flags = output->flags;
u->stream = pa_droid_open_output_stream(u->hw_module, &sample_spec, &channel_map, output->name, dev_out);
u->stream = pa_droid_open_output_stream(u->hw_module, &sample_spec, &channel_map, mix_port, device_port);
if (!u->stream) {
pa_log("Failed to open output stream.");
@ -1309,13 +1077,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
} else
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)) {
pa_log("Failed to parse prewrite_on_resume (%s)", prewrite_resume);
goto fail;
}
}
u->buffer_time = pa_bytes_to_usec(u->buffer_size, &u->stream->output->sample_spec);
u->write_threshold = u->buffer_time - u->buffer_time / 6;
@ -1327,7 +1088,8 @@ pa_sink *pa_droid_sink_new(pa_module *m,
data.module = m;
data.card = card;
set_sink_name(ma, &data, output->name);
sink_name = dm_config_escape_string(module_id);
set_sink_name(ma, &data, am, sink_name);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, PROP_DROID_API_STRING);
@ -1347,15 +1109,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
pa_sink_new_data_set_channel_map(&data, &u->stream->output->channel_map);
pa_sink_new_data_set_alternate_sample_rate(&data, alternate_sample_rate);
/*
if (!(list = pa_list_string_output_device(dev_out))) {
pa_log("Couldn't format device list string.");
goto fail;
}
pa_proplist_sets(data.proplist, PROP_DROID_DEVICES, list);
pa_xfree(list);
*/
if (flags) {
if (!(list = pa_list_string_flags(flags))) {
pa_log("Couldn't format flag list string.");
@ -1383,15 +1136,13 @@ pa_sink *pa_droid_sink_new(pa_module *m,
u->sink->parent.process_msg = sink_process_msg;
u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
u->sink->set_port = sink_set_port_cb;
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
/* Rewind internal memblockq */
pa_sink_set_max_rewind(u->sink, 0);
thread_name = pa_sprintf_malloc("droid-sink-%s", output->name);
thread_name = pa_sprintf_malloc("droid-sink-%s", sink_name);
if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;
@ -1408,28 +1159,34 @@ pa_sink *pa_droid_sink_new(pa_module *m,
if (u->sink->active_port)
sink_set_port_cb(u->sink, u->sink->active_port);
/* Hooks to track appearance and disappearance of sink-inputs. */
/* Hook a little bit earlier and later than module-role-ducking. */
u->sink_input_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE+10,
(pa_hook_cb_t) sink_input_put_hook_cb, u);
u->sink_input_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_EARLY-10,
(pa_hook_cb_t) sink_input_unlink_hook_cb, u);
u->sink_proplist_changed_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_EARLY,
(pa_hook_cb_t) sink_proplist_changed_hook_cb, u);
if (pa_droid_stream_is_primary(u->stream)) {
/* Hooks to track appearance and disappearance of sink-inputs.
* Hook a little bit earlier and later than module-role-ducking.
* Used only in primary sink. */
u->sink_input_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE+10,
(pa_hook_cb_t) sink_input_put_hook_cb, u);
u->sink_input_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_EARLY-10,
(pa_hook_cb_t) sink_input_unlink_hook_cb, u);
u->sink_proplist_changed_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_EARLY,
(pa_hook_cb_t) sink_proplist_changed_hook_cb, u);
/* Port changes are done only in primary sink. */
u->sink->set_port = sink_set_port_cb;
}
update_volumes(u);
if (!pa_droid_stream_is_primary(u->stream))
setup_track_primary(u);
pa_droid_stream_suspend(u->stream, false);
pa_droid_stream_set_data(u->stream, u->sink);
pa_sink_put(u->sink);
pa_xfree(sink_name);
return u->sink;
fail:
pa_xfree(thread_name);
pa_xfree(sink_name);
if (u)
userdata_free(u);
@ -1456,18 +1213,6 @@ static void parameter_free(droid_parameter_mapping *m) {
static void userdata_free(struct userdata *u) {
if (u->primary_stream_sink)
unset_primary_stream_sink(u);
if (u->sink_put_hook_slot)
pa_hook_slot_free(u->sink_put_hook_slot);
if (u->sink_unlink_hook_slot)
pa_hook_slot_free(u->sink_unlink_hook_slot);
if (u->sink_port_changed_hook_slot)
pa_hook_slot_free(u->sink_port_changed_hook_slot);
if (u->sink)
pa_sink_unlink(u->sink);
@ -1513,8 +1258,8 @@ static void userdata_free(struct userdata *u) {
if (u->voice_property_value)
pa_xfree(u->voice_property_value);
if (u->extra_devices_map)
pa_hashmap_free(u->extra_devices_map);
if (u->extra_devices_stack)
dm_list_free(u->extra_devices_stack, NULL);
pa_xfree(u);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2018 Jolla Ltd.
* Copyright (C) 2013-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -69,7 +69,6 @@ struct userdata {
pa_rtpoll *rtpoll;
pa_memchunk memchunk;
audio_devices_t primary_devices;
size_t source_buffer_size;
size_t buffer_size;
@ -94,60 +93,14 @@ 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,
audio_devices_t update_device);
const pa_proplist *proplist,
dm_config_port *update_device_port);
/* Our droid source may be left in a state of not having an input stream
* if reconfiguration fails and fallback to previously active values fails
* as well. In this case just avoid using the stream but don't die. */
#define assert_stream(x, action) if (!x) do { pa_log_warn("Assert " #x " failed."); action; } while(0)
static int do_routing(struct userdata *u, audio_devices_t devices) {
int ret;
audio_devices_t old_device;
pa_assert(u);
assert_stream(u->stream, return 0);
if (u->primary_devices == devices)
pa_log_debug("Refresh active device routing.");
old_device = u->primary_devices;
u->primary_devices = devices;
ret = pa_droid_stream_set_route(u->stream, devices);
if (ret < 0)
u->primary_devices = old_device;
return ret;
}
static bool parse_device_list(const char *str, audio_devices_t *dst) {
pa_assert(str);
pa_assert(dst);
char *dev;
const char *state = NULL;
*dst = 0;
while ((dev = pa_split(str, "|", &state))) {
audio_devices_t d;
if (!pa_string_convert_input_device_str_to_num(dev, &d)) {
pa_log_warn("Unknown device %s", dev);
pa_xfree(dev);
return false;
}
*dst |= d;
pa_xfree(dev);
}
return true;
}
static int thread_read(struct userdata *u) {
void *p;
ssize_t readd;
@ -334,7 +287,7 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) {
data = PA_DEVICE_PORT_DATA(p);
if (!data->device) {
if (!data->device_port) {
/* If there is no device defined, just return 0 to say everything is ok.
* Then next port change can be whatever source port, even the one enabled
* before parking. */
@ -342,12 +295,12 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) {
return 0;
}
pa_log_debug("Source set port %#010x", data->device);
pa_log_debug("Source set port %#010x (%s)", data->device_port->type, data->device_port->name);
if (!PA_SOURCE_IS_OPENED(u->source->state))
do_routing(u, data->device);
pa_droid_stream_set_route(u->stream, data->device_port);
else
source_reconfigure(u, NULL, NULL, data->device);
source_reconfigure(u, NULL, NULL, NULL, data->device_port);
return 0;
}
@ -429,7 +382,8 @@ 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,
audio_devices_t update_device) {
const pa_proplist *proplist,
dm_config_port *update_device_port) {
pa_channel_map old_channel_map;
pa_sample_spec old_sample_spec;
pa_channel_map new_channel_map;
@ -450,10 +404,10 @@ static void source_reconfigure(struct userdata *u,
new_channel_map = reconfigure_channel_map ? *reconfigure_channel_map : old_channel_map;
new_sample_spec = reconfigure_sample_spec ? *reconfigure_sample_spec : old_sample_spec;
if (update_device)
do_routing(u, update_device);
if (update_device_port)
pa_droid_stream_set_route(u->stream, update_device_port);
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 +441,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 +456,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),
0);
new_data->proplist,
NULL);
} 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, NULL);
return PA_HOOK_OK;
}
@ -523,12 +480,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, NULL);
}
}
@ -542,7 +499,6 @@ static pa_hook_result_t source_output_unlink_post_hook_callback(void *hook_data,
pa_source *pa_droid_source_new(pa_module *m,
pa_modargs *ma,
const char *driver,
audio_devices_t device,
pa_droid_card_data *card_data,
pa_droid_mapping *am,
pa_card *card) {
@ -551,9 +507,7 @@ pa_source *pa_droid_source_new(pa_module *m,
char *thread_name = NULL;
pa_source_new_data data;
const char *module_id = NULL;
const char *tmp;
uint32_t alternate_sample_rate;
audio_devices_t dev_in;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
const char *format;
@ -568,7 +522,7 @@ pa_source *pa_droid_source_new(pa_module *m,
/* When running under card use hw module name for source by default. */
if (am)
module_id = am->inputs->module->name;
module_id = am->mix_port->name;
else
module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
@ -639,28 +593,7 @@ pa_source *pa_droid_source_new(pa_module *m,
goto fail;
}
/* Default routing */
if (device)
dev_in = device;
else {
/* FIXME So while setting routing through stream with HALv2 API fails, creation of stream
* requires HALv2 style device to work properly. So until that oddity is resolved we always
* set AUDIO_DEVICE_IN_BUILTIN_MIC as initial device here. */
pa_log_info("FIXME: Setting AUDIO_DEVICE_IN_BUILTIN_MIC as initial device.");
pa_assert_se(pa_string_convert_input_device_str_to_num("AUDIO_DEVICE_IN_BUILTIN_MIC", &dev_in));
if ((tmp = pa_modargs_get_value(ma, "input_devices", NULL))) {
audio_devices_t tmp_dev;
if (parse_device_list(tmp, &tmp_dev) && tmp_dev)
dev_in = tmp_dev;
pa_log_debug("Set initial devices %s", tmp);
}
}
pa_droid_hw_set_input_device(u->hw_module, dev_in);
u->stream = pa_droid_open_input_stream(u->hw_module, &sample_spec, &channel_map);
u->stream = pa_droid_open_input_stream(u->hw_module, &sample_spec, &channel_map, am->mix_port->name);
if (!u->stream) {
pa_log("Failed to open input stream.");

View file

@ -2,7 +2,7 @@
#define foodroidsourcefoo
/*
* Copyright (C) 2013-2018 Jolla Ltd.
* Copyright (C) 2013-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -43,11 +43,9 @@
#include <droid/droid-util.h>
/* If device is non-zero, it will override whatever is set in modargs for input device. */
pa_source *pa_droid_source_new(pa_module *m,
pa_modargs *ma,
const char *driver,
audio_devices_t device,
pa_droid_card_data *card_data,
pa_droid_mapping *am,
pa_card *card);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2018 Jolla Ltd.
* Copyright (C) 2013-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -61,6 +61,7 @@
#include <droid/droid-util.h>
#include <droid/sllist.h>
#include <droid/utils.h>
#include "droid-sink.h"
#include "droid-source.h"
#include "droid-extcon.h"
@ -75,7 +76,6 @@ PA_MODULE_USAGE(
"source_name=<name for the source> "
"namereg_fail=<when false attempt to synthesise new names if they are already taken> "
"rate=<sample rate> "
"output_flags=<flags for sink> "
"module_id=<which droid hw module to load, default primary> "
"voice_source_routing=<always true, parameter left for compatibility> "
"deferred_volume=<synchronize software and hardware volume changes to avoid momentary jumps?> "
@ -83,9 +83,7 @@ PA_MODULE_USAGE(
"voice_property_key=<proplist key searched for sink-input that should control voice call volume> "
"voice_property_value=<proplist value for the key for voice control sink-input> "
"voice_virtual_stream=<true/false> create virtual stream for voice call volume control (default false)"
"default_profile=<boolean. create default profile for primary module or not. defaults to true> "
"merge_inputs=<unused, always true> "
"quirks=<comma separated list of quirks to enable/disable>"
"options=<comma separated list of options to enable/disable>"
);
static const char* const valid_modargs[] = {
@ -100,27 +98,19 @@ static const char* const valid_modargs[] = {
"sink_rate",
"sink_format",
"sink_channel_map",
"sink_mix_route",
"source_rate",
"source_format",
"source_channel_map",
"output_flags",
"module_id",
"voice_source_routing",
"sink_buffer",
"source_buffer",
"deferred_volume",
"mute_routing_before",
"mute_routing_after",
"prewrite_on_resume",
"config",
"voice_property_key",
"voice_property_value",
"voice_virtual_stream",
"default_profile",
"combine",
"merge_inputs",
"quirks",
/* DM_OPTIONS */
NULL,
};
@ -178,7 +168,7 @@ struct profile_data {
struct virtual_profile virtual;
};
#ifdef DROID_AUDIO_HAL_USE_VSID
#ifdef DROID_AUDIO_HAL_DEBUG_VSID
/* From hal/voice_extn/voice_extn.c */
#define AUDIO_PARAMETER_KEY_VSID "vsid"
@ -222,7 +212,7 @@ static bool voicecall_vowlan_vsid_profile_event_cb(struct userdata *u, pa_droid_
static bool voicecall_voicemmode1_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling);
static bool voicecall_voicemmode2_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling);
#endif /* DROID_AUDIO_HAL_USE_VSID */
#endif /* DROID_AUDIO_HAL_DEBUG_VSID */
static void add_disabled_profile(pa_hashmap *profiles) {
pa_card_profile *cp;
@ -275,16 +265,6 @@ static pa_card_profile* add_virtual_profile(struct userdata *u, const char *name
return cp;
}
static int set_parameters_cb(pa_droid_card_data *card_data, const char *str) {
struct userdata *u;
pa_assert(card_data);
pa_assert_se((u = card_data->userdata));
pa_assert(str);
return pa_droid_set_parameters(u->hw_module, str);
}
static void set_card_name(pa_modargs *ma, pa_card_new_data *data, const char *module_id) {
const char *tmp;
char *name;
@ -306,16 +286,57 @@ static void set_card_name(pa_modargs *ma, pa_card_new_data *data, const char *mo
}
static bool output_enabled(struct userdata *u, pa_droid_mapping *am) {
bool enabled = false;
pa_assert(u);
pa_assert(am);
if (!pa_droid_quirk(u->hw_module, QUIRK_OUTPUT_FAST) && am->output->flags & AUDIO_OUTPUT_FLAG_FAST)
return false;
if (!pa_droid_quirk(u->hw_module, QUIRK_OUTPUT_DEEP_BUFFER) && am->output->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER)
return false;
if (am->mix_port->flags & AUDIO_OUTPUT_FLAG_PRIMARY)
enabled = true;
return true;
else if (am->mix_port->flags & AUDIO_OUTPUT_FLAG_RAW)
enabled = false;
else if (pa_droid_option(u->hw_module, DM_OPTION_OUTPUT_FAST) && am->mix_port->flags & AUDIO_OUTPUT_FLAG_FAST)
enabled = true;
else if (pa_droid_option(u->hw_module, DM_OPTION_OUTPUT_DEEP_BUFFER) && am->mix_port->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER)
enabled = true;
pa_log_debug("Output mix port \"%s\" %s", am->name, enabled ? "enabled" : "disabled");
return enabled;
}
static bool input_enabled(struct userdata *u, pa_droid_mapping *am) {
bool enabled = false;
pa_assert(u);
pa_assert(am);
/* Look for primary mix port as the one used for creating droid-source. */
if (dm_strcasestr(am->name, "primary"))
enabled = true;
pa_log_debug("Input mix port \"%s\" %s", am->name, enabled ? "enabled" : "disabled");
return enabled;
}
static uint32_t max_channels_for_mix_port(dm_config_port *mix_port, uint32_t previous_max_channels) {
uint32_t max_channels = 0;
dm_config_profile *profile;
void *state;
DM_LIST_FOREACH_DATA(profile, mix_port->profiles, state) {
for (int i = 0; profile->channel_masks[i]; i++) {
max_channels = audio_channel_count_from_out_mask(profile->channel_masks[i]) > max_channels
? audio_channel_count_from_out_mask(profile->channel_masks[i]) : max_channels;
}
}
return max_channels > previous_max_channels ? max_channels : previous_max_channels;
}
static void add_profile(struct userdata *u, pa_hashmap *h, pa_hashmap *ports, pa_droid_profile *ap) {
@ -336,26 +357,23 @@ static void add_profile(struct userdata *u, pa_hashmap *h, pa_hashmap *ports, pa
cp->available = PA_AVAILABLE_YES;
cp->priority = ap->priority;
/* Output mappings */
max_channels = 0;
PA_IDXSET_FOREACH(am, ap->output_mappings, idx) {
if (!output_enabled(u, am))
continue;
cp->n_sinks++;
pa_droid_add_card_ports(cp, ports, am, u->core);
max_channels = popcount(am->output->channel_masks) > max_channels
? popcount(am->output->channel_masks) : max_channels;
max_channels = max_channels_for_mix_port(am->mix_port, max_channels);
}
cp->max_sink_channels = max_channels;
/* Input mappings */
max_channels = 0;
if ((am = ap->input_mapping)) {
const pa_droid_config_device *input;
PA_IDXSET_FOREACH(am, ap->input_mappings, idx) {
cp->n_sources++;
pa_droid_add_card_ports(cp, ports, am, u->core);
SLLIST_FOREACH(input, am->inputs)
max_channels = popcount(input->channel_masks) > max_channels
? popcount(input->channel_masks) : max_channels;
max_channels = max_channels_for_mix_port(am->mix_port, max_channels);
}
cp->max_source_channels = max_channels;
@ -401,8 +419,13 @@ static void init_profile(struct userdata *u) {
}
}
if (d->droid_profile && (am = d->droid_profile->input_mapping)) {
am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
if (d->droid_profile && pa_idxset_size(d->droid_profile->input_mappings) > 0) {
PA_IDXSET_FOREACH(am, d->droid_profile->input_mappings, idx) {
if (!input_enabled(u, am))
continue;
am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card);
}
}
}
@ -454,19 +477,51 @@ static bool voicecall_profile_event_cb(struct userdata *u, pa_droid_profile *p,
if (enabling) {
pa_droid_sink_set_voice_control(am_output->sink, true);
if (pa_droid_quirk(u->hw_module, QUIRK_REALCALL))
if (pa_droid_option(u->hw_module, DM_OPTION_REALCALL))
pa_droid_set_parameters(u->hw_module, VENDOR_EXT_REALCALL_ON);
} else {
pa_droid_sink_set_voice_control(am_output->sink, false);
if (pa_droid_quirk(u->hw_module, QUIRK_REALCALL))
if (pa_droid_option(u->hw_module, DM_OPTION_REALCALL))
pa_droid_set_parameters(u->hw_module, VENDOR_EXT_REALCALL_OFF);
}
return true;
}
#ifdef DROID_AUDIO_HAL_USE_VSID
static bool in_communication_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) {
pa_droid_profile *dp;
pa_assert(u);
pa_assert(u->real_profile);
dp = card_get_droid_profile(u->real_profile);
if (pa_idxset_size(dp->output_mappings) > 0) {
pa_droid_mapping *am;
uint32_t idx;
PA_IDXSET_FOREACH(am, dp->output_mappings, idx) {
if (am->mix_port->flags & AUDIO_OUTPUT_FLAG_VOIP_RX) {
if (enabling && !am->sink) {
pa_log_info("in communication: enable VOIP sink");
am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card);
} else if (!enabling && am->sink) {
/* Don't rescue sink-inputs. */
pa_log_info("in communication: disable VOIP sink");
pa_droid_sink_free(am->sink);
am->sink = NULL;
}
break;
}
}
}
return true;
}
#ifdef DROID_AUDIO_HAL_DEBUG_VSID
static bool voicecall_vsid(struct userdata *u, pa_droid_profile *p, uint32_t vsid, bool enabling)
{
char *setparam;
@ -515,7 +570,7 @@ static bool voicecall_voicemmode2_vsid_profile_event_cb(struct userdata *u, pa_d
{
return voicecall_vsid(u, p, VOICEMMODE2_VSID, enabling);
}
#endif /* DROID_AUDIO_HAL_USE_VSID */
#endif /* DROID_AUDIO_HAL_DEBUG_VSID */
static void virtual_event(struct userdata *u, struct profile_data *profile, bool enabling) {
pa_assert(u);
@ -698,13 +753,18 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
}
}
if (next->droid_profile && (am = next->droid_profile->input_mapping)) {
if (!am->source)
am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
if (next->droid_profile && pa_idxset_size(next->droid_profile->input_mappings) > 0) {
PA_IDXSET_FOREACH(am, next->droid_profile->input_mappings, idx) {
if (!input_enabled(u, am))
continue;
if (source_outputs && am->source) {
pa_source_move_all_finish(am->source, source_outputs, false);
source_outputs = NULL;
if (!am->source)
am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card);
if (source_outputs && am->source) {
pa_source_move_all_finish(am->source, source_outputs, false);
source_outputs = NULL;
}
}
}
@ -731,23 +791,17 @@ int pa__init(pa_module *m) {
pa_card_new_data data;
const char *module_id;
bool namereg_fail = false;
bool default_profile = true;
pa_card_profile *voicecall = NULL;
pa_assert(m);
pa_log_info("Create new droid-card");
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
if (!(ma = pa_droid_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "default_profile", &default_profile) < 0) {
pa_log("Failed to parse default_profile argument. Expects boolean value");
goto fail;
}
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
m->userdata = u;
@ -757,16 +811,12 @@ int pa__init(pa_module *m) {
if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id)))
goto fail;
pa_droid_quirk_log(u->hw_module);
pa_droid_options_log(u->hw_module);
u->card_data.set_parameters = set_parameters_cb;
u->card_data.module_id = pa_xstrdup(module_id);
u->card_data.userdata = u;
if (default_profile)
u->profile_set = pa_droid_profile_set_default_new(u->hw_module->enabled_module);
else
u->profile_set = pa_droid_profile_set_new(u->hw_module->enabled_module);
u->profile_set = pa_droid_profile_set_default_new(u->hw_module->enabled_module);
pa_card_new_data_init(&data);
data.driver = __FILE__;
@ -802,19 +852,18 @@ int pa__init(pa_module *m) {
AUDIO_MODE_IN_CALL, NULL,
PA_AVAILABLE_YES, voicecall, data.profiles);
add_virtual_profile(u, COMMUNICATION_PROFILE_NAME, COMMUNICATION_PROFILE_DESC,
AUDIO_MODE_IN_COMMUNICATION, NULL,
AUDIO_MODE_IN_COMMUNICATION, in_communication_profile_event_cb,
PA_AVAILABLE_YES, NULL, data.profiles);
add_virtual_profile(u, RINGTONE_PROFILE_NAME, RINGTONE_PROFILE_DESC,
AUDIO_MODE_RINGTONE, NULL,
PA_AVAILABLE_YES, NULL, data.profiles);
#ifdef DROID_AUDIO_HAL_USE_VSID
#ifdef DROID_AUDIO_HAL_DEBUG_VSID
add_virtual_profile(u, VOICE_SESSION_VOICE1_PROFILE_NAME, VOICE_SESSION_VOICE1_PROFILE_DESC,
AUDIO_MODE_IN_CALL, voicecall_voice1_vsid_profile_event_cb,
PA_AVAILABLE_YES, voicecall, data.profiles);
add_virtual_profile(u, VOICE_SESSION_VOICE2_PROFILE_NAME, VOICE_SESSION_VOICE2_PROFILE_DESC,
AUDIO_MODE_IN_CALL, voicecall_voice2_vsid_profile_event_cb,
PA_AVAILABLE_YES, voicecall, data.profiles);
/* TODO: Probably enabled state needs to be determined dynamically for VOLTE and friends. */
add_virtual_profile(u, VOICE_SESSION_VOLTE_PROFILE_NAME, VOICE_SESSION_VOLTE_PROFILE_DESC,
AUDIO_MODE_IN_CALL, voicecall_volte_vsid_profile_event_cb,
PA_AVAILABLE_YES, voicecall, data.profiles);
@ -830,7 +879,7 @@ int pa__init(pa_module *m) {
add_virtual_profile(u, VOICE_SESSION_VOICEMMODE2_PROFILE_NAME, VOICE_SESSION_VOICEMMODE2_PROFILE_DESC,
AUDIO_MODE_IN_CALL, voicecall_voicemmode2_vsid_profile_event_cb,
PA_AVAILABLE_YES, voicecall, data.profiles);
#endif /* DROID_AUDIO_HAL_USE_VSID */
#endif /* DROID_AUDIO_HAL_DEBUG_VSID */
add_disabled_profile(data.profiles);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2018 Jolla Ltd.
* Copyright (C) 2013-2022 Jolla Ltd.
*
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
*
@ -83,7 +83,7 @@ int pa__init(pa_module *m) {
goto fail;
}
if (!(m->userdata = pa_droid_source_new(m, ma, __FILE__, (audio_devices_t) 0, NULL, NULL, NULL)))
if (!(m->userdata = pa_droid_source_new(m, ma, __FILE__, NULL, NULL, NULL)))
goto fail;
pa_modargs_free(ma);