UniMRCP开发ASR插件
背景
工作需要,对外封装MRCP Server,使用自己的ASR和TTS能力。
在技术选型上,使用了主流的UniMRCP做为MRCP Server。
本文记录一下,在UniMRCP中,开发ASR插件的过程。
初体验
准备工作
见 《UniMRCP 1.7.0初体验 》一文,已经可以顺利编译出 demorecog.so
的文件。
创建meta-recog插件demo
第一步,我们先开发一个metar-ecog的插件demo,返回预置的内容。
创建模板
共三步。
第一步:编辑 configure.ac
文件,diff如下:
@@ -180,6 +180,11 @@ UNI_PLUGIN_ENABLED(demorecog)
AM_CONDITIONAL([DEMORECOG_PLUGIN],[test "${enable_demorecog_plugin}" = "yes"])
+dnl Meta recognizer plugin.
+UNI_PLUGIN_ENABLED(metarecog)
+
+AM_CONDITIONAL([METARECOG_PLUGIN],[test "${enable_metarecog_plugin}" = "yes"])
+
dnl Demo verifier plugin.
UNI_PLUGIN_ENABLED(demoverifier)
@@ -219,6 +224,7 @@ AC_CONFIG_FILES([
plugins/mrcp-recorder/Makefile
plugins/demo-synth/Makefile
plugins/demo-recog/Makefile
+ plugins/meta-recog/Makefile
plugins/demo-verifier/Makefile
platforms/Makefile
platforms/libunimrcp-server/Makefile
@@ -270,6 +276,7 @@ echo UniMRCP server app............ : $enable_server_app
echo
echo Demo synthesizer plugin....... : $enable_demosynth_plugin
echo Demo recognizer plugin........ : $enable_demorecog_plugin
+echo Meta recognizer plugin........ : $enable_metarecog_plugin
echo Demo verifier plugin.......... : $enable_demoverifier_plugin
echo Recorder plugin............... : $enable_recorder_plugin
echo
第二步:创建插件目录 plugins/meta-recog
,内容如下:
plugins/meta-recog/
├── CMakeLists.txt
├── Makefile.am
├── Makefile.in
└── src
└── meta_recog_engine.c
直接从 plugins/demo-recog
目录下复制过来前3个文件,以及复制更名第3个文件。再将4个文件中的demo
替换为meta
即可。
第三步:修改 plugins/Makefile.am
,内容如下:
if METARECOG_PLUGIN
SUBDIRS += meta-recog
endif
测试:执行 ./configure --prefix=/opt/unimrcp
,部分内容如下:
Demo synthesizer plugin....... : yes
Demo recognizer plugin........ : yes
Meta recognizer plugin........ : yes
Demo verifier plugin.......... : yes
Recorder plugin............... : yes
执行 make
,部分结果如下:
ls plugins/meta-recog/.libs/
metarecog.a metarecog.la metarecog.lai metarecog.so metarecog.so.0 metarecog.so.0.7.0
阶段测试
怎么测试是否使用了自己的插件呢。
定制返回结果
在 src/meta_recog_engine.c
文件中,指定了从 result.xml
中读取结果。所以我们修改如下:
char *file_path = apt_datadir_filepath_get(dir_layout,"result_meta.xml",message->pool);
指定识别路由
修改配置文件 conf/unimrcpserver.xml
,启用自己的引擎,修改结果如下:
<!-- <engine id="Demo-Recog-1" name="demorecog" enable="true"/> -->
<engine id="Meta-Recog-1" name="metarecog" enable="true"/>
<engine id="Demo-Verifier-1" name="demoverifier" enable="true"/>
运行server
cd /opt/unimrcp/bin
LD_LIBRARY_PATH=/usr/local/lib/ ./unimrcpserver
运行client
cd /opt/unimrcp/bin
LD_LIBRARY_PATH=/usr/local/lib/ ./umc
运行识别
run recog
结果如下,我前面将one改为了meta,这里显示的符合预期。
<?xml version="1.0"?>
<result>
<interpretation grammar="session:[email protected]" confidence="0.97">
<instance>meta</instance>
<input mode="speech">meta</input>
</interpretation>
</result>
对接自己的ASR
原理
[1]
在引擎层面,有 plugin 的 create, open, close 和 destroy 四个操作,如下:
/** Table of MRCP engine virtual methods */
struct mrcp_engine_method_vtable_t {
/** Virtual destroy */
apt_bool_t (*destroy)(mrcp_engine_t *engine);
/** Virtual open */
apt_bool_t (*open)(mrcp_engine_t *engine);
/** Virtual close */
apt_bool_t (*close)(mrcp_engine_t *engine);
/** Virtual channel create */
mrcp_engine_channel_t* (*create_channel)(mrcp_engine_t *engine, apr_pool_t *pool);
};
在客户端与服务端plugin通信时,在session内会创建和销毁channel,相关回调如下:
/** Table of channel virtual methods */
struct mrcp_engine_channel_method_vtable_t {
/** Virtual destroy */
apt_bool_t (*destroy)(mrcp_engine_channel_t *channel);
/** Virtual open */
apt_bool_t (*open)(mrcp_engine_channel_t *channel);
/** Virtual close */
apt_bool_t (*close)(mrcp_engine_channel_t *channel);
/** Virtual process_request */
apt_bool_t (*process_request)(mrcp_engine_channel_t *channel, mrcp_message_t *request);
};
处理ASR音频数据流入,TTS音频数据流出的相关音频数据回调如下:
/** Table of audio stream virtual methods */
struct mpf_audio_stream_vtable_t {
/** Virtual destroy method */
apt_bool_t (*destroy)(mpf_audio_stream_t *stream);
/** Virtual open receiver method */
apt_bool_t (*open_rx)(mpf_audio_stream_t *stream, mpf_codec_t *codec);
/** Virtual close receiver method */
apt_bool_t (*close_rx)(mpf_audio_stream_t *stream);
/** Virtual read frame method */
apt_bool_t (*read_frame)(mpf_audio_stream_t *stream, mpf_frame_t *frame);
/** Virtual open transmitter method */
apt_bool_t (*open_tx)(mpf_audio_stream_t *stream, mpf_codec_t *codec);
/** Virtual close transmitter method */
apt_bool_t (*close_tx)(mpf_audio_stream_t *stream);
/** Virtual write frame method */
apt_bool_t (*write_frame)(mpf_audio_stream_t *stream, const mpf_frame_t *frame);
/** Virtual trace method */
void (*trace)(mpf_audio_stream_t *stream, mpf_stream_direction_e direction, apt_text_stream_t *output);
};
实现
默认的实现,以及[3]中的实现,是在vad断句后,返回整句的结果。这种情况下,是逐句返回结果给上游的。
调试
分析其中的crash相关情况。
调试编译
CFLAGS=-g -O0 -fsanitize=address -fno-omit-frame-pointer CXXFLAGS=-I/home/abeffect/opt/grpc/include -g -O0 -fsanitize=address -fno-omit-frame-pointer ./configure --prefix=/opt/unimrcp --enable-maintainer-mode
去掉-O2编译标识
find . -type f | grep Makefile | xargs sed -i 's/-O2 -pthread/-pthread/g'