// 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
}
// 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
} 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));
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
//
// $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 "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;
-
- //
- // 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]);
-
- // 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]);
- }
-
-
- //
- // 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");
-
-
- //
- // 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);
+ const SGPropertyNode * node = fgGetNode("/sim/sound", true);
+ int i;
+
+ SGPath path( globals->get_fg_root() );
+ if (node->getStringValue("path") == "") {
+ SG_LOG(SG_GENERAL, SG_ALERT, "Incorect path in configuration file.");
+ return;
+ }
+
+ 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;
+ }
+
+ node = root.getNode("fx");
+ for (i = 0; i < node->nChildren(); i++) {
+ FGSound * sound;
+ sound = new FGSound(node->getChild(i));
+ _sound.push_back(sound);
+ }
+
+ for (i = 0; i < _sound.size(); i++ ) {
+ _sound[i]->init();
+ }
}
void
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
#ifndef __FGFX_HXX
#define __FGFX_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"
+#include "fg_sound.hxx"
/**
public:
- enum {
- MAX_ENGINES = 2, // TODO: increase later
- MAX_GEAR = 20,
- };
-
FGFX ();
virtual ~FGFX ();
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<FGSound *> _sound;
};
--- /dev/null
+// 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);
+ }
+}
--- /dev/null
+// 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