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