Cómo utilizar DirectSound para realizar grabaciones con tarjeta de sonido
Hemos estudiado el desarrollo de Directshow, pero no hemos estudiado Dsound en detalle. Solo sabemos que Dsound es para desarrollo de audio. Siempre pensamos que tiene una estructura similar al sistema Dshow. , descubrimos que en realidad son cosas completamente diferentes. DirectSound también se basa en COM, pero no tiene tantos filtros como Dshow.
Sin más preámbulos, echemos un vistazo a lo que DirectSound puede hacer por nosotros.
1. Reproduce archivos o recursos de audio en formato WAVE.
2. Se pueden reproducir varios audios al mismo tiempo.
3. Asigne sonidos de alta prioridad a buffers controlados por hardware
4. Reproduzca sonido estéreo 3D
5. , cambiar dinámicamente los parámetros de efectos especiales, etc.
6. Grabe el sonido del micrófono u otro dispositivo de entrada de audio en formato de forma de onda.
DirectSound puede hacer tantas cosas que dudo que DirectSound encapsule las funciones de las series mmio y wav. . Porque estas API de bajo nivel también pueden hacer estas cosas. Aquí, discutiremos principalmente si usa DirectSound para grabarlo y guardarlo como un archivo en formato wave.
Antes de comenzar, presentaremos tres objetos muy importantes que DirectSound utiliza para grabar:
-IDirectSoundCapture8, un objeto de dispositivo creado en función del dispositivo en el que estás grabando, que te permite obtener las propiedades del dispositivo.
-IDirectSoundCaptureBuffer8, objeto de búfer, este objeto es creado por el objeto del dispositivo y se utiliza principalmente para operar datos de audio
-IDirectSoundNotify8, objeto de notificación de eventos, este objeto se utiliza para notificar a la aplicación del buffer District obtiene los datos, los escribe en un archivo y los guarda.
La idea principal de usar la grabación DirectSound es crear un objeto de dispositivo basado en el dispositivo de grabación seleccionado y luego crear un objeto de búfer auxiliar a través del objeto del dispositivo. Cuando comienza la grabación, el dispositivo escribe datos en él. el búfer y la aplicación La función de grabación se puede realizar escribiendo activamente los datos leídos del búfer en el archivo. Presentemos brevemente la función de notificación de dsound. La aplicación creará un objeto de notificación, luego indicará el objeto de notificación y luego establecerá la posición de notificación (posición). Por ejemplo, el tamaño del búfer es de 4000 bytes. desea usarlo como datos Cuando se alcance la mitad del búfer, se le puede notificar que comience a copiar datos. En este momento, puede configurar la posición de notificación en 2000. La posición de notificación se puede configurar arbitrariamente. la posición que establezca, notificará a la aplicación que copie los datos del búfer al copiarlos a un archivo, el búfer se usa circularmente, cuando el búfer se llena, comienza desde el principio de los datos, por lo que el búfer es una lectura simultánea. proceso de escritura.
A continuación presentaré los pasos principales de la grabación, que pueden aclarar su pensamiento
1. Enumere los dispositivos que se grabarán
2. un objeto de dispositivo para el dispositivo seleccionado
3. Utilice el objeto de dispositivo para crear un objeto de búfer
4. Establezca el mecanismo de notificación
5. hilo con Se utiliza para escribir los datos de escritura en el búfer en el archivo.
Primero, definamos los datos utilizados
LPDIRECTSOUNDCAPTURE8 g_pDSCapture = NULL; //puntero de objeto de dispositivo
LPDIRECTSOUNDCAPTUREBUFFER g_pDSBCapture = NULL; //puntero de objeto de búfer
p>LPDIRECTSOUNDNOTIFY8 g_pDSNotify = NULL; // Interfaz de objeto para configurar notificaciones
GUID g_guidCaptureDevice = GUID_NULL // ID del dispositivo
BOOL g_ bRecording = FALSE; está en progreso
WAVEFORMATEX g_wfxInput; //Formato de audio de entrada
DSBPOSITIONNOTIFY g_aPosNotify[NUM_REC_NOTIFICATIONS 1] //Establecer matriz de indicadores de notificación
HANDLE g_hNotificationEvent;/ / Evento de notificación
BOOL g_abInputFormatSupported[20];
DWORD g_dwCaptureBufferSize; //Tamaño del búfer de registro
DWORD g_dwNextCaptureOffset; // posición de desplazamiento
DWORD g_dwNotifySize; // notificar posición
CWaveFile* g_pWaveFile;
Enumerar dispositivos para grabar
Si su programa solo quiere grabar sonido en el dispositivo predeterminado , no es necesario enumerar todos los dispositivos de grabación en el sistema. Cuando llama a DirectSoundCaptureCreate8 u otras funciones DirectSoundFullDuplexCreate8, en realidad especifica un dispositivo de grabación predeterminado.
Por supuesto, debes enumerar todos los dispositivos en el sistema, por ejemplo, si tu aplicación no admite todos los dispositivos de salida, o si tu aplicación requiere dos o más dispositivos, o si quieres que el usuario elija el propio dispositivo de salida.
Para enumerar dispositivos, primero debe definir una función de devolución de llamada que pueda ser llamada por cada dispositivo en el sistema. Puede hacer cualquier cosa con cada función. No hay restricciones en el nombre de la función. La función debe modelarse a partir de DSEnumCallback, donde la función de devolución de llamada devuelve VERDADERO si la enumeración no se completa o VERDADERO si se completa la enumeración.
El siguiente es un ejemplo de una función de devolución de llamada que agrega cada dispositivo enumerado al combox y guarda el GUID del dispositivo en un proyecto. Los primeros tres parámetros de la función los determina el dispositivo. , el cuarto parámetro es la función DirectSoundCaptureEnumerate. Este parámetro puede ser cualquier valor de 32 bits. En este caso, es el identificador del cuadro combinado
BOOL CALLBACK DSEnumProc(LPGUID lpGUID,
<). p >LPCTSTR lpszDesc,LPCTSTR lpszDrvName,
LPVOID lpContext )
{
HWND hCombo = (HWND) lpContext ;
LPGUID lpTemp = NULL;
if (lpGUID != NULL) // NULL solo se aplica al "Controlador de sonido principal".
{
if ((lpTemp = (LPGUID)malloc(sizeof(GUID))) == NULL)
{
return(TRUE);
}
memcpy(lpTemp, lpGUID, sizeof(GUID)) sizeof(GUID));
}
// El siguiente código es principalmente para agregar el dispositivo a CComboBox. De hecho, puede pasar directamente el puntero de CComboBox para agregarlo directamente. El siguiente es el método utilizado para enviar mensajes a la ventana combinada.
ComboBox_ AddString (hCombo, lpszDesc);
ComboBox_SetItemData(hCombo,
ComboBox_FindString(hCombo, 0, lpszDesc),
lpTemp ); p>
free(lpTemp);
return(TRUE);
}
La enumeración de dispositivos generalmente se realiza al inicializar el cuadro de diálogo, así que supongamos que hCombo es el identificador del cuadro combinado y hDlg es el identificador del cuadro de diálogo, y luego observe cómo enumerar los dispositivos.
if (FAILED(DirectSoundCaptureEnumerate ((LPDSENUMCALLBACK)DSEnumProc,
(VOID*)amp;hCombo)))
{
EndDialog(hDlg, TRUE);
return(TRUE);
}
En este ejemplo, el identificador combox se pasa como parámetro a la función DirectSoundEnumerate y luego se pasa a la función de devolución de llamada. Este parámetro puede ser cualquier valor de 32 bits que desee.
Nota: El primer dispositivo enumerado generalmente se denomina controlador de sonido principal y su lpGUID en la función de devolución de llamada está vacío. Este es el dispositivo de grabación de sonido predeterminado configurado por el usuario a través del panel de control.
Crear un objeto de dispositivo
Puedes crear un objeto de dispositivo directamente a través de la función DirectSoundCaptureCreate8 o DirectSoundFullDuplexCreate8, que devolverá un puntero a la interfaz IDirectSoundCapture8
if( FALLADO( hr = CoInitialize(NULL) )))
return hr;
if(pDeviceGuid)
{
if( FALLADO ( hr = DirectSoundCaptureCreate(pDeviceGuid, amp; g_ pDSCapture, NULL)))
return hr;
}
else
{
if(FAILED(hr = DirectSoundCaptureCreate(amp;DSDEVID_DefaultCapture,amp;g_pDSCapture,NULL)))
return hr
}
donde pDeviceGuid es el ID del dispositivo seleccionado en el cuadro combinado de enumeración.
Ahora que se ha creado el objeto del dispositivo, puede crear el objeto a través del método IDirectSoundCapture8::GetCaps para obtener el rendimiento del dispositivo de grabación. El parámetro de esta función es una estructura de tipo DSCCAPS. Al pasar este parámetro, asegúrese de inicializar la variable miembro dwSize de esta estructura. Además, puede utilizar esta estructura para devolver el número de canales admitidos por el dispositivo y otras propiedades del dispositivo similares a la estructura WAVEINCAPS
Crear un objeto de búfer para grabar
Podemos use IDirectSoundCapture8: :CreateCaptureBuffer para crear un objeto de búfer de grabación. Un parámetro de esta función usa una estructura de tipo DSCBUFFERDESC para describir algunas características del búfer. La última variable miembro de la estructura es la estructura WAVEFORMATEX, que debe inicializarse al formato wav. requerido por el barro.
Para ser claros, si su aplicación está grabando mientras se reproduce y si está grabando usando un formato de búfer diferente al búfer principal, entonces no podrá crear un objeto de búfer de grabación. La razón es que algunas tarjetas de sonido sólo admiten un tipo de reloj y no pueden admitir la grabación y reproducción de dos formatos diferentes al mismo tiempo.
La siguiente función demuestra cómo crear un objeto de búfer de grabación que puede manejar 1 segundo de datos. Tenga en cuenta que los parámetros del objeto del dispositivo de grabación pasados aquí deben crearse a través de DirectSoundCaptureCreate8 en lugar de la interfaz DirectSoundCaptureCreate anterior; de lo contrario, el objeto de búfer no admitirá la interfaz 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.dwFXCount = 0; lpDSCFXDesc = NULL;
if (SUCCEEDED(hr = pDSC-gt;CreateCaptureBuffer(amp;dscbd, amp;pDSCB, NULL)))
{
hr = pDSCB-gt.QueryInterface(IID_IDirectSoundCaptureBuffer8, (LPVOID*)ppDSCB8);
pDSCB-gt(); ;
}
Puede obtener el tamaño del búfer de grabación a través del método IDirectSoundCaptureBuffer8::GetCaps, pero asegúrese de inicializar la variable miembro dwSize del parámetro de tipo de estructura DSCBCAPS. .
Para obtener el formato de los datos en el búfer, puede utilizar el método IDirectSoundCaptureBuffer8::GetFormat. Esta función devuelve la información de los datos de audio a través del búfer. Estructura WAVEFORMATEX Si queremos saber el formato de la grabación podemos obtener el estado actual a través de IDirectSoundCaptureBuffer8::GetStatus. Esta función requiere un parámetro DWORD para indicar si el buffer está grabando.
IDirectSoundCaptureBuffer8::.
El método IDirectSoundCaptureBuffer8::: obtiene la desviación entre el puntero de lectura y el puntero de captura en el buffer.
Mecanismo de notificación de objetos del búfer de grabación
Para copiar periódicamente datos de forma segura desde el búfer de grabación, su aplicación necesita saber cuándo el puntero de lectura apunta a una ubicación específica. El método IDirectSoundCaptureBuffer8::GetCurrentPosition.GetCurrentPosition. se utiliza para obtener la posición del puntero de lectura. Otro método más eficaz es utilizar el mecanismo de notificación. A través del método IDirectSoundNotify8::SetNotificationPositions, puede establecer cualquier posición más pequeña que el búfer para activar. el evento. Recuerde no configurarlo mientras el búfer se esté ejecutando.
¿Cómo configurar el evento desencadenante? Primero, necesita obtener el puntero de la interfaz IDirectSoundNotify8, que puede obtener a través de QuerInterface del objeto de búfer.
Objeto del kernel Win32, y luego asigne el identificador del objeto del kernel al miembro hEventNotify de la estructura DSBPOSITIONNOTIFY y establezca el desplazamiento de la posición que se notificará en el búfer a través del dwOffset de la estructura.
Finalmente, la estructura o conjunto de estructuras se pasará a la función SetNotificationPositions; el siguiente ejemplo establece NUM_REC_NOTIFICATIONS para las notificaciones que se activan cuando la posición alcanza g_dwNotifySize, y así sucesivamente.
HRESULT InitNotifications()
{
HRESULT hr;
g_hNotificationEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /Crear evento
if(g_pDSBCapture == NULL)
return E_FAIL;
if(FAILED(hr = g_pDSBCapture -gt;QueryInterface(IID_IDirectSoundNotify, (VOID ** )amp; g_pDSNotify)))
return hr;
for( INT i = 0; i lt; NUM_REC_NOTIFICATIONS; i )
{ p> p>
g_aPosNotify[i].dwOffset = (g_ dwNotifySize * i) g_dwNotifySize - 1
g_aPosNotify[i].hEventNotify = g_hNotificationEvent;
} p>
if(FAILED( hr =g_pDSNotify- gt;SetNotificationPositions(NUM_REC_NOTIFICATIONS, g_aPosNotify) )
return hr;
return S_OK;
}
p>