]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/doom/s_sound.c
ac97: fix buffering code, games/doom: enable sound
[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 static int              nextcleanup;
127
128
129
130 //
131 // Internals.
132 //
133 int
134 S_getChannel
135 ( void*         origin,
136   sfxinfo_t*    sfxinfo );
137
138
139 int
140 S_AdjustSoundParams
141 ( mobj_t*       listener,
142   mobj_t*       source,
143   int*          vol,
144   int*          sep,
145   int*          pitch );
146
147 void S_StopChannel(int cnum);
148
149
150
151 //
152 // Initializes sound stuff, including volume
153 // Sets channels, SFX and music volume,
154 //  allocates channel buffer, sets S_sfx lookup.
155 //
156 void S_Init
157 ( int           sfxVolume,
158   int           musicVolume )
159 {  
160   int           i;
161
162   fprintf( stderr, "S_Init: default sfx volume %d\n", sfxVolume);
163
164   // Whatever these did with DMX, these are rather dummies now.
165   I_SetChannels();
166   
167   S_SetSfxVolume(sfxVolume);
168   // No music with Linux - another dummy.
169   S_SetMusicVolume(musicVolume);
170
171   // Allocating the internal channels for mixing
172   // (the maximum numer of sounds rendered
173   // simultaneously) within zone memory.
174   channels =
175     (channel_t *) Z_Malloc(numChannels*sizeof(channel_t), PU_STATIC, 0);
176   
177   // Free all channels for use
178   for (i=0 ; i<numChannels ; i++)
179     channels[i].sfxinfo = 0;
180   
181   // no sounds are playing, and they are not mus_paused
182   mus_paused = 0;
183
184   // Note that sounds have not been cached (yet).
185   for (i=1 ; i<NUMSFX ; i++)
186     S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;
187 }
188
189
190
191
192 //
193 // Per level startup code.
194 // Kills playing sounds at start of level,
195 //  determines music if any, changes music.
196 //
197 void S_Start(void)
198 {
199   int cnum;
200   int mnum;
201
202   // kill all playing sounds at start of level
203   //  (trust me - a good idea)
204   for (cnum=0 ; cnum<numChannels ; cnum++)
205     if (channels[cnum].sfxinfo)
206       S_StopChannel(cnum);
207   
208   // start new music for the level
209   mus_paused = 0;
210   
211   if (gamemode == commercial)
212     mnum = mus_runnin + gamemap - 1;
213   else
214   {
215     int spmus[]=
216     {
217       // Song - Who? - Where?
218       
219       mus_e3m4, // American     e4m1
220       mus_e3m2, // Romero       e4m2
221       mus_e3m3, // Shawn        e4m3
222       mus_e1m5, // American     e4m4
223       mus_e2m7, // Tim  e4m5
224       mus_e2m4, // Romero       e4m6
225       mus_e2m6, // J.Anderson   e4m7 CHIRON.WAD
226       mus_e2m5, // Shawn        e4m8
227       mus_e1m9  // Tim          e4m9
228     };
229     
230     if (gameepisode < 4)
231       mnum = mus_e1m1 + (gameepisode-1)*9 + gamemap-1;
232     else
233       mnum = spmus[gamemap-1];
234     }   
235   
236   // HACK FOR COMMERCIAL
237   //  if (commercial && mnum > mus_e3m9)        
238   //      mnum -= mus_e3m9;
239   
240   S_ChangeMusic(mnum, true);
241   
242   nextcleanup = 15;
243 }       
244
245
246
247
248
249 void
250 S_StartSoundAtVolume
251 ( void*         origin_p,
252   int           sfx_id,
253   int           volume )
254 {
255
256   int           rc;
257   int           sep;
258   int           pitch;
259   int           priority;
260   sfxinfo_t*    sfx;
261   int           cnum;
262   
263   mobj_t*       origin = (mobj_t *) origin_p;
264   
265   
266   // Debug.
267   /*fprintf( stderr,
268            "S_StartSoundAtVolume: playing sound %d (%s)\n",
269            sfx_id, S_sfx[sfx_id].name );*/
270   
271   // check for bogus sound #
272   if (sfx_id < 1 || sfx_id > NUMSFX)
273     I_Error("Bad sfx #: %d", sfx_id);
274   
275   sfx = &S_sfx[sfx_id];
276   
277   // Initialize sound parameters
278   if (sfx->link)
279   {
280     pitch = sfx->pitch;
281     priority = sfx->priority;
282     volume += sfx->volume;
283     
284     if (volume < 1)
285       return;
286     
287     if (volume > snd_SfxVolume)
288       volume = snd_SfxVolume;
289   }     
290   else
291   {
292     pitch = NORM_PITCH;
293     priority = NORM_PRIORITY;
294   }
295
296
297   // Check to see if it is audible,
298   //  and if not, modify the params
299   if (origin && origin != players[consoleplayer].mo)
300   {
301     rc = S_AdjustSoundParams(players[consoleplayer].mo,
302                              origin,
303                              &volume,
304                              &sep,
305                              &pitch);
306         
307     if ( origin->x == players[consoleplayer].mo->x
308          && origin->y == players[consoleplayer].mo->y)
309     {   
310       sep       = NORM_SEP;
311     }
312     
313     if (!rc)
314       return;
315   }     
316   else
317   {
318     sep = NORM_SEP;
319   }
320   
321   // hacks to vary the sfx pitches
322   if (sfx_id >= sfx_sawup
323       && sfx_id <= sfx_sawhit)
324   {     
325     pitch += 8 - (M_Random()&15);
326     
327     if (pitch<0)
328       pitch = 0;
329     else if (pitch>255)
330       pitch = 255;
331   }
332   else if (sfx_id != sfx_itemup
333            && sfx_id != sfx_tink)
334   {
335     pitch += 16 - (M_Random()&31);
336     
337     if (pitch<0)
338       pitch = 0;
339     else if (pitch>255)
340       pitch = 255;
341   }
342
343   // kill old sound
344   S_StopSound(origin);
345
346   // try to find a channel
347   cnum = S_getChannel(origin, sfx);
348   
349   if (cnum<0)
350     return;
351
352   //
353   // This is supposed to handle the loading/caching.
354   // For some odd reason, the caching is done nearly
355   //  each time the sound is needed?
356   //
357   
358   // get lumpnum if necessary
359   if (sfx->lumpnum < 0)
360     sfx->lumpnum = I_GetSfxLumpNum(sfx);
361
362 #ifndef SNDSRV
363   // cache data if necessary
364   if (!sfx->data)
365   {
366 /* PORTME 9DOOM uncomment this back in later
367     fprintf( stderr,
368              "S_StartSoundAtVolume: 16bit and not pre-cached - wtf?\n");
369 */
370
371     // DOS remains, 8bit handling
372     //sfx->data = (void *) W_CacheLumpNum(sfx->lumpnum, PU_MUSIC);
373     // fprintf( stderr,
374     //       "S_StartSoundAtVolume: loading %d (lump %d) : 0x%x\n",
375     //       sfx_id, sfx->lumpnum, (int)sfx->data );
376     
377   }
378 #endif
379   
380   // increase the usefulness
381   if (sfx->usefulness++ < 0)
382     sfx->usefulness = 1;
383   
384   // Assigns the handle to one of the channels in the
385   //  mix/output buffer.
386   channels[cnum].handle = I_StartSound(sfx_id,
387                                        /*sfx->data,*/
388                                        volume,
389                                        sep,
390                                        pitch,
391                                        priority);
392 }       
393
394 void
395 S_StartSound
396 ( void*         origin,
397   int           sfx_id )
398 {
399 #ifdef SAWDEBUG
400     // if (sfx_id == sfx_sawful)
401     // sfx_id = sfx_itemup;
402 #endif
403   
404     S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume);
405
406
407     // UNUSED. We had problems, had we not?
408 #ifdef SAWDEBUG
409 {
410     int i;
411     int n;
412         
413     static mobj_t*      last_saw_origins[10] = {1,1,1,1,1,1,1,1,1,1};
414     static int          first_saw=0;
415     static int          next_saw=0;
416         
417     if (sfx_id == sfx_sawidl
418         || sfx_id == sfx_sawful
419         || sfx_id == sfx_sawhit)
420     {
421         for (i=first_saw;i!=next_saw;i=(i+1)%10)
422             if (last_saw_origins[i] != origin)
423                 fprintf(stderr, "old origin 0x%lx != "
424                         "origin 0x%lx for sfx %d\n",
425                         last_saw_origins[i],
426                         origin,
427                         sfx_id);
428             
429         last_saw_origins[next_saw] = origin;
430         next_saw = (next_saw + 1) % 10;
431         if (next_saw == first_saw)
432             first_saw = (first_saw + 1) % 10;
433             
434         for (n=i=0; i<numChannels ; i++)
435         {
436             if (channels[i].sfxinfo == &S_sfx[sfx_sawidl]
437                 || channels[i].sfxinfo == &S_sfx[sfx_sawful]
438                 || channels[i].sfxinfo == &S_sfx[sfx_sawhit]) n++;
439         }
440             
441         if (n>1)
442         {
443             for (i=0; i<numChannels ; i++)
444             {
445                 if (channels[i].sfxinfo == &S_sfx[sfx_sawidl]
446                     || channels[i].sfxinfo == &S_sfx[sfx_sawful]
447                     || channels[i].sfxinfo == &S_sfx[sfx_sawhit])
448                 {
449                     fprintf(stderr,
450                             "chn: sfxinfo=0x%lx, origin=0x%lx, "
451                             "handle=%d\n",
452                             channels[i].sfxinfo,
453                             channels[i].origin,
454                             channels[i].handle);
455                 }
456             }
457             fprintf(stderr, "\n");
458         }
459     }
460 }
461 #endif
462  
463 }
464
465
466
467
468 void S_StopSound(void *origin)
469 {
470
471     int cnum;
472
473     for (cnum=0 ; cnum<numChannels ; cnum++)
474     {
475         if (channels[cnum].sfxinfo && channels[cnum].origin == origin)
476         {
477             S_StopChannel(cnum);
478             break;
479         }
480     }
481 }
482
483
484
485
486
487
488
489
490
491 //
492 // Stop and resume music, during game PAUSE.
493 //
494 void S_PauseSound(void)
495 {
496     if (mus_playing && !mus_paused)
497     {
498         I_PauseSong(mus_playing->handle);
499         mus_paused = true;
500     }
501 }
502
503 void S_ResumeSound(void)
504 {
505     if (mus_playing && mus_paused)
506     {
507         I_ResumeSong(mus_playing->handle);
508         mus_paused = false;
509     }
510 }
511
512
513 //
514 // Updates music & sounds
515 //
516 void S_UpdateSounds(void* listener_p)
517 {
518     int         audible;
519     int         cnum;
520     int         volume;
521     int         sep;
522     int         pitch;
523     sfxinfo_t*  sfx;
524     channel_t*  c;
525     
526     mobj_t*     listener = (mobj_t*)listener_p;
527
528
529     
530     // Clean up unused data.
531     // This is currently not done for 16bit (sounds cached static).
532     // DOS 8bit remains. 
533     /*if (gametic > nextcleanup)
534     {
535         for (i=1 ; i<NUMSFX ; i++)
536         {
537             if (S_sfx[i].usefulness < 1
538                 && S_sfx[i].usefulness > -1)
539             {
540                 if (--S_sfx[i].usefulness == -1)
541                 {
542                     Z_ChangeTag(S_sfx[i].data, PU_CACHE);
543                     S_sfx[i].data = 0;
544                 }
545             }
546         }
547         nextcleanup = gametic + 15;
548     }*/
549     
550     for (cnum=0 ; cnum<numChannels ; cnum++)
551     {
552         c = &channels[cnum];
553         sfx = c->sfxinfo;
554
555         if (c->sfxinfo)
556         {
557             if (I_SoundIsPlaying(c->handle))
558             {
559                 // initialize parameters
560                 volume = snd_SfxVolume;
561                 pitch = NORM_PITCH;
562                 sep = NORM_SEP;
563
564                 if (sfx->link)
565                 {
566                     pitch = sfx->pitch;
567                     volume += sfx->volume;
568                     if (volume < 1)
569                     {
570                         S_StopChannel(cnum);
571                         continue;
572                     }
573                     else if (volume > snd_SfxVolume)
574                     {
575                         volume = snd_SfxVolume;
576                     }
577                 }
578
579                 // check non-local sounds for distance clipping
580                 //  or modify their params
581                 if (c->origin && listener_p != c->origin)
582                 {
583                     audible = S_AdjustSoundParams(listener,
584                                                   c->origin,
585                                                   &volume,
586                                                   &sep,
587                                                   &pitch);
588                     
589                     if (!audible)
590                     {
591                         S_StopChannel(cnum);
592                     }
593                     else
594                         I_UpdateSoundParams(c->handle, volume, sep, pitch);
595                 }
596             }
597             else
598             {
599                 // if channel is allocated but sound has stopped,
600                 //  free it
601                 S_StopChannel(cnum);
602             }
603         }
604     }
605     // kill music if it is a single-play && finished
606     // if (     mus_playing
607     //      && !I_QrySongPlaying(mus_playing->handle)
608     //      && !mus_paused )
609     // S_StopMusic();
610 }
611
612
613 void S_SetMusicVolume(int volume)
614 {
615     if (volume < 0 || volume > 127)
616     {
617         I_Error("Attempt to set music volume at %d",
618                 volume);
619     }    
620
621     I_SetMusicVolume(127);
622     I_SetMusicVolume(volume);
623     snd_MusicVolume = volume;
624 }
625
626
627
628 void S_SetSfxVolume(int volume)
629 {
630
631     if (volume < 0 || volume > 127)
632         I_Error("Attempt to set sfx volume at %d", volume);
633
634     snd_SfxVolume = volume;
635
636 }
637
638 //
639 // Starts some music with the music id found in sounds.h.
640 //
641 void S_StartMusic(int m_id)
642 {
643     S_ChangeMusic(m_id, false);
644 }
645
646 void
647 S_ChangeMusic
648 ( int                   musicnum,
649   int                   looping )
650 {
651     musicinfo_t*        music;
652     char                namebuf[9];
653
654     if ( (musicnum <= mus_None)
655          || (musicnum >= NUMMUSIC) )
656     {
657         music = nil;
658         I_Error("Bad music number %d", musicnum);
659     }
660     else
661         music = &S_music[musicnum];
662
663     if (mus_playing == music)
664         return;
665
666     // shutdown old music
667     S_StopMusic();
668
669     // get lumpnum if neccessary
670     if (!music->lumpnum)
671     {
672         sprintf(namebuf, "d_%s", music->name);
673         music->lumpnum = W_GetNumForName(namebuf);
674     }
675
676     // load & register it
677     music->data = (void *) W_CacheLumpNum(music->lumpnum, PU_MUSIC);
678     music->handle = I_RegisterSong(music->data);
679
680     // play it
681     I_PlaySong(music->handle, looping);
682
683     mus_playing = music;
684 }
685
686
687 void S_StopMusic(void)
688 {
689     if (mus_playing)
690     {
691         if (mus_paused)
692             I_ResumeSong(mus_playing->handle);
693
694         I_StopSong(mus_playing->handle);
695         I_UnRegisterSong(mus_playing->handle);
696         Z_ChangeTag(mus_playing->data, PU_CACHE);
697         
698         mus_playing->data = 0;
699         mus_playing = 0;
700     }
701 }
702
703
704
705
706 void S_StopChannel(int cnum)
707 {
708
709     int         i;
710     channel_t*  c = &channels[cnum];
711
712     if (c->sfxinfo)
713     {
714         // stop the sound playing
715         if (I_SoundIsPlaying(c->handle))
716         {
717 #ifdef SAWDEBUG
718             if (c->sfxinfo == &S_sfx[sfx_sawful])
719                 fprintf(stderr, "stopped\n");
720 #endif
721             I_StopSound(c->handle);
722         }
723
724         // check to see
725         //  if other channels are playing the sound
726         for (i=0 ; i<numChannels ; i++)
727         {
728             if (cnum != i
729                 && c->sfxinfo == channels[i].sfxinfo)
730             {
731                 break;
732             }
733         }
734         
735         // degrade usefulness of sound data
736         c->sfxinfo->usefulness--;
737
738         c->sfxinfo = 0;
739     }
740 }
741
742
743
744 //
745 // Changes volume, stereo-separation, and pitch variables
746 //  from the norm of a sound effect to be played.
747 // If the sound is not audible, returns a 0.
748 // Otherwise, modifies parameters and returns 1.
749 //
750 int
751 S_AdjustSoundParams
752 ( mobj_t*       listener,
753   mobj_t*       source,
754   int*          vol,
755   int*          sep,
756   int*          /*pitch*/ )
757 {
758     fixed_t     approx_dist;
759     fixed_t     adx;
760     fixed_t     ady;
761     angle_t     angle;
762
763     // calculate the distance to sound origin
764     //  and clip it if necessary
765     adx = abs(listener->x - source->x);
766     ady = abs(listener->y - source->y);
767
768     // From _GG1_ p.428. Appox. eucledian distance fast.
769     approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);
770     
771     if (gamemap != 8
772         && approx_dist > S_CLIPPING_DIST)
773     {
774         return 0;
775     }
776     
777     // angle of source to listener
778     angle = R_PointToAngle2(listener->x,
779                             listener->y,
780                             source->x,
781                             source->y);
782
783     if (angle > listener->angle)
784         angle = angle - listener->angle;
785     else
786         angle = angle + (0xffffffff - listener->angle);
787
788     angle >>= ANGLETOFINESHIFT;
789
790     // stereo separation
791     *sep = 128 - (FixedMul(S_STEREO_SWING,finesine[angle])>>FRACBITS);
792
793     // volume calculation
794     if (approx_dist < S_CLOSE_DIST)
795     {
796         *vol = snd_SfxVolume;
797     }
798     else if (gamemap == 8)
799     {
800         if (approx_dist > S_CLIPPING_DIST)
801             approx_dist = S_CLIPPING_DIST;
802
803         *vol = 15+ ((snd_SfxVolume-15)
804                     *((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
805             / S_ATTENUATOR;
806     }
807     else
808     {
809         // distance effect
810         *vol = (snd_SfxVolume
811                 * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
812             / S_ATTENUATOR; 
813     }
814     
815     return (*vol > 0);
816 }
817
818
819
820
821 //
822 // S_getChannel :
823 //   If none available, return -1.  Otherwise channel #.
824 //
825 int
826 S_getChannel
827 ( void*         origin,
828   sfxinfo_t*    sfxinfo )
829 {
830     // channel number to use
831     int         cnum;
832     
833     channel_t*  c;
834
835     // Find an open channel
836     for (cnum=0 ; cnum<numChannels ; cnum++)
837     {
838         if (!channels[cnum].sfxinfo)
839             break;
840         else if (origin &&  channels[cnum].origin ==  origin)
841         {
842             S_StopChannel(cnum);
843             break;
844         }
845     }
846
847     // None available
848     if (cnum == numChannels)
849     {
850         // Look for lower priority
851         for (cnum=0 ; cnum<numChannels ; cnum++)
852             if (channels[cnum].sfxinfo->priority >= sfxinfo->priority) break;
853
854         if (cnum == numChannels)
855         {
856             // FUCK!  No lower priority.  Sorry, Charlie.    
857             return -1;
858         }
859         else
860         {
861             // Otherwise, kick out lower priority.
862             S_StopChannel(cnum);
863         }
864     }
865
866     c = &channels[cnum];
867
868     // channel is decided to be cnum.
869     c->sfxinfo = sfxinfo;
870     c->origin = origin;
871
872     return cnum;
873 }
874
875
876
877