diff --git a/src/Main/main.cxx b/src/Main/main.cxx index cd56e6971..ec8c49161 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -1139,7 +1139,8 @@ static void fgMainLoop( void ) { // Run audio scheduler #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_soundmgr()->update(1); // FIXME: use dt } diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 5fbc483fa..5bba73077 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -150,7 +150,7 @@ fgSetDefaults () // Features fgSetBool("/sim/hud/visibility", false); fgSetBool("/sim/panel/visibility", true); - fgSetBool("/sim/sound", true); + fgSetBool("/sim/sound/audible", true); fgSetBool("/sim/hud/antialiased", false); // Flight Model options @@ -587,9 +587,9 @@ parse_option (const string& arg) } else if ( arg == "--enable-panel" ) { fgSetBool("/sim/panel/visibility", true); } else if ( arg == "--disable-sound" ) { - fgSetBool("/sim/sound", false); + fgSetBool("/sim/sound/audible", false); } else if ( arg == "--enable-sound" ) { - fgSetBool("/sim/sound", true); + fgSetBool("/sim/sound/audible", true); } else if ( arg.find( "--airport-id=") == 0 ) { // NB: changed property name!!! fgSetString("/sim/startup/airport-id", arg.substr(13)); diff --git a/src/Sound/Makefile.am b/src/Sound/Makefile.am index 57fad4fd9..ef3619b84 100644 --- a/src/Sound/Makefile.am +++ b/src/Sound/Makefile.am @@ -2,6 +2,7 @@ noinst_LIBRARIES = libSound.a libSound_a_SOURCES = \ beacon.cxx beacon.hxx \ + fg_sound.cxx fg_sound.hxx \ fg_fx.cxx fg_fx.hxx \ morse.cxx morse.hxx \ soundmgr.cxx soundmgr.hxx diff --git a/src/Sound/fg_fx.cxx b/src/Sound/fg_fx.cxx index 1e28523e1..f76bfdd94 100644 --- a/src/Sound/fg_fx.cxx +++ b/src/Sound/fg_fx.cxx @@ -21,185 +21,63 @@ // // $Id$ -#include // FIXME: remove direct dependencies +#include +#include +#include +#include +#ifdef __BORLANDC__ +# define exception c_exception +#endif + #include
#include "fg_fx.hxx" - - -static const char * engine_names[FGFX::MAX_ENGINES] = { - "engine0", - "engine1" -}; - -static const char * crank_names[FGFX::MAX_ENGINES] = { - "crank0", - "crank1" -}; +#include "fg_sound.hxx" 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 () { - // 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 -FGFX::init () +FGFX::init() { - FGSoundMgr * mgr = globals->get_soundmgr(); - int i; + const SGPropertyNode * node = fgGetNode("/sim/sound", true); + int i; - // - // Create and add engine-related sounds. - // - for (i = 0; i < MAX_ENGINES; i++) { - // 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]); + SGPath path( globals->get_fg_root() ); + if (node->getStringValue("path") == "") { + SG_LOG(SG_GENERAL, SG_ALERT, "Incorect path in configuration file."); + return; + } - // Starter - _crank[i] = new FGSimpleSound(fgGetString("/sim/sounds/cranking/path", - "Sounds/cranking.wav")); - _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]); - } + path.append(node->getStringValue("path")); + SG_LOG(SG_GENERAL, SG_INFO, "Reading Instrument " << node->getName() + << " from " << path.str()); + 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; + } - // - // Create and add the wind noise. - // - _wind = new FGSimpleSound(fgGetString("/sim/sounds/wind/path", - "Sounds/wind.wav")); - _wind->set_volume(fgGetFloat("/sim/sounds/wind/volume", 1.0)); - _wind->set_pitch(fgGetFloat("/sim/sounds/wind/pitch", 1.0)); - mgr->add(_wind, "wind"); + node = root.getNode("fx"); + for (i = 0; i < node->nChildren(); i++) { + FGSound * sound; + sound = new FGSound(node->getChild(i)); + _sound.push_back(sound); + } - - // - // 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); + for (i = 0; i < _sound.size(); i++ ) { + _sound[i]->init(); + } } void @@ -215,207 +93,8 @@ FGFX::unbind () void FGFX::update (int dt) { - FGSoundMgr * mgr = globals->get_soundmgr(); - int i; - - //////////////////////////////////////////////////////////////////// - // 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); + for (int i = 0; i < _sound.size(); i++ ) + _sound[i]->update(dt); } // end of fg_fx.cxx diff --git a/src/Sound/fg_fx.hxx b/src/Sound/fg_fx.hxx index c119469cb..6e59abb67 100644 --- a/src/Sound/fg_fx.hxx +++ b/src/Sound/fg_fx.hxx @@ -24,15 +24,9 @@ #ifndef __FGFX_HXX #define __FGFX_HXX 1 -#ifdef HAVE_CONFIG_H -# include -#endif - #include -#include
-#include
-#include "soundmgr.hxx" +#include "fg_sound.hxx" /** @@ -47,11 +41,6 @@ class FGFX : public FGSubsystem public: - enum { - MAX_ENGINES = 2, // TODO: increase later - MAX_GEAR = 20, - }; - FGFX (); virtual ~FGFX (); @@ -62,35 +51,7 @@ public: private: - void set_playing (const char * soundName, bool state = true); - - 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; + vector _sound; }; diff --git a/src/Sound/fg_sound.cxx b/src/Sound/fg_sound.cxx new file mode 100644 index 000000000..c8bb87eed --- /dev/null +++ b/src/Sound/fg_sound.cxx @@ -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 + +#ifdef SG_HAVE_STD_INCLUDES +# include +#else +# include +#endif + +#include + +#include
+ +#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 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); + } +} diff --git a/src/Sound/fg_sound.hxx b/src/Sound/fg_sound.hxx new file mode 100644 index 000000000..2ceb2ae80 --- /dev/null +++ b/src/Sound/fg_sound.hxx @@ -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 +#endif + +#include +#include
+#include
+ +#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