waveInAddBuffer :向聲音輸入設備發送緩沖區 ;waveInClose :關閉聲音輸入設備
waveInGetDevCaps:獲取聲音輸入設備性能; waveInGetErrorText:獲取聲音出錯信息文本
waveInGetID :獲取聲音輸入設備ID; waveInGetNumDevs:返回聲音輸入設備數量
waveInGetPosition :獲取聲音設備輸入位置; waveInMessage :向聲音輸入設備發送信息
waveInOpen :打開聲音輸入設備; waveInPrepareHeader:預備聲音輸入緩沖區
waveInReset :停止聲音輸入設備工作; waveInStart :停止聲音輸入設備工作
waveInStop :停止聲音輸入; waveInUnprepareHeader : 清除預備的聲音文件頭
需要說明的是:不同的編程工具多會含有對這些低級波形音頻函數進行說明的頭文件(比如在Delphi4.0中,對Mmsystem.dll說明的文件是Mmsystem.pas),所以在不同的編程工具中調用這些函數時有可能會使用不同的名稱。
與使用其他設備壹樣,要想用波形音頻函數來控制聲卡,必須要經過以下的步驟:
1. 打開波形輸入設備。函數waveInOpen用於打開波形輸入設備,其原型如下:
① WORD waveInOpen(lphWaveIn,wDeviceID,lpFormat,dwCallback,dwCallbackInstan ce, dwFlags) LPHWaveIn: lphWaveIn 該變量用來接收波形輸入設備的句柄,該句柄應當保存下來,因為其他的波形輸入函數還會用到它.
②WORD wDeviceID 該變量用來指明波形輸入設備的標記號.當PC中有多塊聲卡(準確地說是波形輸入設備)時,操作系統會為每壹塊聲卡分配壹個標記號.可以用waveInGetNumDev s函數來得到能夠作為波形輸入設備的數目N,則wDeviceID的取值範圍為0~N-1.如果想得到沒個標記號所對應的錄音性能,可以使用函數waveInGetDevCaps.若把wDeviceID設為W AVE_MAPPER(即-1),則系統會自動選擇壹符合要求的設備(根據lpFormat的要求). ③lpFormat是壹個指向PCMWAVEFORMAT數據結構的指針,應當在這個數據結構中指明所期望的采樣模式,這個數據結構的定義是這樣的:
Typedef structure pcmwaveformat_tag { WAVEFORMAT wf; //有關PCM格式設置的另外壹種數據結構
WORD wBitsPerSample; //量化位數
}PCMWAVEFORMAT;
Typedef structure waveformat_tag {
WORD wFormatTag; //采樣數據格式,目前只能用PCM格式
WORD nChannels; //通道數目(1或2)
DWORD nSamplesPerSec; //采樣速率
DWORD nAvgBytesPerSec;//每秒采樣得到的數據
WORD nBlockAlign; //記錄區塊對齊的單位。此值為nChannels*wBitsPerSample/8 }WAVEFORMAT;
④ DWORD dwCallback.定義回調函數的地址或回調窗口的句柄。回調函數的地址或回調窗口用來處理波形輸入設備產生的消息。
⑤DWORD dwCallbackInstance。這是壹個用戶自定義的數據,該數據會壹並傳給回調函數(或窗口)。
⑥DWORD dwFlags。定義打開波形輸入設備的標記。
CALLBACK_WINDOW 定義dwCallback為窗口句柄。
CALLBACK_FUNCTION 定義dwCallback為函數地址。
另外還可以在此指定:
WAVE_FORMAT_QUERY 只查詢波形輸入設備是否支持給定格式而不真的打開波形輸入設備。
WAVE_ALLOWSYNC 同步方式開啟波形輸入設備,錄音工作在後臺進行。
下面壹段Delphi程序說明了打開波形輸入設備的過程:
type
TRecorder = class
private
…
FWaveFmt : TWaveFormatEx;//Delphi中,WAVEFORMAT和PCMWAVEFORMAT合為TwaveFor
matEx。
WaveHandle : HWaveIn;
WaveHdr1 : PWAVEHDR; //數據緩沖區頭結構的指針 (見下文)
WaveBuffer1 : lpstr; //數據緩沖區的指針 (見下文)
procedure CallBack(uMsg,dwInstance,dwParam1,dwParam2 : DWORD); stdcall;
…
end;
…
Recorder:=TRecorder.Create;
…
Recorder.FWaveFmt.wFormatTag:=WAVE_FORMAT_PCM;
Recorder.FWaveFmt.wBitsPerSample:=16;
Recorder.FWaveFmt.nSamplesPerSec:=11025;
Recorder.FWaveFmt.nAvgBytesPerSec:=22050;
Recorder.FWaveFmt.nBlockAlign:=2;
WaveInOpen(@Recorder.WaveHandle,Wave_Mapper,mailto:@Recorder.FWaveFmt,
DWORD(@TRecorder.CallBack),DWORD(@Recorder),CALLBACK_FUNCTION + WAVE_ALLOW
SYNC);
…
2. 為采樣數據分配緩沖空間
在Windows環境,可以用GlobalAllocPtr來獲取壹段內存空間,但是由於Windows操作系統采用了虛擬存儲管理機制,這塊內存空間隨時有可能會被置換到硬盤上,讀寫硬盤所耗費的時間會造成采樣的不連續。因此,在將緩沖區送往波形輸入設備之前,必須調用WaveInPrepareHeader函數以保證緩沖區不會被置換到硬盤上。當然在用GlobalFreeP tr來釋放緩沖區之前,必須先要用WaveInUnprepareHeader函數來解除這種保護。
下面幾行Delphi語句說明了使用錄音緩沖區的過程。
…
Recorder.WaveHdr1:=GlobalAllocPtr(GHND or GMEM_SHARE,Sizeof(WAVEHDR));
Recorder.WaveBuffer1:=GlobalAllocPtr(GHND or GMEM_SHARE,1024);
Recorder.WaveHdr1.lpData := Recorder.WaveBuffer1;
Recorder.WaveHdr1.dwBufferLength:=1024;
WaveInPrepareHeader(Recorder.WaveHandle, Recorder.WaveHdr1, sizeof(WAVEHDR
));
WaveInAddBuffer(Recorder.WaveHandle, Recorder.WaveHdr1, sizeof(WAVEHDR));
…
WaveInUnprepareHeader(Recorder.WaveHandle, Recorder.WaveHdr1, sizeof(TWAVE
HDR));
GlobalFreePtr(Recorder.WaveBuffer1);
…
但是,如果只為波形輸入設備開辟壹個緩沖區,則當該緩沖區被采樣數據填滿後,波形輸入設備就無緩沖區可用,不得不停止采樣,從而造成了采樣的斷續。所以在實際應用中,至少要為波形輸入設備準備兩個緩沖區,用上述方法同時送給波形輸入設備。
3. 啟動波形輸入設備
當上述壹切都準備好後,用WaveInStart啟動波形輸入設備,即可開始進行數據采集,在采集的過程中,壹旦有緩沖區被采樣數據填滿,系統就回調WaveInOpen中指定的dwCa llback函數(或向指定的窗口發送消息)。在Delphi4.0中,回調函數的格式是這樣的: procedure CallBack(uMsg,dwInstance,dwParam1,dwParam2 : DWORD); stdcall; 其中uMsg是Windows的消息標記號,有三種情況:
MM_WIM_OPEN 表示波形輸入設備開啟成功
MM_WIM_DATA 表示壹個緩沖區已滿。
此時dwParam1中攜帶有數據緩沖區頭結的指針。正是通過這個指針,才可以隨機地訪問緩沖區中的每壹個采樣數據。如下面程序所示:
…
procedure TRecorder.CallBack(uMsg,dwInstance,dwParam1,dwParam2 : DWORD);
stdcall;
var i:Integer;
SPByte : ^Byte; //假設在打開設備時采用8位量化
SingleData : Integer;
BEGIN
case uMsg of //uMsg是Windows的消息標記號
MM_WIM_OPEN : //波形輸入設備開啟成功發回的消息
…
MM_WIM_DATA : //壹個緩沖區已滿發回的消息
begin
SPByte := Pointer(dwParam1);
for i :=0 to Recorder.DataLength-1 do
begin
SingleData := SPByte^; //通過SPByte來訪問緩沖區中的數據
…
Inc(SPByte);
end;
end;
MM_WIM_CLOSE : //波形輸入設備關閉成功發回的消息
…
end;
END;
…
MM_WIM_CLOSE 表示波形輸入設備關閉成功。當波形輸入設備關閉後,別忘了用Wave InPrepareHeader和GlobalFreePtr來釋放緩沖區內存。
4. 關閉語音輸入設備
waveInStop(hWaveIn) 停止語音輸入
waveInReset(hWaveIn) 重置語音輸入設備
waveInClose(hWaveIn) 關閉語音輸入設備。其中hWaveIn是WaveInOpen得到的設備句柄。
在關閉語音輸入設備前,必須重置語音輸入設備,否則系統會出現這樣的錯誤提示: "MMSYSTEM033 媒體數據仍在播放中,請重置設備或等到數據播放完畢"。但是只有當壹個緩沖區填滿數據後,才能重置語音輸入設備
以上波形輸入函數,若調用成功則返回0;否則返回非0,此時可以用waveInGetErrorText函數來得到出錯信息,這樣做的目的是方便調試。
三、必須註意的幾點
以上闡述了作為A/D卡的聲卡編程技術,但是還必須註意以下幾點
1. 聲卡的采樣頻率並不只限於11025Hz,22050Hz,44100 Hz三種,大多數聲卡的采樣頻率在壹定的範圍內是可調的(當然會存在壹定的偏差)。有的聲卡的最高采樣頻率可達200K Hz(有可能隨不同品牌而異)。
2. 緩沖區不能設得太小,否則也會造成采樣的不連續。在作者的聲卡上,若采用16為量化,22050Hz的采樣速率,緩沖區設為1K字節,理論上每秒鐘可以得到22050*2個字節的數據,實際上每秒鐘只能得到大約16000*2個字節的數據。若緩沖區設為2K字節,則與理論值壹致。
3. A/D轉化後的數據格式是PCM格式,即:若是8位量化,對應著8位無符號數據,0對應著負滿幅值,128對應著零電平,255對應著正滿幅值;若是16位量化,對應著16位有符號數據,-32768對應著負滿幅值,0對應著零電平,32767對應著正滿幅值。編程過程中應註意所聲明的數據類型是否與之相符合,比如在Delphi4.0中,8位無符號數據對應著 Byte型數據,16位有符號數據對應著SmallInt型數據。
4. 由於聲卡的輸入端往往帶有隔直電容,所以不能用聲卡直接對直流量進行采集。解決的辦法就是將這個隔直電容短接。
5. 同樣地,利用windows的API函數和聲卡的D/A功能也可以使聲卡產生模擬音頻信號輸出。