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