doubao_chat/lib/net/dui/dui_api.c

495 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "generic/circular_buf.h"
#include "generic/version.h"
#include "server/server_core.h"
#include "server/ai_server.h"
#include "dui.h"
#ifdef DUI_MEMHEAP_DEBUG
#include "mem_leak_test.h"
#endif
//通过调用版本检测函数使的模块的代码能够被链接
#ifdef DUI_SDK_VERSION
MODULE_VERSION_EXPORT(dui_sdk, DUI_SDK_VERSION);
#endif
const struct ai_sdk_api dui_sdk_api;
static const int events[][2] = {
{ AI_EVENT_SPEAK_END, DUI_SPEAK_END },
{ AI_EVENT_MEDIA_END, DUI_MEDIA_END },
{ AI_EVENT_MEDIA_STOP, DUI_MEDIA_STOP},
{ AI_EVENT_PREVIOUS_SONG, DUI_PREVIOUS_SONG },
{ AI_EVENT_NEXT_SONG, DUI_NEXT_SONG },
{ AI_EVENT_RECORD_START, DUI_RECORD_START },
{ AI_EVENT_RECORD_BREAK, DUI_RECORD_BREAK },
{ AI_EVENT_RECORD_STOP, DUI_RECORD_STOP },
{ AI_EVENT_VOICE_MODE, DUI_VOICE_MODE },
{ AI_EVENT_COLLECT_RES, DUI_COLLECT_RESOURCE },
{ AI_EVENT_SPEAK_START, DUI_SPEAK_START},
{ AI_EVENT_MEDIA_START, DUI_MEDIA_START},
{ AI_EVENT_PLAY_PAUSE, DUI_PLAY_PAUSE},
{ AI_EVENT_QUIT, DUI_QUIT },
{ AI_EVENT_VOLUME_CHANGE, DUI_VOLUME_CHANGE},
};
enum {
DUI_SPEEK_URL,
DUI_MEDIA_URL,
DUI_PICTURE_URL,
};
typedef enum {
RECORDER_START,
RECORDER_STOP
} dui_rec_state_t;
typedef struct {
u8 vad_stop_flag;
u8 vad_status;
u8 vad_enable;
} VAD_INFO;
static u8 curr_url_type;
static VAD_INFO vad_info;
static dui_rec_state_t s_dui_rec_state = RECORDER_STOP;
static OS_MUTEX mutex;
static struct server *enc_server = NULL;
static DUI_AUDIO_INFO *info = NULL;
static cbuffer_t vad_cbuf;
static u8 vad_cache_buf[ONE_CACHE_BUF_LEN];
struct list_head dui_audio_info_list_head = LIST_HEAD_INIT(dui_audio_info_list_head);
static int event2dui(int event)
{
for (int i = 0; i < ARRAY_SIZE(events); i++) {
if (events[i][0] == event) {
return events[i][1];
}
}
return -1;
}
static int dui_sdk_do_event(int event, int arg)
{
int err;
int argc = 2;
int argv[4];
int dui_event;
dui_event = event2dui(event);
if (dui_event == -1) {
log_e("dui_event %s %d", __func__, __LINE__);
return 0;
}
printf("dui_event: 0x%x\n", dui_event);
argv[0] = dui_event;
argv[1] = arg;
if (dui_event == DUI_RECORD_START) {
if (s_dui_rec_state == RECORDER_START) {
return 0;
}
} else if (dui_event == DUI_MEDIA_END) {
if (curr_url_type == DUI_SPEEK_URL) {
} else if (curr_url_type == DUI_PICTURE_URL) {
return 0;
}
}
if (dui_event == DUI_RECORD_START ||
dui_event == DUI_PREVIOUS_SONG ||
dui_event == DUI_COLLECT_RESOURCE ||
dui_event == DUI_NEXT_SONG) {
if (get_dui_msg_notify()) {
return 0;
}
}
err = os_taskq_post_type("dui_app_task", Q_USER, argc, argv);
if (err != OS_NO_ERR) {
printf("send msg to dui_app_task fail, event : %d\n", dui_event);
}
return 0;
}
DUI_AUDIO_INFO *get_dui_audio_info(void)
{
DUI_AUDIO_INFO *info = NULL;
os_mutex_pend(&mutex, 0);
if (!list_empty(&dui_audio_info_list_head)) {
info = list_first_entry(&dui_audio_info_list_head, DUI_AUDIO_INFO, entry); //需要加互斥
if (info) {
list_del(&info->entry);
}
}
os_mutex_post(&mutex);
return info;
}
void put_dui_audio_info(DUI_AUDIO_INFO *info)
{
os_mutex_pend(&mutex, 0);
if (info) {
free(info);
}
os_mutex_post(&mutex);
}
static int dui_vfs_fwrite(void *file, void *data, u32 len)
{
int ret = 0;
int remain = 0;
int data_len = len;
u32 header = 0;
u32 rlen = 0;
u32 ret_len;
u16 curr_offset = 0;
u8 cache_buf[ONE_CACHE_BUF_LEN];
if (s_dui_rec_state == RECORDER_STOP) {
goto exit1;
}
if (vad_info.vad_enable) {
#if 0
if (0 == vad_info.vad_status) {
while (0 == cbuf_is_write_able(&vad_cbuf, len)) {
if (4 != cbuf_read(&vad_cbuf, &header, 4)) {
goto exit1;
}
rlen = ntohl(header) + 4; //帧长度+帧编码参数
if (rlen != cbuf_read(&vad_cbuf, cache_buf, rlen)) {
goto exit1;
}
}
ret = cbuf_write(&vad_cbuf, data, len);
goto exit1;
}
#endif
//只使能vad停止功能
if (2 == vad_info.vad_status && !vad_info.vad_stop_flag) {
DUI_OS_TASKQ_POST("dui_app_task", 1, DUI_RECORD_STOP);
vad_info.vad_stop_flag = 1;
}
#if 0
if (cbuf_get_data_size(&vad_cbuf) > 0) {
curr_offset += cbuf_read(&vad_cbuf, cache_buf, cbuf_get_data_size(&vad_cbuf));//唤醒之后就读出来
}
#endif
}
os_mutex_pend(&mutex, 0);
writing:
if (!info) {
info = calloc(1, sizeof(DUI_AUDIO_INFO));
if (!info) {
log_e("\n calloc audio buf err\n");
goto exit2;
}
//当且仅当VAD唤醒时拷贝一次
if (curr_offset) {
memcpy(info->buf, cache_buf, curr_offset);
info->len += curr_offset;
curr_offset = 0;
}
info->sessionid = get_record_sessionid();
}
if ((info->len + data_len) < ONE_CACHE_BUF_LEN) {
/*当前长度加上数据长度 小于 缓冲区的数据 直接拷贝即可*/
memcpy(info->buf + info->len, data, data_len);
info->len += data_len;
} else {
remain = ONE_CACHE_BUF_LEN - info->len;
memcpy(info->buf + info->len, data, remain);
info->len += remain;
data = (u8 *)data + remain;
data_len = data_len - remain;
list_add_tail(&info->entry, &dui_audio_info_list_head);
info = NULL;
if (s_dui_rec_state != RECORDER_START) {
goto exit2;
}
DUI_OS_TASKQ_POST("dui_app_task", 1, DUI_RECORD_SEND);
if (data_len != 0) {
goto writing;
}
}
ret = len;
exit2:
os_mutex_post(&mutex);
exit1:
return ret;
}
static int dui_vfs_fclose(void *file)
{
os_mutex_pend(&mutex, 0);
if (info) {
if (info->len) {
list_add_tail(&info->entry, &dui_audio_info_list_head);//需要加互斥
} else {
free(info);
}
}
info = NULL;
os_mutex_post(&mutex);
return 0;
}
static const struct audio_vfs_ops dui_vfs_ops = {
.fwrite = dui_vfs_fwrite,
.fclose = dui_vfs_fclose,
};
static void enc_server_event_handler(void *priv, int argc, int *argv)
{
switch (argv[0]) {
case AUDIO_SERVER_EVENT_ERR:
puts("dui rec: AUDIO_SERVER_EVENT_ERR\n");
if (s_dui_rec_state == RECORDER_START) {
DUI_OS_TASKQ_POST("dui_app_task", 1, DUI_RECORD_ERR);
}
break;
case AUDIO_SERVER_EVENT_END:
puts("dui rec: AUDIO_SERVER_EVENT_END\n");
if (s_dui_rec_state == RECORDER_START) {
DUI_OS_TASKQ_POST("dui_app_task", 1, DUI_RECORD_STOP);
}
break;
case AUDIO_SERVER_EVENT_SPEAK_START:
vad_info.vad_status = 1;
puts("speak start ! \n");
break;
case AUDIO_SERVER_EVENT_SPEAK_STOP:
vad_info.vad_status = 2;
puts("speak stop ! \n");
break;
default:
break;
}
}
u8 dui_get_vad_status(void)
{
return vad_info.vad_status;
}
__attribute__((weak)) void *get_asr_read_input_cb(void)
{
return NULL;
}
__attribute__((weak)) u8 last_audio_dec_is_start(void)
{
return 0;
}
int dui_recorder_start(u16 sample_rate, u8 voice_mode, u8 use_vad)
{
int err;
union audio_req req = {0};
OS_SEM mic_sem;
ASSERT(s_dui_rec_state != RECORDER_START, "s_dui_rec_state == RECORDER_START")
if (!enc_server) {
enc_server = server_open("audio_server", "enc");
ASSERT(enc_server != NULL, "audio_server open err");
}
vad_info.vad_stop_flag = 0;
vad_info.vad_status = 0;
vad_info.vad_enable = use_vad;
server_register_event_handler(enc_server, NULL, enc_server_event_handler);
cbuf_init(&vad_cbuf, vad_cache_buf, ONE_CACHE_BUF_LEN);
req.enc.cmd = AUDIO_ENC_OPEN;
req.enc.channel = 1;
req.enc.volume = 100;
req.enc.priority = 0;
req.enc.output_buf = NULL;
req.enc.sample_rate = sample_rate;
req.enc.sample_source = "mic";
req.enc.output_buf_len = 8 * 1024;
//req.enc.vad_start_threshold = 320;
req.enc.msec = 15 * 1000;//录音时间
//还可以支持amr,采样率8000
req.enc.format = "opus";
req.enc.vfs_ops = &dui_vfs_ops;
//当且仅当CONFIG_ALL_ADC_CHANNEL_OPEN_ENABLE打开时,才可赋值这个参数
if (voice_mode & 0x78) {
u8 source = (voice_mode & 0x78) >> 3;
if (source <= 4) {
req.enc.channel_bit_map = BIT(source - 1);
} else if (source == 5) {
req.enc.sample_source = "plnk0";
} else if (source == 6) {
req.enc.sample_source = "plnk1";
} else if (source == 7) {
req.enc.sample_source = "iis0";
} else if (source == 8) {
req.enc.sample_source = "iis1";
}
}
printf("\n req.enc.channel_bit_map = %d\n", req.enc.channel_bit_map);
if (use_vad) {
#if !CONFIG_CLOUD_VAD_ENABLE
req.enc.use_vad = 1;
#endif
}
os_sem_create(&mic_sem, 0);
ai_server_event_notify(&dui_sdk_api, &mic_sem, AI_SERVER_EVENT_MIC_OPEN);
os_sem_pend(&mic_sem, 0);
os_sem_del(&mic_sem, 1);
int(*read_input)(u8 *, u32) = get_asr_read_input_cb();
if (read_input) {
req.enc.sample_source = "virtual";
req.enc.read_input = read_input;
req.enc.frame_size = sample_rate == 16000 ? 640 : 320;
req.enc.channel_bit_map = 0;
}
s_dui_rec_state = RECORDER_START;
err = server_request(enc_server, AUDIO_REQ_ENC, &req);
if (err) {
goto exit;
}
return 0;
exit:
return -1;
}
int dui_recorder_stop(u8 voice_mode)
{
int err;
union audio_req req = {0};
if (!enc_server || s_dui_rec_state == RECORDER_STOP) {
return 0;
}
printf("\n dui_recorder_stop\n");
s_dui_rec_state = RECORDER_STOP;
req.enc.cmd = AUDIO_ENC_CLOSE;
err = server_request(enc_server, AUDIO_REQ_ENC, &req);
if (err == 0) {
ai_server_event_notify(&dui_sdk_api, NULL, AI_SERVER_EVENT_MIC_CLOSE);
}
return err;
}
void dui_media_speak_play(const char *url)
{
curr_url_type = DUI_SPEEK_URL;
ai_server_event_url(&dui_sdk_api, url, AI_SERVER_EVENT_URL_TTS);
}
void dui_media_audio_play(const char *url)
{
curr_url_type = DUI_MEDIA_URL;
ai_server_event_url(&dui_sdk_api, url, AI_SERVER_EVENT_URL);
dui_media_set_playing_status(PLAY);
}
void dui_media_audio_continue_play(const char *url)
{
ai_server_event_url(&dui_sdk_api, url, AI_SERVER_EVENT_CONTINUE);
dui_media_set_playing_status(PLAY);
}
void dui_media_audio_pause_play(const char *url)
{
ai_server_event_url(&dui_sdk_api, url, AI_SERVER_EVENT_PAUSE);
dui_media_set_playing_status(PAUSE);
}
void dui_media_audio_resume_play(void)
{
extern u8 last_audio_dec_is_start(void);
if (last_audio_dec_is_start()) {
ai_server_event_url(&dui_sdk_api, NULL, AI_SERVER_EVENT_RESUME_PLAY);
}
}
void dui_media_audio_prev_play(void)
{
ai_server_event_url(&dui_sdk_api, NULL, AI_SERVER_EVENT_PREV_PLAY);
}
void dui_media_audio_next_play(void)
{
ai_server_event_url(&dui_sdk_api, NULL, AI_SERVER_EVENT_NEXT_PLAY);
}
void dui_media_audio_stop_play(const char *url)
{
ai_server_event_url(&dui_sdk_api, url, AI_SERVER_EVENT_STOP);
}
void dui_volume_change_notify(int volume)
{
ai_server_event_notify(&dui_sdk_api, (void *)volume, AI_SERVER_EVENT_VOLUME_CHANGE);
}
int dui_media_audio_play_seek(u32 time_elapse)
{
return ai_server_event_notify(&dui_sdk_api, (void *)time_elapse, AI_SERVER_EVENT_SEEK);
}
int dui_tone_tts_play(int type)
{
return 0;
}
void dui_event_notify(int event, void *arg)
{
ai_server_event_notify(&dui_sdk_api, arg, event);
}
static int dui_sdk_open()
{
os_mutex_create(&mutex);
return dui_app_init();
}
static int dui_sdk_check()
{
if (dui_app_get_connect_status()) {
return AI_STAT_CONNECTED;
}
return AI_STAT_DISCONNECTED;
}
static int dui_sdk_disconnect()
{
dui_app_uninit();
if (enc_server) {
server_close(enc_server);
enc_server = NULL;
}
os_mutex_del(&mutex, 1);
return 0;
}
REGISTER_AI_SDK(dui_sdk_api) = {
.name = "dui",
.connect = dui_sdk_open,
.state_check = dui_sdk_check,
.do_event = dui_sdk_do_event,
.disconnect = dui_sdk_disconnect,
};