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