2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Sound/Sound.cpp $
15 * Low-level sound code
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
25 * Revision 1.9 2002/06/22 16:34:40 relnev
28 * Revision 1.8 2002/06/17 06:33:11 relnev
29 * ryan's struct patch for gcc 2.95
31 * Revision 1.7 2002/06/09 04:41:27 relnev
32 * added copyright header
34 * Revision 1.6 2002/06/05 08:05:29 relnev
35 * stub/warning removal.
37 * reworked the sound code.
39 * Revision 1.5 2002/06/02 21:11:12 cemason
42 * Revision 1.4 2002/05/27 01:06:01 theoddone33
45 * Revision 1.3 2002/05/27 00:40:47 theoddone33
46 * Fix net_addr vs net_addr_t
48 * Revision 1.2 2002/05/07 03:16:52 theoddone33
49 * The Great Newline Fix
51 * Revision 1.1.1.1 2002/05/03 03:28:10 root
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.
59 * 8 8/27/99 6:38p Alanl
60 * crush the blasted repeating messages bug
62 * 7 8/22/99 11:06p Alanl
63 * don't convert priority in snd_play_3d
65 * 6 8/04/99 9:48p Alanl
66 * fix bug with setting 3D properties on a 2D sound buffer
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.
72 * 4 5/23/99 8:11p Alanl
73 * Added support for EAX
75 * 3 1/29/99 12:47a Dave
76 * Put in sounds for beam weapon. A bunch of interface screens (tech
79 * 2 10/07/98 10:54a Dave
82 * 1 10/07/98 10:51a Dave
84 * 77 6/13/98 6:02p Hoffoss
85 * Externalized all new (or forgot to be added) strings to all the code.
87 * 76 5/23/98 9:27p Lawrance
88 * Change ACM failure message to use a message box, not a Warning()
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.
94 * 74 5/12/98 5:39p Lawrance
95 * Increase MAX_SOUNDS to 175.. to account for missions with lots of voice
97 * 73 5/12/98 2:43p Lawrance
98 * Make snd_time_remaining() work for arbitrary format
100 * 72 5/11/98 2:02p Andsager
103 * 71 5/11/98 1:56p Andsager
104 * increase min_range in snd_play_3d when range_factor is not 1.0
106 * 70 5/10/98 3:49p Sandeep
107 * Fix problem with having the audio streaming while trying to close down
110 * 69 5/09/98 10:48p Lawrance
111 * Default voice volume to 0.7
113 * 68 5/05/98 4:49p Lawrance
114 * Put in code to authenticate A3D, improve A3D support
116 * 67 4/25/98 12:01p Lawrance
117 * try to init sound for 5 seconds... to overcome conflicts with launch
120 * 66 4/20/98 12:03a Lawrance
121 * Allow prioritizing of CTRL3D buffers
123 * 65 4/19/98 9:30p Lawrance
124 * Use Aureal_enabled flag
126 * 64 4/18/98 9:12p Lawrance
127 * Added Aureal support.
129 * 63 4/14/98 3:29p John
132 * 62 4/13/98 5:04p Lawrance
133 * Write functions to determine how many milliseconds are left in a sound
135 * 61 4/09/98 5:53p Lawrance
136 * Make DirectSound init more robust
138 * 60 4/01/98 9:21p John
139 * Made NDEBUG, optimized build with no warnings or errors.
141 * 59 3/29/98 12:56a Lawrance
142 * preload the warp in and explosions sounds before a mission.
144 * 58 3/25/98 6:10p Lawrance
145 * Work on DirectSound3D
147 * 57 3/24/98 4:28p Lawrance
148 * Make DirectSound3D support more robust
150 * 56 3/23/98 10:32a Lawrance
151 * Add functions for extracting raw sound data
153 * 55 3/21/98 3:34p Lawrance
154 * Allow 3d sounds to have their ranges modified dynamically
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.
160 * 53 3/17/98 5:55p Lawrance
161 * Support object-linked sounds for asteroids.
163 * 52 2/20/98 8:32p Lawrance
164 * Add radius parm to sound_play_3d()
166 * 51 2/18/98 5:49p Lawrance
167 * Even if the ADPCM codec is unavailable, allow game to continue.
169 * 50 2/15/98 4:43p Lawrance
170 * work on real-time voice
172 * 49 2/06/98 7:30p John
173 * Added code to monitor the number of channels of sound actually playing.
175 * 48 2/05/98 9:21p John
176 * Some new Direct3D code. Added code to monitor a ton of stuff in the
179 * 47 1/31/98 5:57p Lawrance
180 * remove debug code that was stopping any sounds from playing
182 * 46 1/31/98 5:48p Lawrance
183 * Start on real-time voice recording
185 * 45 1/19/98 11:37p Lawrance
186 * Fixing Optimization build warnings
188 * 44 1/13/98 5:36p Lawrance
189 * Reduce default sound volume to 0.9.
191 * 43 1/11/98 11:14p Lawrance
192 * Preload sounds that we expect will get played.
194 * 42 1/10/98 1:14p John
195 * Added explanation to debug console commands
197 * 41 1/07/98 11:08a Lawrance
198 * pass priority to snd_play_raw()
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.
205 * 39 12/05/97 5:19p Lawrance
206 * re-do sound priorities to make more general and extensible
208 * 38 12/04/97 10:21a Lawrance
209 * if a 3D sound has priority, only play if above a minimum volume level
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
217 * 36 11/20/97 1:06a Lawrance
218 * Add Master_voice_volume, make voices play back at correctly scaled
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.
226 * 34 10/14/97 11:33p Lawrance
227 * get RSX implemented
229 * 33 10/13/97 7:41p Lawrance
230 * store duration of sound
232 * 32 10/06/97 4:12p Lawrance
233 * fix volume bug with 3d sounds
235 * 31 10/01/97 5:55p Lawrance
236 * change call to snd_play_3d() to allow for arbitrary listening position
238 * 30 9/09/97 3:39p Sandeep
239 * warning level 4 bugs
241 * 29 9/03/97 5:03p Lawrance
242 * add support for -nosound command line parm
244 * 28 7/31/97 11:16a Dan
245 * Add some Asserts() to check validity of vectors send to DirectSound3D
247 * 27 7/28/97 11:39a Lawrance
248 * allow individual volume scaling on 3D buffers
250 * 26 7/17/97 9:53a Lawrance
251 * if a sound is supposed to use DirectSound3D, ensure it does when
254 * 25 7/17/97 9:32a John
255 * made all directX header files name start with a v
257 * 24 7/15/97 11:15a Lawrance
258 * limit the max instances of simultaneous sound effects, implement
259 * priorities to force critical sounds
261 * 23 7/13/97 5:52p Lawrance
262 * allow snd_set_volume() to set volume at any level
264 * 22 6/13/97 4:44p Lawrance
265 * added another sound hook in ship selection
267 * 21 6/09/97 11:50p Lawrance
268 * integrating DirectSound3D
270 * 20 6/05/97 11:25a Lawrance
271 * use sound signatures to ensure correct sound is loaded
273 * 19 6/05/97 1:36a Lawrance
274 * using a new interface to play sounds
276 * 18 6/05/97 1:08a Lawrance
277 * new sound play interface
279 * 17 6/03/97 9:23a Lawrance
280 * fix bug that caused when sound not found
282 * 16 6/02/97 1:45p Lawrance
283 * implementing hardware mixing
285 * 15 5/29/97 4:01p Lawrance
286 * don't fail sound init if dsound3d fails init
288 * 14 5/29/97 3:32p Lawrance
289 * added call to snd_do_frame()
291 * 13 5/29/97 12:04p Lawrance
292 * split off acm, direct sound, and direct sound 3d portions to separate
295 * 12 5/21/97 3:55p Lawrance
296 * when reclaiming sound channels, never stop a looping sound
298 * 11 5/19/97 4:32p Lawrance
299 * remove misleading nprintf
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)
305 * 9 5/12/97 10:32a Lawrance
308 * 8 5/09/97 4:51p Lawrance
311 * 7 5/09/97 4:34p Lawrance
314 * 6 5/07/97 3:29p Lawrance
315 * make volume conversion use a lookup table, fix errors in volume
318 * 5 5/07/97 11:33a Lawrance
319 * improve snd_chg_loop_status()
321 * 4 5/06/97 9:36a Lawrance
322 * added support for min and max distances for 3d sounds
324 * 3 5/02/97 4:36p Lawrance
325 * send correct volume scaling when playing a 3D sound
327 * 2 4/28/97 5:13p John
328 * more moving sound/movie code out of osapi.
334 #include "3dinternal.h"
336 #include "audiostr.h"
342 #include "alphacolors.h"
345 #include "oal_capture.h"
350 #define SND_F_USED (1<<0) // Sounds[] element is used
352 typedef struct sound {
353 int sid; // software id
354 char filename[MAX_FILENAME_LEN];
358 int uncompressed_size; // size (in bytes) of sound (uncompressed)
362 sound Sounds[MAX_SOUNDS];
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
369 // min volume to play a sound after all volume processing (range is 0.0 -> 1.0)
370 #define MIN_SOUND_VOLUME 0.05f
372 static int snd_next_sig = 1;
379 // flag all Sounds[] as free
380 for (i=0; i<MAX_SOUNDS; i++ ) {
381 Sounds[i].flags &= ~SND_F_USED;
385 // reset how much storage sounds are taking up in memory
389 // ---------------------------------------------------------------------------------------
390 // Initialize the game sound system
392 // Initialize the game sound system. Depending on what sound library is being used,
393 // call the appropriate low-level initiailizations
395 // returns: 1 => init success
398 int snd_init(int use_eax)
402 if ( Cmdline_freespace_no_sound )
406 nprintf(( "Sound", "SOUND => Sound is already initialized!\n" ));
413 rval = oal_init(use_eax);
416 nprintf(( "Sound", "SOUND => Direct Sound init unsuccessful, continuing without sound.\n" ));
420 // Init the audio streaming stuff
433 CFILE *out = cfopen("sounds.txt", "wt", CFILE_NORMAL, CF_TYPE_DATA);
438 cfwrite_string("Sounds loaded :\n", out);
440 // spew info for all sounds
441 for(idx=0; idx<MAX_SOUNDS; idx++){
442 if(!(Sounds[idx].flags & SND_F_USED)){
446 sprintf(txt, "%s (%ds)\n", Sounds[idx].filename, Sounds[idx].info.duration);
447 cfwrite_string(txt, out);
460 Sound_spew = !Sound_spew;
462 dc_printf("Sound debug info ON");
464 dc_printf("Sound debug info OFF");
467 void snd_spew_debug_info()
470 int message_sounds = 0;
471 int interface_sounds = 0;
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){
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)){
496 for(s_idx=0; s_idx<MAX_GAME_SOUNDS; s_idx++){
497 if(!SDL_strcasecmp(Snds_iface[s_idx].filename, Sounds[idx].filename)){
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);
517 // ---------------------------------------------------------------------------------------
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.
524 // parameters: gs => file of sound to load
525 // allow_hardware_load => whether to try to allocate in hardware
527 // returns: success => index of sound in Sounds[] array
530 //int snd_load( char *filename, int hardware, int use_ds3d, int *sig)
531 int snd_load(game_snd *gs)
536 WAVE_chunk *header = NULL;
538 if ( !Sound_enabled ) {
542 if ( gs->filename == NULL || gs->filename[0] == 0 )
545 for (n=0; n<MAX_SOUNDS; n++ ) {
546 if (!(Sounds[n].flags & SND_F_USED))
548 else if ( !SDL_strcasecmp( Sounds[n].filename, gs->filename )) {
549 gs->sig = Sounds[n].sig;
554 if ( n == MAX_SOUNDS ) {
568 if ( oal_parse_wave(gs->filename, &si->data, &si->size, &header) == -1 )
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
578 snd->duration = fl2i(1000.0f * (si->size / (si->bits/8.0f)) / si->sample_rate);
580 nprintf(("SOUND", "SOUND ==> duration = %dms (%d %d %d)\n", snd->duration,
581 si->size, si->bits, si->sample_rate));
583 rc = oal_load_buffer(&snd->sid, &snd->uncompressed_size, header, si, gs->flags);
585 if (header->extra_data != NULL) {
586 free(header->extra_data);
590 free(si->data); // don't want to keep this around
595 strncpy( snd->filename, gs->filename, MAX_FILENAME_LEN );
596 snd->flags = SND_F_USED;
598 snd->sig = snd_next_sig++;
599 if (snd_next_sig < 0 ) snd_next_sig = 1;
600 gs->id_sig = snd->sig;
603 nprintf(("Sound", "Loaded %s\n", gs->filename));
608 // ---------------------------------------------------------------------------------------
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.
614 int snd_unload( int n )
616 if ( !Sound_enabled ) {
620 if ( (n < 0) || ( n >= MAX_SOUNDS) )
623 if ( !(Sounds[n].flags & SND_F_USED) )
626 oal_unload_buffer(Sounds[n].sid);
628 if ( Sounds[n].sid != -1 ) {
629 Snd_sram -= Sounds[n].uncompressed_size;
632 Sounds[n].flags &= ~SND_F_USED;
637 // ---------------------------------------------------------------------------------------
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.
643 void snd_unload_all()
645 if ( !Sound_enabled ) {
650 for (i=0; i<MAX_SOUNDS; i++ ) {
651 if ( Sounds[i].flags & SND_F_USED )
656 // ---------------------------------------------------------------------------------------
659 // This is the companion function to snd_init()... it closes down the game sound system.
663 if ( !Sound_enabled ) {
668 snd_unload_all(); // free the sound data stored in secondary buffers
670 oal_capture_close(); // Close Capture
675 // ---------------------------------------------------------------------------------------
678 // Allow a sound to be played directly from the index in Sounds[]. This bypasses the
679 // normal game sound management system.
681 // returns: -1 => sound could not be played
682 // n => handle for instance of sound
684 int snd_play_raw( int soundnum, float pan, float vol_scale, int priority )
689 if ( !Sound_enabled ) {
694 gs.id_sig = Sounds[soundnum].sig;
696 gs.default_volume = 1.0f;
697 // gs.flags = GAME_SND_VOICE | GAME_SND_USE_DS3D;
698 gs.flags = GAME_SND_VOICE;
700 rval = snd_play(&gs, 0.0f, vol_scale, priority, true);
704 MONITOR( NumSoundsStarted );
705 MONITOR( NumSoundsLoaded );
707 // ---------------------------------------------------------------------------------------
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)
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
721 // returns: -1 => sound could not be played
722 // n => handle for instance of sound
724 int snd_play( game_snd *gs, float pan, float vol_scale, int priority, bool is_voice_msg )
735 SDL_assert( gs != NULL );
737 MONITOR_INC( NumSoundsStarted, 1 );
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);
749 volume = gs->default_volume * vol_scale;
750 if ( gs->flags&GAME_SND_VOICE ) {
751 volume *= Master_voice_volume;
753 volume *= Master_sound_volume;
758 snd = &Sounds[gs->id];
760 if ( !(snd->flags & SND_F_USED) )
764 flags |= SND_FLAG_VOICE;
767 if ( volume > MIN_SOUND_VOLUME ) {
768 handle = oal_play( snd->sid, gs->id_sig, priority, volume, pan, flags);
774 MONITOR( Num3DSoundsStarted );
775 MONITOR( Num3DSoundsLoaded );
777 // ---------------------------------------------------------------------------------------
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)
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)
797 // returns: -1 => sound could not be played
798 // n => handle for instance of sound
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 )
802 int handle, min_range, max_range;
803 vector vector_to_sound;
805 float volume, distance, max_volume;
807 if ( !Sound_enabled )
810 SDL_assert(gs != NULL);
812 MONITOR_INC( Num3DSoundsStarted, 1 );
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);
824 snd = &Sounds[gs->id];
826 if ( !(snd->flags & SND_F_USED) )
831 min_range = fl2i( (gs->min + radius) * range_factor);
832 max_range = fl2i( (gs->max + radius) * range_factor + 0.5f);
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){
841 if ( distance <= min_range ) {
845 volume = max_volume - max_volume*(distance/max_range);
848 if ( volume > 1.0f ){
852 if ( priority == SND_PRIORITY_MUST_PLAY ) {
853 if ( volume < 0.3 ) {
854 priority = SND_PRIORITY_DOUBLE_INSTANCE;
858 volume *= Master_sound_volume;
859 if ( (volume < MIN_SOUND_VOLUME) && !force) {
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);
868 // update the given 3d sound with a new position
869 void snd_update_3d_pos(int soundnum, game_snd *gs, vector *new_pos)
873 if ( !Sound_enabled ) {
880 int channel = oal_get_channel(soundnum);
882 if ( channel == -1 ) {
883 nprintf(( "Sound", "WARNING: Trying to update position for a non-playing sound.\n" ));
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);
893 snd_set_volume(soundnum, vol);
896 snd_set_pan(soundnum, pan);
900 // ---------------------------------------------------------------------------------------
901 // snd_get_3d_vol_and_pan()
903 // Based on the 3D position the player and the object, calculate
904 // the correct volume and pan.
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
913 // returns: -1 => could not determine vol or pan
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
919 int snd_get_3d_vol_and_pan(game_snd *gs, vector *pos, float* vol, float *pan, float radius)
921 vector vector_to_sound;
922 float distance, max_volume;
928 if ( !Sound_enabled ) {
932 SDL_assert(gs != NULL);
934 if ( gs->id == -1 ) {
935 gs->id = snd_load(gs);
938 snd = &Sounds[gs->id];
939 if ( !(snd->flags & SND_F_USED) )
942 distance = vm_vec_normalized_dir_quick( &vector_to_sound, pos, &View_position );
945 max_volume = gs->default_volume;
946 if ( distance <= gs->min ) {
950 *vol = max_volume - (distance - gs->min) * max_volume / (gs->max - gs->min);
956 if ( *vol > MIN_SOUND_VOLUME ) {
960 *pan = vm_vec_dot(&View_matrix.v.rvec,&vector_to_sound);
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.
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)
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
984 // returns: -1 => sound could not be played
985 // n => handle for instance of sound
987 int snd_play_looping( game_snd *gs, float pan, float vol_scale, int priority, int force )
996 SDL_assert( gs != NULL );
998 if ( gs->id == -1 ) {
999 gs->id = snd_load(gs);
1001 else if ( gs->id_sig != Sounds[gs->id].sig ) {
1002 gs->id = snd_load(gs);
1008 snd = &Sounds[gs->id];
1010 if ( !(snd->flags & SND_F_USED) )
1013 volume = gs->default_volume * vol_scale;
1014 volume *= Master_sound_volume;
1016 if ( volume > 1.0f )
1020 if ( (volume > MIN_SOUND_VOLUME) || force) {
1021 handle = oal_play( snd->sid, gs->id_sig, priority, volume, pan, SND_FLAG_LOOPING);
1027 // ---------------------------------------------------------------------------------------
1030 // Stop a sound from playing.
1032 // parameters: sig => handle to sound, what is returned from snd_play()
1034 void snd_stop( int sig )
1038 if ( !Sound_enabled ) {
1046 channel = oal_get_channel(sig);
1047 if ( channel == -1 )
1050 oal_stop_channel(channel);
1053 // ---------------------------------------------------------------------------------------
1056 // Set the volume of a currently playing sound
1058 // parameters: sig => handle to sound, what is returned from snd_play()
1059 // volume => volume of sound (range: 0.0 -> 1.0)
1061 void snd_set_volume( int sig, float volume )
1066 if ( !Sound_enabled ) {
1074 channel = oal_get_channel(sig);
1075 if ( channel == -1 ) {
1076 nprintf(( "Sound", "WARNING: Trying to set volume for a non-playing sound.\n" ));
1080 new_volume = volume * Master_sound_volume;
1081 oal_set_volume( channel, new_volume );
1084 // ---------------------------------------------------------------------------------------
1087 // Set the pan of a currently playing sound
1089 // parameters: sig => handle to sound, what is returned from snd_play()
1090 // pan => pan of sound (range: -1.0 -> 1.0)
1092 void snd_set_pan( int sig, float pan )
1096 if ( !Sound_enabled ) {
1104 channel = oal_get_channel(sig);
1105 if ( channel == -1 ) {
1106 nprintf(( "Sound", "WARNING: Trying to set pan for a non-playing sound.\n" ));
1110 oal_set_pan( channel, pan );
1113 // ---------------------------------------------------------------------------------------
1116 // Return the pitch of a currently playing sound
1118 // returns: pitch of sound ( range: > 0)
1120 // parameters: sig => handle to sound, what is returned from snd_play()
1122 float snd_get_pitch(int sig)
1127 if ( !Sound_enabled ) {
1135 channel = oal_get_channel(sig);
1136 if ( channel == -1 ) {
1137 nprintf(( "Sound", "WARNING: Trying to get pitch for a non-playing sound.\n" ));
1141 pitch = oal_get_pitch(channel);
1146 // ---------------------------------------------------------------------------------------
1149 // Set the pitch of a currently playing sound
1151 // parameters: sig => handle to sound, what is returned from snd_play()
1152 // pan => pitch of sound (range: 100 to 100000)
1154 void snd_set_pitch( int sig, float pitch )
1158 if ( !Sound_enabled ) {
1166 channel = oal_get_channel(sig);
1167 if ( channel == -1 ) {
1168 nprintf(( "Sound", "WARNING: Trying to set pitch for a non-playing sound.\n" ));
1172 oal_set_pitch(channel, pitch);
1175 // ---------------------------------------------------------------------------------------
1178 // Determine if a sound is playing
1180 // returns: 1 => sound is currently playing
1181 // 0 => sound is not playing
1183 // parameters: sig => signature of sound, what is returned from snd_play()
1185 int snd_is_playing( int sig )
1189 if ( !Sound_enabled ) {
1197 channel = oal_get_channel(sig);
1198 if ( channel == -1 )
1201 return oal_is_channel_playing(channel);
1205 // ---------------------------------------------------------------------------------------
1206 // snd_chg_loop_status()
1208 // Change whether a currently playing song is looping or not
1210 // parameters: sig => handle to sound, what is returned from snd_play()
1211 // loop => whether to start (1) or stop (0) looping
1213 void snd_chg_loop_status(int sig, int loop)
1217 if ( !Sound_enabled ) {
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" ));
1231 oal_chg_loop_status(channel, loop);
1234 // ---------------------------------------------------------------------------------------
1237 // Stop all playing sound channels (including looping sounds)
1239 // NOTE: This stops all sounds that are playing from Channels[] sound buffers. It doesn't
1240 // stop every secondary sound buffer in existance
1244 if ( !Sound_enabled ) {
1248 oal_stop_channel_all();
1251 // ---------------------------------------------------------------------------------------
1257 if ( !Sound_enabled ) {
1264 // return the time in ms for the duration of the sound
1265 int snd_get_duration(int snd_id)
1270 return Sounds[snd_id].duration;
1274 MONITOR( SoundChannels );
1276 // update the position of the listener for the specific 3D sound API we're
1278 void snd_update_listener(vector *pos, vector *vel, matrix *orient)
1280 MONITOR_INC( SoundChannels, oal_get_number_channels() );
1281 oal_update_listener(pos, vel, orient);
1284 void snd_update_source(int snd_handle, int min, int max, vector *pos, vector *vel)
1286 if ( !Sound_enabled ) {
1290 if (snd_handle < 0) {
1294 oal_update_source(oal_get_channel(snd_handle), min, max, pos, vel);
1297 // this could probably be optimized a bit
1298 void snd_rewind(int snd_handle, game_snd *gs, float seconds)
1300 float current_time,desired_time;
1302 int current_offset, desired_offset;
1306 if ( !Sound_enabled ) {
1310 if (snd_handle < 0) {
1314 channel = oal_get_channel(snd_handle);
1316 // invalid snd handle, or sound not playing
1321 snd = &Sounds[gs->id].info;
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
1327 // don't rewind if it'll put us before the beginning of the sound
1328 if(current_time - seconds < 0.0f)
1331 desired_time = current_time - seconds; // where we want to be
1332 desired_offset = desired_time * bps; // the target
1334 oal_set_play_position(channel, desired_offset);
1337 // this could probably be optimized a bit
1338 void snd_ffwd(int snd_handle, game_snd *gs, float seconds)
1340 float current_time,desired_time;
1342 int current_offset,desired_offset;
1346 if ( !Sound_enabled ) {
1350 if (snd_handle < 0) {
1354 channel = oal_get_channel(snd_handle);
1356 // invalid snd handle, or sound not playing
1361 snd = &Sounds[gs->id].info;
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
1367 // don't rewind if it'll put us past the end of the sound
1368 if(current_time + seconds > (float)snd->duration)
1371 desired_time = current_time + seconds; // where we want to be
1372 desired_offset = desired_time * bps; // the target
1374 oal_set_play_position(channel, desired_offset);
1377 // this could probably be optimized a bit
1378 void snd_set_pos(int snd_handle, game_snd *gs, float val,int as_pct)
1383 if ( !Sound_enabled ) {
1387 if (snd_handle < 0) {
1391 channel = oal_get_channel(snd_handle);
1393 // invalid snd handle, or sound not playing
1398 snd = &Sounds[gs->id].info;
1400 // set position as an absolute from 0 to 1
1402 SDL_assert((val >= 0.0) && (val <= 1.0));
1403 oal_set_play_position(channel, fl2i((float)snd->size * val));
1405 // set the position as an absolute # of seconds from the beginning of the sound
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));
1414 // Return the number of sounds currently playing
1415 int snd_num_playing()
1417 if ( !Sound_enabled ) {
1421 return oal_get_number_channels();
1424 // Stop the first channel found that is playing a sound
1425 void snd_stop_any_sound()
1429 if ( !Sound_enabled ) {
1433 for ( i = 0; i < 16; i++ ) {
1434 if ( oal_is_channel_playing(i) ) {
1435 oal_stop_channel(i);
1441 // Return the raw sound data for a loaded sound
1443 // input: handle => index into Sounds[] array
1444 // data => allocated mem to hold sound
1446 // exit: 0 => success
1448 int snd_get_data(int handle, char *data)
1450 if ( !Sound_enabled ) {
1454 SDL_assert(handle >= 0 && handle < MAX_SOUNDS);
1456 sound *snd = &Sounds[handle];
1458 WAVE_chunk *header = NULL;
1459 ubyte *u_data = (ubyte*)data;
1461 if ( oal_parse_wave(snd->filename, &u_data, &size, &header) == -1 ) {
1465 if (header->extra_data != NULL) {
1466 free(header->extra_data);
1474 // return the size of the sound data associated with the sound handle
1475 int snd_size(int handle, int *size)
1477 if ( !Sound_enabled ) {
1481 SDL_assert(handle >= 0 && handle < MAX_SOUNDS);
1483 if ( oal_get_buffer_size(Sounds[handle].sid, size) ) {
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)
1493 SDL_assert(handle >= 0 && handle < MAX_SOUNDS);
1494 *bits_per_sample = Sounds[handle].info.bits;
1495 *frequency = Sounds[handle].info.sample_rate;
1498 // return the time for the sound to play in milliseconds
1499 int snd_time_remaining(int handle, int bits_per_sample, int frequency)
1501 int channel, time_remaining = 0;
1502 int current_offset, max_offset = 0;
1504 if ( !Sound_enabled ) {
1512 channel = oal_get_channel(handle);
1518 current_offset = oal_get_play_position(channel);
1519 max_offset = oal_get_channel_size(channel);
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);
1527 // mprintf(("time_remaining: %d\n", time_remaining));
1528 return time_remaining;
1532 // snd_env_ interface
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;
1539 // Set the sound environment
1541 int sound_env_set(sound_env *se)
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;
1554 // Get the sound environment
1556 int sound_env_get(sound_env *se)
1558 EAX_REVERBPROPERTIES er;
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;
1571 // Turn off the sound environment
1573 int sound_env_disable()
1576 se.id = SND_ENV_GENERIC;
1584 // Return 1 if EAX can used to set the sound environment, otherwise return 0
1586 int sound_env_supported()
1588 return oal_efx_is_inited();
1591 // Called once per game frame