first
This commit is contained in:
172
apps/common/jl_kws/jl_kws_algo.c
Normal file
172
apps/common/jl_kws/jl_kws_algo.c
Normal file
@ -0,0 +1,172 @@
|
||||
#include "jl_kws_common.h"
|
||||
|
||||
#if TCFG_KWS_VOICE_RECOGNITION_ENABLE
|
||||
|
||||
//===================================================//
|
||||
// 算法接口函数 //
|
||||
//===================================================//
|
||||
#define JL_KWS_PARAM_FILE SDFILE_RES_ROOT_PATH"jl_kws.cfg"
|
||||
#define KWS_FRAME_LEN 320
|
||||
void *JL_kws_init(void *ptr, void *fp1, int online_cmvn, float confidence_yes, float confidence_no, int smooth_yes, int smooth_no);
|
||||
extern int jl_kws_get_need_buf_len(void);
|
||||
//每帧固定320 bytes, 16k采样率, 10ms
|
||||
extern int jl_detect_kws(char *data, int len, void *jl_kws1);
|
||||
extern void JL_kws_free(void *_jl_det_kws);
|
||||
|
||||
#define ALIGN(a, b) \
|
||||
({ \
|
||||
int m = (u32)(a) & ((b)-1); \
|
||||
int rets = (u32)(a) + (m?((b)-m):0); \
|
||||
rets;\
|
||||
})
|
||||
|
||||
|
||||
//===================================================//
|
||||
// IO 口DEBUG(测量算法时间) //
|
||||
//===================================================//
|
||||
#define KWS_IO_DEBUG_0(i,x) //{JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT &= ~BIT(x);}
|
||||
#define KWS_IO_DEBUG_1(i,x) //{JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT |= BIT(x);}
|
||||
|
||||
/*
|
||||
*Yes/No词条识别阈值:
|
||||
*(1)越大越难识别,相应的,词条识别匹配度就越高,即越不容易误触发
|
||||
*(2)越小越容易识别,相应的,词条识别匹配度就越低,即越容易误触发
|
||||
*/
|
||||
#define KWS_YES_THR 0.6f
|
||||
#define KWS_NO_THR 0.6f
|
||||
|
||||
struct jl_kws_algo {
|
||||
void *kws_handle;
|
||||
void *kws_algo_buf;
|
||||
u32 *frame_buf;
|
||||
void *param_ptr;
|
||||
};
|
||||
|
||||
static struct jl_kws_algo __kws_algo = {0};
|
||||
|
||||
static void kws_algo_local_init(void *buf, void *param_ptr)
|
||||
{
|
||||
//===============================//
|
||||
// 算法初始化 //
|
||||
//===============================//
|
||||
void *buf_align = (void *)ALIGN(buf, 4);
|
||||
kws_info("file ptr: 0x%x, buf_align: 0x%x", (u32)param_ptr, (u32)buf_align);
|
||||
memset(buf, 0, jl_kws_get_need_buf_len());
|
||||
int cmn_online = 1;
|
||||
//int online_cmvn = 1;
|
||||
int smooth_yes = 25;
|
||||
int smooth_no = 30;
|
||||
float confidence_yes = KWS_YES_THR;
|
||||
float confidence_no = KWS_NO_THR;
|
||||
__kws_algo.kws_handle = JL_kws_init(buf_align, param_ptr, cmn_online, confidence_yes, confidence_no, smooth_yes, smooth_no);
|
||||
}
|
||||
|
||||
int jl_kws_algo_init(void)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
struct vfs_attr param_attr = {0};
|
||||
u32 param_len = 0;
|
||||
u8 *param_ptr = NULL;
|
||||
u32 need_buf_len = 0 ;
|
||||
void *buf_ptr = NULL;
|
||||
int ret = 0;
|
||||
|
||||
//===============================//
|
||||
// 打开参数文件 //
|
||||
//===============================//
|
||||
fp = fopen(JL_KWS_PARAM_FILE, "r");
|
||||
if (!fp) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
fget_attrs(fp, ¶m_attr);
|
||||
param_len = param_attr.fsize;
|
||||
__kws_algo.param_ptr = (void *)param_attr.sclust; //cpu access addr
|
||||
|
||||
fclose(fp);
|
||||
//===============================//
|
||||
// 申请算法需要的buf //
|
||||
//===============================//
|
||||
need_buf_len = jl_kws_get_need_buf_len();
|
||||
buf_ptr = zalloc(need_buf_len);
|
||||
kws_debug("need_buf_len = 0x%x", need_buf_len);
|
||||
if (!buf_ptr) {
|
||||
return JL_KWS_ERR_ALGO_NO_BUF;
|
||||
}
|
||||
|
||||
__kws_algo.kws_algo_buf = buf_ptr;
|
||||
|
||||
buf_ptr = zalloc(KWS_FRAME_LEN);
|
||||
kws_debug("KWS_FRAME_LEN = 0x%x", KWS_FRAME_LEN);
|
||||
if (!buf_ptr) {
|
||||
free(__kws_algo.kws_algo_buf);
|
||||
__kws_algo.kws_algo_buf = NULL;
|
||||
return JL_KWS_ERR_ALGO_NO_BUF;
|
||||
}
|
||||
__kws_algo.frame_buf = buf_ptr;
|
||||
|
||||
kws_algo_local_init(__kws_algo.kws_algo_buf, __kws_algo.param_ptr);
|
||||
|
||||
return JL_KWS_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
int jl_kws_algo_detect_run(u8 *buf, u32 len)
|
||||
{
|
||||
int ret = 0;
|
||||
/* local_irq_disable(); */
|
||||
/* KWS_IO_DEBUG_1(A,4); */
|
||||
ret = jl_detect_kws((char *)buf, len, __kws_algo.kws_handle);
|
||||
/* KWS_IO_DEBUG_0(A,4); */
|
||||
/* local_irq_enable(); */
|
||||
|
||||
if ((ret != KWS_VOICE_EVENT_YES) &&
|
||||
(ret != KWS_VOICE_EVENT_NO)) {
|
||||
ret = KWS_VOICE_EVENT_NONE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *jl_kws_algo_get_frame_buf(u32 *buf_len)
|
||||
{
|
||||
if (__kws_algo.frame_buf) {
|
||||
*buf_len = KWS_FRAME_LEN;
|
||||
return __kws_algo.frame_buf;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int jl_kws_algo_start(void)
|
||||
{
|
||||
return JL_KWS_ERR_NONE;
|
||||
}
|
||||
|
||||
void jl_kws_algo_stop(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void jl_kws_algo_close(void)
|
||||
{
|
||||
kws_info("%s", __func__);
|
||||
|
||||
if (__kws_algo.kws_handle) {
|
||||
JL_kws_free(__kws_algo.kws_handle);
|
||||
__kws_algo.kws_handle = NULL;
|
||||
}
|
||||
|
||||
if (__kws_algo.kws_algo_buf) {
|
||||
free(__kws_algo.kws_algo_buf);
|
||||
__kws_algo.kws_algo_buf = NULL;
|
||||
}
|
||||
|
||||
if (__kws_algo.frame_buf) {
|
||||
free(__kws_algo.frame_buf);
|
||||
__kws_algo.frame_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if TCFG_KWS_VOICE_RECOGNITION_ENABLE */
|
||||
15
apps/common/jl_kws/jl_kws_api.h
Normal file
15
apps/common/jl_kws/jl_kws_api.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef _JL_KWS_API_H_
|
||||
#define _JL_KWS_API_H_
|
||||
|
||||
int jl_kws_speech_recognition_open(void);
|
||||
int jl_kws_speech_recognition_start(void);
|
||||
void jl_kws_speech_recognition_stop(void);
|
||||
void jl_kws_speech_recognition_close(void);
|
||||
|
||||
void jl_kws_main_user_demo(void);
|
||||
|
||||
//for jl_kws audio extern input
|
||||
u8 kws_aec_get_state(void);
|
||||
void kws_aec_data_output(void *priv, s16 *data, int len);
|
||||
|
||||
#endif /* #ifndef _JL_KWS_API_H_ */
|
||||
301
apps/common/jl_kws/jl_kws_audio.c
Normal file
301
apps/common/jl_kws/jl_kws_audio.c
Normal file
@ -0,0 +1,301 @@
|
||||
#include "jl_kws_common.h"
|
||||
|
||||
#if TCFG_KWS_VOICE_RECOGNITION_ENABLE
|
||||
#include "audio_enc.h"
|
||||
#include "asm/audio_adc.h"
|
||||
#include "audio_codec_clock.h"
|
||||
//===================================================//
|
||||
// ADC buf配置 //
|
||||
//===================================================//
|
||||
#define ADC_DEMO_BUF_NUM 2
|
||||
#define ADC_DEMO_IRQ_POINTS 256
|
||||
#define ADC_DEMO_BUFS_SIZE (ADC_DEMO_BUF_NUM * ADC_DEMO_IRQ_POINTS)
|
||||
#define KWS_CBUF_SIZE (ADC_DEMO_BUFS_SIZE * sizeof(s16))
|
||||
|
||||
|
||||
//==============================================//
|
||||
// KWS 数据来自外部输入(AEC)配置 //
|
||||
//==============================================//
|
||||
#ifdef CONFIG_EARPHONE_CASE_ENABLE
|
||||
#define TCFG_JL_KWS_AUDIO_DATA_FROM_EXTERN 1 //配置为1, 不需要本地开MIC, 由AEC外部输入
|
||||
#else
|
||||
#define TCFG_JL_KWS_AUDIO_DATA_FROM_EXTERN 0 //配置为0, 需要本地开MIC
|
||||
#endif /* #ifdef CONFIG_EARPHONE_CASE_ENABLE */
|
||||
|
||||
|
||||
//==============================================//
|
||||
// KWS RAM使用OVERLAY配置 //
|
||||
//==============================================//
|
||||
#define KWS_USE_OVERLAY_RAM_ENABLE 0
|
||||
#define KWS_USE_OVERLAY_RAM_SIZE (35 * 1024)
|
||||
#if KWS_USE_OVERLAY_RAM_ENABLE
|
||||
static int kws_ram[KWS_USE_OVERLAY_RAM_SIZE / sizeof(int)] sec(.jl_kws_ram);
|
||||
#endif /* #if KWS_USE_OVERLAY_RAM_ENABLE */
|
||||
|
||||
enum KWS_AUDIO_STATE {
|
||||
KWS_AUDIO_STATE_IDLE = 0,
|
||||
KWS_AUDIO_STATE_RUN,
|
||||
KWS_AUDIO_STATE_STOP,
|
||||
KWS_AUDIO_STATE_CLOSE,
|
||||
};
|
||||
|
||||
struct kws_adc_mic {
|
||||
struct audio_adc_output_hdl adc_output;
|
||||
struct adc_mic_ch mic_ch;
|
||||
s16 adc_buf[ADC_DEMO_BUFS_SIZE]; //align 4Bytes
|
||||
};
|
||||
|
||||
struct jl_kws_audio {
|
||||
u8 kws_audio_state;
|
||||
OS_SEM rx_sem;
|
||||
struct kws_adc_mic *kws_adc;
|
||||
cbuffer_t kws_cbuf;
|
||||
u32 cbuf[KWS_CBUF_SIZE / 4];
|
||||
};
|
||||
|
||||
static struct jl_kws_audio *__kws_audio = NULL;
|
||||
|
||||
extern struct audio_adc_hdl adc_hdl;
|
||||
|
||||
static void kws_adc_mic_output(void *priv, s16 *data, int len)
|
||||
{
|
||||
int wlen = 0;
|
||||
|
||||
local_irq_disable();/*防止关闭kws时,kws任务和aec任务互相打断导致释放信号量错误死机的问题*/
|
||||
if (__kws_audio == NULL) {
|
||||
local_irq_enable();
|
||||
return;
|
||||
}
|
||||
if (__kws_audio->kws_audio_state != KWS_AUDIO_STATE_RUN) {
|
||||
local_irq_enable();
|
||||
return;
|
||||
}
|
||||
|
||||
/* kws_putchar('w'); */
|
||||
wlen = cbuf_write(&(__kws_audio->kws_cbuf), data, len);
|
||||
if (wlen < len) {
|
||||
kws_info("kws cbuf full");
|
||||
} else {
|
||||
//kws_debug("wlen: %d", wlen);
|
||||
}
|
||||
|
||||
os_sem_post(&(__kws_audio->rx_sem));
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
|
||||
static int jl_kws_adc_mic_open(void)
|
||||
{
|
||||
u16 mic_sr = 16000;
|
||||
u8 mic_gain = 10;
|
||||
|
||||
if (__kws_audio->kws_adc != NULL) {
|
||||
return JL_KWS_ERR_AUDIO_MIC_STATE_ERR;
|
||||
}
|
||||
|
||||
__kws_audio->kws_adc = zalloc(sizeof(struct kws_adc_mic));
|
||||
kws_debug("struct kws_adc_mic = 0x%x", (u32)sizeof(struct kws_adc_mic));
|
||||
if (__kws_audio->kws_adc == NULL) {
|
||||
return JL_KWS_ERR_AUDIO_MIC_NO_BUF;
|
||||
}
|
||||
|
||||
kws_debug("kws local open mic");
|
||||
//audio_mic_pwr_ctl(MIC_PWR_ON);
|
||||
audio_adc_mic_open(&(__kws_audio->kws_adc->mic_ch), AUDIO_ADC_MIC_CH, &adc_hdl);
|
||||
audio_adc_mic_set_sample_rate(&(__kws_audio->kws_adc->mic_ch), mic_sr);
|
||||
audio_adc_mic_set_gain(&(__kws_audio->kws_adc->mic_ch), mic_gain);
|
||||
audio_adc_mic_set_buffs(&(__kws_audio->kws_adc->mic_ch), __kws_audio->kws_adc->adc_buf, ADC_DEMO_IRQ_POINTS * 2, ADC_DEMO_BUF_NUM);
|
||||
__kws_audio->kws_adc->adc_output.handler = kws_adc_mic_output;
|
||||
audio_adc_add_output_handler(&adc_hdl, &(__kws_audio->kws_adc->adc_output));
|
||||
|
||||
return JL_KWS_ERR_NONE;
|
||||
}
|
||||
|
||||
static int kws_speech_recognition_local_mic_open(void)
|
||||
{
|
||||
jl_kws_adc_mic_open();
|
||||
|
||||
jl_kws_audio_start();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================//
|
||||
// JL_KWS AUDIO API //
|
||||
//==========================================================//
|
||||
int jl_kws_audio_init(void)
|
||||
{
|
||||
int ret = JL_KWS_ERR_NONE;
|
||||
|
||||
if (__kws_audio != NULL) {
|
||||
return JL_KWS_ERR_AUDIO_INIT_STATE_ERR;
|
||||
}
|
||||
|
||||
__kws_audio = zalloc(sizeof(struct jl_kws_audio));
|
||||
kws_debug("struct jl_kws_audio = 0x%x", (u32)sizeof(struct jl_kws_audio));
|
||||
|
||||
if (__kws_audio == NULL) {
|
||||
return JL_KWS_ERR_AUDIO_MIC_NO_BUF;
|
||||
}
|
||||
|
||||
os_sem_create(&(__kws_audio->rx_sem), 0);
|
||||
|
||||
cbuf_init(&(__kws_audio->kws_cbuf), __kws_audio->cbuf, KWS_CBUF_SIZE);
|
||||
|
||||
audio_codec_clock_set(AUDIO_KWS_MODE, AUDIO_CODING_MSBC, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
u8 kws_aec_get_state(void)
|
||||
{
|
||||
#if TCFG_JL_KWS_AUDIO_DATA_FROM_EXTERN
|
||||
if (__kws_audio) {
|
||||
//aec初始化, 查询是否进入kws模式, 这时有可能需要kws本身打开了mic,需要close
|
||||
if (__kws_audio->kws_adc) {
|
||||
kws_debug("kws free adc buf");
|
||||
audio_adc_mic_close(&__kws_audio->kws_adc->mic_ch);
|
||||
audio_adc_del_output_handler(&adc_hdl, &(__kws_audio->kws_adc->adc_output));
|
||||
free(__kws_audio->kws_adc);
|
||||
__kws_audio->kws_adc = NULL;
|
||||
|
||||
cbuf_clear(&(__kws_audio->kws_cbuf));
|
||||
os_sem_set(&(__kws_audio->rx_sem), 0);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
return 0;
|
||||
|
||||
#endif /* #if TCFG_JL_KWS_AUDIO_DATA_FROM_EXTERN */
|
||||
}
|
||||
|
||||
|
||||
u8 kws_get_state(void)
|
||||
{
|
||||
return kws_aec_get_state();
|
||||
}
|
||||
|
||||
u8 kws_is_running(void)
|
||||
{
|
||||
return (__kws_audio != NULL);
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
int audio_aec_update(u8 EnableBit)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kws_aec_data_output(void *priv, s16 *data, int len)
|
||||
{
|
||||
if (__kws_audio == NULL) {
|
||||
return ;
|
||||
}
|
||||
|
||||
if (__kws_audio->kws_adc) {
|
||||
audio_adc_del_output_handler(&adc_hdl, &__kws_audio->kws_adc->adc_output);
|
||||
kws_debug("kws free adc buf");
|
||||
free(__kws_audio->kws_adc);
|
||||
__kws_audio->kws_adc = NULL;
|
||||
}
|
||||
|
||||
kws_adc_mic_output(priv, data, len);
|
||||
}
|
||||
|
||||
|
||||
int jl_kws_audio_get_data(void *buf, u32 len)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 data_len = 0;
|
||||
u32 rlen = 0;
|
||||
|
||||
if (__kws_audio == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data_len = cbuf_get_data_len(&(__kws_audio->kws_cbuf));
|
||||
|
||||
if (data_len >= len) {
|
||||
cbuf_read(&(__kws_audio->kws_cbuf), buf, len);
|
||||
rlen = len;
|
||||
} else {
|
||||
os_sem_set(&(__kws_audio->rx_sem), 0);
|
||||
#if TCFG_JL_KWS_AUDIO_DATA_FROM_EXTERN
|
||||
ret = os_sem_pend(&(__kws_audio->rx_sem), 100);
|
||||
if (ret == OS_TIMEOUT) {
|
||||
kws_speech_recognition_local_mic_open();
|
||||
}
|
||||
#else
|
||||
os_sem_pend(&(__kws_audio->rx_sem), portMAX_DELAY);
|
||||
#endif /* #if TCFG_JL_KWS_AUDIO_DATA_FROM_EXTERN */
|
||||
}
|
||||
|
||||
return rlen;
|
||||
}
|
||||
|
||||
|
||||
int jl_kws_audio_start(void)
|
||||
{
|
||||
int ret = JL_KWS_ERR_NONE;
|
||||
kws_debug("%s", __func__);
|
||||
__kws_audio->kws_audio_state = KWS_AUDIO_STATE_RUN;
|
||||
|
||||
#if (TCFG_JL_KWS_AUDIO_DATA_FROM_EXTERN == 0)
|
||||
ret = jl_kws_adc_mic_open();
|
||||
if (ret != JL_KWS_ERR_NONE) {
|
||||
return ret;
|
||||
}
|
||||
#endif /* #if TCFG_JL_KWS_AUDIO_DATA_FROM_EXTERN */
|
||||
|
||||
if (__kws_audio && (__kws_audio->kws_adc)) {
|
||||
audio_adc_mic_start(&(__kws_audio->kws_adc->mic_ch));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jl_kws_audio_stop(void)
|
||||
{
|
||||
if (__kws_audio) {
|
||||
__kws_audio->kws_audio_state = KWS_AUDIO_STATE_STOP;
|
||||
if (__kws_audio->kws_adc) {
|
||||
audio_adc_mic_close(&__kws_audio->kws_adc->mic_ch);
|
||||
audio_adc_del_output_handler(&adc_hdl, &(__kws_audio->kws_adc->adc_output));
|
||||
free(__kws_audio->kws_adc);
|
||||
__kws_audio->kws_adc = NULL;
|
||||
}
|
||||
|
||||
cbuf_clear(&(__kws_audio->kws_cbuf));
|
||||
os_sem_set(&(__kws_audio->rx_sem), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void jl_kws_audio_close(void)
|
||||
{
|
||||
kws_debug("%s", __func__);
|
||||
if (__kws_audio) {
|
||||
__kws_audio->kws_audio_state = KWS_AUDIO_STATE_CLOSE;
|
||||
if (__kws_audio->kws_adc) {
|
||||
audio_adc_mic_close(&__kws_audio->kws_adc->mic_ch);
|
||||
audio_adc_del_output_handler(&adc_hdl, &(__kws_audio->kws_adc->adc_output));
|
||||
free(__kws_audio->kws_adc);
|
||||
__kws_audio->kws_adc = NULL;
|
||||
}
|
||||
audio_codec_clock_del(AUDIO_KWS_MODE);
|
||||
extern u8 audio_aec_status(void);
|
||||
if (audio_aec_status()) {
|
||||
audio_aec_update(0);
|
||||
}
|
||||
free(__kws_audio);
|
||||
__kws_audio = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* #if TCFG_KWS_VOICE_RECOGNITION_ENABLE */
|
||||
70
apps/common/jl_kws/jl_kws_common.h
Normal file
70
apps/common/jl_kws/jl_kws_common.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef _JL_KWS_COMMON_H_
|
||||
#define _JL_KWS_COMMON_H_
|
||||
|
||||
#include "includes.h"
|
||||
#include "app_config.h"
|
||||
|
||||
// #ifdef SUPPORT_MS_EXTENSIONS
|
||||
// #pragma bss_seg( ".jl_kws_bss")
|
||||
// #pragma data_seg( ".jl_kws_data")
|
||||
// #pragma const_seg( ".jl_kws_const")
|
||||
// #pragma code_seg( ".jl_kws_code")
|
||||
// #endif
|
||||
|
||||
|
||||
#define kws_info printf
|
||||
|
||||
#define KWS_DEBUG_ENABLE
|
||||
#ifdef KWS_DEBUG_ENABLE
|
||||
#define kws_debug printf
|
||||
#define kws_putchar putchar
|
||||
#else
|
||||
#define kws_debug(...)
|
||||
#define kws_putchar(...)
|
||||
#endif /* #define KWS_DEBUG_ENABLE */
|
||||
|
||||
|
||||
enum JL_KWS_ERR {
|
||||
JL_KWS_ERR_NONE = 0,
|
||||
JL_KWS_ERR_AUDIO_MIC_NO_BUF = -400,
|
||||
JL_KWS_ERR_AUDIO_INIT_STATE_ERR,
|
||||
JL_KWS_ERR_AUDIO_MIC_STATE_ERR,
|
||||
|
||||
JL_KWS_ERR_ALGO_NO_BUF = -300,
|
||||
JL_KWS_ERR_ALGO_NO_FRAME_BUF,
|
||||
};
|
||||
|
||||
enum KWS_VOICE_EVENT {
|
||||
KWS_VOICE_EVENT_NONE = 0,
|
||||
KWS_VOICE_EVENT_YES = 2,
|
||||
KWS_VOICE_EVENT_NO,
|
||||
};
|
||||
|
||||
//========================================//
|
||||
// jl_kws_algo API //
|
||||
//========================================//
|
||||
extern int jl_kws_algo_init(void);
|
||||
extern int jl_kws_algo_detect_run(u8 *buf, u32 len);
|
||||
extern void *jl_kws_algo_get_frame_buf(u32 *buf_len);
|
||||
extern int jl_kws_algo_start(void);
|
||||
extern void jl_kws_algo_close(void);
|
||||
extern void jl_kws_algo_stop(void);
|
||||
|
||||
//========================================//
|
||||
// jl_kws_audio API //
|
||||
//========================================//
|
||||
extern int jl_kws_audio_init(void);
|
||||
extern int jl_kws_audio_start(void);
|
||||
extern void jl_kws_audio_stop(void);
|
||||
extern void jl_kws_audio_close(void);
|
||||
extern int jl_kws_audio_get_data(void *buf, u32 len);
|
||||
|
||||
//========================================//
|
||||
// jl_kws_event API //
|
||||
//========================================//
|
||||
extern int jl_kws_event_init(void);
|
||||
extern void jl_kws_event_stop(void);
|
||||
extern void jl_kws_event_close(void);
|
||||
extern void jl_kws_event_state_update(u8 voice_event);
|
||||
|
||||
#endif /* #ifndef _JL_KWS_COMMON_H_ */
|
||||
113
apps/common/jl_kws/jl_kws_event.c
Normal file
113
apps/common/jl_kws/jl_kws_event.c
Normal file
@ -0,0 +1,113 @@
|
||||
#include "jl_kws_common.h"
|
||||
#include "btstack/avctp_user.h"
|
||||
#if TCFG_USER_TWS_ENABLE
|
||||
#include "bt_tws.h"
|
||||
#endif /* #if TCFG_USER_TWS_ENABLE */
|
||||
|
||||
#if TCFG_KWS_VOICE_RECOGNITION_ENABLE
|
||||
|
||||
//=========================================================//
|
||||
// KWS 事件处理 //
|
||||
//=========================================================//
|
||||
|
||||
struct jl_kws_event {
|
||||
u32 last_event_jiffies;
|
||||
u8 last_event;
|
||||
};
|
||||
|
||||
static struct jl_kws_event __kws_event = {0};
|
||||
|
||||
static void kws_event_handle(u8 voice_event)
|
||||
{
|
||||
u32 cur_jiffies = jiffies;
|
||||
|
||||
if (voice_event == __kws_event.last_event) {
|
||||
if (jiffies_to_msecs(cur_jiffies - __kws_event.last_event_jiffies) < 1000) {
|
||||
kws_info("voice event %d same, ignore", voice_event);
|
||||
__kws_event.last_event_jiffies = cur_jiffies;
|
||||
return;
|
||||
}
|
||||
}
|
||||
__kws_event.last_event_jiffies = cur_jiffies;
|
||||
__kws_event.last_event = voice_event;
|
||||
|
||||
kws_info("%s: %d", __func__, voice_event);
|
||||
|
||||
switch (voice_event) {
|
||||
case KWS_VOICE_EVENT_YES:
|
||||
kws_info("send ANSWER cmd");
|
||||
user_send_cmd_prepare(USER_CTRL_HFP_CALL_ANSWER, 0, NULL);
|
||||
break;
|
||||
case KWS_VOICE_EVENT_NO:
|
||||
kws_info("send HANGUP cmd");
|
||||
user_send_cmd_prepare(USER_CTRL_HFP_CALL_HANGUP, 0, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//==========================================================//
|
||||
// TWS 消息同步 //
|
||||
//==========================================================//
|
||||
#if TCFG_USER_TWS_ENABLE
|
||||
extern bool get_tws_sibling_connect_state(void);
|
||||
|
||||
#define TWS_FUNC_ID_KWS_EVENT_SYNC TWS_FUNC_ID('K', 'W', 'S', 'V')
|
||||
|
||||
static void kws_event_sync_tws_state_deal(void *_data, u16 len, bool rx)
|
||||
{
|
||||
u8 *data = (u8 *)_data;
|
||||
u8 voice_event = data[0];
|
||||
kws_info("tws event rx sync: %d", voice_event);
|
||||
kws_event_handle(voice_event);
|
||||
}
|
||||
|
||||
static void kws_sync_tws_event(u8 voice_event)
|
||||
{
|
||||
if (get_tws_sibling_connect_state() == TRUE) {
|
||||
tws_api_send_data_to_sibling(&voice_event, 1, TWS_FUNC_ID_KWS_EVENT_SYNC);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_TWS_FUNC_STUB(kws_voice_event_sync) = {
|
||||
.func_id = TWS_FUNC_ID_KWS_EVENT_SYNC,
|
||||
.func = kws_event_sync_tws_state_deal,
|
||||
};
|
||||
#endif /* #if TCFG_USER_TWS_ENABLE */
|
||||
|
||||
//==========================================================//
|
||||
// JL_KWS EVENT API //
|
||||
//==========================================================//
|
||||
int jl_kws_event_init(void)
|
||||
{
|
||||
return JL_KWS_ERR_NONE;
|
||||
}
|
||||
|
||||
void jl_kws_event_stop(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void jl_kws_event_close(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void jl_kws_event_state_update(u8 voice_event)
|
||||
{
|
||||
#if TCFG_USER_TWS_ENABLE
|
||||
if (get_tws_sibling_connect_state() == TRUE) {
|
||||
kws_sync_tws_event(voice_event);
|
||||
} else
|
||||
#endif /* #if TCFG_USER_TWS_ENABLE */
|
||||
{
|
||||
kws_event_handle(voice_event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* #if TCFG_KWS_VOICE_RECOGNITION_ENABLE */
|
||||
306
apps/common/jl_kws/jl_kws_main.c
Normal file
306
apps/common/jl_kws/jl_kws_main.c
Normal file
@ -0,0 +1,306 @@
|
||||
#include "jl_kws_common.h"
|
||||
#include "jl_kws_api.h"
|
||||
|
||||
#if TCFG_KWS_VOICE_RECOGNITION_ENABLE
|
||||
|
||||
//==========================================================//
|
||||
// KWS 语音识别 //
|
||||
//==========================================================//
|
||||
struct kws_speech_recognition {
|
||||
u8 task_init;
|
||||
u8 kws_state;
|
||||
u8 kws_task_state;
|
||||
};
|
||||
|
||||
static struct kws_speech_recognition jl_kws = {0};
|
||||
//==============================================//
|
||||
// KWS CPU频率配置 //
|
||||
//==============================================//
|
||||
#define KWS_BT_CALL_SYS_FREQUENCE_HZ (48 * 1000000)
|
||||
|
||||
#define __this (&jl_kws)
|
||||
|
||||
enum KWS_TASK_MSG {
|
||||
KWS_SPEECH_RECOGNITION_RUN = 1,
|
||||
KWS_SPEECH_RECOGNITION_STOP,
|
||||
KWS_SPEECH_RECOGNITION_CLOSE,
|
||||
};
|
||||
|
||||
enum KWS_STATE {
|
||||
KWS_STATE_IDLE = 0,
|
||||
KWS_STATE_INIT,
|
||||
KWS_STATE_RUN,
|
||||
KWS_STATE_STOP,
|
||||
KWS_STATE_CLOSE,
|
||||
};
|
||||
|
||||
enum KWS_TASK_STATE {
|
||||
KWS_TASK_STATE_IDLE = 0,
|
||||
KWS_TASK_STATE_INIT,
|
||||
KWS_TASK_STATE_RUN,
|
||||
KWS_TASK_STATE_STOP,
|
||||
KWS_TASK_STATE_CLOSE,
|
||||
};
|
||||
|
||||
//=======================================================//
|
||||
// jl_kws_main //
|
||||
//=======================================================//
|
||||
//=========== 线程名称
|
||||
#define THIS_TASK_NAME "kws"
|
||||
|
||||
static int kws_speech_recognition_run(void)
|
||||
{
|
||||
void *rbuf = 0;
|
||||
u32 rbuf_len = 0;
|
||||
u32 audio_data_len = 0;
|
||||
int ret = JL_KWS_ERR_NONE;
|
||||
int event = KWS_VOICE_EVENT_NONE;
|
||||
|
||||
kws_info("%s", __func__);
|
||||
|
||||
ret = jl_kws_audio_start();
|
||||
if (ret != JL_KWS_ERR_NONE) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = jl_kws_algo_start();
|
||||
if (ret != JL_KWS_ERR_NONE) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
__this->kws_task_state = KWS_TASK_STATE_RUN;
|
||||
|
||||
while (1) {
|
||||
if (__this->kws_state != KWS_STATE_RUN) {
|
||||
break;
|
||||
}
|
||||
rbuf = jl_kws_algo_get_frame_buf(&rbuf_len);
|
||||
|
||||
if (rbuf == NULL) {
|
||||
ret = JL_KWS_ERR_ALGO_NO_FRAME_BUF;
|
||||
break;
|
||||
}
|
||||
|
||||
audio_data_len = jl_kws_audio_get_data(rbuf, rbuf_len);
|
||||
|
||||
if (audio_data_len == rbuf_len) {
|
||||
/* kws_putchar('r'); */
|
||||
event = jl_kws_algo_detect_run(rbuf, rbuf_len);
|
||||
if (event != KWS_VOICE_EVENT_NONE) {
|
||||
jl_kws_event_state_update(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int jl_kws_speech_recognition_init(void)
|
||||
{
|
||||
kws_info("%s", __func__);
|
||||
//1.算法初始化
|
||||
int ret = 0;
|
||||
ret = jl_kws_algo_init();
|
||||
if (ret != JL_KWS_ERR_NONE) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
//2.Audio MIC初始化
|
||||
ret = jl_kws_audio_init();
|
||||
if (ret != JL_KWS_ERR_NONE) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
//3. event初始化
|
||||
ret = jl_kws_event_init();
|
||||
if (ret != JL_KWS_ERR_NONE) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return JL_KWS_ERR_NONE;
|
||||
}
|
||||
|
||||
static int kws_speech_recognition_stop(void)
|
||||
{
|
||||
kws_info("%s", __func__);
|
||||
|
||||
if (__this->task_init) {
|
||||
|
||||
jl_kws_algo_stop();
|
||||
|
||||
jl_kws_audio_stop();
|
||||
|
||||
jl_kws_event_stop();
|
||||
|
||||
__this->kws_task_state = KWS_TASK_STATE_STOP;
|
||||
}
|
||||
|
||||
return JL_KWS_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
static void kws_speech_recognition_close(void)
|
||||
{
|
||||
kws_info("%s", __func__);
|
||||
|
||||
if (__this->task_init) {
|
||||
|
||||
jl_kws_algo_close();
|
||||
|
||||
jl_kws_audio_close();
|
||||
|
||||
jl_kws_event_close();
|
||||
|
||||
__this->kws_task_state = KWS_TASK_STATE_CLOSE;
|
||||
//task_kill(THIS_TASK_NAME);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void kws_speech_recognition_task(void *param)
|
||||
{
|
||||
int msg[16];
|
||||
int ret = 0;
|
||||
|
||||
kws_info("%s", __func__);
|
||||
|
||||
if (param) {
|
||||
os_sem_post((OS_SEM *)(param)); //wait task ready sem
|
||||
}
|
||||
|
||||
ret = jl_kws_speech_recognition_init();
|
||||
kws_debug("ret = %d", ret);
|
||||
if (ret != JL_KWS_ERR_NONE) {
|
||||
kws_speech_recognition_close();
|
||||
__this->task_init = 0;
|
||||
task_kill(THIS_TASK_NAME);
|
||||
}
|
||||
|
||||
__this->kws_state = KWS_STATE_INIT;
|
||||
__this->kws_task_state = KWS_TASK_STATE_INIT;
|
||||
|
||||
while (1) {
|
||||
ret = os_taskq_pend(NULL, msg, ARRAY_SIZE(msg));
|
||||
if (ret == OS_TASKQ) {
|
||||
switch (msg[1]) {
|
||||
case KWS_SPEECH_RECOGNITION_RUN:
|
||||
kws_speech_recognition_run();
|
||||
break;
|
||||
case KWS_SPEECH_RECOGNITION_STOP:
|
||||
kws_speech_recognition_stop();
|
||||
break;
|
||||
case KWS_SPEECH_RECOGNITION_CLOSE:
|
||||
kws_speech_recognition_close();
|
||||
os_sem_post((OS_SEM *)msg[2]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u8 kws_idle_query(void)
|
||||
{
|
||||
return !(__this->kws_task_state == KWS_TASK_STATE_RUN);
|
||||
}
|
||||
|
||||
REGISTER_LP_TARGET(kws_lp_target) = {
|
||||
.name = "kws",
|
||||
.is_idle = kws_idle_query,
|
||||
};
|
||||
|
||||
//==========================================================//
|
||||
// JL_KWS MAIN API //
|
||||
//==========================================================//
|
||||
int jl_kws_speech_recognition_open(void)
|
||||
{
|
||||
kws_info("%s", __func__);
|
||||
OS_SEM ready_sem;
|
||||
|
||||
if (__this->task_init == 0) {
|
||||
__this->task_init = 1;
|
||||
os_sem_create(&ready_sem, 0);
|
||||
task_create(kws_speech_recognition_task, (void *)&ready_sem, THIS_TASK_NAME);
|
||||
//wait task ready
|
||||
os_sem_pend(&ready_sem, 20);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jl_kws_speech_recognition_start(void)
|
||||
{
|
||||
int ret;
|
||||
kws_info("%s", __func__);
|
||||
if (__this->task_init) {
|
||||
if (__this->kws_state == KWS_STATE_RUN) {
|
||||
return 0;
|
||||
}
|
||||
__this->kws_state = KWS_STATE_RUN;
|
||||
ret = os_taskq_post_msg(THIS_TASK_NAME, 1, KWS_SPEECH_RECOGNITION_RUN);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jl_kws_speech_recognition_stop(void)
|
||||
{
|
||||
int ret;
|
||||
kws_info("%s", __func__);
|
||||
if (__this->task_init) {
|
||||
if (__this->kws_state == KWS_STATE_STOP) {
|
||||
return;
|
||||
}
|
||||
__this->kws_state = KWS_STATE_STOP;
|
||||
ret = os_taskq_post_msg(THIS_TASK_NAME, 1, KWS_SPEECH_RECOGNITION_STOP);
|
||||
}
|
||||
}
|
||||
|
||||
void jl_kws_speech_recognition_close(void)
|
||||
{
|
||||
kws_info("%s", __func__);
|
||||
OS_SEM del_sem;
|
||||
if (__this->task_init) {
|
||||
if (__this->kws_state == KWS_STATE_CLOSE) {
|
||||
return;
|
||||
}
|
||||
__this->kws_state = KWS_STATE_CLOSE;
|
||||
os_sem_create(&del_sem, 0);
|
||||
os_taskq_post_msg(THIS_TASK_NAME, 2, KWS_SPEECH_RECOGNITION_CLOSE, (void *)&del_sem);
|
||||
os_sem_pend(&del_sem, 0xffff);
|
||||
task_kill(THIS_TASK_NAME);
|
||||
__this->task_init = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================//
|
||||
// 测试代码 //
|
||||
//==========================================================//
|
||||
void jl_kws_main_user_demo(void)
|
||||
{
|
||||
//1.打开jl_kws模块
|
||||
jl_kws_speech_recognition_open();
|
||||
|
||||
//2.在某时刻开始识别
|
||||
os_time_dly(1000);
|
||||
jl_kws_speech_recognition_start();
|
||||
|
||||
//3.在某时刻之后停止识别
|
||||
os_time_dly(1500);
|
||||
jl_kws_speech_recognition_stop();
|
||||
|
||||
//4.在某时刻之后重新开启识别
|
||||
os_time_dly(2000);
|
||||
jl_kws_speech_recognition_start();
|
||||
|
||||
//5.在某时刻之后关闭jl_kws模块
|
||||
os_time_dly(2500);
|
||||
jl_kws_speech_recognition_close();
|
||||
}
|
||||
|
||||
#endif /* #if TCFG_KWS_VOICE_RECOGNITION_ENABLE */
|
||||
Reference in New Issue
Block a user