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