]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/doom/s_sound.c
games/doom: fix mips
[plan9front.git] / sys / src / games / doom / s_sound.c
1 // Emacs style mode select   -*- C++ -*- 
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:  none
20 //
21 //-----------------------------------------------------------------------------
22
23
24 static const char
25 rcsid[] = "$Id: s_sound.c,v 1.6 1997/02/03 22:45:12 b1 Exp $";
26
27 #include "i_system.h"
28 #include "i_sound.h"
29 #include "sounds.h"
30 #include "s_sound.h"
31
32 #include "z_zone.h"
33 #include "m_random.h"
34 #include "w_wad.h"
35
36 #include "doomdef.h"
37 #include "p_local.h"
38
39 #include "doomstat.h"
40
41
42 // Purpose?
43 const char snd_prefixen[]
44 = { 'P', 'P', 'A', 'S', 'S', 'S', 'M', 'M', 'M', 'S', 'S', 'S' };
45
46 #define S_MAX_VOLUME            127
47
48 // when to clip out sounds
49 // Does not fit the large outdoor areas.
50 #define S_CLIPPING_DIST         (1200*0x10000)
51
52 // Distance tp origin when sounds should be maxed out.
53 // This should relate to movement clipping resolution
54 // (see BLOCKMAP handling).
55 // Originally: (200*0x10000).
56 #define S_CLOSE_DIST            (160*0x10000)
57
58
59 #define S_ATTENUATOR            ((S_CLIPPING_DIST-S_CLOSE_DIST)>>FRACBITS)
60
61 // Adjustable by menu.
62 #define NORM_VOLUME             snd_MaxVolume
63
64 #define NORM_PITCH              128
65 #define NORM_PRIORITY           64
66 #define NORM_SEP                128
67
68 #define S_PITCH_PERTURB         1
69 #define S_STEREO_SWING          (96*0x10000)
70
71 // percent attenuation from front to back
72 #define S_IFRACVOL              30
73
74 #define NA                      0
75 #define S_NUMCHANNELS           2
76
77
78 // Current music/sfx card - index useless
79 //  w/o a reference LUT in a sound module.
80 extern int snd_MusicDevice;
81 extern int snd_SfxDevice;
82 // Config file? Same disclaimer as above.
83 extern int snd_DesiredMusicDevice;
84 extern int snd_DesiredSfxDevice;
85
86
87
88 typedef struct
89 {
90     // sound information (if null, channel avail.)
91     sfxinfo_t*  sfxinfo;
92
93     // origin of sound
94     void*       origin;
95
96     // handle of the sound being played
97     int         handle;
98     
99 } channel_t;
100
101
102 // the set of channels available
103 static channel_t*       channels;
104
105 // These are not used, but should be (menu).
106 // Maximum volume of a sound effect.
107 // Internal default is max out of 0-15.
108 int             snd_SfxVolume = 15;
109
110 // Maximum volume of music. Useless so far.
111 int             snd_MusicVolume = 15; 
112
113
114
115 // whether songs are mus_paused
116 static boolean          mus_paused;     
117
118 // music currently being played
119 static musicinfo_t*     mus_playing=0;
120
121 // following is set
122 //  by the defaults code in M_misc:
123 // number of channels available
124 int                     numChannels;    
125
126 //
127 // Internals.
128 //
129 int
130 S_getChannel
131 ( void*         origin,
132   sfxinfo_t*    sfxinfo );
133
134
135 int
136 S_AdjustSoundParams
137 ( mobj_t*       listener,
138   mobj_t*       source,
139   int*          vol,
140   int*          sep,
141   int*          pitch );
142
143 void S_StopChannel(int cnum);
144
145
146
147 //
148 // Initializes sound stuff, including volume
149 // Sets channels, SFX and music volume,
150 //  allocates channel buffer, sets S_sfx lookup.
151 //
152 void S_Init
153 ( int           sfxVolume,
154   int           musicVolume )
155 {  
156   int           i;
157
158   fprintf( stderr, "S_Init: default sfx volume %d\n", sfxVolume);
159
160   // Whatever these did with DMX, these are rather dummies now.
161   I_SetChannels();
162   
163   S_SetSfxVolume(sfxVolume);
164   // No music with Linux - another dummy.
165   S_SetMusicVolume(musicVolume);
166
167   // Allocating the internal channels for mixing
168   // (the maximum numer of sounds rendered
169   // simultaneously) within zone memory.
170   channels =
171     (channel_t *) Z_Malloc(numChannels*sizeof(channel_t), PU_STATIC, 0);
172   
173   // Free all channels for use
174   for (i=0 ; i<numChannels ; i++)
175     channels[i].sfxinfo = 0;
176   
177   // no sounds are playing, and they are not mus_paused
178   mus_paused = 0;
179 }
180
181
182
183
184 //
185 // Per level startup code.
186 // Kills playing sounds at start of level,
187 //  determines music if any, changes music.
188 //
189 void S_Start(void)
190 {
191   int cnum;
192   int mnum;
193
194   // kill all playing sounds at start of level
195   //  (trust me - a good idea)
196   for (cnum=0 ; cnum<numChannels ; cnum++)
197     if (channels[cnum].sfxinfo)
198       S_StopChannel(cnum);
199   
200   // start new music for the level
201   mus_paused = 0;
202   
203   if (gamemode == commercial)
204     mnum = mus_runnin + gamemap - 1;
205   else
206   {
207     int spmus[]=
208     {
209       // Song - Who? - Where?
210       
211       mus_e3m4, // American     e4m1
212       mus_e3m2, // Romero       e4m2
213       mus_e3m3, // Shawn        e4m3
214       mus_e1m5, // American     e4m4
215       mus_e2m7, // Tim  e4m5
216       mus_e2m4, // Romero       e4m6
217       mus_e2m6, // J.Anderson   e4m7 CHIRON.WAD
218       mus_e2m5, // Shawn        e4m8
219       mus_e1m9  // Tim          e4m9
220     };
221     
222     if (gameepisode < 4)
223       mnum = mus_e1m1 + (gameepisode-1)*9 + gamemap-1;
224     else
225       mnum = spmus[gamemap-1];
226     }   
227   
228   // HACK FOR COMMERCIAL
229   //  if (commercial && mnum > mus_e3m9)        
230   //      mnum -= mus_e3m9;
231   
232   S_ChangeMusic(mnum, true);
233 }       
234
235
236
237
238
239 void
240 S_StartSoundAtVolume
241 ( void*         origin_p,
242   int           sfx_id,
243   int           volume )
244 {
245
246   int           rc;
247   int           sep;
248   int           pitch;
249   int           priority;
250   sfxinfo_t*    sfx;
251   int           cnum;
252   
253   mobj_t*       origin = (mobj_t *) origin_p;
254   
255   
256   // check for bogus sound #
257   if (sfx_id < 1 || sfx_id > NUMSFX)
258     I_Error("Bad sfx #: %d", sfx_id);
259   
260   sfx = &S_sfx[sfx_id];
261   
262   // Initialize sound parameters
263   if (sfx->link)
264   {
265     pitch = sfx->pitch;
266     priority = sfx->priority;
267     volume += sfx->volume;
268     
269     if (volume < 1)
270       return;
271     
272     if (volume > snd_SfxVolume)
273       volume = snd_SfxVolume;
274   }     
275   else
276   {
277     pitch = NORM_PITCH;
278     priority = NORM_PRIORITY;
279   }
280
281
282   // Check to see if it is audible,
283   //  and if not, modify the params
284   if (origin && origin != players[consoleplayer].mo)
285   {
286     rc = S_AdjustSoundParams(players[consoleplayer].mo,
287                              origin,
288                              &volume,
289                              &sep,
290                              &pitch);
291         
292     if ( origin->x == players[consoleplayer].mo->x
293          && origin->y == players[consoleplayer].mo->y)
294     {   
295       sep = NORM_SEP;
296     }
297     
298     if (!rc)
299       return;
300   }     
301   else
302   {
303     sep = NORM_SEP;
304   }
305   
306   // hacks to vary the sfx pitches
307   if (sfx_id >= sfx_sawup
308       && sfx_id <= sfx_sawhit)
309   {     
310     pitch += 8 - (M_Random()&15);
311     
312     if (pitch<0)
313       pitch = 0;
314     else if (pitch>255)
315       pitch = 255;
316   }
317   else if (sfx_id != sfx_itemup
318            && sfx_id != sfx_tink)
319   {
320     pitch += 16 - (M_Random()&31);
321     
322     if (pitch<0)
323       pitch = 0;
324     else if (pitch>255)
325       pitch = 255;
326   }
327
328   // kill old sound
329   S_StopSound(origin);
330
331   // try to find a channel
332   cnum = S_getChannel(origin, sfx);
333   
334   if (cnum<0)
335     return;
336
337   // Assigns the handle to one of the channels in the
338   //  mix/output buffer.
339   channels[cnum].handle = I_StartSound(sfx_id,
340                                        volume,
341                                        sep,
342                                        pitch,
343                                        priority);
344 }       
345
346 void
347 S_StartSound
348 ( void*         origin,
349   int           sfx_id )
350 {
351 #ifdef SAWDEBUG
352     // if (sfx_id == sfx_sawful)
353     // sfx_id = sfx_itemup;
354 #endif
355   
356     S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume);
357
358
359     // UNUSED. We had problems, had we not?
360 #ifdef SAWDEBUG
361 {
362     int i;
363     int n;
364         
365     static mobj_t*      last_saw_origins[10] = {1,1,1,1,1,1,1,1,1,1};
366     static int          first_saw=0;
367     static int          next_saw=0;
368         
369     if (sfx_id == sfx_sawidl
370         || sfx_id == sfx_sawful
371         || sfx_id == sfx_sawhit)
372     {
373         for (i=first_saw;i!=next_saw;i=(i+1)%10)
374             if (last_saw_origins[i] != origin)
375                 fprintf(stderr, "old origin 0x%lx != "
376                         "origin 0x%lx for sfx %d\n",
377                         last_saw_origins[i],
378                         origin,
379                         sfx_id);
380             
381         last_saw_origins[next_saw] = origin;
382         next_saw = (next_saw + 1) % 10;
383         if (next_saw == first_saw)
384             first_saw = (first_saw + 1) % 10;
385             
386         for (n=i=0; i<numChannels ; i++)
387         {
388             if (channels[i].sfxinfo == &S_sfx[sfx_sawidl]
389                 || channels[i].sfxinfo == &S_sfx[sfx_sawful]
390                 || channels[i].sfxinfo == &S_sfx[sfx_sawhit]) n++;
391         }
392             
393         if (n>1)
394         {
395             for (i=0; i<numChannels ; i++)
396             {
397                 if (channels[i].sfxinfo == &S_sfx[sfx_sawidl]
398                     || channels[i].sfxinfo == &S_sfx[sfx_sawful]
399                     || channels[i].sfxinfo == &S_sfx[sfx_sawhit])
400                 {
401                     fprintf(stderr,
402                             "chn: sfxinfo=0x%lx, origin=0x%lx, "
403                             "handle=%d\n",
404                             channels[i].sfxinfo,
405                             channels[i].origin,
406                             channels[i].handle);
407                 }
408             }
409             fprintf(stderr, "\n");
410         }
411     }
412 }
413 #endif
414  
415 }
416
417
418
419
420 void S_StopSound(void *origin)
421 {
422
423     int cnum;
424
425     for (cnum=0 ; cnum<numChannels ; cnum++)
426     {
427         if (channels[cnum].sfxinfo && channels[cnum].origin == origin)
428         {
429             S_StopChannel(cnum);
430             break;
431         }
432     }
433 }
434
435
436
437
438
439
440
441
442
443 //
444 // Stop and resume music, during game PAUSE.
445 //
446 void S_PauseSound(void)
447 {
448     if (mus_playing && !mus_paused)
449     {
450         I_PauseSong(mus_playing->handle);
451         mus_paused = true;
452     }
453 }
454
455 void S_ResumeSound(void)
456 {
457     if (mus_playing && mus_paused)
458     {
459         I_ResumeSong(mus_playing->handle);
460         mus_paused = false;
461     }
462 }
463
464
465 //
466 // Updates music & sounds
467 //
468 void S_UpdateSounds(void* listener_p)
469 {
470     int         audible;
471     int         cnum;
472     int         volume;
473     int         sep;
474     int         pitch;
475     sfxinfo_t*  sfx;
476     channel_t*  c;
477     
478     mobj_t*     listener = (mobj_t*)listener_p;
479     
480     for (cnum=0 ; cnum<numChannels ; cnum++)
481     {
482         c = &channels[cnum];
483         sfx = c->sfxinfo;
484
485         if (c->sfxinfo)
486         {
487             if (I_SoundIsPlaying(c->handle))
488             {
489                 // initialize parameters
490                 volume = snd_SfxVolume;
491                 pitch = NORM_PITCH;
492                 sep = NORM_SEP;
493
494                 if (sfx->link)
495                 {
496                     pitch = sfx->pitch;
497                     volume += sfx->volume;
498                     if (volume < 1)
499                     {
500                         S_StopChannel(cnum);
501                         continue;
502                     }
503                     else if (volume > snd_SfxVolume)
504                     {
505                         volume = snd_SfxVolume;
506                     }
507                 }
508
509                 // check non-local sounds for distance clipping
510                 //  or modify their params
511                 if (c->origin && listener_p != c->origin)
512                 {
513                     audible = S_AdjustSoundParams(listener,
514                                                   c->origin,
515                                                   &volume,
516                                                   &sep,
517                                                   &pitch);
518                     
519                     if (!audible)
520                     {
521                         S_StopChannel(cnum);
522                     }
523                     else
524                         I_UpdateSoundParams(c->handle, volume, sep, pitch);
525                 }
526             }
527             else
528             {
529                 // if channel is allocated but sound has stopped,
530                 //  free it
531                 S_StopChannel(cnum);
532             }
533         }
534     }
535     // kill music if it is a single-play && finished
536     // if (     mus_playing
537     //      && !I_QrySongPlaying(mus_playing->handle)
538     //      && !mus_paused )
539     // S_StopMusic();
540 }
541
542
543 void S_SetMusicVolume(int volume)
544 {
545     if (volume < 0 || volume > 127)
546     {
547         I_Error("Attempt to set music volume at %d",
548                 volume);
549     }    
550
551     I_SetMusicVolume(127);
552     I_SetMusicVolume(volume);
553     snd_MusicVolume = volume;
554 }
555
556
557
558 void S_SetSfxVolume(int volume)
559 {
560
561     if (volume < 0 || volume > 127)
562         I_Error("Attempt to set sfx volume at %d", volume);
563
564     snd_SfxVolume = volume;
565
566 }
567
568 //
569 // Starts some music with the music id found in sounds.h.
570 //
571 void S_StartMusic(int m_id)
572 {
573     S_ChangeMusic(m_id, false);
574 }
575
576 void
577 S_ChangeMusic
578 ( int                   musicnum,
579   int                   looping )
580 {
581     musicinfo_t*        music;
582     char                namebuf[9];
583
584     if ( (musicnum <= mus_None)
585          || (musicnum >= NUMMUSIC) )
586     {
587         music = nil;
588         I_Error("Bad music number %d", musicnum);
589     }
590     else
591         music = &S_music[musicnum];
592
593     if (mus_playing == music)
594         return;
595
596     // shutdown old music
597     S_StopMusic();
598
599     // get lumpnum if neccessary
600     if (!music->lumpnum)
601     {
602         sprintf(namebuf, "d_%s", music->name);
603         music->lumpnum = W_GetNumForName(namebuf);
604     }
605
606     // load & register it
607     music->data = (void *) W_CacheLumpNum(music->lumpnum, PU_MUSIC);
608     music->handle = I_RegisterSong(music->data);
609
610     // play it
611     I_PlaySong(music->handle, looping);
612
613     mus_playing = music;
614 }
615
616
617 void S_StopMusic(void)
618 {
619     if (mus_playing)
620     {
621         if (mus_paused)
622             I_ResumeSong(mus_playing->handle);
623
624         I_StopSong(mus_playing->handle);
625         I_UnRegisterSong(mus_playing->handle);
626         Z_ChangeTag(mus_playing->data, PU_CACHE);
627         
628         mus_playing->data = 0;
629         mus_playing = 0;
630     }
631 }
632
633
634
635
636 void S_StopChannel(int cnum)
637 {
638
639     int         i;
640     channel_t*  c = &channels[cnum];
641
642     if (c->sfxinfo)
643     {
644         // stop the sound playing
645         if (I_SoundIsPlaying(c->handle))
646         {
647 #ifdef SAWDEBUG
648             if (c->sfxinfo == &S_sfx[sfx_sawful])
649                 fprintf(stderr, "stopped\n");
650 #endif
651             I_StopSound(c->handle);
652         }
653
654         // check to see
655         //  if other channels are playing the sound
656         for (i=0 ; i<numChannels ; i++)
657         {
658             if (cnum != i
659                 && c->sfxinfo == channels[i].sfxinfo)
660             {
661                 break;
662             }
663         }
664
665         c->sfxinfo = 0;
666     }
667 }
668
669
670
671 //
672 // Changes volume, stereo-separation, and pitch variables
673 //  from the norm of a sound effect to be played.
674 // If the sound is not audible, returns a 0.
675 // Otherwise, modifies parameters and returns 1.
676 //
677 int
678 S_AdjustSoundParams
679 ( mobj_t*       listener,
680   mobj_t*       source,
681   int*          vol,
682   int*          sep,
683   int*          /*pitch*/ )
684 {
685     fixed_t     approx_dist;
686     fixed_t     adx;
687     fixed_t     ady;
688     angle_t     angle;
689
690     // calculate the distance to sound origin
691     //  and clip it if necessary
692     adx = abs(listener->x - source->x);
693     ady = abs(listener->y - source->y);
694
695     // From _GG1_ p.428. Appox. eucledian distance fast.
696     approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);
697     
698     if (gamemap != 8
699         && approx_dist > S_CLIPPING_DIST)
700     {
701         return 0;
702     }
703     
704     // angle of source to listener
705     angle = R_PointToAngle2(listener->x,
706                             listener->y,
707                             source->x,
708                             source->y);
709
710     if (angle > listener->angle)
711         angle = angle - listener->angle;
712     else
713         angle = angle + (0xffffffff - listener->angle);
714
715     angle >>= ANGLETOFINESHIFT;
716
717     // stereo separation
718     *sep = 128 - (FixedMul(S_STEREO_SWING,finesine[angle])>>FRACBITS);
719
720     // volume calculation
721     if (approx_dist < S_CLOSE_DIST)
722     {
723         *vol = snd_SfxVolume;
724     }
725     else if (gamemap == 8)
726     {
727         if (approx_dist > S_CLIPPING_DIST)
728             approx_dist = S_CLIPPING_DIST;
729
730         *vol = 15+ ((snd_SfxVolume-15)
731                     *((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
732             / S_ATTENUATOR;
733     }
734     else
735     {
736         // distance effect
737         *vol = (snd_SfxVolume
738                 * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
739             / S_ATTENUATOR; 
740     }
741     
742     return (*vol > 0);
743 }
744
745
746
747
748 //
749 // S_getChannel :
750 //   If none available, return -1.  Otherwise channel #.
751 //
752 int
753 S_getChannel
754 ( void*         origin,
755   sfxinfo_t*    sfxinfo )
756 {
757     // channel number to use
758     int         cnum;
759     
760     channel_t*  c;
761
762     // Find an open channel
763     for (cnum=0 ; cnum<numChannels ; cnum++)
764     {
765         if (!channels[cnum].sfxinfo)
766             break;
767         else if (origin &&  channels[cnum].origin ==  origin)
768         {
769             S_StopChannel(cnum);
770             break;
771         }
772     }
773
774     // None available
775     if (cnum == numChannels)
776     {
777         // Look for lower priority
778         for (cnum=0 ; cnum<numChannels ; cnum++)
779             if (channels[cnum].sfxinfo->priority >= sfxinfo->priority) break;
780
781         if (cnum == numChannels)
782         {
783             // FUCK!  No lower priority.  Sorry, Charlie.    
784             return -1;
785         }
786         else
787         {
788             // Otherwise, kick out lower priority.
789             S_StopChannel(cnum);
790         }
791     }
792
793     c = &channels[cnum];
794
795     // channel is decided to be cnum.
796     c->sfxinfo = sfxinfo;
797     c->origin = origin;
798
799     return cnum;
800 }
801
802
803
804