主页C++ Builder 资料C++ Builder 编程技巧多媒体处理TTS 文字转语音
C++ Builder 串口控件
C++ Builder 编程技巧
字符串及文字处理
多媒体处理
 • TTS 文字转语音
 • 使用 LAV 解码器的播放器
图片处理
文件处理
界面处理
C++ Builder 操作指南
C++ Builder 参考手册
网友留言/技术支持
TTS 文字转语音 - 多媒体处理
 • 软件介绍 - TTSDemo
 • 源码介绍 - C++ Builder 源码
   · TVictorTTS 类介绍
   · TVictorTTS 类源码
   · TTSDemo 源码介绍
 • TTSDemo 软件下载 + 源码下载

软件介绍

TTSDemo 可以朗读文字,或者把文字转为 wav 声音文件,

 • 包括 Win32 和 Win64 两个版本。兼容的操作系统:Windows XP, Windows 7, Windows Vista, Windows 8, Windows 10。
 • 把文字复制 → 粘贴在软件的文本框里面,也可以在文本框里面输入和修改,可以把这些文字读出来。
 • 支持把文本框里面的文字转为 wav 声音文件。

进度显示朗读时,会随着朗读的进度加亮正在朗读的字词,自动向后翻阅显示的内容;
选择语音库支持 Windows 自带的语音库、NeoSpeech语音库、科大讯飞语音库等所有兼容 Windows TTS 引擎的语音库;
朗读速度调整朗读速度的快慢,+0 为正常速度,-10 为最慢,+10 为最快;
朗读音量调整语音朗读的音量,100% 为标准音量,即音量最大,0% 为音量最小,即没有声音;
最终听到的音量受操作系统音量的控制,即最终音量 = 朗读音量 × 操作系统媒体播放音量;
朗读内容在这个文本框里面贴上复制过来的文字,也可以输入和编辑文字,软件会对这些文字进行朗读或转 wav 声音文件等处理;
朗读从头开始朗读;
停止停止朗读;
暂停暂时停止朗读,可以点击继续接着朗读;
继续继续暂停的朗读;
光标处开始读从文本框里面光标位置开始朗读,如果想暂停却错点了停止,可以从光标位置继续朗读,也可以随意点击个位置朗读;
存为 .wav把文本框里面的内容转为 wav 声音文件,可以用媒体播放器进行播放,或者进行其他音频处理。

   这里是 Windows 10 自带的语音库,支持 NeoSpeech语音库、科大讯飞语音库等所有兼容 Windows TTS 引擎的语音库

   

 

软件的 ini 配置文件保存在当前用户的数据文件夹里面,即:

C:\Users\用户名\AppData\Roaming\Victor Chen\TTSDemo

文件夹。

 

源码介绍

TTSDemo / TVictorTTS: 兼容 C++ Builder 2010 ~ C++ Builder 10 (CX) 版本

 

TVictorTTS 类介绍

属性 类型 说明
TokenID String 语音库的 ID,使用这个 ID 的语音库来播放语音。
 • 可以从 TokenList 里面选取一个 TokenID,即 TokenList[i].TokenID
 • 也可以用操作系统默认的语音库 ID ,即 DefTokenID
DefTokenID String 操作系统默认的语音库 ID
TokenList TVictorTokenList 系统里面安装的语音库列表。
 • TTS.TokenList.size() 为语音库的个数
 • TokenList[i].TokenID 为第 i 个语音库的 ID
 • TokenList[i].TokenDesc 为第 i 个语音库的描述
Token ISpObjectToken TTS 引擎 ISpObjectToken 接口
Voice ISpVoice TTS 引擎 ISpVoice 接口
Rate long 播放速度,范围为 -10 ~ 10, 默认值 0 为标准速度,-10 为最慢,+10 为最快
Volume unsigned short 播放音量,范围为 0 ~ 100,默认值 100 为最大音量,0 为音量最小,即没有声音
最终听到的音量受操作系统音量的控制,即最终音量 = 朗读音量% × 操作系统媒体播放音量
Speaking bool 是否正在播放,true 为正在播放状态,false 为停止状态
Paused bool 是否暂停播放,true 为播放处于暂停状态,false 为不在暂停状态

方法 函数原型 说明
TVictorTTS TVictorTTS(); 构造函数
TVictorTTS virtual ~TVictorTTS(); 析构函数
Speak bool Speak(String s); 朗读文字 s,异步方式,函数立即返回,不等待播放结束。
成功返回 true,失败返回 false
SpeakSync bool SpeakSync(String s); 朗读文字 s,同步方式,函数要等待播放语音完成才返回
成功返回 true,失败返回 false
Skip unsigned long Skip(long n); 向前或向后跳过 n 个播放的内容,返回实际跳过的内容个数
Stop bool Stop(void); 停止播放语音,成功返回 true,失败返回 false
SaveWav bool SaveWav(String sText, String sFile); 把文字 sText 的内容转成 wav 声音文件,文件名为 sFile
成功返回 true,失败返回 false
GetSpeakingWord bool GetSpeakingWord(unsigned long &ulPos, unsigned long &ulLen); 正在播放的单词,ulPos 为这个单词是文字当中从第几个字符开始的位置,ulLen 为这个单词的字符个数,汉语也是按照单词播放的,一个单词可以包含一个或多个汉字。成功返回 true,失败返回 false
RefreshTokenList int RefreshTokenList(void); 刷新 TokenList 属性的语音库列表,重新从系统里面读出安装的语音列表。返回列表里面语音库的个数。

这个类的使用很简单,例如播放 Memo1 里面的文字:

TVictorTTS TTS;
TTS.TokenID = TTS.DefTokenID; // 使用操作系统默认的语音库
// TTS.TokenID = TTS.TokenList[i].TokenID; // 使用第 i 个语音库
TTS.Speak(Memo1->Text); // 播放 Memo1->Text 朗读的内容

系统就会在后台播放朗读文字的内容。

如果 TTS.Speaking 的值是 true 就是正在播放,false 就是播放完成了

TTS.Stop();

为停止播放。

 

TVictorTTS 类源码

VictorTTS.h
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\
|        VictorTTS, copyright © Victor Chen, http://www.cppfans.com/        |
\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
#ifndef VictorTTSH
#define VictorTTSH
//---------------------------------------------------------------------------
#include <vcl.h>
#include <sapi.h>
#include <vector>
//---------------------------------------------------------------------------
namespace VictorTTS {
//---------------------------------------------------------------------------
class TVictorTokenItem
{
public:
  String TokenID;
  String TokenDesc;

  TVictorTokenItem &operator =(const TVictorTokenItem &t){ TokenID=t.TokenID; TokenDesc=t.TokenDesc; return *this; }

  TVictorTokenItem(){}
  TVictorTokenItem(const TVictorTokenItem &t){ TokenID=t.TokenID; TokenDesc=t.TokenDesc; }
};
typedef std::vector<TVictorTokenItem>TVictorTokenList;
//---------------------------------------------------------------------------
class TVictorTTS
{
public:
  __property String           TokenID     = { read = _GetTokenID, write = _SetTokenID };
  __property String           DefTokenID  = { read = _wsDefTokenID                    };
  __property TVictorTokenList TokenList   = { read = _vtlTokenList                    };
  __property ISpObjectToken  *Token       = { read = _pToken                          };
  __property ISpVoice        *Voice       = { read = _pVoice                          };
  __property long             Rate        = { read = _GetRate   , write = _SetRate    };
  __property unsigned short   Volume      = { read = _GetVolume , write = _SetVolume  };
  __property bool             Speaking    = { read = _GetSpeaking                     };
  __property bool             Paused      = { read = _bPaused   , write = _SetPaused  };

  bool SaveWav(String sText, String sFile);
  bool Speak(String);
  bool SpeakSync(String);
  unsigned long Skip(long);
  bool Stop(void);
  int RefreshTokenList(void);
  bool GetSpeakingWord(unsigned long &ulPos, unsigned long &ulLen);

  TVictorTTS();
  virtual ~TVictorTTS();
private:
  ISpVoice *_pVoice;
  ISpObjectToken *_pToken;
  String _wsDefTokenID;
  TVictorTokenList _vtlTokenList;
  HRESULT _hr;
  bool _bPaused;
  String _GetTokenID(void)const;
  void _SetTokenID(String);
  long _GetRate(void)const;
  void _SetRate(long);
  unsigned short _GetVolume(void)const;
  void _SetVolume(unsigned short);
  bool _GetSpeaking(void)const;
  void _SetPaused(bool);
};
//---------------------------------------------------------------------------
} using namespace VictorTTS;
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------

VictorTTS.cpp
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\
|        VictorTTS, copyright © Victor Chen, http://www.cppfans.com/        |
\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
#pragma hdrstop
#include "VictorTTS.h"
//---------------------------------------------------------------------------
namespace VictorTTS {
//---------------------------------------------------------------------------
TVictorTTS::TVictorTTS()
{
  _pVoice = NULL;
  _pToken = NULL;

  _bPaused = false;

  if(SUCCEEDED(_hr=CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&_pVoice)))
   {
     if(SUCCEEDED(_hr=CoCreateInstance(CLSID_SpObjectToken, NULL, CLSCTX_ALL, IID_ISpObjectToken, (void**)&_pToken)))
       _hr=_pVoice->SetVoice(_pToken);
   }
  RefreshTokenList();
}
//---------------------------------------------------------------------------
TVictorTTS::~TVictorTTS()
{
  if(_pToken)
   {
     _pToken->Release();
     _pToken = NULL;
   }
  if(_pVoice)
   {
     _pVoice->Release();
     _pVoice = NULL;
   }
}
//---------------------------------------------------------------------------
String TVictorTTS::_GetTokenID(void)const
{
  String ws;
  if(_pToken)
   {
     wchar_t *lpws = NULL;
     if(SUCCEEDED(_pToken->GetId(&lpws)))
      {
        ws = lpws;
        CoTaskMemFree(lpws);
      }
   }
  return ws;
}
//---------------------------------------------------------------------------
void TVictorTTS::_SetTokenID(String id)
{
  if(_GetTokenID()!=id)
   {
     if(_pToken)
      {
        _pToken->Release();
        _pToken = NULL;
        if(SUCCEEDED(_hr=CoCreateInstance(CLSID_SpObjectToken, NULL, CLSCTX_ALL, IID_ISpObjectToken, (void **)&_pToken)))
          _hr=_pVoice->SetVoice(_pToken);
        _pToken->SetId(SPCAT_VOICES, id.c_str(), false);
      }
   }
}
//---------------------------------------------------------------------------
long TVictorTTS::_GetRate(void)const
{
  if(_pVoice)
   {
     long rt = 0;
     _pVoice->GetRate(&rt);
     return rt;
   }
  return 0;
}
//---------------------------------------------------------------------------
void TVictorTTS::_SetRate(long rt)
{
  if(_pVoice)_pVoice->SetRate(rt);
}
//---------------------------------------------------------------------------
unsigned short TVictorTTS::_GetVolume(void)const
{
  if(_pVoice)
   {
     unsigned short v = 100;
     _pVoice->GetVolume(&v);
     return v;
   }
  return 100;
}
//---------------------------------------------------------------------------
void TVictorTTS::_SetVolume(unsigned short v)
{
  if(_pVoice)_pVoice->SetVolume(v);
}
//---------------------------------------------------------------------------
bool TVictorTTS::_GetSpeaking(void)const
{
  if(_pVoice)
    return _pVoice->WaitUntilDone(0) == S_FALSE;
  return false;
}
//---------------------------------------------------------------------------
unsigned long TVictorTTS::Skip(long n)
{
  unsigned long v = 0;
  if(_pVoice)
    _pVoice->Skip(L"SENTENCE",n,&v);
  return v;
}
//---------------------------------------------------------------------------
bool TVictorTTS::Stop(void)
{
  if(_pVoice)
    return SUCCEEDED(_pVoice->Speak(L"", SPF_PURGEBEFORESPEAK, NULL));
  return false;
}
//---------------------------------------------------------------------------
bool TVictorTTS::SaveWav(String sText, String sFile)
{
  if(_pVoice)
   {
     ISpStream *pISpStream;
     HRESULT hResult = CoCreateInstance(CLSID_SpStream,NULL,CLSCTX_INPROC_SERVER, IID_ISpStream, (void**)&pISpStream);
     if(SUCCEEDED(hResult))
      {
        ISpStreamFormat *pISpStreamFormat;
        hResult = Voice->GetOutputStream(&pISpStreamFormat);
        if(SUCCEEDED(hResult))
         {
           GUID guidFormatId;
           WAVEFORMATEX *pCoMemWaveFormatEx;
           hResult = pISpStreamFormat->GetFormat(&guidFormatId,&pCoMemWaveFormatEx);
           if(SUCCEEDED(hResult))
            {
              hResult = pISpStream->BindToFile(sFile.c_str(), SPFM_CREATE_ALWAYS, &guidFormatId, pCoMemWaveFormatEx,0);
              if(SUCCEEDED(hResult))
               {
                 Voice->SetOutput(pISpStream, TRUE);
                 Voice->Speak(sText.c_str(), SPF_DEFAULT, NULL);
                 Voice->SetOutput(NULL, TRUE);
               }
              CoTaskMemFree(pCoMemWaveFormatEx);
            }
           pISpStreamFormat->Release();
         }
        pISpStream->Release();
      }
     return SUCCEEDED(hResult);
   }
  return false;
}
//---------------------------------------------------------------------------
bool TVictorTTS::Speak(String s)
{
  if(_pVoice)
   {
     Paused = false;
     return SUCCEEDED(_pVoice->Speak(s.c_str(), SPF_ASYNC|SPF_PURGEBEFORESPEAK, NULL));
   }
  return false;
}
//---------------------------------------------------------------------------
bool TVictorTTS::SpeakSync(String s)
{
  if(_pVoice)
   {
     Paused = false;
     return SUCCEEDED(_pVoice->Speak(s.c_str(), SPF_PURGEBEFORESPEAK, NULL));
   }
  return false;
}
//---------------------------------------------------------------------------
void TVictorTTS::_SetPaused(bool b)
{
  if(_pVoice)
   {
     if(b)
      {
        if(!_bPaused)
         {
           if(SUCCEEDED(_pVoice->Pause()))
             _bPaused = true;
         }
      }
     else
      {
        if(_bPaused)
         {
           if(SUCCEEDED(_pVoice->Resume()))
             _bPaused = false;
         }
      }
   }
}
//---------------------------------------------------------------------------
bool TVictorTTS::GetSpeakingWord(unsigned long &ulPos, unsigned long &ulLen)
{
  bool bOK = false;
  if(_pVoice)
   {
     SPVOICESTATUS vs;
     if(SUCCEEDED(_pVoice->GetStatus(&vs,NULL)))
      {
        ulPos = vs.ulInputWordPos;
        ulLen = vs.ulInputWordLen;
        bOK = true;
      }
   }
  return bOK;
}
//---------------------------------------------------------------------------
int TVictorTTS::RefreshTokenList(void)
{
  _wsDefTokenID = L"";
  _vtlTokenList.clear();
  if(_pVoice)
   {
     ISpObjectTokenCategory *pCategory = NULL;
     if(SUCCEEDED(CoCreateInstance(CLSID_SpObjectTokenCategory, NULL, CLSCTX_ALL, IID_ISpObjectTokenCategory, (void**)&pCategory)))
      {
        pCategory->SetId(SPCAT_VOICES, false);
        IEnumSpObjectTokens *pEnum = NULL;
        if(SUCCEEDED(pCategory->EnumTokens(NULL, NULL, &pEnum)))
         {
           ULONG ulCount = 0;
           if(SUCCEEDED(pEnum->GetCount(&ulCount))) //Get the number of voices
            {
              for(ULONG i=0; i<ulCount; i++)
               {
                 ISpObjectToken *pVoiceToken = NULL;
                 if(SUCCEEDED(pEnum->Next(1, &pVoiceToken, NULL)))
                  {
                    TVictorTokenItem vti;
                    wchar_t *lpws = NULL;
                    if(SUCCEEDED(pVoiceToken->GetId(&lpws)))
                     {
                       vti.TokenID = lpws;
                       CoTaskMemFree(lpws);
                     }
                    lpws = NULL;
                    if(SUCCEEDED(pVoiceToken->GetStringValue(NULL, &lpws)));
                     {
                       vti.TokenDesc = lpws;
                       CoTaskMemFree(lpws);
                     }
                    _vtlTokenList.push_back(vti);
                    pVoiceToken->Release();
                  }
               }
            }
           pEnum->Release();
         }
        wchar_t *lpws = NULL;
        if(SUCCEEDED(pCategory->GetDefaultTokenId(&lpws)))
         {
           _wsDefTokenID = lpws;
           CoTaskMemFree(lpws);
         }
        pCategory->Release();
      }
   }
  return _vtlTokenList.size();
}
//---------------------------------------------------------------------------
} // namespace VictorTTS
//---------------------------------------------------------------------------

 

TTSDemo 源码介绍

Projects 文件夹:含有 C10.1 和 C2010 两个文件夹,是由 C++ Builder 10.1 Berlin 和 C++ Builder 2010 创建的项目文件。
经过测试,比较新一点版本的,例如 C++ Builder XE8,可以打开 C10.1 项目使用,
早期版本的,例如 C++ Builder XE2,可以打开 C2010 项目使用。

Release 文件夹:编译生成的 exe 文件在这里。

Source 文件夹:源程序在这个文件夹里面。

主窗口 UnitTtsMain.h / UnitTtsMain.cpp 里面的函数 说明
__fastcall TForm1::TForm1(TComponent* Owner) 主窗口的构造函数。
在 ini 文件里面读出 TokenID, 播放速度和音量
void TForm1::SetRate(long r) 设定播放速度为 r
void TForm1::SetVolume(unsigned short v) 设定播放音量为 v
void __fastcall TForm1::ComboBox1Select(TObject *Sender) 选择了语音库下拉表 ComboBox1 里面的选项
void __fastcall TForm1::TrackBarRateChange(TObject *Sender) 拖动 TrackBarRate 改变了播放速度
void __fastcall TForm1::TrackBarVolumeChange(TObject *Sender) 拖动 TrackBarVolume 改变了播放音量
void __fastcall TForm1::Timer1Timer(TObject *Sender) 定时器 Timer 事件:监视播放状态的改变
void __fastcall TForm1::ButtonSpeakClick(TObject *Sender) 点击播放按钮
void __fastcall TForm1::ButtonCaretClick(TObject *Sender) 点击从光标处开始播放按钮
void __fastcall TForm1::ButtonStopClick(TObject *Sender) 点击停止按钮
void __fastcall TForm1::ButtonPauseClick(TObject *Sender) 点击暂停按钮
void __fastcall TForm1::ButtonResumeClick(TObject *Sender) 点击继续按钮
void __fastcall TForm1::ButtonSaveWavClick(TObject *Sender) 点击保存 .wav 按钮
UnicodeString TForm1::GetIniFilePath(void) const 获取 ini 文件的路径 (文件夹位置)
UnicodeString TForm1::GetIniFileName(void) const 获取 ini 文件的文件名 (含完整路径)

TTSDemo 软件下载 + 源码下载

文件 字节数 发布时间 下载次数
TTSDemo 软件 Win32 + Win64 1,598,950 2017-04-27 3856
TTSDemo 源码 C++ Builder 2010 ~ C++ Builder 10 13,582 2017-04-27 2944
下一页:使用 LAV 解码器的播放器

C++ 爱好者 -- Victor Chen 的个人网站 www.cppfans.com 辽ICP备11016859号