Major sound-effect architectural reworking from Eric Hofman, allowing
individual aircraft to have different sounds (and cleaning up my code a fair bit). The most important user-visible change is the renaming of the /sim/sound property to /sim/sound/audible.
This commit is contained in:
parent
ad8558a54f
commit
d514e8c255
7 changed files with 499 additions and 405 deletions
|
@ -1139,7 +1139,8 @@ static void fgMainLoop( void ) {
|
||||||
|
|
||||||
// Run audio scheduler
|
// Run audio scheduler
|
||||||
#ifdef ENABLE_AUDIO_SUPPORT
|
#ifdef ENABLE_AUDIO_SUPPORT
|
||||||
if ( fgGetBool("/sim/sound") && globals->get_soundmgr()->is_working() ) {
|
if ( fgGetBool("/sim/sound/audible")
|
||||||
|
&& globals->get_soundmgr()->is_working() ) {
|
||||||
globals->get_fx()->update(1); // FIXME: use dt
|
globals->get_fx()->update(1); // FIXME: use dt
|
||||||
globals->get_soundmgr()->update(1); // FIXME: use dt
|
globals->get_soundmgr()->update(1); // FIXME: use dt
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ fgSetDefaults ()
|
||||||
// Features
|
// Features
|
||||||
fgSetBool("/sim/hud/visibility", false);
|
fgSetBool("/sim/hud/visibility", false);
|
||||||
fgSetBool("/sim/panel/visibility", true);
|
fgSetBool("/sim/panel/visibility", true);
|
||||||
fgSetBool("/sim/sound", true);
|
fgSetBool("/sim/sound/audible", true);
|
||||||
fgSetBool("/sim/hud/antialiased", false);
|
fgSetBool("/sim/hud/antialiased", false);
|
||||||
|
|
||||||
// Flight Model options
|
// Flight Model options
|
||||||
|
@ -587,9 +587,9 @@ parse_option (const string& arg)
|
||||||
} else if ( arg == "--enable-panel" ) {
|
} else if ( arg == "--enable-panel" ) {
|
||||||
fgSetBool("/sim/panel/visibility", true);
|
fgSetBool("/sim/panel/visibility", true);
|
||||||
} else if ( arg == "--disable-sound" ) {
|
} else if ( arg == "--disable-sound" ) {
|
||||||
fgSetBool("/sim/sound", false);
|
fgSetBool("/sim/sound/audible", false);
|
||||||
} else if ( arg == "--enable-sound" ) {
|
} else if ( arg == "--enable-sound" ) {
|
||||||
fgSetBool("/sim/sound", true);
|
fgSetBool("/sim/sound/audible", true);
|
||||||
} else if ( arg.find( "--airport-id=") == 0 ) {
|
} else if ( arg.find( "--airport-id=") == 0 ) {
|
||||||
// NB: changed property name!!!
|
// NB: changed property name!!!
|
||||||
fgSetString("/sim/startup/airport-id", arg.substr(13));
|
fgSetString("/sim/startup/airport-id", arg.substr(13));
|
||||||
|
|
|
@ -2,6 +2,7 @@ noinst_LIBRARIES = libSound.a
|
||||||
|
|
||||||
libSound_a_SOURCES = \
|
libSound_a_SOURCES = \
|
||||||
beacon.cxx beacon.hxx \
|
beacon.cxx beacon.hxx \
|
||||||
|
fg_sound.cxx fg_sound.hxx \
|
||||||
fg_fx.cxx fg_fx.hxx \
|
fg_fx.cxx fg_fx.hxx \
|
||||||
morse.cxx morse.hxx \
|
morse.cxx morse.hxx \
|
||||||
soundmgr.cxx soundmgr.hxx
|
soundmgr.cxx soundmgr.hxx
|
||||||
|
|
|
@ -21,185 +21,63 @@
|
||||||
//
|
//
|
||||||
// $Id$
|
// $Id$
|
||||||
|
|
||||||
#include <FDM/flight.hxx> // FIXME: remove direct dependencies
|
#include <simgear/misc/props.hxx>
|
||||||
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
#include <simgear/misc/exception.hxx>
|
||||||
|
#ifdef __BORLANDC__
|
||||||
|
# define exception c_exception
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
|
|
||||||
#include "fg_fx.hxx"
|
#include "fg_fx.hxx"
|
||||||
|
#include "fg_sound.hxx"
|
||||||
|
|
||||||
static const char * engine_names[FGFX::MAX_ENGINES] = {
|
|
||||||
"engine0",
|
|
||||||
"engine1"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char * crank_names[FGFX::MAX_ENGINES] = {
|
|
||||||
"crank0",
|
|
||||||
"crank1"
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
FGFX::FGFX ()
|
FGFX::FGFX ()
|
||||||
: _old_flap_position(0),
|
|
||||||
_old_gear_position(0),
|
|
||||||
_wind(0),
|
|
||||||
_stall(0),
|
|
||||||
_rumble(0),
|
|
||||||
_flaps(0),
|
|
||||||
_gear_up(0),
|
|
||||||
_gear_dn(0),
|
|
||||||
_squeal(0),
|
|
||||||
_click(0),
|
|
||||||
_stall_warning_prop(0),
|
|
||||||
_flaps_prop(0),
|
|
||||||
_gear_prop(0)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < MAX_ENGINES; i++) {
|
|
||||||
_engine[i] = 0;
|
|
||||||
_crank[i] = 0;
|
|
||||||
_engine_running_prop[i] = 0;
|
|
||||||
_engine_cranking_prop[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FGFX::~FGFX ()
|
FGFX::~FGFX ()
|
||||||
{
|
{
|
||||||
// FIXME: is this right, or does the
|
|
||||||
// sound manager assume pointer ownership?
|
|
||||||
for (int i = 0; i < MAX_ENGINES; i++) {
|
|
||||||
delete _engine[i];
|
|
||||||
delete _crank[i];
|
|
||||||
}
|
|
||||||
delete _wind;
|
|
||||||
delete _stall;
|
|
||||||
delete _rumble;
|
|
||||||
|
|
||||||
delete _flaps;
|
|
||||||
delete _gear_up;
|
|
||||||
delete _gear_dn;
|
|
||||||
delete _squeal;
|
|
||||||
delete _click;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
FGFX::init ()
|
FGFX::init()
|
||||||
{
|
{
|
||||||
FGSoundMgr * mgr = globals->get_soundmgr();
|
const SGPropertyNode * node = fgGetNode("/sim/sound", true);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
//
|
SGPath path( globals->get_fg_root() );
|
||||||
// Create and add engine-related sounds.
|
if (node->getStringValue("path") == "") {
|
||||||
//
|
SG_LOG(SG_GENERAL, SG_ALERT, "Incorect path in configuration file.");
|
||||||
for (i = 0; i < MAX_ENGINES; i++) {
|
return;
|
||||||
// Engine
|
}
|
||||||
_engine[i] =
|
|
||||||
new FGSimpleSound(fgGetString("/sim/sounds/engine/path",
|
|
||||||
"Sounds/wasp.wav"));
|
|
||||||
_engine[i]->set_volume(fgGetFloat("/sim/sounds/engine/volume", 1.0));
|
|
||||||
_engine[i]->set_pitch(fgGetFloat("/sim/sounds/engine/pitch", 1.0));
|
|
||||||
mgr->add(_engine[i], engine_names[i]);
|
|
||||||
|
|
||||||
// Starter
|
path.append(node->getStringValue("path"));
|
||||||
_crank[i] = new FGSimpleSound(fgGetString("/sim/sounds/cranking/path",
|
SG_LOG(SG_GENERAL, SG_INFO, "Reading Instrument " << node->getName()
|
||||||
"Sounds/cranking.wav"));
|
<< " from " << path.str());
|
||||||
_crank[i]->set_volume(fgGetFloat("/sim/sounds/cranking/volume", 0.5));
|
|
||||||
_crank[i]->set_pitch(fgGetFloat("/sim/sounds/cranking/pitch", 0.80));
|
|
||||||
mgr->add(_crank[i], crank_names[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
SGPropertyNode root;
|
||||||
|
try {
|
||||||
|
readProperties(path.str(), &root);
|
||||||
|
} catch (const sg_exception &e) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_ALERT,
|
||||||
|
"Incorrect path specified in configuration file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
node = root.getNode("fx");
|
||||||
// Create and add the wind noise.
|
for (i = 0; i < node->nChildren(); i++) {
|
||||||
//
|
FGSound * sound;
|
||||||
_wind = new FGSimpleSound(fgGetString("/sim/sounds/wind/path",
|
sound = new FGSound(node->getChild(i));
|
||||||
"Sounds/wind.wav"));
|
_sound.push_back(sound);
|
||||||
_wind->set_volume(fgGetFloat("/sim/sounds/wind/volume", 1.0));
|
}
|
||||||
_wind->set_pitch(fgGetFloat("/sim/sounds/wind/pitch", 1.0));
|
|
||||||
mgr->add(_wind, "wind");
|
|
||||||
|
|
||||||
|
for (i = 0; i < _sound.size(); i++ ) {
|
||||||
//
|
_sound[i]->init();
|
||||||
// Create and add the stall noise.
|
}
|
||||||
//
|
|
||||||
_stall = new FGSimpleSound(fgGetString("/sim/sounds/stall/path",
|
|
||||||
"Sounds/stall.wav"));
|
|
||||||
_stall->set_volume(fgGetFloat("/sim/sounds/stall/volume", 1.0));
|
|
||||||
_stall->set_pitch(fgGetFloat("/sim/sounds/stall/pitch", 1.0));
|
|
||||||
mgr->add(_stall, "stall");
|
|
||||||
|
|
||||||
//
|
|
||||||
// Create and add the rumble noise.
|
|
||||||
//
|
|
||||||
_rumble = new FGSimpleSound(fgGetString("/sim/sounds/rumble/path",
|
|
||||||
"Sounds/rumble.wav"));
|
|
||||||
_rumble->set_volume(fgGetFloat("/sim/sounds/rumble/volume", 1.0));
|
|
||||||
_rumble->set_pitch(fgGetFloat("/sim/sounds/rumble/pitch", 1.0));
|
|
||||||
mgr->add(_rumble, "rumble");
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Create and add the flaps noise
|
|
||||||
//
|
|
||||||
_flaps = new FGSimpleSound(fgGetString("/sim/sounds/flaps/path",
|
|
||||||
"Sounds/flaps.wav"));
|
|
||||||
_flaps->set_volume(fgGetFloat("/sim/sounds/flaps/volume", 0.25));
|
|
||||||
_flaps->set_pitch(fgGetFloat("/sim/sounds/flaps/pitch", 1.0));
|
|
||||||
mgr->add(_flaps, "flaps");
|
|
||||||
|
|
||||||
//
|
|
||||||
// Create and add the gear noises.
|
|
||||||
//
|
|
||||||
_gear_up = new FGSimpleSound(fgGetString("/sim/sounds/gear-up/path",
|
|
||||||
"Sounds/gear-up.wav"));
|
|
||||||
_gear_dn = new FGSimpleSound(fgGetString("/sim/sounds/gear-down/path",
|
|
||||||
"Sounds/gear-dn.wav"));
|
|
||||||
_gear_up->set_volume(fgGetFloat("/sim/sounds/gear-up/volume", 1.0));
|
|
||||||
_gear_dn->set_volume(fgGetFloat("/sim/sounds/gear-down/volume", 1.0));
|
|
||||||
_gear_up->set_pitch(fgGetFloat("/sim/sounds/gear-up/pitch", 1.0));
|
|
||||||
_gear_dn->set_pitch(fgGetFloat("/sim/sounds/gear-down/pitch", 1.0));
|
|
||||||
mgr->add(_gear_up, "gear-up");
|
|
||||||
mgr->add(_gear_dn, "gear-down");
|
|
||||||
|
|
||||||
//
|
|
||||||
// Create and add the squeal noise.
|
|
||||||
//
|
|
||||||
_squeal = new FGSimpleSound(fgGetString("/sim/sounds/squeal/path",
|
|
||||||
"Sounds/squeal.wav"));
|
|
||||||
_squeal->set_volume(fgGetFloat("/sim/sounds/squeal/volume", 1.0));
|
|
||||||
_squeal->set_pitch(fgGetFloat("/sim/sounds/squeal/pitch", 1.0));
|
|
||||||
mgr->add(_squeal, "squeal");
|
|
||||||
|
|
||||||
//
|
|
||||||
// Simplistic wheel spin model for audio effect purposes only. We
|
|
||||||
// don't want to play a full squeel, if the wheel has only departed
|
|
||||||
// from the ground for a split second.
|
|
||||||
//
|
|
||||||
for (i = 0; i < MAX_GEAR; i++) {
|
|
||||||
_wheel_spin[i] = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Create and add the click noise.
|
|
||||||
_click = new FGSimpleSound(fgGetString("/sim/sounds/click/path",
|
|
||||||
"Sounds/click.wav"));
|
|
||||||
_flaps->set_volume(fgGetFloat("/sim/sounds/click/volume", 0.5));
|
|
||||||
_flaps->set_pitch(fgGetFloat("/sim/sounds/click/pitch", 1.0));
|
|
||||||
mgr->add(_click, "click");
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Grab some properties.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_ENGINES; i++) {
|
|
||||||
SGPropertyNode * node = fgGetNode("engines/engine", i, true);
|
|
||||||
_engine_running_prop[i] = node->getChild("running", 0, true);
|
|
||||||
_engine_cranking_prop[i] = node->getChild("cranking", 0, true);
|
|
||||||
}
|
|
||||||
_stall_warning_prop = fgGetNode("/sim/aero/alarms/stall-warning", true);
|
|
||||||
_vc_prop = fgGetNode("/velocities/airspeed-kt", true);
|
|
||||||
_flaps_prop = fgGetNode("/controls/flaps", true);
|
|
||||||
_gear_prop = fgGetNode("/controls/gear-down", true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -215,207 +93,8 @@ FGFX::unbind ()
|
||||||
void
|
void
|
||||||
FGFX::update (int dt)
|
FGFX::update (int dt)
|
||||||
{
|
{
|
||||||
FGSoundMgr * mgr = globals->get_soundmgr();
|
for (int i = 0; i < _sound.size(); i++ )
|
||||||
int i;
|
_sound[i]->update(dt);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Update the engine sound.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_ENGINES; i++) {
|
|
||||||
|
|
||||||
SGPropertyNode * node = fgGetNode("engines/engine", i, true);
|
|
||||||
|
|
||||||
if (_engine_running_prop[i]->getBoolValue()) {
|
|
||||||
// pitch corresponds to rpm
|
|
||||||
// volume corresponds to manifold pressure
|
|
||||||
|
|
||||||
double rpm_factor;
|
|
||||||
rpm_factor = node->getDoubleValue("rpm") / 2500.0;
|
|
||||||
|
|
||||||
double pitch = 0.3 + rpm_factor * 3.0;
|
|
||||||
|
|
||||||
// don't run at absurdly slow rates -- not realistic
|
|
||||||
// and sounds bad to boot. :-)
|
|
||||||
if (pitch < 0.7)
|
|
||||||
pitch = 0.7;
|
|
||||||
if (pitch > 5.0)
|
|
||||||
pitch = 5.0;
|
|
||||||
|
|
||||||
double mp_factor;
|
|
||||||
mp_factor = node->getDoubleValue("mp-osi") / 100;
|
|
||||||
|
|
||||||
double volume = 0.15 + mp_factor / 2.0;
|
|
||||||
|
|
||||||
if (volume < 0.15)
|
|
||||||
volume = 0.15;
|
|
||||||
if (volume > 0.5)
|
|
||||||
volume = 0.5;
|
|
||||||
|
|
||||||
_engine[i]->set_pitch( pitch );
|
|
||||||
_engine[i]->set_volume( volume );
|
|
||||||
set_playing(engine_names[i], true);
|
|
||||||
} else {
|
|
||||||
set_playing(engine_names[i], false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
set_playing(crank_names[i], _engine_cranking_prop[i]->getBoolValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Update the wind noise.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// The wind sound is airspeed and altitude
|
|
||||||
// dependent. The wind noise should become
|
|
||||||
// silent at about 65000 feet, and is based
|
|
||||||
// on a logarithmic scale.
|
|
||||||
float rel_wind = cur_fdm_state->get_V_rel_wind(); // FPS
|
|
||||||
float alt_vol = 1-log(cur_fdm_state->get_Altitude()/65000.0)/4.81;
|
|
||||||
if ((rel_wind > 2.0) && (alt_vol > 0.2)) {
|
|
||||||
float volume = rel_wind/937;
|
|
||||||
double pitch = 1.0+(rel_wind/53.0);
|
|
||||||
_wind->set_volume(volume*alt_vol/2);
|
|
||||||
_wind->set_pitch(pitch);
|
|
||||||
set_playing("wind", true);
|
|
||||||
} else {
|
|
||||||
set_playing("wind", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Update the stall horn.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
double stall = _stall_warning_prop->getDoubleValue();
|
|
||||||
double vc = _vc_prop->getDoubleValue();
|
|
||||||
if (stall > 0.0 && vc > 30.0) {
|
|
||||||
_stall->set_volume(stall);
|
|
||||||
set_playing("stall", true);
|
|
||||||
} else {
|
|
||||||
set_playing("stall", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Update the rumble.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
float gearOnGround = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// Calculate whether a squeal is
|
|
||||||
// required, and set the volume.
|
|
||||||
// Currently, the squeal volume is the
|
|
||||||
// current local down velocity in feet
|
|
||||||
// per second divided by 10.0, and
|
|
||||||
// will not be played if under 0.1.
|
|
||||||
|
|
||||||
// FIXME: take rotational velocities
|
|
||||||
// into account as well.
|
|
||||||
for (i = 0; i < MAX_GEAR; i++) {
|
|
||||||
SGPropertyNode * node = fgGetNode("gear/gear", i, true);
|
|
||||||
// cout << "air speed = " << cur_fdm_state->get_V_equiv_kts();
|
|
||||||
// cout << " wheel " << i << " speed = " << _wheel_spin[i];
|
|
||||||
if (node->getBoolValue("wow")) {
|
|
||||||
gearOnGround++;
|
|
||||||
if (!_gear_on_ground[i]) {
|
|
||||||
// wheel just touched down
|
|
||||||
// 3 parts horizontal velocity + 1 part vertical velocity
|
|
||||||
double squeal_volume = 0.75 * cur_fdm_state->get_V_equiv_kts() / 90.0
|
|
||||||
+ 0.25 * cur_fdm_state->get_V_down() / 5.0;
|
|
||||||
|
|
||||||
// scale volume by difference between wheel spin speed and
|
|
||||||
// ground speed
|
|
||||||
double diff = fabs( cur_fdm_state->get_V_equiv_kts()
|
|
||||||
- _wheel_spin[i] );
|
|
||||||
// cout << " speed diff = " << diff;
|
|
||||||
double scale_factor = 0.0;
|
|
||||||
if ( diff > 10 ) {
|
|
||||||
scale_factor = ( diff - 10.0 ) / 30.0f;
|
|
||||||
if ( scale_factor > 1.0 ) { scale_factor = 1.0; }
|
|
||||||
}
|
|
||||||
// cout << " scale_factor = " << scale_factor;
|
|
||||||
squeal_volume *= scale_factor;
|
|
||||||
|
|
||||||
if (squeal_volume > 0.1) {
|
|
||||||
_squeal->set_volume(squeal_volume);
|
|
||||||
_squeal->set_pitch(1.25);
|
|
||||||
mgr->play_once("squeal");
|
|
||||||
}
|
|
||||||
_gear_on_ground[i] = true;
|
|
||||||
}
|
|
||||||
// cout << " wow";
|
|
||||||
_wheel_spin[i] = cur_fdm_state->get_V_equiv_kts();
|
|
||||||
} else {
|
|
||||||
// cout << " airborn";
|
|
||||||
_gear_on_ground[i] = false;
|
|
||||||
/* fix me: wheel spindown is currently frame rate dependent which
|
|
||||||
it shouldn't be */
|
|
||||||
_wheel_spin[i] -= 0.2;
|
|
||||||
if ( _wheel_spin[i] < 0.0 ) { _wheel_spin[i] = 0.0; }
|
|
||||||
}
|
|
||||||
// cout << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, if any of the gear is in
|
|
||||||
// contact with the ground play the
|
|
||||||
// rumble sound. The volume is a
|
|
||||||
// logarthmic scale of the absolute
|
|
||||||
// velocity in knots divided by 12.0.
|
|
||||||
// No rumble will be played if the
|
|
||||||
// velocity is under ~0.25 kt.
|
|
||||||
double speed = cur_fdm_state->get_V_equiv_kts();
|
|
||||||
if (gearOnGround > 0 && speed > 0.5) {
|
|
||||||
double volume = 2.0 * (gearOnGround/MAX_GEAR) * log(speed)/12; //(speed/60.0);
|
|
||||||
_rumble->set_volume(volume);
|
|
||||||
set_playing("rumble", true);
|
|
||||||
} else {
|
|
||||||
set_playing("rumble", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Check for flap movement.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
double flap_position = _flaps_prop->getDoubleValue();
|
|
||||||
if (fabs(flap_position - _old_flap_position) > 0.1) {
|
|
||||||
mgr->play_once("flaps");
|
|
||||||
_old_flap_position = flap_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Check for gear movement.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
double gear_position = _gear_prop->getDoubleValue();
|
|
||||||
if (gear_position != _old_gear_position) {
|
|
||||||
if (gear_position < _old_gear_position) {
|
|
||||||
mgr->play_once("gear-up");
|
|
||||||
} else {
|
|
||||||
mgr->play_once("gear-down");
|
|
||||||
}
|
|
||||||
_old_gear_position = gear_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: click
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
FGFX::set_playing (const char * soundName, bool state)
|
|
||||||
{
|
|
||||||
FGSoundMgr * mgr = globals->get_soundmgr();
|
|
||||||
bool playing = mgr->is_playing(soundName);
|
|
||||||
if (state && !playing)
|
|
||||||
mgr->play_looped(soundName);
|
|
||||||
else if (!state && playing)
|
|
||||||
mgr->stop(soundName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// end of fg_fx.cxx
|
// end of fg_fx.cxx
|
||||||
|
|
|
@ -24,15 +24,9 @@
|
||||||
#ifndef __FGFX_HXX
|
#ifndef __FGFX_HXX
|
||||||
#define __FGFX_HXX 1
|
#define __FGFX_HXX 1
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
#include <Main/fgfs.hxx>
|
|
||||||
#include <Main/globals.hxx>
|
|
||||||
|
|
||||||
#include "soundmgr.hxx"
|
#include "fg_sound.hxx"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,11 +41,6 @@ class FGFX : public FGSubsystem
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum {
|
|
||||||
MAX_ENGINES = 2, // TODO: increase later
|
|
||||||
MAX_GEAR = 20,
|
|
||||||
};
|
|
||||||
|
|
||||||
FGFX ();
|
FGFX ();
|
||||||
virtual ~FGFX ();
|
virtual ~FGFX ();
|
||||||
|
|
||||||
|
@ -62,35 +51,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void set_playing (const char * soundName, bool state = true);
|
vector<FGSound *> _sound;
|
||||||
|
|
||||||
double _old_flap_position;
|
|
||||||
double _old_gear_position;
|
|
||||||
|
|
||||||
bool _gear_on_ground[MAX_GEAR];
|
|
||||||
float _wheel_spin[MAX_GEAR];
|
|
||||||
|
|
||||||
// looped sounds
|
|
||||||
FGSimpleSound * _engine[MAX_ENGINES];
|
|
||||||
FGSimpleSound * _crank[MAX_ENGINES];
|
|
||||||
FGSimpleSound * _wind;
|
|
||||||
FGSimpleSound * _stall;
|
|
||||||
FGSimpleSound * _rumble;
|
|
||||||
|
|
||||||
// one-off sounds
|
|
||||||
FGSimpleSound * _flaps;
|
|
||||||
FGSimpleSound * _gear_up;
|
|
||||||
FGSimpleSound * _gear_dn;
|
|
||||||
FGSimpleSound * _squeal;
|
|
||||||
FGSimpleSound * _click;
|
|
||||||
|
|
||||||
// Cached property nodes.
|
|
||||||
const SGPropertyNode * _engine_running_prop[MAX_ENGINES];
|
|
||||||
const SGPropertyNode * _engine_cranking_prop[MAX_ENGINES];
|
|
||||||
const SGPropertyNode * _stall_warning_prop;
|
|
||||||
const SGPropertyNode * _vc_prop;
|
|
||||||
const SGPropertyNode * _flaps_prop;
|
|
||||||
const SGPropertyNode * _gear_prop;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
354
src/Sound/fg_sound.cxx
Normal file
354
src/Sound/fg_sound.cxx
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
// fg_sound.hxx -- Sound class implementation
|
||||||
|
//
|
||||||
|
// Started by Erik Hofman, February 2002
|
||||||
|
// (Reuses some code from fg_fx.cxx created by David Megginson)
|
||||||
|
//
|
||||||
|
// Copyright (C) 2002 Curtis L. Olson - curt@flightgear.org
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
#include <simgear/compiler.h>
|
||||||
|
|
||||||
|
#ifdef SG_HAVE_STD_INCLUDES
|
||||||
|
# include <cmath>
|
||||||
|
#else
|
||||||
|
# include <math.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
|
||||||
|
#include <Main/fg_props.hxx>
|
||||||
|
|
||||||
|
#include "fg_sound.hxx"
|
||||||
|
|
||||||
|
FGSound::FGSound(const SGPropertyNode * node)
|
||||||
|
: _name(""),
|
||||||
|
_factor(1.0),
|
||||||
|
_active(false),
|
||||||
|
_mode(FGSound::ONCE),
|
||||||
|
_type(FGSound::LEVEL)
|
||||||
|
{
|
||||||
|
_node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGSound::~FGSound()
|
||||||
|
{
|
||||||
|
delete _sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGSound::init()
|
||||||
|
{
|
||||||
|
FGSoundMgr * mgr = globals->get_soundmgr();
|
||||||
|
vector<const SGPropertyNode *> kids;
|
||||||
|
float p = 0.0;
|
||||||
|
float v = 0.0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
_property = fgGetNode(_node->getStringValue("property"), true);
|
||||||
|
|
||||||
|
//
|
||||||
|
// seet sound global properties
|
||||||
|
//
|
||||||
|
_name = _node->getStringValue("name");
|
||||||
|
|
||||||
|
if ((_factor = _node->getFloatValue("factor")) == 0.0)
|
||||||
|
_factor = 1.0;
|
||||||
|
|
||||||
|
SG_LOG(SG_GENERAL, SG_INFO,
|
||||||
|
"Loading sound information for: " << _name );
|
||||||
|
|
||||||
|
if (_node->getStringValue("mode") == "looped") {
|
||||||
|
_mode = FGSound::LOOPED;
|
||||||
|
} else {
|
||||||
|
_mode = FGSound::ONCE;
|
||||||
|
if (_node->getStringValue("mode") != (string)"once")
|
||||||
|
SG_LOG( SG_GENERAL, SG_INFO, "Unknown sound mode, default to 'once'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_node->getStringValue("type") == "flipflop") {
|
||||||
|
_type = FGSound::FLIPFLOP;
|
||||||
|
} else if (_node->getStringValue("type") == "inverted") {
|
||||||
|
_type = FGSound::INVERTED;
|
||||||
|
} else {
|
||||||
|
_type = FGSound::LEVEL;
|
||||||
|
if (_node->getStringValue("type") != (string)"level")
|
||||||
|
SG_LOG( SG_GENERAL, SG_INFO, "Unknown sound type, default to 'level'");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//
|
||||||
|
// set position properties
|
||||||
|
//
|
||||||
|
_pos.dist = _node->getFloatValue("dist");
|
||||||
|
_pos.hor = _node->getFloatValue("pos_hor");
|
||||||
|
_pos.vert = _node->getFloatValue("pos_vert");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// set volume properties
|
||||||
|
//
|
||||||
|
kids = _node->getChildren("volume");
|
||||||
|
for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
|
||||||
|
_snd_prop volume;
|
||||||
|
|
||||||
|
if ((volume.prop=fgGetNode(kids[i]->getStringValue("property"), true))
|
||||||
|
== 0)
|
||||||
|
volume.prop = fgGetNode("/null", true);
|
||||||
|
|
||||||
|
if ((volume.factor = kids[i]->getFloatValue("factor")) == 0.0)
|
||||||
|
volume.factor = 1.0;
|
||||||
|
else
|
||||||
|
if (volume.factor < 0.0) {
|
||||||
|
volume.factor = -volume.factor;
|
||||||
|
volume.subtract = true;
|
||||||
|
} else
|
||||||
|
volume.subtract = false;
|
||||||
|
|
||||||
|
if (kids[i]->getStringValue("type") == "log")
|
||||||
|
volume.type = FGSound::LOG;
|
||||||
|
|
||||||
|
else if (kids[i]->getStringValue("type") == "ln")
|
||||||
|
volume.type = FGSound::LN;
|
||||||
|
|
||||||
|
else {
|
||||||
|
volume.type = FGSound::LIN;
|
||||||
|
if (kids[i]->getStringValue("type") != (string)"lin")
|
||||||
|
SG_LOG( SG_GENERAL, SG_INFO,
|
||||||
|
"Unknown volume type, default to 'lin'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((volume.offset = kids[i]->getFloatValue("offset")) == 0.0)
|
||||||
|
volume.offset = 0.0;
|
||||||
|
|
||||||
|
if ((volume.min = kids[i]->getFloatValue("min")) < 0.0) {
|
||||||
|
SG_LOG( SG_GENERAL, SG_WARN,
|
||||||
|
"Volume minimum value below 0. Forced to 0.");
|
||||||
|
volume.min = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((volume.max = kids[i]->getFloatValue("max")) <= volume.min) {
|
||||||
|
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||||
|
"Volume maximum value below minimum value. Forced above minimum.");
|
||||||
|
volume.max = volume.min + 5.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_volume.push_back(volume);
|
||||||
|
v += volume.offset;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// set pitch properties
|
||||||
|
//
|
||||||
|
kids = _node->getChildren("pitch");
|
||||||
|
|
||||||
|
for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
|
||||||
|
_snd_prop pitch;
|
||||||
|
|
||||||
|
if ((pitch.prop = fgGetNode(kids[i]->getStringValue("property"), true))
|
||||||
|
== 0)
|
||||||
|
pitch.prop = fgGetNode("/null", true);
|
||||||
|
|
||||||
|
if ((pitch.factor = kids[i]->getFloatValue("factor")) == 0.0)
|
||||||
|
pitch.factor = 1.0;
|
||||||
|
|
||||||
|
if (kids[i]->getStringValue("type") == "log")
|
||||||
|
pitch.type = FGSound::LOG;
|
||||||
|
|
||||||
|
else if (kids[i]->getStringValue("type") == "ln")
|
||||||
|
pitch.type = FGSound::LN;
|
||||||
|
|
||||||
|
else {
|
||||||
|
pitch.type = FGSound::LIN;
|
||||||
|
if (kids[i]->getStringValue("type") != (string)"lin")
|
||||||
|
SG_LOG( SG_GENERAL, SG_INFO,
|
||||||
|
"Unknown pitch type, default to 'lin'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pitch.offset = kids[i]->getFloatValue("offset")) == 0.0)
|
||||||
|
pitch.offset = 1.0;
|
||||||
|
|
||||||
|
if ((pitch.min = kids[i]->getFloatValue("min")) < 0.0) {
|
||||||
|
SG_LOG( SG_GENERAL, SG_WARN,
|
||||||
|
"Pitch minimum value below 0. Forced to 0.");
|
||||||
|
pitch.min = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pitch.max = kids[i]->getFloatValue("max")) <= pitch.min) {
|
||||||
|
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||||
|
"Pitch maximum value below minimum value. Forced above minimum.");
|
||||||
|
pitch.max = pitch.min + 5.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pitch.push_back(pitch);
|
||||||
|
p += pitch.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize the sample
|
||||||
|
//
|
||||||
|
_sample = new FGSimpleSound(_node->getStringValue("path"));
|
||||||
|
_sample->set_volume(v);
|
||||||
|
_sample->set_pitch(p);
|
||||||
|
|
||||||
|
if (!mgr->exists(_name))
|
||||||
|
mgr->add(_sample, _name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGSound::bind ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGSound::unbind ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGSound::update (int dt)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
FGSoundMgr * mgr = globals->get_soundmgr();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do we have something to do?
|
||||||
|
//
|
||||||
|
|
||||||
|
// if (!_property)
|
||||||
|
// return;
|
||||||
|
|
||||||
|
i = _property->getFloatValue() * _factor;
|
||||||
|
if (_type == FGSound::INVERTED)
|
||||||
|
i = !i;
|
||||||
|
|
||||||
|
if ((_type == FGSound::LEVEL) || (_type == FGSound::INVERTED)) {
|
||||||
|
if (i == 0) {
|
||||||
|
_active = false;
|
||||||
|
if (mgr->is_playing(_name)) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name);
|
||||||
|
mgr->stop(_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_active && (_mode == FGSound::ONCE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else { // FGSound::FLIPFLOP
|
||||||
|
|
||||||
|
if ((bool)i == _active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check for state changes.
|
||||||
|
// If the state changed, and the sound is still playing: stop playing.
|
||||||
|
//
|
||||||
|
if (mgr->is_playing(_name)) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name);
|
||||||
|
mgr->stop(_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update the volume
|
||||||
|
//
|
||||||
|
int max = _volume.size();
|
||||||
|
double volume = 1.0, volume_offset = 0.0;
|
||||||
|
for(i = 0; i < max; i++) {
|
||||||
|
double v = _volume[i].prop->getDoubleValue();
|
||||||
|
|
||||||
|
if (_volume[i].type == FGSound::LOG)
|
||||||
|
v = log10(1+v);
|
||||||
|
else
|
||||||
|
if (_volume[i].type == FGSound::LN)
|
||||||
|
v = log(1+v);
|
||||||
|
|
||||||
|
v *= _volume[i].factor;
|
||||||
|
|
||||||
|
if (v > _volume[i].max)
|
||||||
|
v = _volume[i].max;
|
||||||
|
else
|
||||||
|
if (v < _volume[i].min)
|
||||||
|
v = 0; // v = _volume[i].min;
|
||||||
|
|
||||||
|
if (_volume[i].subtract) // Hack!
|
||||||
|
volume = _volume[i].offset - v;
|
||||||
|
else {
|
||||||
|
volume_offset += _volume[i].offset;
|
||||||
|
volume *= v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update the pitch
|
||||||
|
//
|
||||||
|
max = _pitch.size();
|
||||||
|
double pitch = 1.0, pitch_offset = 0.0;
|
||||||
|
for(i = 0; i < max; i++) {
|
||||||
|
double p = _pitch[i].prop->getDoubleValue();
|
||||||
|
|
||||||
|
if (_pitch[i].type == FGSound::LOG)
|
||||||
|
p = log10(1+p);
|
||||||
|
else
|
||||||
|
if (_pitch[i].type == FGSound::LN)
|
||||||
|
p = log(1+p);
|
||||||
|
|
||||||
|
p *= _pitch[i].factor;
|
||||||
|
|
||||||
|
if (p > _pitch[i].max)
|
||||||
|
p = _pitch[i].max;
|
||||||
|
else
|
||||||
|
if (p < _pitch[i].min)
|
||||||
|
p = _pitch[i].min;
|
||||||
|
|
||||||
|
pitch *= p;
|
||||||
|
pitch_offset += _pitch[i].offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Change sample state
|
||||||
|
//
|
||||||
|
_sample->set_pitch( pitch_offset + pitch );
|
||||||
|
_sample->set_volume( volume_offset + volume );
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do we need to start playing the sample?
|
||||||
|
//
|
||||||
|
if ((!_active) || (_type == FGSound::FLIPFLOP)) {
|
||||||
|
//
|
||||||
|
// This is needed for FGSound::FLIPFLOP and it works for
|
||||||
|
// FGSound::LEVEl. Doing it this way saves an extra 'if'.
|
||||||
|
//
|
||||||
|
_active = !_active;
|
||||||
|
|
||||||
|
if (_mode == FGSound::ONCE)
|
||||||
|
mgr->play_once(_name);
|
||||||
|
else
|
||||||
|
mgr->play_looped(_name);
|
||||||
|
|
||||||
|
SG_LOG(SG_GENERAL, SG_INFO, "Starting audio playback for: " << _name);
|
||||||
|
SG_LOG(SG_GENERAL, SG_BULK,
|
||||||
|
"Playing " << ((_mode == ONCE) ? "once" : "looped"));
|
||||||
|
SG_LOG(SG_GENERAL, SG_BULK, "Initial volume: " << volume_offset);
|
||||||
|
SG_LOG(SG_GENERAL, SG_BULK, "Initial pitch: " << pitch_offset);
|
||||||
|
}
|
||||||
|
}
|
98
src/Sound/fg_sound.hxx
Normal file
98
src/Sound/fg_sound.hxx
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// fg_sound.hxx -- Sound class implementation
|
||||||
|
//
|
||||||
|
// Started by Erik Hofman, February 2002
|
||||||
|
//
|
||||||
|
// Copyright (C) 2002 Erik Hofman - erik@ehofman.com
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
#ifndef __FGSOUND_HXX
|
||||||
|
#define __FGSOUND_HXX 1
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <simgear/compiler.h>
|
||||||
|
#include <Main/fgfs.hxx>
|
||||||
|
#include <Main/globals.hxx>
|
||||||
|
|
||||||
|
#include "soundmgr.hxx"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for handling one sound.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class FGSound : public FGSubsystem
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum { MAXPROP=5 };
|
||||||
|
enum { LIN=0, LOG, LN };
|
||||||
|
enum { ONCE=0, LOOPED };
|
||||||
|
enum { LEVEL=0, INVERTED, FLIPFLOP };
|
||||||
|
|
||||||
|
FGSound(const SGPropertyNode *);
|
||||||
|
virtual ~FGSound();
|
||||||
|
|
||||||
|
virtual void init ();
|
||||||
|
virtual void bind ();
|
||||||
|
virtual void unbind ();
|
||||||
|
virtual void update (int dt);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const SGPropertyNode * _node;
|
||||||
|
|
||||||
|
FGSimpleSound * _sample;
|
||||||
|
const SGPropertyNode * _property;
|
||||||
|
|
||||||
|
bool _active;
|
||||||
|
|
||||||
|
int _mode;
|
||||||
|
int _type;
|
||||||
|
string _name;
|
||||||
|
float _factor;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Sound source (distance, horizontal position in degrees and
|
||||||
|
// vertical position in degrees)
|
||||||
|
struct {
|
||||||
|
float dist;
|
||||||
|
float hor;
|
||||||
|
float vert;
|
||||||
|
} _pos;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Sound properties
|
||||||
|
typedef struct {
|
||||||
|
const SGPropertyNode * prop;
|
||||||
|
float factor;
|
||||||
|
int type;
|
||||||
|
float offset;
|
||||||
|
float min;
|
||||||
|
float max;
|
||||||
|
bool subtract;
|
||||||
|
} _snd_prop;
|
||||||
|
|
||||||
|
vector<_snd_prop> _volume;
|
||||||
|
vector<_snd_prop> _pitch;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Reference in a new issue