软件介绍
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 软件下载 + 源码下载
|