1 #ifdef LINUXAUDIO
2 /* SoundIt library 0.021
3 
4    Copyright 1994 Brad Pitzel  pitzel@cs.sfu.ca
5 
6    Feel free to use/distribute/modify as long as proper credits
7    are included.
8 */
9 
10 #include "soundIt.h"
11 #include <stdlib.h>
12 #include <limits.h>	/* PATH_MAX */
13 #include <stdio.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/time.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <sys/soundcard.h>
21 #include <sys/ioctl.h>
22 #include <sys/wait.h>
23 
24 /*==========================================================================*/
25 /* the mix buff, where the channels are mixed into. The mix buffer is then
26    dumped to the sound device (/dev/dsp). Samples are mixed in
27    Vunclipbuf (buffer of ints), then the values in Vunclipbuf are clipped to
28    values between 0 and 255, and stored into Vclippedbuf (buffer of unsigned
29    chars).
30 */
31 
32 struct Mix
33     {
34     unsigned char *Vclippedbuf;
35     int *Vunclipbuf;
36     int Vsize;
37     };
38 typedef struct Mix Mix;
39 
40 /*==========================================================================*/
41 struct Channel
42     {
43     unsigned char *Vstart,*Vcurrent;	/* ptr's into a playing sample */
44     int	Vlen;				/* length of sample in bytes */
45     int	Vleft;				/* bytes left of sample to play */
46     int Snum;
47     };
48 typedef struct Channel Channel;
49 
50 /*==========================================================================*/
51 
52 /* variables prefixed with S_ are static */
53 /* 0 if mixer isn't initialized or init failed, 1 if mixer is good */
54 static int sampleMixerStatus = 0;
55 
56 static const Sample 	*S_sounds = NULL; /* ptr to array of samples */
57 static int S_num_sounds = 0;		/* size of 'sounds' array above */
58 static int S_fd_snddev = -1;		/* file # for sound device once open */
59 static int S_fd_pipe[2] = { -1, -1 };	/* pipe to talk to child process */
60 static int S_son_pid = -1;		/* process ID for the forked sound mixer */
61 static const char *S_snddev = NULL;	/* char string for device, ie "/dev/dsp" */
62 static int S_num_channels = 6;		/* number of channels to mix */
63 static int S_playback_freq = 0;		/* playback frequency (in Hz) */
64 /*==========================================================================*/
65 /* non-public functions, used only within this file*/
66 
67 int Snd_init_dev();
68 int Snd_restore_dev();
69 
70 void Chan_reset( Channel *chan );	/* init channel structure */
71 
72 	/* start a sample playing on a channel */
73 void Chan_assign( Channel *chan, const Sample *snd, int sound_num );
74 
75 	/* mix all channels together into the 'mix' structure */
76 int  Chan_mixAll( Mix *mix, Channel *ch );
77 
78 	/* used by Chan_mixAll to mix the 1st channel */
79 int  Chan_copyIn( Channel *chan, Mix *mix );
80 
81 	/* used by Chan_mixAll to mix the middle channels */
82 int  Chan_mixIn( Channel *chan, Mix *mix );
83 
84 	/* used by Chan_mixAll to mix the last channel */
85 int  Chan_finalMixIn( Channel *chan, Mix *mix );
86 
87 void Chan_resetSound( Channel *chan, int nr );
88 
89 /* alloc mem for mix buffer, and deallocate function */
90 /* The sound channels are mixed together into the mix buffer   */
91 /* then the mix buffer data is sent directly to the sound device */
92 void Mix_alloc( Mix *mix, int size );
93 void Mix_dealloc( Mix *mix );
94 
95 /*==========================================================================*/
96 /* justing for testing, normally not called */
dump_snd_list()97 void dump_snd_list()
98 	{
99 	int i=0;
100 
101 	for(i=0; i<S_num_sounds; i++)
102 		{
103 		printf("snd %d: len = %d \n", i, S_sounds[i].len );
104 		}
105 	}
106 
107 /*==========================================================================*/
Snd_init(int num_snd,const Sample * sa,int frequency,int channels,const char * dev)108 int Snd_init( int num_snd, const Sample *sa, int frequency,
109               int channels, const char *dev )
110 	{
111 	int result;
112 
113 	S_num_sounds     = num_snd;
114 	S_sounds         = sa;	/* array of sound samples*/
115 	S_playback_freq  = frequency;
116 	S_num_channels   = channels;
117 	S_snddev= dev;	/* sound device, eg /dev/dsp*/
118 
119 	if (S_sounds==NULL)
120 		return EXIT_FAILURE;
121 
122 	result=Snd_init_dev();
123 
124 	if (result==EXIT_SUCCESS)
125 		{
126 		sampleMixerStatus=1;
127 		}
128 	else
129 		{
130 		sampleMixerStatus=0;
131 		}
132 
133 	return result;
134 	}
135 
136 /*==========================================================================*/
Snd_restore()137 int Snd_restore()
138 	{
139 	int result;
140 
141 	if (!sampleMixerStatus)
142 		return EXIT_FAILURE;
143 
144 	result=Snd_restore_dev();
145 
146 	if (result==EXIT_SUCCESS)
147 		{
148 		sampleMixerStatus=0;
149 		}
150 	else
151 		{
152 		sampleMixerStatus=0;
153 		}
154 
155 	return result;
156 	}
157 
158 /*==========================================================================*/
159 /* volume control not implemented yet.*/
Snd_effect(int sound_num,int channel)160 int Snd_effect( int sound_num, int channel )
161 	{
162 	if(! sampleMixerStatus )
163 		return EXIT_FAILURE;
164 
165 	if(S_sounds[sound_num].data != NULL)
166 		{
167 		write(S_fd_pipe[1], &sound_num, sizeof(sound_num));
168 		write(S_fd_pipe[1], &channel, sizeof(channel));
169 		}
170 	else
171 		fprintf(stderr,"Referencing NULL sound entry\n");
172 
173 	return EXIT_SUCCESS;
174 	}
175 
176 /*============================================================================*/
Snd_init_dev()177 int Snd_init_dev()
178 	{
179 	int whoami;
180 	S_fd_snddev = -1;
181 
182 	S_son_pid = 0;
183 
184 
185 	if(access(S_snddev,W_OK) != 0)
186 		{
187 		perror("No access to sound device");
188 		return EXIT_FAILURE;
189 		}
190 
191 	S_fd_snddev = open(S_snddev,O_WRONLY);
192 
193 	if(S_fd_snddev < 0)
194 		{
195 		fprintf(stderr,"int_snddev: Cannot open sound device \n");
196 		return EXIT_FAILURE;
197 		}
198 
199 	close(S_fd_snddev);
200 
201 	if(pipe(S_fd_pipe) < 0)
202 		{
203 		fprintf(stderr,"Cannot create pipe for sound control \n");
204 		return EXIT_FAILURE;
205 		}
206 
207 	/* now setup 2nd process for writing the data... */
208 	if((whoami = fork()) < 0)
209 		{
210 		fprintf(stderr,"Cannot fork sound driver\n");
211 		return EXIT_FAILURE;
212 		}
213 
214 	if(whoami != 0)	/* successfully created son */
215 		{
216 		close(S_fd_pipe[0]);	/* close end for reading */
217 		S_son_pid = whoami;
218 		return EXIT_SUCCESS;
219 		}
220 
221 		/* Here is the code for the son... */
222 		{
223 		int sound_num,ch,i;
224 		struct timeval tval = {0L,0L};
225 		fd_set readfds,dsp;
226 
227 		Mix mix;
228 
229 		int frag, fragsize;
230 
231 		Channel *chan = (Channel*)malloc( sizeof(Channel)*S_num_channels );
232 
233 		for (i=0; i<S_num_channels; i++)
234 			Chan_reset( chan+i );
235 
236 		S_fd_snddev = open(S_snddev,O_WRONLY );
237 		if(S_fd_snddev < 0)
238 			{
239 			perror("Cannot open sound device: ");
240 			exit(1);
241 			}
242 
243 		frag = FRAG_SPEC; /*defined in soundIt.h */
244 
245  		ioctl(S_fd_snddev, SNDCTL_DSP_SETFRAGMENT, &frag);
246 
247 		if ( ioctl(S_fd_snddev,SNDCTL_DSP_SPEED, &S_playback_freq)==-1 )
248 			perror("Sound driver ioctl ");
249 
250 		fragsize=0;
251 		if ( ioctl(S_fd_snddev,SNDCTL_DSP_GETBLKSIZE, &fragsize)==-1 )
252 			perror("Sound driver ioctl ");
253 
254 		/* printf("after: block size: %d \n",fragsize); */
255 
256 		/* init mixer object*/
257 		Mix_alloc( &mix, fragsize );
258 
259 		close(S_fd_pipe[1]);	/* close end for writing */
260 
261 		FD_ZERO(&dsp);
262 		FD_SET(S_fd_snddev, &dsp);
263 
264 		FD_ZERO(&readfds);
265 		FD_SET(S_fd_pipe[0], &readfds);
266 
267 		printf("Sound driver initialized.\n");
268 
269 		for(;;)
270 			{
271 			FD_SET(S_fd_pipe[0], &readfds);
272 			tval.tv_sec=0L;
273 			tval.tv_usec=0L;
274 			select(S_fd_pipe[0]+1, &readfds,NULL,NULL,&tval);
275 
276 			if (FD_ISSET(S_fd_pipe[0], &readfds))
277 				{
278 				if (read(S_fd_pipe[0], &sound_num, sizeof(int))==0)
279 					break;
280 
281 				read(S_fd_pipe[0], &ch, sizeof(int));
282 
283 				/* printf("chan=%d snd=%d len=%d \n", ch, sound_num, S_sounds[sound_num].len ); */
284 				/* Find free channel for sample */
285 				if(ch == -1) {
286 			printf("Reset %d\n", sound_num);
287 				    Chan_resetSound(chan, sound_num);
288 				} else
289 				    for(i = 0; i < S_num_channels; i++ ){
290 					if (chan[i].Vleft==0){
291 					    Chan_assign( &(chan[i]), &(S_sounds[sound_num]), sound_num );
292 					    break;
293 					}
294 				    }
295 
296 //				Chan_assign( &(chan[ch]), &(S_sounds[sound_num]), 0 );
297 				}
298 
299 			Chan_mixAll(&mix,chan);
300 			write(S_fd_snddev, mix.Vclippedbuf, fragsize );
301 			}
302 
303 		Mix_dealloc( &mix );
304 		printf("Sound process exiting..\n");
305 		close(S_fd_pipe[0]);
306 		close(S_fd_pipe[1]);
307 		exit (0);
308 		} /*end of child process */
309 	}
310 
311 
312 /*==========================================================================*/
Snd_restore_dev()313 int Snd_restore_dev()
314 	{
315 	close(S_fd_pipe[0]);
316 	close(S_fd_pipe[1]);
317 
318 	/* wait for child process to die*/
319 	wait(NULL);
320 	return EXIT_SUCCESS;
321 	}
322 
323 /*==========================================================================*/
324 /*   CHANNEL MIXING FUNCTIONS						    */
325 /*==========================================================================*/
Chan_reset(Channel * chan)326 void Chan_reset( Channel *chan )
327     {
328     chan->Vstart=NULL;
329     chan->Vcurrent=NULL;
330     chan->Vlen=0;
331     chan->Vleft=0;
332     }
333 
334 /*==========================================================================*/
Chan_assign(Channel * chan,const Sample * snd,int sound_num)335 void Chan_assign( Channel *chan, const Sample *snd, int sound_num )
336     {
337     chan->Vstart  = snd->data;
338     chan->Vcurrent= chan->Vstart;
339     chan->Vlen    = snd->len;
340     chan->Vleft   = snd->len;
341     chan->Snum	  = sound_num;
342     }
343 
344 /*==========================================================================*/
Chan_copyIn(Channel * chan,Mix * mix)345 int Chan_copyIn( Channel *chan, Mix *mix )
346     {
347     int    i,*p = mix->Vunclipbuf, result, min;
348 
349     result = (chan->Vleft>0) ? 1 : 0;
350     min = (chan->Vleft < mix->Vsize) ? chan->Vleft : mix->Vsize;
351 
352     for(i=0; i<min; i++)
353         {
354         *p++ = (int) *chan->Vcurrent++;
355         }
356     chan->Vleft -= i;
357 
358     /* fill the remaining (if any) part of the mix buffer with silence */
359     while (i<mix->Vsize)
360             {
361             *p++ = 128;
362             i++;
363             }
364     return result;
365     }
366 
367 /*==========================================================================*/
Chan_mixIn(Channel * chan,Mix * mix)368 int Chan_mixIn( Channel *chan, Mix *mix )
369     {
370     int    i,*p = mix->Vunclipbuf, result, min;
371 
372     result = (chan->Vleft>0) ? 1 : 0;
373     min = (chan->Vleft < mix->Vsize) ? chan->Vleft : mix->Vsize;
374 
375     for(i=0; i<min; i++)
376         {
377         *p++ += (int) (*chan->Vcurrent++) - 128;
378         }
379 
380     chan->Vleft -= i;
381     return result;
382     }
383 
384 /*========================================================================*/
385 /* clip an int to a value between 0 and 255 */
386 static inline
clip(int i)387 unsigned char clip(int i)
388     {
389     return (i<0) ? 0 : ( (i>255) ? 255 : i );
390     }
391 
392 /*==========================================================================*/
Chan_finalMixIn(Channel * chan,Mix * mix)393 int Chan_finalMixIn( Channel *chan, Mix *mix )
394     {
395     register int    i;
396     int   *p = mix->Vunclipbuf, result, min;
397     unsigned char *final = mix->Vclippedbuf;
398 
399     result = (chan->Vleft>0) ? 1 : 0;
400     min = (chan->Vleft < mix->Vsize) ? chan->Vleft : mix->Vsize;
401 
402     for(i=0; i<min; i++)
403         {
404         *p += (int) (*chan->Vcurrent++) - 128;
405         *final++ = clip(*p++);
406         }
407     chan->Vleft -= i;
408 
409     /* copy rest of Vunclipbuf over to Vclippedbuf */
410     while (i<mix->Vsize)
411             {
412             *final++ = clip(*p++);
413             i++;
414             }
415 
416     return result;
417     }
418 
419 
420 /*==========================================================================*/
Mix_alloc(Mix * mix,int size)421 void Mix_alloc(Mix *mix, int size)
422     {
423     mix->Vclippedbuf = (unsigned char *)calloc( sizeof(char), size);
424     mix->Vunclipbuf = (int *)calloc( sizeof(int), size);
425     mix->Vsize  = size;
426 
427     if ((mix->Vclippedbuf==NULL)||(mix->Vunclipbuf==NULL))
428     	{
429    	fprintf(stderr,"Unable to allocate memory for mixer buffer\n");
430     	exit(-1);
431     	}
432     }
433 
434 /*==========================================================================*/
Mix_dealloc(Mix * mix)435 void Mix_dealloc( Mix *mix)
436     {
437     if (mix->Vclippedbuf) free(mix->Vclippedbuf);
438     if (mix->Vunclipbuf) free(mix->Vunclipbuf);
439     }
440 
441 /*==========================================================================*/
442 /* Mixes together the channels into one sound.
443    Returns # of channels currently playing *any* sound
444    Therefore, return 0 means to channels have a sample, therefore no
445    sound is playing
446 */
Chan_mixAll(Mix * mix,Channel * chan)447 int Chan_mixAll( Mix *mix, Channel *chan )
448     {
449     int result  = 0,i=0;
450 
451     result  = Chan_copyIn( chan,  mix);
452 //printf("***************************\n");
453     /* we want to loop for S_num_channels-2 */
454     for(i=2;i<S_num_channels;i++){
455 //printf("Vlen = %d , Vleft = %d\n",chan->Vlen, chan->Vleft);
456     	result += Chan_mixIn( ++chan, mix);
457     }
458 //printf("Vlen = %d , Vleft = %d\n",chan->Vlen, chan->Vleft);
459     result += Chan_finalMixIn( ++chan, mix);
460 
461     return result;
462     }
463 
464 /*==========================================================================*/
465 /* Stop playing select sound in channels */
Chan_resetSound(Channel * chan,int nr)466 void Chan_resetSound( Channel *chan, int nr )
467 {
468     int i;
469     printf("kill here  ");
470     for(i = 0; i < S_num_channels; i++){
471 	if (chan->Snum == nr) { Chan_reset(chan); printf("KILLL OK!!!\n"); }
472 	chan++;
473     }
474 }
475 
476 /*==========================================================================*/
477 /* given the name of a .raw sound file, load it into the Sample struct */
478 /* pointed to by 'sample'                                              */
479 /* Returns -1 couldn't open/read file				       */
480 /*         -2 couldn't alloc memory)                                   */
481 int
Snd_loadRawSample(const char * file,Sample * sample)482 Snd_loadRawSample( const char *file, Sample *sample )
483    {
484    FILE *fp;
485 
486    sample->data = NULL;
487    sample->len  = 0;
488 
489    fp = fopen(file,"r");
490 
491    if (fp==NULL) return -1;
492 
493    /* get length of the file */
494    sample->len = lseek( fileno(fp), 0, SEEK_END );
495 
496    /* go back to beginning of file */
497    lseek( fileno(fp), 0, SEEK_SET );
498 
499    /* alloc memory for sample */
500    sample->data = (unsigned char *)malloc( sample->len );
501 
502    if (sample->data==NULL)
503    	{
504    	fclose(fp);
505    	return -2;
506    	}
507 
508    fread( sample->data, 1, sample->len, fp );
509 
510    fclose(fp);
511 
512    return 0;
513    }
514 #endif