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