?? api 層實現語音播放.txt
字號:
CSDN - 文檔中心 - Visual C++
標題 API 層實現語音播放 temp77(原作)
關鍵字 NO MFC , 語音播放 , waveOutWrite
昨天寫了語音錄制(見 http://www.csdn.net/develop/Read_Article.asp?Id=17627 ),現在繼續講語音播放。 要用到 .wav 文件頭內容部分的請參看上一文《語音錄制》 里的相關介紹。(我希望把這兩個模塊用在我正做的local語音通訊試驗中)
好的,上次的程序生成了一個 "myTest.wav" 的音頻文件,根據上次的文件格式,那么從開頭數第21個字節開始就是 WAVEFORMATEX 的結構了,提供你的指針、讀到你的結構中去吧。 還有,裸音頻數據的長度,在開頭第43個字節開始的地方,然后,從開頭數第47個字節的位置就是音頻裸數據的起始地址了,把數據也讀入到你的緩沖中吧。(如果地址從00開始算,剛才給出的位置就減1)
小事已經具備,開始干吧。按照上次的編排,先講一下用到的 API 。放音通常都是使用 waveOutXXX一類的 API 。 最主要的是 waveOutWrite (馬上播放指定的音頻緩沖),配對的是 waveOutReset(馬上停止聲音的播放), 外加對播放的控制 waveOutPause (暫停播放,注意: 可以在 waveOutWrite 前就已經暫停 ) 和 waveOutRestart (繼續被暫停的播放)。
說完這些函數,也提一下為以上幾個函數做準備工作的函數吧, waveOutOpen 和 waveOutClose 配對( waveOutOpen 里面指定音頻格式,還有通知回調函數的地址 ),waveOutPrepareHeader 和 waveOutUnprepareHeader 配對(也是在 waveOutPrepareHeader 里面指定用來播放的緩沖大小和首地址)就是這么多了。
下面看詳細調用過程簡介。
waveOutOpen (指定音頻格式和回調函數地址)
waveOutPrepareHeader (指定音頻緩沖的地址)
waveOutWrite
(放音中....)
waveOutPause (你可以嘗試在 waveOutWrite 之前暫停)
(聲音暫停)
waveOutRestart
(繼續播放中...)
waveOutReset
waveOutUnprepareHeader
waveOutClose
小提示:最好在 waveOutReset 前先執行 waveOutPause 否則在我的機器上會有一個噪音發生???
需要指出的是,在播放時并不能動態的知道這個聲音什么時候就播放完了(總不能大概估算時間就執行 waveOutReset 吧),為了處理這個問題,我想了一個解決辦法,就是打開設備的時候指定通知回調函數,在那個回調函數中執行 waveOutReset 就行了(實現時作了一點小改動)
下面將給出源程序,執行時候最好用上次介紹錄音的模塊生成 "myTest.wav" 文件,然后拷貝到當前目錄下用本程序執行播放,(該源程序中要包含調試類文件 RunTimeLog.cpp,見http://www.csdn.net/develop/Read_Article.asp?Id=17477 詳細調試信息請看 "XXX.log") (另:程序只是做試驗用,沒有注釋,敬請見諒 )
(全文完 - 2003年03月28日_pm: 03時35分 `海風: 昨天忘署名了)
// ******************* FileName: WinMain.cpp *****************************
// 該源程序需要加入到 VC6 的 Win32 Application 的 empty Project 中
// 請包含我自定義的調試類,見 #include "RunTimeLog.cpp"
// 對于工程的 Link 選項,至少要包含以下庫: msvcrt.lib kernel32.lib user32.lib Winmm.lib
#define WIN32_LEAN_AND_MEAN // Say No to MFC
#include <windows.h>
#include <Mmsystem.h>
#include "RunTimeLog.cpp"
RunTimeLog log;
char lpTemp[256]="";
DWORD CurThreadID;
DWORD FCC(LPSTR lpStr)
{
DWORD Number = lpStr[0] + lpStr[1] *0x100 + lpStr[2] *0x10000 + lpStr[3] *0x1000000 ;
return Number;
}
void CALLBACK
waveOutProc( HWAVEOUT hwo,
UINT uMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2 )
{
;
log.numberwrite( "Get a waveOutProc uMsg =", uMsg );
if (uMsg == WOM_DONE)
{
log.write("WOM_DONE");
PostThreadMessage( CurThreadID, WM_QUIT , 11, 22 ); // WM_QUIT
}
return ;
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
CreateMutex( NULL, false, "MyMutex");
if ( GetLastError() == ERROR_ALREADY_EXISTS ) { log.write("Exists and Exit"); log.last(); ExitProcess( NULL); }
CurThreadID = GetCurrentThreadId( ); // 取得當前線程的 ID (標識號)
log.write("Program Start."); // log.msg("Start Test");
log.nobuff = true;
DWORD datasize = 48000;
WAVEFORMATEX waveformat;
waveformat.wFormatTag=0; //WAVE_FORMAT_PCM;
waveformat.nChannels=0; //1;
waveformat.nSamplesPerSec=0; //8000;
waveformat.nAvgBytesPerSec=0; //8000;
waveformat.nBlockAlign=0; //1;
waveformat.wBitsPerSample=0; //8; //指定錄音格式
waveformat.cbSize=0;
wsprintf( lpTemp, "WAVEFORMATEX size = %lu", sizeof(WAVEFORMATEX) );
log.write(lpTemp);
// 打開文件,然后讀入全部的文件長度,
HANDLE fileHandle =
CreateFile( "myTest.wav", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (fileHandle) log.write("打開可讀文件"); else log.write("嘗試打開文件失敗");
if (fileHandle)
{
DWORD dwtemp = 0; // FCC("RIFF");
DWORD ReadCount = 0;
DWORD bufflong = 0;
ReadFile( fileHandle , &dwtemp, 4, &ReadCount, NULL );
if ( dwtemp == FCC("RIFF") ) log.write("找到 RIFF 文件標志"); else log.write("沒有找到 RIFF 文件標志");
ReadFile( fileHandle , &dwtemp, 4, &ReadCount, NULL );
bufflong = dwtemp;
log.numberwrite("總的文件大小", bufflong + 8 );
log.numberwrite("真的文件大小", GetFileSize(fileHandle, NULL) );
if ( (bufflong+8)!=GetFileSize(fileHandle, NULL) ) bufflong = GetFileSize(fileHandle, NULL) - 8;
SetFilePointer( fileHandle, 8, NULL, FILE_CURRENT);
bufflong = bufflong - 8;
ReadFile( fileHandle , &dwtemp, 4, &ReadCount, NULL );
bufflong = bufflong - 4;
log.numberwrite( "sizeof(WAVEFORMAT) =", dwtemp );
ReadFile( fileHandle , &waveformat, dwtemp, &ReadCount, NULL );
bufflong = bufflong - dwtemp;
log.numberwrite( "waveformat.wBitsPerSample =", waveformat.wBitsPerSample );
log.numberwrite( "waveformat.nSamplesPerSec =", waveformat.nSamplesPerSec );
while (dwtemp != bufflong)
{
ReadFile( fileHandle , &dwtemp, 4, &ReadCount, NULL );
bufflong = bufflong - 4;
if ( (bufflong < 8)||(bufflong < bufflong - 128) ) { log.write("找不到準確的緩沖大小"); break; }
}
log.numberwrite( "bufflong =", bufflong );
datasize = bufflong;
HWAVEOUT phwo;
if ( waveOutGetNumDevs() ) log.write("有可以使用的 WaveOut 通道"); else log.write("沒有可以使用的 waveOut 通道");
int res=waveOutOpen( &phwo, WAVE_MAPPER, &waveformat, (DWORD)waveOutProc, NULL, CALLBACK_FUNCTION ); //打開錄音設備
if ( res == MMSYSERR_NOERROR ) log.write("打開 waveOut 成功"); // 驗證創建是否成功
else log.numberwrite( "打開 waveOut 失敗,Error_Code =", res );
WAVEHDR m_pWaveHdr;
m_pWaveHdr.lpData = (char *)GlobalLock( GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, datasize) );
memset(m_pWaveHdr.lpData, 0, datasize );
// 讀入磁盤數據
ReadFile( fileHandle , m_pWaveHdr.lpData, datasize, &ReadCount, NULL );
m_pWaveHdr.dwBufferLength = datasize;
m_pWaveHdr.dwBytesRecorded = 0;
m_pWaveHdr.dwUser = 0;
m_pWaveHdr.dwFlags = 0;
m_pWaveHdr.dwLoops = 0;
log.numberwrite( "WAVEHDR size =", sizeof(WAVEHDR) );
UINT resPrepare = 0;
resPrepare = waveOutPrepareHeader( phwo, &m_pWaveHdr, sizeof(WAVEHDR) );
if ( resPrepare == MMSYSERR_NOERROR) log.write("準備放音用頭文件成功");
else log.numberwrite( "不能開辟放音頭文件,Error_Code =", resPrepare );
if ( waveOutPause(phwo) ) log.write("無法 Pause"); else log.write("成功 Pause");
if ( waveOutWrite( phwo, &m_pWaveHdr, sizeof(WAVEHDR) ) ) log.write("開始寫入放音數據失敗");
else log.write("開始寫入放音數據成功");
if ( waveOutRestart(phwo) ) log.write(" 非法 Resume"); else log.write(" Resume 到 Playback");
log.write(""); // 寫入空字符串可以分行
PostThreadMessage( CurThreadID, WM_USER , 11, 22 ); // WM_QUIT
// 進入消息循環
MSG msg;
while(1)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{ log.numberwrite( "Get The uMsg =", msg.message ); if (msg.message == WM_QUIT) break; }
else { log.write("沒有取到消息"); WaitMessage(); }
} // end while
if ( waveOutPause(phwo) ) log.write("無法 Pause"); else log.write("成功 Pause");
if ( waveOutReset(phwo) ) log.write("不能 Reset"); else log.write("成功 Reset");
resPrepare = waveOutUnprepareHeader( phwo, &m_pWaveHdr, sizeof(WAVEHDR) );
if ( resPrepare == MMSYSERR_NOERROR) log.write("釋放放音用頭文件成功");
else log.numberwrite( "不能釋放放音頭文件,Error_Code =", resPrepare );
if ( m_pWaveHdr.lpData )
if ( GlobalFree(GlobalHandle( m_pWaveHdr.lpData )) ) log.write("Global Free 失敗"); else log.write("Global Free 成功");
if (res == MMSYSERR_NOERROR ) //關閉錄音設備
if (waveOutClose(phwo)==MMSYSERR_NOERROR)log.write("正常關閉放音設備");
else log.write("非正常關閉放音設備");
CloseHandle(fileHandle); fileHandle = INVALID_HANDLE_VALUE;
}
log.last(true);
// ExitProcess(0);
return 0;
}
// ******************* End of File *****************************
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -