mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2026-03-29 00:09:40 -06:00
340 lines
7.4 KiB
C++
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);
|
|
}
|