1251 lines
40 KiB
C++
1251 lines
40 KiB
C++
|
//========= 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
|