|
|
//===== Copyright � 1996-2005, 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, float volume[CCHANVOLUMES], int mixchans ); //extern void S_SpatializeChannel( int nSlot, 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 );
int64 PaintBegin( float mixAheadTime, int64 soundtime, int64 paintedtime ); void ClearBuffer( void ); 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 nSlot, 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 ) { static 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_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
//-----------------------------------------------------------------------------
int64 CAudioDeviceWave::PaintBegin( float mixAheadTime, int64 soundtime, int64 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
int64 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::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 ) { float 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 ) { float 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 ) { float 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 ) { float 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 ) { int64 lpaintedtime = g_paintedtime; int64 endtime = end; // resumes playback...
if ( m_pBuffer ) { S_TransferStereo16( m_pBuffer, PAINTBUFFER, lpaintedtime, endtime ); } }
// temporarily deprecating to be sure which version of SpatializeChannel is used
/*void CAudioDeviceWave::SpatializeChannel( int nSlot, int volume[CCHANVOLUMES/2], int master_vol, const Vector& sourceDir, float gain, float mono )
{ VPROF("CAudioDeviceWave::SpatializeChannel"); S_SpatializeChannel( nSlot, 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 ); }
|