This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

View File

@ -0,0 +1,58 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include <stdio.h>
#include "soundsystem/snd_audio_source.h"
#include "soundsystem/isoundsystem.h"
#include "soundsystem.h"
extern CAudioSource *Audio_CreateMemoryWave( const char *pName );
//-----------------------------------------------------------------------------
// Purpose: Simple wrapper to crack naming convention and create the proper wave source
// Input : *pName - WAVE filename
// Output : CAudioSource
//-----------------------------------------------------------------------------
CAudioSource *AudioSource_Create( const char *pName )
{
if ( !pName )
return NULL;
// if ( pName[0] == '!' ) // sentence
;
// Names that begin with "*" are streaming.
// Skip over the * and create a streamed source
if ( pName[0] == '*' )
{
return NULL;
}
// These are loaded into memory directly
return Audio_CreateMemoryWave( pName );
}
CAudioSource::~CAudioSource( void )
{
CAudioMixer *mixer;
while ( 1 )
{
mixer = g_pSoundSystem->FindMixer( this );
if ( !mixer )
break;
g_pSoundSystem->StopSound( mixer );
}
}
CAudioSource::CAudioSource( void )
{
}

View File

@ -0,0 +1,899 @@
//========= Copyright 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"
//-----------------------------------------------------------------------------
// 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 )
{
DWarning( "soundsystem", 1, "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 )
{
DWarning( "soundsystem", 1, "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 )
{
DWarning( "soundsystem", 1, "Sound: Out of memory.\n");
CloseWaveOut();
return NULL;
}
HPSTR lpData = (char *)GlobalLock( hMemory );
if ( !lpData )
{
DWarning( "soundsystem", 1, "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 )
{
DWarning( "soundsystem", 1, "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.Size(); 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.Size(); 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.Size(); 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 );
}

View File

@ -0,0 +1,27 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef SND_DEV_WAVE_H
#define SND_DEV_WAVE_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class IAudioDevice;
//-----------------------------------------------------------------------------
// Creates a device that mixes WAVs using windows
//-----------------------------------------------------------------------------
IAudioDevice *Audio_CreateWaveDevice( void );
#endif // SND_DEV_WAVE_H

93
soundsystem/snd_io.cpp Normal file
View File

@ -0,0 +1,93 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "soundsystem.h"
#include "tier2/riff.h"
#include "filesystem.h"
#include "tier1/strtools.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose: Implements Audio IO on the engine's COMMON filesystem
//-----------------------------------------------------------------------------
class COM_IOReadBinary : public IFileReadBinary
{
public:
int open( const char *pFileName );
int read( void *pOutput, int size, int file );
void seek( int file, int pos );
unsigned int tell( int file );
unsigned int size( int file );
void close( int file );
};
// prepend sound/ to the filename -- all sounds are loaded from the sound/ directory
int COM_IOReadBinary::open( const char *pFileName )
{
char namebuffer[512];
FileHandle_t hFile;
Q_strncpy(namebuffer, "sound", sizeof( namebuffer ) );
//HACK HACK HACK the server is sending back sound names with slashes in front...
if (pFileName[0]!='/')
{
Q_strncat(namebuffer,"/", sizeof( namebuffer ), COPY_ALL_CHARACTERS );
}
Q_strncat( namebuffer, pFileName, sizeof( namebuffer ), COPY_ALL_CHARACTERS );
hFile = g_pFullFileSystem->Open( namebuffer, "rb", "GAME" );
return (int)hFile;
}
int COM_IOReadBinary::read( void *pOutput, int size, int file )
{
if ( !file )
return 0;
return g_pFullFileSystem->Read( pOutput, size, (FileHandle_t)file );
}
void COM_IOReadBinary::seek( int file, int pos )
{
if ( !file )
return;
g_pFullFileSystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
}
unsigned int COM_IOReadBinary::tell( int file )
{
if ( !file )
return 0;
return g_pFullFileSystem->Tell( (FileHandle_t)file );
}
unsigned int COM_IOReadBinary::size( int file )
{
if (!file)
return 0;
return g_pFullFileSystem->Size( (FileHandle_t)file );
}
void COM_IOReadBinary::close( int file )
{
if (!file)
return;
g_pFullFileSystem->Close( (FileHandle_t)file );
}
static COM_IOReadBinary io;
IFileReadBinary *g_pSndIO = &io;

View File

@ -0,0 +1,527 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
#include <stdio.h>
#include "snd_dev_wave.h"
#include "snd_wave_source.h"
#include "soundsystem/snd_audio_source.h"
#include "snd_wave_mixer_private.h"
#include "snd_wave_mixer_adpcm.h"
#include "tier2/riff.h"
//-----------------------------------------------------------------------------
// These mixers provide an abstraction layer between the audio device and
// mixing/decoding code. They allow data to be decoded and mixed using
// optimized, format sensitive code by calling back into the device that
// controls them.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: maps mixing to 8-bit mono mixer
//-----------------------------------------------------------------------------
class CAudioMixerWave8Mono : public CAudioMixerWave
{
public:
CAudioMixerWave8Mono( CWaveData *data ) : CAudioMixerWave( data ) {}
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
{
pDevice->Mix8Mono( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
}
};
//-----------------------------------------------------------------------------
// Purpose: maps mixing to 8-bit stereo mixer
//-----------------------------------------------------------------------------
class CAudioMixerWave8Stereo : public CAudioMixerWave
{
public:
CAudioMixerWave8Stereo( CWaveData *data ) : CAudioMixerWave( data ) {}
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
{
pDevice->Mix8Stereo( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
}
};
//-----------------------------------------------------------------------------
// Purpose: maps mixing to 16-bit mono mixer
//-----------------------------------------------------------------------------
class CAudioMixerWave16Mono : public CAudioMixerWave
{
public:
CAudioMixerWave16Mono( CWaveData *data ) : CAudioMixerWave( data ) {}
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
{
pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
}
};
//-----------------------------------------------------------------------------
// Purpose: maps mixing to 16-bit stereo mixer
//-----------------------------------------------------------------------------
class CAudioMixerWave16Stereo : public CAudioMixerWave
{
public:
CAudioMixerWave16Stereo( CWaveData *data ) : CAudioMixerWave( data ) {}
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
{
pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
}
};
//-----------------------------------------------------------------------------
// Purpose: Create an approprite mixer type given the data format
// Input : *data - data access abstraction
// format - pcm or adpcm (1 or 2 -- RIFF format)
// channels - number of audio channels (1 = mono, 2 = stereo)
// bits - bits per sample
// Output : CAudioMixer * abstract mixer type that maps mixing to appropriate code
//-----------------------------------------------------------------------------
CAudioMixer *CreateWaveMixer( CWaveData *data, int format, int channels, int bits )
{
if ( format == WAVE_FORMAT_PCM )
{
if ( channels > 1 )
{
if ( bits == 8 )
return new CAudioMixerWave8Stereo( data );
else
return new CAudioMixerWave16Stereo( data );
}
else
{
if ( bits == 8 )
return new CAudioMixerWave8Mono( data );
else
return new CAudioMixerWave16Mono( data );
}
}
else if ( format == WAVE_FORMAT_ADPCM )
{
return CreateADPCMMixer( data );
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Init the base WAVE mixer.
// Input : *data - data access object
//-----------------------------------------------------------------------------
CAudioMixerWave::CAudioMixerWave( CWaveData *data ) : m_pData(data), m_pChannel(NULL)
{
m_loop = 0;
m_sample = 0;
m_absoluteSample = 0;
m_scubSample = -1;
m_fracOffset = 0;
m_bActive = false;
m_nModelIndex = -1;
m_bForward = true;
m_bAutoDelete = true;
m_pChannel = new channel_t;
m_pChannel->leftvol = 127;
m_pChannel->rightvol = 127;
m_pChannel->pitch = 1.0;
}
//-----------------------------------------------------------------------------
// Purpose: Frees the data access object (we own it after construction)
//-----------------------------------------------------------------------------
CAudioMixerWave::~CAudioMixerWave( void )
{
delete m_pData;
delete m_pChannel;
}
//-----------------------------------------------------------------------------
// Purpose: Decode and read the data
// by default we just pass the request on to the data access object
// other mixers may need to buffer or decode the data for some reason
//
// Input : **pData - dest pointer
// sampleCount - number of samples needed
// Output : number of samples available in this batch
//-----------------------------------------------------------------------------
int CAudioMixerWave::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ )
{
if ( samplePosition != m_sample )
{
// Seek
m_sample = samplePosition;
m_absoluteSample = samplePosition;
}
return m_pData->ReadSourceData( pData, m_sample, sampleCount, forward );
}
//-----------------------------------------------------------------------------
// Purpose: calls through the wavedata to get the audio source
// Output : CAudioSource
//-----------------------------------------------------------------------------
CAudioSource *CAudioMixerWave::GetSource( void )
{
if ( m_pData )
return &m_pData->Source();
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Gets the current sample location in playback
// Output : int (samples from start of wave)
//-----------------------------------------------------------------------------
int CAudioMixerWave::GetSamplePosition( void )
{
return m_sample;
}
//-----------------------------------------------------------------------------
// Purpose: Gets the current sample location in playback
// Output : int (samples from start of wave)
//-----------------------------------------------------------------------------
int CAudioMixerWave::GetScubPosition( void )
{
if (m_scubSample != -1)
{
return m_scubSample;
}
return m_sample;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : position -
//-----------------------------------------------------------------------------
bool CAudioMixerWave::SetSamplePosition( int position, bool scrubbing )
{
position = max( 0, position );
m_sample = position;
m_absoluteSample = position;
m_startpos = m_sample;
if (scrubbing)
{
m_scubSample = position;
}
else
{
m_scubSample = -1;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : position -
//-----------------------------------------------------------------------------
void CAudioMixerWave::SetLoopPosition( int position )
{
m_loop = position;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int CAudioMixerWave::GetStartPosition( void )
{
return m_startpos;
}
bool CAudioMixerWave::GetActive( void )
{
return m_bActive;
}
void CAudioMixerWave::SetActive( bool active )
{
m_bActive = active;
}
void CAudioMixerWave::SetModelIndex( int index )
{
m_nModelIndex = index;
}
int CAudioMixerWave::GetModelIndex( void ) const
{
return m_nModelIndex;
}
void CAudioMixerWave::SetDirection( bool forward )
{
m_bForward = forward;
}
bool CAudioMixerWave::GetDirection( void ) const
{
return m_bForward;
}
void CAudioMixerWave::SetAutoDelete( bool autodelete )
{
m_bAutoDelete = autodelete;
}
bool CAudioMixerWave::GetAutoDelete( void ) const
{
return m_bAutoDelete;
}
void CAudioMixerWave::SetVolume( float volume )
{
int ivolume = (int)( clamp( volume, 0.0f, 1.0f ) * 127.0f );
m_pChannel->leftvol = ivolume;
m_pChannel->rightvol = ivolume;
}
channel_t *CAudioMixerWave::GetChannel()
{
Assert( m_pChannel );
return m_pChannel;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pChannel -
// sampleCount -
// outputRate -
//-----------------------------------------------------------------------------
void CAudioMixerWave::IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward /*= true*/ )
{
int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate());
float rate = (float)inputSampleRate / outputRate;
int startpos = startSample;
if ( !forward )
{
int requestedstart = startSample - (int)( sampleCount * rate );
if ( requestedstart < 0 )
return;
startpos = max( 0, requestedstart );
SetSamplePosition( startpos );
}
while ( sampleCount > 0 )
{
int inputSampleCount;
int outputSampleCount = sampleCount;
if ( outputRate != inputSampleRate )
{
inputSampleCount = (int)(sampleCount * rate);
}
else
{
inputSampleCount = sampleCount;
}
sampleCount -= outputSampleCount;
if ( forward )
{
m_sample += inputSampleCount;
m_absoluteSample += inputSampleCount;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: The device calls this to request data. The mixer must provide the
// full amount of samples or have silence in its output stream.
// Input : *pDevice - requesting device
// sampleCount - number of samples at the output rate
// outputRate - sampling rate of the request
// Output : Returns true to keep mixing, false to delete this mixer
//-----------------------------------------------------------------------------
bool CAudioMixerWave::SkipSamples( IAudioDevice *pDevice, channel_t *pChannel,
int startSample, int sampleCount, int outputRate, bool forward /*= true*/ )
{
int offset = 0;
int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate());
float rate = (float)inputSampleRate / outputRate;
sampleCount = min( sampleCount, pDevice->PaintBufferSampleCount() );
int startpos = startSample;
if ( !forward )
{
int requestedstart = startSample - (int)( sampleCount * rate );
if ( requestedstart < 0 )
return false;
startpos = max( 0, requestedstart );
SetSamplePosition( startpos );
}
while ( sampleCount > 0 )
{
int availableSamples;
int inputSampleCount;
char *pData = NULL;
int outputSampleCount = sampleCount;
if ( outputRate != inputSampleRate )
{
inputSampleCount = (int)(sampleCount * rate);
if ( !forward )
{
startSample = max( 0, startSample - inputSampleCount );
}
int availableSamples = GetOutputData( (void **)&pData, startSample, inputSampleCount, forward );
if ( !availableSamples )
break;
if ( availableSamples < inputSampleCount )
{
outputSampleCount = (int)(availableSamples / rate);
inputSampleCount = availableSamples;
}
// compute new fraction part of sample index
float offset = (m_fracOffset / FIX_SCALE) + (rate * outputSampleCount);
offset = offset - (float)((int)offset);
m_fracOffset = FIX_FLOAT(offset);
}
else
{
if ( !forward )
{
startSample = max( 0, startSample - sampleCount );
}
availableSamples = GetOutputData( (void **)&pData, startSample, sampleCount, forward );
if ( !availableSamples )
break;
outputSampleCount = availableSamples;
inputSampleCount = availableSamples;
}
offset += outputSampleCount;
sampleCount -= outputSampleCount;
if ( forward )
{
m_sample += inputSampleCount;
m_absoluteSample += inputSampleCount;
}
if ( m_loop != 0 && m_sample >= m_loop )
{
SetSamplePosition( m_startpos );
}
}
if ( sampleCount > 0 )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: The device calls this to request data. The mixer must provide the
// full amount of samples or have silence in its output stream.
// Input : *pDevice - requesting device
// sampleCount - number of samples at the output rate
// outputRate - sampling rate of the request
// Output : Returns true to keep mixing, false to delete this mixer
//-----------------------------------------------------------------------------
bool CAudioMixerWave::MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward /*= true*/ )
{
int offset = 0;
int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate());
float rate = (float)inputSampleRate / outputRate;
fixedint fracstep = FIX_FLOAT( rate );
sampleCount = min( sampleCount, pDevice->PaintBufferSampleCount() );
int startpos = startSample;
if ( !forward )
{
int requestedstart = startSample - (int)( sampleCount * rate );
if ( requestedstart < 0 )
return false;
startpos = max( 0, requestedstart );
SetSamplePosition( startpos );
}
while ( sampleCount > 0 )
{
int availableSamples;
int inputSampleCount;
char *pData = NULL;
int outputSampleCount = sampleCount;
if ( outputRate != inputSampleRate )
{
inputSampleCount = (int)(sampleCount * rate);
int availableSamples = GetOutputData( (void **)&pData, startpos, inputSampleCount, forward );
if ( !availableSamples )
break;
if ( availableSamples < inputSampleCount )
{
outputSampleCount = (int)(availableSamples / rate);
inputSampleCount = availableSamples;
}
Mix( pDevice, pChannel, pData, offset, m_fracOffset, fracstep, outputSampleCount, 0, forward );
// compute new fraction part of sample index
float offset = (m_fracOffset / FIX_SCALE) + (rate * outputSampleCount);
offset = offset - (float)((int)offset);
m_fracOffset = FIX_FLOAT(offset);
}
else
{
availableSamples = GetOutputData( (void **)&pData, startpos, sampleCount, forward );
if ( !availableSamples )
break;
outputSampleCount = availableSamples;
inputSampleCount = availableSamples;
Mix( pDevice, pChannel, pData, offset, m_fracOffset, FIX(1), outputSampleCount, 0, forward );
}
offset += outputSampleCount;
sampleCount -= outputSampleCount;
if ( forward )
{
m_sample += inputSampleCount;
m_absoluteSample += inputSampleCount;
}
if ( m_loop != 0 && m_sample >= m_loop )
{
SetSamplePosition( m_startpos );
}
}
if ( sampleCount > 0 )
return false;
return true;
}

View File

@ -0,0 +1,31 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
#ifndef SND_WAVE_MIXER_H
#define SND_WAVE_MIXER_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CWaveData;
class CAudioMixer;
//-----------------------------------------------------------------------------
// Wave mixer
//-----------------------------------------------------------------------------
CAudioMixer *CreateWaveMixer( CWaveData *data, int format, int channels, int bits );
#endif // SND_WAVE_MIXER_H

View File

@ -0,0 +1,516 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#pragma warning( disable: 4201 )
#include <mmsystem.h>
#pragma warning( default: 4201 )
#include <mmreg.h>
#include "snd_wave_source.h"
#include "snd_wave_mixer_adpcm.h"
#include "snd_wave_mixer_private.h"
#include "soundsystem.h"
// max size of ADPCM block in bytes
#define MAX_BLOCK_SIZE 4096
//-----------------------------------------------------------------------------
// Purpose: Mixer for ADPCM encoded audio
//-----------------------------------------------------------------------------
class CAudioMixerWaveADPCM : public CAudioMixerWave
{
public:
CAudioMixerWaveADPCM( CWaveData *data );
~CAudioMixerWaveADPCM( void );
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true );
virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true );
virtual bool SetSamplePosition( int position, bool scrubbing = false );
private:
bool DecodeBlock( void );
int NumChannels( void );
void DecompressBlockMono( short *pOut, const char *pIn, int count );
void DecompressBlockStereo( short *pOut, const char *pIn, int count );
void SetCurrentBlock( int block );
int GetCurrentBlock( void ) const;
int GetBlockNumberForSample( int samplePosition );
bool IsSampleInCurrentBlock( int samplePosition );
int GetFirstSampleForBlock( int blocknum ) const;
const ADPCMWAVEFORMAT *m_pFormat;
const ADPCMCOEFSET *m_pCoefficients;
short *m_pSamples;
int m_sampleCount;
int m_samplePosition;
int m_blockSize;
int m_offset;
int m_currentBlock;
};
CAudioMixerWaveADPCM::CAudioMixerWaveADPCM( CWaveData *data ) : CAudioMixerWave( data )
{
m_currentBlock = -1;
m_pSamples = NULL;
m_sampleCount = 0;
m_samplePosition = 0;
m_offset = 0;
m_pFormat = (const ADPCMWAVEFORMAT *)m_pData->Source().GetHeader();
if ( m_pFormat )
{
m_pCoefficients = (ADPCMCOEFSET *)((char *)m_pFormat + sizeof(WAVEFORMATEX) + 4);
// create the decode buffer
m_pSamples = new short[m_pFormat->wSamplesPerBlock * m_pFormat->wfx.nChannels];
// number of bytes for samples
m_blockSize = ((m_pFormat->wSamplesPerBlock - 2) * m_pFormat->wfx.nChannels ) / 2;
// size of channel header
m_blockSize += 7 * m_pFormat->wfx.nChannels;
// Assert(m_blockSize < MAX_BLOCK_SIZE);
}
}
CAudioMixerWaveADPCM::~CAudioMixerWaveADPCM( void )
{
delete[] m_pSamples;
}
int CAudioMixerWaveADPCM::NumChannels( void )
{
if ( m_pFormat )
{
return m_pFormat->wfx.nChannels;
}
return 0;
}
void CAudioMixerWaveADPCM::Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward /*= true*/ )
{
if ( NumChannels() == 1 )
pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
else
pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
}
static int error_sign_lut[] = { 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 };
static int error_coefficients_lut[] = { 230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230 };
//-----------------------------------------------------------------------------
// Purpose: ADPCM decompress a single block of 1-channel audio
// Input : *pOut - output buffer 16-bit
// *pIn - input block
// count - number of samples to decode (to support partial blocks)
//-----------------------------------------------------------------------------
void CAudioMixerWaveADPCM::DecompressBlockMono( short *pOut, const char *pIn, int count )
{
int pred = *pIn++;
int co1 = m_pCoefficients[pred].iCoef1;
int co2 = m_pCoefficients[pred].iCoef2;
// read initial delta
int delta = *((short *)pIn);
pIn += 2;
// read initial samples for prediction
int samp1 = *((short *)pIn);
pIn += 2;
int samp2 = *((short *)pIn);
pIn += 2;
// write out the initial samples (stored in reverse order)
*pOut++ = (short)samp2;
*pOut++ = (short)samp1;
// subtract the 2 samples in the header
count -= 2;
// this is a toggle to read nibbles, first nibble is high
int high = 1;
int error = 0, sample = 0;
// now process the block
while ( count )
{
// read the error nibble from the input stream
if ( high )
{
sample = (unsigned char) (*pIn++);
// high nibble
error = sample >> 4;
// cache low nibble for next read
sample = sample & 0xf;
// Next read is from cache, not stream
high = 0;
}
else
{
// stored in previous read (low nibble)
error = sample;
// next read is from stream
high = 1;
}
// convert to signed with LUT
int errorSign = error_sign_lut[error];
// interpolate the new sample
int predSample = (samp1 * co1) + (samp2 * co2);
// coefficients are fixed point 8-bit, so shift back to 16-bit integer
predSample >>= 8;
// Add in current error estimate
predSample += (errorSign * delta);
// Correct error estimate
delta = (delta * error_coefficients_lut[error]) >> 8;
// Clamp error estimate
if ( delta < 16 )
delta = 16;
// clamp
if ( predSample > 32767L )
predSample = 32767L;
else if ( predSample < -32768L )
predSample = -32768L;
// output
*pOut++ = (short)predSample;
// move samples over
samp2 = samp1;
samp1 = predSample;
count--;
}
}
//-----------------------------------------------------------------------------
// Purpose: Decode a single block of stereo ADPCM audio
// Input : *pOut - 16-bit output buffer
// *pIn - ADPCM encoded block data
// count - number of sample pairs to decode
//-----------------------------------------------------------------------------
void CAudioMixerWaveADPCM::DecompressBlockStereo( short *pOut, const char *pIn, int count )
{
int pred[2], co1[2], co2[2];
int i;
for ( i = 0; i < 2; i++ )
{
pred[i] = *pIn++;
co1[i] = m_pCoefficients[pred[i]].iCoef1;
co2[i] = m_pCoefficients[pred[i]].iCoef2;
}
int delta[2], samp1[2], samp2[2];
for ( i = 0; i < 2; i++, pIn += 2 )
{
// read initial delta
delta[i] = *((short *)pIn);
}
// read initial samples for prediction
for ( i = 0; i < 2; i++, pIn += 2 )
{
samp1[i] = *((short *)pIn);
}
for ( i = 0; i < 2; i++, pIn += 2 )
{
samp2[i] = *((short *)pIn);
}
// write out the initial samples (stored in reverse order)
*pOut++ = (short)samp2[0]; // left
*pOut++ = (short)samp2[1]; // right
*pOut++ = (short)samp1[0]; // left
*pOut++ = (short)samp1[1]; // right
// subtract the 2 samples in the header
count -= 2;
// this is a toggle to read nibbles, first nibble is high
int high = 1;
int error, sample = 0;
// now process the block
while ( count )
{
for ( i = 0; i < 2; i++ )
{
// read the error nibble from the input stream
if ( high )
{
sample = (unsigned char) (*pIn++);
// high nibble
error = sample >> 4;
// cache low nibble for next read
sample = sample & 0xf;
// Next read is from cache, not stream
high = 0;
}
else
{
// stored in previous read (low nibble)
error = sample;
// next read is from stream
high = 1;
}
// convert to signed with LUT
int errorSign = error_sign_lut[error];
// interpolate the new sample
int predSample = (samp1[i] * co1[i]) + (samp2[i] * co2[i]);
// coefficients are fixed point 8-bit, so shift back to 16-bit integer
predSample >>= 8;
// Add in current error estimate
predSample += (errorSign * delta[i]);
// Correct error estimate
delta[i] = (delta[i] * error_coefficients_lut[error]) >> 8;
// Clamp error estimate
if ( delta[i] < 16 )
delta[i] = 16;
// clamp
if ( predSample > 32767L )
predSample = 32767L;
else if ( predSample < -32768L )
predSample = -32768L;
// output
*pOut++ = (short)predSample;
// move samples over
samp2[i] = samp1[i];
samp1[i] = predSample;
}
count--;
}
}
bool CAudioMixerWaveADPCM::DecodeBlock( void )
{
char tmpBlock[MAX_BLOCK_SIZE];
char *pData;
int available = m_pData->ReadSourceData( (void **) (&pData), m_offset, m_blockSize );
if ( available < m_blockSize )
{
int total = 0;
while ( available && total < m_blockSize )
{
memcpy( tmpBlock + total, pData, available );
total += available;
available = m_pData->ReadSourceData( (void **) (&pData), m_offset + total, m_blockSize - total );
}
pData = tmpBlock;
available = total;
}
Assert( m_blockSize > 0 );
// Current block number is based on starting offset
int blockNumber = m_offset / m_blockSize;
SetCurrentBlock( blockNumber );
if ( !available )
{
return false;
}
// advance the file pointer
m_offset += available;
int channelCount = NumChannels();
// this is sample pairs for stereo, samples for mono
m_sampleCount = m_pFormat->wSamplesPerBlock;
// short block?, fixup sample count (2 samples per byte, divided by number of channels per sample set)
m_sampleCount -= ((m_blockSize - available) * 2) / channelCount;
// new block, start at the first sample
m_samplePosition = 0;
// no need to subclass for different channel counts...
if ( channelCount == 1 )
{
DecompressBlockMono( m_pSamples, pData, m_sampleCount );
}
else
{
DecompressBlockStereo( m_pSamples, pData, m_sampleCount );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : block -
//-----------------------------------------------------------------------------
void CAudioMixerWaveADPCM::SetCurrentBlock( int block )
{
m_currentBlock = block;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int CAudioMixerWaveADPCM::GetCurrentBlock( void ) const
{
return m_currentBlock;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : samplePosition -
// Output : int
//-----------------------------------------------------------------------------
int CAudioMixerWaveADPCM::GetBlockNumberForSample( int samplePosition )
{
int blockNum = samplePosition / m_pFormat->wSamplesPerBlock;
return blockNum;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : samplePosition -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAudioMixerWaveADPCM::IsSampleInCurrentBlock( int samplePosition )
{
int currentBlock = GetCurrentBlock();
int startSample = currentBlock * m_pFormat->wSamplesPerBlock;
int endSample = startSample + m_pFormat->wSamplesPerBlock - 1;
if ( samplePosition >= startSample &&
samplePosition <= endSample )
{
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : blocknum -
// Output : int
//-----------------------------------------------------------------------------
int CAudioMixerWaveADPCM::GetFirstSampleForBlock( int blocknum ) const
{
return m_pFormat->wSamplesPerBlock * blocknum;
}
//-----------------------------------------------------------------------------
// Purpose: Read existing buffer or decompress a new block when necessary
// Input : **pData - output data pointer
// sampleCount - number of samples (or pairs)
// Output : int - available samples (zero to stop decoding)
//-----------------------------------------------------------------------------
int CAudioMixerWaveADPCM::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ )
{
int requestedBlock = GetBlockNumberForSample( samplePosition );
if ( requestedBlock != GetCurrentBlock() )
{
// Ran out of data!!!
if ( !SetSamplePosition( samplePosition ) )
return 0;
}
Assert( requestedBlock == GetCurrentBlock() );
if ( m_samplePosition >= m_sampleCount )
{
if ( !DecodeBlock() )
return 0;
}
if ( m_samplePosition < m_sampleCount )
{
*pData = (void *)(m_pSamples + m_samplePosition * NumChannels());
int available = m_sampleCount - m_samplePosition;
if ( available > sampleCount )
available = sampleCount;
m_samplePosition += available;
return available;
}
return 0;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : position -
//-----------------------------------------------------------------------------
bool CAudioMixerWaveADPCM::SetSamplePosition( int position, bool scrubbing )
{
position = max( 0, position );
CAudioMixerWave::SetSamplePosition( position, scrubbing );
int requestedBlock = GetBlockNumberForSample( position );
int firstSample = GetFirstSampleForBlock( requestedBlock );
if ( firstSample >= m_pData->Source().SampleCount() )
{
// Read past end of file!!!
return false;
}
int currentSample = ( position - firstSample );
if ( requestedBlock != GetCurrentBlock() )
{
// Rewind file to beginning of block
m_offset = requestedBlock * m_blockSize;
if ( !DecodeBlock() )
{
return false;
}
}
m_samplePosition = currentSample;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Abstract factory function for ADPCM mixers
// Input : *data - wave data access object
// channels -
// Output : CAudioMixer
//-----------------------------------------------------------------------------
CAudioMixer *CreateADPCMMixer( CWaveData *data )
{
return new CAudioMixerWaveADPCM( data );
}

View File

@ -0,0 +1,24 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#ifndef SND_WAVE_MIXER_ADPCM_H
#define SND_WAVE_MIXER_ADPCM_H
#pragma once
class CAudioMixer;
class CWaveData;
CAudioMixer *CreateADPCMMixer( CWaveData *data );
#endif // SND_WAVE_MIXER_ADPCM_H

View File

@ -0,0 +1,103 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
#ifndef SND_WAVE_MIXER_PRIVATE_H
#define SND_WAVE_MIXER_PRIVATE_H
#ifdef _WIN32
#pragma once
#endif
#include "soundsystem/snd_audio_source.h"
#include "soundsystem/snd_device.h"
#include "snd_wave_mixer.h"
//-----------------------------------------------------------------------------
// Purpose: Linear iterator over source data.
// Keeps track of position in source, and maintains necessary buffers
//-----------------------------------------------------------------------------
class CWaveData
{
public:
virtual ~CWaveData( void ) {}
virtual CAudioSourceWave &Source( void ) = 0;
virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, bool forward = true ) = 0;
};
class CAudioMixerWave : public CAudioMixer
{
public:
CAudioMixerWave( CWaveData *data );
virtual ~CAudioMixerWave( void );
virtual bool MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward = true );
virtual void IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true );
virtual bool SkipSamples( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true );
virtual void Mix( IAudioDevice *pDevice,
channel_t *pChannel,
void *pData,
int outputOffset,
int inputOffset,
fixedint fracRate,
int outCount,
int timecompress,
bool forward = true ) = 0;
virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true );
virtual CAudioSource *GetSource( void );
virtual int GetSamplePosition( void );
virtual int GetScubPosition( void );
virtual bool SetSamplePosition( int position, bool scrubbing = false );
virtual void SetLoopPosition( int position );
virtual int GetStartPosition( void );
virtual bool GetActive( void );
virtual void SetActive( bool active );
virtual void SetModelIndex( int index );
virtual int GetModelIndex( void ) const;
virtual void SetDirection( bool forward );
virtual bool GetDirection( void ) const;
virtual void SetAutoDelete( bool autodelete );
virtual bool GetAutoDelete( void ) const;
virtual void SetVolume( float volume );
virtual channel_t *GetChannel();
protected:
int m_sample;
int m_absoluteSample;
int m_scubSample;
int m_startpos;
int m_loop;
int m_fracOffset;
CWaveData *m_pData;
int m_absoluteStartPos;
bool m_bActive;
// Associated playback model in faceposer
int m_nModelIndex;
bool m_bForward;
bool m_bAutoDelete;
channel_t *m_pChannel;
};
#endif // SND_WAVE_MIXER_PRIVATE_H

View File

@ -0,0 +1,602 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include "tier2/riff.h"
#include "snd_wave_source.h"
#include "snd_wave_mixer_private.h"
#include "soundsystem/snd_audio_source.h"
#include <mmsystem.h> // wave format
#include <mmreg.h> // adpcm format
#include "soundsystem.h"
#include "filesystem.h"
#include "tier1/utlbuffer.h"
//-----------------------------------------------------------------------------
// Purpose: Implements the RIFF i/o interface on stdio
//-----------------------------------------------------------------------------
class StdIOReadBinary : public IFileReadBinary
{
public:
int open( const char *pFileName )
{
return (int)g_pFullFileSystem->Open( pFileName, "rb", "GAME" );
}
int read( void *pOutput, int size, int file )
{
if ( !file )
return 0;
return g_pFullFileSystem->Read( pOutput, size, (FileHandle_t)file );
}
void seek( int file, int pos )
{
if ( !file )
return;
g_pFullFileSystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
}
unsigned int tell( int file )
{
if ( !file )
return 0;
return g_pFullFileSystem->Tell( (FileHandle_t)file );
}
unsigned int size( int file )
{
if ( !file )
return 0;
return g_pFullFileSystem->Size( (FileHandle_t)file );
}
void close( int file )
{
if ( !file )
return;
g_pFullFileSystem->Close( (FileHandle_t)file );
}
};
static StdIOReadBinary io;
#define RIFF_WAVE MAKEID('W','A','V','E')
#define WAVE_FMT MAKEID('f','m','t',' ')
#define WAVE_DATA MAKEID('d','a','t','a')
#define WAVE_FACT MAKEID('f','a','c','t')
#define WAVE_CUE MAKEID('c','u','e',' ')
void ChunkError( unsigned int id )
{
}
//-----------------------------------------------------------------------------
// Purpose: Init to empty wave
//-----------------------------------------------------------------------------
CAudioSourceWave::CAudioSourceWave( void )
{
m_bits = 0;
m_rate = 0;
m_channels = 0;
m_format = 0;
m_pHeader = NULL;
// no looping
m_loopStart = -1;
m_sampleSize = 1;
m_sampleCount = 0;
}
CAudioSourceWave::~CAudioSourceWave( void )
{
// for non-standard waves, we store a copy of the header in RAM
delete[] m_pHeader;
// m_pWords points into m_pWordBuffer, no need to delete
}
//-----------------------------------------------------------------------------
// Purpose: Init the wave data.
// Input : *pHeaderBuffer - the RIFF fmt chunk
// headerSize - size of that chunk
//-----------------------------------------------------------------------------
void CAudioSourceWave::Init( const char *pHeaderBuffer, int headerSize )
{
const WAVEFORMATEX *pHeader = (const WAVEFORMATEX *)pHeaderBuffer;
// copy the relevant header data
m_format = pHeader->wFormatTag;
m_bits = pHeader->wBitsPerSample;
m_rate = pHeader->nSamplesPerSec;
m_channels = pHeader->nChannels;
m_sampleSize = (m_bits * m_channels) / 8;
// this can never be zero -- other functions divide by this.
// This should never happen, but avoid crashing
if ( m_sampleSize <= 0 )
m_sampleSize = 1;
// For non-standard waves (like ADPCM) store the header, it has some useful data
if ( m_format != WAVE_FORMAT_PCM )
{
m_pHeader = new char[headerSize];
memcpy( m_pHeader, pHeader, headerSize );
if ( m_format == WAVE_FORMAT_ADPCM )
{
// treat ADPCM sources as a file of bytes. They are decoded by the mixer
m_sampleSize = 1;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : float
//-----------------------------------------------------------------------------
float CAudioSourceWave::TrueSampleSize( void )
{
if ( m_format == WAVE_FORMAT_ADPCM )
{
return 0.5f;
}
return (float)m_sampleSize;
}
//-----------------------------------------------------------------------------
// Purpose: Total number of samples in this source
// Output : int
//-----------------------------------------------------------------------------
int CAudioSourceWave::SampleCount( void )
{
if ( m_format == WAVE_FORMAT_ADPCM )
{
ADPCMWAVEFORMAT *pFormat = (ADPCMWAVEFORMAT *)m_pHeader;
int blockSize = ((pFormat->wSamplesPerBlock - 2) * pFormat->wfx.nChannels ) / 2;
blockSize += 7 * pFormat->wfx.nChannels;
int blockCount = m_sampleCount / blockSize;
int blockRem = m_sampleCount % blockSize;
// total samples in complete blocks
int sampleCount = blockCount * pFormat->wSamplesPerBlock;
// add remaining in a short block
if ( blockRem )
{
sampleCount += pFormat->wSamplesPerBlock - (((blockSize - blockRem) * 2) / m_channels);
}
return sampleCount;
}
return m_sampleCount;
}
//-----------------------------------------------------------------------------
// Purpose: Do any sample conversion
// For 8 bit PCM, convert to signed because the mixing routine assumes this
// Input : *pData - pointer to sample data
// sampleCount - number of samples
//-----------------------------------------------------------------------------
void CAudioSourceWave::ConvertSamples( char *pData, int sampleCount )
{
if ( m_format == WAVE_FORMAT_PCM )
{
if ( m_bits == 8 )
{
for ( int i = 0; i < sampleCount; i++ )
{
for ( int j = 0; j < m_channels; j++ )
{
*pData = (unsigned char)((int)((unsigned)*pData) - 128);
pData++;
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &walk -
//-----------------------------------------------------------------------------
void CAudioSourceWave::ParseSentence( IterateRIFF &walk )
{
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
buf.EnsureCapacity( walk.ChunkSize() );
walk.ChunkRead( buf.Base() );
buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() );
m_Sentence.InitFromDataChunk( buf.Base(), buf.TellPut() );
}
//-----------------------------------------------------------------------------
// Purpose: Parse base chunks
// Input : &walk - riff file to parse
// : chunkName - name of the chunk to parse
//-----------------------------------------------------------------------------
// UNDONE: Move parsing loop here and drop each chunk into a virtual function
// instead of this being virtual.
void CAudioSourceWave::ParseChunk( IterateRIFF &walk, int chunkName )
{
switch( chunkName )
{
case WAVE_CUE:
{
m_loopStart = ParseCueChunk( walk );
}
break;
case WAVE_VALVEDATA:
{
ParseSentence( walk );
}
break;
// unknown/don't care
default:
{
ChunkError( walk.ChunkName() );
}
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : CSentence
//-----------------------------------------------------------------------------
CSentence *CAudioSourceWave::GetSentence( void )
{
return &m_Sentence;
}
//-----------------------------------------------------------------------------
// Purpose: Bastardized construction routine. This is just to avoid complex
// constructor functions so code can be shared more easily by sub-classes
// Input : *pFormatBuffer - RIFF header
// formatSize - header size
// &walk - RIFF file
//-----------------------------------------------------------------------------
void CAudioSourceWave::Setup( const char *pFormatBuffer, int formatSize, IterateRIFF &walk )
{
Init( pFormatBuffer, formatSize );
while ( walk.ChunkAvailable() )
{
ParseChunk( walk, walk.ChunkName() );
walk.ChunkNext();
}
}
//-----------------------------------------------------------------------------
// Purpose: Wave file that is completely in memory
// UNDONE: Implement Lock/Unlock and caching
//-----------------------------------------------------------------------------
class CAudioSourceMemWave : public CAudioSourceWave
{
public:
CAudioSourceMemWave( void );
~CAudioSourceMemWave( void );
// Create an instance (mixer) of this audio source
virtual CAudioMixer *CreateMixer( void );
virtual void ParseChunk( IterateRIFF &walk, int chunkName );
void ParseDataChunk( IterateRIFF &walk );
virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true );
virtual float GetRunningLength( void ) { return CAudioSourceWave::GetRunningLength(); };
virtual int GetNumChannels();
private:
char *m_pData; // wave data
};
//-----------------------------------------------------------------------------
// Purpose: Iterator for wave data (this is to abstract streaming/buffering)
//-----------------------------------------------------------------------------
class CWaveDataMemory : public CWaveData
{
public:
CWaveDataMemory( CAudioSourceWave &source ) : m_source(source) {}
~CWaveDataMemory( void ) {}
CAudioSourceWave &Source( void ) { return m_source; }
// this file is in memory, simply pass along the data request to the source
virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, bool forward /*= true*/ )
{
return m_source.GetOutputData( pData, sampleIndex, sampleCount, forward );
}
private:
CAudioSourceWave &m_source; // pointer to source
};
//-----------------------------------------------------------------------------
// Purpose: NULL the wave data pointer (we haven't loaded yet)
//-----------------------------------------------------------------------------
CAudioSourceMemWave::CAudioSourceMemWave( void )
{
m_pData = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Free any wave data we've allocated
//-----------------------------------------------------------------------------
CAudioSourceMemWave::~CAudioSourceMemWave( void )
{
delete[] m_pData;
}
//-----------------------------------------------------------------------------
// Purpose: Creates a mixer and initializes it with an appropriate mixer
//-----------------------------------------------------------------------------
CAudioMixer *CAudioSourceMemWave::CreateMixer( void )
{
return CreateWaveMixer( new CWaveDataMemory(*this), m_format, m_channels, m_bits );
}
//-----------------------------------------------------------------------------
// Purpose: parse chunks with unique processing to in-memory waves
// Input : &walk - RIFF file
//-----------------------------------------------------------------------------
void CAudioSourceMemWave::ParseChunk( IterateRIFF &walk, int chunkName )
{
switch( chunkName )
{
// this is the audio data
case WAVE_DATA:
{
ParseDataChunk( walk );
}
return;
}
CAudioSourceWave::ParseChunk( walk, chunkName );
}
//-----------------------------------------------------------------------------
// Purpose: reads the actual sample data and parses it
// Input : &walk - RIFF file
//-----------------------------------------------------------------------------
void CAudioSourceMemWave::ParseDataChunk( IterateRIFF &walk )
{
int size = walk.ChunkSize();
// create a buffer for the samples
m_pData = new char[size];
// load them into memory
walk.ChunkRead( m_pData );
if ( m_format == WAVE_FORMAT_PCM )
{
// number of samples loaded
m_sampleCount = size / m_sampleSize;
// some samples need to be converted
ConvertSamples( m_pData, m_sampleCount );
}
else if ( m_format == WAVE_FORMAT_ADPCM )
{
// The ADPCM mixers treat the wave source as a flat file of bytes.
m_sampleSize = 1;
// Since each "sample" is a byte (this is a flat file), the number of samples is the file size
m_sampleCount = size;
// file says 4, output is 16
m_bits = 16;
}
}
int CAudioSourceMemWave::GetNumChannels()
{
return m_channels;
}
//-----------------------------------------------------------------------------
// Purpose: parses loop information from a cue chunk
// Input : &walk - RIFF iterator
// Output : int loop start position
//-----------------------------------------------------------------------------
int CAudioSourceWave::ParseCueChunk( IterateRIFF &walk )
{
// Cue chunk as specified by RIFF format
// see $/research/jay/sound/riffnew.htm
struct
{
unsigned int dwName;
unsigned int dwPosition;
unsigned int fccChunk;
unsigned int dwChunkStart;
unsigned int dwBlockStart;
unsigned int dwSampleOffset;
} cue_chunk;
int cueCount;
// assume that the cue chunk stored in the wave is the start of the loop
// assume only one cue chunk, UNDONE: Test this assumption here?
cueCount = walk.ChunkReadInt();
walk.ChunkReadPartial( &cue_chunk, sizeof(cue_chunk) );
return cue_chunk.dwSampleOffset;
}
//-----------------------------------------------------------------------------
// Purpose: get the wave header
//-----------------------------------------------------------------------------
void *CAudioSourceWave::GetHeader( void )
{
return m_pHeader;
}
//-----------------------------------------------------------------------------
// Purpose: wrap the position wrt looping
// Input : samplePosition - absolute position
// Output : int - looped position
//-----------------------------------------------------------------------------
int CAudioSourceWave::ConvertLoopedPosition( int samplePosition )
{
// if the wave is looping and we're past the end of the sample
// convert to a position within the loop
// At the end of the loop, we return a short buffer, and subsequent call
// will loop back and get the rest of the buffer
if ( m_loopStart >= 0 )
{
if ( samplePosition >= m_sampleCount )
{
// size of loop
int loopSize = m_sampleCount - m_loopStart;
// subtract off starting bit of the wave
samplePosition -= m_loopStart;
if ( loopSize )
{
// "real" position in memory (mod off extra loops)
samplePosition = m_loopStart + (samplePosition % loopSize);
}
// ERROR? if no loopSize
}
}
return samplePosition;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : **pData - output pointer to samples
// samplePosition - position (in samples not bytes)
// sampleCount - number of samples (not bytes)
// Output : int - number of samples available
//-----------------------------------------------------------------------------
int CAudioSourceMemWave::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ )
{
// handle position looping
samplePosition = ConvertLoopedPosition( samplePosition );
// how many samples are available (linearly not counting looping)
int availableSampleCount = m_sampleCount - samplePosition;
if ( !forward )
{
if ( samplePosition >= m_sampleCount )
{
availableSampleCount = 0;
}
else
{
availableSampleCount = samplePosition;
}
}
// may be asking for a sample out of range, clip at zero
if ( availableSampleCount < 0 )
availableSampleCount = 0;
// clip max output samples to max available
if ( sampleCount > availableSampleCount )
sampleCount = availableSampleCount;
// byte offset in sample database
samplePosition *= m_sampleSize;
// if we are returning some samples, store the pointer
if ( sampleCount )
{
*pData = m_pData + samplePosition;
}
return sampleCount;
}
//-----------------------------------------------------------------------------
// Purpose: Create a wave audio source (streaming or in memory)
// Input : *pName - file name
// streaming - if true, don't load, stream each instance
// Output : CAudioSource * - a new source
//-----------------------------------------------------------------------------
// UNDONE : Pool these and check for duplicates?
CAudioSource *CreateWave( const char *pName )
{
char formatBuffer[1024];
InFileRIFF riff( pName, io );
if ( riff.RIFFName() != RIFF_WAVE )
{
Warning("Bad RIFF file type %s\n", pName );
return NULL;
}
// set up the iterator for the whole file (root RIFF is a chunk)
IterateRIFF walk( riff, riff.RIFFSize() );
int format = 0;
int formatSize = 0;
// This chunk must be first as it contains the wave's format
// break out when we've parsed it
while ( walk.ChunkAvailable() && format == 0 )
{
switch( walk.ChunkName() )
{
case WAVE_FMT:
{
if ( walk.ChunkSize() <= 1024 )
{
walk.ChunkRead( formatBuffer );
formatSize = walk.ChunkSize();
format = ((WAVEFORMATEX *)formatBuffer)->wFormatTag;
}
}
break;
default:
{
ChunkError( walk.ChunkName() );
}
break;
}
walk.ChunkNext();
}
// Not really a WAVE file or no format chunk, bail
if ( !format )
return NULL;
CAudioSourceWave *pWave;
// create the source from this file
pWave = new CAudioSourceMemWave();
// init the wave source
pWave->Setup( formatBuffer, formatSize, walk );
return pWave;
}
//-----------------------------------------------------------------------------
// Purpose: Wrapper for CreateWave()
//-----------------------------------------------------------------------------
CAudioSource *Audio_CreateMemoryWave( const char *pName )
{
return CreateWave( pName );
}

View File

@ -0,0 +1,76 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#ifndef SND_WAVE_SOURCE_H
#define SND_WAVE_SOURCE_H
#ifdef _WIN32
#pragma once
#endif
#include "soundsystem/snd_audio_source.h"
#include "sentence.h"
class IterateRIFF;
class CAudioSourceWave : public CAudioSource
{
public:
CAudioSourceWave( void );
~CAudioSourceWave( void );
void Setup( const char *pFormat, int formatSize, IterateRIFF &walk );
virtual int SampleRate( void ) { return m_rate; }
inline int SampleSize( void ) { return m_sampleSize; }
virtual float TrueSampleSize( void );
void *GetHeader( void );
// Legacy
virtual void ParseChunk( IterateRIFF &walk, int chunkName );
virtual void ParseSentence( IterateRIFF &walk );
void ConvertSamples( char *pData, int sampleCount );
bool IsLooped( void ) { return (m_loopStart >= 0) ? true : false; }
bool IsStreaming( void ) { return false; }
int ConvertLoopedPosition( int samplePosition );
int SampleCount( void );
virtual float GetRunningLength( void )
{
if ( m_rate > 0.0 )
{
return (float)SampleCount() / m_rate;
}
return 0.0f; }
CSentence *GetSentence( void );
protected:
// returns the loop start from a cue chunk
int ParseCueChunk( IterateRIFF &walk );
void Init( const char *pHeaderBuffer, int headerSize );
int m_bits;
int m_rate;
int m_channels;
int m_format;
int m_sampleSize;
int m_loopStart;
int m_sampleCount;
private:
char *m_pHeader;
CSentence m_Sentence;
};
#endif // SND_WAVE_SOURCE_H

279
soundsystem/soundsystem.cpp Normal file
View File

@ -0,0 +1,279 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: DLL interface for low-level sound utilities
//
//===========================================================================//
#include "soundsystem/isoundsystem.h"
#include "filesystem.h"
#include "tier1/strtools.h"
#include "tier1/convar.h"
#include "mathlib/mathlib.h"
#include "soundsystem/snd_device.h"
#include "datacache/idatacache.h"
#include "soundchars.h"
#include "tier1/utldict.h"
#include "snd_wave_source.h"
#include "snd_dev_wave.h"
#include "tier2/tier2.h"
//-----------------------------------------------------------------------------
// External interfaces
//-----------------------------------------------------------------------------
IAudioDevice *g_pAudioDevice = NULL;
ISoundSystem *g_pSoundSystem = NULL;
IDataCache *g_pDataCache = NULL;
//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
int g_nSoundFrameCount = 0;
//-----------------------------------------------------------------------------
// Purpose: DLL interface for low-level sound utilities
//-----------------------------------------------------------------------------
class CSoundSystem : public CTier2AppSystem< ISoundSystem >
{
typedef CTier2AppSystem< ISoundSystem > BaseClass;
public:
// Inherited from IAppSystem
virtual bool Connect( CreateInterfaceFn factory );
virtual void Disconnect();
virtual void *QueryInterface( const char *pInterfaceName );
virtual InitReturnVal_t Init();
virtual void Shutdown();
void Update( float dt );
void Flush( void );
CAudioSource *FindOrAddSound( const char *filename );
CAudioSource *LoadSound( const char *wavfile );
void PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer );
bool IsSoundPlaying( CAudioMixer *pMixer );
CAudioMixer *FindMixer( CAudioSource *source );
void StopAll( void );
void StopSound( CAudioMixer *mixer );
private:
struct CSoundFile
{
char filename[ 512 ];
CAudioSource *source;
long filetime;
};
IAudioDevice *m_pAudioDevice;
float m_flElapsedTime;
CUtlVector < CSoundFile > m_ActiveSounds;
};
//-----------------------------------------------------------------------------
// Singleton interface
//-----------------------------------------------------------------------------
static CSoundSystem s_SoundSystem;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSoundSystem, ISoundSystem, SOUNDSYSTEM_INTERFACE_VERSION, s_SoundSystem );
//-----------------------------------------------------------------------------
// Connect, disconnect
//-----------------------------------------------------------------------------
bool CSoundSystem::Connect( CreateInterfaceFn factory )
{
if ( !BaseClass::Connect( factory ) )
return false;
g_pDataCache = (IDataCache*)factory( DATACACHE_INTERFACE_VERSION, NULL );
g_pSoundSystem = this;
return (g_pFullFileSystem != NULL) && (g_pDataCache != NULL);
}
void CSoundSystem::Disconnect()
{
g_pSoundSystem = NULL;
g_pDataCache = NULL;
BaseClass::Disconnect();
}
//-----------------------------------------------------------------------------
// Query interface
//-----------------------------------------------------------------------------
void *CSoundSystem::QueryInterface( const char *pInterfaceName )
{
if (!Q_strncmp( pInterfaceName, SOUNDSYSTEM_INTERFACE_VERSION, Q_strlen(SOUNDSYSTEM_INTERFACE_VERSION) + 1))
return (ISoundSystem*)this;
return NULL;
}
//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
InitReturnVal_t CSoundSystem::Init()
{
InitReturnVal_t nRetVal = BaseClass::Init();
if ( nRetVal != INIT_OK )
return nRetVal;
MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
m_flElapsedTime = 0.0f;
m_pAudioDevice = Audio_CreateWaveDevice();
if ( !m_pAudioDevice->Init() )
return INIT_FAILED;
return INIT_OK;
}
void CSoundSystem::Shutdown()
{
Msg( "Removing %i sounds\n", m_ActiveSounds.Size() );
for ( int i = 0 ; i < m_ActiveSounds.Size(); i++ )
{
CSoundFile *p = &m_ActiveSounds[ i ];
Msg( "Removing sound: %s\n", p->filename );
delete p->source;
}
m_ActiveSounds.RemoveAll();
if ( m_pAudioDevice )
{
m_pAudioDevice->Shutdown();
delete m_pAudioDevice;
}
BaseClass::Shutdown();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CAudioSource *CSoundSystem::FindOrAddSound( const char *filename )
{
CSoundFile *s;
int i;
for ( i = 0; i < m_ActiveSounds.Size(); i++ )
{
s = &m_ActiveSounds[ i ];
Assert( s );
if ( !stricmp( s->filename, filename ) )
{
long filetime = g_pFullFileSystem->GetFileTime( filename );
if ( filetime != s->filetime )
{
Msg( "Reloading sound %s\n", filename );
delete s->source;
s->source = LoadSound( filename );
s->filetime = filetime;
}
return s->source;
}
}
i = m_ActiveSounds.AddToTail();
s = &m_ActiveSounds[ i ];
strcpy( s->filename, filename );
s->source = LoadSound( filename );
s->filetime = g_pFullFileSystem->GetFileTime( filename );
return s->source;
}
CAudioSource *CSoundSystem::LoadSound( const char *wavfile )
{
if ( !m_pAudioDevice )
return NULL;
CAudioSource *wave = AudioSource_Create( wavfile );
return wave;
}
void CSoundSystem::PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer )
{
if ( ppMixer )
{
*ppMixer = NULL;
}
if ( m_pAudioDevice )
{
CAudioMixer *mixer = source->CreateMixer();
if ( ppMixer )
{
*ppMixer = mixer;
}
mixer->SetVolume( volume );
m_pAudioDevice->AddSource( mixer );
}
}
void CSoundSystem::Update( float dt )
{
// closecaptionmanager->PreProcess( g_nSoundFrameCount );
if ( m_pAudioDevice )
{
m_pAudioDevice->Update( m_flElapsedTime );
}
// closecaptionmanager->PostProcess( g_nSoundFrameCount, dt );
m_flElapsedTime += dt;
g_nSoundFrameCount++;
}
void CSoundSystem::Flush( void )
{
if ( m_pAudioDevice )
{
m_pAudioDevice->Flush();
}
}
void CSoundSystem::StopAll( void )
{
if ( m_pAudioDevice )
{
m_pAudioDevice->StopSounds();
}
}
void CSoundSystem::StopSound( CAudioMixer *mixer )
{
int idx = m_pAudioDevice->FindSourceIndex( mixer );
if ( idx != -1 )
{
m_pAudioDevice->FreeChannel( idx );
}
}
bool CSoundSystem::IsSoundPlaying( CAudioMixer *pMixer )
{
if ( !m_pAudioDevice || !pMixer )
return false;
//
int index = m_pAudioDevice->FindSourceIndex( pMixer );
if ( index != -1 )
return true;
return false;
}
CAudioMixer *CSoundSystem::FindMixer( CAudioSource *source )
{
if ( !m_pAudioDevice )
return NULL;
return m_pAudioDevice->GetMixerForSource( source );
}

35
soundsystem/soundsystem.h Normal file
View File

@ -0,0 +1,35 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: DLL interface for low-level sound utilities
//
//===========================================================================//
#ifndef SOUNDSYSTEM_H
#define SOUNDSYSTEM_H
#ifdef _WIN32
#pragma once
#endif
#include "tier2/tier2.h"
#include "tier3/tier3.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class ISoundSystem;
class ISoundSystemServices;
class IAudioDevice;
//-----------------------------------------------------------------------------
// Singleton interface
//-----------------------------------------------------------------------------
extern ISoundSystem *g_pSoundSystem;
extern ISoundSystemServices *g_pSoundServices;
extern IAudioDevice *g_pAudioDevice;
#endif // SOUNDSYSTEM_H

View File

@ -0,0 +1,64 @@
//-----------------------------------------------------------------------------
// SOUNDSYSTEM.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
$Configuration
{
$Compiler
{
$PreprocessorDefinitions "$BASE;SOUNDSYSTEM_EXPORTS"
}
$Linker
{
$AdditionalDependencies "$BASE winmm.lib"
}
}
$Project "Soundsystem"
{
$Folder "Source Files"
{
$File "$SRCDIR\public\sentence.cpp"
$File "snd_audio_source.cpp"
$File "snd_dev_wave.cpp"
$File "snd_io.cpp"
$File "snd_wave_mixer.cpp"
$File "snd_wave_mixer_adpcm.cpp"
$File "snd_wave_source.cpp"
$File "soundsystem.cpp"
}
$Folder "Header Files"
{
$File "snd_dev_wave.h"
$File "snd_wave_mixer.h"
$File "snd_wave_mixer_adpcm.h"
$File "snd_wave_mixer_private.h"
$File "snd_wave_source.h"
$File "soundsystem.h"
}
$Folder "Interface"
{
$File "$SRCDIR\public\soundsystem\isoundsystem.h"
$File "$SRCDIR\public\soundsystem\snd_audio_source.h"
$File "$SRCDIR\public\soundsystem\snd_device.h"
}
$Folder "Link Libraries"
{
$Lib mathlib
$Lib tier2
$File "$SRCDIR\dx9sdk\lib\dsound.lib"
$File "$SRCDIR\dx9sdk\lib\dxguid.lib"
$File "$SRCDIR\lib\common\mss32.lib"
}
}