1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19*/ 20// snd_dma.c -- main control for any streaming sound output device 21 22#include "quakedef.h" 23 24#ifdef _WIN32 25#include "winquake.h" 26#endif 27 28void S_Play(void); 29void S_PlayVol(void); 30void S_SoundList(void); 31void S_Update_(); 32void S_StopAllSounds(qboolean clear); 33void S_StopAllSoundsC(void); 34 35// QuakeWorld hack... 36#define viewentity playernum+1 37 38// ======================================================================= 39// Internal sound data & structures 40// ======================================================================= 41 42channel_t channels[MAX_CHANNELS]; 43int total_channels; 44 45int snd_blocked = 0; 46static qboolean snd_ambient = 1; 47qboolean snd_initialized = false; 48 49// pointer should go away 50volatile dma_t *shm = 0; 51volatile dma_t sn; 52 53vec3_t listener_origin; 54vec3_t listener_forward; 55vec3_t listener_right; 56vec3_t listener_up; 57vec_t sound_nominal_clip_dist=1000.0; 58 59int soundtime; // sample PAIRS 60int paintedtime; // sample PAIRS 61 62 63#define MAX_SFX 512 64sfx_t *known_sfx; // hunk allocated [MAX_SFX] 65int num_sfx; 66 67sfx_t *ambient_sfx[NUM_AMBIENTS]; 68 69int desired_speed = 11025; 70int desired_bits = 16; 71 72int sound_started=0; 73 74cvar_t bgmvolume = CVAR3("bgmvolume", "1", true); 75cvar_t volume = CVAR3("volume", "0.7", true); 76 77cvar_t nosound = CVAR2("nosound", "0"); 78cvar_t precache = CVAR2("precache", "1"); 79cvar_t loadas8bit = CVAR2("loadas8bit", "0"); 80cvar_t bgmbuffer = CVAR2("bgmbuffer", "4096"); 81cvar_t ambient_level = CVAR2("ambient_level", "0.3"); 82cvar_t ambient_fade = CVAR2("ambient_fade", "100"); 83cvar_t snd_noextraupdate = CVAR2("snd_noextraupdate", "0"); 84cvar_t snd_show = CVAR2("snd_show", "0"); 85cvar_t _snd_mixahead = CVAR3("_snd_mixahead", "0.1", true); 86 87 88// ==================================================================== 89// User-setable variables 90// ==================================================================== 91 92 93// 94// Fake dma is a synchronous faking of the DMA progress used for 95// isolating performance in the renderer. The fakedma_updates is 96// number of times S_Update() is called per second. 97// 98 99qboolean fakedma = false; 100int fakedma_updates = 15; 101 102 103void S_AmbientOff (void) 104{ 105 snd_ambient = false; 106} 107 108 109void S_AmbientOn (void) 110{ 111 snd_ambient = true; 112} 113 114 115void S_SoundInfo_f(void) 116{ 117 if (!sound_started || !shm) 118 { 119 Con_Printf ("sound system not started\n"); 120 return; 121 } 122 123 Con_Printf("%5d stereo\n", shm->channels - 1); 124 Con_Printf("%5d samples\n", shm->samples); 125 Con_Printf("%5d samplepos\n", shm->samplepos); 126 Con_Printf("%5d samplebits\n", shm->samplebits); 127 Con_Printf("%5d submission_chunk\n", shm->submission_chunk); 128 Con_Printf("%5d speed\n", shm->speed); 129 Con_Printf("0x%x dma buffer\n", shm->buffer); 130 Con_Printf("%5d total_channels\n", total_channels); 131} 132 133 134/* 135================ 136S_Startup 137================ 138*/ 139 140void S_Startup (void) 141{ 142 int rc; 143 144 if (!snd_initialized) 145 return; 146 147 if (!fakedma) 148 { 149 rc = SNDDMA_Init(); 150 151 if (!rc) 152 { 153#ifndef _WIN32 154 Con_Printf("S_Startup: SNDDMA_Init failed.\n"); 155#endif 156 sound_started = 0; 157 return; 158 } 159 } 160 161 sound_started = 1; 162} 163 164 165/* 166================ 167S_Init 168================ 169*/ 170void S_Init (void) 171{ 172 173// Con_Printf("\nSound Initialization\n"); 174 175 if (COM_CheckParm("-nosound")) 176 return; 177 178 if (COM_CheckParm("-simsound")) 179 fakedma = true; 180 181 Cmd_AddCommand("play", S_Play); 182 Cmd_AddCommand("playvol", S_PlayVol); 183 Cmd_AddCommand("stopsound", S_StopAllSoundsC); 184 Cmd_AddCommand("soundlist", S_SoundList); 185 Cmd_AddCommand("soundinfo", S_SoundInfo_f); 186 187 Cvar_RegisterVariable(&nosound); 188 Cvar_RegisterVariable(&volume); 189 Cvar_RegisterVariable(&precache); 190 Cvar_RegisterVariable(&loadas8bit); 191 Cvar_RegisterVariable(&bgmvolume); 192 Cvar_RegisterVariable(&bgmbuffer); 193 Cvar_RegisterVariable(&ambient_level); 194 Cvar_RegisterVariable(&ambient_fade); 195 Cvar_RegisterVariable(&snd_noextraupdate); 196 Cvar_RegisterVariable(&snd_show); 197 Cvar_RegisterVariable(&_snd_mixahead); 198 199 if (host_parms.memsize < 0x800000) 200 { 201 Cvar_Set ("loadas8bit", "1"); 202 Con_Printf ("loading all sounds as 8bit\n"); 203 } 204 205 206 207 snd_initialized = true; 208 209 S_Startup (); 210 211 SND_InitScaletable (); 212 213 known_sfx = Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t"); 214 num_sfx = 0; 215 216// create a piece of DMA memory 217 218 if (fakedma) 219 { 220 shm = (void *) Hunk_AllocName(sizeof(*shm), "shm"); 221 shm->splitbuffer = 0; 222 shm->samplebits = 16; 223 shm->speed = 22050; 224 shm->channels = 2; 225 shm->samples = 32768; 226 shm->samplepos = 0; 227 shm->soundalive = true; 228 shm->gamealive = true; 229 shm->submission_chunk = 1; 230 shm->buffer = Hunk_AllocName(1<<16, "shmbuf"); 231 } 232 233// Con_Printf ("Sound sampling rate: %i\n", shm->speed); 234 235 // provides a tick sound until washed clean 236 237// if (shm->buffer) 238// shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging 239 240 ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav"); 241 ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav"); 242 243 S_StopAllSounds (true); 244} 245 246 247// ======================================================================= 248// Shutdown sound engine 249// ======================================================================= 250 251void S_Shutdown(void) 252{ 253 254 if (!sound_started) 255 return; 256 257 if (shm) 258 shm->gamealive = 0; 259 260 shm = 0; 261 sound_started = 0; 262 263 if (!fakedma) 264 { 265 SNDDMA_Shutdown(); 266 } 267} 268 269 270// ======================================================================= 271// Load a sound 272// ======================================================================= 273 274/* 275================== 276S_FindName 277 278================== 279*/ 280sfx_t *S_FindName (char *name) 281{ 282 int i; 283 sfx_t *sfx; 284 285 if (!name) 286 Sys_Error ("S_FindName: NULL\n"); 287 288 if (Q_strlen(name) >= MAX_QPATH) 289 Sys_Error ("Sound name too long: %s", name); 290 291// see if already loaded 292 for (i=0 ; i < num_sfx ; i++) 293 if (!Q_strcmp(known_sfx[i].name, name)) 294 { 295 return &known_sfx[i]; 296 } 297 298 if (num_sfx == MAX_SFX) 299 Sys_Error ("S_FindName: out of sfx_t"); 300 301 sfx = &known_sfx[i]; 302 strcpy (sfx->name, name); 303 304 num_sfx++; 305 306 return sfx; 307} 308 309 310/* 311================== 312S_TouchSound 313 314================== 315*/ 316void S_TouchSound (char *name) 317{ 318 sfx_t *sfx; 319 320 if (!sound_started) 321 return; 322 323 sfx = S_FindName (name); 324 Cache_Check (&sfx->cache); 325} 326 327/* 328================== 329S_PrecacheSound 330 331================== 332*/ 333sfx_t *S_PrecacheSound (char *name) 334{ 335 sfx_t *sfx; 336 337 if (!sound_started || nosound.value) 338 return NULL; 339 340 sfx = S_FindName (name); 341 342// cache it in 343 if (precache.value) 344 S_LoadSound (sfx); 345 346 return sfx; 347} 348 349 350//============================================================================= 351 352/* 353================= 354SND_PickChannel 355================= 356*/ 357channel_t *SND_PickChannel(int entnum, int entchannel) 358{ 359 int ch_idx; 360 int first_to_die; 361 int life_left; 362 363// Check for replacement sound, or find the best one to replace 364 first_to_die = -1; 365 life_left = 0x7fffffff; 366 for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) 367 { 368 if (entchannel != 0 // channel 0 never overrides 369 && channels[ch_idx].entnum == entnum 370 && (channels[ch_idx].entchannel == entchannel || entchannel == -1) ) 371 { // allways override sound from same entity 372 first_to_die = ch_idx; 373 break; 374 } 375 376 // don't let monster sounds override player sounds 377 if (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && channels[ch_idx].sfx) 378 continue; 379 380 if (channels[ch_idx].end - paintedtime < life_left) 381 { 382 life_left = channels[ch_idx].end - paintedtime; 383 first_to_die = ch_idx; 384 } 385 } 386 387 if (first_to_die == -1) 388 return NULL; 389 390 if (channels[first_to_die].sfx) 391 channels[first_to_die].sfx = NULL; 392 393 return &channels[first_to_die]; 394} 395 396/* 397================= 398SND_Spatialize 399================= 400*/ 401void SND_Spatialize(channel_t *ch) 402{ 403 vec_t dot; 404 vec_t dist; 405 vec_t lscale, rscale, scale; 406 vec3_t source_vec; 407 sfx_t *snd; 408 409// anything coming from the view entity will allways be full volume 410 if (ch->entnum == cl.viewentity) 411 { 412 ch->leftvol = ch->master_vol; 413 ch->rightvol = ch->master_vol; 414 return; 415 } 416 417// calculate stereo seperation and distance attenuation 418 419 snd = ch->sfx; 420 VectorSubtract(ch->origin, listener_origin, source_vec); 421 422 dist = VectorNormalize(source_vec) * ch->dist_mult; 423 424 dot = DotProduct(listener_right, source_vec); 425 426 if (shm->channels == 1) 427 { 428 rscale = 1.0; 429 lscale = 1.0; 430 } 431 else 432 { 433 rscale = 1.0 + dot; 434 lscale = 1.0 - dot; 435 } 436 437// add in distance effect 438 scale = (1.0 - dist) * rscale; 439 ch->rightvol = (int) (ch->master_vol * scale); 440 if (ch->rightvol < 0) 441 ch->rightvol = 0; 442 443 scale = (1.0 - dist) * lscale; 444 ch->leftvol = (int) (ch->master_vol * scale); 445 if (ch->leftvol < 0) 446 ch->leftvol = 0; 447} 448 449 450// ======================================================================= 451// Start a sound effect 452// ======================================================================= 453 454void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) 455{ 456 channel_t *target_chan, *check; 457 sfxcache_t *sc; 458 int vol; 459 int ch_idx; 460 int skip; 461 462 if (!sound_started) 463 return; 464 465 if (!sfx) 466 return; 467 468 if (nosound.value) 469 return; 470 471 vol = fvol*255; 472 473// pick a channel to play on 474 target_chan = SND_PickChannel(entnum, entchannel); 475 if (!target_chan) 476 return; 477 478// spatialize 479 memset (target_chan, 0, sizeof(*target_chan)); 480 VectorCopy(origin, target_chan->origin); 481 target_chan->dist_mult = attenuation / sound_nominal_clip_dist; 482 target_chan->master_vol = vol; 483 target_chan->entnum = entnum; 484 target_chan->entchannel = entchannel; 485 SND_Spatialize(target_chan); 486 487 if (!target_chan->leftvol && !target_chan->rightvol) 488 return; // not audible at all 489 490// new channel 491 sc = S_LoadSound (sfx); 492 if (!sc) 493 { 494 target_chan->sfx = NULL; 495 return; // couldn't load the sound's data 496 } 497 498 target_chan->sfx = sfx; 499 target_chan->pos = 0.0; 500 target_chan->end = paintedtime + sc->length; 501 502// if an identical sound has also been started this frame, offset the pos 503// a bit to keep it from just making the first one louder 504 check = &channels[NUM_AMBIENTS]; 505 for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++) 506 { 507 if (check == target_chan) 508 continue; 509 if (check->sfx == sfx && !check->pos) 510 { 511 skip = rand () % (int)(0.1*shm->speed); 512 if (skip >= target_chan->end) 513 skip = target_chan->end - 1; 514 target_chan->pos += skip; 515 target_chan->end -= skip; 516 break; 517 } 518 519 } 520} 521 522void S_StopSound(int entnum, int entchannel) 523{ 524 int i; 525 526 for (i=0 ; i<MAX_DYNAMIC_CHANNELS ; i++) 527 { 528 if (channels[i].entnum == entnum 529 && channels[i].entchannel == entchannel) 530 { 531 channels[i].end = 0; 532 channels[i].sfx = NULL; 533 return; 534 } 535 } 536} 537 538void S_StopAllSounds(qboolean clear) 539{ 540 int i; 541 542 if (!sound_started) 543 return; 544 545 total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics 546 547 for (i=0 ; i<MAX_CHANNELS ; i++) 548 if (channels[i].sfx) 549 channels[i].sfx = NULL; 550 551 Q_memset(channels, 0, MAX_CHANNELS * sizeof(channel_t)); 552 553 if (clear) 554 S_ClearBuffer (); 555} 556 557void S_StopAllSoundsC (void) 558{ 559 S_StopAllSounds (true); 560} 561 562void S_ClearBuffer (void) 563{ 564 int clear; 565 566#ifdef _WIN32 567 if (!sound_started || !shm || (!shm->buffer && !pDSBuf)) 568#else 569 if (!sound_started || !shm || !shm->buffer) 570#endif 571 return; 572 573 if (shm->samplebits == 8) 574 clear = 0x80; 575 else 576 clear = 0; 577 578#ifdef _WIN32 579 if (pDSBuf) 580 { 581 DWORD dwSize; 582 DWORD *pData; 583 int reps; 584 HRESULT hresult; 585 586 reps = 0; 587 588 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pData, &dwSize, NULL, NULL, 0)) != DS_OK) 589 { 590 if (hresult != DSERR_BUFFERLOST) 591 { 592 Con_Printf ("S_ClearBuffer: DS::Lock Sound Buffer Failed\n"); 593 S_Shutdown (); 594 return; 595 } 596 597 if (++reps > 10000) 598 { 599 Con_Printf ("S_ClearBuffer: DS: couldn't restore buffer\n"); 600 S_Shutdown (); 601 return; 602 } 603 } 604 605 Q_memset(pData, clear, shm->samples * shm->samplebits/8); 606 607 pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0); 608 609 } 610 else 611#endif 612 { 613 Q_memset(shm->buffer, clear, shm->samples * shm->samplebits/8); 614 } 615} 616 617 618/* 619================= 620S_StaticSound 621================= 622*/ 623void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) 624{ 625 channel_t *ss; 626 sfxcache_t *sc; 627 628 if (!sfx) 629 return; 630 631 if (total_channels == MAX_CHANNELS) 632 { 633 Con_Printf ("total_channels == MAX_CHANNELS\n"); 634 return; 635 } 636 637 ss = &channels[total_channels]; 638 total_channels++; 639 640 sc = S_LoadSound (sfx); 641 if (!sc) 642 return; 643 644 if (sc->loopstart == -1) 645 { 646 Con_Printf ("Sound %s not looped\n", sfx->name); 647 return; 648 } 649 650 ss->sfx = sfx; 651 VectorCopy (origin, ss->origin); 652 ss->master_vol = vol; 653 ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist; 654 ss->end = paintedtime + sc->length; 655 656 SND_Spatialize (ss); 657} 658 659 660//============================================================================= 661 662/* 663=================== 664S_UpdateAmbientSounds 665=================== 666*/ 667void S_UpdateAmbientSounds (void) 668{ 669 mleaf_t *l; 670 float vol; 671 int ambient_channel; 672 channel_t *chan; 673 674 if (!snd_ambient) 675 return; 676 677// calc ambient sound levels 678 if (!cl.worldmodel) 679 return; 680 681 l = Mod_PointInLeaf (listener_origin, cl.worldmodel); 682 if (!l || !ambient_level.value) 683 { 684 for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) 685 channels[ambient_channel].sfx = NULL; 686 return; 687 } 688 689 for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) 690 { 691 chan = &channels[ambient_channel]; 692 chan->sfx = ambient_sfx[ambient_channel]; 693 694 vol = ambient_level.value * l->ambient_sound_level[ambient_channel]; 695 if (vol < 8) 696 vol = 0; 697 698 // don't adjust volume too fast 699 if (chan->master_vol < vol) 700 { 701 chan->master_vol += host_frametime * ambient_fade.value; 702 if (chan->master_vol > vol) 703 chan->master_vol = vol; 704 } 705 else if (chan->master_vol > vol) 706 { 707 chan->master_vol -= host_frametime * ambient_fade.value; 708 if (chan->master_vol < vol) 709 chan->master_vol = vol; 710 } 711 712 chan->leftvol = chan->rightvol = chan->master_vol; 713 } 714} 715 716 717/* 718============ 719S_Update 720 721Called once each time through the main loop 722============ 723*/ 724void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) 725{ 726 int i, j; 727 int total; 728 channel_t *ch; 729 channel_t *combine; 730 731 if (!sound_started || (snd_blocked > 0)) 732 return; 733 734 VectorCopy(origin, listener_origin); 735 VectorCopy(forward, listener_forward); 736 VectorCopy(right, listener_right); 737 VectorCopy(up, listener_up); 738 739// update general area ambient sound sources 740 S_UpdateAmbientSounds (); 741 742 combine = NULL; 743 744// update spatialization for static and dynamic sounds 745 ch = channels+NUM_AMBIENTS; 746 for (i=NUM_AMBIENTS ; i<total_channels; i++, ch++) 747 { 748 if (!ch->sfx) 749 continue; 750 SND_Spatialize(ch); // respatialize channel 751 if (!ch->leftvol && !ch->rightvol) 752 continue; 753 754 // try to combine static sounds with a previous channel of the same 755 // sound effect so we don't mix five torches every frame 756 757 if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) 758 { 759 // see if it can just use the last one 760 if (combine && combine->sfx == ch->sfx) 761 { 762 combine->leftvol += ch->leftvol; 763 combine->rightvol += ch->rightvol; 764 ch->leftvol = ch->rightvol = 0; 765 continue; 766 } 767 // search for one 768 combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; 769 for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; j<i; j++, combine++) 770 if (combine->sfx == ch->sfx) 771 break; 772 773 if (j == total_channels) 774 { 775 combine = NULL; 776 } 777 else 778 { 779 if (combine != ch) 780 { 781 combine->leftvol += ch->leftvol; 782 combine->rightvol += ch->rightvol; 783 ch->leftvol = ch->rightvol = 0; 784 } 785 continue; 786 } 787 } 788 789 790 } 791 792// 793// debugging output 794// 795 if (snd_show.value) 796 { 797 total = 0; 798 ch = channels; 799 for (i=0 ; i<total_channels; i++, ch++) 800 if (ch->sfx && (ch->leftvol || ch->rightvol) ) 801 { 802 //Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name); 803 total++; 804 } 805 806 Con_Printf ("----(%i)----\n", total); 807 } 808 809// mix some sound 810 S_Update_(); 811} 812 813void GetSoundtime(void) 814{ 815 int samplepos; 816 static int buffers; 817 static int oldsamplepos; 818 int fullsamples; 819 820 fullsamples = shm->samples / shm->channels; 821 822// it is possible to miscount buffers if it has wrapped twice between 823// calls to S_Update. Oh well. 824 samplepos = SNDDMA_GetDMAPos(); 825 826 if (samplepos < oldsamplepos) 827 { 828 buffers++; // buffer wrapped 829 830 if (paintedtime > 0x40000000) 831 { // time to chop things off to avoid 32 bit limits 832 buffers = 0; 833 paintedtime = fullsamples; 834 S_StopAllSounds (true); 835 } 836 } 837 oldsamplepos = samplepos; 838 839 soundtime = buffers*fullsamples + samplepos/shm->channels; 840} 841 842void S_ExtraUpdate (void) 843{ 844 845#ifdef _WIN32 846 IN_Accumulate (); 847#endif 848 849 if (snd_noextraupdate.value) 850 return; // don't pollute timings 851 S_Update_(); 852} 853 854 855 856void S_Update_(void) 857{ 858 unsigned endtime; 859 int samps; 860 861 if (!sound_started || (snd_blocked > 0)) 862 return; 863 864// Updates DMA time 865 GetSoundtime(); 866 867// check to make sure that we haven't overshot 868 if (paintedtime < soundtime) 869 { 870 //Con_Printf ("S_Update_ : overflow\n"); 871 paintedtime = soundtime; 872 } 873 874// mix ahead of current position 875 endtime = soundtime + _snd_mixahead.value * shm->speed; 876 samps = shm->samples >> (shm->channels-1); 877 if ((int)(endtime - soundtime) > samps) 878 endtime = soundtime + samps; 879 880#ifdef _WIN32 881// if the buffer was lost or stopped, restore it and/or restart it 882 { 883 DWORD dwStatus; 884 885 if (pDSBuf) 886 { 887 if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DD_OK) 888 Con_Printf ("Couldn't get sound buffer status\n"); 889 890 if (dwStatus & DSBSTATUS_BUFFERLOST) 891 pDSBuf->lpVtbl->Restore (pDSBuf); 892 893 if (!(dwStatus & DSBSTATUS_PLAYING)) 894 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); 895 } 896 } 897#endif 898 899 S_PaintChannels (endtime); 900 901 SNDDMA_Submit (); 902} 903 904/* 905=============================================================================== 906 907console functions 908 909=============================================================================== 910*/ 911 912void S_Play(void) 913{ 914 static int hash=345; 915 int i; 916 char name[256]; 917 sfx_t *sfx; 918 919 i = 1; 920 while (i<Cmd_Argc()) 921 { 922 if (!Q_strrchr(Cmd_Argv(i), '.')) 923 { 924 Q_strcpy(name, Cmd_Argv(i)); 925 Q_strcat(name, ".wav"); 926 } 927 else 928 Q_strcpy(name, Cmd_Argv(i)); 929 sfx = S_PrecacheSound(name); 930 S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 1.0); 931 i++; 932 } 933} 934 935void S_PlayVol(void) 936{ 937 static int hash=543; 938 int i; 939 float vol; 940 char name[256]; 941 sfx_t *sfx; 942 943 i = 1; 944 while (i<Cmd_Argc()) 945 { 946 if (!Q_strrchr(Cmd_Argv(i), '.')) 947 { 948 Q_strcpy(name, Cmd_Argv(i)); 949 Q_strcat(name, ".wav"); 950 } 951 else 952 Q_strcpy(name, Cmd_Argv(i)); 953 sfx = S_PrecacheSound(name); 954 vol = Q_atof(Cmd_Argv(i+1)); 955 S_StartSound(hash++, 0, sfx, listener_origin, vol, 1.0); 956 i+=2; 957 } 958} 959 960void S_SoundList(void) 961{ 962 int i; 963 sfx_t *sfx; 964 sfxcache_t *sc; 965 int size, total; 966 967 total = 0; 968 for (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++) 969 { 970 sc = Cache_Check (&sfx->cache); 971 if (!sc) 972 continue; 973 size = sc->length*sc->width*(sc->stereo+1); 974 total += size; 975 if (sc->loopstart >= 0) 976 Con_Printf ("L"); 977 else 978 Con_Printf (" "); 979 Con_Printf("(%2db) %6i : %s\n",sc->width*8, size, sfx->name); 980 } 981 Con_Printf ("Total resident: %i\n", total); 982} 983 984 985void S_LocalSound (char *sound) 986{ 987 sfx_t *sfx; 988 989 if (nosound.value) 990 return; 991 if (!sound_started) 992 return; 993 994 sfx = S_PrecacheSound (sound); 995 if (!sfx) 996 { 997 Con_Printf ("S_LocalSound: can't cache %s\n", sound); 998 return; 999 } 1000 S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1); 1001} 1002 1003 1004void S_ClearPrecache (void) 1005{ 1006} 1007 1008 1009void S_BeginPrecaching (void) 1010{ 1011} 1012 1013 1014void S_EndPrecaching (void) 1015{ 1016} 1017 1018