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