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