构建简单的智能客服系统(三)——基于 UniMRCP 实现讯飞 TTS MRCP Server

MRCP V2
上一篇我们封装了科大讯飞接口实现了 UniMRCP ASR Plugin,这篇文章我们再简单说一下 TTS 的实现。

MRCP plugin

以下内容请参考《基于 UniMRCP 实现讯飞 ASR MRCP Server》:

  • UniMRCP 的编译、安装运行
  • UniMRCP plugin 的加载、调用流程
  • UniMRCP plugin 的新建
  • 讯飞 SDK 的导入

调用讯飞 API 实现 plugin

引用头文件

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "qtts.h"
#include "msp_cmn.h"
#include "msp_errors.h"
#include "mpf_buffer.h"

channel 新增变量

新增 buffer 用于存放 TTS 转化后的语音:

1
2
3
4
struct xfyun_synth_channel_t {
...
mpf_buffer_t *audio_buffer;
}

讯飞 login

因为编写 ASR plugin 的时候我们已经调用过,这里可以省略。

语音合成

跟 ASR 不同,TTS 的请求一下子就发送过来,没有一个长时间处理语音流的过程。所以我们把 session 的创建销毁直接放在一个处理过程中即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
static apt_bool_t xfyun_synth_text_to_speech(const char* src_text, const char* params, mpf_buffer_t *buffer) {
int ret = -1;
const char* sessionID = NULL;
int synth_status = MSP_TTS_FLAG_STILL_HAVE_DATA;
unsigned int audio_len = 0;

sessionID = QTTSSessionBegin(params, &ret);
if (MSP_SUCCESS != ret)
{
apt_log(APT_LOG_MARK, APT_PRIO_WARNING,"[xfyun] QTTSSessionBegin failed, error code: %d.", ret);
return FALSE;
}
ret = QTTSTextPut(sessionID, src_text, (unsigned int)strlen(src_text), NULL);
if (MSP_SUCCESS != ret)
{
apt_log(APT_LOG_MARK, APT_PRIO_WARNING,"[xfyun] QTTSTextPut failed, error code: %d.",ret);
QTTSSessionEnd(sessionID, "TextPutError");
return FALSE;
}
apt_log(APT_LOG_MARK, APT_PRIO_WARNING,"[xfyun] 正在合成 ...");
while (1)
{
/* 获取合成音频 */
const void* data = QTTSAudioGet(sessionID, &audio_len, &synth_status, &ret);
if (MSP_SUCCESS != ret)
break;
if (NULL != data)
{
mpf_buffer_audio_write(buffer, data, audio_len);
}
if (MSP_TTS_FLAG_DATA_END == synth_status)
break;
usleep(150*1000); //防止频繁占用CPU
}
if (MSP_SUCCESS != ret)
{
apt_log(APT_LOG_MARK, APT_PRIO_WARNING,"[xfyun] QTTSAudioGet failed, error code: %d.",ret);
QTTSSessionEnd(sessionID, "AudioGetError");
return FALSE;
}
/* 合成完毕 */
ret = QTTSSessionEnd(sessionID, "Normal");
if (MSP_SUCCESS != ret)
{
apt_log(APT_LOG_MARK, APT_PRIO_WARNING,"[xfyun] QTTSSessionEnd failed, error code: %d.",ret);
return FALSE;
}
return TRUE;
}

xfyun_synth_channel_request_dispatch处理 SYNTHESIZER_SPEAK 消息时调用转换接口。转换完成后,调用如下接口触发媒体流事件:

1
mpf_buffer_event_write(synth_channel->audio_buffer, MEDIA_FRAME_TYPE_EVENT);

传递语音流

xfyun_synth_stream_read中读取 audio_buffer 中的语音流发送给客户端:

1
2
3
4
5
6
static apt_bool_t xfyun_synth_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame)
{
...
mpf_buffer_frame_read(synth_channel->audio_buffer,frame);
...
}

修改配置文件

重新编译安装后,我们还需要修改配置文件,使用我们自己的 engine。编辑conf/unimrcpserver.xml文件,启用我们自己的 engine:

1
2
<engine id="Demo-Synth-1" name="demosynth" enable="false"/>
<engine id="XFyun-Synth-1" name="xfyunsynth" enable="true"/>

运行后就可以看到 xfyunsynth 被加载了。

源码

GitHub:MRCP-Plugin-Demo,该 Demo 只是实现基本流程,还有很多可以完善的地方,如处理 synth 请求的参数。

Q&A

UniMRCP Custom Development Q&A
Cotin Yang wechat
欢迎订阅我的微信公众号 CotinDev
小小地鼓励一下吧~😘