mcu_hi3321_watch/application/audio/sample_ai/sample_ai.c
2025-05-26 20:15:20 +08:00

1040 lines
26 KiB
C

/*
* Copyright (c) CompanyNameMagicTag 2019-2020. All rights reserved.
* Description: ai sample
* Author: audio
* Create: 2019-09-17
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "thread_os.h"
#include "chip_type.h"
#include "audio_type.h"
#include "osal_list.h"
#include "soc_uapi_adp.h"
#include "soc_uapi_ai.h"
#include "sample_audio_api.h"
#include "sample_audio_utils.h"
#include "sample_phone.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
#define MIC0_TYPE "mic0"
#define MIC1_TYPE "mic1"
typedef enum {
CHANNEL_L = 0x1,
CHANNEL_R = 0x2,
} channel_type;
typedef struct {
td_char *sample;
td_char *size;
td_char *file;
} sample_ai_arg;
typedef struct sample_ai_inst {
athread_handle task;
td_bool task_enable;
td_bool mute;
FILE *h_file;
td_u32 file_size;
td_u32 file_limit_size;
td_handle h_adp;
td_handle h_ai;
td_handle h_player;
td_u32 channel_type;
uapi_ai_port ai_port;
uapi_audio_pcm_format pcm_format;
uapi_ai_gain volume;
/* ao */
uapi_snd snd_id;
uapi_snd_out_port ao_port;
td_u32 (*save_frame)(struct sample_ai_inst *inst, const uapi_audio_frame *frame);
data_info write_data;
td_bool run_bt_loopback;
td_void (*sample_ai_process)(struct sample_ai_inst *inst, const uapi_audio_frame *frame);
struct osal_list_head node;
} sample_ai_inst;
static OSAL_LIST_HEAD(g_sample_ai_list);
static inline td_bool ai_check_volume_adjust_support(uapi_ai_port ai_port)
{
if ((ai_port >= UAPI_AI_PORT_ADC0 && ai_port <= UAPI_AI_PORT_ADC3) ||
(ai_port >= UAPI_AI_PORT_LPADC0 && ai_port <= UAPI_AI_PORT_LPADC1) ||
(ai_port >= UAPI_AI_PORT_PDM0 && ai_port <= UAPI_AI_PORT_PDM3)) {
return TD_TRUE;
}
return TD_FALSE;
}
static sample_ai_inst *sample_ai_alloc_inst(td_void)
{
sample_ai_inst *inst = (sample_ai_inst *)malloc(sizeof(sample_ai_inst));
if (inst == TD_NULL) {
return TD_NULL;
}
(td_void)memset_s(inst, sizeof(*inst), 0, sizeof(sample_ai_inst));
osal_list_add_tail(&inst->node, &g_sample_ai_list);
return inst;
}
static td_void sample_ai_free_inst(sample_ai_inst *inst)
{
if (inst == TD_NULL) {
return;
}
osal_list_del(&inst->node);
free(inst);
}
static sample_ai_inst *sample_ai_get_inst(td_void)
{
if (osal_list_empty(&g_sample_ai_list) != TD_FALSE) {
return TD_NULL;
}
return osal_list_first_entry(&g_sample_ai_list, sample_ai_inst, node);
}
static td_bool run_bt_loopback = TD_FALSE;
static td_s32 ai_event_proc(td_handle ai, uapi_ai_event_type event, const td_void *param, td_void *context)
{
sample_ai_inst *test_inst = (sample_ai_inst *)context;
sap_err_log_h32(event);
if (event == UAPI_AI_EVENT_VAD_VALID) {
} else {
}
sap_unused(ai);
sap_unused(param);
sap_unused(test_inst);
return EXT_SUCCESS;
}
static uapi_ai_port sample_ai_get_test_port(td_void)
{
#if (SAP_CHIP_TYPE == brandy)
return UAPI_AI_PORT_ADC0;
#elif (SAP_CHIP_TYPE == socmn1)
return UAPI_AI_PORT_LPADC0;
#else
return UAPI_AI_PORT_I2S0;
#endif /* SAP_CHIP_TYPE */
}
static td_u32 save_frame_to_buf(sample_ai_inst *inst, const uapi_audio_frame *frame)
{
circ_buf *cb = &inst->write_data.cb;
if (circ_buf_query_free(cb) <= frame->bits_bytes) {
sap_printf("Warning: PCM data enough");
return 0;
}
return circ_buf_write(cb, (const td_u8 *)frame->bits_buffer, frame->bits_bytes);
}
static td_u32 save_frame_to_file(sample_ai_inst *inst, const uapi_audio_frame *frame)
{
td_u32 ret;
if (inst->h_file == TD_NULL) {
return 0;
}
ret = fwrite((td_void *)frame->bits_buffer, 1, frame->bits_bytes, inst->h_file);
return ret;
}
static td_void sample_ai_save_frame(sample_ai_inst *inst, const uapi_audio_frame *frame)
{
inst->file_size += frame->bits_bytes;
if (inst->file_size >= inst->file_limit_size) {
return;
}
if (inst->save_frame(inst, frame) == 0) {
inst->file_size = inst->file_limit_size;
}
}
static td_void sample_ai_ao_parse_mic(const td_char *mic_name, sample_ai_inst *inst)
{
if (strcmp(MIC0_TYPE, mic_name) == 0) {
inst->channel_type |= CHANNEL_L;
} else if (strcmp(MIC1_TYPE, mic_name) == 0) {
inst->channel_type |= CHANNEL_R;
}
}
#if (SAP_CHIP_TYPE == socmn1)
static td_void smaple_ai_ao_channel_sel(td_void *pcm, td_u32 samples, td_u32 channel_type)
{
td_u32 i;
td_s16 *in = TD_NULL;
if (pcm == NULL) {
return;
}
in = (td_s16 *)pcm;
for (i = 0; i < samples; i++) {
if (channel_type == CHANNEL_L) {
*in = 0;
} else if (channel_type == CHANNEL_R) {
*(in + 1) = 0;
}
in += 2; /* 2 is num */
}
}
#endif
static td_void sample_ai_ao_process(sample_ai_inst *inst, const uapi_audio_frame *frame)
{
td_s32 ret;
#if (SAP_CHIP_TYPE == socmn1)
if (inst->channel_type == CHANNEL_L || inst->channel_type == CHANNEL_R) {
smaple_ai_ao_channel_sel((td_void *)frame->bits_buffer, frame->pcm_samples, inst->channel_type);
}
#endif
while (inst->task_enable) {
ret = uapi_adp_send_frame(inst->h_player, frame);
if (ret != EXT_SUCCESS) {
sap_msleep(THREAD_SLEEP_10MS); /* */
continue;
}
break;
}
}
static td_void sample_ai_thread(td_void *args)
{
td_s32 ret;
uapi_audio_frame frame;
sample_ai_inst *inst = (sample_ai_inst *)args;
if (inst == TD_NULL) {
sap_err_log_info("ai_inst is null");
return;
}
while (inst->task_enable) {
ret = uapi_adp_acquire_frame(inst->h_adp, &frame);
if (ret != EXT_SUCCESS) {
sap_msleep(THREAD_SLEEP_10MS);
continue;
}
if (inst->sample_ai_process != NULL) {
inst->sample_ai_process(inst, &frame);
}
ret = uapi_adp_release_frame(inst->h_adp, &frame);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_adp_release_frame, ret);
}
}
athread_set_exit(inst->task, TD_TRUE);
}
static td_s32 sample_ai_sys_init(td_void)
{
td_s32 ret;
ret = uapi_adp_init();
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_adp_init, ret);
return ret;
}
ret = uapi_ai_init();
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_init, ret);
goto out;
}
return EXT_SUCCESS;
out:
(td_void)uapi_adp_deinit();
return ret;
}
static td_void sample_ai_sys_deinit(td_void)
{
td_s32 ret;
ret = uapi_ai_deinit();
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_deinit, ret);
}
ret = uapi_adp_deinit();
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_adp_deinit, ret);
}
}
static td_void sample_ai_close_adp(sample_ai_inst *inst)
{
if (inst->h_adp != 0) {
uapi_adp_destroy(inst->h_adp);
inst->h_adp = 0;
}
}
static td_s32 sample_ai_open_adp(sample_ai_inst *inst)
{
td_s32 ret;
uapi_adp_attr adp_attr;
inst->h_adp = 0;
ret = uapi_adp_get_def_attr(&adp_attr);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_adp_get_def_attr, ret);
return ret;
}
ret = uapi_adp_create(&inst->h_adp, &adp_attr);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_adp_create, ret);
return ret;
}
return EXT_SUCCESS;
}
static td_void sample_ai_close_ai(sample_ai_inst *inst)
{
td_s32 ret;
if (inst->h_ai == 0) {
return;
}
/* Detach ADP instance from AI instance */
ret = uapi_ai_detach_output(inst->h_ai, inst->h_adp);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_detach_output, ret);
}
ret = uapi_ai_close(inst->h_ai);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_close, ret);
}
inst->h_ai = 0;
}
static td_void sample_ai_set_port_attr(const sample_ai_inst *inst, uapi_ai_attr *ai_attr)
{
td_s32 ret;
ret = memcpy_s(&ai_attr->pcm_attr, sizeof(ai_attr->pcm_attr), &inst->pcm_format, sizeof(inst->pcm_format));
if (ret != EOK) {
return;
}
ai_attr->ref_attr.enable = TD_FALSE;
switch (inst->ai_port) {
case UAPI_AI_PORT_PDM0:
ai_attr->port_attr.pdm.rx_type = UAPI_AI_RX_DEFAULT;
ai_attr->port_attr.pdm.i2s_attr.channels = inst->pcm_format.channels;
ai_attr->port_attr.pdm.i2s_attr.bit_depth = inst->pcm_format.bit_depth;
break;
case UAPI_AI_PORT_ADC0:
case UAPI_AI_PORT_LPADC0:
case UAPI_AI_PORT_LPADC1:
ai_attr->port_attr.adc.rx_type = UAPI_AI_RX_DEFAULT;
break;
case UAPI_AI_PORT_I2S0:
case UAPI_AI_PORT_I2S1:
case UAPI_AI_PORT_I2S2:
get_i2s_attr(&ai_attr->port_attr.i2s.i2s_attr, (const uapi_audio_pcm_format *)&inst->pcm_format, TD_FALSE);
break;
default:
break;
}
}
static td_s32 sample_ai_open_ai(sample_ai_inst *inst)
{
td_s32 ret;
uapi_ai_attr ai_attr;
ret = uapi_ai_get_default_attr(inst->ai_port, &ai_attr);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_get_default_attr, ret);
return ret;
}
sample_ai_set_port_attr(inst, &ai_attr);
ret = uapi_ai_open(&inst->h_ai, inst->ai_port, &ai_attr);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_open, ret);
return ret;
}
/* Register AI event process callback */
ret = uapi_ai_register_event_proc(inst->h_ai, ai_event_proc, (td_void *)inst);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_register_event_proc, ret);
goto out;
}
if (ai_check_volume_adjust_support(inst->ai_port) == TD_TRUE) {
ret = uapi_ai_set_volume(inst->h_ai, &inst->volume);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_set_volume, ret);
goto out;
}
ret = uapi_ai_set_mute(inst->h_ai, inst->mute);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_set_mute, ret);
goto out;
}
}
/* Attach ADP instance to AI instance */
ret = uapi_ai_attach_output(inst->h_ai, inst->h_adp);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_attach_output, ret);
goto out;
}
return EXT_SUCCESS;
out:
(td_void)uapi_ai_close(inst->h_ai);
return ret;
}
static td_void sample_ai_close_file(sample_ai_inst *inst)
{
if (inst->h_file != TD_NULL) {
fclose(inst->h_file);
inst->h_file = TD_NULL;
}
}
static td_s32 sample_ai_open_file(sample_ai_inst *inst, const td_char *file)
{
if (file == TD_NULL || strcmp("null", file) == 0) {
inst->h_file = TD_NULL;
data_info_init(&inst->write_data, FPGA_SAVE_DATA_ADDR, FPGA_SAVE_DATA_SIZE);
inst->save_frame = save_frame_to_buf;
} else {
td_s32 ret;
td_char file_path[FILE_PATH_LEN];
ret = snprintf_s(file_path, FILE_PATH_LEN - 1, FILE_PATH_LEN - 1, "%s_%uk_%ubit_%uch.pcm", file,
inst->pcm_format.sample_rate / 1000, /* 1000 Hz to kHz */
inst->pcm_format.bit_depth, inst->pcm_format.channels);
if (ret < 0) {
sap_err_log_fun(snprintf_s, ret);
return EXT_FAILURE;
}
inst->h_file = fopen(file_path, "wb");
if (inst->h_file == TD_NULL) {
sap_printf("open %s failed", file_path);
return EXT_FAILURE;
}
inst->save_frame = save_frame_to_file;
}
return EXT_SUCCESS;
}
static td_void sample_ai_inst_deinit(sample_ai_inst *inst)
{
sample_ai_close_file(inst);
}
static td_s32 sample_ai_inst_init(const sample_ai_arg *arg, sample_ai_inst *inst)
{
td_s32 ret;
td_u32 size;
inst->ai_port = sample_ai_get_test_port();
size = (td_u32)strtoul(arg->size, TD_NULL, 0);
if (size == 0 || size > 100) { /* 100 max file size */
sap_err_log_ret(size);
return EXT_FAILURE;
}
inst->file_limit_size = (size << 20); /* 20: bytes to MB */
inst->pcm_format.channels = UAPI_AUDIO_CHANNEL_1;
inst->pcm_format.bit_depth = UAPI_AUDIO_BIT_DEPTH_16;
inst->pcm_format.sample_rate = UAPI_AUDIO_SAMPLE_RATE_16K;
inst->pcm_format.sample_per_frame = inst->pcm_format.sample_rate / 100; /* 100 --> 10ms */
inst->volume.integer = AI_VOLUME_DEFAULT;
inst->volume.decimal = 0;
ret = sample_ai_open_file(inst, arg->file);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ai_open_file, ret);
return ret;
}
return EXT_SUCCESS;
}
static td_void sample_ai_close_inst(sample_ai_inst *inst)
{
sample_ai_close_ai(inst);
sample_ai_close_adp(inst);
sample_ai_sys_deinit();
}
static td_s32 sample_ai_open_inst(sample_ai_inst *inst)
{
td_s32 ret;
ret = sample_ai_sys_init();
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ai_sys_init, ret);
return ret;
}
/*
* 数据流向 ai --> adp
* 按照数据流向反方向打开实例
* 按照数据方向attach output
*/
ret = sample_ai_open_adp(inst);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ai_open_adp, ret);
goto out0;
}
ret = sample_ai_open_ai(inst);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ai_open_ai, ret);
goto out1;
}
return EXT_SUCCESS;
out1:
sample_ai_close_adp(inst);
out0:
sample_ai_sys_deinit();
return ret;
}
static td_void sample_ai_stop_inst(sample_ai_inst *inst)
{
td_s32 ret;
if (inst->task != TD_NULL) {
/* Destory Task to stop ADP instance reading pcm data */
inst->task_enable = TD_FALSE;
athread_exit(inst->task);
inst->task = TD_NULL;
}
/* Stop AI instance output pcm data */
ret = uapi_ai_stop(inst->h_ai);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_stop, ret);
}
}
static td_s32 sample_ai_start_inst(sample_ai_inst *inst)
{
td_s32 ret;
athread_attr attr;
/* Start AI instance to write data into ADP instance */
ret = uapi_ai_start(inst->h_ai);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_start, ret);
return ret;
}
inst->sample_ai_process = sample_ai_save_frame;
inst->task_enable = TD_TRUE;
attr.priority = ATHREAD_PRIORITY_NORMAL;
attr.stack_size = 0x1000; /* 4k */
attr.name = "sample_ai";
ret = athread_create(&inst->task, sample_ai_thread, (td_void *)inst, &attr);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(athread_create, ret);
goto out;
}
return EXT_SUCCESS;
out:
(td_void)uapi_ai_stop(inst->h_ai);
return ret;
}
static td_void sample_ai_usage(const td_char *name)
{
sap_printf(" usage: %s size(MB) file(*.pcm)\n", name);
sap_printf(" examples:\n");
sap_printf(" %s 100 /mnt/ai.pcm\n", name);
sap_printf(" %s 100 null\n", name);
}
static td_s32 sample_ai_entry(td_s32 argc, td_char *argv[])
{
td_s32 ret;
sample_ai_arg *arg = (sample_ai_arg *)argv;
sample_ai_inst *inst = sample_ai_get_inst();
if (inst != TD_NULL) {
sap_printf("%s is already running\n", argv[0]);
return EXT_FAILURE;
}
inst = sample_ai_alloc_inst();
if (inst == TD_NULL) {
sap_printf("sample_ai_alloc_inst failed\n");
return EXT_FAILURE;
}
ret = sample_ai_inst_init(arg, inst);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ai_inst_init, ret);
goto out0;
}
ret = sample_ai_open_inst(inst);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ai_open_inst, ret);
goto out1;
}
ret = sample_ai_start_inst(inst);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ai_start_inst, ret);
goto out2;
}
sap_printf("sample_rate: %u, channels: %u, bit_depth: %u", inst->pcm_format.sample_rate,
inst->pcm_format.channels, inst->pcm_format.bit_depth);
return EXT_SUCCESS;
out2:
sample_ai_close_inst(inst);
out1:
sample_ai_inst_deinit(inst);
out0:
sample_ai_free_inst(inst);
audio_unused(argc);
return ret;
}
static td_s32 sample_ai_exit(td_void)
{
sample_ai_inst *inst = sample_ai_get_inst();
if (inst != NULL) {
sample_ai_stop_inst(inst);
sample_ai_close_inst(inst);
sample_ai_inst_deinit(inst);
sample_ai_free_inst(inst);
}
return EXT_SUCCESS;
}
td_s32 sample_ai(td_s32 argc, td_char *argv[])
{
if (argc <= 1) {
sample_ai_usage(argv[0]);
return EXT_SUCCESS;
}
if (strcmp(argv[1], "q") == 0) {
return sample_ai_exit();
} else {
return sample_ai_entry(argc, argv);
}
}
static td_void sample_ai_ao_usage(const td_char *name)
{
sap_printf(" usage: %s -p [port] -c [channels] -b [bit_depth] -s [sample_rate] -v [volume]\n", name);
sap_printf(" -h show this usage message and abort\n");
sap_printf(" -p specify ai port(mad0/i2s0/i2s1/adc0/lpadc0)\n"
" -c channels of ai port(default: 2)\n"
" -b bit_depth of ai port(default: 16)\n"
" -s samplerate of ai port(default: 48000)\n"
" -v volume of ai port(default: 0)\n"
" -m mute ai port(default: false)\n"
" -l specify a long parameter(bt_loopback/mic0/mic1)\n"
"\n");
sap_printf(" examples:\n");
sap_printf(" %s -p mad0 -c 1 -s 16000\n", name);
sap_printf(" %s -p mad0 -c 2 -s 48000\n", name);
sap_printf(" %s -p i2s1\n", name);
sap_printf(" %s -p i2s1 -c 1\n", name);
sap_printf(" %s -p adc0 -c 2 -s 48000 -v 30\n", name);
sap_printf("\n");
}
static td_void sample_ai_ao_parse_port(const td_char *port_name, sample_ai_inst *inst)
{
td_u32 i;
const struct {
const td_char *name;
uapi_ai_port ai_port;
} ai_port_name[] = {
{"mad0", UAPI_AI_PORT_PDM0},
{"pdm0", UAPI_AI_PORT_PDM0},
{"pdm1", UAPI_AI_PORT_PDM1},
{"i2s0", UAPI_AI_PORT_I2S0},
{"i2s1", UAPI_AI_PORT_I2S1},
{"i2s2", UAPI_AI_PORT_I2S2},
{"i2s3", UAPI_AI_PORT_I2S3},
{"adc0", UAPI_AI_PORT_ADC0},
{"adc1", UAPI_AI_PORT_ADC1},
{"lpadc0", UAPI_AI_PORT_LPADC0},
{"lpadc1", UAPI_AI_PORT_LPADC1},
};
for (i = 0; i < sizeof(ai_port_name) / sizeof(ai_port_name[0]); i++) {
if (strcmp(ai_port_name[i].name, port_name) == 0) {
inst->ai_port = ai_port_name[i].ai_port;
return;
}
}
inst->ai_port = sample_ai_get_test_port();
}
static td_void sample_ai_ao_parse_snd_port(const td_char *port_name, sample_ai_inst *inst)
{
td_u32 i;
const struct {
const td_char *name;
uapi_snd_out_port ao_port;
} ao_port_name[] = {
{"dac0", UAPI_SND_OUT_PORT_DAC0},
{"i2s0", UAPI_SND_OUT_PORT_I2S0},
{"i2s1", UAPI_SND_OUT_PORT_I2S1},
{"i2s2", UAPI_SND_OUT_PORT_I2S2},
};
for (i = 0; i < sizeof(ao_port_name) / sizeof(ao_port_name[0]); i++) {
if (strcmp(ao_port_name[i].name, port_name) == 0) {
inst->ao_port = ao_port_name[i].ao_port;
return;
}
}
inst->ao_port = SND_OUT_PORT_DEFAULT;
}
static td_void sample_ai_ao_parse_snd_id(const td_char *snd_name, sample_ai_inst *inst)
{
td_u32 i;
const struct {
const td_char *name;
uapi_snd snd_id;
} ao_snd_name[] = {
{"snd0", UAPI_SND_0},
{"snd1", UAPI_SND_1},
{"snd2", UAPI_SND_2},
};
for (i = 0; i < sizeof(ao_snd_name) / sizeof(ao_snd_name[0]); i++) {
if (strcmp(ao_snd_name[i].name, snd_name) == 0) {
inst->snd_id = ao_snd_name[i].snd_id;
return;
}
}
inst->snd_id = UAPI_SND_0;
}
static td_void sample_ai_ao_parse_long_opt(td_char opt, const td_char *opt_arg, sample_ai_inst *inst)
{
if (opt_arg == TD_NULL) {
sap_printf("invalid option -- '%c'\n", opt);
return;
}
if (strcmp(opt_arg, "bt_loopback") == 0) {
inst->run_bt_loopback = TD_TRUE;
return;
}
sample_ai_ao_parse_mic(opt_arg, inst);
}
static td_s32 sample_ai_ao_parse_opt(td_char opt, const td_char *opt_arg, sample_ai_inst *inst)
{
if (opt == 'h') {
return EXT_FAILURE;
}
if (opt_arg == TD_NULL) {
sap_printf("invalid option -- '%c'\n", opt);
return EXT_FAILURE;
}
switch (opt) {
case 's':
inst->pcm_format.sample_rate = (td_u32)strtoul(opt_arg, TD_NULL, 0);
break;
case 'c':
inst->pcm_format.channels = (td_u32)strtoul(opt_arg, TD_NULL, 0);
break;
case 'b':
inst->pcm_format.bit_depth = (td_u32)strtoul(opt_arg, TD_NULL, 0);
break;
case 'p':
sample_ai_ao_parse_port(opt_arg, inst);
break;
case 'o':
sample_ai_ao_parse_snd_port(opt_arg, inst);
break;
case 'd':
sample_ai_ao_parse_snd_id(opt_arg, inst);
break;
case 'v':
inst->volume.integer = (td_s32)strtol(opt_arg, TD_NULL, 0);
break;
case 'm':
inst->mute = TD_TRUE;
break;
case 'l':
sample_ai_ao_parse_long_opt(opt, opt_arg, inst);
break;
default:
sap_printf("invalid command line\n");
return EXT_FAILURE;
}
return EXT_SUCCESS;
}
static td_s32 sample_ai_ao_parse_cmd_line(td_s32 argc, td_char *argv[], sample_ai_inst *inst)
{
td_s32 ret;
td_char opt;
parg_state ps;
if (argv == TD_NULL) {
return EXT_FAILURE;
}
parg_init(&ps);
while (1) {
ret = parg_getopt(&ps, argc, argv, "s:c:b:p:v:o:d:hml:");
if (ret == -1) {
break;
}
opt = (td_char)ret;
if (sample_ai_ao_parse_opt(opt, ps.optarg, inst) != EXT_SUCCESS) {
sample_ai_ao_usage(argv[0]);
return EXT_FAILURE;
}
}
inst->pcm_format.sample_per_frame = inst->pcm_format.sample_rate / 100; /* 100 --> 10ms */
return EXT_SUCCESS;
}
static td_s32 sample_ai_ao_start_inst(sample_ai_inst *inst)
{
td_s32 ret;
athread_attr attr;
/* Start AI instance to write data into ADP instance */
ret = uapi_ai_start(inst->h_ai);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(uapi_ai_start, ret);
return ret;
}
/* Create task for ADP instance to read pcm data */
inst->task_enable = TD_TRUE;
inst->sample_ai_process = sample_ai_ao_process;
if (inst->run_bt_loopback != TD_TRUE) {
attr.priority = ATHREAD_PRIORITY_NORMAL;
attr.stack_size = 0x1000; /* 4k */
attr.name = "sample_ai_ao";
ret = athread_create(&inst->task, sample_ai_thread, (td_void *)inst, &attr);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(athread_create, ret);
goto out;
}
} else {
adp_handle_share_set(inst->h_adp, inst->h_player, 0);
sap_printf("[audio] ul handle = x%x, dl handle = x%x\n", inst->h_adp, inst->h_player);
}
return EXT_SUCCESS;
out:
(td_void)uapi_ai_stop(inst->h_ai);
return ret;
}
static td_void sample_ai_ao_inst_init(sample_ai_inst *inst)
{
inst->ai_port = sample_ai_get_test_port();
inst->snd_id = UAPI_SND_0;
inst->ao_port = SND_OUT_PORT_DEFAULT;
inst->pcm_format.channels = UAPI_AUDIO_CHANNEL_1;
inst->pcm_format.bit_depth = UAPI_AUDIO_BIT_DEPTH_16;
inst->pcm_format.sample_rate = UAPI_AUDIO_SAMPLE_RATE_16K;
inst->pcm_format.sample_per_frame = inst->pcm_format.sample_rate / 100; /* 100 --> 10ms */
inst->run_bt_loopback = TD_FALSE;
#if (SAP_CHIP_TYPE == socmn1)
inst->channel_type = 0x0;
#endif
}
static td_s32 sample_ai_player_open(sample_ai_inst *inst)
{
td_s32 ret;
td_handle h_snd;
sample_acodec_arg in_arg;
const uapi_snd_attr snd_attr = {
.port_num = 1,
.port_attr[0].out_port = inst->ao_port,
.channels = SND_OUT_CHANNEL_DEF,
.bit_depth = SND_OUT_BIT_DEPTH_DEF,
.sample_rate = SND_OUT_SAMPLE_RATE_DEF,
};
sample_escast_arg escast_arg = {
.player = &inst->h_player,
.in_arg = &in_arg,
.snd = &h_snd,
.snd_attr = &snd_attr,
.aef_profile = UAPI_SND_AEF_PROFILE_NONE,
};
/* set track input pcm format matching ai output pcm format */
ret = memcpy_s(&in_arg.pcm_format, sizeof(in_arg.pcm_format), &inst->pcm_format, sizeof(inst->pcm_format));
if (ret != EOK) {
sap_err_log_fun(memcpy_s, ret);
return ret;
}
return sample_ao_player_open(&escast_arg, inst->snd_id);
}
static td_s32 sample_ai_ao_entry(td_s32 argc, td_char *argv[])
{
td_s32 ret;
sample_ai_inst *inst = TD_NULL;
inst = sample_ai_alloc_inst();
if (inst == TD_NULL) {
sap_err_log_info("sample_ai_ao_alloc_inst failed");
return EXT_FAILURE;
}
sample_ai_ao_inst_init(inst);
ret = sample_ai_ao_parse_cmd_line(argc, argv, inst);
if (ret != EXT_SUCCESS) {
goto out0;
}
ret = sample_ai_player_open(inst);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ao_player_open, ret);
goto out0;
}
ret = sample_ai_open_inst(inst);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ai_open_inst, ret);
goto out1;
}
ret = sample_ai_ao_start_inst(inst);
if (ret != EXT_SUCCESS) {
sap_err_log_fun(sample_ai_start_inst, ret);
goto out2;
}
sap_alert_log_u32(inst->pcm_format.sample_rate);
sap_alert_log_u32(inst->pcm_format.channels);
sap_alert_log_u32(inst->pcm_format.bit_depth);
return EXT_SUCCESS;
out2:
sample_ai_close_inst(inst);
out1:
sample_ao_player_close();
out0:
sample_ai_free_inst(inst);
return ret;
}
static td_s32 sample_ai_ao_exit(td_void)
{
sample_ai_inst *inst = sample_ai_get_inst();
if (inst != NULL) {
sample_ai_stop_inst(inst);
sample_ai_close_inst(inst);
sample_ao_player_close();
sample_ai_free_inst(inst);
}
return EXT_SUCCESS;
}
td_s32 sample_ai_ao(td_s32 argc, td_char *argv[])
{
if (argc <= 1) {
sample_ai_ao_usage(argv[0]);
return EXT_FAILURE;
}
if (strcmp(argv[1], "q") == 0) {
return sample_ai_ao_exit();
} else {
return sample_ai_ao_entry(argc, argv);
}
}
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif