]> icculus.org git repositories - taylor/freespace2.git/blob - src/object/objectsnd.cpp
missing braces warning fixes
[taylor/freespace2.git] / src / object / objectsnd.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Object/ObjectSnd.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module for managing object-linked persistant sounds
16  *
17  * $Log$
18  * Revision 1.5  2002/06/17 06:33:10  relnev
19  * ryan's struct patch for gcc 2.95
20  *
21  * Revision 1.4  2002/06/09 04:41:24  relnev
22  * added copyright header
23  *
24  * Revision 1.3  2002/06/05 08:05:29  relnev
25  * stub/warning removal.
26  *
27  * reworked the sound code.
28  *
29  * Revision 1.2  2002/05/07 03:16:48  theoddone33
30  * The Great Newline Fix
31  *
32  * Revision 1.1.1.1  2002/05/03 03:28:10  root
33  * Initial import.
34  *
35  * 
36  * 11    9/08/99 8:56a Mikek
37  * Blast.  Suppress a debug warning.
38  * 
39  * 10    9/08/99 8:56a Mikek
40  * Whoops, broke the build with debug code.
41  * 
42  * 9     9/08/99 8:42a Mikek
43  * Make flyby sounds a lot easier to trigger.  Decrease some thresholds
44  * and remove the dot product check.  This is handled by the delta
45  * velocity check.
46  * 
47  * 8     7/01/99 4:23p Dave
48  * Full support for multiple linked ambient engine sounds. Added "big
49  * damage" flag.
50  * 
51  * 7     7/01/99 11:44a Dave
52  * Updated object sound system to allow multiple obj sounds per ship.
53  * Added hit-by-beam sound. Added killed by beam sound.
54  * 
55  * 6     6/25/99 3:08p Dave
56  * Multiple flyby sounds.
57  * 
58  * 5     5/23/99 8:11p Alanl
59  * Added support for EAX
60  * 
61  * 4     5/18/99 11:50a Andsager
62  * Remove unused object type OBJ_GHOST_SAVE
63  * 
64  * 3     4/23/99 12:01p Johnson
65  * Added SIF_HUGE_SHIP
66  * 
67  * 2     10/07/98 10:53a Dave
68  * Initial checkin.
69  * 
70  * 1     10/07/98 10:50a Dave
71  * 
72  * 59    5/15/98 5:16p Dave
73  * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy
74  * status for team vs. team. Put in asserts to check for invalid team vs.
75  * team situations.
76  * 
77  * 58    5/07/98 12:24a Hoffoss
78  * Finished up sidewinder force feedback support.
79  * 
80  * 57    5/06/98 1:29p Dan
81  * AL: allow engine sounds to play polyphonically
82  * 
83  * 56    5/06/98 10:27a Dan
84  * AL: Fix bug with object sounds and Aureal
85  * 
86  * 55    4/19/98 9:32p Lawrance
87  * Add support for Shivan flyby sound
88  * 
89  * 54    4/18/98 9:12p Lawrance
90  * Added Aureal support.
91  * 
92  * 53    4/12/98 5:31p Lawrance
93  * use timer_get_milliseconds() instead of gettime()
94  * 
95  * 52    4/08/98 8:33p Lawrance
96  * increase distance at which flyby sound  is heard
97  * 
98  * 51    4/01/98 9:21p John
99  * Made NDEBUG, optimized build with no warnings or errors.
100  * 
101  * 50    3/21/98 3:36p Lawrance
102  * Don't change frequency unless a threshold change has been reached.
103  * 
104  * 49    3/19/98 5:34p Lawrance
105  * Only play flyby sound if both ships involved are above a minimum speed
106  * 
107  * 48    3/18/98 9:49a Lawrance
108  * fix uninitialized data bug
109  * 
110  * 47    3/17/98 5:55p Lawrance
111  * Support object-linked sounds for asteroids.
112  * 
113  * 46    3/14/98 4:59p Lawrance
114  * Fix bug with object sounds not getting cleared
115  * 
116  * 45    2/22/98 2:48p John
117  * More String Externalization Classification
118  * 
119  * 44    2/20/98 8:32p Lawrance
120  * Add radius parm to sound_play_3d()
121  * 
122  * 43    2/12/98 11:53p Lawrance
123  * move engine pos closer to center of ship, ensure flyby sounds only play
124  * when flying past a small ship
125  * 
126  * 42    2/11/98 5:38p Dave
127  * Put in a global inited flag for objsound.
128  * 
129  * 41    2/11/98 11:30a Dan
130  * AL: fix DirectSound3D bug
131  * 
132  * 40    2/11/98 10:28a Lawrance
133  * Fix attenuation problems related to engine position.
134  * 
135  * 39    2/09/98 9:09p Lawrance
136  * ensure we don't get two flyby sounds too close together
137  * 
138  * 38    2/02/98 8:23p Lawrance
139  * double distance at which a flyby sound is played
140  * 
141  * 37    1/29/98 10:31a Lawrance
142  * Don't play doppler effect for cruisers and capital ships
143  * 
144  * 36    1/20/98 10:04a Lawrance
145  * fix volume bug, do obj_snd_do_frame at 10Hz
146  * 
147  * 35    1/20/98 9:47a Mike
148  * Suppress optimized compiler warnings.
149  * Some secondary weapon work.
150  * 
151  * 34    1/10/98 1:14p John
152  * Added explanation to debug console commands
153  * 
154  * 33    12/21/97 4:33p John
155  * Made debug console functions a class that registers itself
156  * automatically, so you don't need to add the function to
157  * debugfunctions.cpp.  
158  * 
159  * 32    11/20/97 6:45p Lawrance
160  * allow two looping debris sounds to play
161  * 
162  * 31    11/04/97 10:18a Dan
163  * initialize closest_obj to NULL
164  * 
165  * 30    11/03/97 11:09p Lawrance
166  * Only play flyby sound for ships (ie not debris).
167  * 
168  * 29    10/26/97 3:22p Lawrance
169  * use volume and not distance when deciding which engine sounds to play
170  * 
171  * 28    10/22/97 10:37a Johnson
172  * ALAN: ensure that object sounds are only deleted once
173  * 
174  * 27    10/10/97 7:43p Lawrance
175  * use SF_ENGINES_ON flag
176  * 
177  * 26    10/06/97 4:13p Lawrance
178  * use engine pos when updating volume for object sounds
179  * 
180  * 25    10/01/97 5:55p Lawrance
181  * change call to snd_play_3d() to allow for arbitrary listening position
182  * 
183  * 24    9/18/97 7:58a Lawrance
184  * use rear of ship when deciding where to play the engine sound
185  * 
186  * 23    9/11/97 5:01p Dave
187  * Minor changes to handle ingame joining/dropping for multiplayer.
188  * 
189  * 22    9/03/97 5:02p Lawrance
190  * add engine stuttering when a ship is dying
191  * 
192  * 21    8/29/97 5:04p Dave
193  * Made sure ghost objects are handled correctly.
194  * 
195  * 20    7/28/97 11:38a Lawrance
196  * let ship engine scale with speed for DirectSound3D engine sounds too
197  * 
198  * 19    7/27/97 5:14p Lawrance
199  * add afterburners to the player control info
200  * 
201  * 18    7/14/97 12:04a Lawrance
202  * make Obj_snd_enabled visible
203  * 
204  * 17    7/09/97 12:07a Mike
205  * Changes in ship_info struct.
206  * 
207  * 16    7/08/97 11:38a Lawrance
208  * added SIF_BIG_SHIP flag (used for object-linked engine sounds)
209  * 
210  * 15    6/23/97 10:15a Lawrance
211  * fix bug with object linked sounds not stopping playing sounds
212  * 
213  * 14    6/19/97 2:05p Lawrance
214  * when stopping  a linked sound, stop it right away
215  * 
216  * 13    6/09/97 11:50p Lawrance
217  * integrating DirectSound3D
218  * 
219  * 12    6/08/97 5:59p Lawrance
220  * comment out ds3d stuff for now
221  * 
222  * 11    6/06/97 4:13p Lawrance
223  * use an index instead of a pointer for object-linked sounds
224  * 
225  * 10    6/05/97 1:37a Lawrance
226  * change to support snd_get_vol_and_pan()
227  * 
228  * 9     6/05/97 1:07a Lawrance
229  * changes to support sound interface
230  * 
231  * 8     6/02/97 1:50p Lawrance
232  * supporting integration with Direct3D
233  * 
234  * 7     5/18/97 2:40p Lawrance
235  * added debug function to toggle doppler effect
236  * 
237  * 6     5/15/97 9:05a Lawrance
238  * comment out some debugging nprintf()'s
239  * 
240  * 5     5/14/97 10:47a Lawrance
241  * only play external engine sound for loudest ship
242  * 
243  * 4     5/09/97 4:33p Lawrance
244  * doppler effects
245  * 
246  * 3     5/09/97 9:41a Lawrance
247  * modified comments
248  * 
249  * 2     5/08/97 4:30p Lawrance
250  * split off object sound stuff into separate file
251  *
252  * $NoKeywords: $
253  */
254
255
256 #include "object.h"
257 #include "objectsnd.h"
258 #include "linklist.h"
259 #include "ship.h"
260 #include "gamesnd.h"
261 #include "ds.h"
262 #include "ds3d.h"
263 #include "timer.h"
264 #include "3d.h"
265 #include "joy_ff.h"
266
267 // Persistant sounds for objects (pointer to obj_snd is in object struct)
268 typedef struct _obj_snd {
269         _obj_snd        *next, *prev;
270         int             objnum;                 // object index of object that contains this sound
271         int             id;                             // Index into Snds[] array
272         int             instance;               // handle of currently playing sound (a ds3d handle if USES_DS3D flag set)
273         int             next_update;    // timestamp that marks next allowed vol/pan change
274         float           vol;                            // volume of sound (range: 0.0 -> 1.0)
275         float           pan;                            // pan of sound (range: -1.0 -> 1.0)
276         int             freq;                           // valid range: 100 -> 100000 Hz
277         int             flags;                  
278         vector  offset;                 // offset from the center of the object where the sound lives
279 } obj_snd;
280
281 #define VOL_PAN_UPDATE                  50                                              // time in ms to update a persistant sound vol/pan
282 #define MIN_PERSISTANT_VOL              0.10f
283 #define MIN_FORWARD_SPEED               5
284 #define SPEED_SOUND                             600.0f                          // speed of sound in FreeSpace
285
286 #define MAX_OBJ_SOUNDS_PLAYING                                          5
287 static  int Num_obj_sounds_playing;
288
289 #define OBJSND_CHANGE_FREQUENCY_THRESHOLD                       10
290
291 static  obj_snd obj_snd_list;                                           // head of linked list of object sound structs
292 static  int             Doppler_enabled = TRUE;
293
294 #define MAX_OBJ_SNDS    256
295 obj_snd Objsnds[MAX_OBJ_SNDS];
296
297 int             Obj_snd_enabled = TRUE;
298 int             Obj_snd_last_update;                                                    // timer used to run object sound updates at fixed time intervals
299 int             Obj_snd_level_inited=0;
300
301 // ship flyby data
302 #define FLYBY_MIN_DISTANCE                              90
303 #define FLYBY_MIN_SPEED                                 50
304 #define FLYBY_MIN_RELATIVE_SPEED                100
305 #define FLYBY_MIN_NEXT_TIME                             1000    // in ms
306 #define FLYBY_MIN_REPEAT_TIME                   4000    // in ms
307 int             Flyby_next_sound;
308 int             Flyby_next_repeat;
309 object  *Flyby_last_objp;
310
311 // return the world pos of the sound source on a ship.  
312 void obj_snd_source_pos(vector *sound_pos, obj_snd *osp)
313 {
314         vector offset_world;
315         object *objp = &Objects[osp->objnum];
316
317         // get sound pos in world coords
318         vm_vec_unrotate(&offset_world, &osp->offset, &objp->orient);
319         vm_vec_add(sound_pos, &objp->pos, &offset_world);
320 }
321
322 // ---------------------------------------------------------------------------------------
323 // dcf_objsnd()
324 //
325 // Debug console function for object linked persistant sounds
326 //
327 //XSTR:OFF
328 DCF(objsnd, "Persistant sound stuff" )
329 {
330         char            buf1[16], buf2[64];
331         obj_snd *osp;
332
333         if ( Dc_command )       {
334                 dc_get_arg(ARG_STRING|ARG_NONE);
335
336                 if ( Dc_arg_type & ARG_NONE ) {
337                         if ( Obj_snd_enabled == TRUE ) {
338                                 obj_snd_stop_all();
339                                 Obj_snd_enabled = FALSE;
340                         }
341                         else {
342                                 Obj_snd_enabled = TRUE;
343                         }
344                 }
345                 if ( !stricmp( Dc_arg, "list" ))        {
346                         for ( osp = GET_FIRST(&obj_snd_list); osp !=END_OF_LIST(&obj_snd_list); osp = GET_NEXT(osp) ) {
347                                 Assert(osp != NULL);
348                                 if ( osp->instance == -1 ) {
349                                         continue;
350                                         //sprintf(buf1,"OFF");
351                                 } else {
352                                         sprintf(buf1,"ON");
353                                 }
354
355                                 if ( Objects[osp->objnum].type == OBJ_SHIP ) {
356                                         sprintf(buf2, Ships[Objects[osp->objnum].instance].ship_name);
357                                 }
358                                 else if ( Objects[osp->objnum].type == OBJ_DEBRIS ) {
359                                         sprintf(buf2, "Debris");
360                                 }
361                                 else {
362                                         sprintf(buf2, "Unknown");
363                                 }
364
365                                 vector source_pos;
366                                 float distance;
367
368                                 obj_snd_source_pos(&source_pos, osp);
369                                 distance = vm_vec_dist_quick( &source_pos, &View_position );
370
371                                 dc_printf("Object %d => name: %s vol: %.2f pan: %.2f dist: %.2f status: %s\n", osp->objnum, buf2, osp->vol, osp->pan, distance, buf1);
372                         } // end for
373                                 dc_printf("Number object-linked sounds playing: %d\n", Num_obj_sounds_playing);
374                 }
375         }
376
377         if ( Dc_help ) {
378                 dc_printf ("Usage: objsnd [list]\n");
379                 dc_printf ("[list] --  displays status of all objects with linked sounds\n");
380                 dc_printf ("with no parameters, object sounds are toggled on/off\n");
381                 Dc_status = 0;
382         }
383
384         if ( Dc_status )        {
385                 dc_printf( "Object sounds are: %s\n", (Obj_snd_enabled?"ON":"OFF") );
386         }
387 }
388 //XSTR:ON
389
390 // ---------------------------------------------------------------------------------------
391 // Debug console function for toggling doppler effection on/off
392 //
393 DCF_BOOL( doppler, Doppler_enabled )
394
395
396 // ---------------------------------------------------------------------------------------
397 // obj_snd_get_slot()
398 //
399 // Get a free slot in the Objsnds[] array
400 //
401 //      returns -1 if no slot is available
402 int obj_snd_get_slot()
403 {
404         int i;
405
406         for ( i = 0; i < MAX_OBJ_SNDS; i++ ) {
407                 if ( !(Objsnds[i].flags & OS_USED) ) 
408                         return i;
409         }
410
411         return -1;
412 }
413
414 // ---------------------------------------------------------------------------------------
415 // obj_snd_init()
416 //
417 // Called once at level start to initialize the persistant object sound system
418 //
419 void obj_snd_level_init()
420 {
421         int i;
422
423         list_init(&obj_snd_list);
424         for ( i = 0; i < MAX_OBJ_SNDS; i++ ) {
425                 Objsnds[i].flags = 0;
426         }
427
428         Num_obj_sounds_playing = 0;
429         Flyby_next_sound = 1;
430         Flyby_next_repeat = 1;
431         Flyby_last_objp = NULL;
432         Obj_snd_last_update=0;
433
434         Obj_snd_level_inited=1;
435 }
436
437
438 // ---------------------------------------------------------------------------------------
439 // obj_snd_stop()
440 //
441 // Stop a persistant sound from playing.
442 //
443 // parameters:  objp                    => pointer to object that sound is to be stopped for
444 //
445 //
446 void obj_snd_stop(object *objp, int index)
447 {
448         obj_snd *osp;
449         int idx;
450
451         // sanity
452         if(index >= MAX_OBJECT_SOUNDS){
453                 Int3();
454                 return;
455         }
456
457         // if index is -1, kill all sounds for this guy
458         if(index == -1){
459                 // kill all sounds for this guy
460                 for(idx=0; idx<MAX_OBJECT_SOUNDS; idx++){
461                         if ( objp->objsnd_num[idx] == -1 ){
462                                 continue;
463                         }
464
465                         osp = &Objsnds[objp->objsnd_num[idx]];
466
467                         if ( osp->instance != -1 ) {
468                                 snd_stop(osp->instance);
469                                 osp->instance = -1;
470                                 switch(objp->type) {
471                                         case OBJ_SHIP:
472                                         case OBJ_GHOST:
473                                         case OBJ_DEBRIS:
474                                         case OBJ_ASTEROID:
475                                                 Num_obj_sounds_playing--;
476                                                 Assert(Num_obj_sounds_playing >= 0);                                    
477                                                 break;
478
479                                         default:
480                                                 Int3(); // get Alan
481                                                 break;
482                                 }                               
483                         }               
484                 }
485         } else {                
486                 if ( objp->objsnd_num[index] == -1 ){
487                         return;
488                 }
489
490                 osp = &Objsnds[objp->objsnd_num[index]];
491
492                 if ( osp->instance != -1 ) {
493                         snd_stop(osp->instance);
494                         osp->instance = -1;
495                         switch(objp->type) {
496                         case OBJ_SHIP:
497                         case OBJ_GHOST:
498                         case OBJ_DEBRIS:
499                         case OBJ_ASTEROID:
500                                 Num_obj_sounds_playing--;
501                                 Assert(Num_obj_sounds_playing >= 0);                                    
502                                 break;
503
504                         default:
505                                 Int3(); // get Alan
506                                 break;
507                         }                               
508                 }               
509         }
510 }
511
512 // ---------------------------------------------------------------------------------------
513 // obj_snd_stop_all()
514 //
515 // Stop all object-linked persistant sounds from playing
516 //
517 //
518 void obj_snd_stop_all()
519 {
520         object* A;
521
522         for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
523                 if ( A ) {
524                         obj_snd_stop(A, -1);
525                 }
526         }
527 }
528
529 // ---------------------------------------------------------------------------------------
530 // obj_snd_get_freq()
531 //
532 // Calculate the frequency of a sound to be played, based on the relative velocities
533 // of the source and observor
534 //
535 //      returns:                frequency of the sound
536 //
537 int obj_snd_get_freq(int source_freq, object* source, object* observor, vector *source_pos)
538 {
539         vector  v_os, v_so;     // os == observor to source, so == source to observor
540         float           vo, vs, freq;
541
542         vm_vec_normalized_dir(&v_os, source_pos, &observor->pos);
543         vm_vec_normalized_dir(&v_so, &observor->pos, source_pos);
544         
545         vo = vm_vec_dotprod(&v_os, &observor->phys_info.vel);
546         vs = vm_vec_dotprod(&v_so, &source->phys_info.vel);
547
548         freq = source_freq * ( (SPEED_SOUND + vo) / (SPEED_SOUND - vs) );
549         return fl2i(freq);
550 }
551
552
553 // ---------------------------------------------------------------------------------------
554 // obj_snd_stop_lowest_vol()
555 //
556 //      Stop a playing object sound, if it is quieter than sound at new_distance
557 //
558 // input:               new_vol                 =>      volume of requested sound to play
559 //
560 //      returns:                TRUE    =>              A sound was stopped 
561 //                                      FALSE   =>              A sound was not stopped
562 //
563 int obj_snd_stop_lowest_vol(float new_vol)
564 {
565         obj_snd                 *osp;
566         object                  *objp = NULL;
567         obj_snd                 *lowest_vol_osp = NULL;
568         float                           lowest_vol;
569         int obj_snd_index = -1;
570         int idx;
571         
572         lowest_vol = 1000.0f;
573         for ( osp = GET_FIRST(&obj_snd_list); osp !=END_OF_LIST(&obj_snd_list); osp = GET_NEXT(osp) ) {
574                 Assert(osp->objnum != -1);
575                 objp = &Objects[osp->objnum];
576
577                 if ( (osp->instance != -1) && (osp->vol < lowest_vol) ) {
578                         lowest_vol = osp->vol;
579                         lowest_vol_osp = osp;
580                 }
581         }
582
583         Assert(lowest_vol_osp != NULL);
584         objp = &Objects[lowest_vol_osp->objnum];
585
586         if ( (lowest_vol < new_vol) && (objp != NULL) ) {
587                 // determine what index in this guy the sound is
588                 for(idx=0; idx<MAX_OBJECT_SOUNDS; idx++){
589                         if(objp->objsnd_num[idx] == (lowest_vol_osp - Objsnds)){
590                                 obj_snd_index = idx;
591                                 break;
592                         }
593                 }
594
595                 if((obj_snd_index == -1) || (obj_snd_index >= MAX_OBJECT_SOUNDS)){
596                         Int3();         // get dave
597                 } else {
598                         obj_snd_stop(objp, obj_snd_index);
599                 }
600
601                 return TRUE;
602         }
603         
604         return FALSE;
605 }
606
607 //int Debug_1 = 0, Debug_2 = 0;
608
609 // ---------------------------------------------------------------------------------------
610 // maybe_play_flyby_snd()
611 //
612 // Based on how close the object is to the player ship (and relative speed), maybe
613 // play a flyby sound.  Only play flyby sound for OBJ_SHIP objects.
614 //
615 // NOTE: global data Flyby_last_objp, Flyby_next_sound, Flyby_next_repeat are 
616 //                      used.
617 //
618 void maybe_play_flyby_snd(float closest_dist, object *closest_objp)
619 {
620         if ( closest_objp == NULL || closest_objp->type != OBJ_SHIP ) {
621                 goto play_no_flyby_sound;
622         }
623
624         if ( closest_dist < FLYBY_MIN_DISTANCE ) {
625                 float relative_speed;
626                 vector diff;
627                 vm_vec_sub(&diff, &Player_obj->phys_info.vel, &closest_objp->phys_info.vel);
628
629
630                 relative_speed = vm_vec_mag_quick(&diff);
631                 if ( relative_speed > FLYBY_MIN_RELATIVE_SPEED ) {
632                         if ( timestamp_elapsed(Flyby_next_sound) ) {
633
634                                 if ( closest_objp == Flyby_last_objp ) {
635                                         if ( timestamp_elapsed(Flyby_next_repeat) ) {
636                                                 Flyby_next_repeat = timestamp(FLYBY_MIN_REPEAT_TIME);
637                                         }
638                                         else 
639                                                 goto play_no_flyby_sound;
640                                 }                               
641
642                                 Assert(closest_objp->type == OBJ_SHIP);
643                                 if(closest_objp->type != OBJ_SHIP){
644                                         return;
645                                 }
646
647                                 // pick a random species-based sound                            
648                                 ship_info *sip = &Ship_info[Ships[closest_objp->instance].ship_info_index];
649                                 int species = SPECIES_TERRAN;
650                                 if((sip->species >= 0) && (sip->species < MAX_SPECIES_NAMES)){
651                                         species = sip->species;
652                                 } 
653                                 int ship_size = 0;      // fighter
654                                 if(sip->flags & SIF_BOMBER){
655                                         ship_size = 1;
656                                 }
657
658                                 // play da sound
659                                 snd_play_3d(&Snds_flyby[species][ship_size], &closest_objp->pos, &View_position);
660                                 //snd_play_3d(&Snds_flyby[Debug_1][Debug_2], &closest_objp->pos, &View_position);
661
662                                 //float dist = vm_vec_dist(&closest_objp->pos, &View_position);
663                                 //nprintf(("AI", "Frame %i: Playing flyby sound, species = %i, size = %i, dist = %7.3f\n", Framecount, species, ship_size, dist));
664 //                              nprintf(("AI", "Frame %i: Playing flyby sound, species = %i, size = %i, dist = %7.3f\n", Framecount, Debug_1, Debug_2, dist));
665 //Debug_1 = (Debug_1+1)%3;
666 //Debug_2 = (Debug_2+1)%2;
667
668                                 joy_ff_fly_by(100 - (int) (100.0f * closest_dist / FLYBY_MIN_DISTANCE));
669
670                                 Flyby_next_sound = timestamp(FLYBY_MIN_NEXT_TIME);
671                                 Flyby_last_objp = closest_objp;
672                         }
673                 }
674         }
675
676         play_no_flyby_sound:
677         return;
678 }
679
680 // ---------------------------------------------------------------------------------------
681 // obj_snd_do_frame()
682 //
683 // Called once per frame to process the persistant sound objects
684 //
685 void obj_snd_do_frame()
686 {
687         float                           closest_dist, distance, speed_vol_multiplier, percent_max;
688         obj_snd                 *osp;
689         object                  *objp, *closest_objp;
690         game_snd                        *gs;
691         ship                            *sp;
692         int                             channel, go_ahead_flag;
693         vector                  source_pos;
694         float                           add_distance;
695
696         if ( Obj_snd_enabled == FALSE )
697                 return;
698
699         int now = timer_get_milliseconds();
700         if ( (now - Obj_snd_last_update) > 100 ) {
701                 Obj_snd_last_update=now;
702         } else {
703                 return;
704         }
705
706         closest_dist = 1000000.0f;
707         closest_objp = NULL;
708
709         for ( osp = GET_FIRST(&obj_snd_list); osp !=END_OF_LIST(&obj_snd_list); osp = GET_NEXT(osp) ) {
710                 Assert(osp != NULL);
711                 objp = &Objects[osp->objnum];
712                 if ( Player_obj == objp ) {
713                         continue;
714                 }
715                 
716                 gs = &Snds[osp->id];
717
718                 obj_snd_source_pos(&source_pos, osp);
719                 distance = vm_vec_dist_quick( &source_pos, &View_position );
720
721                 // how much extra distance do we add before attentuation?
722                 add_distance = 0.0f;
723                 if(osp->flags & OS_MAIN){
724                         add_distance = objp->radius;
725                 } 
726
727                 distance -= add_distance;
728                 if ( distance < 0 ) {
729                         distance = 0.0f;
730                 }
731
732                 // save closest distance (used for flyby sound) if this is a small ship
733                 if ( (objp->type == OBJ_SHIP) && (distance < closest_dist) ) {
734                         if ( Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP ) {
735                                 closest_dist = distance;
736                                 closest_objp = objp;
737                         }
738                 }
739
740                 // If the object is a ship, we don't want to start the engine sound unless the ship is
741                 // moving (unless flag SIF_BIG_SHIP is set)
742                 speed_vol_multiplier = 1.0f;
743                 if ( objp->type == OBJ_SHIP ) {
744                         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
745                                 if ( objp->phys_info.max_vel.xyz.z <= 0 ) {
746                                         percent_max = 0.0f;
747                                 }
748                                 else
749                                         percent_max = objp->phys_info.fspeed / objp->phys_info.max_vel.xyz.z;
750
751                                 if ( percent_max >= 0.5 )
752                                         speed_vol_multiplier = 1.0f;
753                                 else {
754                                         speed_vol_multiplier = 0.5f + (percent_max);    // linear interp: 0.5->1.0 when 0.0->0.5
755                                 }
756                         }
757                 }
758         
759                 go_ahead_flag = TRUE;
760                 float max_vol,new_vol;
761                 if ( osp->instance == -1 ) {
762                         if ( distance < Snds[osp->id].max ) {
763                                 max_vol = Snds[osp->id].default_volume;
764                                 if ( distance <= Snds[osp->id].min ) {
765                                         new_vol = max_vol;
766                                 }
767                                 else {
768                                         new_vol = max_vol - (distance - Snds[osp->id].min) * max_vol / (Snds[osp->id].max - Snds[osp->id].min);
769                                 }
770
771                                 if ( new_vol < 0.1 ) {
772                                         continue;
773                                 }
774
775                                 switch( objp->type ) {
776                                         case OBJ_SHIP:
777                                         case OBJ_DEBRIS:
778                                         case OBJ_ASTEROID:
779                                                 if ( Num_obj_sounds_playing >= MAX_OBJ_SOUNDS_PLAYING ) {
780                                                         go_ahead_flag = obj_snd_stop_lowest_vol(new_vol);
781                                                 }
782                                                 break;
783
784                                         default:
785                                                 Int3(); // get Alan
786                                                 break;
787                                 } // end switch
788
789                                 if ( go_ahead_flag ) {
790                                         if ( ds_using_ds3d() ) {
791                                                 osp->instance = snd_play_3d(gs, &source_pos, &View_position, add_distance, &objp->phys_info.vel, 1, 1.0f, SND_PRIORITY_TRIPLE_INSTANCE);
792                                                 if ( osp->instance != -1 ) {
793                                                         Num_obj_sounds_playing++;
794                                                 }
795                                         }
796                                         else {
797                                                 snd_get_3d_vol_and_pan(gs, &source_pos, &osp->vol, &osp->pan, add_distance);
798                                                 osp->instance = snd_play_looping( gs, osp->pan, 0, 0, (osp->vol*speed_vol_multiplier)/gs->default_volume, SND_PRIORITY_TRIPLE_INSTANCE );
799                                                 if ( osp->instance != -1 ) {
800                                                         osp->freq =     snd_get_pitch(osp->instance);
801                                                         Num_obj_sounds_playing++;
802                                                 }
803                                         }
804                                 }
805                                 Assert(Num_obj_sounds_playing <= MAX_OBJ_SOUNDS_PLAYING);
806
807                         } //            end if ( distance < Snds[osp->id].max )
808                 } //            if ( osp->instance == -1 )
809                 else {
810                         if ( distance > Snds[osp->id].max ) {
811                                 int sound_index = -1;
812                                 int idx;
813
814                                 // determine which sound index it is for this guy
815                                 for(idx=0; idx<MAX_OBJECT_SOUNDS; idx++){
816                                         if(objp->objsnd_num[idx] == (osp - Objsnds)){
817                                                 sound_index = idx;
818                                                 break;
819                                         }
820                                 }
821
822                                 Assert(sound_index != -1);
823                                 obj_snd_stop(objp, sound_index);                                                // currently playing sound has gone past maximum
824                         }
825                 }
826
827                 if ( osp->instance == -1 )
828                         continue;
829
830                 sp = NULL;
831                 if ( objp->type == OBJ_SHIP )
832                         sp = &Ships[objp->instance];
833
834                 if (ds_using_ds3d()) {
835                         channel = ds_get_channel(osp->instance);
836                         // for DirectSound3D sounds, re-establish the maximum speed based on the
837                         //      speed_vol_multiplier
838                         if ( sp == NULL || ( (sp != NULL) && (sp->flags & SF_ENGINES_ON) ) ) {
839                                 snd_set_volume( osp->instance, gs->default_volume*speed_vol_multiplier );
840                         }
841                         else {
842                                 // engine sound is disabled
843                                 snd_set_volume( osp->instance, 0.0f );
844                         }
845
846                         vector *vel=NULL;
847                         vel = &objp->phys_info.vel;
848
849                         // Don't play doppler effect for cruisers or captials
850                         if ( sp ) {
851                                 if ( ship_get_SIF(sp) & (SIF_BIG_SHIP | SIF_HUGE_SHIP) ) {
852                                         vel=NULL;
853                                 }
854                         }
855
856                         ds3d_update_buffer(channel, i2fl(gs->min), i2fl(gs->max), &source_pos, vel);
857                         snd_get_3d_vol_and_pan(gs, &source_pos, &osp->vol, &osp->pan, add_distance);
858                 }
859                 else {
860                         if ( sp == NULL || (sp != NULL && (sp->flags & SF_ENGINES_ON) ) ) {
861                                 snd_get_3d_vol_and_pan(gs, &source_pos, &osp->vol, &osp->pan, add_distance);
862                                 snd_set_volume( osp->instance, osp->vol*speed_vol_multiplier );
863                                 snd_set_pan( osp->instance, osp->pan );
864                                 // Don't play doppler effect for cruisers or captials
865                                 if ( objp->type == OBJ_SHIP && Doppler_enabled == TRUE ) {
866                                         if ( !(ship_get_SIF(sp) & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
867                                                 int new_freq;
868                                                 // calc doppler effect
869                                                 new_freq = obj_snd_get_freq(osp->freq, objp, Player_obj, &source_pos);
870                                                 if ( abs(new_freq - osp->freq) > OBJSND_CHANGE_FREQUENCY_THRESHOLD ) {
871                                                         snd_set_pitch( osp->instance, new_freq);
872                                                 }
873                                         }
874                                 }
875                         }
876                         else
877                                 snd_set_volume( osp->instance, 0.0f );
878                 }
879         }       // end for
880
881         // see if we want to play a flyby sound
882         maybe_play_flyby_snd(closest_dist, closest_objp);
883 }
884
885 // ---------------------------------------------------------------------------------------
886 // obj_snd_assign()
887 //
888 // Assign a persistant sound to an object.
889 //
890 // parameters:  objnum          => index of object that sound is being assigned to
891 //              i                               => Index into Snds[] array
892 //                                       fname          => filename of sound to play ( so DS3D can load the sound )
893 //
894 // returns:     -1                      => sound could not be assigned (possible, since only MAX_OBJECT_SOUNDS persistant
895 //                                                                              sound can be assigned per object).  
896 //               0                      => sound was successfully assigned
897 //
898 int obj_snd_assign(int objnum, int i, vector *pos, int main)
899 {
900         obj_snd *snd;
901         object  *objp;
902         int idx, sound_index;
903
904         objp = &Objects[objnum];
905
906         if ( Obj_snd_enabled == FALSE )
907                 return -1;
908
909         // try and find a valid objsound index
910         sound_index = -1;
911         for(idx=0; idx<MAX_OBJECT_SOUNDS; idx++){
912                 if(objp->objsnd_num[idx] == -1){
913                         sound_index = idx;
914                         break;
915                 }
916         }
917         
918         // no sound. doh!
919         if ( sound_index == -1 ){
920                 return -1;
921         }
922
923         objp->objsnd_num[sound_index] = (short)obj_snd_get_slot();
924         if ( objp->objsnd_num[sound_index] == -1 ) {
925                 nprintf(("Sound", "SOUND ==> No free object-linked sounds left\n"));
926                 return -1;
927         }
928         snd = &Objsnds[objp->objsnd_num[sound_index]];
929         snd->flags = OS_USED;
930
931         if(main){
932                 snd->flags |= OS_MAIN;
933         }
934
935         if ( i == -1 ) {
936                 return -1;
937         }
938         snd->id = i;
939
940         snd->instance = -1;
941         snd->vol = 0.0f;
942         snd->objnum = OBJ_INDEX(objp);
943         snd->next_update = 1;
944         snd->offset = *pos;
945         // vm_vec_sub(&snd->offset, pos, &objp->pos);   
946
947         // add objp to the obj_snd_list
948         list_append( &obj_snd_list, snd );
949
950         return 0;
951 }
952
953
954 // ---------------------------------------------------------------------------------------
955 // obj_snd_delete()
956 //
957 // Remove a persistant sound that has been assigned to an object.
958 //
959 // parameters:  objnum          => index of object that sound is being removed from.
960 //
961 //
962 void    obj_snd_delete(int objnum, int sndnum)
963 {
964         object  *objp;
965         obj_snd *osp;
966         int idx;
967
968         Assert(objnum >= 0 && objnum < MAX_OBJECTS);
969         objp = &Objects[objnum];
970
971         // delete all object sounds for this guy
972         for(idx=0; idx<MAX_OBJECT_SOUNDS; idx++){
973                 // no sound
974                 if ( objp->objsnd_num[idx] == -1 ){
975                         continue;
976                 }
977
978                 osp = &Objsnds[objp->objsnd_num[idx]];
979
980                 // if we're just deleting a specific sound type
981                 // and this is not one of them. skip it.
982                 if((sndnum != -1) && (osp->id != sndnum)){
983                         continue;
984                 }
985
986                 obj_snd_stop(objp, -1);
987
988                 // remove objp from the obj_snd_list
989                 list_remove( &obj_snd_list, osp );
990                 osp->objnum = -1;
991                 osp->flags = 0;
992                 osp = NULL;
993                 objp->objsnd_num[idx] = -1;
994         }
995 }
996
997 // ---------------------------------------------------------------------------------------
998 // obj_snd_delete_all()
999 //
1000 // Remove all persistant sounds
1001 //
1002 void obj_snd_delete_all()
1003 {
1004         /*
1005         obj_snd *osp, *temp;    
1006         
1007         osp = GET_FIRST(&obj_snd_list); 
1008         while( (osp != NULL) && (osp !=END_OF_LIST(&obj_snd_list)) )    {
1009                 temp = GET_NEXT(osp);
1010                 Assert( osp->objnum != -1 );
1011
1012                 obj_snd_delete( osp->objnum );
1013
1014                 osp = temp;
1015         }
1016         */
1017
1018         int idx;
1019         for(idx=0; idx<MAX_OBJ_SNDS; idx++){
1020                 if(Objsnds[idx].flags & OS_USED){
1021                         obj_snd_delete(Objsnds[idx].objnum);
1022                 }
1023         }
1024 }
1025
1026 // ---------------------------------------------------------------------------------------
1027 // obj_snd_close()
1028 //
1029 // Called once at game close to de-initialize the persistant object sound system
1030 //
1031 void obj_snd_level_close()
1032 {
1033         if ( !Obj_snd_level_inited ) {
1034                 return;
1035         }
1036         obj_snd_delete_all();
1037         Obj_snd_level_inited=0;
1038 }
1039
1040 // ---------------------------------------------------------------------------------------
1041 // obj_snd_is_playing()
1042 //
1043 // Determines if a given object-linked sound is currently playing
1044 //
1045 int obj_snd_is_playing(int index)
1046 {
1047         obj_snd *osp;
1048
1049         if ( index == -1 )
1050                 return 0;
1051
1052         Assert( index >= 0 && index < MAX_OBJ_SNDS );
1053
1054         osp = &Objsnds[index];
1055         if ( osp->instance == -1 ) 
1056                 return 0;
1057
1058         return 1;
1059 }
1060
1061 // ---------------------------------------------------------------------------------------
1062 // obj_snd_return_instance()
1063 //
1064 // Returns the sound instance for a given object-linked sound
1065 //
1066 int obj_snd_return_instance(int index)
1067 {
1068         if ( index == -1 )
1069                 return -1;
1070
1071         Assert( index >= 0 && index < MAX_OBJ_SNDS );
1072
1073         return Objsnds[index].instance;
1074 }
1075