1
0
Fork 0

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:
curt 2002-05-09 04:24:39 +00:00
parent 93e0274eb8
commit 1c513868b7
6 changed files with 175 additions and 269 deletions

View file

@ -1,5 +1,5 @@
Users Guide to FlightGear sound configuration 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> Author: Erik Hofman <erik@ehofman.com>
This document is an attempt to describe the configuration of 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> <name>engine</name>
<path>Sounds/wasp.wav</path> <path>Sounds/wasp.wav</path>
<mode>looped</mode> <mode>looped</mode>
<property>/engines/engine/running</property> <condition>
<property>/engines/engine/running</property>
</condition>
<volume> <volume>
<property>/engines/engine/mp-osi</property> <property>/engines/engine/mp-osi</property>
<factor>0.005</factor> <factor>0.005</factor>
@ -81,7 +83,7 @@ Configuration description:
< ... > < ... >
This is the event seperator. The text inside the brackets This is the event seperator. The text inside the brackets
can be anything. Bit it is adviced to give it a meaningfull name can be anything. Bit it is adviced to give it a meaningfull name
like: crank, engine, rumble, gear, squeal, flap, wind or stall like: crank, engine, rumble, gear, squeal, flap, wind or stall
The value can be defined multiple times, thus anything which is The value can be defined multiple times, thus anything which is
related may have the same name (grouping them together). related may have the same name (grouping them together).
@ -89,7 +91,7 @@ Configuration description:
<name> <name>
This defines the name of the event. This name is used internally This defines the name of the event. This name is used internally
and, although it can me defined multiple times in the same file, and, although it can me defined multiple times in the same file,
should normally have an unique value. should normally have an unique value.
Multiple definitions of the same name will allow multiple sections Multiple definitions of the same name will allow multiple sections
to interfere in the starting and stopping of the sample. to interfere in the starting and stopping of the sample.
@ -104,7 +106,7 @@ Configuration description:
Using the type "fall" will stop playback when the event turns true. Using the type "fall" will stop playback when the event turns true.
IMPORTANT: IMPORTANT:
If the trigger is used for anything else but stopping the sound If the trigger is used for anything else but stopping the sound
at a certain event, all sections with the same name *should* have at a certain event, all sections with the same name *should* have
exactly the same sections for everything but property and type. exactly the same sections for everything but property and type.
@ -115,42 +117,20 @@ Configuration description:
This defined th path to the sound file. The path is relative to the This defined th path to the sound file. The path is relative to the
FlightGear root directory but could be specified absolute. 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> <property>
Define which property triggers the event, and reffers to a node 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 A more sophisticated mechanism to trigger the event is described
is considered to be 0) and handled if it were a boolean value in <condition>
(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.
<mode> <mode>
This defines how the sample should be played: This defines how the sample should be played:
@ -161,6 +141,8 @@ Configuration description:
looped: the sample plays continuesly, looped: the sample plays continuesly,
until the event turns false. until the event turns false.
in-transit: the sample plays continuesly,
while the property is changing its value.
<volume> / <pitch> <volume> / <pitch>
Volume or Pitch definition. Currently there may be up to 5 Volume or Pitch definition. Currently there may be up to 5
@ -183,10 +165,12 @@ Configuration description:
this is the default. this is the default.
ln: convert the property value to a natural logarithmic 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 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). inv: inverse lineair handling (1/x).
@ -211,7 +195,6 @@ Configuration description:
will be truncated to this value. will be truncated to this value.
<max> <max>
Maximum allowed value. Maximum allowed value.
This is usefull if sounds gets to loud. Anything higher will be This is usefull if sounds gets to loud. Anything higher will be
truncated to this value. truncated to this value.
@ -227,7 +210,7 @@ Default values are:
type: lin type: lin
factor: 1.0 factor: 1.0
offset: 0.0 for volume, 1.0 for pitch offset: 0.0 for volume, 1.0 for pitch
min: 0.0 (don't check) min: 0.0
max: 0.0 (don't check) max: 0.0 (don't check)
@ -240,12 +223,12 @@ Calculations are made the following way (for both pitch and volume):
for (n = 0; n < max; n++) { for (n = 0; n < max; n++) {
if (factor < 0) if (factor < 0)
{ {
value += offset[n] - abs(factor[n]) * function(property[n]); value += offset[n] - abs(factor[n]) * function(property[n]);
} }
else else
{ {
value += factor[n] * function(property[n]); value += factor[n] * function(property[n]);
offs += offset[n]; offs += offset[n];
} }
} }

View file

@ -52,7 +52,7 @@ FGFX::~FGFX ()
void void
FGFX::init() FGFX::init()
{ {
const SGPropertyNode * node = fgGetNode("/sim/sound", true); SGPropertyNode * node = fgGetNode("/sim/sound", true);
int i; int i;
string path_str = node->getStringValue("path"); string path_str = node->getStringValue("path");
@ -77,13 +77,10 @@ FGFX::init()
node = root.getNode("fx"); node = root.getNode("fx");
for (i = 0; i < node->nChildren(); i++) { for (i = 0; i < node->nChildren(); i++) {
FGSound * sound; FGSound *sound = new FGSound();
sound = new FGSound(node->getChild(i)); sound->init(node->getChild(i));
_sound.push_back(sound);
}
for (i = 0; i < (int)_sound.size(); i++ ) { _sound.push_back(sound);
_sound[i]->init();
} }
} }

View file

@ -41,8 +41,8 @@
static double _fg_inv(double v) { return (v == 0) ? 1e99 : 1/v; }; 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_abs(double v) { return (v >= 0) ? v : -v; };
static double _fg_sqrt(double v) { return (v < 0) ? sqrt(-v) : sqrt(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_log10(double v) { return (v < 1) ? 0 : log10(v); };
static double _fg_log(double v) { return (v < 1) ? 0 : log(v+1); }; 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_sqr(double v) { return pow(v, 2); };
// static double _fg_pow3(double v) { return pow(v, 3); }; // static double _fg_pow3(double v) { return pow(v, 3); };
@ -61,44 +61,40 @@ static const struct {
{"", NULL} {"", NULL}
}; };
FGSound::FGSound(const SGPropertyNode * node) FGSound::FGSound()
: _node(node), : _condition(NULL),
_property(NULL),
_sample(NULL), _sample(NULL),
_active(false),
_mode(FGSound::ONCE), _mode(FGSound::ONCE),
_type(FGSound::LEVEL), _prev_value(0),
_name(""), _name("")
_factor(1.0)
{ {
} }
FGSound::~FGSound() FGSound::~FGSound()
{ {
delete _condition;
delete _sample; delete _sample;
} }
void void
FGSound::init() FGSound::init(SGPropertyNode *node)
{ {
_property = fgGetNode(_node->getStringValue("property"), true);
// //
// set global sound properties // 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 ); SG_LOG(SG_GENERAL, SG_INFO, "Loading sound information for: " << _name );
const char *mode_str = _node->getStringValue("mode"); const char *mode_str = node->getStringValue("mode");
if ( !strcmp(mode_str,"looped") ) { if ( !strcmp(mode_str, "looped") ) {
_mode = FGSound::LOOPED; _mode = FGSound::LOOPED;
} else if ( !strcmp(mode_str, "in-transit") ) {
_mode = FGSound::IN_TRANSIT;
} else { } else {
_mode = FGSound::ONCE; _mode = FGSound::ONCE;
@ -106,41 +102,21 @@ FGSound::init()
SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound mode, default to 'once'"); SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound mode, default to 'once'");
} }
const char *type_str = _node->getStringValue("type"); _property = fgGetNode(node->getStringValue("property"), true);
if ( !strcmp(type_str, "flipflop") ) { SGPropertyNode *condition = node->getChild("condition");
_type = FGSound::FLIPFLOP; if (condition != NULL)
_condition = fgReadCondition(condition);
} else if ( !strcmp(type_str, "inverted") ) { if (!_property && !_condition)
_type = FGSound::INVERTED; SG_LOG(SG_GENERAL, SG_WARN,
" Neither a condition nor a property specified");
} 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
// //
// set volume properties // set volume properties
// //
unsigned int i; unsigned int i;
float v = 0.0; 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++) { for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
_snd_prop volume; _snd_prop volume;
@ -199,7 +175,7 @@ FGSound::init()
// set pitch properties // set pitch properties
// //
float p = 0.0; float p = 0.0;
kids = _node->getChildren("pitch"); kids = node->getChildren("pitch");
for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) { for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
_snd_prop pitch; _snd_prop pitch;
@ -256,9 +232,9 @@ FGSound::init()
// //
// Initialize the sample // Initialize the sample
// //
FGSoundMgr * mgr = globals->get_soundmgr(); _mgr = globals->get_soundmgr();
if ((_sample = mgr->find(_name)) == NULL) if ((_sample = _mgr->find(_name)) == NULL)
_sample = mgr->add(_name, _node->getStringValue("path")); _sample = _mgr->add(_name, node->getStringValue("path"));
_sample->set_volume(v); _sample->set_volume(v);
_sample->set_pitch(p); _sample->set_pitch(p);
@ -277,150 +253,129 @@ FGSound::unbind ()
void void
FGSound::update (int dt) FGSound::update (int dt)
{ {
FGSoundMgr * mgr = globals->get_soundmgr(); double curr_value = 0.0;
// //
// Do we have something to do? // If the state changes to false, stop playing.
// //
if (_property)
curr_value = _property->getDoubleValue();
// if (!_property) if ( // Lisp, anyone?
// return; (_condition && !_condition->test()) ||
(_property &&
(
!curr_value ||
( (_mode == FGSound::IN_TRANSIT) && (curr_value == _prev_value) )
)
)
)
{
if ((_type == FGSound::LEVEL) || (_type == FGSound::INVERTED)) { _active = false;
//
// use an integer to get false when: -1 < check < 1
//
bool check = (int)(_offset + _property->getDoubleValue() * _factor);
if (_type == FGSound::INVERTED)
check = !check;
//
// 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 );
_active = false;
}
return;
}
//
// If the sound is already playing we have nothing to do.
//
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.
//
if (_sample->is_playing()) { if (_sample->is_playing()) {
SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name); SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name);
_sample->stop( mgr->get_scheduler() ); _sample->stop( _mgr->get_scheduler() );
} }
if ( ((_type == FGSound::RAISE) && !check) || return;
((_type == FGSound::FALL) && check) )
return;
} }
{ //
int i, max; // If the mode is ONCE and the sound is still playing,
// we have nothing to do anymore.
//
if (_active && (_mode == FGSound::ONCE))
return;
// //
// Update the volume // Cache current value;
// //
max = _volume.size(); _prev_value = curr_value;
double volume = 1.0;
double volume_offset = 0.0;
for(i = 0; i < max; i++) { //
double v = _volume[i].prop->getDoubleValue(); // Update the volume
//
int i;
int max = _volume.size();
double volume = 1.0;
double volume_offset = 0.0;
if (_volume[i].fn) for(i = 0; i < max; i++) {
v = _volume[i].fn(v); double v = _volume[i].prop->getDoubleValue();
v *= _volume[i].factor; if (_volume[i].fn)
v = _volume[i].fn(v);
if (!_volume[i].max && (v > _volume[i].max)) v *= _volume[i].factor;
v = _volume[i].max;
else if (!_volume[i].min && (v < _volume[i].min)) if (_volume[i].max && (v > _volume[i].max))
v = _volume[i].min; v = _volume[i].max;
if (_volume[i].subtract) // Hack! else if (v < _volume[i].min)
volume = _volume[i].offset - v; v = _volume[i].min;
else {
volume_offset += _volume[i].offset; if (_volume[i].subtract) // Hack!
volume *= v; volume = _volume[i].offset - v;
}
else {
volume_offset += _volume[i].offset;
volume *= v;
} }
//
// Update the pitch
//
max = _pitch.size();
double pitch = 1.0;
double pitch_offset = 0.0;
for(i = 0; i < max; i++) {
double p = _pitch[i].prop->getDoubleValue();
if (_pitch[i].fn)
p = _pitch[i].fn(p);
p *= _pitch[i].factor;
if (!_pitch[i].max && (p > _pitch[i].max))
p = _pitch[i].max;
else if (!_pitch[i].min && (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;
}
}
//
// Change sample state
//
_sample->set_pitch( pitch_offset + pitch );
_sample->set_volume( volume_offset + volume );
} }
//
// Update the pitch
//
max = _pitch.size();
double pitch = 1.0;
double pitch_offset = 0.0;
for(i = 0; i < max; i++) {
double p = _pitch[i].prop->getDoubleValue();
if (_pitch[i].fn)
p = _pitch[i].fn(p);
p *= _pitch[i].factor;
if (_pitch[i].max && (p > _pitch[i].max))
p = _pitch[i].max;
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;
}
}
//
// Change sample state
//
_sample->set_pitch( pitch_offset + pitch );
_sample->set_volume( volume_offset + volume );
// //
// Do we need to start playing the sample? // Do we need to start playing the sample?
// //
if (_active && ((_type == FGSound::LEVEL) || (_type == FGSound::INVERTED))) if (!_active) {
return;
// _active = true;
// This is needed for FGSound::FLIPFLOP and it works for if (_mode == FGSound::ONCE)
// FGSound::LEVEl. Doing it this way saves an extra 'if'. _sample->play(_mgr->get_scheduler(), false);
//
_active = !_active;
if (_mode == FGSound::ONCE) else
_sample->play(mgr->get_scheduler(), false); _sample->play(_mgr->get_scheduler(), true);
else
_sample->play(mgr->get_scheduler(), true);
SG_LOG(SG_GENERAL, SG_INFO, "Starting audio playback for: " << _name); SG_LOG(SG_GENERAL, SG_INFO, "Starting audio playback for: " << _name);
SG_LOG(SG_GENERAL, SG_BULK, SG_LOG(SG_GENERAL, SG_BULK,
"Playing " << ((_mode == ONCE) ? "once" : "looped")); "Playing " << ((_mode == ONCE) ? "once" : "looped"));
}
} }

View file

@ -34,18 +34,18 @@
#include "soundmgr.hxx" #include "soundmgr.hxx"
/** /**
* Class for handling one sound. * Class for handling one sound event.
* *
*/ */
class FGSound : public FGSubsystem class FGSound
{ {
public: public:
FGSound(const SGPropertyNode *); FGSound();
virtual ~FGSound(); virtual ~FGSound();
virtual void init (); virtual void init (SGPropertyNode *);
virtual void bind (); virtual void bind ();
virtual void unbind (); virtual void unbind ();
virtual void update (int dt); virtual void update (int dt);
@ -53,13 +53,13 @@ public:
protected: protected:
enum { MAXPROP=5 }; enum { MAXPROP=5 };
enum { ONCE=0, LOOPED }; enum { ONCE=0, LOOPED, IN_TRANSIT };
enum { LEVEL=0, INVERTED, FLIPFLOP, RAISE, FALL }; enum { LEVEL=0, INVERTED, FLIPFLOP };
// Sound properties // Sound properties
typedef struct { typedef struct {
const SGPropertyNode * prop; SGPropertyNode * prop;
double (*fn)(double); double (*fn)(double);
double factor; double factor;
double offset; double offset;
@ -68,30 +68,18 @@ protected:
bool subtract; bool subtract;
} _snd_prop; } _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: private:
const SGPropertyNode * _node; FGSoundMgr * _mgr;
FGSimpleSound * _sample; FGSimpleSound * _sample;
const SGPropertyNode * _property; FGCondition * _condition;
SGPropertyNode * _property;
double _prev_value;
bool _active; bool _active;
int _mode;
int _type;
string _name; string _name;
double _factor; int _mode;
double _offset;
vector<_snd_prop> _volume; vector<_snd_prop> _volume;
vector<_snd_prop> _pitch; vector<_snd_prop> _pitch;

View file

@ -41,8 +41,7 @@
// constructor // constructor
FGSimpleSound::FGSimpleSound( string file ) FGSimpleSound::FGSimpleSound( string file )
: pitch(1.0), : pitch(1.0),
volume(1.0), volume(1.0)
requests(0)
{ {
SGPath slfile( globals->get_fg_root() ); SGPath slfile( globals->get_fg_root() );
slfile.append( file ); slfile.append( file );
@ -55,8 +54,7 @@ FGSimpleSound::FGSimpleSound( string file )
FGSimpleSound::FGSimpleSound( unsigned char *buffer, int len ) FGSimpleSound::FGSimpleSound( unsigned char *buffer, int len )
: pitch(1.0), : pitch(1.0),
volume(1.0), volume(1.0)
requests(0)
{ {
sample = new slSample ( buffer, len ); sample = new slSample ( buffer, len );
pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT ); pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
@ -74,14 +72,12 @@ FGSimpleSound::~FGSimpleSound() {
void FGSimpleSound::play( slScheduler *sched, bool looped ) { void FGSimpleSound::play( slScheduler *sched, bool looped ) {
requests++;
// make sure sound isn't already playing // make sure sound isn't already playing
if ( sample->getPlayCount() > 0 ) { if ( sample->getPlayCount() > 0 ) {
return; sched->stopSample(sample);
// return;
} }
// sched->stopSample(sample);
if ( looped ) { if ( looped ) {
sched->loopSample(sample); sched->loopSample(sample);
} else { } else {
@ -94,18 +90,6 @@ void FGSimpleSound::play( slScheduler *sched, bool looped ) {
void FGSimpleSound::stop( slScheduler *sched, bool quick ) { void FGSimpleSound::stop( slScheduler *sched, bool quick ) {
if ( quick ) {
requests = 0;
} else {
if ( --requests < 0 ) {
requests = 0;
}
}
if ( requests > 0 ) {
return;
}
sched->stopSample( sample ); sched->stopSample( sample );
} }

View file

@ -57,7 +57,6 @@ private:
slEnvelope *volume_envelope; slEnvelope *volume_envelope;
double pitch; double pitch;
double volume; double volume;
int requests;
public: public: