Abstract: This paper described in detail how the use of DirectSound sound card and microphone after the data capture, recording, and save it as a wave file.
Directshow have studied the development of the Dsound has not careful study, previously only know that Dsound development of the audio is done, and I have always thought that it almost Dshow structural system, after careful study and found that in fact they are completely different issues. Although based on COM DirectSound, but not as many as Dshow the filter component linked list.
Gossip less, the following DirectSound in the end help us look at what we do.
1, players WAVE format audio files or resources.
2, multiple audio can be broadcast simultaneously.
3, Assign high-priority sounds to hardware-controlled buffers
4, and 3-D sound player
5, in the voice of adding special effects such as echo and dynamic change parameters such as stunt
6, microphone or other audio input of the voice recording equipment into wave files
DirectSound can do so many things, here, I doubt it is not DirectSound package is a series mmio series and wav function. The bottom of the API because they are able to complete these things. Here we discuss the main, if we use Directsound recording, and the preservation of a wave format.
At the beginning of the work before the first recorded use of DirectSound on three very important target:
IDirectSoundCapture8, equipment object, in accordance with your recording equipment to create equipment object, the object can be obtained using equipment attributes.
IDirectSoundCaptureBuffer8, buffer object, the object by object creation equipment, mainly for operating audio data
IDirectSoundNotify8, events notification object, the object used to notify the application data will be removed from the buffer zone, writes the document saved.
Recording using DirectSound the main ideas is the first choice of recording equipment in accordance with established equipment object, and then through the creation of auxiliary equipment object buffer object and began recording time, equipment data into the buffer zone, the application of active data from the buffer zone written document can be read out to implement the recording function. Dsound briefly tell us about the notification, the application will create a circular object, and then will notify the state object, and then notify the set position (position), what is the location of this notice, such as the size of the buffer zone for the 4000 bytes, if you want to be half of the data to the buffer zone will be notified starting time copy of data, at this time you should be able to notify the position set for 2000, informed the position can be set when the data buffer to the location of your settings , it will notify the application will copy the data buffer to a file, the buffer zone is recycling, when the buffer is filled, the data will be starting from scratch filling Therefore, the side of the buffer zone is read, written by side process.
Now I talk about recording the main steps, so that we can more clearly some of the ideas
1, enumerated recording equipment
2, depending on the chosen object creates the device equipment
3, using equipment to create buffer object object
4, notification mechanism set up
5, the work of creating threads, will be used to buffer the data into documents.
First used to define what data
LPDIRECTSOUNDCAPTURE8 g_pDSCapture = NULL; / / equipment object pointer
LPDIRECTSOUNDCAPTUREBUFFER g_pDSBCapture = NULL; / / buffer object pointer
LPDIRECTSOUNDNOTIFY8 g_pDSNotify = NULL; / / notice to the target set Interface
GUID g_guidCaptureDevice = GUID_NULL; / / equipment id
BOOL g_bRecording = FALSE; / / whether it is recording
WAVEFORMATEX g_wfxInput; / / input audio formats
DSBPOSITIONNOTIFY g_aPosNotify [NUM_REC_NOTIFICATIONS + 1] / / set up an array of notice signs
HANDLE g_hNotificationEvent; / / notification event
BOOL g_abInputFormatSupported [20];
DWORD g_dwCaptureBufferSize; / / recording the size of the buffer zone
DWORD g_dwNextCaptureOffset; / / offset positions
DWORD g_dwNotifySize; / / notice position
CWaveFile * g_pWaveFile; / /
Enumerating recording equipment
If you just want the procedure from the default user equipment on the voice recording, then there is no need for a system to enumeration of all the recording equipment, DirectSoundCaptureCreate8 When you call a function or another DirectSoundFullDuplexCreate8 when, in fact designated on the default a default recording equipment.
Of course, in the following circumstances, you must be enumerated the equipment in the system, for example, your application does not support all of the output of equipment, or your application process requires two or more equipment, or do you want users to choose their own output device.
Enumerating equipment, you must first define a callback function, the callback function can be in the system to call each device, you can function at all to do anything, this function has not named any restrictions, but should function DSEnumCallback to prototype, if not enumerated end, the callback function will return TRUE if the enumeration end, for example, you find the right equipment, this function will return FALSE.
Below is an example of callback function, and this function will be enumerated for each device added to a combox, the equipment will be kept to a GUID item, the function parameters from the first three drivers to provide equipment, The fourth parameter is provided DirectSoundCaptureEnumerate function, the parameter can be any of the 32 value, this example is combox the handle,
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));
)
/ / The following code will be the main equipment is added to the CComboBox, in fact you will be completely direct transmission from CComboBox guide and direct add, is here used to send information combox window,
ComboBox_AddString (hCombo, lpszDesc);
ComboBox_SetItemData (hCombo,
ComboBox_FindString (hCombo, 0, lpszDesc)
LpTemp);
Free (lpTemp);
Return (TRUE);
)
Enumerating devices are usually initialized in the dialog box when it conducted, we assume that hCombo is combox handle, hDlg dialog on the handle, we look at how to enumeration equipment this.
If (FAILED (DirectSoundCaptureEnumerate ((LPDSENUMCALLBACK) DSEnumProc,
(VOID *) & hCombo)))
(
EndDialog (hDlg, TRUE);
Return (TRUE);
)
In this example, the combox handle as a parameter to DirectSoundEnumerate transfer function, and then be transmitted to the callback function, the parameters you can be you want to pass the value of arbitrary 32.
Note: The first was an enumeration of equipment commonly referred to as Primary sound driver, and the callback function lpGUID to NULL, the device is user control panel settings default voice recording equipment,
Object creation equipment
DirectSoundCaptureCreate8 or you can create a direct function DirectSoundFullDuplexCreate8 equipment object, the function returns a pointer pointing IDirectSoundCapture8 Interface
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 which is enumerated in the combox choice of equipment ID.
Now create the equipment you can IDirectSoundCapture8 Object:: GetCaps method to obtain recording equipment performance, the parameters of this function is a DSCCAPS type of structure, in the passing of this parameter before, we must initialize the variable structure dwSize members. At the same time, you can use this structure to support the return of equipment channels, as well as the structure of other similar equipment WAVEINCAPS attribute
Object creation of the buffer zone recording
We can IDirectSoundCapture8:: CreateCaptureBuffer to create a recording buffer object, the function of the parameters of a type of structure DSCBUFFERDESC used to illustrate some of the buffer, the final structure of a member variable is a WAVEFORMATEX structure, this must be the initial structure into mud wav format needs.
Elaborate, if your application at the same time while playing the record, if you recorded buffer formats and the main buffer buffer you do not, then you create recording buffer object will fail, because some only support a sound card Clock can not support the recording and playback at the same time in two different formats.
Below the function, demonstrated how to create a recording buffer object, and this buffer object will be able to handle one second of data, attention here transmission parameters of the recording equipment must pass DirectSoundCaptureCreate8 to create, rather than early DirectSoundCaptureCreate interface, otherwise, IDirectSoundCaptureBuffer8 buffer object does not support the interface.
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 ((pDSC == NULL) | | (ppDSCB8 == NULL)) return E_INVALIDARG;
Dscbd.dwSize = sizeof (DSCBUFFERDESC);
Dscbd.dwFlags = 0;
Dscbd.dwBufferBytes = wfx.nAvgBytesPerSec;
Dscbd.dwReserved = 0;
Dscbd.lpwfxFormat = &wfx; / / Set the recording format of the wave
Dscbd.dwFXCount = 0;
Dscbd.lpDSCFXDesc = NULL;
If (SUCCEEDED (hr = pDSC-> CreateCaptureBuffer (& dscbd, & pDSCB, NULL)))
(
PDSCB-hr => QueryInterface (IID_IDirectSoundCaptureBuffer8, (LPVOID *) ppDSCB8);
PDSCB-> Release ();
)
Return hr;
)
You can IDirectSoundCaptureBuffer8:: GetCaps ways to access the recording buffer size, but we must remember that initialization DSCBCAPS dwSize types of variable parameters.
In order to obtain the data buffer in the format, you can IDirectSoundCaptureBuffer8:: GetFormat. Buffer methods to obtain the data in the format, this function through WAVEFORMATEX audio data structure to return to the information, if we want to know that a recording buffer to the current state, can be IDirectSoundCaptureBuffer8:: GetStatus to access, this function through a DWORD type of parameters that the buffer whether it is recording,
IDirectSoundCaptureBuffer8:: GetCurrentPosition access buffer can be read in the guide and recording the deviation indicator. Read indicators pointing to the buffer filled with the data in the end, the indicators capture hardware at the data copied to the end, you read the indicators point to previous data are safe data, you should be able to copy security.
Recording buffer object notification mechanism
In order to secure regular copy from the recording of data in the buffer, you should be aware that the application, when read indicators point to a specific location, a method is adopted IDirectSoundCaptureBuffer8:: GetCurrentPosition. Read pointer method to obtain the location, and a more effective the method used notification mechanism through IDirectSoundNotify8:: SetNotificationPositions, you can set the buffer is less than any one location to trigger an event, bear in mind that when the buffer is running, not set up.
How to set up a trigger events, it must first be IDirectSoundNotify8 interface guideline, you can buffer object QuerInterface interface to access this guide, you specify any one position, you must CreateEvent through, the creation of a core win32 object, and then the kernel will assign object handles DSBPOSITIONNOTIFY structure hEventNotify members, the adoption of the structure need to be informed dwOffset to set the location of the buffer in the offset.
Finally, this structure or structure, the array passed to SetNotificationPositions function, the following examples set NUM_REC_NOTIFICATIONS months notice, when the position will be reached g_dwNotifySize trigger a notification, and so on.
HRESULT InitNotifications ()
(
HRESULT hr;
G_hNotificationEvent = CreateEvent (NULL, FALSE, FALSE, NULL); / / create events
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;
)
The work of creating threads, will be used to buffer the data into documents
:: CreateThread (NULL, 0, ThreadRecord, this, 0, NULL);
Look at the thread of the work it
DWORD WINAPI ThreadRecord (LPVOID lpParameter)
(
DWORD dwResult = 0;
G_bRecording = TRUE;
While (g_bRecording)
(
WaitForMultipleObjects dwResult = (1, & g_hNotificationEvent, FALSE, INFINITE);
Switch (dwResult)
(
Case WAIT_OBJECT_0 + 0:
RecordCapturedData ();
)
)
Return 0;
)
This thread has been the trigger for notification event when the data buffer zone set to fill the position, we will trigger threads, here there are mainly a function RecordCaptureedData (). This function mainly done the following things:
1) call IDirectSoundCaptureBuffer8:: Start buffer object to start work, through you, to give this function dwFlags send a DSCBSTART_LOOPING parameters that will work non-stop buffer, rather than when the buffer is full of Reclamation to stop work when the buffer is full , we will re-filling from scratch.
2) wait for the events to inform you expect, when the buffer is filled to a certain position, do you expect, would trigger notice.
3) When you receive the notice, you should call IDirectSoundCaptureBuffer8:: Lock locked bufer to a part of, remember, not to capture point-memory locked, you can call IDirectSoundCaptureBuffer8:: GetCurrentPosition method to obtain read pointer position. Lock function in the transfer to the parameters, you must specify the size of memory and offsets, this function will return to you locked the initial memory address, and block size.
4) locked in the memory copying data.
5) Copy to remember that after the completion of IDirectSoundCaptureBuffer8:: Unlock methods to unlock the memory.
6) before you stop recording, you can repeat repeated steps 2 to 5, if you wish to stop recording, and you can call IDirectSoundCaptureBuffer8:: Stop method. Wav file into the recording is used RIFF WAV file format documents in the file contains a series of chunks, the first to describe the information and data information, mmio win32API provide a series of functions for operating RIFF format files, but Directsound and did not provide literacy wav format function, but Directsound package in a category for operating CWaveFile wav file, you can open the first document to write information to write the data into the document, and close function writes the document the length of the closure of the paper. You can find the path DirectSound this type definition (SDK root) \ samples \ C + + \ Common \ Src \ Dsutil.cpp.
Below is the code, how to create a wav files
CWaveFile g_pWaveFile;
WAVEFORMATEX wfxInput;
ZeroMemory (& wfxInput, sizeof (wfxInput));
WfxInput.wFormatTag = WAVE_FORMAT_PCM;
WfxInput.nSamplesPerSec = 22050
WfxInput.wBitsPerSample = 8;
WfxInput.nChannels = 1;
WfxInput.nBlockAlign =
WfxInput.nChannels * (wfxInput.wBitsPerSample / 8);
WfxInput.nAvgBytesPerSec =
WfxInput.nBlockAlign * wfxInput.nSamplesPerSec;
G_pWaveFile = new CWaveFile;
If (FAILED (g_pWaveFile-> Open ( "mywave.wav", & wfxInput,
WAVEFILE_WRITE)))
(
G_pWaveFile-> Close ();
)
The following code is demonstrated on RecordCapturedData () function of the definition of integrity
HRESULT CCaptureSoundDlg:: RecordCapturedData ()
(
HRESULT hr;
VOID * pbCaptureData = NULL;
DWORD dwCaptureLength;
VOID * pbCaptureData2 = NULL;
DWORD dwCaptureLength2;
UINT dwDataWrote;
DWORD dwReadPos;
DWORD dwCapturePos;
LONG lLockSize;
If (g_pDSBCapture == NULL)
Return S_FALSE;
If (NULL == g_pWaveFile)
Return S_FALSE;
If (FAILED (hr = g_pDSBCapture-> GetCurrentPosition (& dwCapturePos, & dwReadPos)))
Return hr;
LLockSize = dwReadPos-g_dwNextCaptureOffset;
If (lLockSize <0)
LLockSize + = g_dwCaptureBufferSize;
/ / Locked memory size
/ / Here modulus is designed to make the data we read several times the size of the entire g_dwNotifySize This buffer is also notify the remaining in the multiplier
LLockSize -= (g_dwNotifySize lLockSize%);
If (lLockSize == 0)
Return S_FALSE;
/ / Locked memory
If (FAILED (hr = g_pDSBCapture-> Lock (g_dwNextCaptureOffset, lLockSize,
& PbCaptureData, & dwCaptureLength,
& PbCaptureData2, & dwCaptureLength2, 0L)))
Return hr;
/ / Copy the data in memory to wave paper
If (FAILED (hr = g_pWaveFile-> Write (dwCaptureLength, (BYTE *) pbCaptureData,
& DwDataWrote)))
Return hr;
/ / Offset mobile signs, the recycling movement
G_dwNextCaptureOffset + = dwCaptureLength;
G_dwNextCaptureOffset% = g_dwCaptureBufferSize; / / Circular buffer
If (pbCaptureData2! = NULL)
(
/ / Copy the data in memory to wave paper
If (FAILED (hr = g_pWaveFile-> Write (dwCaptureLength2, (BYTE *) pbCaptureData2,
& DwDataWrote)))
Return hr;
/ / Offset signs mobile
G_dwNextCaptureOffset + = dwCaptureLength2;
G_dwNextCaptureOffset% = g_dwCaptureBufferSize; / / Circular buffer
)
/ / Unlock memory
G_pDSBCapture-> Unlock (pbCaptureData, dwCaptureLength, pbCaptureData2, dwCaptureLength2);
Return S_OK;
)
Here explain IDirectSoundBuffer8:: Lock may be returned to the two addresses because you locked memory is the number of random, and sometimes you just lock the regional buffer contains the starting point and when that happens, you will return to the two addresses, move - example.
Suppose you lock the 30000 byte offset location for the 20,000 bytes, which is beginning here, if your buffer size of 40,000 bytes, then you will return to the four data:
The offset position memory address 20,000,
Migration from location to buffer the end of the most number of bytes, is 20,000, you should be read in the first address of the contents of 20,000 bytes
Offset to the address 0
From the beginning of the beginning of bytes, which is 10,000 bytes, you should address the second, that is from 0:00 start reading 10,000 bytes.
If we do not include 0.1, the last two numerical return to NULL and 0,
On the audio recording on DirectSound briefly introduced here, leaving msn: aooang@hotmail.com, and we welcome the exchange, I secrets of the blog, there are about a lot of DirectSound and DirectShow, and I translated DirectShow DirectSound the SDK documentation, welcome letter from, learning together. |