1 /*
2  * koules.sndsrv.c - VoxWare(tm) Compatible Sound - Apr. 1995
3  *                 PC Speaker  Compatible Sound
4  *                 This server is FreeBSD Specific.
5  *
6  * Copyright 1994-1995 Sujal M. Patel (smpatel@wam.umd.edu)
7  * Conditions in "copyright.h"
8  *
9  * Modified for Koules and bugfixed by Jan Hubicka
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <sys/ioctl.h>
17 #include <sys/soundcard.h>
18 #include <sys/time.h>
19 #include <signal.h>
20 #include <string.h>
21 
22 
23 
24 char           *FILENAME[] =
25 {
26   "/start.raw",
27   "/end.raw",
28   "/colize.raw",
29   "/destroy1.raw",
30   "/destroy2.raw",
31   "/creator1.raw",
32   "/creator2.raw"
33 
34 };
35 
36 #define NUM_SOUNDS	(sizeof(FILENAME)/sizeof(char*))
37 
38 signed char    *sound_buffer[NUM_SOUNDS];
39 int             sound_size[NUM_SOUNDS];
40 int             fragsize;
41 
42 
43 /* Terminate: Signal Handler */
44 void
quit()45 quit ()
46 {
47   exit (0);
48 }
49 
50 
51 
52 void
init(int argc,char ** argv)53 init (int argc, char **argv)
54 {
55   int             i;
56   char            s[1024];
57 
58   if (argc != 3)
59     {
60       printf ("This program is only executed by koules\n");
61       exit (1);
62     }
63 
64   for (i = 0; i < NUM_SOUNDS; i++)
65     {
66       s[0] = 0;
67       strcat (s, argv[1]);
68       if (s[(int) strlen (s) - 1] == '/')
69 	FILENAME[i]++;
70       strcat (s, FILENAME[i]);
71       FILENAME[i] = malloc ((int) strlen (s) + 1);
72       strcpy (FILENAME[i], s);
73       sound_buffer[i] = NULL;
74       sound_size[i] = 0;
75     }
76 
77   signal (SIGTERM, quit);	/* Setup Terminate Signal Handler */
78 }
79 
80 
81 /*
82    Setup DSP: Opens /dev/dsp or /dev/pcdsp
83    Sets fragment size on VoxWare
84    Sets speed to 8000hz
85    Should set mono mode
86    Error checking
87  */
88 int
setup_dsp(char * dspdev,int * is_pcsp)89 setup_dsp (char *dspdev, int *is_pcsp)
90 {
91   int             dsp, frag, value;
92   int             mixer;
93 
94   dsp = open (dspdev, O_RDWR);
95   if (dsp < 1)
96     {
97       fprintf (stderr, "koules.sndsrv: Couldn't open DSP %s\n", dspdev);
98       return -1;
99     }
100 
101   *is_pcsp = 0;
102   fragsize = 0;
103 
104   frag = 0x00020009;		/* try 512 bytes, for 1/16 second frag size */
105   ioctl (dsp, SNDCTL_DSP_SETFRAGMENT, &frag);
106   value = 8010;
107   if (ioctl (dsp, SNDCTL_DSP_SPEED, &value))
108     {
109       fprintf (stderr, "koules.sndsrv: Couldn't set DSP rate!\n");
110     };
111   value = 0;
112   ioctl (dsp, SNDCTL_DSP_STEREO, &value);
113   ioctl (dsp, SNDCTL_DSP_GETBLKSIZE, &fragsize);
114   fprintf (stderr, "koules.sndsrv: fragment set to %d\n", fragsize);
115 
116   if (!fragsize)
117     {
118       /* Don't Assume just because you can't set the fragment, use proper IOCTL */
119       fprintf (stderr, "koules.sndsrv: Couldn't set Fragment Size.\nAssuming PC Speaker!\n");
120       fragsize = 128;
121       *is_pcsp = 1;
122     }
123   else
124     {
125       mixer = open ("/dev/mixer", O_RDWR | O_NONBLOCK);
126       if (mixer == -1)
127 	{
128 	  fprintf (stderr, "koules.sndsrv: Couldn't open mixer %s\n", "/dev/mixer");
129 	  return (-1);
130 	};
131 #if 0
132       value = 0x6464;
133       ioctl (mixer, SOUND_MIXER_WRITE_PCM, &value);
134       ioctl (mixer, SOUND_MIXER_WRITE_VOLUME, &value);	/*what does this do? */
135 #endif
136       close (mixer);
137 
138     }
139 
140   return dsp;
141 }
142 
143 /*
144    This just keeps the pipe from breaking...
145    Eventually I'll look at the koules signal handlers and
146    just trap this.
147  */
148 int
do_nothing(void)149 do_nothing (void)
150 {
151   fprintf (stderr, "koules.sndsrv: doing nothing, something is broken\n");
152   while (1)
153     sleep (5);
154 }
155 
156 int
read_sound(int k)157 read_sound (int k)
158 {
159   int             i, fd, size;
160 
161   /*fprintf(stderr,"loading sound %d, %s\n",k,FILENAME[k]); */
162 
163   fd = open (FILENAME[k], O_RDONLY);
164   if (fd <= 0)
165     {
166       fprintf (stderr, "koules.sndsrv: The sound %s could not be opened\n", FILENAME[k]);
167       sound_size[k] = -1;
168       return (0);
169     };
170   size = lseek (fd, 0, SEEK_END);
171   sound_size[k] = (size / fragsize) + 1;	/*size in fragments */
172   sound_buffer[k] = malloc (sound_size[k] * fragsize);
173   if (sound_buffer[k] == NULL)
174     {
175       fprintf (stderr, "koules.sndsrv: couldn't malloc memory for sound\n");
176       sound_size[k] = -1;
177       close (fd);
178       return (0);
179     };
180   lseek (fd, 0, SEEK_SET);
181   read (fd, sound_buffer[k], size);
182   close (fd);
183   for (i = 0; i < size; i++)
184     sound_buffer[k][i] ^= 0x80;
185   memset (sound_buffer[k] + size, 0, sound_size[k] * fragsize - size);
186 
187   /*fprintf(stderr,"sound has been loaded, %d bytes\n",size); *//*DEBUG */
188   return (1);
189 }
190 
191 
192 void
do_everything(int dsp,int is_pcsp)193 do_everything (int dsp, int is_pcsp)
194 {
195   char            k;
196   int             i, j;
197   int             terminate = -1;	/* Which Sound to Terminate                              */
198   int             playing[16];	/* Sound numbers that we are playing                     */
199   int             position[16];	/* Current position in each sound file */
200   int             playnum = 0;	/* Number of sounds currently being played               */
201   unsigned char   final[512];	/* Final Mixing Buffer                                   */
202   int             premix[512];
203   char           *sample;
204 
205   for (;;)
206     {
207       terminate = -1;
208       /* Try to open a new sound if we get an integer on the 'in' pipe */
209       i = read (STDIN_FILENO, &k, sizeof (k));
210       if (i == 0)
211 	{			/* EOF on pipe means parent has closed its end */
212 	  /*fprintf(stderr,"koules.sndsrv: shutting down\n"); */
213 	  kill (getpid (), SIGTERM);
214 	};
215       if (i != -1)
216 	{			/* there was something in the pipe */
217 	  /*fprintf(stderr,"Just read a %d from pipe\n",(int)k); *//*DEBUG */
218 	  /* Negative means terminate the FIRST sound in the buffer */
219 	  if (k < 0)
220 	    {
221 	      /*fprintf(stderr,"terminating sound\n"); *//*DEBUG */
222 	      terminate = 0;
223 	    }
224 	  else
225 	    {
226 	      if (sound_size[k] == 0)
227 		read_sound (k);
228 	      if (sound_size[k] > 0 && playnum < 16)
229 		{
230 		  position[playnum] = 0;
231 		  playing[playnum++] = k;
232 		  /*fprintf(stderr,"sound %d added to play queue\n",playnum-1); *//*DEBUG */
233 		};
234 	    };
235 	};
236 
237       /* terminate a sound if necessary */
238       for (i = 0; i < playnum; i++)
239 	{
240 	  if ((position[i] == sound_size[playing[i]]) || (terminate == i))
241 	    {
242 	      /*fprintf(stderr,"finished playing sound %d\n",i); *//*DEBUG */
243 	      /*fprintf(stderr,"is was at position %d\n",position[i]); *//*DEBUG */
244 	      memmove (playing + i, playing + i + 1, (playnum - i) * sizeof (int));
245 	      memmove (position + i, position + i + 1, (playnum - i) * sizeof (int));
246 	      playnum--;
247 	      i--;
248 	    };
249 	};
250 
251       if (playnum)
252 	{
253 	  /* Mix each sound into the final buffer */
254 	  memset (premix, 0, sizeof (premix));
255 	  for (i = 0; i < playnum; i++)
256 	    {
257 	      sample = sound_buffer[playing[i]] + position[i] * fragsize;
258 	      for (j = 0; j < fragsize; j++)
259 		{
260 		  premix[j] += *(sample + j);
261 		};
262 	      position[i]++;
263 	    };
264 	  for (i = 0; i < fragsize; i++)
265 	    final[i] = (premix[i] > 255) ? 255 : (premix[i] < -256 ? 0 : (premix[i] >> 1) + 128);
266 	}
267       else
268 	{
269 	  /*
270 	     We have no sounds to play
271 	     Just fill the buffer with silence and maybe play it
272 	   */
273 	  memset (final, 128, sizeof (final));
274 	};
275       write (dsp, final, fragsize);
276       /*
277          The sound server is in a tight loop, EXCEPT for this
278          write which blocks.  Any optimizations in the above
279          code would really be helpful.  Right now the server
280          takes up to 7% cpu on a 486DX/50.
281        */
282     }
283 }
284 
285 
286 
287 void
main(argc,argv)288 main (argc, argv)
289      int             argc;
290      char          **argv;
291 {
292   int             dsp, is_pcsp, ppid;
293   char            filename[512];
294 
295   fcntl (STDIN_FILENO, F_SETFL, O_NONBLOCK);
296   init (argc, argv);
297   dsp = setup_dsp (argv[2], &is_pcsp);
298 
299   if (!dsp)
300     do_nothing ();
301 
302   do_everything (dsp, is_pcsp);
303 }
304