123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /*
- ===========================================================================
- 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 "../idlib/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;
- }
- }
|