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