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:
parent
ad8558a54f
commit
d514e8c255
7 changed files with 499 additions and 405 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
354
src/Sound/fg_sound.cxx
Normal 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
98
src/Sound/fg_sound.hxx
Normal 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
|
Loading…
Add table
Reference in a new issue