DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

SoundVoice.cpp (11029B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 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.
     23 
     24 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.
     25 
     26 ===========================================================================
     27 */
     28 #pragma hdrstop
     29 #include "../idlib/precompiled.h"
     30 
     31 #include "snd_local.h"
     32 
     33 idCVar s_subFraction( "s_subFraction", "0.5", CVAR_ARCHIVE | CVAR_FLOAT, "Amount of each sound to send to the LFE channel" );
     34 
     35 idVec2 idSoundVoice_Base::speakerPositions[idWaveFile::CHANNEL_INDEX_MAX];
     36 int idSoundVoice_Base::speakerLeft[idWaveFile::CHANNEL_INDEX_MAX] = {0 };
     37 int idSoundVoice_Base::speakerRight[idWaveFile::CHANNEL_INDEX_MAX] = {0 };
     38 int idSoundVoice_Base::dstChannels = 0;
     39 int idSoundVoice_Base::dstMask = 0;
     40 int idSoundVoice_Base::dstCenter = -1;
     41 int idSoundVoice_Base::dstLFE = -1;
     42 int idSoundVoice_Base::dstMap[MAX_CHANNELS_PER_VOICE] = { 0 };
     43 int idSoundVoice_Base::invMap[idWaveFile::CHANNEL_INDEX_MAX] = { 0 };
     44 float idSoundVoice_Base::omniLevel = 1.0f;
     45 
     46 /*
     47 ========================
     48 idSoundVoice_Base::idSoundVoice_Base
     49 ========================
     50 */
     51 idSoundVoice_Base::idSoundVoice_Base() :
     52 position( 0.0f ),
     53 gain( 1.0f ),
     54 centerChannel( 0.0f ),
     55 pitch( 1.0f ),
     56 innerRadius( 32.0f ),
     57 occlusion( 0.0f ),
     58 channelMask( 0 ),
     59 innerSampleRangeSqr( 0.0f ),
     60 outerSampleRangeSqr( 0.0f )
     61 {
     62 }
     63 
     64 /*
     65 ========================
     66 idSoundVoice_Base::InitSurround
     67 ========================
     68 */
     69 void idSoundVoice_Base::InitSurround( int outputChannels, int channelMask ) {
     70 
     71 	speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_LEFT			].Set(  0.70710678118654752440084436210485f,  0.70710678118654752440084436210485f );	// 45 degrees
     72 	speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT			].Set(  0.70710678118654752440084436210485f, -0.70710678118654752440084436210485f );	// 315 degrees
     73 	speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_CENTER			].Set(  0.0f,								  0.0f );									// 0 degrees
     74 	speakerPositions[idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY		].Set(  0.0f,								  0.0f );									// -
     75 	speakerPositions[idWaveFile::CHANNEL_INDEX_BACK_LEFT			].Set( -0.70710678118654752440084436210485f,  0.70710678118654752440084436210485f );	// 135 degrees
     76 	speakerPositions[idWaveFile::CHANNEL_INDEX_BACK_RIGHT			].Set( -0.70710678118654752440084436210485f, -0.70710678118654752440084436210485f );	// 225 degrees
     77 	speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER	].Set(  0.92387953251128675612818318939679f,  0.3826834323650897717284599840304f );		// 22.5 degrees
     78 	speakerPositions[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER	].Set(  0.92387953251128675612818318939679f, -0.3826834323650897717284599840304f );		// 337.5 degrees
     79 	speakerPositions[idWaveFile::CHANNEL_INDEX_BACK_CENTER			].Set( -1.0f,								  0.0f );									// 180 degrees
     80 	speakerPositions[idWaveFile::CHANNEL_INDEX_SIDE_LEFT			].Set(  0.0f,								  1.0f );									// 90 degrees
     81 	speakerPositions[idWaveFile::CHANNEL_INDEX_SIDE_RIGHT			].Set( 0.0f,								 -1.0f );									// 270 degrees
     82 
     83 	speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_LEFT;
     84 	speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_LEFT] = idWaveFile::CHANNEL_INDEX_SIDE_LEFT;
     85 	speakerLeft[idWaveFile::CHANNEL_INDEX_SIDE_LEFT] = idWaveFile::CHANNEL_INDEX_BACK_LEFT;
     86 	speakerLeft[idWaveFile::CHANNEL_INDEX_BACK_LEFT] = idWaveFile::CHANNEL_INDEX_BACK_CENTER;
     87 	speakerLeft[idWaveFile::CHANNEL_INDEX_BACK_CENTER] = idWaveFile::CHANNEL_INDEX_BACK_RIGHT;
     88 	speakerLeft[idWaveFile::CHANNEL_INDEX_BACK_RIGHT] = idWaveFile::CHANNEL_INDEX_SIDE_RIGHT;
     89 	speakerLeft[idWaveFile::CHANNEL_INDEX_SIDE_RIGHT] = idWaveFile::CHANNEL_INDEX_FRONT_RIGHT;
     90 	speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT] = idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER;
     91 	speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER;
     92 
     93 	speakerLeft[idWaveFile::CHANNEL_INDEX_FRONT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_CENTER;
     94 	speakerLeft[idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY] = idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY;
     95 
     96 	speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_RIGHT;
     97 	speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_RIGHT] = idWaveFile::CHANNEL_INDEX_SIDE_RIGHT;
     98 	speakerRight[idWaveFile::CHANNEL_INDEX_SIDE_RIGHT] = idWaveFile::CHANNEL_INDEX_BACK_RIGHT;
     99 	speakerRight[idWaveFile::CHANNEL_INDEX_BACK_RIGHT] = idWaveFile::CHANNEL_INDEX_BACK_CENTER;
    100 	speakerRight[idWaveFile::CHANNEL_INDEX_BACK_CENTER] = idWaveFile::CHANNEL_INDEX_BACK_LEFT;
    101 	speakerRight[idWaveFile::CHANNEL_INDEX_BACK_LEFT] = idWaveFile::CHANNEL_INDEX_SIDE_LEFT;
    102 	speakerRight[idWaveFile::CHANNEL_INDEX_SIDE_LEFT] = idWaveFile::CHANNEL_INDEX_FRONT_LEFT;
    103 	speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_LEFT] = idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER;
    104 	speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_LEFT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_RIGHT_CENTER;
    105 
    106 	speakerRight[idWaveFile::CHANNEL_INDEX_FRONT_CENTER] = idWaveFile::CHANNEL_INDEX_FRONT_CENTER;
    107 	speakerRight[idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY] = idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY;
    108 
    109 	dstChannels = outputChannels;
    110 	dstMask = channelMask;
    111 
    112 	// dstMap maps a destination channel to a speaker
    113 	// invMap maps a speaker to a destination channel
    114 	dstLFE = -1;
    115 	dstCenter = -1;
    116 	memset( dstMap, 0, sizeof( dstMap ) );
    117 	memset( invMap, 0, sizeof( invMap ) );
    118 	for ( int i = 0, c = 0; i < idWaveFile::CHANNEL_INDEX_MAX && c < MAX_CHANNELS_PER_VOICE; i++ ) {
    119 		if ( dstMask & BIT(i) ) {
    120 			if ( i == idWaveFile::CHANNEL_INDEX_LOW_FREQUENCY ) {
    121 				dstLFE = c;
    122 			}
    123 			if ( i == idWaveFile::CHANNEL_INDEX_FRONT_CENTER ) {
    124 				dstCenter = c;
    125 			}
    126 			dstMap[c] = i;
    127 			invMap[i] = c++;
    128 		} else {
    129 			// Remove this speaker from the chain
    130 			int right = speakerRight[i];
    131 			int left = speakerLeft[i];
    132 			speakerRight[left] = right;
    133 			speakerLeft[right] = left;
    134 		}
    135 	}
    136 	assert( ( dstLFE == -1 ) || ( ( dstMask & idWaveFile::CHANNEL_MASK_LOW_FREQUENCY ) != 0 ) );
    137 	assert( ( dstCenter == -1 ) || ( ( dstMask & idWaveFile::CHANNEL_MASK_FRONT_CENTER ) != 0 ) );
    138 
    139 	float omniChannels = (float)dstChannels;
    140 	if ( dstMask & idWaveFile::CHANNEL_MASK_LOW_FREQUENCY ) {
    141 		omniChannels -= 1.0f;
    142 	}
    143 	if ( dstMask & idWaveFile::CHANNEL_MASK_FRONT_CENTER ) {
    144 		omniChannels -= 1.0f;
    145 	}
    146 	if ( omniChannels > 0.0f ) {
    147 		omniLevel = 1.0f / omniChannels;
    148 	} else {
    149 		// This happens in mono mode
    150 		omniLevel = 1.0f;
    151 	}
    152 }
    153 
    154 /*
    155 ========================
    156 idSoundVoice_Base::CalculateSurround
    157 ========================
    158 */
    159 void idSoundVoice_Base::CalculateSurround( int srcChannels, float pLevelMatrix[ MAX_CHANNELS_PER_VOICE * MAX_CHANNELS_PER_VOICE ], float scale ) {
    160 	// Hack for mono
    161 	if ( dstChannels == 1 ) {
    162 		if ( srcChannels == 1 ) {
    163 			pLevelMatrix[ 0 ] = scale;
    164 		} else if ( srcChannels == 2 ) {
    165 			pLevelMatrix[ 0 ] = scale * 0.7071f;
    166 			pLevelMatrix[ 1 ] = scale * 0.7071f;
    167 		}
    168 		return;
    169 	}
    170 
    171 #define MATINDEX( src, dst ) ( srcChannels * dst + src )
    172 
    173 	float subFraction = s_subFraction.GetFloat();
    174 
    175 	if ( srcChannels == 1 ) {
    176 		idVec2 p2 = position.ToVec2();
    177 
    178 		float centerFraction = centerChannel;
    179 
    180 		float sqrLength = p2.LengthSqr();
    181 		if ( sqrLength <= 0.01f ) {
    182 			// If we are on top of the listener, simply route all channels to each speaker equally
    183 			for ( int i = 0; i < dstChannels; i++ ) {
    184 				pLevelMatrix[MATINDEX( 0, i )] = omniLevel;
    185 			}
    186 		} else {
    187 			float invLength = idMath::InvSqrt( sqrLength );
    188 			float distance = ( invLength * sqrLength );
    189 			p2 *= invLength;
    190 
    191 			float spatialize = 1.0f;
    192 			if ( distance < innerRadius ) {
    193 				spatialize = distance / innerRadius;
    194 			}
    195 			float omni = omniLevel * ( 1.0f - spatialize );
    196 
    197 			if ( dstCenter != -1 ) {
    198 				centerFraction *= Max( 0.0f, p2.x );
    199 				spatialize *= ( 1.0f - centerFraction );
    200 				omni *= ( 1.0f - centerFraction );
    201 			}
    202 
    203 			float channelDots[MAX_CHANNELS_PER_VOICE] = { 0 };
    204 			for ( int i = 0; i < dstChannels; i++ ) {
    205 				// Calculate the contribution to each destination channel
    206 				channelDots[i] = speakerPositions[dstMap[i]] * p2;
    207 			}
    208 			// Find the speaker nearest to the sound
    209 			int channelA = 0;
    210 			for ( int i = 1; i < dstChannels; i++ ) {
    211 				if ( channelDots[i] > channelDots[channelA] ) {
    212 					channelA = i;
    213 				}
    214 			}
    215 			int speakerA = dstMap[channelA];
    216 
    217 			// Find the 2nd nearest speaker
    218 			int speakerB;
    219 			float speakerACross = ( speakerPositions[speakerA].x * p2.y ) - ( speakerPositions[speakerA].y * p2.x );
    220 			if ( speakerACross > 0.0f ) {
    221 				speakerB = speakerLeft[speakerA];
    222 			} else {
    223 				speakerB = speakerRight[speakerA];
    224 			}
    225 			int channelB = invMap[speakerB];
    226 
    227 			// Divide the amplitude between the 2 closest speakers
    228 			float distA = ( speakerPositions[speakerA] - p2 ).Length();
    229 			float distB = ( speakerPositions[speakerB] - p2 ).Length();
    230 			float distCinv = 1.0f / ( distA + distB );
    231 			float volumes[MAX_CHANNELS_PER_VOICE] = { 0 };
    232 			volumes[channelA] = ( distB * distCinv );
    233 			volumes[channelB] = ( distA * distCinv );
    234 			for ( int i = 0; i < dstChannels; i++ ) {
    235 				pLevelMatrix[MATINDEX( 0, i )] = ( volumes[i] * spatialize ) + omni;
    236 			}
    237 		}
    238 		if ( dstLFE != -1 ) {
    239 			pLevelMatrix[MATINDEX( 0, dstLFE )] = subFraction;
    240 		}
    241 		if ( dstCenter != -1 ) {
    242 			pLevelMatrix[MATINDEX( 0, dstCenter )] = centerFraction;
    243 		}
    244 	} else if ( srcChannels == 2 ) {
    245 		pLevelMatrix[ MATINDEX( 0, 0 ) ] = 1.0f;
    246 		pLevelMatrix[ MATINDEX( 1, 1 ) ] = 1.0f;
    247 		if ( dstLFE != -1 ) {
    248 			pLevelMatrix[ MATINDEX( 0, dstLFE ) ] = subFraction * 0.5f;
    249 			pLevelMatrix[ MATINDEX( 1, dstLFE ) ] = subFraction * 0.5f;
    250 		}
    251 	} else {
    252 		idLib::Warning( "We don't support %d channel sound files", srcChannels );
    253 	}
    254 	for ( int i = 0; i < srcChannels * dstChannels; i++ ) {
    255 		pLevelMatrix[ i ] *= scale;
    256 	}
    257 }