]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/doom/i_sound.c
games/doom: fix switch textures swapping in ultimate doom (thansk qu7uux)
[plan9front.git] / sys / src / games / doom / i_sound.c
1 /* i_sound.c */
2
3 #include "i_system.h"
4 #include "i_sound.h"
5 #include "w_wad.h"      // W_GetNumForName()
6 #include "z_zone.h"
7
8 /* The number of internal mixing channels,
9 **  the samples calculated for each mixing step,
10 **  the size of the 16bit, 2 hardware channel (stereo)
11 **  mixing buffer, and the samplerate of the raw data.
12 */
13
14 /* Needed for calling the actual sound output. */
15 #define SAMPLECOUNT     (512<<2)
16 #define NUM_CHANNELS    8
17
18 /* The actual lengths of all sound effects. */
19 int     lengths[NUMSFX];
20
21 /* The actual output device. */
22 static int audio_fd;
23
24 /* The global mixing buffer.
25 ** Basically, samples from all active internal channels
26 **  are modified and added, and stored in the buffer
27 **  that is submitted to the audio device.
28 */
29 signed short    mixbuffer[SAMPLECOUNT*2];
30
31 /* The channel step amount... */
32 uint    channelstep[NUM_CHANNELS];
33 /* ... and a 0.16 bit remainder of last step. */
34 uint    channelstepremainder[NUM_CHANNELS];
35
36 /* The channel data pointers, start and end. */
37 uchar*  channels[NUM_CHANNELS];
38 uchar*  channelsend[NUM_CHANNELS];
39
40 /* Time/gametic that the channel started playing,
41 **  used to determine oldest, which automatically
42 **  has lowest priority.
43 ** In case number of active sounds exceeds
44 **  available channels.
45 */
46 int     channelstart[NUM_CHANNELS];
47
48 /* The sound in channel handles,
49 **  determined on registration,
50 **  might be used to unregister/stop/modify,
51 **  currently unused.
52 */
53 int     channelhandles[NUM_CHANNELS];
54
55 /* SFX id of the playing sound effect.
56 ** Used to catch duplicates (like chainsaw).
57 */
58 int     channelids[NUM_CHANNELS];
59
60 /* Pitch to stepping lookup, unused. */
61 int     steptable[256];
62
63 /* Volume lookups. */
64 int     vol_lookup[128*256];
65
66 /* Hardware left and right channel volume lookup. */
67 int*    channelleftvol_lookup[NUM_CHANNELS];
68 int*    channelrightvol_lookup[NUM_CHANNELS];
69
70 static void* getsfx(char *sfxname, int *len)
71 {
72         uchar   *sfx;
73         uchar   *paddedsfx;
74         int     i;
75         int     size;
76         int     paddedsize;
77         char    name[20];
78         int     sfxlump;
79
80         /* Get the sound data from the WAD, allocate lump
81         **  in zone memory. */
82         sprintf(name, "ds%s", sfxname);
83
84         /* Now, there is a severe problem with the
85         **  sound handling, in it is not (yet/anymore)
86         **  gamemode aware. That means, sounds from
87         **  DOOM II will be requested even with DOOM
88         **  shareware.
89         ** The sound list is wired into sounds.c,
90         **  which sets the external variable.
91         ** I do not do runtime patches to that
92         **  variable. Instead, we will use a
93         **  default sound for replacement.
94         */
95         if ( W_CheckNumForName(name) == -1 )
96                 sfxlump = W_GetNumForName("dspistol");
97         else
98                 sfxlump = W_GetNumForName(name);
99
100         size = W_LumpLength( sfxlump );
101
102         sfx = (uchar *)W_CacheLumpNum(sfxlump, PU_STATIC);
103
104         /* Pads the sound effect out to the mixing buffer size.
105         ** The original realloc would interfere with zone memory.
106         */
107         paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT;
108
109         /* Allocate from zone memory. */
110         paddedsfx = (uchar *)Z_Malloc(paddedsize+8, PU_STATIC, 0);
111
112         /* Now copy and pad. */
113         memcpy(paddedsfx, sfx, size);
114         for (i=size ; i<paddedsize+8 ; i++)
115                 paddedsfx[i] = 128;
116
117         /* Remove the cached lump. */
118         Z_Free(sfx);
119
120         /* Preserve padded length. */
121         *len = paddedsize;
122
123         /* Return allocated padded data. */
124         return (void *)(paddedsfx + 8);
125 }
126
127 void I_InitSound(void)
128 {
129         int i;
130
131         audio_fd = open("/dev/audio", OWRITE);
132         if(audio_fd < 0) 
133                 printf("WARN Failed to open /dev/audio, sound disabled\n");
134
135         /* Initialize external data (all sounds) at start, keep static. */
136         for (i=1 ; i<NUMSFX ; i++)
137         {
138                 /* Alias? Example is the chaingun sound linked to pistol. */
139                 if (!S_sfx[i].link)
140                 {
141                         /* Load data from WAD file. */
142                         S_sfx[i].data = getsfx( S_sfx[i].name, &lengths[i] );
143                 }
144                 else
145                 {
146                         /* Previously loaded already? */
147                         S_sfx[i].data = S_sfx[i].link->data;
148                         lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)];
149                 }
150         }
151
152         /* Now initialize mixbuffer with zero. */
153         memset(mixbuffer, 0, sizeof mixbuffer);
154 }
155
156 /* This function loops all active (internal) sound
157 **  channels, retrieves a given number of samples
158 **  from the raw sound data, modifies it according
159 **  to the current (internal) channel parameters,
160 **  mixes the per-channel samples into the global
161 **  mixbuffer, clamping it to the allowed range,
162 **  and sets up everything for transferring the
163 **  contents of the mixbuffer to the (two)
164 **  hardware channels (left and right, that is).
165 **
166 ** This function currently supports only 16bit.
167 */
168 void I_UpdateSound(void)
169 {
170         /* Mix current sound data.
171         ** Data, from raw sound, for right and left.
172         */
173         register uint   sample;
174         register int    dl;
175         register int    dr;
176
177         /* Pointers in global mixbuffer, left, right, end. */
178         signed short    *leftout;
179         signed short    *rightout;
180         signed short    *leftend;
181         /* Step in mixbuffer, left and right, thus two. */
182         int             step;
183
184         /* Mixing channel index. */
185         int             chan;
186
187         /* Left and right channel
188         **  are in global mixbuffer, alternating. */
189         leftout = mixbuffer;
190         rightout = mixbuffer+1;
191         step = 2;
192
193         /* Determine end, for left channel only
194         **  (right channel is implicit). */
195         leftend = mixbuffer + SAMPLECOUNT*step;
196
197         /* Mix sounds into the mixing buffer.
198         ** Loop over step*SAMPLECOUNT
199         **   that is 512 values for two channels.
200         */
201         while (leftout != leftend)
202         {
203                 /* Reset left/right value. */
204                 dl = 0;
205                 dr = 0;
206
207                 /* Love thy L2 cache - made this a loop.
208                 ** Now more channels could be set at compile time
209                 **  as well. Thus loop those channels.
210                 */
211                 for(chan=0; chan < NUM_CHANNELS; chan++)
212                 {
213                         /* Check channel, if active. */
214                         if(channels[ chan ])
215                         {
216                                 /* Get the raw data from the channel. */
217                                 sample = *channels[ chan ];
218                                 /* Add left and right part
219                                 **  for this channel (sound)
220                                 **  to the current data.
221                                 ** Adjust volume accordingly.
222                                 */
223                                 dl += channelleftvol_lookup[ chan ][sample];
224                                 dr += channelrightvol_lookup[ chan ][sample];
225                                 /* Increment index ??? */
226                                 channelstepremainder[ chan ] += channelstep[ chan ];
227                                 /* MSB is next sample??? */
228                                 channels[ chan ] += channelstepremainder[ chan ] >> 16;
229                                 /* Limit to LSB??? */
230                                 channelstepremainder[ chan ] &= 65536-1;
231
232                                 /* Check whether we are done. */
233                                 if (channels[ chan ] >= channelsend[ chan ])
234                                         channels[ chan ] = 0;
235                         }
236                 }
237
238                 /* Clamp to range. */
239                 if (dl > 0x7fff)
240                         dl = 0x7fff;
241                 else if (dl < -0x8000)
242                         dl = -0x8000;
243                 if (dr > 0x7fff)
244                         dr = 0x7fff;
245                 else if (dr < -0x8000)
246                         dr = -0x8000;
247
248                 *leftout = dl;
249                 *rightout = dr;
250
251                 /* Increment current pointers in mixbuffer. */
252                 leftout += step;
253                 rightout += step;
254
255                 *leftout = dl;
256                 *rightout = dr;
257                 leftout += step;
258                 rightout += step;
259
260                 *leftout = dl;
261                 *rightout = dr;
262                 leftout += step;
263                 rightout += step;
264
265                 *leftout = dl;
266                 *rightout = dr;
267                 leftout += step;
268                 rightout += step;
269         }
270 }
271
272 void I_SubmitSound(void)
273 {
274         if(audio_fd >= 0)
275                 write(audio_fd, mixbuffer, sizeof mixbuffer);
276 }
277
278 void I_ShutdownSound(void)
279 {
280         if(audio_fd >= 0) {
281                 close(audio_fd);
282                 audio_fd = -1;
283         }
284 }
285
286 void I_SetChannels(void)
287 {
288         /* Init internal lookups (raw data, mixing buffer, channels).
289         ** This function sets up internal lookups used during
290         **  the mixing process.
291         */
292         int     i;
293         int     j;
294
295         int     *steptablemid = steptable + 128;
296
297         /* This table provides step widths for pitch parameters.
298         ** I fail to see that this is currently used. */
299         for (i=-128 ; i<128 ; i++)
300                 steptablemid[i] = (int)(pow(2.0, (i/64.0))*65536.0);
301
302         /* Generates volume lookup tables
303         **  which also turn the unsigned samples
304         **  into signed samples.
305         */
306         for (i=0 ; i<128 ; i++)
307                 for (j=0 ; j<256 ; j++)
308                         vol_lookup[i*256+j] = (i*(j-128)*256)/127;
309 }
310
311 int I_GetSfxLumpNum(sfxinfo_t *sfxinfo)
312 {
313         char namebuf[9];
314         sprintf(namebuf, "ds%s", sfxinfo->name);
315         return W_GetNumForName(namebuf);
316 }
317
318 /* This function adds a sound to the
319 **  list of currently active sounds,
320 **  which is maintained as a given number
321 **  (eight, usually) of internal channels.
322 ** eturns a handle.
323 */
324 static int
325 addsfx(int id, int vol, int step, int sep)
326 {
327         static unsigned short   handlenums = 0;
328         int                     i;
329         int                     rc;
330         int                     oldest = gametic;
331         int                     oldestnum = 0;
332         int                     slot;
333         int                     rightvol;
334         int                     leftvol;
335
336         /* Chainsaw troubles.
337         ** Play these sound effects only one at a time. */
338         if ( id == sfx_sawup ||
339              id == sfx_sawidl ||
340              id == sfx_sawful ||
341              id == sfx_sawhit ||
342              id == sfx_stnmov ||
343              id == sfx_pistol )
344         {
345                 /* Loop all channels, check. */
346                 for (i=0 ; i < NUM_CHANNELS ; i++)
347                 {
348                         /* Active and using the same SFX? */
349                         if( (channels[i]) && (channelids[i] == id) )
350                         {
351                                 /* Reset. */
352                                 channels[i] = 0;
353                                 /* We are sure that iff,
354                                 **  there will only be one. */
355                                 break;
356                         }
357                 }
358         }
359
360         /* Loop all channels to find oldest SFX. */
361         for (i=0 ; (i<NUM_CHANNELS) && (channels[i]) ; i++)
362         {
363                 if(channelstart[i] < oldest)
364                 {
365                         oldestnum = i;
366                         oldest = channelstart[i];
367                 }
368         }
369
370         /* Tales from the cryptic.
371         ** If we found a channel, fine.
372         ** If not, we simply overwrite the first one, 0.
373         ** Probably only happens at startup.
374         */
375         if (i == NUM_CHANNELS)
376                 slot = oldestnum;
377         else
378                 slot = i;
379
380         /* Okay, in the less recent channel,
381         **  we will handle the new SFX.
382         ** Set pointer to raw data.
383         */
384         channels[slot] = (uchar*) S_sfx[id].data;
385         /* Set pointer to end of raw data. */
386         channelsend[slot] = channels[slot] + lengths[id];
387
388         /* Reset current handle number, limited to 0..100. */
389         if (!handlenums)
390                 handlenums = 100;
391
392         /* Assign current handle number.
393         ** Preserved so sounds could be stopped (unused).
394         */
395         channelhandles[slot] = rc = handlenums++;
396
397         /* Set stepping???
398         ** Kinda getting the impression this is never used.
399         */
400         channelstep[slot] = step;
401         /* ??? */
402         channelstepremainder[slot] = 0;
403         /* Should be gametic, I presume. */
404         channelstart[slot] = gametic;
405
406         /* Separation, that is, orientation/stereo.
407         ** range is : 1 - 256
408         */
409         sep += 1;
410
411         /* Per left/right channel.
412         **  x^2 seperation,
413         **  adjust volume properly.
414         */
415         leftvol = vol - ((vol*sep*sep) >> 16);  // /(256*256);
416         sep = sep - 257;
417         rightvol = vol - ((vol*sep*sep) >> 16);
418
419         /* Sanity check, clamp volume. */
420         if (rightvol < 0 || rightvol > 127)
421                 I_Error("rightvol out of bounds");
422         if (leftvol < 0 || leftvol > 127)
423                 I_Error("leftvol out of bounds");
424
425         /* Get the proper lookup table piece
426         **  for this volume level???
427         */
428         channelleftvol_lookup[slot] = &vol_lookup[leftvol*256];
429         channelrightvol_lookup[slot] = &vol_lookup[rightvol*256];
430
431         /* Preserve sound SFX id,
432         **  e.g. for avoiding duplicates of chainsaw.
433         channelids[slot] = id;
434
435         /* You tell me. */
436         return rc;
437 }
438
439 int I_StartSound(int id, int vol, int sep, int pitch, int priority)
440 {
441         USED(priority);
442         id = addsfx(id, vol, steptable[pitch], sep);
443         return id;
444 }
445
446 void I_StopSound(int handle)
447 {
448         USED(handle);
449 //      printf("PORTME i_sound.c I_StopSound\n");
450 }
451
452 int I_SoundIsPlaying(int handle)
453 {
454         /* Ouch. */
455         return gametic < handle;
456 }
457
458 void I_UpdateSoundParams(int handle, int vol, int sep, int pitch)
459 {
460         /* I fail to see that this is used.
461         ** Would be using the handle to identify
462         **  on which channel the sound might be active,
463         **  and resetting the channel parameters.
464         */
465         USED(handle, vol, sep, pitch);
466 }
467
468 void I_InitMusic(void)
469 {
470 //      printf("PORTME i_sound.c I_InitMusic\n");
471 }
472
473 void I_ShutdownMusic(void)
474 {
475 //      printf("PORTME i_sound.c I_ShutdownMusic\n");
476 }
477
478 void I_SetMusicVolume(int volume)
479 {
480         USED(volume);
481 //      printf("PORTME i_sound.c I_SetMusicVolume\n");
482 }
483
484 void I_PauseSong(int handle)
485 {
486         USED(handle);
487 //      printf("PORTME i_sound.c I_PauseSong\n");
488 }
489
490 void I_ResumeSong(int handle)
491 {
492         USED(handle);
493 //      printf("PORTME i_sound.c I_ResumeSong\n");
494 }
495
496 int I_RegisterSong(void *data)
497 {
498         USED(data);
499 //      printf("PORTME i_sound.c I_RegisterSong\n");
500         return 0;
501 }
502
503 void I_PlaySong(int handle, int looping)
504 {
505         USED(handle, looping);
506 //      printf("PORTME i_sound.c I_PlaySong\n");
507 }
508
509 void I_StopSong(int handle)
510 {
511         USED(handle);
512 //      printf("PORTME i_sound.c I_StopSong\n");
513 }
514
515 void I_UnRegisterSong(int handle)
516 {
517         USED(handle);
518 //      printf("PORTME i_sound.c I_UnregisterSong\n");
519 }