日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

API 层实现语音录制

發布時間:2025/3/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 API 层实现语音录制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
API 層實現語音錄制
?發表日期:2003-03-31作者:temp77[] 出處:??
?

?? 我從前出于需要,上網尋找了關于錄音部分的源代碼,收獲不大,現在把自己摸索的部分源代碼公開出來吧,希望對部分人有用。

?

?? 其實要實現聲音的錄制,可以用多種的方式(例如 mci ),我這里介紹的是在 API 的層面來實現,你可以對其中進行更多的控制。比方說,要音頻裸數據存到你指定的內存中去。

?

?? 先說一下要用到的 API , 錄音通常都是使用 waveInXXX 一類 API 的,最主要的是 waveInStart (顧名思義就是開始錄音),然后是 waveInStop(它能夠在你指定的緩沖滿的時候返回) ; 如果不用 waveInStop 可以使用 waveInReset(不同于Stop的是該函數不等緩沖滿就馬上返回那樣你才可以終止錄音),不過最好跟 waveInGetPosition 配合使用。

?

??說完這些函數,不得不提的是為以上幾個函數做準備工作的函數(注意配對使用), waveInOpen 和 waveInClose 配對( waveInOpen 里面指定音頻的格式,比方說立體聲 和16位音質等); waveInPrepareHeader 和 waveInUnprepareHeader 配對( waveInPrepareHeader 里面指定用來錄音緩沖的大小和首地址),緊跟著 waveInPrepareHeader要例行公事調用 waveInAddBuffer( 作用未詳,不多說了)。

?

??詳細調用過程可以看下面

?

?

?

(之前最好調用 waveInGetNumDevs 看看有沒有可用的設備)

?

waveInOpen (該處用 WAVEFORMATEX 結構指定音頻格式)

?

?

?

?? waveInPrepareHeader (該處用 WAVEHDR 結構的 lpData 成員指定緩沖首地址)

?

?? waveInAddBuffer

?

?

?

????waveInStart

?

???? (錄制中....)

?

????waveInStop (warning:一定要緩沖滿了才返回)

?

?

?

?? waveInUnprepareHeader

?

?

?

waveInClose

?

?

?

?

?

?

?

??需要指出的是,上面的代碼你不能隨心所欲的停止錄音過程(如果你指定的緩沖非常大,比方說足夠錄音一個小時,那么你就要乖乖的等上一個小時),如果希望馬上停止,請使用下面的方法。

?

?

?

?

?

????waveInStart

?

????(錄制中....)

?

????(n 時間后,用戶提出停止請求)

?

????waveInGetPosition (保留該值,用來設置 WAVEHDR 結構的 dwBytesRecorded 成員)

?

????waveInReset

?

????(重新設置 WAVEHDR 結構的 dwBytesRecorded 成員)

?

?

?

?

?

?

?

??整個過程就是如此的幾句,下面將給出源程序以驗證。

?

?

?

?

?

??需要說明一下,正常情況下錄音以后,總共耗費的內存(以字節算) 會保存在WAVEHDR 結構的 dwBytesRecorded 成員中,可用的音頻裸數據當然就放進你指定的內存緩沖中去了,你大可不必等 waveInUnprepareHeader 就可以馬上拿來用了。

?

?

?

?

?

? ( 補充一個內容,粗略講解一下 .wav 文件的文件頭格式 )

?

??看下面的結構,一開始的八個字節是一個結構, 第一個 四字節 是標志,剛好等于 ascii 的 "RIFF" ,第二個四字節是總的文件長度減去8。你可以驗證一下。(參照16進制閱讀數字的方法)

?

??跟著是第二個數據結構,占12個字節。如果你是標準的 wave 格式的文件,那么就是緊接著兩個標志,第一個四字節的標志是 ascii 的 "WAVE" ,第二個 四字節的標志就是 ascii 的 "fmt "(注意,有一個空格),然后剩下的四字節里面藏的是 WAVEFORMATEX 結構的大小(參照MSDN),應該是18個字節。

?

??然后,當然就是 18個字節的 WAVEFORMATEX 結構里面的值。詳細的就不說了,自己查一下 MSDN。 (需要注意的是,這個 WAVEFORMATEXEX 的末一個成員,講了可能的長度擴充)

?

??在這 18 個字節后面 (按照舊時候的方法),應該是跟著一個8字節的結構的,然后就是 "裸數據" 的開始地址了,這八字節結構的開始四字節是標志,應該等于 ascii 的 "data",然后緊跟著的四字節就是裸數據的大小了,也就是最重要的部分。

?

??好了,如此一來,你就可以得到音頻裸數據的起始位置(緊緊跟在含"data"標志的數據結構的后面),還有就是音頻數據的長度了。應該是所有的問題都很容易解決的。

?

??

?

??不過,還要注意,就是現在的很多 .wav 文件都會外加一個數據結構(12字節),就插在 WAVEFORMATEX 的后面和 含 "data" 的數據結構前面。?這個外加的結構 第一個四字節是標志,等于 ascii 碼的 "fact",然后第二個四字節的值在大部分情況下都等于 4, 第三個四字節的值也是等于 音頻裸數據的長度。?基本上就是這樣了。

?

?

?

?

?

???下面給出的源程序文件只要加進新建的 VC win32工程中,編譯即可,執行效果是錄音三秒后自動生成 mytest.wav 文件供播放測試(記得選好默認錄音通道)。

?

??廢話不多說,給出源程序(該源程序中要包含 RunTimeLog.cpp,見http://www.csdn.net/develop/Read_Article.asp?Id=17477) 希望對大家有用。(全文完)

?

???(全文完 - 2003年03月27日_am: 11時27分)

?

?

?

?

?

?

?

// *******************?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 FCC(LPSTR lpStr)

?

{

?

? DWORD Number = lpStr[0] + lpStr[1] *0x100 + lpStr[2] *0x10000 + lpStr[3] *0x1000000 ;

?

? return Number;

?

}

?

?

?

?

?

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); }

?

?

?

?

?

??log.write("Program Start.");

?

??log.nobuff = true;

?

?

?

DWORD datasize = 48000;

?

??

?

??// 最常用法

?

WAVEFORMATEX waveformat;

?

waveformat.wFormatTag=WAVE_FORMAT_PCM;

?

waveformat.nChannels=1;

?

waveformat.nSamplesPerSec=8000;

?

waveformat.nAvgBytesPerSec=8000;

?

waveformat.nBlockAlign=1;

?

waveformat.wBitsPerSample=8; //指定錄音格式

?

waveformat.cbSize=0;

?

?

?

?wsprintf( lpTemp, "WAVEFORMATEX size = %lu", sizeof(WAVEFORMATEX) );

?

?log.write(lpTemp);

?

?

?

?

?

?

?

HWAVEIN?m_hWaveIn;

?

?

?

if ( waveInGetNumDevs() ) log.write("有可以使用的 WaveIn 通道");?else log.write("沒有可以使用的 waveIn 通道");

?

?

?

int res=waveInOpen(&m_hWaveIn,WAVE_MAPPER, &waveformat, (DWORD)NULL,0L,CALLBACK_WINDOW); //打開錄音設備

?

?

?

if ( res == MMSYSERR_NOERROR ) log.write("打開 waveIn 成功");?// 驗證創建是否成功

?

else?{

?

?? wsprintf(lpTemp, "打開 waveIn 通道失敗,Error_Code = 0x%x", res );

?

?? log.write(lpTemp);

?

} // End of 驗證創建是否成功

?

?

?

?

?

?

?

WAVEHDR m_pWaveHdr;

?

?

?

?m_pWaveHdr.lpData = (char *)GlobalLock( GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, datasize) );

?

?memset(m_pWaveHdr.lpData, 0, datasize );

?

?m_pWaveHdr.dwBufferLength = datasize;

?

?m_pWaveHdr.dwBytesRecorded = 0;

?

?m_pWaveHdr.dwUser = 0;

?

?m_pWaveHdr.dwFlags = 0;

?

?m_pWaveHdr.dwLoops = 0;

?

?

?

?

?

?wsprintf( lpTemp, "WAVEHDR size = %lu", sizeof(WAVEHDR) );

?

?log.write(lpTemp);

?

?

?

?

?

int resPrepare = waveInPrepareHeader( m_hWaveIn, &m_pWaveHdr, sizeof(WAVEHDR) ); //準備內存塊錄音

?

?

?

if ( resPrepare == MMSYSERR_NOERROR) log.write("準備錄音用頭文件成功");

?

?else?{

?

?? wsprintf(lpTemp, "不能開辟錄音頭文件,Error_Code = 0x%03X", resPrepare );

?

?? log.write(lpTemp);

?

} // End of 驗證開辟緩沖

?

?

?

?

?

?

?

?resPrepare = waveInAddBuffer( m_hWaveIn, &m_pWaveHdr, sizeof(WAVEHDR) );

?

?

?

if ( resPrepare == MMSYSERR_NOERROR) log.write("準備錄音用內存成功");

?

?else?{

?

?? wsprintf(lpTemp, "不能開辟錄音用緩沖,Error_Code = 0x%03X", resPrepare );

?

?? log.write(lpTemp);

?

} // End of 驗證開辟緩沖

?

?

?

?

?

?

?

?

?

?log.write("");?// 寫入空字符串可以分行

?

?

?

?

?

?if (! waveInStart(m_hWaveIn) ) log.write("開始錄音"); else log.write("開始錄音失敗");

?

?

?

?Sleep(3200);

?

?

?

?

?

MMTIME mmt;

?

mmt.wType = TIME_BYTES;

?

log.numberwrite( "sizeof(MMTIME) =", sizeof(MMTIME) );

?

log.numberwrite( "sizeof(UINT) =", sizeof(UINT) );

?

?

?

if ( waveInGetPosition(m_hWaveIn, &mmt, sizeof(MMTIME)) ) log.write("不能取得音頻長度");

?

?else log.numberwrite( "取得現在音頻位置 =", mmt.u.cb );

?

?

?

if (mmt.wType == TIME_BYTES) { log.write("得到的 TIME_BYTES 格式的音頻長度"); }

?

? else log.write("指定的 TIME_BYTES 格式音頻長度不支持");

?

?

?

?

?

//?if (! waveInStop(m_hWaveIn) )?log.write("停止錄音"); else? log.write("停止錄音失敗");

?

?

?

?if (! waveInReset(m_hWaveIn) )?log.write("重置內存區成功"); else log.write("重置內存區失敗");

?

?

?

?

?

m_pWaveHdr.dwBytesRecorded = mmt.u.cb;

?

?

?

?

?

?

?

DWORD NumToWrite=0;?DWORD dwNumber = 0;

?

?

?

HANDLE FileHandle =

?

??? CreateFile( "myTest.wav", GENERIC_WRITE, FILE_SHARE_READ, NULL,

?

????????? CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

?

?

?

//?memset(m_pWaveHdr.lpData, 0, datasize);

?

?

?

dwNumber = FCC("RIFF");

?

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

?

?

?

dwNumber = m_pWaveHdr.dwBytesRecorded + 18 + 20;

?

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

?

?

?

dwNumber = FCC("WAVE");

?

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

?

?

?

dwNumber = FCC("fmt ");

?

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

?

?

?

dwNumber = 18L;

?

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

?

?

?

WriteFile(FileHandle, &waveformat, sizeof(WAVEFORMATEX), &NumToWrite, NULL);

?

?

?

dwNumber = FCC("data");

?

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

?

?

?

dwNumber = m_pWaveHdr.dwBytesRecorded;

?

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

?

?

?

WriteFile(FileHandle, m_pWaveHdr.lpData, m_pWaveHdr.dwBytesRecorded, &NumToWrite, NULL);

?

?

?

SetEndOfFile(FileHandle);

?

CloseHandle( FileHandle );??FileHandle = INVALID_HANDLE_VALUE; // 收尾關閉句柄

?

?

?

log.write("應該已生成 myTest.wav 文件");

?

?

?

?

?

?

?

?if ( waveInUnprepareHeader(m_hWaveIn, &m_pWaveHdr, sizeof(WAVEHDR)) ) log.write("Un_Prepare Header 失敗"); else log.write("Un_Prepare Header 成功");

?

?

?

?if ( GlobalFree(GlobalHandle( m_pWaveHdr.lpData )) ) log.write("Global Free 失敗"); else log.write("Global Free 成功");

?

?

?

?

?

?

?

if (res == MMSYSERR_NOERROR )?//關閉錄音設備

?

if (waveInClose(m_hWaveIn)==MMSYSERR_NOERROR)log.write("正常關閉錄音設備");

?

else log.write("非正常關閉錄音設備");

?

?

?

?

?

?

?

??log.last(true);

?

??// ExitProcess(0);

?

??return 0;

?

}

?

?

?

// *******************?End of File?*****************************

總結

以上是生活随笔為你收集整理的API 层实现语音录制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。