source-engine/utils/voice_tweak/waveout.cpp

206 lines
3.6 KiB
C++
Raw Normal View History

2020-04-23 00:56:21 +08:00
//========= 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;
}
}