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.
340 #include "3dinternal.h"
342 #include "audiostr.h"
348 #include "alphacolors.h"
355 #define SND_F_USED (1<<0) // Sounds[] element is used
357 typedef struct sound {
358 int sid; // software id
359 int hid; // hardware id, -1 if sound is not in hardware
360 char filename[MAX_FILENAME_LEN];
364 int uncompressed_size; // size (in bytes) of sound (uncompressed)
368 sound Sounds[MAX_SOUNDS];
370 int Sound_enabled = TRUE; // global flag to turn sound on/off
371 int Snd_sram; // mem (in bytes) used up by storing sounds in system memory
372 int Snd_hram; // mem (in bytes) used up by storing sounds in soundcard memory
373 float Master_sound_volume = 1.0f; // range is 0 -> 1, used for non-music sound fx
374 float Master_voice_volume = 0.7f; // range is 0 -> 1, used for all voice playback
376 // min volume to play a sound after all volume processing (range is 0.0 -> 1.0)
377 #define MIN_SOUND_VOLUME 0.10f
379 static int snd_next_sig = 1;
381 // convert the game level sound priorities to the DirectSound priority descriptions
382 int ds_priority(int priority)
385 case SND_PRIORITY_MUST_PLAY:
387 case SND_PRIORITY_SINGLE_INSTANCE:
389 case SND_PRIORITY_DOUBLE_INSTANCE:
391 case SND_PRIORITY_TRIPLE_INSTANCE:
392 return DS_LIMIT_THREE;
403 // flag all Sounds[] as free
404 for (i=0; i<MAX_SOUNDS; i++ ) {
405 Sounds[i].flags &= ~SND_F_USED;
410 // reset how much storage sounds are taking up in memory
415 // ---------------------------------------------------------------------------------------
416 // Initialize the game sound system
418 // Initialize the game sound system. Depending on what sound library is being used,
419 // call the appropriate low-level initiailizations
421 // returns: 1 => init success
424 int snd_init(int use_a3d, int use_eax)
428 if ( Cmdline_freespace_no_sound )
431 if (ds_initialized) {
432 nprintf(( "Sound", "SOUND => Direct Sound is already initialized!\n" ));
440 // Connect to DirectSound
442 int gave_warning = 0;
444 rval = ds_init(use_a3d, use_eax);
447 nprintf(( "Sound", "SOUND ==> Error initializing DirectSound, trying again in 1 second.\n"));
453 if ( num_tries++ > 5 ) {
454 if ( !gave_warning ) {
456 MessageBox(NULL, XSTR("DirectSound could not be initialized. If you are running any applications playing sound in the background, you should stop them before continuing.",971), NULL, MB_OK);
458 fprintf (stderr, "Sound could not be initialized\n");
467 if ( ACM_init() == -1 ) {
469 // Init the Audio Compression Manager
470 HWND hwnd = (HWND)os_get_window();
471 MessageBox(hwnd, XSTR("Could not properly initialize the Microsoft ADPCM codec.\n\nPlease see the readme.txt file for detailed instructions on installing the Microsoft ADPCM codec.",972), NULL, MB_OK);
472 // Warning(LOCATION, "Could not properly initialize the Microsoft ADPCM codec.\nPlease see the readme.txt file for detailed instructions on installing the Microsoft ADPCM codec.");
474 nprintf(( "Sound", "Could not initialize ADPCM codec.\n" ));
478 // Init the audio streaming stuff
485 // Warning(LOCATION, "Sound system was unable to be initialized. If you continue, sound will be disabled.\n");
486 nprintf(( "Sound", "SOUND => Direct Sound init unsuccessful, continuing without sound.\n" ));
495 CFILE *out = cfopen("sounds.txt", "wt", CFILE_NORMAL, CF_TYPE_DATA);
500 cfwrite_string("Sounds loaded :\n", out);
502 // spew info for all sounds
503 for(idx=0; idx<MAX_SOUNDS; idx++){
504 if(!(Sounds[idx].flags & SND_F_USED)){
508 sprintf(txt, "%s (%ds)\n", Sounds[idx].filename, Sounds[idx].info.duration);
509 cfwrite_string(txt, out);
522 Sound_spew = !Sound_spew;
524 dc_printf("Sound debug info ON");
526 dc_printf("Sound debug info OFF");
529 void snd_spew_debug_info()
532 int message_sounds = 0;
533 int interface_sounds = 0;
541 // count up game, interface and message sounds
542 for(int idx=0; idx<MAX_SOUNDS; idx++){
543 if(!Sounds[idx].flags & SND_F_USED){
549 // what kind of sound is this
550 for(s_idx=0; s_idx<MAX_GAME_SOUNDS; s_idx++){
551 if(!stricmp(Snds[s_idx].filename, Sounds[idx].filename)){
558 for(s_idx=0; s_idx<MAX_GAME_SOUNDS; s_idx++){
559 if(!stricmp(Snds_iface[s_idx].filename, Sounds[idx].filename)){
572 gr_set_color_fast(&Color_normal);
573 gr_printf(30, 100, "Game sounds : %d\n", game_sounds);
574 gr_printf(30, 110, "Interface sounds : %d\n", interface_sounds);
575 gr_printf(30, 120, "Message sounds : %d\n", message_sounds);
576 gr_printf(30, 130, "Total sounds : %d\n", game_sounds + interface_sounds + message_sounds);
579 // ---------------------------------------------------------------------------------------
582 // Load a sound into memory and prepare it for playback. The sound will reside in memory as
583 // a single instance, and can be played multiple times simultaneously. Through the magic of
584 // DirectSound, only 1 copy of the sound is used.
586 // parameters: gs => file of sound to load
587 // allow_hardware_load => whether to try to allocate in hardware
589 // returns: success => index of sound in Sounds[] array
592 //int snd_load( char *filename, int hardware, int use_ds3d, int *sig)
593 int snd_load( game_snd *gs, int allow_hardware_load )
598 WAVEFORMATEX *header = NULL;
600 if ( gs->filename == NULL || gs->filename[0] == 0 )
603 for (n=0; n<MAX_SOUNDS; n++ ) {
604 if (!(Sounds[n].flags & SND_F_USED))
606 else if ( !stricmp( Sounds[n].filename, gs->filename )) {
607 gs->sig = Sounds[n].sig;
612 if ( n == MAX_SOUNDS ) {
624 if ( !ds_initialized )
629 if ( ds_parse_wave(gs->filename, &si->data, &si->size, &header) == -1 )
632 si->format = header->wFormatTag; // 16-bit flag (wFormatTag)
633 si->n_channels = header->nChannels; // 16-bit channel count (nChannels)
634 si->sample_rate = header->nSamplesPerSec; // 32-bit sample rate (nSamplesPerSec)
635 si->avg_bytes_per_sec = header->nAvgBytesPerSec; // 32-bit average bytes per second (nAvgBytesPerSec)
636 si->n_block_align = header->nBlockAlign; // 16-bit block alignment (nBlockAlign)
637 si->bits = header->wBitsPerSample; // Read 16-bit bits per sample
639 snd->duration = fl2i(1000.0f * (si->size / (si->bits/8.0f)) / si->sample_rate);
640 nprintf(("SOUND", "SOUND ==> duration = %dms (%d %d %d)\n", snd->duration,
641 si->size, si->bits, si->sample_rate));
644 if ( allow_hardware_load ) {
650 if ( (gs->flags&GAME_SND_USE_DS3D) ) {
654 rc = ds_load_buffer(&snd->sid, &snd->hid, &snd->uncompressed_size, header, si, type);
657 free(si->data); // don't want to keep this around
662 strncpy( snd->filename, gs->filename, MAX_FILENAME_LEN );
663 snd->flags = SND_F_USED;
665 snd->sig = snd_next_sig++;
666 if (snd_next_sig < 0 ) snd_next_sig = 1;
667 gs->id_sig = snd->sig;
670 nprintf(("Sound", "Loaded %s\n", gs->filename));
674 // ---------------------------------------------------------------------------------------
677 // Unload a sound from memory. This will release the storage, and the sound must be re-loaded via
678 // sound_load() before it can be played again.
680 int snd_unload( int n )
685 if ( (n < 0) || ( n >= MAX_SOUNDS) )
688 if ( !(Sounds[n].flags & SND_F_USED) )
691 ds_unload_buffer(Sounds[n].sid, Sounds[n].hid);
692 if ( Sounds[n].sid != -1 ) {
693 Snd_sram -= Sounds[n].uncompressed_size;
695 if ( Sounds[n].hid != -1 ) {
696 Snd_hram -= Sounds[n].uncompressed_size;
699 Sounds[n].flags &= ~SND_F_USED;
704 // ---------------------------------------------------------------------------------------
707 // Unload all sounds from memory. This will release the storage, and the sound must be re-loaded via
708 // sound_load() before it can be played again.
710 void snd_unload_all()
713 for (i=0; i<MAX_SOUNDS; i++ ) {
714 if ( Sounds[i].flags & SND_F_USED )
719 // ---------------------------------------------------------------------------------------
722 // This is the companion function to snd_init()... it closes down the game sound system.
727 if (!ds_initialized) return;
728 snd_unload_all(); // free the sound data stored in DirectSound secondary buffers
729 ACM_close(); // Close the Audio Compression Manager (ACM)
730 ds3d_close(); // Close DirectSound3D
731 dscap_close(); // Close DirectSoundCapture
732 ds_close(); // Close DirectSound off
735 // ---------------------------------------------------------------------------------------
738 // Allow a sound to be played directly from the index in Sounds[]. This bypasses the
739 // normal game sound management system.
741 // returns: -1 => sound could not be played
742 // n => handle for instance of sound
744 int snd_play_raw( int soundnum, float pan, float vol_scale, int priority )
750 gs.id_sig = Sounds[soundnum].sig;
752 gs.default_volume = 1.0f;
753 // gs.flags = GAME_SND_VOICE | GAME_SND_USE_DS3D;
754 gs.flags = GAME_SND_VOICE;
756 rval = snd_play(&gs, 0.0f, vol_scale, priority, true);
760 MONITOR( NumSoundsStarted );
761 MONITOR( NumSoundsLoaded );
763 // ---------------------------------------------------------------------------------------
766 // NOTE: vol_scale parameter is the multiplicative scaling applied to the default volume
767 // (vol_scale is a default parameter with a default value of 1.0f)
769 // input: gs => game-level sound description
770 // pan => -1 (full left) to 1.0 (full right), this is a default parm
771 // vol_scale => factor to scale default volume by (applied before global sound volume applied)
772 // priority => SND_PRIORITY_MUST_PLAY
773 // SND_PRIORITY_SINGLE_INSTANCE (default value)
774 // SND_PRIORITY_DOUBLE_INSTANCE
775 // SND_PRIORITY_TRIPLE_INSTANCE
777 // returns: -1 => sound could not be played
778 // n => handle for instance of sound
780 int snd_play( game_snd *gs, float pan, float vol_scale, int priority, bool is_voice_msg )
790 Assert( gs != NULL );
792 MONITOR_INC( NumSoundsStarted, 1 );
794 if ( gs->id == -1 ) {
795 gs->id = snd_load(gs);
796 MONITOR_INC( NumSoundsLoaded, 1);
797 } else if ( gs->id_sig != Sounds[gs->id].sig ) {
798 gs->id = snd_load(gs);
804 volume = gs->default_volume * vol_scale;
805 if ( gs->flags&GAME_SND_VOICE ) {
806 volume *= Master_voice_volume;
808 volume *= Master_sound_volume;
813 snd = &Sounds[gs->id];
815 if ( !(snd->flags & SND_F_USED) )
821 if ( volume > MIN_SOUND_VOLUME ) {
822 handle = ds_play( snd->sid, snd->hid, gs->id_sig, ds_priority(priority), ds_convert_volume(volume), fl2i(pan*MAX_PAN), 0, is_voice_msg);
828 MONITOR( Num3DSoundsStarted );
829 MONITOR( Num3DSoundsLoaded );
831 // ---------------------------------------------------------------------------------------
834 // NOTE: vol_scale parameter is the multiplicative scaling applied to the default volume
835 // (vol_scale is a default parameter with a default value of 1.0f)
837 // input: gs => game-level sound description
838 // source_pos => global pos of where the sound is
839 // listen_pos => global pos of where listener is
840 // radius => optional parameter, this specifes distance at which to apply min/max distances
841 // source_vel => velocity of the source playing the sound (used for DirectSound3D only)
842 // looping => flag to indicate the sound should loop (default value 0)
843 // vol_scale => factor to scale the static volume by (applied before attenuation)
844 // priority => SND_PRIORITY_MUST_PLAY
845 // SND_PRIORITY_SINGLE_INSTANCE (default value)
846 // SND_PRIORITY_DOUBLE_INSTANCE
847 // SND_PRIORITY_TRIPLE_INSTANCE
848 // sound_fvec => forward vector of where sound is emitting from (RSX use only)
849 // range_factor => factor N, which increases distance sound is heard by N times (default value 1)
851 // returns: -1 => sound could not be played
852 // n => handle for instance of sound
854 int snd_play_3d(game_snd *gs, vector *source_pos, vector *listen_pos, float radius, vector *source_vel, int looping, float vol_scale, int priority, vector *sound_fvec, float range_factor, int force )
856 int handle, min_range, max_range;
857 vector vector_to_sound;
859 float volume, distance, pan, max_volume;
861 if ( !Sound_enabled )
866 MONITOR_INC( Num3DSoundsStarted, 1 );
868 if ( gs->id == -1 ) {
869 gs->id = snd_load(gs);
870 MONITOR_INC( Num3DSoundsLoaded, 1 );
871 }else if ( gs->id_sig != Sounds[gs->id].sig ) {
872 gs->id = snd_load(gs);
878 snd = &Sounds[gs->id];
880 if ( !(snd->flags & SND_F_USED) )
885 min_range = fl2i( (gs->min + radius) * range_factor);
886 max_range = fl2i( (gs->max + radius) * range_factor + 0.5f);
891 // DirectSound3D will not cut off sounds, no matter how quite they become.. so manually
892 // prevent sounds from playing past the max distance.
893 distance = vm_vec_normalized_dir_quick( &vector_to_sound, source_pos, listen_pos );
894 max_volume = gs->default_volume * vol_scale;
895 if ( (distance > max_range) && !force){
899 if ( distance <= min_range ) {
903 volume = max_volume - max_volume*(distance/max_range);
906 if ( volume > 1.0f ){
910 if ( priority == SND_PRIORITY_MUST_PLAY ) {
911 if ( volume < 0.3 ) {
912 priority = SND_PRIORITY_DOUBLE_INSTANCE;
916 volume *= Master_sound_volume;
917 if ( (volume < MIN_SOUND_VOLUME) && !force) {
921 int play_using_ds3d = 0;
923 if (ds_using_ds3d()) {
924 if ( ds_is_3d_buffer(snd->sid) ) {
929 if ( play_using_ds3d ) {
930 // play through DirectSound3D
931 handle = ds3d_play( snd->sid, snd->hid, gs->id_sig, source_pos, source_vel, min_range, max_range, looping, ds_convert_volume(max_volume*Master_sound_volume), ds_convert_volume(volume), ds_priority(priority));
934 // play sound as a fake 3D sound
935 if ( distance <= 0 ) {
939 pan = vm_vec_dot(&View_matrix.v.rvec,&vector_to_sound);
942 handle = snd_play_looping( gs, pan, -1, -1, volume/gs->default_volume, priority, force );
944 handle = snd_play( gs, pan, volume/gs->default_volume, priority);
951 // update the given 3d sound with a new position
952 void snd_update_3d_pos(int soundnum, game_snd *gs, vector *new_pos)
956 // get new volume and pan vals
957 snd_get_3d_vol_and_pan(gs, new_pos, &vol, &pan);
960 snd_set_volume(soundnum, vol);
963 snd_set_pan(soundnum, pan);
966 // ---------------------------------------------------------------------------------------
967 // snd_get_3d_vol_and_pan()
969 // Based on the 3D position the player and the object, calculate
970 // the correct volume and pan.
972 // parameters: gs => pointer to sound description
973 // pos => 3D position used to calc volume and pan
974 // vol => output parameter for the volume
975 // pan => output parameter for the pan
976 // radius => optional parameter (default value 0) which indicates sound attenuation
977 // should occur from this radius
979 // returns: -1 => could not determine vol or pan
982 // NOTE: the volume is not scaled by the Master_sound_volume, since this always occurs
983 // when snd_play() or snd_play_looping() is called
985 int snd_get_3d_vol_and_pan(game_snd *gs, vector *pos, float* vol, float *pan, float radius)
987 vector vector_to_sound;
988 float distance, max_volume;
999 if ( gs->id == -1 ) {
1000 gs->id = snd_load(gs);
1003 snd = &Sounds[gs->id];
1004 if ( !(snd->flags & SND_F_USED) )
1007 distance = vm_vec_normalized_dir_quick( &vector_to_sound, pos, &View_position );
1010 max_volume = gs->default_volume;
1011 if ( distance <= gs->min ) {
1015 *vol = max_volume - (distance - gs->min) * max_volume / (gs->max - gs->min);
1021 if ( *vol > MIN_SOUND_VOLUME ) {
1022 if ( distance <= 0 )
1025 *pan = vm_vec_dot(&View_matrix.v.rvec,&vector_to_sound);
1031 // ---------------------------------------------------------------------------------------
1032 // volume 0 to 1.0. Returns the handle of the sound. -1 if failed.
1033 // If startloop or stoploop are not -1, then then are used.
1035 // NOTE: vol_scale parameter is the multiplicative scaling applied to the default volume
1036 // (vol_scale is a default parameter with a default value of 1.0f)
1038 // input: gs => game-level sound description
1039 // source_pos => global pos of where the sound is
1040 // listen_pos => global pos of where listener is
1041 // source_vel => velocity of the source playing the sound (used for DirectSound3D only)
1042 // looping => flag to indicate the sound should loop (default value 0)
1043 // vol_scale => factor to scale the static volume by (applied before attenuation)
1044 // priority => SND_PRIORITY_MUST_PLAY (default value)
1045 // SND_PRIORITY_SINGLE_INSTANCE
1046 // SND_PRIORITY_DOUBLE_INSTANCE
1047 // SND_PRIORITY_TRIPLE_INSTANCE
1049 // returns: -1 => sound could not be played
1050 // n => handle for instance of sound
1052 int snd_play_looping( game_snd *gs, float pan, int start_loop, int stop_loop, float vol_scale, int priority, int force )
1061 Assert( gs != NULL );
1063 if (!ds_initialized)
1066 if ( gs->id == -1 ) {
1067 gs->id = snd_load(gs);
1069 else if ( gs->id_sig != Sounds[gs->id].sig ) {
1070 gs->id = snd_load(gs);
1076 snd = &Sounds[gs->id];
1078 if ( !(snd->flags & SND_F_USED) )
1081 volume = gs->default_volume * vol_scale;
1082 volume *= Master_sound_volume;
1083 if ( volume > 1.0f )
1086 if ( (volume > MIN_SOUND_VOLUME) || force) {
1087 handle = ds_play( snd->sid, snd->hid, gs->id_sig, ds_priority(priority), ds_convert_volume(volume), fl2i(pan*MAX_PAN), 1);
1093 // ---------------------------------------------------------------------------------------
1096 // Stop a sound from playing.
1098 // parameters: sig => handle to sound, what is returned from snd_play()
1100 void snd_stop( int sig )
1104 if (!ds_initialized) return;
1105 if ( sig < 0 ) return;
1107 channel = ds_get_channel(sig);
1108 if ( channel == -1 )
1111 ds_stop_channel(channel);
1114 // ---------------------------------------------------------------------------------------
1117 // Set the volume of a currently playing sound
1119 // parameters: sig => handle to sound, what is returned from snd_play()
1120 // volume => volume of sound (range: 0.0 -> 1.0)
1122 void snd_set_volume( int sig, float volume )
1127 if (!ds_initialized)
1133 channel = ds_get_channel(sig);
1134 if ( channel == -1 ) {
1135 nprintf(( "Sound", "WARNING: Trying to set volume for a non-playing sound.\n" ));
1139 new_volume = volume * Master_sound_volume;
1140 ds_set_volume( channel, ds_convert_volume(new_volume) );
1143 // ---------------------------------------------------------------------------------------
1146 // Set the pan of a currently playing sound
1148 // parameters: sig => handle to sound, what is returned from snd_play()
1149 // pan => pan of sound (range: -1.0 -> 1.0)
1151 void snd_set_pan( int sig, float pan )
1155 if (!ds_initialized)
1161 channel = ds_get_channel(sig);
1162 if ( channel == -1 ) {
1163 nprintf(( "Sound", "WARNING: Trying to set pan for a non-playing sound.\n" ));
1167 ds_set_pan( channel, fl2i(pan*MAX_PAN) );
1170 // ---------------------------------------------------------------------------------------
1173 // Return the pitch of a currently playing sound
1175 // returns: pitch of sound ( range: 100 to 100000)
1177 // parameters: sig => handle to sound, what is returned from snd_play()
1179 int snd_get_pitch(int sig)
1181 int channel, pitch=10000;
1183 if (!ds_initialized)
1189 channel = ds_get_channel(sig);
1190 if ( channel == -1 ) {
1191 nprintf(( "Sound", "WARNING: Trying to get pitch for a non-playing sound.\n" ));
1195 pitch = ds_get_pitch(channel);
1200 // ---------------------------------------------------------------------------------------
1203 // Set the pitch of a currently playing sound
1205 // parameters: sig => handle to sound, what is returned from snd_play()
1206 // pan => pitch of sound (range: 100 to 100000)
1208 void snd_set_pitch( int sig, int pitch )
1212 if (!ds_initialized) return;
1213 if ( sig < 0 ) return;
1215 channel = ds_get_channel(sig);
1216 if ( channel == -1 ) {
1217 nprintf(( "Sound", "WARNING: Trying to set pitch for a non-playing sound.\n" ));
1221 ds_set_pitch(channel, pitch);
1224 // ---------------------------------------------------------------------------------------
1227 // Determine if a sound is playing
1229 // returns: 1 => sound is currently playing
1230 // 0 => sound is not playing
1232 // parameters: sig => signature of sound, what is returned from snd_play()
1234 int snd_is_playing( int sig )
1236 int channel, is_playing;
1238 if (!ds_initialized)
1244 channel = ds_get_channel(sig);
1245 if ( channel == -1 )
1248 is_playing = ds_is_channel_playing(channel);
1249 if ( is_playing == TRUE ) {
1257 // ---------------------------------------------------------------------------------------
1258 // snd_chg_loop_status()
1260 // Change whether a currently playing song is looping or not
1262 // parameters: sig => handle to sound, what is returned from snd_play()
1263 // loop => whether to start (1) or stop (0) looping
1265 void snd_chg_loop_status(int sig, int loop)
1269 if (!ds_initialized)
1275 channel = ds_get_channel(sig);
1276 if ( channel == -1 ) {
1277 nprintf(( "Sound", "WARNING: Trying to change loop status of a non-playing sound!\n" ));
1281 ds_chg_loop_status(channel, loop);
1284 // ---------------------------------------------------------------------------------------
1287 // Stop all playing sound channels (including looping sounds)
1289 // NOTE: This stops all sounds that are playing from Channels[] sound buffers. It doesn't
1290 // stop every secondary sound buffer in existance
1294 if (!ds_initialized)
1297 ds_stop_channel_all();
1300 // ---------------------------------------------------------------------------------------
1303 // Return the pointer to the DirectSound interface
1312 return (uint)pDirectSound;
1316 // ---------------------------------------------------------------------------------------
1322 if ( !ds_initialized )
1328 // return the time in ms for the duration of the sound
1329 int snd_get_duration(int snd_id)
1334 return Sounds[snd_id].duration;
1338 MONITOR( SoundChannels );
1340 // update the position of the listener for the specific 3D sound API we're
1342 void snd_update_listener(vector *pos, vector *vel, matrix *orient)
1344 MONITOR_INC( SoundChannels, ds_get_number_channels() );
1345 ds3d_update_listener(pos, vel, orient);
1348 // this could probably be optimized a bit
1349 void snd_rewind(int snd_handle, game_snd *gs, float seconds)
1351 float current_time,desired_time;
1353 DWORD current_offset,desired_offset;
1356 if(!snd_is_playing(snd_handle))
1359 snd = &Sounds[gs->id].info;
1361 current_offset = ds_get_play_position(ds_get_channel(snd_handle)); // current offset into the sound
1362 bps = (float)snd->sample_rate * (float)snd->bits; // data rate
1363 current_time = (float)current_offset/bps; // how many seconds we're into the sound
1365 // don't rewind if it'll put us before the beginning of the sound
1366 if(current_time - seconds < 0.0f)
1369 desired_time = current_time - seconds; // where we want to be
1370 desired_offset = (DWORD)(desired_time * bps); // the target
1372 ds_set_position(ds_get_channel(snd_handle),desired_offset);
1375 // this could probably be optimized a bit
1376 void snd_ffwd(int snd_handle, game_snd *gs, float seconds)
1378 if(!snd_is_playing(snd_handle))
1381 float current_time,desired_time;
1383 DWORD current_offset,desired_offset;
1386 if(!snd_is_playing(snd_handle))
1389 snd = &Sounds[gs->id].info;
1391 current_offset = ds_get_play_position(ds_get_channel(snd_handle)); // current offset into the sound
1392 bps = (float)snd->sample_rate * (float)snd->bits; // data rate
1393 current_time = (float)current_offset/bps; // how many seconds we're into the sound
1395 // don't rewind if it'll put us past the end of the sound
1396 if(current_time + seconds > (float)snd->duration)
1399 desired_time = current_time + seconds; // where we want to be
1400 desired_offset = (DWORD)(desired_time * bps); // the target
1402 ds_set_position(ds_get_channel(snd_handle),desired_offset);
1405 // this could probably be optimized a bit
1406 void snd_set_pos(int snd_handle, game_snd *gs, float val,int as_pct)
1408 if(!snd_is_playing(snd_handle))
1413 snd = &Sounds[gs->id].info;
1414 // set position as an absolute from 0 to 1
1416 Assert((val >= 0.0) && (val <= 1.0));
1417 ds_set_position(ds_get_channel(snd_handle),(DWORD)((float)snd->size * val));
1419 // set the position as an absolute # of seconds from the beginning of the sound
1422 Assert(val <= (float)snd->duration/1000.0f);
1423 bps = (float)snd->sample_rate * (float)snd->bits; // data rate
1424 ds_set_position(ds_get_channel(snd_handle),(DWORD)(bps * val));
1428 // Return the number of sounds currently playing
1429 int snd_num_playing()
1431 return ds_get_number_channels();
1434 // Stop the first channel found that is playing a sound
1435 void snd_stop_any_sound()
1439 for ( i = 0; i < 16; i++ ) {
1440 if ( ds_is_channel_playing(i) ) {
1447 // Return the raw sound data for a loaded sound
1449 // input: handle => index into Sounds[] array
1450 // data => allocated mem to hold sound
1452 // exit: 0 => success
1454 int snd_get_data(int handle, char *data)
1456 Assert(handle >= 0 && handle < MAX_SOUNDS);
1457 if ( ds_get_data(Sounds[handle].sid, data) ) {
1464 // return the size of the sound data associated with the sound handle
1465 int snd_size(int handle, int *size)
1467 Assert(handle >= 0 && handle < MAX_SOUNDS);
1468 if ( ds_get_size(Sounds[handle].sid, size) ) {
1475 // retrieve the bits per sample and frequency for a given sound
1476 void snd_get_format(int handle, int *bits_per_sample, int *frequency)
1478 Assert(handle >= 0 && handle < MAX_SOUNDS);
1479 *bits_per_sample = Sounds[handle].info.bits;
1480 *frequency = Sounds[handle].info.sample_rate;
1483 // return the time for the sound to play in milliseconds
1484 int snd_time_remaining(int handle, int bits_per_sample, int frequency)
1486 int channel, is_playing, time_remaining = 0;
1488 if (!ds_initialized)
1494 channel = ds_get_channel(handle);
1495 if ( channel == -1 )
1498 is_playing = ds_is_channel_playing(channel);
1499 if ( !is_playing ) {
1503 int current_offset, max_offset;
1505 current_offset = ds_get_play_position(channel);
1506 max_offset = ds_get_channel_size(channel);
1508 if ( current_offset < max_offset ) {
1509 int bytes_remaining = max_offset - current_offset;
1510 int samples_remaining = bytes_remaining / fl2i(bits_per_sample/8.0f);
1511 time_remaining = fl2i(1000 * samples_remaining/frequency + 0.5f);
1514 // mprintf(("time_remaining: %d\n", time_remaining));
1515 return time_remaining;
1519 // snd_env_ interface
1521 static unsigned long Sound_env_id;
1522 static float Sound_env_volume;
1523 static float Sound_env_damping;
1524 static float Sound_env_decay;
1526 // Set the sound environment
1528 int sound_env_set(sound_env *se)
1530 if (ds_eax_set_all(se->id, se->volume, se->damping, se->decay) == 0) {
1531 Sound_env_id = se->id;
1532 Sound_env_volume = se->volume;
1533 Sound_env_damping = se->damping;
1534 Sound_env_decay = se->decay;
1541 // Get the sound environment
1543 int sound_env_get(sound_env *se)
1545 EAX_REVERBPROPERTIES er;
1547 if (ds_eax_get_all(&er) == 0) {
1548 se->id = er.environment;
1549 se->volume = er.fVolume;
1550 se->decay = er.fDecayTime_sec;
1551 se->damping = er.fDamping;
1558 // Turn off the sound environment
1560 int sound_env_disable()
1563 se.id = SND_ENV_GENERIC;
1571 // Return 1 if EAX can used to set the sound environment, otherwise return 0
1573 int sound_env_supported()
1575 return ds_eax_is_inited();
1578 // Called once per game frame