//---------------------------------------------------------------------------- // File: CaptureSound.cpp // // Desc: The CaptureSound sample shows how to use DirectSoundCapture to capture // sound into a wave file // // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved. //----------------------------------------------------------------------------- #define STRICT #include #include #include #include #include #include #include "resource.h" #include "DSUtil.h" //----------------------------------------------------------------------------- // Function-prototypes //----------------------------------------------------------------------------- HRESULT InitDirectSound( HWND hDlg, GUID* pDeviceGuid ); HRESULT FreeDirectSound(); INT_PTR CALLBACK DSoundEnumCallback( GUID* pGUID, LPSTR strDesc, LPSTR strDrvName, VOID* pContext ); INT_PTR CALLBACK DevicesDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ); INT_PTR CALLBACK FormatsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ); HRESULT OnInitDevicesDialog( HWND hDlg ); HRESULT OnInitFormatsDialog( HWND hDlg ); HRESULT ScanAvailableInputFormats(); VOID GetWaveFormatFromIndex( INT nIndex, WAVEFORMATEX* pwfx ); HRESULT FillFormatListBox( HWND hListBox, BOOL* aFormatSupported ); VOID ConvertWaveFormatToString( WAVEFORMATEX* pwfx, TCHAR* strFormatName ); HRESULT OnInputFormatBoxSelected( HWND hDlg ); HRESULT OnFormatsOK( HWND hDlg ); INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ); HRESULT OnInitMainDialog( HWND hDlg ); VOID OnSaveSoundFile( HWND hDlg ); HRESULT CreateCaptureBuffer( WAVEFORMATEX* pwfxInput ); HRESULT InitNotifications(); HRESULT StartOrStopRecord( BOOL bStartRecording ); HRESULT RecordCapturedData(); //----------------------------------------------------------------------------- // Defines, constants, and global variables //----------------------------------------------------------------------------- #define NUM_REC_NOTIFICATIONS 16 #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } LPDIRECTSOUNDCAPTURE g_pDSCapture = NULL; LPDIRECTSOUNDCAPTUREBUFFER g_pDSBCapture = NULL; LPDIRECTSOUNDNOTIFY g_pDSNotify = NULL; HINSTANCE g_hInst = NULL; GUID g_guidCaptureDevice = GUID_NULL; BOOL g_bRecording; WAVEFORMATEX g_wfxInput; DSBPOSITIONNOTIFY g_aPosNotify[ NUM_REC_NOTIFICATIONS + 1 ]; HANDLE g_hNotificationEvent; BOOL g_abInputFormatSupported[16]; DWORD g_dwCaptureBufferSize; DWORD g_dwNextCaptureOffset; DWORD g_dwNotifySize; CWaveFile* g_pWaveFile; //----------------------------------------------------------------------------- // Name: WinMain() // Desc: Entry point for the application. Since we use a simple dialog for // user interaction we don't need to pump messages. //----------------------------------------------------------------------------- INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, INT nCmdShow ) { HRESULT hr; DWORD dwResult; MSG msg; BOOL bDone; HWND hDlg; g_hInst = hInst; g_hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); // Create the main dialog box, but keep it hidden for now hDlg = CreateDialog( hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc ); // Display the formats dialog box, and show it dwResult = (DWORD)DialogBox( hInst, MAKEINTRESOURCE(IDD_DEVICES), NULL, DevicesDlgProc ); if( dwResult != IDOK ) { // The user canceled, so stop message pump, // and fall through to the cleanup code PostQuitMessage( 0 ); } else { // Init DirectSound if( FAILED( hr = InitDirectSound( hDlg, &g_guidCaptureDevice ) ) ) { DXTRACE_ERR( TEXT("InitDirectSound"), hr ); MessageBox( hDlg, "Error initializing DirectSound. Sample will now exit.", "DirectSound Sample", MB_OK | MB_ICONERROR ); PostQuitMessage( 0 ); } } // Display the formats dialog box, and show it if( dwResult == IDOK ) { dwResult = (DWORD)DialogBox( hInst, MAKEINTRESOURCE(IDD_FORMATS), NULL, FormatsDlgProc ); } if( dwResult != IDOK ) { // The user canceled, so stop message pump, // and fall through to the cleanup code PostQuitMessage( 0 ); } else { WAVEFORMATEX wfxInput; TCHAR strInputFormat[255]; HWND hInputFormatText; hInputFormatText = GetDlgItem( hDlg, IDC_MAIN_INPUTFORMAT_TEXT ); ZeroMemory( &wfxInput, sizeof(wfxInput)); g_pDSBCapture->GetFormat( &wfxInput, sizeof(wfxInput), NULL ); ConvertWaveFormatToString( &wfxInput, strInputFormat ); SetWindowText( hInputFormatText, strInputFormat ); g_bRecording = FALSE; ShowWindow( hDlg, SW_SHOW ); } bDone = FALSE; while( !bDone ) { dwResult = MsgWaitForMultipleObjects( 1, &g_hNotificationEvent, FALSE, INFINITE, QS_ALLEVENTS ); switch( dwResult ) { case WAIT_OBJECT_0 + 0: // g_hNotificationEvents[0] is signaled // This means that DirectSound just finished playing // a piece of the buffer, so we need to fill the circular // buffer with new sound from the wav file if( FAILED( hr = RecordCapturedData() ) ) { DXTRACE_ERR( TEXT("RecordCapturedData"), hr ); MessageBox( hDlg, "Error handling DirectSound notifications. " "Sample will now exit.", "DirectSound Sample", MB_OK | MB_ICONERROR ); bDone = TRUE; } break; case WAIT_OBJECT_0 + 1: // Windows messages are available while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { if( !IsDialogMessage( hDlg, &msg ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } if( msg.message == WM_QUIT ) bDone = TRUE; } break; } } // Stop the capture and read any data that was not caught by a notification StartOrStopRecord( FALSE ); // Clean up everything FreeDirectSound(); CloseHandle( g_hNotificationEvent ); return TRUE; } //----------------------------------------------------------------------------- // Name: InitDirectSound() // Desc: Initilizes DirectSound //----------------------------------------------------------------------------- HRESULT InitDirectSound( HWND hDlg, GUID* pDeviceGuid ) { HRESULT hr; ZeroMemory( &g_aPosNotify, sizeof(DSBPOSITIONNOTIFY) * (NUM_REC_NOTIFICATIONS + 1) ); g_dwCaptureBufferSize = 0; g_dwNotifySize = 0; g_pWaveFile = NULL; // Initialize COM if( FAILED( hr = CoInitialize(NULL) ) ) return DXTRACE_ERR( TEXT("CoInitialize"), hr ); // Create IDirectSoundCapture using the preferred capture device if( FAILED( hr = DirectSoundCaptureCreate( pDeviceGuid, &g_pDSCapture, NULL ) ) ) return DXTRACE_ERR( TEXT("DirectSoundCaptureCreate"), hr ); return S_OK; } //----------------------------------------------------------------------------- // Name: FreeDirectSound() // Desc: Releases DirectSound //----------------------------------------------------------------------------- HRESULT FreeDirectSound() { SAFE_DELETE( g_pWaveFile ); // Release DirectSound interfaces SAFE_RELEASE( g_pDSNotify ); SAFE_RELEASE( g_pDSBCapture ); SAFE_RELEASE( g_pDSCapture ); // Release COM CoUninitialize(); return S_OK; } //----------------------------------------------------------------------------- // Name: DevicesDlgProc() // Desc: Handles dialog messages for devices dlg box //----------------------------------------------------------------------------- INT_PTR CALLBACK DevicesDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { HRESULT hr; switch( msg ) { case WM_INITDIALOG: if( FAILED( hr = OnInitDevicesDialog( hDlg ) ) ) { DXTRACE_ERR( TEXT("OnInitDevicesDialog"), hr ); MessageBox( hDlg, "Error scanning DirectSoundCapture devices. " "Sample will now exit.", "DirectSound Sample", MB_OK | MB_ICONERROR ); EndDialog( hDlg, IDABORT ); } break; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDCANCEL: EndDialog( hDlg, IDCANCEL ); break; case IDOK: { HWND hCaptureDeviceCombo = GetDlgItem( hDlg, IDC_CAPTURE_DEVICE_COMBO ); // Get the index of the currently selected devices INT nCaptureIndex = (INT)SendMessage( hCaptureDeviceCombo, CB_GETCURSEL, 0, 0 ); // Get the GUID attached to the combo box item GUID* pCaptureGUID = (GUID*) SendMessage( hCaptureDeviceCombo, CB_GETITEMDATA, nCaptureIndex, 0 ); // Remember that guid if( pCaptureGUID ) g_guidCaptureDevice = *pCaptureGUID; EndDialog( hDlg, IDOK ); break; } default: return FALSE; // Didn't handle message } break; default: return FALSE; // Didn't handle message } return TRUE; // Handled message } //----------------------------------------------------------------------------- // Name: OnInitDevicesDialog() // Desc: Initializes the devices dialog //----------------------------------------------------------------------------- HRESULT OnInitDevicesDialog( HWND hDlg ) { // Enumerate the capture devices and place them in the combo box HWND hCaptureDeviceCombo = GetDlgItem( hDlg, IDC_CAPTURE_DEVICE_COMBO ); DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)DSoundEnumCallback, (VOID*)hCaptureDeviceCombo ); // Select the first device in the combo box SendMessage( hCaptureDeviceCombo, CB_SETCURSEL, 0, 0 ); return S_OK; } //----------------------------------------------------------------------------- // Name: FormatsDlgProc() // Desc: Handles dialog messages for formats dlg box //----------------------------------------------------------------------------- INT_PTR CALLBACK FormatsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { HRESULT hr; switch( msg ) { case WM_INITDIALOG: if( FAILED( hr = OnInitFormatsDialog( hDlg ) ) ) { DXTRACE_ERR( TEXT("OnInitFormatsDialog"), hr ); MessageBox( hDlg, "Error scanning DirectSound formats. " "Sample will now exit.", "DirectSound Sample", MB_OK | MB_ICONERROR ); EndDialog( hDlg, IDABORT ); } break; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDCANCEL: EndDialog( hDlg, IDCANCEL ); break; case IDOK: if( FAILED( hr = OnFormatsOK( hDlg ) ) ) { DXTRACE_ERR( TEXT("OnFormatsOK"), hr ); MessageBox( hDlg, "Error accepting DirectSound formats. " "Sample will now exit.", "DirectSound Sample", MB_OK | MB_ICONERROR ); EndDialog( hDlg, IDABORT ); } break; case IDC_FORMATS_INPUT_LISTBOX: OnInputFormatBoxSelected( hDlg ); break; default: return FALSE; // Didn't handle message } break; default: return FALSE; // Didn't handle message } return TRUE; // Handled message } //----------------------------------------------------------------------------- // Name: OnInitFormatsDialog() // Desc: Initializes the formats dialog //----------------------------------------------------------------------------- HRESULT OnInitFormatsDialog( HWND hDlg ) { HRESULT hr; if( FAILED( hr = ScanAvailableInputFormats() ) ) return DXTRACE_ERR( TEXT("ScanAvailableInputFormats"), hr ); HWND hInputList = GetDlgItem( hDlg, IDC_FORMATS_INPUT_LISTBOX ); FillFormatListBox( hInputList, g_abInputFormatSupported ); return S_OK; } //----------------------------------------------------------------------------- // Name: ScanAvailableInputFormats() // Desc: Tests to see if 16 different standard wave formats are supported by // the capture device //----------------------------------------------------------------------------- HRESULT ScanAvailableInputFormats() { HRESULT hr; WAVEFORMATEX wfx; HCURSOR hCursor; DSCBUFFERDESC dscbd; LPDIRECTSOUNDCAPTUREBUFFER pDSCaptureBuffer = NULL; // This might take a second or two, so throw up the hourglass hCursor = GetCursor(); SetCursor( LoadCursor( NULL, IDC_WAIT ) ); ZeroMemory( &wfx, sizeof(wfx)); wfx.wFormatTag = WAVE_FORMAT_PCM; ZeroMemory( &dscbd, sizeof(dscbd) ); dscbd.dwSize = sizeof(dscbd); // Try 16 different standard formats to see if they are supported for( INT iIndex = 0; iIndex < 16; iIndex++ ) { GetWaveFormatFromIndex( iIndex, &wfx ); // To test if a capture format is supported, try to create a // new capture buffer using a specific format. If it works // then the format is supported, otherwise not. dscbd.dwBufferBytes = wfx.nAvgBytesPerSec; dscbd.lpwfxFormat = &wfx; if( FAILED( hr = g_pDSCapture->CreateCaptureBuffer( &dscbd, &pDSCaptureBuffer, NULL ) ) ) g_abInputFormatSupported[ iIndex ] = FALSE; else g_abInputFormatSupported[ iIndex ] = TRUE; SAFE_RELEASE( pDSCaptureBuffer ); } SetCursor( hCursor ); return S_OK; } //----------------------------------------------------------------------------- // Name: GetWaveFormatFromIndex() // Desc: Returns 16 different wave formats based on nIndex //----------------------------------------------------------------------------- VOID GetWaveFormatFromIndex( INT nIndex, WAVEFORMATEX* pwfx ) { INT iSampleRate = nIndex / 4; INT iType = nIndex % 4; switch( iSampleRate ) { case 0: pwfx->nSamplesPerSec = 8000; break; case 1: pwfx->nSamplesPerSec = 11025; break; case 2: pwfx->nSamplesPerSec = 22050; break; case 3: pwfx->nSamplesPerSec = 44100; break; } switch( iType ) { case 0: pwfx->wBitsPerSample = 8; pwfx->nChannels = 1; break; case 1: pwfx->wBitsPerSample = 16; pwfx->nChannels = 1; break; case 2: pwfx->wBitsPerSample = 8; pwfx->nChannels = 2; break; case 3: pwfx->wBitsPerSample = 16; pwfx->nChannels = 2; break; } pwfx->nBlockAlign = pwfx->nChannels * ( pwfx->wBitsPerSample / 8 ); pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } //----------------------------------------------------------------------------- // Name: FillFormatListBox() // Desc: Fills the format list box based on the availible formats //----------------------------------------------------------------------------- HRESULT FillFormatListBox( HWND hListBox, BOOL* aFormatSupported ) { TCHAR strFormatName[255]; WAVEFORMATEX wfx; DWORD dwStringIndex; SendMessage( hListBox, LB_RESETCONTENT, 0, 0 ); for( INT iIndex = 0; iIndex < 16; iIndex++ ) { if( aFormatSupported[ iIndex ] ) { // Turn the index into a WAVEFORMATEX then turn that into a // string and put the string in the listbox GetWaveFormatFromIndex( iIndex, &wfx ); ConvertWaveFormatToString( &wfx, strFormatName ); dwStringIndex = (DWORD)SendMessage( hListBox, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strFormatName ); SendMessage( hListBox, LB_SETITEMDATA, dwStringIndex, iIndex ); } } return S_OK; } //----------------------------------------------------------------------------- // Name: ConvertWaveFormatToString() // Desc: Converts a wave format to a text string //----------------------------------------------------------------------------- VOID ConvertWaveFormatToString( WAVEFORMATEX* pwfx, TCHAR* strFormatName ) { wsprintf( strFormatName, TEXT("%u Hz, %u-bit %s"), pwfx->nSamplesPerSec, pwfx->wBitsPerSample, ( pwfx->nChannels == 1 ) ? TEXT("Mono") : TEXT("Stereo") ); } //----------------------------------------------------------------------------- // Name: OnInputFormatBoxSelected() // Desc: Enables the OK button when there is a selection //----------------------------------------------------------------------------- HRESULT OnInputFormatBoxSelected( HWND hDlg ) { HWND hInputList = GetDlgItem( hDlg, IDC_FORMATS_INPUT_LISTBOX ); HWND hOK = GetDlgItem( hDlg, IDOK ); if( SendMessage( hInputList, LB_GETCURSEL, 0, 0 ) != -1 ) EnableWindow( hOK, TRUE ); else EnableWindow( hOK, FALSE ); return S_OK; } //----------------------------------------------------------------------------- // Name: OnFormatsOK() // Desc: Creates a capture buffer format based on what was selected //----------------------------------------------------------------------------- HRESULT OnFormatsOK( HWND hDlg ) { HRESULT hr; DWORD dwInputSelect; DWORD dwInputWavIndex; HWND hInputList; hInputList = GetDlgItem( hDlg, IDC_FORMATS_INPUT_LISTBOX ); dwInputSelect = (DWORD)SendMessage( hInputList, LB_GETCURSEL, 0, 0 ); dwInputWavIndex = (DWORD)SendMessage( hInputList, LB_GETITEMDATA, dwInputSelect, 0 ); ZeroMemory( &g_wfxInput, sizeof(g_wfxInput)); g_wfxInput.wFormatTag = WAVE_FORMAT_PCM; GetWaveFormatFromIndex( dwInputWavIndex, &g_wfxInput ); if( FAILED( hr = CreateCaptureBuffer( &g_wfxInput ) ) ) return DXTRACE_ERR( TEXT("CreateCaptureBuffer"), hr ); EndDialog( hDlg, IDOK ); return S_OK; } //----------------------------------------------------------------------------- // Name: MainDlgProc() // Desc: Handles dialog messages //----------------------------------------------------------------------------- INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { HRESULT hr; switch( msg ) { case WM_INITDIALOG: OnInitMainDialog( hDlg ); break; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDCANCEL: PostQuitMessage( 0 ); EndDialog( hDlg, IDCANCEL ); break; case IDC_SOUNDFILE: OnSaveSoundFile( hDlg ); break; case IDC_RECORD: g_bRecording = !g_bRecording; if( FAILED( hr = StartOrStopRecord( g_bRecording ) ) ) { DXTRACE_ERR( TEXT("StartOrStopRecord"), hr ); MessageBox( hDlg, "Error with DirectSoundCapture buffer." "Sample will now exit.", "DirectSound Sample", MB_OK | MB_ICONERROR ); PostQuitMessage( 0 ); EndDialog( hDlg, IDABORT ); } if( !g_bRecording ) EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), FALSE ); break; default: return FALSE; // Didn't handle message } break; default: return FALSE; // Didn't handle message } return TRUE; // Handled message } //----------------------------------------------------------------------------- // Name: OnInitMainDialog() // Desc: Initializes the main dialog //----------------------------------------------------------------------------- HRESULT OnInitMainDialog( HWND hDlg ) { // Load the icon HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDR_MAINFRAME ) ); // Set the icon for this dialog. SendMessage( hDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon ); // Set big icon SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon ); // Set small icon EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), FALSE); SetDlgItemText( hDlg, IDC_FILENAME, TEXT("No file loaded.") ); return S_OK; } //----------------------------------------------------------------------------- // Name: DSoundEnumCallback() // Desc: Enumeration callback called by DirectSoundEnumerate //----------------------------------------------------------------------------- INT_PTR CALLBACK DSoundEnumCallback( GUID* pGUID, LPSTR strDesc, LPSTR strDrvName, VOID* pContext ) { // Set aside static storage space for 20 audio drivers static GUID AudioDriverGUIDs[20]; static DWORD dwAudioDriverIndex = 0; GUID* pTemp = NULL; if( pGUID ) { if( dwAudioDriverIndex >= 20 ) return TRUE; pTemp = &AudioDriverGUIDs[dwAudioDriverIndex++]; memcpy( pTemp, pGUID, sizeof(GUID) ); } HWND hSoundDeviceCombo = (HWND)pContext; // Add the string to the combo box SendMessage( hSoundDeviceCombo, CB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strDesc ); // Get the index of the string in the combo box INT nIndex = (INT)SendMessage( hSoundDeviceCombo, CB_FINDSTRING, 0, (LPARAM) (LPCTSTR) strDesc ); // Set the item data to a pointer to the static guid stored in AudioDriverGUIDs SendMessage( hSoundDeviceCombo, CB_SETITEMDATA, nIndex, (LPARAM) pTemp ); return TRUE; } //----------------------------------------------------------------------------- // Name: OnSaveSoundFile() // Desc: Called when the user requests to save to a sound file //----------------------------------------------------------------------------- VOID OnSaveSoundFile( HWND hDlg ) { HRESULT hr; static TCHAR strFileName[MAX_PATH] = TEXT(""); static TCHAR strPath[MAX_PATH] = TEXT(""); // Setup the OPENFILENAME structure OPENFILENAME ofn = { sizeof(OPENFILENAME), hDlg, NULL, TEXT("Wave Files\0*.wav\0All Files\0*.*\0\0"), NULL, 0, 1, strFileName, MAX_PATH, NULL, 0, strPath, TEXT("Save Sound File"), OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOREADONLYRETURN, 0, 0, TEXT(".wav"), 0, NULL, NULL }; // Get the default media path (something like C:\WINDOWS\MEDIA) if( '\0' == strPath[0] ) { GetWindowsDirectory( strPath, MAX_PATH ); if( strcmp( &strPath[strlen(strPath)], TEXT("\\") ) ) strcat( strPath, TEXT("\\") ); strcat( strPath, TEXT("MEDIA") ); } if( g_bRecording ) { // Stop the capture and read any data that // was not caught by a notification StartOrStopRecord( FALSE ); g_bRecording = FALSE; } // Update the UI controls to show the sound as loading a file EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), FALSE ); SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Saving file...") ); // Display the SaveFileName dialog. Then, try to load the specified file if( TRUE != GetSaveFileName( &ofn ) ) { SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Save aborted.") ); return; } SetDlgItemText( hDlg, IDC_FILENAME, TEXT("") ); SAFE_DELETE( g_pWaveFile ); g_pWaveFile = new CWaveFile; // Get the format of the capture buffer in g_wfxCaptureWaveFormat WAVEFORMATEX wfxCaptureWaveFormat; ZeroMemory( &wfxCaptureWaveFormat, sizeof(WAVEFORMATEX) ); g_pDSBCapture->GetFormat( &wfxCaptureWaveFormat, sizeof(WAVEFORMATEX), NULL ); // Load the wave file if( FAILED( hr = g_pWaveFile->Open( strFileName, &wfxCaptureWaveFormat, WAVEFILE_WRITE ) ) ) { DXTRACE_ERR( TEXT("Open"), hr ); SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Can not create wave file.") ); return; } // Update the UI controls to show the sound as the file is loaded SetDlgItemText( hDlg, IDC_FILENAME, strFileName ); EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), TRUE ); // Remember the path for next time strcpy( strPath, strFileName ); char* strLastSlash = strrchr( strPath, '\\' ); strLastSlash[0] = '\0'; } //----------------------------------------------------------------------------- // Name: CreateCaptureBuffer() // Desc: Creates a capture buffer and sets the format //----------------------------------------------------------------------------- HRESULT CreateCaptureBuffer( WAVEFORMATEX* pwfxInput ) { HRESULT hr; DSCBUFFERDESC dscbd; SAFE_RELEASE( g_pDSNotify ); SAFE_RELEASE( g_pDSBCapture ); // Set the notification size g_dwNotifySize = MAX( 1024, pwfxInput->nAvgBytesPerSec / 8 ); g_dwNotifySize -= g_dwNotifySize % pwfxInput->nBlockAlign; // Set the buffer sizes g_dwCaptureBufferSize = g_dwNotifySize * NUM_REC_NOTIFICATIONS; SAFE_RELEASE( g_pDSNotify ); SAFE_RELEASE( g_pDSBCapture ); // Create the capture buffer ZeroMemory( &dscbd, sizeof(dscbd) ); dscbd.dwSize = sizeof(dscbd); dscbd.dwBufferBytes = g_dwCaptureBufferSize; dscbd.lpwfxFormat = pwfxInput; // Set the format during creatation if( FAILED( hr = g_pDSCapture->CreateCaptureBuffer( &dscbd, &g_pDSBCapture, NULL ) ) ) return DXTRACE_ERR( TEXT("CreateCaptureBuffer"), hr ); g_dwNextCaptureOffset = 0; if( FAILED( hr = InitNotifications() ) ) return DXTRACE_ERR( TEXT("InitNotifications"), hr ); return S_OK; } //----------------------------------------------------------------------------- // Name: InitNotifications() // Desc: Inits the notifications on the capture buffer which are handled // in WinMain() //----------------------------------------------------------------------------- HRESULT InitNotifications() { HRESULT hr; if( NULL == g_pDSBCapture ) return E_FAIL; // Create a notification event, for when the sound stops playing if( FAILED( hr = g_pDSBCapture->QueryInterface( IID_IDirectSoundNotify, (VOID**)&g_pDSNotify ) ) ) return DXTRACE_ERR( TEXT("QueryInterface"), hr ); // Setup the notification positions 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; } // Tell DirectSound when to notify us. the notification will come in the from // of signaled events that are handled in WinMain() if( FAILED( hr = g_pDSNotify->SetNotificationPositions( NUM_REC_NOTIFICATIONS, g_aPosNotify ) ) ) return DXTRACE_ERR( TEXT("SetNotificationPositions"), hr ); return S_OK; } //----------------------------------------------------------------------------- // Name: StartOrStopRecord() // Desc: Starts or stops the capture buffer from recording //----------------------------------------------------------------------------- HRESULT StartOrStopRecord( BOOL bStartRecording ) { HRESULT hr; if( bStartRecording ) { // Create a capture buffer, and tell the capture // buffer to start recording if( FAILED( hr = CreateCaptureBuffer( &g_wfxInput ) ) ) return DXTRACE_ERR( TEXT("CreateCaptureBuffer"), hr ); if( FAILED( hr = g_pDSBCapture->Start( DSCBSTART_LOOPING ) ) ) return DXTRACE_ERR( TEXT("Start"), hr ); } else { // Stop the capture and read any data that // was not caught by a notification if( NULL == g_pDSBCapture ) return S_OK; // Stop the buffer, and read any data that was not // caught by a notification if( FAILED( hr = g_pDSBCapture->Stop() ) ) return DXTRACE_ERR( TEXT("Stop"), hr ); if( FAILED( hr = RecordCapturedData() ) ) return DXTRACE_ERR( TEXT("RecordCapturedData"), hr ); // Close the wav file SAFE_DELETE( g_pWaveFile ); } return S_OK; } //----------------------------------------------------------------------------- // Name: RecordCapturedData() // Desc: Copies data from the capture buffer to the output buffer //----------------------------------------------------------------------------- HRESULT RecordCapturedData() { HRESULT hr; VOID* pbCaptureData = NULL; DWORD dwCaptureLength; VOID* pbCaptureData2 = NULL; DWORD dwCaptureLength2; VOID* pbPlayData = NULL; UINT dwDataWrote; DWORD dwReadPos; DWORD dwCapturePos; LONG lLockSize; if( NULL == g_pDSBCapture ) return S_FALSE; if( NULL == g_pWaveFile ) return S_FALSE; if( FAILED( hr = g_pDSBCapture->GetCurrentPosition( &dwCapturePos, &dwReadPos ) ) ) return DXTRACE_ERR( TEXT("GetCurrentPosition"), hr ); lLockSize = dwReadPos - g_dwNextCaptureOffset; if( lLockSize < 0 ) lLockSize += g_dwCaptureBufferSize; // Block align lock size so that we are always write on a boundary lLockSize -= (lLockSize % g_dwNotifySize); if( lLockSize == 0 ) return S_FALSE; // Lock the capture buffer down if( FAILED( hr = g_pDSBCapture->Lock( g_dwNextCaptureOffset, lLockSize, &pbCaptureData, &dwCaptureLength, &pbCaptureData2, &dwCaptureLength2, 0L ) ) ) return DXTRACE_ERR( TEXT("Lock"), hr ); // Write the data into the wav file if( FAILED( hr = g_pWaveFile->Write( dwCaptureLength, (BYTE*)pbCaptureData, &dwDataWrote ) ) ) return DXTRACE_ERR( TEXT("Write"), hr ); // Move the capture offset along g_dwNextCaptureOffset += dwCaptureLength; g_dwNextCaptureOffset %= g_dwCaptureBufferSize; // Circular buffer if( pbCaptureData2 != NULL ) { // Write the data into the wav file if( FAILED( hr = g_pWaveFile->Write( dwCaptureLength2, (BYTE*)pbCaptureData2, &dwDataWrote ) ) ) return DXTRACE_ERR( TEXT("Write"), hr ); // Move the capture offset along g_dwNextCaptureOffset += dwCaptureLength2; g_dwNextCaptureOffset %= g_dwCaptureBufferSize; // Circular buffer } // Unlock the capture buffer g_pDSBCapture->Unlock( pbCaptureData, dwCaptureLength, pbCaptureData2, dwCaptureLength2 ); return S_OK; }