mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-15 08:56:38 -05:00
tests: add audioconvert test
Add a little test framework for writing end-to-end audioconvert tests.
This commit is contained in:
parent
df2c5d6a14
commit
4036b8a3f0
1 changed files with 231 additions and 1 deletions
|
|
@ -36,6 +36,7 @@
|
||||||
#include <spa/param/audio/format.h>
|
#include <spa/param/audio/format.h>
|
||||||
#include <spa/param/audio/format-utils.h>
|
#include <spa/param/audio/format-utils.h>
|
||||||
#include <spa/node/node.h>
|
#include <spa/node/node.h>
|
||||||
|
#include <spa/node/io.h>
|
||||||
#include <spa/debug/mem.h>
|
#include <spa/debug/mem.h>
|
||||||
#include <spa/support/log-impl.h>
|
#include <spa/support/log-impl.h>
|
||||||
|
|
||||||
|
|
@ -71,6 +72,7 @@ static int setup_context(struct context *ctx)
|
||||||
size_t size;
|
size_t size;
|
||||||
int res;
|
int res;
|
||||||
struct spa_support support[1];
|
struct spa_support support[1];
|
||||||
|
struct spa_dict_item items[2];
|
||||||
const struct spa_handle_factory *factory;
|
const struct spa_handle_factory *factory;
|
||||||
void *iface;
|
void *iface;
|
||||||
|
|
||||||
|
|
@ -86,9 +88,11 @@ static int setup_context(struct context *ctx)
|
||||||
ctx->convert_handle = calloc(1, size);
|
ctx->convert_handle = calloc(1, size);
|
||||||
spa_assert_se(ctx->convert_handle != NULL);
|
spa_assert_se(ctx->convert_handle != NULL);
|
||||||
|
|
||||||
|
items[0] = SPA_DICT_ITEM_INIT("clock.quantum-limit", "8192");
|
||||||
|
|
||||||
res = spa_handle_factory_init(factory,
|
res = spa_handle_factory_init(factory,
|
||||||
ctx->convert_handle,
|
ctx->convert_handle,
|
||||||
NULL,
|
&SPA_DICT_INIT(items, 1),
|
||||||
support, 1);
|
support, 1);
|
||||||
spa_assert_se(res >= 0);
|
spa_assert_se(res >= 0);
|
||||||
|
|
||||||
|
|
@ -510,6 +514,230 @@ static int test_set_in_format2(struct context *ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int setup_direction(struct context *ctx, enum spa_direction direction, uint32_t mode,
|
||||||
|
struct spa_audio_info_raw *info)
|
||||||
|
{
|
||||||
|
struct spa_pod_builder b = { 0 };
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
struct spa_pod *param, *format;
|
||||||
|
int res;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||||
|
format = spa_format_audio_raw_build(&b, SPA_PARAM_Format, info);
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case SPA_PARAM_PORT_CONFIG_MODE_dsp:
|
||||||
|
param = spa_pod_builder_add_object(&b,
|
||||||
|
SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig,
|
||||||
|
SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(direction),
|
||||||
|
SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(mode),
|
||||||
|
SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(format));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPA_PARAM_PORT_CONFIG_MODE_convert:
|
||||||
|
param = spa_pod_builder_add_object(&b,
|
||||||
|
SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig,
|
||||||
|
SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(direction),
|
||||||
|
SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(mode));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
res = spa_node_set_param(ctx->convert_node, SPA_PARAM_PortConfig, 0, param);
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case SPA_PARAM_PORT_CONFIG_MODE_convert:
|
||||||
|
res = spa_node_port_set_param(ctx->convert_node, direction, 0,
|
||||||
|
SPA_PARAM_Format, 0, format);
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
break;
|
||||||
|
case SPA_PARAM_PORT_CONFIG_MODE_dsp:
|
||||||
|
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||||
|
format = spa_format_audio_dsp_build(&b, SPA_PARAM_Format,
|
||||||
|
&SPA_AUDIO_INFO_DSP_INIT(
|
||||||
|
.format = SPA_AUDIO_FORMAT_F32P));
|
||||||
|
for (i = 0; i < info->channels; i++) {
|
||||||
|
res = spa_node_port_set_param(ctx->convert_node, direction, i,
|
||||||
|
SPA_PARAM_Format, 0, format);
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct buffer {
|
||||||
|
struct spa_buffer buffer;
|
||||||
|
struct spa_data datas[MAX_PORTS];
|
||||||
|
struct spa_chunk chunks[MAX_PORTS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct data {
|
||||||
|
uint32_t mode;
|
||||||
|
struct spa_audio_info_raw info;
|
||||||
|
uint32_t ports;
|
||||||
|
uint32_t planes;
|
||||||
|
const void *data[MAX_PORTS];
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int run_convert(struct context *ctx, struct data *in_data,
|
||||||
|
struct data *out_data)
|
||||||
|
{
|
||||||
|
struct spa_command cmd;
|
||||||
|
int res;
|
||||||
|
uint32_t i, j;
|
||||||
|
struct buffer in_buffers[in_data->ports];
|
||||||
|
struct buffer out_buffers[out_data->ports];
|
||||||
|
struct spa_io_buffers in_io[in_data->ports];
|
||||||
|
struct spa_io_buffers out_io[out_data->ports];
|
||||||
|
|
||||||
|
setup_direction(ctx, SPA_DIRECTION_INPUT, in_data->mode, &in_data->info);
|
||||||
|
setup_direction(ctx, SPA_DIRECTION_OUTPUT, out_data->mode, &out_data->info);
|
||||||
|
|
||||||
|
cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Start);
|
||||||
|
res = spa_node_send_command(ctx->convert_node, &cmd);
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
|
||||||
|
for (i = 0; i < in_data->ports; i++) {
|
||||||
|
struct buffer *b = &in_buffers[i];
|
||||||
|
struct spa_buffer *buffers[1];
|
||||||
|
spa_zero(*b);
|
||||||
|
b->buffer.datas = b->datas;
|
||||||
|
b->buffer.n_datas = in_data->planes;
|
||||||
|
|
||||||
|
for (j = 0; j < in_data->planes; j++) {
|
||||||
|
b->datas[j].type = SPA_DATA_MemPtr;
|
||||||
|
b->datas[j].flags = 0;
|
||||||
|
b->datas[j].fd = -1;
|
||||||
|
b->datas[j].mapoffset = 0;
|
||||||
|
b->datas[j].maxsize = in_data->size;
|
||||||
|
b->datas[j].data = (void *)in_data->data[i];
|
||||||
|
b->datas[j].chunk = &b->chunks[j];
|
||||||
|
b->datas[j].chunk->offset = 0;
|
||||||
|
b->datas[j].chunk->size = in_data->size;
|
||||||
|
b->datas[j].chunk->stride = 0;
|
||||||
|
}
|
||||||
|
buffers[0] = &b->buffer;
|
||||||
|
res = spa_node_port_use_buffers(ctx->convert_node, SPA_DIRECTION_INPUT, i,
|
||||||
|
0, buffers, 1);
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
|
||||||
|
in_io[i].status = SPA_STATUS_HAVE_DATA;
|
||||||
|
in_io[i].buffer_id = 0;
|
||||||
|
|
||||||
|
res = spa_node_port_set_io(ctx->convert_node, SPA_DIRECTION_INPUT, i,
|
||||||
|
SPA_IO_Buffers, &in_io[i], sizeof(in_io[i]));
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
}
|
||||||
|
for (i = 0; i < out_data->ports; i++) {
|
||||||
|
struct buffer *b = &out_buffers[i];
|
||||||
|
struct spa_buffer *buffers[1];
|
||||||
|
spa_zero(*b);
|
||||||
|
b->buffer.datas = b->datas;
|
||||||
|
b->buffer.n_datas = out_data->planes;
|
||||||
|
|
||||||
|
for (j = 0; j < out_data->planes; j++) {
|
||||||
|
b->datas[j].type = SPA_DATA_MemPtr;
|
||||||
|
b->datas[j].flags = 0;
|
||||||
|
b->datas[j].fd = -1;
|
||||||
|
b->datas[j].mapoffset = 0;
|
||||||
|
b->datas[j].maxsize = out_data->size;
|
||||||
|
b->datas[j].data = calloc(1, out_data->size);
|
||||||
|
b->datas[j].chunk = &b->chunks[j];
|
||||||
|
b->datas[j].chunk->offset = 0;
|
||||||
|
b->datas[j].chunk->size = 0;
|
||||||
|
b->datas[j].chunk->stride = 0;
|
||||||
|
}
|
||||||
|
buffers[0] = &b->buffer;
|
||||||
|
res = spa_node_port_use_buffers(ctx->convert_node,
|
||||||
|
SPA_DIRECTION_OUTPUT, i, 0, buffers, 1);
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
|
||||||
|
out_io[i].status = SPA_STATUS_NEED_DATA;
|
||||||
|
out_io[i].buffer_id = -1;
|
||||||
|
|
||||||
|
res = spa_node_port_set_io(ctx->convert_node, SPA_DIRECTION_OUTPUT, i,
|
||||||
|
SPA_IO_Buffers, &out_io[i], sizeof(out_io[i]));
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = spa_node_process(ctx->convert_node);
|
||||||
|
spa_assert_se(res == (SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA));
|
||||||
|
|
||||||
|
for (i = 0; i < out_data->ports; i++) {
|
||||||
|
struct buffer *b = &out_buffers[i];
|
||||||
|
|
||||||
|
spa_assert_se(out_io[i].status == SPA_STATUS_HAVE_DATA);
|
||||||
|
spa_assert_se(out_io[i].buffer_id == 0);
|
||||||
|
|
||||||
|
for (j = 0; j < out_data->planes; j++) {
|
||||||
|
spa_assert_se(b->datas[j].chunk->offset == 0);
|
||||||
|
spa_assert_se(b->datas[j].chunk->size == out_data->size);
|
||||||
|
|
||||||
|
res = memcmp(b->datas[j].data, out_data->data[j], out_data->size);
|
||||||
|
if (res != 0) {
|
||||||
|
spa_debug_mem(0, b->datas[j].data, out_data->size);
|
||||||
|
spa_debug_mem(0, out_data->data[j], out_data->size);
|
||||||
|
}
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
|
||||||
|
free(b->datas[j].data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Suspend);
|
||||||
|
res = spa_node_send_command(ctx->convert_node, &cmd);
|
||||||
|
spa_assert_se(res == 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_convert_dsp_pass(struct context *ctx)
|
||||||
|
{
|
||||||
|
static const float fl_data[] = { 0.1f, 0.0f, 0.0f };
|
||||||
|
static const float fr_data[] = { 0.0f, 0.2f, 0.0f };
|
||||||
|
static const float lfe_data[] = { 0.0f, 0.0f, 0.3f };
|
||||||
|
static const float out_data[] = { 0.1f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f, 0.3f };
|
||||||
|
|
||||||
|
run_convert(ctx, &(struct data) {
|
||||||
|
.mode = SPA_PARAM_PORT_CONFIG_MODE_dsp,
|
||||||
|
.info = SPA_AUDIO_INFO_RAW_INIT(
|
||||||
|
.format = SPA_AUDIO_FORMAT_F32,
|
||||||
|
.rate = 48000,
|
||||||
|
.channels = 3,
|
||||||
|
.position = {
|
||||||
|
SPA_AUDIO_CHANNEL_FL,
|
||||||
|
SPA_AUDIO_CHANNEL_FR,
|
||||||
|
SPA_AUDIO_CHANNEL_LFE,
|
||||||
|
}),
|
||||||
|
.ports = 3,
|
||||||
|
.planes = 1,
|
||||||
|
.data = { fl_data, fr_data, lfe_data },
|
||||||
|
.size = sizeof(fl_data)
|
||||||
|
},
|
||||||
|
&(struct data) {
|
||||||
|
.mode = SPA_PARAM_PORT_CONFIG_MODE_convert,
|
||||||
|
.info = SPA_AUDIO_INFO_RAW_INIT(
|
||||||
|
.format = SPA_AUDIO_FORMAT_F32,
|
||||||
|
.rate = 48000,
|
||||||
|
.channels = 3,
|
||||||
|
.position = {
|
||||||
|
SPA_AUDIO_CHANNEL_FL,
|
||||||
|
SPA_AUDIO_CHANNEL_FR,
|
||||||
|
SPA_AUDIO_CHANNEL_LFE,
|
||||||
|
}),
|
||||||
|
.ports = 1,
|
||||||
|
.planes = 1,
|
||||||
|
.data = { out_data },
|
||||||
|
.size = sizeof(out_data)
|
||||||
|
});
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct context ctx;
|
struct context ctx;
|
||||||
|
|
@ -531,6 +759,8 @@ int main(int argc, char *argv[])
|
||||||
test_set_in_format2(&ctx);
|
test_set_in_format2(&ctx);
|
||||||
test_set_out_format(&ctx);
|
test_set_out_format(&ctx);
|
||||||
|
|
||||||
|
test_convert_dsp_pass(&ctx);
|
||||||
|
|
||||||
clean_context(&ctx);
|
clean_context(&ctx);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue