You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
3.6 KiB
206 lines
3.6 KiB
5 years ago
|
//========= 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|