本示例使用的CPU:rk3588。
操作系统:kylin V10
架构:aarch64
在Windows端,我们很容易想到使用Qt自带的类QTextToSpeech来实现文字转语音功能,Qt版本得在5.11.0以上才支持。但是在嵌入式平台,尤其是在国产的操作系统中,我们需要编译Qt源码,使得Qt能够支持文字转语音功能,我使用的Qt版本5.12.8,我的想法是在编译配置中将文字转语音功能编译进去,但是我并没有找到相关的配置选项,如果有知道的大佬,请指教。
查看系统的版本:
lsb_release -a
查看系统架构:
uname -m
查看Qt的编译选项,生成makefile。
./configure -help
后来我采用了另外的三方库eSpeak,来实现了文字转语音。
这里我并不是使用的交叉编译,我直接放到将源码放到平台上编译。
eSpeak依赖PortAudio进行播放音频,所以在编译eSpeak前需要准备好PortAudio的库。
PortAudio是一个免费、跨平台、开源的音频I/O库。它能够简化C/C++的音频程序的设计实现,能够运行在Windows、Macintosh OS X和UNIX之上(Linux的各种版本也不在话下)。
PortAudio下载:我下载的如下图所示的包。
PortAudio - an Open-Source Cross-Platform Audio API
PortAudio编译:
解压完后在portaudio目录下创建build目录用于安装。生成makefile。
./configure --prefix=/home/kylin/wzz/build
编译:make -j8 && make install
在build目录下就有了安装好的头文件和库。
eSpeak下载地址:
解压完成后,进入到src中。
cd src && cp portaudio19.h portaudio.h?
修改Makefile内容:
vi Makefile
修改3-7行,根据自己的路径来修改。
注释掉30行:AUDIO = portaudio
打开31行:AUDIO = portaudio0
注释掉53行:LIB_AUDIO=/usr/lib/libportaudio.so.0
添加LIB_AUDIO=$(PREFIX)/lib/libportaudio.so
?(刚刚编译生成的portaudio库)
?
保存后执行:
make -j8 && make install
生成的库在/home/kylin/wzz/build下。
在bin目录下生成espeak可执行程序。
执行命令:
./espeak "你好" -v zh
嵌入到Qt代码中:源码示例。
espeak_Initialize:初始化。
espeak_SetVoiceByName:设置声音。
espeak_SetSynthCallback:设置回调。
espeak_Synth:语音合成。
espeak_Cancel:停止合成。
espeak_Terminate:终止。
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QList>
#include <QMutex>
class MyThread : public QThread
{
public:
MyThread(QObject *parent = nullptr);
public:
void stop();
void add(QString text);
void cancel();
protected:
void run();
private:
QString m_text;
bool m_isStart = false;
QMutex m_mutex;
bool m_isNewText;
};
#endif // MYTHREAD_H
#include "mythread.h"
#include <QDebug>
#ifdef __LINUX__
extern "C"
{
#include "espeak/speak_lib.h"
}
int speakCallback(short *wav, int numsamples, espeak_EVENT *events)
{
fwrite(wav, sizeof(short), numsamples, stdout);
}
#endif // __LINUX__
MyThread::MyThread(QObject *parent)
: QThread(parent)
{
#ifdef __LINUX__
if(espeak_Initialize(AUDIO_OUTPUT_PLAYBACK,0,NULL,0) != EE_OK)
{
qDebug()<<"espeak_Initialize error";
}
espeak_SetVoiceByName("zh+f2");
#endif // __LINUX__
//espeak_SetSynthCallback(speakCallback);
}
void MyThread::stop()
{
m_isStart = false;
}
void MyThread::add(QString text)
{
#ifdef __LINUX__
m_mutex.lock();
if(espeak_IsPlaying())
espeak_Cancel();
m_text = text;
m_isNewText = true;
m_mutex.unlock();
#endif // __LINUX__
}
void MyThread::cancel()
{
#ifdef __LINUX__
espeak_Cancel();
#endif // __LINUX__
}
void MyThread::run()
{
#ifdef __LINUX__
m_isStart = true;
while(m_isStart)
{
if(m_isNewText)
{
QString text = m_text;
QByteArray byte = text.toUtf8();
if(espeak_Synth(byte.data(),byte.length(),0,POS_CHARACTER,0,espeakCHARS_UTF8,NULL,NULL) != EE_OK)
{
qDebug()<<"espeak_Synth error";
}
m_isNewText = false;
}
else
{
msleep(50);
}
}
espeak_Terminate();
#endif // __LINUX__
}