mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-28 07:20:05 +01:00
Compare commits
248 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f2a5d99b1 | ||
|
|
29bbd79830 | ||
|
|
3738c3fc38 | ||
|
|
165bd7b219 | ||
|
|
e15e50c5ee | ||
|
|
f468529084 | ||
|
|
385161b12a | ||
|
|
8c7890eb52 | ||
|
|
e2262617aa | ||
|
|
12b2e5d67c | ||
|
|
ee42b18226 | ||
|
|
d89d1668dc | ||
|
|
9a48bbaa36 | ||
|
|
04cf29f7cd | ||
|
|
bb564d5eb6 | ||
|
|
f03021edd1 | ||
|
|
c7ebc66e64 | ||
|
|
a6f8e209ac | ||
|
|
6f1938d501 | ||
|
|
bb1ef8ea5e | ||
|
|
b22e442b10 | ||
|
|
43bf1b8f7c | ||
|
|
ba8c6154a0 | ||
|
|
548f26882f | ||
|
|
63abd4e71c | ||
|
|
c2ada3175e | ||
|
|
8153efc6ed | ||
|
|
40aa6fbb64 | ||
|
|
34122b4bf3 | ||
|
|
d9fa0629f6 | ||
|
|
25a6fdcdb1 | ||
|
|
7aa8d8d628 | ||
|
|
8e6945c496 | ||
|
|
e1392cec0e | ||
|
|
13def13f01 | ||
|
|
ea653a52e3 | ||
|
|
6054c1a12b | ||
|
|
b43d915e71 | ||
|
|
4f8f7980f0 | ||
|
|
a6d7e98db3 | ||
|
|
ad43eba25c | ||
|
|
a97abf10ab | ||
|
|
86168ab1e2 | ||
|
|
2f83c5dab5 | ||
|
|
63a37e4947 | ||
|
|
a1a33141d7 | ||
|
|
82fe584f51 | ||
|
|
b90bd2c528 | ||
|
|
43448f147c | ||
|
|
1b39e7836d | ||
|
|
034e8683c8 | ||
|
|
2942bae034 | ||
|
|
c623886625 | ||
|
|
f65d5654d3 | ||
|
|
198f4a92f5 | ||
|
|
57e589f2e1 | ||
|
|
e30ee9c846 | ||
|
|
b68698a086 | ||
|
|
0d7cb9b39f | ||
|
|
c81ee31c3b | ||
|
|
a172bf0f55 | ||
|
|
4f39329ca9 | ||
|
|
4152c5d292 | ||
|
|
52f2137397 | ||
|
|
6619aba582 | ||
|
|
1aacf8d15a | ||
|
|
93b59609a8 | ||
|
|
e7c7b5058d | ||
|
|
986254f56f | ||
|
|
32ceb47937 | ||
|
|
354006a699 | ||
|
|
17812c33cc | ||
|
|
2673558a52 | ||
|
|
a1b829997e | ||
|
|
bcf6b185d7 | ||
|
|
8e870c809c | ||
|
|
5eea411a3c | ||
|
|
8e135c1015 | ||
|
|
cdf1ebe861 | ||
|
|
99a131a91d | ||
|
|
929ac1f09f | ||
|
|
f3d0642994 | ||
|
|
9f1c11ac34 | ||
|
|
c48e835d0c | ||
|
|
af62143327 | ||
|
|
b60623df4d | ||
|
|
a3ce0f3e28 | ||
|
|
c10f869836 | ||
|
|
c1dbba1a31 | ||
|
|
8d99bf66bd | ||
|
|
2ff45313de | ||
|
|
52ec847cbd | ||
|
|
933ac4be43 | ||
|
|
8c698366b8 | ||
|
|
21bb281c75 | ||
|
|
831357ee88 | ||
|
|
f30a0c1864 | ||
|
|
76f8ebb1f2 | ||
|
|
a13d5eeccb | ||
|
|
875dd91bc2 | ||
|
|
546dafa0b0 | ||
|
|
a88b4bfecd | ||
|
|
8593235571 | ||
|
|
98b4693525 | ||
|
|
94c05e9e2d | ||
|
|
172a2af982 | ||
|
|
1efa2bda30 | ||
|
|
6ced56e11d | ||
|
|
1408dd5245 | ||
|
|
3b6609f13a | ||
|
|
2c6aa8e0d0 | ||
|
|
fc26e6321b | ||
|
|
ed2889cecf | ||
|
|
33c7d9cba5 | ||
|
|
7d5940101b | ||
|
|
4760fd7f52 | ||
|
|
3f292e3ce3 | ||
|
|
941fc5f51c | ||
|
|
8d59ad2713 | ||
|
|
2010a525d3 | ||
|
|
ff6db3e08e | ||
|
|
914e8c6c7a | ||
|
|
bf801f4f7f | ||
|
|
f9f08f7f5c | ||
|
|
b940d9f3a1 | ||
|
|
3372d8f102 | ||
|
|
55e4c7e4cb | ||
|
|
57af462ecf | ||
|
|
8ea56477d9 | ||
|
|
f2093a3f76 | ||
|
|
03428f3380 | ||
|
|
5d39e1357e | ||
|
|
99dbd109ce | ||
|
|
a7735677ae | ||
|
|
ae7ac460b9 | ||
|
|
ead1c144b2 | ||
|
|
207421cb7b | ||
|
|
f4efb37b03 | ||
|
|
df075e6628 | ||
|
|
faf4641625 | ||
|
|
e8268969ea | ||
|
|
bfd26c98e3 | ||
|
|
18ff08243b | ||
|
|
b9a895f825 | ||
|
|
dabd2af828 | ||
|
|
954f76d107 | ||
|
|
fb20b96024 | ||
|
|
f322a8b159 | ||
|
|
60c47e96a8 | ||
|
|
8df58db415 | ||
|
|
878dd7a0c9 | ||
|
|
567d5181ca | ||
|
|
3413ca9617 | ||
|
|
78b6df769b | ||
|
|
9a0053a501 | ||
|
|
963d10f1ac | ||
|
|
3337af64ca | ||
|
|
2374d034d7 | ||
|
|
7a8ecbf41d | ||
|
|
7bd65cfe93 | ||
|
|
eec5eaf8df | ||
|
|
8f6566422f | ||
|
|
f4b3536b9b | ||
|
|
1191c18641 | ||
|
|
1f3bc6eff5 | ||
|
|
fc55ceb2f4 | ||
|
|
ffb7663f4d | ||
|
|
d610c84e31 | ||
|
|
3aa931fdb0 | ||
|
|
78f5df2846 | ||
|
|
b0fe422b4f | ||
|
|
3eb011c9d1 | ||
|
|
41cdd82291 | ||
|
|
ef7d4a3fc3 | ||
|
|
296abbf7ca | ||
|
|
49a803eaa0 | ||
|
|
5bca3731a3 | ||
|
|
23f0acfd1d | ||
|
|
fa8b0ba018 | ||
|
|
d3368ee0d5 | ||
|
|
62022ce623 | ||
|
|
c63c100c4b | ||
|
|
d89c85d374 | ||
|
|
a60eb4fe64 | ||
|
|
95fb03c8e3 | ||
|
|
77a5100280 | ||
|
|
e9a89822f8 | ||
|
|
ece9545695 | ||
|
|
ff0bc22cb1 | ||
|
|
8ba08f3029 | ||
|
|
056f257058 | ||
|
|
7706ca6361 | ||
|
|
9f3c553298 | ||
|
|
a813830024 | ||
|
|
a837dcd40b | ||
|
|
752de866ae | ||
|
|
ec11859a48 | ||
|
|
1096d63468 | ||
|
|
955c9ae837 | ||
|
|
3e0f4daf60 | ||
|
|
5d21e12658 | ||
|
|
f1ffd5e5e8 | ||
|
|
80e7302a05 | ||
|
|
b57bd00be0 | ||
|
|
c1e737bbe4 | ||
|
|
76a31a47c2 | ||
|
|
23c449af5d | ||
|
|
94d0d8bc09 | ||
|
|
0276bb5b06 | ||
|
|
614186a590 | ||
|
|
c6d0b364ab | ||
|
|
8a23b13798 | ||
|
|
3d08c0557f | ||
|
|
68dc45cc62 | ||
|
|
b0e308e0dc | ||
|
|
fe2c62b9b1 | ||
|
|
3febf09b85 | ||
|
|
93495d3a75 | ||
|
|
9f1a149876 | ||
|
|
88c65932d8 | ||
|
|
c8d4de5e77 | ||
|
|
c4244a6cf3 | ||
|
|
f7c3d37969 | ||
|
|
d18670d7bb | ||
|
|
aa0272f6f3 | ||
|
|
78219471ff | ||
|
|
6d74eee874 | ||
|
|
be29ae4ef6 | ||
|
|
5e1e3fca1e | ||
|
|
b8eeb2db45 | ||
|
|
c5533b3c32 | ||
|
|
11f1298f53 | ||
|
|
7177f8269d | ||
|
|
6465a63bf6 | ||
|
|
ae50bb5dc0 | ||
|
|
99bbac9cbf | ||
|
|
f19b075306 | ||
|
|
dbc5c81e4a | ||
|
|
818d1435ce | ||
|
|
8bbca3b8f3 | ||
|
|
9e7cae13df | ||
|
|
13b8c23767 | ||
|
|
eb096bfb62 | ||
|
|
ede13a8cb5 | ||
|
|
f453b1545d | ||
|
|
c94aff8cae | ||
|
|
fb49759d1f | ||
|
|
f70b0892ea |
236 changed files with 10247 additions and 2567 deletions
|
|
@ -38,7 +38,7 @@ include:
|
|||
.fedora:
|
||||
variables:
|
||||
# Update this tag when you want to trigger a rebuild
|
||||
FDO_DISTRIBUTION_TAG: '2025-10-15.0'
|
||||
FDO_DISTRIBUTION_TAG: '2025-10-22.0'
|
||||
FDO_DISTRIBUTION_VERSION: '42'
|
||||
FDO_DISTRIBUTION_PACKAGES: >-
|
||||
alsa-lib-devel
|
||||
|
|
@ -48,6 +48,7 @@ include:
|
|||
dbus-devel
|
||||
doxygen
|
||||
fdk-aac-free-devel
|
||||
file
|
||||
findutils
|
||||
gcc
|
||||
gcc-c++
|
||||
|
|
|
|||
128
NEWS
128
NEWS
|
|
@ -1,3 +1,121 @@
|
|||
# PipeWire 1.5.84 (2025-11-27)
|
||||
|
||||
This is the fourth 1.6 release candidate that is API and ABI
|
||||
compatible with previous 1.4.x, 1.2.x and 1.0.x releases.
|
||||
|
||||
Changes since the last pre-release:
|
||||
|
||||
## Highlights
|
||||
- Capabilities wer added to improve negotiation over links.
|
||||
- The audio resampler now has a configurable window function to better
|
||||
tune the resampler quality. A kaiser and blackman window was added
|
||||
and the default parameters were tuned.
|
||||
- Various small fixes and improvements.
|
||||
|
||||
## PipeWire
|
||||
- Capabilities and PeerCapabilities were added to exchange key/value
|
||||
pairs between consumer and producer right after a link is made. This
|
||||
can be used to detect how the negotiation of formats and buffers
|
||||
should be done.
|
||||
|
||||
## Modules
|
||||
- Avoid segfaults in RTP source. (#4970)
|
||||
- The AVB module has seen some improvements.
|
||||
|
||||
## Pulse-server
|
||||
- @NONE@ can now be used to clear the default sink/source.
|
||||
|
||||
## SPA
|
||||
- Support longer convolver filenames and also support inline
|
||||
IRs.
|
||||
- The audio resampler window function is now selectable and
|
||||
configurable. A kaiser window and blackman window was added
|
||||
and the default qualities were tweaked to improve quality.
|
||||
- The filter-graph convolver latency is now set by default to
|
||||
something more sensible. (0 by default and N/2 for hilbert).
|
||||
(#4980)
|
||||
|
||||
## Bluetooth
|
||||
- Better xrun and error handling for iso streams.
|
||||
- The +CNUM reply was fixed.
|
||||
- The CIEC call status was fixed. (#1744)
|
||||
- Add BAP context metadata to improve compatibility.
|
||||
- Improve compatiblity with Creative Zen Hybrid Pro by releasing
|
||||
transports simultaneously.
|
||||
|
||||
|
||||
Older versions:
|
||||
|
||||
# PipeWire 1.5.83 (2025-11-06)
|
||||
|
||||
This is the third 1.6 release candidate that is API and ABI
|
||||
compatible with previous 1.4.x, 1.2.x and 1.0.x releases.
|
||||
|
||||
Changes since the last pre-release:
|
||||
|
||||
## Highlights
|
||||
- Include the NEWS and updated version number.
|
||||
|
||||
# PipeWire 1.5.82 (2025-11-06)
|
||||
|
||||
This is the second 1.6 release candidate that is API and ABI
|
||||
compatible with previous 1.4.x, 1.2.x and 1.0.x releases.
|
||||
|
||||
Changes since the last pre-release:
|
||||
|
||||
## Highlights
|
||||
- The max channel limit is now a compile time option.
|
||||
- The SAP and RTP module have seen some robustness improvements.
|
||||
- Add audio.layout propperty.
|
||||
- Cleanups to the code here and there.
|
||||
|
||||
## PipeWire
|
||||
- Handle Tags more like Latency with a NULL param when no ports are linked
|
||||
and some sort of (empty) Tag when the ports are linked.
|
||||
|
||||
## Modules
|
||||
- Improve the echo-cancel module to keep the streams more aligned
|
||||
and cause less latency.
|
||||
- Improve format parsing errors in most modules.
|
||||
- The RTP module now has extra code for better network robustness, including
|
||||
cases when network interfaces are not yet up and running, and multicast
|
||||
sockets are silently kicked out of IGMP groups.
|
||||
- The direct timestamp mode in the RTP module was effectively broken and is
|
||||
now fixed.
|
||||
- Add support for audio.layout.
|
||||
- Add multichannel support to ROC.
|
||||
|
||||
## SPA
|
||||
- Rework the maximum number of channel handling. Because this is a
|
||||
potential ABI break, it is now a compile time option with new
|
||||
functions to handle more than the previous 64 channels.
|
||||
- The 64 channel limit was removed from the noise shaper.
|
||||
- spa_strbuf is used in more places instead of custom snprintf code.
|
||||
- The volume ramp code was simplified.
|
||||
- The driver node now has properties to configure the clock.
|
||||
- The adapter will try to renegotiate when the driver changes.
|
||||
- Fix relaxed array parsing with od number of elements. (#4944)
|
||||
- audio.layout was added to set the channel positions to some
|
||||
predefined layouts.
|
||||
- Added more POD choice checks to ensure the right amount of values
|
||||
are present in the choice.
|
||||
- Fix __has_attribute usage. (#4962)
|
||||
- Thread RESET_ON_FORK is now disabled for JACK application so that
|
||||
forking will preserve any real-time thread priorities, like JACK.
|
||||
(#4966)
|
||||
- Fix some compilation issues. (#4960 and #4961).
|
||||
|
||||
## Pulse-server
|
||||
- Fix missing subscription events on device port changes.
|
||||
- Increase min.quantum to 256/48000. (#4875)
|
||||
|
||||
## GStreamer
|
||||
- Avoid overflow in clock time calculations.
|
||||
- Fix renegotiation.
|
||||
|
||||
## Docs
|
||||
- Swap the name and id of device.product
|
||||
|
||||
# PipeWire 1.5.81 (2025-10-16)
|
||||
|
||||
This is the first 1.6 release candidate that is API and ABI
|
||||
|
|
@ -166,8 +284,11 @@ also contains some new features:
|
|||
## Bluetooth
|
||||
- Telephony improvements.
|
||||
- ASHA support was added.
|
||||
- Add packet loss correction with spandsp for some codecs.
|
||||
- Synchronisation between ISO streams in the same group.
|
||||
- Packet loss concealment was added.
|
||||
- Improved synchronisation between LE Audio streams in the same group.
|
||||
- Improved LE Audio device compatibility.
|
||||
- LC3-24kHz voice codec was added (used by Airpods)
|
||||
- LDAC decoding support added (requires separate decoder library)
|
||||
|
||||
## Pulse-server
|
||||
- The SUSPEND event is now correctly generated. fail-on-suspend is
|
||||
|
|
@ -193,9 +314,6 @@ also contains some new features:
|
|||
## Docs
|
||||
- Document the client-node flow a bit more.
|
||||
|
||||
|
||||
Older versions:
|
||||
|
||||
# PipeWire 1.4.0 (2025-03-06)
|
||||
|
||||
This is the 1.4 release that is API and ABI compatible with previous
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ POD's can contain a number of basic SPA types:
|
|||
- `SPA_TYPE_Bytes`: A byte array.
|
||||
- `SPA_TYPE_Rectangle`: A rectangle with width and height.
|
||||
- `SPA_TYPE_Fraction`: A fraction with numerator and denominator.
|
||||
- `SPA_TYPE_Bitmap`: An array of bits.
|
||||
- `SPA_TYPE_Bitmap`: An array of bits. Deprecated and unused.
|
||||
|
||||
POD's can be grouped together in these container types:
|
||||
|
||||
|
|
@ -435,10 +435,11 @@ spa_pod_parser_get_object(&p,
|
|||
\endcode
|
||||
|
||||
`spa_pod_get_values()` is a useful function. It returns a
|
||||
`struct spa_pod*` with and array of values. For normal POD's
|
||||
and choice none values, it simply returns the POD and one value.
|
||||
For other choice values it returns the choice type and an array
|
||||
of values:
|
||||
`struct spa_pod*` with and array of values. For invalid PODs
|
||||
it returns the POD and no values. For normal PODs it returns
|
||||
the POD and one value. For choice values it returns the choice
|
||||
type and an array of values. If the choice doesn't fit even a
|
||||
single value, the array will have no values.
|
||||
|
||||
\code{.c}
|
||||
struct spa_pod *value;
|
||||
|
|
|
|||
|
|
@ -59,6 +59,14 @@ stream.properties = {
|
|||
#node.autoconnect = true
|
||||
#resample.disable = false
|
||||
#resample.quality = 4
|
||||
#resample.window = exp # blackman kaiser
|
||||
#resample.cutoff = 0.0
|
||||
#resample.n-taps = 0
|
||||
#resample.param.exp.A = 0.0
|
||||
#resample.param.blackman.alpha = 0.0
|
||||
#resample.param.kaiser.alpha = 0.0
|
||||
#resample.param.kaiser.stopband-attenuation = 0.0
|
||||
#resample.param.kaiser.transition-bandwidth = 0.0
|
||||
#monitor.channel-volumes = false
|
||||
#channelmix.disable = false
|
||||
#channelmix.min-volume = 0.0
|
||||
|
|
|
|||
|
|
@ -174,12 +174,12 @@ ie. for example `device.Param.Props = { ... }` to set `Props`.
|
|||
|
||||
@PAR@ device-prop device.product.id # integer
|
||||
\parblock
|
||||
\copydoc PW_KEY_DEVICE_PRODUCT_NAME
|
||||
\copydoc PW_KEY_DEVICE_PRODUCT_ID
|
||||
\endparblock
|
||||
|
||||
@PAR@ device-prop device.product.name # string
|
||||
\parblock
|
||||
\copydoc PW_KEY_DEVICE_PRODUCT_ID
|
||||
\copydoc PW_KEY_DEVICE_PRODUCT_NAME
|
||||
\endparblock
|
||||
|
||||
@PAR@ device-prop device.class # string
|
||||
|
|
@ -547,17 +547,137 @@ Below is an explanation of the options that can be tuned in the sample converter
|
|||
\parblock
|
||||
The quality of the resampler. from 0 to 14, the default is 4.
|
||||
|
||||
The quality of a resampler depends on multiple factors:
|
||||
|
||||
1. Anti-Aliasing, how well are unwanted frequencies filtered out. Poor anti-aliasing
|
||||
will make the original inaudible frequencies audible as distortion and noise.
|
||||
2. Cutoff frequence. At what frequency the transition band will start. This is the
|
||||
frequency where the signal will start to fade out. A too low cutoff might remove too
|
||||
much of the high frequencies and make the sound dull. A too high cutoff might cause
|
||||
aliasing. The cutoff frequency is usually expressed as a ratio of the Nyquist
|
||||
frequency, 1.0 being the Nyquist frequency (frequency/2).
|
||||
3. Transition band length. How quickly the unwanted frequencies are filtered out. A
|
||||
shorter transition band requires longer filters with more CPU and latency but
|
||||
causes less aliasing. The transition band length is expressed as a ratio of the
|
||||
Nyquist frequency.
|
||||
4. Stopband attenuation. How well the unwanted frequencies are filtered out. This is
|
||||
usually measured in dB. 96dB is below audidle on CD quality audio, 150dB is below
|
||||
the precision of floating point values.
|
||||
5. CPU usage. Better anti-aliasing needs longer filters and is therefore more CPU
|
||||
intensive.
|
||||
6. Latency. Longer filters have a higher Latency. In real-time application the latency
|
||||
should be kept as low as possible.
|
||||
7. Ringing. A too short transition band length might cause ringing because of how the
|
||||
sinc filters work. This can sound like flutter on sharp attacks in the audio signal.
|
||||
|
||||
Increasing the quality will result in better cutoff and less aliasing at the expense of
|
||||
(much) more CPU consumption. The default quality of 4 has been selected as a good compromise
|
||||
between quality and performance with no artifacts that are well below the audible range.
|
||||
(much) more CPU consumption and more ringing. The default quality of 4 has been selected
|
||||
as a good compromise between quality and performance with no artifacts that are well
|
||||
below the audible range.
|
||||
|
||||
The default resampler quality for the exp window results in a cutoff of 0.87 and a
|
||||
filter size of about 48 taps. It has a Stopband attenuation of about 150 dB.
|
||||
|
||||
See [Infinite Wave](https://src.infinitewave.ca/) for a comparison of the performance.
|
||||
|
||||
You can tune the resampler in a variaty of ways:
|
||||
|
||||
* Tune the cutoff frequency. Increase the cutoff to preserve more high frequencies at the
|
||||
expense of more aliasing.
|
||||
* Tune the transition band. Reduce the transition band to better filter out the frequencies
|
||||
around the cutoff frequency and get less aliasing at the expense of more ringing, CPU and
|
||||
Latency. This can be done by increasing the number of taps.
|
||||
* Tune the stopband attenuation. Increase the attenuation to reduce aliasing at the expense
|
||||
of a wider transition band. This can only be done on the kaiser window, the exp and
|
||||
blackman window have 150dB and 96dB attenuation respecively.
|
||||
\endparblock
|
||||
|
||||
@PAR@ node-prop resample.disable = false
|
||||
Disable the resampler entirely. The node will only be able to negotiate with the graph
|
||||
when the samplerates are compatible.
|
||||
|
||||
@PAR@ node-prop resample.window = exp
|
||||
\parblock
|
||||
The resampler window function to use. By default an exponential window function is used
|
||||
that gives a good balance between complexitiy and quality.
|
||||
|
||||
You can also specify a blackman or kaiser window, both with different tradeoffs. The
|
||||
kaiser window has some extra tunable parameters for the specific use cases.
|
||||
\endparblock
|
||||
|
||||
@PAR@ node-prop resample.cutoff = 0.0
|
||||
\parblock
|
||||
The resampler cutoff frequency. This is a value between 0.0 and 1.0. A value of 0.0 will
|
||||
use a predefined value based on the resampler quality.
|
||||
|
||||
A higher cutoff value will preserve more high frequency content but depending on the
|
||||
size of the transition band will cause more aliasing.
|
||||
|
||||
The default quality 4 setting for all windows is 0.87.
|
||||
\endparblock
|
||||
|
||||
@PAR@ node-prop resample.n-taps = 0
|
||||
\parblock
|
||||
The resampler number of taps. A value of 0 will use a predefined value based on
|
||||
the resampler quality or other window function parameters.
|
||||
|
||||
A higher number of taps will use more CPU, Latency and cause more ringing but will
|
||||
reduce aliasing.
|
||||
|
||||
The default quality setting for the exp window is 48.
|
||||
\endparblock
|
||||
|
||||
@PAR@ node-prop resample.param.exp.A = 0.0
|
||||
\parblock
|
||||
The A parameter for the exponential window function. A value of 0.0 will use a predefined
|
||||
value based on the quality when the exp window is in use.
|
||||
|
||||
The default setting for the exp window is 16.97789.
|
||||
\endparblock
|
||||
|
||||
@PAR@ node-prop resample.param.blackman.alpha = 0.0
|
||||
\parblock
|
||||
The alpha value of the blackman function. A value of 0.0 will use a predefined
|
||||
value based on the quality when the blackman window is in use.
|
||||
|
||||
The default quality setting for the blackman window is 0.16.
|
||||
\endparblock
|
||||
|
||||
@PAR@ node-prop resample.param.kaiser.stopband-attenuation = 0.0
|
||||
\parblock
|
||||
The kaiser window stopband attenuation parameter in dB. A default value of 0.0 will use a
|
||||
predefined value based on the quality.
|
||||
|
||||
A higher value will filter out more of the unwanted frequencies and reduce aliasing at the
|
||||
expense of a larger transition band. A value of 96dB is below the dynamic range of CD quality
|
||||
audio. 150dB is the limit of the precision of the resampler.
|
||||
|
||||
The default quality setting for the kaiser window is 130.000000.
|
||||
\endparblock
|
||||
|
||||
@PAR@ node-prop resample.param.kaiser.transition-bandwidth = 0.0
|
||||
\parblock
|
||||
The kaiser window transition bandwidth parameter. A default value of 0.0 will use a
|
||||
predefined value based on the quality.
|
||||
|
||||
A smaller transition band will cause a steeper cutoff with less unwanted frequencies
|
||||
in the final signal at the expense of more a larger filter and more CPU usage and
|
||||
latency. A smaller transition band can also cause more ringing.
|
||||
|
||||
The default quality setting for the kaiser window is 0.177032
|
||||
\endparblock
|
||||
|
||||
@PAR@ node-prop resample.param.kaiser.alpha = 0.0
|
||||
\parblock
|
||||
The kaiser window alpha parameter. A default value of 0.0 will calculate an alpha value
|
||||
based on the stopband-attenuation and transition-bandwidth parameters.
|
||||
|
||||
This value is usually calculated from the other parameters but can be set explicitly
|
||||
with this property.
|
||||
|
||||
The default quality setting for the kaiser window is 4.254931.
|
||||
\endparblock
|
||||
|
||||
## Channel Mixer Parameters
|
||||
|
||||
Source, sinks, capture and playback streams can apply channel mixing on the incoming signal.
|
||||
|
|
@ -843,6 +963,9 @@ The audio format to open the device in. By default this is "UNKNOWN", which will
|
|||
@PAR@ node-prop audio.position # JSON array of strings
|
||||
The audio position of the channels in the device. This is auto detected based on the profile. You can configure an array of channel positions, like "[ FL, FR ]".
|
||||
|
||||
@PAR@ node-prop audio.layout # string
|
||||
The audio layout of the channels in the device. You can use any of the predefined layouts, like "Stereo", "5.1" etc.
|
||||
|
||||
@PAR@ node-prop audio.allowed-rates # JSON array of integers
|
||||
\parblock
|
||||
The allowed audio rates to open the device with. Default is "[ ]", which means the device can be opened in any supported rate.
|
||||
|
|
@ -1015,6 +1138,15 @@ HFP/HSP backend (default: native). Available values: any, none, hsphfpd, ofono,
|
|||
|
||||
@PAR@ monitor-prop bluez5.hfphsp-backend-native-modem # string
|
||||
|
||||
@PAR@ monitor-prop bluez5.hfphsp-backend-native-pts # boolean
|
||||
Enable specific workarounds for Bluetooth qualification.
|
||||
|
||||
@PAR@ monitor-prop bluez5.disable-dummy-call # boolean
|
||||
By default a call status event is sent on audio stream connection/disconnection to
|
||||
workaround some headset timeout disconnection when the HFP HF is used by another
|
||||
application than telephony one, e.g. a conference application/website.
|
||||
This prevent to send this event.
|
||||
|
||||
@PAR@ monitor-prop bluez5.dummy-avrcp player # boolean
|
||||
Register dummy AVRCP player. Some devices have wrongly functioning
|
||||
volume or playback controls if this is not enabled. Default: false
|
||||
|
|
@ -1134,6 +1266,18 @@ Available source contexts PACS bitmask of the the server.
|
|||
@PAR@ monitor-prop bluez5.bap-server-capabilities.source.supported-contexts # integer
|
||||
Supported source contexts PACS bitmask of the the server.
|
||||
|
||||
@PAR@ monitor-prop bluez5.bap-server-tmap-features = null # array of string
|
||||
Override advertised TMAP service features. See TMAP specification for their meaning.
|
||||
Possible values: "cg", "ct", "ums", "umr", "bms", "bmr".
|
||||
Default: none.
|
||||
|
||||
@PAR@ monitor-prop bluez5.bap-server-gmap-features = null # array of string
|
||||
Override advertised GMAP service features. See GMAP specification for their meaning.
|
||||
Possible values: "ugg", "ugt", "bgs", "bgr", "ugg-multiplex", "ugg-96kbps-source", "ugg-multisink",
|
||||
"ugt-source", "ugt-80kbps-source", "ugt-sink", "ugt-64kbps-sink", "ugt-multiplex", "ugt-multisink",
|
||||
"ugt-multisource", "bgs-96kbps", "bgr-multisink", "bgr-multiplex".
|
||||
Default: none.
|
||||
|
||||
## Device properties
|
||||
|
||||
@PAR@ device-prop bluez5.auto-connect # boolean
|
||||
|
|
|
|||
|
|
@ -1130,6 +1130,8 @@ follows:
|
|||
|<----------------------------------------|
|
||||
| ClientNode::PortSetMixInfo | mixer inputs for each linked port
|
||||
|<----------------------------------------|
|
||||
| ClientNode::PortSetParam | PeerCapability of the ports
|
||||
|<----------------------------------------|
|
||||
| ClientNode::PortSetParam | Latency of the ports
|
||||
|<----------------------------------------|
|
||||
| ClientNode::SetActivation | activation of port peers
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
project('pipewire', ['c' ],
|
||||
version : '1.5.81',
|
||||
version : '1.5.84',
|
||||
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
|
||||
meson_version : '>= 0.61.1',
|
||||
default_options : [ 'warning_level=3',
|
||||
|
|
@ -115,10 +115,11 @@ cc_flags = common_flags + [
|
|||
'-Werror=old-style-definition',
|
||||
'-Werror=missing-parameter-type',
|
||||
'-Werror=strict-prototypes',
|
||||
'-DSPA_AUDIO_MAX_CHANNELS=128u',
|
||||
]
|
||||
add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c')
|
||||
|
||||
cc_flags_native = cc_native.get_supported_arguments(cc_flags)
|
||||
add_project_arguments(cc_native.get_supported_arguments(cc_flags),
|
||||
language: 'c', native: true)
|
||||
|
||||
have_cpp = add_languages('cpp', native: false, required : false)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,11 @@ PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.ctl");
|
|||
#define VOLUME_MIN ((uint32_t) 0U)
|
||||
#define VOLUME_MAX ((uint32_t) 0x10000U)
|
||||
|
||||
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
|
||||
|
||||
struct volume {
|
||||
uint32_t channels;
|
||||
long values[SPA_AUDIO_MAX_CHANNELS];
|
||||
long values[MAX_CHANNELS];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -498,7 +500,7 @@ static struct spa_pod *build_volume_mute(struct spa_pod_builder *b, struct volum
|
|||
spa_pod_builder_push_object(b, &f[0],
|
||||
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
|
||||
if (volume) {
|
||||
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
float volumes[MAX_CHANNELS];
|
||||
uint32_t i, n_volumes = 0;
|
||||
|
||||
n_volumes = volume->channels;
|
||||
|
|
@ -850,11 +852,11 @@ static void parse_props(struct global *g, const struct spa_pod *param, bool devi
|
|||
break;
|
||||
case SPA_PROP_channelVolumes:
|
||||
{
|
||||
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
float volumes[MAX_CHANNELS];
|
||||
uint32_t n_volumes, i;
|
||||
|
||||
n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
|
||||
volumes, SPA_AUDIO_MAX_CHANNELS);
|
||||
volumes, SPA_N_ELEMENTS(volumes));
|
||||
|
||||
g->node.channel_volume.channels = n_volumes;
|
||||
for (i = 0; i < n_volumes; i++)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.pcm");
|
|||
#define MAX_BUFFERS 64u
|
||||
|
||||
#define MAX_RATE (48000*8)
|
||||
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
|
||||
|
||||
#define MIN_PERIOD 64
|
||||
|
||||
|
|
@ -642,7 +643,7 @@ static int snd_pcm_pipewire_pause(snd_pcm_ioplug_t * io, int enable)
|
|||
#define _FORMAT_BE(p, fmt) p ? SPA_AUDIO_FORMAT_UNKNOWN : SPA_AUDIO_FORMAT_ ## fmt ## _OE
|
||||
#endif
|
||||
|
||||
static int set_default_channels(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS])
|
||||
static int set_default_channels(uint32_t channels, uint32_t position[MAX_CHANNELS])
|
||||
{
|
||||
switch (channels) {
|
||||
case 8:
|
||||
|
|
@ -915,12 +916,16 @@ static int snd_pcm_pipewire_set_chmap(snd_pcm_ioplug_t * io,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (map->channels > MAX_CHANNELS)
|
||||
return -ENOTSUP;
|
||||
|
||||
for (i = 0; i < map->channels; i++) {
|
||||
char buf[8];
|
||||
position[i] = chmap_to_channel(map->pos[i]);
|
||||
pw_log_debug("map %d: %s / %s", i,
|
||||
snd_pcm_chmap_name(map->pos[i]),
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
position[i]));
|
||||
spa_type_audio_channel_make_short_name(position[i],
|
||||
buf, sizeof(buf), "UNK"));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1097,7 +1102,7 @@ struct param_info infos[] = {
|
|||
{ "alsa.rate", SND_PCM_IOPLUG_HW_RATE, TYPE_MIN_MAX,
|
||||
{ 1, MAX_RATE }, 2, collect_int },
|
||||
{ "alsa.channels", SND_PCM_IOPLUG_HW_CHANNELS, TYPE_MIN_MAX,
|
||||
{ 1, SPA_AUDIO_MAX_CHANNELS }, 2, collect_int },
|
||||
{ 1, MAX_CHANNELS }, 2, collect_int },
|
||||
{ "alsa.buffer-bytes", SND_PCM_IOPLUG_HW_BUFFER_BYTES, TYPE_MIN_MAX,
|
||||
{ MIN_BUFFER_BYTES, MAX_BUFFER_BYTES }, 2, collect_int },
|
||||
{ "alsa.period-bytes", SND_PCM_IOPLUG_HW_PERIOD_BYTES, TYPE_MIN_MAX,
|
||||
|
|
|
|||
|
|
@ -4299,6 +4299,7 @@ jack_client_t * jack_client_open (const char *client_name,
|
|||
|
||||
client->props = pw_properties_new(
|
||||
PW_KEY_LOOP_CANCEL, "true",
|
||||
SPA_KEY_THREAD_RESET_ON_FORK, "false",
|
||||
PW_KEY_REMOTE_NAME, client->server_name,
|
||||
PW_KEY_CLIENT_NAME, client_name,
|
||||
PW_KEY_CLIENT_API, "jack",
|
||||
|
|
|
|||
304
po/sl.po
304
po/sl.po
|
|
@ -9,16 +9,16 @@ msgstr ""
|
|||
"Project-Id-Version: PipeWire master\n"
|
||||
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2025-09-11 03:34+0000\n"
|
||||
"PO-Revision-Date: 2025-09-11 11:47+0200\n"
|
||||
"POT-Creation-Date: 2025-12-04 15:34+0000\n"
|
||||
"PO-Revision-Date: 2025-12-07 08:53+0100\n"
|
||||
"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
|
||||
"Language-Team: Slovenian <gnome-si@googlegroups.com>\n"
|
||||
"Language: sl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && "
|
||||
"n%100<=4 ? 2 : 3);\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n"
|
||||
"%100<=4 ? 2 : 3);\n"
|
||||
"X-Generator: Poedit 2.2.1\n"
|
||||
|
||||
#: src/daemon/pipewire.c:29
|
||||
|
|
@ -57,7 +57,7 @@ msgstr "Prehod do %s%s%s"
|
|||
msgid "Dummy Output"
|
||||
msgstr "Lažni izhod"
|
||||
|
||||
#: src/modules/module-pulse-tunnel.c:760
|
||||
#: src/modules/module-pulse-tunnel.c:761
|
||||
#, c-format
|
||||
msgid "Tunnel for %s@%s"
|
||||
msgstr "Prehod za %s@%s"
|
||||
|
|
@ -76,7 +76,7 @@ msgstr "%s na %s@%s"
|
|||
msgid "%s on %s"
|
||||
msgstr "%s na %s"
|
||||
|
||||
#: src/tools/pw-cat.c:1084
|
||||
#: src/tools/pw-cat.c:1103
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s [options] [<file>|-]\n"
|
||||
|
|
@ -92,7 +92,7 @@ msgstr ""
|
|||
"\n"
|
||||
"</file>\n"
|
||||
|
||||
#: src/tools/pw-cat.c:1091
|
||||
#: src/tools/pw-cat.c:1110
|
||||
#, c-format
|
||||
msgid ""
|
||||
" -R, --remote Remote daemon name\n"
|
||||
|
|
@ -129,7 +129,7 @@ msgstr ""
|
|||
" -P --properties Nastavi lastnosti vozlišča\n"
|
||||
"\n"
|
||||
|
||||
#: src/tools/pw-cat.c:1109
|
||||
#: src/tools/pw-cat.c:1128
|
||||
#, c-format
|
||||
msgid ""
|
||||
" --rate Sample rate (req. for rec) (default "
|
||||
|
|
@ -137,8 +137,8 @@ msgid ""
|
|||
" --channels Number of channels (req. for rec) "
|
||||
"(default %u)\n"
|
||||
" --channel-map Channel map\n"
|
||||
" one of: \"stereo\", "
|
||||
"\"surround-51\",... or\n"
|
||||
" one of: \"Stereo\", \"5.1\",... "
|
||||
"or\n"
|
||||
" comma separated list of channel "
|
||||
"names: eg. \"FL,FR\"\n"
|
||||
" --format Sample format %s (req. for rec) "
|
||||
|
|
@ -157,8 +157,8 @@ msgstr ""
|
|||
" --channels Število kanalov (zaht. za snemanje) "
|
||||
"(privzeto %u)\n"
|
||||
" --channel-map Preslikava kanalov\n"
|
||||
" Ena izmed: \"stereo\", "
|
||||
"\"surround-51\",... ali\n"
|
||||
" Ena izmed: \"Stereo\", "
|
||||
"\"5.1\",... ali\n"
|
||||
" seznam imen kanalov, ločen z "
|
||||
"vejico: npr. \"FL,FR\"\n"
|
||||
" --format Vzorčne oblike zapisa %s (zahtevano "
|
||||
|
|
@ -167,12 +167,12 @@ msgstr ""
|
|||
" -q --quality Kakovost prevzorčenja (0 - 15) "
|
||||
"(privzeto %d)\n"
|
||||
" -a, --raw neobdelan način (RAW)\n"
|
||||
" -M, --force-midi Vsili zapis midi, eden izmed "
|
||||
"\"midi\" ali \"ump\" (privzeto ump)\n"
|
||||
" -M, --force-midi Vsili zapis midi, eden izmed \"midi"
|
||||
"\" ali \"ump\" (privzeto ump)\n"
|
||||
" -n, --sample-count ŠTEVEC Ustavi po ŠTEVEC vzorcih\n"
|
||||
"\n"
|
||||
|
||||
#: src/tools/pw-cat.c:1129
|
||||
#: src/tools/pw-cat.c:1148
|
||||
msgid ""
|
||||
" -p, --playback Playback mode\n"
|
||||
" -r, --record Recording mode\n"
|
||||
|
|
@ -212,203 +212,203 @@ msgstr ""
|
|||
" -m, --monitor Spremljaj dejavnosti\n"
|
||||
"\n"
|
||||
|
||||
#: spa/plugins/alsa/acp/acp.c:351
|
||||
#: spa/plugins/alsa/acp/acp.c:361
|
||||
msgid "Pro Audio"
|
||||
msgstr "Profesionalni zvok"
|
||||
|
||||
#: spa/plugins/alsa/acp/acp.c:527 spa/plugins/alsa/acp/alsa-mixer.c:4635
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1974
|
||||
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1976
|
||||
msgid "Off"
|
||||
msgstr "Izklopljeno"
|
||||
|
||||
#: spa/plugins/alsa/acp/acp.c:610
|
||||
#: spa/plugins/alsa/acp/acp.c:620
|
||||
#, c-format
|
||||
msgid "%s [ALSA UCM error]"
|
||||
msgstr "%s [napaka ALSA UCM]"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2652
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
|
||||
msgid "Input"
|
||||
msgstr "Vhod"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2653
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
|
||||
msgid "Docking Station Input"
|
||||
msgstr "Vhod priklopne postaje"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2654
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
|
||||
msgid "Docking Station Microphone"
|
||||
msgstr "Mikrofon priklopne postaje"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2655
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
|
||||
msgid "Docking Station Line In"
|
||||
msgstr "Linijski vhod priklopne postaje"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2656
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2747
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
|
||||
msgid "Line In"
|
||||
msgstr "Linijski vhod"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2657
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2372
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2374
|
||||
msgid "Microphone"
|
||||
msgstr "Mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2658
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
|
||||
msgid "Front Microphone"
|
||||
msgstr "Sprednji mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2659
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2743
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
|
||||
msgid "Rear Microphone"
|
||||
msgstr "Zadnji mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2660
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
|
||||
msgid "External Microphone"
|
||||
msgstr "Zunanji mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2661
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2745
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
|
||||
msgid "Internal Microphone"
|
||||
msgstr "Notranji mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2662
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2748
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2731
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
|
||||
msgid "Radio"
|
||||
msgstr "Radio"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2663
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2749
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2732
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2664
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2733
|
||||
msgid "Automatic Gain Control"
|
||||
msgstr "Samodejni nadzor ojačanja"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2665
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2734
|
||||
msgid "No Automatic Gain Control"
|
||||
msgstr "Brez samodejnega nadzora ojačanja"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2666
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2735
|
||||
msgid "Boost"
|
||||
msgstr "Ojačitev"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2667
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2736
|
||||
msgid "No Boost"
|
||||
msgstr "Brez ojačitve"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2668
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2737
|
||||
msgid "Amplifier"
|
||||
msgstr "Ojačevalnik"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2669
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2738
|
||||
msgid "No Amplifier"
|
||||
msgstr "Brez ojačevalnika"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2670
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2739
|
||||
msgid "Bass Boost"
|
||||
msgstr "Ojačitev nizkih tonov"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2671
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
|
||||
msgid "No Bass Boost"
|
||||
msgstr "Brez ojačitve nizkih tonov"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2672
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2378
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2380
|
||||
msgid "Speaker"
|
||||
msgstr "Zvočnik"
|
||||
|
||||
#. Don't call it "headset", the HF one has the mic
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2673
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2751
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2384
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2451
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2386
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2453
|
||||
msgid "Headphones"
|
||||
msgstr "Slušalke"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
|
||||
msgid "Analog Input"
|
||||
msgstr "Analogni vhod"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2744
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
|
||||
msgid "Dock Microphone"
|
||||
msgstr "Priklopni mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2746
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
|
||||
msgid "Headset Microphone"
|
||||
msgstr "Mikrofon s slušalkami"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2750
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
|
||||
msgid "Analog Output"
|
||||
msgstr "Analogni izhod"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2752
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
|
||||
msgid "Headphones 2"
|
||||
msgstr "Slušalke 2"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2753
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
|
||||
msgid "Headphones Mono Output"
|
||||
msgstr "Mono izhod slušalk"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2754
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
|
||||
msgid "Line Out"
|
||||
msgstr "Linijsk izhod"
|
||||
msgstr "Linijski izhod"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2755
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2824
|
||||
msgid "Analog Mono Output"
|
||||
msgstr "Analogni mono izhod"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2756
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2825
|
||||
msgid "Speakers"
|
||||
msgstr "Govorniki"
|
||||
msgstr "Zvočniki"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2757
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2826
|
||||
msgid "HDMI / DisplayPort"
|
||||
msgstr "HDMI / DisplayPort"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2758
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2827
|
||||
msgid "Digital Output (S/PDIF)"
|
||||
msgstr "Digitalni izhod (S/PDIF)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2759
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2828
|
||||
msgid "Digital Input (S/PDIF)"
|
||||
msgstr "Digitalni vhod (S/PDIF)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2760
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2829
|
||||
msgid "Multichannel Input"
|
||||
msgstr "Večkanalni vhod"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2761
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2830
|
||||
msgid "Multichannel Output"
|
||||
msgstr "Večkanalni izhod"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2762
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2831
|
||||
msgid "Game Output"
|
||||
msgstr "Vhod igre"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2763
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2764
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2832
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2833
|
||||
msgid "Chat Output"
|
||||
msgstr "Izhod klepeta"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2765
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2834
|
||||
msgid "Chat Input"
|
||||
msgstr "Vhod klepeta"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2766
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2835
|
||||
msgid "Virtual Surround 7.1"
|
||||
msgstr "Navidezni prostorski zvok 7.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4458
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4522
|
||||
msgid "Analog Mono"
|
||||
msgstr "Analogni mono"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4459
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4523
|
||||
msgid "Analog Mono (Left)"
|
||||
msgstr "Analogni mono (levo)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4460
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4524
|
||||
msgid "Analog Mono (Right)"
|
||||
msgstr "Analogni mono (desno)"
|
||||
|
||||
|
|
@ -417,142 +417,142 @@ msgstr "Analogni mono (desno)"
|
|||
#. * here would lead to the source name to become "Analog Stereo Input
|
||||
#. * Input". The same logic applies to analog-stereo-output,
|
||||
#. * multichannel-input and multichannel-output.
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4461
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4469
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4470
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4525
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4533
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4534
|
||||
msgid "Analog Stereo"
|
||||
msgstr "Analogni stereo"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4462
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4526
|
||||
msgid "Mono"
|
||||
msgstr "Mono"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4463
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
|
||||
msgid "Stereo"
|
||||
msgstr "Stereo"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4471
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4629
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2360
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2362
|
||||
msgid "Headset"
|
||||
msgstr "Slušalka"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4472
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4630
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4536
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4694
|
||||
msgid "Speakerphone"
|
||||
msgstr "Zvočnik telefona"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4473
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4474
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4537
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
|
||||
msgid "Multichannel"
|
||||
msgstr "Večkanalno"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4475
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
|
||||
msgid "Analog Surround 2.1"
|
||||
msgstr "Analogni prostorski zvok 2.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4476
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
|
||||
msgid "Analog Surround 3.0"
|
||||
msgstr "Analogni prostorski zvok 3.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4477
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
|
||||
msgid "Analog Surround 3.1"
|
||||
msgstr "Analogni prostorski zvok 3.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4478
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
|
||||
msgid "Analog Surround 4.0"
|
||||
msgstr "Analogni prostorski zvok 4.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4479
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
|
||||
msgid "Analog Surround 4.1"
|
||||
msgstr "Analogni prostorski zvok 4.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4480
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
|
||||
msgid "Analog Surround 5.0"
|
||||
msgstr "Analogni prostorski zvok 5.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4481
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
|
||||
msgid "Analog Surround 5.1"
|
||||
msgstr "Analogni prostorski zvok 5.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4482
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
|
||||
msgid "Analog Surround 6.0"
|
||||
msgstr "Analogni prostorski zvok 6.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4483
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
|
||||
msgid "Analog Surround 6.1"
|
||||
msgstr "Analogni prostorski zvok 6.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4484
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
|
||||
msgid "Analog Surround 7.0"
|
||||
msgstr "Analogni prostorski zvok 7.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4485
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
|
||||
msgid "Analog Surround 7.1"
|
||||
msgstr "Analogni prostorski zvok 7.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4486
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
|
||||
msgid "Digital Stereo (IEC958)"
|
||||
msgstr "Digitalni stereo (IEC958)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4487
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
|
||||
msgid "Digital Surround 4.0 (IEC958/AC3)"
|
||||
msgstr "Digitalni prostorski zvok 4.0 (IEC958/AC3)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4488
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
|
||||
msgid "Digital Surround 5.1 (IEC958/AC3)"
|
||||
msgstr "Digitalni prostorski zvok 5.1 (IEC958/AC3)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4489
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
|
||||
msgid "Digital Surround 5.1 (IEC958/DTS)"
|
||||
msgstr "Digitalni prostorski zvok 5.1 (IEC958/DTS)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4490
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
|
||||
msgid "Digital Stereo (HDMI)"
|
||||
msgstr "Digitalni stereo (HDMI)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4491
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
|
||||
msgid "Digital Surround 5.1 (HDMI)"
|
||||
msgstr "Digitalni prostorski zvok 5.1 (HDMI)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4492
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
|
||||
msgid "Chat"
|
||||
msgstr "Klepet"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4493
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
|
||||
msgid "Game"
|
||||
msgstr "Igra"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4627
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4691
|
||||
msgid "Analog Mono Duplex"
|
||||
msgstr "Analogni mono dupleks"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4628
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4692
|
||||
msgid "Analog Stereo Duplex"
|
||||
msgstr "Analogni stereo dupleks"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4631
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4695
|
||||
msgid "Digital Stereo Duplex (IEC958)"
|
||||
msgstr "Digitalni stereo dupleks (IEC958)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4632
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
|
||||
msgid "Multichannel Duplex"
|
||||
msgstr "Večkanalni dupleks"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4633
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
|
||||
msgid "Stereo Duplex"
|
||||
msgstr "Stereo dupleks"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4634
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
|
||||
msgid "Mono Chat + 7.1 Surround"
|
||||
msgstr "Mono klepet + prostorski zvok 7.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4735
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4799
|
||||
#, c-format
|
||||
msgid "%s Output"
|
||||
msgstr "Izhod %s"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4743
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4807
|
||||
#, c-format
|
||||
msgid "%s Input"
|
||||
msgstr "Vhod %s"
|
||||
|
|
@ -592,13 +592,13 @@ msgstr[3] ""
|
|||
#: spa/plugins/alsa/acp/alsa-util.c:1299
|
||||
#, c-format
|
||||
msgid ""
|
||||
"snd_pcm_delay() returned a value that is exceptionally large: %li byte "
|
||||
"(%s%lu ms).\n"
|
||||
"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
|
||||
"%lu ms).\n"
|
||||
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
|
||||
"to the ALSA developers."
|
||||
msgid_plural ""
|
||||
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
|
||||
"(%s%lu ms).\n"
|
||||
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
|
||||
"%lu ms).\n"
|
||||
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
|
||||
"to the ALSA developers."
|
||||
msgstr[0] ""
|
||||
|
|
@ -668,7 +668,7 @@ msgstr[3] ""
|
|||
"Najverjetneje je to napaka v gonilniku ALSA »%s«. O tej težavi obvestite "
|
||||
"razvijalce ALSA."
|
||||
|
||||
#: spa/plugins/alsa/acp/channelmap.h:457
|
||||
#: spa/plugins/alsa/acp/channelmap.h:460
|
||||
msgid "(invalid)"
|
||||
msgstr "(neveljavno)"
|
||||
|
||||
|
|
@ -680,100 +680,100 @@ msgstr "Vgrajen zvok"
|
|||
msgid "Modem"
|
||||
msgstr "Modem"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1985
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1987
|
||||
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
|
||||
msgstr "Zvožni prehod (vir A2DP in HSP/HFP AG)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2014
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2016
|
||||
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
|
||||
msgstr "Pretakanje zvoka za slušne aparate (ponor ASHA)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2057
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2059
|
||||
#, c-format
|
||||
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
|
||||
msgstr "Predvajanje visoke ločljivosti (ponor A2DP, kodek %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2060
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2062
|
||||
#, c-format
|
||||
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
|
||||
msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP, kodek %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2068
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2070
|
||||
msgid "High Fidelity Playback (A2DP Sink)"
|
||||
msgstr "Predvajanje visoke ločljivosti (ponor A2DP)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2070
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2072
|
||||
msgid "High Fidelity Duplex (A2DP Source/Sink)"
|
||||
msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2144
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2146
|
||||
#, c-format
|
||||
msgid "High Fidelity Playback (BAP Sink, codec %s)"
|
||||
msgstr "Predvajanje visoke ločljivosti (ponor BAP, kodek %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2149
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2151
|
||||
#, c-format
|
||||
msgid "High Fidelity Input (BAP Source, codec %s)"
|
||||
msgstr "Vhod visoke ločljivosti (vir BAP, kodek %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2153
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2155
|
||||
#, c-format
|
||||
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
|
||||
msgstr "Dupleks visoke ločljivosti (vir/ponor BAP, kodek %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2162
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2164
|
||||
msgid "High Fidelity Playback (BAP Sink)"
|
||||
msgstr "Predvajanje visoke ločljivosti (ponor BAP)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2166
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2168
|
||||
msgid "High Fidelity Input (BAP Source)"
|
||||
msgstr "Vhod visoke ločljivosti (vir BAP)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2169
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2171
|
||||
msgid "High Fidelity Duplex (BAP Source/Sink)"
|
||||
msgstr "Dupleks visoke ločljivosti (vir/ponor BAP)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2209
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2211
|
||||
#, c-format
|
||||
msgid "Headset Head Unit (HSP/HFP, codec %s)"
|
||||
msgstr "Naglavna enota slušalk (HSP/HFP, kodek %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2361
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2366
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2373
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2379
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2385
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2391
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2397
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2403
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2409
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2363
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2368
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2375
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2381
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2387
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2393
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2399
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2405
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2411
|
||||
msgid "Handsfree"
|
||||
msgstr "Prostoročno telefoniranje"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2367
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2369
|
||||
msgid "Handsfree (HFP)"
|
||||
msgstr "Prostoročno telefoniranje (HFP)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2390
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2392
|
||||
msgid "Portable"
|
||||
msgstr "Prenosna naprava"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2396
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2398
|
||||
msgid "Car"
|
||||
msgstr "Avtomobil"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2402
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2404
|
||||
msgid "HiFi"
|
||||
msgstr "HiFi"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2408
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2410
|
||||
msgid "Phone"
|
||||
msgstr "Telefon"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2415
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2417
|
||||
msgid "Bluetooth"
|
||||
msgstr "Bluetooth"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2416
|
||||
msgid "Bluetooth (HFP)"
|
||||
msgstr "Bluetooth (HFP)"
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2418
|
||||
msgid "Bluetooth Handsfree"
|
||||
msgstr "Bluetooth - prostoročno"
|
||||
|
|
|
|||
341
po/tr.po
341
po/tr.po
|
|
@ -1,46 +1,51 @@
|
|||
# Turkish translation for PipeWire.
|
||||
# Copyright (C) 2014-2024 PipeWire's COPYRIGHT HOLDER
|
||||
# Copyright (C) 2014-2025 PipeWire's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PipeWire package.
|
||||
#
|
||||
# Necdet Yücel <necdetyucel@gmail.com>, 2014.
|
||||
# Kaan Özdinçer <kaanozdincer@gmail.com>, 2014.
|
||||
# Muhammet Kara <muhammetk@gmail.com>, 2015, 2016, 2017.
|
||||
# Oğuz Ersen <oguz@ersen.moe>, 2021-2022.
|
||||
# Sabri Ünal <libreajans@gmail.com>, 2024.
|
||||
# Sabri Ünal <yakushabb@gmail.com>, 2024, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PipeWire master\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-25 03:43+0300\n"
|
||||
"PO-Revision-Date: 2024-02-25 03:49+0300\n"
|
||||
"Last-Translator: Sabri Ünal <libreajans@gmail.com>\n"
|
||||
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2025-10-24 15:37+0000\n"
|
||||
"PO-Revision-Date: 2025-10-24 20:15+0300\n"
|
||||
"Last-Translator: Sabri Ünal <yakushabb@gmail.com>\n"
|
||||
"Language-Team: Türkçe <takim@gnome.org.tr>\n"
|
||||
"Language: tr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Poedit 3.4.2\n"
|
||||
"X-Generator: Poedit 3.8\n"
|
||||
|
||||
#: src/daemon/pipewire.c:26
|
||||
#: src/daemon/pipewire.c:29
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s [options]\n"
|
||||
" -h, --help Show this help\n"
|
||||
" -v, --verbose Increase verbosity by one level\n"
|
||||
" --version Show version\n"
|
||||
" -c, --config Load config (Default %s)\n"
|
||||
" -P --properties Set context properties\n"
|
||||
msgstr ""
|
||||
"%s [seçenekler]\n"
|
||||
" -h, --help Bu yardımı göster\n"
|
||||
" -v, --verbose Ayrıntı düzeyini bir düzey artır\n"
|
||||
" --version Sürümü göster\n"
|
||||
" -c, --config Yapılandırmayı yükle (Öntanımlı %s)\n"
|
||||
" -P --properties Bağlam özelliklerini ayarla\n"
|
||||
|
||||
#: src/daemon/pipewire.desktop.in:4
|
||||
#: src/daemon/pipewire.desktop.in:3
|
||||
msgid "PipeWire Media System"
|
||||
msgstr "PipeWire Ortam Sistemi"
|
||||
|
||||
#: src/daemon/pipewire.desktop.in:5
|
||||
#: src/daemon/pipewire.desktop.in:4
|
||||
msgid "Start the PipeWire Media System"
|
||||
msgstr "PipeWire Ortam Sistemini Başlat"
|
||||
|
||||
|
|
@ -54,26 +59,26 @@ msgstr "%s%s%s tüneli"
|
|||
msgid "Dummy Output"
|
||||
msgstr "Temsili Çıkış"
|
||||
|
||||
#: src/modules/module-pulse-tunnel.c:774
|
||||
#: src/modules/module-pulse-tunnel.c:760
|
||||
#, c-format
|
||||
msgid "Tunnel for %s@%s"
|
||||
msgstr "%s@%s için tünel"
|
||||
|
||||
#: src/modules/module-zeroconf-discover.c:315
|
||||
#: src/modules/module-zeroconf-discover.c:320
|
||||
msgid "Unknown device"
|
||||
msgstr "Bilinmeyen aygıt"
|
||||
|
||||
#: src/modules/module-zeroconf-discover.c:327
|
||||
#: src/modules/module-zeroconf-discover.c:332
|
||||
#, c-format
|
||||
msgid "%s on %s@%s"
|
||||
msgstr "%s, %s@%s"
|
||||
|
||||
#: src/modules/module-zeroconf-discover.c:331
|
||||
#: src/modules/module-zeroconf-discover.c:336
|
||||
#, c-format
|
||||
msgid "%s on %s"
|
||||
msgstr "%s, %s"
|
||||
|
||||
#: src/tools/pw-cat.c:991
|
||||
#: src/tools/pw-cat.c:1096
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s [options] [<file>|-]\n"
|
||||
|
|
@ -88,7 +93,7 @@ msgstr ""
|
|||
" -v, --verbose Ayrıntılı işlemleri etkinleştir\n"
|
||||
"\n"
|
||||
|
||||
#: src/tools/pw-cat.c:998
|
||||
#: src/tools/pw-cat.c:1103
|
||||
#, c-format
|
||||
msgid ""
|
||||
" -R, --remote Remote daemon name\n"
|
||||
|
|
@ -122,7 +127,7 @@ msgstr ""
|
|||
" -P --properties Düğüm özelliklerini ayarla\n"
|
||||
"\n"
|
||||
|
||||
#: src/tools/pw-cat.c:1016
|
||||
#: src/tools/pw-cat.c:1121
|
||||
#, c-format
|
||||
msgid ""
|
||||
" --rate Sample rate (req. for rec) (default "
|
||||
|
|
@ -139,6 +144,10 @@ msgid ""
|
|||
" --volume Stream volume 0-1.0 (default %.3f)\n"
|
||||
" -q --quality Resampler quality (0 - 15) (default "
|
||||
"%d)\n"
|
||||
" -a, --raw RAW mode\n"
|
||||
" -M, --force-midi Force midi format, one of \"midi\" "
|
||||
"or \"ump\", (default ump)\n"
|
||||
" -n, --sample-count COUNT Stop after COUNT samples\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
" --rate Örnekleme oranı (kayıt için gerekli) "
|
||||
|
|
@ -156,15 +165,21 @@ msgstr ""
|
|||
"%.3f)\n"
|
||||
" -q --quality Yeniden örnekleyici kalitesi (0 - "
|
||||
"15) (öntanımlı %d)\n"
|
||||
" -a, --raw HAM kipi\n"
|
||||
" -M, --force-midi Midi biçimini zorla, ikisinden "
|
||||
"birisi \"midi\" ya da\"ump\", (öntanımlı ump)\n"
|
||||
" -n, --sample-count COUNT COUNT örnekleme sonrası dur\n"
|
||||
"\n"
|
||||
|
||||
#: src/tools/pw-cat.c:1033
|
||||
#: src/tools/pw-cat.c:1141
|
||||
msgid ""
|
||||
" -p, --playback Playback mode\n"
|
||||
" -r, --record Recording mode\n"
|
||||
" -m, --midi Midi mode\n"
|
||||
" -d, --dsd DSD mode\n"
|
||||
" -o, --encoded Encoded mode\n"
|
||||
" -s, --sysex SysEx mode\n"
|
||||
" -c, --midi-clip MIDI clip mode\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
" -p, --playback Çalma kipi\n"
|
||||
|
|
@ -172,9 +187,11 @@ msgstr ""
|
|||
" -m, --midi Midi kipi\n"
|
||||
" -d, --dsd DSD kipi\n"
|
||||
" -o, --encoded Kodlanmış kip\n"
|
||||
" -s, --sysex SysEx kipi\n"
|
||||
" -c, --midi-clip MIDI klip kipi\n"
|
||||
"\n"
|
||||
|
||||
#: src/tools/pw-cli.c:2252
|
||||
#: src/tools/pw-cli.c:2386
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s [options] [command]\n"
|
||||
|
|
@ -193,195 +210,203 @@ msgstr ""
|
|||
" -r, --remote Uzak arka plan programı adı\n"
|
||||
" -m, --monitor Etkinliği izle\n"
|
||||
|
||||
#: spa/plugins/alsa/acp/acp.c:327
|
||||
#: spa/plugins/alsa/acp/acp.c:361
|
||||
msgid "Pro Audio"
|
||||
msgstr "Profesyonel Ses"
|
||||
|
||||
#: spa/plugins/alsa/acp/acp.c:488 spa/plugins/alsa/acp/alsa-mixer.c:4633
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1701
|
||||
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1976
|
||||
msgid "Off"
|
||||
msgstr "Kapalı"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2652
|
||||
#: spa/plugins/alsa/acp/acp.c:620
|
||||
#, c-format
|
||||
msgid "%s [ALSA UCM error]"
|
||||
msgstr "%s [ALSA UCM hatası]"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
|
||||
msgid "Input"
|
||||
msgstr "Giriş"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2653
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
|
||||
msgid "Docking Station Input"
|
||||
msgstr "Yerleştirme İstasyonu Girişi"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2654
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
|
||||
msgid "Docking Station Microphone"
|
||||
msgstr "Yerleştirme İstasyonu Mikrofonu"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2655
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
|
||||
msgid "Docking Station Line In"
|
||||
msgstr "Yerleştirme İstasyonu Hat Girişi"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2656
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2747
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
|
||||
msgid "Line In"
|
||||
msgstr "Hat Girişi"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2657
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1989
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2374
|
||||
msgid "Microphone"
|
||||
msgstr "Mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2658
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
|
||||
msgid "Front Microphone"
|
||||
msgstr "Ön Mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2659
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2743
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
|
||||
msgid "Rear Microphone"
|
||||
msgstr "Arka Mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2660
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
|
||||
msgid "External Microphone"
|
||||
msgstr "Harici Mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2661
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2745
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
|
||||
msgid "Internal Microphone"
|
||||
msgstr "Dahili Mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2662
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2748
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2731
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
|
||||
msgid "Radio"
|
||||
msgstr "Radyo"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2663
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2749
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2732
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2664
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2733
|
||||
msgid "Automatic Gain Control"
|
||||
msgstr "Otomatik Kazanç Denetimi"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2665
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2734
|
||||
msgid "No Automatic Gain Control"
|
||||
msgstr "Otomatik Kazanç Denetimi Yok"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2666
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2735
|
||||
msgid "Boost"
|
||||
msgstr "Artır"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2667
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2736
|
||||
msgid "No Boost"
|
||||
msgstr "Artırma Yok"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2668
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2737
|
||||
msgid "Amplifier"
|
||||
msgstr "Yükseltici"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2669
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2738
|
||||
msgid "No Amplifier"
|
||||
msgstr "Yükseltici Yok"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2670
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2739
|
||||
msgid "Bass Boost"
|
||||
msgstr "Bas Artır"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2671
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
|
||||
msgid "No Bass Boost"
|
||||
msgstr "Bas Artırma Yok"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2672
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1995
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2380
|
||||
msgid "Speaker"
|
||||
msgstr "Hoparlör"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2673
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2751
|
||||
#. Don't call it "headset", the HF one has the mic
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2386
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2453
|
||||
msgid "Headphones"
|
||||
msgstr "Kulaklık"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
|
||||
msgid "Analog Input"
|
||||
msgstr "Analog Giriş"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2744
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
|
||||
msgid "Dock Microphone"
|
||||
msgstr "Yapışık Mikrofon"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2746
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
|
||||
msgid "Headset Microphone"
|
||||
msgstr "Mikrofonlu Kulaklık"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2750
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
|
||||
msgid "Analog Output"
|
||||
msgstr "Analog Çıkış"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2752
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
|
||||
msgid "Headphones 2"
|
||||
msgstr "Kulaklık 2"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2753
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
|
||||
msgid "Headphones Mono Output"
|
||||
msgstr "Kulaklık Tek Kanallı Çıkış"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2754
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
|
||||
msgid "Line Out"
|
||||
msgstr "Hat Çıkışı"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2755
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2824
|
||||
msgid "Analog Mono Output"
|
||||
msgstr "Analog Tek Kanallı Çıkış"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2756
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2825
|
||||
msgid "Speakers"
|
||||
msgstr "Hoparlörler"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2757
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2826
|
||||
msgid "HDMI / DisplayPort"
|
||||
msgstr "HDMI / DisplayPort"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2758
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2827
|
||||
msgid "Digital Output (S/PDIF)"
|
||||
msgstr "Sayısal Çıkış (S/PDIF)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2759
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2828
|
||||
msgid "Digital Input (S/PDIF)"
|
||||
msgstr "Sayısal Giriş (S/PDIF)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2760
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2829
|
||||
msgid "Multichannel Input"
|
||||
msgstr "Çok Kanallı Giriş"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2761
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2830
|
||||
msgid "Multichannel Output"
|
||||
msgstr "Çok Kanallı Çıkış"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2762
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2831
|
||||
msgid "Game Output"
|
||||
msgstr "Oyun Çıkışı"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2763
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2764
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2832
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2833
|
||||
msgid "Chat Output"
|
||||
msgstr "Sohbet Çıkışı"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2765
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2834
|
||||
msgid "Chat Input"
|
||||
msgstr "Sohbet Girişi"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2766
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2835
|
||||
msgid "Virtual Surround 7.1"
|
||||
msgstr "Sanal Çevresel Ses 7.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4456
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4522
|
||||
msgid "Analog Mono"
|
||||
msgstr "Analog Tek Kanallı"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4457
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4523
|
||||
msgid "Analog Mono (Left)"
|
||||
msgstr "Analog Tek Kanallı (Sol)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4458
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4524
|
||||
msgid "Analog Mono (Right)"
|
||||
msgstr "Analog Tek Kanallı (Sağ)"
|
||||
|
||||
|
|
@ -390,147 +415,147 @@ msgstr "Analog Tek Kanallı (Sağ)"
|
|||
#. * here would lead to the source name to become "Analog Stereo Input
|
||||
#. * Input". The same logic applies to analog-stereo-output,
|
||||
#. * multichannel-input and multichannel-output.
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4459
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4467
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4468
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4525
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4533
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4534
|
||||
msgid "Analog Stereo"
|
||||
msgstr "Analog Stereo"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4460
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4526
|
||||
msgid "Mono"
|
||||
msgstr "Tek Kanallı"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4461
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
|
||||
msgid "Stereo"
|
||||
msgstr "Stereo"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4469
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4627
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1977
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2362
|
||||
msgid "Headset"
|
||||
msgstr "Kulaklık"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4470
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4628
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4536
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4694
|
||||
msgid "Speakerphone"
|
||||
msgstr "Hoparlör"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4471
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4472
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4537
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
|
||||
msgid "Multichannel"
|
||||
msgstr "Çok kanallı"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4473
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
|
||||
msgid "Analog Surround 2.1"
|
||||
msgstr "Analog Çevresel Ses 2.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4474
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
|
||||
msgid "Analog Surround 3.0"
|
||||
msgstr "Analog Çevresel Ses 3.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4475
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
|
||||
msgid "Analog Surround 3.1"
|
||||
msgstr "Analog Çevresel Ses 3.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4476
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
|
||||
msgid "Analog Surround 4.0"
|
||||
msgstr "Analog Çevresel Ses 4.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4477
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
|
||||
msgid "Analog Surround 4.1"
|
||||
msgstr "Analog Çevresel Ses 4.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4478
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
|
||||
msgid "Analog Surround 5.0"
|
||||
msgstr "Analog Çevresel Ses 5.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4479
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
|
||||
msgid "Analog Surround 5.1"
|
||||
msgstr "Analog Çevresel Ses 5.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4480
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
|
||||
msgid "Analog Surround 6.0"
|
||||
msgstr "Analog Çevresel Ses 6.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4481
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
|
||||
msgid "Analog Surround 6.1"
|
||||
msgstr "Analog Çevresel Ses 6.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4482
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
|
||||
msgid "Analog Surround 7.0"
|
||||
msgstr "Analog Çevresel Ses 7.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4483
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
|
||||
msgid "Analog Surround 7.1"
|
||||
msgstr "Analog Çevresel Ses 7.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4484
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
|
||||
msgid "Digital Stereo (IEC958)"
|
||||
msgstr "Sayısal Stereo (IEC958)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4485
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
|
||||
msgid "Digital Surround 4.0 (IEC958/AC3)"
|
||||
msgstr "Sayısal Çevresel Ses 4.0 (IEC958/AC3)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4486
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
|
||||
msgid "Digital Surround 5.1 (IEC958/AC3)"
|
||||
msgstr "Sayısal Çevresel Ses 5.1 (IEC958/AC3)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4487
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
|
||||
msgid "Digital Surround 5.1 (IEC958/DTS)"
|
||||
msgstr "Sayısal Çevresel Ses 5.1 (IEC958/DTS)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4488
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
|
||||
msgid "Digital Stereo (HDMI)"
|
||||
msgstr "Sayısal Stereo (HDMI)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4489
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
|
||||
msgid "Digital Surround 5.1 (HDMI)"
|
||||
msgstr "Sayısal Çevresel Ses 5.1 (HDMI)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4490
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
|
||||
msgid "Chat"
|
||||
msgstr "Sohbet"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4491
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
|
||||
msgid "Game"
|
||||
msgstr "Oyun"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4625
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4691
|
||||
msgid "Analog Mono Duplex"
|
||||
msgstr "Analog Tek Kanallı İkili"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4626
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4692
|
||||
msgid "Analog Stereo Duplex"
|
||||
msgstr "Analog İkili Stereo"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4629
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4695
|
||||
msgid "Digital Stereo Duplex (IEC958)"
|
||||
msgstr "Sayısal İkili Stereo (IEC958)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4630
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
|
||||
msgid "Multichannel Duplex"
|
||||
msgstr "Çok Kanallı İkili"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4631
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
|
||||
msgid "Stereo Duplex"
|
||||
msgstr "İkili Stereo"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4632
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
|
||||
msgid "Mono Chat + 7.1 Surround"
|
||||
msgstr "Tek Kanallı Sohbet + 7.1 Çevresel Ses"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4733
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4799
|
||||
#, c-format
|
||||
msgid "%s Output"
|
||||
msgstr "%s Çıkışı"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4741
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4807
|
||||
#, c-format
|
||||
msgid "%s Input"
|
||||
msgstr "%s Girişi"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-util.c:1220 spa/plugins/alsa/acp/alsa-util.c:1314
|
||||
#: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
|
||||
#, c-format
|
||||
msgid ""
|
||||
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
|
||||
|
|
@ -547,16 +572,16 @@ msgstr[0] ""
|
|||
"Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
|
||||
"geliştiricilerine bildirin."
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-util.c:1286
|
||||
#: spa/plugins/alsa/acp/alsa-util.c:1299
|
||||
#, c-format
|
||||
msgid ""
|
||||
"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
|
||||
"%lu ms).\n"
|
||||
"snd_pcm_delay() returned a value that is exceptionally large: %li byte "
|
||||
"(%s%lu ms).\n"
|
||||
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
|
||||
"to the ALSA developers."
|
||||
msgid_plural ""
|
||||
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
|
||||
"%lu ms).\n"
|
||||
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
|
||||
"(%s%lu ms).\n"
|
||||
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
|
||||
"to the ALSA developers."
|
||||
msgstr[0] ""
|
||||
|
|
@ -564,7 +589,7 @@ msgstr[0] ""
|
|||
"Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
|
||||
"geliştiricilerine bildirin."
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-util.c:1333
|
||||
#: spa/plugins/alsa/acp/alsa-util.c:1346
|
||||
#, c-format
|
||||
msgid ""
|
||||
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
|
||||
|
|
@ -577,7 +602,7 @@ msgstr ""
|
|||
"Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
|
||||
"geliştiricilerine bildirin."
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-util.c:1376
|
||||
#: spa/plugins/alsa/acp/alsa-util.c:1389
|
||||
#, c-format
|
||||
msgid ""
|
||||
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
|
||||
|
|
@ -595,112 +620,112 @@ msgstr[0] ""
|
|||
"Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
|
||||
"geliştiricilerine bildirin."
|
||||
|
||||
#: spa/plugins/alsa/acp/channelmap.h:457
|
||||
#: spa/plugins/alsa/acp/channelmap.h:460
|
||||
msgid "(invalid)"
|
||||
msgstr "(geçersiz)"
|
||||
|
||||
#: spa/plugins/alsa/acp/compat.c:193
|
||||
#: spa/plugins/alsa/acp/compat.c:194
|
||||
msgid "Built-in Audio"
|
||||
msgstr "Dahili Ses"
|
||||
|
||||
#: spa/plugins/alsa/acp/compat.c:198
|
||||
#: spa/plugins/alsa/acp/compat.c:199
|
||||
msgid "Modem"
|
||||
msgstr "Modem"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1712
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1987
|
||||
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
|
||||
msgstr "Ses Geçidi (A2DP Kaynak & HSP/HFP AG)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1760
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2016
|
||||
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
|
||||
msgstr "İşitme Aygıtları İçin Ses Akışı (ASHA Alıcı)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2059
|
||||
#, c-format
|
||||
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
|
||||
msgstr "Yüksek Kaliteli Çalma (A2DP Alıcı, çözücü %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1763
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2062
|
||||
#, c-format
|
||||
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
|
||||
msgstr "Yüksek Kaliteli İkili (A2DP Kaynak/Alıcı, çözücü %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1771
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2070
|
||||
msgid "High Fidelity Playback (A2DP Sink)"
|
||||
msgstr "Yüksek Kaliteli Çalma (A2DP Alıcı)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1773
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2072
|
||||
msgid "High Fidelity Duplex (A2DP Source/Sink)"
|
||||
msgstr "Yüksek Kaliteli İkili (A2DP Kaynak/Alıcı)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1823
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2146
|
||||
#, c-format
|
||||
msgid "High Fidelity Playback (BAP Sink, codec %s)"
|
||||
msgstr "Yüksek Kaliteli Çalma (BAP Alıcı, çözücü %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1828
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2151
|
||||
#, c-format
|
||||
msgid "High Fidelity Input (BAP Source, codec %s)"
|
||||
msgstr "Yüksek Kaliteli Giriş (BAP Kaynak, çözücü %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1832
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2155
|
||||
#, c-format
|
||||
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
|
||||
msgstr "Yüksek Kaliteli İkili (BAP Kaynak/Alıcı, çözücü %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1841
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2164
|
||||
msgid "High Fidelity Playback (BAP Sink)"
|
||||
msgstr "Yüksek Kaliteli Çalma (BAP Alıcı)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1845
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2168
|
||||
msgid "High Fidelity Input (BAP Source)"
|
||||
msgstr "Yüksek Kaliteli Giriş (BAP Kaynak)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1848
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2171
|
||||
msgid "High Fidelity Duplex (BAP Source/Sink)"
|
||||
msgstr "Yüksek Kaliteli İkili (BAP Kaynak/Alıcı)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1897
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2211
|
||||
#, c-format
|
||||
msgid "Headset Head Unit (HSP/HFP, codec %s)"
|
||||
msgstr "Kulaklık Ana Birimi (HSP/HFP, çözücü %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1978
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1983
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1990
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1996
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2002
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2008
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2014
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2020
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2026
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2363
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2368
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2375
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2381
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2387
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2393
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2399
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2405
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2411
|
||||
msgid "Handsfree"
|
||||
msgstr "Ahizesiz"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1984
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2369
|
||||
msgid "Handsfree (HFP)"
|
||||
msgstr "Ahizesiz (HFP)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2001
|
||||
msgid "Headphone"
|
||||
msgstr "Kulaklık"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2007
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2392
|
||||
msgid "Portable"
|
||||
msgstr "Taşınabilir"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2013
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2398
|
||||
msgid "Car"
|
||||
msgstr "Araba"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2019
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2404
|
||||
msgid "HiFi"
|
||||
msgstr "Yüksek Kalite"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2025
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2410
|
||||
msgid "Phone"
|
||||
msgstr "Telefon"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2032
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2417
|
||||
msgid "Bluetooth"
|
||||
msgstr "Bluetooth"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2033
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2418
|
||||
msgid "Bluetooth (HFP)"
|
||||
msgstr "Bluetooth (HFP)"
|
||||
|
|
|
|||
290
po/zh_CN.po
290
po/zh_CN.po
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"Project-Id-Version: pipewire.master-tx\n"
|
||||
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2025-09-21 15:33+0000\n"
|
||||
"PO-Revision-Date: 2025-09-22 08:53+0800\n"
|
||||
"POT-Creation-Date: 2025-11-25 15:35+0000\n"
|
||||
"PO-Revision-Date: 2025-11-26 10:19+0800\n"
|
||||
"Last-Translator: lumingzh <lumingzh@qq.com>\n"
|
||||
"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
|
||||
"Language: zh_CN\n"
|
||||
|
|
@ -60,7 +60,7 @@ msgstr "至 %s%s%s 的隧道"
|
|||
msgid "Dummy Output"
|
||||
msgstr "虚拟输出"
|
||||
|
||||
#: src/modules/module-pulse-tunnel.c:760
|
||||
#: src/modules/module-pulse-tunnel.c:761
|
||||
#, c-format
|
||||
msgid "Tunnel for %s@%s"
|
||||
msgstr "用于 %s@%s 的隧道"
|
||||
|
|
@ -79,7 +79,7 @@ msgstr "%2$s@%3$s 上的 %1$s"
|
|||
msgid "%s on %s"
|
||||
msgstr "%2$s 上的 %1$s"
|
||||
|
||||
#: src/tools/pw-cat.c:1084
|
||||
#: src/tools/pw-cat.c:1088
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s [options] [<file>|-]\n"
|
||||
|
|
@ -94,7 +94,7 @@ msgstr ""
|
|||
" -v, --verbose 输出详细操作\n"
|
||||
"\n"
|
||||
|
||||
#: src/tools/pw-cat.c:1091
|
||||
#: src/tools/pw-cat.c:1095
|
||||
#, c-format
|
||||
msgid ""
|
||||
" -R, --remote Remote daemon name\n"
|
||||
|
|
@ -126,7 +126,7 @@ msgstr ""
|
|||
" -P --properties 设置节点属性\n"
|
||||
"\n"
|
||||
|
||||
#: src/tools/pw-cat.c:1109
|
||||
#: src/tools/pw-cat.c:1113
|
||||
#, c-format
|
||||
msgid ""
|
||||
" --rate Sample rate (req. for rec) (default "
|
||||
|
|
@ -134,8 +134,8 @@ msgid ""
|
|||
" --channels Number of channels (req. for rec) "
|
||||
"(default %u)\n"
|
||||
" --channel-map Channel map\n"
|
||||
" one of: \"stereo\", "
|
||||
"\"surround-51\",... or\n"
|
||||
" one of: \"Stereo\", \"5.1\",... "
|
||||
"or\n"
|
||||
" comma separated list of channel "
|
||||
"names: eg. \"FL,FR\"\n"
|
||||
" --format Sample format %s (req. for rec) "
|
||||
|
|
@ -152,9 +152,9 @@ msgstr ""
|
|||
" --rate 采样率 (录制模式需要) (默认 %u)\n"
|
||||
" --channels 通道数 (录制模式需要) (默认 %u)\n"
|
||||
" --channel-map 通道映射\n"
|
||||
" \"stereo\", \"surround-51\",... "
|
||||
"中的其一或\n"
|
||||
" 以\",\"分隔的通道名列表: 如 "
|
||||
" \"stereo\", \"5.1\",... 中的其一"
|
||||
"或\n"
|
||||
" 以英文逗号分隔的通道名列表: 如 "
|
||||
"\"FL,FR\"\n"
|
||||
" --format 采样格式 %s (录制模式需要) (默认 "
|
||||
"%s)\n"
|
||||
|
|
@ -166,7 +166,7 @@ msgstr ""
|
|||
" -n, --sample-count COUNT 计数采样后停止\n"
|
||||
"\n"
|
||||
|
||||
#: src/tools/pw-cat.c:1129
|
||||
#: src/tools/pw-cat.c:1133
|
||||
msgid ""
|
||||
" -p, --playback Playback mode\n"
|
||||
" -r, --record Recording mode\n"
|
||||
|
|
@ -204,203 +204,203 @@ msgstr ""
|
|||
" -m, --monitor 监视器活动\n"
|
||||
"\n"
|
||||
|
||||
#: spa/plugins/alsa/acp/acp.c:351
|
||||
#: spa/plugins/alsa/acp/acp.c:361
|
||||
msgid "Pro Audio"
|
||||
msgstr "专业音频"
|
||||
|
||||
#: spa/plugins/alsa/acp/acp.c:527 spa/plugins/alsa/acp/alsa-mixer.c:4635
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1974
|
||||
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1976
|
||||
msgid "Off"
|
||||
msgstr "关"
|
||||
|
||||
#: spa/plugins/alsa/acp/acp.c:610
|
||||
#: spa/plugins/alsa/acp/acp.c:620
|
||||
#, c-format
|
||||
msgid "%s [ALSA UCM error]"
|
||||
msgstr "%s [ALSA UCM 错误]"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2652
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
|
||||
msgid "Input"
|
||||
msgstr "输入"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2653
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
|
||||
msgid "Docking Station Input"
|
||||
msgstr "扩展坞输入"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2654
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
|
||||
msgid "Docking Station Microphone"
|
||||
msgstr "扩展坞话筒"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2655
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
|
||||
msgid "Docking Station Line In"
|
||||
msgstr "扩展坞线输入"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2656
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2747
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
|
||||
msgid "Line In"
|
||||
msgstr "输入插孔"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2657
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2372
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2374
|
||||
msgid "Microphone"
|
||||
msgstr "话筒"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2658
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
|
||||
msgid "Front Microphone"
|
||||
msgstr "前麦克风"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2659
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2743
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
|
||||
msgid "Rear Microphone"
|
||||
msgstr "后麦克风"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2660
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
|
||||
msgid "External Microphone"
|
||||
msgstr "外部话筒"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2661
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2745
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
|
||||
msgid "Internal Microphone"
|
||||
msgstr "内部话筒"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2662
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2748
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2731
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
|
||||
msgid "Radio"
|
||||
msgstr "无线电"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2663
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2749
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2732
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
|
||||
msgid "Video"
|
||||
msgstr "视频"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2664
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2733
|
||||
msgid "Automatic Gain Control"
|
||||
msgstr "自动增益控制"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2665
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2734
|
||||
msgid "No Automatic Gain Control"
|
||||
msgstr "无自动增益控制"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2666
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2735
|
||||
msgid "Boost"
|
||||
msgstr "增强"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2667
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2736
|
||||
msgid "No Boost"
|
||||
msgstr "无增强"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2668
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2737
|
||||
msgid "Amplifier"
|
||||
msgstr "功放"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2669
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2738
|
||||
msgid "No Amplifier"
|
||||
msgstr "无功放"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2670
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2739
|
||||
msgid "Bass Boost"
|
||||
msgstr "重低音增强"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2671
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
|
||||
msgid "No Bass Boost"
|
||||
msgstr "无重低音增强"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2672
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2378
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2380
|
||||
msgid "Speaker"
|
||||
msgstr "扬声器"
|
||||
|
||||
#. Don't call it "headset", the HF one has the mic
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2673
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2751
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2384
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2451
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2386
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2453
|
||||
msgid "Headphones"
|
||||
msgstr "模拟耳机"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
|
||||
msgid "Analog Input"
|
||||
msgstr "模拟输入"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2744
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
|
||||
msgid "Dock Microphone"
|
||||
msgstr "扩展坞麦克风"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2746
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
|
||||
msgid "Headset Microphone"
|
||||
msgstr "头挂麦克风"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2750
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
|
||||
msgid "Analog Output"
|
||||
msgstr "模拟输出"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2752
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
|
||||
msgid "Headphones 2"
|
||||
msgstr "模拟耳机 2"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2753
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
|
||||
msgid "Headphones Mono Output"
|
||||
msgstr "模拟单声道输出"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2754
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
|
||||
msgid "Line Out"
|
||||
msgstr "线缆输出"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2755
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2824
|
||||
msgid "Analog Mono Output"
|
||||
msgstr "模拟单声道输出"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2756
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2825
|
||||
msgid "Speakers"
|
||||
msgstr "扬声器"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2757
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2826
|
||||
msgid "HDMI / DisplayPort"
|
||||
msgstr "HDMI / DisplayPort"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2758
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2827
|
||||
msgid "Digital Output (S/PDIF)"
|
||||
msgstr "数字输出 (S/PDIF)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2759
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2828
|
||||
msgid "Digital Input (S/PDIF)"
|
||||
msgstr "数字输入 (S/PDIF)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2760
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2829
|
||||
msgid "Multichannel Input"
|
||||
msgstr "多声道输入"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2761
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2830
|
||||
msgid "Multichannel Output"
|
||||
msgstr "多声道输出"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2762
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2831
|
||||
msgid "Game Output"
|
||||
msgstr "游戏输出"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2763
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2764
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2832
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2833
|
||||
msgid "Chat Output"
|
||||
msgstr "语音输出"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2765
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2834
|
||||
msgid "Chat Input"
|
||||
msgstr "语音输入"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2766
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2835
|
||||
msgid "Virtual Surround 7.1"
|
||||
msgstr "虚拟环绕 7.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4458
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4522
|
||||
msgid "Analog Mono"
|
||||
msgstr "模拟单声道"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4459
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4523
|
||||
msgid "Analog Mono (Left)"
|
||||
msgstr "模拟单声道 (左声道)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4460
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4524
|
||||
msgid "Analog Mono (Right)"
|
||||
msgstr "模拟单声道 (右声道)"
|
||||
|
||||
|
|
@ -409,142 +409,142 @@ msgstr "模拟单声道 (右声道)"
|
|||
#. * here would lead to the source name to become "Analog Stereo Input
|
||||
#. * Input". The same logic applies to analog-stereo-output,
|
||||
#. * multichannel-input and multichannel-output.
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4461
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4469
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4470
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4525
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4533
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4534
|
||||
msgid "Analog Stereo"
|
||||
msgstr "模拟立体声"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4462
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4526
|
||||
msgid "Mono"
|
||||
msgstr "单声道"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4463
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
|
||||
msgid "Stereo"
|
||||
msgstr "立体声"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4471
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4629
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2360
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2362
|
||||
msgid "Headset"
|
||||
msgstr "耳机"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4472
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4630
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4536
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4694
|
||||
msgid "Speakerphone"
|
||||
msgstr "扬声麦克风"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4473
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4474
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4537
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
|
||||
msgid "Multichannel"
|
||||
msgstr "多声道"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4475
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
|
||||
msgid "Analog Surround 2.1"
|
||||
msgstr "模拟环绕 2.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4476
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
|
||||
msgid "Analog Surround 3.0"
|
||||
msgstr "模拟环绕 3.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4477
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
|
||||
msgid "Analog Surround 3.1"
|
||||
msgstr "模拟环绕 3.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4478
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
|
||||
msgid "Analog Surround 4.0"
|
||||
msgstr "模拟环绕 4.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4479
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
|
||||
msgid "Analog Surround 4.1"
|
||||
msgstr "模拟环绕 4.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4480
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
|
||||
msgid "Analog Surround 5.0"
|
||||
msgstr "模拟环绕 5.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4481
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
|
||||
msgid "Analog Surround 5.1"
|
||||
msgstr "模拟环绕 5.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4482
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
|
||||
msgid "Analog Surround 6.0"
|
||||
msgstr "模拟环绕 6.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4483
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
|
||||
msgid "Analog Surround 6.1"
|
||||
msgstr "模拟环绕 6.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4484
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
|
||||
msgid "Analog Surround 7.0"
|
||||
msgstr "模拟环绕 7.0"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4485
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
|
||||
msgid "Analog Surround 7.1"
|
||||
msgstr "模拟环绕 7.1"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4486
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
|
||||
msgid "Digital Stereo (IEC958)"
|
||||
msgstr "数字立体声 (IEC958)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4487
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
|
||||
msgid "Digital Surround 4.0 (IEC958/AC3)"
|
||||
msgstr "数字环绕 4.0 (IEC958/AC3)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4488
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
|
||||
msgid "Digital Surround 5.1 (IEC958/AC3)"
|
||||
msgstr "数字环绕 5.1 (IEC958/AC3)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4489
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
|
||||
msgid "Digital Surround 5.1 (IEC958/DTS)"
|
||||
msgstr "数字环绕 5.1 (IEC958/DTS)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4490
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
|
||||
msgid "Digital Stereo (HDMI)"
|
||||
msgstr "数字立体声 (HDMI)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4491
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
|
||||
msgid "Digital Surround 5.1 (HDMI)"
|
||||
msgstr "数字环绕 5.1 (HDMI)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4492
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
|
||||
msgid "Chat"
|
||||
msgstr "语音"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4493
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
|
||||
msgid "Game"
|
||||
msgstr "游戏"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4627
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4691
|
||||
msgid "Analog Mono Duplex"
|
||||
msgstr "模拟单声道双工"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4628
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4692
|
||||
msgid "Analog Stereo Duplex"
|
||||
msgstr "模拟立体声双工"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4631
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4695
|
||||
msgid "Digital Stereo Duplex (IEC958)"
|
||||
msgstr "数字立体声双工 (IEC958)"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4632
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
|
||||
msgid "Multichannel Duplex"
|
||||
msgstr "多声道双工"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4633
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
|
||||
msgid "Stereo Duplex"
|
||||
msgstr "模拟立体声双工"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4634
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
|
||||
msgid "Mono Chat + 7.1 Surround"
|
||||
msgstr "单声道语音 + 7.1 环绕声"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4735
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4799
|
||||
#, c-format
|
||||
msgid "%s Output"
|
||||
msgstr "%s 输出"
|
||||
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4743
|
||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4807
|
||||
#, c-format
|
||||
msgid "%s Input"
|
||||
msgstr "%s 输入"
|
||||
|
|
@ -608,7 +608,7 @@ msgstr[0] ""
|
|||
"snd_pcm_mmap_begin() 返回的值非常大:%lu 字节(%lu ms)。\n"
|
||||
"这很可能是由 ALSA 驱动程序 %s 的缺陷导致的。请向 ALSA 开发者报告这个问题。"
|
||||
|
||||
#: spa/plugins/alsa/acp/channelmap.h:457
|
||||
#: spa/plugins/alsa/acp/channelmap.h:460
|
||||
msgid "(invalid)"
|
||||
msgstr "(无效)"
|
||||
|
||||
|
|
@ -620,103 +620,103 @@ msgstr "内置音频"
|
|||
msgid "Modem"
|
||||
msgstr "调制解调器"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1985
|
||||
#: spa/plugins/bluez5/bluez5-device.c:1987
|
||||
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
|
||||
msgstr "音频网关 (A2DP 信源 或 HSP/HFP 网关)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2014
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2016
|
||||
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
|
||||
msgstr "助听器音频流 (ASHA 信宿)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2057
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2059
|
||||
#, c-format
|
||||
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
|
||||
msgstr "高保真回放 (A2DP 信宿, 编码 %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2060
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2062
|
||||
#, c-format
|
||||
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
|
||||
msgstr "高保真双工 (A2DP 信源/信宿, 编码 %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2068
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2070
|
||||
msgid "High Fidelity Playback (A2DP Sink)"
|
||||
msgstr "高保真回放 (A2DP 信宿)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2070
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2072
|
||||
msgid "High Fidelity Duplex (A2DP Source/Sink)"
|
||||
msgstr "高保真双工 (A2DP 信源/信宿)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2144
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2146
|
||||
#, c-format
|
||||
msgid "High Fidelity Playback (BAP Sink, codec %s)"
|
||||
msgstr "高保真回放 (BAP 信宿, 编码 %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2149
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2151
|
||||
#, c-format
|
||||
msgid "High Fidelity Input (BAP Source, codec %s)"
|
||||
msgstr "高保真输入 (BAP 信源, 编码 %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2153
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2155
|
||||
#, c-format
|
||||
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
|
||||
msgstr "高保真双工 (BAP 信源/信宿, 编码 %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2162
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2164
|
||||
msgid "High Fidelity Playback (BAP Sink)"
|
||||
msgstr "高保真回放 (BAP 信宿)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2166
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2168
|
||||
msgid "High Fidelity Input (BAP Source)"
|
||||
msgstr "高保真输入 (BAP 信源)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2169
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2171
|
||||
msgid "High Fidelity Duplex (BAP Source/Sink)"
|
||||
msgstr "高保真双工 (BAP 信源/信宿)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2209
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2211
|
||||
#, c-format
|
||||
msgid "Headset Head Unit (HSP/HFP, codec %s)"
|
||||
msgstr "头戴式耳机单元 (HSP/HFP, 编码 %s)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2361
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2366
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2373
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2379
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2385
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2391
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2397
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2403
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2409
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2363
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2368
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2375
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2381
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2387
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2393
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2399
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2405
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2411
|
||||
msgid "Handsfree"
|
||||
msgstr "免手操作"
|
||||
msgstr "免提"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2367
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2369
|
||||
msgid "Handsfree (HFP)"
|
||||
msgstr "免手操作 (HFP)"
|
||||
msgstr "免提(HFP)"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2390
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2392
|
||||
msgid "Portable"
|
||||
msgstr "便携式"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2396
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2398
|
||||
msgid "Car"
|
||||
msgstr "车内"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2402
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2404
|
||||
msgid "HiFi"
|
||||
msgstr "高保真"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2408
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2410
|
||||
msgid "Phone"
|
||||
msgstr "电话"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2415
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2417
|
||||
msgid "Bluetooth"
|
||||
msgstr "蓝牙"
|
||||
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2416
|
||||
msgid "Bluetooth (HFP)"
|
||||
msgstr "蓝牙 (HFP)"
|
||||
#: spa/plugins/bluez5/bluez5-device.c:2418
|
||||
msgid "Bluetooth Handsfree"
|
||||
msgstr "蓝牙免提"
|
||||
|
||||
#~ msgid "Headphone"
|
||||
#~ msgstr "头戴耳机"
|
||||
|
|
|
|||
|
|
@ -578,7 +578,7 @@ static int make_nodes(struct data *data)
|
|||
SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(SPA_DIRECTION_OUTPUT),
|
||||
SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp),
|
||||
SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param));
|
||||
if ((res = spa_node_set_param(data->source_node, SPA_PARAM_PortConfig, 0, param) < 0)) {
|
||||
if ((res = spa_node_set_param(data->source_node, SPA_PARAM_PortConfig, 0, param)) < 0) {
|
||||
printf("can't setup source node %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
|
@ -647,7 +647,7 @@ static int make_nodes(struct data *data)
|
|||
SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param));
|
||||
|
||||
|
||||
if ((res = spa_node_set_param(data->sink_node, SPA_PARAM_PortConfig, 0, param) < 0)) {
|
||||
if ((res = spa_node_set_param(data->sink_node, SPA_PARAM_PortConfig, 0, param)) < 0) {
|
||||
printf("can't setup sink node %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
|
@ -987,7 +987,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hdmstiac:", long_options, NULL)) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "hd:m:s:t:i:a:c:", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
show_help(&data, argv[0], false);
|
||||
|
|
|
|||
|
|
@ -164,8 +164,7 @@ SPA_API_DEBUG_FORMAT int spa_debugc_format(struct spa_debug_context *ctx, int in
|
|||
type = val->type;
|
||||
size = val->size;
|
||||
|
||||
if (type < SPA_TYPE_None || type >= _SPA_TYPE_LAST || n_vals < 1 ||
|
||||
size < spa_pod_type_size(type))
|
||||
if (type < SPA_TYPE_None || type >= _SPA_TYPE_LAST || n_vals < 1)
|
||||
continue;
|
||||
|
||||
vals = SPA_POD_BODY(val);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_d
|
|||
{
|
||||
struct spa_pod *position = NULL;
|
||||
int res;
|
||||
uint32_t max_position = SPA_N_ELEMENTS(info->position);
|
||||
|
||||
info->flags = 0;
|
||||
res = spa_pod_parse_object(format,
|
||||
SPA_TYPE_OBJECT_Format, NULL,
|
||||
|
|
@ -41,10 +43,13 @@ spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_d
|
|||
SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate),
|
||||
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
|
||||
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
|
||||
if (info->channels > max_position)
|
||||
return -ECHRNG;
|
||||
if (position == NULL ||
|
||||
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS))
|
||||
spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) {
|
||||
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||
|
||||
spa_memzero(info->position, max_position * sizeof(info->position[0]));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,20 +46,61 @@ extern "C" {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
SPA_API_AUDIO_FORMAT_UTILS bool
|
||||
spa_format_audio_ext_valid_size(uint32_t media_subtype, size_t size)
|
||||
{
|
||||
switch (media_subtype) {
|
||||
case SPA_MEDIA_SUBTYPE_raw:
|
||||
return size >= offsetof(struct spa_audio_info, info.raw) &&
|
||||
SPA_AUDIO_INFO_RAW_VALID_SIZE(size - offsetof(struct spa_audio_info, info.raw));
|
||||
|
||||
#define _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(format) \
|
||||
case SPA_MEDIA_SUBTYPE_ ## format: \
|
||||
return size >= offsetof(struct spa_audio_info, info.format) + sizeof(struct spa_audio_info_ ## format);
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(dsp)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(iec958)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(dsd)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(mp3)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(aac)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(vorbis)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(wma)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(ra)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(amr)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(alac)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(flac)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(ape)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(ac3)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(eac3)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(truehd)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(dts)
|
||||
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(mpegh)
|
||||
#undef _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_FORMAT_UTILS int
|
||||
spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info)
|
||||
spa_format_audio_ext_parse(const struct spa_pod *format, struct spa_audio_info *info, size_t size)
|
||||
{
|
||||
int res;
|
||||
uint32_t media_type, media_subtype;
|
||||
|
||||
if ((res = spa_format_parse(format, &info->media_type, &info->media_subtype)) < 0)
|
||||
if ((res = spa_format_parse(format, &media_type, &media_subtype)) < 0)
|
||||
return res;
|
||||
|
||||
if (info->media_type != SPA_MEDIA_TYPE_audio)
|
||||
if (media_type != SPA_MEDIA_TYPE_audio)
|
||||
return -EINVAL;
|
||||
|
||||
switch (info->media_subtype) {
|
||||
if (!spa_format_audio_ext_valid_size(media_subtype, size))
|
||||
return -EINVAL;
|
||||
|
||||
info->media_type = media_type;
|
||||
info->media_subtype = media_subtype;
|
||||
|
||||
switch (media_subtype) {
|
||||
case SPA_MEDIA_SUBTYPE_raw:
|
||||
return spa_format_audio_raw_parse(format, &info->info.raw);
|
||||
return spa_format_audio_raw_ext_parse(format, &info->info.raw,
|
||||
size - offsetof(struct spa_audio_info, info.raw));
|
||||
case SPA_MEDIA_SUBTYPE_dsp:
|
||||
return spa_format_audio_dsp_parse(format, &info->info.dsp);
|
||||
case SPA_MEDIA_SUBTYPE_iec958:
|
||||
|
|
@ -98,13 +139,25 @@ spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_FORMAT_UTILS struct spa_pod *
|
||||
spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
|
||||
const struct spa_audio_info *info)
|
||||
SPA_API_AUDIO_FORMAT_UTILS int
|
||||
spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info)
|
||||
{
|
||||
return spa_format_audio_ext_parse(format, info, sizeof(*info));
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_FORMAT_UTILS struct spa_pod *
|
||||
spa_format_audio_ext_build(struct spa_pod_builder *builder, uint32_t id,
|
||||
const struct spa_audio_info *info, size_t size)
|
||||
{
|
||||
if (!spa_format_audio_ext_valid_size(info->media_subtype, size)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (info->media_subtype) {
|
||||
case SPA_MEDIA_SUBTYPE_raw:
|
||||
return spa_format_audio_raw_build(builder, id, &info->info.raw);
|
||||
return spa_format_audio_raw_ext_build(builder, id, &info->info.raw,
|
||||
size - offsetof(struct spa_audio_info, info.raw));
|
||||
case SPA_MEDIA_SUBTYPE_dsp:
|
||||
return spa_format_audio_dsp_build(builder, id, &info->info.dsp);
|
||||
case SPA_MEDIA_SUBTYPE_iec958:
|
||||
|
|
@ -143,6 +196,13 @@ spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
|
|||
errno = ENOTSUP;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_FORMAT_UTILS struct spa_pod *
|
||||
spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
|
||||
const struct spa_audio_info *info)
|
||||
{
|
||||
return spa_format_audio_ext_build(builder, id, info, sizeof(*info));
|
||||
}
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ struct spa_audio_info {
|
|||
struct spa_audio_info_dts dts;
|
||||
struct spa_audio_info_mpegh mpegh;
|
||||
} info;
|
||||
|
||||
/* padding follows here when info has flexible size */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
118
spa/include/spa/param/audio/layout-types.h
Normal file
118
spa/include/spa/param/audio/layout-types.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/* Simple Plugin API */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2025 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_AUDIO_LAYOUT_TYPES_H
|
||||
#define SPA_AUDIO_LAYOUT_TYPES_H
|
||||
|
||||
#include <spa/utils/type.h>
|
||||
#include <spa/utils/string.h>
|
||||
#include <spa/param/audio/layout.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \addtogroup spa_param
|
||||
* \{
|
||||
*/
|
||||
|
||||
#ifndef SPA_API_AUDIO_LAYOUT_TYPES
|
||||
#ifdef SPA_API_IMPL
|
||||
#define SPA_API_AUDIO_LAYOUT_TYPES SPA_API_IMPL
|
||||
#else
|
||||
#define SPA_API_AUDIO_LAYOUT_TYPES static inline
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const struct spa_type_audio_layout_info {
|
||||
const char *name;
|
||||
struct spa_audio_layout_info layout;
|
||||
} spa_type_audio_layout_info[] = {
|
||||
{ "Mono", { SPA_AUDIO_LAYOUT_Mono } },
|
||||
{ "Stereo", { SPA_AUDIO_LAYOUT_Stereo } },
|
||||
{ "Quad", { SPA_AUDIO_LAYOUT_Quad } },
|
||||
{ "Pentagonal", { SPA_AUDIO_LAYOUT_Pentagonal } },
|
||||
{ "Hexagonal", { SPA_AUDIO_LAYOUT_Hexagonal } },
|
||||
{ "Octagonal", { SPA_AUDIO_LAYOUT_Octagonal } },
|
||||
{ "Cube", { SPA_AUDIO_LAYOUT_Cube } },
|
||||
{ "MPEG-1.0", { SPA_AUDIO_LAYOUT_MPEG_1_0 } },
|
||||
{ "MPEG-2.0", { SPA_AUDIO_LAYOUT_MPEG_2_0 } },
|
||||
{ "MPEG-3.0A", { SPA_AUDIO_LAYOUT_MPEG_3_0A } },
|
||||
{ "MPEG-3.0B", { SPA_AUDIO_LAYOUT_MPEG_3_0B } },
|
||||
{ "MPEG-4.0A", { SPA_AUDIO_LAYOUT_MPEG_4_0A } },
|
||||
{ "MPEG-4.0B", { SPA_AUDIO_LAYOUT_MPEG_4_0B } },
|
||||
{ "MPEG-5.0A", { SPA_AUDIO_LAYOUT_MPEG_5_0A } },
|
||||
{ "MPEG-5.0B", { SPA_AUDIO_LAYOUT_MPEG_5_0B } },
|
||||
{ "MPEG-5.0C", { SPA_AUDIO_LAYOUT_MPEG_5_0C } },
|
||||
{ "MPEG-5.0D", { SPA_AUDIO_LAYOUT_MPEG_5_0D } },
|
||||
{ "MPEG-5.1A", { SPA_AUDIO_LAYOUT_MPEG_5_1A } },
|
||||
{ "MPEG-5.1B", { SPA_AUDIO_LAYOUT_MPEG_5_1B } },
|
||||
{ "MPEG-5.1C", { SPA_AUDIO_LAYOUT_MPEG_5_1C } },
|
||||
{ "MPEG-5.1D", { SPA_AUDIO_LAYOUT_MPEG_5_1D } },
|
||||
{ "MPEG-6.1A", { SPA_AUDIO_LAYOUT_MPEG_6_1A } },
|
||||
{ "MPEG-7.1A", { SPA_AUDIO_LAYOUT_MPEG_7_1A } },
|
||||
{ "MPEG-7.1B", { SPA_AUDIO_LAYOUT_MPEG_7_1B } },
|
||||
{ "MPEG-7.1C", { SPA_AUDIO_LAYOUT_MPEG_7_1C } },
|
||||
{ "2.1", { SPA_AUDIO_LAYOUT_2_1 } },
|
||||
{ "2RC", { SPA_AUDIO_LAYOUT_2RC } },
|
||||
{ "2FC", { SPA_AUDIO_LAYOUT_2FC } },
|
||||
{ "3.1", { SPA_AUDIO_LAYOUT_3_1 } },
|
||||
{ "4.0", { SPA_AUDIO_LAYOUT_4_0 } },
|
||||
{ "2.2", { SPA_AUDIO_LAYOUT_2_2 } },
|
||||
{ "4.1", { SPA_AUDIO_LAYOUT_4_1 } },
|
||||
{ "5.0", { SPA_AUDIO_LAYOUT_5_0 } },
|
||||
{ "5.0R", { SPA_AUDIO_LAYOUT_5_0R } },
|
||||
{ "5.1", { SPA_AUDIO_LAYOUT_5_1 } },
|
||||
{ "5.1R", { SPA_AUDIO_LAYOUT_5_1R } },
|
||||
{ "6.0", { SPA_AUDIO_LAYOUT_6_0 } },
|
||||
{ "6.0F", { SPA_AUDIO_LAYOUT_6_0F } },
|
||||
{ "6.1", { SPA_AUDIO_LAYOUT_6_1 } },
|
||||
{ "6.1F", { SPA_AUDIO_LAYOUT_6_1F } },
|
||||
{ "7.0", { SPA_AUDIO_LAYOUT_7_0 } },
|
||||
{ "7.0F", { SPA_AUDIO_LAYOUT_7_0F } },
|
||||
{ "7.1", { SPA_AUDIO_LAYOUT_7_1 } },
|
||||
{ "7.1W", { SPA_AUDIO_LAYOUT_7_1W } },
|
||||
{ "7.1WR", { SPA_AUDIO_LAYOUT_7_1WR } },
|
||||
{ NULL, { 0, { SPA_AUDIO_CHANNEL_UNKNOWN } } },
|
||||
};
|
||||
|
||||
SPA_API_AUDIO_LAYOUT_TYPES int
|
||||
spa_audio_layout_info_parse_name(struct spa_audio_layout_info *layout, size_t size,
|
||||
const char *name)
|
||||
{
|
||||
uint32_t max_position = SPA_AUDIO_LAYOUT_INFO_MAX_POSITION(size);
|
||||
if (spa_strstartswith(name, "AUX")) {
|
||||
uint32_t i, n_pos;
|
||||
if (spa_atou32(name+3, &n_pos, 10)) {
|
||||
if (n_pos > max_position)
|
||||
return -ECHRNG;
|
||||
for (i = 0; i < 0x1000 && i < n_pos; i++)
|
||||
layout->position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
|
||||
for (; i < n_pos; i++)
|
||||
layout->position[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
|
||||
layout->n_channels = n_pos;
|
||||
return n_pos;
|
||||
}
|
||||
}
|
||||
SPA_FOR_EACH_ELEMENT_VAR(spa_type_audio_layout_info, i) {
|
||||
if (spa_streq(name, i->name)) {
|
||||
if (i->layout.n_channels > max_position)
|
||||
return -ECHRNG;
|
||||
*layout = i->layout;
|
||||
return i->layout.n_channels;
|
||||
}
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SPA_AUDIO_LAYOUT_TYPES_H */
|
||||
|
|
@ -21,8 +21,11 @@ extern "C" {
|
|||
struct spa_audio_layout_info {
|
||||
uint32_t n_channels;
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
/* padding may follow to allow more channels */
|
||||
};
|
||||
|
||||
#define SPA_AUDIO_LAYOUT_INFO_MAX_POSITION(size) (((size)-offsetof(struct spa_audio_layout_info,position))/sizeof(uint32_t))
|
||||
|
||||
#define SPA_AUDIO_LAYOUT_Mono 1, { SPA_AUDIO_CHANNEL_MONO, }
|
||||
#define SPA_AUDIO_LAYOUT_Stereo 2, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, }
|
||||
#define SPA_AUDIO_LAYOUT_Quad 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
|
||||
|
|
@ -37,7 +40,7 @@ struct spa_audio_layout_info {
|
|||
SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
|
||||
SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, \
|
||||
SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
|
||||
#define SPA_AUDIO_LAYOUT_Cube 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR }, \
|
||||
#define SPA_AUDIO_LAYOUT_Cube 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
|
||||
SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
|
||||
SPA_AUDIO_CHANNEL_TFL, SPA_AUDIO_CHANNEL_TFR, \
|
||||
SPA_AUDIO_CHANNEL_TRL, SPA_AUDIO_CHANNEL_TRR, }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <spa/utils/json.h>
|
||||
#include <spa/param/audio/raw.h>
|
||||
#include <spa/param/audio/raw-types.h>
|
||||
#include <spa/param/audio/layout-types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -28,8 +29,8 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
SPA_API_AUDIO_RAW_JSON int
|
||||
spa_audio_parse_position(const char *str, size_t len,
|
||||
uint32_t *position, uint32_t *n_channels)
|
||||
spa_audio_parse_position_n(const char *str, size_t len,
|
||||
uint32_t *position, uint32_t max_position, uint32_t *n_channels)
|
||||
{
|
||||
struct spa_json iter;
|
||||
char v[256];
|
||||
|
|
@ -38,18 +39,46 @@ spa_audio_parse_position(const char *str, size_t len,
|
|||
if (spa_json_begin_array_relax(&iter, str, len) <= 0)
|
||||
return 0;
|
||||
|
||||
while (spa_json_get_string(&iter, v, sizeof(v)) > 0 &&
|
||||
channels < SPA_AUDIO_MAX_CHANNELS) {
|
||||
position[channels++] = spa_type_audio_channel_from_short_name(v);
|
||||
while (spa_json_get_string(&iter, v, sizeof(v)) > 0) {
|
||||
if (channels < max_position)
|
||||
position[channels] = spa_type_audio_channel_from_short_name(v);
|
||||
channels++;
|
||||
}
|
||||
*n_channels = channels;
|
||||
return channels;
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_JSON int
|
||||
spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, const char *val, bool force)
|
||||
spa_audio_parse_position(const char *str, size_t len,
|
||||
uint32_t *position, uint32_t *n_channels)
|
||||
{
|
||||
return spa_audio_parse_position_n(str, len, position, SPA_AUDIO_MAX_CHANNELS, n_channels);
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_JSON int
|
||||
spa_audio_parse_layout(const char *str, uint32_t *position, uint32_t max_position,
|
||||
uint32_t *n_channels)
|
||||
{
|
||||
struct spa_audio_layout_info l;
|
||||
uint32_t i;
|
||||
if (spa_audio_layout_info_parse_name(&l, sizeof(l), str) <= 0)
|
||||
return 0;
|
||||
for (i = 0; i < l.n_channels && i < max_position; i++)
|
||||
position[i] = l.position[i];
|
||||
*n_channels = l.n_channels;
|
||||
return l.n_channels;
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_JSON int
|
||||
spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
|
||||
const char *key, const char *val, bool force)
|
||||
{
|
||||
uint32_t v;
|
||||
uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size);
|
||||
|
||||
if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size))
|
||||
return -EINVAL;
|
||||
|
||||
if (spa_streq(key, SPA_KEY_AUDIO_FORMAT)) {
|
||||
if (force || info->format == 0)
|
||||
info->format = (enum spa_audio_format)spa_type_audio_format_from_short_name(val);
|
||||
|
|
@ -57,41 +86,97 @@ spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, cons
|
|||
if (spa_atou32(val, &v, 0) && (force || info->rate == 0))
|
||||
info->rate = v;
|
||||
} else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) {
|
||||
if (spa_atou32(val, &v, 0) && (force || info->channels == 0))
|
||||
info->channels = SPA_MIN(v, SPA_AUDIO_MAX_CHANNELS);
|
||||
if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) {
|
||||
if (v > max_position)
|
||||
return -ECHRNG;
|
||||
info->channels = v;
|
||||
}
|
||||
} else if (spa_streq(key, SPA_KEY_AUDIO_LAYOUT)) {
|
||||
if (force || info->channels == 0) {
|
||||
if (spa_audio_parse_layout(val, info->position, max_position, &v) > 0) {
|
||||
if (v > max_position)
|
||||
return -ECHRNG;
|
||||
info->channels = v;
|
||||
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||
}
|
||||
}
|
||||
} else if (spa_streq(key, SPA_KEY_AUDIO_POSITION)) {
|
||||
if (force || info->channels == 0) {
|
||||
if (spa_audio_parse_position(val, strlen(val), info->position, &info->channels) > 0)
|
||||
if (spa_audio_parse_position_n(val, strlen(val), info->position,
|
||||
max_position, &v) > 0) {
|
||||
if (v > max_position)
|
||||
return -ECHRNG;
|
||||
info->channels = v;
|
||||
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_JSON int
|
||||
spa_audio_info_raw_update(struct spa_audio_info_raw *info,
|
||||
const char *key, const char *val, bool force)
|
||||
{
|
||||
return spa_audio_info_raw_ext_update(info, sizeof(*info), key, val, force);
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_JSON int
|
||||
spa_audio_info_raw_ext_init_dict_keys_va(struct spa_audio_info_raw *info, size_t size,
|
||||
const struct spa_dict *defaults,
|
||||
const struct spa_dict *dict, va_list args)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size))
|
||||
return -EINVAL;
|
||||
|
||||
memset(info, 0, size);
|
||||
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||
if (dict) {
|
||||
const char *val, *key;
|
||||
while ((key = va_arg(args, const char *))) {
|
||||
if ((val = spa_dict_lookup(dict, key)) == NULL)
|
||||
continue;
|
||||
if ((res = spa_audio_info_raw_ext_update(info, size,
|
||||
key, val, true)) < 0)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (defaults) {
|
||||
const struct spa_dict_item *it;
|
||||
spa_dict_for_each(it, defaults)
|
||||
if ((res = spa_audio_info_raw_ext_update(info, size,
|
||||
it->key, it->value, false)) < 0)
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_JSON int SPA_SENTINEL
|
||||
spa_audio_info_raw_ext_init_dict_keys(struct spa_audio_info_raw *info, size_t size,
|
||||
const struct spa_dict *defaults,
|
||||
const struct spa_dict *dict, ...)
|
||||
{
|
||||
va_list args;
|
||||
int res;
|
||||
va_start(args, dict);
|
||||
res = spa_audio_info_raw_ext_init_dict_keys_va(info, size, defaults, dict, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_JSON int SPA_SENTINEL
|
||||
spa_audio_info_raw_init_dict_keys(struct spa_audio_info_raw *info,
|
||||
const struct spa_dict *defaults,
|
||||
const struct spa_dict *dict, ...)
|
||||
{
|
||||
spa_zero(*info);
|
||||
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||
if (dict) {
|
||||
const char *val, *key;
|
||||
va_list args;
|
||||
va_start(args, dict);
|
||||
while ((key = va_arg(args, const char *))) {
|
||||
if ((val = spa_dict_lookup(dict, key)) == NULL)
|
||||
continue;
|
||||
spa_audio_info_raw_update(info, key, val, true);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
if (defaults) {
|
||||
const struct spa_dict_item *it;
|
||||
spa_dict_for_each(it, defaults)
|
||||
spa_audio_info_raw_update(info, it->key, it->value, false);
|
||||
}
|
||||
return 0;
|
||||
va_list args;
|
||||
int res;
|
||||
va_start(args, dict);
|
||||
res = spa_audio_info_raw_ext_init_dict_keys_va(info, sizeof(*info), defaults, dict, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -267,12 +267,34 @@ static const struct spa_type_info spa_type_audio_channel[] = {
|
|||
|
||||
SPA_API_AUDIO_RAW_TYPES uint32_t spa_type_audio_channel_from_short_name(const char *name)
|
||||
{
|
||||
return spa_type_from_short_name(name, spa_type_audio_channel, SPA_AUDIO_CHANNEL_UNKNOWN);
|
||||
uint32_t res;
|
||||
if (spa_strstartswith(name, "AUX")) {
|
||||
if (spa_atou32(name+3, &res, 10) && res < 0x1000)
|
||||
res = SPA_AUDIO_CHANNEL_AUX0 + res;
|
||||
else
|
||||
res = SPA_AUDIO_CHANNEL_UNKNOWN;
|
||||
} else {
|
||||
res = spa_type_from_short_name(name, spa_type_audio_channel, SPA_AUDIO_CHANNEL_UNKNOWN);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
SPA_API_AUDIO_RAW_TYPES const char * spa_type_audio_channel_to_short_name(uint32_t type)
|
||||
{
|
||||
return spa_type_to_short_name(type, spa_type_audio_channel, "UNK");
|
||||
}
|
||||
SPA_API_AUDIO_RAW_TYPES const char * spa_type_audio_channel_make_short_name(uint32_t type,
|
||||
char *buf, size_t size, const char *unknown)
|
||||
{
|
||||
if (SPA_AUDIO_CHANNEL_IS_AUX(type)) {
|
||||
snprintf(buf, size, "AUX%u", type - SPA_AUDIO_CHANNEL_AUX0);
|
||||
} else {
|
||||
const char *str = spa_type_to_short_name(type, spa_type_audio_channel, NULL);
|
||||
if (str == NULL)
|
||||
return unknown;
|
||||
snprintf(buf, size, "%.7s", str);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define SPA_TYPE_INFO_AudioVolumeRampScale SPA_TYPE_INFO_ENUM_BASE "AudioVolumeRampScale"
|
||||
#define SPA_TYPE_INFO_AUDIO_VOLUME_RAMP_SCALE_BASE SPA_TYPE_INFO_AudioVolumeRampScale ":"
|
||||
|
|
@ -292,4 +314,4 @@ static const struct spa_type_info spa_type_audio_volume_ramp_scale[] = {
|
|||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SPA_AUDIO_RAW_RAW_TYPES_H */
|
||||
#endif /* SPA_AUDIO_RAW_TYPES_H */
|
||||
|
|
|
|||
|
|
@ -29,10 +29,15 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
SPA_API_AUDIO_RAW_UTILS int
|
||||
spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info)
|
||||
spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_info_raw *info, size_t size)
|
||||
{
|
||||
struct spa_pod *position = NULL;
|
||||
int res;
|
||||
uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size);
|
||||
|
||||
if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size))
|
||||
return -EINVAL;
|
||||
|
||||
info->flags = 0;
|
||||
res = spa_pod_parse_object(format,
|
||||
SPA_TYPE_OBJECT_Format, NULL,
|
||||
|
|
@ -40,18 +45,35 @@ spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_r
|
|||
SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate),
|
||||
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
|
||||
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
|
||||
if (info->channels > max_position)
|
||||
return -ECHRNG;
|
||||
if (position == NULL ||
|
||||
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS))
|
||||
spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) {
|
||||
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||
spa_memzero(info->position, max_position * sizeof(info->position[0]));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_UTILS int
|
||||
spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info)
|
||||
{
|
||||
return spa_format_audio_raw_ext_parse(format, info, sizeof(*info));
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_UTILS struct spa_pod *
|
||||
spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
|
||||
const struct spa_audio_info_raw *info)
|
||||
spa_format_audio_raw_ext_build(struct spa_pod_builder *builder, uint32_t id,
|
||||
const struct spa_audio_info_raw *info, size_t size)
|
||||
{
|
||||
struct spa_pod_frame f;
|
||||
uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size);
|
||||
|
||||
if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
|
||||
spa_pod_builder_add(builder,
|
||||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
||||
|
|
@ -66,7 +88,10 @@ spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
|
|||
if (info->channels != 0) {
|
||||
spa_pod_builder_add(builder,
|
||||
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels), 0);
|
||||
if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) {
|
||||
/* we drop the positions here when we can't read all of them. This is
|
||||
* really a malformed spa_audio_info structure. */
|
||||
if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED) &&
|
||||
info->channels <= max_position) {
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position,
|
||||
SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id,
|
||||
info->channels, info->position), 0);
|
||||
|
|
@ -75,6 +100,13 @@ spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
|
|||
return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
|
||||
}
|
||||
|
||||
SPA_API_AUDIO_RAW_UTILS struct spa_pod *
|
||||
spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
|
||||
const struct spa_audio_info_raw *info)
|
||||
{
|
||||
return spa_format_audio_raw_ext_build(builder, id, info, sizeof(*info));
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ extern "C" {
|
|||
* \{
|
||||
*/
|
||||
|
||||
#define SPA_AUDIO_MAX_CHANNELS 128u
|
||||
/* This is the max number of channels, changing this will change the
|
||||
* size of some helper structures. This value should be at least 64 */
|
||||
#ifndef SPA_AUDIO_MAX_CHANNELS
|
||||
#define SPA_AUDIO_MAX_CHANNELS 64u
|
||||
#endif
|
||||
|
||||
enum spa_audio_format {
|
||||
SPA_AUDIO_FORMAT_UNKNOWN,
|
||||
|
|
@ -259,6 +263,8 @@ enum spa_audio_channel {
|
|||
SPA_AUDIO_CHANNEL_START_Custom = 0x10000,
|
||||
};
|
||||
|
||||
#define SPA_AUDIO_CHANNEL_IS_AUX(ch) ((ch)>=SPA_AUDIO_CHANNEL_START_Aux && (ch)<=SPA_AUDIO_CHANNEL_LAST_Aux)
|
||||
|
||||
enum spa_audio_volume_ramp_scale {
|
||||
SPA_AUDIO_VOLUME_RAMP_INVALID,
|
||||
SPA_AUDIO_VOLUME_RAMP_LINEAR,
|
||||
|
|
@ -269,23 +275,34 @@ enum spa_audio_volume_ramp_scale {
|
|||
#define SPA_AUDIO_FLAG_NONE (0) /*< no valid flag */
|
||||
#define SPA_AUDIO_FLAG_UNPOSITIONED (1 << 0) /*< the position array explicitly
|
||||
* contains unpositioned channels. */
|
||||
/** Audio information description */
|
||||
/** Audio information description. You can assume when you receive this structure
|
||||
* that there is enought padding to accomodate all channel positions in case the
|
||||
* channel count is more than SPA_AUDIO_MAX_CHANNELS. */
|
||||
struct spa_audio_info_raw {
|
||||
enum spa_audio_format format; /*< format, one of enum spa_audio_format */
|
||||
uint32_t flags; /*< extra flags */
|
||||
uint32_t rate; /*< sample rate */
|
||||
uint32_t channels; /*< number of channels */
|
||||
uint32_t channels; /*< number of channels. This can be more than SPA_AUDIO_MAX_CHANNELS
|
||||
* and you may assume there is enough padding for the extra
|
||||
* channel positions. */
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */
|
||||
/* padding follows here when channels > SPA_AUDIO_MAX_CHANNELS */
|
||||
};
|
||||
|
||||
#define SPA_AUDIO_INFO_RAW_INIT(...) ((struct spa_audio_info_raw) { __VA_ARGS__ })
|
||||
|
||||
#define SPA_AUDIO_INFO_RAW_MAX_POSITION(size) (((size)-offsetof(struct spa_audio_info_raw,position))/sizeof(uint32_t))
|
||||
|
||||
#define SPA_AUDIO_INFO_RAW_VALID_SIZE(size) ((size) >= offsetof(struct spa_audio_info_raw, position))
|
||||
|
||||
|
||||
#define SPA_KEY_AUDIO_FORMAT "audio.format" /**< an audio format as string,
|
||||
* Ex. "S16LE" */
|
||||
#define SPA_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel as string,
|
||||
* Ex. "FL" */
|
||||
#define SPA_KEY_AUDIO_CHANNELS "audio.channels" /**< an audio channel count as int */
|
||||
#define SPA_KEY_AUDIO_RATE "audio.rate" /**< an audio sample rate as int */
|
||||
#define SPA_KEY_AUDIO_LAYOUT "audio.layout" /**< channel positions as predefined layout */
|
||||
#define SPA_KEY_AUDIO_POSITION "audio.position" /**< channel positions as comma separated list
|
||||
* of channels ex. "FL,FR" */
|
||||
#define SPA_KEY_AUDIO_ALLOWED_RATES "audio.allowed-rates" /**< a list of allowed samplerates
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#define SPA_AUDIO_TYPES_H
|
||||
|
||||
#include <spa/param/audio/raw-types.h>
|
||||
#include <spa/param/audio/layout-types.h>
|
||||
#include <spa/param/audio/iec958-types.h>
|
||||
#include <spa/param/audio/mp3-types.h>
|
||||
#include <spa/param/audio/aac-types.h>
|
||||
|
|
|
|||
38
spa/include/spa/param/dict-types.h
Normal file
38
spa/include/spa/param/dict-types.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* Simple Plugin API */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_PARAM_DICT_TYPES_H
|
||||
#define SPA_PARAM_DICT_TYPES_H
|
||||
|
||||
#include <spa/utils/enum-types.h>
|
||||
#include <spa/param/param-types.h>
|
||||
#include <spa/param/dict.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \addtogroup spa_param
|
||||
* \{
|
||||
*/
|
||||
|
||||
#define SPA_TYPE_INFO_PARAM_Dict SPA_TYPE_INFO_PARAM_BASE "Dict"
|
||||
#define SPA_TYPE_INFO_PARAM_DICT_BASE SPA_TYPE_INFO_PARAM_Dict ":"
|
||||
|
||||
static const struct spa_type_info spa_type_param_dict[] = {
|
||||
{ SPA_PARAM_DICT_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_DICT_BASE, spa_type_param, },
|
||||
{ SPA_PARAM_DICT_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_DICT_BASE "info", NULL, },
|
||||
{ 0, 0, NULL, NULL },
|
||||
};
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SPA_PARAM_DICT_TYPES_H */
|
||||
125
spa/include/spa/param/dict-utils.h
Normal file
125
spa/include/spa/param/dict-utils.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* Simple Plugin API */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_PARAM_DICT_UTILS_H
|
||||
#define SPA_PARAM_DICT_UTILS_H
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#include <spa/utils/dict.h>
|
||||
#include <spa/pod/builder.h>
|
||||
#include <spa/pod/iter.h>
|
||||
#include <spa/pod/parser.h>
|
||||
#include <spa/pod/compare.h>
|
||||
#include <spa/param/dict.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \addtogroup spa_param
|
||||
* \{
|
||||
*/
|
||||
|
||||
#ifndef SPA_API_DICT_UTILS
|
||||
#ifdef SPA_API_IMPL
|
||||
#define SPA_API_DICT_UTILS SPA_API_IMPL
|
||||
#else
|
||||
#define SPA_API_DICT_UTILS static inline
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SPA_API_DICT_UTILS int
|
||||
spa_param_dict_compare(const struct spa_pod *a, const struct spa_pod *b)
|
||||
{
|
||||
return spa_pod_memcmp(a, b);
|
||||
}
|
||||
|
||||
SPA_API_DICT_UTILS struct spa_pod *
|
||||
spa_param_dict_build_dict(struct spa_pod_builder *builder, uint32_t id, struct spa_dict *dict)
|
||||
{
|
||||
struct spa_pod_frame f[2];
|
||||
uint32_t i, n_items;
|
||||
|
||||
spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_ParamDict, id);
|
||||
|
||||
n_items = dict ? dict->n_items : 0;
|
||||
|
||||
spa_pod_builder_prop(builder, SPA_PARAM_DICT_info, SPA_POD_PROP_FLAG_HINT_DICT);
|
||||
spa_pod_builder_push_struct(builder, &f[1]);
|
||||
spa_pod_builder_int(builder, n_items);
|
||||
for (i = 0; i < n_items; i++) {
|
||||
spa_pod_builder_string(builder, dict->items[i].key);
|
||||
spa_pod_builder_string(builder, dict->items[i].value);
|
||||
}
|
||||
spa_pod_builder_pop(builder, &f[1]);
|
||||
|
||||
return (struct spa_pod*)spa_pod_builder_pop(builder, &f[0]);
|
||||
}
|
||||
|
||||
SPA_API_DICT_UTILS struct spa_pod *
|
||||
spa_param_dict_build_info(struct spa_pod_builder *builder, uint32_t id, struct spa_param_dict_info *info)
|
||||
{
|
||||
struct spa_pod_frame f;
|
||||
spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_ParamDict, id);
|
||||
spa_pod_builder_add(builder, SPA_PARAM_DICT_info, SPA_POD_PROP_FLAG_HINT_DICT);
|
||||
spa_pod_builder_primitive(builder, info->info);
|
||||
return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
|
||||
}
|
||||
|
||||
SPA_API_DICT_UTILS int
|
||||
spa_param_dict_parse(const struct spa_pod *dict, struct spa_param_dict_info *info, size_t size)
|
||||
{
|
||||
memset(info, 0, size);
|
||||
return spa_pod_parse_object(dict,
|
||||
SPA_TYPE_OBJECT_ParamDict, NULL,
|
||||
SPA_PARAM_DICT_info, SPA_POD_PodStruct(&info->info));
|
||||
}
|
||||
|
||||
SPA_API_DICT_UTILS int
|
||||
spa_param_dict_info_parse(const struct spa_param_dict_info *info, size_t size,
|
||||
struct spa_dict *dict, struct spa_dict_item *items)
|
||||
{
|
||||
struct spa_pod_parser prs;
|
||||
uint32_t n, n_items;
|
||||
const char *key, *value;
|
||||
struct spa_pod_frame f[1];
|
||||
|
||||
spa_pod_parser_pod(&prs, info->info);
|
||||
if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 ||
|
||||
spa_pod_parser_get_int(&prs, (int32_t*)&n_items) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (items == NULL) {
|
||||
dict->n_items = n_items;
|
||||
return 0;
|
||||
}
|
||||
n_items = SPA_MIN(dict->n_items, n_items);
|
||||
|
||||
for (n = 0; n < n_items; n++) {
|
||||
if (spa_pod_parser_get(&prs,
|
||||
SPA_POD_String(&key),
|
||||
SPA_POD_String(&value),
|
||||
NULL) < 0)
|
||||
break;
|
||||
if (key == NULL || value == NULL)
|
||||
return -EINVAL;
|
||||
items[n].key = key;
|
||||
items[n].value = value;
|
||||
}
|
||||
dict->items = items;
|
||||
spa_pod_parser_pop(&prs, &f[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SPA_PARAM_DICT_UTILS_H */
|
||||
42
spa/include/spa/param/dict.h
Normal file
42
spa/include/spa/param/dict.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* Simple Plugin API */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2025 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_PARAM_DICT_H
|
||||
#define SPA_PARAM_DICT_H
|
||||
|
||||
#include <spa/param/param.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \addtogroup spa_param
|
||||
* \{
|
||||
*/
|
||||
|
||||
/** properties for SPA_TYPE_OBJECT_ParamDict */
|
||||
enum spa_param_dict {
|
||||
SPA_PARAM_DICT_START,
|
||||
SPA_PARAM_DICT_info, /**< Struct(
|
||||
* Int: n_items
|
||||
* (String: key
|
||||
* String: value)*
|
||||
* ) */
|
||||
};
|
||||
|
||||
/** helper structure for managing info objects */
|
||||
struct spa_param_dict_info {
|
||||
const struct spa_pod *info;
|
||||
};
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SPA_PARAM_DICT_H */
|
||||
|
|
@ -41,7 +41,9 @@ static const struct spa_type_info spa_type_param[] = {
|
|||
{ SPA_PARAM_Latency, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_INFO_PARAM_ID_BASE "Latency", NULL },
|
||||
{ SPA_PARAM_ProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_INFO_PARAM_ID_BASE "ProcessLatency", NULL },
|
||||
{ SPA_PARAM_Tag, SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_INFO_PARAM_ID_BASE "Tag", NULL },
|
||||
{ SPA_PARAM_PeerFormats, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_ID_BASE "PeerFormats", NULL },
|
||||
{ SPA_PARAM_PeerEnumFormat, SPA_TYPE_OBJECT_PeerParam, SPA_TYPE_INFO_PARAM_ID_BASE "PeerEnumFormat", NULL },
|
||||
{ SPA_PARAM_Capability, SPA_TYPE_OBJECT_ParamDict, SPA_TYPE_INFO_PARAM_ID_BASE "Capability", NULL },
|
||||
{ SPA_PARAM_PeerCapability, SPA_TYPE_OBJECT_PeerParam, SPA_TYPE_INFO_PARAM_ID_BASE "PeerCapability", NULL },
|
||||
{ 0, 0, NULL, NULL },
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,11 @@ enum spa_param_type {
|
|||
SPA_PARAM_Latency, /**< latency reporting, a SPA_TYPE_OBJECT_ParamLatency */
|
||||
SPA_PARAM_ProcessLatency, /**< processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency */
|
||||
SPA_PARAM_Tag, /**< tag reporting, a SPA_TYPE_OBJECT_ParamTag. Since 0.3.79 */
|
||||
SPA_PARAM_PeerFormats, /**< peer formats, a SPA_TYPE_Struct of SPA_TYPE_OBJECT_Format. Since 1.5.0 */
|
||||
SPA_PARAM_PeerEnumFormat, /**< peer formats, a SPA_TYPE_OBJECT_PeerParam with
|
||||
* SPA_TYPE_OBJECT_Format. Since 1.5.0 */
|
||||
SPA_PARAM_Capability, /**< capability info, a SPA_TYPE_OBJECT_ParamDict, Since 1.5.84 */
|
||||
SPA_PARAM_PeerCapability, /**< peer capabilities, a SPA_TYPE_OBJECT_PeerParam with
|
||||
* SPA_TYPE_OBJECT_ParamDict, since 1.5.84 */
|
||||
};
|
||||
|
||||
/** information about a parameter */
|
||||
|
|
|
|||
38
spa/include/spa/param/peer-types.h
Normal file
38
spa/include/spa/param/peer-types.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* Simple Plugin API */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_PARAM_PEER_TYPES_H
|
||||
#define SPA_PARAM_PEER_TYPES_H
|
||||
|
||||
#include <spa/utils/enum-types.h>
|
||||
#include <spa/param/param-types.h>
|
||||
#include <spa/param/peer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \addtogroup spa_param
|
||||
* \{
|
||||
*/
|
||||
|
||||
#define SPA_TYPE_INFO_PeerParam SPA_TYPE_INFO_OBJECT_BASE "PeerParam"
|
||||
#define SPA_TYPE_INFO_PEER_PARAM_BASE SPA_TYPE_INFO_PeerParam ":"
|
||||
|
||||
static const struct spa_type_info spa_type_peer_param[] = {
|
||||
{ SPA_PEER_PARAM_START, SPA_TYPE_Id, SPA_TYPE_INFO_PEER_PARAM_BASE, spa_type_param, },
|
||||
{ SPA_ID_INVALID, SPA_TYPE_Id, SPA_TYPE_INFO_PEER_PARAM_BASE, NULL, },
|
||||
{ 0, 0, NULL, NULL },
|
||||
};
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SPA_PARAM_PEER_TYPES_H */
|
||||
92
spa/include/spa/param/peer-utils.h
Normal file
92
spa/include/spa/param/peer-utils.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* Simple Plugin API */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_PARAM_PEER_PARAM_UTILS_H
|
||||
#define SPA_PARAM_PEER_PARAM_UTILS_H
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#include <spa/utils/dict.h>
|
||||
#include <spa/pod/builder.h>
|
||||
#include <spa/pod/iter.h>
|
||||
#include <spa/pod/parser.h>
|
||||
#include <spa/pod/compare.h>
|
||||
#include <spa/param/peer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \addtogroup spa_param
|
||||
* \{
|
||||
*/
|
||||
|
||||
#ifndef SPA_API_PEER_PARAM_UTILS
|
||||
#ifdef SPA_API_IMPL
|
||||
#define SPA_API_PEER_PARAM_UTILS SPA_API_IMPL
|
||||
#else
|
||||
#define SPA_API_PEER_PARAM_UTILS static inline
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SPA_API_PEER_PARAM_UTILS int
|
||||
spa_peer_param_parse(const struct spa_pod *param, struct spa_peer_param_info *info,
|
||||
size_t size, void **state)
|
||||
{
|
||||
int res;
|
||||
const struct spa_pod_object *obj = (const struct spa_pod_object*)param;
|
||||
const struct spa_pod_prop *first, *start, *cur;
|
||||
|
||||
if ((res = spa_pod_parse_object(param,
|
||||
SPA_TYPE_OBJECT_PeerParam, NULL)) < 0)
|
||||
return res;
|
||||
|
||||
first = spa_pod_prop_first(&obj->body);
|
||||
start = *state ? spa_pod_prop_next((struct spa_pod_prop*)*state) : first;
|
||||
|
||||
res = 0;
|
||||
for (cur = start; spa_pod_prop_is_inside(&obj->body, obj->pod.size, cur);
|
||||
cur = spa_pod_prop_next(cur)) {
|
||||
info->peer_id = cur->key;
|
||||
info->param = &cur->value;
|
||||
*state = (void*)cur;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
SPA_API_PEER_PARAM_UTILS void
|
||||
spa_peer_param_build_start(struct spa_pod_builder *builder, struct spa_pod_frame *f, uint32_t id)
|
||||
{
|
||||
spa_pod_builder_push_object(builder, f, SPA_TYPE_OBJECT_PeerParam, id);
|
||||
}
|
||||
|
||||
SPA_API_PEER_PARAM_UTILS void
|
||||
spa_peer_param_build_add_param(struct spa_pod_builder *builder, uint32_t peer_id,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
spa_pod_builder_prop(builder, peer_id, 0);
|
||||
if (param)
|
||||
spa_pod_builder_primitive(builder, param);
|
||||
else
|
||||
spa_pod_builder_none(builder);
|
||||
}
|
||||
|
||||
SPA_API_PEER_PARAM_UTILS struct spa_pod *
|
||||
spa_peer_param_build_end(struct spa_pod_builder *builder, struct spa_pod_frame *f)
|
||||
{
|
||||
return (struct spa_pod*)spa_pod_builder_pop(builder, f);
|
||||
}
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SPA_PARAM_PEER_PARAM_UTILS_H */
|
||||
37
spa/include/spa/param/peer.h
Normal file
37
spa/include/spa/param/peer.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Simple Plugin API */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2025 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_PARAM_PEER_PARAM_H
|
||||
#define SPA_PARAM_PEER_PARAM_H
|
||||
|
||||
#include <spa/param/param.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \addtogroup spa_param
|
||||
* \{
|
||||
*/
|
||||
|
||||
/** properties for SPA_TYPE_OBJECT_PeerParam */
|
||||
enum spa_peer_param {
|
||||
SPA_PEER_PARAM_START, /**< id of peer as key, SPA_TYPE_Pod as value */
|
||||
SPA_PEER_PARAM_END = 0xfffffffe,
|
||||
};
|
||||
|
||||
struct spa_peer_param_info {
|
||||
uint32_t peer_id;
|
||||
const struct spa_pod *param;
|
||||
};
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SPA_PARAM_PEER_PARAM_H */
|
||||
|
|
@ -40,6 +40,9 @@ static const struct spa_type_info spa_type_props[] = {
|
|||
{ SPA_PROP_quality, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "quality", NULL },
|
||||
{ SPA_PROP_bluetoothAudioCodec, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "bluetoothAudioCodec", spa_type_bluetooth_audio_codec },
|
||||
{ SPA_PROP_bluetoothOffloadActive, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "bluetoothOffloadActive", NULL },
|
||||
{ SPA_PROP_clockId, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "clockId", NULL },
|
||||
{ SPA_PROP_clockDevice, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "clockDevice", NULL },
|
||||
{ SPA_PROP_clockInterface, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "clockInterface", NULL },
|
||||
|
||||
{ SPA_PROP_waveType, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "waveType", NULL },
|
||||
{ SPA_PROP_frequency, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "frequency", NULL },
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ enum spa_prop {
|
|||
SPA_PROP_quality,
|
||||
SPA_PROP_bluetoothAudioCodec,
|
||||
SPA_PROP_bluetoothOffloadActive,
|
||||
SPA_PROP_clockId,
|
||||
SPA_PROP_clockDevice,
|
||||
SPA_PROP_clockInterface,
|
||||
|
||||
SPA_PROP_START_Audio = 0x10000, /**< audio related properties */
|
||||
SPA_PROP_waveType,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <spa/pod/builder.h>
|
||||
#include <spa/pod/iter.h>
|
||||
#include <spa/pod/parser.h>
|
||||
#include <spa/pod/compare.h>
|
||||
#include <spa/param/tag.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -33,8 +34,7 @@ extern "C" {
|
|||
SPA_API_TAG_UTILS int
|
||||
spa_tag_compare(const struct spa_pod *a, const struct spa_pod *b)
|
||||
{
|
||||
return ((a == b) || (a && b && SPA_POD_SIZE(a) == SPA_POD_SIZE(b) &&
|
||||
memcmp(a, b, SPA_POD_SIZE(b)) == 0)) ? 0 : 1;
|
||||
return spa_pod_memcmp(a, b);
|
||||
}
|
||||
|
||||
SPA_API_TAG_UTILS int
|
||||
|
|
|
|||
|
|
@ -15,5 +15,7 @@
|
|||
#include <spa/param/profile-types.h>
|
||||
#include <spa/param/route-types.h>
|
||||
#include <spa/param/tag-types.h>
|
||||
#include <spa/param/dict-types.h>
|
||||
#include <spa/param/peer-types.h>
|
||||
|
||||
#endif /* SPA_PARAM_TYPE_INFO_H */
|
||||
|
|
|
|||
|
|
@ -78,6 +78,36 @@ SPA_API_POD_BODY uint32_t spa_pod_type_size(uint32_t type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
SPA_API_POD_BODY int spa_pod_choice_n_values(uint32_t choice_type, uint32_t *min, uint32_t *max)
|
||||
{
|
||||
switch (choice_type) {
|
||||
case SPA_CHOICE_Enum:
|
||||
*min = 2;
|
||||
*max = UINT32_MAX;
|
||||
break;
|
||||
case SPA_CHOICE_Range:
|
||||
*min = *max = 3;
|
||||
break;
|
||||
case SPA_CHOICE_Step:
|
||||
*min = *max = 4;
|
||||
break;
|
||||
case SPA_CHOICE_None:
|
||||
case SPA_CHOICE_Flags:
|
||||
*min = *max = 1;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* This must always return at least 1, because callers
|
||||
* assume that n_vals >= spa_pod_choice_n_values()
|
||||
* mean that n_vals is at least 1.
|
||||
*/
|
||||
*min = 1;
|
||||
*max = UINT32_MAX;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
SPA_API_POD_BODY int spa_pod_body_from_data(void *data, size_t maxsize, off_t offset, size_t size,
|
||||
struct spa_pod *pod, const void **body)
|
||||
{
|
||||
|
|
@ -243,9 +273,9 @@ SPA_API_POD_BODY int spa_pod_is_pointer(const struct spa_pod *pod)
|
|||
SPA_API_POD_BODY int spa_pod_body_get_pointer(const struct spa_pod *pod, const void *body,
|
||||
uint32_t *type, const void **value)
|
||||
{
|
||||
struct spa_pod_pointer_body b;
|
||||
if (!spa_pod_is_pointer(pod))
|
||||
return -EINVAL;
|
||||
struct spa_pod_pointer_body b;
|
||||
SPA_POD_BODY_LOAD_FIELD_ONCE(&b, body, type);
|
||||
SPA_POD_BODY_LOAD_FIELD_ONCE(&b, body, value);
|
||||
*type = b.type;
|
||||
|
|
@ -333,6 +363,8 @@ SPA_API_POD_BODY const void *spa_pod_array_body_get_values(const struct spa_pod_
|
|||
*n_values = child_size ? (arr->pod.size - sizeof(arr->body)) / child_size : 0;
|
||||
*val_size = child_size;
|
||||
*val_type = arr->body.child.type;
|
||||
if (*val_size < spa_pod_type_size(*val_type))
|
||||
*n_values = 0;
|
||||
return body;
|
||||
}
|
||||
|
||||
|
|
@ -366,13 +398,16 @@ SPA_API_POD_BODY const void *spa_pod_choice_body_get_values(const struct spa_pod
|
|||
const void *body, uint32_t *n_values, uint32_t *choice,
|
||||
uint32_t *val_size, uint32_t *val_type)
|
||||
{
|
||||
uint32_t child_size = pod->body.child.size;
|
||||
uint32_t child_size = pod->body.child.size, min, max;
|
||||
*val_size = child_size;
|
||||
*val_type = pod->body.child.type;
|
||||
*n_values = child_size ? (pod->pod.size - sizeof(pod->body)) / child_size : 0;
|
||||
*choice = pod->body.type;
|
||||
if (*choice == SPA_CHOICE_None)
|
||||
*n_values = SPA_MIN(1u, *n_values);
|
||||
spa_pod_choice_n_values(*choice, &min, &max);
|
||||
if (*n_values < min || *val_size < spa_pod_type_size(*val_type))
|
||||
*n_values = 0;
|
||||
else if (*n_values > max)
|
||||
*n_values = max;
|
||||
return body;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,13 @@ SPA_API_POD_COMPARE int spa_pod_compare_value(uint32_t type, const void *r1, con
|
|||
return 0;
|
||||
}
|
||||
|
||||
SPA_API_POD_COMPARE int spa_pod_memcmp(const struct spa_pod *a,
|
||||
const struct spa_pod *b)
|
||||
{
|
||||
return ((a == b) || (a && b && SPA_POD_SIZE(a) == SPA_POD_SIZE(b) &&
|
||||
memcmp(a, b, SPA_POD_SIZE(b)) == 0)) ? 0 : 1;
|
||||
}
|
||||
|
||||
SPA_API_POD_COMPARE int spa_pod_compare(const struct spa_pod *pod1,
|
||||
const struct spa_pod *pod2)
|
||||
{
|
||||
|
|
@ -149,12 +156,8 @@ SPA_API_POD_COMPARE int spa_pod_compare(const struct spa_pod *pod1,
|
|||
break;
|
||||
}
|
||||
case SPA_TYPE_Array:
|
||||
{
|
||||
if (pod1->size != pod2->size)
|
||||
return -EINVAL;
|
||||
res = memcmp(SPA_POD_BODY(pod1), SPA_POD_BODY(pod2), pod2->size);
|
||||
res = spa_pod_memcmp(pod1, pod2);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (pod1->size != pod2->size)
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -70,8 +70,10 @@ SPA_API_POD_DYNAMIC void spa_pod_dynamic_builder_continue(struct spa_pod_dynamic
|
|||
|
||||
SPA_API_POD_DYNAMIC void spa_pod_dynamic_builder_clean(struct spa_pod_dynamic_builder *builder)
|
||||
{
|
||||
if (builder->data != builder->b.data)
|
||||
if (builder->data != builder->b.data) {
|
||||
free(builder->b.data);
|
||||
builder->b.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
SPA_DEFINE_AUTO_CLEANUP(spa_pod_dynamic_builder, struct spa_pod_dynamic_builder, {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ spa_pod_filter_prop(struct spa_pod_builder *b,
|
|||
v1 = spa_pod_get_values(&p1->value, &nalt1, &p1c);
|
||||
v2 = spa_pod_get_values(&p2->value, &nalt2, &p2c);
|
||||
|
||||
/* empty choices */
|
||||
/* empty/invalid choices */
|
||||
if (nalt1 < 1 || nalt2 < 1)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -95,8 +95,6 @@ spa_pod_filter_prop(struct spa_pod_builder *b,
|
|||
/* incompatible property types */
|
||||
if (type != v2->type || size != v2->size || p1->key != p2->key)
|
||||
return -EINVAL;
|
||||
if (size < spa_pod_type_size(type))
|
||||
return -EINVAL;
|
||||
|
||||
/* start with copying the property */
|
||||
spa_pod_builder_prop(b, p1->key, p1->flags & p2->flags);
|
||||
|
|
@ -341,9 +339,7 @@ SPA_API_POD_FILTER int spa_pod_filter_part(struct spa_pod_builder *b,
|
|||
|
||||
default:
|
||||
if (pf != NULL) {
|
||||
if (pp->size != pf->size)
|
||||
return -EINVAL;
|
||||
if (memcmp(pp, pf, pp->size) != 0)
|
||||
if (spa_pod_memcmp(pp, pf) != 0)
|
||||
return -EINVAL;
|
||||
do_advance = true;
|
||||
}
|
||||
|
|
@ -406,7 +402,7 @@ SPA_API_POD_FILTER int spa_pod_filter_object_make(struct spa_pod_object *pod)
|
|||
struct spa_pod *v = spa_pod_get_values(&res->value, &nvals, &choice);
|
||||
const void *vals = SPA_POD_BODY(v);
|
||||
|
||||
if (v->size < spa_pod_type_size(v->type))
|
||||
if (nvals < 1)
|
||||
continue;
|
||||
|
||||
if (spa_pod_compare_is_valid_choice(v->type, v->size,
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ SPA_API_POD_ITER struct spa_pod *spa_pod_get_values(const struct spa_pod *pod,
|
|||
spa_pod_choice_body_get_values(p, SPA_POD_BODY_CONST(p), n_vals, choice, &size, &type);
|
||||
return (struct spa_pod*)&p->body.child;
|
||||
} else {
|
||||
*n_vals = 1;
|
||||
*n_vals = pod->size < spa_pod_type_size(pod->type) ? 0 : 1;
|
||||
*choice = SPA_CHOICE_None;
|
||||
return (struct spa_pod*)pod;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,13 +90,14 @@ spa_pod_parser_read_header(struct spa_pod_parser *parser, uint32_t offset, uint3
|
|||
/* Cast to uint64_t to avoid wraparound. */
|
||||
const uint64_t long_offset = (uint64_t)offset + header_size;
|
||||
if (long_offset <= size && (offset & 7) == 0) {
|
||||
struct spa_pod *pod;
|
||||
/* a barrier around the memcpy to make sure it is not moved around or
|
||||
* duplicated after the size check below. We need to to work on shared
|
||||
* memory while there could be updates happening while we read. */
|
||||
* duplicated after the size check below. We need to work on shared
|
||||
* memory and so there could be updates happening while we read. */
|
||||
SPA_BARRIER;
|
||||
memcpy(header, SPA_PTROFF(parser->data, offset, void), header_size);
|
||||
SPA_BARRIER;
|
||||
struct spa_pod *pod = SPA_PTROFF(header, pod_offset, struct spa_pod);
|
||||
pod = SPA_PTROFF(header, pod_offset, struct spa_pod);
|
||||
/* Check that the size (rounded to the next multiple of 8) is in bounds. */
|
||||
if (long_offset + SPA_ROUND_UP_N((uint64_t)pod->size, SPA_POD_ALIGN) <= size) {
|
||||
*body = SPA_PTROFF(parser->data, long_offset, void);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ extern "C" {
|
|||
|
||||
#define SPA_CHOICE_RANGE(def,min,max) 3,(def),(min),(max)
|
||||
#define SPA_CHOICE_STEP(def,min,max,step) 4,(def),(min),(max),(step)
|
||||
#define SPA_CHOICE_ENUM(n_vals,...) (n_vals),##__VA_ARGS__
|
||||
#define SPA_CHOICE_ENUM(n_vals,def,alt1,...) (n_vals),(def),(alt1),##__VA_ARGS__
|
||||
#define SPA_CHOICE_FLAGS(flags) 1, (flags)
|
||||
#define SPA_CHOICE_FEATURES(features) 1, (features)
|
||||
#define SPA_CHOICE_BOOL(def) 3,(def),(def),!(def)
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@ SPA_API_THREAD int spa_thread_utils_drop_rt(struct spa_thread_utils *o,
|
|||
#define SPA_KEY_THREAD_STACK_SIZE "thread.stack-size" /* the stack size of the thread */
|
||||
#define SPA_KEY_THREAD_AFFINITY "thread.affinity" /* array of CPUs for this thread */
|
||||
#define SPA_KEY_THREAD_CREATOR "thread.creator" /* platform specific thread creator function */
|
||||
#define SPA_KEY_THREAD_RESET_ON_FORK "thread.reset-on-fork" /* reset priority and policy for real-time threads
|
||||
on fork. Default true */
|
||||
|
||||
/**
|
||||
* \}
|
||||
|
|
|
|||
|
|
@ -46,9 +46,13 @@ __extension__ ({ \
|
|||
|
||||
/* ========================================================================== */
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__cleanup__)
|
||||
|
||||
#ifdef __has_attribute
|
||||
#if __has_attribute(__cleanup__)
|
||||
#define spa_cleanup(func) __attribute__((__cleanup__(func)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef spa_cleanup
|
||||
|
||||
#define SPA_DEFINE_AUTO_CLEANUP(name, type, ...) \
|
||||
typedef __typeof__(type) _spa_auto_cleanup_type_ ## name; \
|
||||
|
|
|
|||
|
|
@ -54,6 +54,15 @@ SPA_API_JSON void spa_json_init(struct spa_json * iter, const char *data, size_t
|
|||
{
|
||||
*iter = SPA_JSON_INIT(data, size);
|
||||
}
|
||||
|
||||
#define SPA_JSON_INIT_RELAX(type,data,size) \
|
||||
((struct spa_json) { (data), (data)+(size), NULL, (uint32_t)((type) == '[' ? 0x10 : 0x0), 0 })
|
||||
|
||||
SPA_API_JSON void spa_json_init_relax(struct spa_json * iter, char type, const char *data, size_t size)
|
||||
{
|
||||
*iter = SPA_JSON_INIT_RELAX(type, data, size);
|
||||
}
|
||||
|
||||
#define SPA_JSON_ENTER(iter) ((struct spa_json) { (iter)->cur, (iter)->end, (iter), (iter)->state & 0xff0, 0 })
|
||||
|
||||
SPA_API_JSON void spa_json_enter(struct spa_json * iter, struct spa_json * sub)
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ SPA_API_JSON_UTILS int spa_json_begin_container(struct spa_json * iter,
|
|||
spa_json_init(iter, data, size);
|
||||
res = spa_json_enter_container(iter, iter, type);
|
||||
if (res == -EPROTO && relax)
|
||||
spa_json_init(iter, data, size);
|
||||
spa_json_init_relax(iter, type, data, size);
|
||||
else if (res <= 0)
|
||||
return res;
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <locale.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef SPA_TYPE_INFO_H
|
||||
#define SPA_TYPE_INFO_H
|
||||
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
#include <spa/utils/type.h>
|
||||
#include <spa/utils/enum-types.h>
|
||||
|
|
@ -78,6 +79,8 @@ static const struct spa_type_info spa_types[] = {
|
|||
{ SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Latency, spa_type_param_latency },
|
||||
{ SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ProcessLatency, spa_type_param_process_latency },
|
||||
{ SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Tag, spa_type_param_tag },
|
||||
{ SPA_TYPE_OBJECT_PeerParam, SPA_TYPE_Object, SPA_TYPE_INFO_PeerParam, spa_type_peer_param },
|
||||
{ SPA_TYPE_OBJECT_ParamDict, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Dict, spa_type_param_dict },
|
||||
|
||||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ enum {
|
|||
SPA_TYPE_OBJECT_ParamLatency,
|
||||
SPA_TYPE_OBJECT_ParamProcessLatency,
|
||||
SPA_TYPE_OBJECT_ParamTag,
|
||||
SPA_TYPE_OBJECT_PeerParam,
|
||||
SPA_TYPE_OBJECT_ParamDict,
|
||||
_SPA_TYPE_OBJECT_LAST, /**< not part of ABI */
|
||||
|
||||
/* vendor extensions */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
#undef SPA_AUDIO_MAX_CHANNELS
|
||||
|
||||
#define SPA_API_IMPL SPA_EXPORT
|
||||
#include <spa/utils/defs.h>
|
||||
#include <spa/buffer/alloc.h>
|
||||
|
|
@ -61,6 +63,7 @@
|
|||
#include <spa/param/audio/iec958-types.h>
|
||||
#include <spa/param/audio/iec958-utils.h>
|
||||
#include <spa/param/audio/layout.h>
|
||||
#include <spa/param/audio/layout-types.h>
|
||||
#include <spa/param/audio/mp3.h>
|
||||
#include <spa/param/audio/mp3-types.h>
|
||||
#include <spa/param/audio/mp3-utils.h>
|
||||
|
|
@ -165,9 +168,3 @@
|
|||
#include <spa/utils/string.h>
|
||||
#include <spa/utils/type.h>
|
||||
#include <spa/utils/type-info.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ static void init_device(pa_card *impl, pa_alsa_device *dev, pa_alsa_direction_t
|
|||
pa_proplist_update(dev->proplist, PA_UPDATE_REPLACE, m->input_proplist);
|
||||
}
|
||||
if (m->split) {
|
||||
char pos[2048];
|
||||
char pos[PA_CHANNELS_MAX*8];
|
||||
struct spa_strbuf b;
|
||||
int i;
|
||||
|
||||
|
|
@ -1137,51 +1137,56 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask)
|
|||
}
|
||||
|
||||
old_position = pa_proplist_gets(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED);
|
||||
if (eld.speakers == 0) {
|
||||
if (eld.speakers == 0 || eld.lpcm_channels == 0) {
|
||||
changed |= old_position != NULL;
|
||||
pa_proplist_unset(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED);
|
||||
} else {
|
||||
uint32_t positions[eld.lpcm_channels];
|
||||
char position[64];
|
||||
int i = 0, pos = 0;
|
||||
char position[eld.lpcm_channels * 8];
|
||||
struct spa_strbuf b;
|
||||
int i = 0;
|
||||
|
||||
#define _ADD_CHANNEL_POSITION(pos) \
|
||||
{ \
|
||||
if (i < eld.lpcm_channels) \
|
||||
positions[i++] = pos; \
|
||||
}
|
||||
|
||||
if (eld.speakers & 0x01) {
|
||||
positions[i++] = ACP_CHANNEL_FL;
|
||||
positions[i++] = ACP_CHANNEL_FR;
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_FL);
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_FR);
|
||||
}
|
||||
if (eld.speakers & 0x02) {
|
||||
positions[i++] = ACP_CHANNEL_LFE;
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_LFE);
|
||||
}
|
||||
if (eld.speakers & 0x04) {
|
||||
positions[i++] = ACP_CHANNEL_FC;
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_FC);
|
||||
}
|
||||
if (eld.speakers & 0x08) {
|
||||
positions[i++] = ACP_CHANNEL_RL;
|
||||
positions[i++] = ACP_CHANNEL_RR;
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RL);
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RR);
|
||||
}
|
||||
/* The rest are out of order in order of what channels we would prefer to use/expose first */
|
||||
if (eld.speakers & 0x40) {
|
||||
/* Use SL/SR instead of RLC/RRC */
|
||||
positions[i++] = ACP_CHANNEL_SL;
|
||||
positions[i++] = ACP_CHANNEL_SR;
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_SL);
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_SR);
|
||||
}
|
||||
if (eld.speakers & 0x20) {
|
||||
positions[i++] = ACP_CHANNEL_RLC;
|
||||
positions[i++] = ACP_CHANNEL_RRC;
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RLC);
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RRC);
|
||||
}
|
||||
if (eld.speakers & 0x10) {
|
||||
positions[i++] = ACP_CHANNEL_RC;
|
||||
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RC);
|
||||
}
|
||||
|
||||
while (i < eld.lpcm_channels)
|
||||
positions[i++] = ACP_CHANNEL_UNKNOWN;
|
||||
|
||||
for (i = 0, pos = 0; i < eld.lpcm_channels; i++) {
|
||||
pos += snprintf(&position[pos], sizeof(position) - pos, "%s,", channel_names[positions[i]]);
|
||||
}
|
||||
|
||||
/* Overwrite trailing , */
|
||||
position[pos - 1] = 0;
|
||||
spa_strbuf_init(&b, position, sizeof(position));
|
||||
spa_strbuf_append(&b, "[");
|
||||
for (i = 0; i < eld.lpcm_channels; i++)
|
||||
spa_strbuf_append(&b, "%s%s", i ? "," : "", channel_names[positions[i]]);
|
||||
spa_strbuf_append(&b, "]");
|
||||
|
||||
changed |= (old_position == NULL) || (!spa_streq(old_position, position));
|
||||
pa_proplist_sets(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED, position);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,11 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef SPA_AUDIO_MAX_CHANNELS
|
||||
#define PA_CHANNELS_MAX ((int)SPA_AUDIO_MAX_CHANNELS)
|
||||
#else
|
||||
#define PA_CHANNELS_MAX 64
|
||||
#endif
|
||||
|
||||
#define PA_CHANNEL_MAP_SNPRINT_MAX (PA_CHANNELS_MAX * 32)
|
||||
|
||||
|
|
@ -451,7 +455,6 @@ static inline int pa_channel_map_equal(const pa_channel_map *a, const pa_channel
|
|||
|
||||
static inline char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
|
||||
unsigned channel;
|
||||
bool first = true;
|
||||
char *e;
|
||||
if (!pa_channel_map_valid(map)) {
|
||||
pa_snprintf(s, l, "%s", _("(invalid)"));
|
||||
|
|
@ -460,12 +463,10 @@ static inline char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_m
|
|||
*(e = s) = 0;
|
||||
for (channel = 0; channel < map->channels && l > 1; channel++) {
|
||||
l -= pa_snprintf(e, l, "%s%s",
|
||||
first ? "" : ",",
|
||||
channel == 0 ? "" : ",",
|
||||
pa_channel_position_to_string(map->map[channel]));
|
||||
e = strchr(e, 0);
|
||||
first = false;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
extern struct spa_i18n *acp_i18n;
|
||||
|
||||
#define MAX_POLL 16
|
||||
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
|
||||
|
||||
#define DEFAULT_DEVICE "hw:0"
|
||||
#define DEFAULT_AUTO_PROFILE true
|
||||
|
|
@ -155,12 +156,13 @@ static int emit_node(struct impl *this, struct acp_device *dev)
|
|||
const struct acp_dict_item *it;
|
||||
uint32_t n_items, i;
|
||||
char device_name[128], path[210], channels[16], ch[12], routes[16];
|
||||
char card_index[16], card_name[64], *p;
|
||||
char positions[SPA_AUDIO_MAX_CHANNELS * 12];
|
||||
char card_index[16], card_name[64];
|
||||
char positions[MAX_CHANNELS * 12];
|
||||
char codecs[512];
|
||||
struct spa_device_object_info info;
|
||||
struct acp_card *card = this->card;
|
||||
const char *stream, *card_id, *bus;
|
||||
struct spa_strbuf b;
|
||||
|
||||
info = SPA_DEVICE_OBJECT_INFO_INIT();
|
||||
info.type = SPA_TYPE_INTERFACE_Node;
|
||||
|
|
@ -199,11 +201,13 @@ static int emit_node(struct impl *this, struct acp_device *dev)
|
|||
snprintf(channels, sizeof(channels), "%d", dev->format.channels);
|
||||
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNELS, channels);
|
||||
|
||||
p = positions;
|
||||
spa_strbuf_init(&b, positions, sizeof(positions));
|
||||
spa_strbuf_append(&b, "[");
|
||||
for (i = 0; i < dev->format.channels; i++) {
|
||||
p += snprintf(p, 12, "%s%s", i == 0 ? "" : ",",
|
||||
spa_strbuf_append(&b, "%s%s", i == 0 ? " " : ", ",
|
||||
acp_channel_str(ch, sizeof(ch), dev->format.map[i]));
|
||||
}
|
||||
spa_strbuf_append(&b, " ]");
|
||||
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_POSITION, positions);
|
||||
|
||||
if (dev->n_codecs > 0) {
|
||||
|
|
@ -673,8 +677,8 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct
|
|||
struct spa_pod_prop *prop;
|
||||
struct spa_pod_object *obj = (struct spa_pod_object *) props;
|
||||
int changed = 0;
|
||||
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
|
||||
float volumes[MAX_CHANNELS];
|
||||
uint32_t channels[MAX_CHANNELS];
|
||||
uint32_t n_volumes = 0;
|
||||
|
||||
if (!spa_pod_is_object_type(props, SPA_TYPE_OBJECT_Props))
|
||||
|
|
@ -696,13 +700,13 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct
|
|||
break;
|
||||
case SPA_PROP_channelVolumes:
|
||||
if ((n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
|
||||
volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
|
||||
volumes, SPA_N_ELEMENTS(volumes))) > 0) {
|
||||
changed++;
|
||||
}
|
||||
break;
|
||||
case SPA_PROP_channelMap:
|
||||
if (spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
|
||||
channels, SPA_AUDIO_MAX_CHANNELS) > 0) {
|
||||
channels, SPA_N_ELEMENTS(channels)) > 0) {
|
||||
changed++;
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -163,10 +163,10 @@ static int alsa_set_param(struct state *state, const char *k, const char *s)
|
|||
int fmt_change = 0;
|
||||
if (spa_streq(k, SPA_KEY_AUDIO_CHANNELS)) {
|
||||
state->default_channels = atoi(s);
|
||||
if (state->default_channels > SPA_AUDIO_MAX_CHANNELS) {
|
||||
if (state->default_channels > MAX_CHANNELS) {
|
||||
spa_log_warn(state->log, "%p: %s: %s > %d, clamping",
|
||||
state, k, s, SPA_AUDIO_MAX_CHANNELS);
|
||||
state->default_channels = SPA_AUDIO_MAX_CHANNELS;
|
||||
state, k, s, MAX_CHANNELS);
|
||||
state->default_channels = MAX_CHANNELS;
|
||||
}
|
||||
fmt_change++;
|
||||
} else if (spa_streq(k, SPA_KEY_AUDIO_RATE)) {
|
||||
|
|
@ -240,35 +240,33 @@ static int alsa_set_param(struct state *state, const char *k, const char *s)
|
|||
|
||||
static int position_to_string(struct channel_map *map, char *val, size_t len)
|
||||
{
|
||||
uint32_t i, o = 0;
|
||||
int r;
|
||||
o += snprintf(val, len, "[ ");
|
||||
for (i = 0; i < map->channels; i++) {
|
||||
r = snprintf(val+o, len-o, "%s%s", i == 0 ? "" : ", ",
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
map->pos[i]));
|
||||
if (r < 0 || o + r >= len)
|
||||
return -ENOSPC;
|
||||
o += r;
|
||||
uint32_t i;
|
||||
char pos[8];
|
||||
struct spa_strbuf b;
|
||||
|
||||
spa_strbuf_init(&b, val, len);
|
||||
spa_strbuf_append(&b, "[");
|
||||
for (i = 0; i < map->n_pos; i++) {
|
||||
spa_strbuf_append(&b, "%s%s", i == 0 ? " " : ", ",
|
||||
spa_type_audio_channel_make_short_name(map->pos[i],
|
||||
pos, sizeof(pos), "UNK"));
|
||||
}
|
||||
if (len > o)
|
||||
o += snprintf(val+o, len-o, " ]");
|
||||
if (spa_strbuf_append(&b, " ]") < 2)
|
||||
return -ENOSPC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uint32_array_to_string(uint32_t *vals, uint32_t n_vals, char *val, size_t len)
|
||||
{
|
||||
uint32_t i, o = 0;
|
||||
int r;
|
||||
o += snprintf(val, len, "[ ");
|
||||
for (i = 0; i < n_vals; i++) {
|
||||
r = snprintf(val+o, len-o, "%s%d", i == 0 ? "" : ", ", vals[i]);
|
||||
if (r < 0 || o + r >= len)
|
||||
return -ENOSPC;
|
||||
o += r;
|
||||
}
|
||||
if (len > o)
|
||||
o += snprintf(val+o, len-o, " ]");
|
||||
uint32_t i;
|
||||
struct spa_strbuf b;
|
||||
|
||||
spa_strbuf_init(&b, val, len);
|
||||
spa_strbuf_append(&b, "[");
|
||||
for (i = 0; i < n_vals; i++)
|
||||
spa_strbuf_append(&b, "%s%d", i == 0 ? " " : ", ", vals[i]);
|
||||
if (spa_strbuf_append(&b, " ]") < 2)
|
||||
return -ENOSPC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -775,7 +773,7 @@ static void bind_ctl_event(struct spa_source *source)
|
|||
snd_ctl_elem_id_alloca(&bound_id);
|
||||
snd_ctl_elem_value_alloca(&old_value);
|
||||
|
||||
while ((err = snd_ctl_read(state->ctl, ev) > 0)) {
|
||||
while ((err = snd_ctl_read(state->ctl, ev)) > 0) {
|
||||
bool changed = false;
|
||||
|
||||
if (snd_ctl_event_get_type(ev) != SND_CTL_EVENT_ELEM)
|
||||
|
|
@ -1584,8 +1582,8 @@ static int add_channels(struct state *state, bool all, uint32_t index, uint32_t
|
|||
spa_log_debug(state->log, "channels (%d %d) default:%d all:%d",
|
||||
min, max, state->default_channels, all);
|
||||
|
||||
min = SPA_MIN(min, SPA_AUDIO_MAX_CHANNELS);
|
||||
max = SPA_MIN(max, SPA_AUDIO_MAX_CHANNELS);
|
||||
min = SPA_MIN(min, MAX_CHANNELS);
|
||||
max = SPA_MIN(max, MAX_CHANNELS);
|
||||
|
||||
if (state->default_channels != 0 && !all) {
|
||||
if (min > state->default_channels ||
|
||||
|
|
@ -1645,7 +1643,7 @@ skip_channels:
|
|||
} else {
|
||||
const struct channel_map *map = NULL;
|
||||
spa_pod_builder_int(b, min);
|
||||
if (state->default_pos.channels == min) {
|
||||
if (state->default_pos.n_pos == min) {
|
||||
map = &state->default_pos;
|
||||
spa_log_debug(state->log, "%p: using provided default", state);
|
||||
} else if (min <= 8) {
|
||||
|
|
@ -1655,7 +1653,7 @@ skip_channels:
|
|||
if (map) {
|
||||
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0);
|
||||
spa_pod_builder_push_array(b, &f[0]);
|
||||
for (i = 0; i < map->channels; i++) {
|
||||
for (i = 0; i < map->n_pos; i++) {
|
||||
spa_log_debug(state->log, "%p: position %zd %d", state, i, map->pos[i]);
|
||||
spa_pod_builder_id(b, map->pos[i]);
|
||||
}
|
||||
|
|
@ -2038,7 +2036,9 @@ static void recalc_headroom(struct state *state)
|
|||
uint32_t latency;
|
||||
uint32_t rate = 0;
|
||||
|
||||
if (state->position != NULL)
|
||||
if (state->force_quantum && !state->following)
|
||||
rate = state->rate;
|
||||
else if (state->position != NULL)
|
||||
rate = state->position->clock.target_rate.denom;
|
||||
|
||||
if (state->use_period_size_min_as_headroom)
|
||||
|
|
@ -2065,8 +2065,6 @@ static void recalc_headroom(struct state *state)
|
|||
state->headroom = 0;
|
||||
|
||||
latency = SPA_MAX(state->min_delay, SPA_MIN(state->max_delay, state->headroom));
|
||||
if (rate != 0 && state->rate != 0)
|
||||
latency = SPA_SCALE32_UP(latency, rate, state->rate);
|
||||
|
||||
if (state->is_firewire) {
|
||||
/* XXX: For ALSA FireWire drivers, unlike for other ALSA drivers, buffer size
|
||||
|
|
@ -2074,6 +2072,8 @@ static void recalc_headroom(struct state *state)
|
|||
*/
|
||||
latency += state->buffer_frames;
|
||||
}
|
||||
if (rate != 0 && state->rate != 0)
|
||||
latency = SPA_SCALE32_UP(latency, rate, state->rate);
|
||||
|
||||
state->latency[state->port_direction].min_rate =
|
||||
state->latency[state->port_direction].max_rate = latency;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#define MAX_RATES 16
|
||||
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
|
||||
|
||||
#define DEFAULT_PERIOD 1024u
|
||||
#define DEFAULT_RATE 48000u
|
||||
|
|
@ -71,8 +72,8 @@ struct buffer {
|
|||
#define BW_PERIOD (3 * SPA_NSEC_PER_SEC)
|
||||
|
||||
struct channel_map {
|
||||
uint32_t channels;
|
||||
uint32_t pos[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t n_pos;
|
||||
uint32_t pos[MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct card {
|
||||
|
|
@ -314,7 +315,7 @@ void spa_alsa_emit_port_info(struct state *state, bool full);
|
|||
|
||||
static inline void spa_alsa_parse_position(struct channel_map *map, const char *val, size_t len)
|
||||
{
|
||||
spa_audio_parse_position(val, len, map->pos, &map->channels);
|
||||
spa_audio_parse_position_n(val, len, map->pos, SPA_N_ELEMENTS(map->pos), &map->n_pos);
|
||||
}
|
||||
|
||||
static inline uint32_t spa_alsa_parse_rates(uint32_t *rates, uint32_t max, const char *val, size_t len)
|
||||
|
|
|
|||
|
|
@ -808,6 +808,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
if (spa_format_audio_parse(param, &info) < 0) {
|
||||
spa_log_error(this->log, "%p: cannot set Format param: "
|
||||
"parsing the POD failed", this);
|
||||
spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 0, NULL, param);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (info.media_subtype != SPA_MEDIA_SUBTYPE_raw) {
|
||||
|
|
@ -841,6 +842,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
SPA_PARAM_PORT_CONFIG_format, SPA_POD_OPT_Pod(&format)) < 0) {
|
||||
spa_log_error(this->log, "%p: cannot set PortConfig param: "
|
||||
"parsing the POD failed", this);
|
||||
spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 0, NULL, param);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -848,8 +850,12 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
struct spa_audio_info info;
|
||||
|
||||
spa_zero(info);
|
||||
if ((res = spa_format_audio_parse(format, &info)) < 0)
|
||||
if ((res = spa_format_audio_parse(format, &info)) < 0) {
|
||||
spa_log_error(this->log, "%p: cannot set PortConfig param: "
|
||||
"parsing format failed: %s", this, spa_strerror(res));
|
||||
spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 0, NULL, format);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (info.media_subtype == SPA_MEDIA_SUBTYPE_raw) {
|
||||
info.info.raw.rate = 0;
|
||||
|
|
@ -1213,6 +1219,9 @@ static void follower_convert_port_info(void *data,
|
|||
case SPA_PARAM_Tag:
|
||||
idx = IDX_Tag;
|
||||
break;
|
||||
case SPA_PARAM_EnumFormat:
|
||||
idx = IDX_EnumFormat;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1240,6 +1249,11 @@ static void follower_convert_port_info(void *data,
|
|||
spa_log_debug(this->log, "tag: %d (%s)", res,
|
||||
spa_strerror(res));
|
||||
}
|
||||
if (idx == IDX_EnumFormat) {
|
||||
spa_log_info(this->log, "new EnumFormat from converter");
|
||||
/* we will renegotiate when restarting */
|
||||
this->recheck_format = true;
|
||||
}
|
||||
spa_log_debug(this->log, "param %d changed", info->params[i].id);
|
||||
}
|
||||
}
|
||||
|
|
@ -1438,7 +1452,7 @@ static void follower_port_info(void *data,
|
|||
spa_strerror(res));
|
||||
}
|
||||
if (idx == IDX_EnumFormat) {
|
||||
spa_log_debug(this->log, "new formats");
|
||||
spa_log_debug(this->log, "new EnumFormat from follower");
|
||||
/* we will renegotiate when restarting */
|
||||
this->recheck_format = true;
|
||||
}
|
||||
|
|
@ -2046,11 +2060,12 @@ static int do_auto_port_config(struct impl *this, const char *str)
|
|||
return -ENOENT;
|
||||
|
||||
if (format.media_subtype == SPA_MEDIA_SUBTYPE_raw) {
|
||||
uint32_t n_pos = SPA_MIN(SPA_N_ELEMENTS(format.info.raw.position), format.info.raw.channels);
|
||||
if (position == POSITION_AUX) {
|
||||
for (i = 0; i < format.info.raw.channels; i++)
|
||||
for (i = 0; i < n_pos; i++)
|
||||
format.info.raw.position[i] = SPA_AUDIO_CHANNEL_START_Aux + i;
|
||||
} else if (position == POSITION_UNKNOWN) {
|
||||
for (i = 0; i < format.info.raw.channels; i++)
|
||||
for (i = 0; i < n_pos; i++)
|
||||
format.info.raw.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,10 +47,11 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert");
|
|||
#define DEFAULT_RATE 48000
|
||||
#define DEFAULT_CHANNELS 2
|
||||
|
||||
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
|
||||
#define MAX_ALIGN FMT_OPS_MAX_ALIGN
|
||||
#define MAX_BUFFERS 32
|
||||
#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS
|
||||
#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1)
|
||||
#define MAX_DATAS MAX_CHANNELS
|
||||
#define MAX_PORTS (MAX_CHANNELS+1)
|
||||
#define MAX_STAGES 64
|
||||
#define MAX_GRAPH 9 /* 8 active + 1 replacement slot */
|
||||
|
||||
|
|
@ -62,7 +63,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert");
|
|||
struct volumes {
|
||||
bool mute;
|
||||
uint32_t n_volumes;
|
||||
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
float volumes[MAX_CHANNELS];
|
||||
};
|
||||
|
||||
static void init_volumes(struct volumes *vol)
|
||||
|
|
@ -70,7 +71,7 @@ static void init_volumes(struct volumes *vol)
|
|||
uint32_t i;
|
||||
vol->mute = DEFAULT_MUTE;
|
||||
vol->n_volumes = 0;
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
|
||||
for (i = 0; i < MAX_CHANNELS; i++)
|
||||
vol->volumes[i] = DEFAULT_VOLUME;
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +92,7 @@ struct props {
|
|||
float max_volume;
|
||||
float prev_volume;
|
||||
uint32_t n_channels;
|
||||
uint32_t channel_map[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t channel_map[MAX_CHANNELS];
|
||||
struct volumes channel;
|
||||
struct volumes soft;
|
||||
struct volumes monitor;
|
||||
|
|
@ -99,6 +100,7 @@ struct props {
|
|||
unsigned int mix_disabled:1;
|
||||
unsigned int resample_disabled:1;
|
||||
unsigned int resample_quality;
|
||||
struct resample_config resample_config;
|
||||
double rate;
|
||||
char wav_path[512];
|
||||
unsigned int lock_volumes:1;
|
||||
|
|
@ -112,7 +114,7 @@ static void props_reset(struct props *props)
|
|||
props->min_volume = DEFAULT_MIN_VOLUME;
|
||||
props->max_volume = DEFAULT_MAX_VOLUME;
|
||||
props->n_channels = 0;
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
|
||||
for (i = 0; i < MAX_CHANNELS; i++)
|
||||
props->channel_map[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
|
||||
init_volumes(&props->channel);
|
||||
init_volumes(&props->soft);
|
||||
|
|
@ -121,6 +123,7 @@ static void props_reset(struct props *props)
|
|||
props->mix_disabled = false;
|
||||
props->resample_disabled = false;
|
||||
props->resample_quality = RESAMPLE_DEFAULT_QUALITY;
|
||||
spa_zero(props->resample_config);
|
||||
props->rate = 1.0;
|
||||
spa_zero(props->wav_path);
|
||||
props->lock_volumes = false;
|
||||
|
|
@ -241,9 +244,9 @@ struct filter_graph {
|
|||
struct spa_filter_graph *graph;
|
||||
struct spa_hook listener;
|
||||
uint32_t n_inputs;
|
||||
uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t inputs_position[MAX_CHANNELS];
|
||||
uint32_t n_outputs;
|
||||
uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t outputs_position[MAX_CHANNELS];
|
||||
uint32_t latency;
|
||||
bool removing;
|
||||
bool setup;
|
||||
|
|
@ -281,6 +284,7 @@ struct impl {
|
|||
|
||||
struct props props;
|
||||
|
||||
struct spa_io_clock *io_clock;
|
||||
struct spa_io_position *io_position;
|
||||
struct spa_io_rate_match *io_rate_match;
|
||||
|
||||
|
|
@ -423,7 +427,6 @@ static int init_port(struct impl *this, enum spa_direction direction, uint32_t p
|
|||
uint32_t position, bool is_dsp, bool is_monitor, bool is_control)
|
||||
{
|
||||
struct port *port = GET_PORT(this, direction, port_id);
|
||||
const char *name;
|
||||
|
||||
spa_assert(port_id < MAX_PORTS);
|
||||
|
||||
|
|
@ -438,8 +441,7 @@ static int init_port(struct impl *this, enum spa_direction direction, uint32_t p
|
|||
port->latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
|
||||
port->latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
|
||||
|
||||
name = spa_debug_type_find_short_name(spa_type_audio_channel, position);
|
||||
snprintf(port->position, sizeof(port->position), "%s", name ? name : "UNK");
|
||||
spa_type_audio_channel_make_short_name(position, port->position, sizeof(port->position), "UNK");
|
||||
|
||||
port->info = SPA_PORT_INFO_INIT();
|
||||
port->info.change_mask = port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
|
||||
|
|
@ -1006,12 +1008,43 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
|
|||
spa_log_debug(this->log, "%p: io %d %p/%zd", this, id, data, size);
|
||||
|
||||
switch (id) {
|
||||
case SPA_IO_Position:
|
||||
this->io_position = data;
|
||||
case SPA_IO_Clock:
|
||||
this->io_clock = data;
|
||||
break;
|
||||
case SPA_IO_Position:
|
||||
{
|
||||
struct port *p;
|
||||
uint32_t i;
|
||||
|
||||
this->io_position = data;
|
||||
|
||||
if (this->io_position && this->io_clock &&
|
||||
this->io_position->clock.target_rate.denom != this->io_clock->target_rate.denom &&
|
||||
!this->props.resample_disabled) {
|
||||
spa_log_debug(this->log, "driver %d changed rate:%u -> %u", this->io_position->clock.id,
|
||||
this->io_clock->target_rate.denom,
|
||||
this->io_position->clock.target_rate.denom);
|
||||
|
||||
this->io_clock->target_rate = this->io_position->clock.target_rate;
|
||||
for (i = 0; i < this->dir[SPA_DIRECTION_INPUT].n_ports; i++) {
|
||||
if ((p = GET_IN_PORT(this, i)) && p->valid && !p->is_dsp && !p->is_control) {
|
||||
p->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
|
||||
p->params[IDX_EnumFormat].user++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < this->dir[SPA_DIRECTION_OUTPUT].n_ports; i++) {
|
||||
if ((p = GET_OUT_PORT(this, i)) && p->valid && !p->is_dsp && !p->is_control) {
|
||||
p->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
|
||||
p->params[IDX_EnumFormat].user++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
emit_info(this, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1103,11 +1136,11 @@ static void graph_info(void *object, const struct spa_filter_graph_info *info)
|
|||
else if (spa_streq(k, "n_outputs"))
|
||||
spa_atou32(s, &g->n_outputs, 0);
|
||||
else if (spa_streq(k, "inputs.audio.position"))
|
||||
spa_audio_parse_position(s, strlen(s),
|
||||
g->inputs_position, &g->n_inputs);
|
||||
spa_audio_parse_position_n(s, strlen(s), g->inputs_position,
|
||||
SPA_N_ELEMENTS(g->inputs_position), &g->n_inputs);
|
||||
else if (spa_streq(k, "outputs.audio.position"))
|
||||
spa_audio_parse_position(s, strlen(s),
|
||||
g->outputs_position, &g->n_outputs);
|
||||
spa_audio_parse_position_n(s, strlen(s), g->outputs_position,
|
||||
SPA_N_ELEMENTS(g->outputs_position), &g->n_outputs);
|
||||
else if (spa_streq(k, "latency")) {
|
||||
double latency;
|
||||
if (spa_atod(s, &latency))
|
||||
|
|
@ -1311,6 +1344,14 @@ static int do_sync_filter_graph(struct spa_loop *loop, bool async, uint32_t seq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void sync_filter_graph(struct impl *impl)
|
||||
{
|
||||
if (impl->data_loop)
|
||||
spa_loop_locked(impl->data_loop, do_sync_filter_graph, 0, NULL, 0, impl);
|
||||
else
|
||||
do_sync_filter_graph(NULL, false, 0, NULL, 0, impl);
|
||||
}
|
||||
|
||||
static void clean_filter_handles(struct impl *impl, bool force)
|
||||
{
|
||||
struct filter_graph *g, *t;
|
||||
|
|
@ -1398,7 +1439,7 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
|
|||
if (impl->setup)
|
||||
res = setup_filter_graphs(impl, false);
|
||||
|
||||
spa_loop_locked(impl->data_loop, do_sync_filter_graph, 0, NULL, 0, impl);
|
||||
sync_filter_graph(impl);
|
||||
|
||||
if (impl->in_filter_props == 0)
|
||||
clean_filter_handles(impl, false);
|
||||
|
|
@ -1447,6 +1488,16 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
|
|||
this->props.resample_quality = atoi(s);
|
||||
else if (spa_streq(k, "resample.disable"))
|
||||
this->props.resample_disabled = spa_atob(s);
|
||||
else if (spa_streq(k, "resample.window"))
|
||||
this->props.resample_config.window = resample_window_from_label(s);
|
||||
else if (spa_streq(k, "resample.cutoff"))
|
||||
spa_atod(s, &this->props.resample_config.cutoff);
|
||||
else if (spa_streq(k, "resample.n-taps"))
|
||||
spa_atou32(s, &this->props.resample_config.n_taps, 0);
|
||||
else if (spa_strstartswith(k, "resample.param.")) {
|
||||
uint32_t idx = resample_param_from_label(k+strlen("resample.param."));
|
||||
spa_atod(s, &this->props.resample_config.params[idx]);
|
||||
}
|
||||
else if (spa_streq(k, "dither.noise"))
|
||||
spa_atou32(s, &this->dir[1].conv.noise_bits, 0);
|
||||
else if (spa_streq(k, "dither.method"))
|
||||
|
|
@ -1457,6 +1508,10 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
|
|||
}
|
||||
else if (spa_streq(k, "channelmix.lock-volumes"))
|
||||
this->props.lock_volumes = spa_atob(s);
|
||||
else if (spa_streq(k, "audioconvert.filter-graph.disable")) {
|
||||
if (!*disable_filter)
|
||||
*disable_filter = spa_atob(s);
|
||||
}
|
||||
else if (spa_strstartswith(k, "audioconvert.filter-graph.")) {
|
||||
int order = atoi(k + strlen("audioconvert.filter-graph."));
|
||||
if ((res = load_filter_graph(this, s, order)) < 0) {
|
||||
|
|
@ -1464,10 +1519,6 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
|
|||
order, spa_strerror(res));
|
||||
}
|
||||
}
|
||||
else if (spa_streq(k, "audioconvert.filter-graph.disable")) {
|
||||
if (!*disable_filter)
|
||||
*disable_filter = spa_atob(s);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return 1;
|
||||
|
|
@ -1539,8 +1590,6 @@ static int get_ramp_samples(struct impl *this, struct volume_ramp_params *vrp)
|
|||
samples = (vrp->volume_ramp_time * vrp->rate) / 1000;
|
||||
spa_log_info(this->log, "volume ramp samples calculated from time is %d", samples);
|
||||
}
|
||||
if (!samples)
|
||||
samples = -1;
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
|
@ -1551,12 +1600,10 @@ static int get_ramp_step_samples(struct impl *this, struct volume_ramp_params *v
|
|||
if (vrp->volume_ramp_step_samples)
|
||||
samples = vrp->volume_ramp_step_samples;
|
||||
else if (vrp->volume_ramp_step_time) {
|
||||
/* convert the step time which is in nano seconds to seconds */
|
||||
samples = (vrp->volume_ramp_step_time/1000) * (vrp->rate/1000);
|
||||
/* convert the step time which is in nano seconds to seconds, round up */
|
||||
samples = SPA_MAX(1u, vrp->volume_ramp_step_time/1000) * (vrp->rate/1000);
|
||||
spa_log_debug(this->log, "volume ramp step samples calculated from time is %d", samples);
|
||||
}
|
||||
if (!samples)
|
||||
samples = -1;
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
|
@ -1569,76 +1616,52 @@ static float get_volume_at_scale(struct volume_ramp_params *vrp, float value)
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
static struct spa_pod *generate_ramp_up_seq(struct impl *this, struct volume_ramp_params *vrp,
|
||||
static struct spa_pod *generate_ramp_seq(struct impl *this, struct volume_ramp_params *vrp,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
struct spa_pod_dynamic_builder b;
|
||||
struct spa_pod_frame f[1];
|
||||
float start = vrp->start, end = vrp->end, volume_accum = start;
|
||||
int ramp_samples = get_ramp_samples(this, vrp);
|
||||
int ramp_step_samples = get_ramp_step_samples(this, vrp);
|
||||
float volume_step = ((end - start) / (ramp_samples / ramp_step_samples));
|
||||
uint32_t volume_offs = 0;
|
||||
float start = vrp->start, end = vrp->end;
|
||||
int samples = get_ramp_samples(this, vrp);
|
||||
int step = get_ramp_step_samples(this, vrp);
|
||||
int offs = 0;
|
||||
|
||||
if (samples < 0 || step < 0 || (samples > 0 && step == 0))
|
||||
return NULL;
|
||||
|
||||
spa_pod_dynamic_builder_init(&b, buffer, size, 4096);
|
||||
|
||||
spa_pod_builder_push_sequence(&b.b, &f[0], 0);
|
||||
spa_log_info(this->log, "generating ramp up sequence from %f to %f with a"
|
||||
" step value %f at scale %d", start, end, volume_step, vrp->scale);
|
||||
do {
|
||||
float vas = get_volume_at_scale(vrp, volume_accum);
|
||||
spa_log_trace(this->log, "volume accum %f", vas);
|
||||
spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties);
|
||||
spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_Props, 0,
|
||||
SPA_PROP_volume, SPA_POD_Float(vas));
|
||||
volume_accum += volume_step;
|
||||
volume_offs += ramp_step_samples;
|
||||
} while (volume_accum < end);
|
||||
return spa_pod_builder_pop(&b.b, &f[0]);
|
||||
}
|
||||
spa_log_info(this->log, "generating ramp sequence from %f to %f with "
|
||||
"step %d/%d at scale %d", start, end, step, samples, vrp->scale);
|
||||
|
||||
static struct spa_pod *generate_ramp_down_seq(struct impl *this, struct volume_ramp_params *vrp,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
struct spa_pod_dynamic_builder b;
|
||||
struct spa_pod_frame f[1];
|
||||
int ramp_samples = get_ramp_samples(this, vrp);
|
||||
int ramp_step_samples = get_ramp_step_samples(this, vrp);
|
||||
float start = vrp->start, end = vrp->end, volume_accum = start;
|
||||
float volume_step = ((start - end) / (ramp_samples / ramp_step_samples));
|
||||
uint32_t volume_offs = 0;
|
||||
while (1) {
|
||||
float pos = (samples == 0) ? end :
|
||||
SPA_CLAMP(start + (end - start) * offs / samples,
|
||||
SPA_MIN(start, end), SPA_MAX(start, end));
|
||||
float vas = get_volume_at_scale(vrp, pos);
|
||||
|
||||
spa_pod_dynamic_builder_init(&b, buffer, size, 4096);
|
||||
|
||||
spa_pod_builder_push_sequence(&b.b, &f[0], 0);
|
||||
spa_log_info(this->log, "generating ramp down sequence from %f to %f with a"
|
||||
" step value %f at scale %d", start, end, volume_step, vrp->scale);
|
||||
do {
|
||||
float vas = get_volume_at_scale(vrp, volume_accum);
|
||||
spa_log_trace(this->log, "volume accum %f", vas);
|
||||
spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties);
|
||||
spa_log_trace(this->log, "volume %d accum %f", offs, vas);
|
||||
spa_pod_builder_control(&b.b, offs, SPA_CONTROL_Properties);
|
||||
spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_Props, 0,
|
||||
SPA_PROP_volume, SPA_POD_Float(vas));
|
||||
|
||||
volume_accum -= volume_step;
|
||||
volume_offs += ramp_step_samples;
|
||||
} while (volume_accum > end);
|
||||
if (offs >= samples)
|
||||
break;
|
||||
|
||||
offs = SPA_MIN(samples, offs + step);
|
||||
}
|
||||
|
||||
return spa_pod_builder_pop(&b.b, &f[0]);
|
||||
}
|
||||
|
||||
static void generate_volume_ramp(struct impl *this, struct volume_ramp_params *vrp,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
void *sequence = NULL;
|
||||
if (vrp->start == vrp->end)
|
||||
spa_log_error(this->log, "no change in volume, cannot ramp volume");
|
||||
else if (vrp->end > vrp->start)
|
||||
sequence = generate_ramp_up_seq(this, vrp, buffer, size);
|
||||
else
|
||||
sequence = generate_ramp_down_seq(this, vrp, buffer, size);
|
||||
void *sequence;
|
||||
|
||||
sequence = generate_ramp_seq(this, vrp, buffer, size);
|
||||
if (!sequence)
|
||||
spa_log_error(this->log, "unable to generate sequence");
|
||||
|
||||
|
|
@ -1748,7 +1771,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
|
|||
case SPA_PROP_channelVolumes:
|
||||
if (!p->lock_volumes &&
|
||||
(n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
|
||||
p->channel.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
|
||||
p->channel.volumes, SPA_N_ELEMENTS(p->channel.volumes))) > 0) {
|
||||
have_channel_volume = true;
|
||||
p->channel.n_volumes = n;
|
||||
changed++;
|
||||
|
|
@ -1756,7 +1779,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
|
|||
break;
|
||||
case SPA_PROP_channelMap:
|
||||
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
|
||||
p->channel_map, SPA_AUDIO_MAX_CHANNELS)) > 0) {
|
||||
p->channel_map, SPA_N_ELEMENTS(p->channel_map))) > 0) {
|
||||
p->n_channels = n;
|
||||
changed++;
|
||||
}
|
||||
|
|
@ -1771,7 +1794,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
|
|||
case SPA_PROP_softVolumes:
|
||||
if (!p->lock_volumes &&
|
||||
(n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
|
||||
p->soft.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
|
||||
p->soft.volumes, SPA_N_ELEMENTS(p->soft.volumes))) > 0) {
|
||||
have_soft_volume = true;
|
||||
p->soft.n_volumes = n;
|
||||
changed++;
|
||||
|
|
@ -1783,7 +1806,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
|
|||
break;
|
||||
case SPA_PROP_monitorVolumes:
|
||||
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
|
||||
p->monitor.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
|
||||
p->monitor.volumes, SPA_N_ELEMENTS(p->monitor.volumes))) > 0) {
|
||||
p->monitor.n_volumes = n;
|
||||
changed++;
|
||||
}
|
||||
|
|
@ -1895,10 +1918,11 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m
|
|||
this->dir[SPA_DIRECTION_OUTPUT].n_ports = dir->n_ports + 1;
|
||||
|
||||
for (i = 0; i < dir->n_ports; i++) {
|
||||
init_port(this, direction, i, info->info.raw.position[i], true, false, false);
|
||||
uint32_t pos = info->info.raw.position[i];
|
||||
init_port(this, direction, i, pos, true, false, false);
|
||||
if (this->monitor && direction == SPA_DIRECTION_INPUT)
|
||||
init_port(this, SPA_DIRECTION_OUTPUT, i+1,
|
||||
info->info.raw.position[i], true, true, false);
|
||||
pos, true, true, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1966,7 +1990,7 @@ static int node_set_param_port_config(struct impl *this, uint32_t flags,
|
|||
return -EINVAL;
|
||||
|
||||
if (info.info.raw.channels == 0 ||
|
||||
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
|
||||
info.info.raw.channels > MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
infop = &info;
|
||||
|
|
@ -2056,22 +2080,24 @@ static int setup_in_convert(struct impl *this)
|
|||
dst_info.info.raw.rate);
|
||||
|
||||
qsort(dst_info.info.raw.position, dst_info.info.raw.channels,
|
||||
sizeof(uint32_t), int32_cmp);
|
||||
sizeof(uint32_t), int32_cmp);
|
||||
|
||||
for (i = 0; i < src_info.info.raw.channels; i++) {
|
||||
for (j = 0; j < dst_info.info.raw.channels; j++) {
|
||||
if (src_info.info.raw.position[i] !=
|
||||
dst_info.info.raw.position[j])
|
||||
uint32_t pi, pj;
|
||||
char b1[8], b2[8];
|
||||
|
||||
pi = src_info.info.raw.position[i];
|
||||
pj = dst_info.info.raw.position[j];
|
||||
if (pi != pj)
|
||||
continue;
|
||||
in->remap[i] = j;
|
||||
if (i != j)
|
||||
remap = true;
|
||||
spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this,
|
||||
i, in->remap[i], j,
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
src_info.info.raw.position[i]),
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
dst_info.info.raw.position[j]));
|
||||
spa_type_audio_channel_make_short_name(pi, b1, 8, "UNK"),
|
||||
spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK"));
|
||||
dst_info.info.raw.position[j] = -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -2120,9 +2146,10 @@ static int remap_volumes(struct impl *this, const struct spa_audio_info *info)
|
|||
|
||||
for (i = 0; i < p->n_channels; i++) {
|
||||
for (j = i; j < target; j++) {
|
||||
uint32_t pj = info->info.raw.position[j];
|
||||
spa_log_debug(this->log, "%d %d: %d <-> %d", i, j,
|
||||
p->channel_map[i], info->info.raw.position[j]);
|
||||
if (p->channel_map[i] != info->info.raw.position[j])
|
||||
p->channel_map[i], pj);
|
||||
if (p->channel_map[i] != pj)
|
||||
continue;
|
||||
if (i != j) {
|
||||
SPA_SWAP(p->channel_map[i], p->channel_map[j]);
|
||||
|
|
@ -2153,7 +2180,7 @@ static void set_volume(struct impl *this)
|
|||
{
|
||||
struct volumes *vol;
|
||||
uint32_t i;
|
||||
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
float volumes[MAX_CHANNELS];
|
||||
struct dir *dir = &this->dir[this->direction];
|
||||
|
||||
spa_log_debug(this->log, "%p set volume %f have_format:%d", this, this->props.volume, dir->have_format);
|
||||
|
|
@ -2184,10 +2211,11 @@ static void set_volume(struct impl *this)
|
|||
static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position)
|
||||
{
|
||||
uint32_t i, idx = 0;
|
||||
char buf[8];
|
||||
for (i = 0; i < channels; i++)
|
||||
idx += snprintf(str + idx, len - idx, "%s%s", i == 0 ? "" : " ",
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
position[i]));
|
||||
spa_type_audio_channel_make_short_name(position[i],
|
||||
buf, sizeof(buf), "UNK"));
|
||||
return str;
|
||||
}
|
||||
|
||||
|
|
@ -2281,6 +2309,7 @@ static int setup_resample(struct impl *this)
|
|||
this->resample.o_rate = out->format.info.raw.rate;
|
||||
this->resample.log = this->log;
|
||||
this->resample.quality = this->props.resample_quality;
|
||||
this->resample.config = this->props.resample_config;
|
||||
this->resample.cpu_flags = this->cpu_flags;
|
||||
|
||||
this->rate_adjust = this->props.rate != 1.0;
|
||||
|
|
@ -2344,12 +2373,16 @@ static int setup_out_convert(struct impl *this)
|
|||
dst_info.info.raw.rate);
|
||||
|
||||
qsort(src_info.info.raw.position, src_info.info.raw.channels,
|
||||
sizeof(uint32_t), int32_cmp);
|
||||
sizeof(uint32_t), int32_cmp);
|
||||
|
||||
for (i = 0; i < src_info.info.raw.channels; i++) {
|
||||
for (j = 0; j < dst_info.info.raw.channels; j++) {
|
||||
if (src_info.info.raw.position[i] !=
|
||||
dst_info.info.raw.position[j])
|
||||
uint32_t pi, pj;
|
||||
char b1[8], b2[8];
|
||||
|
||||
pi = src_info.info.raw.position[i];
|
||||
pj = dst_info.info.raw.position[j];
|
||||
if (pi != pj)
|
||||
continue;
|
||||
out->remap[i] = j;
|
||||
if (i != j)
|
||||
|
|
@ -2357,10 +2390,9 @@ static int setup_out_convert(struct impl *this)
|
|||
|
||||
spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this,
|
||||
i, out->remap[i], j,
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
src_info.info.raw.position[i]),
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
dst_info.info.raw.position[j]));
|
||||
spa_type_audio_channel_make_short_name(pi, b1, 8, "UNK"),
|
||||
spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK"));
|
||||
|
||||
dst_info.info.raw.position[j] = -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -2521,6 +2553,8 @@ static int setup_convert(struct impl *this)
|
|||
this->setup = true;
|
||||
this->recalc = true;
|
||||
|
||||
sync_filter_graph(this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2537,6 +2571,7 @@ static void reset_node(struct impl *this)
|
|||
resample_reset(&this->resample);
|
||||
this->in_offset = 0;
|
||||
this->out_offset = 0;
|
||||
this->setup = false;
|
||||
}
|
||||
|
||||
static int impl_node_send_command(void *object, const struct spa_command *command)
|
||||
|
|
@ -2557,7 +2592,6 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
|
|||
break;
|
||||
case SPA_NODE_COMMAND_Suspend:
|
||||
reset_node(this);
|
||||
this->setup = false;
|
||||
SPA_FALLTHROUGH;
|
||||
case SPA_NODE_COMMAND_Pause:
|
||||
this->started = false;
|
||||
|
|
@ -2671,7 +2705,7 @@ static int port_param_enum_formats(struct impl *impl, struct port *port, uint32_
|
|||
}
|
||||
spa_pod_builder_add(b,
|
||||
SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(
|
||||
DEFAULT_CHANNELS, 1, SPA_AUDIO_MAX_CHANNELS),
|
||||
DEFAULT_CHANNELS, 1, MAX_CHANNELS),
|
||||
0);
|
||||
*param = spa_pod_builder_pop(b, &f[0]);
|
||||
}
|
||||
|
|
@ -3064,7 +3098,7 @@ static int port_set_format(void *object,
|
|||
if (info.info.raw.format == 0 ||
|
||||
(!this->props.resample_disabled && info.info.raw.rate == 0) ||
|
||||
info.info.raw.channels == 0 ||
|
||||
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) {
|
||||
info.info.raw.channels > MAX_CHANNELS) {
|
||||
spa_log_error(this->log, "invalid format:%d rate:%d channels:%d",
|
||||
info.info.raw.format, info.info.raw.rate,
|
||||
info.info.raw.channels);
|
||||
|
|
@ -4295,9 +4329,18 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
this->direction = SPA_DIRECTION_INPUT;
|
||||
}
|
||||
else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) {
|
||||
if (s != NULL)
|
||||
spa_audio_parse_position(s, strlen(s), this->props.channel_map,
|
||||
&this->props.n_channels);
|
||||
if (s == NULL)
|
||||
continue;
|
||||
spa_audio_parse_position_n(s, strlen(s),
|
||||
this->props.channel_map, SPA_N_ELEMENTS(this->props.channel_map),
|
||||
&this->props.n_channels);
|
||||
}
|
||||
else if (spa_streq(k, SPA_KEY_AUDIO_LAYOUT)) {
|
||||
if (s == NULL)
|
||||
continue;
|
||||
spa_audio_parse_layout(s,
|
||||
this->props.channel_map, SPA_N_ELEMENTS(this->props.channel_map),
|
||||
&this->props.n_channels);
|
||||
}
|
||||
else if (spa_streq(k, SPA_KEY_PORT_IGNORE_LATENCY))
|
||||
this->port_ignore_latency = spa_atob(s);
|
||||
|
|
|
|||
|
|
@ -142,29 +142,29 @@ static uint32_t mask_to_ch(struct channelmix *mix, uint64_t mask)
|
|||
}
|
||||
|
||||
static void distribute_mix(struct channelmix *mix,
|
||||
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS],
|
||||
float matrix[MAX_CHANNELS][MAX_CHANNELS],
|
||||
uint64_t mask)
|
||||
{
|
||||
uint32_t i, ch = mask_to_ch(mix, mask);
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
|
||||
for (i = 0; i < MAX_CHANNELS; i++)
|
||||
matrix[i][ch]= 1.0f;
|
||||
}
|
||||
static void average_mix(struct channelmix *mix,
|
||||
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS],
|
||||
float matrix[MAX_CHANNELS][MAX_CHANNELS],
|
||||
uint64_t mask)
|
||||
{
|
||||
uint32_t i, ch = mask_to_ch(mix, mask);
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
|
||||
for (i = 0; i < MAX_CHANNELS; i++)
|
||||
matrix[ch][i]= 1.0f;
|
||||
}
|
||||
static void pair_mix(float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS])
|
||||
static void pair_mix(float matrix[MAX_CHANNELS][MAX_CHANNELS])
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
|
||||
for (i = 0; i < MAX_CHANNELS; i++)
|
||||
matrix[i][i]= 1.0f;
|
||||
}
|
||||
static bool match_mix(struct channelmix *mix,
|
||||
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS],
|
||||
float matrix[MAX_CHANNELS][MAX_CHANNELS],
|
||||
uint64_t src_mask, uint64_t dst_mask)
|
||||
{
|
||||
bool matched = false;
|
||||
|
|
@ -181,7 +181,7 @@ static bool match_mix(struct channelmix *mix,
|
|||
|
||||
static int make_matrix(struct channelmix *mix)
|
||||
{
|
||||
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS] = {{ 0.0f }};
|
||||
float matrix[MAX_CHANNELS][MAX_CHANNELS] = {{ 0.0f }};
|
||||
uint64_t src_mask = mix->src_mask, src_paired;
|
||||
uint64_t dst_mask = mix->dst_mask, dst_paired;
|
||||
uint32_t src_chan = mix->src_chan;
|
||||
|
|
@ -293,7 +293,7 @@ static int make_matrix(struct channelmix *mix)
|
|||
keep &= ~STEREO;
|
||||
} else if (dst_mask & _MASK(MONO)){
|
||||
spa_log_info(mix->log, "assign FC to MONO (%f)", 1.0f);
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
|
||||
for (i = 0; i < MAX_CHANNELS; i++)
|
||||
matrix[i][_CH(FC)]= 1.0f;
|
||||
normalize = true;
|
||||
} else {
|
||||
|
|
@ -313,7 +313,7 @@ static int make_matrix(struct channelmix *mix)
|
|||
keep &= ~FRONT;
|
||||
} else if ((dst_mask & _MASK(MONO))){
|
||||
spa_log_info(mix->log, "assign STEREO to MONO (%f)", 1.0f);
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
|
||||
for (i = 0; i < MAX_CHANNELS; i++) {
|
||||
matrix[i][_CH(FL)]= 1.0f;
|
||||
matrix[i][_CH(FR)]= 1.0f;
|
||||
}
|
||||
|
|
@ -352,7 +352,7 @@ static int make_matrix(struct channelmix *mix)
|
|||
_MATRIX(FC,RC) += slev * SQRT1_2;
|
||||
} else if (dst_mask & _MASK(MONO)){
|
||||
spa_log_info(mix->log, "assign RC to MONO (%f)", 1.0f);
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
|
||||
for (i = 0; i < MAX_CHANNELS; i++)
|
||||
matrix[i][_CH(RC)]= 1.0f;
|
||||
normalize = true;
|
||||
} else {
|
||||
|
|
@ -398,7 +398,7 @@ static int make_matrix(struct channelmix *mix)
|
|||
_MATRIX(FC,RR)+= slev * SQRT1_2;
|
||||
} else if (dst_mask & _MASK(MONO)){
|
||||
spa_log_info(mix->log, "assign RL+RR to MONO (%f)", 1.0f);
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
|
||||
for (i = 0; i < MAX_CHANNELS; i++) {
|
||||
matrix[i][_CH(RL)]= 1.0f;
|
||||
matrix[i][_CH(RR)]= 1.0f;
|
||||
}
|
||||
|
|
@ -450,7 +450,7 @@ static int make_matrix(struct channelmix *mix)
|
|||
_MATRIX(FC,SR) += slev * SQRT1_2;
|
||||
} else if (dst_mask & _MASK(MONO)){
|
||||
spa_log_info(mix->log, "assign SL+SR to MONO (%f)", 1.0f);
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
|
||||
for (i = 0; i < MAX_CHANNELS; i++) {
|
||||
matrix[i][_CH(SL)]= 1.0f;
|
||||
matrix[i][_CH(SR)]= 1.0f;
|
||||
}
|
||||
|
|
@ -471,7 +471,7 @@ static int make_matrix(struct channelmix *mix)
|
|||
_MATRIX(FC,FRC)+= SQRT1_2;
|
||||
} else if (dst_mask & _MASK(MONO)){
|
||||
spa_log_info(mix->log, "assign FLC+FRC to MONO (%f)", 1.0f);
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
|
||||
for (i = 0; i < MAX_CHANNELS; i++) {
|
||||
matrix[i][_CH(FLC)]= 1.0f;
|
||||
matrix[i][_CH(FRC)]= 1.0f;
|
||||
}
|
||||
|
|
@ -492,7 +492,7 @@ static int make_matrix(struct channelmix *mix)
|
|||
_MATRIX(FR,LFE) += llev * SQRT1_2;
|
||||
} else if ((dst_mask & _MASK(MONO))){
|
||||
spa_log_info(mix->log, "assign LFE to MONO (%f)", 1.0f);
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
|
||||
for (i = 0; i < MAX_CHANNELS; i++)
|
||||
matrix[i][_CH(LFE)]= 1.0f;
|
||||
normalize = true;
|
||||
} else {
|
||||
|
|
@ -690,7 +690,7 @@ done:
|
|||
static void impl_channelmix_set_volume(struct channelmix *mix, float volume, bool mute,
|
||||
uint32_t n_channel_volumes, float *channel_volumes)
|
||||
{
|
||||
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
float volumes[MAX_CHANNELS];
|
||||
float vol = mute ? 0.0f : volume, t;
|
||||
uint32_t i, j;
|
||||
uint32_t src_chan = mix->src_chan;
|
||||
|
|
@ -760,8 +760,8 @@ int channelmix_init(struct channelmix *mix)
|
|||
{
|
||||
const struct channelmix_info *info;
|
||||
|
||||
if (mix->src_chan > SPA_AUDIO_MAX_CHANNELS ||
|
||||
mix->dst_chan > SPA_AUDIO_MAX_CHANNELS)
|
||||
if (mix->src_chan > MAX_CHANNELS ||
|
||||
mix->dst_chan > MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
info = find_channelmix_info(mix->src_chan, mix->src_mask, mix->dst_chan, mix->dst_mask,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#define BUFFER_SIZE 4096
|
||||
#define MAX_TAPS 255u
|
||||
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
|
||||
|
||||
#define CHANNELMIX_OPS_MAX_ALIGN 16
|
||||
|
||||
|
|
@ -50,8 +51,8 @@ struct channelmix {
|
|||
#define CHANNELMIX_FLAG_EQUAL (1<<2) /**< all values are equal */
|
||||
#define CHANNELMIX_FLAG_COPY (1<<3) /**< 1 on diagonal, can be nxm */
|
||||
uint32_t flags;
|
||||
float matrix_orig[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS];
|
||||
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS];
|
||||
float matrix_orig[MAX_CHANNELS][MAX_CHANNELS];
|
||||
float matrix[MAX_CHANNELS][MAX_CHANNELS];
|
||||
|
||||
float freq; /* sample frequency */
|
||||
float lfe_cutoff; /* in Hz, 0 is disabled */
|
||||
|
|
@ -59,7 +60,7 @@ struct channelmix {
|
|||
float rear_delay; /* in ms, 0 is disabled */
|
||||
float widen; /* stereo widen. 0 is disabled */
|
||||
uint32_t hilbert_taps; /* to phase shift, 0 disabled */
|
||||
struct lr4 lr4[SPA_AUDIO_MAX_CHANNELS];
|
||||
struct lr4 lr4[MAX_CHANNELS];
|
||||
|
||||
float buffer_mem[2 * BUFFER_SIZE*2 + CHANNELMIX_OPS_MAX_ALIGN/4];
|
||||
float *buffer[2];
|
||||
|
|
|
|||
147
spa/plugins/audioconvert/dbesi0.c
Normal file
147
spa/plugins/audioconvert/dbesi0.c
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/* Copyright(C) 1996 Takuya OOURA
|
||||
|
||||
You may use, copy, modify this code for any purpose and
|
||||
without fee.
|
||||
|
||||
Package home: http://www.kurims.kyoto-u.ac.jp/~ooura/bessel.html
|
||||
*/
|
||||
|
||||
/* Bessel I_0(x) function in double precision */
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static double
|
||||
dbesi0 (double x)
|
||||
{
|
||||
int k;
|
||||
double w, t, y;
|
||||
static double a[65] = {
|
||||
8.5246820682016865877e-11, 2.5966600546497407288e-9,
|
||||
7.9689994568640180274e-8, 1.9906710409667748239e-6,
|
||||
4.0312469446528002532e-5, 6.4499871606224265421e-4,
|
||||
0.0079012345761930579108, 0.071111111109207045212,
|
||||
0.444444444444724909, 1.7777777777777532045,
|
||||
4.0000000000000011182, 3.99999999999999998,
|
||||
1.0000000000000000001,
|
||||
1.1520919130377195927e-10, 2.2287613013610985225e-9,
|
||||
8.1903951930694585113e-8, 1.9821560631611544984e-6,
|
||||
4.0335461940910133184e-5, 6.4495330974432203401e-4,
|
||||
0.0079013012611467520626, 0.071111038160875566622,
|
||||
0.44444450319062699316, 1.7777777439146450067,
|
||||
4.0000000132337935071, 3.9999999968569015366,
|
||||
1.0000000003426703174,
|
||||
1.5476870780515238488e-10, 1.2685004214732975355e-9,
|
||||
9.2776861851114223267e-8, 1.9063070109379044378e-6,
|
||||
4.0698004389917945832e-5, 6.4370447244298070713e-4,
|
||||
0.0079044749458444976958, 0.071105052411749363882,
|
||||
0.44445280640924755082, 1.7777694934432109713,
|
||||
4.0000055808824003386, 3.9999977081165740932,
|
||||
1.0000004333949319118,
|
||||
2.0675200625006793075e-10, -6.1689554705125681442e-10,
|
||||
1.2436765915401571654e-7, 1.5830429403520613423e-6,
|
||||
4.2947227560776583326e-5, 6.3249861665073441312e-4,
|
||||
0.0079454472840953930811, 0.070994327785661860575,
|
||||
0.44467219586283000332, 1.7774588182255374745,
|
||||
4.0003038986252717972, 3.9998233869142057195,
|
||||
1.0000472932961288324,
|
||||
2.7475684794982708655e-10, -3.8991472076521332023e-9,
|
||||
1.9730170483976049388e-7, 5.9651531561967674521e-7,
|
||||
5.1992971474748995357e-5, 5.7327338675433770752e-4,
|
||||
0.0082293143836530412024, 0.069990934858728039037,
|
||||
0.44726764292723985087, 1.7726685170014087784,
|
||||
4.0062907863712704432, 3.9952750700487845355,
|
||||
1.0016354346654179322
|
||||
};
|
||||
static double b[70] = {
|
||||
6.7852367144945531383e-8, 4.6266061382821826854e-7,
|
||||
6.9703135812354071774e-6, 7.6637663462953234134e-5,
|
||||
7.9113515222612691636e-4, 0.0073401204731103808981,
|
||||
0.060677114958668837046, 0.43994941411651569622,
|
||||
2.7420017097661750609, 14.289661921740860534,
|
||||
59.820609640320710779, 188.78998681199150629,
|
||||
399.8731367825601118, 427.56411572180478514,
|
||||
1.8042097874891098754e-7, 1.2277164312044637357e-6,
|
||||
1.8484393221474274861e-5, 2.0293995900091309208e-4,
|
||||
0.0020918539850246207459, 0.019375315654033949297,
|
||||
0.15985869016767185908, 1.1565260527420641724,
|
||||
7.1896341224206072113, 37.354773811947484532,
|
||||
155.80993164266268457, 489.5211371158540918,
|
||||
1030.9147225169564806, 1093.5883545113746958,
|
||||
4.8017305613187493564e-7, 3.261317843912380074e-6,
|
||||
4.9073137508166159639e-5, 5.3806506676487583755e-4,
|
||||
0.0055387918291051866561, 0.051223717488786549025,
|
||||
0.42190298621367914765, 3.0463625987357355872,
|
||||
18.895299447327733204, 97.915189029455461554,
|
||||
407.13940115493494659, 1274.3088990480582632,
|
||||
2670.9883037012547506, 2815.7166284662544712,
|
||||
1.2789926338424623394e-6, 8.6718263067604918916e-6,
|
||||
1.3041508821299929489e-4, 0.001428224737372747892,
|
||||
0.014684070635768789378, 0.13561403190404185755,
|
||||
1.1152592585977393953, 8.0387088559465389038,
|
||||
49.761318895895479206, 257.2684232313529138,
|
||||
1066.8543146269566231, 3328.3874581009636362,
|
||||
6948.8586598121634874, 7288.4893398212481055,
|
||||
3.409350368197032893e-6, 2.3079025203103376076e-5,
|
||||
3.4691373283901830239e-4, 0.003794994977222908545,
|
||||
0.038974209677945602145, 0.3594948380414878371,
|
||||
2.9522878893539528226, 21.246564609514287056,
|
||||
131.28727387146173141, 677.38107093296675421,
|
||||
2802.3724744545046518, 8718.5731420798254081,
|
||||
18141.348781638832286, 18948.925349296308859
|
||||
};
|
||||
static double c[45] = {
|
||||
2.5568678676452702768e-15, 3.0393953792305924324e-14,
|
||||
6.3343751991094840009e-13, 1.5041298011833009649e-11,
|
||||
4.4569436918556541414e-10, 1.746393051427167951e-8,
|
||||
1.0059224011079852317e-6, 1.0729838945088577089e-4,
|
||||
0.05150322693642527738,
|
||||
5.2527963991711562216e-15, 7.202118481421005641e-15,
|
||||
7.2561421229904797156e-13, 1.482312146673104251e-11,
|
||||
4.4602670450376245434e-10, 1.7463600061788679671e-8,
|
||||
1.005922609132234756e-6, 1.0729838937545111487e-4,
|
||||
0.051503226936437300716,
|
||||
1.3365917359358069908e-14, -1.2932643065888544835e-13,
|
||||
1.7450199447905602915e-12, 1.0419051209056979788e-11,
|
||||
4.58047881980598326e-10, 1.7442405450073548966e-8,
|
||||
1.0059461453281292278e-6, 1.0729837434500161228e-4,
|
||||
0.051503226940658446941,
|
||||
5.3771611477352308649e-14, -1.1396193006413731702e-12,
|
||||
1.2858641335221653409e-11, -5.9802086004570057703e-11,
|
||||
7.3666894305929510222e-10, 1.6731837150730356448e-8,
|
||||
1.0070831435812128922e-6, 1.0729733111203704813e-4,
|
||||
0.051503227360726294675,
|
||||
3.7819492084858931093e-14, -4.8600496888588034879e-13,
|
||||
1.6898350504817224909e-12, 4.5884624327524255865e-11,
|
||||
1.2521615963377513729e-10, 1.8959658437754727957e-8,
|
||||
1.0020716710561353622e-6, 1.073037119856927559e-4,
|
||||
0.05150322383300230775
|
||||
};
|
||||
|
||||
w = fabs (x);
|
||||
if (w < 8.5) {
|
||||
t = w * w * 0.0625;
|
||||
k = 13 * ((int) t);
|
||||
y = (((((((((((a[k] * t + a[k + 1]) * t +
|
||||
a[k + 2]) * t + a[k + 3]) * t +
|
||||
a[k + 4]) * t + a[k + 5]) * t + a[k +
|
||||
6]) * t + a[k + 7]) * t + a[k + 8]) * t + a[k +
|
||||
9]) * t + a[k + 10]) * t + a[k + 11]) * t + a[k + 12];
|
||||
} else if (w < 12.5) {
|
||||
k = (int) w;
|
||||
t = w - k;
|
||||
k = 14 * (k - 8);
|
||||
y = ((((((((((((b[k] * t + b[k + 1]) * t + b[k + 2]) * t + b[k + 3]) * t +
|
||||
b[k + 4]) * t + b[k + 5]) * t + b[k +
|
||||
6]) * t + b[k + 7]) * t + b[k + 8]) * t +
|
||||
b[k + 9]) * t + b[k + 10]) * t + b[k + 11]) * t + b[k +
|
||||
12]) * t + b[k + 13];
|
||||
} else {
|
||||
t = 60 / w;
|
||||
k = 9 * ((int) t);
|
||||
y = ((((((((c[k] * t + c[k + 1]) * t +
|
||||
c[k + 2]) * t + c[k + 3]) * t + c[k + 4]) * t +
|
||||
c[k + 5]) * t + c[k + 6]) * t + c[k + 7]) * t +
|
||||
c[k + 8]) * sqrt (t) * exp (w);
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
|
@ -284,7 +284,8 @@ conv_s24_to_f32d_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
// Non static, referenced by `fmt-ops-sse41.c`.
|
||||
void
|
||||
conv_s24_to_f32d_2s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
|
||||
uint32_t n_channels, uint32_t n_samples)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
#include <tmmintrin.h>
|
||||
|
||||
static void
|
||||
// Non static, referenced by `fmt-ops-sse41.c`.
|
||||
void
|
||||
conv_s24_to_f32d_4s_ssse3(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
|
||||
uint32_t n_channels, uint32_t n_samples)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -551,7 +551,7 @@ int convert_init(struct convert *conv)
|
|||
const struct dither_info *dinfo;
|
||||
const struct noise_info *ninfo;
|
||||
const struct clear_info *cinfo;
|
||||
uint32_t i, conv_flags, data_size[3];
|
||||
uint32_t i, conv_flags, data_size[4];
|
||||
|
||||
/* we generate int32 bits of random values. With this scale
|
||||
* factor, we bring this in the [-1.0, 1.0] range */
|
||||
|
|
@ -615,15 +615,17 @@ int convert_init(struct convert *conv)
|
|||
data_size[0] = SPA_ROUND_UP(conv->noise_size * sizeof(float), FMT_OPS_MAX_ALIGN);
|
||||
data_size[1] = SPA_ROUND_UP(RANDOM_SIZE * sizeof(uint32_t), FMT_OPS_MAX_ALIGN);
|
||||
data_size[2] = SPA_ROUND_UP(RANDOM_SIZE * sizeof(int32_t), FMT_OPS_MAX_ALIGN);
|
||||
data_size[3] = SPA_ROUND_UP(conv->n_channels * sizeof(struct shaper), FMT_OPS_MAX_ALIGN);
|
||||
|
||||
conv->data = calloc(FMT_OPS_MAX_ALIGN +
|
||||
data_size[0] + data_size[1] + data_size[2], 1);
|
||||
data_size[0] + data_size[1] + data_size[2] + data_size[3], 1);
|
||||
if (conv->data == NULL)
|
||||
return -errno;
|
||||
|
||||
conv->noise = SPA_PTR_ALIGN(conv->data, FMT_OPS_MAX_ALIGN, float);
|
||||
conv->random = SPA_PTROFF(conv->noise, data_size[0], uint32_t);
|
||||
conv->prev = SPA_PTROFF(conv->random, data_size[1], int32_t);
|
||||
conv->shaper = SPA_PTROFF(conv->prev, data_size[2], struct shaper);
|
||||
|
||||
for (i = 0; i < RANDOM_SIZE; i++)
|
||||
conv->random[i] = random();
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ struct convert {
|
|||
uint32_t noise_size;
|
||||
const float *ns;
|
||||
uint32_t n_ns;
|
||||
struct shaper shaper[64];
|
||||
struct shaper *shaper;
|
||||
|
||||
void (*update_noise) (struct convert *conv, float *noise, uint32_t n_samples);
|
||||
void (*process) (struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ sparesampledumpcoeffs_sources = [
|
|||
sparesampledumpcoeffs = executable(
|
||||
'spa-resample-dump-coeffs',
|
||||
sparesampledumpcoeffs_sources,
|
||||
c_args : [ cc_flags_native, '-DRESAMPLE_DISABLE_PRECOMP' ],
|
||||
c_args : [ '-DRESAMPLE_DISABLE_PRECOMP' ],
|
||||
dependencies : [ spa_dep, mathlib_native ],
|
||||
install : false,
|
||||
native : true,
|
||||
|
|
|
|||
|
|
@ -13,72 +13,186 @@
|
|||
|
||||
SPA_LOG_TOPIC_DEFINE(resample_log_topic, "spa.resample");
|
||||
|
||||
#define INHERIT_PARAM(c,q,p) if ((c)->params[p] == 0.0) (c)->params[p] = (q)->params[p];
|
||||
|
||||
struct quality {
|
||||
uint32_t n_taps;
|
||||
double cutoff;
|
||||
double cutoff_up; /* when upsampling */
|
||||
double cutoff_down; /* for downsampling */
|
||||
double params[RESAMPLE_MAX_PARAMS];
|
||||
};
|
||||
|
||||
static const struct quality window_qualities[] = {
|
||||
{ 8, 0.53, },
|
||||
{ 16, 0.67, },
|
||||
{ 24, 0.75, },
|
||||
{ 32, 0.80, },
|
||||
{ 48, 0.85, }, /* default */
|
||||
{ 64, 0.88, },
|
||||
{ 80, 0.895, },
|
||||
{ 96, 0.910, },
|
||||
{ 128, 0.936, },
|
||||
{ 144, 0.945, },
|
||||
{ 160, 0.950, },
|
||||
{ 192, 0.960, },
|
||||
{ 256, 0.970, },
|
||||
{ 896, 0.990, },
|
||||
{ 1024, 0.995, },
|
||||
|
||||
struct window_info {
|
||||
uint32_t window;
|
||||
void (*func) (struct resample *r, double *w, double t, uint32_t n_taps);
|
||||
uint32_t n_qualities;
|
||||
const struct quality *qualities;
|
||||
void (*config) (struct resample *r);
|
||||
};
|
||||
struct window_info window_info[];
|
||||
|
||||
static const struct quality blackman_qualities[] = {
|
||||
{ 8, 0.58, 0.58, { 0.16, }},
|
||||
{ 16, 0.70, 0.70, { 0.20, }},
|
||||
{ 24, 0.77, 0.77, { 0.16, }},
|
||||
{ 32, 0.82, 0.82, { 0.16, }},
|
||||
{ 48, 0.87, 0.87, { 0.16, }}, /* default */
|
||||
{ 64, 0.895, 0.895, { 0.16, }},
|
||||
{ 80, 0.910, 0.910, { 0.16, }},
|
||||
{ 96, 0.925, 0.925, { 0.16, }},
|
||||
{ 128, 0.942, 0.942, { 0.16, }},
|
||||
{ 144, 0.950, 0.950, { 0.16, }},
|
||||
{ 160, 0.958, 0.958, { 0.16, }},
|
||||
{ 192, 0.966, 0.966, { 0.16, }},
|
||||
{ 256, 0.975, 0.975, { 0.16, }},
|
||||
{ 896, 0.988, 0.988, { 0.16, }},
|
||||
{ 1024, 0.990, 0.990, { 0.16, }},
|
||||
};
|
||||
|
||||
static inline double sinc(double x)
|
||||
static inline void blackman_window(struct resample *r, double *w, double t, uint32_t n_taps)
|
||||
{
|
||||
if (x < 1e-6) return 1.0;
|
||||
double x, alpha = r->config.params[RESAMPLE_PARAM_BLACKMAN_ALPHA];
|
||||
uint32_t i, n_taps12 = n_taps/2;
|
||||
for (i = 0; i < n_taps12; i++, t += 1.0) {
|
||||
x = 2.0 * M_PI * t / n_taps;
|
||||
w[i] = (1.0 - alpha) / 2.0 + (1.0 / 2.0) * cos(x) +
|
||||
(alpha / 2.0) * cos(2.0 * x);
|
||||
}
|
||||
}
|
||||
static inline void blackman_config(struct resample *r)
|
||||
{
|
||||
const struct quality *q = &window_info[r->config.window].qualities[r->quality];
|
||||
INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_BLACKMAN_ALPHA);
|
||||
}
|
||||
|
||||
static const struct quality exp_qualities[] = {
|
||||
{ 8, 0.58, 0.58, { 16.97789, }},
|
||||
{ 16, 0.70, 0.70, { 16.97789, }},
|
||||
{ 24, 0.77, 0.77, { 16.97789, }},
|
||||
{ 32, 0.82, 0.82, { 16.97789, }},
|
||||
{ 48, 0.87, 0.87, { 16.97789, }}, /* default */
|
||||
{ 64, 0.895, 0.895, { 16.97789, }},
|
||||
{ 80, 0.910, 0.910, { 16.97789, }},
|
||||
{ 96, 0.925, 0.925, { 16.97789, }},
|
||||
{ 128, 0.942, 0.942, { 16.97789, }},
|
||||
{ 144, 0.950, 0.950, { 16.97789, }},
|
||||
{ 160, 0.958, 0.958, { 16.97789, }},
|
||||
{ 192, 0.966, 0.966, { 16.97789, }},
|
||||
{ 256, 0.975, 0.975, { 16.97789, }},
|
||||
{ 896, 0.988, 0.988, { 16.97789, }},
|
||||
{ 1024, 0.990, 0.990, { 16.97789, }},
|
||||
};
|
||||
|
||||
static inline void exp_window(struct resample *r, double *w, double t, uint32_t n_taps)
|
||||
{
|
||||
double x, A = r->config.params[RESAMPLE_PARAM_EXP_A];
|
||||
uint32_t i, n_taps12 = n_taps/2;
|
||||
|
||||
for (i = 0; i < n_taps12; i++, t += 1.0) {
|
||||
x = 2.0 * t / n_taps;
|
||||
/* doi:10.1109/RME.2008.4595727 with tweak */
|
||||
w[i] = (exp(A * sqrt(fmax(0.0, 1.0 - x*x))) - 1) / (exp(A) - 1);
|
||||
}
|
||||
}
|
||||
static inline void exp_config(struct resample *r)
|
||||
{
|
||||
const struct quality *q = &window_info[r->config.window].qualities[r->quality];
|
||||
INHERIT_PARAM(&r->config, q, RESAMPLE_PARAM_EXP_A);
|
||||
}
|
||||
|
||||
#include "dbesi0.c"
|
||||
|
||||
static const struct quality kaiser_qualities[] = {
|
||||
{ 8, 0.620000, 0.620000, { 3.553376, 110.000000, 0.888064 }},
|
||||
{ 16, 0.780000, 0.780000, { 3.553376, 110.000000, 0.444032 }},
|
||||
{ 24, 0.820000, 0.820000, { 3.904154, 120.000000, 0.325043 }},
|
||||
{ 32, 0.865000, 0.865000, { 4.254931, 130.000000, 0.265548 }},
|
||||
{ 48, 0.895000, 0.895000, { 4.254931, 130.000000, 0.177032 }},
|
||||
{ 64, 0.915000, 0.915000, { 4.254931, 130.000000, 0.132774 }},
|
||||
{ 80, 0.928000, 0.928000, { 4.254931, 130.000000, 0.106219 }},
|
||||
{ 96, 0.942000, 0.942000, { 4.254931, 130.000000, 0.088516 }},
|
||||
{ 128, 0.952000, 0.952000, { 4.254931, 130.000000, 0.066387 }},
|
||||
{ 160, 0.960000, 0.960000, { 4.254931, 130.000000, 0.053110 }},
|
||||
{ 192, 0.968000, 0.968000, { 4.254931, 130.000000, 0.044258 }},
|
||||
{ 256, 0.976000, 0.976000, { 4.605709, 140.000000, 0.035914 }},
|
||||
{ 512, 0.985000, 0.985000, { 4.781097, 145.000000, 0.018637 }},
|
||||
{ 768, 0.990000, 0.990000, { 4.956486, 150.000000, 0.012878 }},
|
||||
{ 1024, 0.993000, 0.993000, { 5.131875, 155.000000, 0.009999 }},
|
||||
};
|
||||
|
||||
static inline void kaiser_window(struct resample *r, double *w, double t, uint32_t n_taps)
|
||||
{
|
||||
double x, beta = r->config.params[RESAMPLE_PARAM_KAISER_ALPHA] * M_PI;
|
||||
double den = dbesi0(beta);
|
||||
uint32_t i, n_taps12 = n_taps/2;
|
||||
for (i = 0; i < n_taps12; i++, t += 1.0) {
|
||||
x = 2.0 * t / n_taps;
|
||||
w[i] = dbesi0(beta * sqrt(fmax(0.0, 1.0 - x*x))) / den;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void kaiser_config(struct resample *r)
|
||||
{
|
||||
double A, B, dw, tr_bw, alpha;
|
||||
uint32_t n;
|
||||
const struct quality *q = &window_info[r->config.window].qualities[r->quality];
|
||||
|
||||
if ((A = r->config.params[RESAMPLE_PARAM_KAISER_SB_ATT]) == 0.0)
|
||||
A = q->params[RESAMPLE_PARAM_KAISER_SB_ATT];
|
||||
if ((tr_bw = r->config.params[RESAMPLE_PARAM_KAISER_TR_BW]) == 0.0)
|
||||
tr_bw = q->params[RESAMPLE_PARAM_KAISER_TR_BW];
|
||||
|
||||
if ((alpha = r->config.params[RESAMPLE_PARAM_KAISER_ALPHA]) == 0.0) {
|
||||
/* calculate Beta and alpha */
|
||||
if (A > 50)
|
||||
B = 0.1102 * (A - 8.7);
|
||||
else if (A >= 21)
|
||||
B = 0.5842 * pow (A - 21, 0.4) + 0.07886 * (A - 21);
|
||||
else
|
||||
B = 0.0;
|
||||
|
||||
r->config.params[RESAMPLE_PARAM_KAISER_ALPHA] = B / M_PI;
|
||||
}
|
||||
if (r->config.n_taps == 0) {
|
||||
/* calculate transition width in radians */
|
||||
dw = 2 * M_PI * (tr_bw);
|
||||
/* order of the filter */
|
||||
n = (uint32_t)((A - 8.0) / (2.285 * dw));
|
||||
r->config.n_taps = n + 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct window_info window_info[] = {
|
||||
[RESAMPLE_WINDOW_EXP] = { RESAMPLE_WINDOW_EXP, exp_window,
|
||||
SPA_N_ELEMENTS(exp_qualities), exp_qualities, exp_config },
|
||||
[RESAMPLE_WINDOW_BLACKMAN] = { RESAMPLE_WINDOW_BLACKMAN, blackman_window,
|
||||
SPA_N_ELEMENTS(blackman_qualities), blackman_qualities, blackman_config },
|
||||
[RESAMPLE_WINDOW_KAISER] = { RESAMPLE_WINDOW_KAISER, kaiser_window,
|
||||
SPA_N_ELEMENTS(kaiser_qualities), kaiser_qualities, kaiser_config },
|
||||
};
|
||||
|
||||
static inline double sinc(double x, double cutoff)
|
||||
{
|
||||
if (x < 1e-6) return cutoff;
|
||||
x *= M_PI;
|
||||
return sin(x) / x;
|
||||
return sin(x * cutoff) / x;
|
||||
}
|
||||
|
||||
static inline double window_blackman(double x, double n_taps)
|
||||
{
|
||||
double alpha = 0.232, r;
|
||||
x = 2.0 * M_PI * x / n_taps;
|
||||
r = (1.0 - alpha) / 2.0 + (1.0 / 2.0) * cos(x) +
|
||||
(alpha / 2.0) * cos(2.0 * x);
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline double window_cosh(double x, double n_taps)
|
||||
{
|
||||
double r;
|
||||
double A = 16.97789;
|
||||
double x2;
|
||||
x = 2.0 * x / n_taps;
|
||||
x2 = x * x;
|
||||
if (x2 >= 1.0)
|
||||
return 0.0;
|
||||
/* doi:10.1109/RME.2008.4595727 with tweak */
|
||||
r = (exp(A * sqrt(1 - x2)) - 1) / (exp(A) - 1);
|
||||
return r;
|
||||
}
|
||||
|
||||
#define window (1 ? window_cosh : window_blackman)
|
||||
|
||||
static int build_filter(float *taps, uint32_t stride, uint32_t n_taps, uint32_t n_phases, double cutoff)
|
||||
static int build_filter(struct resample *r, float *taps, uint32_t stride, uint32_t n_taps,
|
||||
uint32_t n_phases, double cutoff)
|
||||
{
|
||||
uint32_t i, j, n_taps12 = n_taps/2;
|
||||
double window[n_taps12+1];
|
||||
|
||||
for (i = 0; i <= n_phases; i++) {
|
||||
double t = (double) i / (double) n_phases;
|
||||
window_info[r->config.window].func(r, window, t, n_taps);
|
||||
for (j = 0; j < n_taps12; j++, t += 1.0) {
|
||||
/* exploit symmetry in filter taps */
|
||||
taps[(n_phases - i) * stride + n_taps12 + j] =
|
||||
taps[i * stride + (n_taps12 - j - 1)] = (float)
|
||||
(cutoff * sinc(t * cutoff) * window(t, n_taps));
|
||||
(sinc(t, cutoff) * window[j]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -352,11 +466,18 @@ int resample_native_init(struct resample *r)
|
|||
{
|
||||
struct native_data *d;
|
||||
const struct quality *q;
|
||||
double scale;
|
||||
uint32_t c, n_taps, n_phases, filter_size, in_rate, out_rate, gcd, filter_stride;
|
||||
double scale, cutoff;
|
||||
uint32_t i, n_taps, n_phases, filter_size, in_rate, out_rate, gcd, filter_stride;
|
||||
uint32_t history_stride, history_size, oversample;
|
||||
struct resample_config *c = &r->config;
|
||||
#ifndef RESAMPLE_DISABLE_PRECOMP
|
||||
struct resample_config def = { 0 };
|
||||
bool default_config;
|
||||
|
||||
r->quality = SPA_CLAMP(r->quality, 0, (int) SPA_N_ELEMENTS(window_qualities) - 1);
|
||||
default_config = memcmp(c, &def, sizeof(def)) == 0;
|
||||
#endif
|
||||
c->window = SPA_CLAMP(c->window, 0u, SPA_N_ELEMENTS(window_info)-1);
|
||||
r->quality = SPA_CLAMP(r->quality, 0, (int)(window_info[c->window].n_qualities - 1));
|
||||
r->free = impl_native_free;
|
||||
r->update_rate = impl_native_update_rate;
|
||||
r->in_len = impl_native_in_len;
|
||||
|
|
@ -366,17 +487,22 @@ int resample_native_init(struct resample *r)
|
|||
r->delay = impl_native_delay;
|
||||
r->phase = impl_native_phase;
|
||||
|
||||
q = &window_qualities[r->quality];
|
||||
window_info[c->window].config(r);
|
||||
|
||||
q = &window_info[c->window].qualities[r->quality];
|
||||
cutoff = r->o_rate < r->i_rate ? q->cutoff_down : q->cutoff_up;
|
||||
c->cutoff = c->cutoff <= 0.0 ? cutoff: c->cutoff;
|
||||
n_taps = c->n_taps == 0 ? q->n_taps : c->n_taps;
|
||||
|
||||
gcd = calc_gcd(r->i_rate, r->o_rate);
|
||||
|
||||
in_rate = r->i_rate / gcd;
|
||||
out_rate = r->o_rate / gcd;
|
||||
|
||||
scale = SPA_MIN(q->cutoff * out_rate / in_rate, q->cutoff);
|
||||
scale = SPA_MIN(c->cutoff * out_rate / in_rate, c->cutoff);
|
||||
|
||||
/* multiple of 8 taps to ease simd optimizations */
|
||||
n_taps = SPA_ROUND_UP_N((uint32_t)ceil(q->n_taps / scale), 8);
|
||||
n_taps = SPA_ROUND_UP_N((uint32_t)ceil(n_taps / scale), 8);
|
||||
n_taps = SPA_MIN(n_taps, 1u << 18);
|
||||
|
||||
/* try to get at least 256 phases so that interpolation is
|
||||
|
|
@ -400,7 +526,7 @@ int resample_native_init(struct resample *r)
|
|||
return -errno;
|
||||
|
||||
r->data = d;
|
||||
d->n_taps = n_taps;
|
||||
c->n_taps = d->n_taps = n_taps;
|
||||
d->n_phases = n_phases;
|
||||
d->in_rate = UINT32_TO_FIXP(in_rate);
|
||||
d->out_rate = out_rate;
|
||||
|
|
@ -411,25 +537,26 @@ int resample_native_init(struct resample *r)
|
|||
d->history = SPA_PTROFF(d->hist_mem, history_size, float*);
|
||||
d->filter_stride = filter_stride / sizeof(float);
|
||||
d->filter_stride_os = d->filter_stride * oversample;
|
||||
for (c = 0; c < r->channels; c++)
|
||||
d->history[c] = SPA_PTROFF(d->hist_mem, c * history_stride, float);
|
||||
for (i = 0; i < r->channels; i++)
|
||||
d->history[i] = SPA_PTROFF(d->hist_mem, i * history_stride, float);
|
||||
|
||||
#ifndef RESAMPLE_DISABLE_PRECOMP
|
||||
/* See if we have precomputed coefficients */
|
||||
for (c = 0; precomp_coeffs[c].filter; c++) {
|
||||
if (precomp_coeffs[c].in_rate == r->i_rate &&
|
||||
precomp_coeffs[c].out_rate == r->o_rate &&
|
||||
precomp_coeffs[c].quality == r->quality)
|
||||
for (i = 0; precomp_coeffs[i].filter; i++) {
|
||||
if (default_config &&
|
||||
precomp_coeffs[i].in_rate == r->i_rate &&
|
||||
precomp_coeffs[i].out_rate == r->o_rate &&
|
||||
precomp_coeffs[i].quality == r->quality)
|
||||
break;
|
||||
}
|
||||
|
||||
if (precomp_coeffs[c].filter) {
|
||||
spa_log_debug(r->log, "using precomputed filter for %u->%u(%u)",
|
||||
if (precomp_coeffs[i].filter) {
|
||||
spa_log_info(r->log, "using precomputed filter for %u->%u(%u)",
|
||||
r->i_rate, r->o_rate, r->quality);
|
||||
spa_memcpy(d->filter, precomp_coeffs[c].filter, filter_size);
|
||||
spa_memcpy(d->filter, precomp_coeffs[i].filter, filter_size);
|
||||
} else {
|
||||
#endif
|
||||
build_filter(d->filter, d->filter_stride, n_taps, n_phases, scale);
|
||||
build_filter(r, d->filter, d->filter_stride, n_taps, n_phases, scale);
|
||||
#ifndef RESAMPLE_DISABLE_PRECOMP
|
||||
}
|
||||
#endif
|
||||
|
|
@ -440,8 +567,8 @@ int resample_native_init(struct resample *r)
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
spa_log_debug(r->log, "native %p: q:%d in:%d out:%d gcd:%d n_taps:%d n_phases:%d features:%08x:%08x",
|
||||
r, r->quality, r->i_rate, r->o_rate, gcd, n_taps, n_phases,
|
||||
spa_log_info(r->log, "native %p: c:%f q:%d w:%d in:%d out:%d gcd:%d n_taps:%d n_phases:%d features:%08x:%08x",
|
||||
r, c->cutoff, r->quality, c->window, r->i_rate, r->o_rate, gcd, n_taps, n_phases,
|
||||
r->cpu_flags, d->info->cpu_flags);
|
||||
|
||||
r->cpu_flags = d->info->cpu_flags;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,30 @@
|
|||
#include <spa/support/cpu.h>
|
||||
#include <spa/support/log.h>
|
||||
|
||||
#ifndef RESAMPLE_DEFAULT_QUALITY
|
||||
#define RESAMPLE_DEFAULT_QUALITY 4
|
||||
#endif
|
||||
|
||||
#define RESAMPLE_WINDOW_DEFAULT RESAMPLE_WINDOW_EXP
|
||||
#define RESAMPLE_MAX_PARAMS 16
|
||||
|
||||
struct resample_config {
|
||||
#define RESAMPLE_WINDOW_EXP 0
|
||||
#define RESAMPLE_WINDOW_BLACKMAN 1
|
||||
#define RESAMPLE_WINDOW_KAISER 2
|
||||
uint32_t window;
|
||||
|
||||
double cutoff;
|
||||
uint32_t n_taps;
|
||||
|
||||
#define RESAMPLE_PARAM_EXP_A 0
|
||||
#define RESAMPLE_PARAM_BLACKMAN_ALPHA 0
|
||||
#define RESAMPLE_PARAM_KAISER_ALPHA 0
|
||||
#define RESAMPLE_PARAM_KAISER_SB_ATT 1 /* stopband attenuation */
|
||||
#define RESAMPLE_PARAM_KAISER_TR_BW 2 /* transition bandwidth */
|
||||
#define RESAMPLE_PARAM_INVALID (RESAMPLE_MAX_PARAMS-1)
|
||||
double params[RESAMPLE_MAX_PARAMS];
|
||||
};
|
||||
|
||||
struct resample {
|
||||
struct spa_log *log;
|
||||
|
|
@ -23,6 +46,8 @@ struct resample {
|
|||
double rate;
|
||||
int quality;
|
||||
|
||||
struct resample_config config; /* set to all 0 for defaults */
|
||||
|
||||
void (*free) (struct resample *r);
|
||||
void (*update_rate) (struct resample *r, double rate);
|
||||
uint32_t (*in_len) (struct resample *r, uint32_t out_len);
|
||||
|
|
@ -49,6 +74,56 @@ struct resample {
|
|||
#define resample_phase(r) (r)->phase(r)
|
||||
|
||||
int resample_native_init(struct resample *r);
|
||||
int resample_native_init_config(struct resample *r, struct resample_config *conf);
|
||||
int resample_peaks_init(struct resample *r);
|
||||
|
||||
static const struct resample_window_info {
|
||||
uint32_t window;
|
||||
const char *label;
|
||||
const char *description;
|
||||
uint32_t n_params;
|
||||
} resample_window_info[] = {
|
||||
[RESAMPLE_WINDOW_EXP] = { RESAMPLE_WINDOW_EXP,
|
||||
"exp", "Exponential window", 1 },
|
||||
[RESAMPLE_WINDOW_BLACKMAN] = { RESAMPLE_WINDOW_BLACKMAN,
|
||||
"blackman", "Blackman window", 1 },
|
||||
[RESAMPLE_WINDOW_KAISER] = { RESAMPLE_WINDOW_KAISER,
|
||||
"kaiser", "Kaiser window", 3 },
|
||||
};
|
||||
|
||||
static inline uint32_t resample_window_from_label(const char *label)
|
||||
{
|
||||
SPA_FOR_EACH_ELEMENT_VAR(resample_window_info, i) {
|
||||
if (spa_streq(i->label, label))
|
||||
return i->window;
|
||||
}
|
||||
return RESAMPLE_WINDOW_EXP;
|
||||
}
|
||||
|
||||
static inline const char *resample_window_name(uint32_t idx)
|
||||
{
|
||||
return resample_window_info[SPA_CLAMP(idx, 0u, SPA_N_ELEMENTS(resample_window_info)-1)].label;
|
||||
}
|
||||
|
||||
static const struct resample_param_info {
|
||||
uint32_t window;
|
||||
uint32_t idx;
|
||||
const char *label;
|
||||
} resample_param_info[] = {
|
||||
{ RESAMPLE_WINDOW_EXP, RESAMPLE_PARAM_EXP_A, "exp.A" },
|
||||
{ RESAMPLE_WINDOW_BLACKMAN, RESAMPLE_PARAM_BLACKMAN_ALPHA, "blackman.alpha" },
|
||||
{ RESAMPLE_WINDOW_KAISER, RESAMPLE_PARAM_KAISER_ALPHA, "kaiser.alpha" },
|
||||
{ RESAMPLE_WINDOW_KAISER, RESAMPLE_PARAM_KAISER_SB_ATT, "kaiser.stopband-attenuation" },
|
||||
{ RESAMPLE_WINDOW_KAISER, RESAMPLE_PARAM_KAISER_TR_BW, "kaiser.transition-bandwidth" },
|
||||
};
|
||||
|
||||
static inline uint32_t resample_param_from_label(const char *label)
|
||||
{
|
||||
SPA_FOR_EACH_ELEMENT_VAR(resample_param_info, i) {
|
||||
if (spa_streq(i->label, label))
|
||||
return i->idx;
|
||||
}
|
||||
return RESAMPLE_PARAM_INVALID;
|
||||
}
|
||||
|
||||
#endif /* RESAMPLE_H */
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ struct data {
|
|||
bool verbose;
|
||||
int rate;
|
||||
int format;
|
||||
uint32_t window;
|
||||
int quality;
|
||||
struct resample_config config;
|
||||
int cpu_flags;
|
||||
|
||||
const char *iname;
|
||||
|
|
@ -43,15 +45,19 @@ struct data {
|
|||
|
||||
#define STR_FMTS "(s8|s16|s32|f32|f64)"
|
||||
|
||||
#define OPTIONS "hvr:f:q:c:"
|
||||
#define OPTIONS "hvc:r:f:w:q:u:t:p:"
|
||||
static const struct option long_options[] = {
|
||||
{ "help", no_argument, NULL, 'h'},
|
||||
{ "verbose", no_argument, NULL, 'v'},
|
||||
{ "cpuflags", required_argument, NULL, 'c' },
|
||||
|
||||
{ "rate", required_argument, NULL, 'r' },
|
||||
{ "format", required_argument, NULL, 'f' },
|
||||
{ "window", required_argument, NULL, 'w' },
|
||||
{ "quality", required_argument, NULL, 'q' },
|
||||
{ "cpuflags", required_argument, NULL, 'c' },
|
||||
{ "cutoff", required_argument, NULL, 'u' },
|
||||
{ "taps", required_argument, NULL, 't' },
|
||||
{ "param", required_argument, NULL, 'p' },
|
||||
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
|
@ -59,6 +65,7 @@ static const struct option long_options[] = {
|
|||
static void show_usage(const char *name, bool is_error)
|
||||
{
|
||||
FILE *fp;
|
||||
uint32_t i;
|
||||
|
||||
fp = is_error ? stderr : stdout;
|
||||
|
||||
|
|
@ -66,14 +73,31 @@ static void show_usage(const char *name, bool is_error)
|
|||
fprintf(fp,
|
||||
" -h, --help Show this help\n"
|
||||
" -v --verbose Be verbose\n"
|
||||
" -c --cpuflags CPU flags (default 0)\n"
|
||||
"\n");
|
||||
fprintf(fp,
|
||||
" -r --rate Output sample rate (default as input)\n"
|
||||
" -f --format Output sample format %s (default as input)\n"
|
||||
" -f --format Output sample format %s (default as input)\n\n"
|
||||
" -w --window Window function (default %s)\n",
|
||||
STR_FMTS, resample_window_name(RESAMPLE_WINDOW_DEFAULT));
|
||||
for (i = 0; i < SPA_N_ELEMENTS(resample_window_info); i++) {
|
||||
fprintf(fp,
|
||||
" %s: %s\n",
|
||||
resample_window_info[i].label,
|
||||
resample_window_info[i].description);
|
||||
}
|
||||
fprintf(fp,
|
||||
" -q --quality Resampler quality (default %u)\n"
|
||||
" -c --cpuflags CPU flags (default 0)\n"
|
||||
"\n",
|
||||
STR_FMTS, DEFAULT_QUALITY);
|
||||
" -u --cutoff Cutoff frequency [0.0..1.0] (default from quality)\n"
|
||||
" -t --taps Resampler taps (default from quality)\n"
|
||||
" -p --param Resampler param <name>=<value> (default from quality)\n",
|
||||
DEFAULT_QUALITY);
|
||||
for (i = 0; i < SPA_N_ELEMENTS(resample_param_info); i++) {
|
||||
fprintf(fp,
|
||||
" %s\n",
|
||||
resample_param_info[i].label);
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
static inline const char *
|
||||
|
|
@ -163,6 +187,8 @@ static int open_files(struct data *d)
|
|||
d->oname, sf_strerror(NULL));
|
||||
return -EIO;
|
||||
}
|
||||
sf_command(d->ofile, SFC_SET_CLIPPING, NULL, 1);
|
||||
|
||||
if (d->verbose) {
|
||||
fprintf(stdout, "input '%s': channels:%d rate:%d format:%s\n",
|
||||
d->iname, d->iinfo.channels, d->iinfo.samplerate,
|
||||
|
|
@ -207,10 +233,23 @@ static int do_conversion(struct data *d)
|
|||
r.i_rate = d->iinfo.samplerate;
|
||||
r.o_rate = d->oinfo.samplerate;
|
||||
r.quality = d->quality < 0 ? DEFAULT_QUALITY : d->quality;
|
||||
r.config = d->config;
|
||||
if ((res = resample_native_init(&r)) < 0) {
|
||||
fprintf(stderr, "can't init converter: %s\n", spa_strerror(res));
|
||||
return res;
|
||||
}
|
||||
if (d->verbose) {
|
||||
fprintf(stdout, "window:%s cutoff:%f n_taps:%u\n",
|
||||
resample_window_name(r.config.window),
|
||||
r.config.cutoff, r.config.n_taps);
|
||||
for (i = 0; i < SPA_N_ELEMENTS(resample_param_info); i++) {
|
||||
if (resample_param_info[i].window != r.config.window)
|
||||
continue;
|
||||
fprintf(stdout, " param:%s %f\n",
|
||||
resample_param_info[i].label,
|
||||
r.config.params[resample_param_info[i].idx]);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < channels; j++)
|
||||
src[j] = &in[MAX_SAMPLES * j];
|
||||
|
|
@ -259,9 +298,8 @@ static int do_conversion(struct data *d)
|
|||
|
||||
if (pout_len > 0) {
|
||||
for (k = 0, i = 0; i < pout_len; i++) {
|
||||
for (j = 0; j < channels; j++) {
|
||||
for (j = 0; j < channels; j++)
|
||||
obuf[k++] = out[MAX_SAMPLES * j + i];
|
||||
}
|
||||
}
|
||||
pout_len = sf_writef_float(d->ofile, obuf, pout_len);
|
||||
|
||||
|
|
@ -320,6 +358,27 @@ int main(int argc, char *argv[])
|
|||
case 'c':
|
||||
data.cpu_flags = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'u':
|
||||
data.config.cutoff = strtod(optarg, NULL);
|
||||
fprintf(stderr, "%f\n", data.config.cutoff);
|
||||
break;
|
||||
case 'w':
|
||||
data.config.window = resample_window_from_label(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
char *eq;
|
||||
if ((eq = strchr(optarg, '=')) != NULL) {
|
||||
uint32_t idx;
|
||||
*eq = 0;
|
||||
idx = resample_param_from_label(optarg);
|
||||
data.config.params[idx] = atof(eq+1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
data.config.n_taps = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "error: unknown option '%c'\n", c);
|
||||
goto error_usage;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ SPA_LOG_IMPL(logger);
|
|||
|
||||
extern const struct spa_handle_factory test_source_factory;
|
||||
|
||||
#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1)
|
||||
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
|
||||
#define MAX_PORTS (MAX_CHANNELS+1)
|
||||
|
||||
struct context {
|
||||
struct spa_handle *convert_handle;
|
||||
|
|
|
|||
|
|
@ -790,6 +790,7 @@ static int impl_node_process(void *object)
|
|||
uint32_t n_buffers, maxsize;
|
||||
struct buffer **buffers;
|
||||
struct buffer *outb;
|
||||
struct spa_data *d;
|
||||
const void **datas;
|
||||
uint32_t cycle = this->position->clock.cycle & 1;
|
||||
|
||||
|
|
@ -856,12 +857,11 @@ static int impl_node_process(void *object)
|
|||
outport->n_buffers);
|
||||
return -EPIPE;
|
||||
}
|
||||
d = outb->buf.datas;
|
||||
|
||||
if (n_buffers == 1) {
|
||||
if (n_buffers == 1 && SPA_FLAG_IS_SET(d[0].flags, SPA_DATA_FLAG_DYNAMIC)) {
|
||||
*outb->buffer = *buffers[0]->buffer;
|
||||
} else {
|
||||
struct spa_data *d = outb->buf.datas;
|
||||
|
||||
*outb->buffer = outb->buf;
|
||||
|
||||
maxsize = SPA_MIN(maxsize, d[0].maxsize);
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ struct impl {
|
|||
struct props props;
|
||||
struct spa_io_clock *clock;
|
||||
struct spa_io_position *position;
|
||||
bool following;
|
||||
|
||||
struct spa_hook_list hooks;
|
||||
struct spa_callbacks callbacks;
|
||||
|
|
@ -306,6 +307,8 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
|
|||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
this->following = this->position && this->clock && this->position->clock.id != this->clock->id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -422,7 +425,8 @@ static int make_buffer(struct impl *this)
|
|||
|
||||
this->sample_count += n_samples;
|
||||
this->elapsed_time = SAMPLES_TO_TIME(this, this->sample_count);
|
||||
set_timer(this, true);
|
||||
if (!this->following)
|
||||
set_timer(this, true);
|
||||
|
||||
io->buffer_id = b->id;
|
||||
io->status = SPA_STATUS_HAVE_DATA;
|
||||
|
|
@ -475,7 +479,8 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
|
|||
this->elapsed_time = 0;
|
||||
|
||||
this->started = true;
|
||||
set_timer(this, true);
|
||||
if (!this->following)
|
||||
set_timer(this, true);
|
||||
break;
|
||||
}
|
||||
case SPA_NODE_COMMAND_Suspend:
|
||||
|
|
@ -891,7 +896,7 @@ static inline void reuse_buffer(struct impl *this, struct port *port, uint32_t i
|
|||
b->outstanding = false;
|
||||
spa_list_append(&port->empty, &b->link);
|
||||
|
||||
if (!this->props.live)
|
||||
if (!this->props.live && !this->following)
|
||||
set_timer(this, true);
|
||||
}
|
||||
|
||||
|
|
@ -966,7 +971,7 @@ static int impl_node_process(void *object)
|
|||
io->buffer_id = SPA_ID_INVALID;
|
||||
}
|
||||
|
||||
if (!this->props.live)
|
||||
if (!this->props.live || this->following)
|
||||
return make_buffer(this);
|
||||
else
|
||||
return SPA_STATUS_OK;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,13 @@ static int avb_set_param(struct state *state, const char *k, const char *s)
|
|||
state->default_format = spa_type_audio_format_from_short_name(s);
|
||||
fmt_change++;
|
||||
} else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) {
|
||||
spa_audio_parse_position(s, strlen(s), state->default_pos.pos,
|
||||
spa_audio_parse_position_n(s, strlen(s), state->default_pos.pos,
|
||||
SPA_N_ELEMENTS(state->default_pos.pos),
|
||||
&state->default_pos.channels);
|
||||
fmt_change++;
|
||||
} else if (spa_streq(k, SPA_KEY_AUDIO_LAYOUT)) {
|
||||
spa_audio_parse_layout(s, state->default_pos.pos,
|
||||
SPA_N_ELEMENTS(state->default_pos.pos),
|
||||
&state->default_pos.channels);
|
||||
fmt_change++;
|
||||
} else if (spa_streq(k, SPA_KEY_AUDIO_ALLOWED_RATES)) {
|
||||
|
|
@ -85,11 +91,12 @@ static int position_to_string(struct channel_map *map, char *val, size_t len)
|
|||
{
|
||||
uint32_t i, o = 0;
|
||||
int r;
|
||||
char pos[8];
|
||||
o += snprintf(val, len, "[ ");
|
||||
for (i = 0; i < map->channels; i++) {
|
||||
r = snprintf(val+o, len-o, "%s%s", i == 0 ? "" : ", ",
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
map->pos[i]));
|
||||
spa_type_audio_channel_make_short_name(map->pos[i],
|
||||
pos, sizeof(pos), "UNK"));
|
||||
if (r < 0 || o + r >= len)
|
||||
return -ENOSPC;
|
||||
o += r;
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ static inline char *format_streamid(char *str, size_t size, const uint64_t strea
|
|||
return str;
|
||||
}
|
||||
|
||||
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
|
||||
#define MAX_BUFFERS 32
|
||||
|
||||
struct buffer {
|
||||
|
|
@ -127,7 +128,7 @@ struct buffer {
|
|||
|
||||
struct channel_map {
|
||||
uint32_t channels;
|
||||
uint32_t pos[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t pos[MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct port {
|
||||
|
|
|
|||
|
|
@ -139,7 +139,8 @@ static int get_valid_aac_bitrate(a2dp_aac_t *conf)
|
|||
static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
a2dp_aac_t conf;
|
||||
int i;
|
||||
|
|
@ -200,7 +201,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
a2dp_aac_t conf;
|
||||
struct spa_pod_frame f[2];
|
||||
struct spa_pod_choice *choice;
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[2];
|
||||
uint32_t i = 0;
|
||||
|
||||
if (caps_size < sizeof(conf))
|
||||
|
|
|
|||
|
|
@ -110,7 +110,8 @@ aptx_frequencies[] = {
|
|||
static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
a2dp_aptx_t conf;
|
||||
int i;
|
||||
|
|
@ -146,7 +147,8 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
|||
static int codec_select_config_ll(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
a2dp_aptx_ll_ext_t conf = { 0 };
|
||||
size_t actual_conf_size;
|
||||
|
|
@ -166,7 +168,7 @@ static int codec_select_config_ll(const struct media_codec *codec, uint32_t flag
|
|||
if (codec->duplex_codec && !conf.base.bidirect_link)
|
||||
return -ENOTSUP;
|
||||
|
||||
if ((res = codec_select_config(codec, flags, caps, caps_size, info, settings, config)) < 0)
|
||||
if ((res = codec_select_config(codec, flags, caps, caps_size, info, settings, config, NULL)) < 0)
|
||||
return res;
|
||||
|
||||
memcpy(&conf.base.aptx, config, sizeof(conf.base.aptx));
|
||||
|
|
@ -205,7 +207,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
a2dp_aptx_t conf;
|
||||
struct spa_pod_frame f[2];
|
||||
struct spa_pod_choice *choice;
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[2];
|
||||
uint32_t i = 0;
|
||||
|
||||
if (caps_size < sizeof(conf))
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@ duplex_frequencies[] = {
|
|||
static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
a2dp_faststream_t conf;
|
||||
int i;
|
||||
|
|
@ -114,7 +115,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
a2dp_faststream_t conf;
|
||||
struct spa_pod_frame f[2];
|
||||
struct spa_pod_choice *choice;
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[2];
|
||||
uint32_t i = 0;
|
||||
|
||||
if (caps_size < sizeof(conf))
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
|
|||
static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
a2dp_lc3plus_hr_t conf;
|
||||
|
||||
|
|
@ -137,8 +138,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f
|
|||
int a, b;
|
||||
|
||||
/* Order selected configurations by preference */
|
||||
res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1);
|
||||
res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2);
|
||||
res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1, NULL);
|
||||
res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2, NULL);
|
||||
|
||||
#define PREFER_EXPR(expr) \
|
||||
do { \
|
||||
|
|
@ -175,7 +176,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
a2dp_lc3plus_hr_t conf;
|
||||
struct spa_pod_frame f[2];
|
||||
struct spa_pod_choice *choice;
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[2];
|
||||
uint32_t i = 0;
|
||||
|
||||
if (caps_size < sizeof(conf))
|
||||
|
|
|
|||
|
|
@ -115,7 +115,8 @@ ldac_channel_modes[] = {
|
|||
static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
a2dp_ldac_t conf;
|
||||
int i;
|
||||
|
|
@ -158,7 +159,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
struct spa_pod_frame f[2];
|
||||
struct spa_pod_choice *choice;
|
||||
uint32_t i = 0;
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[2];
|
||||
|
||||
if (caps_size < sizeof(conf))
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,8 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
|
|||
static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
a2dp_opus_g_t conf;
|
||||
int frequency, duration, channels;
|
||||
|
|
@ -126,8 +127,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f
|
|||
int a, b;
|
||||
|
||||
/* Order selected configurations by preference */
|
||||
res1 = codec->select_config(codec, flags, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1);
|
||||
res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2);
|
||||
res1 = codec->select_config(codec, flags, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1, NULL);
|
||||
res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2, NULL);
|
||||
|
||||
#define PREFER_EXPR(expr) \
|
||||
do { \
|
||||
|
|
@ -164,7 +165,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
{
|
||||
a2dp_opus_g_t conf;
|
||||
struct spa_pod_frame f[1];
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[2];
|
||||
int channels;
|
||||
|
||||
if (caps_size < sizeof(conf))
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ static struct spa_log *log;
|
|||
|
||||
#define BITRATE_DUPLEX_BIDI 160000
|
||||
|
||||
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
|
||||
|
||||
#define OPUS_05_MAX_BYTES (15 * 1024)
|
||||
|
||||
struct props {
|
||||
|
|
@ -251,14 +253,8 @@ static const struct surround_encoder_mapping surround_encoders[] = {
|
|||
static uint32_t bt_channel_from_name(const char *name)
|
||||
{
|
||||
size_t i;
|
||||
enum spa_audio_channel position = SPA_AUDIO_CHANNEL_UNKNOWN;
|
||||
enum spa_audio_channel position = spa_type_audio_channel_from_short_name(name);
|
||||
|
||||
for (i = 0; spa_type_audio_channel[i].name; i++) {
|
||||
if (spa_streq(name, spa_debug_type_short_name(spa_type_audio_channel[i].name))) {
|
||||
position = spa_type_audio_channel[i].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < SPA_N_ELEMENTS(audio_locations); i++) {
|
||||
if (position == audio_locations[i].position)
|
||||
return audio_locations[i].mask;
|
||||
|
|
@ -313,14 +309,14 @@ static void parse_settings(struct props *props, const struct spa_dict *settings)
|
|||
return;
|
||||
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.channels"), &v, 0))
|
||||
props->channels = SPA_CLAMP(v, 1u, SPA_AUDIO_MAX_CHANNELS);
|
||||
props->channels = SPA_CLAMP(v, 1u, MAX_CHANNELS);
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.max-bitrate"), &v, 0))
|
||||
props->max_bitrate = SPA_MAX(v, (uint32_t)BITRATE_MIN);
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.coupled-streams"), &v, 0))
|
||||
props->coupled_streams = SPA_CLAMP(v, 0u, props->channels / 2);
|
||||
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.channels"), &v, 0))
|
||||
props->bidi_channels = SPA_CLAMP(v, 0u, SPA_AUDIO_MAX_CHANNELS);
|
||||
props->bidi_channels = SPA_CLAMP(v, 0u, MAX_CHANNELS);
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.max-bitrate"), &v, 0))
|
||||
props->bidi_max_bitrate = SPA_MAX(v, (uint32_t)BITRATE_MIN);
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.coupled-streams"), &v, 0))
|
||||
|
|
@ -503,7 +499,7 @@ static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direc
|
|||
const uint8_t *permutation = NULL;
|
||||
size_t i, j;
|
||||
|
||||
if (channels > SPA_AUDIO_MAX_CHANNELS)
|
||||
if (channels > MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
if (2 * coupled_streams > channels)
|
||||
return -EINVAL;
|
||||
|
|
@ -542,10 +538,9 @@ static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direc
|
|||
const struct audio_location loc = audio_locations[i];
|
||||
|
||||
if (location & loc.mask) {
|
||||
if (permutation)
|
||||
positions[permutation[j++]] = loc.position;
|
||||
else
|
||||
positions[j++] = loc.position;
|
||||
uint32_t idx = permutation ? permutation[j] : j;
|
||||
positions[idx] = loc.position;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
for (i = SPA_AUDIO_CHANNEL_START_Aux; j < channels; ++i, ++j)
|
||||
|
|
@ -561,7 +556,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
|
|||
a2dp_opus_05_t a2dp_opus_05 = {
|
||||
.info = codec->vendor,
|
||||
.main = {
|
||||
.channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS),
|
||||
.channels = SPA_MIN(255u, MAX_CHANNELS),
|
||||
.frame_duration = (OPUS_05_FRAME_DURATION_25 |
|
||||
OPUS_05_FRAME_DURATION_50 |
|
||||
OPUS_05_FRAME_DURATION_100 |
|
||||
|
|
@ -571,7 +566,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
|
|||
OPUS_05_INIT_BITRATE(0)
|
||||
},
|
||||
.bidi = {
|
||||
.channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS),
|
||||
.channels = SPA_MIN(255u, MAX_CHANNELS),
|
||||
.frame_duration = (OPUS_05_FRAME_DURATION_25 |
|
||||
OPUS_05_FRAME_DURATION_50 |
|
||||
OPUS_05_FRAME_DURATION_100 |
|
||||
|
|
@ -595,7 +590,8 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
|
|||
static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
struct props props;
|
||||
a2dp_opus_05_t conf;
|
||||
|
|
@ -704,8 +700,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f
|
|||
int a, b;
|
||||
|
||||
/* Order selected configurations by preference */
|
||||
res1 = codec->select_config(codec, flags, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1);
|
||||
res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2);
|
||||
res1 = codec->select_config(codec, flags, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1, NULL);
|
||||
res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2, NULL);
|
||||
|
||||
#define PREFER_EXPR(expr) \
|
||||
do { \
|
||||
|
|
@ -771,7 +767,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
a2dp_opus_05_t conf;
|
||||
a2dp_opus_05_direction_t *dir;
|
||||
struct spa_pod_frame f[1];
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[MAX_CHANNELS];
|
||||
|
||||
if (caps_size < sizeof(conf))
|
||||
return -EINVAL;
|
||||
|
|
@ -835,7 +831,8 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
|
|||
}
|
||||
|
||||
info->info.raw.channels = dir1->channels;
|
||||
if (get_mapping(codec, dir1, surround_encoder, NULL, NULL, NULL, info->info.raw.position) < 0)
|
||||
if (get_mapping(codec, dir1, surround_encoder, NULL, NULL, NULL,
|
||||
info->info.raw.position) < 0)
|
||||
return -EINVAL;
|
||||
if (get_mapping(codec, dir2, surround_encoder, NULL, NULL, NULL, NULL) < 0)
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -135,7 +135,8 @@ sbc_xq_channel_modes[] = {
|
|||
static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
a2dp_sbc_t conf;
|
||||
int bitpool, i;
|
||||
|
|
@ -221,8 +222,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f
|
|||
bool xq = (spa_streq(codec->name, "sbc_xq"));
|
||||
|
||||
/* Order selected configurations by preference */
|
||||
res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1);
|
||||
res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2);
|
||||
res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1, NULL);
|
||||
res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2, NULL);
|
||||
|
||||
#define PREFER_EXPR(expr) \
|
||||
do { \
|
||||
|
|
@ -346,7 +347,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
struct spa_pod_frame f[2];
|
||||
struct spa_pod_choice *choice;
|
||||
uint32_t i = 0;
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[2];
|
||||
|
||||
if (caps_size < sizeof(conf))
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ struct impl {
|
|||
struct spa_source *ring_timer;
|
||||
void *upower;
|
||||
struct spa_bt_telephony *telephony;
|
||||
bool pts;
|
||||
};
|
||||
|
||||
struct transport_data {
|
||||
|
|
@ -852,7 +853,7 @@ done:
|
|||
if (cfg)
|
||||
libusb_free_config_descriptor(cfg);
|
||||
if (devices)
|
||||
libusb_free_device_list(devices, 0);
|
||||
libusb_free_device_list(devices, true);
|
||||
if (ctx)
|
||||
libusb_exit(ctx);
|
||||
return ok;
|
||||
|
|
@ -1010,6 +1011,7 @@ static void process_hfp_hf_indicator(struct rfcomm *rfcomm, unsigned int indicat
|
|||
|
||||
switch (indicator) {
|
||||
case SPA_BT_HFP_HF_INDICATOR_ENHANCED_SAFETY:
|
||||
rfcomm_send_error(rfcomm, CMEE_AG_FAILURE);
|
||||
break;
|
||||
case SPA_BT_HFP_HF_INDICATOR_BATTERY_LEVEL:
|
||||
// Battery level is reported in range 0-100
|
||||
|
|
@ -1018,12 +1020,15 @@ static void process_hfp_hf_indicator(struct rfcomm *rfcomm, unsigned int indicat
|
|||
if (value <= 100) {
|
||||
// TODO: report without Battery Provider (using props)
|
||||
spa_bt_device_report_battery_level(rfcomm->device, value);
|
||||
rfcomm_send_reply(rfcomm, "OK");
|
||||
} else {
|
||||
spa_log_warn(backend->log, "battery HF indicator %u outside of range [0, 100]: %u", indicator, value);
|
||||
rfcomm_send_error(rfcomm, CMEE_AG_FAILURE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
spa_log_warn(backend->log, "unknown HF indicator:%u value:%u", indicator, value);
|
||||
rfcomm_send_error(rfcomm, CMEE_AG_FAILURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1307,7 +1312,7 @@ next_indicator:
|
|||
type = INTERNATIONAL_NUMBER;
|
||||
else
|
||||
type = NATIONAL_NUMBER;
|
||||
rfcomm_send_reply(rfcomm, "+CNUM: ,\"%s\",%u", backend->modem.own_number, type);
|
||||
rfcomm_send_reply(rfcomm, "+CNUM: ,\"%s\",%u,,4", backend->modem.own_number, type);
|
||||
}
|
||||
rfcomm_send_reply(rfcomm, "OK");
|
||||
} else if (spa_strstartswith(buf, "AT+COPS=")) {
|
||||
|
|
@ -1350,6 +1355,7 @@ next_indicator:
|
|||
rfcomm_send_reply(rfcomm, "+BIND: (2)");
|
||||
rfcomm_send_reply(rfcomm, "OK");
|
||||
} else if (spa_strstartswith(buf, "AT+BIND?")) {
|
||||
rfcomm_send_reply(rfcomm, "+BIND: 1,0");
|
||||
rfcomm_send_reply(rfcomm, "+BIND: 2,1");
|
||||
rfcomm_send_reply(rfcomm, "OK");
|
||||
} else if (spa_strstartswith(buf, "AT+BIND=")) {
|
||||
|
|
@ -1359,7 +1365,6 @@ next_indicator:
|
|||
rfcomm_send_reply(rfcomm, "OK");
|
||||
} else if (sscanf(buf, "AT+BIEV=%u,%u", &indicator, &indicator_value) == 2) {
|
||||
process_hfp_hf_indicator(rfcomm, indicator, indicator_value);
|
||||
rfcomm_send_reply(rfcomm, "OK");
|
||||
} else if (sscanf(buf, "AT+XAPL=%04x-%04x-%*[^,],%u", &xapl_vendor, &xapl_product, &xapl_features) == 3) {
|
||||
if (xapl_features & SPA_BT_HFP_HF_XAPL_FEATURE_BATTERY_REPORTING) {
|
||||
/* claim, that we support battery status reports */
|
||||
|
|
@ -1434,6 +1439,15 @@ next_indicator:
|
|||
rfcomm_send_error(rfcomm, error);
|
||||
return true;
|
||||
}
|
||||
} else if (spa_strstartswith(buf, "AT+BLDN") && backend->pts) {
|
||||
enum cmee_error error;
|
||||
|
||||
/* For PTS tests HFP/AG/OCL/BV-01-C and HFP/AG/OCL/BV-02-C, fake last dial
|
||||
* number by calling first memory */
|
||||
if (!mm_do_call(backend->modemmanager, ">1", rfcomm, &error)) {
|
||||
rfcomm_send_error(rfcomm, error);
|
||||
return true;
|
||||
}
|
||||
} else if (spa_strstartswith(buf, "AT+CHUP")) {
|
||||
enum cmee_error error;
|
||||
|
||||
|
|
@ -2759,7 +2773,8 @@ static int sco_acquire_cb(void *data, bool optional)
|
|||
goto fail;
|
||||
|
||||
#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
|
||||
rfcomm_hfp_ag_set_cind(td->rfcomm, true);
|
||||
if (!td->rfcomm->device->disable_dummy_call)
|
||||
rfcomm_hfp_ag_set_cind(td->rfcomm, true);
|
||||
#endif
|
||||
|
||||
t->fd = sock;
|
||||
|
|
@ -2813,7 +2828,8 @@ static int sco_release_cb(void *data)
|
|||
spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_IDLE);
|
||||
|
||||
#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
|
||||
rfcomm_hfp_ag_set_cind(td->rfcomm, false);
|
||||
if (!td->rfcomm->device->disable_dummy_call)
|
||||
rfcomm_hfp_ag_set_cind(td->rfcomm, false);
|
||||
#endif
|
||||
|
||||
sco_destroy_cb(t);
|
||||
|
|
@ -2926,7 +2942,11 @@ static void sco_listen_event(struct spa_source *source)
|
|||
|
||||
/* Find transport for local and remote address */
|
||||
spa_list_for_each(rfcomm, &backend->rfcomm_list, link) {
|
||||
if ((rfcomm->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY) &&
|
||||
/* Audio connection is allowed from both side with legacy peer, i.e. HSP or codec negotion not supported
|
||||
* (except if PTS workaround has been enabled in which case audio coonection is allowed as for HSP),
|
||||
* or only from the HFP Audio Gateway. */
|
||||
if ((((!rfcomm->codec_negotiation_supported || backend->pts) && (rfcomm->profile & SPA_BT_PROFILE_HEADSET_AUDIO)) ||
|
||||
(rfcomm->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY)) &&
|
||||
rfcomm->transport &&
|
||||
spa_streq(rfcomm->device->address, remote_address) &&
|
||||
spa_streq(rfcomm->device->adapter->address, local_address)) {
|
||||
|
|
@ -2940,7 +2960,7 @@ static void sco_listen_event(struct spa_source *source)
|
|||
return;
|
||||
}
|
||||
|
||||
spa_assert(t->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY);
|
||||
spa_assert(t->profile & SPA_BT_PROFILE_HEADSET_AUDIO);
|
||||
|
||||
if (rfcomm->telephony_ag && rfcomm->telephony_ag->transport.rejectSCO) {
|
||||
spa_log_info(backend->log, "rejecting SCO, AudioGatewayTransport1.RejectSCO=true");
|
||||
|
|
@ -3381,6 +3401,43 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
|
|||
}
|
||||
spa_bt_device_add_profile(d, profile);
|
||||
|
||||
/* Prevent to connect HSP/HFP in both directions, i.e. HS->AG and AG->HS.
|
||||
* This may only occur when connecting to a device which provides both
|
||||
* HS and AG which should not be the case with headsets and phones. */
|
||||
spa_list_for_each(rfcomm, &backend->rfcomm_list, link) {
|
||||
if (spa_streq(rfcomm->device->address, d->address) &&
|
||||
spa_streq(rfcomm->device->adapter->address, d->adapter->address)) {
|
||||
bool connected = false;
|
||||
|
||||
switch (profile) {
|
||||
case SPA_BT_PROFILE_HFP_HF:
|
||||
if (rfcomm->profile == SPA_BT_PROFILE_HFP_AG)
|
||||
connected = true;
|
||||
break;
|
||||
case SPA_BT_PROFILE_HFP_AG:
|
||||
if (rfcomm->profile == SPA_BT_PROFILE_HFP_HF)
|
||||
connected = true;
|
||||
break;
|
||||
case SPA_BT_PROFILE_HSP_HS:
|
||||
if (rfcomm->profile == SPA_BT_PROFILE_HSP_AG)
|
||||
connected = true;
|
||||
break;
|
||||
case SPA_BT_PROFILE_HSP_AG:
|
||||
if (rfcomm->profile == SPA_BT_PROFILE_HSP_HS)
|
||||
connected = true;
|
||||
break;
|
||||
default:
|
||||
spa_log_warn(backend->log, "Unsupported profile: %s", handler);
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
spa_log_debug(backend->log, "Already connected in the opposite direction");
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbus_message_iter_next(&it);
|
||||
dbus_message_iter_get_basic(&it, &fd);
|
||||
|
||||
|
|
@ -4011,6 +4068,16 @@ static void parse_hfp_disable_nrec(struct impl *backend, const struct spa_dict *
|
|||
backend->hfp_disable_nrec = false;
|
||||
}
|
||||
|
||||
static void parse_hfp_pts(struct impl *backend, const struct spa_dict *info)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
if ((str = spa_dict_lookup(info, "bluez5.hfphsp-backend-native-pts")) != NULL)
|
||||
backend->pts = spa_atob(str);
|
||||
else
|
||||
backend->pts = false;
|
||||
}
|
||||
|
||||
static void parse_hfp_default_volumes(struct impl *backend, const struct spa_dict *info)
|
||||
{
|
||||
const char *str;
|
||||
|
|
@ -4095,6 +4162,7 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
|
|||
|
||||
parse_hfp_disable_nrec(backend, info);
|
||||
parse_hfp_default_volumes(backend, info);
|
||||
parse_hfp_pts(backend, info);
|
||||
|
||||
#ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE
|
||||
if (!dbus_connection_register_object_path(backend->conn,
|
||||
|
|
|
|||
|
|
@ -82,6 +82,22 @@
|
|||
|
||||
#define LC3_MAX_CHANNELS 28
|
||||
|
||||
/* Metadata types */
|
||||
#define BAP_META_TYPE_PREFERRED_CONTEXT 0x01
|
||||
#define BAP_META_TYPE_STREAMING_CONTEXT 0x02
|
||||
#define BAP_META_TYPE_PROGRAM_INFO 0x03
|
||||
#define BAP_META_TYPE_LANGUAGE 0x04
|
||||
#define BAP_META_TYPE_CCID_LIST 0x05
|
||||
#define BAP_META_TYPE_PARENTAL_RATING 0x06
|
||||
#define BAP_META_TYPE_PROGRAM_INFO_URI 0x07
|
||||
#define BAP_META_TYPE_AUDIO_ACTIVE_STATE 0x08
|
||||
#define BAP_META_TYPE_BCAST_IMMEDIATE 0x09
|
||||
#define BAP_META_TYPE_ASSISTED_LISTENING 0x0a
|
||||
#define BAP_META_TYPE_BCAST_NAME 0x0b
|
||||
#define BAP_META_TYPE_EXTENDED 0xfe
|
||||
#define BAP_META_TYPE_VENDOR 0xff
|
||||
|
||||
|
||||
#define BAP_CHANNEL_MONO 0x00000000 /* mono */
|
||||
#define BAP_CHANNEL_FL 0x00000001 /* front left */
|
||||
#define BAP_CHANNEL_FR 0x00000002 /* front right */
|
||||
|
|
@ -137,11 +153,68 @@
|
|||
#define BT_ISO_QOS_TARGET_LATENCY_BALANCED 0x02
|
||||
#define BT_ISO_QOS_TARGET_LATENCY_RELIABILITY 0x03
|
||||
|
||||
struct __attribute__((packed)) ltv {
|
||||
uint8_t len;
|
||||
uint8_t type;
|
||||
uint8_t value[];
|
||||
};
|
||||
|
||||
#define BT_TMAP_UUID "00001855-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define BT_TMAP_ROLE_CG_STR "cg"
|
||||
#define BT_TMAP_ROLE_CT_STR "ct"
|
||||
#define BT_TMAP_ROLE_UMS_STR "ums"
|
||||
#define BT_TMAP_ROLE_UMR_STR "umr"
|
||||
#define BT_TMAP_ROLE_BMS_STR "bms"
|
||||
#define BT_TMAP_ROLE_BMR_STR "bmr"
|
||||
|
||||
#define BT_GMAP_ROLE_UGG_STR "ugg"
|
||||
#define BT_GMAP_ROLE_UGT_STR "ugt"
|
||||
#define BT_GMAP_ROLE_BGS_STR "bgs"
|
||||
#define BT_GMAP_ROLE_BGR_STR "bgr"
|
||||
|
||||
#define BT_TMAP_ROLE_LIST(role) \
|
||||
role(BT_TMAP_ROLE_CG) \
|
||||
role(BT_TMAP_ROLE_CT) \
|
||||
role(BT_TMAP_ROLE_UMS) \
|
||||
role(BT_TMAP_ROLE_UMR) \
|
||||
role(BT_TMAP_ROLE_BMS) \
|
||||
role(BT_TMAP_ROLE_BMR)
|
||||
|
||||
#define BT_GMAP_UUID "00001858-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define BT_GMAP_UGG_MULTIPLEX_STR "ugg-multiplex"
|
||||
#define BT_GMAP_UGG_96KBPS_SOURCE_STR "ugg-96kbps-source"
|
||||
#define BT_GMAP_UGG_MULTISINK_STR "ugg-multisink"
|
||||
|
||||
#define BT_GMAP_UGT_SOURCE_STR "ugt-source"
|
||||
#define BT_GMAP_UGT_80KBPS_SOURCE_STR "ugt-80kbps-source"
|
||||
#define BT_GMAP_UGT_SINK_STR "ugt-sink"
|
||||
#define BT_GMAP_UGT_64KBPS_SINK_STR "ugt-64kbps-sink"
|
||||
#define BT_GMAP_UGT_MULTIPLEX_STR "ugt-multiplex"
|
||||
#define BT_GMAP_UGT_MULTISINK_STR "ugt-multisink"
|
||||
#define BT_GMAP_UGT_MULTISOURCE_STR "ugt-multisource"
|
||||
|
||||
#define BT_GMAP_BGS_96KBPS_STR "bgs-96kbps"
|
||||
|
||||
#define BT_GMAP_BGR_MULTISINK_STR "bgr-multisink"
|
||||
#define BT_GMAP_BGR_MULTIPLEX_STR "bgr-multiplex"
|
||||
|
||||
#define BT_GMAP_ROLE_LIST(role) \
|
||||
role(BT_GMAP_ROLE_UGG) \
|
||||
role(BT_GMAP_ROLE_UGT) \
|
||||
role(BT_GMAP_ROLE_BGS) \
|
||||
role(BT_GMAP_ROLE_BGR)
|
||||
|
||||
#define BT_GMAP_FEATURE_LIST(feature) \
|
||||
feature(BT_GMAP_UGG_MULTIPLEX) \
|
||||
feature(BT_GMAP_UGG_96KBPS_SOURCE) \
|
||||
feature(BT_GMAP_UGG_MULTISINK) \
|
||||
feature(BT_GMAP_UGT_SOURCE) \
|
||||
feature(BT_GMAP_UGT_80KBPS_SOURCE) \
|
||||
feature(BT_GMAP_UGT_SINK) \
|
||||
feature(BT_GMAP_UGT_64KBPS_SINK) \
|
||||
feature(BT_GMAP_UGT_MULTIPLEX) \
|
||||
feature(BT_GMAP_UGT_MULTISINK) \
|
||||
feature(BT_GMAP_UGT_MULTISOURCE) \
|
||||
feature(BT_GMAP_BGS_96KBPS) \
|
||||
feature(BT_GMAP_BGR_MULTISINK) \
|
||||
feature(BT_GMAP_BGR_MULTIPLEX)
|
||||
|
||||
struct bap_endpoint_qos {
|
||||
uint8_t framing;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <spa/param/audio/format-utils.h>
|
||||
#include <spa/utils/string.h>
|
||||
#include <spa/utils/json.h>
|
||||
#include <spa/utils/cleanup.h>
|
||||
#include <spa/debug/log.h>
|
||||
|
||||
#include <lc3.h>
|
||||
|
|
@ -45,9 +46,11 @@ struct impl {
|
|||
struct settings {
|
||||
uint32_t locations;
|
||||
uint32_t channel_allocation;
|
||||
uint16_t supported_context;
|
||||
uint16_t available_context;
|
||||
bool sink;
|
||||
bool duplex;
|
||||
const char *qos_name;
|
||||
char *qos_name;
|
||||
int retransmission;
|
||||
int latency;
|
||||
int64_t delay;
|
||||
|
|
@ -55,8 +58,10 @@ struct settings {
|
|||
};
|
||||
|
||||
struct pac_data {
|
||||
const uint8_t *data;
|
||||
const void *data;
|
||||
size_t size;
|
||||
const void *metadata;
|
||||
size_t metadata_size;
|
||||
int index;
|
||||
const struct settings *settings;
|
||||
};
|
||||
|
|
@ -81,9 +86,16 @@ typedef struct {
|
|||
uint8_t n_blks;
|
||||
bool sink;
|
||||
bool duplex;
|
||||
uint16_t preferred_context;
|
||||
unsigned int priority;
|
||||
} bap_lc3_t;
|
||||
|
||||
struct config_data {
|
||||
bap_lc3_t conf;
|
||||
int pac_index;
|
||||
struct settings settings;
|
||||
};
|
||||
|
||||
#define BAP_QOS(name_, rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_) \
|
||||
((struct bap_qos){ .name = (name_), .rate = (rate_), .frame_duration = (duration_), .framing = (framing_), \
|
||||
.framelen = (framelen_), .retransmission = (rtn_), .latency = (latency_), \
|
||||
|
|
@ -191,32 +203,6 @@ static unsigned int get_duration_mask(uint8_t rate) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int write_ltv(uint8_t *dest, uint8_t type, void* value, size_t len)
|
||||
{
|
||||
struct ltv *ltv = (struct ltv *)dest;
|
||||
|
||||
ltv->len = len + 1;
|
||||
ltv->type = type;
|
||||
memcpy(ltv->value, value, len);
|
||||
|
||||
return len + 2;
|
||||
}
|
||||
|
||||
static int write_ltv_uint8(uint8_t *dest, uint8_t type, uint8_t value)
|
||||
{
|
||||
return write_ltv(dest, type, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static int write_ltv_uint16(uint8_t *dest, uint8_t type, uint16_t value)
|
||||
{
|
||||
return write_ltv(dest, type, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static int write_ltv_uint32(uint8_t *dest, uint8_t type, uint32_t value)
|
||||
{
|
||||
return write_ltv(dest, type, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static uint16_t parse_rates(const char *str)
|
||||
{
|
||||
struct spa_json it;
|
||||
|
|
@ -319,7 +305,6 @@ static uint8_t parse_channel_counts(const char *str)
|
|||
static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
|
||||
const struct spa_dict *settings, uint8_t caps[A2DP_MAX_CAPS_SIZE])
|
||||
{
|
||||
uint8_t *data = caps;
|
||||
const char *str;
|
||||
uint16_t framelen[2];
|
||||
uint16_t rate_mask = LC3_FREQ_48KHZ | LC3_FREQ_44KHZ | LC3_FREQ_32KHZ | \
|
||||
|
|
@ -330,6 +315,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
|
|||
uint16_t framelen_max = LC3_MAX_FRAME_BYTES;
|
||||
uint8_t max_frames = 2;
|
||||
uint32_t value;
|
||||
struct ltv_writer writer = LTV_WRITER(caps, A2DP_MAX_CAPS_SIZE);
|
||||
|
||||
if (settings && (str = spa_dict_lookup(settings, "bluez5.bap-server-capabilities.rates")))
|
||||
rate_mask = parse_rates(str);
|
||||
|
|
@ -355,17 +341,17 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
|
|||
framelen[0] = htobs(framelen_min);
|
||||
framelen[1] = htobs(framelen_max);
|
||||
|
||||
data += write_ltv_uint16(data, LC3_TYPE_FREQ, htobs(rate_mask));
|
||||
data += write_ltv_uint8(data, LC3_TYPE_DUR, duration_mask);
|
||||
data += write_ltv_uint8(data, LC3_TYPE_CHAN, channel_counts);
|
||||
data += write_ltv(data, LC3_TYPE_FRAMELEN, framelen, sizeof(framelen));
|
||||
ltv_writer_uint16(&writer, LC3_TYPE_FREQ, rate_mask);
|
||||
ltv_writer_uint8(&writer, LC3_TYPE_DUR, duration_mask);
|
||||
ltv_writer_uint8(&writer, LC3_TYPE_CHAN, channel_counts);
|
||||
ltv_writer_data(&writer, LC3_TYPE_FRAMELEN, framelen, sizeof(framelen));
|
||||
/* XXX: we support only one frame block -> max 2 frames per SDU */
|
||||
if (max_frames > 2)
|
||||
max_frames = 2;
|
||||
|
||||
data += write_ltv_uint8(data, LC3_TYPE_BLKS, max_frames);
|
||||
ltv_writer_uint8(&writer, LC3_TYPE_BLKS, max_frames);
|
||||
|
||||
return data - caps;
|
||||
return ltv_writer_end(&writer);
|
||||
}
|
||||
|
||||
static void debugc_ltv(struct spa_debug_context *debug_ctx, int pac, struct ltv *ltv)
|
||||
|
|
@ -391,7 +377,7 @@ static void debugc_ltv(struct spa_debug_context *debug_ctx, int pac, struct ltv
|
|||
}
|
||||
}
|
||||
|
||||
static int parse_bluez_pacs(const uint8_t *data, size_t data_size, struct pac_data pacs[MAX_PACS],
|
||||
static int parse_bluez_pacs_data(const uint8_t *data, size_t data_size, struct pac_data pacs[MAX_PACS],
|
||||
struct spa_debug_context *debug_ctx)
|
||||
{
|
||||
/*
|
||||
|
|
@ -400,7 +386,7 @@ static int parse_bluez_pacs(const uint8_t *data, size_t data_size, struct pac_da
|
|||
*/
|
||||
int pac = 0;
|
||||
|
||||
pacs[pac] = (struct pac_data){ data, 0 };
|
||||
pacs[pac] = (struct pac_data){ .data = data };
|
||||
|
||||
while (data_size > 0) {
|
||||
struct ltv *ltv = (struct ltv *)data;
|
||||
|
|
@ -411,7 +397,7 @@ static int parse_bluez_pacs(const uint8_t *data, size_t data_size, struct pac_da
|
|||
break;
|
||||
|
||||
++pac;
|
||||
pacs[pac] = (struct pac_data){ data + 1, 0, pac };
|
||||
pacs[pac] = (struct pac_data){ .data = data + 1, .index = pac };
|
||||
} else if (ltv->len >= data_size) {
|
||||
return -EINVAL;
|
||||
} else {
|
||||
|
|
@ -425,6 +411,28 @@ static int parse_bluez_pacs(const uint8_t *data, size_t data_size, struct pac_da
|
|||
return pac + 1;
|
||||
}
|
||||
|
||||
static int parse_bluez_pacs(const uint8_t *data, size_t data_size,
|
||||
const uint8_t *metadata, size_t metadata_size, struct pac_data pacs[MAX_PACS],
|
||||
struct spa_debug_context *debug_ctx)
|
||||
{
|
||||
struct pac_data meta[MAX_PACS];
|
||||
int pac_count, meta_count;
|
||||
|
||||
pac_count = parse_bluez_pacs_data(data, data_size, pacs, debug_ctx);
|
||||
if (pac_count < 0)
|
||||
return pac_count;
|
||||
|
||||
meta_count = parse_bluez_pacs_data(metadata, metadata_size, meta, debug_ctx);
|
||||
if (meta_count == pac_count) {
|
||||
for (int i = 0; i < pac_count; ++i) {
|
||||
pacs[i].metadata = meta[i].data;
|
||||
pacs[i].metadata_size = meta[i].size;
|
||||
}
|
||||
}
|
||||
|
||||
return pac_count;
|
||||
}
|
||||
|
||||
static uint8_t get_channel_count(uint32_t channels)
|
||||
{
|
||||
uint8_t num;
|
||||
|
|
@ -549,7 +557,7 @@ static int select_channels(uint8_t channel_counts, uint32_t locations, uint32_t
|
|||
|
||||
static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct spa_debug_context *debug_ctx)
|
||||
{
|
||||
const uint8_t *data = pac->data;
|
||||
const void *data = pac->data;
|
||||
size_t data_size = pac->size;
|
||||
uint16_t framelen_min = 0, framelen_max = 0;
|
||||
int max_frames = -1;
|
||||
|
|
@ -557,9 +565,11 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct sp
|
|||
uint8_t max_channels = 0;
|
||||
uint8_t duration_mask = 0;
|
||||
uint16_t rate_mask = 0;
|
||||
uint16_t preferred_context = 0;
|
||||
struct bap_qos bap_qos;
|
||||
unsigned int i;
|
||||
bool found = false;
|
||||
const struct ltv *ltv;
|
||||
|
||||
if (!data_size)
|
||||
return false;
|
||||
|
|
@ -571,14 +581,7 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct sp
|
|||
/* XXX: we always use one frame block */
|
||||
conf->n_blks = 1;
|
||||
|
||||
while (data_size > 0) {
|
||||
struct ltv *ltv = (struct ltv *)data;
|
||||
|
||||
if (ltv->len < sizeof(struct ltv) || ltv->len >= data_size) {
|
||||
spa_debugc(debug_ctx, "invalid LTV data");
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((ltv = ltv_next(&data, &data_size))) {
|
||||
switch (ltv->type) {
|
||||
case LC3_TYPE_FREQ:
|
||||
spa_return_val_if_fail(ltv->len == 3, false);
|
||||
|
|
@ -605,9 +608,25 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct sp
|
|||
spa_debugc(debug_ctx, "unknown LTV type: 0x%02x", ltv->type);
|
||||
break;
|
||||
}
|
||||
data_size -= ltv->len + 1;
|
||||
data += ltv->len + 1;
|
||||
}
|
||||
if (data) {
|
||||
spa_debugc(debug_ctx, "invalid LTV data");
|
||||
return false;
|
||||
}
|
||||
|
||||
data = pac->metadata;
|
||||
data_size = pac->metadata_size;
|
||||
while ((ltv = ltv_next(&data, &data_size))) {
|
||||
switch (ltv->type) {
|
||||
case BAP_META_TYPE_PREFERRED_CONTEXT:
|
||||
if (ltv->len != 3)
|
||||
break;
|
||||
preferred_context = ltv->value[0] + (ltv->value[1] << 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (data)
|
||||
spa_debugc(debug_ctx, "malformed metadata");
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
if (channel_counts & (1u << i))
|
||||
|
|
@ -646,8 +665,8 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct sp
|
|||
* and reassemble SDUs as needed.
|
||||
*/
|
||||
if (pac->settings->duplex) {
|
||||
/* 16KHz input is mandatory in BAP v1.0.1 Table 3.5, so prefer
|
||||
* it or 32kHz for now for input rate in duplex configuration.
|
||||
/* 16KHz input is mandatory in BAP v1.0.1 Table 3.5, and 32KHz in TMAP,
|
||||
* so prefer those for now for input rate in duplex configuration.
|
||||
*
|
||||
* It appears few devices support 48kHz out + input, so in duplex mode
|
||||
* try 32 kHz or 16 kHz also for output direction.
|
||||
|
|
@ -670,12 +689,15 @@ static bool select_config(bap_lc3_t *conf, const struct pac_data *pac, struct sp
|
|||
conf->frame_duration = bap_qos.frame_duration;
|
||||
conf->framelen = bap_qos.framelen;
|
||||
conf->priority = bap_qos.priority;
|
||||
conf->preferred_context = preferred_context;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_conf(bap_lc3_t *conf, const uint8_t *data, size_t data_size)
|
||||
static bool parse_conf(bap_lc3_t *conf, const void *data, size_t data_size)
|
||||
{
|
||||
const struct ltv *ltv;
|
||||
|
||||
if (!data_size)
|
||||
return false;
|
||||
memset(conf, 0, sizeof(*conf));
|
||||
|
|
@ -685,12 +707,7 @@ static bool parse_conf(bap_lc3_t *conf, const uint8_t *data, size_t data_size)
|
|||
/* Absent Codec_Frame_Blocks_Per_SDU means 0x1 (BAP v1.0.1 Sec 4.3.2) */
|
||||
conf->n_blks = 1;
|
||||
|
||||
while (data_size > 0) {
|
||||
struct ltv *ltv = (struct ltv *)data;
|
||||
|
||||
if (ltv->len < sizeof(struct ltv) || ltv->len >= data_size)
|
||||
return false;
|
||||
|
||||
while ((ltv = ltv_next(&data, &data_size))) {
|
||||
switch (ltv->type) {
|
||||
case LC3_TYPE_FREQ:
|
||||
spa_return_val_if_fail(ltv->len == 2, false);
|
||||
|
|
@ -718,9 +735,9 @@ static bool parse_conf(bap_lc3_t *conf, const uint8_t *data, size_t data_size)
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
data_size -= ltv->len + 1;
|
||||
data += ltv->len + 1;
|
||||
}
|
||||
if (data)
|
||||
return false;
|
||||
|
||||
if (conf->frame_duration == 0xFF || !conf->rate)
|
||||
return false;
|
||||
|
|
@ -728,7 +745,32 @@ static bool parse_conf(bap_lc3_t *conf, const uint8_t *data, size_t data_size)
|
|||
return true;
|
||||
}
|
||||
|
||||
static int conf_cmp(const bap_lc3_t *conf1, int res1, const bap_lc3_t *conf2, int res2)
|
||||
static uint16_t get_wanted_context(const struct settings *settings)
|
||||
{
|
||||
uint16_t context;
|
||||
|
||||
/* Stick with contexts specified in TMAP. Anything else is probably crapshoot due
|
||||
* to interesting device firmware behavior.
|
||||
*
|
||||
* Eg. some Earfun firmwares fail if we set MEDIA | CONVERSATIONAL, other versions
|
||||
* disconnect if LIVE is set, Samsung Galaxy Buds fail on microphone Enable if
|
||||
* CONVERSATIONAL is not set.
|
||||
*/
|
||||
if (settings->sink || settings->duplex)
|
||||
context = BAP_CONTEXT_CONVERSATIONAL;
|
||||
else
|
||||
context = BAP_CONTEXT_MEDIA;
|
||||
|
||||
/* CAP v1.0.1 Sec 7.3.1.2.1: drop contexts if not available, otherwise unspecified */
|
||||
context &= settings->available_context;
|
||||
if (!context)
|
||||
context = BAP_CONTEXT_UNSPECIFIED & settings->available_context;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static int conf_cmp(const bap_lc3_t *conf1, int res1, const bap_lc3_t *conf2, int res2,
|
||||
const struct settings *settings)
|
||||
{
|
||||
const bap_lc3_t *conf;
|
||||
int a, b;
|
||||
|
|
@ -753,6 +795,9 @@ static int conf_cmp(const bap_lc3_t *conf1, int res1, const bap_lc3_t *conf2, in
|
|||
|
||||
PREFER_EXPR(conf->priority == UINT_MAX);
|
||||
|
||||
/* CAP v1.0.1 Sec 7.3.1.2.4: should use PAC preferred context if possible */
|
||||
PREFER_BOOL(conf->preferred_context & get_wanted_context(settings));
|
||||
|
||||
PREFER_BOOL(conf->channels & LC3_CHAN_2);
|
||||
PREFER_BOOL(conf->channels & LC3_CHAN_1);
|
||||
|
||||
|
|
@ -778,7 +823,7 @@ static int pac_cmp(const void *p1, const void *p2)
|
|||
res1 = select_config(&conf1, pac1, &debug_ctx.ctx) ? (int)sizeof(bap_lc3_t) : -EINVAL;
|
||||
res2 = select_config(&conf2, pac2, &debug_ctx.ctx) ? (int)sizeof(bap_lc3_t) : -EINVAL;
|
||||
|
||||
return conf_cmp(&conf1, res1, &conf2, res2);
|
||||
return conf_cmp(&conf1, res1, &conf2, res2, pac1->settings);
|
||||
}
|
||||
|
||||
static void parse_settings(struct settings *s, const struct spa_dict *settings,
|
||||
|
|
@ -797,7 +842,7 @@ static void parse_settings(struct settings *s, const struct spa_dict *settings,
|
|||
return;
|
||||
|
||||
if ((str = spa_dict_lookup(settings, "bluez5.bap.preset")))
|
||||
s->qos_name = str;
|
||||
s->qos_name = strdup(str);
|
||||
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.rtn"), &value, 0))
|
||||
s->retransmission = value;
|
||||
|
|
@ -817,12 +862,18 @@ static void parse_settings(struct settings *s, const struct spa_dict *settings,
|
|||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.channel-allocation"), &value, 0))
|
||||
s->channel_allocation = value;
|
||||
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.supported-context"), &value, 0))
|
||||
s->supported_context = value;
|
||||
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.available-context"), &value, 0))
|
||||
s->available_context = value;
|
||||
|
||||
if (spa_atob(spa_dict_lookup(settings, "bluez5.bap.debug")))
|
||||
*debug_ctx = SPA_LOG_DEBUG_INIT(log_, SPA_LOG_LEVEL_DEBUG);
|
||||
else
|
||||
*debug_ctx = SPA_LOG_DEBUG_INIT(NULL, SPA_LOG_LEVEL_TRACE);
|
||||
|
||||
/* Is remote endpoint sink or source */
|
||||
/* Is local endpoint sink or source */
|
||||
s->sink = spa_atob(spa_dict_lookup(settings, "bluez5.bap.sink"));
|
||||
|
||||
/* Is remote endpoint duplex */
|
||||
|
|
@ -837,26 +888,48 @@ static void parse_settings(struct settings *s, const struct spa_dict *settings,
|
|||
(int)s->sink, (int)s->duplex);
|
||||
}
|
||||
|
||||
static void free_config_data(struct config_data *d)
|
||||
{
|
||||
if (!d)
|
||||
return;
|
||||
free(d->settings.qos_name);
|
||||
free(d);
|
||||
}
|
||||
|
||||
SPA_DEFINE_AUTOPTR_CLEANUP(config_data, struct config_data, { spa_clear_ptr(*thing, free_config_data); });
|
||||
|
||||
static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
|
||||
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data)
|
||||
{
|
||||
struct pac_data pacs[MAX_PACS];
|
||||
int npacs;
|
||||
bap_lc3_t conf;
|
||||
uint8_t *data = config;
|
||||
struct spa_debug_log_ctx debug_ctx;
|
||||
struct settings s;
|
||||
int i;
|
||||
spa_autoptr(config_data) d = NULL;
|
||||
int i, ret;
|
||||
struct ltv_writer writer = LTV_WRITER(config, A2DP_MAX_CAPS_SIZE);
|
||||
const void *metadata = NULL;
|
||||
uint32_t metadata_len = 0;
|
||||
|
||||
if (caps == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
parse_settings(&s, settings, &debug_ctx);
|
||||
d = calloc(1, sizeof(*d));
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
parse_settings(&d->settings, settings, &debug_ctx);
|
||||
|
||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.metadata"), &metadata_len, 0))
|
||||
metadata = spa_dict_lookup(settings, "bluez5.bap.metadata");
|
||||
if (!metadata)
|
||||
metadata_len = 0;
|
||||
|
||||
/* Select best conf from those possible */
|
||||
npacs = parse_bluez_pacs(caps, caps_size, pacs, &debug_ctx.ctx);
|
||||
npacs = parse_bluez_pacs(caps, caps_size, metadata, metadata_len, pacs, &debug_ctx.ctx);
|
||||
if (npacs < 0) {
|
||||
spa_debugc(&debug_ctx.ctx, "malformed PACS");
|
||||
return npacs;
|
||||
|
|
@ -866,7 +939,7 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
|||
}
|
||||
|
||||
for (i = 0; i < npacs; ++i)
|
||||
pacs[i].settings = &s;
|
||||
pacs[i].settings = &d->settings;
|
||||
|
||||
qsort(pacs, npacs, sizeof(struct pac_data), pac_cmp);
|
||||
|
||||
|
|
@ -875,38 +948,55 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
|
|||
if (!select_config(&conf, &pacs[0], &debug_ctx.ctx))
|
||||
return -ENOTSUP;
|
||||
|
||||
data += write_ltv_uint8(data, LC3_TYPE_FREQ, conf.rate);
|
||||
data += write_ltv_uint8(data, LC3_TYPE_DUR, conf.frame_duration);
|
||||
d->conf = conf;
|
||||
d->pac_index = pacs[0].index;
|
||||
|
||||
ltv_writer_uint8(&writer, LC3_TYPE_FREQ, conf.rate);
|
||||
ltv_writer_uint8(&writer, LC3_TYPE_DUR, conf.frame_duration);
|
||||
|
||||
/* Indicate MONO with absent Audio_Channel_Allocation (BAP v1.0.1 Sec. 4.3.2) */
|
||||
if (conf.channels != 0)
|
||||
data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(conf.channels));
|
||||
ltv_writer_uint32(&writer, LC3_TYPE_CHAN, conf.channels);
|
||||
|
||||
data += write_ltv_uint16(data, LC3_TYPE_FRAMELEN, htobs(conf.framelen));
|
||||
data += write_ltv_uint8(data, LC3_TYPE_BLKS, conf.n_blks);
|
||||
ltv_writer_uint16(&writer, LC3_TYPE_FRAMELEN, conf.framelen);
|
||||
ltv_writer_uint8(&writer, LC3_TYPE_BLKS, conf.n_blks);
|
||||
|
||||
return data - config;
|
||||
ret = ltv_writer_end(&writer);
|
||||
|
||||
if (ret >= 0 && config_data)
|
||||
*config_data = spa_steal_ptr(d);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size,
|
||||
const void *caps2, size_t caps2_size, const struct media_codec_audio_info *info, const struct spa_dict *global_settings)
|
||||
{
|
||||
bap_lc3_t conf1, conf2;
|
||||
int res1, res2;
|
||||
int res1, res2, res;
|
||||
void *data1 = NULL;
|
||||
void *data2 = NULL;
|
||||
const struct config_data *d;
|
||||
|
||||
/* Order selected configurations by preference */
|
||||
res1 = codec->select_config(codec, 0, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1);
|
||||
res2 = codec->select_config(codec, 0, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2);
|
||||
res1 = codec->select_config(codec, 0, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1, &data1);
|
||||
res2 = codec->select_config(codec, 0, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2, &data2);
|
||||
|
||||
return conf_cmp(&conf1, res1, &conf2, res2);
|
||||
d = data1 ? data1 : data2;
|
||||
res = conf_cmp(&conf1, res1, &conf2, res2, d ? &d->settings : NULL);
|
||||
|
||||
codec->free_config_data(codec, data1);
|
||||
codec->free_config_data(codec, data2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint8_t channels_to_positions(uint32_t channels, uint32_t *position)
|
||||
static uint8_t channels_to_positions(uint32_t channels, uint32_t *position, uint32_t max_position)
|
||||
{
|
||||
uint32_t n_channels = get_channel_count(channels);
|
||||
uint8_t n_positions = 0;
|
||||
|
||||
spa_assert(n_channels <= SPA_AUDIO_MAX_CHANNELS);
|
||||
spa_assert(n_channels <= max_position);
|
||||
|
||||
if (channels == 0) {
|
||||
position[0] = SPA_AUDIO_CHANNEL_MONO;
|
||||
|
|
@ -932,7 +1022,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
bap_lc3_t conf;
|
||||
struct spa_pod_frame f[2];
|
||||
struct spa_pod_choice *choice;
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[LC3_MAX_CHANNELS];
|
||||
uint32_t i = 0;
|
||||
uint8_t res;
|
||||
|
||||
|
|
@ -990,7 +1080,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
if (i == 0)
|
||||
return -EINVAL;
|
||||
|
||||
res = channels_to_positions(conf.channels, position);
|
||||
res = channels_to_positions(conf.channels, position, SPA_N_ELEMENTS(position));
|
||||
if (res == 0)
|
||||
return -EINVAL;
|
||||
spa_pod_builder_add(b,
|
||||
|
|
@ -1044,7 +1134,8 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = channels_to_positions(conf.channels, info->info.raw.position);
|
||||
res = channels_to_positions(conf.channels, info->info.raw.position,
|
||||
SPA_N_ELEMENTS(info->info.raw.position));
|
||||
if (res == 0)
|
||||
return -EINVAL;
|
||||
info->info.raw.channels = res;
|
||||
|
|
@ -1061,24 +1152,23 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
|
|||
}
|
||||
|
||||
static int codec_get_qos(const struct media_codec *codec,
|
||||
const void *config, size_t config_size,
|
||||
const struct bap_endpoint_qos *endpoint_qos,
|
||||
struct bap_codec_qos *qos, const struct spa_dict *settings)
|
||||
const void *config_data,
|
||||
struct bap_codec_qos *qos)
|
||||
{
|
||||
struct bap_qos bap_qos;
|
||||
bap_lc3_t conf;
|
||||
bool found = false;
|
||||
struct settings s;
|
||||
struct spa_debug_log_ctx debug_ctx;
|
||||
const struct config_data *d = config_data;
|
||||
|
||||
spa_zero(*qos);
|
||||
|
||||
if (!parse_conf(&conf, config, config_size))
|
||||
if (!d)
|
||||
return -EINVAL;
|
||||
|
||||
parse_settings(&s, settings, &debug_ctx);
|
||||
conf = d->conf;
|
||||
|
||||
found = select_bap_qos(&bap_qos, &s, get_rate_mask(conf.rate), get_duration_mask(conf.frame_duration),
|
||||
found = select_bap_qos(&bap_qos, &d->settings, get_rate_mask(conf.rate), get_duration_mask(conf.frame_duration),
|
||||
conf.framelen, conf.framelen);
|
||||
if (!found) {
|
||||
/* shouldn't happen: select_config should pick existing one */
|
||||
|
|
@ -1121,6 +1211,27 @@ static int codec_get_qos(const struct media_codec *codec,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int codec_get_metadata(const struct media_codec *codec, const void *config_data,
|
||||
uint8_t *meta, size_t meta_max_size)
|
||||
{
|
||||
const struct config_data *d = config_data;
|
||||
struct ltv_writer writer = LTV_WRITER(meta, meta_max_size);
|
||||
uint16_t ctx;
|
||||
|
||||
ctx = get_wanted_context(&d->settings);
|
||||
if (!ctx)
|
||||
ctx = BAP_CONTEXT_UNSPECIFIED;
|
||||
|
||||
ltv_writer_uint16(&writer, BAP_META_TYPE_STREAMING_CONTEXT, ctx);
|
||||
|
||||
return ltv_writer_end(&writer);
|
||||
}
|
||||
|
||||
static void codec_free_config_data(const struct media_codec *codec, void *config_data)
|
||||
{
|
||||
free_config_data(config_data);
|
||||
}
|
||||
|
||||
static void *codec_init(const struct media_codec *codec, uint32_t flags,
|
||||
void *config, size_t config_len, const struct spa_audio_info *info,
|
||||
void *props, size_t mtu)
|
||||
|
|
@ -1377,80 +1488,60 @@ static int codec_get_bis_config(const struct media_codec *codec, uint8_t *caps,
|
|||
uint8_t *caps_size, struct spa_dict *settings,
|
||||
struct bap_codec_qos *qos)
|
||||
{
|
||||
int index = 0x0;
|
||||
bool preset_found = false;
|
||||
const char *preset = NULL;
|
||||
const char *preset_name = NULL;
|
||||
int channel_allocation = 0;
|
||||
uint8_t *data = caps;
|
||||
int i, ret;
|
||||
struct ltv_writer writer = LTV_WRITER(caps, *caps_size);
|
||||
const struct bap_qos *preset = NULL;
|
||||
|
||||
*caps_size = 0;
|
||||
int i;
|
||||
|
||||
if (settings) {
|
||||
for (i = 0; i < (int)settings->n_items; ++i) {
|
||||
if (spa_streq(settings->items[i].key, "channel_allocation"))
|
||||
sscanf(settings->items[i].value, "%"PRIu32, &channel_allocation);
|
||||
if (spa_streq(settings->items[i].key, "preset"))
|
||||
preset = spa_dict_lookup(settings, "preset");
|
||||
preset_name = settings->items[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
if (preset == NULL)
|
||||
if (preset_name == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
SPA_FOR_EACH_ELEMENT_VAR(bap_bcast_qos_configs, c) {
|
||||
if (spa_streq(c->name, preset)) {
|
||||
preset_found = true;
|
||||
if (spa_streq(c->name, preset_name)) {
|
||||
preset = c;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if (!preset_found)
|
||||
if (!preset)
|
||||
return -EINVAL;
|
||||
|
||||
switch (bap_bcast_qos_configs[index].rate) {
|
||||
case LC3_CONFIG_FREQ_48KHZ:
|
||||
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_48KHZ);
|
||||
break;
|
||||
case LC3_CONFIG_FREQ_44KHZ:
|
||||
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_44KHZ);
|
||||
break;
|
||||
case LC3_CONFIG_FREQ_32KHZ:
|
||||
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_32KHZ);
|
||||
break;
|
||||
case LC3_CONFIG_FREQ_24KHZ:
|
||||
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_24KHZ);
|
||||
break;
|
||||
case LC3_CONFIG_FREQ_16KHZ:
|
||||
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_16KHZ);
|
||||
break;
|
||||
case LC3_CONFIG_FREQ_8KHZ:
|
||||
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_8KHZ);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
*caps_size += 3;
|
||||
ltv_writer_uint8(&writer, LC3_TYPE_FREQ, preset->rate);
|
||||
ltv_writer_uint16(&writer, LC3_TYPE_FRAMELEN, preset->framelen);
|
||||
ltv_writer_uint8(&writer, LC3_TYPE_DUR, preset->frame_duration);
|
||||
ltv_writer_uint32(&writer, LC3_TYPE_CHAN, channel_allocation);
|
||||
|
||||
data += write_ltv_uint16(data, LC3_TYPE_FRAMELEN, htobs(bap_bcast_qos_configs[index].framelen));
|
||||
*caps_size += 4;
|
||||
data += write_ltv_uint8(data, LC3_TYPE_DUR, bap_bcast_qos_configs[index].frame_duration);
|
||||
*caps_size += 3;
|
||||
data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(channel_allocation));
|
||||
*caps_size += 6;
|
||||
|
||||
if(bap_bcast_qos_configs[index].framing)
|
||||
if (preset->framing)
|
||||
qos->framing = 1;
|
||||
else
|
||||
qos->framing = 0;
|
||||
qos->sdu = bap_bcast_qos_configs[index].framelen * get_channel_count(channel_allocation);
|
||||
qos->retransmission = bap_bcast_qos_configs[index].retransmission;
|
||||
qos->latency = bap_bcast_qos_configs[index].latency;
|
||||
qos->delay = bap_bcast_qos_configs[index].delay;
|
||||
qos->sdu = preset->framelen * get_channel_count(channel_allocation);
|
||||
qos->retransmission = preset->retransmission;
|
||||
qos->latency = preset->latency;
|
||||
qos->delay = preset->delay;
|
||||
qos->phy = 2;
|
||||
qos->interval = (bap_bcast_qos_configs[index].frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);
|
||||
qos->interval = (preset->frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);
|
||||
|
||||
return true;
|
||||
ret = ltv_writer_end(&writer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret > UINT8_MAX)
|
||||
return -ENOSPC;
|
||||
|
||||
*caps_size = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct media_codec bap_codec_lc3 = {
|
||||
|
|
@ -1464,6 +1555,8 @@ const struct media_codec bap_codec_lc3 = {
|
|||
.enum_config = codec_enum_config,
|
||||
.validate_config = codec_validate_config,
|
||||
.get_qos = codec_get_qos,
|
||||
.get_metadata = codec_get_metadata,
|
||||
.free_config_data = codec_free_config_data,
|
||||
.caps_preference_cmp = codec_caps_preference_cmp,
|
||||
.init = codec_init,
|
||||
.deinit = codec_deinit,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <spa/utils/string.h>
|
||||
#include <spa/utils/json.h>
|
||||
#include <spa-private/dbus-helpers.h>
|
||||
#include <spa/param/audio/raw-utils.h>
|
||||
#include <spa/param/audio/raw-json.h>
|
||||
|
||||
#include "codec-loader.h"
|
||||
|
|
@ -76,6 +77,11 @@ enum backend_selection {
|
|||
#define TRANSPORT_ERROR_TIMEOUT (2*BLUEZ_ACTION_RATE_MSEC*SPA_NSEC_PER_MSEC)
|
||||
|
||||
|
||||
struct bap_features {
|
||||
struct spa_dict dict;
|
||||
struct spa_dict_item items[32];
|
||||
};
|
||||
|
||||
struct spa_bt_monitor {
|
||||
struct spa_handle handle;
|
||||
struct spa_device device;
|
||||
|
|
@ -130,6 +136,8 @@ struct spa_bt_monitor {
|
|||
uint32_t bap_source_contexts;
|
||||
uint32_t bap_source_supported_contexts;
|
||||
|
||||
struct bap_features bap_features;
|
||||
|
||||
struct spa_bt_quirks *quirks;
|
||||
|
||||
#define MAX_SETTINGS 128
|
||||
|
|
@ -151,13 +159,17 @@ struct spa_bt_remote_endpoint {
|
|||
char *uuid;
|
||||
unsigned int codec;
|
||||
struct spa_bt_device *device;
|
||||
uint8_t capabilities[A2DP_MAX_CAPS_SIZE];
|
||||
int capabilities_len;
|
||||
uint8_t *capabilities;
|
||||
size_t capabilities_len;
|
||||
uint8_t *metadata;
|
||||
size_t metadata_len;
|
||||
bool delay_reporting;
|
||||
bool acceptor;
|
||||
|
||||
struct bap_endpoint_qos qos;
|
||||
|
||||
struct bap_features bap_features;
|
||||
|
||||
bool asha_right_side;
|
||||
uint64_t hisyncid;
|
||||
};
|
||||
|
|
@ -655,6 +667,77 @@ static bool endpoint_should_be_registered(struct spa_bt_monitor *monitor,
|
|||
codec->fill_caps;
|
||||
}
|
||||
|
||||
static bool bap_features_add(struct bap_features *feat, const char *uuid, const char *name)
|
||||
{
|
||||
#define TMAP_ITEM(item) { BT_TMAP_UUID, item ##_STR, BT_TMAP_UUID ":" item ##_STR },
|
||||
#define GMAP_ITEM(item) { BT_GMAP_UUID, item ##_STR, BT_GMAP_UUID ":" item ##_STR },
|
||||
static const struct {
|
||||
const char *const uuid;
|
||||
const char *const name;
|
||||
const char *const key;
|
||||
} values[] = {
|
||||
BT_TMAP_ROLE_LIST(TMAP_ITEM)
|
||||
BT_GMAP_ROLE_LIST(GMAP_ITEM)
|
||||
BT_GMAP_FEATURE_LIST(GMAP_ITEM)
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
SPA_STATIC_ASSERT(SPA_N_ELEMENTS(feat->items) >= SPA_N_ELEMENTS(values));
|
||||
size_t n_items = feat->dict.n_items;
|
||||
size_t i;
|
||||
|
||||
/* Accept only listed features */
|
||||
for (i = 0; values[i].uuid; ++i)
|
||||
if (spa_streq(values[i].uuid, uuid) && spa_streq(values[i].name, name))
|
||||
break;
|
||||
if (!values[i].uuid)
|
||||
return false;
|
||||
|
||||
if (spa_dict_lookup(&feat->dict, values[i].key))
|
||||
return false;
|
||||
|
||||
spa_assert(n_items < SPA_N_ELEMENTS(feat->items));
|
||||
|
||||
/* Add */
|
||||
feat->items[n_items].key = values[i].key;
|
||||
feat->items[n_items].value = values[i].uuid;
|
||||
n_items++;
|
||||
|
||||
feat->dict = SPA_DICT(feat->items, n_items);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Get feature uuid at \a i */
|
||||
static const char *bap_features_get_uuid(struct bap_features *feat, size_t i)
|
||||
{
|
||||
if (!SPA_FLAG_IS_SET(feat->dict.flags, SPA_DICT_FLAG_SORTED))
|
||||
spa_dict_qsort(&feat->dict);
|
||||
|
||||
if (i >= feat->dict.n_items)
|
||||
return NULL;
|
||||
return feat->dict.items[i].value;
|
||||
}
|
||||
|
||||
/** Get feature name at \a i, or NULL if uuid doesn't match */
|
||||
static const char *bap_features_get_name(struct bap_features *feat, size_t i, const char *uuid)
|
||||
{
|
||||
char *pos;
|
||||
|
||||
if (i >= feat->dict.n_items)
|
||||
return NULL;
|
||||
if (!spa_streq(feat->dict.items[i].value, uuid))
|
||||
return NULL;
|
||||
|
||||
pos = strchr(feat->dict.items[i].key, ':');
|
||||
if (!pos)
|
||||
return NULL;
|
||||
return pos + 1;
|
||||
}
|
||||
|
||||
static void bap_features_clear(struct bap_features *feat)
|
||||
{
|
||||
spa_zero(*feat);
|
||||
}
|
||||
|
||||
static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = userdata;
|
||||
|
|
@ -692,7 +775,7 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu
|
|||
* by codec switching.
|
||||
*/
|
||||
res = codec->select_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0, cap, size, &monitor->default_audio_info,
|
||||
&monitor->global_settings, config);
|
||||
&monitor->global_settings, config, NULL);
|
||||
else
|
||||
res = -ENOTSUP;
|
||||
|
||||
|
|
@ -877,8 +960,9 @@ static void parse_endpoint_qos(struct spa_bt_monitor *monitor, DBusMessageIter *
|
|||
}
|
||||
|
||||
static int parse_endpoint_props(struct spa_bt_monitor *monitor, DBusMessageIter *iter,
|
||||
uint8_t caps[A2DP_MAX_CAPS_SIZE], int *caps_size, const char **endpoint_path,
|
||||
struct bap_endpoint_qos *qos)
|
||||
uint8_t **caps, size_t *caps_size,
|
||||
uint8_t **meta, size_t *meta_size,
|
||||
const char **endpoint_path, struct bap_endpoint_qos *qos)
|
||||
{
|
||||
DBusMessageIter dict_iter = *iter;
|
||||
const char *key = NULL;
|
||||
|
|
@ -899,29 +983,46 @@ static int parse_endpoint_props(struct spa_bt_monitor *monitor, DBusMessageIter
|
|||
|
||||
type = dbus_message_iter_get_arg_type(&it[1]);
|
||||
|
||||
if (spa_streq(key, "Capabilities")) {
|
||||
uint8_t *buf;
|
||||
if (spa_streq(key, "Capabilities") || spa_streq(key, "Metadata")) {
|
||||
uint8_t **dest;
|
||||
size_t *size;
|
||||
uint8_t *data, *buf;
|
||||
int n;
|
||||
|
||||
if (!caps)
|
||||
if (spa_streq(key, "Capabilities")) {
|
||||
dest = caps;
|
||||
size = caps_size;
|
||||
} else {
|
||||
dest = meta;
|
||||
size = meta_size;
|
||||
}
|
||||
|
||||
if (!dest)
|
||||
goto next;
|
||||
|
||||
if (type != DBUS_TYPE_ARRAY)
|
||||
spa_assert(dest && size);
|
||||
|
||||
if (!check_iter_signature(&it[1], "ay"))
|
||||
goto bad_property;
|
||||
|
||||
dbus_message_iter_recurse(&it[1], &it[2]);
|
||||
type = dbus_message_iter_get_arg_type(&it[2]);
|
||||
if (type != DBUS_TYPE_BYTE)
|
||||
goto bad_property;
|
||||
dbus_message_iter_get_fixed_array(&it[2], &data, &n);
|
||||
|
||||
dbus_message_iter_get_fixed_array(&it[2], &buf, caps_size);
|
||||
if (*caps_size > A2DP_MAX_CAPS_SIZE) {
|
||||
spa_log_error(monitor->log, "%s size:%d too large", key, (int)*caps_size);
|
||||
return -EINVAL;
|
||||
if (n) {
|
||||
buf = malloc(n);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
memcpy(buf, data, n);
|
||||
} else {
|
||||
buf = NULL;
|
||||
}
|
||||
memcpy(caps, buf, *caps_size);
|
||||
|
||||
spa_log_info(monitor->log, "%p: %s size:%d", monitor, key, *caps_size);
|
||||
spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', caps, (size_t)*caps_size);
|
||||
free(*dest);
|
||||
*dest = buf;
|
||||
*size = n;
|
||||
|
||||
spa_log_info(monitor->log, "%p: %s size:%zu", monitor, key, *size);
|
||||
spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', *dest, *size);
|
||||
} else if (spa_streq(key, "Endpoint")) {
|
||||
if (!endpoint_path)
|
||||
goto next;
|
||||
|
|
@ -1011,8 +1112,12 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
|
||||
const char *endpoint_path = NULL;
|
||||
uint8_t config[A2DP_MAX_CAPS_SIZE];
|
||||
void *config_data = NULL;
|
||||
char locations[64] = {0};
|
||||
char channel_allocation[64] = {0};
|
||||
char supported_context[64] = {0};
|
||||
char available_context[64] = {0};
|
||||
char metadata_len[64] = {0};
|
||||
int conf_size;
|
||||
DBusMessageIter dict;
|
||||
|
||||
|
|
@ -1027,6 +1132,9 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
|
||||
path = dbus_message_get_path(m);
|
||||
|
||||
if ((r = dbus_message_new_method_return(m)) == NULL)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
/* TODO: for codecs with shared endpoint, this currently always picks the default
|
||||
* one. However, currently we don't have BAP codecs with shared endpoint, so
|
||||
* this does not matter, but in case they are needed later we should pick the
|
||||
|
|
@ -1042,7 +1150,7 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
|
||||
/* Find endpoint */
|
||||
iter = props;
|
||||
if (parse_endpoint_props(monitor, &iter, NULL, NULL, &endpoint_path, NULL) < 0)
|
||||
if (parse_endpoint_props(monitor, &iter, NULL, NULL, NULL, NULL, &endpoint_path, NULL) < 0)
|
||||
goto error_invalid;
|
||||
|
||||
ep = remote_endpoint_find(monitor, endpoint_path);
|
||||
|
|
@ -1058,7 +1166,8 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
|
||||
/* Parse endpoint properties */
|
||||
iter = props;
|
||||
if (parse_endpoint_props(monitor, &iter, ep->capabilities, &ep->capabilities_len, NULL, &ep->qos) < 0)
|
||||
if (parse_endpoint_props(monitor, &iter, &ep->capabilities, &ep->capabilities_len,
|
||||
&ep->metadata, &ep->metadata_len, NULL, &ep->qos) < 0)
|
||||
goto error_invalid;
|
||||
|
||||
if (ep->qos.locations)
|
||||
|
|
@ -1066,6 +1175,10 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
if (ep->qos.channel_allocation)
|
||||
spa_scnprintf(channel_allocation, sizeof(channel_allocation), "%"PRIu32, ep->qos.channel_allocation);
|
||||
|
||||
spa_scnprintf(supported_context, sizeof(supported_context), "%"PRIu16, ep->qos.supported_context);
|
||||
spa_scnprintf(available_context, sizeof(available_context), "%"PRIu16, ep->qos.context);
|
||||
spa_scnprintf(metadata_len, sizeof(metadata_len), "%zu", ep->metadata_len);
|
||||
|
||||
if (!ep->device->preferred_profiles)
|
||||
ep->device->preferred_profiles = ep->device->profiles;
|
||||
|
||||
|
|
@ -1074,16 +1187,22 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
i = 0;
|
||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.locations", locations);
|
||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.channel-allocation", channel_allocation);
|
||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.supported-context", supported_context);
|
||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.available-context", available_context);
|
||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.sink", sink ? "true" : "false");
|
||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.duplex", duplex ? "true" : "false");
|
||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.debug", "true");
|
||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.metadata", (void *)ep->metadata);
|
||||
setting_items[i++] = SPA_DICT_ITEM_INIT("bluez5.bap.metadata-len", metadata_len);
|
||||
for (j = 0; j < ep->bap_features.dict.n_items && i < SPA_N_ELEMENTS(setting_items); ++i, ++j)
|
||||
setting_items[i] = ep->bap_features.dict.items[j];
|
||||
if (ep->device->settings)
|
||||
for (j = 0; j < ep->device->settings->n_items && i < SPA_N_ELEMENTS(setting_items); ++i, ++j)
|
||||
setting_items[i] = ep->device->settings->items[j];
|
||||
settings = SPA_DICT_INIT(setting_items, i);
|
||||
|
||||
conf_size = codec->select_config(codec, 0, ep->capabilities, ep->capabilities_len,
|
||||
&monitor->default_audio_info, &settings, config);
|
||||
&monitor->default_audio_info, &settings, config, &config_data);
|
||||
if (conf_size < 0) {
|
||||
spa_log_error(monitor->log, "can't select config: %d (%s)",
|
||||
conf_size, spa_strerror(conf_size));
|
||||
|
|
@ -1092,8 +1211,6 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
spa_log_info(monitor->log, "%p: selected conf %d", monitor, conf_size);
|
||||
spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', (uint8_t *)config, (size_t)conf_size);
|
||||
|
||||
if ((r = dbus_message_new_method_return(m)) == NULL)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
dbus_message_iter_init_append(r, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
|
|
@ -1112,7 +1229,7 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
|
||||
spa_zero(qos);
|
||||
|
||||
res = codec->get_qos(codec, config, conf_size, &ep->qos, &qos, &settings);
|
||||
res = codec->get_qos(codec, &ep->qos, config_data, &qos);
|
||||
if (res < 0) {
|
||||
spa_log_error(monitor->log, "can't select QOS config: %d (%s)",
|
||||
res, spa_strerror(res));
|
||||
|
|
@ -1160,8 +1277,30 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
|
|||
dbus_message_iter_close_container(&dict, &entry);
|
||||
}
|
||||
|
||||
if (codec->get_metadata) {
|
||||
uint8_t meta[4096] = {};
|
||||
size_t meta_size;
|
||||
|
||||
meta_size = res = codec->get_metadata(codec, config_data, meta, sizeof(meta));
|
||||
if (res < 0) {
|
||||
spa_log_error(monitor->log, "can't select metadata config: %d (%s)",
|
||||
res, spa_strerror(res));
|
||||
goto error_invalid;
|
||||
}
|
||||
|
||||
if (meta_size) {
|
||||
spa_log_info(monitor->log, "%p: selected metadata %d", monitor, (int)meta_size);
|
||||
spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', meta, meta_size);
|
||||
|
||||
append_basic_array_variant_dict_entry(&dict, "Metadata", "ay", "y", DBUS_TYPE_BYTE, &meta, meta_size);
|
||||
}
|
||||
}
|
||||
|
||||
dbus_message_iter_close_container(&iter, &dict);
|
||||
|
||||
if (config_data && codec->free_config_data)
|
||||
codec->free_config_data(codec, config_data);
|
||||
|
||||
if (!dbus_connection_send(conn, r, NULL))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
|
|
@ -1172,6 +1311,9 @@ error_invalid:
|
|||
goto error;
|
||||
|
||||
error:
|
||||
if (config_data && codec->free_config_data)
|
||||
codec->free_config_data(codec, config_data);
|
||||
|
||||
if (!reply_with_error(conn, m, "org.bluez.Error.InvalidArguments", err_msg))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
|
|
@ -2796,6 +2938,38 @@ static struct spa_bt_device *create_bcast_device(struct spa_bt_monitor *monitor,
|
|||
|
||||
static int setup_asha_transport(struct spa_bt_remote_endpoint *remote_endpoint, struct spa_bt_monitor *monitor);
|
||||
|
||||
static void parse_supported_features(struct spa_bt_monitor *monitor,
|
||||
DBusMessageIter *dict, struct bap_features *features)
|
||||
{
|
||||
while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
|
||||
DBusMessageIter entry, variant, array;
|
||||
const char *key;
|
||||
|
||||
dbus_message_iter_recurse(dict, &entry);
|
||||
dbus_message_iter_get_basic(&entry, &key);
|
||||
dbus_message_iter_next(&entry);
|
||||
dbus_message_iter_recurse(&entry, &variant);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY)
|
||||
goto next;
|
||||
|
||||
dbus_message_iter_recurse(&variant, &array);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
|
||||
const char *name;
|
||||
|
||||
dbus_message_iter_get_basic(&array, &name);
|
||||
if (bap_features_add(features, key, name))
|
||||
spa_log_debug(monitor->log, "remote_endpoint: BAP feature %s %s", key, name);
|
||||
dbus_message_iter_next(&array);
|
||||
}
|
||||
|
||||
next:
|
||||
dbus_message_iter_next(dict);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_endpoint,
|
||||
DBusMessageIter *props_iter,
|
||||
DBusMessageIter *invalidated_iter)
|
||||
|
|
@ -2804,8 +2978,9 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
|
|||
DBusMessageIter copy_iter = *props_iter;
|
||||
|
||||
parse_endpoint_props(monitor, ©_iter,
|
||||
remote_endpoint->capabilities, &remote_endpoint->capabilities_len, NULL,
|
||||
&remote_endpoint->qos);
|
||||
&remote_endpoint->capabilities, &remote_endpoint->capabilities_len,
|
||||
&remote_endpoint->metadata, &remote_endpoint->metadata_len,
|
||||
NULL, &remote_endpoint->qos);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) {
|
||||
DBusMessageIter it[2];
|
||||
|
|
@ -2819,7 +2994,8 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
|
|||
|
||||
type = dbus_message_iter_get_arg_type(&it[1]);
|
||||
|
||||
if (spa_streq(key, "Capabilities") || spa_streq(key, "Locations") ||
|
||||
if (spa_streq(key, "Capabilities") || spa_streq(key, "Metadata") ||
|
||||
spa_streq(key, "Locations") ||
|
||||
spa_streq(key, "QoS") || spa_streq(key, "Context") ||
|
||||
spa_streq(key, "SupportedContext")) {
|
||||
/* parsed by parse_endpoint_props */
|
||||
|
|
@ -2929,8 +3105,13 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
|
|||
remote_endpoint->hisyncid = *(uint64_t *)value;
|
||||
|
||||
spa_log_debug(monitor->log, "remote_endpoint %p: %s=%"PRIu64, remote_endpoint, key, remote_endpoint->hisyncid);
|
||||
}
|
||||
else {
|
||||
} else if (spa_streq(key, "SupportedFeatures")) {
|
||||
if (!check_iter_signature(&it[1], "a{sv}"))
|
||||
goto next;
|
||||
|
||||
dbus_message_iter_recurse(&it[1], &it[2]);
|
||||
parse_supported_features(monitor, &it[2], &remote_endpoint->bap_features);
|
||||
} else {
|
||||
unhandled:
|
||||
spa_log_debug(monitor->log, "remote_endpoint %p: unhandled key %s", remote_endpoint, key);
|
||||
}
|
||||
|
|
@ -2985,10 +3166,14 @@ static void remote_endpoint_free(struct spa_bt_remote_endpoint *remote_endpoint)
|
|||
if (remote_endpoint->device)
|
||||
spa_list_remove(&remote_endpoint->device_link);
|
||||
|
||||
bap_features_clear(&remote_endpoint->bap_features);
|
||||
|
||||
spa_list_remove(&remote_endpoint->link);
|
||||
free(remote_endpoint->path);
|
||||
free(remote_endpoint->transport_path);
|
||||
free(remote_endpoint->uuid);
|
||||
free(remote_endpoint->capabilities);
|
||||
free(remote_endpoint->metadata);
|
||||
free(remote_endpoint);
|
||||
}
|
||||
|
||||
|
|
@ -3586,6 +3771,11 @@ static int transport_update_props(struct spa_bt_transport *transport,
|
|||
free(transport->configuration);
|
||||
transport->configuration_len = 0;
|
||||
|
||||
if (!len) {
|
||||
transport->configuration = NULL;
|
||||
goto next;
|
||||
}
|
||||
|
||||
transport->configuration = malloc(len);
|
||||
if (transport->configuration) {
|
||||
memcpy(transport->configuration, value, len);
|
||||
|
|
@ -4086,13 +4276,22 @@ static int transport_acquire(void *data, bool optional)
|
|||
return do_transport_acquire(data);
|
||||
}
|
||||
|
||||
static int do_transport_release(struct spa_bt_transport *transport)
|
||||
struct pending_release {
|
||||
struct spa_list link;
|
||||
DBusPendingCall *pending;
|
||||
struct spa_bt_transport *transport;
|
||||
bool is_idle;
|
||||
};
|
||||
|
||||
static struct pending_release *do_transport_release(struct spa_bt_transport *transport)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = transport->monitor;
|
||||
spa_autoptr(DBusMessage) m = NULL, r = NULL;
|
||||
spa_autoptr(DBusMessage) m = NULL;
|
||||
struct spa_bt_transport *t_linked;
|
||||
bool is_idle = (transport->state == SPA_BT_TRANSPORT_STATE_IDLE);
|
||||
bool linked = false;
|
||||
struct pending_release *pending;
|
||||
DBusPendingCall *p;
|
||||
|
||||
spa_log_debug(monitor->log, "transport %p: Release %s",
|
||||
transport, transport->path);
|
||||
|
|
@ -4129,7 +4328,7 @@ static int do_transport_release(struct spa_bt_transport *transport)
|
|||
if (linked) {
|
||||
spa_log_info(monitor->log, "Linked transport %s released", transport->path);
|
||||
transport->fd = -1;
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
release:
|
||||
|
|
@ -4145,46 +4344,39 @@ release:
|
|||
BLUEZ_MEDIA_TRANSPORT_INTERFACE,
|
||||
"Release");
|
||||
if (m == NULL)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
spa_auto(DBusError) err = DBUS_ERROR_INIT;
|
||||
r = dbus_connection_send_with_reply_and_block(monitor->conn, m, -1, &err);
|
||||
if (r == NULL) {
|
||||
if (is_idle) {
|
||||
/* XXX: The fd always needs to be closed. However, Release()
|
||||
* XXX: apparently doesn't need to be called on idle transports
|
||||
* XXX: and fails. We call it just to be sure (e.g. in case
|
||||
* XXX: there's a race with updating the property), but tone down the error.
|
||||
*/
|
||||
spa_log_debug(monitor->log, "Failed to release idle transport %s: %s",
|
||||
transport->path, err.message);
|
||||
} else if (spa_streq(err.name, DBUS_ERROR_UNKNOWN_METHOD) ||
|
||||
spa_streq(err.name, DBUS_ERROR_UNKNOWN_OBJECT)) {
|
||||
/* Transport disappeared */
|
||||
spa_log_debug(monitor->log, "Failed to release (gone) transport %s: %s",
|
||||
transport->path, err.message);
|
||||
} else {
|
||||
spa_log_error(monitor->log, "Failed to release transport %s: %s",
|
||||
transport->path, err.message);
|
||||
}
|
||||
} else {
|
||||
spa_log_info(monitor->log, "Transport %s released", transport->path);
|
||||
p = send_with_reply(monitor->conn, m, NULL, NULL);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
pending = calloc(1, sizeof(*pending));
|
||||
if (!pending) {
|
||||
dbus_pending_call_block(p);
|
||||
dbus_pending_call_unref(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
pending->pending = p;
|
||||
pending->transport = transport;
|
||||
pending->is_idle = is_idle;
|
||||
return pending;
|
||||
}
|
||||
|
||||
static int transport_release(void *data)
|
||||
{
|
||||
struct spa_bt_transport *transport = data;
|
||||
struct spa_bt_monitor *monitor = transport->monitor;
|
||||
struct spa_bt_transport *t;
|
||||
struct spa_list pending = SPA_LIST_INIT(&pending);
|
||||
struct pending_release *item;
|
||||
|
||||
/*
|
||||
* XXX: When as BAP Central, release CIS in a CIG when the last transport
|
||||
* XXX: goes away.
|
||||
*/
|
||||
if (transport->bap_initiator) {
|
||||
struct spa_bt_transport *t;
|
||||
|
||||
/* Check if another transport is alive */
|
||||
if (another_cig_transport_active(transport)) {
|
||||
spa_log_debug(monitor->log, "Releasing %s: wait for CIG %d",
|
||||
|
|
@ -4200,15 +4392,61 @@ static int transport_release(void *data)
|
|||
spa_log_debug(monitor->log, "Release CIG %d: transport %s",
|
||||
transport->bap_cig, t->path);
|
||||
|
||||
if (t->fd >= 0)
|
||||
do_transport_release(t);
|
||||
if (t->fd >= 0) {
|
||||
item = do_transport_release(t);
|
||||
if (item)
|
||||
spa_list_append(&pending, &item->link);
|
||||
}
|
||||
}
|
||||
|
||||
spa_log_debug(monitor->log, "Release CIG %d: transport %s",
|
||||
transport->bap_cig, transport->path);
|
||||
}
|
||||
|
||||
return do_transport_release(data);
|
||||
item = do_transport_release(transport);
|
||||
if (item)
|
||||
spa_list_append(&pending, &item->link);
|
||||
|
||||
spa_list_consume(item, &pending, link) {
|
||||
struct spa_bt_transport *t = item->transport;
|
||||
bool is_idle = item->is_idle;
|
||||
DBusPendingCall *p = item->pending;
|
||||
spa_autoptr(DBusMessage) r = NULL;
|
||||
spa_auto(DBusError) err = DBUS_ERROR_INIT;
|
||||
|
||||
spa_list_remove(&item->link);
|
||||
free(item);
|
||||
if (!p)
|
||||
continue;
|
||||
|
||||
dbus_pending_call_block(p);
|
||||
r = steal_reply_and_unref(&p);
|
||||
|
||||
if (r == NULL) {
|
||||
if (is_idle) {
|
||||
/* XXX: The fd always needs to be closed. However, Release()
|
||||
* XXX: apparently doesn't need to be called on idle transports
|
||||
* XXX: and fails. We call it just to be sure (e.g. in case
|
||||
* XXX: there's a race with updating the property), but tone down the error.
|
||||
*/
|
||||
spa_log_debug(monitor->log, "Failed to release idle transport %s: %s",
|
||||
t->path, err.message);
|
||||
} else if (spa_streq(err.name, DBUS_ERROR_UNKNOWN_METHOD) ||
|
||||
spa_streq(err.name, DBUS_ERROR_UNKNOWN_OBJECT)) {
|
||||
/* Transport disappeared */
|
||||
spa_log_debug(monitor->log, "Failed to release (gone) transport %s: %s",
|
||||
t->path, err.message);
|
||||
} else {
|
||||
spa_log_error(monitor->log, "Failed to release transport %s: %s",
|
||||
t->path, err.message);
|
||||
}
|
||||
} else {
|
||||
spa_log_info(monitor->log, "Transport %s released", t->path);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int transport_set_delay(void *data, int64_t delay_nsec)
|
||||
|
|
@ -4480,7 +4718,7 @@ static bool codec_switch_configure_a2dp(struct spa_bt_codec_switch *sw, const ch
|
|||
}
|
||||
|
||||
res = codec->select_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0, ep->capabilities, ep->capabilities_len,
|
||||
&monitor->default_audio_info, &monitor->global_settings, config);
|
||||
&monitor->default_audio_info, &monitor->global_settings, config, NULL);
|
||||
if (res < 0) {
|
||||
spa_log_error(monitor->log, "media codec switch %p: incompatible capabilities (%d)",
|
||||
sw, res);
|
||||
|
|
@ -5037,6 +5275,10 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
|
|||
spa_log_error(monitor->log, "invalid transport configuration");
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
if (info.info.raw.channels > MAX_CHANNELS) {
|
||||
spa_log_error(monitor->log, "too many channels in transport");
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
transport->n_channels = info.info.raw.channels;
|
||||
memcpy(transport->channels, info.info.raw.position,
|
||||
transport->n_channels * sizeof(uint32_t));
|
||||
|
|
@ -5301,6 +5543,42 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void append_supported_features(DBusMessageIter *dict, struct bap_features *features)
|
||||
{
|
||||
const char *key = "SupportedFeatures";
|
||||
DBusMessageIter dict_entry, dict_variant, value_dict;
|
||||
DBusMessageIter entry, variant, array;
|
||||
const char *uuid, *name;
|
||||
size_t i;
|
||||
|
||||
dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry);
|
||||
dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &key);
|
||||
dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, "a{sv}", &dict_variant);
|
||||
|
||||
dbus_message_iter_open_container(&dict_variant, DBUS_TYPE_ARRAY, "{sv}", &value_dict);
|
||||
|
||||
i = 0;
|
||||
while ((uuid = bap_features_get_uuid(features, i))) {
|
||||
dbus_message_iter_open_container(&value_dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
|
||||
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &uuid);
|
||||
dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "as", &variant);
|
||||
dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "s", &array);
|
||||
|
||||
while ((name = bap_features_get_name(features, i, uuid))) {
|
||||
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &name);
|
||||
++i;
|
||||
}
|
||||
|
||||
dbus_message_iter_close_container(&variant, &array);
|
||||
dbus_message_iter_close_container(&entry, &variant);
|
||||
dbus_message_iter_close_container(&value_dict, &entry);
|
||||
}
|
||||
|
||||
dbus_message_iter_close_container(&dict_variant, &value_dict);
|
||||
dbus_message_iter_close_container(&dict_entry, &dict_variant);
|
||||
dbus_message_iter_close_container(dict, &dict_entry);
|
||||
}
|
||||
|
||||
static void append_media_object(struct spa_bt_monitor *monitor, DBusMessageIter *iter, const char *endpoint,
|
||||
const char *uuid, uint8_t codec_id, uint8_t *caps, size_t caps_size)
|
||||
{
|
||||
|
|
@ -5348,6 +5626,9 @@ static void append_media_object(struct spa_bt_monitor *monitor, DBusMessageIter
|
|||
append_basic_variant_dict_entry(&dict, "SupportedContext", DBUS_TYPE_UINT16, "q", &supported_contexts);
|
||||
}
|
||||
|
||||
if (spa_bt_profile_from_uuid(uuid) & SPA_BT_PROFILE_BAP_AUDIO)
|
||||
append_supported_features(&dict, &monitor->bap_features);
|
||||
|
||||
dbus_message_iter_close_container(&entry, &dict);
|
||||
dbus_message_iter_close_container(&array, &entry);
|
||||
dbus_message_iter_close_container(&object, &array);
|
||||
|
|
@ -5577,10 +5858,10 @@ static int register_media_endpoint(struct spa_bt_monitor *monitor,
|
|||
static int register_media_application(struct spa_bt_monitor * monitor)
|
||||
{
|
||||
const struct media_codec * const * const media_codecs = monitor->media_codecs;
|
||||
const DBusObjectPathVTable vtable_object_manager_a2dp = {
|
||||
static const DBusObjectPathVTable vtable_object_manager_a2dp = {
|
||||
.message_function = object_manager_handler_a2dp,
|
||||
};
|
||||
const DBusObjectPathVTable vtable_object_manager_bap = {
|
||||
static const DBusObjectPathVTable vtable_object_manager_bap = {
|
||||
.message_function = object_manager_handler_bap,
|
||||
};
|
||||
|
||||
|
|
@ -5811,6 +6092,7 @@ static void configure_bis(struct spa_bt_monitor *monitor,
|
|||
int sync_cte_type = 0;
|
||||
int sync_timeout = 2000;
|
||||
int timeout = 2000;
|
||||
int ret;
|
||||
|
||||
/* Configure each BIS from a BIG */
|
||||
spa_list_for_each(metadata_entry, &bis->metadata_list, link) {
|
||||
|
|
@ -5834,7 +6116,12 @@ static void configure_bis(struct spa_bt_monitor *monitor,
|
|||
setting_items[1] = SPA_DICT_ITEM_INIT("preset", bis->qos_preset);
|
||||
settings = SPA_DICT_INIT(setting_items, 2);
|
||||
|
||||
codec->get_bis_config(codec, caps, &caps_size, &settings, &qos);
|
||||
caps_size = sizeof(caps);
|
||||
ret = codec->get_bis_config(codec, caps, &caps_size, &settings, &qos);
|
||||
if (ret < 0) {
|
||||
spa_log_warn(monitor->log, "Getting BIS config failed");
|
||||
return;
|
||||
}
|
||||
|
||||
msg = dbus_message_new_method_call(BLUEZ_SERVICE,
|
||||
object_path,
|
||||
|
|
@ -6563,6 +6850,8 @@ static int impl_clear(struct spa_handle *handle)
|
|||
monitor->backend = NULL;
|
||||
monitor->backend_selection = BACKEND_NATIVE;
|
||||
|
||||
bap_features_clear(&monitor->bap_features);
|
||||
|
||||
spa_bt_quirks_destroy(monitor->quirks);
|
||||
|
||||
free_media_codecs(monitor->media_codecs);
|
||||
|
|
@ -6850,7 +7139,7 @@ static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_di
|
|||
const char *key, uint32_t *value)
|
||||
{
|
||||
const char *str;
|
||||
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t position[MAX_CHANNELS];
|
||||
uint32_t n_channels;
|
||||
uint32_t locations;
|
||||
unsigned int i, j;
|
||||
|
|
@ -6861,7 +7150,8 @@ static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_di
|
|||
if (spa_atou32(str, value, 0))
|
||||
return;
|
||||
|
||||
if (!spa_audio_parse_position(str, strlen(str), position, &n_channels)) {
|
||||
if (!spa_audio_parse_position_n(str, strlen(str), position,
|
||||
SPA_N_ELEMENTS(position), &n_channels)) {
|
||||
spa_log_error(this->log, "property %s '%s' is not valid position array", key, str);
|
||||
return;
|
||||
}
|
||||
|
|
@ -6875,10 +7165,36 @@ static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_di
|
|||
*value = locations;
|
||||
}
|
||||
|
||||
static void bap_feature_parse(struct spa_bt_monitor *this, const char *uuid, const char *str)
|
||||
{
|
||||
struct spa_json it;
|
||||
char name[64];
|
||||
|
||||
if (!str)
|
||||
return;
|
||||
|
||||
if (spa_json_begin_array_relax(&it, str, strlen(str)) < 0)
|
||||
return;
|
||||
|
||||
while (spa_json_get_string(&it, name, sizeof(name)) > 0) {
|
||||
if (bap_features_add(&this->bap_features, uuid, name))
|
||||
spa_log_debug(this->log, "advertise BAP feature %s %s", uuid, name);
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_bap_features(struct spa_bt_monitor *this, const struct spa_dict *info)
|
||||
{
|
||||
static const char *const tmap_uuid = "00001855-0000-1000-8000-00805f9b34fb";
|
||||
static const char *const gmap_uuid = "00001858-0000-1000-8000-00805f9b34fb";
|
||||
|
||||
bap_feature_parse(this, tmap_uuid, spa_dict_lookup(info, "bluez5.bap-server-tmap-features"));
|
||||
bap_feature_parse(this, gmap_uuid, spa_dict_lookup(info, "bluez5.bap-server-gmap-features"));
|
||||
}
|
||||
|
||||
static void parse_bap_server(struct spa_bt_monitor *this, const struct spa_dict *info)
|
||||
{
|
||||
this->bap_sink_locations = BAP_CHANNEL_ALL;
|
||||
this->bap_source_locations = BAP_CHANNEL_ALL;
|
||||
this->bap_sink_locations = BAP_CHANNEL_FL | BAP_CHANNEL_FR;
|
||||
this->bap_source_locations = BAP_CHANNEL_FL | BAP_CHANNEL_FR;
|
||||
this->bap_sink_contexts = this->bap_sink_supported_contexts = BAP_CONTEXT_ALL;
|
||||
this->bap_source_contexts = this->bap_source_supported_contexts = (BAP_CONTEXT_UNSPECIFIED | BAP_CONTEXT_CONVERSATIONAL |
|
||||
BAP_CONTEXT_MEDIA | BAP_CONTEXT_GAME);
|
||||
|
|
@ -6893,6 +7209,8 @@ static void parse_bap_server(struct spa_bt_monitor *this, const struct spa_dict
|
|||
parse_bap_locations(this, info, "bluez5.bap-server-capabilities.source.locations", &this->bap_source_locations);
|
||||
spa_atou32(spa_dict_lookup(info, "bluez5.bap-server-capabilities.source.contexts"), &this->bap_source_contexts, 0);
|
||||
spa_atou32(spa_dict_lookup(info, "bluez5.bap-server-capabilities.source.supported-contexts"), &this->bap_source_supported_contexts, 0);
|
||||
|
||||
parse_bap_features(this, info);
|
||||
}
|
||||
|
||||
static void get_global_settings(struct spa_bt_monitor *this, const struct spa_dict *dict)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <spa/support/log.h>
|
||||
#include <spa/utils/type.h>
|
||||
#include <spa/utils/json.h>
|
||||
#include <spa/utils/keys.h>
|
||||
#include <spa/utils/names.h>
|
||||
#include <spa/utils/string.h>
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
#include <spa/pod/parser.h>
|
||||
#include <spa/param/param.h>
|
||||
#include <spa/param/audio/raw.h>
|
||||
#include <spa/param/audio/raw-utils.h>
|
||||
#include <spa/param/bluetooth/audio.h>
|
||||
#include <spa/param/bluetooth/type-info.h>
|
||||
#include <spa/debug/pod.h>
|
||||
|
|
@ -38,7 +40,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.device");
|
|||
#undef SPA_LOG_TOPIC_DEFAULT
|
||||
#define SPA_LOG_TOPIC_DEFAULT &log_topic
|
||||
|
||||
#define MAX_NODES (2*SPA_AUDIO_MAX_CHANNELS)
|
||||
#define MAX_NODES (2*MAX_CHANNELS)
|
||||
|
||||
#define DEVICE_ID_SOURCE 0
|
||||
#define DEVICE_ID_SINK 1
|
||||
|
|
@ -99,9 +101,9 @@ struct node {
|
|||
unsigned int offload_acquired:1;
|
||||
uint32_t n_channels;
|
||||
int64_t latency_offset;
|
||||
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
|
||||
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
float soft_volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t channels[MAX_CHANNELS];
|
||||
float volumes[MAX_CHANNELS];
|
||||
float soft_volumes[MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct dynamic_node
|
||||
|
|
@ -129,8 +131,8 @@ struct device_set {
|
|||
bool leader;
|
||||
uint32_t sinks;
|
||||
uint32_t sources;
|
||||
struct device_set_member sink[SPA_AUDIO_MAX_CHANNELS];
|
||||
struct device_set_member source[SPA_AUDIO_MAX_CHANNELS];
|
||||
struct device_set_member sink[MAX_CHANNELS];
|
||||
struct device_set_member source[MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct impl {
|
||||
|
|
@ -182,7 +184,7 @@ static void init_node(struct impl *this, struct node *node, uint32_t id)
|
|||
|
||||
spa_zero(*node);
|
||||
node->id = id;
|
||||
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
|
||||
for (i = 0; i < MAX_CHANNELS; i++) {
|
||||
node->volumes[i] = 1.0f;
|
||||
node->soft_volumes[i] = 1.0f;
|
||||
}
|
||||
|
|
@ -476,6 +478,7 @@ static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t
|
|||
*n_channels = info.info.raw.channels;
|
||||
memcpy(channels, info.info.raw.position,
|
||||
info.info.raw.channels * sizeof(uint32_t));
|
||||
|
||||
}
|
||||
|
||||
static const char *get_channel_name(uint32_t channel)
|
||||
|
|
@ -546,7 +549,7 @@ static void emit_device_set_node(struct impl *this, uint32_t id)
|
|||
if (node->channels[k] == t->channels[j])
|
||||
break;
|
||||
}
|
||||
if (k == node->n_channels && node->n_channels < SPA_AUDIO_MAX_CHANNELS)
|
||||
if (k == node->n_channels && node->n_channels < MAX_CHANNELS)
|
||||
node->channels[node->n_channels++] = t->channels[j];
|
||||
}
|
||||
}
|
||||
|
|
@ -2413,7 +2416,7 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b,
|
|||
default:
|
||||
name_prefix = "bluetooth";
|
||||
description = _("Bluetooth");
|
||||
hfp_description = _("Bluetooth (HFP)");
|
||||
hfp_description = _("Bluetooth Handsfree");
|
||||
port_type = "bluetooth";
|
||||
break;
|
||||
}
|
||||
|
|
@ -2674,11 +2677,24 @@ static struct spa_pod *build_prop_info_codec(struct impl *this, struct spa_pod_b
|
|||
static struct spa_pod *build_props(struct impl *this, struct spa_pod_builder *b, uint32_t id)
|
||||
{
|
||||
struct props *p = &this->props;
|
||||
struct spa_pod_frame f[2];
|
||||
struct spa_pod *param;
|
||||
|
||||
return spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_Props, id,
|
||||
SPA_PROP_bluetoothAudioCodec, SPA_POD_Id(p->codec),
|
||||
SPA_PROP_bluetoothOffloadActive, SPA_POD_Bool(p->offload_active));
|
||||
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Props, id);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_PROP_bluetoothAudioCodec, SPA_POD_Id(p->codec),
|
||||
SPA_PROP_bluetoothOffloadActive, SPA_POD_Bool(p->offload_active),
|
||||
0);
|
||||
|
||||
spa_pod_builder_prop(b, SPA_PROP_params, 0);
|
||||
spa_pod_builder_push_struct(b, &f[1]);
|
||||
spa_pod_builder_string(b, "bluez5.disable-dummy-call");
|
||||
spa_pod_builder_bool(b, this->bt_dev->disable_dummy_call);
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
|
||||
param = spa_pod_builder_pop(b, &f[0]);
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
static int impl_enum_params(void *object, int seq,
|
||||
|
|
@ -2946,8 +2962,8 @@ static int apply_device_props(struct impl *this, struct node *node, struct spa_p
|
|||
struct spa_pod_prop *prop;
|
||||
struct spa_pod_object *obj = (struct spa_pod_object *) props;
|
||||
int changed = 0;
|
||||
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
|
||||
float volumes[MAX_CHANNELS];
|
||||
uint32_t channels[MAX_CHANNELS];
|
||||
uint32_t n_volumes = 0, SPA_UNUSED n_channels = 0;
|
||||
int64_t latency_offset = 0;
|
||||
|
||||
|
|
@ -2972,11 +2988,11 @@ static int apply_device_props(struct impl *this, struct node *node, struct spa_p
|
|||
break;
|
||||
case SPA_PROP_channelVolumes:
|
||||
n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
|
||||
volumes, SPA_AUDIO_MAX_CHANNELS);
|
||||
volumes, SPA_N_ELEMENTS(volumes));
|
||||
break;
|
||||
case SPA_PROP_channelMap:
|
||||
n_channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
|
||||
channels, SPA_AUDIO_MAX_CHANNELS);
|
||||
channels, SPA_N_ELEMENTS(channels));
|
||||
break;
|
||||
case SPA_PROP_latencyOffsetNsec:
|
||||
if (spa_pod_get_long(&prop->value, &latency_offset) == 0) {
|
||||
|
|
@ -3015,6 +3031,47 @@ static void apply_prop_offload_active(struct impl *this, bool active)
|
|||
}
|
||||
}
|
||||
|
||||
static int parse_prop_params(struct impl *this, struct spa_pod *params)
|
||||
{
|
||||
struct spa_pod_parser prs;
|
||||
struct spa_pod_frame f;
|
||||
int changed = 0;
|
||||
|
||||
if (params == NULL)
|
||||
return 0;
|
||||
|
||||
spa_pod_parser_pod(&prs, params);
|
||||
if (spa_pod_parser_push_struct(&prs, &f) < 0)
|
||||
return 0;
|
||||
|
||||
while (true) {
|
||||
const char *name;
|
||||
struct spa_pod *pod;
|
||||
|
||||
if (spa_pod_parser_get_string(&prs, &name) < 0)
|
||||
break;
|
||||
|
||||
if (spa_pod_parser_get_pod(&prs, &pod) < 0)
|
||||
break;
|
||||
|
||||
if (spa_streq(name, "bluez5.disable-dummy-call") && spa_pod_is_bool(pod)) {
|
||||
bool disable_dummy_call = SPA_POD_VALUE(struct spa_pod_bool, pod);
|
||||
spa_log_info(this->log, "key:'%s' val:'%u'", name, disable_dummy_call);
|
||||
this->bt_dev->disable_dummy_call = disable_dummy_call;
|
||||
} else
|
||||
continue;
|
||||
|
||||
changed++;
|
||||
}
|
||||
|
||||
if (changed > 0) {
|
||||
this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
|
||||
this->params[IDX_Props].user++;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int impl_set_param(void *object,
|
||||
uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
|
|
@ -3091,6 +3148,7 @@ static int impl_set_param(void *object,
|
|||
{
|
||||
uint32_t codec_id = SPA_ID_INVALID;
|
||||
bool offload_active = this->props.offload_active;
|
||||
struct spa_pod *params = NULL;
|
||||
|
||||
if (param == NULL)
|
||||
return 0;
|
||||
|
|
@ -3098,7 +3156,8 @@ static int impl_set_param(void *object,
|
|||
if ((res = spa_pod_parse_object(param,
|
||||
SPA_TYPE_OBJECT_Props, NULL,
|
||||
SPA_PROP_bluetoothAudioCodec, SPA_POD_OPT_Id(&codec_id),
|
||||
SPA_PROP_bluetoothOffloadActive, SPA_POD_OPT_Bool(&offload_active))) < 0) {
|
||||
SPA_PROP_bluetoothOffloadActive, SPA_POD_OPT_Bool(&offload_active),
|
||||
SPA_PROP_params, SPA_POD_OPT_Pod(¶ms))) < 0) {
|
||||
spa_log_warn(this->log, "can't parse props");
|
||||
spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param);
|
||||
return res;
|
||||
|
|
@ -3106,10 +3165,15 @@ static int impl_set_param(void *object,
|
|||
|
||||
spa_log_debug(this->log, "setting props codec:%d offload:%d", (int)codec_id, (int)offload_active);
|
||||
|
||||
parse_prop_params(this, params);
|
||||
|
||||
apply_prop_offload_active(this, offload_active);
|
||||
|
||||
if (codec_id == SPA_ID_INVALID)
|
||||
if (codec_id == SPA_ID_INVALID) {
|
||||
this->params[IDX_Props].flags ^= SPA_PARAM_INFO_SERIAL;
|
||||
emit_info(this, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this->profile == DEVICE_PROFILE_A2DP || profile_is_bap(this->profile) ||
|
||||
this->profile == DEVICE_PROFILE_ASHA || this->profile == DEVICE_PROFILE_HSP_HFP) {
|
||||
|
|
@ -3247,6 +3311,13 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
if ((profiles = spa_bt_profiles_from_json_array(str)) >= 0)
|
||||
this->bt_dev->hw_volume_profiles = profiles;
|
||||
}
|
||||
|
||||
if ((str = spa_dict_lookup(info, "bluez5.disable-dummy-call")) != NULL) {
|
||||
bool value;
|
||||
|
||||
if (spa_json_parse_bool(str, strlen(str), &value) > 0)
|
||||
this->bt_dev->disable_dummy_call = value;
|
||||
}
|
||||
}
|
||||
|
||||
this->device.iface = SPA_INTERFACE_INIT(
|
||||
|
|
|
|||
|
|
@ -157,6 +157,8 @@ extern "C" {
|
|||
|
||||
#define SPA_BT_NO_BATTERY ((uint8_t)255)
|
||||
|
||||
#define MAX_CHANNELS (SPA_AUDIO_MAX_CHANNELS)
|
||||
|
||||
enum spa_bt_media_direction {
|
||||
SPA_BT_MEDIA_SOURCE,
|
||||
SPA_BT_MEDIA_SINK,
|
||||
|
|
@ -571,6 +573,8 @@ struct spa_bt_device {
|
|||
|
||||
const struct media_codec *preferred_codec;
|
||||
uint32_t preferred_profiles;
|
||||
|
||||
bool disable_dummy_call;
|
||||
};
|
||||
|
||||
struct spa_bt_device *spa_bt_device_find(struct spa_bt_monitor *monitor, const char *path);
|
||||
|
|
@ -678,7 +682,7 @@ struct spa_bt_transport {
|
|||
struct spa_list bap_transport_linked;
|
||||
|
||||
uint32_t n_channels;
|
||||
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
|
||||
uint32_t channels[MAX_CHANNELS];
|
||||
|
||||
struct spa_bt_transport_volume volumes[SPA_BT_VOLUME_ID_TERM];
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
struct spa_pod_builder *b, struct spa_pod **param)
|
||||
{
|
||||
struct spa_pod_frame f[1];
|
||||
const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO };
|
||||
const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO };
|
||||
const int channels = 1;
|
||||
|
||||
spa_assert(caps == NULL && caps_size == 0);
|
||||
|
|
@ -47,7 +47,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16_LE),
|
||||
SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_ENUM_Int(1, 8000),
|
||||
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(8000),
|
||||
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(channels),
|
||||
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
|
||||
SPA_TYPE_Id, channels, position),
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
struct spa_pod_builder *b, struct spa_pod **param)
|
||||
{
|
||||
struct spa_pod_frame f[1];
|
||||
const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO };
|
||||
const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO };
|
||||
const int channels = 1;
|
||||
|
||||
spa_assert(caps == NULL && caps_size == 0);
|
||||
|
|
@ -52,7 +52,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_F32),
|
||||
SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_ENUM_Int(1, 24000),
|
||||
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(24000),
|
||||
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(channels),
|
||||
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
|
||||
SPA_TYPE_Id, channels, position),
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
struct spa_pod_builder *b, struct spa_pod **param)
|
||||
{
|
||||
struct spa_pod_frame f[1];
|
||||
const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO };
|
||||
const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO };
|
||||
const int channels = 1;
|
||||
|
||||
spa_assert(caps == NULL && caps_size == 0);
|
||||
|
|
@ -55,7 +55,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_F32),
|
||||
SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_ENUM_Int(1, 32000),
|
||||
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(32000),
|
||||
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(channels),
|
||||
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
|
||||
SPA_TYPE_Id, channels, position),
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
struct spa_pod_builder *b, struct spa_pod **param)
|
||||
{
|
||||
struct spa_pod_frame f[1];
|
||||
const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO };
|
||||
const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO };
|
||||
const int channels = 1;
|
||||
|
||||
spa_assert(caps == NULL && caps_size == 0);
|
||||
|
|
@ -62,7 +62,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
|
|||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16),
|
||||
SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_ENUM_Int(1, 16000),
|
||||
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(16000),
|
||||
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(channels),
|
||||
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
|
||||
SPA_TYPE_Id, channels, position),
|
||||
|
|
|
|||
|
|
@ -309,6 +309,7 @@ static void group_on_timeout(struct spa_source *source)
|
|||
if (stream->this.size == 0) {
|
||||
spa_log_debug(group->log, "%p: ISO group:%u miss fd:%d",
|
||||
group, group->id, stream->fd);
|
||||
stream->this.resync = true;
|
||||
if (stream_silence(stream) < 0) {
|
||||
fail = true;
|
||||
continue;
|
||||
|
|
@ -625,6 +626,17 @@ int spa_bt_iso_io_recv_errqueue(struct spa_bt_iso_io *this)
|
|||
struct stream *stream = SPA_CONTAINER_OF(this, struct stream, this);
|
||||
struct group *group = stream->group;
|
||||
|
||||
if (!stream->sink) {
|
||||
struct stream *s;
|
||||
|
||||
spa_list_for_each(s, &group->streams, link) {
|
||||
if (s->sink && s->fd == stream->fd) {
|
||||
stream = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spa_bt_latency_recv_errqueue(&stream->tx_latency, stream->fd, group->log);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
|
||||
#include <spa/utils/string.h>
|
||||
#include <spa/utils/cleanup.h>
|
||||
|
||||
|
|
@ -90,7 +92,7 @@ bool media_codec_check_caps(const struct media_codec *codec, unsigned int codec_
|
|||
if (caps == NULL)
|
||||
return false;
|
||||
|
||||
res = codec->select_config(codec, 0, caps, caps_size, info, global_settings, config);
|
||||
res = codec->select_config(codec, 0, caps, caps_size, info, global_settings, config, NULL);
|
||||
if (res < 0)
|
||||
return false;
|
||||
|
||||
|
|
@ -100,6 +102,52 @@ bool media_codec_check_caps(const struct media_codec *codec, unsigned int codec_
|
|||
return ((size_t)res == caps_size);
|
||||
}
|
||||
|
||||
void ltv_writer_data(struct ltv_writer *w, uint8_t type, void* value, size_t len)
|
||||
{
|
||||
struct ltv *ltv;
|
||||
size_t sz = (size_t)w->size + sizeof(struct ltv) + len;
|
||||
|
||||
if (!w->buf || sz > w->max_size || (uint16_t)sz != sz) {
|
||||
w->buf = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
ltv = SPA_PTROFF(w->buf, w->size, struct ltv);
|
||||
ltv->len = len + 1;
|
||||
ltv->type = type;
|
||||
memcpy(ltv->value, value, len);
|
||||
|
||||
w->size = sz;
|
||||
}
|
||||
|
||||
void ltv_writer_uint8(struct ltv_writer *w, uint8_t type, uint8_t v)
|
||||
{
|
||||
ltv_writer_data(w, type, &v, sizeof(v));
|
||||
}
|
||||
|
||||
void ltv_writer_uint16(struct ltv_writer *w, uint8_t type, uint16_t value)
|
||||
{
|
||||
uint16_t v = htobs(value);
|
||||
|
||||
ltv_writer_data(w, type, &v, sizeof(v));
|
||||
}
|
||||
|
||||
void ltv_writer_uint32(struct ltv_writer *w, uint8_t type, uint32_t value)
|
||||
{
|
||||
uint32_t v = htobl(value);
|
||||
|
||||
ltv_writer_data(w, type, &v, sizeof(v));
|
||||
}
|
||||
|
||||
int ltv_writer_end(struct ltv_writer *w)
|
||||
{
|
||||
if (!w->buf)
|
||||
return -ENOSPC;
|
||||
|
||||
w->buf = NULL;
|
||||
return w->size;
|
||||
}
|
||||
|
||||
#ifdef CODEC_PLUGIN
|
||||
|
||||
struct impl {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <spa/pod/pod.h>
|
||||
#include <spa/pod/builder.h>
|
||||
#include <spa/support/log.h>
|
||||
#include <spa/debug/log.h>
|
||||
|
||||
#include "a2dp-codec-caps.h"
|
||||
#include "bap-codec-caps.h"
|
||||
|
|
@ -26,7 +27,7 @@
|
|||
|
||||
#define SPA_TYPE_INTERFACE_Bluez5CodecMedia SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:Media:Private"
|
||||
|
||||
#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 15
|
||||
#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 16
|
||||
|
||||
struct spa_bluez5_codec_a2dp {
|
||||
struct spa_interface iface;
|
||||
|
|
@ -93,7 +94,7 @@ struct media_codec {
|
|||
* called again to parse the remaining data. */
|
||||
|
||||
int (*get_bis_config)(const struct media_codec *codec, uint8_t *caps,
|
||||
uint8_t *caps_size, struct spa_dict *settings,
|
||||
uint8_t *caps_size, struct spa_dict *settings,
|
||||
struct bap_codec_qos *qos);
|
||||
|
||||
/** If fill_caps is NULL, no endpoint is registered (for sharing with another codec). */
|
||||
|
|
@ -103,7 +104,8 @@ struct media_codec {
|
|||
int (*select_config) (const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size,
|
||||
const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE]);
|
||||
const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE],
|
||||
void **config_data);
|
||||
int (*enum_config) (const struct media_codec *codec, uint32_t flags,
|
||||
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
|
||||
struct spa_pod_builder *builder, struct spa_pod **param);
|
||||
|
|
@ -111,9 +113,12 @@ struct media_codec {
|
|||
const void *caps, size_t caps_size,
|
||||
struct spa_audio_info *info);
|
||||
int (*get_qos)(const struct media_codec *codec,
|
||||
const void *config, size_t config_size,
|
||||
const struct bap_endpoint_qos *endpoint_qos,
|
||||
struct bap_codec_qos *qos, const struct spa_dict *settings);
|
||||
const void *config_data,
|
||||
struct bap_codec_qos *qos);
|
||||
int (*get_metadata)(const struct media_codec *codec, const void *config_data,
|
||||
uint8_t *meta, size_t meta_max_size);
|
||||
void (*free_config_data)(const struct media_codec *codec, void *config_data);
|
||||
|
||||
/** qsort comparison sorting caps in order of preference for the codec.
|
||||
* Used in codec switching to select best remote endpoints.
|
||||
|
|
@ -264,4 +269,44 @@ bool media_codec_check_caps(const struct media_codec *codec, unsigned int codec_
|
|||
const void *caps, size_t caps_size, const struct media_codec_audio_info *info,
|
||||
const struct spa_dict *global_settings);
|
||||
|
||||
struct __attribute__((packed)) ltv {
|
||||
uint8_t len;
|
||||
uint8_t type;
|
||||
uint8_t value[];
|
||||
};
|
||||
|
||||
struct ltv_writer {
|
||||
void *buf;
|
||||
uint16_t size;
|
||||
size_t max_size;
|
||||
};
|
||||
|
||||
#define LTV_WRITER(ptr, max) ((struct ltv_writer) { .buf = (ptr), .max_size = (max) })
|
||||
|
||||
void ltv_writer_data(struct ltv_writer *w, uint8_t type, void* value, size_t len);
|
||||
void ltv_writer_uint8(struct ltv_writer *w, uint8_t type, uint8_t v);
|
||||
void ltv_writer_uint16(struct ltv_writer *w, uint8_t type, uint16_t value);
|
||||
void ltv_writer_uint32(struct ltv_writer *w, uint8_t type, uint32_t value);
|
||||
int ltv_writer_end(struct ltv_writer *w);
|
||||
|
||||
static inline const struct ltv *ltv_next(const void **data, size_t *size)
|
||||
{
|
||||
const struct ltv *ltv;
|
||||
|
||||
if (*size == 0) {
|
||||
*data = NULL;
|
||||
return NULL;
|
||||
}
|
||||
if (*size < sizeof(struct ltv))
|
||||
return NULL;
|
||||
|
||||
ltv = *data;
|
||||
if (ltv->len >= *size)
|
||||
return NULL;
|
||||
|
||||
*data = SPA_PTROFF(*data, ltv->len + 1, void);
|
||||
*size -= ltv->len + 1;
|
||||
return ltv;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2139,7 +2139,7 @@ static int port_set_format(struct impl *this, struct port *port,
|
|||
|
||||
if (info.info.raw.rate == 0 ||
|
||||
info.info.raw.channels == 0 ||
|
||||
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
|
||||
info.info.raw.channels > MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
if (this->transport && this->transport->iso_io) {
|
||||
|
|
|
|||
|
|
@ -613,6 +613,10 @@ static void handle_errqueue(struct impl *this)
|
|||
{
|
||||
int res;
|
||||
|
||||
if (this->transport && this->transport->iso_io)
|
||||
if (spa_bt_iso_io_recv_errqueue(this->transport->iso_io) == 0)
|
||||
return;
|
||||
|
||||
/* iso-io/media-sink use these for TX latency.
|
||||
* Someone else should be reading them, so drop
|
||||
* only after yielding.
|
||||
|
|
@ -1439,7 +1443,7 @@ static int port_set_format(struct impl *this, struct port *port,
|
|||
|
||||
if (info.info.raw.rate == 0 ||
|
||||
info.info.raw.channels == 0 ||
|
||||
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
|
||||
info.info.raw.channels > MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
port->frame_size = info.info.raw.channels;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@
|
|||
|
||||
#include "modemmanager.h"
|
||||
|
||||
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.modemmanager");
|
||||
#undef SPA_LOG_TOPIC_DEFAULT
|
||||
#define SPA_LOG_TOPIC_DEFAULT &log_topic
|
||||
|
||||
#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager"
|
||||
|
||||
struct modem {
|
||||
|
|
@ -32,6 +36,8 @@ struct impl {
|
|||
|
||||
struct modem modem;
|
||||
struct spa_list call_list;
|
||||
|
||||
bool pts;
|
||||
};
|
||||
|
||||
struct dbus_cmd_data {
|
||||
|
|
@ -412,6 +418,26 @@ static void mm_get_managed_objects_reply(DBusPendingCall *pending, void *user_da
|
|||
}
|
||||
}
|
||||
|
||||
static bool mm_get_managed_objects(struct impl *this)
|
||||
{
|
||||
spa_autoptr(DBusMessage) m = dbus_message_new_method_call(MM_DBUS_SERVICE,
|
||||
"/org/freedesktop/ModemManager1",
|
||||
DBUS_INTERFACE_OBJECTMANAGER,
|
||||
"GetManagedObjects");
|
||||
if (m == NULL)
|
||||
return false;
|
||||
|
||||
dbus_message_set_auto_start(m, false);
|
||||
|
||||
this->pending = send_with_reply(this->conn, m, mm_get_managed_objects_reply, this);
|
||||
if (!this->pending) {
|
||||
spa_log_error(this->log, "dbus call failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void call_free(struct call *call)
|
||||
{
|
||||
spa_list_remove(&call->link);
|
||||
|
|
@ -488,8 +514,12 @@ static DBusHandlerResult mm_filter_cb(DBusConnection *bus, DBusMessage *m, void
|
|||
mm_clean_modem(this);
|
||||
}
|
||||
|
||||
if (new_owner && *new_owner)
|
||||
if (new_owner && *new_owner) {
|
||||
spa_log_debug(this->log, "ModemManager daemon appeared (%s)", new_owner);
|
||||
|
||||
if (!mm_get_managed_objects(this))
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
} else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECTMANAGER, DBUS_SIGNAL_INTERFACES_ADDED)) {
|
||||
DBusMessageIter arg_i;
|
||||
|
|
@ -916,8 +946,13 @@ bool mm_do_call(void *modemmanager, const char* number, void *user_data, enum cm
|
|||
spa_autofree struct dbus_cmd_data *data = NULL;
|
||||
spa_autoptr(DBusMessage) m = NULL;
|
||||
DBusMessageIter iter, dict;
|
||||
size_t i = 0;
|
||||
|
||||
for (size_t i = 0; number[i]; i++) {
|
||||
/* Allow memory dial for PTS tests HFP/AG/OCM/BV-01-C and HFP/AG/OCM/BV-02-C */
|
||||
if (this->pts && number[0] == '>')
|
||||
i++;
|
||||
|
||||
for (; number[i]; i++) {
|
||||
if (!is_valid_dial_string_char(number[i])) {
|
||||
spa_log_warn(this->log, "Call creation canceled, invalid character found in dial string: %c", number[i]);
|
||||
if (error)
|
||||
|
|
@ -1050,6 +1085,8 @@ void *mm_register(struct spa_log *log, void *dbus_connection, const struct spa_d
|
|||
{
|
||||
const char *modem_device_str = NULL;
|
||||
bool modem_device_found = false;
|
||||
const char *pts_str = NULL;
|
||||
bool pts = false;
|
||||
|
||||
spa_assert(log);
|
||||
spa_assert(dbus_connection);
|
||||
|
|
@ -1059,6 +1096,9 @@ void *mm_register(struct spa_log *log, void *dbus_connection, const struct spa_d
|
|||
if (!spa_streq(modem_device_str, "none"))
|
||||
modem_device_found = true;
|
||||
}
|
||||
if ((pts_str = spa_dict_lookup(info, "bluez5.hfphsp-backend-native-pts")) != NULL) {
|
||||
pts = spa_atob(pts_str);
|
||||
}
|
||||
}
|
||||
if (!modem_device_found) {
|
||||
spa_log_info(log, "No modem allowed, doesn't link to ModemManager");
|
||||
|
|
@ -1076,25 +1116,14 @@ void *mm_register(struct spa_log *log, void *dbus_connection, const struct spa_d
|
|||
if (modem_device_str && !spa_streq(modem_device_str, "any"))
|
||||
this->allowed_modem_device = strdup(modem_device_str);
|
||||
spa_list_init(&this->call_list);
|
||||
this->pts = pts;
|
||||
|
||||
if (add_filters(this) < 0)
|
||||
return NULL;
|
||||
|
||||
spa_autoptr(DBusMessage) m = dbus_message_new_method_call(MM_DBUS_SERVICE,
|
||||
"/org/freedesktop/ModemManager1",
|
||||
DBUS_INTERFACE_OBJECTMANAGER,
|
||||
"GetManagedObjects");
|
||||
if (m == NULL)
|
||||
if (!mm_get_managed_objects(this))
|
||||
return NULL;
|
||||
|
||||
dbus_message_set_auto_start(m, false);
|
||||
|
||||
this->pending = send_with_reply(this->conn, m, mm_get_managed_objects_reply, this);
|
||||
if (!this->pending) {
|
||||
spa_log_error(this->log, "dbus call failure");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return spa_steal_ptr(this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -247,11 +247,12 @@ static void update_properties(struct impl *impl, bool send_signal)
|
|||
|
||||
struct spa_bt_player *spa_bt_player_new(void *dbus_connection, struct spa_log *log)
|
||||
{
|
||||
struct impl *impl;
|
||||
const DBusObjectPathVTable vtable = {
|
||||
static const DBusObjectPathVTable vtable = {
|
||||
.message_function = player_handler,
|
||||
};
|
||||
|
||||
struct impl *impl;
|
||||
|
||||
spa_log_topic_init(log, &log_topic);
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue