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