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.
532 lines
15 KiB
532 lines
15 KiB
//===== Copyright © 1996-2005, 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"
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.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;
|
|
}
|