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

原文链接:https://cotin.tech/AI/UniMRCPTTS/

构建简单的智能客服系统(三)——基于 UniMRCP 实现讯飞 TTS MRCP Server
上一篇我们封装了科大讯飞接口实现了 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

上一篇:【SPOJ】1812. Longest Common Substring II(后缀自动机)


下一篇:构建简单的智能客服系统(二)——基于 UniMRCP 实现讯飞 ASR MRCP Server