]> icculus.org git repositories - taylor/freespace2.git/blob - src/sound/sound.cpp
added copyright header
[taylor/freespace2.git] / src / sound / sound.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Sound/Sound.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Low-level sound code
16  *
17  * $Log$
18  * Revision 1.7  2002/06/09 04:41:27  relnev
19  * added copyright header
20  *
21  * Revision 1.6  2002/06/05 08:05:29  relnev
22  * stub/warning removal.
23  *
24  * reworked the sound code.
25  *
26  * Revision 1.5  2002/06/02 21:11:12  cemason
27  * Few changes
28  *
29  * Revision 1.4  2002/05/27 01:06:01  theoddone33
30  * sound.cpp works
31  *
32  * Revision 1.3  2002/05/27 00:40:47  theoddone33
33  * Fix net_addr vs net_addr_t
34  *
35  * Revision 1.2  2002/05/07 03:16:52  theoddone33
36  * The Great Newline Fix
37  *
38  * Revision 1.1.1.1  2002/05/03 03:28:10  root
39  * Initial import.
40  *
41  * 
42  * 9     9/12/99 8:09p Dave
43  * Fixed problem where skip-training button would cause mission messages
44  * not to get paged out for the current mission.
45  * 
46  * 8     8/27/99 6:38p Alanl
47  * crush the blasted repeating messages bug
48  * 
49  * 7     8/22/99 11:06p Alanl
50  * don't convert priority in snd_play_3d
51  * 
52  * 6     8/04/99 9:48p Alanl
53  * fix bug with setting 3D properties on a 2D sound buffer
54  * 
55  * 5     6/18/99 5:16p Dave
56  * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
57  * dialog to PXO screen.
58  * 
59  * 4     5/23/99 8:11p Alanl
60  * Added support for EAX
61  * 
62  * 3     1/29/99 12:47a Dave
63  * Put in sounds for beam weapon. A bunch of interface screens (tech
64  * database stuff).
65  * 
66  * 2     10/07/98 10:54a Dave
67  * Initial checkin.
68  * 
69  * 1     10/07/98 10:51a Dave
70  * 
71  * 77    6/13/98 6:02p Hoffoss
72  * Externalized all new (or forgot to be added) strings to all the code.
73  * 
74  * 76    5/23/98 9:27p Lawrance
75  * Change ACM failure message to use a message box, not a Warning()
76  * 
77  * 75    5/15/98 3:36p John
78  * Fixed bug with new graphics window code and standalone server.  Made
79  * hwndApp not be a global anymore.
80  * 
81  * 74    5/12/98 5:39p Lawrance
82  * Increase MAX_SOUNDS to 175.. to account for missions with lots of voice
83  * 
84  * 73    5/12/98 2:43p Lawrance
85  * Make snd_time_remaining() work for arbitrary format
86  * 
87  * 72    5/11/98 2:02p Andsager
88  * doh!
89  * 
90  * 71    5/11/98 1:56p Andsager
91  * increase min_range in snd_play_3d when range_factor is not 1.0
92  * 
93  * 70    5/10/98 3:49p Sandeep
94  * Fix problem with having the audio streaming while trying to close down
95  * sound
96  * 
97  * 69    5/09/98 10:48p Lawrance
98  * Default voice volume to 0.7
99  * 
100  * 68    5/05/98 4:49p Lawrance
101  * Put in code to authenticate A3D, improve A3D support
102  * 
103  * 67    4/25/98 12:01p Lawrance
104  * try to init sound for 5 seconds... to overcome conflicts with launch
105  * app sounds
106  * 
107  * 66    4/20/98 12:03a Lawrance
108  * Allow prioritizing of CTRL3D buffers
109  * 
110  * 65    4/19/98 9:30p Lawrance
111  * Use Aureal_enabled flag
112  * 
113  * 64    4/18/98 9:12p Lawrance
114  * Added Aureal support.
115  * 
116  * 63    4/14/98 3:29p John
117  * took out mprintf
118  * 
119  * 62    4/13/98 5:04p Lawrance
120  * Write functions to determine how many milliseconds are left in a sound
121  * 
122  * 61    4/09/98 5:53p Lawrance
123  * Make DirectSound init more robust
124  * 
125  * 60    4/01/98 9:21p John
126  * Made NDEBUG, optimized build with no warnings or errors.
127  * 
128  * 59    3/29/98 12:56a Lawrance
129  * preload the warp in and explosions sounds before a mission.
130  * 
131  * 58    3/25/98 6:10p Lawrance
132  * Work on DirectSound3D
133  * 
134  * 57    3/24/98 4:28p Lawrance
135  * Make DirectSound3D support more robust
136  * 
137  * 56    3/23/98 10:32a Lawrance
138  * Add functions for extracting raw sound data
139  * 
140  * 55    3/21/98 3:34p Lawrance
141  * Allow 3d sounds to have their ranges modified dynamically
142  * 
143  * 54    3/19/98 5:36p Lawrance
144  * Add some sound debug functions to see how many sounds are playing, and
145  * to start/stop random looping sounds.
146  * 
147  * 53    3/17/98 5:55p Lawrance
148  * Support object-linked sounds for asteroids.
149  * 
150  * 52    2/20/98 8:32p Lawrance
151  * Add radius parm to sound_play_3d()
152  * 
153  * 51    2/18/98 5:49p Lawrance
154  * Even if the ADPCM codec is unavailable, allow game to continue.
155  * 
156  * 50    2/15/98 4:43p Lawrance
157  * work on real-time voice
158  * 
159  * 49    2/06/98 7:30p John
160  * Added code to monitor the number of channels of sound actually playing.
161  * 
162  * 48    2/05/98 9:21p John
163  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
164  * game.
165  * 
166  * 47    1/31/98 5:57p Lawrance
167  * remove debug code that was stopping any sounds from playing
168  * 
169  * 46    1/31/98 5:48p Lawrance
170  * Start on real-time voice recording
171  * 
172  * 45    1/19/98 11:37p Lawrance
173  * Fixing Optimization build warnings
174  * 
175  * 44    1/13/98 5:36p Lawrance
176  * Reduce default sound volume to 0.9.
177  * 
178  * 43    1/11/98 11:14p Lawrance
179  * Preload sounds that we expect will get played.
180  * 
181  * 42    1/10/98 1:14p John
182  * Added explanation to debug console commands
183  * 
184  * 41    1/07/98 11:08a Lawrance
185  * pass priority to snd_play_raw()
186  * 
187  * 40    12/21/97 4:33p John
188  * Made debug console functions a class that registers itself
189  * automatically, so you don't need to add the function to
190  * debugfunctions.cpp.  
191  * 
192  * 39    12/05/97 5:19p Lawrance
193  * re-do sound priorities to make more general and extensible
194  * 
195  * 38    12/04/97 10:21a Lawrance
196  * if a 3D sound has priority, only play if above a minimum volume level
197  * 
198  * 37    11/20/97 5:36p Dave
199  * Hooked in a bunch of main hall changes (including sound). Made it
200  * possible to reposition (rewind/ffwd) 
201  * sound buffer pointers. Fixed animation direction change framerate
202  * problem.
203  * 
204  * 36    11/20/97 1:06a Lawrance
205  * Add Master_voice_volume, make voices play back at correctly scaled
206  * volumes
207  * 
208  * 35    11/12/97 4:40p Dave
209  * Put in multiplayer campaign support parsing, loading and saving. Made
210  * command-line variables better named. Changed some things on the initial
211  * pilot select screen.
212  * 
213  * 34    10/14/97 11:33p Lawrance
214  * get RSX implemented
215  * 
216  * 33    10/13/97 7:41p Lawrance
217  * store duration of sound
218  * 
219  * 32    10/06/97 4:12p Lawrance
220  * fix volume bug with 3d sounds
221  * 
222  * 31    10/01/97 5:55p Lawrance
223  * change call to snd_play_3d() to allow for arbitrary listening position
224  * 
225  * 30    9/09/97 3:39p Sandeep
226  * warning level 4 bugs
227  * 
228  * 29    9/03/97 5:03p Lawrance
229  * add support for -nosound command line parm
230  * 
231  * 28    7/31/97 11:16a Dan
232  * Add some Asserts() to check validity of vectors send to DirectSound3D
233  * 
234  * 27    7/28/97 11:39a Lawrance
235  * allow individual volume scaling on 3D buffers
236  * 
237  * 26    7/17/97 9:53a Lawrance
238  * if a sound is supposed to use DirectSound3D, ensure it does when
239  * reloading it
240  * 
241  * 25    7/17/97 9:32a John
242  * made all directX header files name start with a v
243  * 
244  * 24    7/15/97 11:15a Lawrance
245  * limit the max instances of simultaneous sound effects, implement
246  * priorities to force critical sounds
247  * 
248  * 23    7/13/97 5:52p Lawrance
249  * allow snd_set_volume() to set volume at any level
250  * 
251  * 22    6/13/97 4:44p Lawrance
252  * added another sound hook in ship selection
253  * 
254  * 21    6/09/97 11:50p Lawrance
255  * integrating DirectSound3D
256  * 
257  * 20    6/05/97 11:25a Lawrance
258  * use sound signatures to ensure correct sound is loaded
259  * 
260  * 19    6/05/97 1:36a Lawrance
261  * using a new interface to play sounds
262  * 
263  * 18    6/05/97 1:08a Lawrance
264  * new sound play interface
265  * 
266  * 17    6/03/97 9:23a Lawrance
267  * fix bug that caused when sound not found
268  * 
269  * 16    6/02/97 1:45p Lawrance
270  * implementing hardware mixing
271  * 
272  * 15    5/29/97 4:01p Lawrance
273  * don't fail sound init if dsound3d fails init
274  * 
275  * 14    5/29/97 3:32p Lawrance
276  * added call to snd_do_frame()
277  * 
278  * 13    5/29/97 12:04p Lawrance
279  * split off acm, direct sound, and direct sound 3d portions to separate
280  * files
281  * 
282  * 12    5/21/97 3:55p Lawrance
283  * when reclaiming sound channels, never stop a looping sound
284  * 
285  * 11    5/19/97 4:32p Lawrance
286  * remove misleading nprintf
287  * 
288  * 10    5/15/97 9:05a Lawrance
289  * If no sound channels, take away the lowest volume sound (if that volume
290  * is less than requested sound fx volume)
291  * 
292  * 9     5/12/97 10:32a Lawrance
293  * take out nprintf
294  * 
295  * 8     5/09/97 4:51p Lawrance
296  * improve pan sound
297  * 
298  * 7     5/09/97 4:34p Lawrance
299  * improve comments
300  * 
301  * 6     5/07/97 3:29p Lawrance
302  * make volume conversion use a lookup table, fix errors in volume
303  * conversion
304  * 
305  * 5     5/07/97 11:33a Lawrance
306  * improve snd_chg_loop_status()
307  * 
308  * 4     5/06/97 9:36a Lawrance
309  * added support for min and max distances for 3d sounds
310  * 
311  * 3     5/02/97 4:36p Lawrance
312  * send correct volume scaling when playing a 3D sound
313  * 
314  * 2     4/28/97 5:13p John
315  * more moving sound/movie code out of osapi.
316  * 
317  * $NoKeywords: $
318  */
319 #include "pstypes.h"
320
321 #ifndef PLAT_UNIX
322 #include <windows.h>
323 #include <mmreg.h>
324 #include "vdsound.h"
325 #endif
326
327 #include "3dinternal.h"
328 #include "sound.h"
329 #include "audiostr.h"
330 #include "object.h"
331 #include "cmdline.h"
332 #include "osapi.h"
333
334 #include "gamesnd.h"
335 #include "alphacolors.h"
336
337 #include "ds.h"
338 #include "ds3d.h"
339 #include "acm.h"
340 #include "dscap.h"
341                 
342 #define SND_F_USED                      (1<<0)          // Sounds[] element is used
343
344 typedef struct sound    {
345         int                             sid;                    // software id
346         int                             hid;                    // hardware id, -1 if sound is not in hardware
347         char                            filename[MAX_FILENAME_LEN];
348         int                             sig;
349         int                             flags;
350         sound_info              info;
351         int                             uncompressed_size;              // size (in bytes) of sound (uncompressed)
352         int                             duration;
353 } sound;
354
355 sound   Sounds[MAX_SOUNDS];
356
357 int Sound_enabled = TRUE;                               // global flag to turn sound on/off
358 int Snd_sram;                                                           // mem (in bytes) used up by storing sounds in system memory
359 int Snd_hram;                                                           // mem (in bytes) used up by storing sounds in soundcard memory
360 float Master_sound_volume = 1.0f;       // range is 0 -> 1, used for non-music sound fx
361 float Master_voice_volume = 0.7f;       // range is 0 -> 1, used for all voice playback
362
363 // min volume to play a sound after all volume processing (range is 0.0 -> 1.0)
364 #define MIN_SOUND_VOLUME                                0.10f
365
366 static int snd_next_sig = 1;
367
368 // convert the game level sound priorities to the DirectSound priority descriptions
369 int ds_priority(int priority)
370 {
371         switch(priority){
372                 case SND_PRIORITY_MUST_PLAY:
373                         return DS_MUST_PLAY;
374                 case SND_PRIORITY_SINGLE_INSTANCE:
375                         return DS_LIMIT_ONE;
376                 case SND_PRIORITY_DOUBLE_INSTANCE:
377                         return DS_LIMIT_TWO;
378                 case SND_PRIORITY_TRIPLE_INSTANCE:
379                         return DS_LIMIT_THREE;
380                 default:
381                         Int3();
382                         return DS_MUST_PLAY;
383         }
384 }
385
386 void snd_clear()
387 {
388         int i;
389
390         // flag all Sounds[] as free
391         for (i=0; i<MAX_SOUNDS; i++ )   {
392                 Sounds[i].flags &=  ~SND_F_USED;
393                 Sounds[i].sid = -1;
394                 Sounds[i].hid = -1;
395         }
396
397         // reset how much storage sounds are taking up in memory
398         Snd_sram = 0;
399         Snd_hram = 0;
400 }
401
402 // ---------------------------------------------------------------------------------------
403 // Initialize the game sound system
404 // 
405 // Initialize the game sound system.  Depending on what sound library is being used,
406 // call the appropriate low-level initiailizations
407 //
408 // returns:     1               => init success
409 //              0               => init failed
410 //
411 int snd_init(int use_a3d, int use_eax)
412 {
413         int rval;
414
415         if ( Cmdline_freespace_no_sound )
416                 return 0;
417
418         if (ds_initialized)     {
419                 nprintf(( "Sound", "SOUND => Direct Sound is already initialized!\n" ));
420                 return 1;
421         }
422
423         snd_clear();
424
425         // Init DirectSound 
426
427         // Connect to DirectSound
428         int num_tries=0;
429         int gave_warning = 0;
430         while(1) {
431                 rval = ds_init(use_a3d, use_eax);
432
433                 if( rval != 0 ) {
434                         nprintf(( "Sound", "SOUND ==> Error initializing DirectSound, trying again in 1 second.\n"));
435                         Sleep(1000);
436                 } else {
437                         break;
438                 }
439
440                 if ( num_tries++ > 5 ) {
441                         if ( !gave_warning ) {
442 #ifndef PLAT_UNIX
443                                 MessageBox(NULL, XSTR("DirectSound could not be initialized.  If you are running any applications playing sound in the background, you should stop them before continuing.",971), NULL, MB_OK);
444 #else
445                                 fprintf (stderr, "Sound could not be initialized\n");
446 #endif
447                                 gave_warning = 1;
448                         } else {
449                                 goto Failure;
450                         }
451                 }
452         }
453
454         // Init the Audio Compression Manager
455         if ( ACM_init() == -1 ) {
456 #ifndef PLAT_UNIX
457                 HWND hwnd = (HWND)os_get_window();
458                 MessageBox(hwnd, XSTR("Could not properly initialize the Microsoft ADPCM codec.\n\nPlease see the readme.txt file for detailed instructions on installing the Microsoft ADPCM codec.",972), NULL, MB_OK);
459 #else
460                 fprintf (stderr, "ACM_init failed\n");
461 #endif
462 //              Warning(LOCATION, "Could not properly initialize the Microsoft ADPCM codec.\nPlease see the readme.txt file for detailed instructions on installing the Microsoft ADPCM codec.");
463         }
464
465         // Init the audio streaming stuff
466         audiostream_init();
467                         
468         ds_initialized = 1;
469         return 1;
470
471 Failure:
472 //      Warning(LOCATION, "Sound system was unable to be initialized.  If you continue, sound will be disabled.\n");
473         nprintf(( "Sound", "SOUND => Direct Sound init unsuccessful, continuing without sound.\n" ));
474         return 0;
475 }
476
477
478 void snd_spew_info()
479 {
480         int idx;
481         char txt[512] = "";
482         CFILE *out = cfopen("sounds.txt", "wt", CFILE_NORMAL, CF_TYPE_DATA);
483         if(out == NULL){
484                 return;
485         }
486         
487         cfwrite_string("Sounds loaded :\n", out);
488
489         // spew info for all sounds
490         for(idx=0; idx<MAX_SOUNDS; idx++){
491                 if(!(Sounds[idx].flags & SND_F_USED)){
492                         continue;
493                 }
494                 
495                 sprintf(txt, "%s (%ds)\n", Sounds[idx].filename, Sounds[idx].info.duration); 
496                 cfwrite_string(txt, out);
497         }
498
499         // close the outfile
500         if(out != NULL){
501                 cfclose(out);
502                 out = NULL;
503         }
504 }
505
506 int Sound_spew = 0;
507 DCF(show_sounds, "")
508 {
509         Sound_spew = !Sound_spew;
510         if(Sound_spew){
511                 dc_printf("Sound debug info ON");
512         } else {
513                 dc_printf("Sound debug info OFF");
514         }
515 }
516 void snd_spew_debug_info()
517 {
518         int game_sounds = 0;
519         int message_sounds = 0;
520         int interface_sounds = 0;
521         int done = 0;
522         int s_idx;
523
524         if(!Sound_spew){
525                 return;
526         }
527
528         // count up game, interface and message sounds
529         for(int idx=0; idx<MAX_SOUNDS; idx++){
530                 if(!Sounds[idx].flags & SND_F_USED){
531                         continue;
532                 }
533
534                 done = 0;
535
536                 // what kind of sound is this
537                 for(s_idx=0; s_idx<MAX_GAME_SOUNDS; s_idx++){
538                         if(!stricmp(Snds[s_idx].filename, Sounds[idx].filename)){
539                                 game_sounds++;
540                                 done = 1;
541                         }
542                 }
543
544                 if(!done){
545                         for(s_idx=0; s_idx<MAX_GAME_SOUNDS; s_idx++){
546                                 if(!stricmp(Snds_iface[s_idx].filename, Sounds[idx].filename)){
547                                         interface_sounds++;
548                                         done = 1;
549                                 }
550                         }
551                 }
552
553                 if(!done){
554                         message_sounds++;
555                 }               
556         }
557
558         // spew info
559         gr_set_color_fast(&Color_normal);
560         gr_printf(30, 100, "Game sounds : %d\n", game_sounds);
561         gr_printf(30, 110, "Interface sounds : %d\n", interface_sounds);
562         gr_printf(30, 120, "Message sounds : %d\n", message_sounds);
563         gr_printf(30, 130, "Total sounds : %d\n", game_sounds + interface_sounds + message_sounds);
564 }
565
566 // ---------------------------------------------------------------------------------------
567 // snd_load() 
568 //
569 // Load a sound into memory and prepare it for playback.  The sound will reside in memory as
570 // a single instance, and can be played multiple times simultaneously.  Through the magic of
571 // DirectSound, only 1 copy of the sound is used.
572 //
573 // parameters:          gs                                                      => file of sound to load
574 //                                              allow_hardware_load     => whether to try to allocate in hardware
575 //
576 // returns:                     success => index of sound in Sounds[] array
577 //                                              failure => -1
578 //
579 //int snd_load( char *filename, int hardware, int use_ds3d, int *sig)
580 int snd_load( game_snd *gs, int allow_hardware_load )
581 {
582         int                             n, rc, type;
583         sound_info              *si;
584         sound                           *snd;
585         WAVEFORMATEX    *header = NULL;
586
587         if ( gs->filename == NULL || gs->filename[0] == 0 )
588                 return -1;
589
590         for (n=0; n<MAX_SOUNDS; n++ )   {
591                 if (!(Sounds[n].flags & SND_F_USED))
592                         break;
593                 else if ( !stricmp( Sounds[n].filename, gs->filename )) {
594                         gs->sig = Sounds[n].sig;
595                         return n;
596                 }
597         }
598
599         if ( n == MAX_SOUNDS ) {
600 #ifndef NDEBUG
601                 // spew sound info
602                 snd_spew_info();
603 #endif
604
605                 Int3();
606                 return -1;
607         }
608
609         snd = &Sounds[n];
610
611         if ( !ds_initialized )
612                 return -1;
613
614         si = &snd->info;
615
616         if ( ds_parse_wave(gs->filename, &si->data, &si->size, &header) == -1 )
617                 return -1;
618
619         si->format                                      = header->wFormatTag;           // 16-bit flag (wFormatTag)
620         si->n_channels                          = header->nChannels;                    // 16-bit channel count (nChannels)
621         si->sample_rate                 = header->nSamplesPerSec;       // 32-bit sample rate (nSamplesPerSec)
622         si->avg_bytes_per_sec   = header->nAvgBytesPerSec;      // 32-bit average bytes per second (nAvgBytesPerSec)
623         si->n_block_align                       = header->nBlockAlign;          // 16-bit block alignment (nBlockAlign)
624         si->bits                                                = header->wBitsPerSample;       // Read 16-bit bits per sample                  
625
626         snd->duration = fl2i(1000.0f * (si->size / (si->bits/8.0f)) / si->sample_rate);
627 nprintf(("SOUND", "SOUND ==> duration = %dms (%d %d %d)\n", snd->duration,
628 si->size, si->bits, si->sample_rate));
629         type = 0;
630
631         if ( allow_hardware_load ) {
632                 if ( gs->preload ) {
633                         type |= DS_HARDWARE;
634                 }
635         }
636
637         if ( (gs->flags&GAME_SND_USE_DS3D)  ) {
638                 type |= DS_USE_DS3D;
639         }
640         
641         rc = ds_load_buffer(&snd->sid, &snd->hid, &snd->uncompressed_size, header, si, type);
642
643         free(header);
644         free(si->data); // don't want to keep this around
645
646         if ( rc == -1 )
647                 return -1;
648
649         strncpy( snd->filename, gs->filename, MAX_FILENAME_LEN );
650         snd->flags = SND_F_USED;
651
652         snd->sig = snd_next_sig++;
653         if (snd_next_sig < 0 ) snd_next_sig = 1;
654         gs->id_sig = snd->sig;
655         gs->id = n;
656
657         nprintf(("Sound", "Loaded %s\n", gs->filename));
658         return n;
659 }
660
661 // ---------------------------------------------------------------------------------------
662 // snd_unload() 
663 //
664 // Unload a sound from memory.  This will release the storage, and the sound must be re-loaded via
665 // sound_load() before it can be played again.
666 //
667 int snd_unload( int n )
668 {
669         if (!ds_initialized)
670                 return 0;
671
672         if ( (n < 0) || ( n >= MAX_SOUNDS) )
673                 return 0;
674
675         if ( !(Sounds[n].flags & SND_F_USED) )
676                 return 0;
677         
678         ds_unload_buffer(Sounds[n].sid, Sounds[n].hid);
679         if ( Sounds[n].sid != -1 ) {
680                 Snd_sram -= Sounds[n].uncompressed_size;
681         }
682         if ( Sounds[n].hid != -1 ) {
683                 Snd_hram -= Sounds[n].uncompressed_size;
684         }
685
686         Sounds[n].flags &= ~SND_F_USED;
687
688         return 1;
689 }
690
691 // ---------------------------------------------------------------------------------------
692 // snd_unload_all() 
693 //
694 // Unload all sounds from memory.  This will release the storage, and the sound must be re-loaded via
695 // sound_load() before it can be played again.
696 //
697 void snd_unload_all()
698 {
699         int i;
700         for (i=0; i<MAX_SOUNDS; i++ )   {
701                 if ( Sounds[i].flags & SND_F_USED )
702                         snd_unload(i);
703         }
704 }
705
706 // ---------------------------------------------------------------------------------------
707 // snd_close()
708 //
709 // This is the companion function to snd_init()... it closes down the game sound system.
710 //
711 void snd_close(void)
712 {
713         snd_stop_all();
714         if (!ds_initialized) return;
715         snd_unload_all();               // free the sound data stored in DirectSound secondary buffers
716         ACM_close();    // Close the Audio Compression Manager (ACM)
717         ds3d_close();   // Close DirectSound3D
718         dscap_close();  // Close DirectSoundCapture
719         ds_close();             // Close DirectSound off
720 }
721
722 // ---------------------------------------------------------------------------------------
723 //      snd_play_raw()
724 //
725 // Allow a sound to be played directly from the index in Sounds[].  This bypasses the 
726 // normal game sound management system.
727 //
728 // returns:             -1              =>              sound could not be played
729 //                                      n               =>              handle for instance of sound
730 //
731 int snd_play_raw( int soundnum, float pan, float vol_scale, int priority )
732 {
733         game_snd gs;
734         int             rval;
735
736         gs.id = soundnum;
737         gs.id_sig = Sounds[soundnum].sig;
738         gs.filename[0] = 0;
739         gs.default_volume = 1.0f;
740 //      gs.flags = GAME_SND_VOICE | GAME_SND_USE_DS3D;
741         gs.flags = GAME_SND_VOICE;
742
743         rval = snd_play(&gs, 0.0f, vol_scale, priority, true);
744         return rval;
745 }
746
747 MONITOR( NumSoundsStarted );
748 MONITOR( NumSoundsLoaded );
749
750 // ---------------------------------------------------------------------------------------
751 //      snd_play()
752 //
753 //      NOTE: vol_scale parameter is the multiplicative scaling applied to the default volume
754 //       (vol_scale is a default parameter with a default value of 1.0f)
755 //
756 // input:       gs                              =>      game-level sound description
757 //                              pan                     =>      -1 (full left) to 1.0 (full right), this is a default parm
758 //                              vol_scale       =>      factor to scale default volume by (applied before global sound volume applied)
759 //                              priority                => SND_PRIORITY_MUST_PLAY
760 //                                                                      SND_PRIORITY_SINGLE_INSTANCE            (default value)
761 //                                                                      SND_PRIORITY_DOUBLE_INSTANCE
762 //                                                                      SND_PRIORITY_TRIPLE_INSTANCE
763 //
764 // returns:             -1              =>              sound could not be played
765 //                                      n               =>              handle for instance of sound
766 //
767 int snd_play( game_snd *gs, float pan, float vol_scale, int priority, bool is_voice_msg )
768 {
769         float volume;
770         sound   *snd;
771
772         int handle = -1;
773
774         if (!Sound_enabled)
775                 return -1;
776
777         Assert( gs != NULL );
778
779         MONITOR_INC( NumSoundsStarted, 1 );
780
781         if ( gs->id == -1 ) {
782                 gs->id = snd_load(gs);
783                 MONITOR_INC( NumSoundsLoaded, 1);
784         } else if ( gs->id_sig != Sounds[gs->id].sig ) {
785                 gs->id = snd_load(gs);
786         }
787
788         if ( gs->id == -1 )
789                 return -1;
790
791         volume = gs->default_volume * vol_scale;
792         if ( gs->flags&GAME_SND_VOICE ) {
793                 volume *= Master_voice_volume;
794         } else {
795                 volume *= Master_sound_volume;
796         }
797         if ( volume > 1.0f )
798                 volume = 1.0f;
799
800         snd = &Sounds[gs->id];
801
802         if ( !(snd->flags & SND_F_USED) )
803                 return -1;
804
805         if (!ds_initialized)
806                 return -1;
807
808         if ( volume > MIN_SOUND_VOLUME ) {
809                 handle = ds_play( snd->sid, snd->hid, gs->id_sig, ds_priority(priority), ds_convert_volume(volume), fl2i(pan*MAX_PAN), 0, is_voice_msg);
810         }
811
812         return handle;
813 }
814
815 MONITOR( Num3DSoundsStarted );
816 MONITOR( Num3DSoundsLoaded );
817
818 // ---------------------------------------------------------------------------------------
819 // snd_play_3d()
820 //
821 //      NOTE: vol_scale parameter is the multiplicative scaling applied to the default volume
822 //       (vol_scale is a default parameter with a default value of 1.0f)
823 //
824 // input:       gs                              =>      game-level sound description
825 //                              source_pos      =>      global pos of where the sound is
826 //                              listen_pos      =>      global pos of where listener is
827 //                              radius          =>      optional parameter, this specifes distance at which to apply min/max distances
828 //                              source_vel      =>      velocity of the source playing the sound (used for DirectSound3D only)
829 //                              looping         =>      flag to indicate the sound should loop (default value 0)
830 //                              vol_scale       =>      factor to scale the static volume by (applied before attenuation)
831 //                              priority                => SND_PRIORITY_MUST_PLAY
832 //                                                                      SND_PRIORITY_SINGLE_INSTANCE    (default value)
833 //                                                                      SND_PRIORITY_DOUBLE_INSTANCE
834 //                                                                      SND_PRIORITY_TRIPLE_INSTANCE
835 //                              sound_fvec              => forward vector of where sound is emitting from (RSX use only)
836 //                              range_factor    =>      factor N, which increases distance sound is heard by N times (default value 1)
837 //
838 // returns:             -1              =>              sound could not be played
839 //                                      n               =>              handle for instance of sound
840 //
841 int snd_play_3d(game_snd *gs, vector *source_pos, vector *listen_pos, float radius, vector *source_vel, int looping, float vol_scale, int priority, vector *sound_fvec, float range_factor, int force )
842 {
843         int             handle, min_range, max_range;
844         vector  vector_to_sound;
845         sound           *snd;
846         float           volume, distance, pan, max_volume;
847
848         if ( !Sound_enabled )
849                 return -1;
850
851         Assert(gs != NULL);
852
853         MONITOR_INC( Num3DSoundsStarted, 1 );
854
855         if ( gs->id == -1 ) {
856                 gs->id = snd_load(gs);
857                 MONITOR_INC( Num3DSoundsLoaded, 1 );
858         }else if ( gs->id_sig != Sounds[gs->id].sig ) {
859                 gs->id = snd_load(gs);
860         }
861
862         if ( gs->id == -1 )
863                 return -1;
864
865         snd = &Sounds[gs->id];
866
867         if ( !(snd->flags & SND_F_USED) )
868                 return -1;
869
870         handle = -1;
871
872         min_range = fl2i( (gs->min + radius) * range_factor);
873         max_range = fl2i( (gs->max + radius) * range_factor + 0.5f);
874
875         if (!ds_initialized)
876                 return -1;
877         
878         // DirectSound3D will not cut off sounds, no matter how quite they become.. so manually
879         // prevent sounds from playing past the max distance.
880         distance = vm_vec_normalized_dir_quick( &vector_to_sound, source_pos, listen_pos );
881         max_volume = gs->default_volume * vol_scale;
882         if ( (distance > max_range) && !force){
883                 return -1;
884         }
885
886         if ( distance <= min_range ) {
887                 volume = max_volume;
888         }
889         else {
890                 volume = max_volume - max_volume*(distance/max_range);
891         }
892
893         if ( volume > 1.0f ){
894                 volume = 1.0f;
895         }
896
897         if ( priority == SND_PRIORITY_MUST_PLAY ) {
898                 if ( volume < 0.3 ) {
899                         priority = SND_PRIORITY_DOUBLE_INSTANCE;
900                 } 
901         }
902
903         volume *= Master_sound_volume;
904         if ( (volume < MIN_SOUND_VOLUME) && !force) {
905                 return -1;
906         }
907
908         int play_using_ds3d = 0;
909
910         if (ds_using_ds3d()) {
911                 if ( ds_is_3d_buffer(snd->sid) ) {
912                         play_using_ds3d = 1;
913                 }
914         }
915
916         if ( play_using_ds3d ) {
917                 // play through DirectSound3D
918                 handle = ds3d_play( snd->sid, snd->hid, gs->id_sig, source_pos, source_vel, min_range, max_range, looping, ds_convert_volume(max_volume*Master_sound_volume), ds_convert_volume(volume), ds_priority(priority));
919         }
920         else {
921                 // play sound as a fake 3D sound
922                 if ( distance <= 0 ) {
923                         pan = 0.0f;
924                 }
925                 else {
926                         pan = vm_vec_dot(&View_matrix.rvec,&vector_to_sound);
927                 }
928                 if(looping){
929                         handle = snd_play_looping( gs, pan, -1, -1, volume/gs->default_volume, priority, force );
930                 } else {
931                         handle = snd_play( gs, pan, volume/gs->default_volume, priority);
932                 }
933         }
934
935         return handle;
936 }
937
938 // update the given 3d sound with a new position
939 void snd_update_3d_pos(int soundnum, game_snd *gs, vector *new_pos)
940 {
941         float vol, pan;
942         
943         // get new volume and pan vals
944         snd_get_3d_vol_and_pan(gs, new_pos, &vol, &pan);
945
946         // set volume
947         snd_set_volume(soundnum, vol);
948
949         // set pan
950         snd_set_pan(soundnum, pan);
951 }
952
953 // ---------------------------------------------------------------------------------------
954 // snd_get_3d_vol_and_pan()
955 //
956 // Based on the 3D position the player and the object, calculate
957 // the correct volume and pan.
958 //
959 // parameters:          gs                      => pointer to sound description
960 //                                              pos             => 3D position used to calc volume and pan
961 //                                              vol             => output parameter for the volume
962 //                                              pan             => output parameter for the pan
963 //                                              radius  =>      optional parameter (default value 0) which indicates sound attenuation
964 //                                                                              should occur from this radius
965 //
966 // returns:                     -1                      => could not determine vol or pan
967 //                                              0                       => success 
968 //
969 //      NOTE: the volume is not scaled by the Master_sound_volume, since this always occurs
970 //                      when snd_play() or snd_play_looping() is called
971 //
972 int snd_get_3d_vol_and_pan(game_snd *gs, vector *pos, float* vol, float *pan, float radius)
973 {
974         vector  vector_to_sound;
975         float           distance, max_volume;
976         sound           *snd;
977
978         *vol = 0.0f;
979         *pan = 0.0f;
980
981         if (!ds_initialized)
982                 return -1;
983
984         Assert(gs != NULL);
985
986         if ( gs->id == -1 ) {
987                 gs->id = snd_load(gs);
988         }
989         
990         snd = &Sounds[gs->id];
991         if ( !(snd->flags & SND_F_USED) )
992                 return -1;
993
994         distance = vm_vec_normalized_dir_quick( &vector_to_sound, pos, &View_position );
995         distance -= radius;
996
997         max_volume = gs->default_volume;
998         if ( distance <= gs->min ) {
999                 *vol = max_volume;
1000         }
1001         else {
1002                 *vol = max_volume - (distance - gs->min) * max_volume / (gs->max - gs->min);
1003         }
1004
1005         if ( *vol > 1.0f )
1006                 *vol = 1.0f;
1007
1008         if ( *vol > MIN_SOUND_VOLUME ) {
1009                 if ( distance <= 0 )
1010                         *pan = 0.0f;
1011                 else
1012                         *pan = vm_vec_dot(&View_matrix.rvec,&vector_to_sound);
1013         }
1014
1015         return 0;
1016 }
1017
1018 // ---------------------------------------------------------------------------------------
1019 // volume 0 to 1.0.  Returns the handle of the sound. -1 if failed.
1020 // If startloop or stoploop are not -1, then then are used.
1021 //
1022 //      NOTE: vol_scale parameter is the multiplicative scaling applied to the default volume
1023 //       (vol_scale is a default parameter with a default value of 1.0f)
1024 //
1025 // input:       gs                              =>      game-level sound description
1026 //                              source_pos      =>      global pos of where the sound is
1027 //                              listen_pos      =>      global pos of where listener is
1028 //                              source_vel      =>      velocity of the source playing the sound (used for DirectSound3D only)
1029 //                              looping         =>      flag to indicate the sound should loop (default value 0)
1030 //                              vol_scale       =>      factor to scale the static volume by (applied before attenuation)
1031 //                              priority                => SND_PRIORITY_MUST_PLAY                       (default value)
1032 //                                                                      SND_PRIORITY_SINGLE_INSTANCE
1033 //                                                                      SND_PRIORITY_DOUBLE_INSTANCE
1034 //                                                                      SND_PRIORITY_TRIPLE_INSTANCE
1035 //
1036 // returns:             -1              =>              sound could not be played
1037 //                                      n               =>              handle for instance of sound
1038 //
1039 int snd_play_looping( game_snd *gs, float pan, int start_loop, int stop_loop, float vol_scale, int priority, int force )
1040 {       
1041         float volume;
1042         int     handle = -1;
1043         sound   *snd;   
1044
1045         if (!Sound_enabled)
1046                 return -1;
1047
1048         Assert( gs != NULL );
1049
1050         if (!ds_initialized)
1051                 return -1;
1052
1053         if ( gs->id == -1 ) {
1054                 gs->id = snd_load(gs);
1055         }
1056         else if ( gs->id_sig != Sounds[gs->id].sig ) {
1057                 gs->id = snd_load(gs);
1058         }
1059
1060         if ( gs->id == -1 )
1061                 return -1;
1062
1063         snd = &Sounds[gs->id];
1064
1065         if ( !(snd->flags & SND_F_USED) )
1066                 return -1;
1067
1068         volume = gs->default_volume * vol_scale;
1069         volume *= Master_sound_volume;
1070         if ( volume > 1.0f )
1071                 volume = 1.0f;
1072
1073         if ( (volume > MIN_SOUND_VOLUME) || force) {
1074                 handle = ds_play( snd->sid, snd->hid, gs->id_sig, ds_priority(priority), ds_convert_volume(volume), fl2i(pan*MAX_PAN), 1);
1075         }
1076
1077         return handle;
1078 }
1079
1080 // ---------------------------------------------------------------------------------------
1081 // snd_stop()
1082 //
1083 // Stop a sound from playing.
1084 //
1085 // parameters:          sig => handle to sound, what is returned from snd_play()
1086 //
1087 void snd_stop( int sig )
1088 {
1089         int channel;
1090
1091         if (!ds_initialized) return;
1092         if ( sig < 0 ) return;
1093
1094         channel = ds_get_channel(sig);
1095         if ( channel == -1 )
1096                 return;
1097         
1098         ds_stop_channel(channel);
1099 }
1100
1101 // ---------------------------------------------------------------------------------------
1102 // snd_set_volume()
1103 //
1104 // Set the volume of a currently playing sound
1105 //
1106 // parameters:          sig             => handle to sound, what is returned from snd_play()
1107 //                                              volume  => volume of sound (range: 0.0 -> 1.0)
1108 //
1109 void snd_set_volume( int sig, float volume )
1110 {
1111         int     channel;
1112         float   new_volume;
1113
1114         if (!ds_initialized)
1115                 return;
1116
1117         if ( sig < 0 )
1118                 return;
1119
1120         channel = ds_get_channel(sig);
1121         if ( channel == -1 ) {
1122                 nprintf(( "Sound", "WARNING: Trying to set volume for a non-playing sound.\n" ));
1123                 return;
1124         }
1125
1126         new_volume = volume * Master_sound_volume;
1127         ds_set_volume( channel, ds_convert_volume(new_volume) );
1128 }
1129
1130 // ---------------------------------------------------------------------------------------
1131 // snd_set_pan()
1132 //
1133 // Set the pan of a currently playing sound
1134 //
1135 // parameters:          sig     => handle to sound, what is returned from snd_play()
1136 //                                              pan     => pan of sound (range: -1.0 -> 1.0)
1137 //
1138 void snd_set_pan( int sig, float pan )
1139 {
1140         int channel;
1141
1142         if (!ds_initialized)
1143                 return;
1144
1145         if ( sig < 0 )
1146                 return;
1147         
1148         channel = ds_get_channel(sig);
1149         if ( channel == -1 ) {
1150                 nprintf(( "Sound", "WARNING: Trying to set pan for a non-playing sound.\n" ));
1151                 return;
1152         }
1153
1154         ds_set_pan( channel, fl2i(pan*MAX_PAN) );
1155 }
1156
1157 // ---------------------------------------------------------------------------------------
1158 // snd_get_pitch()
1159 //
1160 // Return the pitch of a currently playing sound
1161 //
1162 // returns:                     pitch of sound ( range: 100 to 100000)
1163 //
1164 // parameters:          sig     => handle to sound, what is returned from snd_play()
1165 //
1166 int snd_get_pitch(int sig)
1167 {
1168         int channel, pitch=10000;
1169
1170         if (!ds_initialized)
1171                 return -1;
1172
1173         if ( sig < 0 )
1174                 return -1;
1175
1176         channel = ds_get_channel(sig);
1177         if ( channel == -1 ) {
1178                 nprintf(( "Sound", "WARNING: Trying to get pitch for a non-playing sound.\n" ));
1179                 return -1;
1180         }
1181
1182         pitch = ds_get_pitch(channel);
1183
1184         return pitch;
1185 }
1186
1187 // ---------------------------------------------------------------------------------------
1188 // snd_set_pitch()
1189 //
1190 // Set the pitch of a currently playing sound
1191 //
1192 // parameters:          sig             => handle to sound, what is returned from snd_play()
1193 //                                              pan             => pitch of sound (range: 100 to 100000)
1194 //
1195 void snd_set_pitch( int sig, int pitch )
1196 {
1197         int channel;
1198
1199         if (!ds_initialized) return;
1200         if ( sig < 0 ) return;
1201
1202         channel = ds_get_channel(sig);
1203         if ( channel == -1 ) {
1204                 nprintf(( "Sound", "WARNING: Trying to set pitch for a non-playing sound.\n" ));
1205                 return;
1206         }
1207
1208         ds_set_pitch(channel, pitch);
1209 }
1210
1211 // ---------------------------------------------------------------------------------------
1212 // snd_is_playing()
1213 //
1214 // Determine if a sound is playing
1215 //
1216 // returns:                     1                               => sound is currently playing
1217 //                                              0                               => sound is not playing
1218 //
1219 // parameters:          sig     => signature of sound, what is returned from snd_play()
1220 //
1221 int snd_is_playing( int sig )
1222 {
1223         int     channel, is_playing;
1224
1225         if (!ds_initialized)
1226                 return 0;
1227
1228         if ( sig < 0 )
1229                 return 0;
1230
1231         channel = ds_get_channel(sig);
1232         if ( channel == -1 )
1233                 return 0;
1234
1235         is_playing = ds_is_channel_playing(channel);
1236         if ( is_playing == TRUE ) {
1237                 return 1;
1238         }
1239
1240         return 0;
1241 }
1242
1243
1244 // ---------------------------------------------------------------------------------------
1245 // snd_chg_loop_status()
1246 //
1247 // Change whether a currently playing song is looping or not
1248 //
1249 // parameters:          sig                     => handle to sound, what is returned from snd_play()
1250 //                                              loop                    => whether to start (1) or stop (0) looping
1251 //
1252 void snd_chg_loop_status(int sig, int loop)
1253 {
1254         int channel;
1255
1256         if (!ds_initialized)
1257                 return;
1258
1259         if ( sig < 0 )
1260                 return;
1261
1262         channel = ds_get_channel(sig);
1263         if ( channel == -1 ) {
1264                 nprintf(( "Sound", "WARNING: Trying to change loop status of a non-playing sound!\n" ));
1265                 return;
1266         }
1267
1268         ds_chg_loop_status(channel, loop);
1269 }
1270
1271 // ---------------------------------------------------------------------------------------
1272 // snd_stop_all()
1273 //
1274 // Stop all playing sound channels (including looping sounds)
1275 //
1276 // NOTE: This stops all sounds that are playing from Channels[] sound buffers.  It doesn't
1277 //                      stop every secondary sound buffer in existance
1278 //
1279 void snd_stop_all()
1280 {
1281         if (!ds_initialized)
1282                 return;
1283
1284         ds_stop_channel_all();
1285 }
1286
1287 // ---------------------------------------------------------------------------------------
1288 // sound_get_ds()
1289 //
1290 // Return the pointer to the DirectSound interface
1291 //
1292 //
1293 uint sound_get_ds()
1294 {
1295 #ifdef PLAT_UNIX
1296         // unused
1297         return 0;
1298 #else
1299         return (uint)pDirectSound;
1300 #endif
1301 }
1302
1303 // ---------------------------------------------------------------------------------------
1304 // snd_is_inited()
1305 //
1306 // 
1307 int snd_is_inited()
1308 {
1309         if ( !ds_initialized )
1310                 return FALSE;
1311
1312         return TRUE;
1313 }
1314
1315 // return the time in ms for the duration of the sound
1316 int snd_get_duration(int snd_id)
1317 {
1318         if ( snd_id < 0 )
1319                 return 0;
1320
1321         return Sounds[snd_id].duration;
1322 }
1323
1324
1325 MONITOR( SoundChannels );
1326
1327 // update the position of the listener for the specific 3D sound API we're 
1328 // using
1329 void snd_update_listener(vector *pos, vector *vel, matrix *orient)
1330 {
1331         MONITOR_INC( SoundChannels, ds_get_number_channels() );
1332         ds3d_update_listener(pos, vel, orient);
1333 }
1334
1335 // this could probably be optimized a bit
1336 void snd_rewind(int snd_handle, game_snd *gs, float seconds)
1337 {                       
1338         float current_time,desired_time;
1339         float bps;
1340         DWORD current_offset,desired_offset;
1341         sound_info *snd;
1342
1343         if(!snd_is_playing(snd_handle))
1344                 return;
1345
1346         snd = &Sounds[gs->id].info;
1347         
1348         current_offset = ds_get_play_position(ds_get_channel(snd_handle));      // current offset into the sound
1349         bps = (float)snd->sample_rate * (float)snd->bits;                                                       // data rate
1350         current_time = (float)current_offset/bps;                                                                               // how many seconds we're into the sound
1351
1352         // don't rewind if it'll put us before the beginning of the sound
1353         if(current_time - seconds < 0.0f)
1354                 return;
1355
1356         desired_time = current_time - seconds;                                                                                  // where we want to be
1357         desired_offset = (DWORD)(desired_time * bps);                                                           // the target
1358                         
1359         ds_set_position(ds_get_channel(snd_handle),desired_offset);
1360 }
1361
1362 // this could probably be optimized a bit
1363 void snd_ffwd(int snd_handle, game_snd *gs, float seconds)
1364 {
1365         if(!snd_is_playing(snd_handle))
1366                 return;
1367
1368         float current_time,desired_time;
1369         float bps;
1370         DWORD current_offset,desired_offset;
1371         sound_info *snd;
1372
1373         if(!snd_is_playing(snd_handle))
1374                 return;
1375
1376         snd = &Sounds[gs->id].info;
1377
1378         current_offset = ds_get_play_position(ds_get_channel(snd_handle));      // current offset into the sound
1379         bps = (float)snd->sample_rate * (float)snd->bits;                                                       // data rate
1380         current_time = (float)current_offset/bps;                                                                               // how many seconds we're into the sound
1381
1382         // don't rewind if it'll put us past the end of the sound
1383         if(current_time + seconds > (float)snd->duration)
1384                 return;
1385
1386         desired_time = current_time + seconds;                                                                                  // where we want to be
1387         desired_offset = (DWORD)(desired_time * bps);                                                           // the target
1388                         
1389         ds_set_position(ds_get_channel(snd_handle),desired_offset);
1390 }
1391
1392 // this could probably be optimized a bit
1393 void snd_set_pos(int snd_handle, game_snd *gs, float val,int as_pct)
1394 {
1395         if(!snd_is_playing(snd_handle))
1396                 return;
1397
1398         sound_info *snd;
1399
1400         snd = &Sounds[gs->id].info;             
1401         // set position as an absolute from 0 to 1
1402         if(as_pct){
1403                 Assert((val >= 0.0) && (val <= 1.0));
1404                 ds_set_position(ds_get_channel(snd_handle),(DWORD)((float)snd->size * val));
1405         } 
1406         // set the position as an absolute # of seconds from the beginning of the sound
1407         else {
1408                 float bps;
1409                 Assert(val <= (float)snd->duration/1000.0f);
1410                 bps = (float)snd->sample_rate * (float)snd->bits;                                                       // data rate                    
1411                 ds_set_position(ds_get_channel(snd_handle),(DWORD)(bps * val));
1412         }
1413 }
1414
1415 // Return the number of sounds currently playing
1416 int snd_num_playing()
1417 {
1418         return ds_get_number_channels();
1419 }
1420
1421 // Stop the first channel found that is playing a sound
1422 void snd_stop_any_sound()
1423 {
1424         int i;
1425
1426         for ( i = 0; i < 16; i++ ) {
1427                 if ( ds_is_channel_playing(i) ) {
1428                         ds_stop_channel(i);
1429                         break;
1430                 }
1431         }
1432 }
1433
1434 // Return the raw sound data for a loaded sound
1435 //
1436 // input:       handle  =>      index into Sounds[] array
1437 //                              data            =>      allocated mem to hold sound
1438 //
1439 // exit:                0       =>      success
1440 //                              !0      =>      fail
1441 int snd_get_data(int handle, char *data)
1442 {
1443         Assert(handle >= 0 && handle < MAX_SOUNDS);
1444         if ( ds_get_data(Sounds[handle].sid, data) ) {
1445                 return -1;
1446         }
1447
1448         return 0;
1449 }
1450
1451 // return the size of the sound data associated with the sound handle
1452 int snd_size(int handle, int *size)
1453 {
1454         Assert(handle >= 0 && handle < MAX_SOUNDS);
1455         if ( ds_get_size(Sounds[handle].sid, size) ) {
1456                 return -1;
1457         }
1458
1459         return 0;
1460 }
1461
1462 // retrieve the bits per sample and frequency for a given sound
1463 void snd_get_format(int handle, int *bits_per_sample, int *frequency)
1464 {
1465         Assert(handle >= 0 && handle < MAX_SOUNDS);
1466         *bits_per_sample = Sounds[handle].info.bits;
1467         *frequency = Sounds[handle].info.sample_rate;
1468 }
1469
1470 // return the time for the sound to play in milliseconds
1471 int snd_time_remaining(int handle, int bits_per_sample, int frequency)
1472 {
1473         int channel, is_playing, time_remaining = 0;
1474
1475         if (!ds_initialized)
1476                 return 0;
1477
1478         if ( handle < 0 )
1479                 return 0;
1480
1481         channel = ds_get_channel(handle);
1482         if ( channel == -1 )
1483                 return 0;
1484
1485         is_playing = ds_is_channel_playing(channel);
1486         if ( !is_playing ) {
1487                 return 0;
1488         }
1489
1490         int current_offset, max_offset;
1491
1492         current_offset = ds_get_play_position(channel);
1493         max_offset = ds_get_channel_size(channel);
1494
1495         if ( current_offset < max_offset ) {
1496                 int bytes_remaining = max_offset - current_offset;
1497                 int samples_remaining = bytes_remaining / fl2i(bits_per_sample/8.0f);
1498                 time_remaining = fl2i(1000 * samples_remaining/frequency + 0.5f);
1499         }       
1500
1501 //      mprintf(("time_remaining: %d\n", time_remaining));      
1502         return time_remaining;
1503 }
1504
1505
1506 // snd_env_ interface
1507
1508 static unsigned long Sound_env_id;
1509 static float Sound_env_volume;
1510 static float Sound_env_damping;
1511 static float Sound_env_decay;
1512
1513 // Set the sound environment
1514 //
1515 int sound_env_set(sound_env *se)
1516 {
1517         if (ds_eax_set_all(se->id, se->volume, se->damping, se->decay) == 0) {
1518                 Sound_env_id = se->id;
1519                 Sound_env_volume = se->volume;
1520                 Sound_env_damping = se->damping;
1521                 Sound_env_decay = se->decay;
1522                 return 0;
1523         } else {
1524                 return -1;
1525         }
1526 }
1527
1528 // Get the sound environment
1529 //
1530 int sound_env_get(sound_env *se)
1531 {
1532         EAX_REVERBPROPERTIES er;
1533
1534         if (ds_eax_get_all(&er) == 0) {
1535                 se->id = er.environment;
1536                 se->volume = er.fVolume;
1537                 se->decay = er.fDecayTime_sec;
1538                 se->damping = er.fDamping;
1539                 return 0;
1540         } else {
1541                 return -1;
1542         }
1543 }
1544
1545 // Turn off the sound environment
1546 //
1547 int sound_env_disable()
1548 {
1549         sound_env se;
1550         se.id = SND_ENV_GENERIC;
1551         se.volume = 0.0f;
1552         se.damping = 0.0f;
1553         se.decay = 0.0f;
1554         sound_env_set(&se);
1555         return 0;
1556 }
1557
1558 // Return 1 if EAX can used to set the sound environment, otherwise return 0
1559 //
1560 int sound_env_supported()
1561 {
1562         return ds_eax_is_inited();
1563 }
1564
1565 // Called once per game frame
1566 //
1567 void snd_do_frame()
1568 {
1569         ds_do_frame();
1570 }