hostile-takeover/game/win/win32sounddev.cpp
2014-07-06 17:47:28 -07:00

340 lines
7.4 KiB
C++

#include "..\ht.h"
#define kcChannels 4
class Win32SoundDevice : public SoundDevice // sndd
{
public:
Win32SoundDevice();
virtual ~Win32SoundDevice();
bool Init();
virtual void Enable(bool fEnable);
virtual bool IsEnabled();
virtual void PlayAdpcm(int ichnl, byte *pb, word cb);
virtual int GetChannelCount();
virtual bool IsChannelFree(int ichnl);
virtual void ServiceProc();
virtual bool IsSilent();
virtual void SetVolume(int nVolume);
virtual int GetVolume();
private:
void InitNextBufferForPlayback(int nBuffer);
int m_cBuffers;
int m_cbBuffer;
HWAVEOUT m_hwavo;
WAVEHDR *m_awavHeader;
byte *m_abWaveData;
Channel m_achnl[kcChannels];
bool m_fEnable;
long m_tSilence;
int m_nVolumeLast;
bool m_fVolumeFixed;
CRITICAL_SECTION m_crit;
friend void CALLBACK WaveOutProc(HWAVEOUT hwavo, UINT msg, dword dwInstance, dword wp, dword lp);
};
SoundDevice *CreateWin32SoundDevice()
{
Win32SoundDevice *pwinsndd = new Win32SoundDevice;
if (pwinsndd == NULL)
return NULL;
if (!pwinsndd->Init()) {
delete pwinsndd;
return NULL;
}
return (SoundDevice *)pwinsndd;
}
Win32SoundDevice::Win32SoundDevice()
{
m_tSilence = 0;
m_fEnable = false;
m_hwavo = NULL;
m_awavHeader = NULL;
m_abWaveData = NULL;
m_cBuffers = 0;
m_cbBuffer = 0;
m_fVolumeFixed = false;
InitializeCriticalSection(&m_crit);
}
Win32SoundDevice::~Win32SoundDevice()
{
EnterCriticalSection(&m_crit);
Enable(false);
if (m_hwavo != NULL) {
waveOutReset(m_hwavo);
for (int n = 0; n < m_cBuffers; n++)
waveOutUnprepareHeader(m_hwavo, &m_awavHeader[n], sizeof(WAVEHDR));
if (m_hwavo != NULL) {
waveOutClose(m_hwavo);
m_hwavo = NULL;
}
}
LeaveCriticalSection(&m_crit);
DeleteCriticalSection(&m_crit);
delete m_awavHeader;
m_awavHeader = NULL;
delete m_abWaveData;
m_abWaveData = NULL;
}
bool Win32SoundDevice::Init()
{
#ifdef CE
// Get version information. Buffer size will be based on this
OSVERSIONINFO osver;
osver.dwOSVersionInfoSize = sizeof(osver);
GetVersionEx(&osver);
#define kdwBuildPocketPC 9348
#define kdwBuildPocketPC2002 11171
#define kcBuffersSlow 6
#define kcbBufferSlow 512
#define kcBuffersFast 6
#define kcbBufferFast 512
m_cBuffers = kcBuffersFast;
m_cbBuffer = kcbBufferFast;
// Below PocketPC2002 latency performance sucks.
if (osver.dwMajorVersion == 3) {
// Is PocketPC but less than 2002?
if (osver.dwMinorVersion == 0 && osver.dwBuildNumber < kdwBuildPocketPC2002) {
m_cBuffers = kcBuffersSlow;
m_cbBuffer = kcbBufferSlow;
}
}
#else
// Windows
m_cBuffers = 3;
m_cbBuffer = 256;
#endif
m_awavHeader = new WAVEHDR[m_cBuffers];
if (m_awavHeader == NULL)
return false;
m_abWaveData = new byte[m_cBuffers * m_cbBuffer];
if (m_abWaveData == NULL)
return false;
EnterCriticalSection(&m_crit);
// Open sound device
WAVEFORMATEX fmt;
fmt.wFormatTag = WAVE_FORMAT_PCM;
fmt.nChannels = 1;
fmt.nSamplesPerSec = 8000;
fmt.nAvgBytesPerSec = 8000;
fmt.nBlockAlign = 1;
fmt.wBitsPerSample = 8;
fmt.cbSize = 0;
MMRESULT res = waveOutOpen(&m_hwavo, WAVE_MAPPER, &fmt,
(dword)WaveOutProc, (dword)this, CALLBACK_FUNCTION);
if (res != MMSYSERR_NOERROR) {
LeaveCriticalSection(&m_crit);
return false;
}
// Remember what the initial volume is
m_nVolumeLast = GetVolume();
// Init headers
for (int n = 0; n < m_cBuffers; n++) {
memset(&m_awavHeader[n], 0, sizeof(WAVEHDR));
m_awavHeader[n].lpData = (char *)&m_abWaveData[n * m_cbBuffer];
m_awavHeader[n].dwBufferLength = m_cbBuffer;
waveOutPrepareHeader(m_hwavo, &m_awavHeader[n], sizeof(WAVEHDR));
}
// Some PocketPC devices disallow devices to change the volume. Detect if we're running on
// one of these types of devices.
int nVolumeSave = GetVolume();
if (nVolumeSave >= 0x80) {
SetVolume(nVolumeSave - 0x20);
} else {
SetVolume(nVolumeSave + 0x20);
}
int nVolumeNew = GetVolume();
SetVolume(nVolumeSave);
if (nVolumeNew == nVolumeSave)
m_fVolumeFixed = true;
LeaveCriticalSection(&m_crit);
return true;
}
bool Win32SoundDevice::IsEnabled()
{
return m_fEnable;
}
void Win32SoundDevice::Enable(bool fEnable)
{
EnterCriticalSection(&m_crit);
if (fEnable) {
if (!m_fEnable) {
memset(m_achnl, 0, sizeof(m_achnl));
m_tSilence = 0;
m_fEnable = true;
for (int nBuffer = 0; nBuffer < m_cBuffers; nBuffer++) {
m_awavHeader[nBuffer].dwFlags |= WHDR_DONE;
InitNextBufferForPlayback(nBuffer);
}
SetSoundServiceDevice(this);
}
} else {
if (m_fEnable) {
memset(m_achnl, 0, sizeof(m_achnl));
m_tSilence = 0;
m_fEnable = false;
SetSoundServiceDevice(NULL);
}
}
LeaveCriticalSection(&m_crit);
}
int Win32SoundDevice::GetChannelCount()
{
return kcChannels;
}
bool Win32SoundDevice::IsChannelFree(int ichnl)
{
if (ichnl < 0 || ichnl >= kcChannels)
return false;
Channel *pchnl = &m_achnl[ichnl];
return (pchnl->pb >= pchnl->pbEnd);
}
bool Win32SoundDevice::IsSilent()
{
if (!m_fEnable)
return true;
if (m_tSilence == 0)
return true;
long tCurrent = HostGetTickCount();
if (tCurrent < 0 && m_tSilence >= 0)
return true;
return tCurrent >= m_tSilence;
}
void Win32SoundDevice::ServiceProc()
{
#ifdef CHECK_OLD_ALLOCS
my_alloccheck();
#endif
}
void Win32SoundDevice::PlayAdpcm(int ichnl, byte *pb, word cb)
{
if (ichnl < 0 || ichnl >= kcChannels)
return;
if (!m_fEnable)
return;
// Some PocketPC's reset the volume to the "system setting" when the
// unit is turned off then turned back on. Reset the volume to our
// setting in this case
int nVolume = GetVolume();
if (nVolume != m_nVolumeLast)
SetVolume(m_nVolumeLast);
// Start it up
Channel *pchnl = &m_achnl[ichnl];
pchnl->pb = pb;
pchnl->pbEnd = pb + cb;
pchnl->nStepIndex = 0;
pchnl->bSampleLast = 128;
m_tSilence = HostGetTickCount() + (cb + m_cBuffers * m_cbBuffer + 39) / 40 + 1;
}
void Win32SoundDevice::SetVolume(int nVolume)
{
if (m_fVolumeFixed)
return;
if (nVolume < 0)
nVolume = 0;
if (nVolume > 255)
nVolume = 255;
dword dwVolume = nVolume | (nVolume << 8) | (nVolume << 16) | (nVolume << 24);
EnterCriticalSection(&m_crit);
waveOutSetVolume(m_hwavo, dwVolume);
m_nVolumeLast = GetVolume();
LeaveCriticalSection(&m_crit);
}
int Win32SoundDevice::GetVolume()
{
if (m_fVolumeFixed)
return -1;
dword dwVolume;
EnterCriticalSection(&m_crit);
waveOutGetVolume(m_hwavo, &dwVolume);
LeaveCriticalSection(&m_crit);
return (int)(byte)dwVolume;
}
void CALLBACK WaveOutProc(HWAVEOUT hwavo, UINT msg, dword dwInstance, dword wp, dword lp)
{
if (msg != WOM_DONE)
return;
Win32SoundDevice *pwinsndd = (Win32SoundDevice *)dwInstance;
WAVEHDR *pwavHeader = (WAVEHDR *)wp;
int nBuffer = pwavHeader - pwinsndd->m_awavHeader;
pwinsndd->InitNextBufferForPlayback(nBuffer);
}
void Win32SoundDevice::InitNextBufferForPlayback(int nBuffer)
{
// Don't send buffers to the device if not enabled
if (!m_fEnable)
return;
// Fill the chunk and queue it for playing
Assert(nBuffer >= 0 && nBuffer < m_cBuffers);
if (nBuffer < 0 || nBuffer >= m_cBuffers)
return;
// This is protected by critical sections! The second thread and the UI thread
// can both hang on waveOutWrite unless access is serialized.
EnterCriticalSection(&m_crit);
Assert(m_awavHeader[nBuffer].dwFlags & WHDR_DONE);
m_awavHeader[nBuffer].dwFlags &= ~WHDR_DONE;
MixChannels(m_achnl, ARRAYSIZE(m_achnl), &m_abWaveData[nBuffer * m_cbBuffer], m_cbBuffer);
waveOutWrite(m_hwavo, &m_awavHeader[nBuffer], sizeof(WAVEHDR));
// For AVI recording
#ifndef CE
if (gpavir != NULL)
gpavir->AddAudio(&m_abWaveData[nBuffer * m_cbBuffer], m_cbBuffer);
#endif
LeaveCriticalSection(&m_crit);
}