Erik Hofman:
I changed the sound code to let it use FGCondition. This changes the code and configuration files rather drastically. Furthermore I've added an in-transit mode which plays the sound only when the tied property is changing. Changes: Code: * Added condition support to trigger an event * Removed the <type> section from the main event definition (this could be done using conditions) * Removed the abillity to use several events with the same name, instead it is required to use conditions. * Updated the README.xmlsound Base package: * Changed the configuration files accordingly. * Changed flaps and gear to use the new in-stransit mode. * Changed the flps.wav file so it can be looped. * Created a new gear.wav file (whcih can be looped) and a gear-lck.wav file for gear locking sound. IMPORTANT: To change existing configuration files to the new ones, it is important to pack events with the same name together into one singel event, using the condition specification. Also, when using special types (inverted, flip-flop, raise or fall) these should be changed to a conditions also. For more information, please look at FLightGear/docs-mini/README.xmlsound and the supplied aircraft configuration files located under FlightGear/Aircraft (espesially c172/c172-sound.xml and c310/c310-sound.xml).
This commit is contained in:
parent
93e0274eb8
commit
1c513868b7
6 changed files with 175 additions and 269 deletions
|
@ -1,5 +1,5 @@
|
|||
Users Guide to FlightGear sound configuration
|
||||
Version 0.7.10, Mar 02 2002
|
||||
Version 0.7.11, apr 27 2002
|
||||
Author: Erik Hofman <erik@ehofman.com>
|
||||
|
||||
This document is an attempt to describe the configuration of
|
||||
|
@ -44,7 +44,9 @@ A limited sound configuration file would look something like this:
|
|||
<name>engine</name>
|
||||
<path>Sounds/wasp.wav</path>
|
||||
<mode>looped</mode>
|
||||
<condition>
|
||||
<property>/engines/engine/running</property>
|
||||
</condition>
|
||||
<volume>
|
||||
<property>/engines/engine/mp-osi</property>
|
||||
<factor>0.005</factor>
|
||||
|
@ -115,42 +117,20 @@ Configuration description:
|
|||
This defined th path to the sound file. The path is relative to the
|
||||
FlightGear root directory but could be specified absolute.
|
||||
|
||||
<condition>
|
||||
Define a condition that triggers the event.
|
||||
For a complete description of the FlightGear conditions,
|
||||
please read docs-mini/README.conditions
|
||||
|
||||
An event should define either a condition or a property.
|
||||
|
||||
<property>
|
||||
Define which property triggers the event, and reffers to a node
|
||||
in the FlightGear property tree.
|
||||
in the FlightGear property tree. Action is taken when the property
|
||||
is non zero.
|
||||
|
||||
The value is converted to an integer value (anything less than 0.5 is
|
||||
is considered to be 0) and handled if it were a boolean value
|
||||
(0 = false, anything else = true).
|
||||
|
||||
The triger depends on the value of <type>.
|
||||
|
||||
<type>
|
||||
This specifies how the event is triggered. When an event is triggered
|
||||
the sample will start playing. Since the effects scheduler can have
|
||||
multiple events controll a single sound event, it depends on the
|
||||
situation if an event actually stops playing the sound.
|
||||
|
||||
Basically the following is true:
|
||||
The first event requesting to start playback, triggers playback.
|
||||
The last event requesting to stop playback, will stop playback.
|
||||
|
||||
There are multiple options:
|
||||
|
||||
level: events are active if the value is true.
|
||||
this is the default behaviour.
|
||||
|
||||
inverted: events are active if the value is false.
|
||||
|
||||
flipflop: events are triggered on state changes.
|
||||
this is only usefull for samples which are played
|
||||
once.
|
||||
|
||||
raise: start playing at the raise of the event.
|
||||
explicitly stop playing when the event turns false.
|
||||
|
||||
fall: start playing at the fall of the event.
|
||||
explicitly stop playing when the event turns true.
|
||||
A more sophisticated mechanism to trigger the event is described
|
||||
in <condition>
|
||||
|
||||
<mode>
|
||||
This defines how the sample should be played:
|
||||
|
@ -161,6 +141,8 @@ Configuration description:
|
|||
looped: the sample plays continuesly,
|
||||
until the event turns false.
|
||||
|
||||
in-transit: the sample plays continuesly,
|
||||
while the property is changing its value.
|
||||
|
||||
<volume> / <pitch>
|
||||
Volume or Pitch definition. Currently there may be up to 5
|
||||
|
@ -183,10 +165,12 @@ Configuration description:
|
|||
this is the default.
|
||||
|
||||
ln: convert the property value to a natural logarithmic
|
||||
value before scaling it.
|
||||
value before scaling it. Anything below 1 will return
|
||||
zero.
|
||||
|
||||
log: convert the property value to a true logarithmic
|
||||
value before scaling it.
|
||||
value before scaling it. Anything below 1 will return
|
||||
zero.
|
||||
|
||||
inv: inverse lineair handling (1/x).
|
||||
|
||||
|
@ -211,7 +195,6 @@ Configuration description:
|
|||
will be truncated to this value.
|
||||
|
||||
<max>
|
||||
|
||||
Maximum allowed value.
|
||||
This is usefull if sounds gets to loud. Anything higher will be
|
||||
truncated to this value.
|
||||
|
@ -227,7 +210,7 @@ Default values are:
|
|||
type: lin
|
||||
factor: 1.0
|
||||
offset: 0.0 for volume, 1.0 for pitch
|
||||
min: 0.0 (don't check)
|
||||
min: 0.0
|
||||
max: 0.0 (don't check)
|
||||
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ FGFX::~FGFX ()
|
|||
void
|
||||
FGFX::init()
|
||||
{
|
||||
const SGPropertyNode * node = fgGetNode("/sim/sound", true);
|
||||
SGPropertyNode * node = fgGetNode("/sim/sound", true);
|
||||
int i;
|
||||
|
||||
string path_str = node->getStringValue("path");
|
||||
|
@ -77,13 +77,10 @@ FGFX::init()
|
|||
|
||||
node = root.getNode("fx");
|
||||
for (i = 0; i < node->nChildren(); i++) {
|
||||
FGSound * sound;
|
||||
sound = new FGSound(node->getChild(i));
|
||||
_sound.push_back(sound);
|
||||
}
|
||||
FGSound *sound = new FGSound();
|
||||
sound->init(node->getChild(i));
|
||||
|
||||
for (i = 0; i < (int)_sound.size(); i++ ) {
|
||||
_sound[i]->init();
|
||||
_sound.push_back(sound);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@
|
|||
static double _fg_inv(double v) { return (v == 0) ? 1e99 : 1/v; };
|
||||
static double _fg_abs(double v) { return (v >= 0) ? v : -v; };
|
||||
static double _fg_sqrt(double v) { return (v < 0) ? sqrt(-v) : sqrt(v); };
|
||||
static double _fg_log10(double v) { return (v < 1) ? 0 : log10(v+1); };
|
||||
static double _fg_log(double v) { return (v < 1) ? 0 : log(v+1); };
|
||||
static double _fg_log10(double v) { return (v < 1) ? 0 : log10(v); };
|
||||
static double _fg_log(double v) { return (v < 1) ? 0 : log(v); };
|
||||
// static double _fg_sqr(double v) { return pow(v, 2); };
|
||||
// static double _fg_pow3(double v) { return pow(v, 3); };
|
||||
|
||||
|
@ -61,44 +61,40 @@ static const struct {
|
|||
{"", NULL}
|
||||
};
|
||||
|
||||
FGSound::FGSound(const SGPropertyNode * node)
|
||||
: _node(node),
|
||||
FGSound::FGSound()
|
||||
: _condition(NULL),
|
||||
_property(NULL),
|
||||
_sample(NULL),
|
||||
_active(false),
|
||||
_mode(FGSound::ONCE),
|
||||
_type(FGSound::LEVEL),
|
||||
_name(""),
|
||||
_factor(1.0)
|
||||
_prev_value(0),
|
||||
_name("")
|
||||
{
|
||||
}
|
||||
|
||||
FGSound::~FGSound()
|
||||
{
|
||||
delete _condition;
|
||||
delete _sample;
|
||||
}
|
||||
|
||||
void
|
||||
FGSound::init()
|
||||
FGSound::init(SGPropertyNode *node)
|
||||
{
|
||||
|
||||
_property = fgGetNode(_node->getStringValue("property"), true);
|
||||
|
||||
//
|
||||
// set global sound properties
|
||||
//
|
||||
_name = _node->getStringValue("name");
|
||||
|
||||
if ((_factor = _node->getDoubleValue("factor")) == 0.0)
|
||||
_factor = 1.0;
|
||||
|
||||
_offset = _node->getDoubleValue("offset");
|
||||
|
||||
_name = node->getStringValue("name");
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Loading sound information for: " << _name );
|
||||
|
||||
const char *mode_str = _node->getStringValue("mode");
|
||||
if ( !strcmp(mode_str,"looped") ) {
|
||||
const char *mode_str = node->getStringValue("mode");
|
||||
if ( !strcmp(mode_str, "looped") ) {
|
||||
_mode = FGSound::LOOPED;
|
||||
|
||||
} else if ( !strcmp(mode_str, "in-transit") ) {
|
||||
_mode = FGSound::IN_TRANSIT;
|
||||
|
||||
} else {
|
||||
_mode = FGSound::ONCE;
|
||||
|
||||
|
@ -106,41 +102,21 @@ FGSound::init()
|
|||
SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound mode, default to 'once'");
|
||||
}
|
||||
|
||||
const char *type_str = _node->getStringValue("type");
|
||||
if ( !strcmp(type_str, "flipflop") ) {
|
||||
_type = FGSound::FLIPFLOP;
|
||||
_property = fgGetNode(node->getStringValue("property"), true);
|
||||
SGPropertyNode *condition = node->getChild("condition");
|
||||
if (condition != NULL)
|
||||
_condition = fgReadCondition(condition);
|
||||
|
||||
} else if ( !strcmp(type_str, "inverted") ) {
|
||||
_type = FGSound::INVERTED;
|
||||
|
||||
} else if ( !strcmp(type_str, "raise") ) {
|
||||
_type = FGSound::RAISE;
|
||||
|
||||
} else if ( !strcmp(type_str, "fall") ) {
|
||||
_type = FGSound::FALL;
|
||||
|
||||
} else {
|
||||
_type = FGSound::LEVEL;
|
||||
|
||||
if ( strcmp(type_str, "") )
|
||||
SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound type, default to 'level'");
|
||||
}
|
||||
|
||||
#if 0
|
||||
//
|
||||
// set position properties
|
||||
//
|
||||
_pos.dist = _node->getDoubleValue("dist");
|
||||
_pos.hor = _node->getDoubleValue("pos_hor");
|
||||
_pos.vert = _node->getDoubleValue("pos_vert");
|
||||
#endif
|
||||
if (!_property && !_condition)
|
||||
SG_LOG(SG_GENERAL, SG_WARN,
|
||||
" Neither a condition nor a property specified");
|
||||
|
||||
//
|
||||
// set volume properties
|
||||
//
|
||||
unsigned int i;
|
||||
float v = 0.0;
|
||||
vector<const SGPropertyNode *> kids = _node->getChildren("volume");
|
||||
vector<SGPropertyNode *> kids = node->getChildren("volume");
|
||||
for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
|
||||
_snd_prop volume;
|
||||
|
||||
|
@ -199,7 +175,7 @@ FGSound::init()
|
|||
// set pitch properties
|
||||
//
|
||||
float p = 0.0;
|
||||
kids = _node->getChildren("pitch");
|
||||
kids = node->getChildren("pitch");
|
||||
for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
|
||||
_snd_prop pitch;
|
||||
|
||||
|
@ -256,9 +232,9 @@ FGSound::init()
|
|||
//
|
||||
// Initialize the sample
|
||||
//
|
||||
FGSoundMgr * mgr = globals->get_soundmgr();
|
||||
if ((_sample = mgr->find(_name)) == NULL)
|
||||
_sample = mgr->add(_name, _node->getStringValue("path"));
|
||||
_mgr = globals->get_soundmgr();
|
||||
if ((_sample = _mgr->find(_name)) == NULL)
|
||||
_sample = _mgr->add(_name, node->getStringValue("path"));
|
||||
|
||||
_sample->set_volume(v);
|
||||
_sample->set_pitch(p);
|
||||
|
@ -277,71 +253,52 @@ FGSound::unbind ()
|
|||
void
|
||||
FGSound::update (int dt)
|
||||
{
|
||||
FGSoundMgr * mgr = globals->get_soundmgr();
|
||||
|
||||
//
|
||||
// Do we have something to do?
|
||||
//
|
||||
|
||||
// if (!_property)
|
||||
// return;
|
||||
|
||||
if ((_type == FGSound::LEVEL) || (_type == FGSound::INVERTED)) {
|
||||
|
||||
//
|
||||
// use an integer to get false when: -1 < check < 1
|
||||
//
|
||||
bool check = (int)(_offset + _property->getDoubleValue() * _factor);
|
||||
if (_type == FGSound::INVERTED)
|
||||
check = !check;
|
||||
double curr_value = 0.0;
|
||||
|
||||
//
|
||||
// If the state changes to false, stop playing.
|
||||
//
|
||||
if (!check) {
|
||||
if (_active) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name);
|
||||
_sample->stop( mgr->get_scheduler(), false );
|
||||
if (_property)
|
||||
curr_value = _property->getDoubleValue();
|
||||
|
||||
if ( // Lisp, anyone?
|
||||
(_condition && !_condition->test()) ||
|
||||
(_property &&
|
||||
(
|
||||
!curr_value ||
|
||||
( (_mode == FGSound::IN_TRANSIT) && (curr_value == _prev_value) )
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
|
||||
_active = false;
|
||||
if (_sample->is_playing()) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name);
|
||||
_sample->stop( _mgr->get_scheduler() );
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// If the sound is already playing we have nothing to do.
|
||||
// If the mode is ONCE and the sound is still playing,
|
||||
// we have nothing to do anymore.
|
||||
//
|
||||
if (_active && (_mode == FGSound::ONCE))
|
||||
return;
|
||||
|
||||
} else { // FLIPFLOP, RAISE, FALL
|
||||
|
||||
bool check = (int)(_offset + _property->getDoubleValue() * _factor);
|
||||
if (check == _active)
|
||||
return;
|
||||
|
||||
//
|
||||
// Check for state changes.
|
||||
// If the state changed, and the sound is still playing: stop playing.
|
||||
// Cache current value;
|
||||
//
|
||||
if (_sample->is_playing()) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name);
|
||||
_sample->stop( mgr->get_scheduler() );
|
||||
}
|
||||
|
||||
if ( ((_type == FGSound::RAISE) && !check) ||
|
||||
((_type == FGSound::FALL) && check) )
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
int i, max;
|
||||
_prev_value = curr_value;
|
||||
|
||||
//
|
||||
// Update the volume
|
||||
//
|
||||
max = _volume.size();
|
||||
int i;
|
||||
int max = _volume.size();
|
||||
double volume = 1.0;
|
||||
double volume_offset = 0.0;
|
||||
|
||||
|
@ -353,14 +310,15 @@ FGSound::update (int dt)
|
|||
|
||||
v *= _volume[i].factor;
|
||||
|
||||
if (!_volume[i].max && (v > _volume[i].max))
|
||||
if (_volume[i].max && (v > _volume[i].max))
|
||||
v = _volume[i].max;
|
||||
|
||||
else if (!_volume[i].min && (v < _volume[i].min))
|
||||
else if (v < _volume[i].min)
|
||||
v = _volume[i].min;
|
||||
|
||||
if (_volume[i].subtract) // Hack!
|
||||
volume = _volume[i].offset - v;
|
||||
|
||||
else {
|
||||
volume_offset += _volume[i].offset;
|
||||
volume *= v;
|
||||
|
@ -382,14 +340,15 @@ FGSound::update (int dt)
|
|||
|
||||
p *= _pitch[i].factor;
|
||||
|
||||
if (!_pitch[i].max && (p > _pitch[i].max))
|
||||
if (_pitch[i].max && (p > _pitch[i].max))
|
||||
p = _pitch[i].max;
|
||||
|
||||
else if (!_pitch[i].min && (p < _pitch[i].min))
|
||||
else if (p < _pitch[i].min)
|
||||
p = _pitch[i].min;
|
||||
|
||||
if (_pitch[i].subtract) // Hack!
|
||||
pitch = _pitch[i].offset - p;
|
||||
|
||||
else {
|
||||
pitch_offset += _pitch[i].offset;
|
||||
pitch *= p;
|
||||
|
@ -401,26 +360,22 @@ FGSound::update (int dt)
|
|||
//
|
||||
_sample->set_pitch( pitch_offset + pitch );
|
||||
_sample->set_volume( volume_offset + volume );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Do we need to start playing the sample?
|
||||
//
|
||||
if (_active && ((_type == FGSound::LEVEL) || (_type == FGSound::INVERTED)))
|
||||
return;
|
||||
|
||||
//
|
||||
// This is needed for FGSound::FLIPFLOP and it works for
|
||||
// FGSound::LEVEl. Doing it this way saves an extra 'if'.
|
||||
//
|
||||
_active = !_active;
|
||||
if (!_active) {
|
||||
|
||||
_active = true;
|
||||
if (_mode == FGSound::ONCE)
|
||||
_sample->play(mgr->get_scheduler(), false);
|
||||
_sample->play(_mgr->get_scheduler(), false);
|
||||
|
||||
else
|
||||
_sample->play(mgr->get_scheduler(), true);
|
||||
_sample->play(_mgr->get_scheduler(), true);
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Starting audio playback for: " << _name);
|
||||
SG_LOG(SG_GENERAL, SG_BULK,
|
||||
"Playing " << ((_mode == ONCE) ? "once" : "looped"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,18 +34,18 @@
|
|||
#include "soundmgr.hxx"
|
||||
|
||||
/**
|
||||
* Class for handling one sound.
|
||||
* Class for handling one sound event.
|
||||
*
|
||||
*/
|
||||
class FGSound : public FGSubsystem
|
||||
class FGSound
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
FGSound(const SGPropertyNode *);
|
||||
FGSound();
|
||||
virtual ~FGSound();
|
||||
|
||||
virtual void init ();
|
||||
virtual void init (SGPropertyNode *);
|
||||
virtual void bind ();
|
||||
virtual void unbind ();
|
||||
virtual void update (int dt);
|
||||
|
@ -53,13 +53,13 @@ public:
|
|||
protected:
|
||||
|
||||
enum { MAXPROP=5 };
|
||||
enum { ONCE=0, LOOPED };
|
||||
enum { LEVEL=0, INVERTED, FLIPFLOP, RAISE, FALL };
|
||||
enum { ONCE=0, LOOPED, IN_TRANSIT };
|
||||
enum { LEVEL=0, INVERTED, FLIPFLOP };
|
||||
|
||||
|
||||
// Sound properties
|
||||
typedef struct {
|
||||
const SGPropertyNode * prop;
|
||||
SGPropertyNode * prop;
|
||||
double (*fn)(double);
|
||||
double factor;
|
||||
double offset;
|
||||
|
@ -68,30 +68,18 @@ protected:
|
|||
bool subtract;
|
||||
} _snd_prop;
|
||||
|
||||
#if 0
|
||||
// Sound source (distance, horizontal position in degrees and
|
||||
// vertical position in degrees)
|
||||
typedef struct {
|
||||
float dist;
|
||||
float hor;
|
||||
float vert;
|
||||
} _pos_prop;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
const SGPropertyNode * _node;
|
||||
|
||||
FGSoundMgr * _mgr;
|
||||
FGSimpleSound * _sample;
|
||||
const SGPropertyNode * _property;
|
||||
FGCondition * _condition;
|
||||
|
||||
SGPropertyNode * _property;
|
||||
double _prev_value;
|
||||
|
||||
bool _active;
|
||||
|
||||
int _mode;
|
||||
int _type;
|
||||
string _name;
|
||||
double _factor;
|
||||
double _offset;
|
||||
int _mode;
|
||||
|
||||
vector<_snd_prop> _volume;
|
||||
vector<_snd_prop> _pitch;
|
||||
|
|
|
@ -41,8 +41,7 @@
|
|||
// constructor
|
||||
FGSimpleSound::FGSimpleSound( string file )
|
||||
: pitch(1.0),
|
||||
volume(1.0),
|
||||
requests(0)
|
||||
volume(1.0)
|
||||
{
|
||||
SGPath slfile( globals->get_fg_root() );
|
||||
slfile.append( file );
|
||||
|
@ -55,8 +54,7 @@ FGSimpleSound::FGSimpleSound( string file )
|
|||
|
||||
FGSimpleSound::FGSimpleSound( unsigned char *buffer, int len )
|
||||
: pitch(1.0),
|
||||
volume(1.0),
|
||||
requests(0)
|
||||
volume(1.0)
|
||||
{
|
||||
sample = new slSample ( buffer, len );
|
||||
pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
|
||||
|
@ -74,14 +72,12 @@ FGSimpleSound::~FGSimpleSound() {
|
|||
|
||||
void FGSimpleSound::play( slScheduler *sched, bool looped ) {
|
||||
|
||||
requests++;
|
||||
|
||||
// make sure sound isn't already playing
|
||||
if ( sample->getPlayCount() > 0 ) {
|
||||
return;
|
||||
sched->stopSample(sample);
|
||||
// return;
|
||||
}
|
||||
|
||||
// sched->stopSample(sample);
|
||||
if ( looped ) {
|
||||
sched->loopSample(sample);
|
||||
} else {
|
||||
|
@ -94,18 +90,6 @@ void FGSimpleSound::play( slScheduler *sched, bool looped ) {
|
|||
|
||||
void FGSimpleSound::stop( slScheduler *sched, bool quick ) {
|
||||
|
||||
if ( quick ) {
|
||||
requests = 0;
|
||||
} else {
|
||||
if ( --requests < 0 ) {
|
||||
requests = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( requests > 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
sched->stopSample( sample );
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,6 @@ private:
|
|||
slEnvelope *volume_envelope;
|
||||
double pitch;
|
||||
double volume;
|
||||
int requests;
|
||||
|
||||
public:
|
||||
|
||||
|
|
Loading…
Reference in a new issue