木馬編程DIY之文本語音 冷風 德州科技職業學院 在黑防的第2期上天涯衰草(是電腦報上駭客營的老大吧呵呵)對“壽鼠”遠端控制程式進行了介紹,其中有一個功能就是“跟對方講英語”感覺很有意思,就試著自己做了一個發現也不是太難,效果1所示程式在VC6.0+WIN2000下編寫分為服務端(SDK)和用戶端兩個檔案,在其它機器的運行服務端後,就可以通過用戶端讓它“說話”了,如果目標地址為空白的話,會在自己的電腦上朗讀。 關於文本語音 壽鼠的朗讀功能就是通過文本語音實現的,文本語音又稱為TTS(Text-to-Speech)它的作用就是把文本轉化為語音,如果我們想開發這種程式,需要使用軟提供的軟體開發包Microsoft Speech SDK,目前使用最為廣泛的版本是5.1,這個包大小在65M左右在天空,軍華都可以下載到,它本身帶有十分詳細的開發資料,和可執行程式2圖3所示,參考說明文檔提供的例子我們可以很方便的做出自己的程式了。 準備工作 在開始前應該把Microsoft Speech SDK安裝好,裝好後進入安裝目錄把INCLUDE和LIB目錄中的標頭檔與庫檔案複製到VC的開發目錄裡面我這裡是C:/Microsoft Visual Studio/VC98/Include跟LIB目錄,當然你也可以在VC中設定路徑(我喜歡直接複製過去)現在就可以編寫程式了 服務端編寫 服務端運行後就一直在5555連接埠監聽,收到資料就讀出來,實現代碼如下 #include <sapi.h> //TTS所需要的標頭檔
#include <windows.h>
#include <winsock.h>
#pragma comment (lib,"ws2_32") //載入庫函數
const int buffer_len=2048;
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WSADATA wsadata;
SOCKET server;
SOCKET client;
SOCKADDR_IN serveraddr;
SOCKADDR_IN clientaddr;
int port=5555;
char buffer[buffer_len];
WCHAR wbuffer[buffer_len];
memset(wbuffer,0,sizeof(WCHAR)*buffer_len);
memset( buffer,0, sizeof(char)*buffer_len);
WORD ver=MAKEWORD(2,2); //判斷winsock版本
WSAStartup(ver,&wsadata); //初始SOCKET
server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(port);
serveraddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
bind(server,(SOCKADDR*)&serveraddr,sizeof(serveraddr));
listen(server,5);
int len=sizeof(clientaddr);
while(1)//迴圈接受串連
{
client=accept(server,(sockaddr *)&clientaddr,&len);
while(1)//迴圈接受資料
{
if(recv(client,buffer,buffer_len,0)!=0)//有資料則播放
{
memset(wbuffer,0,sizeof(WCHAR)*buffer_len);
//必須先轉換成寬字元
MultiByteToWideChar( CP_ACP,
MB_PRECOMPOSED, // character-type options
buffer, // address of string to map
buffer_len, // number of bytes in string
wbuffer, // address of wide-character buffer
buffer_len // size of buffer
);
memset( buffer,0, sizeof(char)*buffer_len);
ISpVoice * pVoice = NULL;
if (FAILED(CoInitialize(NULL)))//處始化COM介面
{
MessageBox(NULL,"ERROR","Error to intiliaze COM",0);
}
//擷取ISpVoice介面
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL,CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
if( SUCCEEDED( hr ) )
{
hr = pVoice->Speak(wbuffer, 0, NULL); //開始朗讀
pVoice->Release();//完成後釋放聲音對象
pVoice = NULL;
}
CoUninitialize();//釋放
}
else
break;//接受資料為0退出
}
}
closesocket(server);
closesocket(client);
WSACleanup();
return 0;
}
實現代碼挺簡單的,需要注意的是在pVoice->Speak(const WCHAR *pwcs,DWORD dwFlags,ULONG *pulStreamNumber);中pwcs是WCHAR類型的也就是Unicode編碼所以從網路收到資料後要進行Unicode轉換,編碼轉換可以用MultiByteToWideChar API來實現,這是一個很好用的API看看上文的代碼就會用了。 用戶端實現 用戶端實現同樣很簡單,所以我就不再浪費感情了呵呵 void CMyDlg::OnOK()
...{
UpdateData();
if(m_addr.IsEmpty())//地址為空白則本地播放
...{
//實現代碼與服務端相差無幾
}
else//把資料發送至目標
...{
WSADATA wsadata;
SOCKET client;
SOCKADDR_IN serveraddr;
int port=5555;
WORD ver=MAKEWORD(2,2); //判斷winsock版本
WSAStartup(ver,&wsadata); //初始SOCKET
client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(port);
serveraddr.sin_addr.S_un.S_addr=inet_addr(m_addr.LockBuffer());//m_addr為接受地址的文本控制項
connect(client,(SOCKADDR*)&serveraddr,sizeof(serveraddr));
send(client,m_text.LockBuffer(),m_text.GetLength(),0);//m_text為接受內容的文本控制項
closesocket(client);
WSACleanup();
}
}
需要的話,還可以擴充一下,讓它讀中文日文什麼的,不過你還得專門去下載發聲補丁......而且也可以指定男音或女音朗讀不過我感覺女音比較好聽嘿嘿,本文實現的功能比較簡單,如果想編寫功能強大的程式還是請參考Microsoft Speech SDK的協助文檔,