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.
570 lines
16 KiB
570 lines
16 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//===========================================================================// |
|
|
|
#include "audio_pch.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
extern bool snd_firsttime; |
|
extern bool MIX_ScaleChannelVolume( paintbuffer_t *ppaint, channel_t *pChannel, int volume[CCHANVOLUMES], int mixchans ); |
|
extern void S_SpatializeChannel( int volume[6], int master_vol, const Vector *psourceDir, float gain, float mono ); |
|
|
|
// 64K is > 1 second at 16-bit, 22050 Hz |
|
// 44k: UNDONE - need to double buffers now that we're playing back at 44100? |
|
#define WAV_BUFFERS 64 |
|
#define WAV_MASK 0x3F |
|
#define WAV_BUFFER_SIZE 0x0400 |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// NOTE: This only allows 16-bit, stereo wave out |
|
// |
|
//----------------------------------------------------------------------------- |
|
class CAudioDeviceWave : public CAudioDeviceBase |
|
{ |
|
public: |
|
bool IsActive( void ); |
|
bool Init( void ); |
|
void Shutdown( void ); |
|
void PaintEnd( void ); |
|
int GetOutputPosition( void ); |
|
void ChannelReset( int entnum, int channelIndex, float distanceMod ); |
|
void Pause( void ); |
|
void UnPause( void ); |
|
float MixDryVolume( void ); |
|
bool Should3DMix( void ); |
|
void StopAllSounds( void ); |
|
|
|
int PaintBegin( float mixAheadTime, int soundtime, int paintedtime ); |
|
void ClearBuffer( void ); |
|
void UpdateListener( const Vector& position, const Vector& forward, const Vector& right, const Vector& up ); |
|
void MixBegin( int sampleCount ); |
|
void MixUpsample( int sampleCount, int filtertype ); |
|
void Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress ); |
|
void Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress ); |
|
void Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress ); |
|
void Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress ); |
|
|
|
void TransferSamples( int end ); |
|
void SpatializeChannel( int volume[CCHANVOLUMES/2], int master_vol, const Vector& sourceDir, float gain, float mono); |
|
void ApplyDSPEffects( int idsp, portable_samplepair_t *pbuffront, portable_samplepair_t *pbufrear, portable_samplepair_t *pbufcenter, int samplecount ); |
|
|
|
const char *DeviceName( void ) { return "Windows WAVE"; } |
|
int DeviceChannels( void ) { return 2; } |
|
int DeviceSampleBits( void ) { return 16; } |
|
int DeviceSampleBytes( void ) { return 2; } |
|
int DeviceDmaSpeed( void ) { return SOUND_DMA_SPEED; } |
|
int DeviceSampleCount( void ) { return m_deviceSampleCount; } |
|
|
|
private: |
|
void OpenWaveOut( void ); |
|
void CloseWaveOut( void ); |
|
void AllocateOutputBuffers(); |
|
void FreeOutputBuffers(); |
|
void* AllocOutputMemory( int nSize, HGLOBAL &hMemory ); |
|
void FreeOutputMemory( HGLOBAL &hMemory ); |
|
bool ValidWaveOut( void ) const; |
|
|
|
int m_deviceSampleCount; |
|
|
|
int m_buffersSent; |
|
int m_buffersCompleted; |
|
int m_pauseCount; |
|
|
|
// This is a single allocation for all wave headers (there are OUTPUT_BUFFER_COUNT of them) |
|
HGLOBAL m_hWaveHdr; |
|
|
|
// This is a single allocation for all wave data (there are OUTPUT_BUFFER_COUNT of them) |
|
HANDLE m_hWaveData; |
|
|
|
HWAVEOUT m_waveOutHandle; |
|
|
|
// Memory for the wave data + wave headers |
|
void *m_pBuffer; |
|
LPWAVEHDR m_pWaveHdr; |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Class factory |
|
//----------------------------------------------------------------------------- |
|
IAudioDevice *Audio_CreateWaveDevice( void ) |
|
{ |
|
CAudioDeviceWave *wave = NULL; |
|
if ( !wave ) |
|
{ |
|
wave = new CAudioDeviceWave; |
|
} |
|
|
|
if ( wave->Init() ) |
|
return wave; |
|
|
|
delete wave; |
|
wave = NULL; |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Init, shutdown |
|
//----------------------------------------------------------------------------- |
|
bool CAudioDeviceWave::Init( void ) |
|
{ |
|
m_bSurround = false; |
|
m_bSurroundCenter = false; |
|
m_bHeadphone = false; |
|
m_buffersSent = 0; |
|
m_buffersCompleted = 0; |
|
m_pauseCount = 0; |
|
m_waveOutHandle = 0; |
|
m_pBuffer = NULL; |
|
m_pWaveHdr = NULL; |
|
m_hWaveHdr = NULL; |
|
m_hWaveData = NULL; |
|
|
|
OpenWaveOut(); |
|
|
|
if ( snd_firsttime ) |
|
{ |
|
DevMsg( "Wave sound initialized\n" ); |
|
} |
|
return ValidWaveOut(); |
|
} |
|
|
|
void CAudioDeviceWave::Shutdown( void ) |
|
{ |
|
CloseWaveOut(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// WAV out device |
|
//----------------------------------------------------------------------------- |
|
inline bool CAudioDeviceWave::ValidWaveOut( void ) const |
|
{ |
|
return m_waveOutHandle != 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Opens the windows wave out device |
|
//----------------------------------------------------------------------------- |
|
void CAudioDeviceWave::OpenWaveOut( void ) |
|
{ |
|
WAVEFORMATEX waveFormat; |
|
memset( &waveFormat, 0, sizeof(waveFormat) ); |
|
|
|
// Select a PCM, 16-bit stereo playback device |
|
waveFormat.cbSize = sizeof(waveFormat); |
|
waveFormat.wFormatTag = WAVE_FORMAT_PCM; |
|
waveFormat.nChannels = DeviceChannels(); |
|
waveFormat.wBitsPerSample = DeviceSampleBits(); |
|
waveFormat.nSamplesPerSec = DeviceDmaSpeed(); // DeviceSampleRate |
|
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; |
|
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; |
|
|
|
MMRESULT errorCode = waveOutOpen( &m_waveOutHandle, WAVE_MAPPER, &waveFormat, 0, 0L, CALLBACK_NULL ); |
|
while ( errorCode != MMSYSERR_NOERROR ) |
|
{ |
|
if ( errorCode != MMSYSERR_ALLOCATED ) |
|
{ |
|
DevWarning( "waveOutOpen failed\n" ); |
|
m_waveOutHandle = 0; |
|
return; |
|
} |
|
|
|
int nRetVal = MessageBox( NULL, |
|
"The sound hardware is in use by another app.\n\n" |
|
"Select Retry to try to start sound again or Cancel to run with no sound.", |
|
"Sound not available", |
|
MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION); |
|
|
|
if ( nRetVal != IDRETRY ) |
|
{ |
|
DevWarning( "waveOutOpen failure--hardware already in use\n" ); |
|
m_waveOutHandle = 0; |
|
return; |
|
} |
|
|
|
errorCode = waveOutOpen( &m_waveOutHandle, WAVE_MAPPER, &waveFormat, 0, 0L, CALLBACK_NULL ); |
|
} |
|
|
|
AllocateOutputBuffers(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Closes the windows wave out device |
|
//----------------------------------------------------------------------------- |
|
void CAudioDeviceWave::CloseWaveOut( void ) |
|
{ |
|
if ( ValidWaveOut() ) |
|
{ |
|
waveOutReset( m_waveOutHandle ); |
|
FreeOutputBuffers(); |
|
waveOutClose( m_waveOutHandle ); |
|
m_waveOutHandle = NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Alloc output memory |
|
//----------------------------------------------------------------------------- |
|
void* CAudioDeviceWave::AllocOutputMemory( int nSize, HGLOBAL &hMemory ) |
|
{ |
|
// Output memory for waveform data+hdrs must be |
|
// globally allocated with GMEM_MOVEABLE and GMEM_SHARE flags. |
|
hMemory = GlobalAlloc( GMEM_MOVEABLE | GMEM_SHARE, nSize ); |
|
if ( !hMemory ) |
|
{ |
|
DevWarning( "Sound: Out of memory.\n"); |
|
CloseWaveOut(); |
|
return NULL; |
|
} |
|
|
|
HPSTR lpData = (char *)GlobalLock( hMemory ); |
|
if ( !lpData ) |
|
{ |
|
DevWarning( "Sound: Failed to lock.\n"); |
|
GlobalFree( hMemory ); |
|
hMemory = NULL; |
|
CloseWaveOut(); |
|
return NULL; |
|
} |
|
memset( lpData, 0, nSize ); |
|
return lpData; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Free output memory |
|
//----------------------------------------------------------------------------- |
|
void CAudioDeviceWave::FreeOutputMemory( HGLOBAL &hMemory ) |
|
{ |
|
if ( hMemory ) |
|
{ |
|
GlobalUnlock( hMemory ); |
|
GlobalFree( hMemory ); |
|
hMemory = NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Allocate output buffers |
|
//----------------------------------------------------------------------------- |
|
void CAudioDeviceWave::AllocateOutputBuffers() |
|
{ |
|
// Allocate and lock memory for the waveform data. |
|
int nBufferSize = WAV_BUFFER_SIZE * WAV_BUFFERS; |
|
HPSTR lpData = (char *)AllocOutputMemory( nBufferSize, m_hWaveData ); |
|
if ( !lpData ) |
|
return; |
|
|
|
// Allocate and lock memory for the waveform header |
|
int nHdrSize = sizeof( WAVEHDR ) * WAV_BUFFERS; |
|
LPWAVEHDR lpWaveHdr = (LPWAVEHDR)AllocOutputMemory( nHdrSize, m_hWaveHdr ); |
|
if ( !lpWaveHdr ) |
|
return; |
|
|
|
// After allocation, set up and prepare headers. |
|
for ( int i=0 ; i < WAV_BUFFERS; i++ ) |
|
{ |
|
LPWAVEHDR lpHdr = lpWaveHdr + i; |
|
lpHdr->dwBufferLength = WAV_BUFFER_SIZE; |
|
lpHdr->lpData = lpData + (i * WAV_BUFFER_SIZE); |
|
|
|
MMRESULT nResult = waveOutPrepareHeader( m_waveOutHandle, lpHdr, sizeof(WAVEHDR) ); |
|
if ( nResult != MMSYSERR_NOERROR ) |
|
{ |
|
DevWarning( "Sound: failed to prepare wave headers\n" ); |
|
CloseWaveOut(); |
|
return; |
|
} |
|
} |
|
|
|
m_deviceSampleCount = nBufferSize / DeviceSampleBytes(); |
|
|
|
m_pBuffer = (void *)lpData; |
|
m_pWaveHdr = lpWaveHdr; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Free output buffers |
|
//----------------------------------------------------------------------------- |
|
void CAudioDeviceWave::FreeOutputBuffers() |
|
{ |
|
// Unprepare headers. |
|
if ( m_pWaveHdr ) |
|
{ |
|
for ( int i=0 ; i < WAV_BUFFERS; i++ ) |
|
{ |
|
waveOutUnprepareHeader( m_waveOutHandle, m_pWaveHdr+i, sizeof(WAVEHDR) ); |
|
} |
|
} |
|
m_pWaveHdr = NULL; |
|
m_pBuffer = NULL; |
|
|
|
FreeOutputMemory( m_hWaveData ); |
|
FreeOutputMemory( m_hWaveHdr ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Mixing setup |
|
//----------------------------------------------------------------------------- |
|
int CAudioDeviceWave::PaintBegin( float mixAheadTime, int soundtime, int paintedtime ) |
|
{ |
|
// soundtime - total samples that have been played out to hardware at dmaspeed |
|
// paintedtime - total samples that have been mixed at speed |
|
// endtime - target for samples in mixahead buffer at speed |
|
|
|
unsigned int endtime = soundtime + mixAheadTime * DeviceDmaSpeed(); |
|
|
|
int samps = DeviceSampleCount() >> (DeviceChannels()-1); |
|
|
|
if ((int)(endtime - soundtime) > samps) |
|
endtime = soundtime + samps; |
|
|
|
if ((endtime - paintedtime) & 0x3) |
|
{ |
|
// The difference between endtime and painted time should align on |
|
// boundaries of 4 samples. This is important when upsampling from 11khz -> 44khz. |
|
endtime -= (endtime - paintedtime) & 0x3; |
|
} |
|
|
|
return endtime; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Actually performs the mixing |
|
//----------------------------------------------------------------------------- |
|
void CAudioDeviceWave::PaintEnd( void ) |
|
{ |
|
LPWAVEHDR h; |
|
int wResult; |
|
int cblocks; |
|
|
|
// |
|
// find which sound blocks have completed |
|
// |
|
while (1) |
|
{ |
|
if ( m_buffersCompleted == m_buffersSent ) |
|
{ |
|
//DevMsg ("Sound overrun\n"); |
|
break; |
|
} |
|
|
|
if ( ! (m_pWaveHdr[ m_buffersCompleted & WAV_MASK].dwFlags & WHDR_DONE) ) |
|
{ |
|
break; |
|
} |
|
|
|
m_buffersCompleted++; // this buffer has been played |
|
} |
|
|
|
// |
|
// submit a few new sound blocks |
|
// |
|
// 22K sound support |
|
// 44k: UNDONE - double blocks out now that we're at 44k playback? |
|
cblocks = 4 << 1; |
|
|
|
while (((m_buffersSent - m_buffersCompleted) >> SAMPLE_16BIT_SHIFT) < cblocks) |
|
{ |
|
h = m_pWaveHdr + ( m_buffersSent&WAV_MASK ); |
|
|
|
m_buffersSent++; |
|
/* |
|
* Now the data block can be sent to the output device. The |
|
* waveOutWrite function returns immediately and waveform |
|
* data is sent to the output device in the background. |
|
*/ |
|
wResult = waveOutWrite( m_waveOutHandle, h, sizeof(WAVEHDR) ); |
|
|
|
if (wResult != MMSYSERR_NOERROR) |
|
{ |
|
Warning( "Failed to write block to device\n"); |
|
Shutdown(); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
int CAudioDeviceWave::GetOutputPosition( void ) |
|
{ |
|
int s = m_buffersSent * WAV_BUFFER_SIZE; |
|
|
|
s >>= SAMPLE_16BIT_SHIFT; |
|
|
|
s &= (DeviceSampleCount()-1); |
|
|
|
return s / DeviceChannels(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Pausing |
|
//----------------------------------------------------------------------------- |
|
void CAudioDeviceWave::Pause( void ) |
|
{ |
|
m_pauseCount++; |
|
if (m_pauseCount == 1) |
|
{ |
|
waveOutReset( m_waveOutHandle ); |
|
} |
|
} |
|
|
|
|
|
void CAudioDeviceWave::UnPause( void ) |
|
{ |
|
if ( m_pauseCount > 0 ) |
|
{ |
|
m_pauseCount--; |
|
} |
|
} |
|
|
|
bool CAudioDeviceWave::IsActive( void ) |
|
{ |
|
return ( m_pauseCount == 0 ); |
|
} |
|
|
|
float CAudioDeviceWave::MixDryVolume( void ) |
|
{ |
|
return 0; |
|
} |
|
|
|
|
|
bool CAudioDeviceWave::Should3DMix( void ) |
|
{ |
|
return false; |
|
} |
|
|
|
|
|
void CAudioDeviceWave::ClearBuffer( void ) |
|
{ |
|
int clear; |
|
|
|
if ( !m_pBuffer ) |
|
return; |
|
|
|
clear = 0; |
|
|
|
Q_memset(m_pBuffer, clear, DeviceSampleCount() * DeviceSampleBytes() ); |
|
} |
|
|
|
void CAudioDeviceWave::UpdateListener( const Vector& position, const Vector& forward, const Vector& right, const Vector& up ) |
|
{ |
|
} |
|
|
|
|
|
void CAudioDeviceWave::MixBegin( int sampleCount ) |
|
{ |
|
MIX_ClearAllPaintBuffers( sampleCount, false ); |
|
} |
|
|
|
|
|
void CAudioDeviceWave::MixUpsample( int sampleCount, int filtertype ) |
|
{ |
|
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr(); |
|
int ifilter = ppaint->ifilter; |
|
|
|
Assert (ifilter < CPAINTFILTERS); |
|
|
|
S_MixBufferUpsample2x( sampleCount, ppaint->pbuf, &(ppaint->fltmem[ifilter][0]), CPAINTFILTERMEM, filtertype ); |
|
|
|
ppaint->ifilter++; |
|
} |
|
|
|
void CAudioDeviceWave::Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress ) |
|
{ |
|
int volume[CCHANVOLUMES]; |
|
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr(); |
|
|
|
if (!MIX_ScaleChannelVolume( ppaint, pChannel, volume, 1)) |
|
return; |
|
|
|
Mix8MonoWavtype( pChannel, ppaint->pbuf + outputOffset, volume, (byte *)pData, inputOffset, rateScaleFix, outCount ); |
|
} |
|
|
|
|
|
void CAudioDeviceWave::Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress ) |
|
{ |
|
int volume[CCHANVOLUMES]; |
|
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr(); |
|
|
|
if (!MIX_ScaleChannelVolume( ppaint, pChannel, volume, 2 )) |
|
return; |
|
|
|
Mix8StereoWavtype( pChannel, ppaint->pbuf + outputOffset, volume, (byte *)pData, inputOffset, rateScaleFix, outCount ); |
|
} |
|
|
|
|
|
void CAudioDeviceWave::Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress ) |
|
{ |
|
int volume[CCHANVOLUMES]; |
|
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr(); |
|
|
|
if (!MIX_ScaleChannelVolume( ppaint, pChannel, volume, 1 )) |
|
return; |
|
|
|
Mix16MonoWavtype( pChannel, ppaint->pbuf + outputOffset, volume, pData, inputOffset, rateScaleFix, outCount ); |
|
} |
|
|
|
|
|
void CAudioDeviceWave::Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress ) |
|
{ |
|
int volume[CCHANVOLUMES]; |
|
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr(); |
|
|
|
if (!MIX_ScaleChannelVolume( ppaint, pChannel, volume, 2 )) |
|
return; |
|
|
|
Mix16StereoWavtype( pChannel, ppaint->pbuf + outputOffset, volume, pData, inputOffset, rateScaleFix, outCount ); |
|
} |
|
|
|
|
|
void CAudioDeviceWave::ChannelReset( int entnum, int channelIndex, float distanceMod ) |
|
{ |
|
} |
|
|
|
|
|
void CAudioDeviceWave::TransferSamples( int end ) |
|
{ |
|
int lpaintedtime = g_paintedtime; |
|
int endtime = end; |
|
|
|
// resumes playback... |
|
|
|
if ( m_pBuffer ) |
|
{ |
|
S_TransferStereo16( m_pBuffer, PAINTBUFFER, lpaintedtime, endtime ); |
|
} |
|
} |
|
|
|
void CAudioDeviceWave::SpatializeChannel( int volume[CCHANVOLUMES/2], int master_vol, const Vector& sourceDir, float gain, float mono ) |
|
{ |
|
VPROF("CAudioDeviceWave::SpatializeChannel"); |
|
S_SpatializeChannel( volume, master_vol, &sourceDir, gain, mono ); |
|
} |
|
|
|
void CAudioDeviceWave::StopAllSounds( void ) |
|
{ |
|
} |
|
|
|
|
|
void CAudioDeviceWave::ApplyDSPEffects( int idsp, portable_samplepair_t *pbuffront, portable_samplepair_t *pbufrear, portable_samplepair_t *pbufcenter, int samplecount ) |
|
{ |
|
//SX_RoomFX( endtime, filter, timefx ); |
|
DSP_Process( idsp, pbuffront, pbufrear, pbufcenter, samplecount ); |
|
}
|
|
|