mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-12 13:32:08 +00:00
296 lines
11 KiB
C++
296 lines
11 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
#pragma hdrstop
|
|
#include "precompiled.h"
|
|
|
|
#include "snd_local.h"
|
|
|
|
idCVar s_subFraction( "s_subFraction", "0.5", CVAR_ARCHIVE | CVAR_FLOAT, "Amount of each sound to send to the LFE channel" );
|
|
|
|
idVec2 idSoundVoice_Base::speakerPositions[idWaveFile::CHANNEL_INDEX_MAX];
|
|
int idSoundVoice_Base::speakerLeft[idWaveFile::CHANNEL_INDEX_MAX] = {0 };
|
|
int idSoundVoice_Base::speakerRight[idWaveFile::CHANNEL_INDEX_MAX] = {0 };
|
|
int idSoundVoice_Base::dstChannels = 0;
|
|
int idSoundVoice_Base::dstMask = 0;
|
|
int idSoundVoice_Base::dstCenter = -1;
|
|
int idSoundVoice_Base::dstLFE = -1;
|
|
int idSoundVoice_Base::dstMap[MAX_CHANNELS_PER_VOICE] = { 0 };
|
|
int idSoundVoice_Base::invMap[idWaveFile::CHANNEL_INDEX_MAX] = { 0 };
|
|
float idSoundVoice_Base::omniLevel = 1.0f;
|
|
|
|
/*
|
|
========================
|
|
idSoundVoice_Base::idSoundVoice_Base
|
|
========================
|
|
*/
|
|
idSoundVoice_Base::idSoundVoice_Base() :
|
|
position( 0.0f ),
|
|
gain( 1.0f ),
|
|
centerChannel( 0.0f ),
|
|
pitch( 1.0f ),
|
|
innerRadius( 32.0f ),
|
|
occlusion( 0.0f ),
|
|
channelMask( 0 ),
|
|
innerSampleRangeSqr( 0.0f ),
|
|
outerSampleRangeSqr( 0.0f )
|
|
{
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idSoundVoice_Base::InitSurround
|
|
========================
|
|
*/
|
|
void idSoundVoice_Base::InitSurround( int outputChannels, int channelMask )
|
|
{
|
|
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_LEFT ].Set( 0.70710678118654752440084436210485f, 0.70710678118654752440084436210485f ); // 45 degrees
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT ].Set( 0.70710678118654752440084436210485f, -0.70710678118654752440084436210485f ); // 315 degrees
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_CENTER ].Set( 0.0f, 0.0f ); // 0 degrees
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY ].Set( 0.0f, 0.0f ); // -
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_BACK_LEFT ].Set( -0.70710678118654752440084436210485f, 0.70710678118654752440084436210485f ); // 135 degrees
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_BACK_RIGHT ].Set( -0.70710678118654752440084436210485f, -0.70710678118654752440084436210485f ); // 225 degrees
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER ].Set( 0.92387953251128675612818318939679f, 0.3826834323650897717284599840304f ); // 22.5 degrees
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER ].Set( 0.92387953251128675612818318939679f, -0.3826834323650897717284599840304f ); // 337.5 degrees
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_BACK_CENTER ].Set( -1.0f, 0.0f ); // 180 degrees
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_SIDE_LEFT ].Set( 0.0f, 1.0f ); // 90 degrees
|
|
speakerPositions[idWaveFile::CHANNEL_INDEX_SIDE_RIGHT ].Set( 0.0f, -1.0f ); // 270 degrees
|
|
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_LEFT;
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_LEFT] = idWaveFile::CHANNEL_INDEX_SIDE_LEFT;
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_SIDE_LEFT] = idWaveFile::CHANNEL_INDEX_BACK_LEFT;
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_BACK_LEFT] = idWaveFile::CHANNEL_INDEX_BACK_CENTER;
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_BACK_CENTER] = idWaveFile::CHANNEL_INDEX_BACK_RIGHT;
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_BACK_RIGHT] = idWaveFile::CHANNEL_INDEX_SIDE_RIGHT;
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_SIDE_RIGHT] = idWaveFile::CHANNEL_INDEX_FRONT_RIGHT;
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT] = idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER;
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER;
|
|
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_CENTER;
|
|
speakerLeft[idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY] = idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY;
|
|
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_RIGHT;
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT] = idWaveFile::CHANNEL_INDEX_SIDE_RIGHT;
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_SIDE_RIGHT] = idWaveFile::CHANNEL_INDEX_BACK_RIGHT;
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_BACK_RIGHT] = idWaveFile::CHANNEL_INDEX_BACK_CENTER;
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_BACK_CENTER] = idWaveFile::CHANNEL_INDEX_BACK_LEFT;
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_BACK_LEFT] = idWaveFile::CHANNEL_INDEX_SIDE_LEFT;
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_SIDE_LEFT] = idWaveFile::CHANNEL_INDEX_FRONT_LEFT;
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_LEFT] = idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER;
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER;
|
|
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_CENTER;
|
|
speakerRight[idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY] = idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY;
|
|
|
|
dstChannels = outputChannels;
|
|
dstMask = channelMask;
|
|
|
|
// dstMap maps a destination channel to a speaker
|
|
// invMap maps a speaker to a destination channel
|
|
dstLFE = -1;
|
|
dstCenter = -1;
|
|
memset( dstMap, 0, sizeof( dstMap ) );
|
|
memset( invMap, 0, sizeof( invMap ) );
|
|
for( int i = 0, c = 0; i < idWaveFile::CHANNEL_INDEX_MAX && c < MAX_CHANNELS_PER_VOICE; i++ )
|
|
{
|
|
if( dstMask & BIT( i ) )
|
|
{
|
|
if( i == idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY )
|
|
{
|
|
dstLFE = c;
|
|
}
|
|
if( i == idWaveFile::CHANNEL_INDEX_FRONT_CENTER )
|
|
{
|
|
dstCenter = c;
|
|
}
|
|
dstMap[c] = i;
|
|
invMap[i] = c++;
|
|
}
|
|
else
|
|
{
|
|
// Remove this speaker from the chain
|
|
int right = speakerRight[i];
|
|
int left = speakerLeft[i];
|
|
speakerRight[left] = right;
|
|
speakerLeft[right] = left;
|
|
}
|
|
}
|
|
assert( ( dstLFE == -1 ) || ( ( dstMask & idWaveFile::CHANNEL_MASK_LOW_FREQUENCY ) != 0 ) );
|
|
assert( ( dstCenter == -1 ) || ( ( dstMask & idWaveFile::CHANNEL_MASK_FRONT_CENTER ) != 0 ) );
|
|
|
|
float omniChannels = ( float )dstChannels;
|
|
if( dstMask & idWaveFile::CHANNEL_MASK_LOW_FREQUENCY )
|
|
{
|
|
omniChannels -= 1.0f;
|
|
}
|
|
if( dstMask & idWaveFile::CHANNEL_MASK_FRONT_CENTER )
|
|
{
|
|
omniChannels -= 1.0f;
|
|
}
|
|
if( omniChannels > 0.0f )
|
|
{
|
|
omniLevel = 1.0f / omniChannels;
|
|
}
|
|
else
|
|
{
|
|
// This happens in mono mode
|
|
omniLevel = 1.0f;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idSoundVoice_Base::CalculateSurround
|
|
========================
|
|
*/
|
|
void idSoundVoice_Base::CalculateSurround( int srcChannels, float pLevelMatrix[ MAX_CHANNELS_PER_VOICE* MAX_CHANNELS_PER_VOICE ], float scale )
|
|
{
|
|
// Hack for mono
|
|
if( dstChannels == 1 )
|
|
{
|
|
if( srcChannels == 1 )
|
|
{
|
|
pLevelMatrix[ 0 ] = scale;
|
|
}
|
|
else if( srcChannels == 2 )
|
|
{
|
|
pLevelMatrix[ 0 ] = scale * 0.7071f;
|
|
pLevelMatrix[ 1 ] = scale * 0.7071f;
|
|
}
|
|
return;
|
|
}
|
|
|
|
#define MATINDEX( src, dst ) ( srcChannels * dst + src )
|
|
|
|
float subFraction = s_subFraction.GetFloat();
|
|
|
|
if( srcChannels == 1 )
|
|
{
|
|
idVec2 p2 = position.ToVec2();
|
|
|
|
float centerFraction = centerChannel;
|
|
|
|
float sqrLength = p2.LengthSqr();
|
|
if( sqrLength <= 0.01f )
|
|
{
|
|
// If we are on top of the listener, simply route all channels to each speaker equally
|
|
for( int i = 0; i < dstChannels; i++ )
|
|
{
|
|
pLevelMatrix[MATINDEX( 0, i )] = omniLevel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float invLength = idMath::InvSqrt( sqrLength );
|
|
float distance = ( invLength * sqrLength );
|
|
p2 *= invLength;
|
|
|
|
float spatialize = 1.0f;
|
|
if( distance < innerRadius )
|
|
{
|
|
spatialize = distance / innerRadius;
|
|
}
|
|
float omni = omniLevel * ( 1.0f - spatialize );
|
|
|
|
if( dstCenter != -1 )
|
|
{
|
|
centerFraction *= Max( 0.0f, p2.x );
|
|
spatialize *= ( 1.0f - centerFraction );
|
|
omni *= ( 1.0f - centerFraction );
|
|
}
|
|
|
|
float channelDots[MAX_CHANNELS_PER_VOICE] = { 0 };
|
|
for( int i = 0; i < dstChannels; i++ )
|
|
{
|
|
// Calculate the contribution to each destination channel
|
|
channelDots[i] = speakerPositions[dstMap[i]] * p2;
|
|
}
|
|
// Find the speaker nearest to the sound
|
|
int channelA = 0;
|
|
for( int i = 1; i < dstChannels; i++ )
|
|
{
|
|
if( channelDots[i] > channelDots[channelA] )
|
|
{
|
|
channelA = i;
|
|
}
|
|
}
|
|
int speakerA = dstMap[channelA];
|
|
|
|
// Find the 2nd nearest speaker
|
|
int speakerB;
|
|
float speakerACross = ( speakerPositions[speakerA].x * p2.y ) - ( speakerPositions[speakerA].y * p2.x );
|
|
if( speakerACross > 0.0f )
|
|
{
|
|
speakerB = speakerLeft[speakerA];
|
|
}
|
|
else
|
|
{
|
|
speakerB = speakerRight[speakerA];
|
|
}
|
|
int channelB = invMap[speakerB];
|
|
|
|
// Divide the amplitude between the 2 closest speakers
|
|
float distA = ( speakerPositions[speakerA] - p2 ).Length();
|
|
float distB = ( speakerPositions[speakerB] - p2 ).Length();
|
|
float distCinv = 1.0f / ( distA + distB );
|
|
float volumes[MAX_CHANNELS_PER_VOICE] = { 0 };
|
|
volumes[channelA] = ( distB * distCinv );
|
|
volumes[channelB] = ( distA * distCinv );
|
|
for( int i = 0; i < dstChannels; i++ )
|
|
{
|
|
pLevelMatrix[MATINDEX( 0, i )] = ( volumes[i] * spatialize ) + omni;
|
|
}
|
|
}
|
|
if( dstLFE != -1 )
|
|
{
|
|
pLevelMatrix[MATINDEX( 0, dstLFE )] = subFraction;
|
|
}
|
|
if( dstCenter != -1 )
|
|
{
|
|
pLevelMatrix[MATINDEX( 0, dstCenter )] = centerFraction;
|
|
}
|
|
}
|
|
else if( srcChannels == 2 )
|
|
{
|
|
pLevelMatrix[ MATINDEX( 0, 0 ) ] = 1.0f;
|
|
pLevelMatrix[ MATINDEX( 1, 1 ) ] = 1.0f;
|
|
if( dstLFE != -1 )
|
|
{
|
|
pLevelMatrix[ MATINDEX( 0, dstLFE ) ] = subFraction * 0.5f;
|
|
pLevelMatrix[ MATINDEX( 1, dstLFE ) ] = subFraction * 0.5f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
idLib::Warning( "We don't support %d channel sound files", srcChannels );
|
|
}
|
|
for( int i = 0; i < srcChannels * dstChannels; i++ )
|
|
{
|
|
pLevelMatrix[ i ] *= scale;
|
|
}
|
|
}
|