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