|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include <windows.h>
#include <assert.h>
#include <mmsystem.h>
#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; } }
|