rocks_n_diamonds-0.9
[rocksndiamonds.git] / src / sound.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  ©1995 Artsoft Development                               *
5 *        Holger Schemel                                    *
6 *        33659 Bielefeld-Senne                             *
7 *        Telefon: (0521) 493245                            *
8 *        eMail: aeglos@valinor.owl.de                      *
9 *               aeglos@uni-paderborn.de                    *
10 *               q99492@pbhrzx.uni-paderborn.de             *
11 *----------------------------------------------------------*
12 *  sound.c                                                 *
13 *                                                          *
14 *  Letzte Aenderung: 15.06.1995                            *
15 ***********************************************************/
16
17 #include "sound.h"
18
19 /*** THE STUFF BELOW IS ONLY USED BY THE SOUND SERVER CHILD PROCESS ***/
20
21 static struct SoundControl playlist[MAX_SOUNDS_PLAYING];
22 static struct SoundControl emptySoundControl =
23 {
24   -1,0,0, FALSE,FALSE,FALSE,FALSE,FALSE, 0,0L,0L,NULL
25 };
26 static int stereo_volume[PSND_MAX_LEFT2RIGHT+1];
27 static char premix_first_buffer[SND_BLOCKSIZE];
28 static char premix_left_buffer[SND_BLOCKSIZE];
29 static char premix_right_buffer[SND_BLOCKSIZE];
30 static int premix_last_buffer[SND_BLOCKSIZE];
31 static unsigned char playing_buffer[SND_BLOCKSIZE];
32 static int playing_sounds = 0;
33
34 void SoundServer()
35 {
36   struct SoundControl snd_ctrl;
37   fd_set sound_fdset;
38   int i;
39
40   close(sound_pipe[1]);         /* no writing into pipe needed */
41
42   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
43     playlist[i] = emptySoundControl;
44
45   stereo_volume[PSND_MAX_LEFT2RIGHT] = 0;
46   for(i=0;i<PSND_MAX_LEFT2RIGHT;i++)
47     stereo_volume[i] =
48       (int)sqrt((float)(PSND_MAX_LEFT2RIGHT*PSND_MAX_LEFT2RIGHT-i*i));
49
50 #ifdef HPUX_AUDIO
51   HPUX_Audio_Control();
52 #endif
53
54   FD_ZERO(&sound_fdset); 
55   FD_SET(sound_pipe[0], &sound_fdset);
56
57   for(;;)       /* wait for calls from PlaySound(), StopSound(), ... */
58   {
59     FD_SET(sound_pipe[0], &sound_fdset);
60     select(sound_pipe[0]+1, &sound_fdset, NULL, NULL, NULL);
61     if (!FD_ISSET(sound_pipe[0], &sound_fdset))
62       continue;
63     if (read(sound_pipe[0], &snd_ctrl, sizeof(snd_ctrl)) != sizeof(snd_ctrl))
64     {
65       fprintf(stderr,"%s: broken pipe - no sounds\n",progname);
66       exit(0);
67     }
68
69 #ifdef VOXWARE
70
71     if (snd_ctrl.fade_sound)
72     {
73       if (!playing_sounds)
74         continue;
75
76       for(i=0;i<MAX_SOUNDS_PLAYING;i++)
77         if (snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr)
78           playlist[i].fade_sound = TRUE;
79     }
80     else if (snd_ctrl.stop_all_sounds)
81     {
82       if (!playing_sounds)
83         continue;
84
85       for(i=0;i<MAX_SOUNDS_PLAYING;i++)
86         playlist[i]=emptySoundControl;
87       playing_sounds=0;
88
89       close(sound_device);
90     }
91     else if (snd_ctrl.stop_sound)
92     {
93       if (!playing_sounds)
94         continue;
95
96       for(i=0;i<MAX_SOUNDS_PLAYING;i++)
97         if (playlist[i].nr == snd_ctrl.nr)
98         {
99           playlist[i]=emptySoundControl;
100           playing_sounds--;
101         }
102
103       if (!playing_sounds)
104         close(sound_device);
105     }
106
107     if (playing_sounds || snd_ctrl.active)
108     {
109       struct timeval delay = { 0, 0 };
110       char *sample_ptr;
111       long sample_size, max_sample_size;
112       long fragment_size;
113       BOOL stereo;
114
115       if (playing_sounds || (sound_device=open(sound_device_name,O_WRONLY))>=0)
116       {
117         if (!playing_sounds)    /* we just opened the audio device */
118         {
119           /* 2 buffers / 512 bytes, giving 1/16 second resolution */
120           /* (with stereo the effective buffer size will shrink to 256) */
121           fragment_size = 0x00020009;
122           ioctl(sound_device, SNDCTL_DSP_SETFRAGMENT, &fragment_size);
123           /* try if we can use stereo sound */
124           stereo = TRUE;
125           ioctl(sound_device, SNDCTL_DSP_STEREO, &stereo);
126           /* get the real fragmentation size; this should return 512 */
127           ioctl(sound_device, SNDCTL_DSP_GETBLKSIZE, &fragment_size);
128           max_sample_size = fragment_size / (stereo ? 2 : 1);
129         }
130
131         if (snd_ctrl.active)    /* new sound has arrived */
132           SoundServer_InsertNewSound(snd_ctrl);
133
134         while(playing_sounds &&
135               select(sound_pipe[0]+1,&sound_fdset,NULL,NULL,&delay)<1)
136         {       
137           FD_SET(sound_pipe[0], &sound_fdset);
138
139           /* first clear the last premixing buffer */
140           memset(premix_last_buffer,0,fragment_size*sizeof(int));
141
142           for(i=0;i<MAX_SOUNDS_PLAYING;i++)
143           {
144             int j;
145
146             if (!playlist[i].active)
147               continue;
148
149             /* get pointer and size of the actual sound sample */
150             sample_ptr = playlist[i].data_ptr+playlist[i].playingpos;
151             sample_size =
152               MIN(max_sample_size,playlist[i].data_len-playlist[i].playingpos);
153             playlist[i].playingpos += sample_size;
154
155             /* fill the first mixing buffer with original sample */
156             memcpy(premix_first_buffer,sample_ptr,sample_size);
157
158             /* are we about to restart a looping sound? */
159             if (playlist[i].loop && sample_size<max_sample_size)
160             {
161               playlist[i].playingpos = max_sample_size-sample_size;
162               memcpy(premix_first_buffer+sample_size,
163                      playlist[i].data_ptr,max_sample_size-sample_size);
164               sample_size = max_sample_size;
165             }
166
167             /* decrease volume if sound is fading out */
168             if (playlist[i].fade_sound &&
169                 playlist[i].volume>=PSND_MAX_VOLUME/10)
170               playlist[i].volume-=PSND_MAX_VOLUME/20;
171
172             /* adjust volume of actual sound sample */
173             if (playlist[i].volume != PSND_MAX_VOLUME)
174               for(j=0;j<sample_size;j++)
175                 premix_first_buffer[j] =
176                   (playlist[i].volume * (int)premix_first_buffer[j])
177                     >> PSND_MAX_VOLUME_BITS;
178
179             /* fill the last mixing buffer with stereo or mono sound */
180             if (stereo)
181             {
182               int middle_pos = PSND_MAX_LEFT2RIGHT/2;
183               int left_volume = stereo_volume[middle_pos+playlist[i].stereo];
184               int right_volume = stereo_volume[middle_pos-playlist[i].stereo];
185
186               for(j=0;j<sample_size;j++)
187               {
188                 premix_left_buffer[j] =
189                   (left_volume * (int)premix_first_buffer[j])
190                     >> PSND_MAX_LEFT2RIGHT_BITS;
191                 premix_right_buffer[j] =
192                   (right_volume * (int)premix_first_buffer[j])
193                     >> PSND_MAX_LEFT2RIGHT_BITS;
194                 premix_last_buffer[2*j+0] += premix_left_buffer[j];
195                 premix_last_buffer[2*j+1] += premix_right_buffer[j];
196               }
197             }
198             else
199             {
200               for(j=0;j<sample_size;j++)
201                 premix_last_buffer[j] += (int)premix_first_buffer[j];
202             }
203
204             /* delete completed sound entries from the playlist */
205             if (playlist[i].playingpos >= playlist[i].data_len)
206             {
207               if (playlist[i].loop)
208                 playlist[i].playingpos = 0;
209               else
210               {
211                 playlist[i] = emptySoundControl;
212                 playing_sounds--;
213               }
214             }
215             else if (playlist[i].volume <= PSND_MAX_VOLUME/10)
216             {
217               playlist[i] = emptySoundControl;
218               playing_sounds--;
219             }
220           }
221
222           /* put last mixing buffer to final playing buffer */
223           for(i=0;i<fragment_size;i++)
224           {
225             if (premix_last_buffer[i]<-255)
226               playing_buffer[i] = 0;
227             else if (premix_last_buffer[i]>255)
228               playing_buffer[i] = 255;
229             else
230               playing_buffer[i] = (premix_last_buffer[i]>>1)^0x80;
231           }
232
233           /* finally play the sound fragment */
234           write(sound_device,playing_buffer,fragment_size);
235         }
236
237         /* if no sounds playing, free device for other sound programs */
238         if (!playing_sounds)
239           close(sound_device);
240       }
241     }
242
243 #else   /* von '#ifdef VOXWARE' */
244
245     if (snd_ctrl.active && !snd_ctrl.loop)
246     {
247       struct timeval delay = { 0, 0 };
248       char *sample_ptr;
249       long sample_size, max_sample_size = SND_BLOCKSIZE;
250       long sample_rate = 8000;  /* standard "/dev/audio" sampling rate */
251       int wait_percent = 90;    /* wait 90% of the real playing time */
252       int i;
253
254       if ((sound_device=open(sound_device_name,O_WRONLY))>=0)
255       {
256         playing_sounds = 1;
257
258         while(playing_sounds &&
259               select(sound_pipe[0]+1,&sound_fdset,NULL,NULL,&delay)<1)
260         {       
261           FD_SET(sound_pipe[0], &sound_fdset);
262
263           /* get pointer and size of the actual sound sample */
264           sample_ptr = snd_ctrl.data_ptr+snd_ctrl.playingpos;
265           sample_size =
266             MIN(max_sample_size,snd_ctrl.data_len-snd_ctrl.playingpos);
267           snd_ctrl.playingpos += sample_size;
268
269           /* fill the first mixing buffer with original sample */
270           memcpy(premix_first_buffer,sample_ptr,sample_size);
271
272
273           /* adjust volume of actual sound sample */
274           if (snd_ctrl.volume != PSND_MAX_VOLUME)
275             for(i=0;i<sample_size;i++)
276               premix_first_buffer[i] =
277                 (snd_ctrl.volume * (int)premix_first_buffer[i])
278                   >> PSND_MAX_VOLUME_BITS;
279
280           for(i=0;i<sample_size;i++)
281             playing_buffer[i] =
282               linear_to_ulaw(((int)premix_first_buffer[i]) << 8);
283
284           if (snd_ctrl.playingpos >= snd_ctrl.data_len)
285             playing_sounds = 0;
286
287           /* finally play the sound fragment */
288           write(sound_device,playing_buffer,sample_size);
289
290           delay.tv_sec = 0;
291           delay.tv_usec = ((sample_size*10*wait_percent)/(sample_rate))*1000;
292         }
293         close(sound_device);
294       }
295     }
296
297 #endif  /* von '#ifdef VOXWARE' */
298
299   }
300 }
301
302 void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
303 {
304   int i,k;
305
306   /* wenn voll, ältesten Sound 'rauswerfen */
307   if (playing_sounds==MAX_SOUNDS_PLAYING)
308   {
309     int longest=0, longest_nr=0;
310
311     for(i=0;i<MAX_SOUNDS_PLAYING;i++)
312     {
313       int actual =
314         100 * playlist[i].playingpos / playlist[i].data_len;
315
316       if (!playlist[i].loop && actual>longest)
317       {
318         longest=actual;
319         longest_nr=i;
320       }
321     }
322     playlist[longest_nr] = emptySoundControl;
323     playing_sounds--;
324   }
325
326   /* nachsehen, ob (und ggf. wie oft) Sound bereits gespielt wird */
327   for(k=0,i=0;i<MAX_SOUNDS_PLAYING;i++)
328   {
329     if (playlist[i].nr == snd_ctrl.nr)
330       k++;
331   }
332
333   /* falls Sound-Loop: nur neu beginnen, wenn Sound gerade ausklingt */
334   if (k>=1 && snd_ctrl.loop)
335   {
336     for(i=0;i<MAX_SOUNDS_PLAYING;i++)
337     {
338       if (playlist[i].nr == snd_ctrl.nr && playlist[i].fade_sound)
339       {
340         playlist[i].fade_sound = FALSE;
341         playlist[i].volume = PSND_MAX_VOLUME;
342       }
343     }
344     return;
345   }
346
347   /* keinen Sound mehr als n mal gleichzeitig spielen (momentan n==2) */
348   if (k>=2)
349   {
350     int longest=0, longest_nr=0;
351
352     /* den bereits am längsten gespielten (gleichen) Sound suchen */
353     for(i=0;i<MAX_SOUNDS_PLAYING;i++)
354     {
355       int actual;
356
357       if (!playlist[i].active || playlist[i].nr != snd_ctrl.nr)
358         continue;
359
360       actual = 100 * playlist[i].playingpos / playlist[i].data_len;
361       if (actual>=longest)
362       {
363         longest=actual;
364         longest_nr=i;
365       }
366     }
367     playlist[longest_nr] = emptySoundControl;
368     playing_sounds--;
369   }
370
371   /* neuen Sound in Liste packen */
372   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
373   {
374     if (!playlist[i].active)
375     {
376       playlist[i] = snd_ctrl;
377       playing_sounds++;
378       break;
379     }
380   }
381 }
382
383 /*
384 void SoundServer_FadeSound(int nr)
385 {
386   int i;
387
388   if (!playing_sounds)
389     return;
390
391   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
392     if (snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr)
393       playlist[i].fade_sound = TRUE;
394 }
395 */
396
397 void SoundServer_StopSound(int nr)
398 {
399   int i;
400
401   if (!playing_sounds)
402     return;
403
404   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
405     if (playlist[i].nr == nr)
406     {
407       playlist[i] = emptySoundControl;
408       playing_sounds--;
409     }
410
411   if (!playing_sounds)
412     close(sound_device);
413 }
414
415 void SoundServer_StopAllSounds()
416 {
417   int i;
418
419   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
420     playlist[i]=emptySoundControl;
421   playing_sounds=0;
422
423   close(sound_device);
424 }
425
426 #ifdef HPUX_AUDIO
427 void HPUX_Audio_Control()
428 {
429   struct audio_describe ainfo;
430   int audio_ctl;
431
432   audio_ctl = open("/dev/audioCtl", O_WRONLY | O_NDELAY);
433   if (audio_ctl == -1)
434   {
435     fprintf(stderr,"%s: cannot open /dev/audioCtl - no sounds\n",progname);
436     exit(0);
437   }
438
439   if (ioctl(audio_ctl, AUDIO_DESCRIBE, &ainfo) == -1)
440   {
441     fprintf(stderr,"%s: no audio info - no sounds\n",progname);
442     exit(0);
443   }
444
445   if (ioctl(audio_ctl, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_ULAW) == -1)
446   {
447     fprintf(stderr,"%s: ulaw audio not available - no sounds\n",progname);
448     exit(0);
449   }
450
451   ioctl(audio_ctl, AUDIO_SET_CHANNELS, 1);
452   ioctl(audio_ctl, AUDIO_SET_SAMPLE_RATE, 8000);
453
454   close(audio_ctl);
455 }
456 #endif /* HPUX_AUDIO */
457
458 /* these two are stolen from "sox"... :) */
459
460 /*
461 ** This routine converts from linear to ulaw.
462 **
463 ** Craig Reese: IDA/Supercomputing Research Center
464 ** Joe Campbell: Department of Defense
465 ** 29 September 1989
466 **
467 ** References:
468 ** 1) CCITT Recommendation G.711  (very difficult to follow)
469 ** 2) "A New Digital Technique for Implementation of Any
470 **     Continuous PCM Companding Law," Villeret, Michel,
471 **     et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
472 **     1973, pg. 11.12-11.17
473 ** 3) MIL-STD-188-113,"Interoperability and Performance Standards
474 **     for Analog-to_Digital Conversion Techniques,"
475 **     17 February 1987
476 **
477 ** Input: Signed 16 bit linear sample
478 ** Output: 8 bit ulaw sample
479 */
480
481 #define ZEROTRAP    /* turn on the trap as per the MIL-STD */
482 #define BIAS 0x84   /* define the add-in bias for 16 bit samples */
483 #define CLIP 32635
484
485 unsigned char linear_to_ulaw(int sample)
486 {
487   static int exp_lut[256] =
488   {
489     0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
490     4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
491     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
492     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
493     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
494     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
495     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
496     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
497     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
498     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
499     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
500     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
501     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
502     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
503     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
504     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
505   };
506
507   int sign, exponent, mantissa;
508   unsigned char ulawbyte;
509
510   /* Get the sample into sign-magnitude. */
511   sign = (sample >> 8) & 0x80;          /* set aside the sign */
512   if (sign != 0)
513     sample = -sample;                   /* get magnitude */
514   if (sample > CLIP)
515     sample = CLIP;                      /* clip the magnitude */
516
517   /* Convert from 16 bit linear to ulaw. */
518   sample = sample + BIAS;
519   exponent = exp_lut[( sample >> 7 ) & 0xFF];
520   mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
521   ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
522 #ifdef ZEROTRAP
523   if (ulawbyte == 0)
524     ulawbyte = 0x02;                    /* optional CCITT trap */
525 #endif
526
527   return(ulawbyte);
528 }
529
530 /*
531 ** This routine converts from ulaw to 16 bit linear.
532 **
533 ** Craig Reese: IDA/Supercomputing Research Center
534 ** 29 September 1989
535 **
536 ** References:
537 ** 1) CCITT Recommendation G.711  (very difficult to follow)
538 ** 2) MIL-STD-188-113,"Interoperability and Performance Standards
539 **     for Analog-to_Digital Conversion Techniques,"
540 **     17 February 1987
541 **
542 ** Input: 8 bit ulaw sample
543 ** Output: signed 16 bit linear sample
544 */
545
546 int ulaw_to_linear(unsigned char ulawbyte)
547 {
548   static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
549   int sign, exponent, mantissa, sample;
550
551   ulawbyte = ~ ulawbyte;
552   sign = ( ulawbyte & 0x80 );
553   exponent = ( ulawbyte >> 4 ) & 0x07;
554   mantissa = ulawbyte & 0x0F;
555   sample = exp_lut[exponent] + ( mantissa << ( exponent + 3 ) );
556   if (sign != 0)
557     sample = -sample;
558
559   return(sample);
560 }
561
562 /*** THE STUFF ABOVE IS ONLY USED BY THE SOUND SERVER CHILD PROCESS ***/
563
564 /*===========================================================================*/
565
566 /*** THE STUFF BELOW IS ONLY USED BY THE MAIN PROCESS ***/
567
568 BOOL LoadSound(struct SoundInfo *snd_info)
569 {
570   FILE *file;
571   char filename[256];
572   char *sound_ext = "8svx";
573   struct SoundHeader_8SVX *snd_hdr;
574   unsigned char *ptr;
575
576   sprintf(filename,"%s/%s.%s",SND_PATH,snd_info->name,sound_ext);
577
578   if (!(file=fopen(filename,"r")))
579   {
580     fprintf(stderr,"%s: cannot open sound file '%s' - no sounds\n",
581             progname,filename);
582     return(FALSE);
583   }
584
585   if (fseek(file,0,SEEK_END)<0)
586   {
587     fprintf(stderr,"%s: cannot read sound file '%s' - no sounds\n",
588             progname,filename);
589     fclose(file);
590     return(FALSE);
591   }
592
593   snd_info->file_len = ftell(file);
594   rewind(file);
595
596   if (!(snd_info->file_ptr=malloc(snd_info->file_len)))
597   {
598     fprintf(stderr,"%s: out of memory (this shouldn't happen :) - no sounds\n",
599             progname);
600     fclose(file);
601     return(FALSE);
602   }
603
604   if (fread(snd_info->file_ptr,1,snd_info->file_len,file)!=snd_info->file_len)
605   {
606     fprintf(stderr,"%s: cannot read sound file '%s' - no sounds\n",
607             progname,filename);
608     fclose(file);
609     return(FALSE);
610   }
611
612   fclose(file);
613
614   snd_hdr = (struct SoundHeader_8SVX *)snd_info->file_ptr;
615
616   if (strncmp(snd_hdr->magic_FORM,"FORM",4) ||
617       snd_info->file_len!=be2long(&snd_hdr->chunk_size)+8 ||
618       strncmp(snd_hdr->magic_8SVX,"8SVX",4))
619   {
620     fprintf(stderr,"%s: '%s' is not an IFF/8SVX file or broken- no sounds\n",
621             progname,filename);
622     return(FALSE);
623   }
624
625   ptr = (unsigned char *)snd_info->file_ptr;
626
627   while(ptr<(unsigned char *)snd_info->file_ptr+snd_info->file_len)
628   {
629     if (!strncmp(ptr,"VHDR",4))
630     {
631       ptr+=be2long((unsigned long *)(ptr+4));
632     }
633     if (!strncmp(ptr,"ANNO",4))
634     {
635       ptr+=be2long((unsigned long *)(ptr+4));
636     }
637     if (!strncmp(ptr,"CHAN",4))
638     {
639       ptr+=be2long((unsigned long *)(ptr+4));
640     }
641     if (!strncmp(ptr,"BODY",4))
642     {
643       snd_info->data_ptr = ptr+8;
644       snd_info->data_len = be2long((unsigned long *)(ptr+4));
645       return(TRUE);
646     }
647     ptr++;
648   }
649
650   return(FALSE);
651 }
652
653 void PlaySound(int nr)
654 {
655   PlaySoundExt(nr, PSND_MAX_VOLUME, PSND_MIDDLE, PSND_NO_LOOP);
656 }
657
658 void PlaySoundStereo(int nr, int stereo)
659 {
660   PlaySoundExt(nr, PSND_MAX_VOLUME, stereo, PSND_NO_LOOP);
661 }
662
663 void PlaySoundLoop(int nr)
664 {
665   PlaySoundExt(nr, PSND_MAX_VOLUME, PSND_MIDDLE, PSND_LOOP);
666 }
667
668 void PlaySoundExt(int nr, int volume, int stereo, BOOL loop)
669 {
670   struct SoundControl snd_ctrl = emptySoundControl;
671
672   if (sound_status==SOUND_OFF || !sound_on)
673     return;
674
675   if (volume<PSND_MIN_VOLUME)
676     volume = PSND_MIN_VOLUME;
677   else if (volume>PSND_MAX_VOLUME)
678     volume = PSND_MAX_VOLUME;
679
680   if (stereo<PSND_MAX_LEFT)
681     stereo = PSND_MAX_LEFT;
682   else if (stereo>PSND_MAX_RIGHT)
683     stereo = PSND_MAX_RIGHT;
684
685   snd_ctrl.nr           = nr;
686   snd_ctrl.volume       = volume;
687   snd_ctrl.stereo       = stereo;
688   snd_ctrl.loop         = loop;
689   snd_ctrl.active       = TRUE;
690   snd_ctrl.data_ptr     = Sound[nr].data_ptr;
691   snd_ctrl.data_len     = Sound[nr].data_len;
692
693   if (write(sound_pipe[1], &snd_ctrl, sizeof(snd_ctrl))<0)
694   {
695     fprintf(stderr,"%s: cannot pipe to child process - no sounds\n",progname);
696     sound_status=SOUND_OFF;
697     return;
698   }
699 }
700
701 void FadeSound(int nr)
702 {
703   StopSoundExt(nr, SSND_FADE_SOUND);
704 }
705
706 void FadeSounds()
707 {
708   StopSoundExt(-1, SSND_FADE_ALL_SOUNDS);
709 }
710
711 void StopSound(int nr)
712 {
713   StopSoundExt(nr, SSND_STOP_SOUND);
714 }
715
716 void StopSounds()
717 {
718   StopSoundExt(-1, SSND_STOP_ALL_SOUNDS);
719 }
720
721 void StopSoundExt(int nr, int method)
722 {
723   struct SoundControl snd_ctrl = emptySoundControl;
724
725   if (sound_status==SOUND_OFF)
726     return;
727
728   if (SSND_FADING(method))
729     snd_ctrl.fade_sound = TRUE;
730
731   if (SSND_ALL(method))
732     snd_ctrl.stop_all_sounds = TRUE;
733   else
734   {
735     snd_ctrl.nr = nr;
736     snd_ctrl.stop_sound = TRUE;
737   }
738
739   if (write(sound_pipe[1], &snd_ctrl, sizeof(snd_ctrl))<0)
740   {
741     fprintf(stderr,"%s: cannot pipe to child process - no sounds\n",progname);
742     sound_status=SOUND_OFF;
743     return;
744   }
745 }
746
747 void FreeSounds(int max)
748 {
749   int i;
750
751   if (sound_status==SOUND_OFF)
752     return;
753
754   for(i=0;i<max;i++)
755     free(Sound[i].file_ptr);
756 }
757
758 /*** THE STUFF ABOVE IS ONLY USED BY THE MAIN PROCESS ***/