1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdarg.h>
5 #include <string.h>
6 #include <assert.h>
7
8 #include <math.h>
9
10 #include <sys/time.h>
11 #include <sys/types.h>
12
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <sys/ioctl.h>
16
17 #include <gsi/gsi.h>
18
19 #include "i_sound.h"
20 #include "soundst.h"
21
22 #include "doomdef.h"
23
24 char *doomwaddir;
25 char *ptr;
26
27 extern char *doomwaddir;
28
29 /*
30 * The number of internal mixing channels,
31 * the samples calculated for each mixing step,
32 * the size of the 16bit, 2 hardware channel (stereo)
33 * mixing buffer, and the samplerate of the raw data.
34 */
35
36
37 /* Needed for calling the actual sound output. */
38 #define SAMPLECOUNT 512
39 #define NUM_CHANNELS 8
40 /* It is 2 for 16bit, and 2 for two channels. */
41 #define BUFMUL 4
42 #define MIXBUFFERSIZE (SAMPLECOUNT*BUFMUL)
43
44 #define SAMPLERATE 11025 /* Hz */
45 #define SAMPLESIZE 2 /* 16bit */
46
47 /* The actual lengths of all sound effects. */
48 int lengths[NUMSFX];
49
50 /* The actual output device. */
51 int audio_fd;
52
53 /*
54 * The global mixing buffer.
55 * Basically, samples from all active internal channels
56 * are modifed and added, and stored in the buffer
57 * that is submitted to the audio device.
58 */
59 signed short mixbuffer[MIXBUFFERSIZE];
60
61
62 /* The channel step amount... */
63 unsigned int channelstep[NUM_CHANNELS];
64 /* ... and a 0.16 bit remainder of last step. */
65 unsigned int channelstepremainder[NUM_CHANNELS];
66
67
68 /* The channel data pointers, start and end. */
69 unsigned char* channels[NUM_CHANNELS];
70 unsigned char* channelsend[NUM_CHANNELS];
71
72 /*
73 * Time/gametic that the channel started playing,
74 * used to determine oldest, which automatically
75 * has lowest priority.
76 * In case number of active sounds exceeds
77 * available channels.
78 */
79 int channelstart[NUM_CHANNELS];
80
81 /*
82 * The sound in channel handles,
83 * determined on registration,
84 * might be used to unregister/stop/modify,
85 * currently unused.
86 */
87 int channelhandles[NUM_CHANNELS];
88
89 /*
90 * SFX id of the playing sound effect.
91 * Used to catch duplicates (like chainsaw).
92 */
93 int channelids[NUM_CHANNELS];
94
95 /* Pitch to stepping lookup, unused. */
96 int steptable[256];
97
98 /* Volume lookups. */
99 int vol_lookup[128*256];
100
101 /* Hardware left and right channel volume lookup. */
102 int* channelleftvol_lookup[NUM_CHANNELS];
103 int* channelrightvol_lookup[NUM_CHANNELS];
104
105
106
107 /*
108 * This function loads the sound data from the WAD lump,
109 * for single sound.
110 */
getsfx(int sfx_id,char * sfxname,int * len)111 void* getsfx( int sfx_id, char* sfxname, int* len )
112 {
113 int size;
114 char name[20];
115 unsigned char* paddedsfx;
116 unsigned char* sfx;
117 int paddedsize;
118 int sfxlump;
119 int i;
120
121
122 /*
123 * Get the sound data from the WAD, allocate lump
124 * in zone memory.
125 */
126 sprintf(name, "%s", sfxname);
127
128 if ( W_CheckNumForName(name) == -1 )
129 sfxlump = /* W_GetNumForName("dspistol"); */ W_GetNumForName("gldhit");
130 else
131 sfxlump = W_GetNumForName(name);
132
133 size = W_LumpLength( sfxlump );
134
135
136 /* Andre: this should be fixed ! */
137
138 gsi_load_raw_sample(sfx_id, wadfiles[0], lumpinfo[sfxlump].position +8,
139 size-8, 11025, 1, GSI_8BIT);
140 gsi_flush();
141
142 /* Return allocated padded data. */
143 return (void *) (paddedsfx + 8);
144 }
145
146
147
148 /*
149 * This function adds a sound to the
150 * list of currently active sounds,
151 * which is maintained as a given number
152 * (eight, usually) of internal channels.
153 * Returns a handle.
154 */
addsfx(int sfxid,int volume,int step,int seperation)155 int addsfx( int sfxid, int volume, int step, int seperation )
156 {
157 static unsigned short handlenums = 0;
158
159 int i;
160 int rc = -1;
161
162 int oldest = gametic;
163 int oldestnum = 0;
164 int slot;
165
166 int rightvol;
167 int leftvol;
168
169 {
170 /* Loop all channels, check. */
171 for (i=0 ; i<NUM_CHANNELS ; i++)
172 {
173 /* Active, and using the same SFX? */
174 if ( (channels[i])
175 && (channelids[i] == sfxid) )
176 {
177 /* Reset. */
178 channels[i] = 0;
179 /*
180 * We are sure that iff,
181 * there will only be one.
182 */
183 gsi_kill_sound (i);
184 break;
185 }
186 }
187 }
188
189 /* Loop all channels to find oldest SFX. */
190 for (i=0; (i<NUM_CHANNELS) && (channels[i]); i++)
191 {
192 if (channelstart[i] < oldest)
193 {
194 oldestnum = i;
195 oldest = channelstart[i];
196 }
197 }
198
199 /*
200 * Tales from the cryptic.
201 * If we found a channel, fine.
202 * If not, we simply overwrite the first one, 0.
203 * Probably only happens at startup.
204 */
205 if (i == NUM_CHANNELS)
206 slot = oldestnum;
207 else
208 slot = i;
209
210 /*
211 * Okay, in the less recent channel,
212 * we will handle the new SFX.
213 * Set pointer to raw data.
214 */
215 channels[slot] = (unsigned char *) S_sfx[sfxid].data;
216
217 /* Set pointer to end of raw data. */
218 channelsend[slot] = channels[slot] + lengths[sfxid];
219
220 /* Reset current handle number, limited to 0..100. */
221 if (!handlenums)
222 handlenums = 100;
223
224 /*
225 * Assign current handle number.
226 * Preserved so sounds could be stopped (unused).
227 */
228 channelhandles[slot] = rc = handlenums++;
229
230 /*
231 * Set stepping???
232 * Kinda getting the impression this is never used.
233 */
234 channelstep[slot] = step;
235
236 channelstepremainder[slot] = 0;
237
238 /* Should be gametic, I presume. */
239 channelstart[slot] = gametic;
240
241 /*
242 * Separation, that is, orientation/stereo.
243 * range is: 1 - 256
244 */
245 seperation += 1;
246
247 /*
248 * Per left/right channel.
249 * x^2 seperation,
250 * adjust volume properly.
251 */
252 leftvol =
253 volume - ((volume*seperation*seperation) >> 16); /* /(256*256); */
254 seperation = seperation - 257;
255 rightvol =
256 volume - ((volume*seperation*seperation) >> 16);
257
258 fprintf(stderr, "rightvol: %d\n", rightvol);
259 fprintf(stderr, "leftvol: %d\n", leftvol);
260
261 /* Sanity check, clamp volume. */
262 if (rightvol < 0 || rightvol > 127)
263 I_Error("rightvol out of bounds");
264
265 if (leftvol < 0 || leftvol > 127)
266 I_Error("leftvol out of bounds");
267
268 /*
269 * Get the proper lookup table piece
270 * for this volume level???
271 */
272 channelleftvol_lookup[slot] = &vol_lookup[leftvol*256];
273 channelrightvol_lookup[slot] = &vol_lookup[rightvol*256];
274
275 /*
276 * Preserve sound SFX id,
277 * e.g. for avoiding duplicates of chainsaw.
278 */
279 channelids[slot] = sfxid;
280
281 gsi_play_sound(slot, sfxid, GSI_CMD_OVERWRITE);
282 seperation +=257; /* undo above, or just ((seperation *2)-257 -> (seperation *2)+257 .. */
283
284 gsi_send_commands(10,
285 GSI_CMD_SET_VOLUME | GSI_CMD_CURRENT,
286 volume *2,
287 GSI_CMD_SET_PAN | GSI_CMD_CURRENT,
288 (((seperation *2)-257) >>8)&255, ((seperation *2)-257)&255,
289 GSI_CMD_SET_SAMPLE_FACTOR | GSI_CMD_CURRENT,
290 step >>24, step >>16, step >>8, step);
291
292 gsi_flush();
293
294 /* You tell me. */
295 return rc;
296 }
297
298
299 /*
300 * SFX API
301 * Note: this was called by S_Init.
302 * However, whatever they did in the
303 * old DPMS based DOS version, this
304 * were simply dummies in the Linux
305 * version.
306 * See soundserver initdata().
307 */
I_SetChannels()308 void I_SetChannels()
309 {
310 /*
311 * Init internal lookups (raw data, mixing buffer, channels).
312 * This function sets up internal lookups used during
313 * the mixing process.
314 */
315 int i;
316 int j;
317
318 int* steptablemid = steptable + 128;
319
320 /*
321 * This table provides step widths for pitch parameters.
322 * I fail to see that this is currently used.
323 */
324 for (i=-128 ; i<128 ; i++)
325 steptablemid[i] = (int)(pow(2.0, (i/64.0))*65536.0);
326
327 /*
328 * Generates volume lookup tables
329 * which also turn the unsigned samples
330 * into signed samples.
331 */
332 for (i=0 ; i<128 ; i++)
333 for (j=0 ; j<256 ; j++)
334 vol_lookup[i*256+j] = (i*(j-128)*256)/127;
335 }
336
337
I_SetSfxVolume(int volume)338 void I_SetSfxVolume(int volume)
339 {
340 /*
341 * Identical to DOS.
342 * Basically, this should propagate
343 * the menu/config file setting
344 * to the state variable used in
345 * the mixing.
346 */
347 snd_SfxVolume = volume;
348
349 /* doom volume = 0-127 */
350 gsi_set_volume(GSI_PCM, volume*2);
351 gsi_flush();
352 }
353
354
355 /*
356 * MUSIC API
357 */
I_SetMusicVolume(int volume)358 void I_SetMusicVolume(int volume)
359 {
360 /* Internal state variable. */
361 snd_MusicVolume = volume;
362 /*
363 * Now set volume on output device.
364 * Whatever( snd_MusciVolume );
365 */
366
367 /* doom volume = 0-127 */
368 gsi_set_volume(GSI_SYNTH, volume*2);
369 gsi_flush();
370 }
371
372
373 /*
374 * Retrieve the raw data lump index
375 * for a given SFX name.
376 */
I_GetSfxLumpNum(sfxinfo_t * sfx)377 int I_GetSfxLumpNum(sfxinfo_t* sfx)
378 {
379 char namebuf[9];
380 int dummy;
381 sprintf(namebuf, "%s", sfx->name);
382 dummy=W_GetNumForName(namebuf);
383
384 fprintf(stderr, "[I_GetSfxLumpNum] sfx->name: %s ; W_GetNumForName: %d\n",
385 sfx->name, dummy);
386
387 return dummy;
388 }
389
390
391 /*
392 * Starting a sound means adding it
393 * to the current list of active sounds
394 * in the internal channels.
395 * As the SFX info struct contains
396 * e.g. a pointer to the raw data,
397 * it is ignored.
398 * As our sound handling does not handle
399 * priority, it is ignored.
400 * Pitching (that is, increased speed of playback)
401 * is set, but currently not used by mixing.
402 */
I_StartSound(int id,int vol,int sep,int pitch,int priority)403 int I_StartSound( int id, int vol, int sep, int pitch, int priority )
404 {
405 /* UNUSED */
406 priority = 0;
407
408 /* Returns a handle (not used). */
409 id = addsfx( id, vol, steptable[pitch], sep );
410
411 fprintf( stderr, "handle is %d\n", id );
412 return id;
413 }
414
415
I_StopSound(int handle)416 void I_StopSound (int handle)
417 {
418 /*
419 * You need the handle returned by StartSound.
420 * Would be looping all channels,
421 * tracking down the handle,
422 * an setting the channel to zero.
423 */
424 }
425
426
I_SoundIsPlaying(int handle)427 int I_SoundIsPlaying(int handle)
428 {
429 /* Ouch. */
430 return gametic < handle;
431 }
432
433
434 /*
435 * This function loops all active (internal) sound
436 * channels, retrieves a given number of samples
437 * from the raw sound data, modifies it according
438 * to the current (internal) channel parameters,
439 * mixes the per channel samples into the global
440 * mixbuffer, clamping it to the allowed range,
441 * and sets up everything for transferring the
442 * contents of the mixbuffer to the (two)
443 * hardware channels (left and right, that is).
444 *
445 * This function currently supports only 16bit.
446 */
I_UpdateSound(void)447 void I_UpdateSound( void )
448 {
449 /*
450 * Mix current sound data.
451 * Data, from raw sound, for right and left.
452 */
453 register unsigned int sample;
454 register int dl;
455 register int dr;
456
457 /* Pointers in global mixbuffer, left, right, end. */
458 signed short* leftout;
459 signed short* rightout;
460 signed short* leftend;
461 /* Step in mixbuffer, left and right, thus two. */
462 int step;
463
464 /* Mixing channel index. */
465 int chan;
466
467 /*
468 * Left and right channel
469 * are in global mixbuffer, alternating.
470 */
471 leftout = mixbuffer;
472 rightout = mixbuffer+1;
473 step = 2;
474
475 /*
476 * Determine end, for left channel only
477 * (right channel is implicit).
478 */
479 leftend = mixbuffer + SAMPLECOUNT*step;
480
481 /*
482 * Mix sounds into the mixing buffer.
483 * Loop over step*SAMPLECOUNT,
484 * that is 512 values for two channels.
485 */
486 while (leftout != leftend)
487 {
488 /* Reset left/right value. */
489 dl = 0;
490 dr = 0;
491
492 /*
493 * Love thy L2 chache - made this a loop.
494 * Now more channels could be set at compile time
495 * as well. Thus loop those channels.
496 */
497 for ( chan = 0; chan < NUM_CHANNELS; chan++ )
498 {
499 /* Check channel, if active. */
500 if (channels[ chan ])
501 {
502 /* Get the raw data from the channel. */
503 sample = *channels[ chan ];
504 /* Add left and right part */
505
506 /*
507 * for this channel (sound)
508 * to the current data.
509 * Adjust volume accordingly.
510 */
511 dl += channelleftvol_lookup[ chan ][sample];
512 dr += channelrightvol_lookup[ chan ][sample];
513
514 /* Increment index ??? */
515 channelstepremainder[ chan ] += channelstep[ chan ];
516
517 /* MSB is next sample??? */
518 channels[ chan ] += channelstepremainder[ chan ] >> 16;
519
520 /* Limit to LSB??? */
521 channelstepremainder[ chan ] &= 65536-1;
522
523 /* Check whether we are done. */
524 if (channels[ chan ] >= channelsend[ chan ])
525 channels[ chan ] = 0;
526 }
527 }
528
529 /*
530 * Clamp to range. Left hardware channel.
531 * Has been char instead of short.
532 * if (dl > 127) *leftout = 127;
533 * else if (dl < -128) *leftout = -128;
534 * else *leftout = dl;
535 */
536
537 if (dl > 0x7fff)
538 *leftout = 0x7fff;
539 else if (dl < -0x8000)
540 *leftout = -0x8000;
541 else
542 *leftout = dl;
543
544 /* Same for right hardware channel. */
545 if (dr > 0x7fff)
546 *rightout = 0x7fff;
547 else if (dr < -0x8000)
548 *rightout = -0x8000;
549 else
550 *rightout = dr;
551
552 /* Increment current pointers in mixbuffer. */
553 leftout += step;
554 rightout += step;
555 }
556 }
557
558
559 /*
560 * This would be used to write out the mixbuffer
561 * during each game loop update.
562 * Updates sound buffer and audio device at runtime.
563 * It is called during Timer interrupt with SNDINTR.
564 * Mixing now done synchronous, and
565 * only output be done asynchronous?
566 */
I_SubmitSound(void)567 void I_SubmitSound(void)
568 {
569 /* Write it to DSP device. */
570 write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);
571 }
572
573
I_UpdateSoundParams(int handle,int vol,int sep,int pitch)574 void I_UpdateSoundParams( int handle, int vol, int sep, int pitch)
575 {
576 /*
577 * I fail too see that this is used.
578 * Would be using the handle to identify
579 * on which channel the sound might be active,
580 * and resetting the channel parameters.
581 */
582
583 /* #warning add update here. */
584
585
586 fprintf(stderr, "I_UpdateSoundParams for handle=%d\n", handle);
587 /*
588 * ===> is handle really the channel? check
589 * gsi_send_commands(6,
590 * GSI_CMD_SET_VOLUME, handle, volume *2,
591 * GSI_CMD_SET_PAN | GSI_CMD_CURRENT, (((seperation *2)-257) >>8)&255, ((seperation *2)-257)&255
592 * );
593 *gsi_flush();
594 */
595 }
596
597
I_ShutdownSound(void)598 void I_ShutdownSound(void)
599 {
600 gsi_close();
601 gsi_flush();
602
603 /* Done. */
604 return;
605 }
606
607
608 extern char* wadfiles[];
609
I_InitSound()610 void I_InitSound()
611 {
612 int i;
613
614 if (gsi_init(NULL)) {
615 fprintf(stderr, "GSI: unable to connect to server\n");
616 return;
617 }
618
619 doomwaddir=(char*) malloc(256);
620 assert(doomwaddir);
621 strncpy(doomwaddir, wadfiles[0], 255);
622 doomwaddir[255] = 0;
623
624 ptr = getcwd(doomwaddir, 256);
625 if (ptr==NULL) { perror("getcwd"); I_Error("bailing out...\n"); }
626
627 fprintf(stderr, "doomwaddir: %s\n", doomwaddir);
628
629 gsi_chdir(doomwaddir);
630
631 gsi_init_pcm(11025, 2, GSI_16BIT, 10);
632 gsi_flush();
633
634 /* Initialize external data (all sounds) at start, keep static. */
635 fprintf( stderr, "I_InitSound: ");
636
637 for (i=1 ; i<NUMSFX ; i++)
638 {
639 S_sfx[i].lumpnum = -1;
640 /* Alias? Example is the chaingun sound linked to pistol. */
641 if (!S_sfx[i].link)
642 {
643 /* Load data from WAD file. */
644 S_sfx[i].data = getsfx(i, S_sfx[i].name, &lengths[i] );
645 }
646 else
647 {
648 /* Previously loaded already? */
649 S_sfx[i].data = S_sfx[i].link->data;
650 lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)];
651 }
652 }
653 fprintf( stderr, " pre-cached all sound data\n");
654
655 /* Now initialize mixbuffer with zero. */
656 for ( i = 0; i< MIXBUFFERSIZE; i++ )
657 mixbuffer[i] = 0;
658
659 /* Finished initialization. */
660 fprintf(stderr, "I_InitSound: sound module ready\n");
661 }
662
663
664 /*
665 * MUSIC API.
666 */
I_InitMusic(void)667 void I_InitMusic(void)
668 {
669 fprintf(stderr, "I_InitMusic:\n");
670 gsi_grab_synth();
671 gsi_flush();
672 }
673
674
I_ShutdownMusic(void)675 void I_ShutdownMusic(void)
676 {
677 gsi_release_synth();
678 gsi_flush();
679 }
680
681 static int looping=0;
682 static int musicdies=-1;
683
684
I_PlaySong(int handle,int loop)685 void I_PlaySong(int handle, int loop)
686 {
687 /*
688 * WHS: in doom, loop = boolean, i.e. if loop=0 then play only once (see
689 * s_sound.c for a call with loop=false)
690 */
691 gsi_play_song(1-loop);
692 gsi_flush();
693 looping = loop;
694 musicdies = gametic + TICRATE*30;
695 }
696
697
I_PauseSong(int handle)698 void I_PauseSong (int handle)
699 {
700 gsi_stop_song();
701 gsi_flush();
702 handle = 0;
703 }
704
705
I_ResumeSong(int handle)706 void I_ResumeSong (int handle)
707 {
708 gsi_continue_song();
709 gsi_flush();
710 handle = 0;
711 }
712
713
I_StopSong(int handle)714 void I_StopSong(int handle)
715 {
716 gsi_stop_song();
717 gsi_flush();
718
719 looping = 0;
720 musicdies = 0;
721 }
722
723
I_UnRegisterSong(int handle)724 void I_UnRegisterSong(int handle)
725 {
726 }
727
728
I_RegisterSong(void * data)729 int I_RegisterSong(void* data)
730 {
731 data = NULL;
732 return 1;
733 }
734
735
736 /* Is the song playing? */
I_QrySongPlaying(int handle)737 int I_QrySongPlaying(int handle)
738 {
739 handle = 0;
740 return looping || musicdies > gametic;
741 }
742