DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

s_sound.cpp (13525B)


      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 
     29 #include "Precompiled.h"
     30 #include "globaldata.h"
     31 
     32 
     33 
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 
     37 #include "i_system.h"
     38 #include "i_sound.h"
     39 #include "sounds.h"
     40 #include "s_sound.h"
     41 
     42 #include "z_zone.h"
     43 #include "m_random.h"
     44 #include "w_wad.h"
     45 
     46 #include "doomdef.h"
     47 #include "p_local.h"
     48 
     49 #include "doomstat.h"
     50 #include "Main.h"
     51 
     52 // Purpose?
     53 const char snd_prefixen[]
     54 = { 'P', 'P', 'A', 'S', 'S', 'S', 'M', 'M', 'M', 'S', 'S', 'S' };
     55 
     56 
     57 // when to clip out sounds
     58 // Does not fit the large outdoor areas.
     59 
     60 // Distance tp origin when sounds should be maxed out.
     61 // This should relate to movement clipping resolution
     62 // (see BLOCKMAP handling).
     63 // Originally: (200*0x10000).
     64 
     65 
     66 
     67 // Adjustable by menu.
     68 
     69 
     70 
     71 // percent attenuation from front to back
     72 
     73 
     74 
     75 // Current music/sfx card - index useless
     76 //  w/o a reference LUT in a sound module.
     77 // Config file? Same disclaimer as above.
     78 
     79 
     80 
     81 
     82 
     83 // the set of ::g->channels available
     84 
     85 // These are not used, but should be (menu).
     86 // Maximum volume of a sound effect.
     87 // Internal default is max out of 0-15.
     88 
     89 // Maximum volume of music. Useless so far.
     90 
     91 
     92 
     93 // whether songs are ::g->mus_paused
     94 
     95 // music currently being played
     96 
     97 // following is set
     98 //  by the defaults code in M_misc:
     99 // number of ::g->channels available
    100 
    101 
    102 
    103 
    104 //
    105 // Internals.
    106 //
    107 int
    108 S_getChannel
    109 ( void*		origin,
    110  sfxinfo_t*	sfxinfo );
    111 
    112 
    113 int
    114 S_AdjustSoundParams
    115 ( mobj_t*	listener,
    116  mobj_t*	source,
    117  int*		vol,
    118  int*		sep,
    119  int*		pitch );
    120 
    121 void S_StopChannel(int cnum);
    122 
    123 
    124 
    125 //
    126 // Initializes sound stuff, including volume
    127 // Sets ::g->channels, SFX and music volume,
    128 //  allocates channel buffer, sets S_sfx lookup.
    129 //
    130 void S_Init
    131 ( int		sfxVolume,
    132  int		musicVolume )
    133 {  
    134 	int		i;
    135 
    136 	// Whatever these did with DMX, these are rather dummies now.
    137 	I_SetChannels();
    138 
    139 	S_SetSfxVolume(sfxVolume);
    140 	S_SetMusicVolume(musicVolume);
    141 
    142 	// Allocating the internal ::g->channels for mixing
    143 	// (the maximum numer of sounds rendered
    144 	// simultaneously) within zone memory.
    145 	::g->channels =
    146 		(channel_t *) DoomLib::Z_Malloc(::g->numChannels*sizeof(channel_t), PU_STATIC, 0);
    147 
    148 	// Free all ::g->channels for use
    149 	for (i=0 ; i < ::g->numChannels ; i++)
    150 		::g->channels[i].sfxinfo = 0;
    151 
    152 	// no sounds are playing, and they are not ::g->mus_paused
    153 	::g->mus_paused = 0;
    154 	::g->mus_looping = 0;
    155 
    156 	// Note that sounds have not been cached (yet).
    157 	for (i=1 ; i<NUMSFX ; i++)
    158 		S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;
    159 }
    160 
    161 
    162 
    163 
    164 //
    165 // Per level startup code.
    166 // Kills playing sounds at start of level,
    167 //  determines music if any, changes music.
    168 //
    169 void S_Start(void)
    170 {
    171 	int cnum;
    172 	int mnum;
    173 
    174 	// kill all playing sounds at start of level
    175 	//  (trust me - a good idea)
    176 	if( ::g->channels ) {
    177 		for (cnum=0 ; cnum < ::g->numChannels ; cnum++)
    178 			if (::g->channels[cnum].sfxinfo)
    179 				S_StopChannel(cnum);
    180 	}
    181 
    182 	// start new music for the level
    183 	::g->mus_paused = 0;
    184 
    185 	if (::g->gamemode == commercial) {
    186 		
    187 		mnum = mus_runnin + ::g->gamemap - 1;
    188 		
    189 		/*
    190 		Is this necessary?
    191 		
    192 		if ( ::g->gamemission == 0 ) {
    193 			mnum = mus_runnin + ::g->gamemap - 1;
    194 		}
    195 		else {
    196 			mnum = mus_runnin + ( gameLocal->GetMissionData( ::g->gamemission )->mapMusic[ ::g->gamemap-1 ] - 1 );
    197 		}
    198 		*/
    199 	}
    200 	else
    201 	{
    202 		int spmus[] = {
    203 			// Song -	Who? -			Where?
    204 			mus_e3m4,	// American		e4m1
    205 			mus_e3m2,	// Romero		e4m2
    206 			mus_e3m3,	// Shawn		e4m3
    207 			mus_e1m5,	// American		e4m4
    208 			mus_e2m7,	// Tim			e4m5
    209 			mus_e2m4,	// Romero		e4m6
    210 			mus_e2m6,	// J.Anderson	e4m7 CHIRON.WAD
    211 			mus_e2m5,	// Shawn		e4m8
    212 			mus_e1m9	// Tim			e4m9
    213 		};
    214 
    215 		if (::g->gameepisode < 4)
    216 			mnum = mus_e1m1 + (::g->gameepisode-1)*9 + ::g->gamemap-1;
    217 		else
    218 			mnum = spmus[::g->gamemap-1];
    219 	}	
    220 
    221 	S_StopMusic();
    222 	S_ChangeMusic(mnum, true);
    223 
    224 	::g->nextcleanup = 15;
    225 }	
    226 
    227 
    228 
    229 
    230 
    231 void
    232 S_StartSoundAtVolume
    233 ( void*		origin_p,
    234  int		sfx_id,
    235  int		volume )
    236 {
    237 
    238 	int		rc;
    239 	int		sep;
    240 	int		pitch;
    241 	int		priority;
    242 	sfxinfo_t*	sfx;
    243 	int		cnum;
    244 
    245 	mobj_t*	origin = (mobj_t *) origin_p;
    246 
    247 
    248 	// Debug.
    249 	/*I_PrintfE
    250 	"S_StartSoundAtVolume: playing sound %d (%s)\n",
    251 	sfx_id, S_sfx[sfx_id].name );*/
    252 
    253 	// check for bogus sound #
    254 	if (sfx_id < 1 || sfx_id > NUMSFX)
    255 		I_Error("Bad sfx #: %d", sfx_id);
    256 
    257 	sfx = &S_sfx[sfx_id];
    258 
    259 	// Initialize sound parameters
    260 	if (sfx->link)
    261 	{
    262 		pitch = sfx->pitch;
    263 		priority = sfx->priority;
    264 		volume += sfx->volume;
    265 
    266 		if (volume < 1)
    267 			return;
    268 
    269 		if ( volume > s_volume_sound.GetInteger() )
    270 			volume = s_volume_sound.GetInteger();
    271 	}
    272 	else
    273 	{
    274 		pitch = NORM_PITCH;
    275 		priority = NORM_PRIORITY;
    276 
    277 		if (volume < 1)
    278 			return;
    279 
    280 		if ( volume > s_volume_sound.GetInteger() )
    281 			volume = s_volume_sound.GetInteger();
    282 
    283 	}
    284 
    285 
    286 	// Check to see if it is audible,
    287 	//  and if not, modify the params
    288 	// DHM - Nerve :: chainsaw!!!
    289 	if (origin && ::g->players[::g->consoleplayer].mo && origin != ::g->players[::g->consoleplayer].mo)
    290 	{
    291 		rc = S_AdjustSoundParams(::g->players[::g->consoleplayer].mo,
    292 			origin,
    293 			&volume,
    294 			&sep,
    295 			&pitch);
    296 
    297 		if ( origin->x == ::g->players[::g->consoleplayer].mo->x
    298 			&& origin->y == ::g->players[::g->consoleplayer].mo->y)
    299 		{	
    300 			sep 	= NORM_SEP;
    301 		}
    302 
    303 		if (!rc)
    304 			return;
    305 	}	
    306 	else
    307 	{
    308 		sep = NORM_SEP;
    309 	}
    310 
    311 	// hacks to vary the sfx pitches
    312 	const bool isChainsawSound = sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit;
    313 
    314 	if ( !isChainsawSound && sfx_id != sfx_itemup && sfx_id != sfx_tink)
    315 	{
    316 		pitch += 16 - (M_Random()&31);
    317 
    318 		if (pitch<0)
    319 			pitch = 0;
    320 		else if (pitch>255)
    321 			pitch = 255;
    322 	}
    323 
    324 	// kill old sound
    325 	//S_StopSound(origin);
    326 
    327 	// try to find a channel
    328 	cnum = S_getChannel(origin, sfx);
    329 
    330 	if (cnum<0) {
    331 		printf( "No sound channels available for sfxid %d.\n", sfx_id );
    332 		return;
    333 	}
    334 
    335 	// get lumpnum if necessary
    336 	if (sfx->lumpnum < 0)
    337 		sfx->lumpnum = I_GetSfxLumpNum(sfx);
    338 
    339 	// increase the usefulness
    340 	if (sfx->usefulness++ < 0)
    341 		sfx->usefulness = 1;
    342 
    343 	// Assigns the handle to one of the ::g->channels in the
    344 	//  mix/output buffer.
    345 	::g->channels[cnum].handle = I_StartSound(sfx_id, origin, ::g->players[::g->consoleplayer].mo, volume, pitch, priority);
    346 }	
    347 
    348 void S_StartSound ( void*		origin, int		sfx_id )
    349 {
    350 	S_StartSoundAtVolume(origin, sfx_id, s_volume_sound.GetInteger() );
    351 }
    352 
    353 
    354 
    355 
    356 void S_StopSound(void *origin)
    357 {
    358 
    359 	int cnum;
    360 
    361 	for (cnum=0 ; cnum < ::g->numChannels ; cnum++)
    362 	{
    363 		if (::g->channels[cnum].sfxinfo && ::g->channels[cnum].origin == origin)
    364 		{
    365 			S_StopChannel(cnum);
    366 			break;
    367 		}
    368 	}
    369 }
    370 
    371 
    372 
    373 
    374 
    375 //
    376 // Stop and resume music, during game PAUSE.
    377 //
    378 void S_PauseSound(void)
    379 {
    380 	if (::g->mus_playing && !::g->mus_paused)
    381 	{
    382 		I_PauseSong(::g->mus_playing->handle);
    383 		::g->mus_paused = true;
    384 	}
    385 }
    386 
    387 void S_ResumeSound(void)
    388 {
    389 	if (::g->mus_playing && ::g->mus_paused)
    390 	{
    391 		I_ResumeSong(::g->mus_playing->handle);
    392 		::g->mus_paused = false;
    393 	}
    394 }
    395 
    396 
    397 //
    398 // Updates music & sounds
    399 //
    400 void S_UpdateSounds(void* listener_p)
    401 {
    402 	int		audible;
    403 	int		cnum;
    404 	int		volume;
    405 	int		sep;
    406 	int		pitch;
    407 	sfxinfo_t*	sfx;
    408 	channel_t*	c;
    409 
    410 	mobj_t*	listener = (mobj_t*)listener_p;
    411 
    412 	for (cnum=0 ; cnum < ::g->numChannels ; cnum++)
    413 	{
    414 		c = &::g->channels[cnum];
    415 		sfx = c->sfxinfo;
    416 
    417 		if(sfx)
    418 		{
    419 			if (I_SoundIsPlaying(c->handle))
    420 			{
    421 				// initialize parameters
    422 				volume = s_volume_sound.GetInteger();
    423 				pitch = NORM_PITCH;
    424 				sep = NORM_SEP;
    425 
    426 				if (sfx->link)
    427 				{
    428 					pitch = sfx->pitch;
    429 					volume += sfx->volume;
    430 
    431 					if (volume < 1)	{
    432 						S_StopChannel(cnum);
    433 						continue;
    434 
    435 					} else if ( volume > s_volume_sound.GetInteger() ) {
    436 						volume = s_volume_sound.GetInteger();
    437 					}
    438 				}
    439 
    440 				// check non-local sounds for distance clipping or modify their params
    441 				if (c->origin && listener_p != c->origin)
    442 				{
    443 					audible = S_AdjustSoundParams(listener,	(mobj_t*)c->origin,	&volume, &sep, &pitch);
    444 					if (!audible) {
    445 						S_StopChannel(cnum);
    446 					}
    447 				}
    448 			}
    449 			else
    450 			{
    451 				// if channel is allocated but sound has stopped, free it
    452 				S_StopChannel(cnum);
    453 			}
    454 		}
    455 	}
    456 }
    457 
    458 
    459 void S_SetMusicVolume(int volume)
    460 {
    461 	I_SetMusicVolume(volume);
    462 	s_volume_midi.SetInteger( volume );
    463 }
    464 
    465 
    466 
    467 void S_SetSfxVolume(int volume)
    468 {
    469 	I_SetSfxVolume(volume);
    470 	s_volume_sound.SetInteger( volume );
    471 }
    472 
    473 //
    474 // Starts some music with the music id found in sounds.h.
    475 //
    476 void S_StartMusic(int m_id)
    477 {
    478 	S_ChangeMusic(m_id, false);
    479 }
    480 
    481 void S_ChangeMusic ( int			musicnum, int			looping )
    482 {
    483 #ifdef ID_ENABLE_DOOM_CLASSIC_NETWORKING
    484 	if (gameLocal->IsSplitscreen() && DoomLib::GetPlayer() > 0 )
    485 	{
    486 		// we aren't the key player... we have no control over music
    487 		return;
    488 	}
    489 #endif
    490 
    491 	musicinfo_t*	music = NULL;
    492 
    493 	if ( (musicnum <= mus_None)
    494 		|| (musicnum >= NUMMUSIC) )
    495 	{
    496 		I_Error("Bad music number %d", musicnum);
    497 	}
    498 	else
    499 		music = &::g->S_music[musicnum];
    500 
    501 	if (::g->mus_playing == music)
    502 		return;
    503 
    504 	//I_Printf("S_ChangeMusic: Playing new track: '%s'\n", music->name);
    505 
    506 	I_PlaySong( music->name, looping );
    507 
    508 	::g->mus_playing = music;
    509 }
    510 
    511 
    512 void S_StopMusic(void)
    513 {
    514 	if (::g->doomcom.consoleplayer)
    515 	{
    516 		// we aren't the key player... we have no control over music
    517 		return;
    518 	}
    519 	
    520 	if (::g->mus_playing)
    521 	{
    522 		if (::g->mus_paused)
    523 			I_ResumeSong(::g->mus_playing->handle);
    524 
    525 		I_StopSong(::g->mus_playing->handle);
    526 		I_UnRegisterSong(::g->mus_playing->handle);
    527 		//Z_FreeTags( PU_MUSIC_SHARED, PU_MUSIC_SHARED );
    528 
    529 		::g->mus_playing->data = 0;
    530 		::g->mus_playing = 0;
    531 	}
    532 }
    533 
    534 
    535 
    536 
    537 void S_StopChannel(int cnum)
    538 {
    539 
    540 	int		i;
    541 	channel_t*	c = &::g->channels[cnum];
    542 
    543 	if (c->sfxinfo)
    544 	{
    545 		// stop the sound playing
    546 		if (I_SoundIsPlaying(c->handle))
    547 		{
    548 #ifdef SAWDEBUG
    549 			if (c->sfxinfo == &S_sfx[sfx_sawful])
    550 				I_PrintfE( "stopped\n");
    551 #endif
    552 			I_StopSound(c->handle);
    553 		}
    554 
    555 		// check to see
    556 		//  if other ::g->channels are playing the sound
    557 		for (i=0 ; i < ::g->numChannels ; i++)
    558 		{
    559 			if (cnum != i
    560 				&& c->sfxinfo == ::g->channels[i].sfxinfo)
    561 			{
    562 				break;
    563 			}
    564 		}
    565 
    566 		// degrade usefulness of sound data
    567 		c->sfxinfo->usefulness--;
    568 
    569 		c->sfxinfo = 0;
    570 	}
    571 }
    572 
    573 
    574 
    575 //
    576 // Changes volume, stereo-separation, and pitch variables
    577 //  from the norm of a sound effect to be played.
    578 // If the sound is not audible, returns a 0.
    579 // Otherwise, modifies parameters and returns 1.
    580 //
    581 int S_AdjustSoundParams( mobj_t* listener, mobj_t* source, int* vol, int* sep, int* pitch ) {
    582 	fixed_t	approx_dist;
    583 	fixed_t	adx;
    584 	fixed_t	ady;
    585 
    586 	// DHM - Nerve :: Could happen in multiplayer if a player exited the level holding the chainsaw
    587 	if ( listener == NULL || source == NULL ) {
    588 		return 0;
    589 	}
    590 
    591 	// calculate the distance to sound origin
    592 	//  and clip it if necessary
    593 	adx = abs(listener->x - source->x);
    594 	ady = abs(listener->y - source->y);
    595 
    596 	// From _GG1_ p.428. Appox. eucledian distance fast.
    597 	approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);
    598 
    599 	if ( approx_dist > S_CLIPPING_DIST)	{
    600 		return 0;
    601 	}
    602 
    603 	// angle of source to listener
    604 	*sep = NORM_SEP;
    605 
    606 	// volume calculation
    607 	if (approx_dist < S_CLOSE_DIST)	{
    608 		*vol = s_volume_sound.GetInteger();
    609 	}
    610 	else {
    611 		// distance effect
    612 		*vol = ( s_volume_sound.GetInteger()
    613 			* ((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
    614 			/ S_ATTENUATOR; 
    615 	}
    616 
    617 	return (*vol > 0);
    618 }
    619 
    620 
    621 
    622 
    623 //
    624 // S_getChannel :
    625 //   If none available, return -1.  Otherwise channel #.
    626 //
    627 int
    628 S_getChannel
    629 ( void*		origin,
    630  sfxinfo_t*	sfxinfo )
    631 {
    632 	// channel number to use
    633 	int		cnum;
    634 
    635 	channel_t*	c;
    636 
    637 	// Find an open channel
    638 	for (cnum=0 ; cnum < ::g->numChannels ; cnum++)
    639 	{
    640 		if (!::g->channels[cnum].sfxinfo)
    641 			break;
    642 		else if ( origin && ::g->channels[cnum].origin == origin && 
    643 				(::g->channels[cnum].handle == sfx_sawidl || ::g->channels[cnum].handle == sfx_sawful) )
    644 		{
    645 			S_StopChannel(cnum);
    646 			break;
    647 		}
    648 	}
    649 
    650 	// None available
    651 	if (cnum == ::g->numChannels)
    652 	{
    653 		// Look for lower priority
    654 		for (cnum=0 ; cnum < ::g->numChannels ; cnum++)
    655 			if (::g->channels[cnum].sfxinfo->priority >= sfxinfo->priority) break;
    656 
    657 		if (cnum == ::g->numChannels)
    658 		{
    659 			// FUCK!  No lower priority.  Sorry, Charlie.    
    660 			return -1;
    661 		}
    662 		else
    663 		{
    664 			// Otherwise, kick out lower priority.
    665 			S_StopChannel(cnum);
    666 		}
    667 	}
    668 
    669 	c = &::g->channels[cnum];
    670 
    671 	// channel is decided to be cnum.
    672 	c->sfxinfo = sfxinfo;
    673 	c->origin = origin;
    674 
    675 	return cnum;
    676 }
    677 
    678 
    679 
    680 
    681