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.
903 lines
23 KiB
903 lines
23 KiB
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "snd_dev_wave.h"
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#pragma warning( disable: 4201 )
|
|
#include <mmsystem.h>
|
|
#pragma warning( default: 4201 )
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include "soundsystem/snd_audio_source.h"
|
|
#include "soundsystem.h"
|
|
#include "soundsystem/snd_device.h"
|
|
#include "tier1/utlvector.h"
|
|
#include "FileSystem.h"
|
|
#include "sentence.h"
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_SoundSystem, "SoundSystem", 0, LS_ERROR );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Forward declarations
|
|
//-----------------------------------------------------------------------------
|
|
class CAudioMixer;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Important constants
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// 64K is > 1 second at 16-bit, 22050 Hz
|
|
// 44k: UNDONE - need to double buffers now that we're playing back at 44100?
|
|
#define OUTPUT_CHANNEL_COUNT 2
|
|
#define BYTES_PER_SAMPLE 2
|
|
#define OUTPUT_SAMPLE_RATE SOUND_DMA_SPEED
|
|
#define OUTPUT_BUFFER_COUNT 64
|
|
#define OUTPUT_BUFFER_MASK 0x3F
|
|
#define OUTPUT_BUFFER_SAMPLE_COUNT (OUTPUT_BUFFER_SIZE_BYTES / BYTES_PER_SAMPLE)
|
|
#define OUTPUT_BUFFER_SIZE_BYTES 1024
|
|
#define PAINTBUFFER_SIZE 1024
|
|
#define MAX_CHANNELS 16
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Implementation of IAudioDevice for WAV files
|
|
//-----------------------------------------------------------------------------
|
|
class CAudioDeviceWave : public IAudioDevice
|
|
{
|
|
public:
|
|
// Inherited from IAudioDevice
|
|
virtual bool Init( void );
|
|
virtual void Shutdown( void );
|
|
virtual const char *DeviceName( void ) const;
|
|
virtual int DeviceChannels( void ) const;
|
|
virtual int DeviceSampleBits( void ) const;
|
|
virtual int DeviceSampleBytes( void ) const;
|
|
virtual int DeviceSampleRate( void ) const;
|
|
virtual int DeviceSampleCount( void ) const;
|
|
virtual void Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true );
|
|
virtual void Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true );
|
|
virtual void Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true );
|
|
virtual void Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true );
|
|
virtual int PaintBufferSampleCount( void ) const;
|
|
virtual void MixBegin( void );
|
|
|
|
// mix a buffer up to time (time is absolute)
|
|
void Update( float time );
|
|
void Flush( void );
|
|
void TransferBufferStereo16( short *pOutput, int sampleCount );
|
|
int GetOutputPosition( void );
|
|
float GetAmountofTimeAhead( void );
|
|
int GetNumberofSamplesAhead( void );
|
|
void AddSource( CAudioMixer *pSource );
|
|
void StopSounds( void );
|
|
int FindSourceIndex( CAudioMixer *pSource );
|
|
CAudioMixer *GetMixerForSource( CAudioSource *source );
|
|
|
|
private:
|
|
class CAudioMixerState
|
|
{
|
|
public:
|
|
CAudioMixer *mixer;
|
|
};
|
|
|
|
class CAudioBuffer
|
|
{
|
|
public:
|
|
WAVEHDR *hdr;
|
|
bool submitted;
|
|
int submit_sample_count;
|
|
|
|
CUtlVector< CAudioMixerState > m_Referenced;
|
|
};
|
|
|
|
struct portable_samplepair_t
|
|
{
|
|
int left;
|
|
int right;
|
|
};
|
|
|
|
void OpenWaveOut( void );
|
|
void CloseWaveOut( void );
|
|
void AllocateOutputBuffers();
|
|
void FreeOutputBuffers();
|
|
void* AllocOutputMemory( int nSize, HGLOBAL &hMemory );
|
|
void FreeOutputMemory( HGLOBAL &hMemory );
|
|
|
|
bool ValidWaveOut( void ) const;
|
|
CAudioBuffer *GetEmptyBuffer( void );
|
|
void SilenceBuffer( short *pSamples, int sampleCount );
|
|
|
|
void SetChannel( int channelIndex, CAudioMixer *pSource );
|
|
void FreeChannel( int channelIndex );
|
|
|
|
void RemoveMixerChannelReferences( CAudioMixer *mixer );
|
|
void AddToReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer );
|
|
void RemoveFromReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer );
|
|
bool IsSourceReferencedByActiveBuffer( CAudioMixer *mixer );
|
|
bool IsSoundInReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer );
|
|
|
|
// Compute how many samples we've mixed since most recent buffer submission
|
|
void ComputeSampleAheadAmount( void );
|
|
|
|
// 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;
|
|
float m_mixTime;
|
|
float m_baseTime;
|
|
int m_sampleIndex;
|
|
CAudioBuffer m_buffers[ OUTPUT_BUFFER_COUNT ];
|
|
CAudioMixer *m_sourceList[MAX_CHANNELS];
|
|
int m_nEstimatedSamplesAhead;
|
|
|
|
portable_samplepair_t m_paintbuffer[ PAINTBUFFER_SIZE ];
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Singleton
|
|
//-----------------------------------------------------------------------------
|
|
IAudioDevice *Audio_CreateWaveDevice( void )
|
|
{
|
|
return new CAudioDeviceWave;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Init, shutdown
|
|
//-----------------------------------------------------------------------------
|
|
bool CAudioDeviceWave::Init( void )
|
|
{
|
|
m_hWaveData = NULL;
|
|
m_hWaveHdr = NULL;
|
|
m_waveOutHandle = NULL;
|
|
|
|
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
|
{
|
|
CAudioBuffer *buffer = &m_buffers[ i ];
|
|
Assert( buffer );
|
|
buffer->hdr = NULL;
|
|
buffer->submitted = false;
|
|
buffer->submit_sample_count = false;
|
|
}
|
|
|
|
OpenWaveOut();
|
|
|
|
m_mixTime = m_baseTime = -1;
|
|
m_sampleIndex = 0;
|
|
memset( m_sourceList, 0, sizeof(m_sourceList) );
|
|
|
|
m_nEstimatedSamplesAhead = (int)( ( float ) OUTPUT_SAMPLE_RATE / 10.0f );
|
|
|
|
return true;
|
|
}
|
|
|
|
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 = 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 )
|
|
{
|
|
Log_Warning( LOG_SoundSystem, "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 )
|
|
{
|
|
Log_Warning( LOG_SoundSystem, "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 )
|
|
{
|
|
Log_Warning( LOG_SoundSystem, "Sound: Out of memory.\n");
|
|
CloseWaveOut();
|
|
return NULL;
|
|
}
|
|
|
|
HPSTR lpData = (char *)GlobalLock( hMemory );
|
|
if ( !lpData )
|
|
{
|
|
Log_Warning( LOG_SoundSystem, "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, free output buffers
|
|
//-----------------------------------------------------------------------------
|
|
void CAudioDeviceWave::AllocateOutputBuffers()
|
|
{
|
|
// Allocate and lock memory for the waveform data.
|
|
int nBufferSize = OUTPUT_BUFFER_SIZE_BYTES * OUTPUT_BUFFER_COUNT;
|
|
HPSTR lpData = (char *)AllocOutputMemory( nBufferSize, m_hWaveData );
|
|
if ( !lpData )
|
|
return;
|
|
|
|
// Allocate and lock memory for the waveform header
|
|
int nHdrSize = sizeof( WAVEHDR ) * OUTPUT_BUFFER_COUNT;
|
|
LPWAVEHDR lpWaveHdr = (LPWAVEHDR)AllocOutputMemory( nHdrSize, m_hWaveHdr );
|
|
if ( !lpWaveHdr )
|
|
return;
|
|
|
|
// After allocation, set up and prepare headers.
|
|
for ( int i=0 ; i < OUTPUT_BUFFER_COUNT; i++ )
|
|
{
|
|
LPWAVEHDR lpHdr = lpWaveHdr + i;
|
|
lpHdr->dwBufferLength = OUTPUT_BUFFER_SIZE_BYTES;
|
|
lpHdr->lpData = lpData + (i * OUTPUT_BUFFER_SIZE_BYTES);
|
|
|
|
MMRESULT nResult = waveOutPrepareHeader( m_waveOutHandle, lpHdr, sizeof(WAVEHDR) );
|
|
if ( nResult != MMSYSERR_NOERROR )
|
|
{
|
|
Log_Warning( LOG_SoundSystem, "Sound: failed to prepare wave headers\n" );
|
|
CloseWaveOut();
|
|
return;
|
|
}
|
|
|
|
m_buffers[i].hdr = lpHdr;
|
|
}
|
|
}
|
|
|
|
|
|
void CAudioDeviceWave::FreeOutputBuffers()
|
|
{
|
|
// Unprepare headers.
|
|
for ( int i=0 ; i < OUTPUT_BUFFER_COUNT; i++ )
|
|
{
|
|
if ( m_buffers[i].hdr )
|
|
{
|
|
waveOutUnprepareHeader( m_waveOutHandle, m_buffers[i].hdr, sizeof(WAVEHDR) );
|
|
m_buffers[i].hdr = NULL;
|
|
}
|
|
|
|
m_buffers[i].submitted = false;
|
|
m_buffers[i].submit_sample_count = 0;
|
|
m_buffers[i].m_Referenced.Purge();
|
|
}
|
|
|
|
FreeOutputMemory( m_hWaveData );
|
|
FreeOutputMemory( m_hWaveHdr );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Device parameters
|
|
//-----------------------------------------------------------------------------
|
|
const char *CAudioDeviceWave::DeviceName( void ) const
|
|
{
|
|
return "Windows WAVE";
|
|
}
|
|
|
|
int CAudioDeviceWave::DeviceChannels( void ) const
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
int CAudioDeviceWave::DeviceSampleBits( void ) const
|
|
{
|
|
return (BYTES_PER_SAMPLE * 8);
|
|
}
|
|
|
|
int CAudioDeviceWave::DeviceSampleBytes( void ) const
|
|
{
|
|
return BYTES_PER_SAMPLE;
|
|
}
|
|
|
|
int CAudioDeviceWave::DeviceSampleRate( void ) const
|
|
{
|
|
return OUTPUT_SAMPLE_RATE;
|
|
}
|
|
|
|
int CAudioDeviceWave::DeviceSampleCount( void ) const
|
|
{
|
|
return OUTPUT_BUFFER_SAMPLE_COUNT;
|
|
}
|
|
|
|
int CAudioDeviceWave::PaintBufferSampleCount( void ) const
|
|
{
|
|
return PAINTBUFFER_SIZE;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Mixing routines
|
|
//-----------------------------------------------------------------------------
|
|
void CAudioDeviceWave::Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward )
|
|
{
|
|
int sampleIndex = 0;
|
|
fixedint sampleFrac = inputOffset;
|
|
|
|
int fixup = 0;
|
|
int fixupstep = 1;
|
|
|
|
if ( !forward )
|
|
{
|
|
fixup = outCount - 1;
|
|
fixupstep = -1;
|
|
}
|
|
|
|
for ( int i = 0; i < outCount; i++, fixup += fixupstep )
|
|
{
|
|
int dest = max( outputOffset + fixup, 0 );
|
|
|
|
m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex];
|
|
m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex];
|
|
sampleFrac += rateScaleFix;
|
|
sampleIndex += FIX_INTPART(sampleFrac);
|
|
sampleFrac = FIX_FRACPART(sampleFrac);
|
|
}
|
|
}
|
|
|
|
|
|
void CAudioDeviceWave::Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward )
|
|
{
|
|
int sampleIndex = 0;
|
|
fixedint sampleFrac = inputOffset;
|
|
|
|
int fixup = 0;
|
|
int fixupstep = 1;
|
|
|
|
if ( !forward )
|
|
{
|
|
fixup = outCount - 1;
|
|
fixupstep = -1;
|
|
}
|
|
|
|
for ( int i = 0; i < outCount; i++, fixup += fixupstep )
|
|
{
|
|
int dest = max( outputOffset + fixup, 0 );
|
|
|
|
m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex];
|
|
m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex+1];
|
|
sampleFrac += rateScaleFix;
|
|
sampleIndex += FIX_INTPART(sampleFrac)<<1;
|
|
sampleFrac = FIX_FRACPART(sampleFrac);
|
|
}
|
|
}
|
|
|
|
|
|
void CAudioDeviceWave::Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward )
|
|
{
|
|
int sampleIndex = 0;
|
|
fixedint sampleFrac = inputOffset;
|
|
|
|
int fixup = 0;
|
|
int fixupstep = 1;
|
|
|
|
if ( !forward )
|
|
{
|
|
fixup = outCount - 1;
|
|
fixupstep = -1;
|
|
}
|
|
|
|
for ( int i = 0; i < outCount; i++, fixup += fixupstep )
|
|
{
|
|
int dest = max( outputOffset + fixup, 0 );
|
|
|
|
m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8;
|
|
m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex])>>8;
|
|
sampleFrac += rateScaleFix;
|
|
sampleIndex += FIX_INTPART(sampleFrac);
|
|
sampleFrac = FIX_FRACPART(sampleFrac);
|
|
}
|
|
}
|
|
|
|
|
|
void CAudioDeviceWave::Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward )
|
|
{
|
|
int sampleIndex = 0;
|
|
fixedint sampleFrac = inputOffset;
|
|
|
|
int fixup = 0;
|
|
int fixupstep = 1;
|
|
|
|
if ( !forward )
|
|
{
|
|
fixup = outCount - 1;
|
|
fixupstep = -1;
|
|
}
|
|
|
|
for ( int i = 0; i < outCount; i++, fixup += fixupstep )
|
|
{
|
|
int dest = max( outputOffset + fixup, 0 );
|
|
|
|
m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8;
|
|
m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex+1])>>8;
|
|
|
|
sampleFrac += rateScaleFix;
|
|
sampleIndex += FIX_INTPART(sampleFrac)<<1;
|
|
sampleFrac = FIX_FRACPART(sampleFrac);
|
|
}
|
|
}
|
|
|
|
|
|
void CAudioDeviceWave::MixBegin( void )
|
|
{
|
|
memset( m_paintbuffer, 0, sizeof(m_paintbuffer) );
|
|
}
|
|
|
|
void CAudioDeviceWave::TransferBufferStereo16( short *pOutput, int sampleCount )
|
|
{
|
|
for ( int i = 0; i < sampleCount; i++ )
|
|
{
|
|
if ( m_paintbuffer[i].left > 32767 )
|
|
m_paintbuffer[i].left = 32767;
|
|
else if ( m_paintbuffer[i].left < -32768 )
|
|
m_paintbuffer[i].left = -32768;
|
|
|
|
if ( m_paintbuffer[i].right > 32767 )
|
|
m_paintbuffer[i].right = 32767;
|
|
else if ( m_paintbuffer[i].right < -32768 )
|
|
m_paintbuffer[i].right = -32768;
|
|
|
|
*pOutput++ = (short)m_paintbuffer[i].left;
|
|
*pOutput++ = (short)m_paintbuffer[i].right;
|
|
}
|
|
}
|
|
|
|
void CAudioDeviceWave::RemoveMixerChannelReferences( CAudioMixer *mixer )
|
|
{
|
|
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
|
{
|
|
RemoveFromReferencedList( mixer, &m_buffers[ i ] );
|
|
}
|
|
}
|
|
|
|
void CAudioDeviceWave::AddToReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer )
|
|
{
|
|
// Already in list
|
|
for ( int i = 0; i < buffer->m_Referenced.Count(); i++ )
|
|
{
|
|
if ( buffer->m_Referenced[ i ].mixer == mixer )
|
|
return;
|
|
}
|
|
|
|
// Just remove it
|
|
int idx = buffer->m_Referenced.AddToTail();
|
|
|
|
CAudioMixerState *state = &buffer->m_Referenced[ idx ];
|
|
state->mixer = mixer;
|
|
}
|
|
|
|
void CAudioDeviceWave::RemoveFromReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer )
|
|
{
|
|
for ( int i = 0; i < buffer->m_Referenced.Count(); i++ )
|
|
{
|
|
if ( buffer->m_Referenced[ i ].mixer == mixer )
|
|
{
|
|
buffer->m_Referenced.Remove( i );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CAudioDeviceWave::IsSoundInReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer )
|
|
{
|
|
for ( int i = 0; i < buffer->m_Referenced.Count(); i++ )
|
|
{
|
|
if ( buffer->m_Referenced[ i ].mixer == mixer )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CAudioDeviceWave::IsSourceReferencedByActiveBuffer( CAudioMixer *mixer )
|
|
{
|
|
if ( !ValidWaveOut() )
|
|
return false;
|
|
|
|
CAudioBuffer *buffer;
|
|
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
|
{
|
|
buffer = &m_buffers[ i ];
|
|
if ( !buffer->submitted )
|
|
continue;
|
|
|
|
if ( buffer->hdr->dwFlags & WHDR_DONE )
|
|
continue;
|
|
|
|
// See if it's referenced
|
|
if ( IsSoundInReferencedList( mixer, buffer ) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CAudioDeviceWave::CAudioBuffer *CAudioDeviceWave::GetEmptyBuffer( void )
|
|
{
|
|
CAudioBuffer *pOutput = NULL;
|
|
if ( ValidWaveOut() )
|
|
{
|
|
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
|
{
|
|
if ( !(m_buffers[ i ].submitted ) ||
|
|
m_buffers[i].hdr->dwFlags & WHDR_DONE )
|
|
{
|
|
pOutput = &m_buffers[i];
|
|
pOutput->submitted = true;
|
|
pOutput->m_Referenced.Purge();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pOutput;
|
|
}
|
|
|
|
void CAudioDeviceWave::SilenceBuffer( short *pSamples, int sampleCount )
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; i < sampleCount; i++ )
|
|
{
|
|
// left
|
|
*pSamples++ = 0;
|
|
// right
|
|
*pSamples++ = 0;
|
|
}
|
|
}
|
|
|
|
void CAudioDeviceWave::Flush( void )
|
|
{
|
|
waveOutReset( m_waveOutHandle );
|
|
}
|
|
|
|
// mix a buffer up to time (time is absolute)
|
|
void CAudioDeviceWave::Update( float time )
|
|
{
|
|
if ( !ValidWaveOut() )
|
|
return;
|
|
|
|
// reset the system
|
|
if ( m_mixTime < 0 || time < m_baseTime )
|
|
{
|
|
m_baseTime = time;
|
|
m_mixTime = 0;
|
|
}
|
|
|
|
// put time in our coordinate frame
|
|
time -= m_baseTime;
|
|
|
|
if ( time > m_mixTime )
|
|
{
|
|
CAudioBuffer *pBuffer = GetEmptyBuffer();
|
|
|
|
// no free buffers, mixing is ahead of the playback!
|
|
if ( !pBuffer || !pBuffer->hdr )
|
|
{
|
|
//Con_Printf( "out of buffers\n" );
|
|
return;
|
|
}
|
|
|
|
// UNDONE: These numbers are constants
|
|
// calc number of samples (2 channels * 2 bytes per sample)
|
|
int sampleCount = pBuffer->hdr->dwBufferLength >> 2;
|
|
m_mixTime += sampleCount * (1.0f / OUTPUT_SAMPLE_RATE);
|
|
|
|
short *pSamples = reinterpret_cast<short *>(pBuffer->hdr->lpData);
|
|
|
|
SilenceBuffer( pSamples, sampleCount );
|
|
|
|
int tempCount = sampleCount;
|
|
|
|
while ( tempCount > 0 )
|
|
{
|
|
if ( tempCount > PaintBufferSampleCount() )
|
|
{
|
|
sampleCount = PaintBufferSampleCount();
|
|
}
|
|
else
|
|
{
|
|
sampleCount = tempCount;
|
|
}
|
|
|
|
MixBegin();
|
|
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
|
{
|
|
CAudioMixer *pSource = m_sourceList[i];
|
|
if ( !pSource )
|
|
continue;
|
|
|
|
int currentsample = pSource->GetSamplePosition();
|
|
bool forward = pSource->GetDirection();
|
|
|
|
if ( pSource->GetActive() )
|
|
{
|
|
if ( !pSource->MixDataToDevice( this, pSource->GetChannel(), currentsample, sampleCount, DeviceSampleRate(), forward ) )
|
|
{
|
|
// Source becomes inactive when last submitted sample is finally
|
|
// submitted. But it lingers until it's no longer referenced
|
|
pSource->SetActive( false );
|
|
}
|
|
else
|
|
{
|
|
AddToReferencedList( pSource, pBuffer );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !IsSourceReferencedByActiveBuffer( pSource ) )
|
|
{
|
|
if ( !pSource->GetAutoDelete() )
|
|
{
|
|
FreeChannel( i );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSource->IncrementSamples( pSource->GetChannel(), currentsample, sampleCount, DeviceSampleRate(), forward );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
TransferBufferStereo16( pSamples, sampleCount );
|
|
|
|
m_sampleIndex += sampleCount;
|
|
tempCount -= sampleCount;
|
|
pSamples += sampleCount * 2;
|
|
}
|
|
// if the buffers aren't aligned on sample boundaries, this will hard-lock the machine!
|
|
|
|
pBuffer->submit_sample_count = GetOutputPosition();
|
|
|
|
waveOutWrite( m_waveOutHandle, pBuffer->hdr, sizeof(*(pBuffer->hdr)) );
|
|
}
|
|
}
|
|
|
|
/*
|
|
int CAudioDeviceWave::GetNumberofSamplesAhead( void )
|
|
{
|
|
ComputeSampleAheadAmount();
|
|
return m_nEstimatedSamplesAhead;
|
|
}
|
|
|
|
float CAudioDeviceWave::GetAmountofTimeAhead( void )
|
|
{
|
|
ComputeSampleAheadAmount();
|
|
return ( (float)m_nEstimatedSamplesAhead / (float)OUTPUT_SAMPLE_RATE );
|
|
}
|
|
|
|
// Find the most recent submitted sample that isn't flagged as whdr_done
|
|
void CAudioDeviceWave::ComputeSampleAheadAmount( void )
|
|
{
|
|
m_nEstimatedSamplesAhead = 0;
|
|
|
|
int newest_sample_index = -1;
|
|
int newest_sample_count = 0;
|
|
|
|
CAudioBuffer *buffer;
|
|
|
|
if ( ValidDevice() )
|
|
{
|
|
|
|
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
|
{
|
|
buffer = &m_buffers[ i ];
|
|
if ( !buffer->submitted )
|
|
continue;
|
|
|
|
if ( buffer->hdr->dwFlags & WHDR_DONE )
|
|
continue;
|
|
|
|
if ( buffer->submit_sample_count > newest_sample_count )
|
|
{
|
|
newest_sample_index = i;
|
|
newest_sample_count = buffer->submit_sample_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( newest_sample_index == -1 )
|
|
return;
|
|
|
|
|
|
buffer = &m_buffers[ newest_sample_index ];
|
|
int currentPos = GetOutputPosition() ;
|
|
m_nEstimatedSamplesAhead = currentPos - buffer->submit_sample_count;
|
|
}
|
|
*/
|
|
|
|
int CAudioDeviceWave::FindSourceIndex( CAudioMixer *pSource )
|
|
{
|
|
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
|
{
|
|
if ( pSource == m_sourceList[i] )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
CAudioMixer *CAudioDeviceWave::GetMixerForSource( CAudioSource *source )
|
|
{
|
|
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
|
{
|
|
if ( !m_sourceList[i] )
|
|
continue;
|
|
|
|
if ( source == m_sourceList[i]->GetSource() )
|
|
{
|
|
return m_sourceList[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CAudioDeviceWave::AddSource( CAudioMixer *pSource )
|
|
{
|
|
int slot = 0;
|
|
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
|
{
|
|
if ( !m_sourceList[i] )
|
|
{
|
|
slot = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( m_sourceList[slot] )
|
|
{
|
|
FreeChannel( slot );
|
|
}
|
|
SetChannel( slot, pSource );
|
|
|
|
pSource->SetActive( true );
|
|
}
|
|
|
|
|
|
void CAudioDeviceWave::StopSounds( void )
|
|
{
|
|
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
|
{
|
|
if ( m_sourceList[i] )
|
|
{
|
|
FreeChannel( i );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CAudioDeviceWave::SetChannel( int channelIndex, CAudioMixer *pSource )
|
|
{
|
|
if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS )
|
|
return;
|
|
|
|
m_sourceList[channelIndex] = pSource;
|
|
}
|
|
|
|
void CAudioDeviceWave::FreeChannel( int channelIndex )
|
|
{
|
|
if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS )
|
|
return;
|
|
|
|
if ( m_sourceList[channelIndex] )
|
|
{
|
|
RemoveMixerChannelReferences( m_sourceList[channelIndex] );
|
|
|
|
delete m_sourceList[channelIndex];
|
|
m_sourceList[channelIndex] = NULL;
|
|
}
|
|
}
|
|
|
|
int CAudioDeviceWave::GetOutputPosition( void )
|
|
{
|
|
if ( !m_waveOutHandle )
|
|
return 0;
|
|
|
|
MMTIME mmtime;
|
|
mmtime.wType = TIME_SAMPLES;
|
|
waveOutGetPosition( m_waveOutHandle, &mmtime, sizeof( MMTIME ) );
|
|
|
|
// Convert time to sample count
|
|
return ( mmtime.u.sample );
|
|
}
|
|
|