智能客服搭建(2) - MRCP Server ASR插件开发

1. 编码前准备

1.1 新建plugin

  因为unimrcp使用automake 进行源码编译管理,所以除了添加源代码,我们还需要进行相应配置添加。

1.2 修改configure.ac

  首先编辑configure.ac文件,添加如下,其实是一个宏定义会在后面的Makefile 中使用到,以及添加后面我们新增的Makefile

dnl Xxx recognizer plugin.
UNI_PLUGIN_ENABLED(Xxxrecog)

AM_CONDITIONAL([XxxRECOG_PLUGIN],[test "${enable_Xxxrecog_plugin}" = "yes"])

...

AC_CONFIG_FILES([
    plugins/Xxx-recog/Makefile
])

...

AC_OUTPUT
echo Xxx recognizer plugin.... : $enable_Xxxrecog_plugin

1.3 新增源代码及目录

  在 plugin 目录下,新建 Xxx-recog 目录,并在该目录下新建 src 目录,可以将 demo_recog_engine.c 拷贝到该目录下改名为 Xxx_recog_engine.c,并将源代码中的所有的demo关键字替换为Xxx

  新建 Makefile.am 文件,内容如下:

AM_CPPFLAGS                = $(UNIMRCP_PLUGIN_INCLUDES)

plugin_LTLIBRARIES         = Xxxrecog.la

Xxxrecog_la_SOURCES       = src/Xxx_recog_engine.c
Xxxrecog_la_LDFLAGS       = $(UNIMRCP_PLUGIN_OPTS) -std=c++11 -pthread

include $(top_srcdir)/build/rules/uniplugin.am

  修改 plugin 目录下的 Makefile.am 文件,新增如下内容:

if XxxRECOG_PLUGIN
SUBDIRS               += Xxx-recog
endif

2. 代码编写

2.1 语音引擎类封装

  对自己对接的语音引擎模块进行类封装,建议类中包含以下方法。

/** 初始化语音Engine,返回创建后的类指针 */
static AsrXxx* create_Xxx_engine();

/** ws连接成功后,发送语音流数据到ws服务端 */
void ws_send_buffer(const void * wave, const size_t wave_size);

/** 获取到ws服务端推送的语音识别结果的事件 */
bool have_asr_result();

/** 获取ws服务端推送的语音识别结果 */
std::string get_asr_result();

/** 用于语音引擎的销毁,包括销毁线程、释放websocket内存、释放Engine的内存 */
void release_Xxx_engine();

2.2 MRCP Server 框架代码编写

2.2.1 引用头文件

  基于语音引擎的类封装,在Xxx_recog_engine.c中引用其头文件。

#include "AsrXxx.h"

2.2.2 新增类的变量

  在结构体Xxx_recog_channel_t中新增语音引擎类变量。

/** Declaration of Xxx recognizer channel */
struct Xxx_recog_channel_t {
	/** Back pointer to engine */
	Xxx_recog_engine_t     *Xxx_engine;
	/** Engine channel base */
	mrcp_engine_channel_t   *channel;

	/** Active (in-progress) recognition request */
	mrcp_message_t          *recog_request;
	/** Pending stop response */
	mrcp_message_t          *stop_response;
	/** Indicates whether input timers are started */
	apt_bool_t               timers_started;
	/** Voice activity detector */
	mpf_activity_detector_t *detector;
	/** File to write utterance to */
	FILE                    *audio_out;

    /** Xxx asr engine parameter */
    AsrXxx             *Xxx_asr; // Xxx引擎 ASR 类
};

2.2.3 框架核心函数介绍

  对MRCP Server框架的核心函数进行介绍,同时对Xxx语音引擎代码集成进行讲解。

mrcp_plugin_create

  用于语音引擎涉及到登录/注册/鉴权事宜,例如讯飞语音引擎可在其中添加如xxx_login()函数,Xxx暂不涉及到注册,在这部分不进行操作。

/** Create Xxx recognizer engine */
MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool);
Xxx_recog_engine_destroy

  用于语音引擎全局的销毁,如果有语音引擎类/SDK涉及到全局销毁,则添加代码在这里。

/** Destroy recognizer engine */
static apt_bool_t Xxx_recog_engine_destroy(mrcp_engine_t *engine);
Xxx_recog_engine_channel_create

  当有MRCP Client连接MRCP Server时会创建channel,框架会调用该方法,可在其中添加语音引擎实例化的函数,用于处理1路asr结果,create_Xxx_engine就是在这个函数中调用的。

/* create Xxx recognizer channel */
static mrcp_engine_channel_t* Xxx_recog_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool);
Xxx_recog_channel_destroy

  当有MRCP Client断开MRCP Server时会销毁channel,框架会调用该方法,可在其中添加语音引擎销毁的函数,用于对类中创建的对象进行释放,release_Xxx_engine就是在这个函数中调用的。

/** Destroy engine channel */
static apt_bool_t Xxx_recog_channel_destroy(mrcp_engine_channel_t *channel)
Xxx_recog_stream_write

  通过函数名称能看出,这个函数用于接收MRCP Client发送过来的语音流,在这个函数中接收语音流后,做了2件事情:

  • 将语音流传给Xxx_recog_stream_recog函数进行异步语音识别处理
  • 将语音流传给mpf_activity_detector_process函数进行能量计算,端点检测

  下面我们对这两个函数进行介绍。

Xxx_recog_stream_recog

  在创建的channel中,通过Xxx_recog_stream_recog函数,获取语音流,进行语音识别处理,在该函数中,调用ws_send_buffer进行Xxx语音识别。

static apt_bool_t Xxx_recog_stream_recog(Xxx_recog_channel_t *recog_channel, const void *voice_data, unsigned int voice_len) 
mpf_activity_detector_process

  这个函数,是MRCP中封装的涉及到能量计算,端点检测的函数,查看Xxx_recog_engine_channel_create函数,会发现调用mpf_activity_detector_process的入参为mpf_activity_detector_create创建的语音活动检测器。

  根据mpf_activity_detector_create函数的实现,发现level_threshold设置了能量阈值。

MPF_DECLARE(mpf_activity_detector_t*) mpf_activity_detector_create(apr_pool_t *pool)
{
	mpf_activity_detector_t *detector = apr_palloc(pool,sizeof(mpf_activity_detector_t));
	detector->level_threshold = 12; /* 0 .. 255 */
	detector->speech_timeout = 300; /* 0.3 s */
	detector->silence_timeout = 300; /* 0.3 s */
	detector->noinput_timeout = 5000; /* 5 s */
	detector->duration = 0;
	detector->state = DETECTOR_STATE_INACTIVITY;
	return detector;
}

  我使用主播麦进行测试,在默认的12的能量阈值下,无法检测到说话结束,调整到25有时候可以端点检测,有时候也不能,整体感受下来VAD的效果不好,顺便提一下能量阈值的设置方法,可直接通过mpf_activity_detector_level_set函数进行设置。

recog_channel->detector = mpf_activity_detector_create(pool);
mpf_activity_detector_level_set(recog_channel->detector, 30);

  后来我去看了网上对MRCP的端点检测效果,评价基本是不太能用的状态。

  其中一篇帖子如下,感兴趣的可以去了解下:

  unimrcp-voice-activity语音检测

  对于希望自己实现VAD替换自带的VAD,可参考这两篇文章:

  WebRTC的VAD 过程解读

  替换unimrcp的VAD模块

  由于我使用的语音引擎支持VAD,于是没有再深入研究,对于XxxVAD集成方法2.2.4中介绍。

Xxx_recog_result_load

  该函数在检测到语音识别结束的事件后调用,功能就是在这里获取识别结果,组装成xml发送给MRCP Client端,这样就完成了从语音传输和识别的过程。

/* Load Xxx recognition result */
static apt_bool_t Xxx_recog_result_load(Xxx_recog_channel_t *recog_channel, mrcp_message_t *message);

2.2.4 XxxVAD集成

  这部分重点介绍下,通过对语音引擎vad事件的封装,替换MRCP的VAD,函数核心实现如下。

/** Callback is called from MPF engine context to write/send new frame */
static apt_bool_t Xxx_recog_stream_write(mpf_audio_stream_t *stream, const mpf_frame_t *frame)
{
	...
    
	if(recog_channel->recog_request) {
        // 屏蔽以下代码,原因是mrcp原生的vad效果差,使用Xxx的vad
		// mpf_detector_event_e det_event = mpf_activity_detector_process(recog_channel->detector,frame);
        ......

        // 使用Xxxvad,根据asr结果情况,进行mrcp协议的发送
        bool Xxx_vad_completion = recog_channel->Xxx_asr->have_asr_result();
        if (Xxx_vad_completion == true)
        {
            apt_log(RECOG_LOG_MARK,APT_PRIO_INFO,"[Xxx] Detected Voice Inactivity " APT_SIDRES_FMT,
                MRCP_MESSAGE_SIDRES(recog_channel->recog_request));
            Xxx_recog_recognition_complete(recog_channel,RECOGNIZER_COMPLETION_CAUSE_SUCCESS);
        }
        
        
        
		if(recog_channel->audio_out) {
			fwrite(frame->codec_frame.buffer,1,frame->codec_frame.size,recog_channel->audio_out);
		}
	}
	return TRUE;
}

3. 编译

3.1 标准编译方法

  完成编码后,重新编译安装后,就可以进行配置和使用了。

  编译安装方法可参考:智能客服搭建(1) - MRCP Server 搭建

3.2 独立编译方法

  编写Makefile,将mrcp及引擎类的lib/include添加进去,即可进行编译。

  本次开发使用g++进行编译,具体详情可参考makefile

  待完代码开发完成且测试稳定后,根据情况考虑将代码进行上传,由于目前集成的SDK暂未公开,所以后面我会写一篇集成阿里/讯飞语音引擎的详细文章。

  将完成编译的so放到 /usr/local/unimrcp/plugin 中。

  下篇文章介绍MRCP如何与FreeSWITCH对接相关事宜。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小爱玄策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值