2025-04-27 07:49:33 -04:00

541 lines
14 KiB
C++

/*==========================================================================
*
* Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
*
* File: wirecb.cpp
* Content:
* This module contains the implementation of the CWaveInRecordBuffer
* class.
*
* History:
* Date By Reason
* ==== == ======
* 11/04/99 rodtoll Created
* 11/18/99 rodtoll Fixed bug which causes lockup when stopping a buffer then
* restarting it.
* 11/23/99 rodtoll Added SelectMicrophone call to the interface
* 12/01/99 rodtoll Bug #115783 - Always adjusts default device.
* Added support for new mixerline class which supports
* proper selection of devices/adjusting of volumes
* 12/08/99 rodtoll Bug #121054 - DirectX 7.1 support.
* - Added hwndOwner param for capture focus support
* - Added lpfLostFocus param to GetCurrentPosition so upper
* layers can detect lost focus.
* 01/14/2000 rodtoll Updated to use DWORD_PTR to allow proper 64-bit operation
* 01/28/2000 rodtoll Bug #130465: Record Mute/Unmute must call YieldFocus() / ClaimFocus()
*
***************************************************************************/
#include "stdafx.h"
#include "wirecd.h"
#include "dndbg.h"
#include "OSInd.h"
#include "wirecb.h"
#include "dvoice.h"
#include "micutils.h"
#define WAVEIN_STARTLATENCY 2
#define WIFAILED( x ) (x != MMSYSERR_NOERROR)
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::GetStartupLatency"
DWORD CWaveInRecordBuffer::GetStartupLatency()
{
return WAVEIN_STARTLATENCY;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::CWaveInRecordBuffer"
CWaveInRecordBuffer::CWaveInRecordBuffer(
): CAudioRecordBuffer(), m_hwiDevice(NULL), m_uDeviceID(0), m_dwCurrentPosition(0),
m_lpWaveHeaders(NULL), m_dwBufferSize(0), m_dwNumBuffers(0), m_dwFrameSize(0),
m_fRecording(FALSE), m_lpbShadowBuffer(NULL), m_dwShadowStart(0),
m_hFrameProcessed(NULL), m_lpwfxRecordFormat(NULL),
m_fStopping( FALSE )
{
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::~CWaveInRecordBuffer"
CWaveInRecordBuffer::~CWaveInRecordBuffer()
{
// Stop the buffers and unprepare them if they are prepared
Stop();
if( m_dwNumBuffers )
{
for( DWORD dwIndex = 0; dwIndex < m_dwNumBuffers; dwIndex++ )
{
delete [] ((LPBYTE) m_lpWaveHeaders[dwIndex].lpData);
}
delete [] m_lpWaveHeaders;
}
// Close the device
if( m_hwiDevice != NULL )
{
waveInClose( m_hwiDevice );
}
if( m_lpbShadowBuffer != NULL )
{
delete [] m_lpbShadowBuffer;
}
CloseHandle( m_hFrameProcessed );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::Initialize"
HRESULT CWaveInRecordBuffer::Initialize( UINT uDeviceID, LPDSCBUFFERDESC lpdscDesc, DWORD dwFrameSize )
{
HRESULT hr;
DWORD dwIndex;
m_hFrameProcessed = CreateEvent( NULL, FALSE, FALSE, NULL );
if( m_hFrameProcessed == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create event" );
return DVERR_GENERIC;
}
hr = m_mixerLine.Initialize( uDeviceID );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to acquire volume controls" );
return hr;
}
hr = waveInOpen( &m_hwiDevice, uDeviceID, lpdscDesc->lpwfxFormat, (DWORD_PTR) WaveInHandler, (DWORD_PTR) this, CALLBACK_FUNCTION ) ;
if( WIFAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to open waveIn Device (mmresult) hr=0x%x", hr );
return DVERR_RECORDSYSTEMERROR;
}
m_dwNumBuffers = lpdscDesc->dwBufferBytes / dwFrameSize;
m_lpWaveHeaders = new WAVEHDR[m_dwNumBuffers];
if( m_lpWaveHeaders == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Out of memory" );
hr = DVERR_OUTOFMEMORY;
goto INITIALIZE_ERROR;
}
for( dwIndex = 0; dwIndex < m_dwNumBuffers; dwIndex++ )
{
m_lpWaveHeaders[dwIndex].lpData = (LPSTR) new BYTE[dwFrameSize];
if( m_lpWaveHeaders[dwIndex].lpData == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Out of memory" );
// Make sure rest of buffer pointers are NULL;
for( ; dwIndex < m_dwNumBuffers; dwIndex++ )
{
m_lpWaveHeaders[dwIndex].lpData = NULL;
}
hr = DVERR_OUTOFMEMORY;
goto INITIALIZE_ERROR;
}
if( lpdscDesc->lpwfxFormat->wBitsPerSample == 8 )
{
memset( m_lpWaveHeaders[dwIndex].lpData, 0x80, dwFrameSize );
}
else
{
memset( m_lpWaveHeaders[dwIndex].lpData, 0x00, dwFrameSize );
}
m_lpWaveHeaders[dwIndex].dwBufferLength = dwFrameSize;
m_lpWaveHeaders[dwIndex].dwBytesRecorded = 0;
// Used to specify buffer location when this buffer is complete
if( dwIndex == (m_dwNumBuffers-1))
{
m_lpWaveHeaders[dwIndex].dwUser = 0;
}
else
{
m_lpWaveHeaders[dwIndex].dwUser = dwFrameSize*(dwIndex+1);
}
m_lpWaveHeaders[dwIndex].dwFlags = 0;
m_lpWaveHeaders[dwIndex].dwLoops = 0;
m_lpWaveHeaders[dwIndex].lpNext = NULL;
m_lpWaveHeaders[dwIndex].reserved = 0;
}
m_lpbShadowBuffer = new BYTE[lpdscDesc->dwBufferBytes];
m_lpwfxRecordFormat = lpdscDesc->lpwfxFormat;
m_uDeviceID = uDeviceID;
m_dwFrameSize = dwFrameSize;
m_dwCurrentPosition = 0;
m_dwBufferSize = lpdscDesc->dwBufferBytes;
m_fRecording = FALSE;
return DV_OK;
INITIALIZE_ERROR:
if( m_lpbShadowBuffer != NULL )
{
delete [] m_lpbShadowBuffer;
m_lpbShadowBuffer = NULL;
}
if( m_lpWaveHeaders != NULL )
{
for( dwIndex = 0; dwIndex < m_dwNumBuffers; dwIndex++ )
{
if( m_lpWaveHeaders[dwIndex].lpData != NULL )
{
delete [] ((LPBYTE) m_lpWaveHeaders[dwIndex].lpData);
}
}
delete [] m_lpWaveHeaders;
m_lpWaveHeaders = NULL;
}
if( m_hwiDevice != NULL )
{
waveInClose( m_hwiDevice );
m_hwiDevice = NULL;
}
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::WaveInHandler"
void CWaveInRecordBuffer::WaveInHandler( HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
{
CWaveInRecordBuffer *This = (CWaveInRecordBuffer *) dwInstance;
DNASSERT( This != NULL );
HRESULT hr;
DPFX(DPFPREP, DVF_INFOLEVEL, "WaveInHandler: Wakeup" );
if( uMsg == WIM_DATA && !This->m_fStopping)
{
WAVEHDR *lpWaveHeader = (WAVEHDR *) dwParam1;
hr = waveInUnprepareHeader( This->m_hwiDevice, lpWaveHeader, sizeof( WAVEHDR ) );
if( WIFAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error unpreparing header (mmresult) hr=0x%x", hr );
return;
}
else
{
DPFX(DPFPREP, DVF_INFOLEVEL, "WaveInHandler: Unpreparing for location: %d", lpWaveHeader->dwUser );
}
This->m_dwCurrentPosition = lpWaveHeader->dwUser;
if( !This->m_fRecording )
{
SetEvent( This->m_hFrameProcessed );
DPFX(DPFPREP, DVF_INFOLEVEL, "WaveInHandler: Signalling single frame done" );
}
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::Lock"
HRESULT CWaveInRecordBuffer::Lock( DWORD dwWriteCursor, DWORD dwWriteBytes, LPVOID *lplpvBuffer1, LPDWORD lpdwSize1, LPVOID *lplpvBuffer2, LPDWORD lpdwSize2, DWORD dwFlags )
{
*lpdwSize2 = 0;
*lplpvBuffer2 = NULL;
// Special case for the entire buffer
if( dwFlags & DSCBLOCK_ENTIREBUFFER )
{
if( m_fRecording )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot lock entire buffer while recording!" );
return DVERR_GENERIC;
}
*lplpvBuffer1 = m_lpbShadowBuffer;
*lpdwSize1 = m_dwBufferSize;
m_dwShadowStart = 0;
return DV_OK;
}
if( dwWriteBytes % m_dwFrameSize != 0 )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Can only lock block aligned sizes" );
return DVERR_GENERIC;
}
// We're working with a shadow buffer
if( dwWriteBytes > m_dwFrameSize )
{
*lplpvBuffer1 = m_lpbShadowBuffer;
*lpdwSize1 = dwWriteBytes;
m_dwShadowStart = dwWriteCursor;
return DV_OK;
}
// We're doing a plain old lock of a buffer. When we do the unlock we'll commit the buffer
m_dwShadowStart = dwWriteCursor / m_dwFrameSize;
*lpdwSize1 = m_dwFrameSize;
*lplpvBuffer1 = m_lpWaveHeaders[m_dwShadowStart].lpData;
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::UnLock"
HRESULT CWaveInRecordBuffer::UnLock( LPVOID lpvBuffer1, DWORD dwSize1, LPVOID lpvBuffer2, DWORD dwSize2 )
{
HRESULT hr;
// We used the shadow buffer, we're writing across multiple buffers.
//
// We don't commit the buffers to the sound device because this is always used for
// setting silence into the buffers
if( dwSize1 > m_dwFrameSize )
{
DWORD dwCurrentBuffer = m_dwShadowStart / m_dwFrameSize;
LPBYTE lpCurrentShadowLoc = m_lpbShadowBuffer;
for( DWORD dwIndex = 0; dwIndex < (dwSize1 / m_dwFrameSize); dwIndex++ )
{
memcpy( m_lpWaveHeaders[dwCurrentBuffer].lpData , lpCurrentShadowLoc, m_dwFrameSize );
lpCurrentShadowLoc += m_dwFrameSize;
dwCurrentBuffer++;
dwCurrentBuffer %= m_dwNumBuffers;
}
return DV_OK;
}
// We got just one buffer and we're now ready to commit it to the device
m_lpWaveHeaders[m_dwShadowStart].dwFlags = 0;
hr = waveInPrepareHeader( m_hwiDevice, &m_lpWaveHeaders[m_dwShadowStart], sizeof( WAVEHDR ) );
if( WIFAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to prepare the header for output to the sound device (mmresult) hr = 0x%x", hr );
return DVERR_RECORDSYSTEMERROR;
}
hr = waveInAddBuffer( m_hwiDevice, &m_lpWaveHeaders[m_dwShadowStart], sizeof( WAVEHDR ) );
if( WIFAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to write the buffer (mmresult) hr=0x%x", hr );
waveInUnprepareHeader( m_hwiDevice, &m_lpWaveHeaders[m_dwShadowStart], sizeof( WAVEHDR ) );
return DVERR_RECORDSYSTEMERROR;
}
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::SetVolume"
HRESULT CWaveInRecordBuffer::GetVolume( LPLONG lplVolume )
{
HRESULT hr;
hr = m_mixerLine.GetMicrophoneVolume( lplVolume );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to get mic volume, using master rec volume hr=0x%x", hr );
hr = m_mixerLine.GetMasterRecordVolume( lplVolume );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get recording volume hr=0x%x", hr );
return hr;
}
}
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::SetVolume"
HRESULT CWaveInRecordBuffer::SetVolume( LONG lVolume )
{
HRESULT hr;
hr = m_mixerLine.SetMasterRecordVolume( lVolume );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to set master recording volume hr=0x%x", hr );
}
hr = m_mixerLine.SetMicrophoneVolume( lVolume );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to set mic volume hr=0x%x", hr );
}
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::GetCurrentPosition"
HRESULT CWaveInRecordBuffer::GetCurrentPosition( LPDWORD lpdwPosition, LPBOOL lpfLostFocus )
{
*lpdwPosition = m_dwCurrentPosition;
*lpfLostFocus = FALSE;
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::Record"
HRESULT CWaveInRecordBuffer::Record( BOOL fLooping )
{
// Always send two buffers to the record device in order to allow
// for significant write-ahead
HRESULT hr;
m_fRecording = FALSE;
m_dwCurrentPosition = 0;
for( DWORD dwIndex = 0; dwIndex < m_dwNumBuffers; dwIndex++ )
{
m_lpWaveHeaders[dwIndex].dwFlags = 0;
hr = waveInPrepareHeader( m_hwiDevice, &m_lpWaveHeaders[dwIndex], sizeof( WAVEHDR ) );
if( WIFAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to prepare header for recording (mmresult) hr=0x%x", hr );
return DVERR_RECORDSYSTEMERROR;
}
hr = waveInAddBuffer( m_hwiDevice, &m_lpWaveHeaders[dwIndex], sizeof( WAVEHDR ) );
if( WIFAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to write wave chunk (mmresult) hr=0x%x", hr );
return DVERR_RECORDSYSTEMERROR;
}
}
DPFX(DPFPREP, DVF_INFOLEVEL, "Starting recording" );
hr = waveInStart( m_hwiDevice );
if( WIFAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to start input (mmresult) hr=0x%x", hr );
return DVERR_RECORDSYSTEMERROR;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "Waiting for a frame to be processed" );
// Delay for one frame to allow waveIn to start up.
//
WaitForSingleObject( m_hFrameProcessed, INFINITE );
DPFX(DPFPREP, DVF_INFOLEVEL, "Frame processed, recording proceeding" );
m_fRecording = TRUE;
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::Stop"
HRESULT CWaveInRecordBuffer::Stop()
{
HRESULT hr;
DWORD dwIndex;
m_fStopping = TRUE;
hr = waveInReset( m_hwiDevice );
if( WIFAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Stop failed on waveInReset (mmresult) hr=0x%x", hr );
return DVERR_RECORDSYSTEMERROR;
}
ResetEvent( m_hFrameProcessed );
for( dwIndex = 0; dwIndex < m_dwNumBuffers; dwIndex++ )
{
hr = waveInUnprepareHeader( m_hwiDevice, &m_lpWaveHeaders[dwIndex], sizeof( WAVEHDR ) );
if( WIFAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to unprepare header for recording (mmresult) hr=0x%x", hr );
}
}
m_fStopping = FALSE;
m_fRecording = FALSE;
// This should cause the callback to be called which will
// unprepare all the headers.
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::GetRecordFormat"
LPWAVEFORMATEX CWaveInRecordBuffer::GetRecordFormat()
{
return m_lpwfxRecordFormat;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::SelectMicrophone"
HRESULT CWaveInRecordBuffer::SelectMicrophone( BOOL fSelect )
{
return m_mixerLine.EnableMicrophone( fSelect );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::ClaimFocus"
HRESULT CWaveInRecordBuffer::ClaimFocus( )
{
return DVERR_NOTSUPPORTED;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CWaveInRecordBuffer::YieldFocus"
HRESULT CWaveInRecordBuffer::YieldFocus( )
{
return DVERR_NOTSUPPORTED;
}