]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/doom/i_sound.c
e15e988ab78443f073d7e50bf6ecf6d46cb0abb9
[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 #include "m_argv.h"
8
9 /* The number of internal mixing channels,
10 **  the samples calculated for each mixing step,
11 **  the size of the 16bit, 2 hardware channel (stereo)
12 **  mixing buffer, and the samplerate of the raw data.
13 */
14
15 /* Needed for calling the actual sound output. */
16 #define AUDFREQ         44100
17 #define SFXFREQ         11025
18 #define SAMPLECOUNT     (AUDFREQ/TICRATE)
19 #define NUM_CHANNELS    8
20
21 /* The actual lengths of all sound effects. */
22 int     lengths[NUMSFX];
23
24 /* The actual output device. */
25 static int audio_fd = -1;
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 uchar mixbuf[SAMPLECOUNT*4];
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 extern boolean mus_paused;
74
75 static int mpfd[2] = {-1, -1};
76
77 static void* getsfx(char *sfxname, int *len)
78 {
79         uchar   *sfx;
80         uchar   *paddedsfx;
81         int     i;
82         int     size;
83         int     paddedsize;
84         char    name[20];
85         int     sfxlump;
86
87         /* Get the sound data from the WAD, allocate lump
88         **  in zone memory. */
89         sprintf(name, "ds%s", sfxname);
90
91         /* Now, there is a severe problem with the
92         **  sound handling, in it is not (yet/anymore)
93         **  gamemode aware. That means, sounds from
94         **  DOOM II will be requested even with DOOM
95         **  shareware.
96         ** The sound list is wired into sounds.c,
97         **  which sets the external variable.
98         ** I do not do runtime patches to that
99         **  variable. Instead, we will use a
100         **  default sound for replacement.
101         */
102         if ( W_CheckNumForName(name) == -1 )
103                 sfxlump = W_GetNumForName("dspistol");
104         else
105                 sfxlump = W_GetNumForName(name);
106
107         size = W_LumpLength( sfxlump );
108
109         sfx = (uchar *)W_CacheLumpNum(sfxlump, PU_STATIC);
110
111         /* Pads the sound effect out to the mixing buffer size.
112         ** The original realloc would interfere with zone memory.
113         */
114         paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT;
115
116         /* Allocate from zone memory. */
117         paddedsfx = (uchar *)Z_Malloc(paddedsize+8, PU_STATIC, 0);
118
119         /* Now copy and pad. */
120         memcpy(paddedsfx, sfx, size);
121         for (i=size ; i<paddedsize+8 ; i++)
122                 paddedsfx[i] = 128;
123
124         /* Remove the cached lump. */
125         Z_Free(sfx);
126
127         /* Preserve padded length. */
128         *len = paddedsize;
129
130         /* Return allocated padded data. */
131         return (void *)(paddedsfx + 8);
132 }
133
134 void I_InitSound(void)
135 {
136         int i;
137
138         audio_fd = open("/dev/audio", OWRITE);
139         if(audio_fd < 0){
140                 fprint(2, "I_InitSound: disabling sound: %r\n");
141                 return;
142         }
143         I_InitMusic();
144         /* Initialize external data (all sounds) at start, keep static. */
145         for (i=1 ; i<NUMSFX ; i++)
146         {
147                 if (!S_sfx[i].link)
148                 {
149                         /* Load data from WAD file. */
150                         S_sfx[i].data = getsfx( S_sfx[i].name, &lengths[i] );
151                 }
152         }
153         /* Alias? Example is the chaingun sound linked to pistol. */
154         for (i=1 ; i<NUMSFX ; i++)
155         {
156                 if (S_sfx[i].link)
157                 {
158                         /* Previously loaded already? */
159                         S_sfx[i].data = S_sfx[i].link->data;
160                         lengths[i] = lengths[S_sfx[i].link - S_sfx];
161                 }
162         }
163 }
164
165 /* This function loops all active (internal) sound
166 **  channels, retrieves a given number of samples
167 **  from the raw sound data, modifies it according
168 **  to the current (internal) channel parameters,
169 **  mixes the per-channel samples into the global
170 **  mixbuffer, clamping it to the allowed range,
171 **  and sets up everything for transferring the
172 **  contents of the mixbuffer to the (two)
173 **  hardware channels (left and right, that is).
174 **
175 ** This function currently supports only 16bit.
176 */
177 void I_UpdateSound(void)
178 {
179         int l, r, i, v;
180         uchar *p;
181
182         if(audio_fd < 0)
183                 return;
184         memset(mixbuf, 0, sizeof mixbuf);
185         if(mpfd[0]>=0 && !mus_paused && readn(mpfd[0], mixbuf, sizeof mixbuf) < 0){
186                 fprint(2, "I_UpdateSound: disabling music: %r\n");
187                 I_ShutdownMusic();
188         }
189         p = mixbuf;
190         while(p < mixbuf + sizeof mixbuf){
191                 l = 0;
192                 r = 0;
193                 for(i=0; i<NUM_CHANNELS; i++){
194                         if(channels[i] == nil)
195                                 continue;
196                         v = *channels[i];
197                         l += channelleftvol_lookup[i][v];
198                         r += channelrightvol_lookup[i][v];
199                         channelstepremainder[i] += channelstep[i];
200                         channels[i] += channelstepremainder[i] >> 16;
201                         channelstepremainder[i] &= 0xffff;
202                         if(channels[i] >= channelsend[i])
203                                 channels[i] = 0;
204                 }
205                 for(i=0; i<AUDFREQ/SFXFREQ; i++, p+=4){
206                         v = (short)(p[1] << 8 | p[0]);
207                         v = v * snd_MusicVolume / 15;
208                         v += l;
209                         if(v > 0x7fff)
210                                 v = 0x7fff;
211                         else if(v < -0x8000)
212                                 v = -0x8000;
213                         p[0] = v;
214                         p[1] = v >> 8;
215
216                         v = (short)(p[3] << 8 | p[2]);
217                         v = v * snd_MusicVolume / 15;
218                         v += r;
219                         if(v > 0x7fff)
220                                 v = 0x7fff;
221                         else if(v < -0x8000)
222                                 v = -0x8000;
223                         p[2] = v;
224                         p[3] = v >> 8;
225                 }
226         }
227         if(snd_SfxVolume|snd_MusicVolume)
228                 write(audio_fd, mixbuf, sizeof mixbuf);
229 }
230
231 void I_ShutdownSound(void)
232 {
233         if(audio_fd >= 0) {
234                 close(audio_fd);
235                 audio_fd = -1;
236         }
237 }
238
239 void I_SetChannels(void)
240 {
241         /* Init internal lookups (raw data, mixing buffer, channels).
242         ** This function sets up internal lookups used during
243         **  the mixing process.
244         */
245         int     i;
246         int     j;
247
248         int     *steptablemid = steptable + 128;
249
250         /* This table provides step widths for pitch parameters.
251         ** I fail to see that this is currently used. */
252         for (i=-128 ; i<128 ; i++)
253                 steptablemid[i] = (int)(pow(2.0, (i/64.0))*65536.0);
254
255         /* Generates volume lookup tables
256         **  which also turn the unsigned samples
257         **  into signed samples.
258         */
259         for (i=0 ; i<128 ; i++)
260                 for (j=0 ; j<256 ; j++)
261                         vol_lookup[i*256+j] = (i*(j-128)*256)/127;
262 }
263
264 int I_GetSfxLumpNum(sfxinfo_t *sfxinfo)
265 {
266         char namebuf[9];
267         sprintf(namebuf, "ds%s", sfxinfo->name);
268         return W_GetNumForName(namebuf);
269 }
270
271 /* This function adds a sound to the
272 **  list of currently active sounds,
273 **  which is maintained as a given number
274 **  (eight, usually) of internal channels.
275 ** eturns a handle.
276 */
277 static int
278 addsfx(int id, int vol, int step, int sep)
279 {
280         static unsigned short   handlenums = 0;
281         int                     i;
282         int                     rc;
283         int                     oldest = gametic;
284         int                     oldestnum = 0;
285         int                     slot;
286         int                     rightvol;
287         int                     leftvol;
288
289         /* Chainsaw troubles.
290         ** Play these sound effects only one at a time. */
291         if ( id == sfx_sawup ||
292              id == sfx_sawidl ||
293              id == sfx_sawful ||
294              id == sfx_sawhit ||
295              id == sfx_stnmov ||
296              id == sfx_pistol )
297         {
298                 /* Loop all channels, check. */
299                 for (i=0 ; i < NUM_CHANNELS ; i++)
300                 {
301                         /* Active and using the same SFX? */
302                         if( (channels[i]) && (channelids[i] == id) )
303                         {
304                                 /* Reset. */
305                                 channels[i] = 0;
306                                 /* We are sure that iff,
307                                 **  there will only be one. */
308                                 break;
309                         }
310                 }
311         }
312
313         /* Loop all channels to find oldest SFX. */
314         for (i=0 ; (i<NUM_CHANNELS) && (channels[i]) ; i++)
315         {
316                 if(channelstart[i] < oldest)
317                 {
318                         oldestnum = i;
319                         oldest = channelstart[i];
320                 }
321         }
322
323         /* Tales from the cryptic.
324         ** If we found a channel, fine.
325         ** If not, we simply overwrite the first one, 0.
326         ** Probably only happens at startup.
327         */
328         if (i == NUM_CHANNELS)
329                 slot = oldestnum;
330         else
331                 slot = i;
332
333         /* Okay, in the less recent channel,
334         **  we will handle the new SFX.
335         ** Set pointer to raw data.
336         */
337         channels[slot] = (uchar*) S_sfx[id].data;
338         /* Set pointer to end of raw data. */
339         channelsend[slot] = channels[slot] + lengths[id];
340
341         /* Reset current handle number, limited to 0..100. */
342         if (!handlenums)
343                 handlenums = 100;
344
345         /* Assign current handle number.
346         ** Preserved so sounds could be stopped (unused).
347         */
348         channelhandles[slot] = rc = handlenums++;
349
350         /* Set stepping???
351         ** Kinda getting the impression this is never used.
352         */
353         channelstep[slot] = step;
354         /* ??? */
355         channelstepremainder[slot] = 0;
356         /* Should be gametic, I presume. */
357         channelstart[slot] = gametic;
358
359         /* Separation, that is, orientation/stereo.
360         ** range is : 1 - 256
361         */
362         sep += 1;
363
364         /* Per left/right channel.
365         **  x^2 seperation,
366         **  adjust volume properly.
367         */
368         leftvol = vol - ((vol*sep*sep) >> 16);  // /(256*256);
369         sep = sep - 257;
370         rightvol = vol - ((vol*sep*sep) >> 16);
371
372         /* Sanity check, clamp volume. */
373         if (rightvol < 0 || rightvol > 127)
374                 I_Error("rightvol out of bounds");
375         if (leftvol < 0 || leftvol > 127)
376                 I_Error("leftvol out of bounds");
377
378         /* Get the proper lookup table piece
379         **  for this volume level???
380         */
381         channelleftvol_lookup[slot] = &vol_lookup[leftvol*256];
382         channelrightvol_lookup[slot] = &vol_lookup[rightvol*256];
383
384         /* Preserve sound SFX id,
385         **  e.g. for avoiding duplicates of chainsaw.
386         */
387         channelids[slot] = id;
388
389         /* You tell me. */
390         return rc;
391 }
392
393 int I_StartSound(int id, int vol, int sep, int pitch, int)
394 {
395         if(audio_fd < 0)
396                 return -1;
397         id = addsfx(id, vol, steptable[pitch], sep);
398         return id;
399 }
400
401 void I_StopSound(int handle)
402 {
403         USED(handle);
404 //      printf("PORTME i_sound.c I_StopSound\n");
405 }
406
407 int I_SoundIsPlaying(int handle)
408 {
409         /* Ouch. */
410         return gametic < handle;
411 }
412
413 void I_UpdateSoundParams(int handle, int vol, int sep, int pitch)
414 {
415         /* I fail to see that this is used.
416         ** Would be using the handle to identify
417         **  on which channel the sound might be active,
418         **  and resetting the channel parameters.
419         */
420         USED(handle, vol, sep, pitch);
421 }
422
423 void I_InitMusic(void)
424 {
425 }
426
427 void I_ShutdownMusic(void)
428 {
429         if(mpfd[0] >= 0){
430                 close(mpfd[0]);
431                 mpfd[0] = -1;
432                 waitpid();
433         }
434 }
435
436 void I_SetMusicVolume(int)
437 {
438 }
439
440 void I_PauseSong(int)
441 {
442 }
443
444 void I_ResumeSong(int)
445 {
446 }
447
448 void I_PlaySong(musicinfo_t *m, int loop)
449 {
450         char name[64];
451         int n;
452
453         if(M_CheckParm("-nomusic") || audio_fd < 0)
454                 return;
455         I_ShutdownMusic();
456         if(pipe(mpfd) < 0)
457                 return;
458         switch(rfork(RFPROC|RFFDG|RFNAMEG)){
459         case -1:
460                 fprint(2, "I_PlaySong: %r\n");
461                 break;
462         case 0:
463                 dup(mpfd[1], 1);
464                 for(n=3; n<20; n++) close(n);
465                 snprint(name, sizeof(name), "/mnt/wad/d_%s", m->name);
466                 if(bind("/fd/1", "/dev/audio", MREPL) < 0)
467                         sysfatal("bind: %r");
468                 while(loop && fork() > 0){
469                         if(waitpid() < 0 || write(1, "", 0) < 0)
470                                 exits(nil);
471                 }
472                 execl("/bin/dmus", "dmus", name, m->name, nil);
473                 execl("/bin/play", "play", name, nil);
474                 sysfatal("execl: %r");
475         default:
476                 close(mpfd[1]);
477         }
478 }
479
480 void I_StopSong(int)
481 {
482         I_ShutdownMusic();
483 }