Skip to content

Commit 8ab6f9a

Browse files
committed
Tools: Testbench: Add support for ALSA controls script
This patch adds to testbench command line option -s <controls file>. The controls file can contain comments with line starting with #, empty lines, amixer cset name= commands and sleep n commands. The script format is similar to what can be applied to a real SOF device running Linux OS. E.g. amixer -c0 cset name='Post Mixer Analog Playback DRC switch' off amixer -c0 cset name='Post Mixer Analog Playback Volume' 42,43 sleep 0.5 amixer -c0 cset name='Post Mixer Analog Playback Volume' 45 The result simulated waveform would show in the beginning left channel set to gain value 42, and right channel to gain value 43. After processing 0.5 seconds of audio both left and right channel gains are set to 45. The supported controls types are mixer, switch and enum. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent 88e7037 commit 8ab6f9a

7 files changed

Lines changed: 467 additions & 1 deletion

File tree

tools/testbench/include/testbench/topology_ipc4.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ int tb_new_process(struct testbench_prm *tp);
4848
int tb_pipelines_set_state(struct testbench_prm *tp, int state, int dir);
4949
int tb_send_bytes_data(struct tb_mq_desc *ipc_tx, struct tb_mq_desc *ipc_rx,
5050
uint32_t module_id, uint32_t instance_id, struct sof_abi_hdr *abi);
51+
int tb_send_volume_control(struct tb_mq_desc *ipc_tx, struct tb_mq_desc *ipc_rx,
52+
struct tb_ctl *ctl, int *control_values, int num_values);
53+
int tb_send_alsa_control(struct tb_mq_desc *ipc_tx, struct tb_mq_desc *ipc_rx, struct tb_ctl *ctl,
54+
int *control_values, int num_values, int param_id);
5155
int tb_set_reset_state(struct testbench_prm *tp);
5256
int tb_set_running_state(struct testbench_prm *tp);
5357
int tb_set_up_pipeline(struct testbench_prm *tp, struct tplg_pipeline_info *pipe_info);

tools/testbench/include/testbench/utils.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ struct file_comp_lookup {
4040
#define TB_MAX_VOLUME_SIZE 120
4141
#define TB_MAX_DATA_SIZE 512
4242
#define TB_MAX_CTLS 16
43+
#define TB_MAX_CMD_CHARS 256
44+
#define TB_MAX_CTL_NAME_SIZE 128
45+
#define TB_MAX_CHANNELS 8
4346

4447
struct tb_mq_desc {
4548
char queue_name[TB_NAME_SIZE];
@@ -57,12 +60,14 @@ struct tb_config {
5760
};
5861

5962
struct tb_ctl {
63+
struct tplg_comp_info *comp_info;
6064
unsigned int module_id;
6165
unsigned int instance_id;
6266
unsigned int type;
6367
unsigned int volume_table[TB_MAX_VOLUME_SIZE];
6468
unsigned int index;
6569
char data[TB_MAX_DATA_SIZE];
70+
char name[TB_MAX_CTL_NAME_SIZE];
6671
union {
6772
struct snd_soc_tplg_mixer_control mixer_ctl;
6873
struct snd_soc_tplg_enum_control enum_ctl;
@@ -93,6 +98,7 @@ struct testbench_prm {
9398
char *output_file[TB_MAX_OUTPUT_FILE_NUM]; /* output file names */
9499
char *tplg_file; /* topology file to use */
95100
char *bits_in; /* input bit format */
101+
char *control_file;
96102
int input_file_num; /* number of input files */
97103
int output_file_num; /* number of output files */
98104
int pipeline_num;
@@ -123,6 +129,8 @@ struct testbench_prm {
123129
/* topology */
124130
struct tplg_context tplg;
125131

132+
FILE *control_fh;
133+
126134
#if CONFIG_IPC_MAJOR_4
127135
struct list_item widget_list;
128136
struct list_item route_list;
@@ -150,6 +158,7 @@ int tb_pipeline_params(struct testbench_prm *tp, struct ipc *ipc, struct pipelin
150158
int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p);
151159
int tb_pipeline_start(struct ipc *ipc, struct pipeline *p);
152160
int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p);
161+
int tb_read_controls(struct testbench_prm *tp, int64_t *sleep_ns);
153162
int tb_set_reset_state(struct testbench_prm *tp);
154163
int tb_set_running_state(struct testbench_prm *tp);
155164
int tb_set_up_all_pipelines(struct testbench_prm *tp);
@@ -162,5 +171,9 @@ void tb_free_topology(struct testbench_prm *tp);
162171
void tb_getcycles(uint64_t *cycles);
163172
void tb_gettime(struct timespec *td);
164173
void tb_show_file_stats(struct testbench_prm *tp, int pipeline_id);
174+
struct tb_ctl *tb_find_control_by_name(struct testbench_prm *tp, char *name);
175+
176+
int tb_parse_amixer(struct testbench_prm *tp, char *line);
177+
165178

166179
#endif /* _TESTBENCH_UTILS_H */

tools/testbench/testbench.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ static void print_usage(char *executable)
129129
printf(" -p <pipeline1,pipeline2,...>\n");
130130
printf(" -C <number of copy() iterations>\n");
131131
printf(" -P <number of dynamic pipeline iterations>\n");
132+
printf(" -s <script file to set controls, with amixer and sleep commands>\n\n");
132133
printf("Options for input and output format override:\n");
133134
printf(" -b <input_format>, S16_LE, S24_LE, or S32_LE\n");
134135
printf(" -c <input channels>\n");
@@ -146,7 +147,7 @@ static int parse_input_args(int argc, char **argv, struct testbench_prm *tp)
146147
int option = 0;
147148
int ret = 0;
148149

149-
while ((option = getopt(argc, argv, "hd:i:o:t:b:r:R:c:n:C:P:p:")) != -1) {
150+
while ((option = getopt(argc, argv, "hd:i:o:t:b:r:R:c:n:C:P:p:s:")) != -1) {
150151
switch (option) {
151152
/* input sample file */
152153
case 'i':
@@ -216,6 +217,11 @@ static int parse_input_args(int argc, char **argv, struct testbench_prm *tp)
216217
ret = parse_pipelines(optarg, tp);
217218
break;
218219

220+
/* control script file name */
221+
case 's':
222+
tp->control_file = strdup(optarg);
223+
break;
224+
219225
/* print usage */
220226
case 'h':
221227
print_usage(argv[0]);
@@ -302,9 +308,13 @@ static void test_pipeline_stats(struct testbench_prm *tp, long long delta_t)
302308
*/
303309
static int pipline_test(struct testbench_prm *tp)
304310
{
311+
float samples_to_ns;
305312
int dp_count = 0;
306313
struct timespec td0, td1;
314+
struct file_state *out_stat;
307315
long long delta_t;
316+
int64_t next_control_ns;
317+
int64_t time_ns;
308318
int err;
309319

310320
/* build, run and teardown pipelines */
@@ -341,17 +351,46 @@ static int pipline_test(struct testbench_prm *tp)
341351
break;
342352
}
343353

354+
/* Use first file writer to create simulation time. Calculate coefficient
355+
* to calculate current time from file write samples count
356+
*/
357+
out_stat = tp->fw[0].state;
358+
samples_to_ns = 1.0e9 / ((float)out_stat->channels * out_stat->rate);
359+
360+
/* Apply initial controls time to call again controls handler */
361+
err = tb_read_controls(tp, &next_control_ns);
362+
if (err) {
363+
fprintf(stderr, "error: failed to read control commands.\n");
364+
goto out;
365+
}
366+
344367
tb_gettime(&td0);
345368

346369
while (true) {
347370
if (tb_schedule_pipeline_check_state(tp))
348371
break;
372+
373+
if (next_control_ns) {
374+
time_ns = (int64_t)(samples_to_ns * out_stat->n);
375+
if (time_ns >= next_control_ns) {
376+
err = tb_read_controls(tp, &next_control_ns);
377+
if (err) {
378+
fprintf(stderr,
379+
"error: failed to read control commands.\n");
380+
goto out;
381+
}
382+
383+
if (next_control_ns)
384+
next_control_ns += time_ns;
385+
}
386+
}
349387
}
350388

351389
tb_schedule_pipeline_check_state(tp); /* Once more to flush out remaining data */
352390

353391
tb_gettime(&td1);
354392

393+
out:
355394
err = tb_set_reset_state(tp);
356395
if (err < 0) {
357396
fprintf(stderr, "error: pipeline reset %d failed %d\n",
@@ -445,6 +484,16 @@ int main(int argc, char **argv)
445484
goto out;
446485
}
447486

487+
if (tp->control_file) {
488+
tp->control_fh = fopen(tp->control_file, "r");
489+
if (!tp->control_fh) {
490+
fprintf(stderr, "error: opening script %s (%s).\n",
491+
tp->control_file, strerror(errno));
492+
ret = -errno;
493+
goto out;
494+
}
495+
}
496+
448497
/* build, run and teardown pipelines */
449498
pipline_test(tp);
450499

@@ -456,6 +505,10 @@ int main(int argc, char **argv)
456505
/* free all other data */
457506
free(tp->bits_in);
458507
free(tp->tplg_file);
508+
free(tp->control_file);
509+
if (tp->control_fh)
510+
fclose(tp->control_fh);
511+
459512
for (i = 0; i < tp->output_file_num; i++)
460513
free(tp->output_file[i]);
461514

tools/testbench/topology_ipc4.c

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,8 @@ static int tb_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl,
13161316
ctl->type = tplg_ctl->type;
13171317
tlv = &tplg_ctl->tlv;
13181318
scale = &tlv->scale;
1319+
ctl->comp_info = comp_info;
1320+
strncpy(ctl->name, tplg_ctl->name, TB_MAX_CTL_NAME_SIZE);
13191321

13201322
/* populate the volume table */
13211323
for (i = 0; i < tplg_mixer->max + 1 ; i++) {
@@ -1338,6 +1340,8 @@ static int tb_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl,
13381340
ctl->enum_ctl = *tplg_enum;
13391341
ctl->index = index;
13401342
ctl->type = tplg_ctl->type;
1343+
ctl->comp_info = comp_info;
1344+
strncpy(ctl->name, tplg_ctl->name, TB_MAX_CTL_NAME_SIZE);
13411345
break;
13421346
case SND_SOC_TPLG_CTL_BYTES:
13431347
{
@@ -1350,6 +1354,8 @@ static int tb_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl,
13501354
ctl->index = index;
13511355
ctl->type = tplg_ctl->type;
13521356
memcpy(ctl->data, tplg_bytes->priv.data, tplg_bytes->priv.size);
1357+
ctl->comp_info = comp_info;
1358+
strncpy(ctl->name, tplg_ctl->name, TB_MAX_CTL_NAME_SIZE);
13531359
break;
13541360
}
13551361
default:
@@ -1735,4 +1741,146 @@ int tb_send_bytes_data(struct tb_mq_desc *ipc_tx, struct tb_mq_desc *ipc_rx,
17351741
return ret;
17361742
}
17371743

1744+
int tb_send_volume_control(struct tb_mq_desc *ipc_tx, struct tb_mq_desc *ipc_rx,
1745+
struct tb_ctl *ctl, int *control_values, int num_values)
1746+
{
1747+
struct ipc4_module_large_config config = {{ 0 }};
1748+
struct ipc4_peak_volume_config volume;
1749+
struct ipc4_message_reply reply;
1750+
struct ipc4_peak_volume_config *volume_config;
1751+
void *msg;
1752+
bool all_channels_equal = true;
1753+
int max_val;
1754+
int size;
1755+
int ret;
1756+
int val;
1757+
int i;
1758+
1759+
volume_config = ctl->comp_info->ipc_payload + sizeof(struct ipc4_base_module_cfg);
1760+
max_val = ctl->mixer_ctl.max;
1761+
val = control_values[0];
1762+
for (i = 0; i < num_values; i++) {
1763+
if (i > 0 && control_values[i] != val)
1764+
all_channels_equal = false;
1765+
1766+
if (control_values[i] < 0 || control_values[i] > max_val) {
1767+
fprintf(stderr, "error: control value %d is illegal.\n", control_values[i]);
1768+
return -EINVAL;
1769+
}
1770+
}
1771+
1772+
for (i = 0; i < num_values; i++) {
1773+
if (all_channels_equal) {
1774+
volume.channel_id = IPC4_ALL_CHANNELS_MASK;
1775+
volume.target_volume = ctl->volume_table[val];
1776+
} else {
1777+
volume.channel_id = i;
1778+
volume.target_volume = ctl->volume_table[control_values[i]];
1779+
}
1780+
1781+
/* TODO: get curve duration and type from topology */
1782+
volume.curve_type = volume_config->curve_type;
1783+
volume.curve_duration = volume_config->curve_duration;
1784+
1785+
/* configure the IPC message */
1786+
tb_ctl_ipc_message(&config, IPC4_VOLUME, sizeof(volume), ctl->module_id,
1787+
ctl->instance_id, SOF_IPC4_MOD_LARGE_CONFIG_SET);
1788+
config.extension.r.final_block = 1;
1789+
config.extension.r.init_block = 1;
1790+
1791+
size = sizeof(config) + sizeof(volume);
1792+
msg = calloc(size, 1);
1793+
if (!msg)
1794+
return -ENOMEM;
1795+
1796+
memcpy(msg, &config, sizeof(config));
1797+
memcpy(msg + sizeof(config), &volume, sizeof(volume));
1798+
1799+
/* send the message and check status */
1800+
ret = tb_mq_cmd_tx_rx(ipc_tx, ipc_rx, msg, size, &reply, sizeof(reply));
1801+
free(msg);
1802+
if (ret < 0) {
1803+
fprintf(stderr, "failed to set volume control %s\n", ctl->name);
1804+
return ret;
1805+
}
1806+
1807+
if (reply.primary.r.status != IPC4_SUCCESS) {
1808+
fprintf(stderr, "volume control %s set failed with status %d\n",
1809+
ctl->name, reply.primary.r.status);
1810+
return -EINVAL;
1811+
}
1812+
1813+
if (all_channels_equal)
1814+
break;
1815+
}
1816+
1817+
return 0;
1818+
}
1819+
1820+
int tb_send_alsa_control(struct tb_mq_desc *ipc_tx, struct tb_mq_desc *ipc_rx,
1821+
struct tb_ctl *ctl, int *control_values, int num_values, int param_id)
1822+
{
1823+
struct ipc4_module_large_config config = {{ 0 }};
1824+
struct sof_ipc4_control_msg_payload *data;
1825+
struct ipc4_message_reply reply;
1826+
void *msg;
1827+
int data_size;
1828+
int msg_size;
1829+
int ret;
1830+
int i;
1831+
1832+
/* size of control data */
1833+
data_size = num_values * sizeof(struct sof_ipc4_ctrl_value_chan) + sizeof(*data);
1834+
1835+
/* allocate memory for control data */
1836+
data = calloc(data_size, 1);
1837+
if (!data)
1838+
return -ENOMEM;
1839+
1840+
/* set param ID and number of channels */
1841+
data->id = ctl->index;
1842+
data->num_elems = num_values;
1843+
1844+
/* set the enum values */
1845+
for (i = 0; i < data->num_elems; i++) {
1846+
data->chanv[i].channel = i;
1847+
data->chanv[i].value = control_values[i];
1848+
}
1849+
1850+
/* configure the IPC message */
1851+
tb_ctl_ipc_message(&config, param_id, data_size, ctl->module_id, ctl->instance_id,
1852+
SOF_IPC4_MOD_LARGE_CONFIG_SET);
1853+
config.extension.r.final_block = 1;
1854+
config.extension.r.init_block = 1;
1855+
1856+
/* allocate memory for IPC message */
1857+
msg_size = sizeof(config) + data_size;
1858+
msg = calloc(msg_size, 1);
1859+
if (!msg) {
1860+
free(data);
1861+
return -ENOMEM;
1862+
}
1863+
1864+
/* set the IPC message data */
1865+
memcpy(msg, &config, sizeof(config));
1866+
memcpy(msg + sizeof(config), data, data_size);
1867+
free(data);
1868+
1869+
/* send the message and check status */
1870+
ret = tb_mq_cmd_tx_rx(ipc_tx, ipc_rx, msg, msg_size, &reply, sizeof(reply));
1871+
free(msg);
1872+
if (ret < 0) {
1873+
fprintf(stderr, "error: failed to set control %s\n", ctl->name);
1874+
return ret;
1875+
}
1876+
1877+
if (reply.primary.r.status != IPC4_SUCCESS) {
1878+
fprintf(stderr, "error: control %s set failed with status %d\n",
1879+
ctl->name, reply.primary.r.status);
1880+
return -EINVAL;
1881+
}
1882+
1883+
return 0;
1884+
}
1885+
17381886
#endif /* CONFIG_IPC_MAJOR_4 */

0 commit comments

Comments
 (0)