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