所使用的DLL: http://m.shnenglu.com/Files/biao/TTSSpeaker.dll.zip
/////////////////////////////////////////////////////////////////////////////////////////////////////
// TTSSpeaker.cpp: Qt
/////////////////////////////////////////////////////////////////////////////////////////////////////
// 調(diào)用sapi.dll, 使用里面的三個(gè)函數(shù)來(lái)初始化, 釋放資源, 發(fā)音函數(shù)
// 在VS中使用TTSSpeaker.cpp生成DLL文件. 因?yàn)槿绻?/span>QtCreator中使用的話, 有可能自帶的mingw的不完全, 而找不到
// 某些結(jié)構(gòu)的定義而出錯(cuò)題.
#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;
// 對(duì)應(yīng)sapi.dll里的三個(gè)TTS函數(shù), 從DLL中得到的
typedef bool (*InitializeFunc)();
typedef void (*ReleaseFunc)();
typedef void (*SpeakFunc)(char *szU8);
/**
* 管理TTS的類, 對(duì)TTS進(jìn)行加載, 釋放TTS占用的資源, 調(diào)用TTS進(jìn)行文本發(fā)音.
*/
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); // 啟動(dòng)發(fā)音線程
void initializeTts(); // 啟動(dòng)TTS加載線程
void releaseTts(); // 啟動(dòng)釋放TTS資源的線程
void terminateAllThreads();
protected:
void loadTts(); // 真正加載TTS的函數(shù)
void removeTts(); // 真正釋放TTS占用資源的函數(shù)
void speakString(const QString &str); // 真正發(fā)音的函數(shù)
private slots:
void completeLoadTts(); // TTS加載線程結(jié)束的處理糟函數(shù)
void completeRemoveTts(); // 釋放TTS資源線程結(jié)束的處理糟函數(shù)
private:
Speaker();
~Speaker();
Speaker(const Speaker &other);
Speaker& operator=(const Speaker &other);
// TTS 初始化, 釋放資源, 發(fā)音函數(shù)
InitializeFunc initializeFunc;
ReleaseFunc releaseFunc;
SpeakFunc speakFunc;
bool ttsLoaded; // TTS 加載成功
bool speakEnabled; // 啟用語(yǔ)音功能
friend class TtsLoader;
TtsLoader *ttsLoader; // TTS 加載線程
friend class TtsRemover;
TtsRemover *ttsRemover;
friend class SpeakThread;
SpeakThread *speakThread; // 發(fā)音線程
friend class std::auto_ptr<Speaker>;
static std::auto_ptr<Speaker> instance;
};
///////////////////////////////////////////////////////////////////////////////////
// 加載TTS的線程
// 如果不使用線程來(lái)加載, 在加載的時(shí)候就會(huì)感覺(jué)到卡
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發(fā)音線程,
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;
}
// 啟動(dòng)加載線程
// 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引擎已經(jīng)加載了, 就不需要再次加載
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(""); // 加載完后說(shuō)一下,準(zhǔn)備好,因?yàn)榈谝淮握f(shuō)都會(huì)很慢
}
} 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;
}
/*
很奇怪的問(wèn)題: 如果直接使用loadTts()和removeTts()函數(shù)來(lái)初始化和釋放TTS的資源, 就不會(huì)存在內(nèi)存泄漏問(wèn)題. 但是如果使用線程來(lái)處理, 即調(diào)用initializeTts(), releaseTts(), 在線程中調(diào)用loadTts(), removeTts()來(lái)初始化和釋放TTS的資源, 就會(huì)存在內(nèi)存泄漏. 泄漏的內(nèi)存大小為初始化一次TTS的內(nèi)存, 沒(méi)發(fā)現(xiàn)會(huì)疊加.
文本發(fā)音的使用過(guò)程: 由于使用的是單態(tài)模式, 所以在使用發(fā)音函數(shù)speak前, 初始化TTS一次就可以了, 而TTS的資源釋放是自動(dòng)釋放的, 當(dāng)然也可以手動(dòng)釋放.
1. Speaker::getInstance().initializeTts(); // 最好程序啟動(dòng)的時(shí)候調(diào)用.
2. Speaker::getInstance().speak(str);
.................可以在程序的任何地方直接調(diào)用.(如果TTS沒(méi)有被加載, 或者不啟用語(yǔ)音功能, 這時(shí)調(diào)用了不起任何作用)
[不需要自己手動(dòng)釋放TTS的資源, 因?yàn)槭褂昧酥悄苤羔槍?shí)現(xiàn)單態(tài)模式]
*/