mirror of
https://gitlab.freedesktop.org/pipewire/pipewire
synced 2024-07-21 10:16:14 +00:00
audioconvert: remap channels
We also need to remap channels for the splitter and merger. Remember the port-config format and its channel layout. Internally, we use a canonical channel layout which is simply all channels sorted by id. Remap the channels accordingly. Fixes #445
This commit is contained in:
parent
2d8a7809f1
commit
78e7a45e6e
|
@ -109,12 +109,16 @@ struct impl {
|
|||
struct port in_ports[MAX_PORTS];
|
||||
struct port out_ports[MAX_PORTS + 1];
|
||||
|
||||
struct spa_audio_info format;
|
||||
unsigned int have_profile:1;
|
||||
|
||||
struct convert conv;
|
||||
uint32_t cpu_flags;
|
||||
unsigned int is_passthrough:1;
|
||||
unsigned int started:1;
|
||||
unsigned int monitor:1;
|
||||
unsigned int have_profile:1;
|
||||
|
||||
uint32_t remap[SPA_AUDIO_MAX_CHANNELS];
|
||||
|
||||
float empty[MAX_SAMPLES + MAX_ALIGN];
|
||||
};
|
||||
|
@ -255,6 +259,17 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int int32_cmp(const void *v1, const void *v2)
|
||||
{
|
||||
int32_t a1 = *(int32_t*)v1;
|
||||
int32_t a2 = *(int32_t*)v2;
|
||||
if (a1 == 0 && a2 != 0)
|
||||
return 1;
|
||||
if (a2 == 0 && a1 != 0)
|
||||
return -1;
|
||||
return a1 - a2;
|
||||
}
|
||||
|
||||
static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
|
@ -300,8 +315,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
if (spa_format_audio_raw_parse(format, &info.info.raw) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
port = GET_OUT_PORT(this, 0);
|
||||
if (port->have_format && memcmp(&port->format, &info, sizeof(info)) == 0)
|
||||
if (this->have_profile && memcmp(&this->format, &info, sizeof(info)) == 0)
|
||||
return 0;
|
||||
|
||||
spa_log_debug(this->log, NAME " %p: port config %d/%d %d", this,
|
||||
|
@ -315,10 +329,8 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
SPA_DIRECTION_OUTPUT, i+1, NULL);
|
||||
}
|
||||
|
||||
port->have_format = true;
|
||||
port->format = info;
|
||||
this->monitor = monitor;
|
||||
|
||||
this->format = info;
|
||||
this->have_profile = true;
|
||||
this->port_count = info.info.raw.channels;
|
||||
this->monitor_count = this->monitor ? this->port_count : 0;
|
||||
|
@ -328,6 +340,11 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
init_port(this, SPA_DIRECTION_OUTPUT, i+1,
|
||||
info.info.raw.position[i]);
|
||||
}
|
||||
port = GET_OUT_PORT(this, 0);
|
||||
qsort(info.info.raw.position, info.info.raw.channels,
|
||||
sizeof(uint32_t), int32_cmp);
|
||||
port->format = info;
|
||||
port->have_format = true;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
|
@ -577,35 +594,56 @@ static int clear_buffers(struct impl *this, struct port *port)
|
|||
static int setup_convert(struct impl *this)
|
||||
{
|
||||
struct port *outport;
|
||||
uint32_t src_fmt, dst_fmt;
|
||||
struct spa_audio_info informat, outformat;
|
||||
uint32_t i, j, src_fmt, dst_fmt;
|
||||
int res;
|
||||
|
||||
outport = GET_OUT_PORT(this, 0);
|
||||
|
||||
informat = this->format;
|
||||
outformat = outport->format;
|
||||
|
||||
src_fmt = SPA_AUDIO_FORMAT_DSP_F32;
|
||||
dst_fmt = outport->format.info.raw.format;
|
||||
dst_fmt = outformat.info.raw.format;
|
||||
|
||||
spa_log_info(this->log, NAME " %p: %s/%d@%dx%d->%s/%d@%d", this,
|
||||
spa_debug_type_find_name(spa_type_audio_format, src_fmt),
|
||||
1,
|
||||
outport->format.info.raw.rate,
|
||||
this->port_count,
|
||||
informat.info.raw.rate,
|
||||
informat.info.raw.channels,
|
||||
spa_debug_type_find_name(spa_type_audio_format, dst_fmt),
|
||||
outport->format.info.raw.channels,
|
||||
outport->format.info.raw.rate);
|
||||
outformat.info.raw.channels,
|
||||
outformat.info.raw.rate);
|
||||
|
||||
for (i = 0; i < informat.info.raw.channels; i++) {
|
||||
for (j = 0; j < outformat.info.raw.channels; j++) {
|
||||
if (informat.info.raw.position[i] !=
|
||||
outformat.info.raw.position[j])
|
||||
continue;
|
||||
this->remap[i] = j;
|
||||
spa_log_debug(this->log, NAME " %p: channel %d -> %d (%s -> %s)", this,
|
||||
i, j,
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
informat.info.raw.position[i]),
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
outformat.info.raw.position[j]));
|
||||
outformat.info.raw.position[j] = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->conv.src_fmt = src_fmt;
|
||||
this->conv.dst_fmt = dst_fmt;
|
||||
this->conv.n_channels = outport->format.info.raw.channels;
|
||||
this->conv.n_channels = outformat.info.raw.channels;
|
||||
this->conv.cpu_flags = this->cpu_flags;
|
||||
|
||||
if ((res = convert_init(&this->conv)) < 0)
|
||||
return res;
|
||||
|
||||
spa_log_debug(this->log, NAME " %p: got converter features %08x:%08x", this,
|
||||
this->cpu_flags, this->conv.cpu_flags);
|
||||
this->is_passthrough = this->conv.is_passthrough;
|
||||
|
||||
this->is_passthrough = src_fmt == dst_fmt;
|
||||
spa_log_debug(this->log, NAME " %p: got converter features %08x:%08x passthrough:%d", this,
|
||||
this->cpu_flags, this->conv.cpu_flags, this->is_passthrough);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -958,7 +996,7 @@ static int impl_node_process(void *object)
|
|||
struct buffer *sbuf, *dbuf;
|
||||
uint32_t n_src_datas, n_dst_datas;
|
||||
const void **src_datas;
|
||||
void **dst_datas;
|
||||
void **dst_datas, **tmp;
|
||||
int res;
|
||||
|
||||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
||||
|
@ -987,6 +1025,7 @@ static int impl_node_process(void *object)
|
|||
|
||||
n_dst_datas = dbuf->buf->n_datas;
|
||||
dst_datas = alloca(sizeof(void*) * n_dst_datas);
|
||||
tmp = alloca(sizeof(void*) * n_dst_datas);
|
||||
|
||||
/* produce more output if possible */
|
||||
n_src_datas = 0;
|
||||
|
@ -1012,11 +1051,13 @@ static int impl_node_process(void *object)
|
|||
handle_monitor(this, src_datas[i], n_samples, GET_OUT_PORT(this, i + 1));
|
||||
|
||||
for (i = 0; i < n_dst_datas; i++) {
|
||||
dst_datas[i] = this->is_passthrough ? (void*)src_datas[i] : dbuf->datas[i];
|
||||
dbuf->buf->datas[i].data = dst_datas[i];
|
||||
tmp[i] = this->is_passthrough ? (void*)src_datas[i] : dbuf->datas[i];
|
||||
dbuf->buf->datas[i].data = tmp[i];
|
||||
dbuf->buf->datas[i].chunk->offset = 0;
|
||||
dbuf->buf->datas[i].chunk->size = n_samples * outport->stride;
|
||||
}
|
||||
for (i = 0; i < n_dst_datas; i++)
|
||||
dst_datas[i] = tmp[this->remap[i]];
|
||||
|
||||
spa_log_trace_fp(this->log, NAME " %p: n_src:%d n_dst:%d n_samples:%d max:%d p:%d", this,
|
||||
n_src_datas, n_dst_datas, n_samples, maxsize, this->is_passthrough);
|
||||
|
|
|
@ -109,12 +109,15 @@ struct impl {
|
|||
struct port out_ports[MAX_PORTS];
|
||||
uint32_t port_count;
|
||||
|
||||
struct spa_audio_info format;
|
||||
unsigned int have_profile:1;
|
||||
|
||||
uint32_t cpu_flags;
|
||||
struct convert conv;
|
||||
unsigned int is_passthrough:1;
|
||||
unsigned int started:1;
|
||||
|
||||
bool have_profile;
|
||||
uint32_t remap[SPA_AUDIO_MAX_CHANNELS];
|
||||
|
||||
float empty[MAX_SAMPLES + MAX_ALIGN];
|
||||
};
|
||||
|
@ -246,6 +249,17 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int int32_cmp(const void *v1, const void *v2)
|
||||
{
|
||||
int32_t a1 = *(int32_t*)v1;
|
||||
int32_t a2 = *(int32_t*)v2;
|
||||
if (a1 == 0 && a2 != 0)
|
||||
return 1;
|
||||
if (a2 == 0 && a1 != 0)
|
||||
return -1;
|
||||
return a1 - a2;
|
||||
}
|
||||
|
||||
static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
|
@ -289,8 +303,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
if (spa_format_audio_raw_parse(format, &info.info.raw) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
port = GET_IN_PORT(this, 0);
|
||||
if (port->have_format && memcmp(&port->format, &info, sizeof(info)) == 0)
|
||||
if (this->have_profile && memcmp(&this->format, &info, sizeof(info)) == 0)
|
||||
return 0;
|
||||
|
||||
spa_log_debug(this->log, NAME " %p: port config %d/%d", this,
|
||||
|
@ -302,14 +315,18 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
|
||||
this->have_profile = true;
|
||||
this->is_passthrough = true;
|
||||
port->have_format = true;
|
||||
port->format = info;
|
||||
this->format = info;
|
||||
|
||||
this->port_count = info.info.raw.channels;
|
||||
for (i = 0; i < this->port_count; i++) {
|
||||
init_port(this, SPA_DIRECTION_OUTPUT, i,
|
||||
info.info.raw.position[i]);
|
||||
}
|
||||
port = GET_IN_PORT(this, 0);
|
||||
qsort(info.info.raw.position, info.info.raw.channels,
|
||||
sizeof(uint32_t), int32_cmp);
|
||||
port->format = info;
|
||||
port->have_format = true;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
|
@ -559,37 +576,57 @@ static int clear_buffers(struct impl *this, struct port *port)
|
|||
static int setup_convert(struct impl *this)
|
||||
{
|
||||
struct port *inport;
|
||||
uint32_t src_fmt, dst_fmt;
|
||||
struct spa_audio_info informat, outformat;
|
||||
uint32_t i, j, src_fmt, dst_fmt;
|
||||
int res;
|
||||
|
||||
inport = GET_IN_PORT(this, 0);
|
||||
|
||||
src_fmt = inport->format.info.raw.format;
|
||||
informat = inport->format;
|
||||
outformat = this->format;
|
||||
|
||||
src_fmt = informat.info.raw.format;
|
||||
dst_fmt = SPA_AUDIO_FORMAT_DSP_F32;
|
||||
|
||||
spa_log_info(this->log, NAME " %p: %s/%d@%d->%s/%d@%dx%d", this,
|
||||
spa_debug_type_find_name(spa_type_audio_format, src_fmt),
|
||||
inport->format.info.raw.channels,
|
||||
inport->format.info.raw.rate,
|
||||
informat.info.raw.channels,
|
||||
informat.info.raw.rate,
|
||||
spa_debug_type_find_name(spa_type_audio_format, dst_fmt),
|
||||
1,
|
||||
inport->format.info.raw.rate,
|
||||
this->port_count);
|
||||
outformat.info.raw.rate,
|
||||
outformat.info.raw.channels);
|
||||
|
||||
for (i = 0; i < informat.info.raw.channels; i++) {
|
||||
for (j = 0; j < outformat.info.raw.channels; j++) {
|
||||
if (informat.info.raw.position[i] !=
|
||||
outformat.info.raw.position[j])
|
||||
continue;
|
||||
this->remap[i] = j;
|
||||
spa_log_debug(this->log, NAME " %p: channel %d -> %d (%s -> %s)", this,
|
||||
i, j,
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
informat.info.raw.position[i]),
|
||||
spa_debug_type_find_short_name(spa_type_audio_channel,
|
||||
outformat.info.raw.position[j]));
|
||||
outformat.info.raw.position[j] = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->conv.src_fmt = src_fmt;
|
||||
this->conv.dst_fmt = dst_fmt;
|
||||
this->conv.n_channels = inport->format.info.raw.channels;
|
||||
this->conv.n_channels = informat.info.raw.channels;
|
||||
this->conv.cpu_flags = this->cpu_flags;
|
||||
|
||||
if ((res = convert_init(&this->conv)) < 0)
|
||||
return res;
|
||||
|
||||
spa_log_debug(this->log, NAME " %p: got converter features %08x:%08x", this,
|
||||
this->cpu_flags, this->conv.cpu_flags);
|
||||
|
||||
this->is_passthrough &= this->conv.is_passthrough;
|
||||
|
||||
spa_log_debug(this->log, NAME " %p: got converter features %08x:%08x passthrough:%d", this,
|
||||
this->cpu_flags, this->conv.cpu_flags, this->is_passthrough);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -849,7 +886,7 @@ static int impl_node_process(void *object)
|
|||
struct buffer *sbuf, *dbuf;
|
||||
uint32_t n_src_datas, n_dst_datas;
|
||||
const void **src_datas;
|
||||
void **dst_datas;
|
||||
void **dst_datas, **tmp;
|
||||
int res = 0;
|
||||
|
||||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
||||
|
@ -883,6 +920,7 @@ static int impl_node_process(void *object)
|
|||
n_samples = maxsize / inport->stride;
|
||||
|
||||
dst_datas = alloca(sizeof(void*) * MAX_PORTS);
|
||||
tmp = alloca(sizeof(void*) * MAX_PORTS);
|
||||
|
||||
n_dst_datas = 0;
|
||||
for (i = 0; i < this->port_count; i++) {
|
||||
|
@ -919,10 +957,10 @@ static int impl_node_process(void *object)
|
|||
n_samples = SPA_MIN(n_samples, maxsize / outport->stride);
|
||||
|
||||
for (j = 0; j < dbuf->buf->n_datas; j++) {
|
||||
dst_datas[n_dst_datas] = this->is_passthrough ?
|
||||
tmp[n_dst_datas] = this->is_passthrough ?
|
||||
(void *)src_datas[n_dst_datas] :
|
||||
dbuf->datas[j];
|
||||
dd[j].data = dst_datas[n_dst_datas++];
|
||||
dd[j].data = tmp[n_dst_datas++];
|
||||
dd[j].chunk->offset = 0;
|
||||
dd[j].chunk->size = n_samples * outport->stride;
|
||||
}
|
||||
|
@ -933,8 +971,10 @@ static int impl_node_process(void *object)
|
|||
}
|
||||
while (n_dst_datas < this->port_count) {
|
||||
spa_log_trace_fp(this->log, NAME" %p: %d fill output", this, n_dst_datas);
|
||||
dst_datas[n_dst_datas++] = SPA_PTR_ALIGN(this->empty, MAX_ALIGN, void);
|
||||
tmp[n_dst_datas++] = SPA_PTR_ALIGN(this->empty, MAX_ALIGN, void);
|
||||
}
|
||||
for (i = 0; i < this->port_count; i++)
|
||||
dst_datas[i] = tmp[this->remap[i]];
|
||||
|
||||
spa_log_trace_fp(this->log, NAME " %p: n_src:%d n_dst:%d n_samples:%d max:%d stride:%d p:%d", this,
|
||||
n_src_datas, n_dst_datas, n_samples, maxsize, inport->stride,
|
||||
|
|
Loading…
Reference in a new issue