//========= Copyright © 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