Compare commits

..

No commits in common. "master" and "1.5.81" have entirely different histories.

236 changed files with 2564 additions and 10244 deletions

View file

@ -38,7 +38,7 @@ include:
.fedora:
variables:
# Update this tag when you want to trigger a rebuild
FDO_DISTRIBUTION_TAG: '2025-10-22.0'
FDO_DISTRIBUTION_TAG: '2025-10-15.0'
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_PACKAGES: >-
alsa-lib-devel
@ -48,7 +48,6 @@ include:
dbus-devel
doxygen
fdk-aac-free-devel
file
findutils
gcc
gcc-c++

128
NEWS
View file

@ -1,121 +1,3 @@
# 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
@ -284,11 +166,8 @@ also contains some new features:
## Bluetooth
- Telephony improvements.
- ASHA support was added.
- 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)
- Add packet loss correction with spandsp for some codecs.
- Synchronisation between ISO streams in the same group.
## Pulse-server
- The SUSPEND event is now correctly generated. fail-on-suspend is
@ -314,6 +193,9 @@ 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

View file

@ -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. Deprecated and unused.
- `SPA_TYPE_Bitmap`: An array of bits.
POD's can be grouped together in these container types:
@ -435,11 +435,10 @@ 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 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.
`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:
\code{.c}
struct spa_pod *value;

View file

@ -59,14 +59,6 @@ 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

View file

@ -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_ID
\copydoc PW_KEY_DEVICE_PRODUCT_NAME
\endparblock
@PAR@ device-prop device.product.name # string
\parblock
\copydoc PW_KEY_DEVICE_PRODUCT_NAME
\copydoc PW_KEY_DEVICE_PRODUCT_ID
\endparblock
@PAR@ device-prop device.class # string
@ -547,137 +547,17 @@ 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 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.
(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.
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.
@ -963,9 +843,6 @@ 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.
@ -1138,15 +1015,6 @@ 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
@ -1266,18 +1134,6 @@ 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

View file

@ -1130,8 +1130,6 @@ 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

View file

@ -1,5 +1,5 @@
project('pipewire', ['c' ],
version : '1.5.84',
version : '1.5.81',
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
meson_version : '>= 0.61.1',
default_options : [ 'warning_level=3',
@ -115,11 +115,10 @@ 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')
add_project_arguments(cc_native.get_supported_arguments(cc_flags),
language: 'c', native: true)
cc_flags_native = cc_native.get_supported_arguments(cc_flags)
have_cpp = add_languages('cpp', native: false, required : false)

View file

@ -22,11 +22,9 @@ 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[MAX_CHANNELS];
long values[SPA_AUDIO_MAX_CHANNELS];
};
typedef struct {
@ -500,7 +498,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[MAX_CHANNELS];
float volumes[SPA_AUDIO_MAX_CHANNELS];
uint32_t i, n_volumes = 0;
n_volumes = volume->channels;
@ -852,11 +850,11 @@ static void parse_props(struct global *g, const struct spa_pod *param, bool devi
break;
case SPA_PROP_channelVolumes:
{
float volumes[MAX_CHANNELS];
float volumes[SPA_AUDIO_MAX_CHANNELS];
uint32_t n_volumes, i;
n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
volumes, SPA_N_ELEMENTS(volumes));
volumes, SPA_AUDIO_MAX_CHANNELS);
g->node.channel_volume.channels = n_volumes;
for (i = 0; i < n_volumes; i++)

View file

@ -31,7 +31,6 @@ 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
@ -643,7 +642,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[MAX_CHANNELS])
static int set_default_channels(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS])
{
switch (channels) {
case 8:
@ -916,16 +915,12 @@ 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_type_audio_channel_make_short_name(position[i],
buf, sizeof(buf), "UNK"));
spa_debug_type_find_short_name(spa_type_audio_channel,
position[i]));
}
return 1;
}
@ -1102,7 +1097,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, MAX_CHANNELS }, 2, collect_int },
{ 1, SPA_AUDIO_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,

View file

@ -4299,7 +4299,6 @@ 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
View file

@ -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-12-04 15:34+0000\n"
"PO-Revision-Date: 2025-12-07 08:53+0100\n"
"POT-Creation-Date: 2025-09-11 03:34+0000\n"
"PO-Revision-Date: 2025-09-11 11:47+0200\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:761
#: src/modules/module-pulse-tunnel.c:760
#, 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:1103
#: src/tools/pw-cat.c:1084
#, c-format
msgid ""
"%s [options] [<file>|-]\n"
@ -92,7 +92,7 @@ msgstr ""
"\n"
"</file>\n"
#: src/tools/pw-cat.c:1110
#: src/tools/pw-cat.c:1091
#, 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:1128
#: src/tools/pw-cat.c:1109
#, 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\", \"5.1\",... "
"or\n"
" one of: \"stereo\", "
"\"surround-51\",... 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\", "
"\"5.1\",... ali\n"
" Ena izmed: \"stereo\", "
"\"surround-51\",... 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:1148
#: src/tools/pw-cat.c:1129
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:361
#: spa/plugins/alsa/acp/acp.c:351
msgid "Pro Audio"
msgstr "Profesionalni zvok"
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:1976
#: spa/plugins/alsa/acp/acp.c:527 spa/plugins/alsa/acp/alsa-mixer.c:4635
#: spa/plugins/bluez5/bluez5-device.c:1974
msgid "Off"
msgstr "Izklopljeno"
#: spa/plugins/alsa/acp/acp.c:620
#: spa/plugins/alsa/acp/acp.c:610
#, c-format
msgid "%s [ALSA UCM error]"
msgstr "%s [napaka ALSA UCM]"
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
#: spa/plugins/alsa/acp/alsa-mixer.c:2652
msgid "Input"
msgstr "Vhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
#: spa/plugins/alsa/acp/alsa-mixer.c:2653
msgid "Docking Station Input"
msgstr "Vhod priklopne postaje"
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
#: spa/plugins/alsa/acp/alsa-mixer.c:2654
msgid "Docking Station Microphone"
msgstr "Mikrofon priklopne postaje"
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
#: spa/plugins/alsa/acp/alsa-mixer.c:2655
msgid "Docking Station Line In"
msgstr "Linijski vhod priklopne postaje"
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
#: spa/plugins/alsa/acp/alsa-mixer.c:2656
#: spa/plugins/alsa/acp/alsa-mixer.c:2747
msgid "Line In"
msgstr "Linijski vhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2374
#: spa/plugins/alsa/acp/alsa-mixer.c:2657
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2372
msgid "Microphone"
msgstr "Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
#: spa/plugins/alsa/acp/alsa-mixer.c:2658
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
msgid "Front Microphone"
msgstr "Sprednji mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
#: spa/plugins/alsa/acp/alsa-mixer.c:2659
#: spa/plugins/alsa/acp/alsa-mixer.c:2743
msgid "Rear Microphone"
msgstr "Zadnji mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
#: spa/plugins/alsa/acp/alsa-mixer.c:2660
msgid "External Microphone"
msgstr "Zunanji mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
#: spa/plugins/alsa/acp/alsa-mixer.c:2661
#: spa/plugins/alsa/acp/alsa-mixer.c:2745
msgid "Internal Microphone"
msgstr "Notranji mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2731
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
#: spa/plugins/alsa/acp/alsa-mixer.c:2662
#: spa/plugins/alsa/acp/alsa-mixer.c:2748
msgid "Radio"
msgstr "Radio"
#: spa/plugins/alsa/acp/alsa-mixer.c:2732
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
#: spa/plugins/alsa/acp/alsa-mixer.c:2663
#: spa/plugins/alsa/acp/alsa-mixer.c:2749
msgid "Video"
msgstr "Video"
#: spa/plugins/alsa/acp/alsa-mixer.c:2733
#: spa/plugins/alsa/acp/alsa-mixer.c:2664
msgid "Automatic Gain Control"
msgstr "Samodejni nadzor ojačanja"
#: spa/plugins/alsa/acp/alsa-mixer.c:2734
#: spa/plugins/alsa/acp/alsa-mixer.c:2665
msgid "No Automatic Gain Control"
msgstr "Brez samodejnega nadzora ojačanja"
#: spa/plugins/alsa/acp/alsa-mixer.c:2735
#: spa/plugins/alsa/acp/alsa-mixer.c:2666
msgid "Boost"
msgstr "Ojačitev"
#: spa/plugins/alsa/acp/alsa-mixer.c:2736
#: spa/plugins/alsa/acp/alsa-mixer.c:2667
msgid "No Boost"
msgstr "Brez ojačitve"
#: spa/plugins/alsa/acp/alsa-mixer.c:2737
#: spa/plugins/alsa/acp/alsa-mixer.c:2668
msgid "Amplifier"
msgstr "Ojačevalnik"
#: spa/plugins/alsa/acp/alsa-mixer.c:2738
#: spa/plugins/alsa/acp/alsa-mixer.c:2669
msgid "No Amplifier"
msgstr "Brez ojačevalnika"
#: spa/plugins/alsa/acp/alsa-mixer.c:2739
#: spa/plugins/alsa/acp/alsa-mixer.c:2670
msgid "Bass Boost"
msgstr "Ojačitev nizkih tonov"
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
#: spa/plugins/alsa/acp/alsa-mixer.c:2671
msgid "No Bass Boost"
msgstr "Brez ojačitve nizkih tonov"
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2380
#: spa/plugins/alsa/acp/alsa-mixer.c:2672
#: spa/plugins/bluez5/bluez5-device.c:2378
msgid "Speaker"
msgstr "Zvočnik"
#. 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
#: 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
msgid "Headphones"
msgstr "Slušalke"
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "Analog Input"
msgstr "Analogni vhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
#: spa/plugins/alsa/acp/alsa-mixer.c:2744
msgid "Dock Microphone"
msgstr "Priklopni mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
#: spa/plugins/alsa/acp/alsa-mixer.c:2746
msgid "Headset Microphone"
msgstr "Mikrofon s slušalkami"
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
#: spa/plugins/alsa/acp/alsa-mixer.c:2750
msgid "Analog Output"
msgstr "Analogni izhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
#: spa/plugins/alsa/acp/alsa-mixer.c:2752
msgid "Headphones 2"
msgstr "Slušalke 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
#: spa/plugins/alsa/acp/alsa-mixer.c:2753
msgid "Headphones Mono Output"
msgstr "Mono izhod slušalk"
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
#: spa/plugins/alsa/acp/alsa-mixer.c:2754
msgid "Line Out"
msgstr "Linijski izhod"
msgstr "Linijsk izhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2824
#: spa/plugins/alsa/acp/alsa-mixer.c:2755
msgid "Analog Mono Output"
msgstr "Analogni mono izhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2825
#: spa/plugins/alsa/acp/alsa-mixer.c:2756
msgid "Speakers"
msgstr "Zvočniki"
msgstr "Govorniki"
#: spa/plugins/alsa/acp/alsa-mixer.c:2826
#: spa/plugins/alsa/acp/alsa-mixer.c:2757
msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2827
#: spa/plugins/alsa/acp/alsa-mixer.c:2758
msgid "Digital Output (S/PDIF)"
msgstr "Digitalni izhod (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2828
#: spa/plugins/alsa/acp/alsa-mixer.c:2759
msgid "Digital Input (S/PDIF)"
msgstr "Digitalni vhod (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2829
#: spa/plugins/alsa/acp/alsa-mixer.c:2760
msgid "Multichannel Input"
msgstr "Večkanalni vhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2830
#: spa/plugins/alsa/acp/alsa-mixer.c:2761
msgid "Multichannel Output"
msgstr "Večkanalni izhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2831
#: spa/plugins/alsa/acp/alsa-mixer.c:2762
msgid "Game Output"
msgstr "Vhod igre"
#: spa/plugins/alsa/acp/alsa-mixer.c:2832
#: spa/plugins/alsa/acp/alsa-mixer.c:2833
#: spa/plugins/alsa/acp/alsa-mixer.c:2763
#: spa/plugins/alsa/acp/alsa-mixer.c:2764
msgid "Chat Output"
msgstr "Izhod klepeta"
#: spa/plugins/alsa/acp/alsa-mixer.c:2834
#: spa/plugins/alsa/acp/alsa-mixer.c:2765
msgid "Chat Input"
msgstr "Vhod klepeta"
#: spa/plugins/alsa/acp/alsa-mixer.c:2835
#: spa/plugins/alsa/acp/alsa-mixer.c:2766
msgid "Virtual Surround 7.1"
msgstr "Navidezni prostorski zvok 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4522
#: spa/plugins/alsa/acp/alsa-mixer.c:4458
msgid "Analog Mono"
msgstr "Analogni mono"
#: spa/plugins/alsa/acp/alsa-mixer.c:4523
#: spa/plugins/alsa/acp/alsa-mixer.c:4459
msgid "Analog Mono (Left)"
msgstr "Analogni mono (levo)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4524
#: spa/plugins/alsa/acp/alsa-mixer.c:4460
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:4525
#: spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4534
#: spa/plugins/alsa/acp/alsa-mixer.c:4461
#: spa/plugins/alsa/acp/alsa-mixer.c:4469
#: spa/plugins/alsa/acp/alsa-mixer.c:4470
msgid "Analog Stereo"
msgstr "Analogni stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4526
#: spa/plugins/alsa/acp/alsa-mixer.c:4462
msgid "Mono"
msgstr "Mono"
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
#: spa/plugins/alsa/acp/alsa-mixer.c:4463
msgid "Stereo"
msgstr "Stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2362
#: spa/plugins/alsa/acp/alsa-mixer.c:4471
#: spa/plugins/alsa/acp/alsa-mixer.c:4629
#: spa/plugins/bluez5/bluez5-device.c:2360
msgid "Headset"
msgstr "Slušalka"
#: spa/plugins/alsa/acp/alsa-mixer.c:4536
#: spa/plugins/alsa/acp/alsa-mixer.c:4694
#: spa/plugins/alsa/acp/alsa-mixer.c:4472
#: spa/plugins/alsa/acp/alsa-mixer.c:4630
msgid "Speakerphone"
msgstr "Zvočnik telefona"
#: spa/plugins/alsa/acp/alsa-mixer.c:4537
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
#: spa/plugins/alsa/acp/alsa-mixer.c:4473
#: spa/plugins/alsa/acp/alsa-mixer.c:4474
msgid "Multichannel"
msgstr "Večkanalno"
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
#: spa/plugins/alsa/acp/alsa-mixer.c:4475
msgid "Analog Surround 2.1"
msgstr "Analogni prostorski zvok 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
#: spa/plugins/alsa/acp/alsa-mixer.c:4476
msgid "Analog Surround 3.0"
msgstr "Analogni prostorski zvok 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
#: spa/plugins/alsa/acp/alsa-mixer.c:4477
msgid "Analog Surround 3.1"
msgstr "Analogni prostorski zvok 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
#: spa/plugins/alsa/acp/alsa-mixer.c:4478
msgid "Analog Surround 4.0"
msgstr "Analogni prostorski zvok 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
#: spa/plugins/alsa/acp/alsa-mixer.c:4479
msgid "Analog Surround 4.1"
msgstr "Analogni prostorski zvok 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
#: spa/plugins/alsa/acp/alsa-mixer.c:4480
msgid "Analog Surround 5.0"
msgstr "Analogni prostorski zvok 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
#: spa/plugins/alsa/acp/alsa-mixer.c:4481
msgid "Analog Surround 5.1"
msgstr "Analogni prostorski zvok 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
#: spa/plugins/alsa/acp/alsa-mixer.c:4482
msgid "Analog Surround 6.0"
msgstr "Analogni prostorski zvok 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
#: spa/plugins/alsa/acp/alsa-mixer.c:4483
msgid "Analog Surround 6.1"
msgstr "Analogni prostorski zvok 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
#: spa/plugins/alsa/acp/alsa-mixer.c:4484
msgid "Analog Surround 7.0"
msgstr "Analogni prostorski zvok 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
#: spa/plugins/alsa/acp/alsa-mixer.c:4485
msgid "Analog Surround 7.1"
msgstr "Analogni prostorski zvok 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
#: spa/plugins/alsa/acp/alsa-mixer.c:4486
msgid "Digital Stereo (IEC958)"
msgstr "Digitalni stereo (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
#: spa/plugins/alsa/acp/alsa-mixer.c:4487
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Digitalni prostorski zvok 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
#: spa/plugins/alsa/acp/alsa-mixer.c:4488
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Digitalni prostorski zvok 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
#: spa/plugins/alsa/acp/alsa-mixer.c:4489
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "Digitalni prostorski zvok 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
#: spa/plugins/alsa/acp/alsa-mixer.c:4490
msgid "Digital Stereo (HDMI)"
msgstr "Digitalni stereo (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
#: spa/plugins/alsa/acp/alsa-mixer.c:4491
msgid "Digital Surround 5.1 (HDMI)"
msgstr "Digitalni prostorski zvok 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
#: spa/plugins/alsa/acp/alsa-mixer.c:4492
msgid "Chat"
msgstr "Klepet"
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
#: spa/plugins/alsa/acp/alsa-mixer.c:4493
msgid "Game"
msgstr "Igra"
#: spa/plugins/alsa/acp/alsa-mixer.c:4691
#: spa/plugins/alsa/acp/alsa-mixer.c:4627
msgid "Analog Mono Duplex"
msgstr "Analogni mono dupleks"
#: spa/plugins/alsa/acp/alsa-mixer.c:4692
#: spa/plugins/alsa/acp/alsa-mixer.c:4628
msgid "Analog Stereo Duplex"
msgstr "Analogni stereo dupleks"
#: spa/plugins/alsa/acp/alsa-mixer.c:4695
#: spa/plugins/alsa/acp/alsa-mixer.c:4631
msgid "Digital Stereo Duplex (IEC958)"
msgstr "Digitalni stereo dupleks (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
#: spa/plugins/alsa/acp/alsa-mixer.c:4632
msgid "Multichannel Duplex"
msgstr "Večkanalni dupleks"
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
#: spa/plugins/alsa/acp/alsa-mixer.c:4633
msgid "Stereo Duplex"
msgstr "Stereo dupleks"
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
#: spa/plugins/alsa/acp/alsa-mixer.c:4634
msgid "Mono Chat + 7.1 Surround"
msgstr "Mono klepet + prostorski zvok 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4799
#: spa/plugins/alsa/acp/alsa-mixer.c:4735
#, c-format
msgid "%s Output"
msgstr "Izhod %s"
#: spa/plugins/alsa/acp/alsa-mixer.c:4807
#: spa/plugins/alsa/acp/alsa-mixer.c:4743
#, 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:460
#: spa/plugins/alsa/acp/channelmap.h:457
msgid "(invalid)"
msgstr "(neveljavno)"
@ -680,100 +680,100 @@ msgstr "Vgrajen zvok"
msgid "Modem"
msgstr "Modem"
#: spa/plugins/bluez5/bluez5-device.c:1987
#: spa/plugins/bluez5/bluez5-device.c:1985
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "Zvožni prehod (vir A2DP in HSP/HFP AG)"
#: spa/plugins/bluez5/bluez5-device.c:2016
#: spa/plugins/bluez5/bluez5-device.c:2014
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "Pretakanje zvoka za slušne aparate (ponor ASHA)"
#: spa/plugins/bluez5/bluez5-device.c:2059
#: spa/plugins/bluez5/bluez5-device.c:2057
#, 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:2062
#: spa/plugins/bluez5/bluez5-device.c:2060
#, 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:2070
#: spa/plugins/bluez5/bluez5-device.c:2068
msgid "High Fidelity Playback (A2DP Sink)"
msgstr "Predvajanje visoke ločljivosti (ponor A2DP)"
#: spa/plugins/bluez5/bluez5-device.c:2072
#: spa/plugins/bluez5/bluez5-device.c:2070
msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP)"
#: spa/plugins/bluez5/bluez5-device.c:2146
#: spa/plugins/bluez5/bluez5-device.c:2144
#, 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:2151
#: spa/plugins/bluez5/bluez5-device.c:2149
#, 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:2155
#: spa/plugins/bluez5/bluez5-device.c:2153
#, 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:2164
#: spa/plugins/bluez5/bluez5-device.c:2162
msgid "High Fidelity Playback (BAP Sink)"
msgstr "Predvajanje visoke ločljivosti (ponor BAP)"
#: spa/plugins/bluez5/bluez5-device.c:2168
#: spa/plugins/bluez5/bluez5-device.c:2166
msgid "High Fidelity Input (BAP Source)"
msgstr "Vhod visoke ločljivosti (vir BAP)"
#: spa/plugins/bluez5/bluez5-device.c:2171
#: spa/plugins/bluez5/bluez5-device.c:2169
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "Dupleks visoke ločljivosti (vir/ponor BAP)"
#: spa/plugins/bluez5/bluez5-device.c:2211
#: spa/plugins/bluez5/bluez5-device.c:2209
#, 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: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
#: 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
msgid "Handsfree"
msgstr "Prostoročno telefoniranje"
#: spa/plugins/bluez5/bluez5-device.c:2369
#: spa/plugins/bluez5/bluez5-device.c:2367
msgid "Handsfree (HFP)"
msgstr "Prostoročno telefoniranje (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:2392
#: spa/plugins/bluez5/bluez5-device.c:2390
msgid "Portable"
msgstr "Prenosna naprava"
#: spa/plugins/bluez5/bluez5-device.c:2398
#: spa/plugins/bluez5/bluez5-device.c:2396
msgid "Car"
msgstr "Avtomobil"
#: spa/plugins/bluez5/bluez5-device.c:2404
#: spa/plugins/bluez5/bluez5-device.c:2402
msgid "HiFi"
msgstr "HiFi"
#: spa/plugins/bluez5/bluez5-device.c:2410
#: spa/plugins/bluez5/bluez5-device.c:2408
msgid "Phone"
msgstr "Telefon"
#: spa/plugins/bluez5/bluez5-device.c:2417
#: spa/plugins/bluez5/bluez5-device.c:2415
msgid "Bluetooth"
msgstr "Bluetooth"
#: spa/plugins/bluez5/bluez5-device.c:2418
msgid "Bluetooth Handsfree"
msgstr "Bluetooth - prostoročno"
#: spa/plugins/bluez5/bluez5-device.c:2416
msgid "Bluetooth (HFP)"
msgstr "Bluetooth (HFP)"

341
po/tr.po
View file

@ -1,51 +1,46 @@
# Turkish translation for PipeWire.
# Copyright (C) 2014-2025 PipeWire's COPYRIGHT HOLDER
# Copyright (C) 2014-2024 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 <yakushabb@gmail.com>, 2024, 2025.
# Sabri Ünal <libreajans@gmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: PipeWire master\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"
"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"
"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.8\n"
"X-Generator: Poedit 3.4.2\n"
#: src/daemon/pipewire.c:29
#: src/daemon/pipewire.c:26
#, 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:3
#: src/daemon/pipewire.desktop.in:4
msgid "PipeWire Media System"
msgstr "PipeWire Ortam Sistemi"
#: src/daemon/pipewire.desktop.in:4
#: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System"
msgstr "PipeWire Ortam Sistemini Başlat"
@ -59,26 +54,26 @@ msgstr "%s%s%s tüneli"
msgid "Dummy Output"
msgstr "Temsili Çıkış"
#: src/modules/module-pulse-tunnel.c:760
#: src/modules/module-pulse-tunnel.c:774
#, c-format
msgid "Tunnel for %s@%s"
msgstr "%s@%s için tünel"
#: src/modules/module-zeroconf-discover.c:320
#: src/modules/module-zeroconf-discover.c:315
msgid "Unknown device"
msgstr "Bilinmeyen aygıt"
#: src/modules/module-zeroconf-discover.c:332
#: src/modules/module-zeroconf-discover.c:327
#, c-format
msgid "%s on %s@%s"
msgstr "%s, %s@%s"
#: src/modules/module-zeroconf-discover.c:336
#: src/modules/module-zeroconf-discover.c:331
#, c-format
msgid "%s on %s"
msgstr "%s, %s"
#: src/tools/pw-cat.c:1096
#: src/tools/pw-cat.c:991
#, c-format
msgid ""
"%s [options] [<file>|-]\n"
@ -93,7 +88,7 @@ msgstr ""
" -v, --verbose Ayrıntılı işlemleri etkinleştir\n"
"\n"
#: src/tools/pw-cat.c:1103
#: src/tools/pw-cat.c:998
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
@ -127,7 +122,7 @@ msgstr ""
" -P --properties Düğüm özelliklerini ayarla\n"
"\n"
#: src/tools/pw-cat.c:1121
#: src/tools/pw-cat.c:1016
#, c-format
msgid ""
" --rate Sample rate (req. for rec) (default "
@ -144,10 +139,6 @@ 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) "
@ -165,21 +156,15 @@ 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:1141
#: src/tools/pw-cat.c:1033
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"
@ -187,11 +172,9 @@ 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:2386
#: src/tools/pw-cli.c:2252
#, c-format
msgid ""
"%s [options] [command]\n"
@ -210,203 +193,195 @@ msgstr ""
" -r, --remote Uzak arka plan programı adı\n"
" -m, --monitor Etkinliği izle\n"
#: spa/plugins/alsa/acp/acp.c:361
#: spa/plugins/alsa/acp/acp.c:327
msgid "Pro Audio"
msgstr "Profesyonel Ses"
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:1976
#: spa/plugins/alsa/acp/acp.c:488 spa/plugins/alsa/acp/alsa-mixer.c:4633
#: spa/plugins/bluez5/bluez5-device.c:1701
msgid "Off"
msgstr "Kapalı"
#: 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
#: spa/plugins/alsa/acp/alsa-mixer.c:2652
msgid "Input"
msgstr "Giriş"
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
#: spa/plugins/alsa/acp/alsa-mixer.c:2653
msgid "Docking Station Input"
msgstr "Yerleştirme İstasyonu Girişi"
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
#: spa/plugins/alsa/acp/alsa-mixer.c:2654
msgid "Docking Station Microphone"
msgstr "Yerleştirme İstasyonu Mikrofonu"
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
#: spa/plugins/alsa/acp/alsa-mixer.c:2655
msgid "Docking Station Line In"
msgstr "Yerleştirme İstasyonu Hat Girişi"
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
#: spa/plugins/alsa/acp/alsa-mixer.c:2656
#: spa/plugins/alsa/acp/alsa-mixer.c:2747
msgid "Line In"
msgstr "Hat Girişi"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2374
#: spa/plugins/alsa/acp/alsa-mixer.c:2657
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:1989
msgid "Microphone"
msgstr "Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
#: spa/plugins/alsa/acp/alsa-mixer.c:2658
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
msgid "Front Microphone"
msgstr "Ön Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
#: spa/plugins/alsa/acp/alsa-mixer.c:2659
#: spa/plugins/alsa/acp/alsa-mixer.c:2743
msgid "Rear Microphone"
msgstr "Arka Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
#: spa/plugins/alsa/acp/alsa-mixer.c:2660
msgid "External Microphone"
msgstr "Harici Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
#: spa/plugins/alsa/acp/alsa-mixer.c:2661
#: spa/plugins/alsa/acp/alsa-mixer.c:2745
msgid "Internal Microphone"
msgstr "Dahili Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2731
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
#: spa/plugins/alsa/acp/alsa-mixer.c:2662
#: spa/plugins/alsa/acp/alsa-mixer.c:2748
msgid "Radio"
msgstr "Radyo"
#: spa/plugins/alsa/acp/alsa-mixer.c:2732
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
#: spa/plugins/alsa/acp/alsa-mixer.c:2663
#: spa/plugins/alsa/acp/alsa-mixer.c:2749
msgid "Video"
msgstr "Video"
#: spa/plugins/alsa/acp/alsa-mixer.c:2733
#: spa/plugins/alsa/acp/alsa-mixer.c:2664
msgid "Automatic Gain Control"
msgstr "Otomatik Kazanç Denetimi"
#: spa/plugins/alsa/acp/alsa-mixer.c:2734
#: spa/plugins/alsa/acp/alsa-mixer.c:2665
msgid "No Automatic Gain Control"
msgstr "Otomatik Kazanç Denetimi Yok"
#: spa/plugins/alsa/acp/alsa-mixer.c:2735
#: spa/plugins/alsa/acp/alsa-mixer.c:2666
msgid "Boost"
msgstr "Artır"
#: spa/plugins/alsa/acp/alsa-mixer.c:2736
#: spa/plugins/alsa/acp/alsa-mixer.c:2667
msgid "No Boost"
msgstr "Artırma Yok"
#: spa/plugins/alsa/acp/alsa-mixer.c:2737
#: spa/plugins/alsa/acp/alsa-mixer.c:2668
msgid "Amplifier"
msgstr "Yükseltici"
#: spa/plugins/alsa/acp/alsa-mixer.c:2738
#: spa/plugins/alsa/acp/alsa-mixer.c:2669
msgid "No Amplifier"
msgstr "Yükseltici Yok"
#: spa/plugins/alsa/acp/alsa-mixer.c:2739
#: spa/plugins/alsa/acp/alsa-mixer.c:2670
msgid "Bass Boost"
msgstr "Bas Artır"
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
#: spa/plugins/alsa/acp/alsa-mixer.c:2671
msgid "No Bass Boost"
msgstr "Bas Artırma Yok"
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2380
#: spa/plugins/alsa/acp/alsa-mixer.c:2672
#: spa/plugins/bluez5/bluez5-device.c:1995
msgid "Speaker"
msgstr "Hoparlör"
#. 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
#: spa/plugins/alsa/acp/alsa-mixer.c:2673
#: spa/plugins/alsa/acp/alsa-mixer.c:2751
msgid "Headphones"
msgstr "Kulaklık"
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "Analog Input"
msgstr "Analog Giriş"
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
#: spa/plugins/alsa/acp/alsa-mixer.c:2744
msgid "Dock Microphone"
msgstr "Yapışık Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
#: spa/plugins/alsa/acp/alsa-mixer.c:2746
msgid "Headset Microphone"
msgstr "Mikrofonlu Kulaklık"
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
#: spa/plugins/alsa/acp/alsa-mixer.c:2750
msgid "Analog Output"
msgstr "Analog Çıkış"
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
#: spa/plugins/alsa/acp/alsa-mixer.c:2752
msgid "Headphones 2"
msgstr "Kulaklık 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
#: spa/plugins/alsa/acp/alsa-mixer.c:2753
msgid "Headphones Mono Output"
msgstr "Kulaklık Tek Kanallı Çıkış"
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
#: spa/plugins/alsa/acp/alsa-mixer.c:2754
msgid "Line Out"
msgstr "Hat Çıkışı"
#: spa/plugins/alsa/acp/alsa-mixer.c:2824
#: spa/plugins/alsa/acp/alsa-mixer.c:2755
msgid "Analog Mono Output"
msgstr "Analog Tek Kanallı Çıkış"
#: spa/plugins/alsa/acp/alsa-mixer.c:2825
#: spa/plugins/alsa/acp/alsa-mixer.c:2756
msgid "Speakers"
msgstr "Hoparlörler"
#: spa/plugins/alsa/acp/alsa-mixer.c:2826
#: spa/plugins/alsa/acp/alsa-mixer.c:2757
msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2827
#: spa/plugins/alsa/acp/alsa-mixer.c:2758
msgid "Digital Output (S/PDIF)"
msgstr "Sayısal Çıkış (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2828
#: spa/plugins/alsa/acp/alsa-mixer.c:2759
msgid "Digital Input (S/PDIF)"
msgstr "Sayısal Giriş (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2829
#: spa/plugins/alsa/acp/alsa-mixer.c:2760
msgid "Multichannel Input"
msgstr "Çok Kanallı Giriş"
#: spa/plugins/alsa/acp/alsa-mixer.c:2830
#: spa/plugins/alsa/acp/alsa-mixer.c:2761
msgid "Multichannel Output"
msgstr "Çok Kanallı Çıkış"
#: spa/plugins/alsa/acp/alsa-mixer.c:2831
#: spa/plugins/alsa/acp/alsa-mixer.c:2762
msgid "Game Output"
msgstr "Oyun Çıkışı"
#: spa/plugins/alsa/acp/alsa-mixer.c:2832
#: spa/plugins/alsa/acp/alsa-mixer.c:2833
#: spa/plugins/alsa/acp/alsa-mixer.c:2763
#: spa/plugins/alsa/acp/alsa-mixer.c:2764
msgid "Chat Output"
msgstr "Sohbet Çıkışı"
#: spa/plugins/alsa/acp/alsa-mixer.c:2834
#: spa/plugins/alsa/acp/alsa-mixer.c:2765
msgid "Chat Input"
msgstr "Sohbet Girişi"
#: spa/plugins/alsa/acp/alsa-mixer.c:2835
#: spa/plugins/alsa/acp/alsa-mixer.c:2766
msgid "Virtual Surround 7.1"
msgstr "Sanal Çevresel Ses 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4522
#: spa/plugins/alsa/acp/alsa-mixer.c:4456
msgid "Analog Mono"
msgstr "Analog Tek Kanallı"
#: spa/plugins/alsa/acp/alsa-mixer.c:4523
#: spa/plugins/alsa/acp/alsa-mixer.c:4457
msgid "Analog Mono (Left)"
msgstr "Analog Tek Kanallı (Sol)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4524
#: spa/plugins/alsa/acp/alsa-mixer.c:4458
msgid "Analog Mono (Right)"
msgstr "Analog Tek Kanallı (Sağ)"
@ -415,147 +390,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:4525
#: spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4534
#: spa/plugins/alsa/acp/alsa-mixer.c:4459
#: spa/plugins/alsa/acp/alsa-mixer.c:4467
#: spa/plugins/alsa/acp/alsa-mixer.c:4468
msgid "Analog Stereo"
msgstr "Analog Stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4526
#: spa/plugins/alsa/acp/alsa-mixer.c:4460
msgid "Mono"
msgstr "Tek Kanallı"
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
#: spa/plugins/alsa/acp/alsa-mixer.c:4461
msgid "Stereo"
msgstr "Stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2362
#: spa/plugins/alsa/acp/alsa-mixer.c:4469
#: spa/plugins/alsa/acp/alsa-mixer.c:4627
#: spa/plugins/bluez5/bluez5-device.c:1977
msgid "Headset"
msgstr "Kulaklık"
#: spa/plugins/alsa/acp/alsa-mixer.c:4536
#: spa/plugins/alsa/acp/alsa-mixer.c:4694
#: spa/plugins/alsa/acp/alsa-mixer.c:4470
#: spa/plugins/alsa/acp/alsa-mixer.c:4628
msgid "Speakerphone"
msgstr "Hoparlör"
#: spa/plugins/alsa/acp/alsa-mixer.c:4537
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
#: spa/plugins/alsa/acp/alsa-mixer.c:4471
#: spa/plugins/alsa/acp/alsa-mixer.c:4472
msgid "Multichannel"
msgstr "Çok kanallı"
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
#: spa/plugins/alsa/acp/alsa-mixer.c:4473
msgid "Analog Surround 2.1"
msgstr "Analog Çevresel Ses 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
#: spa/plugins/alsa/acp/alsa-mixer.c:4474
msgid "Analog Surround 3.0"
msgstr "Analog Çevresel Ses 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
#: spa/plugins/alsa/acp/alsa-mixer.c:4475
msgid "Analog Surround 3.1"
msgstr "Analog Çevresel Ses 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
#: spa/plugins/alsa/acp/alsa-mixer.c:4476
msgid "Analog Surround 4.0"
msgstr "Analog Çevresel Ses 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
#: spa/plugins/alsa/acp/alsa-mixer.c:4477
msgid "Analog Surround 4.1"
msgstr "Analog Çevresel Ses 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
#: spa/plugins/alsa/acp/alsa-mixer.c:4478
msgid "Analog Surround 5.0"
msgstr "Analog Çevresel Ses 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
#: spa/plugins/alsa/acp/alsa-mixer.c:4479
msgid "Analog Surround 5.1"
msgstr "Analog Çevresel Ses 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
#: spa/plugins/alsa/acp/alsa-mixer.c:4480
msgid "Analog Surround 6.0"
msgstr "Analog Çevresel Ses 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
#: spa/plugins/alsa/acp/alsa-mixer.c:4481
msgid "Analog Surround 6.1"
msgstr "Analog Çevresel Ses 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
#: spa/plugins/alsa/acp/alsa-mixer.c:4482
msgid "Analog Surround 7.0"
msgstr "Analog Çevresel Ses 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
#: spa/plugins/alsa/acp/alsa-mixer.c:4483
msgid "Analog Surround 7.1"
msgstr "Analog Çevresel Ses 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
#: spa/plugins/alsa/acp/alsa-mixer.c:4484
msgid "Digital Stereo (IEC958)"
msgstr "Sayısal Stereo (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
#: spa/plugins/alsa/acp/alsa-mixer.c:4485
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Sayısal Çevresel Ses 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
#: spa/plugins/alsa/acp/alsa-mixer.c:4486
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Sayısal Çevresel Ses 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
#: spa/plugins/alsa/acp/alsa-mixer.c:4487
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "Sayısal Çevresel Ses 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
#: spa/plugins/alsa/acp/alsa-mixer.c:4488
msgid "Digital Stereo (HDMI)"
msgstr "Sayısal Stereo (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
#: spa/plugins/alsa/acp/alsa-mixer.c:4489
msgid "Digital Surround 5.1 (HDMI)"
msgstr "Sayısal Çevresel Ses 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
#: spa/plugins/alsa/acp/alsa-mixer.c:4490
msgid "Chat"
msgstr "Sohbet"
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
#: spa/plugins/alsa/acp/alsa-mixer.c:4491
msgid "Game"
msgstr "Oyun"
#: spa/plugins/alsa/acp/alsa-mixer.c:4691
#: spa/plugins/alsa/acp/alsa-mixer.c:4625
msgid "Analog Mono Duplex"
msgstr "Analog Tek Kanallı İkili"
#: spa/plugins/alsa/acp/alsa-mixer.c:4692
#: spa/plugins/alsa/acp/alsa-mixer.c:4626
msgid "Analog Stereo Duplex"
msgstr "Analog İkili Stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4695
#: spa/plugins/alsa/acp/alsa-mixer.c:4629
msgid "Digital Stereo Duplex (IEC958)"
msgstr "Sayısal İkili Stereo (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
#: spa/plugins/alsa/acp/alsa-mixer.c:4630
msgid "Multichannel Duplex"
msgstr "Çok Kanallı İkili"
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
#: spa/plugins/alsa/acp/alsa-mixer.c:4631
msgid "Stereo Duplex"
msgstr "İkili Stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
#: spa/plugins/alsa/acp/alsa-mixer.c:4632
msgid "Mono Chat + 7.1 Surround"
msgstr "Tek Kanallı Sohbet + 7.1 Çevresel Ses"
#: spa/plugins/alsa/acp/alsa-mixer.c:4799
#: spa/plugins/alsa/acp/alsa-mixer.c:4733
#, c-format
msgid "%s Output"
msgstr "%s Çıkışı"
#: spa/plugins/alsa/acp/alsa-mixer.c:4807
#: spa/plugins/alsa/acp/alsa-mixer.c:4741
#, c-format
msgid "%s Input"
msgstr "%s Girişi"
#: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
#: spa/plugins/alsa/acp/alsa-util.c:1220 spa/plugins/alsa/acp/alsa-util.c:1314
#, c-format
msgid ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
@ -572,16 +547,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:1299
#: spa/plugins/alsa/acp/alsa-util.c:1286
#, 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] ""
@ -589,7 +564,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:1346
#: spa/plugins/alsa/acp/alsa-util.c:1333
#, c-format
msgid ""
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
@ -602,7 +577,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:1389
#: spa/plugins/alsa/acp/alsa-util.c:1376
#, c-format
msgid ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
@ -620,112 +595,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:460
#: spa/plugins/alsa/acp/channelmap.h:457
msgid "(invalid)"
msgstr "(geçersiz)"
#: spa/plugins/alsa/acp/compat.c:194
#: spa/plugins/alsa/acp/compat.c:193
msgid "Built-in Audio"
msgstr "Dahili Ses"
#: spa/plugins/alsa/acp/compat.c:199
#: spa/plugins/alsa/acp/compat.c:198
msgid "Modem"
msgstr "Modem"
#: spa/plugins/bluez5/bluez5-device.c:1987
#: spa/plugins/bluez5/bluez5-device.c:1712
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "Ses Geçidi (A2DP Kaynak & HSP/HFP AG)"
#: 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
#: spa/plugins/bluez5/bluez5-device.c:1760
#, 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:2062
#: spa/plugins/bluez5/bluez5-device.c:1763
#, 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:2070
#: spa/plugins/bluez5/bluez5-device.c:1771
msgid "High Fidelity Playback (A2DP Sink)"
msgstr "Yüksek Kaliteli Çalma (A2DP Alıcı)"
#: spa/plugins/bluez5/bluez5-device.c:2072
#: spa/plugins/bluez5/bluez5-device.c:1773
msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "Yüksek Kaliteli İkili (A2DP Kaynak/Alıcı)"
#: spa/plugins/bluez5/bluez5-device.c:2146
#: spa/plugins/bluez5/bluez5-device.c:1823
#, 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:2151
#: spa/plugins/bluez5/bluez5-device.c:1828
#, 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:2155
#: spa/plugins/bluez5/bluez5-device.c:1832
#, 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:2164
#: spa/plugins/bluez5/bluez5-device.c:1841
msgid "High Fidelity Playback (BAP Sink)"
msgstr "Yüksek Kaliteli Çalma (BAP Alıcı)"
#: spa/plugins/bluez5/bluez5-device.c:2168
#: spa/plugins/bluez5/bluez5-device.c:1845
msgid "High Fidelity Input (BAP Source)"
msgstr "Yüksek Kaliteli Giriş (BAP Kaynak)"
#: spa/plugins/bluez5/bluez5-device.c:2171
#: spa/plugins/bluez5/bluez5-device.c:1848
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "Yüksek Kaliteli İkili (BAP Kaynak/Alıcı)"
#: spa/plugins/bluez5/bluez5-device.c:2211
#: spa/plugins/bluez5/bluez5-device.c:1897
#, 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: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
#: 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
msgid "Handsfree"
msgstr "Ahizesiz"
#: spa/plugins/bluez5/bluez5-device.c:2369
#: spa/plugins/bluez5/bluez5-device.c:1984
msgid "Handsfree (HFP)"
msgstr "Ahizesiz (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:2392
#: spa/plugins/bluez5/bluez5-device.c:2001
msgid "Headphone"
msgstr "Kulaklık"
#: spa/plugins/bluez5/bluez5-device.c:2007
msgid "Portable"
msgstr "Taşınabilir"
#: spa/plugins/bluez5/bluez5-device.c:2398
#: spa/plugins/bluez5/bluez5-device.c:2013
msgid "Car"
msgstr "Araba"
#: spa/plugins/bluez5/bluez5-device.c:2404
#: spa/plugins/bluez5/bluez5-device.c:2019
msgid "HiFi"
msgstr "Yüksek Kalite"
#: spa/plugins/bluez5/bluez5-device.c:2410
#: spa/plugins/bluez5/bluez5-device.c:2025
msgid "Phone"
msgstr "Telefon"
#: spa/plugins/bluez5/bluez5-device.c:2417
#: spa/plugins/bluez5/bluez5-device.c:2032
msgid "Bluetooth"
msgstr "Bluetooth"
#: spa/plugins/bluez5/bluez5-device.c:2418
#: spa/plugins/bluez5/bluez5-device.c:2033
msgid "Bluetooth (HFP)"
msgstr "Bluetooth (HFP)"

View file

@ -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-11-25 15:35+0000\n"
"PO-Revision-Date: 2025-11-26 10:19+0800\n"
"POT-Creation-Date: 2025-09-21 15:33+0000\n"
"PO-Revision-Date: 2025-09-22 08:53+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:761
#: src/modules/module-pulse-tunnel.c:760
#, 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:1088
#: src/tools/pw-cat.c:1084
#, c-format
msgid ""
"%s [options] [<file>|-]\n"
@ -94,7 +94,7 @@ msgstr ""
" -v, --verbose 输出详细操作\n"
"\n"
#: src/tools/pw-cat.c:1095
#: src/tools/pw-cat.c:1091
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
@ -126,7 +126,7 @@ msgstr ""
" -P --properties 设置节点属性\n"
"\n"
#: src/tools/pw-cat.c:1113
#: src/tools/pw-cat.c:1109
#, 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\", \"5.1\",... "
"or\n"
" one of: \"stereo\", "
"\"surround-51\",... 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\", \"5.1\",... 中的其一"
"或\n"
" 以英文逗号分隔的通道名列表: 如 "
" \"stereo\", \"surround-51\",... "
"中的其一或\n"
" 以\",\"分隔的通道名列表: 如 "
"\"FL,FR\"\n"
" --format 采样格式 %s (录制模式需要) (默认 "
"%s)\n"
@ -166,7 +166,7 @@ msgstr ""
" -n, --sample-count COUNT 计数采样后停止\n"
"\n"
#: src/tools/pw-cat.c:1133
#: src/tools/pw-cat.c:1129
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:361
#: spa/plugins/alsa/acp/acp.c:351
msgid "Pro Audio"
msgstr "专业音频"
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:1976
#: spa/plugins/alsa/acp/acp.c:527 spa/plugins/alsa/acp/alsa-mixer.c:4635
#: spa/plugins/bluez5/bluez5-device.c:1974
msgid "Off"
msgstr "关"
#: spa/plugins/alsa/acp/acp.c:620
#: spa/plugins/alsa/acp/acp.c:610
#, c-format
msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM 错误]"
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
#: spa/plugins/alsa/acp/alsa-mixer.c:2652
msgid "Input"
msgstr "输入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
#: spa/plugins/alsa/acp/alsa-mixer.c:2653
msgid "Docking Station Input"
msgstr "扩展坞输入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
#: spa/plugins/alsa/acp/alsa-mixer.c:2654
msgid "Docking Station Microphone"
msgstr "扩展坞话筒"
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
#: spa/plugins/alsa/acp/alsa-mixer.c:2655
msgid "Docking Station Line In"
msgstr "扩展坞线输入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
#: spa/plugins/alsa/acp/alsa-mixer.c:2656
#: spa/plugins/alsa/acp/alsa-mixer.c:2747
msgid "Line In"
msgstr "输入插孔"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2374
#: spa/plugins/alsa/acp/alsa-mixer.c:2657
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2372
msgid "Microphone"
msgstr "话筒"
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
#: spa/plugins/alsa/acp/alsa-mixer.c:2658
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
msgid "Front Microphone"
msgstr "前麦克风"
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
#: spa/plugins/alsa/acp/alsa-mixer.c:2659
#: spa/plugins/alsa/acp/alsa-mixer.c:2743
msgid "Rear Microphone"
msgstr "后麦克风"
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
#: spa/plugins/alsa/acp/alsa-mixer.c:2660
msgid "External Microphone"
msgstr "外部话筒"
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
#: spa/plugins/alsa/acp/alsa-mixer.c:2661
#: spa/plugins/alsa/acp/alsa-mixer.c:2745
msgid "Internal Microphone"
msgstr "内部话筒"
#: spa/plugins/alsa/acp/alsa-mixer.c:2731
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
#: spa/plugins/alsa/acp/alsa-mixer.c:2662
#: spa/plugins/alsa/acp/alsa-mixer.c:2748
msgid "Radio"
msgstr "无线电"
#: spa/plugins/alsa/acp/alsa-mixer.c:2732
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
#: spa/plugins/alsa/acp/alsa-mixer.c:2663
#: spa/plugins/alsa/acp/alsa-mixer.c:2749
msgid "Video"
msgstr "视频"
#: spa/plugins/alsa/acp/alsa-mixer.c:2733
#: spa/plugins/alsa/acp/alsa-mixer.c:2664
msgid "Automatic Gain Control"
msgstr "自动增益控制"
#: spa/plugins/alsa/acp/alsa-mixer.c:2734
#: spa/plugins/alsa/acp/alsa-mixer.c:2665
msgid "No Automatic Gain Control"
msgstr "无自动增益控制"
#: spa/plugins/alsa/acp/alsa-mixer.c:2735
#: spa/plugins/alsa/acp/alsa-mixer.c:2666
msgid "Boost"
msgstr "增强"
#: spa/plugins/alsa/acp/alsa-mixer.c:2736
#: spa/plugins/alsa/acp/alsa-mixer.c:2667
msgid "No Boost"
msgstr "无增强"
#: spa/plugins/alsa/acp/alsa-mixer.c:2737
#: spa/plugins/alsa/acp/alsa-mixer.c:2668
msgid "Amplifier"
msgstr "功放"
#: spa/plugins/alsa/acp/alsa-mixer.c:2738
#: spa/plugins/alsa/acp/alsa-mixer.c:2669
msgid "No Amplifier"
msgstr "无功放"
#: spa/plugins/alsa/acp/alsa-mixer.c:2739
#: spa/plugins/alsa/acp/alsa-mixer.c:2670
msgid "Bass Boost"
msgstr "重低音增强"
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
#: spa/plugins/alsa/acp/alsa-mixer.c:2671
msgid "No Bass Boost"
msgstr "无重低音增强"
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2380
#: spa/plugins/alsa/acp/alsa-mixer.c:2672
#: spa/plugins/bluez5/bluez5-device.c:2378
msgid "Speaker"
msgstr "扬声器"
#. 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
#: 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
msgid "Headphones"
msgstr "模拟耳机"
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "Analog Input"
msgstr "模拟输入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
#: spa/plugins/alsa/acp/alsa-mixer.c:2744
msgid "Dock Microphone"
msgstr "扩展坞麦克风"
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
#: spa/plugins/alsa/acp/alsa-mixer.c:2746
msgid "Headset Microphone"
msgstr "头挂麦克风"
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
#: spa/plugins/alsa/acp/alsa-mixer.c:2750
msgid "Analog Output"
msgstr "模拟输出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
#: spa/plugins/alsa/acp/alsa-mixer.c:2752
msgid "Headphones 2"
msgstr "模拟耳机 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
#: spa/plugins/alsa/acp/alsa-mixer.c:2753
msgid "Headphones Mono Output"
msgstr "模拟单声道输出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
#: spa/plugins/alsa/acp/alsa-mixer.c:2754
msgid "Line Out"
msgstr "线缆输出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2824
#: spa/plugins/alsa/acp/alsa-mixer.c:2755
msgid "Analog Mono Output"
msgstr "模拟单声道输出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2825
#: spa/plugins/alsa/acp/alsa-mixer.c:2756
msgid "Speakers"
msgstr "扬声器"
#: spa/plugins/alsa/acp/alsa-mixer.c:2826
#: spa/plugins/alsa/acp/alsa-mixer.c:2757
msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2827
#: spa/plugins/alsa/acp/alsa-mixer.c:2758
msgid "Digital Output (S/PDIF)"
msgstr "数字输出 (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2828
#: spa/plugins/alsa/acp/alsa-mixer.c:2759
msgid "Digital Input (S/PDIF)"
msgstr "数字输入 (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2829
#: spa/plugins/alsa/acp/alsa-mixer.c:2760
msgid "Multichannel Input"
msgstr "多声道输入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2830
#: spa/plugins/alsa/acp/alsa-mixer.c:2761
msgid "Multichannel Output"
msgstr "多声道输出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2831
#: spa/plugins/alsa/acp/alsa-mixer.c:2762
msgid "Game Output"
msgstr "游戏输出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2832
#: spa/plugins/alsa/acp/alsa-mixer.c:2833
#: spa/plugins/alsa/acp/alsa-mixer.c:2763
#: spa/plugins/alsa/acp/alsa-mixer.c:2764
msgid "Chat Output"
msgstr "语音输出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2834
#: spa/plugins/alsa/acp/alsa-mixer.c:2765
msgid "Chat Input"
msgstr "语音输入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2835
#: spa/plugins/alsa/acp/alsa-mixer.c:2766
msgid "Virtual Surround 7.1"
msgstr "虚拟环绕 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4522
#: spa/plugins/alsa/acp/alsa-mixer.c:4458
msgid "Analog Mono"
msgstr "模拟单声道"
#: spa/plugins/alsa/acp/alsa-mixer.c:4523
#: spa/plugins/alsa/acp/alsa-mixer.c:4459
msgid "Analog Mono (Left)"
msgstr "模拟单声道 (左声道)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4524
#: spa/plugins/alsa/acp/alsa-mixer.c:4460
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:4525
#: spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4534
#: spa/plugins/alsa/acp/alsa-mixer.c:4461
#: spa/plugins/alsa/acp/alsa-mixer.c:4469
#: spa/plugins/alsa/acp/alsa-mixer.c:4470
msgid "Analog Stereo"
msgstr "模拟立体声"
#: spa/plugins/alsa/acp/alsa-mixer.c:4526
#: spa/plugins/alsa/acp/alsa-mixer.c:4462
msgid "Mono"
msgstr "单声道"
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
#: spa/plugins/alsa/acp/alsa-mixer.c:4463
msgid "Stereo"
msgstr "立体声"
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2362
#: spa/plugins/alsa/acp/alsa-mixer.c:4471
#: spa/plugins/alsa/acp/alsa-mixer.c:4629
#: spa/plugins/bluez5/bluez5-device.c:2360
msgid "Headset"
msgstr "耳机"
#: spa/plugins/alsa/acp/alsa-mixer.c:4536
#: spa/plugins/alsa/acp/alsa-mixer.c:4694
#: spa/plugins/alsa/acp/alsa-mixer.c:4472
#: spa/plugins/alsa/acp/alsa-mixer.c:4630
msgid "Speakerphone"
msgstr "扬声麦克风"
#: spa/plugins/alsa/acp/alsa-mixer.c:4537
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
#: spa/plugins/alsa/acp/alsa-mixer.c:4473
#: spa/plugins/alsa/acp/alsa-mixer.c:4474
msgid "Multichannel"
msgstr "多声道"
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
#: spa/plugins/alsa/acp/alsa-mixer.c:4475
msgid "Analog Surround 2.1"
msgstr "模拟环绕 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
#: spa/plugins/alsa/acp/alsa-mixer.c:4476
msgid "Analog Surround 3.0"
msgstr "模拟环绕 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
#: spa/plugins/alsa/acp/alsa-mixer.c:4477
msgid "Analog Surround 3.1"
msgstr "模拟环绕 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
#: spa/plugins/alsa/acp/alsa-mixer.c:4478
msgid "Analog Surround 4.0"
msgstr "模拟环绕 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
#: spa/plugins/alsa/acp/alsa-mixer.c:4479
msgid "Analog Surround 4.1"
msgstr "模拟环绕 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
#: spa/plugins/alsa/acp/alsa-mixer.c:4480
msgid "Analog Surround 5.0"
msgstr "模拟环绕 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
#: spa/plugins/alsa/acp/alsa-mixer.c:4481
msgid "Analog Surround 5.1"
msgstr "模拟环绕 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
#: spa/plugins/alsa/acp/alsa-mixer.c:4482
msgid "Analog Surround 6.0"
msgstr "模拟环绕 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
#: spa/plugins/alsa/acp/alsa-mixer.c:4483
msgid "Analog Surround 6.1"
msgstr "模拟环绕 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
#: spa/plugins/alsa/acp/alsa-mixer.c:4484
msgid "Analog Surround 7.0"
msgstr "模拟环绕 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
#: spa/plugins/alsa/acp/alsa-mixer.c:4485
msgid "Analog Surround 7.1"
msgstr "模拟环绕 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
#: spa/plugins/alsa/acp/alsa-mixer.c:4486
msgid "Digital Stereo (IEC958)"
msgstr "数字立体声 (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
#: spa/plugins/alsa/acp/alsa-mixer.c:4487
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "数字环绕 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
#: spa/plugins/alsa/acp/alsa-mixer.c:4488
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "数字环绕 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
#: spa/plugins/alsa/acp/alsa-mixer.c:4489
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "数字环绕 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
#: spa/plugins/alsa/acp/alsa-mixer.c:4490
msgid "Digital Stereo (HDMI)"
msgstr "数字立体声 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
#: spa/plugins/alsa/acp/alsa-mixer.c:4491
msgid "Digital Surround 5.1 (HDMI)"
msgstr "数字环绕 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
#: spa/plugins/alsa/acp/alsa-mixer.c:4492
msgid "Chat"
msgstr "语音"
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
#: spa/plugins/alsa/acp/alsa-mixer.c:4493
msgid "Game"
msgstr "游戏"
#: spa/plugins/alsa/acp/alsa-mixer.c:4691
#: spa/plugins/alsa/acp/alsa-mixer.c:4627
msgid "Analog Mono Duplex"
msgstr "模拟单声道双工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4692
#: spa/plugins/alsa/acp/alsa-mixer.c:4628
msgid "Analog Stereo Duplex"
msgstr "模拟立体声双工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4695
#: spa/plugins/alsa/acp/alsa-mixer.c:4631
msgid "Digital Stereo Duplex (IEC958)"
msgstr "数字立体声双工 (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
#: spa/plugins/alsa/acp/alsa-mixer.c:4632
msgid "Multichannel Duplex"
msgstr "多声道双工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
#: spa/plugins/alsa/acp/alsa-mixer.c:4633
msgid "Stereo Duplex"
msgstr "模拟立体声双工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
#: spa/plugins/alsa/acp/alsa-mixer.c:4634
msgid "Mono Chat + 7.1 Surround"
msgstr "单声道语音 + 7.1 环绕声"
#: spa/plugins/alsa/acp/alsa-mixer.c:4799
#: spa/plugins/alsa/acp/alsa-mixer.c:4735
#, c-format
msgid "%s Output"
msgstr "%s 输出"
#: spa/plugins/alsa/acp/alsa-mixer.c:4807
#: spa/plugins/alsa/acp/alsa-mixer.c:4743
#, 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:460
#: spa/plugins/alsa/acp/channelmap.h:457
msgid "(invalid)"
msgstr "(无效)"
@ -620,103 +620,103 @@ msgstr "内置音频"
msgid "Modem"
msgstr "调制解调器"
#: spa/plugins/bluez5/bluez5-device.c:1987
#: spa/plugins/bluez5/bluez5-device.c:1985
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "音频网关 (A2DP 信源 或 HSP/HFP 网关)"
#: spa/plugins/bluez5/bluez5-device.c:2016
#: spa/plugins/bluez5/bluez5-device.c:2014
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "助听器音频流 (ASHA 信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2059
#: spa/plugins/bluez5/bluez5-device.c:2057
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "高保真回放 (A2DP 信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2062
#: spa/plugins/bluez5/bluez5-device.c:2060
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "高保真双工 (A2DP 信源/信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2070
#: spa/plugins/bluez5/bluez5-device.c:2068
msgid "High Fidelity Playback (A2DP Sink)"
msgstr "高保真回放 (A2DP 信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2072
#: spa/plugins/bluez5/bluez5-device.c:2070
msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "高保真双工 (A2DP 信源/信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2146
#: spa/plugins/bluez5/bluez5-device.c:2144
#, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "高保真回放 (BAP 信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2151
#: spa/plugins/bluez5/bluez5-device.c:2149
#, c-format
msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "高保真输入 (BAP 信源, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2155
#: spa/plugins/bluez5/bluez5-device.c:2153
#, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "高保真双工 (BAP 信源/信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2164
#: spa/plugins/bluez5/bluez5-device.c:2162
msgid "High Fidelity Playback (BAP Sink)"
msgstr "高保真回放 (BAP 信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2168
#: spa/plugins/bluez5/bluez5-device.c:2166
msgid "High Fidelity Input (BAP Source)"
msgstr "高保真输入 (BAP 信源)"
#: spa/plugins/bluez5/bluez5-device.c:2171
#: spa/plugins/bluez5/bluez5-device.c:2169
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "高保真双工 (BAP 信源/信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2211
#: spa/plugins/bluez5/bluez5-device.c:2209
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "头戴式耳机单元 (HSP/HFP, 编码 %s)"
#: 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
#: 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
msgid "Handsfree"
msgstr "免"
msgstr "免手操作"
#: spa/plugins/bluez5/bluez5-device.c:2369
#: spa/plugins/bluez5/bluez5-device.c:2367
msgid "Handsfree (HFP)"
msgstr "免HFP"
msgstr "免手操作 (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:2392
#: spa/plugins/bluez5/bluez5-device.c:2390
msgid "Portable"
msgstr "便携式"
#: spa/plugins/bluez5/bluez5-device.c:2398
#: spa/plugins/bluez5/bluez5-device.c:2396
msgid "Car"
msgstr "车内"
#: spa/plugins/bluez5/bluez5-device.c:2404
#: spa/plugins/bluez5/bluez5-device.c:2402
msgid "HiFi"
msgstr "高保真"
#: spa/plugins/bluez5/bluez5-device.c:2410
#: spa/plugins/bluez5/bluez5-device.c:2408
msgid "Phone"
msgstr "电话"
#: spa/plugins/bluez5/bluez5-device.c:2417
#: spa/plugins/bluez5/bluez5-device.c:2415
msgid "Bluetooth"
msgstr "蓝牙"
#: spa/plugins/bluez5/bluez5-device.c:2418
msgid "Bluetooth Handsfree"
msgstr "蓝牙免提"
#: spa/plugins/bluez5/bluez5-device.c:2416
msgid "Bluetooth (HFP)"
msgstr "蓝牙 (HFP)"
#~ msgid "Headphone"
#~ msgstr "头戴耳机"

View file

@ -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, "hd:m:s:t:i:a:c:", long_options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "hdmstiac:", long_options, NULL)) != -1) {
switch (c) {
case 'h':
show_help(&data, argv[0], false);

View file

@ -164,7 +164,8 @@ 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)
if (type < SPA_TYPE_None || type >= _SPA_TYPE_LAST || n_vals < 1 ||
size < spa_pod_type_size(type))
continue;
vals = SPA_POD_BODY(val);

View file

@ -33,8 +33,6 @@ 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,
@ -43,13 +41,10 @@ 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, max_position) != info->channels) {
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS))
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
spa_memzero(info->position, max_position * sizeof(info->position[0]));
}
return res;
}

View file

@ -46,61 +46,20 @@ 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_ext_parse(const struct spa_pod *format, struct spa_audio_info *info, size_t size)
spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info)
{
int res;
uint32_t media_type, media_subtype;
if ((res = spa_format_parse(format, &media_type, &media_subtype)) < 0)
if ((res = spa_format_parse(format, &info->media_type, &info->media_subtype)) < 0)
return res;
if (media_type != SPA_MEDIA_TYPE_audio)
if (info->media_type != SPA_MEDIA_TYPE_audio)
return -EINVAL;
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) {
switch (info->media_subtype) {
case SPA_MEDIA_SUBTYPE_raw:
return spa_format_audio_raw_ext_parse(format, &info->info.raw,
size - offsetof(struct spa_audio_info, info.raw));
return spa_format_audio_raw_parse(format, &info->info.raw);
case SPA_MEDIA_SUBTYPE_dsp:
return spa_format_audio_dsp_parse(format, &info->info.dsp);
case SPA_MEDIA_SUBTYPE_iec958:
@ -139,25 +98,13 @@ spa_format_audio_ext_parse(const struct spa_pod *format, struct spa_audio_info *
return -ENOTSUP;
}
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)
spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
const struct spa_audio_info *info)
{
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_ext_build(builder, id, &info->info.raw,
size - offsetof(struct spa_audio_info, info.raw));
return spa_format_audio_raw_build(builder, id, &info->info.raw);
case SPA_MEDIA_SUBTYPE_dsp:
return spa_format_audio_dsp_build(builder, id, &info->info.dsp);
case SPA_MEDIA_SUBTYPE_iec958:
@ -196,13 +143,6 @@ spa_format_audio_ext_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));
}
/**
* \}
*/

View file

@ -59,8 +59,6 @@ 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 */
};
/**

View file

@ -1,118 +0,0 @@
/* 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 */

View file

@ -21,11 +21,8 @@ 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, \
@ -40,7 +37,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, }

View file

@ -9,7 +9,6 @@
#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" {
@ -29,8 +28,8 @@ extern "C" {
#endif
SPA_API_AUDIO_RAW_JSON int
spa_audio_parse_position_n(const char *str, size_t len,
uint32_t *position, uint32_t max_position, uint32_t *n_channels)
spa_audio_parse_position(const char *str, size_t len,
uint32_t *position, uint32_t *n_channels)
{
struct spa_json iter;
char v[256];
@ -39,46 +38,18 @@ spa_audio_parse_position_n(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) {
if (channels < max_position)
position[channels] = spa_type_audio_channel_from_short_name(v);
channels++;
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);
}
*n_channels = channels;
return channels;
}
SPA_API_AUDIO_RAW_JSON int
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)
spa_audio_info_raw_update(struct spa_audio_info_raw *info, 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);
@ -86,97 +57,41 @@ spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
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)) {
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);
}
}
if (spa_atou32(val, &v, 0) && (force || info->channels == 0))
info->channels = SPA_MIN(v, SPA_AUDIO_MAX_CHANNELS);
} else if (spa_streq(key, SPA_KEY_AUDIO_POSITION)) {
if (force || 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;
if (spa_audio_parse_position(val, strlen(val), info->position, &info->channels) > 0)
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, ...)
{
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;
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;
}
/**

View file

@ -267,34 +267,12 @@ 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)
{
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;
return spa_type_from_short_name(name, spa_type_audio_channel, SPA_AUDIO_CHANNEL_UNKNOWN);
}
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 ":"
@ -314,4 +292,4 @@ static const struct spa_type_info spa_type_audio_volume_ramp_scale[] = {
} /* extern "C" */
#endif
#endif /* SPA_AUDIO_RAW_TYPES_H */
#endif /* SPA_AUDIO_RAW_RAW_TYPES_H */

View file

@ -29,15 +29,10 @@ extern "C" {
#endif
SPA_API_AUDIO_RAW_UTILS int
spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_info_raw *info, size_t size)
spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info)
{
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,
@ -45,35 +40,18 @@ spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_in
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, max_position) != info->channels) {
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_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_ext_build(struct spa_pod_builder *builder, uint32_t id,
const struct spa_audio_info_raw *info, size_t size)
spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
const struct spa_audio_info_raw *info)
{
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),
@ -88,10 +66,7 @@ spa_format_audio_raw_ext_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);
/* 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) {
if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) {
spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position,
SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id,
info->channels, info->position), 0);
@ -100,13 +75,6 @@ spa_format_audio_raw_ext_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));
}
/**
* \}
*/

View file

@ -18,11 +18,7 @@ extern "C" {
* \{
*/
/* 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
#define SPA_AUDIO_MAX_CHANNELS 128u
enum spa_audio_format {
SPA_AUDIO_FORMAT_UNKNOWN,
@ -263,8 +259,6 @@ 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,
@ -275,34 +269,23 @@ 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. 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. */
/** Audio information description */
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. This can be more than SPA_AUDIO_MAX_CHANNELS
* and you may assume there is enough padding for the extra
* channel positions. */
uint32_t channels; /*< number of channels */
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

View file

@ -6,7 +6,6 @@
#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>

View file

@ -1,38 +0,0 @@
/* 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 */

View file

@ -1,125 +0,0 @@
/* 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 */

View file

@ -1,42 +0,0 @@
/* 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 */

View file

@ -41,9 +41,7 @@ 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_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 },
{ SPA_PARAM_PeerFormats, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_ID_BASE "PeerFormats", NULL },
{ 0, 0, NULL, NULL },
};

View file

@ -40,11 +40,7 @@ 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_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 */
SPA_PARAM_PeerFormats, /**< peer formats, a SPA_TYPE_Struct of SPA_TYPE_OBJECT_Format. Since 1.5.0 */
};
/** information about a parameter */

View file

@ -1,38 +0,0 @@
/* 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 */

View file

@ -1,92 +0,0 @@
/* 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 */

View file

@ -1,37 +0,0 @@
/* 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 */

View file

@ -40,9 +40,6 @@ 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 },

View file

@ -55,9 +55,6 @@ 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,

View file

@ -11,7 +11,6 @@
#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
@ -34,7 +33,8 @@ extern "C" {
SPA_API_TAG_UTILS int
spa_tag_compare(const struct spa_pod *a, const struct spa_pod *b)
{
return spa_pod_memcmp(a, 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_TAG_UTILS int

View file

@ -15,7 +15,5 @@
#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 */

View file

@ -78,36 +78,6 @@ 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)
{
@ -273,9 +243,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;
@ -363,8 +333,6 @@ 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;
}
@ -398,16 +366,13 @@ 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, min, max;
uint32_t child_size = pod->body.child.size;
*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;
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;
if (*choice == SPA_CHOICE_None)
*n_values = SPA_MIN(1u, *n_values);
return body;
}

View file

@ -80,13 +80,6 @@ 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)
{
@ -156,8 +149,12 @@ SPA_API_POD_COMPARE int spa_pod_compare(const struct spa_pod *pod1,
break;
}
case SPA_TYPE_Array:
res = spa_pod_memcmp(pod1, pod2);
{
if (pod1->size != pod2->size)
return -EINVAL;
res = memcmp(SPA_POD_BODY(pod1), SPA_POD_BODY(pod2), pod2->size);
break;
}
default:
if (pod1->size != pod2->size)
return -EINVAL;

View file

@ -70,10 +70,8 @@ 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, {

View file

@ -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/invalid choices */
/* empty choices */
if (nalt1 < 1 || nalt2 < 1)
return -EINVAL;
@ -95,6 +95,8 @@ 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);
@ -339,7 +341,9 @@ SPA_API_POD_FILTER int spa_pod_filter_part(struct spa_pod_builder *b,
default:
if (pf != NULL) {
if (spa_pod_memcmp(pp, pf) != 0)
if (pp->size != pf->size)
return -EINVAL;
if (memcmp(pp, pf, pp->size) != 0)
return -EINVAL;
do_advance = true;
}
@ -402,7 +406,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 (nvals < 1)
if (v->size < spa_pod_type_size(v->type))
continue;
if (spa_pod_compare_is_valid_choice(v->type, v->size,

View file

@ -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 = pod->size < spa_pod_type_size(pod->type) ? 0 : 1;
*n_vals = 1;
*choice = SPA_CHOICE_None;
return (struct spa_pod*)pod;
}

View file

@ -90,14 +90,13 @@ 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 work on shared
* memory and so there could be updates happening while we read. */
* duplicated after the size check below. We need to to work on shared
* memory while there could be updates happening while we read. */
SPA_BARRIER;
memcpy(header, SPA_PTROFF(parser->data, offset, void), header_size);
SPA_BARRIER;
pod = SPA_PTROFF(header, pod_offset, struct spa_pod);
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);

View file

@ -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,def,alt1,...) (n_vals),(def),(alt1),##__VA_ARGS__
#define SPA_CHOICE_ENUM(n_vals,...) (n_vals),##__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)

View file

@ -117,8 +117,6 @@ 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 */
/**
* \}

View file

@ -46,13 +46,9 @@ __extension__ ({ \
/* ========================================================================== */
#ifdef __has_attribute
#if __has_attribute(__cleanup__)
#define spa_cleanup(func) __attribute__((__cleanup__(func)))
#endif
#endif
#if defined(__has_attribute) && __has_attribute(__cleanup__)
#ifdef spa_cleanup
#define spa_cleanup(func) __attribute__((__cleanup__(func)))
#define SPA_DEFINE_AUTO_CLEANUP(name, type, ...) \
typedef __typeof__(type) _spa_auto_cleanup_type_ ## name; \

View file

@ -54,15 +54,6 @@ 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)

View file

@ -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_relax(iter, type, data, size);
spa_json_init(iter, data, size);
else if (res <= 0)
return res;
return 1;

View file

@ -10,7 +10,6 @@
#include <errno.h>
#include <stdlib.h>
#include <locale.h>
#include <sys/types.h>
#include <spa/utils/defs.h>

View file

@ -5,7 +5,6 @@
#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>
@ -79,8 +78,6 @@ 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 }
};

View file

@ -88,8 +88,6 @@ 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 */

View file

@ -1,6 +1,4 @@
#undef SPA_AUDIO_MAX_CHANNELS
#define SPA_API_IMPL SPA_EXPORT
#include <spa/utils/defs.h>
#include <spa/buffer/alloc.h>
@ -63,7 +61,6 @@
#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>
@ -168,3 +165,9 @@
#include <spa/utils/string.h>
#include <spa/utils/type.h>
#include <spa/utils/type-info.h>

View file

@ -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[PA_CHANNELS_MAX*8];
char pos[2048];
struct spa_strbuf b;
int i;
@ -1137,56 +1137,51 @@ 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 || eld.lpcm_channels == 0) {
if (eld.speakers == 0) {
changed |= old_position != NULL;
pa_proplist_unset(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED);
} else {
uint32_t positions[eld.lpcm_channels];
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; \
}
char position[64];
int i = 0, pos = 0;
if (eld.speakers & 0x01) {
_ADD_CHANNEL_POSITION(ACP_CHANNEL_FL);
_ADD_CHANNEL_POSITION(ACP_CHANNEL_FR);
positions[i++] = ACP_CHANNEL_FL;
positions[i++] = ACP_CHANNEL_FR;
}
if (eld.speakers & 0x02) {
_ADD_CHANNEL_POSITION(ACP_CHANNEL_LFE);
positions[i++] = ACP_CHANNEL_LFE;
}
if (eld.speakers & 0x04) {
_ADD_CHANNEL_POSITION(ACP_CHANNEL_FC);
positions[i++] = ACP_CHANNEL_FC;
}
if (eld.speakers & 0x08) {
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RL);
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RR);
positions[i++] = ACP_CHANNEL_RL;
positions[i++] = 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 */
_ADD_CHANNEL_POSITION(ACP_CHANNEL_SL);
_ADD_CHANNEL_POSITION(ACP_CHANNEL_SR);
positions[i++] = ACP_CHANNEL_SL;
positions[i++] = ACP_CHANNEL_SR;
}
if (eld.speakers & 0x20) {
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RLC);
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RRC);
positions[i++] = ACP_CHANNEL_RLC;
positions[i++] = ACP_CHANNEL_RRC;
}
if (eld.speakers & 0x10) {
_ADD_CHANNEL_POSITION(ACP_CHANNEL_RC);
positions[i++] = ACP_CHANNEL_RC;
}
while (i < eld.lpcm_channels)
positions[i++] = ACP_CHANNEL_UNKNOWN;
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, "]");
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;
changed |= (old_position == NULL) || (!spa_streq(old_position, position));
pa_proplist_sets(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED, position);

View file

@ -27,11 +27,7 @@
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)
@ -455,6 +451,7 @@ 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)"));
@ -463,10 +460,12 @@ 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",
channel == 0 ? "" : ",",
first ? "" : ",",
pa_channel_position_to_string(map->map[channel]));
e = strchr(e, 0);
first = false;
}
return s;
}

View file

@ -38,7 +38,6 @@
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
@ -156,13 +155,12 @@ 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];
char positions[MAX_CHANNELS * 12];
char card_index[16], card_name[64], *p;
char positions[SPA_AUDIO_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;
@ -201,13 +199,11 @@ 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);
spa_strbuf_init(&b, positions, sizeof(positions));
spa_strbuf_append(&b, "[");
p = positions;
for (i = 0; i < dev->format.channels; i++) {
spa_strbuf_append(&b, "%s%s", i == 0 ? " " : ", ",
p += snprintf(p, 12, "%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) {
@ -677,8 +673,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[MAX_CHANNELS];
uint32_t channels[MAX_CHANNELS];
float volumes[SPA_AUDIO_MAX_CHANNELS];
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
uint32_t n_volumes = 0;
if (!spa_pod_is_object_type(props, SPA_TYPE_OBJECT_Props))
@ -700,13 +696,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_N_ELEMENTS(volumes))) > 0) {
volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
changed++;
}
break;
case SPA_PROP_channelMap:
if (spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
channels, SPA_N_ELEMENTS(channels)) > 0) {
channels, SPA_AUDIO_MAX_CHANNELS) > 0) {
changed++;
}
break;

View file

@ -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 > MAX_CHANNELS) {
if (state->default_channels > SPA_AUDIO_MAX_CHANNELS) {
spa_log_warn(state->log, "%p: %s: %s > %d, clamping",
state, k, s, MAX_CHANNELS);
state->default_channels = MAX_CHANNELS;
state, k, s, SPA_AUDIO_MAX_CHANNELS);
state->default_channels = SPA_AUDIO_MAX_CHANNELS;
}
fmt_change++;
} else if (spa_streq(k, SPA_KEY_AUDIO_RATE)) {
@ -240,33 +240,35 @@ 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;
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"));
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;
}
if (spa_strbuf_append(&b, " ]") < 2)
return -ENOSPC;
if (len > o)
o += snprintf(val+o, len-o, " ]");
return 0;
}
static int uint32_array_to_string(uint32_t *vals, uint32_t n_vals, char *val, size_t len)
{
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;
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, " ]");
return 0;
}
@ -773,7 +775,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)
@ -1582,8 +1584,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, MAX_CHANNELS);
max = SPA_MIN(max, MAX_CHANNELS);
min = SPA_MIN(min, SPA_AUDIO_MAX_CHANNELS);
max = SPA_MIN(max, SPA_AUDIO_MAX_CHANNELS);
if (state->default_channels != 0 && !all) {
if (min > state->default_channels ||
@ -1643,7 +1645,7 @@ skip_channels:
} else {
const struct channel_map *map = NULL;
spa_pod_builder_int(b, min);
if (state->default_pos.n_pos == min) {
if (state->default_pos.channels == min) {
map = &state->default_pos;
spa_log_debug(state->log, "%p: using provided default", state);
} else if (min <= 8) {
@ -1653,7 +1655,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->n_pos; i++) {
for (i = 0; i < map->channels; i++) {
spa_log_debug(state->log, "%p: position %zd %d", state, i, map->pos[i]);
spa_pod_builder_id(b, map->pos[i]);
}
@ -2036,9 +2038,7 @@ static void recalc_headroom(struct state *state)
uint32_t latency;
uint32_t rate = 0;
if (state->force_quantum && !state->following)
rate = state->rate;
else if (state->position != NULL)
if (state->position != NULL)
rate = state->position->clock.target_rate.denom;
if (state->use_period_size_min_as_headroom)
@ -2065,6 +2065,8 @@ 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
@ -2072,8 +2074,6 @@ 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;

View file

@ -36,7 +36,6 @@ extern "C" {
#endif
#define MAX_RATES 16
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define DEFAULT_PERIOD 1024u
#define DEFAULT_RATE 48000u
@ -72,8 +71,8 @@ struct buffer {
#define BW_PERIOD (3 * SPA_NSEC_PER_SEC)
struct channel_map {
uint32_t n_pos;
uint32_t pos[MAX_CHANNELS];
uint32_t channels;
uint32_t pos[SPA_AUDIO_MAX_CHANNELS];
};
struct card {
@ -315,7 +314,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_n(val, len, map->pos, SPA_N_ELEMENTS(map->pos), &map->n_pos);
spa_audio_parse_position(val, len, map->pos, &map->channels);
}
static inline uint32_t spa_alsa_parse_rates(uint32_t *rates, uint32_t max, const char *val, size_t len)

View file

@ -808,7 +808,6 @@ 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) {
@ -842,7 +841,6 @@ 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;
}
@ -850,12 +848,8 @@ 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) {
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);
if ((res = spa_format_audio_parse(format, &info)) < 0)
return res;
}
if (info.media_subtype == SPA_MEDIA_SUBTYPE_raw) {
info.info.raw.rate = 0;
@ -1219,9 +1213,6 @@ 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;
}
@ -1249,11 +1240,6 @@ 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);
}
}
@ -1452,7 +1438,7 @@ static void follower_port_info(void *data,
spa_strerror(res));
}
if (idx == IDX_EnumFormat) {
spa_log_debug(this->log, "new EnumFormat from follower");
spa_log_debug(this->log, "new formats");
/* we will renegotiate when restarting */
this->recheck_format = true;
}
@ -2060,12 +2046,11 @@ 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 < n_pos; i++)
for (i = 0; i < format.info.raw.channels; i++)
format.info.raw.position[i] = SPA_AUDIO_CHANNEL_START_Aux + i;
} else if (position == POSITION_UNKNOWN) {
for (i = 0; i < n_pos; i++)
for (i = 0; i < format.info.raw.channels; i++)
format.info.raw.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
}
}

View file

@ -47,11 +47,10 @@ 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 MAX_CHANNELS
#define MAX_PORTS (MAX_CHANNELS+1)
#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS
#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1)
#define MAX_STAGES 64
#define MAX_GRAPH 9 /* 8 active + 1 replacement slot */
@ -63,7 +62,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert");
struct volumes {
bool mute;
uint32_t n_volumes;
float volumes[MAX_CHANNELS];
float volumes[SPA_AUDIO_MAX_CHANNELS];
};
static void init_volumes(struct volumes *vol)
@ -71,7 +70,7 @@ static void init_volumes(struct volumes *vol)
uint32_t i;
vol->mute = DEFAULT_MUTE;
vol->n_volumes = 0;
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
vol->volumes[i] = DEFAULT_VOLUME;
}
@ -92,7 +91,7 @@ struct props {
float max_volume;
float prev_volume;
uint32_t n_channels;
uint32_t channel_map[MAX_CHANNELS];
uint32_t channel_map[SPA_AUDIO_MAX_CHANNELS];
struct volumes channel;
struct volumes soft;
struct volumes monitor;
@ -100,7 +99,6 @@ 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;
@ -114,7 +112,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 < MAX_CHANNELS; i++)
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
props->channel_map[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
init_volumes(&props->channel);
init_volumes(&props->soft);
@ -123,7 +121,6 @@ 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;
@ -244,9 +241,9 @@ struct filter_graph {
struct spa_filter_graph *graph;
struct spa_hook listener;
uint32_t n_inputs;
uint32_t inputs_position[MAX_CHANNELS];
uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t n_outputs;
uint32_t outputs_position[MAX_CHANNELS];
uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t latency;
bool removing;
bool setup;
@ -284,7 +281,6 @@ 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;
@ -427,6 +423,7 @@ 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);
@ -441,7 +438,8 @@ 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);
spa_type_audio_channel_make_short_name(position, port->position, sizeof(port->position), "UNK");
name = spa_debug_type_find_short_name(spa_type_audio_channel, position);
snprintf(port->position, sizeof(port->position), "%s", name ? name : "UNK");
port->info = SPA_PORT_INFO_INIT();
port->info.change_mask = port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
@ -1008,43 +1006,12 @@ 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_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;
}
@ -1136,11 +1103,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_n(s, strlen(s), g->inputs_position,
SPA_N_ELEMENTS(g->inputs_position), &g->n_inputs);
spa_audio_parse_position(s, strlen(s),
g->inputs_position, &g->n_inputs);
else if (spa_streq(k, "outputs.audio.position"))
spa_audio_parse_position_n(s, strlen(s), g->outputs_position,
SPA_N_ELEMENTS(g->outputs_position), &g->n_outputs);
spa_audio_parse_position(s, strlen(s),
g->outputs_position, &g->n_outputs);
else if (spa_streq(k, "latency")) {
double latency;
if (spa_atod(s, &latency))
@ -1344,14 +1311,6 @@ 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;
@ -1439,7 +1398,7 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
if (impl->setup)
res = setup_filter_graphs(impl, false);
sync_filter_graph(impl);
spa_loop_locked(impl->data_loop, do_sync_filter_graph, 0, NULL, 0, impl);
if (impl->in_filter_props == 0)
clean_filter_handles(impl, false);
@ -1488,16 +1447,6 @@ 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"))
@ -1508,10 +1457,6 @@ 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) {
@ -1519,6 +1464,10 @@ 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;
@ -1590,6 +1539,8 @@ 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;
}
@ -1600,10 +1551,12 @@ 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, round up */
samples = SPA_MAX(1u, vrp->volume_ramp_step_time/1000) * (vrp->rate/1000);
/* convert the step time which is in nano seconds to seconds */
samples = (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;
}
@ -1616,52 +1569,76 @@ static float get_volume_at_scale(struct volume_ramp_params *vrp, float value)
return 0.0;
}
static struct spa_pod *generate_ramp_seq(struct impl *this, struct volume_ramp_params *vrp,
static struct spa_pod *generate_ramp_up_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;
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;
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;
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 sequence from %f to %f with "
"step %d/%d at scale %d", start, end, step, samples, vrp->scale);
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]);
}
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);
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;
spa_log_trace(this->log, "volume %d accum %f", offs, vas);
spa_pod_builder_control(&b.b, offs, SPA_CONTROL_Properties);
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_pod_builder_add_object(&b.b,
SPA_TYPE_OBJECT_Props, 0,
SPA_PROP_volume, SPA_POD_Float(vas));
if (offs >= samples)
break;
offs = SPA_MIN(samples, offs + step);
}
volume_accum -= volume_step;
volume_offs += ramp_step_samples;
} while (volume_accum > end);
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;
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);
sequence = generate_ramp_seq(this, vrp, buffer, size);
if (!sequence)
spa_log_error(this->log, "unable to generate sequence");
@ -1771,7 +1748,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_N_ELEMENTS(p->channel.volumes))) > 0) {
p->channel.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
have_channel_volume = true;
p->channel.n_volumes = n;
changed++;
@ -1779,7 +1756,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_N_ELEMENTS(p->channel_map))) > 0) {
p->channel_map, SPA_AUDIO_MAX_CHANNELS)) > 0) {
p->n_channels = n;
changed++;
}
@ -1794,7 +1771,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_N_ELEMENTS(p->soft.volumes))) > 0) {
p->soft.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
have_soft_volume = true;
p->soft.n_volumes = n;
changed++;
@ -1806,7 +1783,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_N_ELEMENTS(p->monitor.volumes))) > 0) {
p->monitor.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
p->monitor.n_volumes = n;
changed++;
}
@ -1918,11 +1895,10 @@ 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++) {
uint32_t pos = info->info.raw.position[i];
init_port(this, direction, i, pos, true, false, false);
init_port(this, direction, i, info->info.raw.position[i], true, false, false);
if (this->monitor && direction == SPA_DIRECTION_INPUT)
init_port(this, SPA_DIRECTION_OUTPUT, i+1,
pos, true, true, false);
info->info.raw.position[i], true, true, false);
}
break;
}
@ -1990,7 +1966,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 > MAX_CHANNELS)
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
return -EINVAL;
infop = &info;
@ -2080,24 +2056,22 @@ 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++) {
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)
if (src_info.info.raw.position[i] !=
dst_info.info.raw.position[j])
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_type_audio_channel_make_short_name(pi, b1, 8, "UNK"),
spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK"));
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]));
dst_info.info.raw.position[j] = -1;
break;
}
@ -2146,10 +2120,9 @@ 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], pj);
if (p->channel_map[i] != pj)
p->channel_map[i], info->info.raw.position[j]);
if (p->channel_map[i] != info->info.raw.position[j])
continue;
if (i != j) {
SPA_SWAP(p->channel_map[i], p->channel_map[j]);
@ -2180,7 +2153,7 @@ static void set_volume(struct impl *this)
{
struct volumes *vol;
uint32_t i;
float volumes[MAX_CHANNELS];
float volumes[SPA_AUDIO_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);
@ -2211,11 +2184,10 @@ 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_type_audio_channel_make_short_name(position[i],
buf, sizeof(buf), "UNK"));
spa_debug_type_find_short_name(spa_type_audio_channel,
position[i]));
return str;
}
@ -2309,7 +2281,6 @@ 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;
@ -2373,16 +2344,12 @@ 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++) {
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)
if (src_info.info.raw.position[i] !=
dst_info.info.raw.position[j])
continue;
out->remap[i] = j;
if (i != j)
@ -2390,9 +2357,10 @@ 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_type_audio_channel_make_short_name(pi, b1, 8, "UNK"),
spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK"));
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]));
dst_info.info.raw.position[j] = -1;
break;
}
@ -2553,8 +2521,6 @@ static int setup_convert(struct impl *this)
this->setup = true;
this->recalc = true;
sync_filter_graph(this);
return 0;
}
@ -2571,7 +2537,6 @@ 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)
@ -2592,6 +2557,7 @@ 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;
@ -2705,7 +2671,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, MAX_CHANNELS),
DEFAULT_CHANNELS, 1, SPA_AUDIO_MAX_CHANNELS),
0);
*param = spa_pod_builder_pop(b, &f[0]);
}
@ -3098,7 +3064,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 > MAX_CHANNELS) {
info.info.raw.channels > SPA_AUDIO_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);
@ -4329,18 +4295,9 @@ 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)
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);
if (s != NULL)
spa_audio_parse_position(s, strlen(s), 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);

View file

@ -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[MAX_CHANNELS][MAX_CHANNELS],
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS],
uint64_t mask)
{
uint32_t i, ch = mask_to_ch(mix, mask);
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
matrix[i][ch]= 1.0f;
}
static void average_mix(struct channelmix *mix,
float matrix[MAX_CHANNELS][MAX_CHANNELS],
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS],
uint64_t mask)
{
uint32_t i, ch = mask_to_ch(mix, mask);
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
matrix[ch][i]= 1.0f;
}
static void pair_mix(float matrix[MAX_CHANNELS][MAX_CHANNELS])
static void pair_mix(float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS])
{
uint32_t i;
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
matrix[i][i]= 1.0f;
}
static bool match_mix(struct channelmix *mix,
float matrix[MAX_CHANNELS][MAX_CHANNELS],
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_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[MAX_CHANNELS][MAX_CHANNELS] = {{ 0.0f }};
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_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 < MAX_CHANNELS; i++)
for (i = 0; i < SPA_AUDIO_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 < MAX_CHANNELS; i++) {
for (i = 0; i < SPA_AUDIO_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 < MAX_CHANNELS; i++)
for (i = 0; i < SPA_AUDIO_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 < MAX_CHANNELS; i++) {
for (i = 0; i < SPA_AUDIO_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 < MAX_CHANNELS; i++) {
for (i = 0; i < SPA_AUDIO_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 < MAX_CHANNELS; i++) {
for (i = 0; i < SPA_AUDIO_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 < MAX_CHANNELS; i++)
for (i = 0; i < SPA_AUDIO_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[MAX_CHANNELS];
float volumes[SPA_AUDIO_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 > MAX_CHANNELS ||
mix->dst_chan > MAX_CHANNELS)
if (mix->src_chan > SPA_AUDIO_MAX_CHANNELS ||
mix->dst_chan > SPA_AUDIO_MAX_CHANNELS)
return -EINVAL;
info = find_channelmix_info(mix->src_chan, mix->src_mask, mix->dst_chan, mix->dst_mask,

View file

@ -24,7 +24,6 @@
#define BUFFER_SIZE 4096
#define MAX_TAPS 255u
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define CHANNELMIX_OPS_MAX_ALIGN 16
@ -51,8 +50,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[MAX_CHANNELS][MAX_CHANNELS];
float matrix[MAX_CHANNELS][MAX_CHANNELS];
float matrix_orig[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS];
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS];
float freq; /* sample frequency */
float lfe_cutoff; /* in Hz, 0 is disabled */
@ -60,7 +59,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[MAX_CHANNELS];
struct lr4 lr4[SPA_AUDIO_MAX_CHANNELS];
float buffer_mem[2 * BUFFER_SIZE*2 + CHANNELMIX_OPS_MAX_ALIGN/4];
float *buffer[2];

View file

@ -1,147 +0,0 @@
/* 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;
}

View file

@ -284,8 +284,7 @@ conv_s24_to_f32d_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA
}
}
// Non static, referenced by `fmt-ops-sse41.c`.
void
static 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)
{

View file

@ -6,8 +6,7 @@
#include <tmmintrin.h>
// Non static, referenced by `fmt-ops-sse41.c`.
void
static 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)
{

View file

@ -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[4];
uint32_t i, conv_flags, data_size[3];
/* we generate int32 bits of random values. With this scale
* factor, we bring this in the [-1.0, 1.0] range */
@ -615,17 +615,15 @@ 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] + data_size[3], 1);
data_size[0] + data_size[1] + data_size[2], 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();

View file

@ -236,7 +236,7 @@ struct convert {
uint32_t noise_size;
const float *ns;
uint32_t n_ns;
struct shaper *shaper;
struct shaper shaper[64];
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[],

View file

@ -125,7 +125,7 @@ sparesampledumpcoeffs_sources = [
sparesampledumpcoeffs = executable(
'spa-resample-dump-coeffs',
sparesampledumpcoeffs_sources,
c_args : [ '-DRESAMPLE_DISABLE_PRECOMP' ],
c_args : [ cc_flags_native, '-DRESAMPLE_DISABLE_PRECOMP' ],
dependencies : [ spa_dep, mathlib_native ],
install : false,
native : true,

View file

@ -13,186 +13,72 @@
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_up; /* when upsampling */
double cutoff_down; /* for downsampling */
double params[RESAMPLE_MAX_PARAMS];
double cutoff;
};
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 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, },
};
static inline void blackman_window(struct resample *r, double *w, double t, uint32_t n_taps)
static inline double sinc(double x)
{
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;
if (x < 1e-6) return 1.0;
x *= M_PI;
return sin(x * cutoff) / x;
return sin(x) / x;
}
static int build_filter(struct resample *r, float *taps, uint32_t stride, uint32_t n_taps,
uint32_t n_phases, double cutoff)
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)
{
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)
(sinc(t, cutoff) * window[j]);
(cutoff * sinc(t * cutoff) * window(t, n_taps));
}
}
return 0;
@ -466,18 +352,11 @@ int resample_native_init(struct resample *r)
{
struct native_data *d;
const struct quality *q;
double scale, cutoff;
uint32_t i, n_taps, n_phases, filter_size, in_rate, out_rate, gcd, filter_stride;
double scale;
uint32_t c, 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;
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->quality = SPA_CLAMP(r->quality, 0, (int) SPA_N_ELEMENTS(window_qualities) - 1);
r->free = impl_native_free;
r->update_rate = impl_native_update_rate;
r->in_len = impl_native_in_len;
@ -487,22 +366,17 @@ int resample_native_init(struct resample *r)
r->delay = impl_native_delay;
r->phase = impl_native_phase;
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;
q = &window_qualities[r->quality];
gcd = calc_gcd(r->i_rate, r->o_rate);
in_rate = r->i_rate / gcd;
out_rate = r->o_rate / gcd;
scale = SPA_MIN(c->cutoff * out_rate / in_rate, c->cutoff);
scale = SPA_MIN(q->cutoff * out_rate / in_rate, q->cutoff);
/* multiple of 8 taps to ease simd optimizations */
n_taps = SPA_ROUND_UP_N((uint32_t)ceil(n_taps / scale), 8);
n_taps = SPA_ROUND_UP_N((uint32_t)ceil(q->n_taps / scale), 8);
n_taps = SPA_MIN(n_taps, 1u << 18);
/* try to get at least 256 phases so that interpolation is
@ -526,7 +400,7 @@ int resample_native_init(struct resample *r)
return -errno;
r->data = d;
c->n_taps = d->n_taps = 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;
@ -537,26 +411,25 @@ 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 (i = 0; i < r->channels; i++)
d->history[i] = SPA_PTROFF(d->hist_mem, i * history_stride, float);
for (c = 0; c < r->channels; c++)
d->history[c] = SPA_PTROFF(d->hist_mem, c * history_stride, float);
#ifndef RESAMPLE_DISABLE_PRECOMP
/* See if we have precomputed coefficients */
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)
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)
break;
}
if (precomp_coeffs[i].filter) {
spa_log_info(r->log, "using precomputed filter for %u->%u(%u)",
if (precomp_coeffs[c].filter) {
spa_log_debug(r->log, "using precomputed filter for %u->%u(%u)",
r->i_rate, r->o_rate, r->quality);
spa_memcpy(d->filter, precomp_coeffs[i].filter, filter_size);
spa_memcpy(d->filter, precomp_coeffs[c].filter, filter_size);
} else {
#endif
build_filter(r, d->filter, d->filter_stride, n_taps, n_phases, scale);
build_filter(d->filter, d->filter_stride, n_taps, n_phases, scale);
#ifndef RESAMPLE_DISABLE_PRECOMP
}
#endif
@ -567,8 +440,8 @@ int resample_native_init(struct resample *r)
return -ENOTSUP;
}
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,
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,
r->cpu_flags, d->info->cpu_flags);
r->cpu_flags = d->info->cpu_flags;

View file

@ -8,30 +8,7 @@
#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;
@ -46,8 +23,6 @@ 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);
@ -74,56 +49,6 @@ 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 */

View file

@ -29,9 +29,7 @@ struct data {
bool verbose;
int rate;
int format;
uint32_t window;
int quality;
struct resample_config config;
int cpu_flags;
const char *iname;
@ -45,19 +43,15 @@ struct data {
#define STR_FMTS "(s8|s16|s32|f32|f64)"
#define OPTIONS "hvc:r:f:w:q:u:t:p:"
#define OPTIONS "hvr:f:q:c:"
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' },
{ "cutoff", required_argument, NULL, 'u' },
{ "taps", required_argument, NULL, 't' },
{ "param", required_argument, NULL, 'p' },
{ "cpuflags", required_argument, NULL, 'c' },
{ NULL, 0, NULL, 0 }
};
@ -65,7 +59,6 @@ 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;
@ -73,31 +66,14 @@ 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\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,
" -f --format Output sample format %s (default as input)\n"
" -q --quality Resampler quality (default %u)\n"
" -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");
" -c --cpuflags CPU flags (default 0)\n"
"\n",
STR_FMTS, DEFAULT_QUALITY);
}
static inline const char *
@ -187,8 +163,6 @@ 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,
@ -233,23 +207,10 @@ 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];
@ -298,8 +259,9 @@ 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);
@ -358,27 +320,6 @@ 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;

View file

@ -25,8 +25,7 @@ SPA_LOG_IMPL(logger);
extern const struct spa_handle_factory test_source_factory;
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define MAX_PORTS (MAX_CHANNELS+1)
#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1)
struct context {
struct spa_handle *convert_handle;

View file

@ -790,7 +790,6 @@ 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;
@ -857,11 +856,12 @@ static int impl_node_process(void *object)
outport->n_buffers);
return -EPIPE;
}
d = outb->buf.datas;
if (n_buffers == 1 && SPA_FLAG_IS_SET(d[0].flags, SPA_DATA_FLAG_DYNAMIC)) {
if (n_buffers == 1) {
*outb->buffer = *buffers[0]->buffer;
} else {
struct spa_data *d = outb->buf.datas;
*outb->buffer = outb->buf;
maxsize = SPA_MIN(maxsize, d[0].maxsize);

View file

@ -113,7 +113,6 @@ 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;
@ -307,8 +306,6 @@ 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;
}
@ -425,8 +422,7 @@ static int make_buffer(struct impl *this)
this->sample_count += n_samples;
this->elapsed_time = SAMPLES_TO_TIME(this, this->sample_count);
if (!this->following)
set_timer(this, true);
set_timer(this, true);
io->buffer_id = b->id;
io->status = SPA_STATUS_HAVE_DATA;
@ -479,8 +475,7 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
this->elapsed_time = 0;
this->started = true;
if (!this->following)
set_timer(this, true);
set_timer(this, true);
break;
}
case SPA_NODE_COMMAND_Suspend:
@ -896,7 +891,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 && !this->following)
if (!this->props.live)
set_timer(this, true);
}
@ -971,7 +966,7 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
}
if (!this->props.live || this->following)
if (!this->props.live)
return make_buffer(this);
else
return SPA_STATUS_OK;

View file

@ -40,13 +40,7 @@ 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_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),
spa_audio_parse_position(s, strlen(s), state->default_pos.pos,
&state->default_pos.channels);
fmt_change++;
} else if (spa_streq(k, SPA_KEY_AUDIO_ALLOWED_RATES)) {
@ -91,12 +85,11 @@ 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_type_audio_channel_make_short_name(map->pos[i],
pos, sizeof(pos), "UNK"));
spa_debug_type_find_short_name(spa_type_audio_channel,
map->pos[i]));
if (r < 0 || o + r >= len)
return -ENOSPC;
o += r;

View file

@ -109,7 +109,6 @@ 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 {
@ -128,7 +127,7 @@ struct buffer {
struct channel_map {
uint32_t channels;
uint32_t pos[MAX_CHANNELS];
uint32_t pos[SPA_AUDIO_MAX_CHANNELS];
};
struct port {

View file

@ -139,8 +139,7 @@ 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],
void **config_data)
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
a2dp_aac_t conf;
int i;
@ -201,7 +200,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[2];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
uint32_t i = 0;
if (caps_size < sizeof(conf))

View file

@ -110,8 +110,7 @@ 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],
void **config_data)
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
a2dp_aptx_t conf;
int i;
@ -147,8 +146,7 @@ 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],
void **config_data)
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
a2dp_aptx_ll_ext_t conf = { 0 };
size_t actual_conf_size;
@ -168,7 +166,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, NULL)) < 0)
if ((res = codec_select_config(codec, flags, caps, caps_size, info, settings, config)) < 0)
return res;
memcpy(&conf.base.aptx, config, sizeof(conf.base.aptx));
@ -207,7 +205,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[2];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
uint32_t i = 0;
if (caps_size < sizeof(conf))

View file

@ -61,8 +61,7 @@ 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],
void **config_data)
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
a2dp_faststream_t conf;
int i;
@ -115,7 +114,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[2];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
uint32_t i = 0;
if (caps_size < sizeof(conf))

View file

@ -83,8 +83,7 @@ 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],
void **config_data)
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
a2dp_lc3plus_hr_t conf;
@ -138,8 +137,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, NULL);
res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2, NULL);
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);
#define PREFER_EXPR(expr) \
do { \
@ -176,7 +175,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[2];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
uint32_t i = 0;
if (caps_size < sizeof(conf))

View file

@ -115,8 +115,7 @@ 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],
void **config_data)
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
a2dp_ldac_t conf;
int i;
@ -159,7 +158,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[2];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
if (caps_size < sizeof(conf))
return -EINVAL;

View file

@ -74,8 +74,7 @@ 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],
void **config_data)
const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
a2dp_opus_g_t conf;
int frequency, duration, channels;
@ -127,8 +126,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, NULL);
res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2, NULL);
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);
#define PREFER_EXPR(expr) \
do { \
@ -165,7 +164,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[2];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
int channels;
if (caps_size < sizeof(conf))

View file

@ -58,8 +58,6 @@ 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 {
@ -253,8 +251,14 @@ 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_type_audio_channel_from_short_name(name);
enum spa_audio_channel position = SPA_AUDIO_CHANNEL_UNKNOWN;
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;
@ -309,14 +313,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, MAX_CHANNELS);
props->channels = SPA_CLAMP(v, 1u, SPA_AUDIO_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, MAX_CHANNELS);
props->bidi_channels = SPA_CLAMP(v, 0u, SPA_AUDIO_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))
@ -499,7 +503,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 > MAX_CHANNELS)
if (channels > SPA_AUDIO_MAX_CHANNELS)
return -EINVAL;
if (2 * coupled_streams > channels)
return -EINVAL;
@ -538,9 +542,10 @@ 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) {
uint32_t idx = permutation ? permutation[j] : j;
positions[idx] = loc.position;
j++;
if (permutation)
positions[permutation[j++]] = loc.position;
else
positions[j++] = loc.position;
}
}
for (i = SPA_AUDIO_CHANNEL_START_Aux; j < channels; ++i, ++j)
@ -556,7 +561,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, MAX_CHANNELS),
.channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS),
.frame_duration = (OPUS_05_FRAME_DURATION_25 |
OPUS_05_FRAME_DURATION_50 |
OPUS_05_FRAME_DURATION_100 |
@ -566,7 +571,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
OPUS_05_INIT_BITRATE(0)
},
.bidi = {
.channels = SPA_MIN(255u, MAX_CHANNELS),
.channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS),
.frame_duration = (OPUS_05_FRAME_DURATION_25 |
OPUS_05_FRAME_DURATION_50 |
OPUS_05_FRAME_DURATION_100 |
@ -590,8 +595,7 @@ 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],
void **config_data)
const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
struct props props;
a2dp_opus_05_t conf;
@ -700,8 +704,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, NULL);
res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2, NULL);
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);
#define PREFER_EXPR(expr) \
do { \
@ -767,7 +771,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[MAX_CHANNELS];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
if (caps_size < sizeof(conf))
return -EINVAL;
@ -831,8 +835,7 @@ 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;

View file

@ -135,8 +135,7 @@ 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],
void **config_data)
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
a2dp_sbc_t conf;
int bitpool, i;
@ -222,8 +221,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, NULL);
res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2, NULL);
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);
#define PREFER_EXPR(expr) \
do { \
@ -347,7 +346,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[2];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
if (caps_size < sizeof(conf))
return -EINVAL;

View file

@ -125,7 +125,6 @@ struct impl {
struct spa_source *ring_timer;
void *upower;
struct spa_bt_telephony *telephony;
bool pts;
};
struct transport_data {
@ -853,7 +852,7 @@ done:
if (cfg)
libusb_free_config_descriptor(cfg);
if (devices)
libusb_free_device_list(devices, true);
libusb_free_device_list(devices, 0);
if (ctx)
libusb_exit(ctx);
return ok;
@ -1011,7 +1010,6 @@ 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
@ -1020,15 +1018,12 @@ 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;
}
}
@ -1312,7 +1307,7 @@ next_indicator:
type = INTERNATIONAL_NUMBER;
else
type = NATIONAL_NUMBER;
rfcomm_send_reply(rfcomm, "+CNUM: ,\"%s\",%u,,4", backend->modem.own_number, type);
rfcomm_send_reply(rfcomm, "+CNUM: ,\"%s\",%u", backend->modem.own_number, type);
}
rfcomm_send_reply(rfcomm, "OK");
} else if (spa_strstartswith(buf, "AT+COPS=")) {
@ -1355,7 +1350,6 @@ 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=")) {
@ -1365,6 +1359,7 @@ 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 */
@ -1439,15 +1434,6 @@ 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;
@ -2773,8 +2759,7 @@ static int sco_acquire_cb(void *data, bool optional)
goto fail;
#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
if (!td->rfcomm->device->disable_dummy_call)
rfcomm_hfp_ag_set_cind(td->rfcomm, true);
rfcomm_hfp_ag_set_cind(td->rfcomm, true);
#endif
t->fd = sock;
@ -2828,8 +2813,7 @@ 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
if (!td->rfcomm->device->disable_dummy_call)
rfcomm_hfp_ag_set_cind(td->rfcomm, false);
rfcomm_hfp_ag_set_cind(td->rfcomm, false);
#endif
sco_destroy_cb(t);
@ -2942,11 +2926,7 @@ 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) {
/* 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)) &&
if ((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)) {
@ -2960,7 +2940,7 @@ static void sco_listen_event(struct spa_source *source)
return;
}
spa_assert(t->profile & SPA_BT_PROFILE_HEADSET_AUDIO);
spa_assert(t->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY);
if (rfcomm->telephony_ag && rfcomm->telephony_ag->transport.rejectSCO) {
spa_log_info(backend->log, "rejecting SCO, AudioGatewayTransport1.RejectSCO=true");
@ -3401,43 +3381,6 @@ 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);
@ -4068,16 +4011,6 @@ 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;
@ -4162,7 +4095,6 @@ 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,

View file

@ -82,22 +82,6 @@
#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 */
@ -153,68 +137,11 @@
#define BT_ISO_QOS_TARGET_LATENCY_BALANCED 0x02
#define BT_ISO_QOS_TARGET_LATENCY_RELIABILITY 0x03
#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 __attribute__((packed)) ltv {
uint8_t len;
uint8_t type;
uint8_t value[];
};
struct bap_endpoint_qos {
uint8_t framing;

View file

@ -16,7 +16,6 @@
#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>
@ -46,11 +45,9 @@ struct impl {
struct settings {
uint32_t locations;
uint32_t channel_allocation;
uint16_t supported_context;
uint16_t available_context;
bool sink;
bool duplex;
char *qos_name;
const char *qos_name;
int retransmission;
int latency;
int64_t delay;
@ -58,10 +55,8 @@ struct settings {
};
struct pac_data {
const void *data;
const uint8_t *data;
size_t size;
const void *metadata;
size_t metadata_size;
int index;
const struct settings *settings;
};
@ -86,16 +81,9 @@ 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_), \
@ -203,6 +191,32 @@ 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;
@ -305,6 +319,7 @@ 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 | \
@ -315,7 +330,6 @@ 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);
@ -341,17 +355,17 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
framelen[0] = htobs(framelen_min);
framelen[1] = htobs(framelen_max);
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));
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));
/* XXX: we support only one frame block -> max 2 frames per SDU */
if (max_frames > 2)
max_frames = 2;
ltv_writer_uint8(&writer, LC3_TYPE_BLKS, max_frames);
data += write_ltv_uint8(data, LC3_TYPE_BLKS, max_frames);
return ltv_writer_end(&writer);
return data - caps;
}
static void debugc_ltv(struct spa_debug_context *debug_ctx, int pac, struct ltv *ltv)
@ -377,7 +391,7 @@ static void debugc_ltv(struct spa_debug_context *debug_ctx, int pac, struct ltv
}
}
static int parse_bluez_pacs_data(const uint8_t *data, size_t data_size, struct pac_data pacs[MAX_PACS],
static int parse_bluez_pacs(const uint8_t *data, size_t data_size, struct pac_data pacs[MAX_PACS],
struct spa_debug_context *debug_ctx)
{
/*
@ -386,7 +400,7 @@ static int parse_bluez_pacs_data(const uint8_t *data, size_t data_size, struct p
*/
int pac = 0;
pacs[pac] = (struct pac_data){ .data = data };
pacs[pac] = (struct pac_data){ data, 0 };
while (data_size > 0) {
struct ltv *ltv = (struct ltv *)data;
@ -397,7 +411,7 @@ static int parse_bluez_pacs_data(const uint8_t *data, size_t data_size, struct p
break;
++pac;
pacs[pac] = (struct pac_data){ .data = data + 1, .index = pac };
pacs[pac] = (struct pac_data){ data + 1, 0, pac };
} else if (ltv->len >= data_size) {
return -EINVAL;
} else {
@ -411,28 +425,6 @@ static int parse_bluez_pacs_data(const uint8_t *data, size_t data_size, struct p
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;
@ -557,7 +549,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 void *data = pac->data;
const uint8_t *data = pac->data;
size_t data_size = pac->size;
uint16_t framelen_min = 0, framelen_max = 0;
int max_frames = -1;
@ -565,11 +557,9 @@ 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;
@ -581,7 +571,14 @@ 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 ((ltv = ltv_next(&data, &data_size))) {
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;
}
switch (ltv->type) {
case LC3_TYPE_FREQ:
spa_return_val_if_fail(ltv->len == 3, false);
@ -608,25 +605,9 @@ 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))
@ -665,8 +646,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, and 32KHz in TMAP,
* so prefer those for now for input rate in duplex configuration.
/* 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.
*
* It appears few devices support 48kHz out + input, so in duplex mode
* try 32 kHz or 16 kHz also for output direction.
@ -689,15 +670,12 @@ 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 void *data, size_t data_size)
static bool parse_conf(bap_lc3_t *conf, const uint8_t *data, size_t data_size)
{
const struct ltv *ltv;
if (!data_size)
return false;
memset(conf, 0, sizeof(*conf));
@ -707,7 +685,12 @@ static bool parse_conf(bap_lc3_t *conf, const void *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 ((ltv = ltv_next(&data, &data_size))) {
while (data_size > 0) {
struct ltv *ltv = (struct ltv *)data;
if (ltv->len < sizeof(struct ltv) || ltv->len >= data_size)
return false;
switch (ltv->type) {
case LC3_TYPE_FREQ:
spa_return_val_if_fail(ltv->len == 2, false);
@ -735,9 +718,9 @@ static bool parse_conf(bap_lc3_t *conf, const void *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;
@ -745,32 +728,7 @@ static bool parse_conf(bap_lc3_t *conf, const void *data, size_t data_size)
return true;
}
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)
static int conf_cmp(const bap_lc3_t *conf1, int res1, const bap_lc3_t *conf2, int res2)
{
const bap_lc3_t *conf;
int a, b;
@ -795,9 +753,6 @@ 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);
@ -823,7 +778,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, pac1->settings);
return conf_cmp(&conf1, res1, &conf2, res2);
}
static void parse_settings(struct settings *s, const struct spa_dict *settings,
@ -842,7 +797,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 = strdup(str);
s->qos_name = str;
if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.rtn"), &value, 0))
s->retransmission = value;
@ -862,18 +817,12 @@ 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 local endpoint sink or source */
/* Is remote endpoint sink or source */
s->sink = spa_atob(spa_dict_lookup(settings, "bluez5.bap.sink"));
/* Is remote endpoint duplex */
@ -888,48 +837,26 @@ 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],
void **config_data)
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
{
struct pac_data pacs[MAX_PACS];
int npacs;
bap_lc3_t conf;
uint8_t *data = config;
struct spa_debug_log_ctx debug_ctx;
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;
struct settings s;
int i;
if (caps == NULL)
return -EINVAL;
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;
parse_settings(&s, settings, &debug_ctx);
/* Select best conf from those possible */
npacs = parse_bluez_pacs(caps, caps_size, metadata, metadata_len, pacs, &debug_ctx.ctx);
npacs = parse_bluez_pacs(caps, caps_size, pacs, &debug_ctx.ctx);
if (npacs < 0) {
spa_debugc(&debug_ctx.ctx, "malformed PACS");
return npacs;
@ -939,7 +866,7 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
}
for (i = 0; i < npacs; ++i)
pacs[i].settings = &d->settings;
pacs[i].settings = &s;
qsort(pacs, npacs, sizeof(struct pac_data), pac_cmp);
@ -948,55 +875,38 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
if (!select_config(&conf, &pacs[0], &debug_ctx.ctx))
return -ENOTSUP;
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);
data += write_ltv_uint8(data, LC3_TYPE_FREQ, conf.rate);
data += write_ltv_uint8(data, 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)
ltv_writer_uint32(&writer, LC3_TYPE_CHAN, conf.channels);
data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(conf.channels));
ltv_writer_uint16(&writer, LC3_TYPE_FRAMELEN, conf.framelen);
ltv_writer_uint8(&writer, LC3_TYPE_BLKS, conf.n_blks);
data += write_ltv_uint16(data, LC3_TYPE_FRAMELEN, htobs(conf.framelen));
data += write_ltv_uint8(data, LC3_TYPE_BLKS, conf.n_blks);
ret = ltv_writer_end(&writer);
if (ret >= 0 && config_data)
*config_data = spa_steal_ptr(d);
return ret;
return data - config;
}
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, res;
void *data1 = NULL;
void *data2 = NULL;
const struct config_data *d;
int res1, res2;
/* Order selected configurations by preference */
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);
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);
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;
return conf_cmp(&conf1, res1, &conf2, res2);
}
static uint8_t channels_to_positions(uint32_t channels, uint32_t *position, uint32_t max_position)
static uint8_t channels_to_positions(uint32_t channels, uint32_t *position)
{
uint32_t n_channels = get_channel_count(channels);
uint8_t n_positions = 0;
spa_assert(n_channels <= max_position);
spa_assert(n_channels <= SPA_AUDIO_MAX_CHANNELS);
if (channels == 0) {
position[0] = SPA_AUDIO_CHANNEL_MONO;
@ -1022,7 +932,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[LC3_MAX_CHANNELS];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
uint32_t i = 0;
uint8_t res;
@ -1080,7 +990,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, SPA_N_ELEMENTS(position));
res = channels_to_positions(conf.channels, position);
if (res == 0)
return -EINVAL;
spa_pod_builder_add(b,
@ -1134,8 +1044,7 @@ 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,
SPA_N_ELEMENTS(info->info.raw.position));
res = channels_to_positions(conf.channels, info->info.raw.position);
if (res == 0)
return -EINVAL;
info->info.raw.channels = res;
@ -1152,23 +1061,24 @@ 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,
const void *config_data,
struct bap_codec_qos *qos)
struct bap_codec_qos *qos, const struct spa_dict *settings)
{
struct bap_qos bap_qos;
bap_lc3_t conf;
bool found = false;
const struct config_data *d = config_data;
struct settings s;
struct spa_debug_log_ctx debug_ctx;
spa_zero(*qos);
if (!d)
if (!parse_conf(&conf, config, config_size))
return -EINVAL;
conf = d->conf;
parse_settings(&s, settings, &debug_ctx);
found = select_bap_qos(&bap_qos, &d->settings, get_rate_mask(conf.rate), get_duration_mask(conf.frame_duration),
found = select_bap_qos(&bap_qos, &s, 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 */
@ -1211,27 +1121,6 @@ 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)
@ -1488,60 +1377,80 @@ 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)
{
const char *preset_name = NULL;
int index = 0x0;
bool preset_found = false;
const char *preset = NULL;
int channel_allocation = 0;
int i, ret;
struct ltv_writer writer = LTV_WRITER(caps, *caps_size);
const struct bap_qos *preset = NULL;
uint8_t *data = caps;
*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_name = settings->items[i].value;
preset = spa_dict_lookup(settings, "preset");
}
}
if (preset_name == NULL)
if (preset == NULL)
return -EINVAL;
SPA_FOR_EACH_ELEMENT_VAR(bap_bcast_qos_configs, c) {
if (spa_streq(c->name, preset_name)) {
preset = c;
if (spa_streq(c->name, preset)) {
preset_found = true;
break;
}
index++;
}
if (!preset)
if (!preset_found)
return -EINVAL;
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);
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;
if (preset->framing)
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)
qos->framing = 1;
else
qos->framing = 0;
qos->sdu = preset->framelen * get_channel_count(channel_allocation);
qos->retransmission = preset->retransmission;
qos->latency = preset->latency;
qos->delay = preset->delay;
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->phy = 2;
qos->interval = (preset->frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);
qos->interval = (bap_bcast_qos_configs[index].frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);
ret = ltv_writer_end(&writer);
if (ret < 0)
return ret;
if (ret > UINT8_MAX)
return -ENOSPC;
*caps_size = ret;
return 0;
return true;
}
const struct media_codec bap_codec_lc3 = {
@ -1555,8 +1464,6 @@ 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,

View file

@ -35,7 +35,6 @@
#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"
@ -77,11 +76,6 @@ 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;
@ -136,8 +130,6 @@ 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
@ -159,17 +151,13 @@ struct spa_bt_remote_endpoint {
char *uuid;
unsigned int codec;
struct spa_bt_device *device;
uint8_t *capabilities;
size_t capabilities_len;
uint8_t *metadata;
size_t metadata_len;
uint8_t capabilities[A2DP_MAX_CAPS_SIZE];
int capabilities_len;
bool delay_reporting;
bool acceptor;
struct bap_endpoint_qos qos;
struct bap_features bap_features;
bool asha_right_side;
uint64_t hisyncid;
};
@ -667,77 +655,6 @@ 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;
@ -775,7 +692,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, NULL);
&monitor->global_settings, config);
else
res = -ENOTSUP;
@ -960,9 +877,8 @@ 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, size_t *caps_size,
uint8_t **meta, size_t *meta_size,
const char **endpoint_path, struct bap_endpoint_qos *qos)
uint8_t caps[A2DP_MAX_CAPS_SIZE], int *caps_size, const char **endpoint_path,
struct bap_endpoint_qos *qos)
{
DBusMessageIter dict_iter = *iter;
const char *key = NULL;
@ -983,46 +899,29 @@ 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") || spa_streq(key, "Metadata")) {
uint8_t **dest;
size_t *size;
uint8_t *data, *buf;
int n;
if (spa_streq(key, "Capabilities")) {
uint8_t *buf;
if (spa_streq(key, "Capabilities")) {
dest = caps;
size = caps_size;
} else {
dest = meta;
size = meta_size;
}
if (!dest)
if (!caps)
goto next;
spa_assert(dest && size);
if (!check_iter_signature(&it[1], "ay"))
if (type != DBUS_TYPE_ARRAY)
goto bad_property;
dbus_message_iter_recurse(&it[1], &it[2]);
dbus_message_iter_get_fixed_array(&it[2], &data, &n);
type = dbus_message_iter_get_arg_type(&it[2]);
if (type != DBUS_TYPE_BYTE)
goto bad_property;
if (n) {
buf = malloc(n);
if (!buf)
return -ENOMEM;
memcpy(buf, data, n);
} else {
buf = NULL;
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;
}
memcpy(caps, buf, *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);
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);
} else if (spa_streq(key, "Endpoint")) {
if (!endpoint_path)
goto next;
@ -1112,12 +1011,8 @@ 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;
@ -1132,9 +1027,6 @@ 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
@ -1150,7 +1042,7 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
/* Find endpoint */
iter = props;
if (parse_endpoint_props(monitor, &iter, NULL, NULL, NULL, NULL, &endpoint_path, NULL) < 0)
if (parse_endpoint_props(monitor, &iter, NULL, NULL, &endpoint_path, NULL) < 0)
goto error_invalid;
ep = remote_endpoint_find(monitor, endpoint_path);
@ -1166,8 +1058,7 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
/* Parse endpoint properties */
iter = props;
if (parse_endpoint_props(monitor, &iter, &ep->capabilities, &ep->capabilities_len,
&ep->metadata, &ep->metadata_len, NULL, &ep->qos) < 0)
if (parse_endpoint_props(monitor, &iter, ep->capabilities, &ep->capabilities_len, NULL, &ep->qos) < 0)
goto error_invalid;
if (ep->qos.locations)
@ -1175,10 +1066,6 @@ 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;
@ -1187,22 +1074,16 @@ 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, &config_data);
&monitor->default_audio_info, &settings, config);
if (conf_size < 0) {
spa_log_error(monitor->log, "can't select config: %d (%s)",
conf_size, spa_strerror(conf_size));
@ -1211,6 +1092,8 @@ 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,
@ -1229,7 +1112,7 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe
spa_zero(qos);
res = codec->get_qos(codec, &ep->qos, config_data, &qos);
res = codec->get_qos(codec, config, conf_size, &ep->qos, &qos, &settings);
if (res < 0) {
spa_log_error(monitor->log, "can't select QOS config: %d (%s)",
res, spa_strerror(res));
@ -1277,30 +1160,8 @@ 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;
@ -1311,9 +1172,6 @@ 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;
@ -2938,38 +2796,6 @@ 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)
@ -2978,9 +2804,8 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
DBusMessageIter copy_iter = *props_iter;
parse_endpoint_props(monitor, &copy_iter,
&remote_endpoint->capabilities, &remote_endpoint->capabilities_len,
&remote_endpoint->metadata, &remote_endpoint->metadata_len,
NULL, &remote_endpoint->qos);
remote_endpoint->capabilities, &remote_endpoint->capabilities_len, NULL,
&remote_endpoint->qos);
while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) {
DBusMessageIter it[2];
@ -2994,8 +2819,7 @@ 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, "Metadata") ||
spa_streq(key, "Locations") ||
if (spa_streq(key, "Capabilities") || spa_streq(key, "Locations") ||
spa_streq(key, "QoS") || spa_streq(key, "Context") ||
spa_streq(key, "SupportedContext")) {
/* parsed by parse_endpoint_props */
@ -3105,13 +2929,8 @@ 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 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 {
}
else {
unhandled:
spa_log_debug(monitor->log, "remote_endpoint %p: unhandled key %s", remote_endpoint, key);
}
@ -3166,14 +2985,10 @@ 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);
}
@ -3771,11 +3586,6 @@ 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);
@ -4276,22 +4086,13 @@ static int transport_acquire(void *data, bool optional)
return do_transport_acquire(data);
}
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)
static int do_transport_release(struct spa_bt_transport *transport)
{
struct spa_bt_monitor *monitor = transport->monitor;
spa_autoptr(DBusMessage) m = NULL;
spa_autoptr(DBusMessage) m = NULL, r = 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);
@ -4328,7 +4129,7 @@ static struct pending_release *do_transport_release(struct spa_bt_transport *tra
if (linked) {
spa_log_info(monitor->log, "Linked transport %s released", transport->path);
transport->fd = -1;
return NULL;
return 0;
}
release:
@ -4344,39 +4145,46 @@ release:
BLUEZ_MEDIA_TRANSPORT_INTERFACE,
"Release");
if (m == NULL)
return NULL;
return -ENOMEM;
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;
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);
}
pending->pending = p;
pending->transport = transport;
pending->is_idle = is_idle;
return pending;
return 0;
}
static int transport_release(void *data)
{
struct spa_bt_transport *transport = data;
struct spa_bt_monitor *monitor = transport->monitor;
struct spa_list pending = SPA_LIST_INIT(&pending);
struct pending_release *item;
struct spa_bt_transport *t;
/*
* 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",
@ -4392,61 +4200,15 @@ 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) {
item = do_transport_release(t);
if (item)
spa_list_append(&pending, &item->link);
}
if (t->fd >= 0)
do_transport_release(t);
}
spa_log_debug(monitor->log, "Release CIG %d: transport %s",
transport->bap_cig, transport->path);
}
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;
return do_transport_release(data);
}
static int transport_set_delay(void *data, int64_t delay_nsec)
@ -4718,7 +4480,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, NULL);
&monitor->default_audio_info, &monitor->global_settings, config);
if (res < 0) {
spa_log_error(monitor->log, "media codec switch %p: incompatible capabilities (%d)",
sw, res);
@ -5275,10 +5037,6 @@ 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));
@ -5543,42 +5301,6 @@ 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)
{
@ -5626,9 +5348,6 @@ 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);
@ -5858,10 +5577,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;
static const DBusObjectPathVTable vtable_object_manager_a2dp = {
const DBusObjectPathVTable vtable_object_manager_a2dp = {
.message_function = object_manager_handler_a2dp,
};
static const DBusObjectPathVTable vtable_object_manager_bap = {
const DBusObjectPathVTable vtable_object_manager_bap = {
.message_function = object_manager_handler_bap,
};
@ -6092,7 +5811,6 @@ 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) {
@ -6116,12 +5834,7 @@ 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);
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;
}
codec->get_bis_config(codec, caps, &caps_size, &settings, &qos);
msg = dbus_message_new_method_call(BLUEZ_SERVICE,
object_path,
@ -6850,8 +6563,6 @@ 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);
@ -7139,7 +6850,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[MAX_CHANNELS];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
uint32_t n_channels;
uint32_t locations;
unsigned int i, j;
@ -7150,8 +6861,7 @@ 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_n(str, strlen(str), position,
SPA_N_ELEMENTS(position), &n_channels)) {
if (!spa_audio_parse_position(str, strlen(str), position, &n_channels)) {
spa_log_error(this->log, "property %s '%s' is not valid position array", key, str);
return;
}
@ -7165,36 +6875,10 @@ 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_FL | BAP_CHANNEL_FR;
this->bap_source_locations = BAP_CHANNEL_FL | BAP_CHANNEL_FR;
this->bap_sink_locations = BAP_CHANNEL_ALL;
this->bap_source_locations = BAP_CHANNEL_ALL;
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);
@ -7209,8 +6893,6 @@ 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)

View file

@ -12,7 +12,6 @@
#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>
@ -27,7 +26,6 @@
#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>
@ -40,7 +38,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*MAX_CHANNELS)
#define MAX_NODES (2*SPA_AUDIO_MAX_CHANNELS)
#define DEVICE_ID_SOURCE 0
#define DEVICE_ID_SINK 1
@ -101,9 +99,9 @@ struct node {
unsigned int offload_acquired:1;
uint32_t n_channels;
int64_t latency_offset;
uint32_t channels[MAX_CHANNELS];
float volumes[MAX_CHANNELS];
float soft_volumes[MAX_CHANNELS];
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
float volumes[SPA_AUDIO_MAX_CHANNELS];
float soft_volumes[SPA_AUDIO_MAX_CHANNELS];
};
struct dynamic_node
@ -131,8 +129,8 @@ struct device_set {
bool leader;
uint32_t sinks;
uint32_t sources;
struct device_set_member sink[MAX_CHANNELS];
struct device_set_member source[MAX_CHANNELS];
struct device_set_member sink[SPA_AUDIO_MAX_CHANNELS];
struct device_set_member source[SPA_AUDIO_MAX_CHANNELS];
};
struct impl {
@ -184,7 +182,7 @@ static void init_node(struct impl *this, struct node *node, uint32_t id)
spa_zero(*node);
node->id = id;
for (i = 0; i < MAX_CHANNELS; i++) {
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
node->volumes[i] = 1.0f;
node->soft_volumes[i] = 1.0f;
}
@ -478,7 +476,6 @@ 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)
@ -549,7 +546,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 < MAX_CHANNELS)
if (k == node->n_channels && node->n_channels < SPA_AUDIO_MAX_CHANNELS)
node->channels[node->n_channels++] = t->channels[j];
}
}
@ -2416,7 +2413,7 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b,
default:
name_prefix = "bluetooth";
description = _("Bluetooth");
hfp_description = _("Bluetooth Handsfree");
hfp_description = _("Bluetooth (HFP)");
port_type = "bluetooth";
break;
}
@ -2677,24 +2674,11 @@ 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;
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;
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));
}
static int impl_enum_params(void *object, int seq,
@ -2962,8 +2946,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[MAX_CHANNELS];
uint32_t channels[MAX_CHANNELS];
float volumes[SPA_AUDIO_MAX_CHANNELS];
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
uint32_t n_volumes = 0, SPA_UNUSED n_channels = 0;
int64_t latency_offset = 0;
@ -2988,11 +2972,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_N_ELEMENTS(volumes));
volumes, SPA_AUDIO_MAX_CHANNELS);
break;
case SPA_PROP_channelMap:
n_channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
channels, SPA_N_ELEMENTS(channels));
channels, SPA_AUDIO_MAX_CHANNELS);
break;
case SPA_PROP_latencyOffsetNsec:
if (spa_pod_get_long(&prop->value, &latency_offset) == 0) {
@ -3031,47 +3015,6 @@ 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)
@ -3148,7 +3091,6 @@ 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;
@ -3156,8 +3098,7 @@ 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),
SPA_PROP_params, SPA_POD_OPT_Pod(&params))) < 0) {
SPA_PROP_bluetoothOffloadActive, SPA_POD_OPT_Bool(&offload_active))) < 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;
@ -3165,15 +3106,10 @@ 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) {
this->params[IDX_Props].flags ^= SPA_PARAM_INFO_SERIAL;
emit_info(this, false);
if (codec_id == SPA_ID_INVALID)
return 0;
}
if (this->profile == DEVICE_PROFILE_A2DP || profile_is_bap(this->profile) ||
this->profile == DEVICE_PROFILE_ASHA || this->profile == DEVICE_PROFILE_HSP_HFP) {
@ -3311,13 +3247,6 @@ 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(

View file

@ -157,8 +157,6 @@ 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,
@ -573,8 +571,6 @@ 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);
@ -682,7 +678,7 @@ struct spa_bt_transport {
struct spa_list bap_transport_linked;
uint32_t n_channels;
uint32_t channels[MAX_CHANNELS];
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
struct spa_bt_transport_volume volumes[SPA_BT_VOLUME_ID_TERM];

View file

@ -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[1] = { SPA_AUDIO_CHANNEL_MONO };
const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { 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_Int(8000),
SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_ENUM_Int(1, 8000),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(channels),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, channels, position),

View file

@ -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[1] = { SPA_AUDIO_CHANNEL_MONO };
const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { 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_Int(24000),
SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_ENUM_Int(1, 24000),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(channels),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, channels, position),

View file

@ -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[1] = { SPA_AUDIO_CHANNEL_MONO };
const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { 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_Int(32000),
SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_ENUM_Int(1, 32000),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(channels),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, channels, position),

View file

@ -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[1] = { SPA_AUDIO_CHANNEL_MONO };
const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { 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_Int(16000),
SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_ENUM_Int(1, 16000),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(channels),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, channels, position),

View file

@ -309,7 +309,6 @@ 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;
@ -626,17 +625,6 @@ 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);
}

View file

@ -8,8 +8,6 @@
*
*/
#include <bluetooth/bluetooth.h>
#include <spa/utils/string.h>
#include <spa/utils/cleanup.h>
@ -92,7 +90,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, NULL);
res = codec->select_config(codec, 0, caps, caps_size, info, global_settings, config);
if (res < 0)
return false;
@ -102,52 +100,6 @@ 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 {

View file

@ -15,7 +15,6 @@
#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"
@ -27,7 +26,7 @@
#define SPA_TYPE_INTERFACE_Bluez5CodecMedia SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:Media:Private"
#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 16
#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 15
struct spa_bluez5_codec_a2dp {
struct spa_interface iface;
@ -94,7 +93,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). */
@ -104,8 +103,7 @@ 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],
void **config_data);
const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE]);
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);
@ -113,12 +111,9 @@ 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,
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);
struct bap_codec_qos *qos, const struct spa_dict *settings);
/** qsort comparison sorting caps in order of preference for the codec.
* Used in codec switching to select best remote endpoints.
@ -269,44 +264,4 @@ 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

View file

@ -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 > MAX_CHANNELS)
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
return -EINVAL;
if (this->transport && this->transport->iso_io) {

View file

@ -613,10 +613,6 @@ 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.
@ -1443,7 +1439,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 > MAX_CHANNELS)
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
return -EINVAL;
port->frame_size = info.info.raw.channels;

View file

@ -10,10 +10,6 @@
#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 {
@ -36,8 +32,6 @@ struct impl {
struct modem modem;
struct spa_list call_list;
bool pts;
};
struct dbus_cmd_data {
@ -418,26 +412,6 @@ 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);
@ -514,12 +488,8 @@ 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;
@ -946,13 +916,8 @@ 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;
/* 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++) {
for (size_t i = 0; 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)
@ -1085,8 +1050,6 @@ 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);
@ -1096,9 +1059,6 @@ 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");
@ -1116,14 +1076,25 @@ 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;
if (!mm_get_managed_objects(this))
spa_autoptr(DBusMessage) m = dbus_message_new_method_call(MM_DBUS_SERVICE,
"/org/freedesktop/ModemManager1",
DBUS_INTERFACE_OBJECTMANAGER,
"GetManagedObjects");
if (m == NULL)
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);
}

View file

@ -247,12 +247,11 @@ 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)
{
static const DBusObjectPathVTable vtable = {
struct impl *impl;
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