//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Portable code to mix sounds for snd_dma.cpp.
//
//=============================================================================//
# include "audio_pch.h"
# include "mouthinfo.h"
# include "cl_main.h"
# include "icliententitylist.h"
# include "icliententity.h"
# include "sys_dll.h"
# include "video/ivideoservices.h"
# include "engine/IEngineSound.h"
# if defined( REPLAY_ENABLED )
# include "demo.h"
# include "replay_internal.h"
# endif
# ifdef GNUC
// we don't suport the ASM in this file right now under GCC, fallback to C libs
# undef id386
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
# if defined( REPLAY_ENABLED )
extern IReplayMovieManager * g_pReplayMovieManager ;
# endif
# if defined(_WIN32) && id386
// warning C4731: frame pointer register 'ebp' modified by inline assembly code
# pragma warning(disable : 4731)
# endif
// NOTE: !!!!!! YOU MUST UPDATE SND_MIXA.S IF THIS VALUE IS CHANGED !!!!!
# define SND_SCALE_BITS 7
# define SND_SCALE_SHIFT (8-SND_SCALE_BITS)
# define SND_SCALE_LEVELS (1<<SND_SCALE_BITS)
# define SND_SCALE_BITS16 8
# define SND_SCALE_SHIFT16 (8-SND_SCALE_BITS16)
# define SND_SCALE_LEVELS16 (1<<SND_SCALE_BITS16)
void Snd_WriteLinearBlastStereo16 ( void ) ;
void SND_PaintChannelFrom8 ( portable_samplepair_t * pOutput , int * volume , byte * pData8 , int count ) ;
bool Con_IsVisible ( void ) ;
void SND_RecordBuffer ( void ) ;
bool DSP_RoomDSPIsOff ( void ) ;
bool BChannelLowVolume ( channel_t * pch , int vol_min ) ;
void ChannelCopyVolumes ( channel_t * pch , int * pvolume_dest , int ivol_start , int cvol ) ;
float ChannelLoudestCurVolume ( const channel_t * RESTRICT pch ) ;
extern int g_soundtime ;
extern float host_frametime ;
extern float host_frametime_unbounded ;
# if !defined( NO_VOICE )
extern int g_SND_VoiceOverdriveInt ;
# endif
extern ConVar dsp_room ;
extern ConVar dsp_water ;
extern ConVar dsp_player ;
extern ConVar dsp_facingaway ;
extern ConVar snd_showstart ;
extern ConVar dsp_automatic ;
extern ConVar snd_pitchquality ;
extern float DSP_ROOM_MIX ;
extern float DSP_NOROOM_MIX ;
portable_samplepair_t * g_paintbuffer ;
// temp paintbuffer - not included in main list of paintbuffers
// NOTE: this paintbuffer is also used as a copy buffer by interpolating pitch
// shift routines. Decreasing TEMP_COPY_BUFFER_SIZE (or PAINTBUFFER_MEM_SIZE)
// will decrease the maximum pitch level (current 4.0)!
portable_samplepair_t * g_temppaintbuffer = NULL ;
CUtlVector < paintbuffer_t > g_paintBuffers ;
// pointer to current paintbuffer (front and reare), used by all mixing, upsampling and dsp routines
portable_samplepair_t * g_curpaintbuffer = NULL ;
portable_samplepair_t * g_currearpaintbuffer = NULL ;
portable_samplepair_t * g_curcenterpaintbuffer = NULL ;
bool g_bdirectionalfx ;
bool g_bDspOff ;
float g_dsp_volume ;
// dsp performance timing
unsigned g_snd_call_time_debug = 0 ;
unsigned g_snd_time_debug = 0 ;
unsigned g_snd_count_debug = 0 ;
unsigned g_snd_samplecount = 0 ;
unsigned g_snd_frametime = 0 ;
unsigned g_snd_frametime_total = 0 ;
int g_snd_profile_type = 0 ; // type 1 dsp, type 2 mixer, type 3 load sound, type 4 all sound
# define FILTERTYPE_NONE 0
# define FILTERTYPE_LINEAR 1
# define FILTERTYPE_CUBIC 2
// filter memory for upsampling
portable_samplepair_t cubicfilter1 [ 3 ] = { { 0 , 0 } , { 0 , 0 } , { 0 , 0 } } ;
portable_samplepair_t cubicfilter2 [ 3 ] = { { 0 , 0 } , { 0 , 0 } , { 0 , 0 } } ;
portable_samplepair_t linearfilter1 [ 1 ] = { { 0 , 0 } } ;
portable_samplepair_t linearfilter2 [ 1 ] = { { 0 , 0 } } ;
portable_samplepair_t linearfilter3 [ 1 ] = { { 0 , 0 } } ;
portable_samplepair_t linearfilter4 [ 1 ] = { { 0 , 0 } } ;
portable_samplepair_t linearfilter5 [ 1 ] = { { 0 , 0 } } ;
portable_samplepair_t linearfilter6 [ 1 ] = { { 0 , 0 } } ;
portable_samplepair_t linearfilter7 [ 1 ] = { { 0 , 0 } } ;
portable_samplepair_t linearfilter8 [ 1 ] = { { 0 , 0 } } ;
int snd_scaletable [ SND_SCALE_LEVELS ] [ 256 ] ; // 32k*4 = 128K
int * snd_p , snd_linear_count , snd_vol ;
short * snd_out ;
extern int DSP_Alloc ( int ipset , float xfade , int cchan ) ;
bool DSP_CheckDspAutoEnabled ( void ) ;
int Get_idsp_room ( void ) ;
int dsp_room_GetInt ( void ) ;
void DSP_SetDspAuto ( int dsp_preset ) ;
bool DSP_CheckDspAutoEnabled ( void ) ;
void MIX_ScalePaintBuffer ( int bufferIndex , int count , float fgain ) ;
bool IsReplayRendering ( )
{
# if defined( REPLAY_ENABLED )
return g_pReplayMovieManager & & g_pReplayMovieManager - > IsRendering ( ) ;
# else
return false ;
# endif
}
//-----------------------------------------------------------------------------
// Free allocated memory buffers
//-----------------------------------------------------------------------------
void MIX_FreeAllPaintbuffers ( void )
{
if ( g_paintBuffers . Count ( ) )
{
if ( g_temppaintbuffer )
{
_aligned_free ( g_temppaintbuffer ) ;
g_temppaintbuffer = NULL ;
}
for ( int i = 0 ; i < g_paintBuffers . Count ( ) ; i + + )
{
if ( g_paintBuffers [ i ] . pbuf )
{
_aligned_free ( g_paintBuffers [ i ] . pbuf ) ;
}
if ( g_paintBuffers [ i ] . pbufrear )
{
_aligned_free ( g_paintBuffers [ i ] . pbufrear ) ;
}
if ( g_paintBuffers [ i ] . pbufcenter )
{
_aligned_free ( g_paintBuffers [ i ] . pbufcenter ) ;
}
}
g_paintBuffers . RemoveAll ( ) ;
}
}
void MIX_InitializePaintbuffer ( paintbuffer_t * pPaintBuffer , bool bSurround , bool bSurroundCenter )
{
V_memset ( pPaintBuffer , 0 , sizeof ( * pPaintBuffer ) ) ;
pPaintBuffer - > pbuf = ( portable_samplepair_t * ) _aligned_malloc ( PAINTBUFFER_MEM_SIZE * sizeof ( portable_samplepair_t ) , 16 ) ;
V_memset ( pPaintBuffer - > pbuf , 0 , PAINTBUFFER_MEM_SIZE * sizeof ( portable_samplepair_t ) ) ;
if ( bSurround )
{
pPaintBuffer - > pbufrear = ( portable_samplepair_t * ) _aligned_malloc ( PAINTBUFFER_MEM_SIZE * sizeof ( portable_samplepair_t ) , 16 ) ;
V_memset ( pPaintBuffer - > pbufrear , 0 , PAINTBUFFER_MEM_SIZE * sizeof ( portable_samplepair_t ) ) ;
}
if ( bSurroundCenter )
{
pPaintBuffer - > pbufcenter = ( portable_samplepair_t * ) _aligned_malloc ( PAINTBUFFER_MEM_SIZE * sizeof ( portable_samplepair_t ) , 16 ) ;
V_memset ( pPaintBuffer - > pbufcenter , 0 , PAINTBUFFER_MEM_SIZE * sizeof ( portable_samplepair_t ) ) ;
}
}
//-----------------------------------------------------------------------------
// Allocate memory buffers
// Initialize paintbuffers array, set current paint buffer to main output buffer SOUND_BUFFER_PAINT
//-----------------------------------------------------------------------------
bool MIX_InitAllPaintbuffers ( void )
{
bool bSurround ;
bool bSurroundCenter ;
bSurroundCenter = g_AudioDevice - > IsSurroundCenter ( ) ;
bSurround = g_AudioDevice - > IsSurround ( ) | | bSurroundCenter ;
g_temppaintbuffer = ( portable_samplepair_t * ) _aligned_malloc ( TEMP_COPY_BUFFER_SIZE * sizeof ( portable_samplepair_t ) , 16 ) ;
V_memset ( g_temppaintbuffer , 0 , TEMP_COPY_BUFFER_SIZE * sizeof ( portable_samplepair_t ) ) ;
while ( g_paintBuffers . Count ( ) < SOUND_BUFFER_BASETOTAL )
{
int nIndex = g_paintBuffers . AddToTail ( ) ;
MIX_InitializePaintbuffer ( & ( g_paintBuffers [ nIndex ] ) , bSurround , bSurroundCenter ) ;
}
g_paintbuffer = g_paintBuffers [ SOUND_BUFFER_PAINT ] . pbuf ;
// buffer flags
g_paintBuffers [ SOUND_BUFFER_ROOM ] . flags = SOUND_BUSS_ROOM ;
g_paintBuffers [ SOUND_BUFFER_FACING ] . flags = SOUND_BUSS_FACING ;
g_paintBuffers [ SOUND_BUFFER_FACINGAWAY ] . flags = SOUND_BUSS_FACINGAWAY ;
g_paintBuffers [ SOUND_BUFFER_SPEAKER ] . flags = SOUND_BUSS_SPEAKER ;
g_paintBuffers [ SOUND_BUFFER_DRY ] . flags = SOUND_BUSS_DRY ;
// buffer surround sound flag
g_paintBuffers [ SOUND_BUFFER_PAINT ] . fsurround = bSurround ;
g_paintBuffers [ SOUND_BUFFER_FACING ] . fsurround = bSurround ;
g_paintBuffers [ SOUND_BUFFER_FACINGAWAY ] . fsurround = bSurround ;
g_paintBuffers [ SOUND_BUFFER_DRY ] . fsurround = bSurround ;
// buffer 5 channel surround sound flag
g_paintBuffers [ SOUND_BUFFER_PAINT ] . fsurround_center = bSurroundCenter ;
g_paintBuffers [ SOUND_BUFFER_FACING ] . fsurround_center = bSurroundCenter ;
g_paintBuffers [ SOUND_BUFFER_FACINGAWAY ] . fsurround_center = bSurroundCenter ;
g_paintBuffers [ SOUND_BUFFER_DRY ] . fsurround_center = bSurroundCenter ;
// room buffer mixes down to mono or stereo, never to 4 or 5 ch
g_paintBuffers [ SOUND_BUFFER_ROOM ] . fsurround = false ;
g_paintBuffers [ SOUND_BUFFER_ROOM ] . fsurround_center = false ;
// speaker buffer mixes to mono
g_paintBuffers [ SOUND_BUFFER_SPEAKER ] . fsurround = false ;
g_paintBuffers [ SOUND_BUFFER_SPEAKER ] . fsurround_center = false ;
MIX_SetCurrentPaintbuffer ( SOUND_BUFFER_PAINT ) ;
return true ;
}
// called before loading samples to mix - cap the mix rate (ie: pitch) so that
// we never overflow the mix copy buffer.
double MIX_GetMaxRate ( double rate , int sampleCount )
{
if ( rate < = 2.0 )
return rate ;
// copybuf_bytes = rate_max * samples_max * samplesize_max
// so:
// rate_max = copybuf_bytes / (samples_max * samplesize_max )
double samplesize_max = 4.0 ; // stereo 16bit samples
double copybuf_bytes = ( double ) ( TEMP_COPY_BUFFER_SIZE * sizeof ( portable_samplepair_t ) ) ;
double samples_max = ( double ) ( PAINTBUFFER_SIZE ) ;
double rate_max = copybuf_bytes / ( samples_max * samplesize_max ) ;
// make sure sampleCount is never greater than paintbuffer samples
// (this should have been set up in MIX_PaintChannels)
Assert ( sampleCount < = PAINTBUFFER_SIZE ) ;
return fpmin ( rate , rate_max ) ;
}
// Transfer (endtime - lpaintedtime) stereo samples in pfront out to hardware
// pfront - pointer to stereo paintbuffer - 32 bit samples, interleaved stereo
// lpaintedtime - total number of 32 bit stereo samples previously output to hardware
// endtime - total number of 32 bit stereo samples currently mixed in paintbuffer
void S_TransferStereo16 ( void * pOutput , const portable_samplepair_t * pfront , int lpaintedtime , int endtime )
{
int lpos ;
if ( IsX360 ( ) )
{
// not the right path for 360
Assert ( 0 ) ;
return ;
}
Assert ( pOutput ) ;
snd_vol = S_GetMasterVolume ( ) * 256 ;
snd_p = ( int * ) pfront ;
// get size of output buffer in full samples (LR pairs)
int samplePairCount = g_AudioDevice - > DeviceSampleCount ( ) > > 1 ;
int sampleMask = samplePairCount - 1 ;
bool bShouldPlaySound = ! cl_movieinfo . IsRecording ( ) & & ! IsReplayRendering ( ) ;
while ( lpaintedtime < endtime )
{
// pbuf can hold 16384, 16 bit L/R samplepairs.
// lpaintedtime - where to start painting into dma buffer.
// (modulo size of dma buffer for current position).
// handle recirculating buffer issues
// lpos - samplepair index into dma buffer. First samplepair from paintbuffer to be xfered here.
lpos = lpaintedtime & sampleMask ;
// snd_out is L/R sample index into dma buffer. First L sample from paintbuffer goes here.
snd_out = ( short * ) pOutput + ( lpos < < 1 ) ;
// snd_linear_count is number of samplepairs between end of dma buffer and xfer start index.
snd_linear_count = samplePairCount - lpos ;
// clamp snd_linear_count to be only as many samplepairs premixed
if ( snd_linear_count > endtime - lpaintedtime )
{
// endtime - lpaintedtime = number of premixed sample pairs ready for xfer.
snd_linear_count = endtime - lpaintedtime ;
}
// snd_linear_count is now number of mono 16 bit samples (L and R) to xfer.
snd_linear_count < < = 1 ;
// write a linear blast of samples
SND_RecordBuffer ( ) ;
if ( bShouldPlaySound )
{
// transfer 16bit samples from snd_p into snd_out, multiplying each sample by volume.
Snd_WriteLinearBlastStereo16 ( ) ;
}
// advance paintbuffer pointer
snd_p + = snd_linear_count ;
// advance lpaintedtime by number of samplepairs just xfered.
lpaintedtime + = ( snd_linear_count > > 1 ) ;
}
}
// Transfer contents of main paintbuffer pfront out to
// device. Perform volume multiply on each sample.
void S_TransferPaintBuffer ( void * pOutput , const portable_samplepair_t * pfront , int lpaintedtime , int endtime )
{
int out_idx ; // mono sample index
int count ; // number of mono samples to output
int out_mask ;
int step ;
int val ;
int nSoundVol ;
const int * p ;
if ( IsX360 ( ) )
{
// not the right path for 360
Assert ( 0 ) ;
return ;
}
Assert ( pOutput ) ;
p = ( const int * ) pfront ;
count = ( ( endtime - lpaintedtime ) * g_AudioDevice - > DeviceChannels ( ) ) ;
out_mask = g_AudioDevice - > DeviceSampleCount ( ) - 1 ;
// 44k: remove old 22k sound support << HISPEED_DMA
// out_idx = ((paintedtime << HISPEED_DMA) * g_AudioDevice->DeviceChannels()) & out_mask;
out_idx = ( lpaintedtime * g_AudioDevice - > DeviceChannels ( ) ) & out_mask ;
step = 3 - g_AudioDevice - > DeviceChannels ( ) ; // mono output buffer - step 2, stereo - step 1
nSoundVol = S_GetMasterVolume ( ) * 256 ;
if ( g_AudioDevice - > DeviceSampleBits ( ) = = 16 )
{
short * out = ( short * ) pOutput ;
while ( count - - )
{
val = ( * p * nSoundVol ) > > 8 ;
p + = step ;
val = CLIP ( val ) ;
out [ out_idx ] = val ;
out_idx = ( out_idx + 1 ) & out_mask ;
}
}
else if ( g_AudioDevice - > DeviceSampleBits ( ) = = 8 )
{
unsigned char * out = ( unsigned char * ) pOutput ;
while ( count - - )
{
val = ( * p * nSoundVol ) > > 8 ;
p + = step ;
val = CLIP ( val ) ;
out [ out_idx ] = ( val > > 8 ) + 128 ;
out_idx = ( out_idx + 1 ) & out_mask ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
CHANNEL MIXING
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
// free channel so that it may be allocated by the
// next request to play a sound. If sound is a
// word in a sentence, release the sentence.
// Works for static, dynamic, sentence and stream sounds
void S_FreeChannel ( channel_t * ch )
{
// Don't reenter in here (can happen inside voice code).
if ( ch - > flags . m_bIsFreeingChannel )
return ;
ch - > flags . m_bIsFreeingChannel = true ;
SND_CloseMouth ( ch ) ;
g_pSoundServices - > OnSoundStopped ( ch - > guid , ch - > soundsource , ch - > entchannel , ch - > sfx - > getname ( ) ) ;
ch - > flags . isSentence = false ;
// Msg("End sound %s\n", ch->sfx->getname() );
delete ch - > pMixer ;
ch - > pMixer = NULL ;
ch - > sfx = NULL ;
// zero all data in channel
g_ActiveChannels . Remove ( ch ) ;
Q_memset ( ch , 0 , sizeof ( channel_t ) ) ;
}
// Mix all channels into active paintbuffers until paintbuffer is full or 'endtime' is reached.
// endtime: time in 44khz samples to mix
// rate: ignore samples which are not natively at this rate (for multipass mixing/filtering)
// if rate == SOUND_ALL_RATES then mix all samples this pass
// flags: if SOUND_MIX_DRY, then mix only samples with channel flagged as 'dry'
// outputRate: target mix rate for all samples. Note, if outputRate = SOUND_DMA_SPEED, then
// this routine will fill the paintbuffer to endtime. Otherwise, fewer samples are mixed.
// if (endtime - paintedtime) is not aligned on boundaries of 4,
// we'll miss data if outputRate < SOUND_DMA_SPEED!
void MIX_MixChannelsToPaintbuffer ( CChannelList & list , int endtime , int flags , int rate , int outputRate )
{
VPROF ( " MixChannelsToPaintbuffer " ) ;
int i ;
int sampleCount ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s c:%d %d/%d " , __FUNCTION__ , list . Count ( ) , rate , outputRate ) ;
// mix each channel into paintbuffer
// validate parameters
Assert ( outputRate < = SOUND_DMA_SPEED ) ;
Assert ( ! ( ( endtime - g_paintedtime ) & 0x3 ) | | ( outputRate = = SOUND_DMA_SPEED ) ) ; // make sure we're not discarding data
// 44k: try to mix this many samples at outputRate
sampleCount = ( endtime - g_paintedtime ) / ( SOUND_DMA_SPEED / outputRate ) ;
if ( sampleCount < = 0 )
return ;
// Apply a global pitch shift if we're playing back a time-scaled replay
float flGlobalPitchScale = 1.0f ;
# if defined( REPLAY_ENABLED )
extern IDemoPlayer * g_pReplayDemoPlayer ;
if ( demoplayer - > IsPlayingBack ( ) & & demoplayer = = g_pReplayDemoPlayer )
{
// adjust time scale if playing back demo
flGlobalPitchScale = demoplayer - > GetPlaybackTimeScale ( ) ;
}
# endif
for ( i = list . Count ( ) ; - - i > = 0 ; )
{
channel_t * ch = list . GetChannel ( i ) ;
Assert ( ch - > sfx ) ;
// must never have a 'dry' and 'speaker' set - causes double mixing & double data reading
Assert ( ! ( ( ch - > flags . bdry & & ch - > flags . bSpeaker ) | | ( ch - > flags . bdry & & ch - > special_dsp ! = 0 ) ) ) ;
// if mixing with SOUND_MIX_DRY flag, ignore (don't even load) all channels not flagged as 'dry'
if ( flags = = SOUND_MIX_DRY )
{
if ( ! ch - > flags . bdry )
continue ;
}
// if mixing with SOUND_MIX_WET flag, ignore (don't even load) all channels flagged as 'dry' or 'speaker'
if ( flags = = SOUND_MIX_WET )
{
if ( ch - > flags . bdry | | ch - > flags . bSpeaker | | ch - > special_dsp ! = 0 )
continue ;
}
// if mixing with SOUND_MIX_SPEAKER flag, ignore (don't even load) all channels not flagged as 'speaker'
if ( flags = = SOUND_MIX_SPEAKER )
{
if ( ! ch - > flags . bSpeaker )
continue ;
}
// if mixing with SOUND_MIX_SPEAKER flag, ignore (don't even load) all channels not flagged as 'speaker'
if ( flags = = SOUND_MIX_SPECIAL_DSP )
{
if ( ch - > special_dsp = = 0 )
continue ;
}
// multipass mixing - only mix samples of specified sample rate
switch ( rate )
{
case SOUND_11k :
case SOUND_22k :
case SOUND_44k :
if ( rate ! = ch - > sfx - > pSource - > SampleRate ( ) )
continue ;
break ;
default :
case SOUND_ALL_RATES :
break ;
}
// Tracker 20771, if breen is speaking through the monitor, the client doesn't have an entity
// for the "soundsource" but we still need the lipsync to pause if the game is paused. Therefore
// I changed SND_IsMouth to look for any .wav on any channels which has sentence data
bool bIsMouth = SND_IsMouth ( ch ) ;
bool bShouldPause = IsX360 ( ) ? ! ch - > sfx - > m_bIsUISound : bIsMouth ;
// Tracker 14637: Pausing the game pauses voice sounds, but not other sounds...
if ( bShouldPause & & g_pSoundServices - > IsGamePaused ( ) )
{
continue ;
}
if ( bIsMouth )
{
if ( ( ch - > soundsource = = SOUND_FROM_UI_PANEL ) | | entitylist - > GetClientEntity ( ch - > soundsource ) | |
( ch - > flags . bSpeaker & & entitylist - > GetClientEntity ( ch - > speakerentity ) ) )
{
// UNDONE: recode this as a member function of CAudioMixer
SND_MoveMouth8 ( ch , ch - > sfx - > pSource , sampleCount ) ;
}
}
// mix channel to all active paintbuffers:
// mix 'dry' sounds only to dry paintbuffer.
// mix 'speaker' sounds only to speaker paintbuffer.
// mix all other sounds between room, facing & facingaway paintbuffers
// NOTE: must be called once per channel only - consecutive calls retrieve additional data.
float flPitch = ch - > pitch ;
ch - > pitch * = flGlobalPitchScale ;
if ( list . IsQuashed ( i ) )
{
// If the sound has been silenced as a performance heuristic, quash it.
ch - > pMixer - > SkipSamples ( ch , sampleCount , outputRate , 0 ) ;
// DevMsg("Quashed channel %d (%s)\n", i, ch->sfx->GetFileName());
}
else
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " MixDataToDevice " ) ;
ch - > pMixer - > MixDataToDevice ( g_AudioDevice , ch , sampleCount , outputRate , 0 ) ;
}
// restore to original pitch settings
ch - > pitch = flPitch ;
if ( ! ch - > pMixer - > ShouldContinueMixing ( ) )
{
S_FreeChannel ( ch ) ;
list . RemoveChannelFromList ( i ) ;
}
if ( ( ch - > nFreeChannelAtSampleTime > 0 & & ( int ) ch - > nFreeChannelAtSampleTime < = endtime ) )
{
S_FreeChannel ( ch ) ;
list . RemoveChannelFromList ( i ) ;
}
}
}
// pass in index -1...count+2, return pointer to source sample in either paintbuffer or delay buffer
inline portable_samplepair_t * S_GetNextpFilter ( int i , portable_samplepair_t * pbuffer , portable_samplepair_t * pfiltermem )
{
// The delay buffer is assumed to precede the paintbuffer by 6 duplicated samples
if ( i = = - 1 )
return ( & ( pfiltermem [ 0 ] ) ) ;
if ( i = = 0 )
return ( & ( pfiltermem [ 1 ] ) ) ;
if ( i = = 1 )
return ( & ( pfiltermem [ 2 ] ) ) ;
// return from paintbuffer, where samples are doubled.
// even samples are to be replaced with interpolated value.
return ( & ( pbuffer [ ( i - 2 ) * 2 + 1 ] ) ) ;
}
// pass forward over passed in buffer and cubic interpolate all odd samples
// pbuffer: buffer to filter (in place)
// prevfilter: filter memory. NOTE: this must match the filtertype ie: filtercubic[] for FILTERTYPE_CUBIC
// if NULL then perform no filtering. UNDONE: should have a filter memory array type
// count: how many samples to upsample. will become count*2 samples in buffer, in place.
void S_Interpolate2xCubic ( portable_samplepair_t * pbuffer , portable_samplepair_t * pfiltermem , int cfltmem , int count )
{
// implement cubic interpolation on 2x upsampled buffer. Effectively delays buffer contents by 2 samples.
// pbuffer: contains samples at 0, 2, 4, 6...
// temppaintbuffer is temp buffer, of same or larger size than a paintbuffer, used to store processed values
// count: number of samples to process in buffer ie: how many samples at 0, 2, 4, 6...
// finpos is the fractional, inpos the integer part.
// finpos = 0.5 for upsampling by 2x
// inpos is the position of the sample
// xm1 = x [inpos - 1];
// x0 = x [inpos + 0];
// x1 = x [inpos + 1];
// x2 = x [inpos + 2];
// a = (3 * (x0-x1) - xm1 + x2) / 2;
// b = 2*x1 + xm1 - (5*x0 + x2) / 2;
// c = (x1 - xm1) / 2;
// y [outpos] = (((a * finpos) + b) * finpos + c) * finpos + x0;
int i , upCount = count < < 1 ;
int a , b , c ;
int xm1 , x0 , x1 , x2 ;
portable_samplepair_t * psamp0 ;
portable_samplepair_t * psamp1 ;
portable_samplepair_t * psamp2 ;
portable_samplepair_t * psamp3 ;
int outpos = 0 ;
Assert ( upCount < = PAINTBUFFER_SIZE ) ;
// pfiltermem holds 6 samples from previous buffer pass
// process 'count' samples
for ( i = 0 ; i < count ; i + + )
{
// get source sample pointer
psamp0 = S_GetNextpFilter ( i - 1 , pbuffer , pfiltermem ) ;
psamp1 = S_GetNextpFilter ( i , pbuffer , pfiltermem ) ;
psamp2 = S_GetNextpFilter ( i + 1 , pbuffer , pfiltermem ) ;
psamp3 = S_GetNextpFilter ( i + 2 , pbuffer , pfiltermem ) ;
// write out original sample to interpolation buffer
g_temppaintbuffer [ outpos + + ] = * psamp1 ;
// get all left samples for interpolation window
xm1 = psamp0 - > left ;
x0 = psamp1 - > left ;
x1 = psamp2 - > left ;
x2 = psamp3 - > left ;
// interpolate
a = ( 3 * ( x0 - x1 ) - xm1 + x2 ) / 2 ;
b = 2 * x1 + xm1 - ( 5 * x0 + x2 ) / 2 ;
c = ( x1 - xm1 ) / 2 ;
// write out interpolated sample
g_temppaintbuffer [ outpos ] . left = a / 8 + b / 4 + c / 2 + x0 ;
// get all right samples for window
xm1 = psamp0 - > right ;
x0 = psamp1 - > right ;
x1 = psamp2 - > right ;
x2 = psamp3 - > right ;
// interpolate
a = ( 3 * ( x0 - x1 ) - xm1 + x2 ) / 2 ;
b = 2 * x1 + xm1 - ( 5 * x0 + x2 ) / 2 ;
c = ( x1 - xm1 ) / 2 ;
// write out interpolated sample, increment output counter
g_temppaintbuffer [ outpos + + ] . right = a / 8 + b / 4 + c / 2 + x0 ;
Assert ( outpos < = TEMP_COPY_BUFFER_SIZE ) ;
}
Assert ( cfltmem > = 3 ) ;
// save last 3 samples from paintbuffer
pfiltermem [ 0 ] = pbuffer [ upCount - 5 ] ;
pfiltermem [ 1 ] = pbuffer [ upCount - 3 ] ;
pfiltermem [ 2 ] = pbuffer [ upCount - 1 ] ;
// copy temppaintbuffer back into paintbuffer
for ( i = 0 ; i < upCount ; i + + )
pbuffer [ i ] = g_temppaintbuffer [ i ] ;
}
// pass forward over passed in buffer and linearly interpolate all odd samples
// pbuffer: buffer to filter (in place)
// prevfilter: filter memory. NOTE: this must match the filtertype ie: filterlinear[] for FILTERTYPE_LINEAR
// if NULL then perform no filtering.
// count: how many samples to upsample. will become count*2 samples in buffer, in place.
void S_Interpolate2xLinear ( portable_samplepair_t * pbuffer , portable_samplepair_t * pfiltermem , int cfltmem , int count )
{
int i , upCount = count < < 1 ;
Assert ( upCount < = PAINTBUFFER_SIZE ) ;
Assert ( cfltmem > = 1 ) ;
// use interpolation value from previous mix
pbuffer [ 0 ] . left = ( pfiltermem - > left + pbuffer [ 0 ] . left ) > > 1 ;
pbuffer [ 0 ] . right = ( pfiltermem - > right + pbuffer [ 0 ] . right ) > > 1 ;
for ( i = 2 ; i < upCount ; i + = 2 )
{
// use linear interpolation for upsampling
pbuffer [ i ] . left = ( pbuffer [ i ] . left + pbuffer [ i - 1 ] . left ) > > 1 ;
pbuffer [ i ] . right = ( pbuffer [ i ] . right + pbuffer [ i - 1 ] . right ) > > 1 ;
}
// save last value to be played out in buffer
* pfiltermem = pbuffer [ upCount - 1 ] ;
}
// Optimized routine. 2.27X faster than the above routine
void S_Interpolate2xLinear_2 ( int count , portable_samplepair_t * pbuffer , portable_samplepair_t * pfiltermem , int cfltmem )
{
Assert ( cfltmem > = 1 ) ;
int sample = count - 1 ;
int end = ( count * 2 ) - 1 ;
portable_samplepair_t * pwrite = & pbuffer [ end ] ;
portable_samplepair_t * pread = & pbuffer [ sample ] ;
portable_samplepair_t last = pread [ 0 ] ;
pread - - ;
// PERFORMANCE: Unroll the loop 8 times. This improves speed quite a bit
for ( ; sample > = 8 ; sample - = 8 )
{
pwrite [ 0 ] = last ;
pwrite [ - 1 ] . left = ( pread [ 0 ] . left + last . left ) > > 1 ;
pwrite [ - 1 ] . right = ( pread [ 0 ] . right + last . right ) > > 1 ;
last = pread [ 0 ] ;
pwrite [ - 2 ] = last ;
pwrite [ - 3 ] . left = ( pread [ - 1 ] . left + last . left ) > > 1 ;
pwrite [ - 3 ] . right = ( pread [ - 1 ] . right + last . right ) > > 1 ;
last = pread [ - 1 ] ;
pwrite [ - 4 ] = last ;
pwrite [ - 5 ] . left = ( pread [ - 2 ] . left + last . left ) > > 1 ;
pwrite [ - 5 ] . right = ( pread [ - 2 ] . right + last . right ) > > 1 ;
last = pread [ - 2 ] ;
pwrite [ - 6 ] = last ;
pwrite [ - 7 ] . left = ( pread [ - 3 ] . left + last . left ) > > 1 ;
pwrite [ - 7 ] . right = ( pread [ - 3 ] . right + last . right ) > > 1 ;
last = pread [ - 3 ] ;
pwrite [ - 8 ] = last ;
pwrite [ - 9 ] . left = ( pread [ - 4 ] . left + last . left ) > > 1 ;
pwrite [ - 9 ] . right = ( pread [ - 4 ] . right + last . right ) > > 1 ;
last = pread [ - 4 ] ;
pwrite [ - 10 ] = last ;
pwrite [ - 11 ] . left = ( pread [ - 5 ] . left + last . left ) > > 1 ;
pwrite [ - 11 ] . right = ( pread [ - 5 ] . right + last . right ) > > 1 ;
last = pread [ - 5 ] ;
pwrite [ - 12 ] = last ;
pwrite [ - 13 ] . left = ( pread [ - 6 ] . left + last . left ) > > 1 ;
pwrite [ - 13 ] . right = ( pread [ - 6 ] . right + last . right ) > > 1 ;
last = pread [ - 6 ] ;
pwrite [ - 14 ] = last ;
pwrite [ - 15 ] . left = ( pread [ - 7 ] . left + last . left ) > > 1 ;
pwrite [ - 15 ] . right = ( pread [ - 7 ] . right + last . right ) > > 1 ;
last = pread [ - 7 ] ;
pread - = 8 ;
pwrite - = 16 ;
}
while ( pread > = pbuffer )
{
pwrite [ 0 ] = last ;
pwrite [ - 1 ] . left = ( pread [ 0 ] . left + last . left ) > > 1 ;
pwrite [ - 1 ] . right = ( pread [ 0 ] . right + last . right ) > > 1 ;
last = pread [ 0 ] ;
pread - - ;
pwrite - = 2 ;
}
pbuffer [ 1 ] = last ;
pbuffer [ 0 ] . left = ( pfiltermem - > left + last . left ) > > 1 ;
pbuffer [ 0 ] . right = ( pfiltermem - > right + last . right ) > > 1 ;
* pfiltermem = pbuffer [ end ] ;
}
// upsample by 2x, optionally using interpolation
// count: how many samples to upsample. will become count*2 samples in buffer, in place.
// pbuffer: buffer to upsample into (in place)
// pfiltermem: filter memory. NOTE: this must match the filtertype ie: filterlinear[] for FILTERTYPE_LINEAR
// if NULL then perform no filtering.
// cfltmem: max number of sample pairs filter can use
// filtertype: FILTERTYPE_NONE, _LINEAR, _CUBIC etc. Must match prevfilter.
void S_MixBufferUpsample2x ( int count , portable_samplepair_t * pbuffer , portable_samplepair_t * pfiltermem , int cfltmem , int filtertype )
{
// JAY: Optimized this routine. Test then remove old routine.
// NOTE: Has been proven equivalent by comparing output.
if ( filtertype = = FILTERTYPE_LINEAR )
{
S_Interpolate2xLinear_2 ( count , pbuffer , pfiltermem , cfltmem ) ;
return ;
}
int i , j , upCount = count < < 1 ;
// reverse through buffer, duplicating contents for 'count' samples
for ( i = upCount - 1 , j = count - 1 ; j > = 0 ; i - = 2 , j - - )
{
pbuffer [ i ] = pbuffer [ j ] ;
pbuffer [ i - 1 ] = pbuffer [ j ] ;
}
// pass forward through buffer, interpolate all even slots
switch ( filtertype )
{
default :
break ;
case FILTERTYPE_LINEAR :
S_Interpolate2xLinear ( pbuffer , pfiltermem , cfltmem , count ) ;
break ;
case FILTERTYPE_CUBIC :
S_Interpolate2xCubic ( pbuffer , pfiltermem , cfltmem , count ) ;
break ;
}
}
//===============================================================================
// PAINTBUFFER ROUTINES
//===============================================================================
// Set current paintbuffer to pbuf.
// The set paintbuffer is used by all subsequent mixing, upsampling and dsp routines.
// Also sets the rear paintbuffer if paintbuffer has fsurround true.
// (otherwise, rearpaintbuffer is NULL)
void MIX_SetCurrentPaintbuffer ( int ipaintbuffer )
{
// set front and rear paintbuffer
Assert ( ipaintbuffer < g_paintBuffers . Count ( ) ) ;
g_curpaintbuffer = g_paintBuffers [ ipaintbuffer ] . pbuf ;
if ( g_paintBuffers [ ipaintbuffer ] . fsurround )
{
g_currearpaintbuffer = g_paintBuffers [ ipaintbuffer ] . pbufrear ;
g_curcenterpaintbuffer = NULL ;
if ( g_paintBuffers [ ipaintbuffer ] . fsurround_center )
g_curcenterpaintbuffer = g_paintBuffers [ ipaintbuffer ] . pbufcenter ;
}
else
{
g_currearpaintbuffer = NULL ;
g_curcenterpaintbuffer = NULL ;
}
Assert ( g_curpaintbuffer ! = NULL ) ;
}
// return index to current paintbuffer
int MIX_GetCurrentPaintbufferIndex ( void )
{
int i ;
for ( i = 0 ; i < g_paintBuffers . Count ( ) ; i + + )
{
if ( g_curpaintbuffer = = g_paintBuffers [ i ] . pbuf )
return i ;
}
return 0 ;
}
// return pointer to current paintbuffer struct
paintbuffer_t * MIX_GetCurrentPaintbufferPtr ( void )
{
int ipaint = MIX_GetCurrentPaintbufferIndex ( ) ;
Assert ( ipaint < g_paintBuffers . Count ( ) ) ;
return & g_paintBuffers [ ipaint ] ;
}
// return pointer to front paintbuffer pbuf, given index
inline portable_samplepair_t * MIX_GetPFrontFromIPaint ( int ipaintbuffer )
{
return g_paintBuffers [ ipaintbuffer ] . pbuf ;
}
paintbuffer_t * MIX_GetPPaintFromIPaint ( int ipaintbuffer )
{
Assert ( ipaintbuffer < g_paintBuffers . Count ( ) ) ;
return & g_paintBuffers [ ipaintbuffer ] ;
}
// return pointer to rear buffer, given index.
// returns null if fsurround is false;
inline portable_samplepair_t * MIX_GetPRearFromIPaint ( int ipaintbuffer )
{
if ( g_paintBuffers [ ipaintbuffer ] . fsurround )
return g_paintBuffers [ ipaintbuffer ] . pbufrear ;
return NULL ;
}
// return pointer to center buffer, given index.
// returns null if fsurround_center is false;
inline portable_samplepair_t * MIX_GetPCenterFromIPaint ( int ipaintbuffer )
{
if ( g_paintBuffers [ ipaintbuffer ] . fsurround_center )
return g_paintBuffers [ ipaintbuffer ] . pbufcenter ;
return NULL ;
}
// return index to paintbuffer, given buffer pointer
inline int MIX_GetIPaintFromPFront ( portable_samplepair_t * pbuf )
{
int i ;
for ( i = 0 ; i < g_paintBuffers . Count ( ) ; i + + )
{
if ( pbuf = = g_paintBuffers [ i ] . pbuf )
return i ;
}
return 0 ;
}
// return pointer to paintbuffer struct, given ptr to buffer data
inline paintbuffer_t * MIX_GetPPaintFromPFront ( portable_samplepair_t * pbuf )
{
int i ;
i = MIX_GetIPaintFromPFront ( pbuf ) ;
return & g_paintBuffers [ i ] ;
}
// up convert mono buffer to full surround
inline void MIX_ConvertBufferToSurround ( int ipaintbuffer )
{
paintbuffer_t * ppaint = & g_paintBuffers [ ipaintbuffer ] ;
// duplicate channel data as needed
if ( g_AudioDevice - > IsSurround ( ) )
{
// set buffer flags
ppaint - > fsurround = g_AudioDevice - > IsSurround ( ) ;
ppaint - > fsurround_center = g_AudioDevice - > IsSurroundCenter ( ) ;
portable_samplepair_t * pfront = MIX_GetPFrontFromIPaint ( ipaintbuffer ) ;
portable_samplepair_t * prear = MIX_GetPRearFromIPaint ( ipaintbuffer ) ;
portable_samplepair_t * pcenter = MIX_GetPCenterFromIPaint ( ipaintbuffer ) ;
// copy front to rear
Q_memcpy ( prear , pfront , sizeof ( portable_samplepair_t ) * PAINTBUFFER_SIZE ) ;
// copy front to center
if ( g_AudioDevice - > IsSurroundCenter ( ) )
Q_memcpy ( pcenter , pfront , sizeof ( portable_samplepair_t ) * PAINTBUFFER_SIZE ) ;
}
}
// Activate a paintbuffer. All active paintbuffers are mixed in parallel within
// MIX_MixChannelsToPaintbuffer, according to flags
inline void MIX_ActivatePaintbuffer ( int ipaintbuffer )
{
Assert ( ipaintbuffer < g_paintBuffers . Count ( ) ) ;
g_paintBuffers [ ipaintbuffer ] . factive = true ;
}
// Don't mix into this paintbuffer
inline void MIX_DeactivatePaintbuffer ( int ipaintbuffer )
{
Assert ( ipaintbuffer < g_paintBuffers . Count ( ) ) ;
g_paintBuffers [ ipaintbuffer ] . factive = false ;
}
// Don't mix into any paintbuffers
inline void MIX_DeactivateAllPaintbuffers ( void )
{
int i ;
for ( i = 0 ; i < g_paintBuffers . Count ( ) ; i + + )
g_paintBuffers [ i ] . factive = false ;
}
// set upsampling filter indexes back to 0
inline void MIX_ResetPaintbufferFilterCounters ( void )
{
int i ;
for ( i = 0 ; i < g_paintBuffers . Count ( ) ; i + + )
g_paintBuffers [ i ] . ifilter = 0 ;
}
inline void MIX_ResetPaintbufferFilterCounter ( int ipaintbuffer )
{
Assert ( ipaintbuffer < g_paintBuffers . Count ( ) ) ;
g_paintBuffers [ ipaintbuffer ] . ifilter = 0 ;
}
// Change paintbuffer's flags
inline void MIX_SetPaintbufferFlags ( int ipaintbuffer , int flags )
{
Assert ( ipaintbuffer < g_paintBuffers . Count ( ) ) ;
g_paintBuffers [ ipaintbuffer ] . flags = flags ;
}
// zero out all paintbuffers
void MIX_ClearAllPaintBuffers ( int SampleCount , bool clearFilters )
{
// g_paintBuffers can be NULL with -nosound
if ( g_paintBuffers . Count ( ) < = 0 )
{
return ;
}
int i ;
int count = min ( SampleCount , PAINTBUFFER_SIZE ) ;
// zero out all paintbuffer data (ignore sampleCount)
for ( i = 0 ; i < g_paintBuffers . Count ( ) ; i + + )
{
if ( g_paintBuffers [ i ] . pbuf ! = NULL )
Q_memset ( g_paintBuffers [ i ] . pbuf , 0 , ( count + 1 ) * sizeof ( portable_samplepair_t ) ) ;
if ( g_paintBuffers [ i ] . pbufrear ! = NULL )
Q_memset ( g_paintBuffers [ i ] . pbufrear , 0 , ( count + 1 ) * sizeof ( portable_samplepair_t ) ) ;
if ( g_paintBuffers [ i ] . pbufcenter ! = NULL )
Q_memset ( g_paintBuffers [ i ] . pbufcenter , 0 , ( count + 1 ) * sizeof ( portable_samplepair_t ) ) ;
if ( clearFilters )
{
Q_memset ( g_paintBuffers [ i ] . fltmem , 0 , sizeof ( g_paintBuffers [ i ] . fltmem ) ) ;
Q_memset ( g_paintBuffers [ i ] . fltmemrear , 0 , sizeof ( g_paintBuffers [ i ] . fltmemrear ) ) ;
Q_memset ( g_paintBuffers [ i ] . fltmemcenter , 0 , sizeof ( g_paintBuffers [ i ] . fltmemcenter ) ) ;
}
}
if ( clearFilters )
{
MIX_ResetPaintbufferFilterCounters ( ) ;
}
}
# define SWAP(a,b,t) {(t) = (a); (a) = (b); (b) = (t);}
# define AVG(a,b) (((a) + (b)) >> 1 )
# define AVG4(a,b,c,d) (((a) + (b) + (c) + (d)) >> 2 )
// Synthesize center channel from left/right values (average).
// Currently just averages, but could actually remove
// the center signal from the l/r channels...
inline void MIX_CenterFromLeftRight ( int * pl , int * pr , int * pc )
{
int l = * pl ;
int r = * pr ;
int c = 0 ;
c = ( l + r ) / 2 ;
/*
l = l - c / 2 ;
r = r - c / 2 ;
if ( l < 0 )
{
l = 0 ;
r + = ( - l ) ;
c + = ( - l ) ;
}
else if ( r < 0 )
{
r = 0 ;
l + = ( - r ) ;
c + = ( - r ) ;
}
*/
* pc = c ;
// *pl = l;
// *pr = r;
}
// mixes pbuf1 + pbuf2 into pbuf3, count samples
// fgain is output gain 0-1.0
// NOTE: pbuf3 may equal pbuf1 or pbuf2!
// mixing algorithms:
// destination 2ch:
// pb1 2ch + pb2 2ch -> pb3 2ch
// pb1 (4ch->2ch) + pb2 2ch -> pb3 2ch
// pb1 2ch + pb2 (4ch->2ch) -> pb3 2ch
// pb1 (4ch->2ch) + pb2 (4ch->2ch) -> pb3 2ch
// destination 4ch:
// pb1 4ch + pb2 4ch -> pb3 4ch
// pb1 (2ch->4ch) + pb2 4ch -> pb3 4ch
// pb1 4ch + pb2 (2ch->4ch) -> pb3 4ch
// pb1 (2ch->4ch) + pb2 (2ch->4ch) -> pb3 4ch
// if all buffers are 4 or 5 ch surround, mix rear & center channels into ibuf3 as well.
// NOTE: for performance, conversion and mixing are done in a single pass instead of
// a two pass channel convert + mix scheme.
void MIX_MixPaintbuffers ( int ibuf1 , int ibuf2 , int ibuf3 , int count , float fgain_out )
{
VPROF ( " Mixpaintbuffers " ) ;
int i ;
portable_samplepair_t * pbuf1 , * pbuf2 , * pbuf3 , * pbuft ;
portable_samplepair_t * pbufrear1 , * pbufrear2 , * pbufrear3 , * pbufreart ;
portable_samplepair_t * pbufcenter1 , * pbufcenter2 , * pbufcenter3 , * pbufcentert ;
int cchan1 , cchan2 , cchan3 , cchant ;
int xl , xr ;
int l , r , l2 , r2 , c , c2 ;
int gain_out ;
gain_out = 256 * fgain_out ;
Assert ( count < = PAINTBUFFER_SIZE ) ;
Assert ( ibuf1 < g_paintBuffers . Count ( ) ) ;
Assert ( ibuf2 < g_paintBuffers . Count ( ) ) ;
Assert ( ibuf3 < g_paintBuffers . Count ( ) ) ;
pbuf1 = g_paintBuffers [ ibuf1 ] . pbuf ;
pbuf2 = g_paintBuffers [ ibuf2 ] . pbuf ;
pbuf3 = g_paintBuffers [ ibuf3 ] . pbuf ;
pbufrear1 = g_paintBuffers [ ibuf1 ] . pbufrear ;
pbufrear2 = g_paintBuffers [ ibuf2 ] . pbufrear ;
pbufrear3 = g_paintBuffers [ ibuf3 ] . pbufrear ;
pbufcenter1 = g_paintBuffers [ ibuf1 ] . pbufcenter ;
pbufcenter2 = g_paintBuffers [ ibuf2 ] . pbufcenter ;
pbufcenter3 = g_paintBuffers [ ibuf3 ] . pbufcenter ;
cchan1 = 2 + ( g_paintBuffers [ ibuf1 ] . fsurround ? 2 : 0 ) + ( g_paintBuffers [ ibuf1 ] . fsurround_center ? 1 : 0 ) ;
cchan2 = 2 + ( g_paintBuffers [ ibuf2 ] . fsurround ? 2 : 0 ) + ( g_paintBuffers [ ibuf2 ] . fsurround_center ? 1 : 0 ) ;
cchan3 = 2 + ( g_paintBuffers [ ibuf3 ] . fsurround ? 2 : 0 ) + ( g_paintBuffers [ ibuf3 ] . fsurround_center ? 1 : 0 ) ;
// make sure pbuf1 always has fewer or equal channels than pbuf2
// NOTE: pbuf3 may equal pbuf1 or pbuf2!
if ( cchan2 < cchan1 )
{
SWAP ( cchan1 , cchan2 , cchant ) ;
SWAP ( pbuf1 , pbuf2 , pbuft ) ;
SWAP ( pbufrear1 , pbufrear2 , pbufreart ) ;
SWAP ( pbufcenter1 , pbufcenter2 , pbufcentert ) ;
}
// UNDONE: implement fast mixing routines for each of the following sections
// destination buffer stereo - average n chans down to stereo
if ( cchan3 = = 2 )
{
// destination 2ch:
// pb1 2ch + pb2 2ch -> pb3 2ch
// pb1 2ch + pb2 (4ch->2ch) -> pb3 2ch
// pb1 (4ch->2ch) + pb2 (4ch->2ch) -> pb3 2ch
if ( cchan1 = = 2 & & cchan2 = = 2 )
{
// mix front channels
for ( i = 0 ; i < count ; i + + )
{
pbuf3 [ i ] . left = pbuf1 [ i ] . left + pbuf2 [ i ] . left ;
pbuf3 [ i ] . right = pbuf1 [ i ] . right + pbuf2 [ i ] . right ;
}
goto gain2ch ;
}
if ( cchan1 = = 2 & & cchan2 = = 4 )
{
// avg rear chan l/r
for ( i = 0 ; i < count ; i + + )
{
pbuf3 [ i ] . left = pbuf1 [ i ] . left + AVG ( pbuf2 [ i ] . left , pbufrear2 [ i ] . left ) ;
pbuf3 [ i ] . right = pbuf1 [ i ] . right + AVG ( pbuf2 [ i ] . right , pbufrear2 [ i ] . right ) ;
}
goto gain2ch ;
}
if ( cchan1 = = 4 & & cchan2 = = 4 )
{
// avg rear chan l/r
for ( i = 0 ; i < count ; i + + )
{
pbuf3 [ i ] . left = AVG ( pbuf1 [ i ] . left , pbufrear1 [ i ] . left ) + AVG ( pbuf2 [ i ] . left , pbufrear2 [ i ] . left ) ;
pbuf3 [ i ] . right = AVG ( pbuf1 [ i ] . right , pbufrear1 [ i ] . right ) + AVG ( pbuf2 [ i ] . right , pbufrear2 [ i ] . right ) ;
}
goto gain2ch ;
}
if ( cchan1 = = 2 & & cchan2 = = 5 )
{
// avg rear chan l/r + center split into left/right
for ( i = 0 ; i < count ; i + + )
{
l = pbuf2 [ i ] . left + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
r = pbuf2 [ i ] . right + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
pbuf3 [ i ] . left = pbuf1 [ i ] . left + AVG ( l , pbufrear2 [ i ] . left ) ;
pbuf3 [ i ] . right = pbuf1 [ i ] . right + AVG ( r , pbufrear2 [ i ] . right ) ;
}
goto gain2ch ;
}
if ( cchan1 = = 4 & & cchan2 = = 5 )
{
for ( i = 0 ; i < count ; i + + )
{
l = pbuf2 [ i ] . left + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
r = pbuf2 [ i ] . right + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
pbuf3 [ i ] . left = AVG ( pbuf1 [ i ] . left , pbufrear1 [ i ] . left ) + AVG ( l , pbufrear2 [ i ] . left ) ;
pbuf3 [ i ] . right = AVG ( pbuf1 [ i ] . right , pbufrear1 [ i ] . right ) + AVG ( r , pbufrear2 [ i ] . right ) ;
}
goto gain2ch ;
}
if ( cchan1 = = 5 & & cchan2 = = 5 )
{
for ( i = 0 ; i < count ; i + + )
{
l = pbuf1 [ i ] . left + ( ( pbufcenter1 [ i ] . left ) > > 1 ) ;
r = pbuf1 [ i ] . right + ( ( pbufcenter1 [ i ] . left ) > > 1 ) ;
l2 = pbuf2 [ i ] . left + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
r2 = pbuf2 [ i ] . right + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
pbuf3 [ i ] . left = AVG ( l , pbufrear1 [ i ] . left ) + AVG ( l2 , pbufrear2 [ i ] . left ) ;
pbuf3 [ i ] . right = AVG ( r , pbufrear1 [ i ] . right ) + AVG ( r2 , pbufrear2 [ i ] . right ) ;
} goto gain2ch ;
}
}
// destination buffer quad - duplicate n chans up to quad
if ( cchan3 = = 4 )
{
// pb1 4ch + pb2 4ch -> pb3 4ch
// pb1 (2ch->4ch) + pb2 4ch -> pb3 4ch
// pb1 (2ch->4ch) + pb2 (2ch->4ch) -> pb3 4ch
if ( cchan1 = = 4 & & cchan2 = = 4 )
{
// mix front -> front, rear -> rear
for ( i = 0 ; i < count ; i + + )
{
pbuf3 [ i ] . left = pbuf1 [ i ] . left + pbuf2 [ i ] . left ;
pbuf3 [ i ] . right = pbuf1 [ i ] . right + pbuf2 [ i ] . right ;
pbufrear3 [ i ] . left = pbufrear1 [ i ] . left + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = pbufrear1 [ i ] . right + pbufrear2 [ i ] . right ;
}
goto gain4ch ;
}
if ( cchan1 = = 2 & & cchan2 = = 4 )
{
for ( i = 0 ; i < count ; i + + )
{
// split 2 ch left -> front left, rear left
// split 2 ch right -> front right, rear right
xl = pbuf1 [ i ] . left ;
xr = pbuf1 [ i ] . right ;
pbuf3 [ i ] . left = xl + pbuf2 [ i ] . left ;
pbuf3 [ i ] . right = xr + pbuf2 [ i ] . right ;
pbufrear3 [ i ] . left = xl + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = xr + pbufrear2 [ i ] . right ;
}
goto gain4ch ;
}
if ( cchan1 = = 2 & & cchan2 = = 2 )
{
// mix l,r, split into front l, front r
for ( i = 0 ; i < count ; i + + )
{
xl = pbuf1 [ i ] . left + pbuf2 [ i ] . left ;
xr = pbuf1 [ i ] . right + pbuf2 [ i ] . right ;
pbufrear3 [ i ] . left = pbuf3 [ i ] . left = xl ;
pbufrear3 [ i ] . right = pbuf3 [ i ] . right = xr ;
}
goto gain4ch ;
}
if ( cchan1 = = 2 & & cchan2 = = 5 )
{
for ( i = 0 ; i < count ; i + + )
{
// split center of chan2 into left/right
l2 = pbuf2 [ i ] . left + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
r2 = pbuf2 [ i ] . right + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
xl = pbuf1 [ i ] . left ;
xr = pbuf1 [ i ] . right ;
pbuf3 [ i ] . left = xl + l2 ;
pbuf3 [ i ] . right = xr + r2 ;
pbufrear3 [ i ] . left = xl + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = xr + pbufrear2 [ i ] . right ;
}
goto gain4ch ;
}
if ( cchan1 = = 4 & & cchan2 = = 5 )
{
for ( i = 0 ; i < count ; i + + )
{
l2 = pbuf2 [ i ] . left + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
r2 = pbuf2 [ i ] . right + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
pbuf3 [ i ] . left = pbuf1 [ i ] . left + l2 ;
pbuf3 [ i ] . right = pbuf1 [ i ] . right + r2 ;
pbufrear3 [ i ] . left = pbufrear1 [ i ] . left + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = pbufrear1 [ i ] . right + pbufrear2 [ i ] . right ;
}
goto gain4ch ;
}
if ( cchan1 = = 5 & & cchan2 = = 5 )
{
for ( i = 0 ; i < count ; i + + )
{
l = pbuf1 [ i ] . left + ( ( pbufcenter1 [ i ] . left ) > > 1 ) ;
r = pbuf1 [ i ] . right + ( ( pbufcenter1 [ i ] . left ) > > 1 ) ;
l2 = pbuf2 [ i ] . left + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
r2 = pbuf2 [ i ] . right + ( ( pbufcenter2 [ i ] . left ) > > 1 ) ;
pbuf3 [ i ] . left = l + l2 ;
pbuf3 [ i ] . right = r + r2 ;
pbufrear3 [ i ] . left = pbufrear1 [ i ] . left + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = pbufrear1 [ i ] . right + pbufrear2 [ i ] . right ;
}
goto gain4ch ;
}
}
// 5 channel destination
if ( cchan3 = = 5 )
{
// up convert from 2 or 4 ch buffer to 5 ch buffer:
// center channel is synthesized from front left, front right
if ( cchan1 = = 2 & & cchan2 = = 2 )
{
for ( i = 0 ; i < count ; i + + )
{
// split 2 ch left -> front left, center, rear left
// split 2 ch right -> front right, center, rear right
l = pbuf1 [ i ] . left ;
r = pbuf1 [ i ] . right ;
MIX_CenterFromLeftRight ( & l , & r , & c ) ;
l2 = pbuf2 [ i ] . left ;
r2 = pbuf2 [ i ] . right ;
MIX_CenterFromLeftRight ( & l2 , & r2 , & c2 ) ;
pbuf3 [ i ] . left = l + l2 ;
pbuf3 [ i ] . right = r + r2 ;
pbufrear3 [ i ] . left = pbuf1 [ i ] . left + pbuf2 [ i ] . left ;
pbufrear3 [ i ] . right = pbuf1 [ i ] . right + pbuf2 [ i ] . right ;
pbufcenter3 [ i ] . left = c + c2 ;
}
goto gain5ch ;
}
if ( cchan1 = = 2 & & cchan2 = = 4 )
{
for ( i = 0 ; i < count ; i + + )
{
l = pbuf1 [ i ] . left ;
r = pbuf1 [ i ] . right ;
MIX_CenterFromLeftRight ( & l , & r , & c ) ;
l2 = pbuf2 [ i ] . left ;
r2 = pbuf2 [ i ] . right ;
MIX_CenterFromLeftRight ( & l2 , & r2 , & c2 ) ;
pbuf3 [ i ] . left = l + l2 ;
pbuf3 [ i ] . right = r + r2 ;
pbufrear3 [ i ] . left = pbuf1 [ i ] . left + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = pbuf1 [ i ] . right + pbufrear2 [ i ] . right ;
pbufcenter3 [ i ] . left = c + c2 ;
}
goto gain5ch ;
}
if ( cchan1 = = 2 & & cchan2 = = 5 )
{
for ( i = 0 ; i < count ; i + + )
{
l = pbuf1 [ i ] . left ;
r = pbuf1 [ i ] . right ;
MIX_CenterFromLeftRight ( & l , & r , & c ) ;
pbuf3 [ i ] . left = l + pbuf2 [ i ] . left ;
pbuf3 [ i ] . right = r + pbuf2 [ i ] . right ;
pbufrear3 [ i ] . left = pbuf1 [ i ] . left + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = pbuf1 [ i ] . right + pbufrear2 [ i ] . right ;
pbufcenter3 [ i ] . left = c + pbufcenter2 [ i ] . left ;
}
goto gain5ch ;
}
if ( cchan1 = = 4 & & cchan2 = = 4 )
{
for ( i = 0 ; i < count ; i + + )
{
l = pbuf1 [ i ] . left ;
r = pbuf1 [ i ] . right ;
MIX_CenterFromLeftRight ( & l , & r , & c ) ;
l2 = pbuf2 [ i ] . left ;
r2 = pbuf2 [ i ] . right ;
MIX_CenterFromLeftRight ( & l2 , & r2 , & c2 ) ;
pbuf3 [ i ] . left = l + l2 ;
pbuf3 [ i ] . right = r + r2 ;
pbufrear3 [ i ] . left = pbufrear1 [ i ] . left + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = pbufrear1 [ i ] . right + pbufrear2 [ i ] . right ;
pbufcenter3 [ i ] . left = c + c2 ;
}
goto gain5ch ;
}
if ( cchan1 = = 4 & & cchan2 = = 5 )
{
for ( i = 0 ; i < count ; i + + )
{
l = pbuf1 [ i ] . left ;
r = pbuf1 [ i ] . right ;
MIX_CenterFromLeftRight ( & l , & r , & c ) ;
pbuf3 [ i ] . left = l + pbuf2 [ i ] . left ;
pbuf3 [ i ] . right = r + pbuf2 [ i ] . right ;
pbufrear3 [ i ] . left = pbufrear1 [ i ] . left + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = pbufrear1 [ i ] . right + pbufrear2 [ i ] . right ;
pbufcenter3 [ i ] . left = c + pbufcenter2 [ i ] . left ;
}
goto gain5ch ;
}
if ( cchan2 = = 5 & & cchan1 = = 5 )
{
for ( i = 0 ; i < count ; i + + )
{
pbuf3 [ i ] . left = pbuf1 [ i ] . left + pbuf2 [ i ] . left ;
pbuf3 [ i ] . right = pbuf1 [ i ] . right + pbuf2 [ i ] . right ;
pbufrear3 [ i ] . left = pbufrear1 [ i ] . left + pbufrear2 [ i ] . left ;
pbufrear3 [ i ] . right = pbufrear1 [ i ] . right + pbufrear2 [ i ] . right ;
pbufcenter3 [ i ] . left = pbufcenter1 [ i ] . left + pbufcenter2 [ i ] . left ;
}
goto gain5ch ;
}
}
gain2ch :
if ( gain_out = = 256 ) // KDB: perf
return ;
for ( i = 0 ; i < count ; i + + )
{
pbuf3 [ i ] . left = ( pbuf3 [ i ] . left * gain_out ) > > 8 ;
pbuf3 [ i ] . right = ( pbuf3 [ i ] . right * gain_out ) > > 8 ;
}
return ;
gain4ch :
if ( gain_out = = 256 ) // KDB: perf
return ;
for ( i = 0 ; i < count ; i + + )
{
pbuf3 [ i ] . left = ( pbuf3 [ i ] . left * gain_out ) > > 8 ;
pbuf3 [ i ] . right = ( pbuf3 [ i ] . right * gain_out ) > > 8 ;
pbufrear3 [ i ] . left = ( pbufrear3 [ i ] . left * gain_out ) > > 8 ;
pbufrear3 [ i ] . right = ( pbufrear3 [ i ] . right * gain_out ) > > 8 ;
}
return ;
gain5ch :
if ( gain_out = = 256 ) // KDB: perf
return ;
for ( i = 0 ; i < count ; i + + )
{
pbuf3 [ i ] . left = ( pbuf3 [ i ] . left * gain_out ) > > 8 ;
pbuf3 [ i ] . right = ( pbuf3 [ i ] . right * gain_out ) > > 8 ;
pbufrear3 [ i ] . left = ( pbufrear3 [ i ] . left * gain_out ) > > 8 ;
pbufrear3 [ i ] . right = ( pbufrear3 [ i ] . right * gain_out ) > > 8 ;
pbufcenter3 [ i ] . left = ( pbufcenter3 [ i ] . left * gain_out ) > > 8 ;
}
return ;
}
// multiply all values in paintbuffer by fgain
void MIX_ScalePaintBuffer ( int bufferIndex , int count , float fgain )
{
portable_samplepair_t * pbuf = g_paintBuffers [ bufferIndex ] . pbuf ;
portable_samplepair_t * pbufrear = g_paintBuffers [ bufferIndex ] . pbufrear ;
portable_samplepair_t * pbufcenter = g_paintBuffers [ bufferIndex ] . pbufcenter ;
int gain = 256 * fgain ;
int i ;
if ( gain = = 256 )
return ;
if ( ! g_paintBuffers [ bufferIndex ] . fsurround )
{
for ( i = 0 ; i < count ; i + + )
{
pbuf [ i ] . left = ( pbuf [ i ] . left * gain ) > > 8 ;
pbuf [ i ] . right = ( pbuf [ i ] . right * gain ) > > 8 ;
}
}
else
{
for ( i = 0 ; i < count ; i + + )
{
pbuf [ i ] . left = ( pbuf [ i ] . left * gain ) > > 8 ;
pbuf [ i ] . right = ( pbuf [ i ] . right * gain ) > > 8 ;
pbufrear [ i ] . left = ( pbufrear [ i ] . left * gain ) > > 8 ;
pbufrear [ i ] . right = ( pbufrear [ i ] . right * gain ) > > 8 ;
}
if ( g_paintBuffers [ bufferIndex ] . fsurround_center )
{
for ( i = 0 ; i < count ; i + + )
{
pbufcenter [ i ] . left = ( pbufcenter [ i ] . left * gain ) > > 8 ;
// pbufcenter[i].right = (pbufcenter[i].right * gain) >> 8; mono center channel
}
}
}
}
// DEBUG peak detection values
# define _SDEBUG 1
# ifdef _SDEBUG
float sdebug_avg_in = 0.0 ;
float sdebug_in_count = 0.0 ;
float sdebug_avg_out = 0.0 ;
float sdebug_out_count = 0.0 ;
# define SDEBUG_TOTAL_COUNT (3*44100)
# endif // DEBUG
// DEBUG code - get and show peak value of specified paintbuffer
// DEBUG code - ibuf is buffer index, count is # samples to test, pppeakprev stores peak
void SDEBUG_GetAvgValue ( int ibuf , int count , float * pav )
{
# ifdef _SDEBUG
if ( snd_showstart . GetInt ( ) ! = 4 )
return ;
float av = 0.0 ;
for ( int i = 0 ; i < count ; i + + )
av + = ( float ) ( abs ( g_paintBuffers [ ibuf ] . pbuf - > left ) + abs ( g_paintBuffers [ ibuf ] . pbuf - > right ) ) / 2.0 ;
* pav = av / count ;
# endif // DEBUG
}
void SDEBUG_GetAvgIn ( int ibuf , int count )
{
float av = 0.0 ;
SDEBUG_GetAvgValue ( ibuf , count , & av ) ;
sdebug_avg_in = ( ( av * count ) + ( sdebug_avg_in * sdebug_in_count ) ) / ( count + sdebug_in_count ) ;
sdebug_in_count + = count ;
}
void SDEBUG_GetAvgOut ( int ibuf , int count )
{
float av = 0.0 ;
SDEBUG_GetAvgValue ( ibuf , count , & av ) ;
sdebug_avg_out = ( ( av * count ) + ( sdebug_avg_out * sdebug_out_count ) ) / ( count + sdebug_out_count ) ;
sdebug_out_count + = count ;
}
void SDEBUG_ShowAvgValue ( )
{
# ifdef _SDEBUG
if ( sdebug_in_count > SDEBUG_TOTAL_COUNT )
{
if ( ( int ) sdebug_avg_in > 20.0 & & ( int ) sdebug_avg_out > 20.0 )
DevMsg ( " dsp avg gain:%1.2f in:%1.2f out:%1.2f 1/gain:%1.2f \n " , sdebug_avg_out / sdebug_avg_in , sdebug_avg_in , sdebug_avg_out , sdebug_avg_in / sdebug_avg_out ) ;
sdebug_avg_in = 0.0 ;
sdebug_avg_out = 0.0 ;
sdebug_in_count = 0.0 ;
sdebug_out_count = 0.0 ;
}
# endif // DEBUG
}
// clip all values in paintbuffer to 16bit.
// if fsurround is set for paintbuffer, also process rear buffer samples
void MIX_CompressPaintbuffer ( int ipaint , int count )
{
VPROF ( " CompressPaintbuffer " ) ;
int i ;
paintbuffer_t * ppaint = MIX_GetPPaintFromIPaint ( ipaint ) ;
portable_samplepair_t * pbf ;
portable_samplepair_t * pbr ;
portable_samplepair_t * pbc ;
pbf = ppaint - > pbuf ;
pbr = ppaint - > pbufrear ;
pbc = ppaint - > pbufcenter ;
for ( i = 0 ; i < count ; i + + )
{
pbf - > left = CLIP ( pbf - > left ) ;
pbf - > right = CLIP ( pbf - > right ) ;
pbf + + ;
}
if ( ppaint - > fsurround )
{
Assert ( pbr ) ;
for ( i = 0 ; i < count ; i + + )
{
pbr - > left = CLIP ( pbr - > left ) ;
pbr - > right = CLIP ( pbr - > right ) ;
pbr + + ;
}
}
if ( ppaint - > fsurround_center )
{
Assert ( pbc ) ;
for ( i = 0 ; i < count ; i + + )
{
pbc - > left = CLIP ( pbc - > left ) ;
//pbc->right = CLIP(pbc->right); mono center channel
pbc + + ;
}
}
}
// mix and upsample channels to 44khz 'ipaintbuffer'
// mix channels matching 'flags' (SOUND_MIX_DRY, SOUND_MIX_WET, SOUND_MIX_SPEAKER) into specified paintbuffer
// upsamples 11khz, 22khz channels to 44khz.
// NOTE: only call this on channels that will be mixed into only 1 paintbuffer
// and that will not be mixed until the next mix pass! otherwise, MIX_MixChannelsToPaintbuffer
// will advance any internal pointers on mixed channels; subsequent calls will be at
// incorrect offset.
void MIX_MixUpsampleBuffer ( CChannelList & list , int ipaintbuffer , int end , int count , int flags )
{
VPROF ( " MixUpsampleBuffer " ) ;
int ipaintcur = MIX_GetCurrentPaintbufferIndex ( ) ; // save current paintbuffer
// reset paintbuffer upsampling filter index
MIX_ResetPaintbufferFilterCounter ( ipaintbuffer ) ;
// prevent other paintbuffers from being mixed
MIX_DeactivateAllPaintbuffers ( ) ;
MIX_ActivatePaintbuffer ( ipaintbuffer ) ; // operates on MIX_MixChannelsToPaintbuffer
MIX_SetCurrentPaintbuffer ( ipaintbuffer ) ; // operates on MixUpSample
// mix 11khz channels to buffer
if ( list . m_has11kChannels )
{
MIX_MixChannelsToPaintbuffer ( list , end , flags , SOUND_11k , SOUND_11k ) ;
// upsample 11khz buffer by 2x
g_AudioDevice - > MixUpsample ( count / ( SOUND_DMA_SPEED / SOUND_11k ) , FILTERTYPE_LINEAR ) ;
}
if ( list . m_has22kChannels | | list . m_has11kChannels )
{
// mix 22khz channels to buffer
MIX_MixChannelsToPaintbuffer ( list , end , flags , SOUND_22k , SOUND_22k ) ;
# if (SOUND_DMA_SPEED > SOUND_22k)
// upsample 22khz buffer by 2x
g_AudioDevice - > MixUpsample ( count / ( SOUND_DMA_SPEED / SOUND_22k ) , FILTERTYPE_LINEAR ) ;
# endif
}
// mix 44khz channels to buffer
MIX_MixChannelsToPaintbuffer ( list , end , flags , SOUND_44k , SOUND_DMA_SPEED ) ;
MIX_DeactivateAllPaintbuffers ( ) ;
// restore previous paintbuffer
MIX_SetCurrentPaintbuffer ( ipaintcur ) ;
}
// upsample and mix sounds into final 44khz versions of the following paintbuffers:
// SOUND_BUFFER_ROOM, SOUND_BUFFER_FACING, IFACINGAWAY, SOUND_BUFFER_DRY, SOUND_BUFFER_SPEAKER, SOUND_BUFFER_SPECIALs
// dsp fx are then applied to these buffers by the caller.
// caller also remixes all into final SOUND_BUFFER_PAINT output.
void MIX_UpsampleAllPaintbuffers ( CChannelList & list , int end , int count )
{
VPROF ( " MixUpsampleAll " ) ;
// 'dry' and 'speaker' channel sounds mix 100% into their corresponding buffers
// mix and upsample all 'dry' sounds (channels) to 44khz SOUND_BUFFER_DRY paintbuffer
if ( list . m_hasDryChannels )
MIX_MixUpsampleBuffer ( list , SOUND_BUFFER_DRY , end , count , SOUND_MIX_DRY ) ;
// mix and upsample all 'speaker' sounds (channels) to 44khz SOUND_BUFFER_SPEAKER paintbuffer
if ( list . m_hasSpeakerChannels )
MIX_MixUpsampleBuffer ( list , SOUND_BUFFER_SPEAKER , end , count , SOUND_MIX_SPEAKER ) ;
// mix and upsample all 'special dsp' sounds (channels) to 44khz SOUND_BUFFER_SPECIALs paintbuffer
for ( int iDSP = 0 ; iDSP < list . m_nSpecialDSPs . Count ( ) ; + + iDSP )
{
for ( int i = SOUND_BUFFER_SPECIAL_START ; i < g_paintBuffers . Count ( ) ; + + i )
{
paintbuffer_t * pSpecialBuffer = MIX_GetPPaintFromIPaint ( i ) ;
if ( pSpecialBuffer - > nSpecialDSP = = list . m_nSpecialDSPs [ iDSP ] & & pSpecialBuffer - > idsp_specialdsp ! = - 1 )
{
MIX_MixUpsampleBuffer ( list , i , end , count , SOUND_MIX_SPECIAL_DSP ) ;
break ;
}
}
}
// 'room', 'facing' 'facingaway' sounds are mixed into up to 3 buffers:
// 11khz sounds are mixed into 3 buffers based on distance from listener, and facing direction
// These buffers are room, facing, facingaway
// These 3 mixed buffers are then each upsampled to 22khz.
// 22khz sounds are mixed into the 3 buffers based on distance from listener, and facing direction
// These 3 mixed buffers are then each upsampled to 44khz.
// 44khz sounds are mixed into the 3 buffers based on distance from listener, and facing direction
MIX_DeactivateAllPaintbuffers ( ) ;
// set paintbuffer upsample filter indices to 0
MIX_ResetPaintbufferFilterCounters ( ) ;
if ( ! g_bDspOff )
{
// only mix to roombuffer if dsp fx are on KDB: perf
MIX_ActivatePaintbuffer ( SOUND_BUFFER_ROOM ) ; // operates on MIX_MixChannelsToPaintbuffer
}
MIX_ActivatePaintbuffer ( SOUND_BUFFER_FACING ) ;
if ( g_bdirectionalfx )
{
// mix to facing away buffer only if directional presets are set
MIX_ActivatePaintbuffer ( SOUND_BUFFER_FACINGAWAY ) ;
}
// mix 11khz sounds:
// pan sounds between 3 busses: facing, facingaway and room buffers
MIX_MixChannelsToPaintbuffer ( list , end , SOUND_MIX_WET , SOUND_11k , SOUND_11k ) ;
// upsample all 11khz buffers by 2x
if ( ! g_bDspOff )
{
// only upsample roombuffer if dsp fx are on KDB: perf
MIX_SetCurrentPaintbuffer ( SOUND_BUFFER_ROOM ) ; // operates on MixUpSample
g_AudioDevice - > MixUpsample ( count / ( SOUND_DMA_SPEED / SOUND_11k ) , FILTERTYPE_LINEAR ) ;
}
MIX_SetCurrentPaintbuffer ( SOUND_BUFFER_FACING ) ;
g_AudioDevice - > MixUpsample ( count / ( SOUND_DMA_SPEED / SOUND_11k ) , FILTERTYPE_LINEAR ) ;
if ( g_bdirectionalfx )
{
MIX_SetCurrentPaintbuffer ( SOUND_BUFFER_FACINGAWAY ) ;
g_AudioDevice - > MixUpsample ( count / ( SOUND_DMA_SPEED / SOUND_11k ) , FILTERTYPE_LINEAR ) ;
}
// mix 22khz sounds:
// pan sounds between 3 busses: facing, facingaway and room buffers
MIX_MixChannelsToPaintbuffer ( list , end , SOUND_MIX_WET , SOUND_22k , SOUND_22k ) ;
// upsample all 22khz buffers by 2x
# if ( SOUND_DMA_SPEED > SOUND_22k )
if ( ! g_bDspOff )
{
// only upsample roombuffer if dsp fx are on KDB: perf
MIX_SetCurrentPaintbuffer ( SOUND_BUFFER_ROOM ) ;
g_AudioDevice - > MixUpsample ( count / ( SOUND_DMA_SPEED / SOUND_22k ) , FILTERTYPE_LINEAR ) ;
}
MIX_SetCurrentPaintbuffer ( SOUND_BUFFER_FACING ) ;
g_AudioDevice - > MixUpsample ( count / ( SOUND_DMA_SPEED / SOUND_22k ) , FILTERTYPE_LINEAR ) ;
if ( g_bdirectionalfx )
{
MIX_SetCurrentPaintbuffer ( SOUND_BUFFER_FACINGAWAY ) ;
g_AudioDevice - > MixUpsample ( count / ( SOUND_DMA_SPEED / SOUND_22k ) , FILTERTYPE_LINEAR ) ;
}
# endif
// mix all 44khz sounds to all active paintbuffers
MIX_MixChannelsToPaintbuffer ( list , end , SOUND_MIX_WET , SOUND_44k , SOUND_DMA_SPEED ) ;
MIX_DeactivateAllPaintbuffers ( ) ;
MIX_SetCurrentPaintbuffer ( SOUND_BUFFER_PAINT ) ;
}
ConVar snd_cull_duplicates ( " snd_cull_duplicates " , " 0 " , FCVAR_ALLOWED_IN_COMPETITIVE , " If nonzero, aggressively cull duplicate sounds during mixing. The number specifies the number of duplicates allowed to be played. " ) ;
// Helper class for determining whether a given channel number should be culled from
// mixing, if snd_cull_duplicates is enabled (psychoacoustic quashing).
class CChannelCullList
{
public :
// default constructor
CChannelCullList ( ) : m_numChans ( 0 ) { } ;
// call if you plan on culling channels - and not otherwise, it's a little expensive
// (that's why it's not in the constructor)
void Initialize ( CChannelList & list ) ;
// returns true if a given channel number has been marked for culling
inline bool ShouldCull ( int channelNum )
{
return ( m_numChans > channelNum ) ? m_bShouldCull [ channelNum ] : false ;
}
// an array of sound names and their volumes
// TODO: there may be a way to do this faster on 360 (eg, pad to 128bit, use SIMD)
struct sChannelVolData
{
int m_channelNum ;
int m_vol ; // max volume of sound. -1 means "do not cull, ever, do not even do the math"
uintp m_nameHash ; // a unique id for a sound file
} ;
protected :
sChannelVolData m_channelInfo [ MAX_CHANNELS ] ;
bool m_bShouldCull [ MAX_CHANNELS ] ; // in ChannelList order, not sorted order
int m_numChans ;
} ;
// comparator for qsort as used below (eg a lambda)
// returns < 0 if a should come before b, > 0 if a should come after, 0 otherwise
static int __cdecl ChannelVolComparator ( const void * a , const void * b )
{
// greater numbers come first.
return static_cast < const CChannelCullList : : sChannelVolData * > ( b ) - > m_vol - static_cast < const CChannelCullList : : sChannelVolData * > ( a ) - > m_vol ;
}
void CChannelCullList : : Initialize ( CChannelList & list )
{
VPROF ( " CChannelCullList::Initialize " ) ;
// First, build a sorted list of channels by decreasing volume, and by a hash of their wavname.
m_numChans = list . Count ( ) ;
for ( int i = m_numChans - 1 ; i > = 0 ; - - i )
{
channel_t * ch = list . GetChannel ( i ) ;
m_channelInfo [ i ] . m_channelNum = i ;
if ( ch & & ch - > pMixer - > IsReadyToMix ( ) )
{
m_channelInfo [ i ] . m_vol = ChannelLoudestCurVolume ( ch ) ;
AssertMsg ( m_channelInfo [ i ] . m_vol > = 0 , " Sound channel has a negative volume? " ) ;
m_channelInfo [ i ] . m_nameHash = ( uintp ) ch - > sfx ;
}
else
{
m_channelInfo [ i ] . m_vol = - 1 ;
m_channelInfo [ i ] . m_nameHash = NULL ; // doesn't matter
}
}
// set the unused channels to invalid data
for ( int i = m_numChans ; i < MAX_CHANNELS ; + + i )
{
m_channelInfo [ i ] . m_channelNum = - 1 ;
m_channelInfo [ i ] . m_vol = - 1 ;
}
// Sort the list.
qsort ( m_channelInfo , MAX_CHANNELS , sizeof ( sChannelVolData ) , ChannelVolComparator ) ;
// Then, determine if the given sound is less than the nth loudest of its hash. If so, mark its flag
// for removal.
// TODO: use an actual algorithm rather than this bogus quadratic technique.
// (I'm using it for now because we don't have convenient/fast hash table
// classes, which would be the linear-time way to deal with this).
const int cutoff = snd_cull_duplicates . GetInt ( ) ;
for ( int i = 0 ; i < m_numChans ; + + i ) // i is index in original channel list
{
channel_t * ch = list . GetChannel ( i ) ;
// for each sound, determine where it ranks in loudness
int howManyLouder = 0 ;
for ( int j = 0 ;
m_channelInfo [ j ] . m_channelNum ! = i & & m_channelInfo [ j ] . m_vol > = 0 & & j < MAX_CHANNELS ;
+ + j )
{
// j steps through the sorted list until we find ourselves:
if ( m_channelInfo [ j ] . m_nameHash = = ( uintp ) ( ch - > sfx ) )
{
// that's another channel playing this sound but louder than me
+ + howManyLouder ;
}
}
if ( howManyLouder > = cutoff )
{
// this sound should be culled
m_bShouldCull [ i ] = true ;
}
else
{
// this sound should not be culled
m_bShouldCull [ i ] = false ;
}
}
}
ConVar snd_mute_losefocus ( " snd_mute_losefocus " , " 1 " , FCVAR_ARCHIVE ) ;
// build a list of channels that will actually do mixing in this update
// remove all active channels that won't mix for some reason
void MIX_BuildChannelList ( CChannelList & list )
{
VPROF ( " MIX_BuildChannelList " ) ;
g_ActiveChannels . GetActiveChannels ( list ) ;
list . m_nSpecialDSPs . RemoveAll ( ) ;
list . m_hasDryChannels = false ;
list . m_hasSpeakerChannels = false ;
list . m_has11kChannels = false ;
list . m_has22kChannels = false ;
list . m_has44kChannels = false ;
bool delayStartServer = false ;
bool delayStartClient = false ;
bool bPaused = g_pSoundServices - > IsGamePaused ( ) ;
# ifdef POSIX
bool bActive = g_pSoundServices - > IsGameActive ( ) ;
bool bStopOnFocusLoss = ! bActive & & snd_mute_losefocus . GetBool ( ) ;
# endif
CChannelCullList cullList ;
if ( snd_cull_duplicates . GetInt ( ) > 0 )
{
cullList . Initialize ( list ) ;
}
// int numQuashed = 0;
for ( int i = list . Count ( ) ; - - i > = 0 ; )
{
channel_t * ch = list . GetChannel ( i ) ;
bool bRemove = false ;
// Certain async loaded sounds lazily load into memory in the background, use this to determine
// if the sound is ready for mixing
CAudioSource * pSource = NULL ;
if ( ch - > pMixer - > IsReadyToMix ( ) )
{
pSource = S_LoadSound ( ch - > sfx , ch ) ;
// Don't mix sound data for sounds with 'zero' volume. If it's a non-looping sound,
// just remove the sound when its volume goes to zero. If it's a 'dry' channel sound (ie: music)
// then assume bZeroVolume is fade in - don't restart
// To be 'zero' volume, all target volume and current volume values must all be less than 5
bool bZeroVolume = BChannelLowVolume ( ch , 1 ) ;
if ( ! pSource | | ( bZeroVolume & & ! pSource - > IsLooped ( ) & & ! ch - > flags . bdry ) )
{
// NOTE: Since we've loaded the sound, check to see if it's a sentence. Play them at zero anyway
// to keep the character's lips moving and the captions happening.
if ( ! pSource | | pSource - > GetSentence ( ) = = NULL )
{
S_FreeChannel ( ch ) ;
bRemove = true ;
}
}
else if ( bZeroVolume )
{
bRemove = true ;
}
// If the sound wants to stop when the game pauses, do so
if ( bPaused & & SND_ShouldPause ( ch ) )
{
bRemove = true ;
}
# ifdef POSIX
// If we aren't the active app and the option for background audio isn't on, mute the audio
// Windows has it's own system for background muting
if ( ! bRemove & & bStopOnFocusLoss )
{
bRemove = true ;
// Free up the sound channels otherwise they start filling up
if ( pSource & & ( ! pSource - > IsLooped ( ) & & ! pSource - > IsStreaming ( ) ) )
{
S_FreeChannel ( ch ) ;
}
}
# endif
// On lowend, aggressively cull duplicate sounds.
if ( ! bRemove & & snd_cull_duplicates . GetInt ( ) > 0 )
{
// We can't simply remove them, because then sounds will pile up waiting to finish later.
// We need to flag them for not mixing.
list . m_quashed [ i ] = cullList . ShouldCull ( i ) ;
/*
if ( list . m_quashed [ i ] )
{
numQuashed + + ;
// Msg("removed %i\n", i);
}
*/
}
else
{
list . m_quashed [ i ] = false ;
}
}
else
{
bRemove = true ;
}
if ( bRemove )
{
list . RemoveChannelFromList ( i ) ;
continue ;
}
if ( ch - > flags . bSpeaker )
{
list . m_hasSpeakerChannels = true ;
}
if ( ch - > special_dsp ! = 0 )
{
if ( list . m_nSpecialDSPs . Find ( ch - > special_dsp ) = = - 1 )
{
list . m_nSpecialDSPs . AddToTail ( ch - > special_dsp ) ;
}
}
if ( ch - > flags . bdry )
{
list . m_hasDryChannels = true ;
}
int rate = pSource - > SampleRate ( ) ;
if ( rate = = SOUND_11k )
{
list . m_has11kChannels = true ;
}
else if ( rate = = SOUND_22k )
{
list . m_has22kChannels = true ;
}
else if ( rate = = SOUND_44k )
{
list . m_has44kChannels = true ;
}
if ( ch - > flags . delayed_start & & ! SND_IsMouth ( ch ) )
{
if ( ch - > flags . fromserver )
{
delayStartServer = true ;
}
else
{
delayStartClient = true ;
}
}
// get playback pitch
ch - > pitch = ch - > pMixer - > ModifyPitch ( ch - > basePitch * 0.01f ) ;
}
// DevMsg( "%d channels quashed.\n", numQuashed );
// This code will resync the delay calculation clock really often
// any time there are no scheduled waves or the game is paused
// we go ahead and reset the clock
// That way the clock is only used for short periods of time
// and we need no solution for drift
if ( bPaused | | ( host_frametime_unbounded > host_frametime ) )
{
delayStartClient = false ;
delayStartServer = false ;
}
if ( ! delayStartServer )
{
S_SyncClockAdjust ( CLOCK_SYNC_SERVER ) ;
}
if ( ! delayStartClient )
{
S_SyncClockAdjust ( CLOCK_SYNC_CLIENT ) ;
}
}
// main mixing rountine - mix up to 'endtime' samples.
// All channels are mixed in a paintbuffer and then sent to
// hardware.
// A mix pass is performed, resulting in mixed sounds in SOUND_BUFFER_ROOM, SOUND_BUFFER_FACING, SOUND_BUFFER_FACINGAWAY, SOUND_BUFFER_DRY, SOUND_BUFFER_SPEAKER, SOUND_BUFFER_SPECIALs
// directional sounds are panned and mixed between SOUND_BUFFER_FACING and SOUND_BUFFER_FACINGAWAY
// omnidirectional sounds are panned 100% into SOUND_BUFFER_FACING
// sound sources far from player (ie: near back of room ) are mixed in proportion to this distance
// into SOUND_BUFFER_ROOM
// sounds with ch->bSpeaker set are mixed in mono into SOUND_BUFFER_SPEAKER
// sounds with ch->bSpecialDSP set are mixed in mono into SOUND_BUFFER_SPECIALs
// dsp_facingaway fx (2 or 4ch filtering) are then applied to the SOUND_BUFFER_FACINGAWAY
// dsp_speaker fx (1ch) are then applied to the SOUND_BUFFER_SPEAKER
// dsp_specialdsp fx (1ch) are then applied to the SOUND_BUFFER_SPECIALs
// dsp_room fx (1ch reverb) are then applied to the SOUND_BUFFER_ROOM
// All buffers are recombined into the SOUND_BUFFER_PAINT
// The dsp_water and dsp_player fx are applied in series to the SOUND_BUFFER_PAINT
// Finally, the SOUND_BUFFER_DRY buffer is mixed into the SOUND_BUFFER_PAINT
extern ConVar dsp_off ;
extern ConVar snd_profile ;
extern void DEBUG_StartSoundMeasure ( int type , int samplecount ) ;
extern void DEBUG_StopSoundMeasure ( int type , int samplecount ) ;
extern ConVar dsp_enhance_stereo ;
extern ConVar dsp_volume ;
extern ConVar dsp_vol_5ch ;
extern ConVar dsp_vol_4ch ;
extern ConVar dsp_vol_2ch ;
extern void MXR_SetCurrentSoundMixer ( const char * szsoundmixer ) ;
extern ConVar snd_soundmixer ;
void MIX_PaintChannels ( int endtime , bool bIsUnderwater )
{
VPROF ( " MIX_PaintChannels " ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
int end ;
int count ;
bool b_spatial_delays = dsp_enhance_stereo . GetInt ( ) ! = 0 ? true : false ;
bool room_fsurround_sav ;
bool room_fsurround_center_sav ;
paintbuffer_t * proom = MIX_GetPPaintFromIPaint ( SOUND_BUFFER_ROOM ) ;
CheckNewDspPresets ( ) ;
MXR_SetCurrentSoundMixer ( snd_soundmixer . GetString ( ) ) ;
// dsp performance tuning
g_snd_profile_type = snd_profile . GetInt ( ) ;
// dsp_off is true if no dsp processing is to run
// directional dsp processing is enabled if dsp_facingaway is non-zero
g_bDspOff = dsp_off . GetInt ( ) ? 1 : 0 ;
CChannelList list ;
MIX_BuildChannelList ( list ) ;
// get master dsp volume
g_dsp_volume = dsp_volume . GetFloat ( ) ;
// attenuate master dsp volume by 2,4 or 5 ch settings
if ( g_AudioDevice - > IsSurround ( ) )
{
g_dsp_volume * = ( g_AudioDevice - > IsSurroundCenter ( ) ? dsp_vol_5ch . GetFloat ( ) : dsp_vol_4ch . GetFloat ( ) ) ;
}
else
{
g_dsp_volume * = dsp_vol_2ch . GetFloat ( ) ;
}
if ( ! g_bDspOff )
{
g_bdirectionalfx = dsp_facingaway . GetInt ( ) ? 1 : 0 ;
}
else
{
g_bdirectionalfx = 0 ;
}
// get dsp preset gain values, update gain crossfaders, used when mixing dsp processed buffers into paintbuffer
SDEBUG_ShowAvgValue ( ) ;
// the cache needs to hold the audio in memory during mixing, so tell it that mixing is starting
wavedatacache - > OnMixBegin ( ) ;
while ( g_paintedtime < endtime )
{
VPROF ( " MIX_PaintChannels inner loop " ) ;
// mix a full 'paintbuffer' of sound
// clamp at paintbuffer size
end = endtime ;
if ( endtime - g_paintedtime > PAINTBUFFER_SIZE )
{
end = g_paintedtime + PAINTBUFFER_SIZE ;
}
// number of 44khz samples to mix into paintbuffer, up to paintbuffer size
count = end - g_paintedtime ;
// clear all mix buffers
g_AudioDevice - > MixBegin ( count ) ;
// upsample all mix buffers.
// results in 44khz versions of:
// SOUND_BUFFER_ROOM, SOUND_BUFFER_FACING, SOUND_BUFFER_FACINGAWAY, SOUND_BUFFER_DRY, SOUND_BUFFER_SPEAKER, SOUND_BUFFER_SPECIALs
MIX_UpsampleAllPaintbuffers ( list , end , count ) ;
// apply appropriate dsp fx to each buffer, remix buffers into single quad output buffer
// apply 2 or 4ch filtering to IFACINGAWAY buffer
if ( g_bdirectionalfx )
{
g_AudioDevice - > ApplyDSPEffects ( idsp_facingaway , MIX_GetPFrontFromIPaint ( SOUND_BUFFER_FACINGAWAY ) , MIX_GetPRearFromIPaint ( SOUND_BUFFER_FACINGAWAY ) , MIX_GetPCenterFromIPaint ( SOUND_BUFFER_FACINGAWAY ) , count ) ;
}
if ( ! g_bDspOff & & list . m_hasSpeakerChannels )
{
// apply 1ch filtering to SOUND_BUFFER_SPEAKER
g_AudioDevice - > ApplyDSPEffects ( idsp_speaker , MIX_GetPFrontFromIPaint ( SOUND_BUFFER_SPEAKER ) , MIX_GetPRearFromIPaint ( SOUND_BUFFER_SPEAKER ) , MIX_GetPCenterFromIPaint ( SOUND_BUFFER_SPEAKER ) , count ) ;
// mix SOUND_BUFFER_SPEAKER with SOUND_BUFFER_ROOM and SOUND_BUFFER_FACING
MIX_ScalePaintBuffer ( SOUND_BUFFER_SPEAKER , count , 0.7 ) ;
MIX_MixPaintbuffers ( SOUND_BUFFER_SPEAKER , SOUND_BUFFER_FACING , SOUND_BUFFER_FACING , count , 1.0 ) ; // +70% dry speaker
MIX_ScalePaintBuffer ( SOUND_BUFFER_SPEAKER , count , 0.43 ) ;
MIX_MixPaintbuffers ( SOUND_BUFFER_SPEAKER , SOUND_BUFFER_ROOM , SOUND_BUFFER_ROOM , count , 1.0 ) ; // +30% wet speaker
}
if ( ! g_bDspOff )
{
// apply 1ch filtering to SOUND_BUFFER_SPECIALs
for ( int iDSP = 0 ; iDSP < list . m_nSpecialDSPs . Count ( ) ; + + iDSP )
{
bool bFoundMixer = false ;
for ( int i = SOUND_BUFFER_SPECIAL_START ; i < g_paintBuffers . Count ( ) ; + + i )
{
paintbuffer_t * pSpecialBuffer = MIX_GetPPaintFromIPaint ( i ) ;
if ( pSpecialBuffer - > nSpecialDSP = = list . m_nSpecialDSPs [ iDSP ] & & pSpecialBuffer - > idsp_specialdsp ! = - 1 )
{
g_AudioDevice - > ApplyDSPEffects ( pSpecialBuffer - > idsp_specialdsp , MIX_GetPFrontFromIPaint ( i ) , MIX_GetPRearFromIPaint ( i ) , MIX_GetPCenterFromIPaint ( i ) , count ) ;
// mix SOUND_BUFFER_SPECIALs with SOUND_BUFFER_ROOM and SOUND_BUFFER_FACING
MIX_ScalePaintBuffer ( i , count , 0.7 ) ;
MIX_MixPaintbuffers ( i , SOUND_BUFFER_FACING , SOUND_BUFFER_FACING , count , 1.0 ) ; // +70% dry speaker
MIX_ScalePaintBuffer ( i , count , 0.43 ) ;
MIX_MixPaintbuffers ( i , SOUND_BUFFER_ROOM , SOUND_BUFFER_ROOM , count , 1.0 ) ; // +30% wet speaker
bFoundMixer = true ;
break ;
}
}
// Couldn't find a mixer with the correct DSP, so make a new one!
if ( ! bFoundMixer )
{
bool bSurroundCenter = g_AudioDevice - > IsSurroundCenter ( ) ;
bool bSurround = g_AudioDevice - > IsSurround ( ) | | bSurroundCenter ;
int nIndex = g_paintBuffers . AddToTail ( ) ;
MIX_InitializePaintbuffer ( & ( g_paintBuffers [ nIndex ] ) , bSurround , bSurroundCenter ) ;
g_paintBuffers [ nIndex ] . flags = SOUND_BUSS_SPECIAL_DSP ;
// special dsp buffer mixes to mono
g_paintBuffers [ nIndex ] . fsurround = false ;
g_paintBuffers [ nIndex ] . fsurround_center = false ;
g_paintBuffers [ nIndex ] . idsp_specialdsp = - 1 ;
g_paintBuffers [ nIndex ] . nSpecialDSP = list . m_nSpecialDSPs [ iDSP ] ;
g_paintBuffers [ nIndex ] . nPrevSpecialDSP = g_paintBuffers [ nIndex ] . nSpecialDSP ;
g_paintBuffers [ nIndex ] . idsp_specialdsp = DSP_Alloc ( g_paintBuffers [ nIndex ] . nSpecialDSP , 300 , 1 ) ;
}
}
}
// apply dsp_room effects to room buffer
g_AudioDevice - > ApplyDSPEffects ( Get_idsp_room ( ) , MIX_GetPFrontFromIPaint ( SOUND_BUFFER_ROOM ) , MIX_GetPRearFromIPaint ( SOUND_BUFFER_ROOM ) , MIX_GetPCenterFromIPaint ( SOUND_BUFFER_ROOM ) , count ) ;
// save room buffer surround status, in case we upconvert it
room_fsurround_sav = proom - > fsurround ;
room_fsurround_center_sav = proom - > fsurround_center ;
// apply left/center/right/lrear/rrear spatial delays to room buffer
if ( b_spatial_delays & & ! g_bDspOff & & ! DSP_RoomDSPIsOff ( ) )
{
// upgrade mono room buffer to surround status so we can apply spatial delays to all channels
MIX_ConvertBufferToSurround ( SOUND_BUFFER_ROOM ) ;
g_AudioDevice - > ApplyDSPEffects ( idsp_spatial , MIX_GetPFrontFromIPaint ( SOUND_BUFFER_ROOM ) , MIX_GetPRearFromIPaint ( SOUND_BUFFER_ROOM ) , MIX_GetPCenterFromIPaint ( SOUND_BUFFER_ROOM ) , count ) ;
}
if ( g_bdirectionalfx ) // KDB: perf
{
// Recombine IFACING and IFACINGAWAY buffers into SOUND_BUFFER_PAINT
MIX_MixPaintbuffers ( SOUND_BUFFER_FACING , SOUND_BUFFER_FACINGAWAY , SOUND_BUFFER_PAINT , count , DSP_NOROOM_MIX ) ;
// Add in dsp room fx to paintbuffer, mix at 75%
MIX_MixPaintbuffers ( SOUND_BUFFER_ROOM , SOUND_BUFFER_PAINT , SOUND_BUFFER_PAINT , count , DSP_ROOM_MIX ) ;
}
else
{
// Mix IFACING buffer with SOUND_BUFFER_ROOM
// (SOUND_BUFFER_FACINGAWAY contains no data, IFACINGBBUFFER has full dry mix based on distance from listener)
// if dsp disabled, mix 100% facingbuffer, otherwise, mix 75% facingbuffer + roombuffer
float mix = g_bDspOff ? 1.0 : DSP_ROOM_MIX ;
MIX_MixPaintbuffers ( SOUND_BUFFER_ROOM , SOUND_BUFFER_FACING , SOUND_BUFFER_PAINT , count , mix ) ;
}
// restore room buffer surround status, in case we upconverted it
proom - > fsurround = room_fsurround_sav ;
proom - > fsurround_center = room_fsurround_center_sav ;
// Apply underwater fx dsp_water (serial in-line)
if ( bIsUnderwater )
{
// BUG: if out of water, previous delays will be heard. must clear dly buffers.
g_AudioDevice - > ApplyDSPEffects ( idsp_water , MIX_GetPFrontFromIPaint ( SOUND_BUFFER_PAINT ) , MIX_GetPRearFromIPaint ( SOUND_BUFFER_PAINT ) , MIX_GetPCenterFromIPaint ( SOUND_BUFFER_PAINT ) , count ) ;
}
// find dsp gain
SDEBUG_GetAvgIn ( SOUND_BUFFER_PAINT , count ) ;
// Apply player fx dsp_player (serial in-line) - does nothing if dsp fx are disabled
g_AudioDevice - > ApplyDSPEffects ( idsp_player , MIX_GetPFrontFromIPaint ( SOUND_BUFFER_PAINT ) , MIX_GetPRearFromIPaint ( SOUND_BUFFER_PAINT ) , MIX_GetPCenterFromIPaint ( SOUND_BUFFER_PAINT ) , count ) ;
// display dsp gain
SDEBUG_GetAvgOut ( SOUND_BUFFER_PAINT , count ) ;
/*
// apply left/center/right/lrear/rrear spatial delays to paint buffer
if ( b_spatial_delays )
g_AudioDevice - > ApplyDSPEffects ( idsp_spatial , MIX_GetPFrontFromIPaint ( SOUND_BUFFER_PAINT ) , MIX_GetPRearFromIPaint ( SOUND_BUFFER_PAINT ) , MIX_GetPCenterFromIPaint ( SOUND_BUFFER_PAINT ) , count ) ;
*/
// Add dry buffer, set output gain to water * player dsp gain (both 1.0 if not active)
MIX_MixPaintbuffers ( SOUND_BUFFER_PAINT , SOUND_BUFFER_DRY , SOUND_BUFFER_PAINT , count , 1.0 ) ;
// clip all values > 16 bit down to 16 bit
// NOTE: This is required - the hardware buffer transfer routines no longer perform clipping.
MIX_CompressPaintbuffer ( SOUND_BUFFER_PAINT , count ) ;
// transfer SOUND_BUFFER_PAINT paintbuffer out to DMA buffer
MIX_SetCurrentPaintbuffer ( SOUND_BUFFER_PAINT ) ;
g_AudioDevice - > TransferSamples ( end ) ;
g_paintedtime = end ;
}
// the cache needs to hold the audio in memory during mixing, so tell it that mixing is complete
wavedatacache - > OnMixEnd ( ) ;
}
// Applies volume scaling (evenly) to all fl,fr,rl,rr volumes
// used for voice ducking and panning between various mix busses
// Ensures if mixing to speaker buffer, only speaker sounds pass through
// Called just before mixing wav data to current paintbuffer.
// a) if another player in a multiplayer game is speaking, scale all volumes down.
// b) if mixing to SOUND_BUFFER_ROOM, scale all volumes by ch.dspmix and dsp_room gain
// c) if mixing to SOUND_BUFFER_FACINGAWAY, scale all volumes by ch.dspface and dsp_facingaway gain
// d) If SURROUND_ON, but buffer is not surround, recombined front/rear volumes
// returns false if channel is to be entirely skipped.
bool MIX_ScaleChannelVolume ( paintbuffer_t * ppaint , channel_t * pChannel , int volume [ CCHANVOLUMES ] , int mixchans )
{
int i ;
int mixflag = ppaint - > flags ;
float scale ;
char wavtype = pChannel - > wavtype ;
float dspmix ;
// copy current channel volumes into output array
ChannelCopyVolumes ( pChannel , volume , 0 , CCHANVOLUMES ) ;
dspmix = pChannel - > dspmix ;
// if dsp is off, or room dsp is off, mix 0% to mono room buffer, 100% to facing buffer
if ( g_bDspOff | | DSP_RoomDSPIsOff ( ) )
dspmix = 0.0 ;
// duck all sound volumes except speaker's voice
# if !defined( NO_VOICE )
int duckScale = min ( ( int ) ( g_DuckScale * 256 ) , g_SND_VoiceOverdriveInt ) ;
# else
int duckScale = ( int ) ( g_DuckScale * 256 ) ;
# endif
if ( duckScale < 256 )
{
if ( pChannel - > pMixer )
{
CAudioSource * pSource = pChannel - > pMixer - > GetSource ( ) ;
if ( ! pSource - > IsVoiceSource ( ) )
{
// Apply voice overdrive..
for ( i = 0 ; i < CCHANVOLUMES ; i + + )
volume [ i ] = ( volume [ i ] * duckScale ) > > 8 ;
}
}
}
// If mixing to the room buss, adjust volume based on channel's dspmix setting.
// dspmix is DSP_MIX_MAX (~0.78) if sound is far from player, DSP_MIX_MIN (~0.24) if sound is near player
if ( mixflag & SOUND_BUSS_ROOM )
{
// set dsp mix volume, scaled by global dsp_volume
float dspmixvol = fpmin ( dspmix * g_dsp_volume , 1.0f ) ;
// if dspmix is 1.0, 100% of sound goes to SOUND_BUFFER_ROOM and 0% to SOUND_BUFFER_FACING
for ( i = 0 ; i < CCHANVOLUMES ; i + + )
volume [ i ] = ( int ) ( ( float ) ( volume [ i ] ) * dspmixvol ) ;
}
// If global dsp volume is less than 1, reduce dspmix (ie: increase dry volume)
// If gloabl dsp volume is greater than 1, do not reduce dspmix
if ( g_dsp_volume < 1.0 )
dspmix * = g_dsp_volume ;
// If mixing to facing/facingaway buss, adjust volume based on sound entity's facing direction.
// If sound directly faces player, ch->dspface = 1.0. If facing directly away, ch->dspface = -1.0.
// mix to lowpass buffer if facing away, to allpass if facing
// scale 1.0 - facing player, scale 0, facing away
scale = ( pChannel - > dspface + 1.0 ) / 2.0 ;
// UNDONE: get front cone % from channel to set this.
// bias scale such that 1.0 to 'cone' is considered facing. Facing cone narrows as cone -> 1.0
// and 'cone' -> 0.0 becomes 1.0 -> 0.0
float cone = 0.6f ;
scale = scale * ( 1 / cone ) ;
scale = clamp ( scale , 0.0f , 1.0f ) ;
// pan between facing and facing away buffers
// if ( !g_bdirectionalfx || wavtype == CHAR_DOPPLER || wavtype == CHAR_OMNI || (wavtype == CHAR_DIRECTIONAL && mixchans == 2) )
if ( ! g_bdirectionalfx | | wavtype ! = CHAR_DIRECTIONAL )
{
// if no directional fx mix 0% to facingaway buffer
// if wavtype is DOPPLER, mix 0% to facingaway buffer - DOPPLER wavs have a custom mixer
// if wavtype is OMNI, mix 0% to facingaway buffer - OMNI wavs have no directionality
// if wavtype is DIRECTIONAL and stereo encoded, mix 0% to facingaway buffer - DIRECTIONAL STEREO wavs have a custom mixer
scale = 1.0 ;
}
if ( mixflag & SOUND_BUSS_FACING )
{
// facing player
// if dspface is 1.0, 100% of sound goes to SOUND_BUFFER_FACING
for ( i = 0 ; i < CCHANVOLUMES ; i + + )
volume [ i ] = ( int ) ( ( float ) ( volume [ i ] ) * scale * ( 1.0 - dspmix ) ) ;
}
else if ( mixflag & SOUND_BUSS_FACINGAWAY )
{
// facing away from player
// if dspface is 0.0, 100% of sound goes to SOUND_BUFFER_FACINGAWAY
for ( i = 0 ; i < CCHANVOLUMES ; i + + )
volume [ i ] = ( int ) ( ( float ) ( volume [ i ] ) * ( 1.0 - scale ) * ( 1.0 - dspmix ) ) ;
}
// NOTE: this must occur last in this routine:
if ( g_AudioDevice - > IsSurround ( ) & & ! ppaint - > fsurround )
{
// if 4ch or 5ch spatialization on, but current mix buffer is 2ch,
// recombine front + rear volumes (revert to 2ch spatialization)
volume [ IFRONT_RIGHT ] + = volume [ IREAR_RIGHT ] ;
volume [ IFRONT_LEFT ] + = volume [ IREAR_LEFT ] ;
volume [ IFRONT_RIGHTD ] + = volume [ IREAR_RIGHTD ] ;
volume [ IFRONT_LEFTD ] + = volume [ IREAR_LEFTD ] ;
// if 5 ch, recombine center channel vol
if ( g_AudioDevice - > IsSurroundCenter ( ) )
{
volume [ IFRONT_RIGHT ] + = volume [ IFRONT_CENTER ] / 2 ;
volume [ IFRONT_LEFT ] + = volume [ IFRONT_CENTER ] / 2 ;
volume [ IFRONT_RIGHTD ] + = volume [ IFRONT_CENTERD ] / 2 ;
volume [ IFRONT_LEFTD ] + = volume [ IFRONT_CENTERD ] / 2 ;
}
// clear rear & center volumes
volume [ IREAR_RIGHT ] = 0 ;
volume [ IREAR_LEFT ] = 0 ;
volume [ IFRONT_CENTER ] = 0 ;
volume [ IREAR_RIGHTD ] = 0 ;
volume [ IREAR_LEFTD ] = 0 ;
volume [ IFRONT_CENTERD ] = 0 ;
}
bool fzerovolume = true ;
for ( i = 0 ; i < CCHANVOLUMES ; i + + )
{
volume [ i ] = clamp ( volume [ i ] , 0 , 255 ) ;
if ( volume [ i ] )
fzerovolume = false ;
}
if ( fzerovolume )
{
// DevMsg ("Skipping mix of 0 volume sound! \n");
return false ;
}
return true ;
}
//===============================================================================
// Low level mixing routines
//===============================================================================
void Snd_WriteLinearBlastStereo16 ( void )
{
# if !id386
int i ;
int val ;
for ( i = 0 ; i < snd_linear_count ; i + = 2 )
{
// scale and clamp left 16bit signed: [0x8000, 0x7FFF]
val = ( snd_p [ i ] * snd_vol ) > > 8 ;
if ( val > 32767 )
snd_out [ i ] = 32767 ;
else if ( val < - 32768 )
snd_out [ i ] = - 32768 ;
else
snd_out [ i ] = val ;
// scale and clamp right 16bit signed: [0x8000, 0x7FFF]
val = ( snd_p [ i + 1 ] * snd_vol ) > > 8 ;
if ( val > 32767 )
snd_out [ i + 1 ] = 32767 ;
else if ( val < - 32768 )
snd_out [ i + 1 ] = - 32768 ;
else
snd_out [ i + 1 ] = val ;
}
# else
__asm
{
// input data
mov ebx , snd_p
// output data
mov edi , snd_out
// iterate from end to beginning
mov ecx , snd_linear_count
// scale table
mov esi , snd_vol
// scale and clamp 16bit signed lsw: [0x8000, 0x7FFF]
WLBS16_LoopTop :
mov eax , [ ebx + ecx * 4 - 8 ]
imul eax , esi
sar eax , 0x08
cmp eax , 0x7FFF
jg WLBS16_ClampHigh
cmp eax , 0xFFFF8000
jnl WLBS16_ClampDone
mov eax , 0xFFFF8000
jmp WLBS16_ClampDone
WLBS16_ClampHigh :
mov eax , 0x7FFF
WLBS16_ClampDone :
// scale and clamp 16bit signed msw: [0x8000, 0x7FFF]
mov edx , [ ebx + ecx * 4 - 4 ]
imul edx , esi
sar edx , 0x08
cmp edx , 0x7FFF
jg WLBS16_ClampHigh2
cmp edx , 0xFFFF8000
jnl WLBS16_ClampDone2
mov edx , 0xFFFF8000
jmp WLBS16_ClampDone2
WLBS16_ClampHigh2 :
mov edx , 0x7FFF
WLBS16_ClampDone2 :
shl edx , 0x10
and eax , 0xFFFF
or edx , eax
mov [ edi + ecx * 2 - 4 ] , edx
// two shorts per iteration
sub ecx , 0x02
jnz WLBS16_LoopTop
}
# endif
}
void SND_InitScaletable ( void )
{
int i , j ;
for ( i = 0 ; i < SND_SCALE_LEVELS ; i + + )
for ( j = 0 ; j < 256 ; j + + )
snd_scaletable [ i ] [ j ] = ( ( signed char ) j ) * i * ( 1 < < SND_SCALE_SHIFT ) ;
}
void SND_PaintChannelFrom8 ( portable_samplepair_t * pOutput , int * volume , byte * pData8 , int count )
{
# if !id386
int data ;
int * lscale , * rscale ;
int i ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
for ( i = 0 ; i < count ; i + + )
{
data = pData8 [ i ] ;
pOutput [ i ] . left + = lscale [ data ] ;
pOutput [ i ] . right + = rscale [ data ] ;
}
# else
// portable_samplepair_t structure
# define psp_left 0
# define psp_right 4
# define psp_size 8
static int tempStore ;
__asm
{
// prologue
push ebp
// esp = pOutput
mov eax , pOutput
mov tempStore , eax
xchg esp , tempStore
// ebx = volume
mov ebx , volume
// esi = pData8
mov esi , pData8
// ecx = count
mov ecx , count
// These values depend on the setting of SND_SCALE_BITS
// The mask must mask off all the lower bits you aren't using in the multiply
// so for 7 bits, the mask is 0xFE, 6 bits 0xFC, etc.
// The shift must multiply by the table size. There are 256 4-byte values in the table at each level.
// So each index must be shifted left by 10, but since the bits we use are in the MSB rather than LSB
// they must be shifted right by 8 - SND_SCALE_BITS. e.g., for a 7 bit number the left shift is:
// 10 - (8-7) = 9. For a 5 bit number it's 10 - (8-5) = 7.
mov eax , [ ebx ]
mov edx , [ ebx + 4 ]
and eax , 0xFE
and edx , 0xFE
// shift up by 10 to index table, down by 1 to make the 7 MSB of the bytes an index
// eax = lscale
// edx = rscale
shl eax , 0x09
shl edx , 0x09
add eax , OFFSET snd_scaletable
add edx , OFFSET snd_scaletable
// ebx = data byte
sub ebx , ebx
mov bl , [ esi + ecx - 1 ]
// odd or even number of L/R samples
test ecx , 0x01
jz PCF8_Loop
// process odd L/R sample
mov edi , [ eax + ebx * 4 ]
mov ebp , [ edx + ebx * 4 ]
add edi , [ esp + ecx * psp_size - psp_size + psp_left ]
add ebp , [ esp + ecx * psp_size - psp_size + psp_right ]
mov [ esp + ecx * psp_size - psp_size + psp_left ] , edi
mov [ esp + ecx * psp_size - psp_size + psp_right ] , ebp
mov bl , [ esi + ecx - 1 - 1 ]
dec ecx
jz PCF8_Done
PCF8_Loop :
// process L/R sample N
mov edi , [ eax + ebx * 4 ]
mov ebp , [ edx + ebx * 4 ]
add edi , [ esp + ecx * psp_size - psp_size + psp_left ]
add ebp , [ esp + ecx * psp_size - psp_size + psp_right ]
mov [ esp + ecx * psp_size - psp_size + psp_left ] , edi
mov [ esp + ecx * psp_size - psp_size + psp_right ] , ebp
mov bl , [ esi + ecx - 1 - 1 ]
// process L/R sample N-1
mov edi , [ eax + ebx * 4 ]
mov ebp , [ edx + ebx * 4 ]
add edi , [ esp + ecx * psp_size - psp_size * 2 + psp_left ]
add ebp , [ esp + ecx * psp_size - psp_size * 2 + psp_right ]
mov [ esp + ecx * psp_size - psp_size * 2 + psp_left ] , edi
mov [ esp + ecx * psp_size - psp_size * 2 + psp_right ] , ebp
mov bl , [ esi + ecx - 1 - 2 ]
// two L/R samples per iteration
sub ecx , 0x02
jnz PCF8_Loop
PCF8_Done :
// epilogue
xchg esp , tempStore
pop ebp
}
# endif
}
//===============================================================================
// SOFTWARE MIXING ROUTINES
//===============================================================================
// UNDONE: optimize these
// grab samples from left source channel only and mix as if mono.
// volume array contains appropriate spatialization volumes for doppler left (incoming sound)
void SW_Mix8StereoDopplerLeft ( portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
for ( int i = 0 ; i < outCount ; i + + )
{
pOutput [ i ] . left + = lscale [ pData [ sampleIndex ] ] ;
pOutput [ i ] . right + = rscale [ pData [ sampleIndex ] ] ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// grab samples from right source channel only and mix as if mono.
// volume array contains appropriate spatialization volumes for doppler right (outgoing sound)
void SW_Mix8StereoDopplerRight ( portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
for ( int i = 0 ; i < outCount ; i + + )
{
pOutput [ i ] . left + = lscale [ pData [ sampleIndex + 1 ] ] ;
pOutput [ i ] . right + = rscale [ pData [ sampleIndex + 1 ] ] ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// grab samples from left source channel only and mix as if mono.
// volume array contains appropriate spatialization volumes for doppler left (incoming sound)
void SW_Mix16StereoDopplerLeft ( portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
for ( int i = 0 ; i < outCount ; i + + )
{
pOutput [ i ] . left + = ( volume [ 0 ] * ( int ) ( pData [ sampleIndex ] ) ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * ( int ) ( pData [ sampleIndex ] ) ) > > 8 ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// grab samples from right source channel only and mix as if mono.
// volume array contains appropriate spatialization volumes for doppler right (outgoing sound)
void SW_Mix16StereoDopplerRight ( portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
for ( int i = 0 ; i < outCount ; i + + )
{
pOutput [ i ] . left + = ( volume [ 0 ] * ( int ) ( pData [ sampleIndex + 1 ] ) ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * ( int ) ( pData [ sampleIndex + 1 ] ) ) > > 8 ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// mix left wav (front facing) with right wav (rear facing) based on soundfacing direction
void SW_Mix8StereoDirectional ( float soundfacing , portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
int x ;
int l , r ;
signed char lb , rb ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
// if soundfacing -1.0, sound source is facing away from player
// if soundfacing 0.0, sound source is perpendicular to player
// if soundfacing 1.0, sound source is facing player
int frontmix = ( int ) ( 256.0f * ( ( 1.f + soundfacing ) / 2.f ) ) ; // 0 -> 256
for ( int i = 0 ; i < outCount ; i + + )
{
lb = ( pData [ sampleIndex ] ) ; // get left byte
rb = ( pData [ sampleIndex + 1 ] ) ; // get right byte
l = ( ( int ) lb ) ;
r = ( ( int ) rb ) ;
x = ( r + ( ( ( l - r ) * frontmix ) > > 8 ) ) ;
pOutput [ i ] . left + = lscale [ x & 0xFF ] ; // multiply by volume and convert to 16 bit
pOutput [ i ] . right + = rscale [ x & 0xFF ] ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// mix left wav (front facing) with right wav (rear facing) based on soundfacing direction
// interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
// pData buffer, ensuring we can always provide 'outCount' samples.
void SW_Mix8StereoDirectional_Interp ( float soundfacing , portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
fixedint sampleIndex = 0 ;
fixedint rateScaleFix14 = FIX_28TO14 ( rateScaleFix ) ; // convert 28 bit fixed point to 14 bit fixed point
fixedint sampleFrac14 = FIX_28TO14 ( inputOffset ) ;
int first , second , interpl , interpr ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
int x ;
// if soundfacing -1.0, sound source is facing away from player
// if soundfacing 0.0, sound source is perpendicular to player
// if soundfacing 1.0, sound source is facing player
int frontmix = ( int ) ( 256.0f * ( ( 1.f + soundfacing ) / 2.f ) ) ; // 0 -> 256
for ( int i = 0 ; i < outCount ; i + + )
{
// interpolate between first & second sample (the samples bordering sampleFrac12 fraction)
first = ( int ) ( ( signed char ) ( pData [ sampleIndex ] ) ) ; // left byte
second = ( int ) ( ( signed char ) ( pData [ sampleIndex + 2 ] ) ) ;
interpl = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
first = ( int ) ( ( signed char ) ( pData [ sampleIndex + 1 ] ) ) ; // right byte
second = ( int ) ( ( signed char ) ( pData [ sampleIndex + 3 ] ) ) ;
interpr = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
// crossfade between right/left based on directional mix
x = ( interpr + ( ( ( interpl - interpr ) * frontmix ) > > 8 ) ) ;
pOutput [ i ] . left + = lscale [ x & 0xFF ] ; // scale and convert to 16 bit
pOutput [ i ] . right + = rscale [ x & 0xFF ] ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
}
// mix left wav (front facing) with right wav (rear facing) based on soundfacing direction
void SW_Mix16StereoDirectional ( float soundfacing , portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
fixedint sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
int x ;
int l , r ;
// if soundfacing -1.0, sound source is facing away from player
// if soundfacing 0.0, sound source is perpendicular to player
// if soundfacing 1.0, sound source is facing player
int frontmix = ( int ) ( 256.0f * ( ( 1.f + soundfacing ) / 2.f ) ) ; // 0 -> 256
for ( int i = 0 ; i < outCount ; i + + )
{
// get left, right samples
l = ( int ) ( pData [ sampleIndex ] ) ;
r = ( int ) ( pData [ sampleIndex + 1 ] ) ;
// crossfade between left & right based on front/rear facing
x = ( r + ( ( ( l - r ) * frontmix ) > > 8 ) ) ;
pOutput [ i ] . left + = ( volume [ 0 ] * x ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * x ) > > 8 ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// mix left wav (front facing) with right wav (rear facing) based on soundfacing direction
// interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
// pData buffer, ensuring we can always provide 'outCount' samples.
void SW_Mix16StereoDirectional_Interp ( float soundfacing , portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
fixedint sampleIndex = 0 ;
fixedint rateScaleFix14 = FIX_28TO14 ( rateScaleFix ) ; // convert 28 bit fixed point to 14 bit fixed point
fixedint sampleFrac14 = FIX_28TO14 ( inputOffset ) ;
int x ;
int first , second , interpl , interpr ;
// if soundfacing -1.0, sound source is facing away from player
// if soundfacing 0.0, sound source is perpendicular to player
// if soundfacing 1.0, sound source is facing player
int frontmix = ( int ) ( 256.0f * ( ( 1.f + soundfacing ) / 2.f ) ) ; // 0 -> 256
for ( int i = 0 ; i < outCount ; i + + )
{
// get interpolated left, right samples
first = ( int ) ( pData [ sampleIndex ] ) ;
second = ( int ) ( pData [ sampleIndex + 2 ] ) ;
interpl = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
first = ( int ) ( pData [ sampleIndex + 1 ] ) ;
second = ( int ) ( pData [ sampleIndex + 3 ] ) ;
interpr = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
// crossfade between left & right based on front/rear facing
x = ( interpr + ( ( ( interpl - interpr ) * frontmix ) > > 8 ) ) ;
pOutput [ i ] . left + = ( volume [ 0 ] * x ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * x ) > > 8 ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
}
// distance variant wav (left is close, right is far)
void SW_Mix8StereoDistVar ( float distmix , portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
int x ;
int l , r ;
signed char lb , rb ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
// distmix 0 - sound is near player (100% wav left)
// distmix 1.0 - sound is far from player (100% wav right)
int nearmix = ( int ) ( 256.0f * ( 1.0f - distmix ) ) ;
int farmix = ( int ) ( 256.0f * distmix ) ;
// if mixing at max or min range, skip crossfade (KDB: perf)
if ( ! nearmix )
{
for ( int i = 0 ; i < outCount ; i + + )
{
rb = ( pData [ sampleIndex + 1 ] ) ; // get right byte
x = ( int ) rb ;
pOutput [ i ] . left + = lscale [ x & 0xFF ] ; // multiply by volume and convert to 16 bit
pOutput [ i ] . right + = rscale [ x & 0xFF ] ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
return ;
}
if ( ! farmix )
{
for ( int i = 0 ; i < outCount ; i + + )
{
lb = ( pData [ sampleIndex ] ) ; // get left byte
x = ( int ) lb ;
pOutput [ i ] . left + = lscale [ x & 0xFF ] ; // multiply by volume and convert to 16 bit
pOutput [ i ] . right + = rscale [ x & 0xFF ] ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
return ;
}
// crossfade left/right
for ( int i = 0 ; i < outCount ; i + + )
{
lb = ( pData [ sampleIndex ] ) ; // get left byte
rb = ( pData [ sampleIndex + 1 ] ) ; // get right byte
l = ( int ) lb ;
r = ( int ) rb ;
x = ( l + ( ( ( r - l ) * farmix ) > > 8 ) ) ;
pOutput [ i ] . left + = lscale [ x & 0xFF ] ; // multiply by volume and convert to 16 bit
pOutput [ i ] . right + = rscale [ x & 0xFF ] ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// distance variant wav (left is close, right is far)
// interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
// pData buffer, ensuring we can always provide 'outCount' samples.
void SW_Mix8StereoDistVar_Interp ( float distmix , portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int x ;
// distmix 0 - sound is near player (100% wav left)
// distmix 1.0 - sound is far from player (100% wav right)
int nearmix = ( int ) ( 256.0f * ( 1.0f - distmix ) ) ;
int farmix = ( int ) ( 256.0f * distmix ) ;
fixedint sampleIndex = 0 ;
fixedint rateScaleFix14 = FIX_28TO14 ( rateScaleFix ) ; // convert 28 bit fixed point to 14 bit fixed point
fixedint sampleFrac14 = FIX_28TO14 ( inputOffset ) ;
int first , second , interpl , interpr ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
// if mixing at max or min range, skip crossfade (KDB: perf)
if ( ! nearmix )
{
for ( int i = 0 ; i < outCount ; i + + )
{
first = ( int ) ( ( signed char ) ( pData [ sampleIndex + 1 ] ) ) ; // right sample
second = ( int ) ( ( signed char ) ( pData [ sampleIndex + 3 ] ) ) ;
interpr = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
pOutput [ i ] . left + = lscale [ interpr & 0xFF ] ; // scale and convert to 16 bit
pOutput [ i ] . right + = rscale [ interpr & 0xFF ] ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
return ;
}
if ( ! farmix )
{
for ( int i = 0 ; i < outCount ; i + + )
{
first = ( int ) ( ( signed char ) ( pData [ sampleIndex ] ) ) ; // left sample
second = ( int ) ( ( signed char ) ( pData [ sampleIndex + 2 ] ) ) ;
interpl = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
pOutput [ i ] . left + = lscale [ interpl & 0xFF ] ; // scale and convert to 16 bit
pOutput [ i ] . right + = rscale [ interpl & 0xFF ] ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
return ;
}
// crossfade left/right
for ( int i = 0 ; i < outCount ; i + + )
{
// interpolate between first & second sample (the samples bordering sampleFrac14 fraction)
first = ( int ) ( ( signed char ) ( pData [ sampleIndex ] ) ) ;
second = ( int ) ( ( signed char ) ( pData [ sampleIndex + 2 ] ) ) ;
interpl = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
first = ( int ) ( ( signed char ) ( pData [ sampleIndex + 1 ] ) ) ;
second = ( int ) ( ( signed char ) ( pData [ sampleIndex + 3 ] ) ) ;
interpr = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
// crossfade between left and right based on distance mix
x = ( interpl + ( ( ( interpr - interpl ) * farmix ) > > 8 ) ) ;
pOutput [ i ] . left + = lscale [ x & 0xFF ] ; // scale and convert to 16 bit
pOutput [ i ] . right + = rscale [ x & 0xFF ] ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
}
// distance variant wav (left is close, right is far)
void SW_Mix16StereoDistVar ( float distmix , portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
int x ;
int l , r ;
// distmix 0 - sound is near player (100% wav left)
// distmix 1.0 - sound is far from player (100% wav right)
int nearmix = Float2Int ( 256.0f * ( 1.f - distmix ) ) ;
int farmix = Float2Int ( 256.0f * distmix ) ;
// if mixing at max or min range, skip crossfade (KDB: perf)
if ( ! nearmix )
{
for ( int i = 0 ; i < outCount ; i + + )
{
x = pData [ sampleIndex + 1 ] ; // right sample
pOutput [ i ] . left + = ( volume [ 0 ] * x ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * x ) > > 8 ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
return ;
}
if ( ! farmix )
{
for ( int i = 0 ; i < outCount ; i + + )
{
x = pData [ sampleIndex ] ; // left sample
pOutput [ i ] . left + = ( volume [ 0 ] * x ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * x ) > > 8 ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
return ;
}
// crossfade left/right
for ( int i = 0 ; i < outCount ; i + + )
{
l = pData [ sampleIndex ] ;
r = pData [ sampleIndex + 1 ] ;
x = ( l + ( ( ( r - l ) * farmix ) > > 8 ) ) ;
pOutput [ i ] . left + = ( volume [ 0 ] * x ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * x ) > > 8 ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// distance variant wav (left is close, right is far)
// interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
// pData buffer, ensuring we can always provide 'outCount' samples.
void SW_Mix16StereoDistVar_Interp ( float distmix , portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int x ;
fixedint sampleIndex = 0 ;
fixedint rateScaleFix14 = FIX_28TO14 ( rateScaleFix ) ; // convert 28 bit fixed point to 14 bit fixed point
fixedint sampleFrac14 = FIX_28TO14 ( inputOffset ) ;
int first , second , interpl , interpr ;
// distmix 0 - sound is near player (100% wav left)
// distmix 1.0 - sound is far from player (100% wav right)
int nearmix = Float2Int ( 256.0f * ( 1.f - distmix ) ) ;
int farmix = Float2Int ( 256.0f * distmix ) ;
// if mixing at max or min range, skip crossfade (KDB: perf)
if ( ! nearmix )
{
for ( int i = 0 ; i < outCount ; i + + )
{
first = ( int ) ( pData [ sampleIndex + 1 ] ) ; // right sample
second = ( int ) ( pData [ sampleIndex + 3 ] ) ;
interpr = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
pOutput [ i ] . left + = ( volume [ 0 ] * interpr ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * interpr ) > > 8 ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
return ;
}
if ( ! farmix )
{
for ( int i = 0 ; i < outCount ; i + + )
{
first = ( int ) ( pData [ sampleIndex ] ) ; // left sample
second = ( int ) ( pData [ sampleIndex + 2 ] ) ;
interpl = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
pOutput [ i ] . left + = ( volume [ 0 ] * interpl ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * interpl ) > > 8 ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
return ;
}
// crossfade left/right
for ( int i = 0 ; i < outCount ; i + + )
{
first = ( int ) ( pData [ sampleIndex ] ) ;
second = ( int ) ( pData [ sampleIndex + 2 ] ) ;
interpl = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
first = ( int ) ( pData [ sampleIndex + 1 ] ) ;
second = ( int ) ( pData [ sampleIndex + 3 ] ) ;
interpr = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
// crossfade between left & right samples
x = ( interpl + ( ( ( interpr - interpl ) * farmix ) > > 8 ) ) ;
pOutput [ i ] . left + = ( volume [ 0 ] * x ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * x ) > > 8 ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
}
void SW_Mix8Mono ( portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
// Not using pitch shift?
if ( rateScaleFix = = FIX ( 1 ) )
{
// native code
SND_PaintChannelFrom8 ( pOutput , volume , ( byte * ) pData , outCount ) ;
return ;
}
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
for ( int i = 0 ; i < outCount ; i + + )
{
pOutput [ i ] . left + = lscale [ pData [ sampleIndex ] ] ;
pOutput [ i ] . right + = rscale [ pData [ sampleIndex ] ] ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
// pData buffer, ensuring we can always provide 'outCount' samples.
void SW_Mix8Mono_Interp ( portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
fixedint sampleIndex = 0 ;
fixedint rateScaleFix14 = FIX_28TO14 ( rateScaleFix ) ; // convert 28 bit fixed point to 14 bit fixed point
fixedint sampleFrac14 = FIX_28TO14 ( inputOffset ) ;
int first , second , interp ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
// iterate 0th sample to outCount-1 sample
for ( int i = 0 ; i < outCount ; i + + )
{
// interpolate between first & second sample (the samples bordering sampleFrac12 fraction)
first = ( int ) ( ( signed char ) ( pData [ sampleIndex ] ) ) ;
second = ( int ) ( ( signed char ) ( pData [ sampleIndex + 1 ] ) ) ;
interp = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
pOutput [ i ] . left + = lscale [ interp & 0xFF ] ; // multiply by volume and convert to 16 bit
pOutput [ i ] . right + = rscale [ interp & 0xFF ] ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
}
void SW_Mix8Stereo ( portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
for ( int i = 0 ; i < outCount ; i + + )
{
pOutput [ i ] . left + = lscale [ pData [ sampleIndex ] ] ;
pOutput [ i ] . right + = rscale [ pData [ sampleIndex + 1 ] ] ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
// pData buffer, ensuring we can always provide 'outCount' samples.
void SW_Mix8Stereo_Interp ( portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
fixedint sampleIndex = 0 ;
fixedint rateScaleFix14 = FIX_28TO14 ( rateScaleFix ) ; // convert 28 bit fixed point to 14 bit fixed point
fixedint sampleFrac14 = FIX_28TO14 ( inputOffset ) ;
int first , second , interpl , interpr ;
int * lscale , * rscale ;
lscale = snd_scaletable [ volume [ 0 ] > > SND_SCALE_SHIFT ] ;
rscale = snd_scaletable [ volume [ 1 ] > > SND_SCALE_SHIFT ] ;
// iterate 0th sample to outCount-1 sample
for ( int i = 0 ; i < outCount ; i + + )
{
// interpolate between first & second sample (the samples bordering sampleFrac12 fraction)
first = ( int ) ( ( signed char ) ( pData [ sampleIndex ] ) ) ; // left
second = ( int ) ( ( signed char ) ( pData [ sampleIndex + 2 ] ) ) ;
interpl = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
first = ( int ) ( ( signed char ) ( pData [ sampleIndex + 1 ] ) ) ; // right
second = ( int ) ( ( signed char ) ( pData [ sampleIndex + 3 ] ) ) ;
interpr = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
pOutput [ i ] . left + = lscale [ interpl & 0xFF ] ; // multiply by volume and convert to 16 bit
pOutput [ i ] . right + = rscale [ interpr & 0xFF ] ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
}
void SW_Mix16Mono_Shift ( portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int vol0 = volume [ 0 ] ;
int vol1 = volume [ 1 ] ;
# if !id386
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
for ( int i = 0 ; i < outCount ; i + + )
{
pOutput [ i ] . left + = ( vol0 * ( int ) ( pData [ sampleIndex ] ) ) > > 8 ;
pOutput [ i ] . right + = ( vol1 * ( int ) ( pData [ sampleIndex ] ) ) > > 8 ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
# else
// in assembly, you can make this 32.32 instead of 4.28 and use the carry flag instead of masking
int rateScaleInt = FIX_INTPART ( rateScaleFix ) ;
unsigned int rateScaleFrac = FIX_FRACPART ( rateScaleFix ) < < ( 32 - FIX_BITS ) ;
__asm
{
mov eax , volume ;
movq mm0 , DWORD PTR [ eax ] ; vol1 , vol0 ( 32 - bits each )
packssdw mm0 , mm0 ; pack and replicate . . . vol1 , vol0 , vol1 , vol0 ( 16 - bits each )
//pxor mm7, mm7 ; mm7 is my zero register...
xor esi , esi
mov eax , DWORD PTR [ pOutput ] ; store initial output ptr
mov edx , DWORD PTR [ pData ] ; store initial input ptr
mov ebx , inputOffset ;
mov ecx , outCount ;
BEGINLOAD :
movd mm2 , WORD PTR [ edx + 2 * esi ] ; load first piece of data from pData
punpcklwd mm2 , mm2 ; 0 , 0 , pData_1st , pData_1st
add ebx , rateScaleFrac ; do the crazy fixed integer math
adc esi , rateScaleInt
movd mm3 , WORD PTR [ edx + 2 * esi ] ; load second piece of data from pData
punpcklwd mm3 , mm3 ; 0 , 0 , pData_2nd , pData_2nd
punpckldq mm2 , mm3 ; pData_2nd , pData_2nd , pData_2nd , pData_2nd
add ebx , rateScaleFrac ; do the crazy fixed integer math
adc esi , rateScaleInt
movq mm3 , mm2 ; copy the goods
pmullw mm2 , mm0 ; pData_2nd * vol1 , pData_2nd * vol0 , pData_1st * vol1 , pData_1st * vol0 ( bits 0 - 15 )
pmulhw mm3 , mm0 ; pData_2nd * vol1 , pData_2nd * vol0 , pData_1st * vol1 , pData_1st * vol0 ( bits 16 - 31 )
movq mm4 , mm2 ; copy
movq mm5 , mm3 ; copy
punpcklwd mm2 , mm3 ; pData_1st * vol1 , pData_1st * vol0 ( bits 0 - 31 )
punpckhwd mm4 , mm5 ; pData_2nd * vol1 , pData_2nd * vol0 ( bits 0 - 31 )
psrad mm2 , 8 ; shift right by 8
psrad mm4 , 8 ; shift right by 8
add ecx , - 2 ; decrement i - value
paddd mm2 , QWORD PTR [ eax ] ; add to existing vals
paddd mm4 , QWORD PTR [ eax + 8 ] ;
movq QWORD PTR [ eax ] , mm2 ; store back
movq QWORD PTR [ eax + 8 ] , mm4 ;
add eax , 10 h ;
cmp ecx , 01 h ; see if we can quit
jg BEGINLOAD ; Kipp Owens is a doof . . .
jl END ; Nick Shaffner is killing me . . .
movsx edi , WORD PTR [ edx + 2 * esi ] ; load first 16 bit val and zero - extend
imul edi , vol0 ; multiply pData [ sampleIndex ] by volume [ 0 ]
sar edi , 08 h ; divide by 256
add DWORD PTR [ eax ] , edi ; add to pOutput [ i ] . left
movsx edi , WORD PTR [ edx + 2 * esi ] ; load same 16 bit val and zero - extend ( cuz I thrashed the reg )
imul edi , vol1 ; multiply pData [ sampleIndex ] by volume [ 1 ]
sar edi , 08 h ; divide by 256
add DWORD PTR [ eax + 04 h ] , edi ; add to pOutput [ i ] . right
END :
emms ;
}
# endif
}
void SW_Mix16Mono_NoShift ( portable_samplepair_t * pOutput , int * volume , short * pData , int outCount )
{
int vol0 = volume [ 0 ] ;
int vol1 = volume [ 1 ] ;
# if !id386
for ( int i = 0 ; i < outCount ; i + + )
{
int x = * pData + + ;
pOutput [ i ] . left + = ( x * vol0 ) > > 8 ;
pOutput [ i ] . right + = ( x * vol1 ) > > 8 ;
}
# else
__asm
{
mov eax , volume ;
movq mm0 , DWORD PTR [ eax ] ; vol1 , vol0 ( 32 - bits each )
packssdw mm0 , mm0 ; pack and replicate . . . vol1 , vol0 , vol1 , vol0 ( 16 - bits each )
//pxor mm7, mm7 ; mm7 is my zero register...
mov eax , DWORD PTR [ pOutput ] ; store initial output ptr
mov edx , DWORD PTR [ pData ] ; store initial input ptr
mov ecx , outCount ;
BEGINLOAD :
movd mm2 , WORD PTR [ edx ] ; load first piece o data from pData
punpcklwd mm2 , mm2 ; 0 , 0 , pData_1st , pData_1st
add edx , 2 ; move to the next sample
movd mm3 , WORD PTR [ edx ] ; load second piece o data from pData
punpcklwd mm3 , mm3 ; 0 , 0 , pData_2nd , pData_2nd
punpckldq mm2 , mm3 ; pData_2nd , pData_2nd , pData_2nd , pData_2nd
add edx , 2 ; move to the next sample
movq mm3 , mm2 ; copy the goods
pmullw mm2 , mm0 ; pData_2nd * vol1 , pData_2nd * vol0 , pData_1st * vol1 , pData_1st * vol0 ( bits 0 - 15 )
pmulhw mm3 , mm0 ; pData_2nd * vol1 , pData_2nd * vol0 , pData_1st * vol1 , pData_1st * vol0 ( bits 16 - 31 )
movq mm4 , mm2 ; copy
movq mm5 , mm3 ; copy
punpcklwd mm2 , mm3 ; pData_1st * vol1 , pData_1st * vol0 ( bits 0 - 31 )
punpckhwd mm4 , mm5 ; pData_2nd * vol1 , pData_2nd * vol0 ( bits 0 - 31 )
psrad mm2 , 8 ; shift right by 8
psrad mm4 , 8 ; shift right by 8
add ecx , - 2 ; decrement i - value
paddd mm2 , QWORD PTR [ eax ] ; add to existing vals
paddd mm4 , QWORD PTR [ eax + 8 ] ;
movq QWORD PTR [ eax ] , mm2 ; store back
movq QWORD PTR [ eax + 8 ] , mm4 ;
add eax , 10 h ;
cmp ecx , 01 h ; see if we can quit
jg BEGINLOAD ; I can cut and paste code !
jl END ;
movsx edi , WORD PTR [ edx ] ; load first 16 bit val and zero - extend
mov esi , edi ; save a copy for the other channel
imul edi , vol0 ; multiply pData [ sampleIndex ] by volume [ 0 ]
sar edi , 08 h ; divide by 256
add DWORD PTR [ eax ] , edi ; add to pOutput [ i ] . left
; esi has a copy , use it now
imul esi , vol1 ; multiply pData [ sampleIndex ] by volume [ 1 ]
sar esi , 08 h ; divide by 256
add DWORD PTR [ eax + 04 h ] , esi ; add to pOutput [ i ] . right
END :
emms ;
}
# endif
}
void SW_Mix16Mono ( portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
if ( rateScaleFix = = FIX ( 1 ) )
{
SW_Mix16Mono_NoShift ( pOutput , volume , pData , outCount ) ;
}
else
{
SW_Mix16Mono_Shift ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
}
}
// interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
// pData buffer, ensuring we can always provide 'outCount' samples.
void SW_Mix16Mono_Interp ( portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
fixedint sampleIndex = 0 ;
fixedint rateScaleFix14 = FIX_28TO14 ( rateScaleFix ) ; // convert 28 bit fixed point to 14 bit fixed point
fixedint sampleFrac14 = FIX_28TO14 ( inputOffset ) ;
int first , second , interp ;
for ( int i = 0 ; i < outCount ; i + + )
{
first = ( int ) ( pData [ sampleIndex ] ) ;
second = ( int ) ( pData [ sampleIndex + 1 ] ) ;
interp = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
pOutput [ i ] . left + = ( volume [ 0 ] * interp ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * interp ) > > 8 ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
}
void SW_Mix16Stereo ( portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
int sampleIndex = 0 ;
fixedint sampleFrac = inputOffset ;
for ( int i = 0 ; i < outCount ; i + + )
{
pOutput [ i ] . left + = ( volume [ 0 ] * ( int ) ( pData [ sampleIndex ] ) ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * ( int ) ( pData [ sampleIndex + 1 ] ) ) > > 8 ;
sampleFrac + = rateScaleFix ;
sampleIndex + = FIX_INTPART ( sampleFrac ) < < 1 ;
sampleFrac = FIX_FRACPART ( sampleFrac ) ;
}
}
// interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
// pData buffer, ensuring we can always provide 'outCount' samples.
void SW_Mix16Stereo_Interp ( portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
fixedint sampleIndex = 0 ;
fixedint rateScaleFix14 = FIX_28TO14 ( rateScaleFix ) ; // convert 28 bit fixed point to 14 bit fixed point
fixedint sampleFrac14 = FIX_28TO14 ( inputOffset ) ;
int first , second , interpl , interpr ;
for ( int i = 0 ; i < outCount ; i + + )
{
first = ( int ) ( pData [ sampleIndex ] ) ;
second = ( int ) ( pData [ sampleIndex + 2 ] ) ;
interpl = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
first = ( int ) ( pData [ sampleIndex + 1 ] ) ;
second = ( int ) ( pData [ sampleIndex + 3 ] ) ;
interpr = first + ( ( ( second - first ) * ( int ) sampleFrac14 ) > > 14 ) ;
pOutput [ i ] . left + = ( volume [ 0 ] * interpl ) > > 8 ;
pOutput [ i ] . right + = ( volume [ 1 ] * interpr ) > > 8 ;
sampleFrac14 + = rateScaleFix14 ;
sampleIndex + = FIX_INTPART14 ( sampleFrac14 ) < < 1 ;
sampleFrac14 = FIX_FRACPART14 ( sampleFrac14 ) ;
}
}
// return true if mixer should use high quality pitch interpolation for this sound
bool FUseHighQualityPitch ( channel_t * pChannel )
{
// do not use interpolating pitch shifter if:
// low quality flag set on sound (ie: wave name is prepended with CHAR_FAST_PITCH)
// or pitch has no fractional part
// or snd_pitchquality is 0
if ( ! snd_pitchquality . GetInt ( ) | | pChannel - > flags . bfast_pitch )
return false ;
return ( ( pChannel - > pitch ! = floor ( pChannel - > pitch ) ) ) ;
}
//===============================================================================
// DISPATCHERS FOR MIXING ROUTINES
//===============================================================================
void Mix8MonoWavtype ( channel_t * pChannel , portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix8Mono_Interp ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
SW_Mix8Mono ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
}
void Mix16MonoWavtype ( channel_t * pChannel , portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix16Mono_Interp ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
// fast native coded mixers with lower quality pitch shift
SW_Mix16Mono ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
}
void Mix8StereoWavtype ( channel_t * pChannel , portable_samplepair_t * pOutput , int * volume , byte * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
switch ( pChannel - > wavtype )
{
case CHAR_DOPPLER :
SW_Mix8StereoDopplerLeft ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
SW_Mix8StereoDopplerRight ( pOutput , & volume [ IFRONT_LEFTD ] , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
case CHAR_DIRECTIONAL :
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix8StereoDirectional_Interp ( pChannel - > dspface , pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
SW_Mix8StereoDirectional ( pChannel - > dspface , pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
case CHAR_DISTVARIANT :
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix8StereoDistVar_Interp ( pChannel - > distmix , pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
SW_Mix8StereoDistVar ( pChannel - > distmix , pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
case CHAR_OMNI :
// non directional stereo - all channel volumes are the same
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix8Stereo_Interp ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
SW_Mix8Stereo ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
default :
case CHAR_SPATIALSTEREO :
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix8Stereo_Interp ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
SW_Mix8Stereo ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
}
}
void Mix16StereoWavtype ( channel_t * pChannel , portable_samplepair_t * pOutput , int * volume , short * pData , int inputOffset , fixedint rateScaleFix , int outCount )
{
switch ( pChannel - > wavtype )
{
case CHAR_DOPPLER :
SW_Mix16StereoDopplerLeft ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
SW_Mix16StereoDopplerRight ( pOutput , & volume [ IFRONT_LEFTD ] , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
case CHAR_DIRECTIONAL :
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix16StereoDirectional_Interp ( pChannel - > dspface , pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
SW_Mix16StereoDirectional ( pChannel - > dspface , pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
case CHAR_DISTVARIANT :
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix16StereoDistVar_Interp ( pChannel - > distmix , pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
SW_Mix16StereoDistVar ( pChannel - > distmix , pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
case CHAR_OMNI :
// non directional stereo - all channel volumes are same
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix16Stereo_Interp ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
SW_Mix16Stereo ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
default :
case CHAR_SPATIALSTEREO :
if ( FUseHighQualityPitch ( pChannel ) )
SW_Mix16Stereo_Interp ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
else
SW_Mix16Stereo ( pOutput , volume , pData , inputOffset , rateScaleFix , outCount ) ;
break ;
}
}
//===============================================================================
// Client entity mouth movement code. Set entity mouthopen variable, based
// on the sound envelope of the voice channel playing.
// KellyB 10/22/97
//===============================================================================
extern IBaseClientDLL * g_ClientDLL ;
// called when voice channel is first opened on this entity
static CMouthInfo * GetMouthInfoForChannel ( channel_t * pChannel )
{
# ifndef DEDICATED
// If it's a sound inside the client UI, ask the client for the mouthinfo
if ( pChannel - > soundsource = = SOUND_FROM_UI_PANEL )
return g_ClientDLL ? g_ClientDLL - > GetClientUIMouthInfo ( ) : NULL ;
# endif
int mouthentity = pChannel - > speakerentity = = - 1 ? pChannel - > soundsource : pChannel - > speakerentity ;
IClientEntity * pClientEntity = entitylist - > GetClientEntity ( mouthentity ) ;
if ( ! pClientEntity )
return NULL ;
return pClientEntity - > GetMouth ( ) ;
}
void SND_InitMouth ( channel_t * pChannel )
{
if ( SND_IsMouth ( pChannel ) )
{
CMouthInfo * pMouth = GetMouthInfoForChannel ( pChannel ) ;
// init mouth movement vars
if ( pMouth )
{
pMouth - > mouthopen = 0 ;
pMouth - > sndavg = 0 ;
pMouth - > sndcount = 0 ;
if ( pChannel - > sfx - > pSource & & pChannel - > sfx - > pSource - > GetSentence ( ) )
{
pMouth - > AddSource ( pChannel - > sfx - > pSource , pChannel - > flags . m_bIgnorePhonemes ) ;
}
}
}
}
// called when channel stops
void SND_CloseMouth ( channel_t * pChannel )
{
if ( SND_IsMouth ( pChannel ) )
{
CMouthInfo * pMouth = GetMouthInfoForChannel ( pChannel ) ;
if ( pMouth )
{
// shut mouth
int idx = pMouth - > GetIndexForSource ( pChannel - > sfx - > pSource ) ;
if ( idx ! = UNKNOWN_VOICE_SOURCE )
{
pMouth - > RemoveSourceByIndex ( idx ) ;
}
else
{
pMouth - > ClearVoiceSources ( ) ;
}
pMouth - > mouthopen = 0 ;
}
}
}
# define CAVGSAMPLES 10
// need this to make the debug code below work.
//#include "snd_wave_source.h"
void SND_MoveMouth8 ( channel_t * ch , CAudioSource * pSource , int count )
{
int data ;
char * pdata = NULL ;
int i ;
int savg ;
int scount ;
CMouthInfo * pMouth = GetMouthInfoForChannel ( ch ) ;
if ( ! pMouth )
return ;
if ( pSource - > GetSentence ( ) )
{
int idx = pMouth - > GetIndexForSource ( pSource ) ;
if ( idx = = UNKNOWN_VOICE_SOURCE )
{
if ( pMouth - > AddSource ( pSource , ch - > flags . m_bIgnorePhonemes ) = = NULL )
{
DevMsg ( 1 , " out of voice sources, won't lipsync %s \n " , ch - > sfx - > getname ( ) ) ;
#if 0
for ( int i = 0 ; i < pMouth - > GetNumVoiceSources ( ) ; i + + )
{
CVoiceData * pVoice = pMouth - > GetVoiceSource ( i ) ;
CAudioSourceWave * pWave = dynamic_cast < CAudioSourceWave * > ( pVoice - > GetSource ( ) ) ;
const char * pName = " unknown " ;
if ( pWave & & pWave - > GetName ( ) )
pName = pWave - > GetName ( ) ;
Msg ( " Playing %s... \n " , pName ) ;
}
# endif
}
}
else
{
// Update elapsed time from mixer
CVoiceData * vd = pMouth - > GetVoiceSource ( idx ) ;
Assert ( vd ) ;
if ( vd )
{
Assert ( pSource - > SampleRate ( ) > 0 ) ;
float elapsed = ( float ) ch - > pMixer - > GetSamplePosition ( ) / ( float ) pSource - > SampleRate ( ) ;
vd - > SetElapsedTime ( elapsed ) ;
}
}
}
if ( IsX360 ( ) )
{
// not supporting because data is assumed to be 8 bit and bypasses mixer (decoding)
return ;
}
if ( pMouth - > NeedsEnvelope ( ) )
{
int availableSamples = pSource - > GetOutputData ( ( void * * ) & pdata , ch - > pMixer - > GetSamplePosition ( ) , count , NULL ) ;
if ( pdata = = NULL )
return ;
i = 0 ;
scount = pMouth - > sndcount ;
savg = 0 ;
while ( i < availableSamples & & scount < CAVGSAMPLES )
{
data = pdata [ i ] ;
savg + = abs ( data ) ;
i + = 80 + ( ( byte ) data & 0x1F ) ;
scount + + ;
}
pMouth - > sndavg + = savg ;
pMouth - > sndcount = ( byte ) scount ;
if ( pMouth - > sndcount > = CAVGSAMPLES )
{
pMouth - > mouthopen = pMouth - > sndavg / CAVGSAMPLES ;
pMouth - > sndavg = 0 ;
pMouth - > sndcount = 0 ;
}
}
else
{
pMouth - > mouthopen = 0 ;
}
}
void SND_UpdateMouth ( channel_t * pChannel )
{
CMouthInfo * m = GetMouthInfoForChannel ( pChannel ) ;
if ( ! m )
return ;
if ( pChannel - > sfx )
{
m - > AddSource ( pChannel - > sfx - > pSource , pChannel - > flags . m_bIgnorePhonemes ) ;
}
}
void SND_ClearMouth ( channel_t * pChannel )
{
CMouthInfo * m = GetMouthInfoForChannel ( pChannel ) ;
if ( ! m )
return ;
if ( pChannel - > sfx )
{
m - > RemoveSource ( pChannel - > sfx - > pSource ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pChannel -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool SND_IsMouth ( channel_t * pChannel )
{
# ifndef DEDICATED
if ( pChannel - > soundsource = = SOUND_FROM_UI_PANEL )
return true ;
# endif
if ( ! entitylist )
{
return false ;
}
if ( pChannel - > entchannel = = CHAN_VOICE | | pChannel - > entchannel = = CHAN_VOICE2 )
{
return true ;
}
if ( pChannel - > sfx & &
pChannel - > sfx - > pSource & &
pChannel - > sfx - > pSource - > GetSentence ( ) )
{
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pChannel -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool SND_ShouldPause ( channel_t * pChannel )
{
return pChannel - > flags . m_bShouldPause ;
}
//===============================================================================
// Movie recording support
//===============================================================================
void SND_RecordInit ( )
{
g_paintedtime = 0 ;
g_soundtime = 0 ;
// TMP Wave file supports stereo only, so force stereo
if ( snd_surround . GetInt ( ) ! = 2 )
{
snd_surround . SetValue ( 2 ) ;
}
}
void SND_MovieStart ( void )
{
if ( IsX360 ( ) )
return ;
if ( ! cl_movieinfo . IsRecording ( ) )
return ;
SND_RecordInit ( ) ;
// 44k: engine playback rate is now 44100...changed from 22050
if ( cl_movieinfo . DoWav ( ) )
{
WaveCreateTmpFile ( cl_movieinfo . moviename , SOUND_DMA_SPEED , 16 , 2 ) ;
}
}
void SND_MovieEnd ( void )
{
if ( IsX360 ( ) )
return ;
if ( ! cl_movieinfo . IsRecording ( ) )
{
return ;
}
if ( cl_movieinfo . DoWav ( ) )
{
WaveFixupTmpFile ( cl_movieinfo . moviename ) ;
}
}
bool SND_IsRecording ( )
{
return ( ( IsReplayRendering ( ) | | cl_movieinfo . IsRecording ( ) ) & & ! Con_IsVisible ( ) ) ;
}
extern IVideoRecorder * g_pVideoRecorder ;
void SND_RecordBuffer ( void )
{
if ( IsX360 ( ) )
return ;
if ( ! SND_IsRecording ( ) )
return ;
int i ;
int val ;
int bufferSize = snd_linear_count * sizeof ( short ) ;
short * tmp = ( short * ) _alloca ( bufferSize ) ;
for ( i = 0 ; i < snd_linear_count ; i + = 2 )
{
val = ( snd_p [ i ] * snd_vol ) > > 8 ;
tmp [ i ] = CLIP ( val ) ;
val = ( snd_p [ i + 1 ] * snd_vol ) > > 8 ;
tmp [ i + 1 ] = CLIP ( val ) ;
}
if ( IsReplayRendering ( ) )
{
# if defined( REPLAY_ENABLED )
extern IClientReplayContext * g_pClientReplayContext ;
IReplayMovieRenderer * pMovieRenderer = g_pClientReplayContext - > GetMovieRenderer ( ) ;
if ( IsReplayRendering ( ) & & pMovieRenderer & & pMovieRenderer - > IsAudioSyncFrame ( ) )
{
pMovieRenderer - > RenderAudio ( ( unsigned char * ) tmp , bufferSize , snd_linear_count ) ;
}
# endif
}
else
{
if ( cl_movieinfo . DoWav ( ) )
{
WaveAppendTmpFile ( cl_movieinfo . moviename , tmp , 16 , snd_linear_count ) ;
}
if ( cl_movieinfo . DoVideoSound ( ) )
{
g_pVideoRecorder - > AppendAudioSamples ( tmp , bufferSize ) ;
}
}
}