Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1002 lines
32 KiB
C++
1002 lines
32 KiB
C++
//----------------------------------------------------------------------------
|
|
// 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 <windows.h>
|
|
#include <basetsd.h>
|
|
#include <commdlg.h>
|
|
#include <mmreg.h>
|
|
#include <dxerr8.h>
|
|
#include <dsound.h>
|
|
#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;
|
|
}
|
|
|
|
|
|
|