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