所使用的DLL: http://www.cppblog.com/Files/biao/TTSSpeaker.dll.zip
/////////////////////////////////////////////////////////////////////////////////////////////////////
// TTSSpeaker.cpp: Qt
/////////////////////////////////////////////////////////////////////////////////////////////////////
// 调用sapi.dll, 使用里面的三个函数来初始化, 释放资源, 发音函数
// 在VS中使用TTSSpeaker.cpp生成DLL文件. 因为如果在QtCreator中使用的话, 有可能自带的mingw的不完全, 而找不到
// 某些结构的定义而出错题.
#include "stdafx.h"
#include <sapi.h>
#ifdef _MANAGED
#pragma managed(push, off)
#endif
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
) {
return TRUE;
}
ISpVoice *pVoice;
HRESULT hr;
extern "C" __declspec(dllexport) bool initialize() {
pVoice = NULL;
if (FAILED(::CoInitialize(NULL))) {
return false;
}
hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&pVoice);
return true;
}
extern "C" __declspec(dllexport) void release() {
if (SUCCEEDED(hr)) {
pVoice->Release();
pVoice = NULL;
}
::CoUninitialize();
}
extern "C" __declspec(dllexport) void speak(char *szU8) {
if (SUCCEEDED(hr)) {
int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);
wchar_t* wszString = new wchar_t[wcsLen + 1];
::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);
wszString[wcsLen] = '\0';
hr = pVoice->Speak(wszString, 0, NULL);
}
}
#ifdef _MANAGED
#pragma managed(pop)
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Speaker.h: Qt
//////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef SPEAKER_H
#define SPEAKER_H
#include <memory>
#include <QThread>
#include <QString>
class Speaker;
class TtsLoader;
class TtsRemover;
class SpeakThread;
// 对应sapi.dll里的三个TTS函数, 从DLL中得到的
typedef bool (*InitializeFunc)();
typedef void (*ReleaseFunc)();
typedef void (*SpeakFunc)(char *szU8);
/**
* 管理TTS的类, 对TTS进行加载, 释放TTS占用的资源, 调用TTS进行文本发音.
*/
class Speaker : public QObject {
Q_OBJECT
public:
static Speaker& getInstance();
void setSpeakEnabled(bool enabled);
bool isSpeakEnabled() const;
bool isTtsLoaded() const;
void speak(const QString &str); // 启动发音线程
void initializeTts(); // 启动TTS加载线程
void releaseTts(); // 启动释放TTS资源的线程
void terminateAllThreads();
protected:
void loadTts(); // 真正加载TTS的函数
void removeTts(); // 真正释放TTS占用资源的函数
void speakString(const QString &str); // 真正发音的函数
private slots:
void completeLoadTts(); // TTS加载线程结束的处理糟函数
void completeRemoveTts(); // 释放TTS资源线程结束的处理糟函数
private:
Speaker();
~Speaker();
Speaker(const Speaker &other);
Speaker& operator=(const Speaker &other);
// TTS 初始化, 释放资源, 发音函数
InitializeFunc initializeFunc;
ReleaseFunc releaseFunc;
SpeakFunc speakFunc;
bool ttsLoaded; // TTS 加载成功
bool speakEnabled; // 启用语音功能
friend class TtsLoader;
TtsLoader *ttsLoader; // TTS 加载线程
friend class TtsRemover;
TtsRemover *ttsRemover;
friend class SpeakThread;
SpeakThread *speakThread; // 发音线程
friend class std::auto_ptr<Speaker>;
static std::auto_ptr<Speaker> instance;
};
///////////////////////////////////////////////////////////////////////////////////
// 加载TTS的线程
// 如果不使用线程来加载, 在加载的时候就会感觉到卡
class TtsLoader : public QThread {
public:
TtsLoader(Speaker *speaker);
virtual void run();
void stop();
private:
Speaker *speaker;
volatile bool stopped;
};
///////////////////////////////////////////////////////////////////////////////////
// 释放TTS资源的线程
class TtsRemover : public QThread {
public:
TtsRemover(Speaker *speaker);
void stop();
protected:
virtual void run();
private:
Speaker *speaker;
volatile bool stopped;
};
///////////////////////////////////////////////////////////////////////////////////
// TTS发音线程,
class SpeakThread : public QThread {
public:
SpeakThread(Speaker *speaker);
void stop();
void setSpeakingString(const QString &speakingString);
protected:
virtual void run();
private:
Speaker *speaker;
volatile bool stopped;
QString speakingString;
};
#endif // SPEAKER_H
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Speaker.cpp: Qt
//////////////////////////////////////////////////////////////////////////////////////////////////////
#include "Speaker.h"
#include <QLibrary>
#include <qDebug>
std::auto_ptr<Speaker> Speaker::instance;
Speaker::Speaker() {
ttsLoaded = false;
speakEnabled = true;
// Threads
ttsLoader = 0;
ttsRemover = 0;
speakThread = 0;
// TTS functions
initializeFunc = 0;
releaseFunc = 0;
speakFunc = 0;
}
Speaker::~Speaker() {
terminateAllThreads();
delete ttsLoader;
delete ttsRemover;
delete speakThread;
//这里释放不用线程,而是直接释放
if (ttsLoaded) {
removeTts();
}
}
Speaker& Speaker::getInstance() {
if (instance.get() == 0) {
instance.reset(new Speaker());
}
return *(instance.get());
}
void Speaker::setSpeakEnabled(bool enabled) {
speakEnabled = enabled;
}
bool Speaker::isSpeakEnabled() const {
return speakEnabled;
}
bool Speaker::isTtsLoaded() const {
return ttsLoaded;
}
void Speaker::speak(const QString &str) {
if (ttsLoaded && speakEnabled && (0 != speakThread)) {
if (speakThread->isRunning()) {
qDebug() << "speakThread is running";
speakThread->stop();
}
qDebug() << "Speaking: " << str;
speakString(""); // 不加这一句, 不知道为什么不行
speakThread->setSpeakingString(str);
speakThread->start();
}
}
void Speaker::speakString(const QString &str) {
if (speakFunc != 0) {
speakFunc(str.toUtf8().data());
}
}
void Speaker::initializeTts() {
// If tts is loaded, don't need to load again.
if (ttsLoaded) {
return;
}
// 启动加载线程
// If ttsLoader is not euqal 0, that means ttsLoader is running.
// Because after ttsLoader is finished, it will be removed.
if (ttsLoader == 0) {
ttsLoader = new TtsLoader(this);
connect(ttsLoader, SIGNAL(finished()), this, SLOT(completeLoadTts()));
ttsLoader->start();
}
}
void Speaker::releaseTts() {
// If tts is not loaded, removing process is not needed.
if (!ttsLoaded) {
return;
}
if (0 == ttsRemover) {
ttsRemover = new TtsRemover(this);
connect(ttsRemover, SIGNAL(finished()), this, SLOT(completeRemoveTts()));
ttsRemover->start();
}
}
void Speaker::loadTts() {
// 如果TTS引擎已经加载了, 就不需要再次加载
if (ttsLoaded) {
return;
}
QLibrary lib("TTSSpeaker");
if (lib.load()) {
initializeFunc = (InitializeFunc)lib.resolve("initialize");
releaseFunc = (ReleaseFunc)lib.resolve("release");
speakFunc = (SpeakFunc)lib.resolve("speak");
if (initializeFunc && releaseFunc && speakFunc) {
initializeFunc();
ttsLoaded = true;
qDebug() << "TTS is loaded.";
// When tts is loaded, initialize the speak thread.
speakThread = new SpeakThread(this);
speak(""); // 加载完后说一下,准备好,因为第一次说都会很慢
}
} else {
qDebug() << lib.errorString();
}
}
void Speaker::removeTts() {
// If tts is not loaded, removing process is not needed.
if (!ttsLoaded) {
return;
}
releaseFunc();
ttsLoaded = false;
initializeFunc = 0;
releaseFunc = 0;
speakFunc = 0;
qDebug() << "TTS is removed.";
// After tts is removed, speak thread is also need to be removed.
if (speakThread != 0) {
speakThread->terminate();
}
delete speakThread;
speakThread = 0;
}
/**
* After the process that loads tts is completed, the load thread is not needed, then remove it.
*/
void Speaker::completeLoadTts() {
delete ttsLoader;
ttsLoader = 0;
}
/**
* After the process that removes tts is completed, the remove thread is not needed, then remove it.
*/
void Speaker::completeRemoveTts() {
delete ttsRemover;
ttsRemover = 0;
}
/**
* Terminated all threads.
*/
void Speaker::terminateAllThreads() {
if (ttsLoader != 0) {
ttsLoader->terminate();
}
if (ttsRemover != 0) {
ttsRemover->terminate();
}
if (speakThread != 0) {
speakThread->terminate();
}
}
///////////////////////////////////////////////////////////////////////////////////
TtsLoader::TtsLoader(Speaker *speaker) {
this->speaker = speaker;
stopped = false;
}
void TtsLoader::run() {
if (!stopped) {
speaker->loadTts();
}
stopped = false; //
}
void TtsLoader::stop() {
stopped = true;
}
///////////////////////////////////////////////////////////////////////////////////
TtsRemover::TtsRemover(Speaker *speaker) {
this->speaker = speaker;
stopped = false;
}
void TtsRemover::run() {
if (!stopped) {
speaker->removeTts();
}
stopped = false;
}
void TtsRemover::stop() {
stopped = true;
}
///////////////////////////////////////////////////////////////////////////////////
SpeakThread::SpeakThread(Speaker *speaker) {
this->speaker = speaker;
stopped = false;
}
void SpeakThread::run() {
if (!stopped) {
speaker->speakString(speakingString);
}
stopped = false;
}
void SpeakThread::stop() {
stopped = true;
}
void SpeakThread::setSpeakingString(const QString &speakingString) {
this->speakingString = speakingString;
}
/*
很奇怪的问题: 如果直接使用loadTts()和removeTts()函数来初始化和释放TTS的资源, 就不会存在内存泄漏问题. 但是如果使用线程来处理, 即调用initializeTts(), releaseTts(), 在线程中调用loadTts(), removeTts()来初始化和释放TTS的资源, 就会存在内存泄漏. 泄漏的内存大小为初始化一次TTS的内存, 没发现会叠加.
文本发音的使用过程: 由于使用的是单态模式, 所以在使用发音函数speak前, 初始化TTS一次就可以了, 而TTS的资源释放是自动释放的, 当然也可以手动释放.
1. Speaker::getInstance().initializeTts(); // 最好程序启动的时候调用.
2. Speaker::getInstance().speak(str);
.................可以在程序的任何地方直接调用.(如果TTS没有被加载, 或者不启用语音功能, 这时调用了不起任何作用)
[不需要自己手动释放TTS的资源, 因为使用了智能指针实现单态模式]
*/