1251 lines
40 KiB
C++
Raw Permalink Normal View History

2021-07-24 21:11:47 -07:00
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Device Common Base Class.
//
//=====================================================================================//
#include "audio_pch.h"
#include "../../cl_splitscreen.h"
#include "snd_dma.h"
#include "../../debugoverlay.h"
#include "server.h"
#include "client.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
#define ISPEAKER_RIGHT_FRONT 0
#define ISPEAKER_LEFT_FRONT 1
#define ISPEAKER_RIGHT_REAR 2
#define ISPEAKER_LEFT_REAR 3
#define ISPEAKER_CENTER_FRONT 4
#define ISPEAKER_DIR_STEREO 99
extern Vector listener_right[ MAX_SPLITSCREEN_CLIENTS ];
extern void DEBUG_StartSoundMeasure(int type, int samplecount );
extern void DEBUG_StopSoundMeasure(int type, int samplecount );
extern bool MIX_ScaleChannelVolume( paintbuffer_t *pPaint, channel_t *pChannel, float volume[CCHANVOLUMES], int mixchans );
inline bool FVolumeFrontNonZero( float *pvol )
{
return (pvol[IFRONT_RIGHT] || pvol[IFRONT_LEFT]);
}
inline bool FVolumeRearNonZero( float *pvol )
{
return (pvol[IREAR_RIGHT] || pvol[IREAR_LEFT]);
}
inline bool FVolumeCenterNonZero( float *pvol )
{
return (pvol[IFRONT_CENTER] != 0);
}
// fade speaker volumes to mono, based on xfade value.
// ie: xfade 1.0 is full mono.
// ispeaker is speaker index, cspeaker is total # of speakers
// fmix2channels causes mono mix for 4 channel mix to mix down to 2 channels
// this is used for the 2 speaker outpu case, which uses recombined 4 channel front/rear mixing
static float XfadeSpeakerVolToMono( float scale, float xfade, int ispeaker, int cspeaker, bool fmix2channels )
{
float scale_out;
float scale_target;
if (cspeaker == 4 )
{
// mono sound distribution:
float scale_targets[] = {0.9, 0.9, 0.9, 0.9}; // RF, LF, RR, LR
float scale_targets2ch[] = {0.9, 0.9, 0.0, 0.0}; // RF, LF, RR, LR
if ( fmix2channels )
scale_target = scale_targets2ch[(int)clamp(ispeaker, 0, 3)];
else
scale_target = scale_targets[(int)clamp(ispeaker, 0, 3)];
goto XfadeExit;
}
if (cspeaker == 5 )
{
// mono sound distribution:
float scale_targets[] = {0.9, 0.9, 0.5, 0.5, 0.9}; // RF, LF, RR, LR, FC
scale_target = scale_targets[(int)clamp(ispeaker, 0, 4)];
goto XfadeExit;
}
// if (cspeaker == 2 )
scale_target = 0.9; // front 2 speakers in stereo each get 50% of total volume in mono case
XfadeExit:
scale_out = scale + (scale_target - scale) * xfade;
return scale_out;
}
// given:
// 2d yaw angle to sound source (0-360), where 0 is listener_right
// pitch angle to source
// angle to speaker position (0-360), where 0 is listener_right
// speaker index
// speaker total count,
// return: scale from 0-1.0 for speaker volume.
// NOTE: as pitch angle goes to +/- 90, sound goes to mono, all speakers.
#define PITCH_ANGLE_THRESHOLD 45.0
#define REAR_VOL_DROP 0.5
#define VOLCURVEPOWER 1.5 // 1.0 is a linear crossfade of volume between speakers.
// 1.5 provides a smoother, nonlinear volume transition - this is done
// because a volume of 255 played in a single speaker is
// percieved as louder than 128 + 128 in two speakers
// separated by at least 45 degrees. The nonlinear curve
// gives the volume boost needed.
static float GetSpeakerVol( float yaw_source, float pitch_source, float mono, float yaw_speaker, int ispeaker, int cspeaker, bool fmix2channels )
{
float adif = fabs(yaw_source - yaw_speaker);
float pitch_angle = pitch_source;
float scale = 0.0;
float xfade = 0.0;
if (adif > 180)
adif = 360 - adif;
// mono goes from 0.0 to 1.0 as listener moves into 'mono' radius of sound source.
// Also, as pitch_angle to sound source approaches 90 (sound above/below listener), sounds become mono.
// convert pitch angle to 0-90 absolute pitch
if (pitch_angle < 0)
pitch_angle += 360;
if (pitch_angle > 180)
pitch_angle = 360 - pitch_angle;
if (pitch_angle > 90)
pitch_angle = 90 - (pitch_angle - 90);
// calculate additional mono crossfade due to pitch angle
if (pitch_angle > PITCH_ANGLE_THRESHOLD)
{
xfade = (pitch_angle - PITCH_ANGLE_THRESHOLD) / (90.0 - PITCH_ANGLE_THRESHOLD); // 0.0 -> 1.0 as angle 45->90
mono += xfade;
mono = clamp(mono, 0.0, 1.0);
}
if (cspeaker == 2)
{
// 2 speaker (headphone) mix: speakers opposing, at 0 & 180 degrees
scale = (1.0 - FastPow(adif / 180.0, VOLCURVEPOWER));
goto GetVolExit;
}
if (adif >= 90.0)
goto GetVolExit; // 0.0 scale
if (cspeaker == 4)
{
// 4 ch surround: all speakers on 90 degree angles,
// scale ranges from 0.0 (at 90 degree difference between source and speaker)
// to 1.0 (0 degree difference between source and speaker)
if (ispeaker == ISPEAKER_DIR_STEREO)
{
scale = (1.0 - FastPow(adif / 135.0f, VOLCURVEPOWER));
}
else
{
scale = (1.0 - FastPow(adif / 90.0, VOLCURVEPOWER));
}
goto GetVolExit;
}
// 5 ch surround:
// rear speakers are on 90 degree angles and return 0.0->1.0 range over +/- 90 degrees each
// center speaker is on 45 degree angle to left/right front speaker
// center speaker has 0.0->1.0 range over 45 degrees
switch (ispeaker)
{
default:
case ISPEAKER_RIGHT_REAR:
case ISPEAKER_LEFT_REAR:
{
// rear speakers get +/- 90 degrees of linear scaling...
scale = (1.0 - FastPow(adif / 90.0, VOLCURVEPOWER));
break;
}
case ISPEAKER_CENTER_FRONT:
{
// center speaker gets +/- 45 degrees of linear scaling...
if (adif > 45.0)
goto GetVolExit; // 0.0 scale
scale = (1.0 - FastPow(adif / 45.0, VOLCURVEPOWER));
break;
}
case ISPEAKER_RIGHT_FRONT:
{
if (yaw_source > yaw_speaker)
{
// if sound source is between right front speaker and center speaker,
// apply scaling over 75 degrees...
if (adif > 75.0)
goto GetVolExit; // 0.0 scale
scale = (1.0 - FastPow(adif / 75.0, VOLCURVEPOWER));
}
/*
if (yaw_source > yaw_speaker && yaw_source < (yaw_speaker + 90.0))
{
// if sound source is between right front speaker and center speaker,
// apply scaling over 45 degrees...
if (adif > 45.0)
goto GetVolExit; // 0.0 scale
scale = (1.0 - FastPow(adif/45.0, VOLCURVEPOWER));
}
*/
else
{
// sound source is CW from right speaker, apply scaling over 90 degrees...
scale = (1.0 - FastPow(adif / 90.0, VOLCURVEPOWER));
}
break;
}
case ISPEAKER_LEFT_FRONT:
{
if (yaw_source < yaw_speaker)
{
// if sound source is between left front speaker and center speaker,
// apply scaling over 75 degrees...
if (adif > 75.0)
goto GetVolExit; // 0.0 scale
scale = (1.0 - FastPow(adif / 75.0, VOLCURVEPOWER));
}
/*
if (yaw_source < yaw_speaker && yaw_source > (yaw_speaker - 90.0))
{
// if sound source is between left front speaker and center speaker,
// apply scaling over 45 degrees...
if (adif > 45.0)
goto GetVolExit; // 0.0 scale
scale = (1.0 - FastPow(adif/45.0, VOLCURVEPOWER));
}
*/
else
{
// sound source is CW from right speaker, apply scaling over 90 degrees...
scale = (1.0 - FastPow(adif / 90.0, VOLCURVEPOWER));
}
break;
}
}
GetVolExit:
Assert(mono <= 1.0 && mono >= 0.0);
Assert(scale <= 1.0 && scale >= 0.0);
// crossfade speaker volumes towards mono with increased pitch angle of sound source
scale = XfadeSpeakerVolToMono(scale, mono, ispeaker, cspeaker, fmix2channels);
Assert(scale <= 1.0 && scale >= 0.0);
return scale;
}
extern ConVar sv_cheats;
// counterstrike options
#define SND_SVACTIVE_CONCMD( VARIABLENAME, DEFAULTVALUE, DESCSTRING ) \
float g_##VARIABLENAME = DEFAULTVALUE; \
CON_COMMAND( VARIABLENAME , DESCSTRING ) \
{ \
if ( args.ArgC () < 2 ) \
{ \
ConMsg ("%s = %f\n", #VARIABLENAME, g_##VARIABLENAME ); \
} \
else if ( sv.IsActive() && !sv_cheats.GetBool() ) \
{ \
ConMsg( "Cannot change %s while the server is running and sv_cheats = 0. To change settings set sv_cheats 1\n", #VARIABLENAME ); \
return; \
} \
else \
{ \
g_##VARIABLENAME = V_atof( args[1] ); \
} \
} \
// headphones
SND_SVACTIVE_CONCMD( snd_front_headphone_position, 90.0, "Specifies the position (in degrees) of the virtual front left/right headphones." )
SND_SVACTIVE_CONCMD( snd_rear_headphone_position, 90.0, "Specifies the position (in degrees) of the virtual rear left/right headphones." )
// speakers
SND_SVACTIVE_CONCMD( snd_front_stereo_speaker_position, 90.0, "Specifies the position (in degrees) of the virtual front left/right speakers." );
SND_SVACTIVE_CONCMD( snd_rear_stereo_speaker_position, 90.0, "Specifies the position (in degrees) of the virtual rear left/right speakers." );
// speakers 4, 5 or 7
SND_SVACTIVE_CONCMD( snd_front_surround_speaker_position, 45.0, "Specifies the position (in degrees) of the virtual front left/right speakers." );
SND_SVACTIVE_CONCMD( snd_rear_surround_speaker_position, 135.0,"Specifies the position (in degrees) of the virtual rear left/right speakers." );
// given unit vector from listener to sound source,
// determine proportion of volume for sound in FL, FC, FR, RL, RR quadrants
// Scale this proportion by the distance scalar 'gain'
// If sound has 'mono' radius, blend sound to mono over 50% of radius.
SND_SVACTIVE_CONCMD( snd_headphone_pan_exponent, 1.0, "Specifies the exponent for the pan xfade from phone to phone if the \"exp\" pan law is being used." );
SND_SVACTIVE_CONCMD( snd_stereo_speaker_pan_exponent, 1.5, "Specifies the exponent for the pan xfade from speaker to speaker if the \"exp\" pan law is being used." );
SND_SVACTIVE_CONCMD( snd_surround_speaker_pan_exponent, 1.5, "Specifies the exponent for the pan xfade from speaker to speaker if the \"exp\" pan law is being used." );
SND_SVACTIVE_CONCMD( snd_headphone_pan_radial_weight, 1.0, "Apply cos(angle) * weight before pan law" );
SND_SVACTIVE_CONCMD( snd_stereo_speaker_pan_radial_weight, 0.0, "Apply cos(angle) * weight before pan law" );
SND_SVACTIVE_CONCMD( snd_surround_speaker_pan_radial_weight, 0.0, "Apply cos(angle) * weight before pan law" );
enum snd_pan_mode_t
{
SND_PAN_EXP = 0,
SND_PAN_EQ_POW,
SND_PAN_GLDSRC
};
snd_pan_mode_t g_snd_headphone_pan_mode = SND_PAN_EXP;
snd_pan_mode_t g_snd_stereo_speaker_pan_mode = SND_PAN_EXP;
snd_pan_mode_t g_snd_surround_speaker_pan_mode = SND_PAN_EXP;
// SND_SVACTIVE_CONCMD( snd_headphone_pan_mode, 0.0, "Specifies which pan law to use when monitoring through headphones. < 0 = exp >, < 1 = equal power >, < 2 = goldsrc >", int );
// SND_SVACTIVE_CONCMD( snd_stereo_speaker_pan_mode, 0.0, "Specifies which pan law to use when monitoring through stereo speakers. < 0 = exp >, < 1 = equal power >, < 2 = goldsrc >", int );
// SND_SVACTIVE_CONCMD( snd_surround_speaker_pan_mode, 0.0, "Specifies which pan law to use when monitoring through stereo speakers. < 0 = exp >, < 1 = equal power >, < 2 = goldsrc >", int );
extern ConVar snd_debug_panlaw;
void DEBUG_DrawPanCurvesLocation( float flYawSource, float flSpeakerMin, float flSpeakerMax, float flFactor, float flMinValue, float flMaxValue )
{
float startY = 0.66;
float totalY = 0.25;
float startX = 0.0;
float endX = 0.25 * ( 9.0 / 16.0 );
float stepX = (float)(endX / (float)180);
float nSource = flFactor * 180;
float flMinY = flMinValue * totalY;
float flMaxY = flMaxValue * totalY;
CDebugOverlay::AddScreenTextOverlay( startX + ( stepX * nSource ), startY - ( flMinY ) - .01, .01, 255, 0, 0, 255, "#" );
CDebugOverlay::AddScreenTextOverlay( startX + ( stepX * nSource ), startY - ( flMinY ) - .02, .01, 255, 0, 0, 255, "#" );
CDebugOverlay::AddScreenTextOverlay( startX + ( stepX * nSource ), startY - ( flMaxY ) + .01, .01, 255, 0, 0, 255, "#" );
CDebugOverlay::AddScreenTextOverlay( startX + ( stepX * nSource ), startY - ( flMaxY ) + .02, .01, 255, 0, 0, 255, "#" );
startX += (((float) 1.5 * endX) + 0.01 );
float flMidX = startX + ( endX * 0.5 );
float flMidY = startY - ( totalY * 0.5);
float flSin, flCos;
FastSinCos( flYawSource * 0.01745329251994329500 , &flSin, &flCos );
CDebugOverlay::AddScreenTextOverlay(flMidX + flCos * endX *0.53, flMidY + -(flSin*totalY*0.53), .01, 255, 0, 0, 255, "#");
}
// given unit vector from listener to sound source,
// determine proportion of volume for sound in FL, FC, FR, RL, RR quadrants
// Scale this proportion by the distance scalar 'gain'
// If sound has 'mono' radius, blend sound to mono over 50% of radius.
void Device_SpatializeChannel( int nSlot, float volume[CCHANVOLUMES/2], int master_vol, const Vector& sourceDir, float gain, float mono, int nWaveType )
{
VPROF("Device_SpatializeChannel");
float rfscale, rrscale, lfscale, lrscale, fcscale;
fcscale = rfscale = lfscale = rrscale = lrscale = 0.0;
bool bSurround = g_AudioDevice->IsSurround();
bool bSurroundCenter = g_AudioDevice->IsSurroundCenter();
bool bHeadphone = g_AudioDevice->IsHeadphone();
// clear volumes
for (int i = 0; i < CCHANVOLUMES/2; i++)
volume[i] = 0;
// linear crossfader for 2, 4 or 5 speakers, using polar coord. separation angle as linear basis
// get pitch & yaw angle from listener origin to sound source
QAngle angles;
float pitch;
float source_yaw;
float yaw;
VectorAngles(sourceDir, angles);
pitch = angles[PITCH];
source_yaw = angles[YAW];
// get 2d listener yaw angle from listener right
QAngle angles2d;
Vector source2d;
float listener_yaw;
source2d.x = listener_right[ nSlot ].x;
source2d.y = listener_right[ nSlot ].y;
source2d.z = 0.0;
VectorNormalize(source2d);
// convert right vector to euler angles (yaw & pitch)
VectorAngles(source2d, angles2d);
listener_yaw = angles2d[YAW];
// get yaw of sound source, with listener_yaw as reference 0.
yaw = source_yaw - listener_yaw;
if (yaw < 0)
yaw += 360;
if ( !bSurround )
{
// 2 ch stereo mixing
if ( bHeadphone )
{
// headphone mix: (NO HRTF)
rfscale = GetSpeakerVol( yaw, pitch, mono, 0.0, ISPEAKER_RIGHT_FRONT, 2, false);
lfscale = GetSpeakerVol( yaw, pitch, mono, 180.0, ISPEAKER_LEFT_FRONT, 2, false );
}
else
{
// stereo speakers at 45 & 135 degrees: (mono sounds mix down to 2 channels)
rfscale = GetSpeakerVol( yaw, pitch, mono, 45.0, ISPEAKER_RIGHT_FRONT, 4, true );
lfscale = GetSpeakerVol( yaw, pitch, mono, 135.0, ISPEAKER_LEFT_FRONT, 4, true );
rrscale = GetSpeakerVol( yaw, pitch, mono, 315.0, ISPEAKER_RIGHT_REAR, 4, true );
lrscale = GetSpeakerVol( yaw, pitch, mono, 225.0, ISPEAKER_LEFT_REAR, 4, true );
// add sounds coming from rear (quieter)
rfscale = clamp((rfscale + rrscale * 0.75), 0.0, 1.0);
lfscale = clamp((lfscale + lrscale * 0.75), 0.0, 1.0);
rrscale = 0;
lrscale = 0;
//DevMsg("lfscale=%f rfscale=%f lrscale=%f rrscale=%f\n",lfscale,rfscale,lrscale,rrscale);
//DevMsg("pitch=%f yaw=%f \n",pitch, yaw);
}
goto SpatialExit;
}
if ( bSurround && !bSurroundCenter )
{
// 4 ch surround
// linearly scale with radial distance from asource to FR, FL, RR, RL
// where FR = 45 degrees, FL = 135, RR = 315 (-45), RL = 225 (-135)
if ( nWaveType == CHAR_DIRSTEREO )
{
// select a different speaker falloff curve specifically for this mode
rfscale = GetSpeakerVol( yaw, pitch, mono, 45.0, ISPEAKER_DIR_STEREO, 4, false );
lfscale = GetSpeakerVol( yaw, pitch, mono, 135.0, ISPEAKER_DIR_STEREO, 4, false );
rrscale = GetSpeakerVol( yaw, pitch, mono, 315.0, ISPEAKER_DIR_STEREO, 4, false );
lrscale = GetSpeakerVol( yaw, pitch, mono, 225.0, ISPEAKER_DIR_STEREO, 4, false );
}
else
{
rfscale = GetSpeakerVol( yaw, pitch, mono, 45.0, ISPEAKER_RIGHT_FRONT, 4, false );
lfscale = GetSpeakerVol( yaw, pitch, mono, 135.0, ISPEAKER_LEFT_FRONT, 4, false );
rrscale = GetSpeakerVol( yaw, pitch, mono, 315.0, ISPEAKER_RIGHT_REAR, 4, false );
lrscale = GetSpeakerVol( yaw, pitch, mono, 225.0, ISPEAKER_LEFT_REAR, 4, false );
}
// DevMsg("lfscale=%f rfscale=%f lrscale=%f rrscale=%f\n",lfscale,rfscale,lrscale,rrscale);
// DevMsg("pitch=%f yaw=%f \n",pitch, yaw);
goto SpatialExit;
}
if ( bSurround && bSurroundCenter )
{
// 5 ch surround
// linearly scale with radial distance from asource to FR, FC, FL, RR, RL
// where FR = 45 degrees, FC = 90, FL = 135, RR = 315 (-45), RL = 225 (-135)
rfscale = GetSpeakerVol( yaw, pitch, mono, 45.0, ISPEAKER_RIGHT_FRONT, 5, false );
fcscale = GetSpeakerVol( yaw, pitch, mono, 90.0, ISPEAKER_CENTER_FRONT, 5, false );
lfscale = GetSpeakerVol( yaw, pitch, mono, 135.0, ISPEAKER_LEFT_FRONT, 5, false );
rrscale = GetSpeakerVol( yaw, pitch, mono, 315.0, ISPEAKER_RIGHT_REAR, 5, false );
lrscale = GetSpeakerVol( yaw, pitch, mono, 225.0, ISPEAKER_LEFT_REAR, 5, false );
//DevMsg("lfscale=%f center= %f rfscale=%f lrscale=%f rrscale=%f\n",lfscale,fcscale, rfscale,lrscale,rrscale);
//DevMsg("pitch=%f yaw=%f \n",pitch, yaw);
goto SpatialExit;
}
SpatialExit:
// scale volumes in each quadrant by distance attenuation.
// volumes are 0-255:
// gain is 0.0->1.0, rscale is 0.0->1.0, so scale is 0.0->1.0
// master_vol is 0->255, so rightvol is 0->255
volume[IFRONT_RIGHT] = master_vol * gain * rfscale;
volume[IFRONT_LEFT] = master_vol * gain * lfscale;
volume[IFRONT_RIGHT] = clamp( volume[IFRONT_RIGHT], 0, 255 );
volume[IFRONT_LEFT] = clamp( volume[IFRONT_LEFT], 0, 255 );
if ( bSurround )
{
volume[IREAR_RIGHT] = master_vol * gain * rrscale;
volume[IREAR_LEFT] = master_vol * gain * lrscale;
volume[IREAR_RIGHT] = clamp( volume[IREAR_RIGHT], 0, 255 );
volume[IREAR_LEFT] = clamp( volume[IREAR_LEFT], 0, 255 );
if ( bSurroundCenter )
{
volume[IFRONT_CENTER] = master_vol * gain * fcscale;
volume[IFRONT_CENTER0] = 0.0;
volume[IFRONT_CENTER] = clamp( volume[IFRONT_CENTER], 0, 255);
}
}
}
void DEBUG_DrawSpeakerValues( float volume[CCHANVOLUMES/2] )
{
float startY = 0.66;
float totalY = 0.25;
float startX = 0.0;
float endX = 0.25 * ( 9.0 / 16.0 );
/* float stepX = (float)(endX / (float)180);*/
char valueString[32];
startX += (((float) 1.5 * endX) + 0.01 );
float flMidX = startX + ( endX * 0.5 );
/* float flMidY = startY - ( totalY * 0.5);*/
CDebugOverlay::AddScreenTextOverlay(flMidX , startY - totalY -0.04, .01, 0, 0, 255, 255, "#");
CDebugOverlay::AddScreenTextOverlay(flMidX , startY + 0.04, .01, 0, 0, 255, 255, "#");
sprintf( valueString, "%.3f", volume[IFRONT_CENTER] );
CDebugOverlay::AddScreenTextOverlay(flMidX -0.01 , startY - totalY -0.015, .01, 255, 255, 255, 255, valueString );
int nCenterVol = volume[IFRONT_CENTER] * 15;
for(int i = 1; i <= nCenterVol; i++ )
{
CDebugOverlay::AddScreenTextOverlay(flMidX, startY - totalY -0.04 - (float)i * 0.005, .01, 0, 255, 0, 255, "#");
}
sprintf( valueString, "%.3f", volume[IFRONT_RIGHT] );
CDebugOverlay::AddScreenTextOverlay(flMidX + endX * 0.5, startY - totalY -0.015, .01, 255, 255, 255, 255, valueString );
int nFrontRightVol = volume[IFRONT_RIGHT] * 15;
for(int i = 1; i <= nFrontRightVol; i++ )
{
CDebugOverlay::AddScreenTextOverlay(flMidX + (float)i * 0.005, startY - totalY -0.04, .01, 0, 255, 0, 255, "#");
}
sprintf( valueString, "%.3f", volume[IFRONT_LEFT] );
CDebugOverlay::AddScreenTextOverlay(flMidX - endX * 0.5, startY - totalY -0.015, .01, 255, 255, 255, 255, valueString );
int nFrontLeftVol = volume[IFRONT_LEFT] * 15;
for(int i = 1; i <= nFrontLeftVol; i++ )
{
CDebugOverlay::AddScreenTextOverlay(flMidX - (float)i * 0.005, startY - totalY -0.04, .01, 0, 255, 0, 255, "#");
}
sprintf( valueString, "%.3f", volume[IREAR_RIGHT] );
CDebugOverlay::AddScreenTextOverlay(flMidX + endX * 0.5, startY + 0.015, .01, 255, 255, 255, 255, valueString );
int nRearRightVol = volume[IREAR_RIGHT] * 15;
for(int i = 1; i <= nRearRightVol; i++ )
{
CDebugOverlay::AddScreenTextOverlay(flMidX + (float)i * 0.005, startY + 0.04, .01, 0, 255, 0, 255, "#");
}
sprintf( valueString, "%.3f", volume[IREAR_LEFT] );
CDebugOverlay::AddScreenTextOverlay(flMidX - endX * 0.5, startY + 0.015, .01, 255, 255, 255, 255, valueString );
int nRearLeftVol = volume[IREAR_LEFT] * 15;
for(int i = 1; i <= nRearLeftVol; i++ )
{
CDebugOverlay::AddScreenTextOverlay(flMidX - (float)i * 0.005, startY + 0.04, .01, 0, 255, 0, 255, "#");
}
}
#define PI 3.14159265
static void InterpSpeakerVol( float flRadialWeight, snd_pan_mode_t nPanMode, float flExponent, float flYawSource, float flSpeakerMin, float flSpeakerMax, float &flMinValue, float &flMaxValue, bool bDraw = true)
{
float flFactor = ( flYawSource - flSpeakerMin ) / (flSpeakerMax - flSpeakerMin );
float flOriginalFactor = flFactor;
if( flRadialWeight > 0.0 && nPanMode != SND_PAN_GLDSRC )
{
float flRadialFactor = 1.0 - ( FastCos( flFactor * PI ) + 1.0 ) * 0.5;
float flRadialDiff = flRadialFactor - flFactor;
flFactor = flFactor + ( flRadialDiff * flRadialWeight );
}
switch( nPanMode )
{
default:
case SND_PAN_EXP:
{
flMinValue = 1.0 - FastPow( flFactor, flExponent );
flMaxValue = 1.0 - FastPow( 1.0 - flFactor, flExponent );
break;
}
case SND_PAN_EQ_POW:
{
// equal power pan
float flFactorAngle = flFactor * ( PI * 0.5);
FastSinCos( flFactorAngle, &flMaxValue, &flMinValue );
break;
}
case SND_PAN_GLDSRC:
{
// goldsrc pan
flMinValue = ( FastCos( flFactor * PI ) + 1.0 ) * 0.5;
flMaxValue = 1.0 - flMinValue;
break;
}
}
flMinValue = clamp( flMinValue, 0.0, 1.0 );
flMaxValue = clamp( flMaxValue, 0.0, 1.0 );
if( snd_debug_panlaw.GetInt() && bDraw )
{
DEBUG_DrawPanCurvesLocation( flYawSource, flSpeakerMin, flSpeakerMax, flOriginalFactor, flMinValue, flMaxValue );
}
}
void DEBUG_DrawPanCurves(void)
{
float startY = 0.66;
float totalY = 0.25;
float startX = 0.0;
float endX = 0.25 * ( 9.0 / 16.0 );
float stepX = (float)(endX / (float)180);
float flMinValue, flMaxValue;
// for( int nOption = 0; nOption < 2; nOption++ )
// {
snd_pan_mode_t nPanMode = (snd_pan_mode_t ) g_snd_headphone_pan_mode;
float flExponent = g_snd_headphone_pan_exponent;
float flRadialPan = g_snd_headphone_pan_radial_weight;
float flFrontSpeakerPos = g_snd_front_headphone_position;
float flRearSpeakerPos = g_snd_rear_headphone_position;
if( snd_surround.GetInt() > 0 )
{
nPanMode = (snd_pan_mode_t ) g_snd_stereo_speaker_pan_mode;
flExponent = g_snd_stereo_speaker_pan_exponent;
flRadialPan = g_snd_stereo_speaker_pan_radial_weight;
CDebugOverlay::AddScreenTextOverlay(startX + (endX * 0.2), startY + 0.01, .1, 250, 250, 200, 255, "Speaker Pan Law");
if( snd_surround.GetInt() == 2 )
{
flFrontSpeakerPos = g_snd_front_stereo_speaker_position;
flRearSpeakerPos = g_snd_rear_stereo_speaker_position;
}
else
{
flExponent = g_snd_surround_speaker_pan_exponent;
flRadialPan = g_snd_surround_speaker_pan_radial_weight;
flFrontSpeakerPos = g_snd_front_surround_speaker_position;
flRearSpeakerPos = g_snd_rear_surround_speaker_position;
nPanMode = (snd_pan_mode_t ) g_snd_surround_speaker_pan_mode;
}
}
else
{
CDebugOverlay::AddScreenTextOverlay(startX + (endX * 0.2), startY + 0.01, .1, 250, 250, 200, 255, "Headphone Pan Law");
}
if(nPanMode == SND_PAN_EQ_POW )
{
CDebugOverlay::AddScreenTextOverlay(startX + (endX * 0.2), startY + 0.03, .1, 250, 250, 200, 255, "Equal Power");
}
else if(nPanMode == SND_PAN_EXP )
{
CDebugOverlay::AddScreenTextOverlay(startX + (endX * 0.2), startY + 0.03, .1, 250, 250, 200, 255, "Exponential");
}
else if(nPanMode == SND_PAN_GLDSRC )
{
CDebugOverlay::AddScreenTextOverlay(startX + (endX * 0.2), startY + 0.03, .1, 250, 250, 200, 255, "Gold Source");
}
//startX += (((float) nOption * endX) + 0.01 );
for( int nSource = 0; nSource <= 180; nSource++ )
{
InterpSpeakerVol( flRadialPan, nPanMode, flExponent, (float) nSource, 0.0, 180.0, flMinValue, flMaxValue, false );
CDebugOverlay::AddScreenTextOverlay(startX + (stepX * (float) nSource), startY - (flMinValue*totalY), .01, 0, 255, 0, 255, "+");
CDebugOverlay::AddScreenTextOverlay(startX + (stepX * (float) nSource), startY - (flMaxValue*totalY), .01, 0, 255, 0, 255, "+");
}
startX += (((float) 1.5 * endX) + 0.01 );
float flMidX = startX + ( endX * 0.5 );
float flMidY = startY - ( totalY * 0.5);
for( int nSource = 0; nSource <= 180; nSource++ )
{
FastSinCos( (float)nSource * 0.01745329251994329500 * 2.0, &flMaxValue, &flMinValue );
CDebugOverlay::AddScreenTextOverlay(flMidX + flMaxValue * endX *0.5, flMidY + -(flMinValue*totalY*0.5), .01, 0, 255, 0, 255, "*");
}
CDebugOverlay::AddScreenTextOverlay(flMidX + 1.0 * endX * 0.38, flMidY + (0.0*totalY*0.38), .01, 0, 255, 0, 255, ">");
CDebugOverlay::AddScreenTextOverlay(flMidX - 1.0 * endX * 0.38, flMidY + (0.0*totalY*0.38), .01, 0, 255, 0, 255, "<");
CDebugOverlay::AddScreenTextOverlay(flMidX + 0.0 * endX *0.38, flMidY - (1.0*totalY*0.38), .01, 0, 255, 0, 255, "^");
float flSin, flCos;
FastSinCos( (flFrontSpeakerPos+90 ) * 0.01745329251994329500, &flSin, &flCos );
CDebugOverlay::AddScreenTextOverlay(flMidX + flCos * (endX * 0.46), flMidY - (flSin*totalY*0.46), .01, 255, 255, 0, 255, "*");
FastSinCos( -(flFrontSpeakerPos+270 ) * 0.01745329251994329500, &flSin, &flCos );
CDebugOverlay::AddScreenTextOverlay(flMidX + flCos * (endX * 0.46), flMidY - (flSin*totalY*0.46), .01, 255, 255, 0, 255, "*");
FastSinCos( (flRearSpeakerPos+90 ) * 0.01745329251994329500, &flSin, &flCos );
CDebugOverlay::AddScreenTextOverlay(flMidX + flCos * (endX * 0.42), flMidY - (flSin*totalY * 0.42), .01, 0, 255, 255, 255, "*");
FastSinCos( -(flRearSpeakerPos+270 ) * 0.01745329251994329500, &flSin, &flCos );
CDebugOverlay::AddScreenTextOverlay(flMidX + flCos * (endX * 0.42), flMidY - (flSin*totalY * 0.42), .01, 0, 255, 255, 255, "*");
// }
}
static float InterpPitchAngle( float flPitchAngle, float flMono )
{
float flNewMono = flMono;
// mono goes from 0.0 to 1.0 as listener moves into 'mono' radius of sound source.
// Also, as pitch_angle to sound source approaches 90 (sound above/below listener), sounds become mono.
// convert pitch angle to 0-90 absolute pitch
if ( flPitchAngle < 0)
flPitchAngle += 360;
if ( flPitchAngle > 180)
flPitchAngle = 360 - flPitchAngle;
if ( flPitchAngle > 90)
flPitchAngle = 90 - (flPitchAngle - 90);
// calculate additional mono crossfade due to pitch angle
if ( flPitchAngle > PITCH_ANGLE_THRESHOLD )
{
float xfade = ( flPitchAngle - PITCH_ANGLE_THRESHOLD ) / ( 90.0 - PITCH_ANGLE_THRESHOLD ); // 0.0 -> 1.0 as angle 45->90
flNewMono = clamp( flNewMono + xfade, 0.0, 1.0 );
}
return flNewMono;
}
static float InterpMonoSpread( float flMono, float flScale, int ispeaker, int cspeaker, bool fmix2channels )
{
Assert(flMono <= 1.0 && flMono >= 0.0);
Assert( flScale <= 1.0 && flScale >= 0.0 );
// crossfade speaker volumes towards mono with increased pitch angle of sound source
float flNewScale = XfadeSpeakerVolToMono( flScale, flMono, ispeaker, cspeaker, fmix2channels );
Assert( flScale <= 1.0 && flScale >= 0.0);
return flNewScale;
}
//////////////////////////////////////////////////////////////////////////
// float only version
//////////////////////////////////////////////////////////////////////////
void Device_SpatializeChannel( int nSlot, float volume[CCHANVOLUMES/2], const Vector& sourceDir, float mono, float flRearToStereoScale /* = 0.75 */ )
{
VPROF("CAudioDeviceBase::SpatializeChannel - 2");
float rfscale, rrscale, lfscale, lrscale, fcscale;
bool bSurround = g_AudioDevice->IsSurround();
bool bSurroundCenter = g_AudioDevice->IsSurroundCenter();
bool bHeadphone = g_AudioDevice->IsHeadphone();
fcscale = rfscale = lfscale = rrscale = lrscale = 0.0;
// clear volumes
for (int i = 0; i < CCHANVOLUMES/2; i++)
volume[i] = 0;
// linear crossfader for 2, 4 or 5 speakers, using polar coord. separation angle as linear basis
// get pitch & yaw angle from listener origin to sound source
QAngle angles;
float pitch;
float source_yaw;
float yaw;
// sourceDir = sourcePos - playerPos
VectorAngles(sourceDir, angles);
pitch = angles[PITCH];
source_yaw = angles[YAW];
// adjust mono spread based on pitch angle
float flMono = InterpPitchAngle( pitch, mono );
// get 2d listener yaw angle from listener right
QAngle angles2d;
Vector source2d;
float listener_yaw;
source2d.x = listener_right[ nSlot ].x;
source2d.y = listener_right[ nSlot ].y;
source2d.z = 0.0;
VectorNormalize(source2d);
// convert right vector to euler angles (yaw & pitch)
VectorAngles(source2d, angles2d);
listener_yaw = angles2d[YAW];
// get yaw of sound source, with listener_yaw as reference 0.
yaw = AngleDiff( source_yaw, listener_yaw );
if ( yaw < 0.0 )
{
yaw += 360;
}
// default to stereo
float flFrontCenter = 90;
// pre-rotation
float flFront = 90;
float flRear = 90;
bool bMixToTwoChannels = true;
int nSpkeakerCount = 4;
snd_pan_mode_t nPanMode = ( snd_pan_mode_t ) g_snd_stereo_speaker_pan_mode;
float flPanExponent = g_snd_stereo_speaker_pan_exponent;
float flRadialPan = g_snd_stereo_speaker_pan_radial_weight;
if ( bHeadphone )
{
// headphone mix: (NO HRTF)
// override with phone settings
flFront = clamp( g_snd_front_headphone_position, 0.0, 90.0 );
flRear = clamp( g_snd_rear_headphone_position, flFront, 180.0 );
bMixToTwoChannels = true;
nSpkeakerCount = 4;
nPanMode = ( snd_pan_mode_t ) g_snd_headphone_pan_mode;
flPanExponent = g_snd_headphone_pan_exponent;
flRadialPan = g_snd_headphone_pan_radial_weight;
}
else if( bSurround && !bSurroundCenter )
{
// override with quad settings
flFront = clamp( g_snd_front_surround_speaker_position, 0.0, 90.0 );
flRear = clamp( g_snd_rear_surround_speaker_position, flFront, 180.0 );
flPanExponent = g_snd_surround_speaker_pan_exponent;
flRadialPan = g_snd_surround_speaker_pan_radial_weight;
nPanMode = ( snd_pan_mode_t ) g_snd_surround_speaker_pan_mode;
bMixToTwoChannels = false;
nSpkeakerCount = 4;
}
else if ( bSurround && bSurroundCenter )
{
// override with 5.1 settings
flFront = clamp( g_snd_front_surround_speaker_position, 0.0, 90.0 );
flRear = clamp( g_snd_rear_surround_speaker_position, flFront, 180.0 );
flPanExponent = g_snd_surround_speaker_pan_exponent;
flRadialPan = g_snd_surround_speaker_pan_radial_weight;
nPanMode = ( snd_pan_mode_t ) g_snd_surround_speaker_pan_mode;
bMixToTwoChannels = false;
nSpkeakerCount = 5;
}
else // stereo
{
// override with stereo settings
flFront = clamp( g_snd_front_stereo_speaker_position, 0.0, 90.0 );
flRear = clamp( g_snd_rear_stereo_speaker_position, flFront, 180.0 );
bMixToTwoChannels = true;
nSpkeakerCount = 4; // stereo is processed as 4 speakers (just for backward compatibility
}
float flFrontRight = 90.0 - flFront;
float flFrontLeft = 90.0 + flFront;
float flRearRight = 450.0 - flRear;
float flRearLeft= 90.0 + flRear;
// if there's a center speaker we interp from right->center->left
if( bSurroundCenter )
{
if( yaw >= flFrontRight && yaw < flFrontCenter )
{
InterpSpeakerVol( flRadialPan, nPanMode, flPanExponent, yaw, flFrontRight, flFrontCenter, rfscale, fcscale );
}
else if( yaw >= flFrontCenter && yaw < flFrontLeft )
{
InterpSpeakerVol( flRadialPan, nPanMode, flPanExponent, yaw, flFrontCenter, flFrontLeft, fcscale, lfscale );
}
} // otw directly right->left
else
{
if( yaw >= flFrontRight && yaw < flFrontLeft )
{
InterpSpeakerVol( flRadialPan, nPanMode, flPanExponent, yaw, flFrontRight, flFrontLeft, rfscale, lfscale );
}
}
if ( yaw >= flFrontLeft && yaw < flRearLeft )
{
InterpSpeakerVol( flRadialPan, nPanMode, flPanExponent, yaw, flFrontLeft, flRearLeft, lfscale, lrscale );
}
else if ( yaw >= flRearLeft && yaw < flRearRight )
{
InterpSpeakerVol( flRadialPan, nPanMode, flPanExponent, yaw, flRearLeft, flRearRight, lrscale, rrscale );
}
// the circle wraps between frontright and rearright
else if ( yaw >= flRearRight ) // between RearRight & 0/360
{
InterpSpeakerVol( flRadialPan, nPanMode, flPanExponent, yaw, flRearRight, 360 + flFrontRight, rrscale, rfscale );
}
else if ( yaw < flFrontRight ) // between 0/360 and FrontRight
{
InterpSpeakerVol( flRadialPan, nPanMode, flPanExponent, yaw, flRearRight - 360, flFrontRight, rrscale, rfscale );
}
Assert( fcscale <= 1.0 && fcscale >= 0.0 );
if( bSurroundCenter )
{
fcscale = InterpMonoSpread( flMono, fcscale, ISPEAKER_CENTER_FRONT, nSpkeakerCount, bMixToTwoChannels );
}
Assert( rfscale <= 1.0 && rfscale >= 0.0 );
rfscale = InterpMonoSpread( flMono, rfscale, ISPEAKER_RIGHT_FRONT, nSpkeakerCount, bMixToTwoChannels );
Assert( lfscale <= 1.0 && lfscale >= 0.0 );
lfscale = InterpMonoSpread( flMono, lfscale, ISPEAKER_LEFT_FRONT, nSpkeakerCount, bMixToTwoChannels );
Assert( rrscale <= 1.0 && rrscale >= 0.0 );
rrscale = InterpMonoSpread( flMono, rrscale, ISPEAKER_RIGHT_REAR, nSpkeakerCount, bMixToTwoChannels );
Assert( lrscale <= 1.0 && lrscale >= 0.0 );
lrscale = InterpMonoSpread( flMono, lrscale, ISPEAKER_LEFT_REAR, nSpkeakerCount, bMixToTwoChannels );
// add sounds coming from rear (potentially scaled)
if( !bSurround )
{
rfscale = rfscale + ( rrscale * flRearToStereoScale );
lfscale = lfscale + ( lrscale * flRearToStereoScale );
rrscale = 0;
lrscale = 0;
}
volume[IFRONT_RIGHT] = clamp( rfscale, 0.0, 1.0 );
volume[IFRONT_LEFT] = clamp( lfscale, 0.0, 1.0 );
if ( bSurround )
{
volume[IREAR_RIGHT] = clamp( rrscale, 0.0, 1.0 );
volume[IREAR_LEFT] = clamp( lrscale, 0.0, 1.0 );
if ( bSurroundCenter )
{
volume[IFRONT_CENTER] = clamp( fcscale, 0.0, 1.0 );
volume[IFRONT_CENTER0] = 0.0;
}
}
if(snd_debug_panlaw.GetInt())
{
DEBUG_DrawSpeakerValues( volume );
}
}
// old skool integer version
ConVar snd_rear_speaker_scale("snd_rear_speaker_scale", "1.0", FCVAR_CHEAT, "How much to scale rear speaker contribution to front stereo output" );
void Device_ApplyDSPEffects( int idsp, portable_samplepair_t *pbuffront, portable_samplepair_t *pbufrear, portable_samplepair_t *pbufcenter, int samplecount)
{
VPROF("CAudioDeviceBase::ApplyDSPEffects");
DEBUG_StartSoundMeasure( 1, samplecount );
DSP_Process( idsp, pbuffront, pbufrear, pbufcenter, samplecount );
DEBUG_StopSoundMeasure( 1, samplecount );
}
void Device_MixUpsample( int sampleCount, int filtertype )
{
VPROF( "CAudioDeviceBase::MixUpsample" );
paintbuffer_t *pPaint = MIX_GetCurrentPaintbufferPtr();
int ifilter = pPaint->ifilter;
Assert (ifilter < CPAINTFILTERS);
S_MixBufferUpsample2x( sampleCount, pPaint->pbuf, &(pPaint->fltmem[ifilter][0]), CPAINTFILTERMEM, filtertype );
if ( pPaint->fsurround )
{
Assert( pPaint->pbufrear );
S_MixBufferUpsample2x( sampleCount, pPaint->pbufrear, &(pPaint->fltmemrear[ifilter][0]), CPAINTFILTERMEM, filtertype );
if ( pPaint->fsurround_center )
{
Assert( pPaint->pbufcenter );
S_MixBufferUpsample2x( sampleCount, pPaint->pbufcenter, &(pPaint->fltmemcenter[ifilter][0]), CPAINTFILTERMEM, filtertype );
}
}
// make sure on next upsample pass for this paintbuffer, new filter memory is used
pPaint->ifilter++;
}
void Device_Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress )
{
VPROF( "CAudioDeviceBase::Mix8Mono" );
float volume[CCHANVOLUMES];
paintbuffer_t *pPaint = MIX_GetCurrentPaintbufferPtr();
if ( !MIX_ScaleChannelVolume( pPaint, pChannel, volume, 1) )
return;
if ( FVolumeFrontNonZero(volume) )
{
Mix8MonoWavtype( pChannel, pPaint->pbuf + outputOffset, volume, (byte *)pData, inputOffset, rateScaleFix, outCount);
}
if ( pPaint->fsurround )
{
if ( FVolumeRearNonZero(volume) )
{
Assert( pPaint->pbufrear );
Mix8MonoWavtype( pChannel, pPaint->pbufrear + outputOffset, &volume[IREAR_LEFT], (byte *)pData, inputOffset, rateScaleFix, outCount );
}
if ( pPaint->fsurround_center && FVolumeCenterNonZero(volume) )
{
Assert( pPaint->pbufcenter );
Mix8MonoWavtype( pChannel, pPaint->pbufcenter + outputOffset, &volume[IFRONT_CENTER], (byte *)pData, inputOffset, rateScaleFix, outCount );
}
}
}
void Device_Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress )
{
VPROF( "CAudioDeviceBase::Mix8Stereo" );
float volume[CCHANVOLUMES];
paintbuffer_t *pPaint = MIX_GetCurrentPaintbufferPtr();
if ( !MIX_ScaleChannelVolume( pPaint, pChannel, volume, 2 ) )
return;
if ( FVolumeFrontNonZero(volume) )
{
Mix8StereoWavtype( pChannel, pPaint->pbuf + outputOffset, volume, (byte *)pData, inputOffset, rateScaleFix, outCount );
}
if ( pPaint->fsurround )
{
if ( FVolumeRearNonZero(volume) )
{
Assert( pPaint->pbufrear );
Mix8StereoWavtype( pChannel, pPaint->pbufrear + outputOffset, &volume[IREAR_LEFT], (byte *)pData, inputOffset, rateScaleFix, outCount );
}
if ( pPaint->fsurround_center && FVolumeCenterNonZero(volume) )
{
Assert( pPaint->pbufcenter );
Mix8StereoWavtype( pChannel, pPaint->pbufcenter + outputOffset, &volume[IFRONT_CENTER], (byte *)pData, inputOffset, rateScaleFix, outCount );
}
}
}
void Device_Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress )
{
VPROF( "CAudioDeviceBase::Mix16Mono" );
float volume[CCHANVOLUMES];
paintbuffer_t *pPaint = MIX_GetCurrentPaintbufferPtr();
if ( !MIX_ScaleChannelVolume( pPaint, pChannel, volume, 1 ) )
return;
if ( FVolumeFrontNonZero(volume) )
{
Mix16MonoWavtype( pChannel, pPaint->pbuf + outputOffset, volume, pData, inputOffset, rateScaleFix, outCount );
}
if ( pPaint->fsurround )
{
if ( FVolumeRearNonZero(volume) )
{
Assert( pPaint->pbufrear );
Mix16MonoWavtype( pChannel, pPaint->pbufrear + outputOffset, &volume[IREAR_LEFT], pData, inputOffset, rateScaleFix, outCount );
}
if ( pPaint->fsurround_center && FVolumeCenterNonZero(volume) )
{
Assert( pPaint->pbufcenter );
Mix16MonoWavtype( pChannel, pPaint->pbufcenter + outputOffset, &volume[IFRONT_CENTER], pData, inputOffset, rateScaleFix, outCount );
}
}
}
void Device_Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress )
{
VPROF( "CAudioDeviceBase::Mix16Stereo" );
float volume[CCHANVOLUMES];
paintbuffer_t *pPaint = MIX_GetCurrentPaintbufferPtr();
if ( !MIX_ScaleChannelVolume( pPaint, pChannel, volume, 2 ) )
return;
if ( FVolumeFrontNonZero(volume) )
{
Mix16StereoWavtype( pChannel, pPaint->pbuf + outputOffset, volume, pData, inputOffset, rateScaleFix, outCount );
}
if ( pPaint->fsurround )
{
if ( FVolumeRearNonZero(volume) )
{
Assert( pPaint->pbufrear );
Mix16StereoWavtype( pChannel, pPaint->pbufrear + outputOffset, &volume[IREAR_LEFT], pData, inputOffset, rateScaleFix, outCount );
}
if ( pPaint->fsurround_center && FVolumeCenterNonZero(volume) )
{
Assert( pPaint->pbufcenter );
Mix16StereoWavtype( pChannel, pPaint->pbufcenter + outputOffset, &volume[IFRONT_CENTER], pData, inputOffset, rateScaleFix, outCount );
}
}
}
#if USE_AUDIO_DEVICE_V1
// Null Audio Device
class CAudioDeviceNull : public CAudioDeviceBase
{
public:
CAudioDeviceNull()
{
m_pName = "Sound Disabled";
m_nChannels = 2;
m_nSampleBits = 16;
m_nSampleRate = SOUND_DMA_SPEED;
m_bIsActive = false;
}
bool IsActive( void ) { return false; }
bool Init( void ) { return true; }
void Shutdown( void ) {}
void Pause( void ) {}
void UnPause( void ) {}
int64 PaintBegin( float, int64, int64 ) { return 0; }
void PaintEnd( void ) {}
int GetOutputPosition( void ) { return 0; }
void ClearBuffer( void ) {}
void TransferSamples( int end ) {}
int DeviceSampleCount( void ) { return 0; }
};
IAudioDevice *Audio_GetNullDevice( void )
{
return new CAudioDeviceNull;
}
#else
void IAudioDevice2::TransferSamples( uint32 )
{
CAudioMixBuffer mixBuffers[SOUND_DEVICE_MAX_CHANNELS];
const portable_samplepair_t *pFront = PAINTBUFFER;
const portable_samplepair_t *pRear = REARPAINTBUFFER;
const portable_samplepair_t *pCenter = CENTERPAINTBUFFER;
float flMasterVolume = S_GetMasterVolume();
for ( int i = 0; i < MIX_BUFFER_SIZE; i++ )
{
mixBuffers[0].m_flData[i] = pFront[i].left;
mixBuffers[1].m_flData[i] = pFront[i].right;
}
ScaleBuffer( mixBuffers[0].m_flData, mixBuffers[0].m_flData, flMasterVolume );
ScaleBuffer( mixBuffers[1].m_flData, mixBuffers[1].m_flData, flMasterVolume );
if ( IsSurroundCenter() )
{
for ( int i = 0; i < MIX_BUFFER_SIZE; i++ )
{
mixBuffers[2].m_flData[i] = pCenter[i].left;
mixBuffers[3].m_flData[i] = 0;
}
ScaleBuffer( mixBuffers[2].m_flData, mixBuffers[2].m_flData, flMasterVolume );
// this is all zeros, so scaling it to the master volume isn't necessary
//ScaleBuffer( mixBuffers[3].m_flData, mixBuffers[3].m_flData, flMasterVolume );
}
if ( IsSurround() )
{
for ( int i = 0; i < MIX_BUFFER_SIZE; i++ )
{
mixBuffers[4].m_flData[i] = pRear[i].left;
mixBuffers[5].m_flData[i] = pRear[i].right;
}
ScaleBuffer( mixBuffers[4].m_flData, mixBuffers[4].m_flData, flMasterVolume );
ScaleBuffer( mixBuffers[5].m_flData, mixBuffers[5].m_flData, flMasterVolume );
}
if ( ChannelCount() > 6 )
{
for ( int i = 0; i < MIX_BUFFER_SIZE; i++ )
{
mixBuffers[6].m_flData[i] = 0;
mixBuffers[7].m_flData[i] = 0;
}
}
OutputBuffer( ChannelCount(), mixBuffers );
}
#endif