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