rnd-19990201-1
[rocksndiamonds.git] / src / sound.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  sound.c                                                 *
12 ***********************************************************/
13
14 #include "sound.h"
15 #include "misc.h"
16
17 /*** THE STUFF BELOW IS ONLY USED BY THE SOUND SERVER CHILD PROCESS ***/
18
19 static int playing_sounds = 0;
20 static struct SoundControl playlist[MAX_SOUNDS_PLAYING];
21 static struct SoundControl emptySoundControl =
22 {
23   -1,0,0, FALSE,FALSE,FALSE,FALSE,FALSE, 0,0L,0L,NULL
24 };
25
26 #ifndef MSDOS
27 static int stereo_volume[PSND_MAX_LEFT2RIGHT+1];
28 static char premix_first_buffer[SND_BLOCKSIZE];
29 #ifdef VOXWARE
30 static char premix_left_buffer[SND_BLOCKSIZE];
31 static char premix_right_buffer[SND_BLOCKSIZE];
32 static int premix_last_buffer[SND_BLOCKSIZE];
33 #endif /* VOXWARE */
34 static unsigned char playing_buffer[SND_BLOCKSIZE];
35 #endif /* MSDOS */
36
37 /* forward declaration of internal functions */
38 #ifdef VOXWARE
39 static void SoundServer_InsertNewSound(struct SoundControl);
40 #endif
41 #ifndef VOXWARE
42 #ifndef MSDOS
43 static unsigned char linear_to_ulaw(int);
44 static int ulaw_to_linear(unsigned char);
45 #endif
46 #endif
47 #ifdef HPUX_AUDIO
48 static void HPUX_Audio_Control();
49 #endif
50 #ifdef MSDOS
51 static void SoundServer_InsertNewSound(struct SoundControl);
52 static void SoundServer_StopSound(int);
53 static void SoundServer_StopAllSounds();
54 #endif
55
56 void SoundServer()
57 {
58   int i;
59 #ifndef MSDOS
60   struct SoundControl snd_ctrl;
61   fd_set sound_fdset;
62
63   close(sound_pipe[1]);         /* no writing into pipe needed */
64 #endif
65
66   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
67     playlist[i] = emptySoundControl;
68   playing_sounds = 0;
69
70 #ifndef MSDOS
71   stereo_volume[PSND_MAX_LEFT2RIGHT] = 0;
72   for(i=0;i<PSND_MAX_LEFT2RIGHT;i++)
73     stereo_volume[i] =
74       (int)sqrt((float)(PSND_MAX_LEFT2RIGHT*PSND_MAX_LEFT2RIGHT-i*i));
75
76 #ifdef HPUX_AUDIO
77   HPUX_Audio_Control();
78 #endif
79
80   FD_ZERO(&sound_fdset); 
81   FD_SET(sound_pipe[0], &sound_fdset);
82
83   while(1)      /* wait for sound playing commands from client */
84   {
85     FD_SET(sound_pipe[0], &sound_fdset);
86     select(sound_pipe[0]+1, &sound_fdset, NULL, NULL, NULL);
87     if (!FD_ISSET(sound_pipe[0], &sound_fdset))
88       continue;
89     if (read(sound_pipe[0], &snd_ctrl, sizeof(snd_ctrl)) != sizeof(snd_ctrl))
90       Error(ERR_EXIT_SOUND_SERVER, "broken pipe - no sounds");
91
92 #ifdef VOXWARE
93
94     if (snd_ctrl.fade_sound)
95     {
96       if (!playing_sounds)
97         continue;
98
99       for(i=0;i<MAX_SOUNDS_PLAYING;i++)
100         if (snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr)
101           playlist[i].fade_sound = TRUE;
102     }
103     else if (snd_ctrl.stop_all_sounds)
104     {
105       if (!playing_sounds)
106         continue;
107
108       for(i=0;i<MAX_SOUNDS_PLAYING;i++)
109         playlist[i]=emptySoundControl;
110       playing_sounds=0;
111
112       close(sound_device);
113     }
114     else if (snd_ctrl.stop_sound)
115     {
116       if (!playing_sounds)
117         continue;
118
119       for(i=0;i<MAX_SOUNDS_PLAYING;i++)
120         if (playlist[i].nr == snd_ctrl.nr)
121         {
122           playlist[i]=emptySoundControl;
123           playing_sounds--;
124         }
125
126       if (!playing_sounds)
127         close(sound_device);
128     }
129
130     if (playing_sounds || snd_ctrl.active)
131     {
132       struct timeval delay = { 0, 0 };
133       byte *sample_ptr;
134       long sample_size;
135       static long max_sample_size = 0;
136       static long fragment_size = 0;
137       boolean stereo;
138
139       if (playing_sounds || (sound_device=open(sound_device_name,O_WRONLY))>=0)
140       {
141         if (!playing_sounds)    /* we just opened the audio device */
142         {
143           /* 2 buffers / 512 bytes, giving 1/16 second resolution */
144           /* (with stereo the effective buffer size will shrink to 256) */
145           fragment_size = 0x00020009;
146           ioctl(sound_device, SNDCTL_DSP_SETFRAGMENT, &fragment_size);
147           /* try if we can use stereo sound */
148           stereo = TRUE;
149           ioctl(sound_device, SNDCTL_DSP_STEREO, &stereo);
150           /* get the real fragmentation size; this should return 512 */
151           ioctl(sound_device, SNDCTL_DSP_GETBLKSIZE, &fragment_size);
152           max_sample_size = fragment_size / (stereo ? 2 : 1);
153         }
154
155         if (snd_ctrl.active)    /* new sound has arrived */
156           SoundServer_InsertNewSound(snd_ctrl);
157
158         while(playing_sounds &&
159               select(sound_pipe[0]+1,&sound_fdset,NULL,NULL,&delay)<1)
160         {       
161           FD_SET(sound_pipe[0], &sound_fdset);
162
163           /* first clear the last premixing buffer */
164           memset(premix_last_buffer,0,fragment_size*sizeof(int));
165
166           for(i=0;i<MAX_SOUNDS_PLAYING;i++)
167           {
168             int j;
169
170             if (!playlist[i].active)
171               continue;
172
173             /* get pointer and size of the actual sound sample */
174             sample_ptr = playlist[i].data_ptr+playlist[i].playingpos;
175             sample_size =
176               MIN(max_sample_size,playlist[i].data_len-playlist[i].playingpos);
177             playlist[i].playingpos += sample_size;
178
179             /* fill the first mixing buffer with original sample */
180             memcpy(premix_first_buffer,sample_ptr,sample_size);
181
182             /* are we about to restart a looping sound? */
183             if (playlist[i].loop && sample_size<max_sample_size)
184             {
185               playlist[i].playingpos = max_sample_size-sample_size;
186               memcpy(premix_first_buffer+sample_size,
187                      playlist[i].data_ptr,max_sample_size-sample_size);
188               sample_size = max_sample_size;
189             }
190
191             /* decrease volume if sound is fading out */
192             if (playlist[i].fade_sound &&
193                 playlist[i].volume>=PSND_MAX_VOLUME/10)
194               playlist[i].volume-=PSND_MAX_VOLUME/20;
195
196             /* adjust volume of actual sound sample */
197             if (playlist[i].volume != PSND_MAX_VOLUME)
198               for(j=0;j<sample_size;j++)
199                 premix_first_buffer[j] =
200                   (playlist[i].volume * (int)premix_first_buffer[j])
201                     >> PSND_MAX_VOLUME_BITS;
202
203             /* fill the last mixing buffer with stereo or mono sound */
204             if (stereo)
205             {
206               int middle_pos = PSND_MAX_LEFT2RIGHT/2;
207               int left_volume = stereo_volume[middle_pos+playlist[i].stereo];
208               int right_volume = stereo_volume[middle_pos-playlist[i].stereo];
209
210               for(j=0;j<sample_size;j++)
211               {
212                 premix_left_buffer[j] =
213                   (left_volume * (int)premix_first_buffer[j])
214                     >> PSND_MAX_LEFT2RIGHT_BITS;
215                 premix_right_buffer[j] =
216                   (right_volume * (int)premix_first_buffer[j])
217                     >> PSND_MAX_LEFT2RIGHT_BITS;
218                 premix_last_buffer[2*j+0] += premix_left_buffer[j];
219                 premix_last_buffer[2*j+1] += premix_right_buffer[j];
220               }
221             }
222             else
223             {
224               for(j=0;j<sample_size;j++)
225                 premix_last_buffer[j] += (int)premix_first_buffer[j];
226             }
227
228             /* delete completed sound entries from the playlist */
229             if (playlist[i].playingpos >= playlist[i].data_len)
230             {
231               if (playlist[i].loop)
232                 playlist[i].playingpos = 0;
233               else
234               {
235                 playlist[i] = emptySoundControl;
236                 playing_sounds--;
237               }
238             }
239             else if (playlist[i].volume <= PSND_MAX_VOLUME/10)
240             {
241               playlist[i] = emptySoundControl;
242               playing_sounds--;
243             }
244           }
245
246           /* put last mixing buffer to final playing buffer */
247           for(i=0;i<fragment_size;i++)
248           {
249             if (premix_last_buffer[i]<-255)
250               playing_buffer[i] = 0;
251             else if (premix_last_buffer[i]>255)
252               playing_buffer[i] = 255;
253             else
254               playing_buffer[i] = (premix_last_buffer[i]>>1)^0x80;
255           }
256
257           /* finally play the sound fragment */
258           write(sound_device,playing_buffer,fragment_size);
259         }
260
261         /* if no sounds playing, free device for other sound programs */
262         if (!playing_sounds)
263           close(sound_device);
264       }
265     }
266
267 #else /* !VOXWARE */
268
269     if (snd_ctrl.active && !snd_ctrl.loop)
270     {
271       struct timeval delay = { 0, 0 };
272       byte *sample_ptr;
273       long sample_size, max_sample_size = SND_BLOCKSIZE;
274       long sample_rate = 8000;  /* standard "/dev/audio" sampling rate */
275       int wait_percent = 90;    /* wait 90% of the real playing time */
276       int i;
277
278       if ((sound_device=open(sound_device_name,O_WRONLY))>=0)
279       {
280         playing_sounds = 1;
281
282         while(playing_sounds &&
283               select(sound_pipe[0]+1,&sound_fdset,NULL,NULL,&delay)<1)
284         {       
285           FD_SET(sound_pipe[0], &sound_fdset);
286
287           /* get pointer and size of the actual sound sample */
288           sample_ptr = snd_ctrl.data_ptr + snd_ctrl.playingpos;
289           sample_size =
290             MIN(max_sample_size, snd_ctrl.data_len - snd_ctrl.playingpos);
291           snd_ctrl.playingpos += sample_size;
292
293           /* fill the first mixing buffer with original sample */
294           memcpy(premix_first_buffer,sample_ptr,sample_size);
295
296
297           /* adjust volume of actual sound sample */
298           if (snd_ctrl.volume != PSND_MAX_VOLUME)
299             for(i=0;i<sample_size;i++)
300               premix_first_buffer[i] =
301                 (snd_ctrl.volume * (int)premix_first_buffer[i])
302                   >> PSND_MAX_VOLUME_BITS;
303
304           for(i=0;i<sample_size;i++)
305             playing_buffer[i] =
306               linear_to_ulaw(((int)premix_first_buffer[i]) << 8);
307
308           if (snd_ctrl.playingpos >= snd_ctrl.data_len)
309             playing_sounds = 0;
310
311           /* finally play the sound fragment */
312           write(sound_device,playing_buffer,sample_size);
313
314           delay.tv_sec = 0;
315           delay.tv_usec = ((sample_size*10*wait_percent)/(sample_rate))*1000;
316         }
317         close(sound_device);
318       }
319     }
320
321 #endif /* !VOXWARE */
322
323   }
324 #endif /* !MSDOS */
325 }
326
327 #ifdef MSDOS
328 static void sound_handler(struct SoundControl snd_ctrl)
329 {
330   int i;
331
332   if (snd_ctrl.fade_sound)
333   {
334     if (!playing_sounds)
335       return;
336
337     for (i=0; i<MAX_SOUNDS_PLAYING; i++)
338       if ((snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr) &&
339           !playlist[i].fade_sound)
340       {
341         playlist[i].fade_sound = TRUE;
342         if (voice_check(playlist[i].voice))
343           voice_ramp_volume(playlist[i].voice, 1000, 0);
344         playlist[i].loop = PSND_NO_LOOP;
345       }
346   }
347   else if (snd_ctrl.stop_all_sounds)
348   {
349     if (!playing_sounds)
350       return;
351     SoundServer_StopAllSounds();
352   }
353   else if (snd_ctrl.stop_sound)
354   {
355     if (!playing_sounds)
356       return;
357     SoundServer_StopSound(snd_ctrl.nr);
358   }
359
360   for (i=0; i<MAX_SOUNDS_PLAYING; i++)
361   {
362     if (!playlist[i].active || playlist[i].loop)
363       continue;
364
365     playlist[i].playingpos = voice_get_position(playlist[i].voice);
366     playlist[i].volume = voice_get_volume(playlist[i].voice);
367     if (playlist[i].playingpos == -1 || !playlist[i].volume)
368     {
369       deallocate_voice(playlist[i].voice);
370       playlist[i] = emptySoundControl;
371       playing_sounds--;
372     }
373   }
374
375   if (snd_ctrl.active)
376     SoundServer_InsertNewSound(snd_ctrl);
377 }
378 #endif /* MSDOS */
379
380 static void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
381 {
382   int i, k;
383
384   /* if playlist is full, remove oldest sound */
385   if (playing_sounds==MAX_SOUNDS_PLAYING)
386   {
387     int longest=0, longest_nr=0;
388
389     for(i=0;i<MAX_SOUNDS_PLAYING;i++)
390     {
391 #ifndef MSDOS
392       int actual = 100 * playlist[i].playingpos / playlist[i].data_len;
393 #else
394       int actual = playlist[i].playingpos;
395 #endif
396
397       if (!playlist[i].loop && actual>longest)
398       {
399         longest=actual;
400         longest_nr=i;
401       }
402     }
403 #ifdef MSDOS
404     voice_set_volume(playlist[longest_nr].voice, 0);
405     deallocate_voice(playlist[longest_nr].voice);
406 #endif
407     playlist[longest_nr] = emptySoundControl;
408     playing_sounds--;
409   }
410
411   /* check if sound is already being played (and how often) */
412   for(k=0,i=0;i<MAX_SOUNDS_PLAYING;i++)
413   {
414     if (playlist[i].nr == snd_ctrl.nr)
415       k++;
416   }
417
418   /* restart loop sounds only if they are just fading out */
419   if (k>=1 && snd_ctrl.loop)
420   {
421     for(i=0;i<MAX_SOUNDS_PLAYING;i++)
422     {
423       if (playlist[i].nr == snd_ctrl.nr && playlist[i].fade_sound)
424       {
425         playlist[i].fade_sound = FALSE;
426         playlist[i].volume = PSND_MAX_VOLUME;
427 #ifdef MSDOS
428         playlist[i].loop = PSND_LOOP;
429         voice_stop_volumeramp(playlist[i].voice);
430         voice_ramp_volume(playlist[i].voice, playlist[i].volume, 1000);
431 #endif
432       }
433     }
434     return;
435   }
436
437   /* don't play sound more than n times simultaneously (with n == 2 for now) */
438   if (k>=2)
439   {
440     int longest=0, longest_nr=0;
441
442     /* look for oldest equal sound */
443     for(i=0;i<MAX_SOUNDS_PLAYING;i++)
444     {
445       int actual;
446
447       if (!playlist[i].active || playlist[i].nr != snd_ctrl.nr)
448         continue;
449
450 #ifndef MSDOS
451       actual = 100 * playlist[i].playingpos / playlist[i].data_len;
452 #else
453       actual = playlist[i].playingpos;
454 #endif
455       if (actual>=longest)
456       {
457         longest=actual;
458         longest_nr=i;
459       }
460     }
461 #ifdef MSDOS
462     voice_set_volume(playlist[longest_nr].voice, 0);
463     deallocate_voice(playlist[longest_nr].voice);
464 #endif
465     playlist[longest_nr] = emptySoundControl;
466     playing_sounds--;
467   }
468
469   /* neuen Sound in Liste packen */
470   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
471   {
472     if (!playlist[i].active)
473     {
474       playlist[i] = snd_ctrl;
475       playing_sounds++;
476 #ifdef MSDOS
477       playlist[i].voice = allocate_voice(Sound[snd_ctrl.nr].sample_ptr);
478       if(snd_ctrl.loop)
479         voice_set_playmode(playlist[i].voice, PLAYMODE_LOOP);
480       voice_set_volume(playlist[i].voice, snd_ctrl.volume);
481       voice_set_pan(playlist[i].voice, snd_ctrl.stereo);
482       voice_start(playlist[i].voice);       
483 #endif
484       break;
485     }
486   }
487 }
488
489 /*
490 void SoundServer_FadeSound(int nr)
491 {
492   int i;
493
494   if (!playing_sounds)
495     return;
496
497   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
498     if (snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr)
499       playlist[i].fade_sound = TRUE;
500 }
501 */
502
503 #ifdef MSDOS
504 static void SoundServer_StopSound(int nr)
505 {
506   int i;
507
508   if (!playing_sounds)
509     return;
510
511   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
512     if (playlist[i].nr == nr)
513     {
514 #ifdef MSDOS
515       voice_set_volume(playlist[i].voice, 0);
516       deallocate_voice(playlist[i].voice);
517 #endif
518       playlist[i] = emptySoundControl;
519       playing_sounds--;
520     }
521
522 #ifndef MSDOS
523   if (!playing_sounds)
524     close(sound_device);
525 #endif
526 }
527
528 static void SoundServer_StopAllSounds()
529 {
530   int i;
531
532   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
533   {
534 #ifdef MSDOS
535     voice_set_volume(playlist[i].voice, 0);
536     deallocate_voice(playlist[i].voice);
537 #endif
538     playlist[i]=emptySoundControl;
539   }
540   playing_sounds = 0;
541
542 #ifndef MSDOS
543   close(sound_device);
544 #endif
545 }
546 #endif /* MSDOS */
547
548 #ifdef HPUX_AUDIO
549 static void HPUX_Audio_Control()
550 {
551   struct audio_describe ainfo;
552   int audio_ctl;
553
554   audio_ctl = open("/dev/audioCtl", O_WRONLY | O_NDELAY);
555   if (audio_ctl == -1)
556     Error(ERR_EXIT_SOUND_SERVER, "cannot open /dev/audioCtl - no sounds");
557
558   if (ioctl(audio_ctl, AUDIO_DESCRIBE, &ainfo) == -1)
559     Error(ERR_EXIT_SOUND_SERVER, "no audio info - no sounds");
560
561   if (ioctl(audio_ctl, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_ULAW) == -1)
562     Error(ERR_EXIT_SOUND_SERVER, "ulaw audio not available - no sounds");
563
564   ioctl(audio_ctl, AUDIO_SET_CHANNELS, 1);
565   ioctl(audio_ctl, AUDIO_SET_SAMPLE_RATE, 8000);
566
567   close(audio_ctl);
568 }
569 #endif /* HPUX_AUDIO */
570
571 #ifndef VOXWARE
572 #ifndef MSDOS
573 /* these two are stolen from "sox"... :) */
574
575 /*
576 ** This routine converts from linear to ulaw.
577 **
578 ** Craig Reese: IDA/Supercomputing Research Center
579 ** Joe Campbell: Department of Defense
580 ** 29 September 1989
581 **
582 ** References:
583 ** 1) CCITT Recommendation G.711  (very difficult to follow)
584 ** 2) "A New Digital Technique for Implementation of Any
585 **     Continuous PCM Companding Law," Villeret, Michel,
586 **     et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
587 **     1973, pg. 11.12-11.17
588 ** 3) MIL-STD-188-113,"Interoperability and Performance Standards
589 **     for Analog-to_Digital Conversion Techniques,"
590 **     17 February 1987
591 **
592 ** Input: Signed 16 bit linear sample
593 ** Output: 8 bit ulaw sample
594 */
595
596 #define ZEROTRAP    /* turn on the trap as per the MIL-STD */
597 #define BIAS 0x84   /* define the add-in bias for 16 bit samples */
598 #define CLIP 32635
599
600 static unsigned char linear_to_ulaw(int sample)
601 {
602   static int exp_lut[256] =
603   {
604     0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
605     4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
606     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
607     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
608     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
609     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
610     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
611     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
612     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
613     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
614     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
615     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
616     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
617     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
618     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
619     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
620   };
621
622   int sign, exponent, mantissa;
623   unsigned char ulawbyte;
624
625   /* Get the sample into sign-magnitude. */
626   sign = (sample >> 8) & 0x80;          /* set aside the sign */
627   if (sign != 0)
628     sample = -sample;                   /* get magnitude */
629   if (sample > CLIP)
630     sample = CLIP;                      /* clip the magnitude */
631
632   /* Convert from 16 bit linear to ulaw. */
633   sample = sample + BIAS;
634   exponent = exp_lut[( sample >> 7 ) & 0xFF];
635   mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
636   ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
637 #ifdef ZEROTRAP
638   if (ulawbyte == 0)
639     ulawbyte = 0x02;                    /* optional CCITT trap */
640 #endif
641
642   return(ulawbyte);
643 }
644
645 /*
646 ** This routine converts from ulaw to 16 bit linear.
647 **
648 ** Craig Reese: IDA/Supercomputing Research Center
649 ** 29 September 1989
650 **
651 ** References:
652 ** 1) CCITT Recommendation G.711  (very difficult to follow)
653 ** 2) MIL-STD-188-113,"Interoperability and Performance Standards
654 **     for Analog-to_Digital Conversion Techniques,"
655 **     17 February 1987
656 **
657 ** Input: 8 bit ulaw sample
658 ** Output: signed 16 bit linear sample
659 */
660
661 static int ulaw_to_linear(unsigned char ulawbyte)
662 {
663   static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
664   int sign, exponent, mantissa, sample;
665
666   ulawbyte = ~ ulawbyte;
667   sign = ( ulawbyte & 0x80 );
668   exponent = ( ulawbyte >> 4 ) & 0x07;
669   mantissa = ulawbyte & 0x0F;
670   sample = exp_lut[exponent] + ( mantissa << ( exponent + 3 ) );
671   if (sign != 0)
672     sample = -sample;
673
674   return(sample);
675 }
676 #endif /* !MSDOS */
677 #endif /* !VOXWARE */
678
679 /*** THE STUFF ABOVE IS ONLY USED BY THE SOUND SERVER CHILD PROCESS ***/
680
681 /*===========================================================================*/
682
683 /*** THE STUFF BELOW IS ONLY USED BY THE MAIN PROCESS ***/
684
685 #ifndef MSDOS
686 static unsigned long be2long(unsigned long *be) /* big-endian -> longword */
687 {
688   unsigned char *ptr = (unsigned char *)be;
689
690   return(ptr[0]<<24 | ptr[1]<<16 | ptr[2]<<8 | ptr[3]);
691 }
692
693 static unsigned long le2long(unsigned long *be) /* little-endian -> longword */
694 {
695   unsigned char *ptr = (unsigned char *)be;
696
697   return(ptr[3]<<24 | ptr[2]<<16 | ptr[1]<<8 | ptr[0]);
698 }
699 #endif /* !MSDOS */
700
701 boolean LoadSound(struct SoundInfo *snd_info)
702 {
703   char filename[256];
704   char *sound_ext = "wav";
705 #ifndef MSDOS
706   struct SoundHeader_WAV *sound_header;
707   FILE *file;
708   int i;
709 #endif
710
711   sprintf(filename, "%s/%s/%s.%s",
712           options.base_directory, SOUNDS_DIRECTORY, snd_info->name, sound_ext);
713
714 #ifndef MSDOS
715
716   if ((file = fopen(filename, "r")) == NULL)
717   {
718     Error(ERR_WARN, "cannot open sound file '%s' - no sounds", filename);
719     return(FALSE);
720   }
721
722   if (fseek(file, 0, SEEK_END) < 0)
723   {
724     Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
725     fclose(file);
726     return(FALSE);
727   }
728
729   snd_info->file_len = ftell(file);
730   rewind(file);
731
732   snd_info->file_ptr = checked_malloc(snd_info->file_len);
733
734   if (fread(snd_info->file_ptr, 1, snd_info->file_len, file) !=
735       snd_info->file_len)
736   {
737     Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
738     fclose(file);
739     return(FALSE);
740   }
741
742   fclose(file);
743
744   sound_header = (struct SoundHeader_WAV *)snd_info->file_ptr;
745
746   if (strncmp(sound_header->magic_RIFF, "RIFF", 4) ||
747       snd_info->file_len != le2long(&sound_header->header_size) + 8 ||
748       strncmp(sound_header->magic_WAVE, "WAVE", 4) ||
749       strncmp(sound_header->magic_DATA, "data", 4) ||
750       snd_info->file_len != le2long(&sound_header->data_size) + 44)
751   {
752     Error(ERR_WARN, "'%s' is not a RIFF/WAVE file or broken - no sounds",
753           filename);
754     return(FALSE);
755   }
756
757   snd_info->data_ptr = snd_info->file_ptr + 44;
758   snd_info->data_len = le2long(&sound_header->data_size);
759
760   for (i=0; i<snd_info->data_len; i++)
761     snd_info->data_ptr[i] = snd_info->data_ptr[i]^0x80;
762
763 #else /* MSDOS */
764
765   snd_info->sample_ptr = load_sample(filename);
766   if (!snd_info->sample_ptr)
767   {
768     Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
769     return(FALSE);
770   }
771
772 #endif /* MSDOS */
773
774   return(TRUE);
775 }
776
777 boolean LoadSound_8SVX(struct SoundInfo *snd_info)
778 {
779   char filename[256];
780 #ifndef MSDOS
781   struct SoundHeader_8SVX *sound_header;
782   FILE *file;
783   char *ptr;
784   char *sound_ext = "8svx";
785 #else
786   char *sound_ext = "wav";
787 #endif
788
789   sprintf(filename, "%s/%s/%s.%s",
790           options.base_directory, SOUNDS_DIRECTORY, snd_info->name, sound_ext);
791
792 #ifndef MSDOS
793   if (!(file=fopen(filename,"r")))
794   {
795     Error(ERR_WARN, "cannot open sound file '%s' - no sounds", filename);
796     return(FALSE);
797   }
798
799   if (fseek(file,0,SEEK_END)<0)
800   {
801     Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
802     fclose(file);
803     return(FALSE);
804   }
805
806   snd_info->file_len = ftell(file);
807   rewind(file);
808
809   if (!(snd_info->file_ptr=malloc(snd_info->file_len)))
810   {
811     Error(ERR_WARN, "out of memory (this shouldn't happen :) - no sounds");
812     fclose(file);
813     return(FALSE);
814   }
815
816   if (fread(snd_info->file_ptr,1,snd_info->file_len,file)!=snd_info->file_len)
817   {
818     Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
819     fclose(file);
820     return(FALSE);
821   }
822
823   fclose(file);
824
825   sound_header = (struct SoundHeader_8SVX *)snd_info->file_ptr;
826
827   if (strncmp(sound_header->magic_FORM,"FORM",4) ||
828       snd_info->file_len != be2long(&sound_header->chunk_size)+8 ||
829       strncmp(sound_header->magic_8SVX,"8SVX",4))
830   {
831     Error(ERR_WARN, "'%s' is not an IFF/8SVX file or broken - no sounds",
832           filename);
833     return(FALSE);
834   }
835
836   ptr = (char *)snd_info->file_ptr + 12;
837
838   while(ptr < (char *)(snd_info->file_ptr + snd_info->file_len))
839   {
840     if (!strncmp(ptr,"VHDR",4))
841     {
842       ptr += be2long((unsigned long *)(ptr + 4)) + 8;
843       continue;
844     }
845     else if (!strncmp(ptr,"ANNO",4))
846     {
847       ptr += be2long((unsigned long *)(ptr + 4)) + 8;
848       continue;
849     }
850     else if (!strncmp(ptr,"CHAN",4))
851     {
852       ptr += be2long((unsigned long *)(ptr + 4)) + 8;
853       continue;
854     }
855     else if (!strncmp(ptr,"BODY",4))
856     {
857       snd_info->data_ptr = (byte *)ptr + 8;
858       snd_info->data_len = be2long((unsigned long *)(ptr + 4));
859       return(TRUE);
860     }
861     else
862     {
863       /* other chunk not recognized here */
864       ptr += be2long((unsigned long *)(ptr + 4)) + 8;
865       continue;
866     }
867   }
868
869   return(FALSE);
870 #else /* MSDOS */
871   snd_info->sample_ptr = load_sample(filename);
872   if(!snd_info->sample_ptr)
873   {
874     Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
875     return(FALSE);
876   }
877   return(TRUE);
878 #endif /* MSDOS */
879 }
880
881 void PlaySound(int nr)
882 {
883   PlaySoundExt(nr, PSND_MAX_VOLUME, PSND_MIDDLE, PSND_NO_LOOP);
884 }
885
886 void PlaySoundStereo(int nr, int stereo)
887 {
888   PlaySoundExt(nr, PSND_MAX_VOLUME, stereo, PSND_NO_LOOP);
889 }
890
891 void PlaySoundLoop(int nr)
892 {
893   PlaySoundExt(nr, PSND_MAX_VOLUME, PSND_MIDDLE, PSND_LOOP);
894 }
895
896 void PlaySoundExt(int nr, int volume, int stereo, boolean loop)
897 {
898   struct SoundControl snd_ctrl = emptySoundControl;
899
900   if (sound_status==SOUND_OFF || !setup.sound)
901     return;
902
903   if (volume<PSND_MIN_VOLUME)
904     volume = PSND_MIN_VOLUME;
905   else if (volume>PSND_MAX_VOLUME)
906     volume = PSND_MAX_VOLUME;
907
908   if (stereo<PSND_MAX_LEFT)
909     stereo = PSND_MAX_LEFT;
910   else if (stereo>PSND_MAX_RIGHT)
911     stereo = PSND_MAX_RIGHT;
912
913   snd_ctrl.nr           = nr;
914   snd_ctrl.volume       = volume;
915   snd_ctrl.stereo       = stereo;
916   snd_ctrl.loop         = loop;
917   snd_ctrl.active       = TRUE;
918   snd_ctrl.data_ptr     = Sound[nr].data_ptr;
919   snd_ctrl.data_len     = Sound[nr].data_len;
920
921 #ifndef MSDOS
922   if (write(sound_pipe[1], &snd_ctrl, sizeof(snd_ctrl))<0)
923   {
924     Error(ERR_WARN, "cannot pipe to child process - no sounds");
925     sound_status = SOUND_OFF;
926     return;
927   }
928 #else
929   sound_handler(snd_ctrl);
930 #endif
931 }
932
933 void FadeSound(int nr)
934 {
935   StopSoundExt(nr, SSND_FADE_SOUND);
936 }
937
938 void FadeSounds()
939 {
940   StopSoundExt(-1, SSND_FADE_ALL_SOUNDS);
941 }
942
943 void StopSound(int nr)
944 {
945   StopSoundExt(nr, SSND_STOP_SOUND);
946 }
947
948 void StopSounds()
949 {
950   StopSoundExt(-1, SSND_STOP_ALL_SOUNDS);
951 }
952
953 void StopSoundExt(int nr, int method)
954 {
955   struct SoundControl snd_ctrl = emptySoundControl;
956
957   if (sound_status==SOUND_OFF)
958     return;
959
960   if (SSND_FADING(method))
961     snd_ctrl.fade_sound = TRUE;
962
963   if (SSND_ALL(method))
964     snd_ctrl.stop_all_sounds = TRUE;
965   else
966   {
967     snd_ctrl.nr = nr;
968     snd_ctrl.stop_sound = TRUE;
969   }
970
971 #ifndef MSDOS
972   if (write(sound_pipe[1], &snd_ctrl, sizeof(snd_ctrl))<0)
973   {
974     Error(ERR_WARN, "cannot pipe to child process - no sounds");
975     sound_status = SOUND_OFF;
976     return;
977   }
978 #else
979   sound_handler(snd_ctrl);
980 #endif
981 }
982
983 void FreeSounds(int max)
984 {
985   int i;
986
987   if (sound_status==SOUND_OFF)
988     return;
989
990   for(i=0;i<max;i++)
991 #ifndef MSDOS
992     free(Sound[i].file_ptr);
993 #else
994     destroy_sample(Sound[i].sample_ptr);
995 #endif
996 }
997
998 /*** THE STUFF ABOVE IS ONLY USED BY THE MAIN PROCESS ***/