diff options
Diffstat (limited to 'engine/audio/private/voice_record_dsound.cpp')
| -rw-r--r-- | engine/audio/private/voice_record_dsound.cpp | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/engine/audio/private/voice_record_dsound.cpp b/engine/audio/private/voice_record_dsound.cpp new file mode 100644 index 0000000..70fc2b6 --- /dev/null +++ b/engine/audio/private/voice_record_dsound.cpp @@ -0,0 +1,400 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// This module implements the voice record and compression functions + +#include "audio_pch.h" +#if !defined( _X360 ) +#include "dsound.h" +#endif +#include <assert.h> +#include "voice.h" +#include "tier0/vcrmode.h" +#include "ivoicerecord.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +// ------------------------------------------------------------------------------ +// Globals. +// ------------------------------------------------------------------------------ + +typedef HRESULT (WINAPI *DirectSoundCaptureCreateFn)(const GUID FAR *lpGUID, LPDIRECTSOUNDCAPTURE *pCapture, LPUNKNOWN pUnkOuter); + + + +// ------------------------------------------------------------------------------ +// Static helpers +// ------------------------------------------------------------------------------ + + + +// ------------------------------------------------------------------------------ +// VoiceRecord_DSound +// ------------------------------------------------------------------------------ + +class VoiceRecord_DSound : public IVoiceRecord +{ +protected: + + virtual ~VoiceRecord_DSound(); + + +// IVoiceRecord. +public: + + VoiceRecord_DSound(); + virtual void Release(); + + virtual bool RecordStart(); + virtual void RecordStop(); + + // Initialize. The format of the data we expect from the provider is + // 8-bit signed mono at the specified sample rate. + virtual bool Init(int sampleRate); + + virtual void Idle(); + + // Get the most recent N samples. + virtual int GetRecordedData(short *pOut, int nSamplesWanted); + +private: + void Term(); // Delete members. + void Clear(); // Clear members. + void UpdateWrapping(); + + inline DWORD NumCaptureBufferBytes() {return m_nCaptureBufferBytes;} + + +private: + HINSTANCE m_hInstDS; + + LPDIRECTSOUNDCAPTURE m_pCapture; + LPDIRECTSOUNDCAPTUREBUFFER m_pCaptureBuffer; + + // How many bytes our capture buffer has. + DWORD m_nCaptureBufferBytes; + + // We need to know when the capture buffer loops, so we install an event and + // update this in the event. + DWORD m_WrapOffset; + HANDLE m_hWrapEvent; + + // This is our (unwrapped) position that tells how much data we've given to the app. + DWORD m_LastReadPos; +}; + + + +VoiceRecord_DSound::VoiceRecord_DSound() +{ + Clear(); +} + + +VoiceRecord_DSound::~VoiceRecord_DSound() +{ + Term(); +} + + +void VoiceRecord_DSound::Release() +{ + delete this; +} + + +bool VoiceRecord_DSound::RecordStart() +{ + //When we start recording we want to make sure we don't provide any audio + //that occurred before now. So set m_LastReadPos to the current + //read position of the audio device + if (m_pCaptureBuffer == NULL) + { + return false; + } + + Idle(); + + DWORD dwStatus; + HRESULT hr = m_pCaptureBuffer->GetStatus(&dwStatus); + if (FAILED(hr) || !(dwStatus & DSCBSTATUS_CAPTURING)) + return false; + + DWORD dwReadPos; + hr = m_pCaptureBuffer->GetCurrentPosition(NULL, &dwReadPos); + if (!FAILED(hr)) + { + m_LastReadPos = dwReadPos + m_WrapOffset; + } + + return true; +} + + +void VoiceRecord_DSound::RecordStop() +{ +} + +static bool IsRunningWindows7() +{ + if ( IsPC() ) + { + OSVERSIONINFOEX osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + if ( GetVersionEx ((OSVERSIONINFO *)&osvi) ) + { + if ( osvi.dwMajorVersion > 6 || (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion >= 1) ) + return true; + } + } + return false; +} + +bool VoiceRecord_DSound::Init(int sampleRate) +{ + HRESULT hr; + DSCBUFFERDESC dscDesc; + DirectSoundCaptureCreateFn createFn; + + + Term(); + + + WAVEFORMATEX recordFormat = + { + WAVE_FORMAT_PCM, // wFormatTag + 1, // nChannels + (uint32)sampleRate, // nSamplesPerSec + (uint32)sampleRate*2, // nAvgBytesPerSec + 2, // nBlockAlign + 16, // wBitsPerSample + sizeof(WAVEFORMATEX) // cbSize + }; + + + + // Load the DSound DLL. + m_hInstDS = LoadLibrary("dsound.dll"); + if(!m_hInstDS) + goto HandleError; + + createFn = (DirectSoundCaptureCreateFn)GetProcAddress(m_hInstDS, "DirectSoundCaptureCreate"); + if(!createFn) + goto HandleError; + + const GUID FAR *pGuid = &DSDEVID_DefaultVoiceCapture; + if ( IsRunningWindows7() ) + { + pGuid = NULL; + } + hr = createFn(pGuid, &m_pCapture, NULL); + if(FAILED(hr)) + goto HandleError; + + // Create the capture buffer. + memset(&dscDesc, 0, sizeof(dscDesc)); + dscDesc.dwSize = sizeof(dscDesc); + dscDesc.dwFlags = 0; + dscDesc.dwBufferBytes = recordFormat.nAvgBytesPerSec; + dscDesc.lpwfxFormat = &recordFormat; + + hr = m_pCapture->CreateCaptureBuffer(&dscDesc, &m_pCaptureBuffer, NULL); + if(FAILED(hr)) + goto HandleError; + + + // Figure out how many bytes we got in our capture buffer. + DSCBCAPS caps; + memset(&caps, 0, sizeof(caps)); + caps.dwSize = sizeof(caps); + + hr = m_pCaptureBuffer->GetCaps(&caps); + if(FAILED(hr)) + goto HandleError; + + m_nCaptureBufferBytes = caps.dwBufferBytes; + + + // Set it up so we get notification when the buffer wraps. + m_hWrapEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(!m_hWrapEvent) + goto HandleError; + + DSBPOSITIONNOTIFY dsbNotify; + dsbNotify.dwOffset = dscDesc.dwBufferBytes - 1; + dsbNotify.hEventNotify = m_hWrapEvent; + + // Get the IDirectSoundNotify interface. + LPDIRECTSOUNDNOTIFY pNotify; + hr = m_pCaptureBuffer->QueryInterface(IID_IDirectSoundNotify, (void**)&pNotify); + if(FAILED(hr)) + goto HandleError; + + hr = pNotify->SetNotificationPositions(1, &dsbNotify); + pNotify->Release(); + if(FAILED(hr)) + goto HandleError; + + // Start capturing. + hr = m_pCaptureBuffer->Start(DSCBSTART_LOOPING); + if(FAILED(hr)) + return false; + + return true; + + +HandleError:; + Term(); + return false; +} + + +void VoiceRecord_DSound::Term() +{ + if(m_pCaptureBuffer) + m_pCaptureBuffer->Release(); + + if(m_pCapture) + m_pCapture->Release(); + + if(m_hWrapEvent) + DeleteObject(m_hWrapEvent); + + if(m_hInstDS) + { + FreeLibrary(m_hInstDS); + m_hInstDS = NULL; + } + + Clear(); +} + + +void VoiceRecord_DSound::Clear() +{ + m_pCapture = NULL; + m_pCaptureBuffer = NULL; + m_WrapOffset = 0; + m_LastReadPos = 0; + m_hWrapEvent = NULL; + m_hInstDS = NULL; +} + + +void VoiceRecord_DSound::Idle() +{ + UpdateWrapping(); +} + +int VoiceRecord_DSound::GetRecordedData( short *pOut, int nSamples ) +{ + if(!m_pCaptureBuffer) + { + assert(false); + return 0; + } + + DWORD dwStatus; + HRESULT hr = m_pCaptureBuffer->GetStatus(&dwStatus); + if(FAILED(hr) || !(dwStatus & DSCBSTATUS_CAPTURING)) + return 0; + + Idle(); // Update wrapping.. + + DWORD nBytesWanted = (DWORD)( nSamples << 1 ); + + DWORD dwReadPos; + hr = m_pCaptureBuffer->GetCurrentPosition( NULL, &dwReadPos); + if(FAILED(hr)) + return 0; + + dwReadPos += m_WrapOffset; + + // Read the range (dwReadPos-nSamplesWanted, dwReadPos), but don't re-read data we've already read. + DWORD readStart = Max( dwReadPos - nBytesWanted, (DWORD)0u ); + if ( readStart < m_LastReadPos ) + { + readStart = m_LastReadPos; + } + + // Lock the buffer. + LPVOID pData[2]; + DWORD dataLen[2]; + + hr = m_pCaptureBuffer->Lock( + readStart % NumCaptureBufferBytes(), // Offset. + dwReadPos - readStart, // Number of bytes to lock. + &pData[0], // Buffer 1. + &dataLen[0], // Buffer 1 length. + &pData[1], // Buffer 2. + &dataLen[1], // Buffer 2 length. + 0 // Flags. + ); + + if(FAILED(hr)) + return 0; + + // Hopefully we didn't get too much data back! + if((dataLen[0]+dataLen[1]) > nBytesWanted ) + { + assert(false); + m_pCaptureBuffer->Unlock(pData[0], dataLen[0], pData[1], dataLen[1]); + return 0; + } + + // Copy the data to the output. + memcpy(pOut, pData[0], dataLen[0]); + memcpy(&pOut[dataLen[0]/2], pData[1], dataLen[1]); + + m_pCaptureBuffer->Unlock(pData[0], dataLen[0], pData[1], dataLen[1]); + + // Last Read Position + m_LastReadPos = dwReadPos; + // Return sample count (not bytes) + return (dataLen[0] + dataLen[1]) >> 1; +} + + +void VoiceRecord_DSound::UpdateWrapping() +{ + if(!m_pCaptureBuffer) + return; + + // Has the buffer wrapped? + if ( VCRHook_WaitForSingleObject(m_hWrapEvent, 0) == WAIT_OBJECT_0 ) + { + m_WrapOffset += m_nCaptureBufferBytes; + } +} + + + +IVoiceRecord* CreateVoiceRecord_DSound(int sampleRate) +{ + VoiceRecord_DSound *pRecord = new VoiceRecord_DSound; + if(pRecord && pRecord->Init(sampleRate)) + { + return pRecord; + } + else + { + if(pRecord) + pRecord->Release(); + + return NULL; + } +} + |