tests: add audioconvert test

Add a little test framework for writing end-to-end audioconvert tests.
This commit is contained in:
Wim Taymans 2022-07-06 19:19:12 +02:00
parent df2c5d6a14
commit 4036b8a3f0

View file

@ -36,6 +36,7 @@
#include <spa/param/audio/format.h>
#include <spa/param/audio/format-utils.h>
#include <spa/node/node.h>
#include <spa/node/io.h>
#include <spa/debug/mem.h>
#include <spa/support/log-impl.h>
@ -71,6 +72,7 @@ static int setup_context(struct context *ctx)
size_t size;
int res;
struct spa_support support[1];
struct spa_dict_item items[2];
const struct spa_handle_factory *factory;
void *iface;
@ -86,9 +88,11 @@ static int setup_context(struct context *ctx)
ctx->convert_handle = calloc(1, size);
spa_assert_se(ctx->convert_handle != NULL);
items[0] = SPA_DICT_ITEM_INIT("clock.quantum-limit", "8192");
res = spa_handle_factory_init(factory,
ctx->convert_handle,
NULL,
&SPA_DICT_INIT(items, 1),
support, 1);
spa_assert_se(res >= 0);
@ -510,6 +514,230 @@ static int test_set_in_format2(struct context *ctx)
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[])
{
struct context ctx;
@ -531,6 +759,8 @@ int main(int argc, char *argv[])
test_set_in_format2(&ctx);
test_set_out_format(&ctx);
test_convert_dsp_pass(&ctx);
clean_context(&ctx);
return 0;