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