Compare commits

...

43 commits

Author SHA1 Message Date
frogarian
b976d6cc8a Merge branch 'feature/libcamera-controls' into 'master'
propagate libcamera control as pipewire properties

See merge request pipewire/pipewire!2355
2025-10-27 20:29:04 +00:00
Jonas Holmberg
76a31a47c2 module-echo-cancel: Avoid discontinuity
Keep the samples in the ringbuffer that are needed the next cycle to
avoid discontinuity when the aec blocksize is not equal to or divisible
by quantum.
2025-10-27 14:39:29 +01:00
Wim Taymans
23c449af5d test: add test for an array with odd number of items
We have to use the relax version to get the expected container type
correct.
2025-10-27 14:20:25 +01:00
Wim Taymans
94d0d8bc09 spa: add spa_json_init_relax
spa_json_init assumes that we start in an object and always requires a
key/value pair. If the last part is a key, it returns and error and does
not want to return the key value.

This causes problems when parsing AUX0,AUX1,AUX2 or any relaxed array
withand odd number of elements.

Make a new spa_json_init_relax that takes the type of the container
we're assuming we're in and set the state of the parser to array when we
are parsing a relaxed array.

Fixes #4944
2025-10-27 13:32:03 +01:00
Wim Taymans
0276bb5b06 modules: ringbuffer avail is signed 2025-10-27 11:43:04 +01:00
Jonas Holmberg
614186a590 module-echo-cancel: Sync capture and sink buffers
Call process() when capture and sink ringbuffers contain data from the
same graph cycle and only process the latest block from them to avoid
adding latency that can accumulate if one of the streams gets more than
one buffer before the other gets its first buffer when starting up.
2025-10-27 08:43:08 +01:00
Pauli Virtanen
c6d0b364ab spa: param: add size checks for spa_audio_info* structs
In the API that take struct size for spa_audio_info*, also check the
struct size.
2025-10-26 18:23:17 +02:00
Pauli Virtanen
8a23b13798 spa: param: pass correct struct size to spa_format_audio_raw_ext_parse/build 2025-10-26 17:44:03 +02:00
Pauli Virtanen
3d08c0557f properties: fix assign + conditional expression 2025-10-26 14:12:19 +00:00
Pauli Virtanen
68dc45cc62 audioconvert: simplify volume ramp generation
Don't use floating point accumulators, interpolate from sample position.
2025-10-26 14:12:19 +00:00
Pauli Virtanen
b0e308e0dc spa: examples: fix getopt usage + typos in adapter-control 2025-10-26 14:12:19 +00:00
Pauli Virtanen
fe2c62b9b1 meson.build: set project cc flags also for native builds
Use the build flags also for all native build targets.
Avoids spurious warnings in spa-json-dump
2025-10-26 14:12:19 +00:00
Pauli Virtanen
3febf09b85 alsa: fix typoed braces in condition + assign 2025-10-26 14:12:19 +00:00
Pauli Virtanen
93495d3a75 spa: param: infer raw audio channels from position if unset
The behavior before b8eeb2db45 was that spa_audio_info_raw_update()
always sets audio.channels when audio.position is updated.  The new
behavior does not set audio.channels when parsing audio.position.

This breaks things e.g. when combine-sink and loopback nodes are created
with only audio.position specified.

Restore the previous behavior.
2025-10-26 15:47:48 +02:00
Pauli Virtanen
9f1a149876 ci: add file package, for coverity
Try to fix coverity by adding missing 'file' package to container
2025-10-25 12:42:45 +03:00
Wim Taymans
88c65932d8 acp: use global max channels if defined 2025-10-24 17:16:03 +02:00
Wim Taymans
c8d4de5e77 acp: bump max channels to 128 2025-10-24 17:00:42 +02:00
Wim Taymans
c4244a6cf3 string: use spa_strbuf instead of snprintf magic 2025-10-24 17:00:11 +02:00
Wim Taymans
f7c3d37969 fmt-ops: allocate shaper memory dynamically
It is based on the number of channels so allocate it dynamically.
2025-10-24 12:46:38 +02:00
Wim Taymans
d18670d7bb pw-cat: improve channel checks
Make sure we don't use too many channels.
2025-10-24 10:42:05 +02:00
Wim Taymans
aa0272f6f3 treewide: remove some obsolete channel checks
The spa_audio_info can not be parsed with too many channels so there
is always enough space for the positions.
2025-10-24 10:31:45 +02:00
Wim Taymans
78219471ff spa: remove some obsolete functions
The spa_audio_info array now always holds enough positions for all
channels and we don't need to wrap around.
2025-10-24 09:35:59 +02:00
Wim Taymans
6d74eee874 spa: bump channels to 128 again
Remove the SPA_AUDIO_MAX_POSITION define and use the
SPA_AUDIO_MAX_CHANNELS again.

Make a compile time define to override the default max channels of 64.

Make sure we compile the SPA library with the default 64 channels. If
you use the SPA library on a spa_audio_info you will get 64 channel
support, like before. If you want more channels, you will need to make
a padded structure or redefine the MAX_CHANNELS before you use the
spa_audio_info structures. You can use the padded structure with the
new functions that take the structure size.

With the extra checks in the parsing code, we avoid making a
valid spa_audio_info with too many channels that don't fit in the
structure. This means that code that receives a spa_audio_info can
assume there is enough padding for all the channels.
2025-10-24 08:53:21 +02:00
Wim Taymans
be29ae4ef6 audioadapter: add some more debug info when parsing fails 2025-10-23 18:05:22 +02:00
Wim Taymans
5e1e3fca1e modules: handle format parsing errors 2025-10-23 18:01:35 +02:00
Wim Taymans
b8eeb2db45 spa: make it possible to extend the spa_audio_info struct
Add functions that take the size of the spa_audio_info struct in various
functions. We can use this to determine how many channels and channel
positions we can store.

Error out if we try to use more channels than we can fit positions. This
is probably the safest thing to do because most code will blindly try to
get the positions without checking the channel count.

Make sure we also propagate errors to the callers.
2025-10-23 17:59:51 +02:00
Wim Taymans
c5533b3c32 spa: add all channel positions to the params
Pass all the positions of all channels to the format param, even when
there are more channels then positions.
2025-10-22 13:26:52 +02:00
Wim Taymans
11f1298f53 spa: make a function to make a channel short name
Make a function that can generate and parse a short name for
the positions that are not in the type list, like the AUX channels.
2025-10-22 13:04:53 +02:00
Wim Taymans
7177f8269d bluez: use function to get the channel position from a name 2025-10-22 12:54:30 +02:00
Wim Taymans
6465a63bf6 spa: parse the audio.position completetly
Parse the audio.position spec completely so that we have the right
number of channels but only store the first max_position channels.

Also rename some field to make it clear that this is about the max
number of channel positions.
2025-10-22 12:47:00 +02:00
Wim Taymans
ae50bb5dc0 audio: don't limit channels to max positions
We can have more channels than we have positions.
2025-10-22 09:39:15 +02:00
Wim Taymans
99bbac9cbf spa: increase SPA_AUDIO_MAX_CHANNELS to 128
This should now not change the ABI because the position array size is
now controlled with the SPA_AUDIO_MAX_POSITION constant.
2025-10-21 17:01:31 +02:00
Wim Taymans
f19b075306 spa: add SPA_AUDIO_MAX_POSITION
Add a new SPA_AUDIO_MAX_POSITION constant with the maximum number of
channel positions that can be kept in the various audio_info structures.

Repurpose the SPA_AUDIO_MAX_CHANNELS as a suggestion for applications
for the max allowed number of channels in the system. Make it possible
to make this a compile time constant.
2025-10-21 16:08:24 +02:00
Wim Taymans
dbc5c81e4a spa: avoid using SPA_AUDIO_MAX_CHANNELS
Use SPA_N_ELEMENTS instead of the array we try to handle.
2025-10-21 16:05:33 +02:00
Wim Taymans
818d1435ce treewide: access the position information using helpers
Make sure we don't access out of bounds and that we use the helpers
wherever we can to access the position information.
2025-10-21 13:06:25 +02:00
Wim Taymans
8bbca3b8f3 spa: add spa_audio_parse_position_n
Add a function that accepts the size of the position array when reading
the audio positions. This makes it possible to decouple the position
array size from SPA_AUDIO_MAX_CHANNELS.

Also use SPA_N_ELEMENTS to pass the number of array elements to
functions instead of a fixed constant. This makes it easier to change
the array size later to a different constant without having to patch up
all the places where the size is used.
2025-10-21 09:59:13 +02:00
Wim Taymans
9e7cae13df alsa: use the amount of positions we will write 2025-10-21 09:43:59 +02:00
Wim Taymans
13b8c23767 Don't use SPA_AUDIO_MAX_CHANNELS directly
Make a MAX_CHANNELS define and use that one in code. This makes it
easier to change the constant later.
2025-10-21 09:43:06 +02:00
Wim Taymans
eb096bfb62 spa: provide information about channels > SPA_AUDIO_MAX_CHANNELS
Define some rules for how the position information works for channels >
SPA_AUDIO_MAX_CHANNELS. We basically wrap around and incrementing the
AUX channel counters. Make a function to implement this.
2025-10-21 09:40:08 +02:00
Wim Taymans
ede13a8cb5 spa: don't add more channels than we have positions 2025-10-20 18:31:35 +02:00
Wim Taymans
f453b1545d audio: don't use SPA_AUDIO_MAX_CHANNELS in some places
When we know the max size of the array, just use this instead of the
SPA_AUDIO_MAX_CHANNELS constant.
2025-10-20 18:31:17 +02:00
Wim Taymans
c94aff8cae Revert "audio: bump max channels to 128"
This reverts commit c91f75ae2e.

This change causes a subtle ABI change and also breaks the Rust
bindings.
2025-10-20 09:17:26 +02:00
van Veen, Stephan
36e8f1553f propagate libcamera control as pipewire properties
So far the libcamera plugin only offers device and device name as properties. To be able to control camera parameters this patch maps libcamera controls onto properties.

Upstream-Status: Pending
2025-09-09 10:33:04 +02:00
88 changed files with 1146 additions and 540 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-15.0'
FDO_DISTRIBUTION_TAG: '2025-10-22.0'
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_PACKAGES: >-
alsa-lib-devel
@ -48,6 +48,7 @@ include:
dbus-devel
doxygen
fdk-aac-free-devel
file
findutils
gcc
gcc-c++

View file

@ -115,10 +115,11 @@ cc_flags = common_flags + [
'-Werror=old-style-definition',
'-Werror=missing-parameter-type',
'-Werror=strict-prototypes',
'-DSPA_AUDIO_MAX_CHANNELS=128u',
]
add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c')
cc_flags_native = cc_native.get_supported_arguments(cc_flags)
add_project_arguments(cc_native.get_supported_arguments(cc_flags),
language: 'c', native: true)
have_cpp = add_languages('cpp', native: false, required : false)

View file

@ -22,9 +22,11 @@ PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.ctl");
#define VOLUME_MIN ((uint32_t) 0U)
#define VOLUME_MAX ((uint32_t) 0x10000U)
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
struct volume {
uint32_t channels;
long values[SPA_AUDIO_MAX_CHANNELS];
long values[MAX_CHANNELS];
};
typedef struct {
@ -498,7 +500,7 @@ static struct spa_pod *build_volume_mute(struct spa_pod_builder *b, struct volum
spa_pod_builder_push_object(b, &f[0],
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
if (volume) {
float volumes[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
uint32_t i, n_volumes = 0;
n_volumes = volume->channels;
@ -850,11 +852,11 @@ static void parse_props(struct global *g, const struct spa_pod *param, bool devi
break;
case SPA_PROP_channelVolumes:
{
float volumes[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
uint32_t n_volumes, i;
n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
volumes, SPA_AUDIO_MAX_CHANNELS);
volumes, SPA_N_ELEMENTS(volumes));
g->node.channel_volume.channels = n_volumes;
for (i = 0; i < n_volumes; i++)

View file

@ -31,6 +31,7 @@ PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.pcm");
#define MAX_BUFFERS 64u
#define MAX_RATE (48000*8)
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define MIN_PERIOD 64
@ -642,7 +643,7 @@ static int snd_pcm_pipewire_pause(snd_pcm_ioplug_t * io, int enable)
#define _FORMAT_BE(p, fmt) p ? SPA_AUDIO_FORMAT_UNKNOWN : SPA_AUDIO_FORMAT_ ## fmt ## _OE
#endif
static int set_default_channels(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS])
static int set_default_channels(uint32_t channels, uint32_t position[MAX_CHANNELS])
{
switch (channels) {
case 8:
@ -915,12 +916,16 @@ static int snd_pcm_pipewire_set_chmap(snd_pcm_ioplug_t * io,
default:
return -EINVAL;
}
if (map->channels > MAX_CHANNELS)
return -ENOTSUP;
for (i = 0; i < map->channels; i++) {
char buf[8];
position[i] = chmap_to_channel(map->pos[i]);
pw_log_debug("map %d: %s / %s", i,
snd_pcm_chmap_name(map->pos[i]),
spa_debug_type_find_short_name(spa_type_audio_channel,
position[i]));
spa_type_audio_channel_make_short_name(position[i],
buf, sizeof(buf), "UNK"));
}
return 1;
}
@ -1097,7 +1102,7 @@ struct param_info infos[] = {
{ "alsa.rate", SND_PCM_IOPLUG_HW_RATE, TYPE_MIN_MAX,
{ 1, MAX_RATE }, 2, collect_int },
{ "alsa.channels", SND_PCM_IOPLUG_HW_CHANNELS, TYPE_MIN_MAX,
{ 1, SPA_AUDIO_MAX_CHANNELS }, 2, collect_int },
{ 1, MAX_CHANNELS }, 2, collect_int },
{ "alsa.buffer-bytes", SND_PCM_IOPLUG_HW_BUFFER_BYTES, TYPE_MIN_MAX,
{ MIN_BUFFER_BYTES, MAX_BUFFER_BYTES }, 2, collect_int },
{ "alsa.period-bytes", SND_PCM_IOPLUG_HW_PERIOD_BYTES, TYPE_MIN_MAX,

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

View file

@ -42,7 +42,7 @@ spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_d
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
if (position == NULL ||
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS))
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_N_ELEMENTS(info->position)))
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
return res;

View file

@ -46,20 +46,61 @@ extern "C" {
#endif
#endif
SPA_API_AUDIO_FORMAT_UTILS bool
spa_format_audio_ext_valid_size(uint32_t media_subtype, size_t size)
{
switch (media_subtype) {
case SPA_MEDIA_SUBTYPE_raw:
return size >= offsetof(struct spa_audio_info, info.raw) &&
SPA_AUDIO_INFO_RAW_VALID_SIZE(size - offsetof(struct spa_audio_info, info.raw));
#define _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(format) \
case SPA_MEDIA_SUBTYPE_ ## format: \
return size >= offsetof(struct spa_audio_info, info.format) + sizeof(struct spa_audio_info_ ## format);
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(dsp)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(iec958)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(dsd)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(mp3)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(aac)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(vorbis)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(wma)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(ra)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(amr)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(alac)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(flac)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(ape)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(ac3)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(eac3)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(truehd)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(dts)
_SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(mpegh)
#undef _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE
}
return false;
}
SPA_API_AUDIO_FORMAT_UTILS int
spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info)
spa_format_audio_ext_parse(const struct spa_pod *format, struct spa_audio_info *info, size_t size)
{
int res;
uint32_t media_type, media_subtype;
if ((res = spa_format_parse(format, &info->media_type, &info->media_subtype)) < 0)
if ((res = spa_format_parse(format, &media_type, &media_subtype)) < 0)
return res;
if (info->media_type != SPA_MEDIA_TYPE_audio)
if (media_type != SPA_MEDIA_TYPE_audio)
return -EINVAL;
switch (info->media_subtype) {
if (!spa_format_audio_ext_valid_size(media_subtype, size))
return -EINVAL;
info->media_type = media_type;
info->media_subtype = media_subtype;
switch (media_subtype) {
case SPA_MEDIA_SUBTYPE_raw:
return spa_format_audio_raw_parse(format, &info->info.raw);
return spa_format_audio_raw_ext_parse(format, &info->info.raw,
size - offsetof(struct spa_audio_info, info.raw));
case SPA_MEDIA_SUBTYPE_dsp:
return spa_format_audio_dsp_parse(format, &info->info.dsp);
case SPA_MEDIA_SUBTYPE_iec958:
@ -98,13 +139,25 @@ spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info
return -ENOTSUP;
}
SPA_API_AUDIO_FORMAT_UTILS struct spa_pod *
spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
const struct spa_audio_info *info)
SPA_API_AUDIO_FORMAT_UTILS int
spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info)
{
return spa_format_audio_ext_parse(format, info, sizeof(*info));
}
SPA_API_AUDIO_FORMAT_UTILS struct spa_pod *
spa_format_audio_ext_build(struct spa_pod_builder *builder, uint32_t id,
const struct spa_audio_info *info, size_t size)
{
if (!spa_format_audio_ext_valid_size(info->media_subtype, size)) {
errno = EINVAL;
return NULL;
}
switch (info->media_subtype) {
case SPA_MEDIA_SUBTYPE_raw:
return spa_format_audio_raw_build(builder, id, &info->info.raw);
return spa_format_audio_raw_ext_build(builder, id, &info->info.raw,
size - offsetof(struct spa_audio_info, info.raw));
case SPA_MEDIA_SUBTYPE_dsp:
return spa_format_audio_dsp_build(builder, id, &info->info.dsp);
case SPA_MEDIA_SUBTYPE_iec958:
@ -143,6 +196,13 @@ spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
errno = ENOTSUP;
return NULL;
}
SPA_API_AUDIO_FORMAT_UTILS struct spa_pod *
spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
const struct spa_audio_info *info)
{
return spa_format_audio_ext_build(builder, id, info, sizeof(*info));
}
/**
* \}
*/

View file

@ -59,6 +59,8 @@ struct spa_audio_info {
struct spa_audio_info_dts dts;
struct spa_audio_info_mpegh mpegh;
} info;
/* padding follows here when info has flexible size */
};
/**

View file

@ -28,8 +28,8 @@ extern "C" {
#endif
SPA_API_AUDIO_RAW_JSON int
spa_audio_parse_position(const char *str, size_t len,
uint32_t *position, uint32_t *n_channels)
spa_audio_parse_position_n(const char *str, size_t len,
uint32_t *position, uint32_t max_position, uint32_t *n_channels)
{
struct spa_json iter;
char v[256];
@ -38,18 +38,32 @@ spa_audio_parse_position(const char *str, size_t len,
if (spa_json_begin_array_relax(&iter, str, len) <= 0)
return 0;
while (spa_json_get_string(&iter, v, sizeof(v)) > 0 &&
channels < SPA_AUDIO_MAX_CHANNELS) {
position[channels++] = spa_type_audio_channel_from_short_name(v);
while (spa_json_get_string(&iter, v, sizeof(v)) > 0) {
if (channels < max_position)
position[channels] = spa_type_audio_channel_from_short_name(v);
channels++;
}
*n_channels = channels;
return channels;
}
SPA_API_AUDIO_RAW_JSON int
spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, const char *val, bool force)
spa_audio_parse_position(const char *str, size_t len,
uint32_t *position, uint32_t *n_channels)
{
return spa_audio_parse_position_n(str, len, position, SPA_AUDIO_MAX_CHANNELS, n_channels);
}
SPA_API_AUDIO_RAW_JSON int
spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
const char *key, const char *val, bool force)
{
uint32_t v;
uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size);
if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size))
return -EINVAL;
if (spa_streq(key, SPA_KEY_AUDIO_FORMAT)) {
if (force || info->format == 0)
info->format = (enum spa_audio_format)spa_type_audio_format_from_short_name(val);
@ -57,41 +71,88 @@ spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, cons
if (spa_atou32(val, &v, 0) && (force || info->rate == 0))
info->rate = v;
} else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) {
if (spa_atou32(val, &v, 0) && (force || info->channels == 0))
info->channels = SPA_MIN(v, SPA_AUDIO_MAX_CHANNELS);
if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) {
if (v > max_position)
return -ECHRNG;
info->channels = v;
}
} else if (spa_streq(key, SPA_KEY_AUDIO_POSITION)) {
if (force || info->channels == 0) {
if (spa_audio_parse_position(val, strlen(val), info->position, &info->channels) > 0)
if (spa_audio_parse_position_n(val, strlen(val), info->position,
max_position, &v) > 0) {
if (v > max_position)
return -ECHRNG;
info->channels = v;
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
}
}
}
return 0;
}
SPA_API_AUDIO_RAW_JSON int
spa_audio_info_raw_update(struct spa_audio_info_raw *info,
const char *key, const char *val, bool force)
{
return spa_audio_info_raw_ext_update(info, sizeof(*info), key, val, force);
}
SPA_API_AUDIO_RAW_JSON int
spa_audio_info_raw_ext_init_dict_keys_va(struct spa_audio_info_raw *info, size_t size,
const struct spa_dict *defaults,
const struct spa_dict *dict, va_list args)
{
int res;
if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size))
return -EINVAL;
memset(info, 0, size);
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
if (dict) {
const char *val, *key;
while ((key = va_arg(args, const char *))) {
if ((val = spa_dict_lookup(dict, key)) == NULL)
continue;
if ((res = spa_audio_info_raw_ext_update(info, size,
key, val, true)) < 0)
return res;
}
}
if (defaults) {
const struct spa_dict_item *it;
spa_dict_for_each(it, defaults)
if ((res = spa_audio_info_raw_ext_update(info, size,
it->key, it->value, false)) < 0)
return res;
}
return 0;
}
SPA_API_AUDIO_RAW_JSON int SPA_SENTINEL
spa_audio_info_raw_ext_init_dict_keys(struct spa_audio_info_raw *info, size_t size,
const struct spa_dict *defaults,
const struct spa_dict *dict, ...)
{
va_list args;
int res;
va_start(args, dict);
res = spa_audio_info_raw_ext_init_dict_keys_va(info, size, defaults, dict, args);
va_end(args);
return res;
}
SPA_API_AUDIO_RAW_JSON int SPA_SENTINEL
spa_audio_info_raw_init_dict_keys(struct spa_audio_info_raw *info,
const struct spa_dict *defaults,
const struct spa_dict *dict, ...)
{
spa_zero(*info);
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
if (dict) {
const char *val, *key;
va_list args;
va_start(args, dict);
while ((key = va_arg(args, const char *))) {
if ((val = spa_dict_lookup(dict, key)) == NULL)
continue;
spa_audio_info_raw_update(info, key, val, true);
}
va_end(args);
}
if (defaults) {
const struct spa_dict_item *it;
spa_dict_for_each(it, defaults)
spa_audio_info_raw_update(info, it->key, it->value, false);
}
return 0;
va_list args;
int res;
va_start(args, dict);
res = spa_audio_info_raw_ext_init_dict_keys_va(info, sizeof(*info), defaults, dict, args);
va_end(args);
return res;
}
/**

View file

@ -267,12 +267,34 @@ static const struct spa_type_info spa_type_audio_channel[] = {
SPA_API_AUDIO_RAW_TYPES uint32_t spa_type_audio_channel_from_short_name(const char *name)
{
return spa_type_from_short_name(name, spa_type_audio_channel, SPA_AUDIO_CHANNEL_UNKNOWN);
uint32_t res;
if (spa_strstartswith(name, "AUX")) {
if (spa_atou32(name+3, &res, 10) && res < 0x1000)
res = SPA_AUDIO_CHANNEL_AUX0 + res;
else
res = SPA_AUDIO_CHANNEL_UNKNOWN;
} else {
res = spa_type_from_short_name(name, spa_type_audio_channel, SPA_AUDIO_CHANNEL_UNKNOWN);
}
return res;
}
SPA_API_AUDIO_RAW_TYPES const char * spa_type_audio_channel_to_short_name(uint32_t type)
{
return spa_type_to_short_name(type, spa_type_audio_channel, "UNK");
}
SPA_API_AUDIO_RAW_TYPES const char * spa_type_audio_channel_make_short_name(uint32_t type,
char *buf, size_t size, const char *unknown)
{
if (SPA_AUDIO_CHANNEL_IS_AUX(type)) {
snprintf(buf, size, "AUX%u", type - SPA_AUDIO_CHANNEL_AUX0);
} else {
const char *str = spa_type_to_short_name(type, spa_type_audio_channel, NULL);
if (str == NULL)
return unknown;
snprintf(buf, size, "%.7s", str);
}
return buf;
}
#define SPA_TYPE_INFO_AudioVolumeRampScale SPA_TYPE_INFO_ENUM_BASE "AudioVolumeRampScale"
#define SPA_TYPE_INFO_AUDIO_VOLUME_RAMP_SCALE_BASE SPA_TYPE_INFO_AudioVolumeRampScale ":"

View file

@ -29,10 +29,15 @@ extern "C" {
#endif
SPA_API_AUDIO_RAW_UTILS int
spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info)
spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_info_raw *info, size_t size)
{
struct spa_pod *position = NULL;
int res;
uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size);
if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size))
return -EINVAL;
info->flags = 0;
res = spa_pod_parse_object(format,
SPA_TYPE_OBJECT_Format, NULL,
@ -40,18 +45,33 @@ spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_r
SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate),
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
if (info->channels > max_position)
return -ECHRNG;
if (position == NULL ||
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS))
spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels)
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
return res;
}
SPA_API_AUDIO_RAW_UTILS int
spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info)
{
return spa_format_audio_raw_ext_parse(format, info, sizeof(*info));
}
SPA_API_AUDIO_RAW_UTILS struct spa_pod *
spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
const struct spa_audio_info_raw *info)
spa_format_audio_raw_ext_build(struct spa_pod_builder *builder, uint32_t id,
const struct spa_audio_info_raw *info, size_t size)
{
struct spa_pod_frame f;
uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size);
if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size)) {
errno = EINVAL;
return NULL;
}
spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
spa_pod_builder_add(builder,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
@ -66,7 +86,10 @@ spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
if (info->channels != 0) {
spa_pod_builder_add(builder,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels), 0);
if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) {
/* we drop the positions here when we can't read all of them. This is
* really a malformed spa_audio_info structure. */
if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED) &&
max_position > info->channels) {
spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position,
SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id,
info->channels, info->position), 0);
@ -75,6 +98,13 @@ spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
}
SPA_API_AUDIO_RAW_UTILS struct spa_pod *
spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
const struct spa_audio_info_raw *info)
{
return spa_format_audio_raw_ext_build(builder, id, info, sizeof(*info));
}
/**
* \}
*/

View file

@ -18,7 +18,11 @@ extern "C" {
* \{
*/
#define SPA_AUDIO_MAX_CHANNELS 128u
/* This is the max number of channels, changing this will change the
* size of some helper structures. This value should be at least 64 */
#ifndef SPA_AUDIO_MAX_CHANNELS
#define SPA_AUDIO_MAX_CHANNELS 64u
#endif
enum spa_audio_format {
SPA_AUDIO_FORMAT_UNKNOWN,
@ -259,6 +263,8 @@ enum spa_audio_channel {
SPA_AUDIO_CHANNEL_START_Custom = 0x10000,
};
#define SPA_AUDIO_CHANNEL_IS_AUX(ch) ((ch)>=SPA_AUDIO_CHANNEL_START_Aux && (ch)<=SPA_AUDIO_CHANNEL_LAST_Aux)
enum spa_audio_volume_ramp_scale {
SPA_AUDIO_VOLUME_RAMP_INVALID,
SPA_AUDIO_VOLUME_RAMP_LINEAR,
@ -269,17 +275,27 @@ enum spa_audio_volume_ramp_scale {
#define SPA_AUDIO_FLAG_NONE (0) /*< no valid flag */
#define SPA_AUDIO_FLAG_UNPOSITIONED (1 << 0) /*< the position array explicitly
* contains unpositioned channels. */
/** Audio information description */
/** Audio information description. You can assume when you receive this structure
* that there is enought padding to accomodate all channel positions in case the
* channel count is more than SPA_AUDIO_MAX_CHANNELS. */
struct spa_audio_info_raw {
enum spa_audio_format format; /*< format, one of enum spa_audio_format */
uint32_t flags; /*< extra flags */
uint32_t rate; /*< sample rate */
uint32_t channels; /*< number of channels */
uint32_t channels; /*< number of channels. This can be more than SPA_AUDIO_MAX_CHANNELS
* and you may assume there is enough padding for the extra
* channel positions. */
uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */
/* padding follows here when channels > SPA_AUDIO_MAX_CHANNELS */
};
#define SPA_AUDIO_INFO_RAW_INIT(...) ((struct spa_audio_info_raw) { __VA_ARGS__ })
#define SPA_AUDIO_INFO_RAW_MAX_POSITION(size) (((size)-offsetof(struct spa_audio_info_raw,position))/sizeof(uint32_t))
#define SPA_AUDIO_INFO_RAW_VALID_SIZE(size) ((size) >= offsetof(struct spa_audio_info_raw, position))
#define SPA_KEY_AUDIO_FORMAT "audio.format" /**< an audio format as string,
* Ex. "S16LE" */
#define SPA_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel as string,

View file

@ -54,6 +54,15 @@ SPA_API_JSON void spa_json_init(struct spa_json * iter, const char *data, size_t
{
*iter = SPA_JSON_INIT(data, size);
}
#define SPA_JSON_INIT_RELAX(type,data,size) \
((struct spa_json) { (data), (data)+(size), NULL, (uint32_t)((type) == '[' ? 0x10 : 0x0), 0 })
SPA_API_JSON void spa_json_init_relax(struct spa_json * iter, char type, const char *data, size_t size)
{
*iter = SPA_JSON_INIT_RELAX(type, data, size);
}
#define SPA_JSON_ENTER(iter) ((struct spa_json) { (iter)->cur, (iter)->end, (iter), (iter)->state & 0xff0, 0 })
SPA_API_JSON void spa_json_enter(struct spa_json * iter, struct spa_json * sub)

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

View file

@ -1,4 +1,6 @@
#undef SPA_AUDIO_MAX_CHANNELS
#define SPA_API_IMPL SPA_EXPORT
#include <spa/utils/defs.h>
#include <spa/buffer/alloc.h>
@ -165,9 +167,3 @@
#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[2048];
char pos[PA_CHANNELS_MAX*8];
struct spa_strbuf b;
int i;
@ -1142,8 +1142,9 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask)
pa_proplist_unset(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED);
} else {
uint32_t positions[eld.lpcm_channels];
char position[64];
int i = 0, pos = 0;
char position[eld.lpcm_channels * 8];
struct spa_strbuf b;
int i = 0;
if (eld.speakers & 0x01) {
positions[i++] = ACP_CHANNEL_FL;
@ -1172,16 +1173,14 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask)
if (eld.speakers & 0x10) {
positions[i++] = ACP_CHANNEL_RC;
}
while (i < eld.lpcm_channels)
positions[i++] = ACP_CHANNEL_UNKNOWN;
for (i = 0, pos = 0; i < eld.lpcm_channels; i++) {
pos += snprintf(&position[pos], sizeof(position) - pos, "%s,", channel_names[positions[i]]);
}
/* Overwrite trailing , */
position[pos - 1] = 0;
spa_strbuf_init(&b, position, sizeof(position));
spa_strbuf_append(&b, "[");
for (i = 0; i < eld.lpcm_channels; i++)
spa_strbuf_append(&b, "%s%s", i ? "," : "", channel_names[positions[i]]);
spa_strbuf_append(&b, "]");
changed |= (old_position == NULL) || (!spa_streq(old_position, position));
pa_proplist_sets(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED, position);

View file

@ -27,7 +27,11 @@
extern "C" {
#endif
#ifdef SPA_AUDIO_MAX_CHANNELS
#define PA_CHANNELS_MAX ((int)SPA_AUDIO_MAX_CHANNELS)
#else
#define PA_CHANNELS_MAX 64
#endif
#define PA_CHANNEL_MAP_SNPRINT_MAX (PA_CHANNELS_MAX * 32)
@ -451,7 +455,6 @@ static inline int pa_channel_map_equal(const pa_channel_map *a, const pa_channel
static inline char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
unsigned channel;
bool first = true;
char *e;
if (!pa_channel_map_valid(map)) {
pa_snprintf(s, l, "%s", _("(invalid)"));
@ -460,12 +463,10 @@ static inline char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_m
*(e = s) = 0;
for (channel = 0; channel < map->channels && l > 1; channel++) {
l -= pa_snprintf(e, l, "%s%s",
first ? "" : ",",
channel == 0 ? "" : ",",
pa_channel_position_to_string(map->map[channel]));
e = strchr(e, 0);
first = false;
}
return s;
}

View file

@ -38,6 +38,7 @@
extern struct spa_i18n *acp_i18n;
#define MAX_POLL 16
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define DEFAULT_DEVICE "hw:0"
#define DEFAULT_AUTO_PROFILE true
@ -155,12 +156,13 @@ static int emit_node(struct impl *this, struct acp_device *dev)
const struct acp_dict_item *it;
uint32_t n_items, i;
char device_name[128], path[210], channels[16], ch[12], routes[16];
char card_index[16], card_name[64], *p;
char positions[SPA_AUDIO_MAX_CHANNELS * 12];
char card_index[16], card_name[64];
char positions[MAX_CHANNELS * 12];
char codecs[512];
struct spa_device_object_info info;
struct acp_card *card = this->card;
const char *stream, *card_id, *bus;
struct spa_strbuf b;
info = SPA_DEVICE_OBJECT_INFO_INIT();
info.type = SPA_TYPE_INTERFACE_Node;
@ -199,11 +201,13 @@ static int emit_node(struct impl *this, struct acp_device *dev)
snprintf(channels, sizeof(channels), "%d", dev->format.channels);
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNELS, channels);
p = positions;
spa_strbuf_init(&b, positions, sizeof(positions));
spa_strbuf_append(&b, "[");
for (i = 0; i < dev->format.channels; i++) {
p += snprintf(p, 12, "%s%s", i == 0 ? "" : ",",
spa_strbuf_append(&b, "%s%s", i == 0 ? " " : ", ",
acp_channel_str(ch, sizeof(ch), dev->format.map[i]));
}
spa_strbuf_append(&b, " ]");
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_POSITION, positions);
if (dev->n_codecs > 0) {
@ -673,8 +677,8 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct
struct spa_pod_prop *prop;
struct spa_pod_object *obj = (struct spa_pod_object *) props;
int changed = 0;
float volumes[SPA_AUDIO_MAX_CHANNELS];
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
uint32_t channels[MAX_CHANNELS];
uint32_t n_volumes = 0;
if (!spa_pod_is_object_type(props, SPA_TYPE_OBJECT_Props))
@ -696,13 +700,13 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct
break;
case SPA_PROP_channelVolumes:
if ((n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
volumes, SPA_N_ELEMENTS(volumes))) > 0) {
changed++;
}
break;
case SPA_PROP_channelMap:
if (spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
channels, SPA_AUDIO_MAX_CHANNELS) > 0) {
channels, SPA_N_ELEMENTS(channels)) > 0) {
changed++;
}
break;

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 > SPA_AUDIO_MAX_CHANNELS) {
if (state->default_channels > MAX_CHANNELS) {
spa_log_warn(state->log, "%p: %s: %s > %d, clamping",
state, k, s, SPA_AUDIO_MAX_CHANNELS);
state->default_channels = SPA_AUDIO_MAX_CHANNELS;
state, k, s, MAX_CHANNELS);
state->default_channels = MAX_CHANNELS;
}
fmt_change++;
} else if (spa_streq(k, SPA_KEY_AUDIO_RATE)) {
@ -240,35 +240,33 @@ static int alsa_set_param(struct state *state, const char *k, const char *s)
static int position_to_string(struct channel_map *map, char *val, size_t len)
{
uint32_t i, o = 0;
int r;
o += snprintf(val, len, "[ ");
for (i = 0; i < map->channels; i++) {
r = snprintf(val+o, len-o, "%s%s", i == 0 ? "" : ", ",
spa_debug_type_find_short_name(spa_type_audio_channel,
map->pos[i]));
if (r < 0 || o + r >= len)
return -ENOSPC;
o += r;
uint32_t i;
char pos[8];
struct spa_strbuf b;
spa_strbuf_init(&b, val, len);
spa_strbuf_append(&b, "[");
for (i = 0; i < map->n_pos; i++) {
spa_strbuf_append(&b, "%s%s", i == 0 ? " " : ", ",
spa_type_audio_channel_make_short_name(map->pos[i],
pos, sizeof(pos), "UNK"));
}
if (len > o)
o += snprintf(val+o, len-o, " ]");
if (spa_strbuf_append(&b, " ]") < 2)
return -ENOSPC;
return 0;
}
static int uint32_array_to_string(uint32_t *vals, uint32_t n_vals, char *val, size_t len)
{
uint32_t i, o = 0;
int r;
o += snprintf(val, len, "[ ");
for (i = 0; i < n_vals; i++) {
r = snprintf(val+o, len-o, "%s%d", i == 0 ? "" : ", ", vals[i]);
if (r < 0 || o + r >= len)
return -ENOSPC;
o += r;
}
if (len > o)
o += snprintf(val+o, len-o, " ]");
uint32_t i;
struct spa_strbuf b;
spa_strbuf_init(&b, val, len);
spa_strbuf_append(&b, "[");
for (i = 0; i < n_vals; i++)
spa_strbuf_append(&b, "%s%d", i == 0 ? " " : ", ", vals[i]);
if (spa_strbuf_append(&b, " ]") < 2)
return -ENOSPC;
return 0;
}
@ -775,7 +773,7 @@ static void bind_ctl_event(struct spa_source *source)
snd_ctl_elem_id_alloca(&bound_id);
snd_ctl_elem_value_alloca(&old_value);
while ((err = snd_ctl_read(state->ctl, ev) > 0)) {
while ((err = snd_ctl_read(state->ctl, ev)) > 0) {
bool changed = false;
if (snd_ctl_event_get_type(ev) != SND_CTL_EVENT_ELEM)
@ -1584,8 +1582,8 @@ static int add_channels(struct state *state, bool all, uint32_t index, uint32_t
spa_log_debug(state->log, "channels (%d %d) default:%d all:%d",
min, max, state->default_channels, all);
min = SPA_MIN(min, SPA_AUDIO_MAX_CHANNELS);
max = SPA_MIN(max, SPA_AUDIO_MAX_CHANNELS);
min = SPA_MIN(min, MAX_CHANNELS);
max = SPA_MIN(max, MAX_CHANNELS);
if (state->default_channels != 0 && !all) {
if (min > state->default_channels ||
@ -1645,7 +1643,7 @@ skip_channels:
} else {
const struct channel_map *map = NULL;
spa_pod_builder_int(b, min);
if (state->default_pos.channels == min) {
if (state->default_pos.n_pos == min) {
map = &state->default_pos;
spa_log_debug(state->log, "%p: using provided default", state);
} else if (min <= 8) {
@ -1655,7 +1653,7 @@ skip_channels:
if (map) {
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0);
spa_pod_builder_push_array(b, &f[0]);
for (i = 0; i < map->channels; i++) {
for (i = 0; i < map->n_pos; i++) {
spa_log_debug(state->log, "%p: position %zd %d", state, i, map->pos[i]);
spa_pod_builder_id(b, map->pos[i]);
}

View file

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

View file

@ -808,6 +808,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
if (spa_format_audio_parse(param, &info) < 0) {
spa_log_error(this->log, "%p: cannot set Format param: "
"parsing the POD failed", this);
spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 0, NULL, param);
return -EINVAL;
}
if (info.media_subtype != SPA_MEDIA_SUBTYPE_raw) {
@ -841,6 +842,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
SPA_PARAM_PORT_CONFIG_format, SPA_POD_OPT_Pod(&format)) < 0) {
spa_log_error(this->log, "%p: cannot set PortConfig param: "
"parsing the POD failed", this);
spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 0, NULL, param);
return -EINVAL;
}
@ -848,8 +850,12 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
struct spa_audio_info info;
spa_zero(info);
if ((res = spa_format_audio_parse(format, &info)) < 0)
if ((res = spa_format_audio_parse(format, &info)) < 0) {
spa_log_error(this->log, "%p: cannot set PortConfig param: "
"parsing format failed: %s", this, spa_strerror(res));
spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 0, NULL, format);
return res;
}
if (info.media_subtype == SPA_MEDIA_SUBTYPE_raw) {
info.info.raw.rate = 0;
@ -2046,11 +2052,12 @@ static int do_auto_port_config(struct impl *this, const char *str)
return -ENOENT;
if (format.media_subtype == SPA_MEDIA_SUBTYPE_raw) {
uint32_t n_pos = SPA_MIN(SPA_N_ELEMENTS(format.info.raw.position), format.info.raw.channels);
if (position == POSITION_AUX) {
for (i = 0; i < format.info.raw.channels; i++)
for (i = 0; i < n_pos; i++)
format.info.raw.position[i] = SPA_AUDIO_CHANNEL_START_Aux + i;
} else if (position == POSITION_UNKNOWN) {
for (i = 0; i < format.info.raw.channels; i++)
for (i = 0; i < n_pos; i++)
format.info.raw.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
}
}

View file

@ -47,10 +47,11 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert");
#define DEFAULT_RATE 48000
#define DEFAULT_CHANNELS 2
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define MAX_ALIGN FMT_OPS_MAX_ALIGN
#define MAX_BUFFERS 32
#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS
#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1)
#define MAX_DATAS MAX_CHANNELS
#define MAX_PORTS (MAX_CHANNELS+1)
#define MAX_STAGES 64
#define MAX_GRAPH 9 /* 8 active + 1 replacement slot */
@ -62,7 +63,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert");
struct volumes {
bool mute;
uint32_t n_volumes;
float volumes[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
};
static void init_volumes(struct volumes *vol)
@ -70,7 +71,7 @@ static void init_volumes(struct volumes *vol)
uint32_t i;
vol->mute = DEFAULT_MUTE;
vol->n_volumes = 0;
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
for (i = 0; i < MAX_CHANNELS; i++)
vol->volumes[i] = DEFAULT_VOLUME;
}
@ -91,7 +92,7 @@ struct props {
float max_volume;
float prev_volume;
uint32_t n_channels;
uint32_t channel_map[SPA_AUDIO_MAX_CHANNELS];
uint32_t channel_map[MAX_CHANNELS];
struct volumes channel;
struct volumes soft;
struct volumes monitor;
@ -112,7 +113,7 @@ static void props_reset(struct props *props)
props->min_volume = DEFAULT_MIN_VOLUME;
props->max_volume = DEFAULT_MAX_VOLUME;
props->n_channels = 0;
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
for (i = 0; i < MAX_CHANNELS; i++)
props->channel_map[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
init_volumes(&props->channel);
init_volumes(&props->soft);
@ -241,9 +242,9 @@ struct filter_graph {
struct spa_filter_graph *graph;
struct spa_hook listener;
uint32_t n_inputs;
uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t inputs_position[MAX_CHANNELS];
uint32_t n_outputs;
uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t outputs_position[MAX_CHANNELS];
uint32_t latency;
bool removing;
bool setup;
@ -423,7 +424,6 @@ static int init_port(struct impl *this, enum spa_direction direction, uint32_t p
uint32_t position, bool is_dsp, bool is_monitor, bool is_control)
{
struct port *port = GET_PORT(this, direction, port_id);
const char *name;
spa_assert(port_id < MAX_PORTS);
@ -438,8 +438,7 @@ static int init_port(struct impl *this, enum spa_direction direction, uint32_t p
port->latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
port->latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
name = spa_debug_type_find_short_name(spa_type_audio_channel, position);
snprintf(port->position, sizeof(port->position), "%s", name ? name : "UNK");
spa_type_audio_channel_make_short_name(position, port->position, sizeof(port->position), "UNK");
port->info = SPA_PORT_INFO_INIT();
port->info.change_mask = port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
@ -1103,11 +1102,11 @@ static void graph_info(void *object, const struct spa_filter_graph_info *info)
else if (spa_streq(k, "n_outputs"))
spa_atou32(s, &g->n_outputs, 0);
else if (spa_streq(k, "inputs.audio.position"))
spa_audio_parse_position(s, strlen(s),
g->inputs_position, &g->n_inputs);
spa_audio_parse_position_n(s, strlen(s), g->inputs_position,
SPA_N_ELEMENTS(g->inputs_position), &g->n_inputs);
else if (spa_streq(k, "outputs.audio.position"))
spa_audio_parse_position(s, strlen(s),
g->outputs_position, &g->n_outputs);
spa_audio_parse_position_n(s, strlen(s), g->outputs_position,
SPA_N_ELEMENTS(g->outputs_position), &g->n_outputs);
else if (spa_streq(k, "latency")) {
double latency;
if (spa_atod(s, &latency))
@ -1539,8 +1538,6 @@ static int get_ramp_samples(struct impl *this, struct volume_ramp_params *vrp)
samples = (vrp->volume_ramp_time * vrp->rate) / 1000;
spa_log_info(this->log, "volume ramp samples calculated from time is %d", samples);
}
if (!samples)
samples = -1;
return samples;
}
@ -1551,12 +1548,10 @@ static int get_ramp_step_samples(struct impl *this, struct volume_ramp_params *v
if (vrp->volume_ramp_step_samples)
samples = vrp->volume_ramp_step_samples;
else if (vrp->volume_ramp_step_time) {
/* convert the step time which is in nano seconds to seconds */
samples = (vrp->volume_ramp_step_time/1000) * (vrp->rate/1000);
/* convert the step time which is in nano seconds to seconds, round up */
samples = SPA_MAX(1u, vrp->volume_ramp_step_time/1000) * (vrp->rate/1000);
spa_log_debug(this->log, "volume ramp step samples calculated from time is %d", samples);
}
if (!samples)
samples = -1;
return samples;
}
@ -1569,76 +1564,52 @@ static float get_volume_at_scale(struct volume_ramp_params *vrp, float value)
return 0.0;
}
static struct spa_pod *generate_ramp_up_seq(struct impl *this, struct volume_ramp_params *vrp,
static struct spa_pod *generate_ramp_seq(struct impl *this, struct volume_ramp_params *vrp,
void *buffer, size_t size)
{
struct spa_pod_dynamic_builder b;
struct spa_pod_frame f[1];
float start = vrp->start, end = vrp->end, volume_accum = start;
int ramp_samples = get_ramp_samples(this, vrp);
int ramp_step_samples = get_ramp_step_samples(this, vrp);
float volume_step = ((end - start) / (ramp_samples / ramp_step_samples));
uint32_t volume_offs = 0;
float start = vrp->start, end = vrp->end;
int samples = get_ramp_samples(this, vrp);
int step = get_ramp_step_samples(this, vrp);
int offs = 0;
if (samples < 0 || step < 0 || (samples > 0 && step == 0))
return NULL;
spa_pod_dynamic_builder_init(&b, buffer, size, 4096);
spa_pod_builder_push_sequence(&b.b, &f[0], 0);
spa_log_info(this->log, "generating ramp up sequence from %f to %f with a"
" step value %f at scale %d", start, end, volume_step, vrp->scale);
do {
float vas = get_volume_at_scale(vrp, volume_accum);
spa_log_trace(this->log, "volume accum %f", vas);
spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties);
spa_pod_builder_add_object(&b.b,
SPA_TYPE_OBJECT_Props, 0,
SPA_PROP_volume, SPA_POD_Float(vas));
volume_accum += volume_step;
volume_offs += ramp_step_samples;
} while (volume_accum < end);
return spa_pod_builder_pop(&b.b, &f[0]);
}
spa_log_info(this->log, "generating ramp sequence from %f to %f with "
"step %d/%d at scale %d", start, end, step, samples, vrp->scale);
static struct spa_pod *generate_ramp_down_seq(struct impl *this, struct volume_ramp_params *vrp,
void *buffer, size_t size)
{
struct spa_pod_dynamic_builder b;
struct spa_pod_frame f[1];
int ramp_samples = get_ramp_samples(this, vrp);
int ramp_step_samples = get_ramp_step_samples(this, vrp);
float start = vrp->start, end = vrp->end, volume_accum = start;
float volume_step = ((start - end) / (ramp_samples / ramp_step_samples));
uint32_t volume_offs = 0;
while (1) {
float pos = (samples == 0) ? end :
SPA_CLAMP(start + (end - start) * offs / samples,
SPA_MIN(start, end), SPA_MAX(start, end));
float vas = get_volume_at_scale(vrp, pos);
spa_pod_dynamic_builder_init(&b, buffer, size, 4096);
spa_pod_builder_push_sequence(&b.b, &f[0], 0);
spa_log_info(this->log, "generating ramp down sequence from %f to %f with a"
" step value %f at scale %d", start, end, volume_step, vrp->scale);
do {
float vas = get_volume_at_scale(vrp, volume_accum);
spa_log_trace(this->log, "volume accum %f", vas);
spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties);
spa_log_trace(this->log, "volume %d accum %f", offs, vas);
spa_pod_builder_control(&b.b, offs, SPA_CONTROL_Properties);
spa_pod_builder_add_object(&b.b,
SPA_TYPE_OBJECT_Props, 0,
SPA_PROP_volume, SPA_POD_Float(vas));
volume_accum -= volume_step;
volume_offs += ramp_step_samples;
} while (volume_accum > end);
if (offs >= samples)
break;
offs = SPA_MIN(samples, offs + step);
}
return spa_pod_builder_pop(&b.b, &f[0]);
}
static void generate_volume_ramp(struct impl *this, struct volume_ramp_params *vrp,
void *buffer, size_t size)
{
void *sequence = NULL;
if (vrp->start == vrp->end)
spa_log_error(this->log, "no change in volume, cannot ramp volume");
else if (vrp->end > vrp->start)
sequence = generate_ramp_up_seq(this, vrp, buffer, size);
else
sequence = generate_ramp_down_seq(this, vrp, buffer, size);
void *sequence;
sequence = generate_ramp_seq(this, vrp, buffer, size);
if (!sequence)
spa_log_error(this->log, "unable to generate sequence");
@ -1748,7 +1719,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
case SPA_PROP_channelVolumes:
if (!p->lock_volumes &&
(n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
p->channel.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
p->channel.volumes, SPA_N_ELEMENTS(p->channel.volumes))) > 0) {
have_channel_volume = true;
p->channel.n_volumes = n;
changed++;
@ -1756,7 +1727,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
break;
case SPA_PROP_channelMap:
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
p->channel_map, SPA_AUDIO_MAX_CHANNELS)) > 0) {
p->channel_map, SPA_N_ELEMENTS(p->channel_map))) > 0) {
p->n_channels = n;
changed++;
}
@ -1771,7 +1742,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
case SPA_PROP_softVolumes:
if (!p->lock_volumes &&
(n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
p->soft.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
p->soft.volumes, SPA_N_ELEMENTS(p->soft.volumes))) > 0) {
have_soft_volume = true;
p->soft.n_volumes = n;
changed++;
@ -1783,7 +1754,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
break;
case SPA_PROP_monitorVolumes:
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
p->monitor.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
p->monitor.volumes, SPA_N_ELEMENTS(p->monitor.volumes))) > 0) {
p->monitor.n_volumes = n;
changed++;
}
@ -1895,10 +1866,11 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m
this->dir[SPA_DIRECTION_OUTPUT].n_ports = dir->n_ports + 1;
for (i = 0; i < dir->n_ports; i++) {
init_port(this, direction, i, info->info.raw.position[i], true, false, false);
uint32_t pos = info->info.raw.position[i];
init_port(this, direction, i, pos, true, false, false);
if (this->monitor && direction == SPA_DIRECTION_INPUT)
init_port(this, SPA_DIRECTION_OUTPUT, i+1,
info->info.raw.position[i], true, true, false);
pos, true, true, false);
}
break;
}
@ -1966,7 +1938,7 @@ static int node_set_param_port_config(struct impl *this, uint32_t flags,
return -EINVAL;
if (info.info.raw.channels == 0 ||
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
info.info.raw.channels > MAX_CHANNELS)
return -EINVAL;
infop = &info;
@ -2056,22 +2028,24 @@ static int setup_in_convert(struct impl *this)
dst_info.info.raw.rate);
qsort(dst_info.info.raw.position, dst_info.info.raw.channels,
sizeof(uint32_t), int32_cmp);
sizeof(uint32_t), int32_cmp);
for (i = 0; i < src_info.info.raw.channels; i++) {
for (j = 0; j < dst_info.info.raw.channels; j++) {
if (src_info.info.raw.position[i] !=
dst_info.info.raw.position[j])
uint32_t pi, pj;
char b1[8], b2[8];
pi = src_info.info.raw.position[i];
pj = dst_info.info.raw.position[j];
if (pi != pj)
continue;
in->remap[i] = j;
if (i != j)
remap = true;
spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this,
i, in->remap[i], j,
spa_debug_type_find_short_name(spa_type_audio_channel,
src_info.info.raw.position[i]),
spa_debug_type_find_short_name(spa_type_audio_channel,
dst_info.info.raw.position[j]));
spa_type_audio_channel_make_short_name(pi, b1, 8, "UNK"),
spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK"));
dst_info.info.raw.position[j] = -1;
break;
}
@ -2120,9 +2094,10 @@ static int remap_volumes(struct impl *this, const struct spa_audio_info *info)
for (i = 0; i < p->n_channels; i++) {
for (j = i; j < target; j++) {
uint32_t pj = info->info.raw.position[j];
spa_log_debug(this->log, "%d %d: %d <-> %d", i, j,
p->channel_map[i], info->info.raw.position[j]);
if (p->channel_map[i] != info->info.raw.position[j])
p->channel_map[i], pj);
if (p->channel_map[i] != pj)
continue;
if (i != j) {
SPA_SWAP(p->channel_map[i], p->channel_map[j]);
@ -2153,7 +2128,7 @@ static void set_volume(struct impl *this)
{
struct volumes *vol;
uint32_t i;
float volumes[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
struct dir *dir = &this->dir[this->direction];
spa_log_debug(this->log, "%p set volume %f have_format:%d", this, this->props.volume, dir->have_format);
@ -2184,10 +2159,11 @@ static void set_volume(struct impl *this)
static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position)
{
uint32_t i, idx = 0;
char buf[8];
for (i = 0; i < channels; i++)
idx += snprintf(str + idx, len - idx, "%s%s", i == 0 ? "" : " ",
spa_debug_type_find_short_name(spa_type_audio_channel,
position[i]));
spa_type_audio_channel_make_short_name(position[i],
buf, sizeof(buf), "UNK"));
return str;
}
@ -2344,12 +2320,16 @@ static int setup_out_convert(struct impl *this)
dst_info.info.raw.rate);
qsort(src_info.info.raw.position, src_info.info.raw.channels,
sizeof(uint32_t), int32_cmp);
sizeof(uint32_t), int32_cmp);
for (i = 0; i < src_info.info.raw.channels; i++) {
for (j = 0; j < dst_info.info.raw.channels; j++) {
if (src_info.info.raw.position[i] !=
dst_info.info.raw.position[j])
uint32_t pi, pj;
char b1[8], b2[8];
pi = src_info.info.raw.position[i];
pj = dst_info.info.raw.position[j];
if (pi != pj)
continue;
out->remap[i] = j;
if (i != j)
@ -2357,10 +2337,9 @@ static int setup_out_convert(struct impl *this)
spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this,
i, out->remap[i], j,
spa_debug_type_find_short_name(spa_type_audio_channel,
src_info.info.raw.position[i]),
spa_debug_type_find_short_name(spa_type_audio_channel,
dst_info.info.raw.position[j]));
spa_type_audio_channel_make_short_name(pi, b1, 8, "UNK"),
spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK"));
dst_info.info.raw.position[j] = -1;
break;
}
@ -2671,7 +2650,7 @@ static int port_param_enum_formats(struct impl *impl, struct port *port, uint32_
}
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(
DEFAULT_CHANNELS, 1, SPA_AUDIO_MAX_CHANNELS),
DEFAULT_CHANNELS, 1, MAX_CHANNELS),
0);
*param = spa_pod_builder_pop(b, &f[0]);
}
@ -3064,7 +3043,7 @@ static int port_set_format(void *object,
if (info.info.raw.format == 0 ||
(!this->props.resample_disabled && info.info.raw.rate == 0) ||
info.info.raw.channels == 0 ||
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) {
info.info.raw.channels > MAX_CHANNELS) {
spa_log_error(this->log, "invalid format:%d rate:%d channels:%d",
info.info.raw.format, info.info.raw.rate,
info.info.raw.channels);
@ -4296,8 +4275,9 @@ impl_init(const struct spa_handle_factory *factory,
}
else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) {
if (s != NULL)
spa_audio_parse_position(s, strlen(s), this->props.channel_map,
&this->props.n_channels);
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_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[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS],
float matrix[MAX_CHANNELS][MAX_CHANNELS],
uint64_t mask)
{
uint32_t i, ch = mask_to_ch(mix, mask);
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
for (i = 0; i < MAX_CHANNELS; i++)
matrix[i][ch]= 1.0f;
}
static void average_mix(struct channelmix *mix,
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS],
float matrix[MAX_CHANNELS][MAX_CHANNELS],
uint64_t mask)
{
uint32_t i, ch = mask_to_ch(mix, mask);
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
for (i = 0; i < MAX_CHANNELS; i++)
matrix[ch][i]= 1.0f;
}
static void pair_mix(float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS])
static void pair_mix(float matrix[MAX_CHANNELS][MAX_CHANNELS])
{
uint32_t i;
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
for (i = 0; i < MAX_CHANNELS; i++)
matrix[i][i]= 1.0f;
}
static bool match_mix(struct channelmix *mix,
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS],
float matrix[MAX_CHANNELS][MAX_CHANNELS],
uint64_t src_mask, uint64_t dst_mask)
{
bool matched = false;
@ -181,7 +181,7 @@ static bool match_mix(struct channelmix *mix,
static int make_matrix(struct channelmix *mix)
{
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS] = {{ 0.0f }};
float matrix[MAX_CHANNELS][MAX_CHANNELS] = {{ 0.0f }};
uint64_t src_mask = mix->src_mask, src_paired;
uint64_t dst_mask = mix->dst_mask, dst_paired;
uint32_t src_chan = mix->src_chan;
@ -293,7 +293,7 @@ static int make_matrix(struct channelmix *mix)
keep &= ~STEREO;
} else if (dst_mask & _MASK(MONO)){
spa_log_info(mix->log, "assign FC to MONO (%f)", 1.0f);
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
for (i = 0; i < MAX_CHANNELS; i++)
matrix[i][_CH(FC)]= 1.0f;
normalize = true;
} else {
@ -313,7 +313,7 @@ static int make_matrix(struct channelmix *mix)
keep &= ~FRONT;
} else if ((dst_mask & _MASK(MONO))){
spa_log_info(mix->log, "assign STEREO to MONO (%f)", 1.0f);
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
for (i = 0; i < MAX_CHANNELS; i++) {
matrix[i][_CH(FL)]= 1.0f;
matrix[i][_CH(FR)]= 1.0f;
}
@ -352,7 +352,7 @@ static int make_matrix(struct channelmix *mix)
_MATRIX(FC,RC) += slev * SQRT1_2;
} else if (dst_mask & _MASK(MONO)){
spa_log_info(mix->log, "assign RC to MONO (%f)", 1.0f);
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
for (i = 0; i < MAX_CHANNELS; i++)
matrix[i][_CH(RC)]= 1.0f;
normalize = true;
} else {
@ -398,7 +398,7 @@ static int make_matrix(struct channelmix *mix)
_MATRIX(FC,RR)+= slev * SQRT1_2;
} else if (dst_mask & _MASK(MONO)){
spa_log_info(mix->log, "assign RL+RR to MONO (%f)", 1.0f);
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
for (i = 0; i < MAX_CHANNELS; i++) {
matrix[i][_CH(RL)]= 1.0f;
matrix[i][_CH(RR)]= 1.0f;
}
@ -450,7 +450,7 @@ static int make_matrix(struct channelmix *mix)
_MATRIX(FC,SR) += slev * SQRT1_2;
} else if (dst_mask & _MASK(MONO)){
spa_log_info(mix->log, "assign SL+SR to MONO (%f)", 1.0f);
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
for (i = 0; i < MAX_CHANNELS; i++) {
matrix[i][_CH(SL)]= 1.0f;
matrix[i][_CH(SR)]= 1.0f;
}
@ -471,7 +471,7 @@ static int make_matrix(struct channelmix *mix)
_MATRIX(FC,FRC)+= SQRT1_2;
} else if (dst_mask & _MASK(MONO)){
spa_log_info(mix->log, "assign FLC+FRC to MONO (%f)", 1.0f);
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
for (i = 0; i < MAX_CHANNELS; i++) {
matrix[i][_CH(FLC)]= 1.0f;
matrix[i][_CH(FRC)]= 1.0f;
}
@ -492,7 +492,7 @@ static int make_matrix(struct channelmix *mix)
_MATRIX(FR,LFE) += llev * SQRT1_2;
} else if ((dst_mask & _MASK(MONO))){
spa_log_info(mix->log, "assign LFE to MONO (%f)", 1.0f);
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
for (i = 0; i < MAX_CHANNELS; i++)
matrix[i][_CH(LFE)]= 1.0f;
normalize = true;
} else {
@ -690,7 +690,7 @@ done:
static void impl_channelmix_set_volume(struct channelmix *mix, float volume, bool mute,
uint32_t n_channel_volumes, float *channel_volumes)
{
float volumes[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
float vol = mute ? 0.0f : volume, t;
uint32_t i, j;
uint32_t src_chan = mix->src_chan;
@ -760,8 +760,8 @@ int channelmix_init(struct channelmix *mix)
{
const struct channelmix_info *info;
if (mix->src_chan > SPA_AUDIO_MAX_CHANNELS ||
mix->dst_chan > SPA_AUDIO_MAX_CHANNELS)
if (mix->src_chan > MAX_CHANNELS ||
mix->dst_chan > MAX_CHANNELS)
return -EINVAL;
info = find_channelmix_info(mix->src_chan, mix->src_mask, mix->dst_chan, mix->dst_mask,

View file

@ -24,6 +24,7 @@
#define BUFFER_SIZE 4096
#define MAX_TAPS 255u
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define CHANNELMIX_OPS_MAX_ALIGN 16
@ -50,8 +51,8 @@ struct channelmix {
#define CHANNELMIX_FLAG_EQUAL (1<<2) /**< all values are equal */
#define CHANNELMIX_FLAG_COPY (1<<3) /**< 1 on diagonal, can be nxm */
uint32_t flags;
float matrix_orig[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS];
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS];
float matrix_orig[MAX_CHANNELS][MAX_CHANNELS];
float matrix[MAX_CHANNELS][MAX_CHANNELS];
float freq; /* sample frequency */
float lfe_cutoff; /* in Hz, 0 is disabled */
@ -59,7 +60,7 @@ struct channelmix {
float rear_delay; /* in ms, 0 is disabled */
float widen; /* stereo widen. 0 is disabled */
uint32_t hilbert_taps; /* to phase shift, 0 disabled */
struct lr4 lr4[SPA_AUDIO_MAX_CHANNELS];
struct lr4 lr4[MAX_CHANNELS];
float buffer_mem[2 * BUFFER_SIZE*2 + CHANNELMIX_OPS_MAX_ALIGN/4];
float *buffer[2];

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[3];
uint32_t i, conv_flags, data_size[4];
/* we generate int32 bits of random values. With this scale
* factor, we bring this in the [-1.0, 1.0] range */
@ -615,15 +615,17 @@ int convert_init(struct convert *conv)
data_size[0] = SPA_ROUND_UP(conv->noise_size * sizeof(float), FMT_OPS_MAX_ALIGN);
data_size[1] = SPA_ROUND_UP(RANDOM_SIZE * sizeof(uint32_t), FMT_OPS_MAX_ALIGN);
data_size[2] = SPA_ROUND_UP(RANDOM_SIZE * sizeof(int32_t), FMT_OPS_MAX_ALIGN);
data_size[3] = SPA_ROUND_UP(conv->n_channels * sizeof(struct shaper), FMT_OPS_MAX_ALIGN);
conv->data = calloc(FMT_OPS_MAX_ALIGN +
data_size[0] + data_size[1] + data_size[2], 1);
data_size[0] + data_size[1] + data_size[2] + data_size[3], 1);
if (conv->data == NULL)
return -errno;
conv->noise = SPA_PTR_ALIGN(conv->data, FMT_OPS_MAX_ALIGN, float);
conv->random = SPA_PTROFF(conv->noise, data_size[0], uint32_t);
conv->prev = SPA_PTROFF(conv->random, data_size[1], int32_t);
conv->shaper = SPA_PTROFF(conv->prev, data_size[2], struct shaper);
for (i = 0; i < RANDOM_SIZE; i++)
conv->random[i] = random();

View file

@ -236,7 +236,7 @@ struct convert {
uint32_t noise_size;
const float *ns;
uint32_t n_ns;
struct shaper shaper[64];
struct shaper *shaper;
void (*update_noise) (struct convert *conv, float *noise, uint32_t n_samples);
void (*process) (struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],

View file

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

View file

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

View file

@ -40,7 +40,8 @@ static int avb_set_param(struct state *state, const char *k, const char *s)
state->default_format = spa_type_audio_format_from_short_name(s);
fmt_change++;
} else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) {
spa_audio_parse_position(s, strlen(s), state->default_pos.pos,
spa_audio_parse_position_n(s, strlen(s), state->default_pos.pos,
SPA_N_ELEMENTS(state->default_pos.pos),
&state->default_pos.channels);
fmt_change++;
} else if (spa_streq(k, SPA_KEY_AUDIO_ALLOWED_RATES)) {
@ -85,11 +86,12 @@ static int position_to_string(struct channel_map *map, char *val, size_t len)
{
uint32_t i, o = 0;
int r;
char pos[8];
o += snprintf(val, len, "[ ");
for (i = 0; i < map->channels; i++) {
r = snprintf(val+o, len-o, "%s%s", i == 0 ? "" : ", ",
spa_debug_type_find_short_name(spa_type_audio_channel,
map->pos[i]));
spa_type_audio_channel_make_short_name(map->pos[i],
pos, sizeof(pos), "UNK"));
if (r < 0 || o + r >= len)
return -ENOSPC;
o += r;

View file

@ -109,6 +109,7 @@ static inline char *format_streamid(char *str, size_t size, const uint64_t strea
return str;
}
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define MAX_BUFFERS 32
struct buffer {
@ -127,7 +128,7 @@ struct buffer {
struct channel_map {
uint32_t channels;
uint32_t pos[SPA_AUDIO_MAX_CHANNELS];
uint32_t pos[MAX_CHANNELS];
};
struct port {

View file

@ -200,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[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[2];
uint32_t i = 0;
if (caps_size < sizeof(conf))

View file

@ -205,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[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[2];
uint32_t i = 0;
if (caps_size < sizeof(conf))

View file

@ -114,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[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[2];
uint32_t i = 0;
if (caps_size < sizeof(conf))

View file

@ -175,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[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[2];
uint32_t i = 0;
if (caps_size < sizeof(conf))

View file

@ -158,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[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[2];
if (caps_size < sizeof(conf))
return -EINVAL;

View file

@ -164,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[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[2];
int channels;
if (caps_size < sizeof(conf))

View file

@ -58,6 +58,8 @@ static struct spa_log *log;
#define BITRATE_DUPLEX_BIDI 160000
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define OPUS_05_MAX_BYTES (15 * 1024)
struct props {
@ -251,14 +253,8 @@ static const struct surround_encoder_mapping surround_encoders[] = {
static uint32_t bt_channel_from_name(const char *name)
{
size_t i;
enum spa_audio_channel position = SPA_AUDIO_CHANNEL_UNKNOWN;
enum spa_audio_channel position = spa_type_audio_channel_from_short_name(name);
for (i = 0; spa_type_audio_channel[i].name; i++) {
if (spa_streq(name, spa_debug_type_short_name(spa_type_audio_channel[i].name))) {
position = spa_type_audio_channel[i].type;
break;
}
}
for (i = 0; i < SPA_N_ELEMENTS(audio_locations); i++) {
if (position == audio_locations[i].position)
return audio_locations[i].mask;
@ -313,14 +309,14 @@ static void parse_settings(struct props *props, const struct spa_dict *settings)
return;
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.channels"), &v, 0))
props->channels = SPA_CLAMP(v, 1u, SPA_AUDIO_MAX_CHANNELS);
props->channels = SPA_CLAMP(v, 1u, MAX_CHANNELS);
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.max-bitrate"), &v, 0))
props->max_bitrate = SPA_MAX(v, (uint32_t)BITRATE_MIN);
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.coupled-streams"), &v, 0))
props->coupled_streams = SPA_CLAMP(v, 0u, props->channels / 2);
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.channels"), &v, 0))
props->bidi_channels = SPA_CLAMP(v, 0u, SPA_AUDIO_MAX_CHANNELS);
props->bidi_channels = SPA_CLAMP(v, 0u, MAX_CHANNELS);
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.max-bitrate"), &v, 0))
props->bidi_max_bitrate = SPA_MAX(v, (uint32_t)BITRATE_MIN);
if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.coupled-streams"), &v, 0))
@ -503,7 +499,7 @@ static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direc
const uint8_t *permutation = NULL;
size_t i, j;
if (channels > SPA_AUDIO_MAX_CHANNELS)
if (channels > MAX_CHANNELS)
return -EINVAL;
if (2 * coupled_streams > channels)
return -EINVAL;
@ -542,10 +538,9 @@ static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direc
const struct audio_location loc = audio_locations[i];
if (location & loc.mask) {
if (permutation)
positions[permutation[j++]] = loc.position;
else
positions[j++] = loc.position;
uint32_t idx = permutation ? permutation[j] : j;
positions[idx] = loc.position;
j++;
}
}
for (i = SPA_AUDIO_CHANNEL_START_Aux; j < channels; ++i, ++j)
@ -561,7 +556,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
a2dp_opus_05_t a2dp_opus_05 = {
.info = codec->vendor,
.main = {
.channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS),
.channels = SPA_MIN(255u, MAX_CHANNELS),
.frame_duration = (OPUS_05_FRAME_DURATION_25 |
OPUS_05_FRAME_DURATION_50 |
OPUS_05_FRAME_DURATION_100 |
@ -571,7 +566,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
OPUS_05_INIT_BITRATE(0)
},
.bidi = {
.channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS),
.channels = SPA_MIN(255u, MAX_CHANNELS),
.frame_duration = (OPUS_05_FRAME_DURATION_25 |
OPUS_05_FRAME_DURATION_50 |
OPUS_05_FRAME_DURATION_100 |
@ -771,7 +766,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
a2dp_opus_05_t conf;
a2dp_opus_05_direction_t *dir;
struct spa_pod_frame f[1];
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[MAX_CHANNELS];
if (caps_size < sizeof(conf))
return -EINVAL;
@ -835,7 +830,8 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
}
info->info.raw.channels = dir1->channels;
if (get_mapping(codec, dir1, surround_encoder, NULL, NULL, NULL, info->info.raw.position) < 0)
if (get_mapping(codec, dir1, surround_encoder, NULL, NULL, NULL,
info->info.raw.position) < 0)
return -EINVAL;
if (get_mapping(codec, dir2, surround_encoder, NULL, NULL, NULL, NULL) < 0)
return -EINVAL;

View file

@ -346,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[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[2];
if (caps_size < sizeof(conf))
return -EINVAL;

View file

@ -901,12 +901,12 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f
return conf_cmp(&conf1, res1, &conf2, res2);
}
static uint8_t channels_to_positions(uint32_t channels, uint32_t *position)
static uint8_t channels_to_positions(uint32_t channels, uint32_t *position, uint32_t max_position)
{
uint32_t n_channels = get_channel_count(channels);
uint8_t n_positions = 0;
spa_assert(n_channels <= SPA_AUDIO_MAX_CHANNELS);
spa_assert(n_channels <= max_position);
if (channels == 0) {
position[0] = SPA_AUDIO_CHANNEL_MONO;
@ -932,7 +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[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[LC3_MAX_CHANNELS];
uint32_t i = 0;
uint8_t res;
@ -990,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);
res = channels_to_positions(conf.channels, position, SPA_N_ELEMENTS(position));
if (res == 0)
return -EINVAL;
spa_pod_builder_add(b,
@ -1044,7 +1044,8 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
return -EINVAL;
}
res = channels_to_positions(conf.channels, info->info.raw.position);
res = channels_to_positions(conf.channels, info->info.raw.position,
SPA_N_ELEMENTS(info->info.raw.position));
if (res == 0)
return -EINVAL;
info->info.raw.channels = res;

View file

@ -35,6 +35,7 @@
#include <spa/utils/string.h>
#include <spa/utils/json.h>
#include <spa-private/dbus-helpers.h>
#include <spa/param/audio/raw-utils.h>
#include <spa/param/audio/raw-json.h>
#include "codec-loader.h"
@ -5037,6 +5038,10 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
spa_log_error(monitor->log, "invalid transport configuration");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
if (info.info.raw.channels > MAX_CHANNELS) {
spa_log_error(monitor->log, "too many channels in transport");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
transport->n_channels = info.info.raw.channels;
memcpy(transport->channels, info.info.raw.position,
transport->n_channels * sizeof(uint32_t));
@ -6850,7 +6855,7 @@ static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_di
const char *key, uint32_t *value)
{
const char *str;
uint32_t position[SPA_AUDIO_MAX_CHANNELS];
uint32_t position[MAX_CHANNELS];
uint32_t n_channels;
uint32_t locations;
unsigned int i, j;
@ -6861,7 +6866,8 @@ static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_di
if (spa_atou32(str, value, 0))
return;
if (!spa_audio_parse_position(str, strlen(str), position, &n_channels)) {
if (!spa_audio_parse_position_n(str, strlen(str), position,
SPA_N_ELEMENTS(position), &n_channels)) {
spa_log_error(this->log, "property %s '%s' is not valid position array", key, str);
return;
}

View file

@ -26,6 +26,7 @@
#include <spa/pod/parser.h>
#include <spa/param/param.h>
#include <spa/param/audio/raw.h>
#include <spa/param/audio/raw-utils.h>
#include <spa/param/bluetooth/audio.h>
#include <spa/param/bluetooth/type-info.h>
#include <spa/debug/pod.h>
@ -38,7 +39,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.device");
#undef SPA_LOG_TOPIC_DEFAULT
#define SPA_LOG_TOPIC_DEFAULT &log_topic
#define MAX_NODES (2*SPA_AUDIO_MAX_CHANNELS)
#define MAX_NODES (2*MAX_CHANNELS)
#define DEVICE_ID_SOURCE 0
#define DEVICE_ID_SINK 1
@ -99,9 +100,9 @@ struct node {
unsigned int offload_acquired:1;
uint32_t n_channels;
int64_t latency_offset;
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
float volumes[SPA_AUDIO_MAX_CHANNELS];
float soft_volumes[SPA_AUDIO_MAX_CHANNELS];
uint32_t channels[MAX_CHANNELS];
float volumes[MAX_CHANNELS];
float soft_volumes[MAX_CHANNELS];
};
struct dynamic_node
@ -129,8 +130,8 @@ struct device_set {
bool leader;
uint32_t sinks;
uint32_t sources;
struct device_set_member sink[SPA_AUDIO_MAX_CHANNELS];
struct device_set_member source[SPA_AUDIO_MAX_CHANNELS];
struct device_set_member sink[MAX_CHANNELS];
struct device_set_member source[MAX_CHANNELS];
};
struct impl {
@ -182,7 +183,7 @@ static void init_node(struct impl *this, struct node *node, uint32_t id)
spa_zero(*node);
node->id = id;
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
for (i = 0; i < MAX_CHANNELS; i++) {
node->volumes[i] = 1.0f;
node->soft_volumes[i] = 1.0f;
}
@ -476,6 +477,7 @@ static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t
*n_channels = info.info.raw.channels;
memcpy(channels, info.info.raw.position,
info.info.raw.channels * sizeof(uint32_t));
}
static const char *get_channel_name(uint32_t channel)
@ -546,7 +548,7 @@ static void emit_device_set_node(struct impl *this, uint32_t id)
if (node->channels[k] == t->channels[j])
break;
}
if (k == node->n_channels && node->n_channels < SPA_AUDIO_MAX_CHANNELS)
if (k == node->n_channels && node->n_channels < MAX_CHANNELS)
node->channels[node->n_channels++] = t->channels[j];
}
}
@ -2946,8 +2948,8 @@ static int apply_device_props(struct impl *this, struct node *node, struct spa_p
struct spa_pod_prop *prop;
struct spa_pod_object *obj = (struct spa_pod_object *) props;
int changed = 0;
float volumes[SPA_AUDIO_MAX_CHANNELS];
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
uint32_t channels[MAX_CHANNELS];
uint32_t n_volumes = 0, SPA_UNUSED n_channels = 0;
int64_t latency_offset = 0;
@ -2972,11 +2974,11 @@ static int apply_device_props(struct impl *this, struct node *node, struct spa_p
break;
case SPA_PROP_channelVolumes:
n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
volumes, SPA_AUDIO_MAX_CHANNELS);
volumes, SPA_N_ELEMENTS(volumes));
break;
case SPA_PROP_channelMap:
n_channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
channels, SPA_AUDIO_MAX_CHANNELS);
channels, SPA_N_ELEMENTS(channels));
break;
case SPA_PROP_latencyOffsetNsec:
if (spa_pod_get_long(&prop->value, &latency_offset) == 0) {

View file

@ -157,6 +157,8 @@ extern "C" {
#define SPA_BT_NO_BATTERY ((uint8_t)255)
#define MAX_CHANNELS (SPA_AUDIO_MAX_CHANNELS)
enum spa_bt_media_direction {
SPA_BT_MEDIA_SOURCE,
SPA_BT_MEDIA_SINK,
@ -678,7 +680,7 @@ struct spa_bt_transport {
struct spa_list bap_transport_linked;
uint32_t n_channels;
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
uint32_t channels[MAX_CHANNELS];
struct spa_bt_transport_volume volumes[SPA_BT_VOLUME_ID_TERM];

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[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO };
const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO };
const int channels = 1;
spa_assert(caps == NULL && caps_size == 0);

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[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO };
const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO };
const int channels = 1;
spa_assert(caps == NULL && caps_size == 0);

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[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO };
const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO };
const int channels = 1;
spa_assert(caps == NULL && caps_size == 0);

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[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO };
const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO };
const int channels = 1;
spa_assert(caps == NULL && caps_size == 0);

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

View file

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

View file

@ -39,6 +39,7 @@
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.filter-graph");
#define MAX_HNDL 64
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define DEFAULT_RATE 48000
@ -154,15 +155,15 @@ struct graph_hndl {
struct volume {
bool mute;
uint32_t n_volumes;
float volumes[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
uint32_t n_ports;
struct port *ports[SPA_AUDIO_MAX_CHANNELS];
float min[SPA_AUDIO_MAX_CHANNELS];
float max[SPA_AUDIO_MAX_CHANNELS];
struct port *ports[MAX_CHANNELS];
float min[MAX_CHANNELS];
float max[MAX_CHANNELS];
#define SCALE_LINEAR 0
#define SCALE_CUBIC 1
int scale[SPA_AUDIO_MAX_CHANNELS];
int scale[MAX_CHANNELS];
};
struct graph {
@ -194,9 +195,9 @@ struct graph {
uint32_t n_inputs;
uint32_t n_outputs;
uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t inputs_position[MAX_CHANNELS];
uint32_t n_inputs_position;
uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t outputs_position[MAX_CHANNELS];
uint32_t n_outputs_position;
float min_latency;
@ -231,16 +232,18 @@ struct impl {
float *discard_data;
};
static inline void print_channels(char *buffer, size_t max_size, uint32_t n_channels, uint32_t *positions)
static inline void print_channels(char *buffer, size_t max_size, uint32_t n_positions, uint32_t *positions)
{
uint32_t i;
struct spa_strbuf buf;
char pos[8];
spa_strbuf_init(&buf, buffer, max_size);
spa_strbuf_append(&buf, "[");
for (i = 0; i < n_channels; i++) {
for (i = 0; i < n_positions; i++) {
spa_strbuf_append(&buf, "%s%s", i ? "," : "",
spa_type_audio_channel_to_short_name(positions[i]));
spa_type_audio_channel_make_short_name(positions[i],
pos, sizeof(pos), "UNK"));
}
spa_strbuf_append(&buf, "]");
}
@ -256,8 +259,8 @@ static void emit_filter_graph_info(struct impl *impl, bool full)
char n_inputs[64], n_outputs[64], latency[64];
struct spa_dict_item items[6];
struct spa_dict dict = SPA_DICT(items, 0);
char in_pos[SPA_AUDIO_MAX_CHANNELS * 8];
char out_pos[SPA_AUDIO_MAX_CHANNELS * 8];
char in_pos[MAX_CHANNELS * 8];
char out_pos[MAX_CHANNELS * 8];
snprintf(n_inputs, sizeof(n_inputs), "%d", impl->graph.n_inputs);
snprintf(n_outputs, sizeof(n_outputs), "%d", impl->graph.n_outputs);
@ -745,10 +748,10 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru
case SPA_PROP_channelVolumes:
{
uint32_t i, n_vols;
float vols[SPA_AUDIO_MAX_CHANNELS];
float vols[MAX_CHANNELS];
if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols,
SPA_AUDIO_MAX_CHANNELS)) > 0) {
SPA_N_ELEMENTS(vols))) > 0) {
if (vol->n_volumes != n_vols)
do_volume = true;
vol->n_volumes = n_vols;
@ -772,7 +775,7 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru
}
}
if (do_volume && vol->n_ports != 0) {
float soft_vols[SPA_AUDIO_MAX_CHANNELS];
float soft_vols[MAX_CHANNELS];
uint32_t i;
for (i = 0; i < vol->n_volumes; i++)
@ -1264,7 +1267,7 @@ static int parse_volume(struct graph *graph, struct spa_json *json, enum spa_dir
spa_log_error(impl->log, "unknown control port %s", control);
return -ENOENT;
}
if (vol->n_ports >= SPA_AUDIO_MAX_CHANNELS) {
if (vol->n_ports >= MAX_CHANNELS) {
spa_log_error(impl->log, "too many volume controls");
return -ENOSPC;
}
@ -2118,8 +2121,9 @@ static int load_graph(struct graph *graph, const struct spa_dict *props)
spa_log_error(impl->log, "%s expects an array", key);
return -EINVAL;
}
spa_audio_parse_position(val, len, graph->inputs_position,
&graph->n_inputs_position);
spa_audio_parse_position_n(val, len, graph->inputs_position,
SPA_N_ELEMENTS(graph->inputs_position),
&graph->n_inputs_position);
impl->info.n_inputs = graph->n_inputs_position;
}
else if (spa_streq("outputs.audio.position", key)) {
@ -2128,8 +2132,9 @@ static int load_graph(struct graph *graph, const struct spa_dict *props)
spa_log_error(impl->log, "%s expects an array", key);
return -EINVAL;
}
spa_audio_parse_position(val, len, graph->outputs_position,
&graph->n_outputs_position);
spa_audio_parse_position_n(val, len, graph->outputs_position,
SPA_N_ELEMENTS(graph->outputs_position),
&graph->n_outputs_position);
impl->info.n_outputs = graph->n_outputs_position;
}
else if (spa_streq("nodes", key)) {

View file

@ -7,6 +7,7 @@
#include <array>
#include <cstddef>
#include <limits>
#include <mutex>
#include <optional>
#include <type_traits>
#include <utility>
@ -34,6 +35,7 @@
#include <spa/control/control.h>
#include <spa/pod/dynamic.h>
#include <spa/pod/filter.h>
#include <spa/pod/dynamic.h>
#include <libcamera/camera.h>
#include <libcamera/control_ids.h>
@ -121,6 +123,7 @@ struct impl {
struct spa_node node = {};
struct spa_log *log;
struct spa_loop *main_loop;
struct spa_loop *data_loop;
struct spa_system *system;
@ -161,10 +164,12 @@ struct impl {
ControlList ctrls;
ControlList initial_controls;
ControlList request_metadata;
std::mutex request_metadata_mutex;
bool active = false;
bool acquired = false;
impl(spa_log *log, spa_loop *data_loop, spa_system *system,
impl(spa_log *log, spa_loop *main_loop, spa_loop *data_loop, spa_system *system,
std::shared_ptr<CameraManager> manager, std::shared_ptr<Camera> camera,
std::unique_ptr<CameraConfiguration> config);
@ -1037,6 +1042,23 @@ int spa_libcamera_apply_controls(struct impl *impl, libcamera::ControlList&& con
);
}
void emit_node_info(struct impl *impl, bool full);
static int do_emit_node_info(struct spa_loop *loop,
bool async,
uint32_t seq,
const void *data,
size_t size,
void *user_data)
{
struct impl *impl = (struct impl *)user_data;
impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
impl->params[NODE_Props].flags ^= SPA_PARAM_INFO_SERIAL;
emit_node_info(impl, true);
return 0;
}
void handle_completed_request(struct impl *impl, libcamera::Request *request)
{
const auto request_id = request->cookie();
@ -1111,6 +1133,34 @@ void handle_completed_request(struct impl *impl, libcamera::Request *request)
d->chunk->flags |= SPA_CHUNK_FLAG_CORRUPTED;
}
// update stored metadata from request metadata
{
const auto lock_guard = std::lock_guard{impl->request_metadata_mutex};
auto update = false;
for (const auto& [id, value] : request->metadata())
{
const auto prop_id = control_to_prop_id(id);
if (prop_id >= SPA_PROP_START_CUSTOM)
{
continue;
}
const auto it = std::find_if(impl->request_metadata.begin(), impl->request_metadata.end(), [id](const auto& entry)
{
return id == entry.first;
});
if ((it == impl->request_metadata.end()) || (value != it->second))
{
impl->request_metadata.set(id, value);
update = true;
}
}
if (update)
{
spa_loop_invoke(impl->main_loop, do_emit_node_info, 0, nullptr, 0, false, impl);
}
}
request->reuse(libcamera::Request::ReuseFlag::ReuseBuffers);
spa_list_append(&port->queue, &b->link);
@ -1305,7 +1355,6 @@ spa_libcamera_alloc_buffers(struct impl *impl, struct port *port,
return 0;
}
void impl::requestComplete(libcamera::Request *request)
{
struct impl *impl = this;
@ -1451,7 +1500,8 @@ int impl_node_enum_params(void *object, int seq,
{
struct impl *impl = (struct impl*)object;
struct spa_pod *param;
struct spa_pod_builder b = { 0 };
spa_auto(spa_pod_dynamic_builder) b = { 0 };
struct spa_pod_builder_state state;
uint8_t buffer[1024];
struct spa_result_node_params result;
uint32_t count = 0;
@ -1460,12 +1510,15 @@ int impl_node_enum_params(void *object, int seq,
spa_return_val_if_fail(impl != nullptr, -EINVAL);
spa_return_val_if_fail(num != 0, -EINVAL);
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
spa_pod_builder_get_state(&b.b, &state);
result.id = id;
result.next = start;
next:
result.index = result.next++;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
spa_pod_builder_reset(&b.b, &state);
switch (id) {
case SPA_PARAM_PropInfo:
@ -1481,6 +1534,39 @@ next:
case SPA_PARAM_Props:
{
switch (result.index) {
case 0: {
const auto add_property = [&b, &impl](unsigned int controlId, const libcamera::ControlValue& value) {
const auto id = control_to_prop_id(controlId);
if (id >= SPA_PROP_START_CUSTOM) {
return;
}
if (value.isArray()) {
return; // not yet supported
}
spa_pod_builder_prop(&b.b, id, 0);
control_value_to_pod(b.b, value);
};
struct spa_pod_frame f;
spa_pod_builder_push_object(&b.b, &f, SPA_TYPE_OBJECT_Props, id);
const auto metadata = [&]()
{
const auto lock_guard = std::lock_guard{impl->request_metadata_mutex};
auto metadata = impl->request_metadata;
return metadata;
}();
for (const auto& [id, value] : metadata)
{
add_property(id, value);
}
for (const auto& [id, info] : impl->camera->controls()) {
if (!metadata.contains(id->id())) {
add_property(id->id(), info.def());
}
}
param = (spa_pod*)spa_pod_builder_pop(&b.b, &f);
}
break;
default:
return 0;
}
@ -1490,14 +1576,14 @@ next:
return spa_libcamera_enum_format(impl, GET_OUT_PORT(impl, 0),
seq, start, num, filter);
case SPA_PARAM_Format:
if ((res = port_get_format(impl, GET_OUT_PORT(impl, 0), result.index, filter, &param, &b)) <= 0)
if ((res = port_get_format(impl, GET_OUT_PORT(impl, 0), result.index, filter, &param, &b.b)) <= 0)
return res;
break;
default:
return -ENOENT;
}
if (spa_pod_filter(&b, &result.param, param, filter) < 0)
if (spa_pod_filter(&b.b, &result.param, param, filter) < 0)
goto next;
spa_node_emit_result(&impl->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
@ -2144,11 +2230,12 @@ int impl_clear(struct spa_handle *handle)
return 0;
}
impl::impl(spa_log *log, spa_loop *data_loop, spa_system *system,
impl::impl(spa_log *log, spa_loop *main_loop, spa_loop *data_loop, spa_system *system,
std::shared_ptr<CameraManager> manager, std::shared_ptr<Camera> camera,
std::unique_ptr<CameraConfiguration> config)
: handle({ SPA_VERSION_HANDLE, impl_get_interface, impl_clear }),
log(log),
main_loop(main_loop),
data_loop(data_loop),
system(system),
out_ports{{this}},
@ -2200,9 +2287,15 @@ impl_init(const struct spa_handle_factory *factory,
spa_return_val_if_fail(handle != nullptr, -EINVAL);
auto log = static_cast<spa_log *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log));
auto main_loop = static_cast<spa_loop *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop));
auto data_loop = static_cast<spa_loop *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop));
auto system = static_cast<spa_system *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System));
if (!main_loop) {
spa_log_error(log, "a main_loop is needed");
return -EINVAL;
}
if (!data_loop) {
spa_log_error(log, "a data_loop is needed");
return -EINVAL;
@ -2235,7 +2328,7 @@ impl_init(const struct spa_handle_factory *factory,
return -EINVAL;
}
new (handle) impl(log, data_loop, system,
new (handle) impl(log, main_loop, data_loop, system,
std::move(manager), std::move(camera),
std::move(config));

View file

@ -35,12 +35,13 @@
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.null-audio-sink");
#define DEFAULT_CLOCK_NAME "clock.system.monotonic"
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
struct props {
uint32_t format;
uint32_t channels;
uint32_t rate;
uint32_t pos[SPA_AUDIO_MAX_CHANNELS];
uint32_t pos[MAX_CHANNELS];
char clock_name[64];
unsigned int debug:1;
unsigned int driver:1;
@ -636,7 +637,7 @@ port_set_format(struct impl *this,
if (info.info.raw.rate == 0 ||
info.info.raw.channels == 0 ||
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
info.info.raw.channels > MAX_CHANNELS)
return -EINVAL;
if (this->props.format != 0) {
@ -949,7 +950,8 @@ impl_init(const struct spa_handle_factory *factory,
} else if (spa_streq(k, SPA_KEY_NODE_DRIVER)) {
this->props.driver = spa_atob(s);
} else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) {
spa_audio_parse_position(s, strlen(s), this->props.pos, &this->props.channels);
spa_audio_parse_position_n(s, strlen(s), this->props.pos,
SPA_N_ELEMENTS(this->props.pos), &this->props.channels);
} else if (spa_streq(k, "clock.name")) {
spa_scnprintf(this->props.clock_name,
sizeof(this->props.clock_name),

View file

@ -454,8 +454,7 @@ static int port_set_format(void *object,
return -EINVAL;
if (info.info.raw.format != SPA_AUDIO_FORMAT_S16 ||
info.info.raw.channels == 0 ||
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
info.info.raw.channels == 0)
return -EINVAL;
this->bpf = 2 * info.info.raw.channels;

View file

@ -268,8 +268,7 @@ static int port_set_format(void *object,
d->format.format != SPA_AUDIO_FORMAT_F32)
return -EINVAL;
if (d->format.rate == 0 ||
d->format.channels == 0 ||
d->format.channels > SPA_AUDIO_MAX_CHANNELS)
d->format.channels == 0)
return -EINVAL;
}

View file

@ -649,7 +649,7 @@ handle_video_fields (ConvertData *d)
static void
set_default_channels (struct spa_pod_builder *b, uint32_t channels)
{
uint32_t position[SPA_AUDIO_MAX_CHANNELS] = {0};
uint32_t position[8] = {0};
gboolean ok = TRUE;
switch (channels) {

View file

@ -231,9 +231,9 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
"( stream.props=<properties> ) " \
"( stream.rules=<properties> ) "
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define DELAYBUF_MAX_SIZE (20 * sizeof(float) * 96000)
static const struct spa_dict_item module_props[] = {
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
{ PW_KEY_MODULE_DESCRIPTION, "Combine multiple streams into a single stream" },
@ -312,10 +312,10 @@ struct stream {
struct spa_latency_info latency;
struct spa_audio_info_raw info;
uint32_t remap[SPA_AUDIO_MAX_CHANNELS];
uint32_t remap[MAX_CHANNELS];
void *delaybuf;
struct ringbuffer delay[SPA_AUDIO_MAX_CHANNELS];
struct ringbuffer delay[MAX_CHANNELS];
int64_t delay_samples; /* for main loop */
int64_t data_delay_samples; /* for data loop */
@ -326,9 +326,9 @@ struct stream {
unsigned int have_latency:1;
};
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"),
SPA_DICT_ITEM(SPA_KEY_AUDIO_POSITION, DEFAULT_POSITION)),
@ -509,7 +509,7 @@ static void update_latency(struct impl *impl)
struct replace_delay_info {
struct stream *stream;
void *buf;
struct ringbuffer delay[SPA_AUDIO_MAX_CHANNELS];
struct ringbuffer delay[MAX_CHANNELS];
};
static int do_replace_delay(struct spa_loop *loop, bool async, uint32_t seq,
@ -866,13 +866,15 @@ static int create_stream(struct stream_info *info)
s->info = impl->info;
if ((str = pw_properties_get(info->stream_props, SPA_KEY_AUDIO_POSITION)) != NULL)
spa_audio_parse_position(str, strlen(str), s->info.position, &s->info.channels);
spa_audio_parse_position_n(str, strlen(str), s->info.position,
SPA_N_ELEMENTS(s->info.position), &s->info.channels);
if (s->info.channels == 0)
s->info = impl->info;
spa_zero(remap_info);
if ((str = pw_properties_get(info->stream_props, "combine.audio.position")) != NULL)
spa_audio_parse_position(str, strlen(str), remap_info.position, &remap_info.channels);
spa_audio_parse_position_n(str, strlen(str), remap_info.position,
SPA_N_ELEMENTS(remap_info.position), &remap_info.channels);
if (remap_info.channels == 0)
remap_info = s->info;
@ -880,7 +882,10 @@ static int create_stream(struct stream_info *info)
for (i = 0; i < remap_info.channels; i++) {
s->remap[i] = i;
for (j = 0; j < tmp_info.channels; j++) {
if (tmp_info.position[j] == remap_info.position[i]) {
uint32_t pj, pi;
pj = tmp_info.position[j];
pi = remap_info.position[i];
if (pj == pi) {
s->remap[i] = j;
break;
}
@ -1228,7 +1233,7 @@ static void combine_output_process(void *d)
struct pw_buffer *in, *out;
struct stream *s;
bool delay_changed = false;
bool mix[SPA_AUDIO_MAX_CHANNELS];
bool mix[MAX_CHANNELS];
if ((out = pw_stream_dequeue_buffer(impl->combine)) == NULL) {
pw_log_debug("%p: out of output buffers: %m", impl);
@ -1633,7 +1638,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(props, impl->combine_props, "resample.prefill");
copy_props(props, impl->combine_props, "resample.disable");
parse_audio_info(impl->combine_props, &impl->info);
if ((res = parse_audio_info(impl->combine_props, &impl->info)) < 0) {
pw_log_error( "can't create format: %s", spa_strerror(res));
goto error;
}
copy_props(props, impl->stream_props, PW_KEY_NODE_LOOP_NAME);
copy_props(props, impl->stream_props, PW_KEY_NODE_GROUP);

View file

@ -154,6 +154,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
#define DEFAULT_RATE 48000
#define DEFAULT_POSITION "[ FL FR ]"
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
/* Hopefully this is enough for any combination of AEC engine and resampler
* input requirement for rate matching */
@ -203,7 +204,7 @@ struct impl {
struct spa_hook source_listener;
struct spa_audio_info_raw source_info;
void *rec_buffer[SPA_AUDIO_MAX_CHANNELS];
void *rec_buffer[MAX_CHANNELS];
uint32_t rec_ringsize;
struct spa_ringbuffer rec_ring;
@ -215,21 +216,23 @@ struct impl {
struct pw_properties *sink_props;
struct pw_stream *sink;
struct spa_hook sink_listener;
void *play_buffer[SPA_AUDIO_MAX_CHANNELS];
void *play_buffer[MAX_CHANNELS];
uint32_t play_ringsize;
struct spa_ringbuffer play_ring;
struct spa_ringbuffer play_delayed_ring;
struct spa_audio_info_raw sink_info;
void *out_buffer[SPA_AUDIO_MAX_CHANNELS];
void *out_buffer[MAX_CHANNELS];
uint32_t out_ringsize;
struct spa_ringbuffer out_ring;
struct spa_audio_aec *aec;
uint32_t aec_blocksize;
unsigned int capture_ready:1;
unsigned int sink_ready:1;
struct spa_io_position *capture_position;
struct spa_io_position *sink_position;
uint32_t capture_cycle;
uint32_t sink_cycle;
unsigned int do_disconnect:1;
@ -306,13 +309,24 @@ static void process(struct impl *impl)
const float *play_delayed[impl->play_info.channels];
float *out[impl->out_info.channels];
struct spa_data *dd;
uint32_t i, size;
uint32_t rindex, pindex, oindex, pdindex, avail;
uint32_t i;
uint32_t rindex, pindex, oindex, pdindex, size;
int32_t avail, pavail, pdavail;
size = impl->aec_blocksize;
/* First read a block from the playback and capture ring buffers */
spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex);
/* First read a block from the capture ring buffer */
avail = spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex);
while (avail >= (int32_t)size * 2) {
/* drop samples that are not needed this or next cycle. Note
* that samples are kept in the ringbuffer until next cycle if
* size is not equal to or divisible by quantum, to avoid
* discontinuity */
pw_log_debug("avail %d", avail);
spa_ringbuffer_read_update(&impl->rec_ring, rindex + size);
avail = spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex);
pw_log_debug("new avail %d, size %u", avail, size);
}
for (i = 0; i < impl->rec_info.channels; i++) {
/* captured samples, with echo from sink */
@ -330,19 +344,34 @@ static void process(struct impl *impl)
out[i] = &out_buf[i][0];
}
spa_ringbuffer_get_read_index(&impl->play_ring, &pindex);
spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex);
pavail = spa_ringbuffer_get_read_index(&impl->play_ring, &pindex);
pdavail = spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex);
if (impl->playback != NULL && (pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) {
pw_log_debug("out of playback buffers: %m");
/* playback stream may not yet be in streaming state, drop play
* data to avoid introducing additional playback latency */
spa_ringbuffer_read_update(&impl->play_ring, pindex + size);
spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size);
spa_ringbuffer_read_update(&impl->play_ring, pindex + pavail);
spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + pdavail);
goto done;
}
if (pavail > avail) {
/* drop too old samples from previous graph cycles */
pw_log_debug("pavail %d, dropping %d", pavail, pavail - avail);
spa_ringbuffer_read_update(&impl->play_ring, pindex + pavail - avail);
pavail = spa_ringbuffer_get_read_index(&impl->play_ring, &pindex);
pw_log_debug("new pavail %d, avail %d", pavail, avail);
}
if (pdavail > avail) {
/* drop too old samples from previous graph cycles */
pw_log_debug("pdavail %d, dropping %d", pdavail, pdavail - avail);
spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + pdavail - avail);
pdavail = spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex);
pw_log_debug("new pdavail %d, avail %d", pdavail, avail);
}
for (i = 0; i < impl->play_info.channels; i++) {
/* echo from sink */
play[i] = &play_buf[i][0];
@ -430,7 +459,7 @@ static void process(struct impl *impl)
* available on the source */
avail = spa_ringbuffer_get_read_index(&impl->out_ring, &oindex);
while (avail >= size) {
while (avail >= (int32_t)size) {
if ((cout = pw_stream_dequeue_buffer(impl->source)) != NULL) {
for (i = 0; i < impl->out_info.channels; i++) {
dd = &cout->buffer->datas[i];
@ -453,8 +482,8 @@ static void process(struct impl *impl)
}
done:
impl->sink_ready = false;
impl->capture_ready = false;
impl->capture_cycle = 0;
impl->sink_cycle = 0;
}
static void reset_buffers(struct impl *impl)
@ -478,8 +507,8 @@ static void reset_buffers(struct impl *impl)
spa_ringbuffer_get_read_index(&impl->play_ring, &index);
spa_ringbuffer_read_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay)));
impl->sink_ready = false;
impl->capture_ready = false;
impl->capture_cycle = 0;
impl->sink_cycle = 0;
}
static void capture_destroy(void *d)
@ -545,8 +574,11 @@ static void capture_process(void *data)
spa_ringbuffer_write_update(&impl->rec_ring, index + size);
if (avail + size >= impl->aec_blocksize) {
impl->capture_ready = true;
if (impl->sink_ready)
if (impl->capture_position)
impl->capture_cycle = impl->capture_position->clock.cycle;
else
pw_log_warn("no capture position");
if (impl->capture_cycle == impl->sink_cycle)
process(impl);
}
@ -739,12 +771,26 @@ static void input_param_changed(void *data, uint32_t id, const struct spa_pod* p
}
}
static void capture_io_changed(void *data, uint32_t id, void *area, uint32_t size)
{
struct impl *impl = data;
switch (id) {
case SPA_IO_Position:
impl->capture_position = area;
break;
default:
break;
}
}
static const struct pw_stream_events capture_events = {
PW_VERSION_STREAM_EVENTS,
.destroy = capture_destroy,
.state_changed = capture_state_changed,
.process = capture_process,
.param_changed = input_param_changed
.param_changed = input_param_changed,
.io_changed = capture_io_changed
};
static void source_destroy(void *d)
@ -929,10 +975,15 @@ static void sink_process(void *data)
SPA_PTROFF(d->data, offs, void), size);
}
spa_ringbuffer_write_update(&impl->play_ring, index + size);
spa_ringbuffer_get_write_index(&impl->play_delayed_ring, &index);
spa_ringbuffer_write_update(&impl->play_delayed_ring, index + size);
if (avail + size >= impl->aec_blocksize) {
impl->sink_ready = true;
if (impl->capture_ready)
if (impl->sink_position)
impl->sink_cycle = impl->sink_position->clock.cycle;
else
pw_log_warn("no sink position");
if (impl->capture_cycle == impl->sink_cycle)
process(impl);
}
@ -954,12 +1005,27 @@ static const struct pw_stream_events playback_events = {
.state_changed = playback_state_changed,
.param_changed = output_param_changed
};
static void sink_io_changed(void *data, uint32_t id, void *area, uint32_t size)
{
struct impl *impl = data;
switch (id) {
case SPA_IO_Position:
impl->sink_position = area;
break;
default:
break;
}
}
static const struct pw_stream_events sink_events = {
PW_VERSION_STREAM_EVENTS,
.destroy = sink_destroy,
.process = sink_process,
.state_changed = sink_state_changed,
.param_changed = output_param_changed
.param_changed = output_param_changed,
.io_changed = sink_io_changed
};
#define MAX_PARAMS 512u
@ -1202,9 +1268,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"),
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)),
@ -1290,7 +1356,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
if (pw_properties_get(props, "resample.prefill") == NULL)
pw_properties_set(props, "resample.prefill", "true");
parse_audio_info(props, &info);
if ((res = parse_audio_info(props, &info)) < 0) {
pw_log_error( "can't parse format: %s", spa_strerror(res));
goto error;
}
impl->capture_info = info;
impl->source_info = info;
@ -1370,21 +1439,21 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
}
if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
spa_audio_parse_position(str, strlen(str),
impl->capture_info.position, &impl->capture_info.channels);
spa_audio_parse_position_n(str, strlen(str), impl->capture_info.position,
SPA_N_ELEMENTS(impl->capture_info.position), &impl->capture_info.channels);
}
if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
spa_audio_parse_position(str, strlen(str),
impl->source_info.position, &impl->source_info.channels);
spa_audio_parse_position_n(str, strlen(str), impl->source_info.position,
SPA_N_ELEMENTS(impl->source_info.position), &impl->source_info.channels);
}
if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
spa_audio_parse_position(str, strlen(str),
impl->sink_info.position, &impl->sink_info.channels);
spa_audio_parse_position_n(str, strlen(str), impl->sink_info.position,
SPA_N_ELEMENTS(impl->sink_info.position), &impl->sink_info.channels);
impl->playback_info = impl->sink_info;
}
if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
spa_audio_parse_position(str, strlen(str),
impl->playback_info.position, &impl->playback_info.channels);
spa_audio_parse_position_n(str, strlen(str), impl->playback_info.position,
SPA_N_ELEMENTS(impl->playback_info.position), &impl->playback_info.channels);
if (impl->playback_info.channels != impl->sink_info.channels)
impl->playback_info = impl->sink_info;
}

View file

@ -298,8 +298,7 @@ static void capture_param_changed(void *data, uint32_t id, const struct spa_pod
if (spa_format_audio_raw_parse(param, &info) < 0)
return;
if (info.rate == 0 ||
info.channels == 0 ||
info.channels > SPA_AUDIO_MAX_CHANNELS)
info.channels == 0)
return;
break;
}
@ -468,9 +467,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"),
SPA_DICT_ITEM(SPA_KEY_AUDIO_POSITION, DEFAULT_POSITION)),
@ -574,8 +573,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
if (pw_properties_get(impl->playback_props, PW_KEY_NODE_DESCRIPTION) == NULL)
pw_properties_set(impl->playback_props, PW_KEY_NODE_DESCRIPTION, str);
parse_audio_info(impl->capture_props, &impl->capture_info);
parse_audio_info(impl->playback_props, &impl->playback_info);
if ((res = parse_audio_info(impl->capture_props, &impl->capture_info)) < 0 ||
(res = parse_audio_info(impl->playback_props, &impl->playback_info)) < 0) {
pw_log_error( "can't parse formats: %s", spa_strerror(res));
goto error;
}
if (!impl->capture_info.rate && !impl->playback_info.rate) {
if (pw_properties_get(impl->playback_props, "resample.disable") == NULL)

View file

@ -273,9 +273,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT),
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)),
@ -395,7 +395,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
copy_props(impl, props, PW_KEY_MEDIA_CLASS);
parse_audio_info(impl->stream_props, &impl->info);
if ((res = parse_audio_info(impl->stream_props, &impl->info)) < 0) {
pw_log_error( "can't parse format: %s", spa_strerror(res));
goto error;
}
impl->frame_size = calc_frame_size(&impl->info);
if (impl->frame_size == 0) {

View file

@ -279,9 +279,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT),
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)),
@ -401,7 +401,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
copy_props(impl, props, PW_KEY_MEDIA_CLASS);
parse_audio_info(impl->stream_props, &impl->info);
if ((res = parse_audio_info(impl->stream_props, &impl->info)) < 0) {
pw_log_error( "can't parse format: %s", spa_strerror(res));
goto error;
}
impl->frame_size = calc_frame_size(&impl->info);
if (impl->frame_size == 0) {

View file

@ -112,6 +112,7 @@
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
#define PW_LOG_TOPIC_DEFAULT mod_topic
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define MAX_PORTS 128
#define FFADO_RT_PRIORITY_PACKETIZER_RELATIVE 5
@ -179,7 +180,7 @@ struct port {
struct volume {
bool mute;
uint32_t n_volumes;
float volumes[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
};
struct stream {
@ -760,7 +761,7 @@ static int make_stream_ports(struct stream *s)
struct port *port = s->ports[i];
char channel[32];
snprintf(channel, sizeof(channel), "AUX%u", n_channels % SPA_AUDIO_MAX_CHANNELS);
snprintf(channel, sizeof(channel), "AUX%u", n_channels);
switch (port->stream_type) {
case ffado_stream_type_audio:
@ -873,9 +874,9 @@ static void parse_props(struct stream *s, const struct spa_pod *param)
case SPA_PROP_channelVolumes:
{
uint32_t n;
float vols[SPA_AUDIO_MAX_CHANNELS];
float vols[MAX_CHANNELS];
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
vols, SPA_N_ELEMENTS(vols))) > 0) {
s->volume.n_volumes = n;
for (n = 0; n < s->volume.n_volumes; n++)
s->volume.volumes[n] = vols[n];
@ -1227,8 +1228,9 @@ static int probe_ffado_device(struct impl *impl)
impl->source.ports[i] = port;
}
if (impl->source.info.channels != n_channels) {
impl->source.info.channels = n_channels;
for (i = 0; i < SPA_MIN(impl->source.info.channels, SPA_AUDIO_MAX_CHANNELS); i++)
uint32_t n_pos = SPA_MIN(n_channels, SPA_N_ELEMENTS(impl->source.info.position));
impl->source.info.channels = n_pos;
for (i = 0; i < n_pos; i++)
impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
}
@ -1253,8 +1255,9 @@ static int probe_ffado_device(struct impl *impl)
impl->sink.ports[i] = port;
}
if (impl->sink.info.channels != n_channels) {
impl->sink.info.channels = n_channels;
for (i = 0; i < SPA_MIN(impl->sink.info.channels, SPA_AUDIO_MAX_CHANNELS); i++)
uint32_t n_pos = SPA_MIN(n_channels, SPA_N_ELEMENTS(impl->sink.info.position));
impl->sink.info.channels = n_pos;
for (i = 0; i < n_pos; i++)
impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
}
@ -1426,9 +1429,9 @@ static void parse_devices(struct impl *impl, const char *val, size_t len)
}
}
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"),
SPA_DICT_ITEM(SPA_KEY_AUDIO_POSITION, DEFAULT_POSITION)),
@ -1579,8 +1582,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
copy_props(impl, props, PW_KEY_NODE_PAUSE_ON_IDLE);
parse_audio_info(impl->source.props, &impl->source.info);
parse_audio_info(impl->sink.props, &impl->sink.info);
if ((res = parse_audio_info(impl->source.props, &impl->source.info)) < 0 ||
(res = parse_audio_info(impl->sink.props, &impl->sink.info)) < 0) {
pw_log_error( "can't parse format: %s", spa_strerror(res));
goto error;
}
impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
if (impl->core == NULL) {

View file

@ -1832,9 +1832,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P")),
&props->dict,
@ -1927,8 +1927,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, PW_KEY_MEDIA_NAME);
copy_props(impl, props, "resample.prefill");
parse_audio_info(impl->capture_props, &impl->capture_info);
parse_audio_info(impl->playback_props, &impl->playback_info);
if ((res = parse_audio_info(impl->capture_props, &impl->capture_info)) < 0 ||
(res = parse_audio_info(impl->playback_props, &impl->playback_info)) < 0) {
pw_log_error( "can't parse format: %s", spa_strerror(res));
goto error;
}
if (!impl->capture_info.rate && !impl->playback_info.rate) {
if (pw_properties_get(impl->playback_props, "resample.disable") == NULL)

View file

@ -115,6 +115,7 @@
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
#define PW_LOG_TOPIC_DEFAULT mod_topic
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define MAX_PORTS 128
#define DEFAULT_CLIENT_NAME "PipeWire"
@ -157,7 +158,7 @@ struct port {
struct volume {
bool mute;
uint32_t n_volumes;
float volumes[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
};
struct stream {
@ -513,6 +514,7 @@ static void make_stream_ports(struct stream *s)
for (i = 0; i < s->n_ports; i++) {
struct port *port = s->ports[i];
char *link_port = NULL;
char pos[8];
if (port != NULL) {
s->ports[i] = NULL;
@ -522,8 +524,8 @@ static void make_stream_ports(struct stream *s)
}
if (i < s->info.channels) {
str = spa_debug_type_find_short_name(spa_type_audio_channel,
s->info.position[i]);
str = spa_type_audio_channel_make_short_name(
s->info.position[i], pos, sizeof(pos), NULL);
if (str)
snprintf(name, sizeof(name), "%s_%s", prefix, str);
else
@ -624,9 +626,9 @@ static void parse_props(struct stream *s, const struct spa_pod *param)
case SPA_PROP_channelVolumes:
{
uint32_t n;
float vols[SPA_AUDIO_MAX_CHANNELS];
float vols[MAX_CHANNELS];
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
vols, SPA_N_ELEMENTS(vols))) > 0) {
s->volume.n_volumes = n;
for (n = 0; n < s->volume.n_volumes; n++)
s->volume.volumes[n] = vols[n];
@ -1050,9 +1052,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"),
SPA_DICT_ITEM(SPA_KEY_AUDIO_POSITION, DEFAULT_POSITION)),
@ -1175,8 +1177,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, "jack.connect-audio");
copy_props(impl, props, "jack.connect-midi");
parse_audio_info(impl->source.props, &impl->source.info);
parse_audio_info(impl->sink.props, &impl->sink.info);
if ((res = parse_audio_info(impl->source.props, &impl->source.info)) < 0 ||
(res = parse_audio_info(impl->sink.props, &impl->sink.info)) < 0) {
pw_log_error( "can't parse format: %s", spa_strerror(res));
goto error;
}
impl->source.n_midi = pw_properties_get_uint32(impl->source.props,
"midi.ports", DEFAULT_MIDI_PORTS);

View file

@ -538,8 +538,7 @@ static void param_format_changed(struct impl *impl, const struct spa_pod *param,
spa_zero(info);
if (param != NULL) {
if (spa_format_audio_raw_parse(param, &info) < 0 ||
info.channels == 0 ||
info.channels > SPA_AUDIO_MAX_CHANNELS)
info.channels == 0)
return;
if ((impl->info.format != 0 && impl->info.format != info.format) ||
@ -839,9 +838,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P")),
&props->dict,
@ -953,9 +952,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
if (pw_properties_get(impl->playback_props, PW_KEY_NODE_DESCRIPTION) == NULL)
pw_properties_set(impl->playback_props, PW_KEY_NODE_DESCRIPTION, str);
parse_audio_info(props, &impl->info);
parse_audio_info(impl->capture_props, &impl->capture_info);
parse_audio_info(impl->playback_props, &impl->playback_info);
if ((res = parse_audio_info(props, &impl->info)) < 0 ||
(res = parse_audio_info(impl->capture_props, &impl->capture_info)) < 0 ||
(res = parse_audio_info(impl->playback_props, &impl->playback_info)) < 0) {
pw_log_error( "can't parse formats: %s", spa_strerror(res));
goto error;
}
if (!impl->capture_info.rate && !impl->playback_info.rate) {
if (pw_properties_get(impl->playback_props, "resample.disable") == NULL)

View file

@ -441,11 +441,11 @@ static void make_stream_ports(struct stream *s)
}
if (i < s->info.channels) {
str = spa_debug_type_find_short_name(spa_type_audio_channel,
s->info.position[i % SPA_AUDIO_MAX_CHANNELS]);
str = spa_type_audio_channel_make_short_name(
s->info.position[i], name, sizeof(name), "UNK");
props = pw_properties_new(
PW_KEY_FORMAT_DSP, "32 bit float mono audio",
PW_KEY_AUDIO_CHANNEL, str ? str : "UNK",
PW_KEY_AUDIO_CHANNEL, str,
PW_KEY_PORT_PHYSICAL, "true",
NULL);
@ -512,9 +512,9 @@ static void parse_props(struct stream *s, const struct spa_pod *param)
case SPA_PROP_channelVolumes:
{
uint32_t n;
float vols[SPA_AUDIO_MAX_CHANNELS];
float vols[MAX_CHANNELS];
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
vols, SPA_N_ELEMENTS(vols))) > 0) {
s->volume.n_volumes = n;
for (n = 0; n < s->volume.n_volumes; n++)
s->volume.volumes[n] = vols[n];
@ -863,7 +863,7 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p
}
impl->sink.info.rate = peer->params.sample_rate;
if ((uint32_t)peer->params.send_audio_channels != impl->sink.info.channels) {
impl->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS);
impl->sink.info.channels = peer->params.send_audio_channels;
for (i = 0; i < impl->sink.info.channels; i++)
impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
}
@ -874,7 +874,7 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p
}
impl->source.info.rate = peer->params.sample_rate;
if ((uint32_t)peer->params.recv_audio_channels != impl->source.info.channels) {
impl->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS);
impl->source.info.channels = peer->params.recv_audio_channels;
for (i = 0; i < impl->source.info.channels; i++)
impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
}
@ -1218,9 +1218,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P")),
&props->dict,
@ -1336,8 +1336,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, "midi.ports");
copy_props(impl, props, "audio.ports");
parse_audio_info(impl->source.props, &impl->source.info);
parse_audio_info(impl->sink.props, &impl->sink.info);
if ((res = parse_audio_info(impl->source.props, &impl->source.info)) < 0 ||
(res = parse_audio_info(impl->sink.props, &impl->sink.info)) < 0) {
pw_log_error( "can't parse format: %s", spa_strerror(res));
goto error;
}
impl->source.wanted_n_midi = pw_properties_get_int32(impl->source.props,
"midi.ports", DEFAULT_MIDI_PORTS);

View file

@ -181,7 +181,7 @@ static const struct spa_dict_item module_props[] = {
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
};
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info);
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info);
struct port {
enum spa_direction direction;
@ -601,12 +601,11 @@ static void make_stream_ports(struct stream *s)
}
if (i < s->info.channels) {
str = spa_debug_type_find_short_name(spa_type_audio_channel,
s->info.position[i]);
str = spa_type_audio_channel_make_short_name(
s->info.position[i], name, sizeof(name), "UNK");
props = pw_properties_new(
PW_KEY_FORMAT_DSP, "32 bit float mono audio",
PW_KEY_AUDIO_CHANNEL, str ? str : "UNK",
PW_KEY_AUDIO_CHANNEL, str,
PW_KEY_PORT_PHYSICAL, "true",
NULL);
@ -677,9 +676,9 @@ static void parse_props(struct stream *s, const struct spa_pod *param)
case SPA_PROP_channelVolumes:
{
uint32_t n;
float vols[SPA_AUDIO_MAX_CHANNELS];
float vols[MAX_CHANNELS];
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
vols, SPA_N_ELEMENTS(vols))) > 0) {
s->volume.n_volumes = n;
for (n = 0; n < s->volume.n_volumes; n++)
s->volume.volumes[n] = vols[n];
@ -969,8 +968,11 @@ static int handle_follower_available(struct impl *impl, struct nj2_session_param
follower->sink.direction = PW_DIRECTION_INPUT;
follower->sink.props = pw_properties_copy(impl->sink_props);
parse_audio_info(follower->source.props, &follower->source.info);
parse_audio_info(follower->sink.props, &follower->sink.info);
if ((res = parse_audio_info(follower->source.props, &follower->source.info)) < 0 ||
(res = parse_audio_info(follower->sink.props, &follower->sink.info)) < 0) {
pw_log_error("can't parse format: %s", spa_strerror(res));
return res;
}
follower->source.n_audio = pw_properties_get_uint32(follower->source.props,
"audio.ports", follower->source.info.channels ?
@ -1026,14 +1028,14 @@ static int handle_follower_available(struct impl *impl, struct nj2_session_param
follower->source.n_ports = peer->params.recv_audio_channels + peer->params.recv_midi_channels;
follower->source.info.rate = peer->params.sample_rate;
if ((uint32_t)peer->params.recv_audio_channels != follower->source.info.channels) {
follower->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS);
follower->source.info.channels = peer->params.recv_audio_channels;
for (i = 0; i < follower->source.info.channels; i++)
follower->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
}
follower->sink.n_ports = peer->params.send_audio_channels + peer->params.send_midi_channels;
follower->sink.info.rate = peer->params.sample_rate;
if ((uint32_t)peer->params.send_audio_channels != follower->sink.info.channels) {
follower->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS);
follower->sink.info.channels = peer->params.send_audio_channels;
for (i = 0; i < follower->sink.info.channels; i++)
follower->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
}
@ -1290,9 +1292,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P")),
&props->dict,

View file

@ -7,10 +7,12 @@
#include <opus/opus_custom.h>
#endif
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
struct volume {
bool mute;
uint32_t n_volumes;
float volumes[SPA_AUDIO_MAX_CHANNELS];
float volumes[MAX_CHANNELS];
};
static inline float bswap_f32(float f)

View file

@ -745,9 +745,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT),
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)),
@ -896,7 +896,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, PW_KEY_TARGET_OBJECT);
copy_props(impl, props, "pipe.filename");
parse_audio_info(impl->stream_props, &impl->info);
if ((res = parse_audio_info(impl->stream_props, &impl->info)) < 0) {
pw_log_error("can't parse format: %s", spa_strerror(res));
goto error;
}
impl->frame_size = calc_frame_size(&impl->info);
if (impl->frame_size == 0) {

View file

@ -12,6 +12,8 @@
#include "format.h"
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
static const struct format audio_formats[] = {
[SAMPLE_U8] = { SAMPLE_U8, SPA_AUDIO_FORMAT_U8, "u8", 1 },
[SAMPLE_ALAW] = { SAMPLE_ALAW, SPA_AUDIO_FORMAT_ALAW, "alaw", 1 },
@ -296,9 +298,9 @@ uint32_t channel_pa2id(enum channel_position channel)
return audio_channels[channel].channel;
}
const char *channel_id2name(uint32_t channel)
const char *channel_id2name(uint32_t channel, char *buf, size_t size)
{
return spa_type_audio_channel_to_short_name(channel);
return spa_type_audio_channel_make_short_name(channel, buf, size, "UNK");
}
uint32_t channel_name2id(const char *name)
@ -346,9 +348,9 @@ uint32_t channel_paname2id(const char *name, size_t size)
}
void channel_map_to_positions(const struct channel_map *map, uint32_t *pos)
void channel_map_to_positions(const struct channel_map *map, uint32_t *pos, uint32_t max_pos)
{
uint32_t i, channels = SPA_MIN(map->channels, SPA_AUDIO_MAX_CHANNELS);
uint32_t i, channels = SPA_MIN(map->channels, max_pos);
for (i = 0; i < channels; i++)
pos[i] = map->map[i];
}
@ -448,8 +450,9 @@ void channel_map_parse(const char *str, struct channel_map *map)
void channel_map_parse_position(const char *str, struct channel_map *map)
{
uint32_t channels = 0, position[SPA_AUDIO_MAX_CHANNELS];
spa_audio_parse_position(str, strlen(str), position, &channels);
uint32_t channels = 0, position[CHANNELS_MAX];
spa_audio_parse_position_n(str, strlen(str), position,
SPA_N_ELEMENTS(position), &channels);
positions_to_channel_map(position, channels, map);
}
@ -532,8 +535,7 @@ int format_parse_param(const struct spa_pod *param, bool collect,
info.info.raw.rate = 48000;
if (info.info.raw.format == 0 ||
info.info.raw.rate == 0 ||
info.info.raw.channels == 0 ||
info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
info.info.raw.channels == 0)
return -ENOTSUP;
}
break;
@ -631,8 +633,8 @@ const struct spa_pod *format_build_param(struct spa_pod_builder *b, uint32_t id,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(spec->channels), 0);
if (map && map->channels == spec->channels) {
uint32_t positions[SPA_AUDIO_MAX_CHANNELS];
channel_map_to_positions(map, positions);
uint32_t positions[spec->channels];
channel_map_to_positions(map, positions, spec->channels);
spa_pod_builder_add(b, SPA_FORMAT_AUDIO_position,
SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id,
spec->channels, positions), 0);

View file

@ -190,13 +190,13 @@ void sample_spec_fix(struct sample_spec *ss, struct channel_map *map,
struct spa_dict *props);
uint32_t channel_pa2id(enum channel_position channel);
const char *channel_id2name(uint32_t channel);
const char *channel_id2name(uint32_t channel, char *buf, size_t size);
uint32_t channel_name2id(const char *name);
enum channel_position channel_id2pa(uint32_t id, uint32_t *aux);
const char *channel_id2paname(uint32_t id, uint32_t *aux);
uint32_t channel_paname2id(const char *name, size_t size);
void channel_map_to_positions(const struct channel_map *map, uint32_t *pos);
void channel_map_to_positions(const struct channel_map *map, uint32_t *pos, uint32_t max_pos);
void channel_map_parse(const char *str, struct channel_map *map);
bool channel_map_valid(const struct channel_map *map);
void channel_map_parse_position(const char *str, struct channel_map *map);

View file

@ -761,11 +761,13 @@ int message_dump(enum spa_log_level level, const char *prefix, struct message *m
case TAG_CHANNEL_MAP:
{
struct channel_map map;
char pos[8];
if ((res = read_channel_map(m, &map)) < 0)
return res;
pw_log(level, "%s %u: channelmap: channels:%u", prefix, o, map.channels);
for (i = 0; i < map.channels; i++)
pw_log(level, "%s %d: %s", prefix, i, channel_id2name(map.map[i]));
pw_log(level, "%s %d: %s", prefix, i,
channel_id2name(map.map[i], pos, sizeof(pos)));
break;
}
case TAG_CVOLUME:

View file

@ -12,6 +12,7 @@
#include <spa/utils/list.h>
#include <spa/utils/hook.h>
#include <spa/utils/string.h>
#include <spa/param/audio/raw-utils.h>
#include <pipewire/log.h>
#include <pipewire/map.h>
#include <pipewire/properties.h>
@ -226,14 +227,15 @@ int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props
info->channels, map.channels);
return -EINVAL;
}
channel_map_to_positions(&map, info->position);
channel_map_to_positions(&map, info->position, SPA_N_ELEMENTS(info->position));
pw_properties_set(props, key_channel_map, NULL);
} else {
if (info->channels == 0)
info->channels = impl->defs.sample_spec.channels;
if (info->channels == impl->defs.channel_map.channels) {
channel_map_to_positions(&impl->defs.channel_map, info->position);
channel_map_to_positions(&impl->defs.channel_map,
info->position, SPA_N_ELEMENTS(info->position));
} else if (info->channels == 1) {
info->position[0] = SPA_AUDIO_CHANNEL_MONO;
} else if (info->channels == 2) {
@ -281,14 +283,14 @@ void audioinfo_to_properties(struct spa_audio_info_raw *info, struct pw_properti
if (info->rate)
pw_properties_setf(props, SPA_KEY_AUDIO_RATE, "%u", info->rate);
if (info->channels) {
char *s, *p;
char *s, *p, pos[8];
pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels);
p = s = alloca(info->channels * 8);
for (i = 0; i < info->channels; i++)
p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ", ",
channel_id2name(info->position[i]));
channel_id2name(info->position[i], pos, sizeof(pos)));
pw_properties_setf(props, SPA_KEY_AUDIO_POSITION, "[ %s ]", s);
}
}

View file

@ -295,9 +295,11 @@ static int do_extension_stream_restore_write(struct module *module, struct clien
fprintf(f, " ]");
}
if (map.channels > 0) {
char pos[8];
fprintf(f, ", \"channels\": [");
for (i = 0; i < map.channels; i++)
fprintf(f, "%s\"%s\"", (i == 0 ? " ":", "), channel_id2name(map.map[i]));
fprintf(f, "%s\"%s\"", (i == 0 ? " ":", "),
channel_id2name(map.map[i], pos, sizeof(pos)));
fprintf(f, " ]");
}
if (device_name != NULL && device_name[0] &&

View file

@ -53,7 +53,7 @@ int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bo
if (monitor)
continue;
info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
info->volume.values, CHANNELS_MAX);
info->volume.values, SPA_N_ELEMENTS(info->volume.values));
SPA_FLAG_UPDATE(info->flags, VOLUME_HW_VOLUME,
prop->flags & SPA_POD_PROP_FLAG_HARDWARE);
break;
@ -68,7 +68,7 @@ int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bo
if (!monitor)
continue;
info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
info->volume.values, CHANNELS_MAX);
info->volume.values, SPA_N_ELEMENTS(info->volume.values));
SPA_FLAG_CLEAR(info->flags, VOLUME_HW_VOLUME);
break;
case SPA_PROP_volumeBase:
@ -84,7 +84,7 @@ int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bo
}
case SPA_PROP_channelMap:
info->map.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
info->map.map, CHANNELS_MAX);
info->map.map, SPA_N_ELEMENTS(info->map.map));
break;
default:
break;

View file

@ -815,13 +815,14 @@ static int calc_frame_size(struct spa_audio_info_raw *info)
case SPA_AUDIO_FORMAT_F64_OE:
return res * 8;
default:
return 0;
return -ENOTSUP;
}
}
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
int res;
if ((res = spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT),
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)),
@ -830,7 +831,8 @@ static int parse_audio_info(const struct pw_properties *props, struct spa_audio_
SPA_KEY_AUDIO_FORMAT,
SPA_KEY_AUDIO_RATE,
SPA_KEY_AUDIO_CHANNELS,
SPA_KEY_AUDIO_POSITION, NULL);
SPA_KEY_AUDIO_POSITION, NULL)) < 0)
return res;
return calc_frame_size(info);
}
@ -851,6 +853,7 @@ static int parse_params(struct impl *impl)
const char *str;
struct spa_json it[1];
char value[512];
int res;
pw_properties_fetch_bool(impl->props, "capture", &impl->capture);
pw_properties_fetch_bool(impl->props, "playback", &impl->playback);
@ -894,19 +897,20 @@ static int parse_params(struct impl *impl)
copy_props(impl, PW_KEY_NODE_VIRTUAL);
copy_props(impl, PW_KEY_NODE_NETWORK);
impl->capture_frame_size = parse_audio_info(impl->capture_props, &impl->capture_info);
if (impl->capture_frame_size == 0) {
if ((res = parse_audio_info(impl->capture_props, &impl->capture_info)) <= 0) {
pw_log_error("unsupported capture audio format:%d channels:%d",
impl->capture_info.format, impl->capture_info.channels);
return -EINVAL;
}
impl->capture_frame_size = res;
impl->playback_frame_size = parse_audio_info(impl->playback_props, &impl->playback_info);
if (impl->playback_frame_size == 0) {
if ((res = parse_audio_info(impl->playback_props, &impl->playback_info)) <= 0) {
pw_log_error("unsupported playback audio format:%d channels:%d",
impl->playback_info.format, impl->playback_info.channels);
return -EINVAL;
}
impl->playback_frame_size = res;
if (impl->capture_info.rate != 0 &&
pw_properties_get(impl->capture_props, PW_KEY_NODE_RATE) == NULL)
pw_properties_setf(impl->capture_props, PW_KEY_NODE_RATE,

View file

@ -117,6 +117,8 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
#define DEFAULT_CHANNELS 2
#define DEFAULT_POSITION "[ FL FR ]"
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define MODULE_USAGE "( remote.name=<remote> ] " \
"( node.latency=<latency as fraction> ] " \
"( node.name=<name of the nodes> ] " \
@ -295,10 +297,10 @@ static void stream_param_changed(void *d, uint32_t id, const struct spa_pod *par
{
struct pa_cvolume volume;
uint32_t n;
float vols[SPA_AUDIO_MAX_CHANNELS];
float vols[MAX_CHANNELS];
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
vols, SPA_N_ELEMENTS(vols))) > 0) {
volume.channels = SPA_MIN(PA_CHANNELS_MAX, n);
for (n = 0; n < volume.channels; n++)
volume.values[n] = pa_sw_volume_from_linear(vols[n]);
@ -752,7 +754,8 @@ static int create_pulse_stream(struct impl *impl)
map.channels = impl->info.channels;
for (i = 0; i < map.channels; i++)
map.map[i] = (pa_channel_position_t)channel_id2pa(impl->info.position[i], &aux);
map.map[i] = (pa_channel_position_t)channel_id2pa(
impl->info.position[i], &aux);
snprintf(stream_name, sizeof(stream_name), _("Tunnel for %s@%s"),
pw_get_user_name(), pw_get_host_name());
@ -832,10 +835,10 @@ do_stream_sync_volumes(struct spa_loop *loop,
struct spa_pod_frame f[1];
struct spa_pod *param;
uint32_t i, channels;
float vols[SPA_AUDIO_MAX_CHANNELS];
float soft_vols[SPA_AUDIO_MAX_CHANNELS];
float vols[MAX_CHANNELS];
float soft_vols[MAX_CHANNELS];
channels = SPA_MIN(impl->volume.channels, SPA_AUDIO_MAX_CHANNELS);
channels = SPA_MIN(impl->volume.channels, MAX_CHANNELS);
for (i = 0; i < channels; i++) {
vols[i] = (float)pa_sw_volume_to_linear(impl->volume.values[i]);
soft_vols[i] = 1.0f;
@ -1045,9 +1048,9 @@ static const struct pw_impl_module_events module_events = {
.destroy = module_destroy,
};
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT),
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)),
@ -1189,7 +1192,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, PW_KEY_NODE_NETWORK);
copy_props(impl, props, PW_KEY_MEDIA_CLASS);
parse_audio_info(impl->stream_props, &impl->info);
if ((res = parse_audio_info(impl->stream_props, &impl->info)) < 0) {
pw_log_error("can't parse format: %s", spa_strerror(res));
goto error;
}
impl->frame_size = calc_frame_size(&impl->info);
if (impl->frame_size == 0) {

View file

@ -158,6 +158,7 @@ PW_LOG_TOPIC(mod_topic, "mod." NAME);
#define RAOP_LATENCY_MS 250
#define DEFAULT_LATENCY_MS 1500
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define VOLUME_MAX 0.0
#define VOLUME_MIN -30.0
#define VOLUME_MUTE -144.0
@ -1612,11 +1613,11 @@ static void stream_props_changed(struct impl *impl, uint32_t id, const struct sp
case SPA_PROP_channelVolumes:
{
uint32_t i, n_vols;
float vols[SPA_AUDIO_MAX_CHANNELS], volume;
float soft_vols[SPA_AUDIO_MAX_CHANNELS];
float vols[MAX_CHANNELS], volume;
float soft_vols[MAX_CHANNELS];
if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
vols, SPA_N_ELEMENTS(vols))) > 0) {
volume = 0.0f;
for (i = 0; i < n_vols; i++) {
volume += vols[i];

View file

@ -156,6 +156,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
#define DEFAULT_LOOP false
#define MAX_SDP 2048
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
#define USAGE "( local.ifname=<local interface name to use> ) " \
"( sap.ip=<SAP IP address to send announce, default:"DEFAULT_SAP_IP"> ) " \
@ -1404,7 +1405,7 @@ static int parse_sdp_i(struct impl *impl, char *c, struct sdp_info *info)
c[strcspn(c, " ")] = '\0';
uint32_t channels;
if (sscanf(c, "%u", &channels) != 1 || channels <= 0 || channels > SPA_AUDIO_MAX_CHANNELS)
if (sscanf(c, "%u", &channels) != 1 || channels <= 0 || channels > MAX_CHANNELS)
return 0;
c += strcspn(c, "\0");

View file

@ -334,9 +334,12 @@ static void rtp_opus_deinit(struct impl *impl, enum spa_direction direction)
static int rtp_opus_init(struct impl *impl, enum spa_direction direction)
{
int err;
unsigned char mapping[SPA_AUDIO_MAX_CHANNELS];
unsigned char mapping[255];
uint32_t i;
if (impl->info.info.opus.channels > 255)
return -EINVAL;
if (impl->psamples >= 2880)
impl->psamples = 2880;
else if (impl->psamples >= 1920)

View file

@ -569,9 +569,9 @@ static const struct format_info *find_audio_format_info(const struct spa_audio_i
return NULL;
}
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT),
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)),
@ -675,7 +675,10 @@ struct rtp_stream *rtp_stream_new(struct pw_core *core,
switch (impl->info.media_subtype) {
case SPA_MEDIA_SUBTYPE_raw:
parse_audio_info(props, &impl->info.info.raw);
if ((res = parse_audio_info(props, &impl->info.info.raw)) < 0) {
pw_log_error("can't parse format: %s", spa_strerror(res));
goto out;
}
impl->stream_info = impl->info;
impl->format_info = find_audio_format_info(&impl->info);
if (impl->format_info == NULL) {
@ -704,7 +707,10 @@ struct rtp_stream *rtp_stream_new(struct pw_core *core,
case SPA_MEDIA_SUBTYPE_opus:
impl->stream_info.media_type = SPA_MEDIA_TYPE_audio;
impl->stream_info.media_subtype = SPA_MEDIA_SUBTYPE_raw;
parse_audio_info(props, &impl->stream_info.info.raw);
if ((res = parse_audio_info(props, &impl->stream_info.info.raw)) < 0) {
pw_log_error("can't parse format: %s", spa_strerror(res));
goto out;
}
impl->stream_info.info.raw.format = SPA_AUDIO_FORMAT_F32;
impl->info.info.opus.rate = impl->stream_info.info.raw.rate;
impl->info.info.opus.channels = impl->stream_info.info.raw.channels;

View file

@ -503,9 +503,11 @@ static int add_snapcast_stream(struct impl *impl, struct tunnel *t,
return -ENOENT;
}
static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
int res;
if ((res = spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT),
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)),
@ -514,12 +516,14 @@ static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_
SPA_KEY_AUDIO_FORMAT,
SPA_KEY_AUDIO_RATE,
SPA_KEY_AUDIO_CHANNELS,
SPA_KEY_AUDIO_POSITION, NULL);
SPA_KEY_AUDIO_POSITION, NULL)) < 0)
return res;
pw_properties_set(props, PW_KEY_AUDIO_FORMAT,
spa_type_audio_format_to_short_name(info->format));
pw_properties_setf(props, PW_KEY_AUDIO_RATE, "%d", info->rate);
pw_properties_setf(props, PW_KEY_AUDIO_CHANNELS, "%d", info->channels);
return res;
}
static int create_stream(struct impl *impl, struct pw_properties *props,
@ -545,7 +549,10 @@ static int create_stream(struct impl *impl, struct pw_properties *props,
if ((str = pw_properties_get(props, "capture.props")) == NULL)
pw_properties_set(props, "capture.props", "{ media.class = Audio/Sink }");
parse_audio_info(props, &t->audio_info);
if ((res = parse_audio_info(props, &t->audio_info)) < 0) {
pw_log_error("Can't parse format: %s", spa_strerror(res));
goto done;
}
if ((f = open_memstream(&args, &size)) == NULL) {
res = -errno;

View file

@ -185,9 +185,9 @@ static const struct format_info *find_audio_format_info(const struct spa_audio_i
return NULL;
}
static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
{
spa_audio_info_raw_init_dict_keys(info,
return spa_audio_info_raw_init_dict_keys(info,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT),
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)),
@ -274,9 +274,14 @@ struct vban_stream *vban_stream_new(struct pw_core *core,
switch (impl->info.media_subtype) {
case SPA_MEDIA_SUBTYPE_raw:
parse_audio_info(props, &impl->info.info.raw);
if (SPA_FLAG_IS_SET(impl->info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED))
if ((res = parse_audio_info(props, &impl->info.info.raw)) < 0) {
pw_log_error("can't parse format: %s", spa_strerror(res));
goto out;
}
if (SPA_FLAG_IS_SET(impl->info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED)) {
default_layout(impl->info.info.raw.channels, impl->info.info.raw.position);
SPA_FLAG_CLEAR(impl->info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED);
}
impl->stream_info = impl->info;
impl->format_info = find_audio_format_info(&impl->info);
if (impl->format_info == NULL) {

View file

@ -188,17 +188,17 @@ static void pw_properties_from_avahi_string(const char *key, const char *value,
else if (spa_streq(key, "channel_map")) {
struct channel_map channel_map;
uint32_t i, pos[CHANNELS_MAX];
char *p, *s;
char *p, *s, buf[8];
spa_zero(channel_map);
channel_map_parse(value, &channel_map);
channel_map_to_positions(&channel_map, pos);
channel_map_to_positions(&channel_map, pos, CHANNELS_MAX);
p = s = alloca(4 + channel_map.channels * 8);
p += spa_scnprintf(p, 2, "[");
for (i = 0; i < channel_map.channels; i++)
p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",",
channel_id2name(pos[i]));
channel_id2name(pos[i], buf, sizeof(buf)));
p += spa_scnprintf(p, 2, "]");
pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s);
}

View file

@ -252,7 +252,8 @@ static int update_string(struct pw_properties *props, const char *str, size_t si
continue;
}
/* item changed or added, apply changes later */
if ((errno = -add_item(&changes, key, false, val, true) < 0)) {
if ((res = add_item(&changes, key, false, val, true)) < 0) {
errno = -res;
it[0].state = SPA_JSON_ERROR_FLAG;
break;
}

View file

@ -245,24 +245,21 @@ void pw_settings_init(struct pw_context *this)
static void expose_settings(struct pw_context *context, struct pw_impl_metadata *metadata)
{
struct settings *s = &context->settings;
uint32_t i, o;
char rates[MAX_RATES*16] = "";
uint32_t i;
char rates[MAX_RATES*16];
struct spa_strbuf b;
pw_impl_metadata_set_propertyf(metadata,
PW_ID_CORE, "log.level", "", "%d", s->log_level);
pw_impl_metadata_set_propertyf(metadata,
PW_ID_CORE, "clock.rate", "", "%d", s->clock_rate);
for (i = 0, o = 0; i < s->n_clock_rates; i++) {
int r = snprintf(rates+o, sizeof(rates)-o, "%s%d", i == 0 ? "" : ", ",
spa_strbuf_init(&b, rates, sizeof(rates));
for (i = 0; i < s->n_clock_rates; i++)
spa_strbuf_append(&b, "%s%d", i == 0 ? "" : ", ",
s->clock_rates[i]);
if (r < 0 || o + r >= (int)sizeof(rates)) {
snprintf(rates, sizeof(rates), "%d", s->clock_rate);
break;
}
o += r;
}
if (s->n_clock_rates == 0)
snprintf(rates, sizeof(rates), "%d", s->clock_rate);
spa_strbuf_append(&b, "%d", s->clock_rate);
pw_impl_metadata_set_propertyf(metadata,
PW_ID_CORE, "clock.allowed-rates", "", "[ %s ]", rates);

View file

@ -70,6 +70,8 @@
#define DEFAULT_VOLUME 1.0
#define DEFAULT_QUALITY 4
#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS
enum mode {
mode_none,
mode_playback,
@ -91,7 +93,7 @@ typedef int (*fill_fn)(struct data *d, void *dest, unsigned int n_frames, bool *
struct channelmap {
uint32_t n_channels;
uint32_t channels[SPA_AUDIO_MAX_CHANNELS];
uint32_t channels[MAX_CHANNELS];
};
struct data {
@ -702,7 +704,8 @@ static int parse_channelmap(const char *channel_map, struct channelmap *map)
}
}
spa_audio_parse_position(channel_map, strlen(channel_map), map->channels, &map->n_channels);
spa_audio_parse_position_n(channel_map, strlen(channel_map),
map->channels, SPA_N_ELEMENTS(map->channels), &map->n_channels);
return 0;
}
@ -744,10 +747,11 @@ static int channelmap_default(struct channelmap *map, int n_channels)
static void channelmap_print(struct channelmap *map)
{
uint32_t i;
char pos[8];
for (i = 0; i < map->n_channels; i++) {
const char *name = spa_type_audio_channel_to_short_name(map->channels[i]);
fprintf(stderr, "%s%s", name, i + 1 < map->n_channels ? "," : "");
fprintf(stderr, "%s%s", i ? "," : "",
spa_type_audio_channel_make_short_name(map->channels[i],
pos, sizeof(pos), "UNK"));
}
}
@ -2344,9 +2348,23 @@ int main(int argc, char *argv[])
.rate = data.rate,
.channels = data.channels);
if (data.channelmap.n_channels)
memcpy(info.position, data.channelmap.channels, data.channels * sizeof(int));
if (data.channels > MAX_CHANNELS) {
fprintf(stderr, "error: too many channels %d > %d\n",
data.channels, MAX_CHANNELS);
goto error_bad_file;
}
if (data.channelmap.n_channels) {
if (data.channels > MAX_CHANNELS) {
fprintf(stderr, "error: too many channels in channelmap %d > %d\n",
data.channelmap.n_channels, MAX_CHANNELS);
goto error_bad_file;
}
uint32_t i;
for (i = 0; i < data.channelmap.n_channels; i++)
info.position[i] = data.channelmap.channels[i];
for (; i < data.channels; i++)
info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
}
params[n_params++] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
break;
}

View file

@ -112,6 +112,7 @@ test('test-spa',
executable('test-spa',
'test-spa-buffer.c',
'test-spa-control.c',
'test-spa-format.c',
'test-spa-json.c',
'test-spa-utils.c',
'test-spa-log.c',

129
test/test-spa-format.c Normal file
View file

@ -0,0 +1,129 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2025 Pauli Virtanen */
/* SPDX-License-Identifier: MIT */
#include <spa/param/audio/format-utils.h>
#include <spa/param/audio/format.h>
#include "pwtest.h"
PWTEST(audio_format_sizes)
{
union {
uint8_t buf[1024];
struct spa_audio_info align;
} data;
struct spa_audio_info info;
size_t i;
memset(&info, 0xf3, sizeof(info));
info.media_type = SPA_MEDIA_TYPE_audio;
info.media_subtype = SPA_MEDIA_SUBTYPE_raw;
info.info.raw.channels = 5;
info.info.raw.format = SPA_AUDIO_FORMAT_F32P;
info.info.raw.rate = 12345;
info.info.raw.flags = 0;
info.info.raw.position[0] = 1;
info.info.raw.position[1] = 2;
info.info.raw.position[2] = 3;
info.info.raw.position[3] = 4;
info.info.raw.position[4] = 5;
for (i = 0; i < sizeof(data.buf); ++i) {
struct spa_pod *pod;
uint8_t buf[4096];
struct spa_pod_builder b;
spa_pod_builder_init(&b, buf, sizeof(buf));
memcpy(data.buf, &info, sizeof(info));
pod = spa_format_audio_ext_build(&b, 123, (void *)data.buf, i);
if (i < offsetof(struct spa_audio_info, info.raw)
+ offsetof(struct spa_audio_info_raw, position))
pwtest_bool_true(!pod);
else
pwtest_bool_true(pod);
}
for (i = 0; i < sizeof(data.buf); ++i) {
struct spa_pod *pod;
uint8_t buf[4096];
struct spa_pod_builder b;
int ret;
spa_pod_builder_init(&b, buf, sizeof(buf));
pod = spa_format_audio_ext_build(&b, 123, &info, sizeof(info));
pwtest_bool_true(pod);
memset(data.buf, 0xf3, sizeof(data.buf));
ret = spa_format_audio_ext_parse(pod, (void *)data.buf, i);
if (i < offsetof(struct spa_audio_info, info.raw)
+ offsetof(struct spa_audio_info_raw, position)
+ info.info.raw.channels*sizeof(uint32_t)) {
for (size_t j = i; j < sizeof(data.buf); ++j)
pwtest_int_eq(data.buf[j], 0xf3);
pwtest_int_lt(ret, 0);
} else {
pwtest_int_ge(ret, 0);
pwtest_bool_true(memcmp(data.buf, &info, SPA_MIN(i, sizeof(info))) == 0);
}
}
memset(&info, 0xf3, sizeof(info));
info.media_type = SPA_MEDIA_TYPE_audio;
info.media_subtype = SPA_MEDIA_SUBTYPE_aac;
info.info.aac.rate = 12345;
info.info.aac.channels = 6;
info.info.aac.bitrate = 54321;
info.info.aac.stream_format = SPA_AUDIO_AAC_STREAM_FORMAT_MP4LATM;
for (i = 0; i < sizeof(data.buf); ++i) {
struct spa_pod *pod;
uint8_t buf[4096];
struct spa_pod_builder b;
spa_pod_builder_init(&b, buf, sizeof(buf));
memcpy(data.buf, &info, sizeof(info));
pod = spa_format_audio_ext_build(&b, 123, (void *)data.buf, i);
if (i < offsetof(struct spa_audio_info, info.raw)
+ sizeof(struct spa_audio_info_aac))
pwtest_bool_true(!pod);
else
pwtest_bool_true(pod);
}
for (i = 0; i < sizeof(data.buf); ++i) {
struct spa_pod *pod;
uint8_t buf[4096];
struct spa_pod_builder b;
int ret;
spa_pod_builder_init(&b, buf, sizeof(buf));
pod = spa_format_audio_ext_build(&b, 123, &info, sizeof(info));
pwtest_bool_true(pod);
memset(data.buf, 0xf3, sizeof(data.buf));
ret = spa_format_audio_ext_parse(pod, (void *)data.buf, i);
if (i < offsetof(struct spa_audio_info, info.raw)
+ sizeof(struct spa_audio_info_aac)) {
for (size_t j = i; j < sizeof(data.buf); ++j)
pwtest_int_eq(data.buf[j], 0xf3);
pwtest_int_lt(ret, 0);
} else {
pwtest_int_ge(ret, 0);
pwtest_bool_true(memcmp(data.buf, &info, SPA_MIN(i, sizeof(info))) == 0);
}
}
return PWTEST_PASS;
}
PWTEST_SUITE(spa_format)
{
pwtest_add(audio_format_sizes, PWTEST_NOARG);
return PWTEST_PASS;
}

View file

@ -609,7 +609,7 @@ static void test_array(const char *str, const char * const vals[])
spa_json_init(&it[0], str, strlen(str));
if (spa_json_enter_array(&it[0], &it[1]) <= 0)
spa_json_init(&it[1], str, strlen(str));
spa_json_init_relax(&it[1], '[', str, strlen(str));
for (i = 0; vals[i]; i++) {
pwtest_int_gt(spa_json_get_string(&it[1], val, sizeof(val)), 0);
pwtest_str_eq(val, vals[i]);
@ -624,6 +624,7 @@ PWTEST(json_array)
test_array("[FL FR]", (const char *[]){ "FL", "FR", NULL });
test_array("FL FR", (const char *[]){ "FL", "FR", NULL });
test_array("[ FL FR ]", (const char *[]){ "FL", "FR", NULL });
test_array("FL FR FC", (const char *[]){ "FL", "FR", "FC", NULL });
return PWTEST_PASS;
}