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>
1057 lines
29 KiB
C++
1057 lines
29 KiB
C++
//------------------------------------------------------------------------------
|
|
// File: AudioCapDlg.cpp
|
|
//
|
|
// Desc: DirectShow sample code - Implementation of CAudioCapDlg class for
|
|
// audio capture sample application.
|
|
//
|
|
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
#include "AudioCap.h"
|
|
#include "AudioCapDlg.h"
|
|
#include <atlbase.h>
|
|
|
|
#include <mtype.h>
|
|
#include "mfcutil.h"
|
|
#include "dshowutil.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CAboutDlg dialog used for App About
|
|
|
|
class CAboutDlg : public CDialog
|
|
{
|
|
public:
|
|
CAboutDlg();
|
|
|
|
// Dialog Data
|
|
//{{AFX_DATA(CAboutDlg)
|
|
enum { IDD = IDD_ABOUTBOX };
|
|
//}}AFX_DATA
|
|
|
|
// ClassWizard generated virtual function overrides
|
|
//{{AFX_VIRTUAL(CAboutDlg)
|
|
protected:
|
|
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
|
|
//}}AFX_VIRTUAL
|
|
|
|
// Implementation
|
|
protected:
|
|
//{{AFX_MSG(CAboutDlg)
|
|
//}}AFX_MSG
|
|
DECLARE_MESSAGE_MAP()
|
|
};
|
|
|
|
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
|
|
{
|
|
//{{AFX_DATA_INIT(CAboutDlg)
|
|
//}}AFX_DATA_INIT
|
|
}
|
|
|
|
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CAboutDlg)
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
|
|
//{{AFX_MSG_MAP(CAboutDlg)
|
|
// No message handlers
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CAudioCapDlg dialog
|
|
|
|
CAudioCapDlg::CAudioCapDlg(CWnd* pParent /*=NULL*/)
|
|
: CDialog(CAudioCapDlg::IDD, pParent)
|
|
{
|
|
//{{AFX_DATA_INIT(CAudioCapDlg)
|
|
//}}AFX_DATA_INIT
|
|
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
|
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
|
}
|
|
|
|
void CAudioCapDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CAudioCapDlg)
|
|
DDX_Control(pDX, IDC_STATIC_STATUS, m_strStatus);
|
|
DDX_Control(pDX, IDC_RADIO_MONO, m_btnMono);
|
|
DDX_Control(pDX, IDC_RADIO_8BIT, m_btn8BIT);
|
|
DDX_Control(pDX, IDC_RADIO_11KHZ, m_btn11KHZ);
|
|
DDX_Control(pDX, IDC_BUTTON_PAUSE, m_btnPause);
|
|
DDX_Control(pDX, IDC_BUTTON_STOP, m_btnStop);
|
|
DDX_Control(pDX, IDC_BUTTON_PLAY, m_btnPlay);
|
|
DDX_Control(pDX, IDC_LIST_FILTER_OUTPUTS, m_ListFilterOutputs);
|
|
DDX_Control(pDX, IDC_LIST_FILTER_INPUTS, m_ListFilterInputs);
|
|
DDX_Control(pDX, IDC_BUTTON_RECORD, m_btnRecord);
|
|
DDX_Control(pDX, IDC_BUTTON_PROPERTIES, m_btnProperties);
|
|
DDX_Control(pDX, IDC_LIST_INPUT_DEVICES, m_ListInputs);
|
|
DDX_Control(pDX, IDC_LIST_INPUT_PINS, m_ListInputPins);
|
|
DDX_Control(pDX, IDC_LIST_FILTERS, m_ListFilters);
|
|
DDX_Control(pDX, IDC_EDIT_FILENAME, m_StrFilename);
|
|
DDX_Control(pDX, IDC_CHECK_WRITE, m_CheckWriteFile);
|
|
DDX_Control(pDX, IDC_CHECK_AUDITION, m_CheckAudition);
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CAudioCapDlg, CDialog)
|
|
//{{AFX_MSG_MAP(CAudioCapDlg)
|
|
ON_WM_SYSCOMMAND()
|
|
ON_WM_PAINT()
|
|
ON_WM_QUERYDRAGICON()
|
|
ON_WM_CLOSE()
|
|
ON_WM_DESTROY()
|
|
ON_LBN_SELCHANGE(IDC_LIST_INPUT_DEVICES, OnSelchangeListInputDevices)
|
|
ON_LBN_SELCHANGE(IDC_LIST_FILTERS, OnSelchangeListFilters)
|
|
ON_LBN_SELCHANGE(IDC_LIST_INPUT_PINS, OnSelchangeListInputPins)
|
|
ON_BN_CLICKED(IDC_BUTTON_PROPERTIES, OnButtonProperties)
|
|
ON_BN_CLICKED(IDC_BUTTON_PLAY, OnButtonPlay)
|
|
ON_BN_CLICKED(IDC_BUTTON_PAUSE, OnButtonPause)
|
|
ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
|
|
ON_BN_CLICKED(IDC_BUTTON_RECORD, OnButtonRecord)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CAudioCapDlg message handlers
|
|
|
|
void CAudioCapDlg::OnSysCommand(UINT nID, LPARAM lParam)
|
|
{
|
|
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
|
|
{
|
|
CAboutDlg dlgAbout;
|
|
dlgAbout.DoModal();
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnSysCommand(nID, lParam);
|
|
}
|
|
}
|
|
|
|
// If you add a minimize button to your dialog, you will need the code below
|
|
// to draw the icon. For MFC applications using the document/view model,
|
|
// this is automatically done for you by the framework.
|
|
|
|
void CAudioCapDlg::OnPaint()
|
|
{
|
|
if (IsIconic())
|
|
{
|
|
CPaintDC dc(this); // device context for painting
|
|
|
|
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
|
|
|
|
// Center icon in client rectangle
|
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
|
|
|
// Draw the icon
|
|
dc.DrawIcon(x, y, m_hIcon);
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnPaint();
|
|
}
|
|
}
|
|
|
|
// The system calls this to obtain the cursor to display while the user drags
|
|
// the minimized window.
|
|
HCURSOR CAudioCapDlg::OnQueryDragIcon()
|
|
{
|
|
return (HCURSOR) m_hIcon;
|
|
}
|
|
|
|
BOOL CAudioCapDlg::OnInitDialog()
|
|
{
|
|
CDialog::OnInitDialog();
|
|
|
|
// Add "About..." menu item to system menu.
|
|
|
|
// IDM_ABOUTBOX must be in the system command range.
|
|
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
|
|
ASSERT(IDM_ABOUTBOX < 0xF000);
|
|
|
|
CMenu* pSysMenu = GetSystemMenu(FALSE);
|
|
if (pSysMenu != NULL)
|
|
{
|
|
CString strAboutMenu;
|
|
strAboutMenu.LoadString(IDS_ABOUTBOX);
|
|
if (!strAboutMenu.IsEmpty())
|
|
{
|
|
pSysMenu->AppendMenu(MF_SEPARATOR);
|
|
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
|
|
}
|
|
}
|
|
|
|
// Set the icon for this dialog. The framework does this automatically
|
|
// when the application's main window is not a dialog
|
|
SetIcon(m_hIcon, TRUE); // Set big icon
|
|
SetIcon(m_hIcon, FALSE); // Set small icon
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DirectShow-specific initialization code
|
|
|
|
CoInitialize(NULL);
|
|
|
|
if (FAILED(InitializeCapture()))
|
|
{
|
|
ReleaseCapture();
|
|
return FALSE;
|
|
}
|
|
|
|
CheckDlgButton(IDC_RADIO_16BIT, TRUE);
|
|
CheckDlgButton(IDC_RADIO_STEREO, TRUE);
|
|
CheckDlgButton(IDC_RADIO_44KHZ, TRUE);
|
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::InitializeCapture()
|
|
{
|
|
HRESULT hr;
|
|
|
|
SetDefaults();
|
|
|
|
if (FAILED(hr = GetInterfaces()))
|
|
return hr;
|
|
|
|
if (FAILED(hr = FillLists()))
|
|
return hr;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::FillLists()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Enumerate and display the audio input devices installed in the system
|
|
hr = EnumFiltersWithMonikerToList(NULL, &CLSID_AudioInputDeviceCategory, m_ListInputs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Select the first audio device in the list for the default
|
|
m_ListInputs.SetCurSel(0);
|
|
OnSelchangeListInputDevices();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::GetInterfaces()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Create the filter graph.
|
|
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
|
|
IID_IGraphBuilder, (void **)&m_pGB);
|
|
if (!m_pGB)
|
|
return E_NOINTERFACE;
|
|
|
|
// Create the capture graph builder.
|
|
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,
|
|
IID_ICaptureGraphBuilder2, (void **)&m_pCapture);
|
|
if (!m_pCapture)
|
|
return E_NOINTERFACE;
|
|
|
|
// Associate the filter graph with the capture graph builder
|
|
hr = m_pCapture->SetFiltergraph(m_pGB);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Get useful interfaces from the graph builder
|
|
JIF(hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC));
|
|
JIF(hr = m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME));
|
|
|
|
// Have the graph signal events via window callbacks
|
|
hr = m_pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, RECORD_EVENT);
|
|
|
|
CLEANUP:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::GetPlaybackInterfaces()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Release any existing interface pointers
|
|
FreePlaybackInterfaces();
|
|
|
|
// Create the filter graph.
|
|
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
|
|
IID_IGraphBuilder, (void **)&m_pGBPlayback);
|
|
if (!m_pGBPlayback)
|
|
return E_NOINTERFACE;
|
|
|
|
// Get useful interfaces from the graph builder
|
|
JIF(hr = m_pGBPlayback->QueryInterface(IID_IMediaControl, (void **)&m_pMCPlayback));
|
|
JIF(hr = m_pGBPlayback->QueryInterface(IID_IMediaEventEx, (void **)&m_pMEPlayback));
|
|
|
|
// Have the graph signal events via window callbacks
|
|
hr = m_pMEPlayback->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, PLAYBACK_EVENT);
|
|
|
|
CLEANUP:
|
|
return hr;
|
|
}
|
|
|
|
void CAudioCapDlg::ClearAllocatedLists()
|
|
{
|
|
// Clear the list and delete its allocated CLSID bytes
|
|
ClearFilterListWithMoniker(m_ListInputs);
|
|
}
|
|
|
|
void CAudioCapDlg::ClearLists()
|
|
{
|
|
// Clear the lists that don't contain extra data
|
|
m_ListInputPins.ResetContent();
|
|
|
|
m_ListFilters.ResetContent();
|
|
m_ListFilterInputs.ResetContent();
|
|
m_ListFilterOutputs.ResetContent();
|
|
}
|
|
|
|
void CAudioCapDlg::FreeInterfaces()
|
|
{
|
|
// Disable event callbacks
|
|
if (m_pME)
|
|
m_pME->SetNotifyWindow((OAHWND)NULL, 0, 0);
|
|
|
|
// Release and zero DirectShow interface pointers
|
|
SAFE_RELEASE(m_pMC);
|
|
SAFE_RELEASE(m_pME);
|
|
SAFE_RELEASE(m_pSplitter);
|
|
SAFE_RELEASE(m_pFileWriter);
|
|
SAFE_RELEASE(m_pWAVDest);
|
|
SAFE_RELEASE(m_pInputDevice);
|
|
SAFE_RELEASE(m_pGB);
|
|
SAFE_RELEASE(m_pCapture);
|
|
}
|
|
|
|
void CAudioCapDlg::FreePlaybackInterfaces()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Disable event callbacks
|
|
if (m_pMEPlayback)
|
|
hr = m_pMEPlayback->SetNotifyWindow((OAHWND)NULL, 0, 0);
|
|
|
|
// Release and zero DirectShow interface pointers
|
|
SAFE_RELEASE(m_pMEPlayback);
|
|
SAFE_RELEASE(m_pMCPlayback);
|
|
SAFE_RELEASE(m_pGBPlayback);
|
|
}
|
|
|
|
void CAudioCapDlg::ReleaseCapture()
|
|
{
|
|
ClearAllocatedLists();
|
|
ClearLists();
|
|
DestroyPreviewStream();
|
|
DestroyCaptureStream();
|
|
FreeInterfaces();
|
|
FreePlaybackInterfaces();
|
|
}
|
|
|
|
void CAudioCapDlg::ResetCapture()
|
|
{
|
|
ClearLists();
|
|
FreeInterfaces();
|
|
GetInterfaces();
|
|
}
|
|
|
|
void CAudioCapDlg::SetDefaults()
|
|
{
|
|
// Zero DirectShow interface pointers (sanity check)
|
|
m_pGB = m_pGBPlayback = 0;
|
|
m_pMC = m_pMCPlayback = 0;
|
|
m_pME = m_pMEPlayback = 0;
|
|
m_pCapture = 0;
|
|
m_pInputDevice =0;
|
|
m_pSplitter = 0;
|
|
m_pRenderer =0;
|
|
m_pWAVDest=0;
|
|
m_pFileWriter=0;
|
|
m_bPausedRecording=FALSE;
|
|
|
|
// Set default values for controls on the dialog
|
|
m_CheckAudition.SetCheck(TRUE);
|
|
m_btnProperties.EnableWindow(FALSE);
|
|
m_StrFilename.SetWindowText(DEFAULT_FILENAME);
|
|
}
|
|
|
|
void CAudioCapDlg::EnableButtons(BOOL bEnable)
|
|
{
|
|
m_btnRecord.EnableWindow(bEnable);
|
|
m_btnPause.EnableWindow(bEnable);
|
|
m_btnStop.EnableWindow(bEnable);
|
|
m_btnPlay.EnableWindow(bEnable);
|
|
}
|
|
|
|
void CAudioCapDlg::EnableRadioButtons(BOOL bEnable)
|
|
{
|
|
GetDlgItem(IDC_RADIO_11KHZ)->EnableWindow(bEnable);
|
|
GetDlgItem(IDC_RADIO_22KHZ)->EnableWindow(bEnable);
|
|
GetDlgItem(IDC_RADIO_44KHZ)->EnableWindow(bEnable);
|
|
GetDlgItem(IDC_RADIO_8BIT)->EnableWindow(bEnable);
|
|
GetDlgItem(IDC_RADIO_16BIT)->EnableWindow(bEnable);
|
|
GetDlgItem(IDC_RADIO_MONO)->EnableWindow(bEnable);
|
|
GetDlgItem(IDC_RADIO_STEREO)->EnableWindow(bEnable);
|
|
}
|
|
|
|
void CAudioCapDlg::OnClose()
|
|
{
|
|
if (m_pMC)
|
|
m_pMC->Stop();
|
|
|
|
ReleaseCapture();
|
|
CoUninitialize();
|
|
|
|
CDialog::OnClose();
|
|
}
|
|
|
|
void CAudioCapDlg::OnDestroy()
|
|
{
|
|
ReleaseCapture();
|
|
CDialog::OnDestroy();
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::HandleGraphEvent(void)
|
|
{
|
|
LONG evCode, evParam1, evParam2;
|
|
HRESULT hr=S_OK;
|
|
|
|
if (!m_pME)
|
|
return S_OK;
|
|
|
|
// Read all events currently on the event queue
|
|
while(SUCCEEDED(m_pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
|
|
(LONG_PTR *) &evParam2, 0)))
|
|
{
|
|
if(EC_DEVICE_LOST == evCode)
|
|
{
|
|
// Device was removed
|
|
if (evParam2 == 0)
|
|
{
|
|
OnButtonStop();
|
|
ReleaseCapture();
|
|
SetDefaults();
|
|
MessageBeep(0);
|
|
MessageBox(TEXT("The selected input device was removed!\0"),
|
|
TEXT("DirectShow Audio Capture Sample\0"),
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
// Device was reattached
|
|
else
|
|
{
|
|
// Restart from scratch
|
|
InitializeCapture();
|
|
MessageBeep(0);
|
|
MessageBox(TEXT("The audio input device has been reattached!\0"),
|
|
TEXT("DirectShow Audio Capture Sample\0"),
|
|
MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
}
|
|
|
|
// Free event parameters to prevent memory leaks
|
|
hr = m_pME->FreeEventParams(evCode, evParam1, evParam2);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::HandlePlaybackGraphEvent(void)
|
|
{
|
|
LONG evCode, evParam1, evParam2;
|
|
HRESULT hr=S_OK;
|
|
|
|
if (!m_pMEPlayback)
|
|
return S_OK;
|
|
|
|
// Read all events currently on the event queue
|
|
while(SUCCEEDED(m_pMEPlayback->GetEvent(&evCode, (LONG_PTR *) &evParam1,
|
|
(LONG_PTR *) &evParam2, 0)))
|
|
{
|
|
// Free event parameters to prevent memory leaks
|
|
hr = m_pMEPlayback->FreeEventParams(evCode, evParam1, evParam2);
|
|
|
|
if (evCode == EC_COMPLETE)
|
|
{
|
|
OnButtonStop();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
LRESULT CAudioCapDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// Field notifications from the DirectShow filter graph manager
|
|
// and those posted by the application
|
|
switch (message)
|
|
{
|
|
case WM_GRAPHNOTIFY:
|
|
if (lParam == RECORD_EVENT)
|
|
HandleGraphEvent();
|
|
else
|
|
HandlePlaybackGraphEvent();
|
|
break;
|
|
}
|
|
|
|
return CDialog::WindowProc(message, wParam, lParam);
|
|
}
|
|
|
|
void CAudioCapDlg::OnButtonProperties()
|
|
{
|
|
// Use helper function to display property page for this device
|
|
ShowFilterPropertyPage(m_pInputDevice, m_hWnd);
|
|
|
|
// Increment the device's reference count
|
|
m_pInputDevice->AddRef();
|
|
}
|
|
|
|
void CAudioCapDlg::OnSelchangeListInputDevices()
|
|
{
|
|
HRESULT hr;
|
|
IMoniker *pMoniker=0;
|
|
|
|
// Release and delete any previous capture filter graph
|
|
if (m_pInputDevice)
|
|
ResetCapture();
|
|
|
|
// Get the currently selected category name
|
|
int nItem = m_ListInputs.GetCurSel();
|
|
|
|
// Read the stored moniker pointer from the list box's item data
|
|
pMoniker = (IMoniker *) m_ListInputs.GetItemDataPtr(nItem);
|
|
|
|
// Use the moniker to create the specified audio capture device
|
|
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&m_pInputDevice);
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
// Add the capture filter to the filter graph
|
|
hr = m_pGB->AddFilter(m_pInputDevice, L"Audio Capture");
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
// List and display the available input pins on the capture filter.
|
|
// Many devices offer multiple pins for input (microphone, CD, LineIn, etc.)
|
|
hr = EnumPinsOnFilter(m_pInputDevice, PINDIR_INPUT, m_ListInputPins);
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
// Enable the properties button, if supported by the selected input device
|
|
m_btnProperties.EnableWindow(SupportsPropertyPage(m_pInputDevice));
|
|
|
|
// Select the first available input pin by default
|
|
m_ListInputPins.SetCurSel(0);
|
|
OnSelchangeListInputPins();
|
|
|
|
// Now display the existing filters in the graph for diagnostic help
|
|
UpdateFilterLists(m_pGB);
|
|
}
|
|
|
|
void CAudioCapDlg::OnSelchangeListFilters()
|
|
{
|
|
// Use a helper function to display the filter's pins in listboxes
|
|
if (m_pGBPlayback)
|
|
{
|
|
AddFilterPinsToLists(m_pGBPlayback, m_ListFilters,
|
|
m_ListFilterInputs, m_ListFilterOutputs);
|
|
}
|
|
else
|
|
{
|
|
AddFilterPinsToLists(m_pGB, m_ListFilters,
|
|
m_ListFilterInputs, m_ListFilterOutputs);
|
|
}
|
|
}
|
|
|
|
void CAudioCapDlg::UpdateFilterLists(IGraphBuilder *pUpdateGB)
|
|
{
|
|
// Now display the existing filters in the graph for diagnostic help
|
|
AddGraphFiltersToList(pUpdateGB, m_ListFilters);
|
|
|
|
// Refresh the filter/pin display lists
|
|
m_ListFilters.SetCurSel(0);
|
|
OnSelchangeListFilters();
|
|
}
|
|
|
|
void CAudioCapDlg::OnButtonPlay()
|
|
{
|
|
USES_CONVERSION;
|
|
TCHAR szPlayFile[MAX_PATH];
|
|
|
|
Say(TEXT("Playing specified file..."));
|
|
m_StrFilename.GetWindowText(szPlayFile, MAX_PATH);
|
|
|
|
// Play the media file using DirectShow methods
|
|
if (SUCCEEDED(GetPlaybackInterfaces()))
|
|
{
|
|
HRESULT hr = m_pGBPlayback->RenderFile(T2W(szPlayFile), NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Update the graph filter display to show rendered graph
|
|
UpdateFilterLists(m_pGBPlayback);
|
|
|
|
hr = m_pMCPlayback->Run();
|
|
}
|
|
}
|
|
|
|
// Disable pause button to remove need for extra pause/play handling code
|
|
m_btnPause.EnableWindow(FALSE);
|
|
}
|
|
|
|
void CAudioCapDlg::OnButtonRecord()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Don't rebuild the graphs if we're returning from paused state
|
|
if (m_bPausedRecording)
|
|
{
|
|
if (m_pMC)
|
|
hr = m_pMC->Run();
|
|
|
|
m_bPausedRecording = FALSE;
|
|
Say(TEXT("Auditioning/recording audio..."));
|
|
return;
|
|
}
|
|
|
|
// If we're currently playing back the recorded file, stop playback
|
|
// and clear the interfaces before beginning recording.
|
|
FreePlaybackInterfaces();
|
|
|
|
if (m_CheckAudition.GetCheck())
|
|
RenderPreviewStream();
|
|
|
|
if (m_CheckWriteFile.GetCheck())
|
|
RenderCaptureStream();
|
|
|
|
// Update the graph filter display to show rendered graph
|
|
UpdateFilterLists(m_pGB);
|
|
|
|
if (m_pMC)
|
|
{
|
|
hr = m_pMC->Run();
|
|
Say(TEXT("Auditioning/recording audio..."));
|
|
}
|
|
|
|
m_btnRecord.EnableWindow(FALSE);
|
|
m_btnPlay.EnableWindow(FALSE);
|
|
m_btnPause.EnableWindow(TRUE);
|
|
EnableRadioButtons(FALSE);
|
|
}
|
|
|
|
void CAudioCapDlg::OnButtonPause()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// We share the functionality of the Pause button with both
|
|
// Record and Playback. If the Playback interfaces aren't NULL,
|
|
// then we're in the middle of playback, so pause playback.
|
|
// Otherwise, pause recording.
|
|
if (m_pMCPlayback)
|
|
{
|
|
hr = m_pMCPlayback->Pause();
|
|
Say(TEXT("Paused audio playback"));
|
|
}
|
|
else
|
|
{
|
|
if (m_pMC)
|
|
{
|
|
hr = m_pMC->Pause();
|
|
Say(TEXT("Paused audio recording"));
|
|
m_bPausedRecording = TRUE;
|
|
}
|
|
}
|
|
|
|
m_btnRecord.EnableWindow(TRUE);
|
|
}
|
|
|
|
void CAudioCapDlg::OnButtonStop()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// We share the functionality of the Stop button with both
|
|
// Record and Playback. If the Playback interfaces aren't NULL,
|
|
// then we're in the middle of playback, so stop playback.
|
|
// Otherwise, stop recording.
|
|
if (m_pMCPlayback)
|
|
{
|
|
// Stop playback
|
|
if (m_pMCPlayback)
|
|
hr = m_pMCPlayback->Stop();
|
|
|
|
FreePlaybackInterfaces();
|
|
|
|
// Clear filter/pins list boxes, since there is no active filter graph
|
|
m_ListFilters.ResetContent();
|
|
m_ListFilterInputs.ResetContent();
|
|
m_ListFilterOutputs.ResetContent();
|
|
}
|
|
else
|
|
{
|
|
// Stop recording
|
|
if (m_pMC)
|
|
hr = m_pMC->StopWhenReady();
|
|
|
|
// Destroy the existing filter graph
|
|
DestroyPreviewStream();
|
|
DestroyCaptureStream();
|
|
m_bPausedRecording = FALSE;
|
|
|
|
// Update the graph filter display to show rendered graph
|
|
UpdateFilterLists(m_pGB);
|
|
|
|
EnableRadioButtons(TRUE);
|
|
}
|
|
|
|
EnableButtons(TRUE);
|
|
Say(TEXT("Ready"));
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::RenderCaptureStream()
|
|
{
|
|
USES_CONVERSION;
|
|
HRESULT hr=0;
|
|
IFileSinkFilter2 *pFileSink;
|
|
|
|
// Create the WAVDest filter
|
|
hr = CoCreateInstance(CLSID_WavDest, NULL, CLSCTX_INPROC,
|
|
IID_IBaseFilter, (void **)&m_pWAVDest);
|
|
if (FAILED(hr))
|
|
{
|
|
MessageBox(TEXT("Couldn't create a WAVDest filter. This sample filter is installed ")
|
|
TEXT("with the DirectX 8.0 SDK in the DirectShow\\Filters\\WavDest ")
|
|
TEXT("directory.\n\nPlease build this filter (with Visual C++) and ")
|
|
TEXT("register it by running 'Tools->Register Control' once the ")
|
|
TEXT("filter finishes building.\nThis helper filter is used ")
|
|
TEXT("when writing .WAV files to disk, and this sample requires it.\n\n")
|
|
TEXT("You may still audition the audio input, but you will not be ")
|
|
TEXT("able to write the data to a file."),
|
|
TEXT("Error loading WAVDest helper filter!"));
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
// Create the FileWriter filter
|
|
hr = CoCreateInstance(CLSID_FileWriter, NULL, CLSCTX_INPROC,
|
|
IID_IFileSinkFilter2, (void **)&pFileSink);
|
|
if (FAILED(hr))
|
|
return E_NOINTERFACE;
|
|
|
|
// Get the file sink interface from the File Writer
|
|
hr = pFileSink->QueryInterface(IID_IBaseFilter, (void **)&m_pFileWriter);
|
|
|
|
// Add the WAVDest filter to the graph
|
|
hr = m_pGB->AddFilter(m_pWAVDest, L"WAV Dest");
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Add the FileWriter filter to the graph
|
|
hr = m_pGB->AddFilter((IBaseFilter *)m_pFileWriter, L"File Writer");
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Set filter to always overwrite the file
|
|
hr = pFileSink->SetMode(AM_FILE_OVERWRITE);
|
|
|
|
// Set the output filename, which must be a wide string
|
|
TCHAR szEditFile[MAX_PATH];
|
|
WCHAR wszFilename[MAX_PATH];
|
|
|
|
m_StrFilename.GetWindowText(szEditFile, MAX_PATH);
|
|
if (szEditFile[0] == TEXT('\0'))
|
|
wcscpy(wszFilename, T2W(DEFAULT_FILENAME));
|
|
else
|
|
wcscpy(wszFilename, T2W(szEditFile));
|
|
|
|
hr = pFileSink->SetFileName(wszFilename, NULL);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Get the pin interface for the capture pin
|
|
IPin *pPin=0;
|
|
if (m_pSplitter)
|
|
hr = GetPin(m_pSplitter, PINDIR_OUTPUT, 0, &pPin);
|
|
else
|
|
hr = GetPin(m_pInputDevice, PINDIR_OUTPUT, 0, &pPin);
|
|
|
|
// Connect the new filters
|
|
if (pPin)
|
|
{
|
|
hr = m_pGB->Render(pPin);
|
|
pPin->Release();
|
|
}
|
|
|
|
// Release the FileSinkFilter2 interface, since it's no longer needed
|
|
SAFE_RELEASE(pFileSink);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::DestroyCaptureStream()
|
|
{
|
|
HRESULT hr=0;
|
|
|
|
// Destroy Audio renderer filter, if it exists
|
|
if (m_pWAVDest)
|
|
{
|
|
hr = m_pGB->RemoveFilter(m_pWAVDest);
|
|
SAFE_RELEASE(m_pWAVDest);
|
|
}
|
|
|
|
// Destroy Smart Tee filter, if it exists
|
|
if (m_pFileWriter)
|
|
{
|
|
hr = m_pGB->RemoveFilter(m_pFileWriter);
|
|
SAFE_RELEASE(m_pFileWriter);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::RenderPreviewStream()
|
|
{
|
|
USES_CONVERSION;
|
|
HRESULT hr=0;
|
|
WCHAR wszFilter[64];
|
|
|
|
// If we've already configured the stream, just exit
|
|
if (m_pRenderer)
|
|
return S_OK;
|
|
|
|
// Set the requested audio properties - buffer sizes, channels, freq, etc.
|
|
hr = SetAudioProperties();
|
|
|
|
// Render the preview stream
|
|
hr = m_pCapture->RenderStream(
|
|
&PIN_CATEGORY_PREVIEW,
|
|
&MEDIATYPE_Audio,
|
|
m_pInputDevice,
|
|
NULL, // No compression filter.
|
|
NULL // Default renderer.
|
|
);
|
|
|
|
// Some capture sources will have only a Capture pin (no Preview pin).
|
|
// In that case, the Capture Graph builder will automatically insert
|
|
// a SmartTee filter, which will split the stream into a Capture stream
|
|
// and a Preview stream. In that case, it's not an error.
|
|
if (hr == VFW_S_NOPREVIEWPIN)
|
|
{
|
|
wcscpy(wszFilter, T2W(TEXT("Smart Tee")));
|
|
|
|
// Get the interface for the Splitter filter for later use
|
|
hr = m_pGB->FindFilterByName(wszFilter, &m_pSplitter);
|
|
}
|
|
|
|
// Get the interface for the audio renderer filter for later use
|
|
wcscpy(wszFilter, T2W(TEXT("Audio Renderer")));
|
|
hr = m_pGB->FindFilterByName(wszFilter, &m_pRenderer);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::DestroyPreviewStream()
|
|
{
|
|
HRESULT hr=0;
|
|
|
|
// Destroy Smart Tee filter, if it exists
|
|
if (m_pSplitter)
|
|
{
|
|
hr = m_pGB->RemoveFilter(m_pSplitter);
|
|
SAFE_RELEASE(m_pSplitter);
|
|
}
|
|
|
|
// Destroy Audio renderer filter, if it exists
|
|
if (m_pRenderer)
|
|
{
|
|
hr = m_pGB->RemoveFilter(m_pRenderer);
|
|
SAFE_RELEASE(m_pRenderer);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CAudioCapDlg::OnSelchangeListInputPins()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Make sure that playback is stopped
|
|
OnButtonStop();
|
|
|
|
// Select the capture device's input pin
|
|
hr = ActivateSelectedInputPin();
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::ActivateSelectedInputPin()
|
|
{
|
|
HRESULT hr=S_OK;
|
|
IPin *pPin=0;
|
|
IAMAudioInputMixer *pPinMixer;
|
|
|
|
// How many pins are in the input pin list?
|
|
int nPins = m_ListInputPins.GetCount();
|
|
int nActivePin = m_ListInputPins.GetCurSel();
|
|
|
|
// Activate the selected input pin and deactivate all others
|
|
for (int i=0; i<nPins; i++)
|
|
{
|
|
// Get this pin's interface
|
|
hr = GetPin(m_pInputDevice, PINDIR_INPUT, i, &pPin);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pPin->QueryInterface(IID_IAMAudioInputMixer, (void **)&pPinMixer);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// If this is our selected pin, enable it
|
|
if (i == nActivePin)
|
|
{
|
|
// Set any other audio properties on this pin
|
|
hr = SetInputPinProperties(pPinMixer);
|
|
|
|
// If there is only one input pin, this method
|
|
// might return E_NOTIMPL.
|
|
hr = pPinMixer->put_Enable(TRUE);
|
|
}
|
|
// Otherwise, disable it
|
|
else
|
|
{
|
|
hr = pPinMixer->put_Enable(FALSE);
|
|
}
|
|
|
|
pPinMixer->Release();
|
|
}
|
|
|
|
// Release pin interfaces
|
|
pPin->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::SetInputPinProperties(IAMAudioInputMixer *pPinMixer)
|
|
{
|
|
HRESULT hr=0;
|
|
BOOL bLoudness=0, bMono=0;
|
|
double dblBass=0, dblBassRange=0, dblMixLevel=0,
|
|
dblPan=0, dblTreble=0, dblTrebleRange=0;
|
|
|
|
// Read current settings for debugging purposes. Many of these interfaces
|
|
// will not be implemented by the input device's driver, so they will
|
|
// return E_NOTIMPL. In that case, just ignore the values.
|
|
hr = pPinMixer->get_Bass(&dblBass);
|
|
hr = pPinMixer->get_BassRange(&dblBassRange);
|
|
hr = pPinMixer->get_Loudness(&bLoudness);
|
|
hr = pPinMixer->get_MixLevel(&dblMixLevel);
|
|
hr = pPinMixer->get_Mono(&bMono);
|
|
hr = pPinMixer->get_Pan(&dblPan);
|
|
hr = pPinMixer->get_Treble(&dblTreble);
|
|
hr = pPinMixer->get_TrebleRange(&dblTrebleRange);
|
|
|
|
// Manipulate desired values here (mono/stereo, pan, etc.)
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CAudioCapDlg::Say(TCHAR *szMsg)
|
|
{
|
|
// Display status information on the main dialog
|
|
m_strStatus.SetWindowText(szMsg);
|
|
}
|
|
|
|
HRESULT CAudioCapDlg::SetAudioProperties()
|
|
{
|
|
HRESULT hr=0;
|
|
IPin *pPin=0;
|
|
IAMBufferNegotiation *pNeg=0;
|
|
IAMStreamConfig *pCfg=0;
|
|
int nFrequency;
|
|
|
|
// Determine audio properties
|
|
int nChannels = m_btnMono.GetCheck() ? 1 : 2;
|
|
int nBytesPerSample = m_btn8BIT.GetCheck() ? 1 : 2;
|
|
|
|
if (IsDlgButtonChecked(IDC_RADIO_11KHZ)) nFrequency = 11025;
|
|
else if (IsDlgButtonChecked(IDC_RADIO_22KHZ)) nFrequency = 22050;
|
|
else nFrequency = 44100;
|
|
|
|
// Find number of bytes in one second
|
|
long lBytesPerSecond = (long) (nBytesPerSample * nFrequency * nChannels);
|
|
|
|
// Set to 50ms worth of data
|
|
long lBufferSize = (long) ((float) lBytesPerSecond * DEFAULT_BUFFER_TIME);
|
|
|
|
// Get the buffer negotiation interface for each pin,
|
|
// since there could be both a Capture and a Preview pin.
|
|
for (int i=0; i<2; i++)
|
|
{
|
|
hr = GetPin(m_pInputDevice, PINDIR_OUTPUT, i, &pPin);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Get buffer negotiation interface
|
|
hr = pPin->QueryInterface(IID_IAMBufferNegotiation, (void **)&pNeg);
|
|
if (FAILED(hr))
|
|
{
|
|
pPin->Release();
|
|
break;
|
|
}
|
|
|
|
// Set the buffer size based on selected settings
|
|
ALLOCATOR_PROPERTIES prop={0};
|
|
prop.cbBuffer = lBufferSize;
|
|
prop.cBuffers = 6;
|
|
prop.cbAlign = nBytesPerSample * nChannels;
|
|
hr = pNeg->SuggestAllocatorProperties(&prop);
|
|
pNeg->Release();
|
|
|
|
// Now set the actual format of the audio data
|
|
hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **)&pCfg);
|
|
if (FAILED(hr))
|
|
{
|
|
pPin->Release();
|
|
break;
|
|
}
|
|
|
|
// Read current media type/format
|
|
AM_MEDIA_TYPE *pmt={0};
|
|
hr = pCfg->GetFormat(&pmt);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Fill in values for the new format
|
|
WAVEFORMATEX *pWF = (WAVEFORMATEX *) pmt->pbFormat;
|
|
pWF->nChannels = (WORD) nChannels;
|
|
pWF->nSamplesPerSec = nFrequency;
|
|
pWF->nAvgBytesPerSec = lBytesPerSecond;
|
|
pWF->wBitsPerSample = (WORD) (nBytesPerSample * 8);
|
|
pWF->nBlockAlign = (WORD) (nBytesPerSample * nChannels);
|
|
|
|
// Set the new formattype for the output pin
|
|
hr = pCfg->SetFormat(pmt);
|
|
DeleteMediaType(pmt);
|
|
}
|
|
|
|
// Release interfaces
|
|
pCfg->Release();
|
|
pPin->Release();
|
|
}
|
|
// No more output pins on this filter
|
|
else
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|