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