當前位置:編程學習大全網 - 源碼下載 - 如何利用DirectSound實現聲卡錄音

如何利用DirectSound實現聲卡錄音

詳細講述了如何利用DirectSound對經過聲卡和麥克風的數據進行捕獲,進行錄音,並保存為wave格式的文件。

曾經學習過Directshow的開發,對於Dsound壹直沒有仔細的學習,以前只是知道Dsound是做音頻開發的,我壹直以為它和Dshow的結構體系差不多,經過仔細學習後,發現,其實他們完全兩碼事。DirectSound雖然也基於COM,但不象Dshow那樣多個的filter組成鏈表。

閑話少說,下面我們看看DirectSound到底能幫我們做些什麽。

1、播放WAVE格式的音頻文件或者資源。

2、可以同時播放多個音頻。

3、Assign high-priority sounds to hardware-controlled buffers

4、播放3D立體聲音

5、在聲音中添加特技效果,比如回聲,動態的改變特技的參數等

6、將麥克風或者其他音頻輸入設備的聲音錄制成wave格式的文件

DirectSound就能做這麽多事情,到這裏,我都有點懷疑DirectSound是不是就是封裝了mmio系列和wav系列的函數。因為這些底層的API也能夠完成這些事情。這裏我們主要討論壹下,如果使用Directsound進行錄音,並保存成wave格式的文件。

在開始工作之前,要先介紹DirectSound錄音用到的三個非常重要的對象:

·IDirectSoundCapture8 ,設備對象,根據妳錄音的設備創建的設備對象,利用該對象可以獲取設備的屬性。

·IDirectSoundCaptureBuffer8,緩沖區對象,該對象由設備對象創建,主要用來操作音頻數據

·IDirectSoundNotify8 ,事件通知對象,該對象用來通知應用程序從緩沖區中將數據取走,寫入文件保存起來。

利用DirectSound錄音的主要思路,就是先根據選擇的錄音設備創建設備對象,然後通過設備對象創建輔助緩沖區對象,開始錄音的時候,設備將數據寫入緩沖區,應用程序主動的從緩沖區將數據讀出來寫文件即可,就實現了錄音功能。這裏簡單介紹壹下dsound的通知功能,應用程序會創建壹個通知對象,然後將通知對象邦定,然後設定通知位置(position),什麽是通知位置呢,比如緩沖區的大小為4000字節,如果妳想當數據達到緩沖區壹半的時候能得到通知開始copy數據,那麽此時妳就可以將通知位置設定為2000,通知位置可以任意的設定,當緩沖區的數據達到妳設定的位置時,就會通知應用程序將緩沖區的數據copy到文件中,緩沖區是循環利用的,當緩沖區填充滿了以後,就會從頭開始充填數據,所以,緩沖區就是壹邊讀,壹邊寫的過程。

下面我講壹下錄音的主要步驟,可以使大家的思路更清晰壹些

1、枚舉錄音的設備

2、根據選擇的設備創建設備對象

3、利用設備對象創建緩沖區對象

4、設置通知機制

5、創建工作線程,用來將緩沖區的數據寫入文件。

先來定義壹下用到的數據

LPDIRECTSOUNDCAPTURE8 g_pDSCapture = NULL;//設備對象指針

LPDIRECTSOUNDCAPTUREBUFFER g_pDSBCapture = NULL;//緩沖區對象指針

LPDIRECTSOUNDNOTIFY8 g_pDSNotify = NULL;//用來設置通知的對象接口

GUID g_guidCaptureDevice = GUID_NULL; //設備id

BOOL g_bRecording = FALSE; //是否正在錄音

WAVEFORMATEX g_wfxInput; //輸入的音頻格式

DSBPOSITIONNOTIFY g_aPosNotify[ NUM_REC_NOTIFICATIONS + 1 ]; //設置通知標誌的數組

HANDLE g_hNotificationEvent; //通知事件

BOOL g_abInputFormatSupported[20];

DWORD g_dwCaptureBufferSize; //錄音用緩沖區的大小

DWORD g_dwNextCaptureOffset;//偏移位置

DWORD g_dwNotifySize;// 通知位置

CWaveFile* g_pWaveFile;//

枚舉錄音的設備

如果妳的程序只是想從用戶缺省的設備上進行聲音的錄制,那麽就沒有必要來枚舉出系統中的所有錄音的設備,當妳調用DirectSoundCaptureCreate8 或者另外壹個函數DirectSoundFullDuplexCreate8的時候,其實就默認指定了壹個缺省的錄音設備。

當然,在下面的情況下,妳就必須要枚舉系統中所有的設備,例如,妳的應用程序並不支持所有的輸出設備,或者妳的應用程需要兩個或者多個設備,或者妳希望用戶自己來選擇輸出設備。

枚舉設備,妳首先要定義壹個回調函數,這個回調函數可以被系統中的每個設備來調用,妳可以在各函數做任何事情,這個函數的命名也沒有任何的限制,但是函數應該以DSEnumCallback為原型,如果枚舉沒有結束,這個回調函數就返回TRUE,如果枚舉結束,例如妳找到合適的設備,這個函數就要返回FALSE。

下面是回調函數的壹個例子,這個函數將枚舉的每壹個設備都添加到壹個combox中,將設備的GUID保存到壹個item 中,這個函數的前三個參數由設備的驅動程序提供,第四個參數有DirectSoundCaptureEnumerate函數提供,這個參數可以是任意的32位值,這個例子裏是combox的句柄,

BOOL CALLBACK DSEnumProc(LPGUID lpGUID,

LPCTSTR lpszDesc,

LPCTSTR lpszDrvName,

LPVOID lpContext )

{

HWND hCombo = (HWND)lpContext;

LPGUID lpTemp = NULL;

if (lpGUID != NULL) // NULL only for "Primary Sound Driver".

{

if ((lpTemp = (LPGUID)malloc(sizeof(GUID))) == NULL)

{

return(TRUE);

}

memcpy(lpTemp, lpGUID, sizeof(GUID));

}

//下面的代碼主要主要是將設備添加到CComboBox,其實妳完全直接將CComboBox指針傳遞過來,直接的添加,這裏采用的是給combox窗口發送消息的方法,

ComboBox_AddString(hCombo, lpszDesc);

ComboBox_SetItemData(hCombo,

ComboBox_FindString(hCombo, 0, lpszDesc),

lpTemp );

free(lpTemp);

return(TRUE);

}

枚舉設備通常都是在對話框初始化的時候才進行的,我們假設hCombo就是combox句柄,hDlg就對話框的句柄,看看我們怎麽來枚舉設備的吧。

if (FAILED(DirectSoundCaptureEnumerate ((LPDSENUMCALLBACK)DSEnumProc,

(VOID*)&hCombo)))

{

EndDialog(hDlg, TRUE);

return(TRUE);

}

在這個例子中,combox的句柄作為參數傳遞到DirectSoundEnumerate函數中,然後又被傳遞到回調函數中,這個參數妳可以是妳想傳遞的任意的32位值。

註:第壹個被枚舉的設備通常稱為Primary sound driver,並且回調函數的lpGUID為NULL,這個設備就是用戶通過控制面板設置的缺省的錄音聲音設備,

創建設備對象

妳可以通過DirectSoundCaptureCreate8或者DirectSoundFullDuplexCreate8函數直接創建設備對象,該函數返回壹個指向IDirectSoundCapture8接口的指針

if( FAILED( hr = CoInitialize(NULL) ) )

return hr;

if(pDeviceGuid)

{

 if(FAILED( hr = DirectSoundCaptureCreate(pDeviceGuid,&g_pDSCapture,NULL)))

return hr;

}

else

{

 if(FAILED(hr= DirectSoundCaptureCreate(&DSDEVID_DefaultCapture ,&g_pDSCapture,NULL)))

return hr

}

其中pDeviceGuid是從枚舉的combox中選擇的設備的ID。

現在創建了設備對象妳可以通過IDirectSoundCapture8::GetCaps方法來獲取錄音設備的性能,這個函數的參數是壹個DSCCAPS類型的結構,在傳遞這個參數之前,壹定要初始化該結構的dwSize成員變量。同時,妳可以通過這個結構返回設備支持的聲道數,以及類似WAVEINCAPS結構的其他設備屬性

創建錄音的緩沖區對象

我們可以通過IDirectSoundCapture8::CreateCaptureBuffer來創建壹個錄音的buffer對象,這個函數的壹個參數采用DSCBUFFERDESC類型的結構來說明buffer的壹些特性,這個結構的最後壹個成員變量是壹個WAVEFORMATEX結構,這個結構壹定要初始化成泥需要的wav格式。

說明壹下,如果妳的應用程序壹邊播放的同時進行錄制,如果妳錄制的buffer格式和妳的主緩沖buffer不壹樣,那麽妳創建錄制buffer對象就會失敗,原因在於,有些聲卡只支持壹種時鐘,不能同時支持錄音和播放同時以兩種不同的格式進行。

下面的函數,演示了如何創建壹個錄音的buffer對象,這個buffer對象能夠處理1秒的數據,註意,這裏傳遞的錄音設備對象參數壹定要通過DirectSoundCaptureCreate8來創建,而不是早期的DirectSoundCaptureCreate接口,否則,buffer對象不支持IDirectSoundCaptureBuffer8接口。

HRESULT CreateCaptureBuffer(LPDIRECTSOUNDCAPTURE8 pDSC,

LPDIRECTSOUNDCAPTUREBUFFER8* ppDSCB8)

{

 HRESULT hr;

 DSCBUFFERDESC dscbd;

 LPDIRECTSOUNDCAPTUREBUFFER pDSCB;

 WAVEFORMATEX wfx ={WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0};

 // wFormatTag, nChannels, nSamplesPerSec, mAvgBytesPerSec,

 // nBlockAlign, wBitsPerSample, cbSize

 if ((NULL == pDSC) || (NULL == ppDSCB8)) return E_INVALIDARG;

 dscbd.dwSize = sizeof(DSCBUFFERDESC);

 dscbd.dwFlags = 0;

 dscbd.dwBufferBytes = wfx.nAvgBytesPerSec;

 dscbd.dwReserved = 0;

 dscbd.lpwfxFormat = &wfx; //設置錄音用的wave格式

 dscbd.dwFXCount = 0;

 dscbd.lpDSCFXDesc = NULL;

 if (SUCCEEDED(hr = pDSC->CreateCaptureBuffer(&dscbd, &pDSCB, NULL)))

 {

hr = pDSCB->QueryInterface(IID_IDirectSoundCaptureBuffer8, (LPVOID*)ppDSCB8);

pDSCB->Release();

 }

 return hr;

}

妳可以通過IDirectSoundCaptureBuffer8::GetCaps方法來獲取錄音buffer的大小,但壹定要記得初始化DSCBCAPS結構類型參數的dwSize成員變量。

為了獲取buffer中數據的格式,妳可以通過IDirectSoundCaptureBuffer8::GetFormat.方法來獲取buffer中的數據格式,這個函數通過WAVEFORMATEX結構返回音頻數據的信息,如果我們想知道壹個錄音buffer目前的狀態如何,可以通過IDirectSoundCaptureBuffer8::GetStatus來獲取,這個函數通過壹個DWORD類型的參數來表示該buffer是否正在錄音,

IDirectSoundCaptureBuffer8::GetCurrentPosition方法可以獲取buffer中read指針和錄制指針的偏差。Read指針指向填充到該buffer中的數據的最末端,capture指針則指向復制到硬件的數據的末端,妳read指針指向的前段數據都是安全數據,妳都可以安全的復制。

錄音buffer對象通知機制

為了安全的定期的從錄音buffer中copy數據,妳的應用程序就要知道,什麽時候read指針指向了特定的位置,壹個方法是通過IDirectSoundCaptureBuffer8::GetCurrentPosition.方法來獲取read指針的位置,另外壹個更有效的方法采用通知機制,通過IDirectSoundNotify8::SetNotificationPositions方法,妳可以設置任何壹個小於buffer的位置來觸發壹個事件,切記,當buffer正在running的時候,不要設置。

如何來設置壹個觸發事件呢,首先要得到IDirectSoundNotify8接口指針,妳可以通過buffer對象的QuerInterface來獲取這個指針接口,對於妳指定的任何壹個position,妳都要通過CreateEvent方法,創建壹個win32內核對象, 然後將內核對象的句柄賦給DSBPOSITIONNOTIFY結構的hEventNotify成員,通過該結構的dwOffset來設置需要通知的位置在buffer中的偏移量。

最後將這個結構或者結構數組,傳遞給SetNotificationPositions函數,下面的例子設置了NUM_REC_NOTIFICATIONS個通知,當position達到g_dwNotifySize時會觸發壹個通知,依次類推。

HRESULT InitNotifications()

{

 HRESULT hr ;

 g_hNotificationEvent = CreateEvent(NULL,FALSE,FALSE,NULL); //創建事件

 if(g_pDSBCapture == NULL)

return E_FAIL;

 if(FAILED(hr = g_pDSBCapture ->QueryInterface(IID_IDirectSoundNotify,(VOID**)&g_pDSNotify)))

return hr;

 for( INT i = 0; i < NUM_REC_NOTIFICATIONS; i++ )

 {

g_aPosNotify[i].dwOffset = (g_dwNotifySize * i) + g_dwNotifySize - 1;

g_aPosNotify[i].hEventNotify = g_hNotificationEvent;

 }

 if(FAILED( hr =g_pDSNotify->SetNotificationPositions( NUM_REC_NOTIFICATIONS, g_aPosNotify ) ) )

return hr;

 return S_OK;

}

  • 上一篇:超級計算機操作系統如何分布?是每個節點配壹個系統還是總體用壹系統
  • 下一篇:百度SSP媒體是什麽,誰通俗說壹下,網上的說的很泛
  • copyright 2024編程學習大全網