Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

432 lines
12 KiB

//===== Copyright © Valve Corporation, All rights reserved. =================//
//
// Purpose: audio mix data structures
//
//===========================================================================//
#ifndef AUDIO_MIX_H
#define AUDIO_MIX_H
#ifdef _WIN32
#pragma once
#endif
#include "strtools.h" // V_memset
#include "utlstring.h"
#include "utlstringtoken.h"
#include "soundsystem/lowlevel.h"
struct dspglobalvars_t
{
float m_flMixMin; // min dsp mix at close range
float m_flMixMax; // max dsp mix at long range
float m_fldbMixDrop; // reduce mix_min/max by n% if sndlvl of new sound less than dbMin
float m_flDistanceMin; // range at which sounds are mixed at flMixMin
float m_flDistanceMax; // range at which sounds are mixed at flMixMax
uint16 m_ndbMin; // if sndlvl of a new sound is < dbMin, reduce mix_min/max by m_fldbMixDrop
bool m_bIsOff;
};
class CAudioProcessor
{
public:
virtual ~CAudioProcessor() {}
CAudioProcessor( const char *pDebugName, int nChannelCount );
void SetDebugName( const char *pName );
virtual void Process( CAudioMixBuffer *pInput, CAudioMixBuffer *pOutput, int nChannelCount, dspglobalvars_t *pGlobals );
virtual void ProcessSingleChannel( const CAudioMixBuffer &input, CAudioMixBuffer *pOutput, int nChannelIndex ) = 0;
// Parameter set can modify internal or global state (or both)
virtual bool SetControlParameter( CUtlStringToken name, float flValue );
virtual float GetControlParameter( CUtlStringToken name, float flDefaultValue = 0.0f ) = 0;
virtual bool SetNameParameter( CUtlStringToken name, uint32 nNameValue ) = 0;
virtual uint32 GetNameParameter( CUtlStringToken name, uint32 nDefaultValue ) = 0;
virtual bool ShouldProcess();
float GetPrevMix( float flMix );
void ApplyMonoProcessor( CAudioMixBuffer *pInput, CAudioMixBuffer *pOutput, int nOutputChannelCount, float flMix );
void ApplyStereoProcessor( CAudioMixBuffer *pInput, CAudioMixBuffer *pOutput, int nOutputChannelCount, float flMix );
void ApplyNChannelProcessor( CAudioMixBuffer *pInput, CAudioMixBuffer *pOutput, int nChannelCount, float flMix );
CUtlString m_debugName;
uint32 m_nNameHashCode;
float m_flXFade;
float m_flXFadePrev;
float m_flMix;
int m_nChannels;
bool m_bEnabled;
};
struct audio_buffer_input_t
{
const short *m_pSamples; // pointer to the samples themselves (in 8, 16, or 32-bit format)
uint m_nSampleCount; // number of whole samples *not bytes* *not multiplied by channel count* (e.g. a one-second 16-bit 44.1KHz file would be 44100 samples)
};
// UNDONE: deprecate this and move to a 8-int, 16-int, 32-float, N channel with extract specific channel design
enum vaudio_sampleformats_t
{
SAMPLE_INT16_MONO = 0, // default
SAMPLE_INT8_MONO, // increase to 16-bit and convert to float
SAMPLE_INT16_STEREO_L, // stereo wave, extract left channel
SAMPLE_INT16_STEREO_R, // stereo wave, extract right channel
SAMPLE_INT8_STEREO_L, // stereo wave, extract left channel
SAMPLE_INT8_STEREO_R, // stereo wave, extract right channel
SAMPLE_FLOAT32_MONO, // no reformat needed
};
struct audio_source_input_t
{
const audio_buffer_input_t *m_pPackets;
uint m_nSamplingRate;
uint16 m_nPacketCount;
uint16 m_nSampleFormat;
void InitPackets( const audio_buffer_input_t *pPacketsIn, int nPacketCountIn, int nSamplingRate, int nBitsPerSample, int nChannelsPerSample )
{
V_memset( this, 0, sizeof(*this) );
Assert( nPacketCountIn >= 0 && nPacketCountIn <= UINT16_MAX );
m_nPacketCount = (uint16)nPacketCountIn;
m_pPackets = pPacketsIn;
m_nSamplingRate = nSamplingRate;
switch( nBitsPerSample )
{
case 16:
m_nSampleFormat = (uint16)( (nChannelsPerSample == 1) ? SAMPLE_INT16_MONO : SAMPLE_INT16_STEREO_L );
break;
case 8:
m_nSampleFormat = (uint16)( (nChannelsPerSample == 1) ? SAMPLE_INT8_MONO : SAMPLE_INT8_STEREO_L );
break;
}
}
};
struct audio_source_indexstate_t
{
uint m_nPacketIndex;
uint m_nBufferSampleOffset;
uint m_nSampleFracOffset;
inline void Clear()
{
m_nPacketIndex = 0;
m_nBufferSampleOffset = 0;
m_nSampleFracOffset = 0;
}
};
class CAudioMixState
{
audio_source_input_t *m_pChannelsIn;
audio_source_indexstate_t *m_pChannelsOut;
uint32 m_nInputStride;
uint32 m_nPlaybackStride;
uint32 m_nChannelCount;
dspglobalvars_t *m_pGlobals;
public:
inline void Init( audio_source_input_t *pInput, uint32 nInputStride, audio_source_indexstate_t *pPlayback, uint32 nPlaybackStride, uint32 nCount )
{
m_pChannelsIn = pInput;
m_pChannelsOut = pPlayback;
m_nInputStride = nInputStride;
m_nPlaybackStride = nPlaybackStride;
m_nChannelCount = nCount;
}
void SetDSPGlobals( dspglobalvars_t *pGlobals )
{
m_pGlobals = pGlobals;
}
CAudioMixState( audio_source_input_t *pInput, uint32 nInputStride, audio_source_indexstate_t *pPlayback, uint32 nPlaybackStride, uint32 nCount )
: m_pChannelsIn(pInput), m_pChannelsOut(pPlayback), m_nInputStride(nInputStride), m_nPlaybackStride(nPlaybackStride), m_nChannelCount(nCount)
{
}
CAudioMixState()
{
m_pChannelsIn = 0;
m_pChannelsOut = 0;
m_nChannelCount = 0;
m_pGlobals = nullptr;
}
inline void Clear()
{
m_pChannelsIn = NULL;
m_pChannelsOut = NULL;
m_nChannelCount = 0;
}
inline audio_source_input_t *GetInput( int nIndex ) const
{
return (audio_source_input_t *)( (byte *)m_pChannelsIn + nIndex * m_nInputStride );
}
inline audio_source_indexstate_t *GetOutput( int nIndex ) const
{
return (audio_source_indexstate_t *)( (byte *)m_pChannelsOut + nIndex * m_nPlaybackStride );
}
bool IsChannelFinished( int nChannel ) const
{
return ( GetOutput( nChannel )->m_nPacketIndex >= GetInput( nChannel )->m_nPacketCount ) ? true : false;
}
dspglobalvars_t *DSPGlobals() const { return m_pGlobals; }
};
enum mix_command_id_t
{
AUDIO_MIX_CLEAR = 0,
AUDIO_MIX_EXTRACT_SOURCE,
AUDIO_MIX_ADVANCE_SOURCE,
AUDIO_MIX_ACCUMULATE,
AUDIO_MIX_ACCUMULATE_RAMP,
AUDIO_MIX_MULTIPLY,
AUDIO_MIX_PROCESS,
AUDIO_MIX_SUM,
AUDIO_MIX_SWAP, // swap two buffers
AUDIO_MIX_MEASURE_DEBUG_LEVEL,
AUDIO_MIX_OUTPUT_LEVEL,
};
struct audio_mix_command_t
{
uint16 m_nCommandId;
uint16 m_nOutput;
uint16 m_nInput0;
uint16 m_nInput1;
float m_flParam0;
float m_flParam1;
void Init( mix_command_id_t cmd, uint16 nOut )
{
m_nCommandId = (uint16)cmd;
m_nOutput = nOut;
m_nInput0 = 0;
m_nInput1 = 0;
m_flParam0 = 0.0f;
m_flParam1 = 0.0f;
}
void Init( mix_command_id_t cmd, uint16 nOut, uint16 nIn0, float flScale )
{
m_nCommandId = (uint16)cmd;
m_nOutput = nOut;
m_nInput0 = nIn0;
m_nInput1 = 0;
m_flParam0 = flScale;
m_flParam1 = 0.0f;
}
void Init( mix_command_id_t cmd, uint16 nOut, uint16 nIn0, uint16 nIn1, float flScale0, float flScale1 )
{
m_nCommandId = (uint16)cmd;
m_nOutput = nOut;
m_nInput0 = nIn0;
m_nInput1 = nIn1;
m_flParam0 = flScale0;
m_flParam1 = flScale1;
}
};
class CAudioMixCommandList
{
public:
inline void ClearBuffer( uint16 nTarget )
{
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_CLEAR, nTarget );
m_commands.AddToTail( cmd );
}
void ClearMultichannel( uint16 nTarget, int nCount );
void ScaleMultichannel( uint16 nTarget, uint16 nInput, int nCount, float flVolume );
inline void ExtractSourceToBuffer( uint16 nTarget, uint16 nChannel, float flVolume, float flPitch )
{
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_EXTRACT_SOURCE, nTarget, nChannel, 0, flVolume, flPitch );
m_commands.AddToTail( cmd );
}
inline void AdvanceSource( uint16 nChannel, float flPitch )
{
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_ADVANCE_SOURCE, 0, nChannel, 0, 0, flPitch );
m_commands.AddToTail( cmd );
}
inline void ProcessBuffer( uint16 nOutput, uint16 nInput, uint16 nProcessor, int nChannelCount )
{
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_PROCESS, nOutput, nInput, nProcessor, float(nChannelCount), 0.0f );
m_commands.AddToTail( cmd );
}
inline uint16 ProcessBuffer( uint16 nOutput, uint16 nInput, int nChannelCount, CAudioProcessor *pProc )
{
uint16 nProcessor = (uint16)m_processors.AddToTail( pProc );
ProcessBuffer( nOutput, nInput, nProcessor, nChannelCount );
return nProcessor;
}
inline void ScaleBuffer( uint16 nOutput, uint16 nInput, float flVolume )
{
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_MULTIPLY, nOutput, nInput, flVolume );
m_commands.AddToTail( cmd );
}
inline void AccumulateToBuffer( uint16 nOutput, uint16 nInput, float flVolume )
{
// if the volume is zero this will have no effect, so it is safe to skip it
if ( flVolume == 0.0f )
return;
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_ACCUMULATE, nOutput, nInput, flVolume );
m_commands.AddToTail( cmd );
}
inline void AccumulateToBufferVolumeRamp( uint16 nOutput, uint16 nInput, float flVolumeStart, float flVolumeEnd )
{
// Too small of a volume change to ramp? Just output without the ramp
// 1e-3f is small enough that we might do it during a normal ramp
// (2e-3f is the slope of a full scale fade at 512 samples per batch)
if ( fabs( flVolumeEnd-flVolumeStart) < 1e-3f )
{
AccumulateToBuffer( nOutput, nInput, flVolumeEnd );
return;
}
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_ACCUMULATE_RAMP, nOutput, nInput, 0, flVolumeStart, flVolumeEnd );
m_commands.AddToTail( cmd );
}
inline void Mix2x1( uint16 nOutput, uint16 nInput0, uint16 nInput1, float flVolume0, float flVolume1 )
{
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_SUM, nOutput, nInput0, nInput1, flVolume0, flVolume1 );
m_commands.AddToTail( cmd );
}
inline void SwapBuffers( uint16 nInput0, uint16 nInput1 )
{
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_SWAP, nInput0, nInput1, 1.0f );
m_commands.AddToTail( cmd );
}
inline void ReadOutputLevel( uint16 nLevelOutput, uint16 nInput0, uint16 nInputChannelCount )
{
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_OUTPUT_LEVEL, nLevelOutput, nInput0, nInputChannelCount, 1.0f, 1.0f );
m_commands.AddToTail( cmd );
}
inline void DebugReadLevel( uint16 nDebugOutput0, uint16 nInput0, uint16 nInputChannelCount )
{
audio_mix_command_t cmd;
cmd.Init( AUDIO_MIX_MEASURE_DEBUG_LEVEL, nDebugOutput0, nInput0, nInputChannelCount, 1.0f, 1.0f );
m_commands.AddToTail( cmd );
}
inline uint16 AddProcessor( CAudioProcessor *pProc )
{
return (uint16)m_processors.AddToTail( pProc );
}
inline void Clear()
{
m_commands.RemoveAll();
m_processors.RemoveAll();
}
void AccumulateMultichannel( uint16 nOutput, int nOutputChannels, uint16 nInput, int nInputChannels, float flInputVolume );
CUtlVectorFixedGrowable<audio_mix_command_t, 256> m_commands;
CUtlVectorFixedGrowable<CAudioProcessor *, 8> m_processors;
};
// This describes the state for each iteration of mixing
// it is the list of all low-level audio operations that need to take place
// in order to produce one buffer of mixed output
class CAudioMixDescription : public CAudioMixCommandList
{
public:
inline void Init( int nChannels )
{
m_nMixBuffersInUse = 0;
m_nMixBufferMax = 0;
m_nDebugOutputCount = 0;
m_nOutputLevelCount = 0;
Clear();
#if USE_VOICE_LAYERS
for ( int i = 0; i < NUM_VOICE_LAYERS; i++ )
{
m_flLayerVolume[i] = 1.0f;
}
#endif
}
// Add a new mix buffer
inline uint16 AllocMixBuffer( uint nCount = 1 )
{
int nOut = m_nMixBuffersInUse;
m_nMixBuffersInUse += nCount;
m_nMixBufferMax = MAX( m_nMixBufferMax, m_nMixBuffersInUse );
return (uint16)nOut;
}
inline void FreeMixBuffer( uint16 nStart, uint nCount = 1 )
{
// we only support freeing from the end of the stack
Assert( nStart + nCount == m_nMixBuffersInUse );
if ( nStart + nCount == m_nMixBuffersInUse )
{
m_nMixBuffersInUse -= nCount;
}
}
inline int AllocDebugOutputs( int nOutputs )
{
int nRet = m_nDebugOutputCount;
m_nDebugOutputCount += nOutputs;
return nRet;
}
inline int AllocOutputLevels( int nOutputs )
{
int nRet = m_nOutputLevelCount;
m_nOutputLevelCount += nOutputs;
return nRet;
}
uint m_nMixBufferMax;
uint m_nMixBuffersInUse;
uint m_nDebugOutputCount;
uint m_nOutputLevelCount;
#if USE_VOICE_LAYERS
float m_flLayerVolume[NUM_VOICE_LAYERS];
#endif
};
struct mix_debug_outputs_t
{
uint32 m_nChannelCount;
float m_flLevel;
float m_flChannelLevels[8];
};
// NOTE: This object is large (>64KB) declaring one on the stack may crash some platforms
class CAudioMixResults
{
public:
CUtlVectorFixedGrowable<mix_debug_outputs_t,8> m_debugOutputs;
CUtlVectorFixedGrowable<float, 16> m_flOutputLevels;
CUtlVectorFixedGrowable<CAudioMixBuffer, 32> m_pOutput;
};
extern void ProcessAudioMix( CAudioMixResults *pResults, const CAudioMixState &mixState, CAudioMixDescription &mixSetup );
#endif // AUDIO_MIX_H