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