mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 04:31:34 -10:00
This adds a new class which allows to create type safe resource handle types. At the moment, the engine uses ints pretty much everywhere for providing handles to resources created by the specific subsystems. Since all ints are the same to the C/C++ typesystem it is perfectly legal to pass a sound handle to the graphics API as a buffer handle which will obviously cause issues. This adds a new type called "ID" which is a wrapper class for a generic handle but with added type safety. An instance of this class cannot be passed to a function expecting another type of handle which provided compile-time type safety for these APIs. As an initial test I converted the game sound handles to this new type. I already found a bug in the mainhall system thanks to this new type. The mainhall used the game sound handles as sound instance handles which probably meant that that specific code never really worked right. I removed the code since there was no better alternative for fixing it.
928 lines
25 KiB
C++
928 lines
25 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "cmdline/cmdline.h"
|
|
#include "debugconsole/console.h"
|
|
#include "gamesnd/gamesnd.h"
|
|
#include "globalincs/linklist.h"
|
|
#include "io/joy_ff.h"
|
|
#include "io/timer.h"
|
|
#include "object/object.h"
|
|
#include "object/objectsnd.h"
|
|
#include "render/3d.h"
|
|
#include "ship/ship.h"
|
|
#include "sound/ds.h"
|
|
#include "sound/ds3d.h"
|
|
#include "species_defs/species_defs.h"
|
|
|
|
|
|
// // --mharris port hack--
|
|
// int ds_using_ds3d();
|
|
// int ds_get_channel(int);
|
|
// int ds3d_update_buffer(int, float, float, vec3d *, vec3d *);
|
|
// --end hack--
|
|
|
|
|
|
// Persistent sounds for objects (pointer to obj_snd is in object struct)
|
|
typedef struct _obj_snd {
|
|
_obj_snd *next, *prev;
|
|
int objnum; // object index of object that contains this sound
|
|
gamesnd_id id; // Index into Snds[] array
|
|
int instance; // handle of currently playing sound (a ds3d handle if USES_DS3D flag set)
|
|
int next_update; // timestamp that marks next allowed vol/pan change
|
|
float vol; // volume of sound (range: 0.0 -> 1.0)
|
|
float pan; // pan of sound (range: -1.0 -> 1.0)
|
|
int freq; // valid range: 100 -> 100000 Hz
|
|
int flags;
|
|
vec3d offset; // offset from the center of the object where the sound lives
|
|
ship_subsys *ss; //Associated subsystem
|
|
} obj_snd;
|
|
|
|
#define VOL_PAN_UPDATE 50 // time in ms to update a persistent sound vol/pan
|
|
#define MIN_PERSISTANT_VOL 0.10f
|
|
#define MIN_FORWARD_SPEED 5
|
|
#define SPEED_SOUND 600.0f // speed of sound in FreeSpace
|
|
|
|
static int MAX_OBJ_SOUNDS_PLAYING = -1; // initialized in obj_snd_level_init()
|
|
static int Num_obj_sounds_playing;
|
|
|
|
#define SND_CHANGE_FREQUENCY_THRESHOLD 10
|
|
|
|
static obj_snd obj_snd_list; // head of linked list of object sound structs
|
|
static int Doppler_enabled = TRUE;
|
|
|
|
#define MAX_OBJ_SNDS 256
|
|
obj_snd Objsnds[MAX_OBJ_SNDS];
|
|
|
|
int Obj_snd_enabled = TRUE;
|
|
int Obj_snd_last_update; // timer used to run object sound updates at fixed time intervals
|
|
int Obj_snd_level_inited=0;
|
|
|
|
// ship flyby data
|
|
#define FLYBY_MIN_DISTANCE 90
|
|
#define FLYBY_MIN_SPEED 50
|
|
#define FLYBY_MIN_RELATIVE_SPEED 100
|
|
#define FLYBY_MIN_NEXT_TIME 1000 // in ms
|
|
#define FLYBY_MIN_REPEAT_TIME 4000 // in ms
|
|
int Flyby_next_sound;
|
|
int Flyby_next_repeat;
|
|
object *Flyby_last_objp;
|
|
|
|
// return the world pos of the sound source on a ship.
|
|
void obj_snd_source_pos(vec3d *sound_pos, obj_snd *osp)
|
|
{
|
|
vec3d offset_world;
|
|
object *objp = &Objects[osp->objnum];
|
|
|
|
// get sound pos in world coords
|
|
vm_vec_unrotate(&offset_world, &osp->offset, &objp->orient);
|
|
vm_vec_add(sound_pos, &objp->pos, &offset_world);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// dcf_objsnd()
|
|
//
|
|
// Debug console function for object linked persistent sounds
|
|
//
|
|
//XSTR:OFF
|
|
DCF(objsnd, "Persistent sound stuff" )
|
|
{
|
|
char buf1[4];
|
|
char buf2[MAX_NAME_LEN];
|
|
obj_snd *osp;
|
|
SCP_string arg;
|
|
|
|
if (dc_optional_string_either("help", "--help")) {
|
|
dc_printf ("Usage: objsnd [-list]\n");
|
|
dc_printf ("[-list] -- displays status of all objects with linked sounds\n");
|
|
dc_printf ("with no parameters, object sounds are toggled on/off\n");
|
|
return;
|
|
}
|
|
|
|
if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
|
|
dc_printf( "Object sounds are: %s\n", (Obj_snd_enabled?"ON":"OFF") );
|
|
}
|
|
|
|
if (dc_optional_string("-list")) {
|
|
for ( osp = GET_FIRST(&obj_snd_list); osp !=END_OF_LIST(&obj_snd_list); osp = GET_NEXT(osp) ) {
|
|
vec3d source_pos;
|
|
float distance;
|
|
|
|
Assert(osp != NULL);
|
|
if ( osp->instance == -1 ) {
|
|
continue;
|
|
//sprintf(buf1,"OFF");
|
|
} else {
|
|
sprintf(buf1,"ON");
|
|
}
|
|
|
|
if ( Objects[osp->objnum].type == OBJ_SHIP ) {
|
|
strcpy_s(buf2, Ships[Objects[osp->objnum].instance].ship_name);
|
|
}
|
|
else if ( Objects[osp->objnum].type == OBJ_DEBRIS ) {
|
|
sprintf(buf2, "Debris");
|
|
}
|
|
else {
|
|
sprintf(buf2, "Unknown");
|
|
}
|
|
|
|
obj_snd_source_pos(&source_pos, osp);
|
|
distance = vm_vec_dist_quick( &source_pos, &View_position );
|
|
|
|
dc_printf("Object %d => name: %s vol: %.2f pan: %.2f dist: %.2f status: %s\n", osp->objnum, buf2, osp->vol, osp->pan, distance, buf1);
|
|
} // end for
|
|
|
|
dc_printf("Number object-linked sounds playing: %d\n", Num_obj_sounds_playing);
|
|
return;
|
|
}
|
|
|
|
if (!dc_maybe_stuff_string_white(arg)) {
|
|
// No arguments, toggle snd on/off
|
|
if ( Obj_snd_enabled == TRUE ) {
|
|
obj_snd_stop_all();
|
|
Obj_snd_enabled = FALSE;
|
|
} else {
|
|
Obj_snd_enabled = TRUE;
|
|
}
|
|
} else {
|
|
dc_printf("Unknown argument '%s'\n", arg.c_str());
|
|
}
|
|
}
|
|
//XSTR:ON
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Debug console function for toggling doppler effection on/off
|
|
//
|
|
DCF_BOOL( doppler, Doppler_enabled )
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_get_slot()
|
|
//
|
|
// Get a free slot in the Objsnds[] array
|
|
//
|
|
// returns -1 if no slot is available
|
|
int obj_snd_get_slot()
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; i < MAX_OBJ_SNDS; i++ ) {
|
|
if ( !(Objsnds[i].flags & OS_USED) )
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_init()
|
|
//
|
|
// Called once at level start to initialize the persistent object sound system
|
|
//
|
|
void obj_snd_level_init()
|
|
{
|
|
int i;
|
|
|
|
if (MAX_OBJ_SOUNDS_PLAYING < 0)
|
|
{
|
|
MAX_OBJ_SOUNDS_PLAYING = Cmdline_no_enhanced_sound ? 5 : 12;
|
|
}
|
|
|
|
list_init(&obj_snd_list);
|
|
for ( i = 0; i < MAX_OBJ_SNDS; i++ ) {
|
|
Objsnds[i].flags = 0;
|
|
}
|
|
|
|
Num_obj_sounds_playing = 0;
|
|
Flyby_next_sound = 1;
|
|
Flyby_next_repeat = 1;
|
|
Flyby_last_objp = NULL;
|
|
Obj_snd_last_update=0;
|
|
|
|
if ( !snd_is_inited() ) {
|
|
Obj_snd_enabled = FALSE;
|
|
return;
|
|
}
|
|
|
|
Obj_snd_level_inited=1;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_stop()
|
|
//
|
|
// Stop a persistent sound from playing.
|
|
//
|
|
// parameters: objp => pointer to object that sound is to be stopped for
|
|
//
|
|
//
|
|
void obj_snd_stop(object *objp, int index)
|
|
{
|
|
obj_snd *osp;
|
|
|
|
// sanity
|
|
if(index >= (int) objp->objsnd_num.size()){
|
|
Error(LOCATION, "Object sound index %d is bigger than the actual size %d!", index, (int) objp->objsnd_num.size());
|
|
return;
|
|
}
|
|
|
|
// if index is -1, kill all sounds for this guy
|
|
if(index == -1){
|
|
// kill all sounds for this guy
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter){
|
|
if ( *iter == -1 ){
|
|
continue;
|
|
}
|
|
|
|
osp = &Objsnds[*iter];
|
|
|
|
if ( osp->instance != -1 ) {
|
|
snd_stop(osp->instance);
|
|
osp->instance = -1;
|
|
switch(objp->type) {
|
|
case OBJ_SHIP:
|
|
case OBJ_GHOST:
|
|
case OBJ_DEBRIS:
|
|
case OBJ_ASTEROID:
|
|
Num_obj_sounds_playing--;
|
|
Assert(Num_obj_sounds_playing >= 0);
|
|
break;
|
|
|
|
default:
|
|
Int3(); // get Alan
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if ( objp->objsnd_num[index] == -1 ){
|
|
return;
|
|
}
|
|
|
|
osp = &Objsnds[objp->objsnd_num[index]];
|
|
|
|
if ( osp->instance != -1 ) {
|
|
snd_stop(osp->instance);
|
|
osp->instance = -1;
|
|
switch(objp->type) {
|
|
case OBJ_SHIP:
|
|
case OBJ_GHOST:
|
|
case OBJ_DEBRIS:
|
|
case OBJ_ASTEROID:
|
|
Num_obj_sounds_playing--;
|
|
Assert(Num_obj_sounds_playing >= 0);
|
|
break;
|
|
|
|
default:
|
|
Int3(); // get Alan
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_stop_all()
|
|
//
|
|
// Stop all object-linked persistent sounds from playing
|
|
//
|
|
//
|
|
void obj_snd_stop_all()
|
|
{
|
|
object* A;
|
|
|
|
for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
|
|
obj_snd_stop(A, -1);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_get_freq()
|
|
//
|
|
// Calculate the frequency of a sound to be played, based on the relative velocities
|
|
// of the source and observor
|
|
//
|
|
// returns: frequency of the sound
|
|
//
|
|
int obj_snd_get_freq(int source_freq, object* source, object* observor, vec3d *source_pos)
|
|
{
|
|
vec3d v_os, v_so; // os == observor to source, so == source to observor
|
|
float vo, vs, freq;
|
|
|
|
vm_vec_normalized_dir(&v_os, source_pos, &observor->pos);
|
|
vm_vec_normalized_dir(&v_so, &observor->pos, source_pos);
|
|
|
|
vo = vm_vec_dot(&v_os, &observor->phys_info.vel);
|
|
vs = vm_vec_dot(&v_so, &source->phys_info.vel);
|
|
|
|
freq = source_freq * ( (SPEED_SOUND + vo) / (SPEED_SOUND - vs) );
|
|
return fl2i(freq);
|
|
}
|
|
|
|
|
|
/**
|
|
* Stop a playing object sound, if it is quieter than sound at new_distance
|
|
*
|
|
* @param new_vol Volume of requested sound to play
|
|
*
|
|
* @return 1 A sound was stopped
|
|
* @return 0 A sound was not stopped
|
|
*/
|
|
int obj_snd_stop_lowest_vol(float new_vol)
|
|
{
|
|
obj_snd *osp;
|
|
object *objp = NULL;
|
|
obj_snd *lowest_vol_osp = NULL;
|
|
float lowest_vol;
|
|
int obj_snd_index = -1;
|
|
|
|
lowest_vol = 1000.0f;
|
|
for ( osp = GET_FIRST(&obj_snd_list); osp !=END_OF_LIST(&obj_snd_list); osp = GET_NEXT(osp) ) {
|
|
Assert(osp->objnum != -1);
|
|
objp = &Objects[osp->objnum];
|
|
|
|
if ( (osp->instance != -1) && (osp->vol < lowest_vol) ) {
|
|
lowest_vol = osp->vol;
|
|
lowest_vol_osp = osp;
|
|
}
|
|
}
|
|
|
|
if (lowest_vol_osp != NULL)
|
|
objp = &Objects[lowest_vol_osp->objnum];
|
|
|
|
if ( (lowest_vol < new_vol) && (objp != NULL) ) {
|
|
int idx = 0;
|
|
// determine what index in this guy the sound is
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter, ++idx){
|
|
if(*iter == (lowest_vol_osp - Objsnds)){
|
|
obj_snd_index = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if((obj_snd_index == -1) || (obj_snd_index >= (int) objp->objsnd_num.size())){
|
|
Int3(); // get dave
|
|
} else {
|
|
obj_snd_stop(objp, obj_snd_index);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//int Debug_1 = 0, Debug_2 = 0;
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// maybe_play_flyby_snd()
|
|
//
|
|
// Based on how close the object is to the camera (and relative speed), maybe
|
|
// play a flyby sound. Only play flyby sound for OBJ_SHIP objects.
|
|
//
|
|
// NOTE: global data Flyby_last_objp, Flyby_next_sound, Flyby_next_repeat are
|
|
// used.
|
|
//
|
|
void maybe_play_flyby_snd(float closest_dist, object *closest_objp, object *listener_objp)
|
|
{
|
|
if ( closest_objp == NULL || closest_objp->type != OBJ_SHIP || closest_objp == listener_objp ) {
|
|
return;
|
|
}
|
|
|
|
if ( closest_dist < FLYBY_MIN_DISTANCE ) {
|
|
float relative_speed;
|
|
vec3d diff;
|
|
vm_vec_sub(&diff, &listener_objp->phys_info.vel, &closest_objp->phys_info.vel);
|
|
|
|
|
|
relative_speed = vm_vec_mag_quick(&diff);
|
|
if ( relative_speed > FLYBY_MIN_RELATIVE_SPEED ) {
|
|
if ( timestamp_elapsed(Flyby_next_sound) ) {
|
|
|
|
if ( closest_objp == Flyby_last_objp ) {
|
|
if ( timestamp_elapsed(Flyby_next_repeat) ) {
|
|
Flyby_next_repeat = timestamp(FLYBY_MIN_REPEAT_TIME);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
Assert(closest_objp->type == OBJ_SHIP);
|
|
if(closest_objp->type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
|
|
// pick a random species-based sound
|
|
|
|
ship_info *sip = &Ship_info[Ships[closest_objp->instance].ship_info_index];
|
|
game_snd *snd;
|
|
|
|
if (sip->flags[Ship::Info_Flags::Bomber])
|
|
snd = &Species_info[sip->species].snd_flyby_bomber;
|
|
else
|
|
snd = &Species_info[sip->species].snd_flyby_fighter;
|
|
|
|
if (snd->sound_entries.empty())
|
|
return; //This species does not define any relevant flyby sounds
|
|
|
|
// play da sound
|
|
snd_play_3d(snd, &closest_objp->pos, &View_position);
|
|
|
|
//float dist = vm_vec_dist(&closest_objp->pos, &View_position);
|
|
//nprintf(("AI", "Frame %i: Playing flyby sound, species = %i, size = %i, dist = %7.3f\n", Framecount, species, ship_size, dist));
|
|
// nprintf(("AI", "Frame %i: Playing flyby sound, species = %i, size = %i, dist = %7.3f\n", Framecount, Debug_1, Debug_2, dist));
|
|
//Debug_1 = (Debug_1+1)%3;
|
|
//Debug_2 = (Debug_2+1)%2;
|
|
|
|
joy_ff_fly_by(100 - (int) (100.0f * closest_dist / FLYBY_MIN_DISTANCE));
|
|
|
|
Flyby_next_sound = timestamp(FLYBY_MIN_NEXT_TIME);
|
|
Flyby_last_objp = closest_objp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_do_frame()
|
|
//
|
|
// Called once per frame to process the persistent sound objects
|
|
//
|
|
void obj_snd_do_frame()
|
|
{
|
|
float closest_dist, distance, speed_vol_multiplier, rot_vol_mult, percent_max, alive_vol_mult;
|
|
obj_snd *osp;
|
|
object *objp, *closest_objp;
|
|
game_snd *gs;
|
|
ship *sp;
|
|
int channel, go_ahead_flag;
|
|
vec3d source_pos;
|
|
float add_distance;
|
|
|
|
if ( Obj_snd_enabled == FALSE )
|
|
return;
|
|
|
|
int now = timer_get_milliseconds();
|
|
if ( (now - Obj_snd_last_update) > 100 ) {
|
|
Obj_snd_last_update=now;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
closest_dist = 1000000.0f;
|
|
closest_objp = NULL;
|
|
|
|
object *observer_obj = NULL;
|
|
if (Viewer_obj != NULL) {
|
|
observer_obj = Viewer_obj;
|
|
} else if (Viewer_mode & VM_OTHER_SHIP && Player_ai->target_objnum != -1) {
|
|
// apparently Viewer_obj is still null when Viewing Other Ship externally
|
|
observer_obj = &Objects[Player_ai->target_objnum];
|
|
} else {
|
|
observer_obj = Player_obj;
|
|
}
|
|
|
|
for ( osp = GET_FIRST(&obj_snd_list); osp !=END_OF_LIST(&obj_snd_list); osp = GET_NEXT(osp) ) {
|
|
Assert(osp != NULL);
|
|
objp = &Objects[osp->objnum];
|
|
if ( Player_obj == objp && observer_obj == Player_obj ) {
|
|
// we don't play the engine sound if the view is from the player
|
|
continue;
|
|
}
|
|
|
|
gs = gamesnd_get_game_sound(osp->id);
|
|
|
|
obj_snd_source_pos(&source_pos, osp);
|
|
distance = vm_vec_dist_quick( &source_pos, &View_position );
|
|
|
|
// how much extra distance do we add before attentuation?
|
|
add_distance = 0.0f;
|
|
if(osp->flags & OS_MAIN){
|
|
add_distance = objp->radius;
|
|
}
|
|
|
|
distance -= add_distance;
|
|
if ( distance < 0.0f ) {
|
|
distance = 0.0f;
|
|
}
|
|
|
|
// save closest distance (used for flyby sound) if this is a small ship (and not the observer)
|
|
if ( (objp->type == OBJ_SHIP) && (distance < closest_dist) && (objp != observer_obj) ) {
|
|
if ( Ship_info[Ships[objp->instance].ship_info_index].is_small_ship() ) {
|
|
closest_dist = distance;
|
|
closest_objp = objp;
|
|
}
|
|
}
|
|
|
|
// If the object is a ship, we don't want to start the engine sound unless the ship is
|
|
// moving (unless flag SIF_BIG_SHIP is set)
|
|
speed_vol_multiplier = 1.0f;
|
|
rot_vol_mult = 1.0f;
|
|
alive_vol_mult = 1.0f;
|
|
if ( objp->type == OBJ_SHIP ) {
|
|
ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
|
|
if ( !(sip->is_big_or_huge()) ) {
|
|
if ( objp->phys_info.max_vel.xyz.z <= 0.0f ) {
|
|
percent_max = 0.0f;
|
|
}
|
|
else
|
|
percent_max = objp->phys_info.fspeed / objp->phys_info.max_vel.xyz.z;
|
|
|
|
if ( sip->min_engine_vol == -1.0f) {
|
|
// Retail behavior: volume ramps from 0.5 (when stationary) to 1.0 (when at half speed)
|
|
if ( percent_max >= 0.5f ) {
|
|
speed_vol_multiplier = 1.0f;
|
|
} else {
|
|
speed_vol_multiplier = 0.5f + (percent_max); // linear interp: 0.5->1.0 when 0.0->0.5
|
|
}
|
|
} else {
|
|
// Volume ramps from min_engine_vol (when stationary) to 1.0 (when at full speed)
|
|
speed_vol_multiplier = sip->min_engine_vol + ((1.0f - sip->min_engine_vol) * percent_max);
|
|
}
|
|
}
|
|
if (osp->ss != NULL)
|
|
{
|
|
if (osp->flags & OS_TURRET_BASE_ROTATION)
|
|
{
|
|
if (osp->ss->base_rotation_rate_pct > 0.0f)
|
|
rot_vol_mult = ((0.25f + (0.75f * osp->ss->base_rotation_rate_pct)) * osp->ss->system_info->turret_base_rotation_snd_mult);
|
|
else
|
|
rot_vol_mult = 0.0f;
|
|
}
|
|
if (osp->flags & OS_TURRET_GUN_ROTATION)
|
|
{
|
|
if (osp->ss->gun_rotation_rate_pct > 0.0f)
|
|
rot_vol_mult = ((0.25f + (0.75f * osp->ss->gun_rotation_rate_pct)) * osp->ss->system_info->turret_gun_rotation_snd_mult);
|
|
else
|
|
rot_vol_mult = 0.0f;
|
|
}
|
|
if (osp->flags & OS_SUBSYS_ROTATION )
|
|
{
|
|
if (osp->ss->flags[Ship::Subsystem_Flags::Rotates]) {
|
|
rot_vol_mult = 1.0f;
|
|
} else {
|
|
rot_vol_mult = 0.0f;
|
|
}
|
|
}
|
|
if (osp->flags & OS_SUBSYS_ALIVE)
|
|
{
|
|
if (osp->ss->current_hits > 0.0f) {
|
|
alive_vol_mult = 1.0f;
|
|
} else {
|
|
alive_vol_mult = 0.0f;
|
|
}
|
|
}
|
|
if (osp->flags & OS_SUBSYS_DEAD)
|
|
{
|
|
if (osp->ss->current_hits <= 0.0f) {
|
|
alive_vol_mult = 1.0f;
|
|
} else {
|
|
alive_vol_mult = 0.0f;
|
|
}
|
|
}
|
|
if (osp->flags & OS_SUBSYS_DAMAGED)
|
|
{
|
|
alive_vol_mult = osp->ss->current_hits / osp->ss->max_hits;
|
|
CLAMP(alive_vol_mult, 0.0f, 1.0f);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
go_ahead_flag = TRUE;
|
|
float max_vol,new_vol;
|
|
if ( osp->instance == -1 ) {
|
|
if ( distance < gs->max ) {
|
|
max_vol = gs->volume_range.max();
|
|
if ( distance <= gs->min ) {
|
|
new_vol = max_vol;
|
|
}
|
|
else {
|
|
new_vol = max_vol - (distance - gs->min) * max_vol
|
|
/ (gs->max - gs->min);
|
|
}
|
|
|
|
if ( new_vol < 0.1f ) {
|
|
continue;
|
|
}
|
|
|
|
switch( objp->type ) {
|
|
case OBJ_SHIP:
|
|
case OBJ_DEBRIS:
|
|
case OBJ_ASTEROID:
|
|
if ( Num_obj_sounds_playing >= MAX_OBJ_SOUNDS_PLAYING ) {
|
|
go_ahead_flag = obj_snd_stop_lowest_vol(new_vol);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Int3(); // get Alan
|
|
break;
|
|
} // end switch
|
|
|
|
if ( go_ahead_flag ) {
|
|
osp->instance = snd_play_3d(gs, &source_pos, &View_position, add_distance, &objp->phys_info.vel, 1, 1.0f, SND_PRIORITY_TRIPLE_INSTANCE, NULL, 1.0f, 0, true);
|
|
if ( osp->instance != -1 ) {
|
|
Num_obj_sounds_playing++;
|
|
}
|
|
}
|
|
Assert(Num_obj_sounds_playing <= MAX_OBJ_SOUNDS_PLAYING);
|
|
|
|
} // end if ( distance < Snds[osp->id].max )
|
|
} // if ( osp->instance == -1 )
|
|
else {
|
|
if ( distance > gamesnd_get_game_sound(osp->id)->max ) {
|
|
int sound_index = -1;
|
|
int idx = 0;
|
|
|
|
// determine which sound index it is for this guy
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter, ++idx){
|
|
if(*iter == (osp - Objsnds)){
|
|
sound_index = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assert(sound_index != -1);
|
|
obj_snd_stop(objp, sound_index); // currently playing sound has gone past maximum
|
|
}
|
|
}
|
|
|
|
if ( osp->instance == -1 )
|
|
continue;
|
|
|
|
sp = NULL;
|
|
if ( objp->type == OBJ_SHIP )
|
|
sp = &Ships[objp->instance];
|
|
|
|
|
|
channel = ds_get_channel(osp->instance);
|
|
// for DirectSound3D sounds, re-establish the maximum speed based on the
|
|
// speed_vol_multiplier
|
|
if ( sp == NULL || ( (sp != NULL) && (sp->flags[Ship::Ship_Flags::Engines_on]) ) ) {
|
|
snd_set_volume( osp->instance, gs->volume_range.next() *speed_vol_multiplier*rot_vol_mult*alive_vol_mult );
|
|
}
|
|
else {
|
|
// engine sound is disabled
|
|
snd_set_volume( osp->instance, 0.0f );
|
|
}
|
|
|
|
vec3d vel = objp->phys_info.vel;
|
|
|
|
// Don't play doppler effect for cruisers or capitals
|
|
if ( sp ) {
|
|
if ( Ship_info[sp->ship_info_index].is_big_or_huge() ) {
|
|
vel = vmd_zero_vector;
|
|
}
|
|
}
|
|
|
|
ds3d_update_buffer(channel, i2fl(gs->min), i2fl(gs->max), &source_pos, &vel);
|
|
snd_get_3d_vol_and_pan(gs, &source_pos, &osp->vol, &osp->pan, add_distance);
|
|
} // end for
|
|
|
|
// see if we want to play a flyby sound
|
|
maybe_play_flyby_snd(closest_dist, closest_objp, observer_obj);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_assign()
|
|
//
|
|
// Assign a persistent sound to an object.
|
|
//
|
|
// parameters: objnum => index of object that sound is being assigned to
|
|
// i => Index into Snds[] array
|
|
// fname => filename of sound to play ( so DS3D can load the sound )
|
|
//
|
|
// returns: -1 => sound could not be assigned (possible, since only MAX_OBJECT_SOUNDS persistent
|
|
// sound can be assigned per object).
|
|
// >= 0 => sound was successfully assigned
|
|
//
|
|
int obj_snd_assign(int objnum, gamesnd_id sndnum, vec3d *pos, int main, int flags, ship_subsys *associated_sub)
|
|
{
|
|
if(objnum < 0 || objnum > MAX_OBJECTS)
|
|
return -1;
|
|
|
|
if(!sndnum.isValid())
|
|
return -1;
|
|
|
|
if ( Obj_snd_enabled == FALSE )
|
|
return -1;
|
|
|
|
//Initialize object sound list, if not initialized
|
|
if (!Obj_snd_level_inited)
|
|
obj_snd_level_init();
|
|
|
|
obj_snd *snd = NULL;
|
|
object *objp = &Objects[objnum];
|
|
int sound_index;
|
|
int idx = 0;
|
|
|
|
// try and find a valid objsound index
|
|
sound_index = -1;
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter, ++idx){
|
|
if(*iter == -1){
|
|
sound_index = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// no sound slot free, make a new one!
|
|
if ( sound_index == -1 ){
|
|
sound_index = (int) objp->objsnd_num.size();
|
|
objp->objsnd_num.push_back(-1);
|
|
}
|
|
|
|
objp->objsnd_num[sound_index] = obj_snd_get_slot();
|
|
if ( objp->objsnd_num[sound_index] == -1 ) {
|
|
nprintf(("Sound", "SOUND ==> No free object-linked sounds left\n"));
|
|
return -1;
|
|
}
|
|
snd = &Objsnds[objp->objsnd_num[sound_index]];
|
|
snd->flags = OS_USED;
|
|
|
|
if(main){
|
|
snd->flags |= OS_MAIN;
|
|
}
|
|
|
|
if(flags > 0){
|
|
snd->flags |= flags;
|
|
}
|
|
|
|
snd->id = sndnum;
|
|
|
|
snd->instance = -1;
|
|
snd->vol = 0.0f;
|
|
snd->objnum = OBJ_INDEX(objp);
|
|
snd->next_update = 1;
|
|
snd->offset = *pos;
|
|
snd->ss = associated_sub;
|
|
// vm_vec_sub(&snd->offset, pos, &objp->pos);
|
|
|
|
// add objp to the obj_snd_list
|
|
list_append( &obj_snd_list, snd );
|
|
|
|
return sound_index;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_delete()
|
|
//
|
|
// Remove a persistent sound that has been assigned to an object.
|
|
//
|
|
// parameters: objnum => index of object that sound is being removed from.
|
|
// index => index of sound in objsnd_num
|
|
//
|
|
void obj_snd_delete(int objnum, int index)
|
|
{
|
|
//Sanity checking
|
|
Assert(objnum > -1 && objnum < MAX_OBJECTS);
|
|
|
|
object *objp = &Objects[objnum];
|
|
|
|
Assert(index > -1 && index < (int) objp->objsnd_num.size());
|
|
|
|
obj_snd *osp = &Objsnds[objp->objsnd_num[index]];
|
|
|
|
//Stop the sound
|
|
obj_snd_stop(objp, index);
|
|
|
|
// remove objp from the obj_snd_list
|
|
list_remove( &obj_snd_list, osp );
|
|
osp->objnum = -1;
|
|
osp->flags = 0;
|
|
osp = NULL;
|
|
objp->objsnd_num[index] = -1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_delete_type()
|
|
//
|
|
// Remove every similar persistent sound that has been assigned to an object.
|
|
//
|
|
// parameters: objnum => index of object that sound is being removed from.
|
|
// sndnum => index of sound that we're trying to completely get rid of
|
|
// -1 to delete all persistent sounds on ship.
|
|
//
|
|
//
|
|
void obj_snd_delete_type(int objnum, gamesnd_id sndnum, ship_subsys *ss)
|
|
{
|
|
object *objp;
|
|
obj_snd *osp;
|
|
|
|
if(objnum < 0 || objnum >= MAX_OBJECTS)
|
|
return;
|
|
|
|
objp = &Objects[objnum];
|
|
|
|
int idx = 0;
|
|
//Go through the list and get sounds that match criteria
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter, ++idx){
|
|
// no sound
|
|
if ( *iter == -1 ){
|
|
continue;
|
|
}
|
|
|
|
osp = &Objsnds[*iter];
|
|
|
|
// if we're just deleting a specific sound type
|
|
// and this is not one of them. skip it.
|
|
//Also check if this is assigned to the right subsystem, if one has been given.
|
|
if(((sndnum.isValid()) && (osp->id != sndnum))
|
|
|| ((ss != NULL) && (osp->ss != ss))){
|
|
continue;
|
|
}
|
|
|
|
obj_snd_delete(objnum, idx);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_delete_all()
|
|
//
|
|
// Remove all persistent sounds
|
|
//
|
|
void obj_snd_delete_all()
|
|
{
|
|
int idx;
|
|
for(idx=0; idx<MAX_OBJ_SNDS; idx++){
|
|
if(Objsnds[idx].flags & OS_USED){
|
|
obj_snd_delete_type(Objsnds[idx].objnum);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_close()
|
|
//
|
|
// Called once at game close to de-initialize the persistent object sound system
|
|
//
|
|
void obj_snd_level_close()
|
|
{
|
|
if ( !Obj_snd_level_inited ) {
|
|
return;
|
|
}
|
|
obj_snd_delete_all();
|
|
Obj_snd_level_inited=0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_is_playing()
|
|
//
|
|
// Determines if a given object-linked sound is currently playing
|
|
//
|
|
int obj_snd_is_playing(int object, int index)
|
|
{
|
|
if ( obj_snd_return_instance(object, index) < 0 )
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_return_instance()
|
|
//
|
|
// Returns the sound instance for a given object-linked sound
|
|
//
|
|
int obj_snd_return_instance(int objnum, int index)
|
|
{
|
|
if ( objnum < 0 || objnum >= MAX_OBJECTS)
|
|
return -1;
|
|
|
|
if ( index < 0 || index >= MAX_OBJ_SNDS )
|
|
return -1;
|
|
|
|
object *objp = &Objects[objnum];
|
|
obj_snd *osp = &Objsnds[objp->objsnd_num[index]];
|
|
|
|
return osp->instance;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_update_offset()
|
|
//
|
|
// Updates offset of the given object sound
|
|
//
|
|
int obj_snd_update_offset(int objnum, int index, vec3d *new_offset)
|
|
{
|
|
if ( objnum < 0 || objnum >= MAX_OBJECTS)
|
|
return 0;
|
|
|
|
if ( index < 0 || index >= MAX_OBJ_SNDS )
|
|
return 0;
|
|
|
|
object *objp = &Objects[objnum];
|
|
obj_snd *osp = &Objsnds[objp->objsnd_num[index]];
|
|
osp->offset = *new_offset;
|
|
|
|
return 1;
|
|
}
|