//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "stdafx.h" #include #include #include #include "waveout.h" #include "ivoicecodec.h" class CWaveOutHdr { public: WAVEHDR m_Hdr; CWaveOutHdr *m_pNext; char m_Data[1]; }; class CWaveOut : public IWaveOut { // IWaveOut overrides. public: CWaveOut(); virtual ~CWaveOut(); virtual void Release(); virtual bool PutSamples(short *pSamples, int nSamples); virtual void Idle(); virtual int GetNumBufferedSamples(); public: bool Init(int sampleRate); void Term(); private: void KillOldHeaders(); private: HWAVEOUT m_hWaveOut; CWaveOutHdr m_Headers; // Head of a linked list of WAVEHDRs. int m_nBufferedSamples; }; CWaveOut::CWaveOut() { m_hWaveOut = NULL; m_Headers.m_pNext = NULL; m_nBufferedSamples = 0; } CWaveOut::~CWaveOut() { Term(); } void CWaveOut::Release() { delete this; } bool CWaveOut::PutSamples(short *pInSamples, int nInSamples) { int granularity = 2048; while( nInSamples ) { int nSamples = (nInSamples > granularity) ? granularity : nInSamples; short *pSamples = pInSamples; nInSamples -= nSamples; pInSamples += nSamples; if(!m_hWaveOut) return false; // Kill any old headers.. KillOldHeaders(); // Allocate a header.. CWaveOutHdr *pHdr; if(!(pHdr = (CWaveOutHdr*)malloc(sizeof(CWaveOutHdr) - 1 + nSamples*2))) return false; // Make a new one. memset(&pHdr->m_Hdr, 0, sizeof(pHdr->m_Hdr)); pHdr->m_Hdr.lpData = pHdr->m_Data; pHdr->m_Hdr.dwBufferLength = nSamples * 2; memcpy(pHdr->m_Data, pSamples, nSamples*2); MMRESULT mmr = waveOutPrepareHeader(m_hWaveOut, &pHdr->m_Hdr, sizeof(pHdr->m_Hdr)); if(mmr != MMSYSERR_NOERROR) return false; mmr = waveOutWrite(m_hWaveOut, &pHdr->m_Hdr, sizeof(pHdr->m_Hdr)); if(mmr != MMSYSERR_NOERROR) { delete pHdr; waveOutUnprepareHeader(m_hWaveOut, &pHdr->m_Hdr, sizeof(pHdr->m_Hdr)); return false; } m_nBufferedSamples += nSamples; // Queue up this header until waveOut is done with it. pHdr->m_pNext = m_Headers.m_pNext; m_Headers.m_pNext = pHdr; } return true; } void CWaveOut::Idle() { KillOldHeaders(); } int CWaveOut::GetNumBufferedSamples() { return m_nBufferedSamples; } bool CWaveOut::Init(int sampleRate) { Term(); WAVEFORMATEX format = { WAVE_FORMAT_PCM, // wFormatTag 1, // nChannels sampleRate, // nSamplesPerSec sampleRate*BYTES_PER_SAMPLE,// nAvgBytesPerSec BYTES_PER_SAMPLE, // nBlockAlign BYTES_PER_SAMPLE * 8, // wBitsPerSample sizeof(WAVEFORMATEX) }; MMRESULT mmr = waveOutOpen( &m_hWaveOut, 0, &format, 0, 0, CALLBACK_NULL); return mmr == MMSYSERR_NOERROR; } void CWaveOut::Term() { if(m_hWaveOut) { waveOutClose(m_hWaveOut); m_hWaveOut = NULL; } } void CWaveOut::KillOldHeaders() { // Look for any headers windows is done with. CWaveOutHdr *pNext; CWaveOutHdr **ppPrev = &m_Headers.m_pNext; for(CWaveOutHdr *pCur=m_Headers.m_pNext; pCur; pCur=pNext) { pNext = pCur->m_pNext; if(pCur->m_Hdr.dwFlags & WHDR_DONE) { m_nBufferedSamples -= (int)(pCur->m_Hdr.dwBufferLength / 2); assert(m_nBufferedSamples >= 0); waveOutUnprepareHeader(m_hWaveOut, &pCur->m_Hdr, sizeof(pCur->m_Hdr)); *ppPrev = pCur->m_pNext; free(pCur); } else { ppPrev = &pCur->m_pNext; } } } IWaveOut* CreateWaveOut(int sampleRate) { CWaveOut *pRet = new CWaveOut; if(pRet && pRet->Init(sampleRate)) { return pRet; } else { delete pRet; return NULL; } }