1
0
Fork 0

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:
david 2002-02-27 15:13:58 +00:00
parent ad8558a54f
commit d514e8c255
7 changed files with 499 additions and 405 deletions

View file

@ -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
}

View file

@ -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));

View file

@ -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

View file

@ -21,185 +21,63 @@
//
// $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;
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

View file

@ -24,15 +24,9 @@
#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"
/**
@ -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<FGSound *> _sound;
};

354
src/Sound/fg_sound.cxx Normal file
View 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
View 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