逛奔的蜗牛

我不聪明,但我会很努力

   ::  :: 新随笔 ::  ::  :: 管理 ::

所使用的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), NULL0);

wchar_t* wszString = new wchar_t[wcsLen + 1];

::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);

wszString[wcsLen] = '\0';

hr = pVoice->Speak(wszString, 0NULL);

}

}


#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的资源因为使用了智能指针实现单态模式]

*/

posted on 2009-09-02 10:35 逛奔的蜗牛 阅读(5620) 评论(29)  编辑 收藏 引用 所属分类: C/C++Qt

评论

# re: Qt: Qt调用windows TTS 2009-09-03 10:30 暗金装备
protected:

void loadTts(); // 真正加载TTS的函数

void removeTts(); // 真正释放TTS占用资源的函数

void speakString(const QString &str); // 真正发音的函数

修改成public的,加载的时候使用多线程加载(其实加载是很快的,只不过第一次发音的时候会很慢,以后就好了),释放TTS资源的时候不使用多线程,直接使用removeTts(),这样可以正常释放资源,原因不明。mainwindow退出前,使用removeTts()先释放一下TTS的资源,防止程序异常退出(即使不会影响数据,但出个错误窗口总不友好)  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 16:04 陈景航
我已经用qt成功使用你的程序,通过tts能够读出文本的英文,但是对于是中文的文本,你的TTSSpeak.dll好像并不能够读出中文,请问什么?  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 16:07 陈景航
我已经用qt成功使用你的程序,通过tts能够读出文本的英文,但是对于是中文的文本,你的TTSSpeak.dll好像并不能够读出中文,请问什么?我已经成功安装tts的中文包,并且运行speech sdk中的sample是可以播放中文的。  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 16:08 暗金装备
因为你没有安装中文的TTS引擎,安装上就要以使用中文发音了。微软的网站上有下载,但是效果非常的差,或者想要更好的效果,可以使用NeoSpeech的中文语音库。  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 16:12 暗金装备
忘了说一件事,这个Speaker中使用的中文,我使用的是Utf-8的编码,所以要能正常发音,你的中文字符串也应该必须是UTF-8的。  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 16:29 陈景航
你好,我是用utf-8的codec了,打印都没有问题,我已经安装了SpeechSDK51LangPack , 请问这个中文的tts 引擎吗?  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 16:43 暗金装备
1. 具体是不是我忘了,只要你的控制面板里的语音中能读出中文,说明中文语音引擎就安装好了。

2. 使用UTF-8编码,打印不一定能说明问题。如果你不确定是否使用了UTF-8编码,最好是使用某些工具,打开你的源文件,查看一下是什么编码。不过,在windows下使用QtCreator创建的工程,默认的编码并不是UTF-8的,而是GB2312,因为Windows默认的编码并不是UTF-8,而Linux,Mac系统的默认编码就是UTF-8。

推荐你重新使用QtCreator创建一个工程,工程的编码选择为UTF-8,再把QApplication::codecForTr, QApplication::codecForCString, QApplication::codecForLocal等都设置成UTF-8的codec,这么,就能保证可以使用中文发音了。

如果你使用的是VS等其他工具来编写Qt程序,怎么创建UTF-8的工程,我不清楚怎么做,因为我一直使用的是QtCreator。但如果使用的是Eclipse的话,这个在工程属性里可以调,也是非常简单的。  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 16:43 陈景航
我用speech sdk 所提供的smaple web 进行中文播放,都是可以的,我觉得我已经安装了中文tts引擎,但是为什么我用utf8字符串输入的时候,没有声音播放出来  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 17:11 陈景航
我按照你上述的流程新建工程为utf8模式,并且设置
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

并通过speak(tr("我很高兴"));
但是发不出声音,如果用speak(tr("i am very happy"));就可以得到英文  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 17:16 暗金装备
呵呵,那我就不清楚了,在我这里,我创建过好几个工程,都使用上面的代码,没有任何问题。  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 17:17 暗金装备
还有,首先你得在控制面板的语音里设置默认的语音引擎为中文的语音引擎,不知道这一步你做了没有。  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 17:18 暗金装备
因为这个调用的就是默认的语音引擎来进行发音的,如果默认的语音引擎是英文的,那只能发英文了。  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2009-10-23 17:23 陈景航
你有没有这个项目,能不能发送给我一个,谢谢,我看看在我平台上能否发出中文
jinghang80@yahoo.com.cn


无限感谢!  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2010-07-17 23:29 学QT的人
最近想研究语音识别,能把你的源码发给我吗?
xcyddr@yahoo.com.cn  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2011-07-25 15:29 索坤
博主,最近想研究语音识别,能把你的源码发给我吗?

724167815@qq.com
  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2011-07-25 15:55 索坤
博主,最近在研究TTS,在Qt平台上面的,能否把你的源码或者工程发一份给我学习一下呢,非常感谢,suokun24@126.com  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2011-11-24 15:19 tear
能给我一份源代码嘛?我的邮箱604922959@qq.com,谢谢,我想学习一下  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2011-11-26 12:53 逛奔的蜗牛
@tear
这个是以前写的,上面就是所有的源码了
  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2012-07-14 14:31 smallcai
英文的可以读出来吗?为什么我的不可以啊  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2012-07-14 14:31 smallcai
英文直接是拼音啊,没有单词那个读出来的  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2012-07-14 15:51 smallcai
中英文怎么互相切换啊   回复  更多评论
  

# re: Qt: Qt调用windows TTS 2012-07-14 16:52 yz008
可以弱弱的问下,主函数应该怎么调用么?在QT中  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2012-07-14 16:56 yz008
不好意思,我没看完楼主的博文,知道怎么调用了,可是感觉音质好差哟!  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2012-07-19 14:48 11
请问怎么调用啊!  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2012-07-19 22:52 hy2012
回复中提到的方法都试过了,但是还是发不了中文,求博主的源代码,我想想看看自己到底是哪错了,我的邮箱:huyang19870102@163.com,谢谢!  回复  更多评论
  

# re: Qt: Qt调用windows TTS[未登录] 2012-07-24 22:18 jason
撸主这个真不错,受教了  回复  更多评论
  

# re: Qt: Qt调用windows TTS 2012-08-09 20:36 zhqi
您好,我想问一下这里为是么要采用单态模式呢?主要有哪方面的考虑呢?  回复  更多评论
  

# re: Qt: Qt调用windows TTS[未登录] 2013-06-14 11:57 石头
#include <QtCore>
#include <QCoreApplication>
#include "speaker.h"

void SetLocalization() {
QTextCodec* codec = QTextCodec::codecForName("GB2312");
QTextCodec::setCodecForLocale(codec);
QTextCodec::setCodecForCStrings(codec);
QTextCodec::setCodecForTr(codec);
}

int main(int argc, char *argv[]) {
SetLocalization();
QCoreApplication a(argc, argv);
Speaker::getInstance().initializeTts();
Speaker::getInstance().speak("main4");
system("pause");
return 0; //a.exec();
}

无效,没有声音,是不是我错了  回复  更多评论
  

# re: Qt: Qt调用windows TTS[未登录] 2013-06-15 00:13 石头
休眠1500主线程来解决,让发音线程取得优先权  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理