Merge branch 'ehofman/sound'
This commit is contained in:
commit
b5c46a8d59
46 changed files with 1338 additions and 796 deletions
|
@ -128,6 +128,17 @@ Configuration description:
|
|||
|
||||
in-transit: the sample plays continuously,
|
||||
while the property is changing its value.
|
||||
|
||||
<type>
|
||||
This defines the type os this sample:
|
||||
|
||||
fx: this is the default type and doesn't need to be defined.
|
||||
|
||||
avionics: sounds set to this type don't have a position and
|
||||
orientation but are treated as if it's mounted to
|
||||
the aircraft panel. it's up to the user to define
|
||||
if it can always be heard or only when in cockpit
|
||||
view.
|
||||
|
||||
<volume> / <pitch>
|
||||
Volume or Pitch definition. Currently there may be up to 5
|
||||
|
|
|
@ -841,7 +841,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
// TODO - At hot 'n high airports this may be 500ft AGL though - need to make this a variable.
|
||||
if((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET > 700) {
|
||||
double cc = 0.0;
|
||||
if(tower->GetCrosswindConstraint(cc)) {
|
||||
if(tower && tower->GetCrosswindConstraint(cc)) {
|
||||
if(orthopos.y() > cc) {
|
||||
//cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n';
|
||||
leg = TURN1;
|
||||
|
@ -884,7 +884,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
// turn 1000m out for now, taking other traffic into accout
|
||||
if(fabs(orthopos.x()) > 900) {
|
||||
double dd = 0.0;
|
||||
if(tower->GetDownwindConstraint(dd)) {
|
||||
if(tower && tower->GetDownwindConstraint(dd)) {
|
||||
if(fabs(orthopos.x()) > fabs(dd)) {
|
||||
//cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n';
|
||||
leg = TURN2;
|
||||
|
@ -930,7 +930,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
// For now we're assuming that we aim to follow the same glidepath regardless of wind.
|
||||
double d1;
|
||||
double d2;
|
||||
CalculateSoD((tower->GetBaseConstraint(d1) ? d1 : -1000.0), (tower->GetDownwindConstraint(d2) ? d2 : 1000.0 * patternDirection), (patternDirection ? true : false));
|
||||
CalculateSoD(((tower && tower->GetBaseConstraint(d1)) ? d1 : -1000.0), ((tower && tower->GetDownwindConstraint(d2)) ? d2 : 1000.0 * patternDirection), (patternDirection ? true : false));
|
||||
if(SoD.leg == DOWNWIND) {
|
||||
descending = (orthopos.y() < SoD.y ? true : false);
|
||||
}
|
||||
|
@ -950,7 +950,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
if(orthopos.y() < -1000.0 + turn_radius) {
|
||||
//if(orthopos.y() < -980) {
|
||||
double bb = 0.0;
|
||||
if(tower->GetBaseConstraint(bb)) {
|
||||
if(tower && tower->GetBaseConstraint(bb)) {
|
||||
if(fabs(orthopos.y()) > fabs(bb)) {
|
||||
//cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n';
|
||||
leg = TURN3;
|
||||
|
@ -982,7 +982,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
double d1;
|
||||
// Make downwind leg position artifically large to avoid any chance of SoD being returned as
|
||||
// on downwind when we are already on base.
|
||||
CalculateSoD((tower->GetBaseConstraint(d1) ? d1 : -1000.0), (10000.0 * patternDirection), (patternDirection ? true : false));
|
||||
CalculateSoD(((tower && tower->GetBaseConstraint(d1)) ? d1 : -1000.0), (10000.0 * patternDirection), (patternDirection ? true : false));
|
||||
if(SoD.leg == BASE) {
|
||||
descending = (fabs(orthopos.y()) < fabs(SoD.y) ? true : false);
|
||||
}
|
||||
|
@ -1207,10 +1207,11 @@ void FGAILocalTraffic::CalculateSoD(double base_leg_pos, double downwind_leg_pos
|
|||
|
||||
void FGAILocalTraffic::TransmitPatternPositionReport(void) {
|
||||
// airport name + "traffic" + airplane callsign + pattern direction + pattern leg + rwy + ?
|
||||
string trns = "";
|
||||
string trns;
|
||||
int code = 0;
|
||||
|
||||
trns += tower->get_name();
|
||||
const string& apt_name = tower ? tower->get_name() : airportID;
|
||||
|
||||
trns += apt_name;
|
||||
trns += " Traffic ";
|
||||
trns += plane.callsign;
|
||||
if(patternDirection == 1) {
|
||||
|
@ -1249,10 +1250,10 @@ void FGAILocalTraffic::TransmitPatternPositionReport(void) {
|
|||
}
|
||||
trns += ConvertRwyNumToSpokenString(rwy.rwyID);
|
||||
|
||||
trns += " ";
|
||||
trns += ' ';
|
||||
|
||||
// And add the airport name again
|
||||
trns += tower->get_name();
|
||||
trns += apt_name;
|
||||
|
||||
pending_transmission = trns;
|
||||
ConditionalTransmit(60.0, code); // Assume a report of this leg will be invalid if we can't transmit within a minute.
|
||||
|
|
|
@ -47,6 +47,12 @@ FGAIPlane::FGAIPlane() {
|
|||
_trackSet = false;
|
||||
_tgtRoll = 0.0;
|
||||
_rollSuspended = false;
|
||||
|
||||
if ( !_sgr ) {
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
_sgr = smgr->find("atc", true);
|
||||
_sgr->tie_to_listener();
|
||||
}
|
||||
}
|
||||
|
||||
FGAIPlane::~FGAIPlane() {
|
||||
|
@ -106,7 +112,7 @@ void FGAIPlane::Update(double dt) {
|
|||
// For now assume in range !!!
|
||||
// TODO - implement range checking
|
||||
// TODO - at the moment the volume is always set off comm1
|
||||
double volume = fgGetDouble("/instrumentation/comm[0]/volume");
|
||||
float volume = fgGetFloat("/instrumentation/comm[0]/volume");
|
||||
Render(plane.callsign, volume, false);
|
||||
}
|
||||
}
|
||||
|
@ -167,8 +173,9 @@ void FGAIPlane::ConditionalTransmit(double timeout, int callback_code) {
|
|||
}
|
||||
|
||||
void FGAIPlane::ImmediateTransmit(int callback_code) {
|
||||
// TODO - at the moment the volume is always set off comm1
|
||||
double volume = fgGetDouble("/instrumentation/comm[0]/volume");
|
||||
// TODO - at the moment the volume is always set off comm1
|
||||
float volume = fgGetFloat("/instrumentation/comm[0]/volume");
|
||||
|
||||
Render(plane.callsign, volume, false);
|
||||
if(callback_code) {
|
||||
ProcessCallback(callback_code);
|
||||
|
@ -183,24 +190,18 @@ void FGAIPlane::ProcessCallback(int code) {
|
|||
// Outputs the transmission either on screen or as audio depending on user preference
|
||||
// The refname is a string to identify this sample to the sound manager
|
||||
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
||||
void FGAIPlane::Render(const string& refname, const double volume, bool repeating) {
|
||||
void FGAIPlane::Render(const string& refname, const float volume, bool repeating) {
|
||||
fgSetString("/sim/messages/ai-plane", pending_transmission.c_str());
|
||||
#ifdef ENABLE_AUDIO_SUPPORT
|
||||
voice = (voiceOK && fgGetBool("/sim/sound/voice"));
|
||||
if(voice) {
|
||||
string buf = vPtr->WriteMessage((char*)pending_transmission.c_str(), voice);
|
||||
if(voice) {
|
||||
SGSoundSample* simple =
|
||||
new SGSoundSample((unsigned char*)buf.c_str(), buf.length(), 8000, AL_FORMAT_MONO8 );
|
||||
// TODO - at the moment the volume can't be changed
|
||||
// after the transmission has started.
|
||||
size_t len;
|
||||
void* buf = vPtr->WriteMessage(pending_transmission, &len);
|
||||
if(voice && (volume > 0.05)) {
|
||||
SGSoundSample* simple = new SGSoundSample(&buf, len, 8000 );
|
||||
simple->set_volume(volume);
|
||||
globals->get_soundmgr()->add(simple, refname);
|
||||
if(repeating) {
|
||||
globals->get_soundmgr()->play_looped(refname);
|
||||
} else {
|
||||
globals->get_soundmgr()->play_once(refname);
|
||||
}
|
||||
_sgr->add(simple, refname);
|
||||
_sgr->play(refname, repeating);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_AUDIO_SUPPORT
|
||||
|
@ -221,8 +222,8 @@ void FGAIPlane::NoRender(const string& refname) {
|
|||
if(playing) {
|
||||
if(voice) {
|
||||
#ifdef ENABLE_AUDIO_SUPPORT
|
||||
globals->get_soundmgr()->stop(refname);
|
||||
globals->get_soundmgr()->remove(refname);
|
||||
_sgr->stop(refname);
|
||||
_sgr->remove(refname);
|
||||
#endif
|
||||
}
|
||||
playing = false;
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include "AIEntity.hxx"
|
||||
#include "ATC.hxx"
|
||||
|
||||
class SGSampleGroup;
|
||||
|
||||
enum PatternLeg {
|
||||
TAKEOFF_ROLL,
|
||||
CLIMBOUT,
|
||||
|
@ -140,7 +142,7 @@ private:
|
|||
// Outputs the transmission either on screen or as audio depending on user preference
|
||||
// The refname is a string to identify this sample to the sound manager
|
||||
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
||||
void Render(const string& refname, const double volume, bool repeating);
|
||||
void Render(const string& refname, const float volume, bool repeating);
|
||||
|
||||
// Cease rendering a transmission.
|
||||
// Requires the sound manager refname if audio, else "".
|
||||
|
@ -157,6 +159,8 @@ private:
|
|||
bool _trackSet; // Set true if tgtTrack is to be followed
|
||||
double _tgtRoll;
|
||||
bool _rollSuspended; // Set true when a derived class has suspended AIPlane's roll control
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr;
|
||||
};
|
||||
|
||||
#endif // _FG_AI_PLANE_HXX
|
||||
|
|
|
@ -35,7 +35,9 @@
|
|||
|
||||
|
||||
FGATC::FGATC() :
|
||||
_voiceOK(false),
|
||||
_voiceOK(false),
|
||||
_playing(false),
|
||||
_sgr(NULL),
|
||||
freqClear(true),
|
||||
receiving(false),
|
||||
respond(false),
|
||||
|
@ -55,6 +57,13 @@ FGATC::FGATC() :
|
|||
_counter(0.0),
|
||||
_max_count(5.0)
|
||||
{
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
_sgr = smgr->find("atc", true);
|
||||
|
||||
_volume = fgGetNode("/sim/sound/atc/volume", true);
|
||||
_enabled = fgGetNode("/sim/sound/atc/enabled", true);
|
||||
_atc_external = fgGetNode("/sim/sound/atc/external-view", true);
|
||||
_internal = fgGetNode("/sim/current-view/internal", true);
|
||||
}
|
||||
|
||||
FGATC::~FGATC() {
|
||||
|
@ -103,6 +112,18 @@ void FGATC::Update(double dt) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AUDIO_SUPPORT
|
||||
bool active = _atc_external->getBoolValue() ||
|
||||
_internal->getBoolValue();
|
||||
|
||||
if ( active && _enabled->getBoolValue() ) {
|
||||
_sgr->set_volume( _volume->getFloatValue() );
|
||||
_sgr->resume(); // no-op if already in resumed state
|
||||
} else {
|
||||
_sgr->suspend();
|
||||
}
|
||||
#endif
|
||||
|
||||
if(_transmit) {
|
||||
_counter = 0.0;
|
||||
_max_count = 5.0; // FIXME - hardwired length of message - need to calculate it!
|
||||
|
@ -203,7 +224,7 @@ int FGATC::RemovePlane() {
|
|||
}
|
||||
|
||||
void FGATC::SetData(ATCData* d) {
|
||||
_type = d->type;
|
||||
_type = d->type;
|
||||
_geod = d->geod;
|
||||
_cart = d->cart;
|
||||
range = d->range;
|
||||
|
@ -216,41 +237,36 @@ void FGATC::SetData(ATCData* d) {
|
|||
// Outputs the transmission either on screen or as audio depending on user preference
|
||||
// The refname is a string to identify this sample to the sound manager
|
||||
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
||||
void FGATC::Render(string& msg, const double volume,
|
||||
const string& refname, const bool repeating) {
|
||||
void FGATC::Render(string& msg, const float volume,
|
||||
const string& refname, const bool repeating) {
|
||||
if (volume < 0.05) return;
|
||||
|
||||
if (repeating)
|
||||
fgSetString("/sim/messages/atis", msg.c_str());
|
||||
else
|
||||
fgSetString("/sim/messages/atc", msg.c_str());
|
||||
|
||||
#ifdef ENABLE_AUDIO_SUPPORT
|
||||
#ifdef ENABLE_AUDIO_SUPPORT
|
||||
_voice = (_voiceOK && fgGetBool("/sim/sound/voice"));
|
||||
if(_voice) {
|
||||
string buf = _vPtr->WriteMessage((char*)msg.c_str(), _voice);
|
||||
if(_voice) {
|
||||
NoRender(refname);
|
||||
try {
|
||||
size_t len;
|
||||
void* buf = _vPtr->WriteMessage((char*)msg.c_str(), &len);
|
||||
if(buf) {
|
||||
NoRender(refname);
|
||||
try {
|
||||
// >>> Beware: must pass a (new) object to the (add) method,
|
||||
// >>> because the (remove) method is going to do a (delete)
|
||||
// >>> whether that's what you want or not.
|
||||
SGSoundSample *simple =
|
||||
new SGSoundSample((unsigned char*) buf.c_str(),
|
||||
buf.length(), 8000, AL_FORMAT_MONO8);
|
||||
// TODO - at the moment the volume can't be changed
|
||||
// after the transmission has started.
|
||||
simple->set_volume(volume);
|
||||
globals->get_soundmgr()->add(simple, refname);
|
||||
if(repeating) {
|
||||
globals->get_soundmgr()->play_looped(refname);
|
||||
} else {
|
||||
globals->get_soundmgr()->play_once(refname);
|
||||
}
|
||||
} catch ( sg_io_exception &e ) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, e.getFormattedMessage());
|
||||
SGSoundSample *simple = new SGSoundSample(&buf, len, 8000);
|
||||
simple->set_volume(volume);
|
||||
_sgr->add(simple, refname);
|
||||
_sgr->play(refname, repeating);
|
||||
} catch ( sg_io_exception &e ) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, e.getFormattedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_AUDIO_SUPPORT
|
||||
#endif // ENABLE_AUDIO_SUPPORT
|
||||
if(!_voice) {
|
||||
// first rip the underscores and the pause hints out of the string - these are for the convienience of the voice parser
|
||||
for(unsigned int i = 0; i < msg.length(); ++i) {
|
||||
|
@ -268,8 +284,8 @@ void FGATC::NoRender(const string& refname) {
|
|||
if(_playing) {
|
||||
if(_voice) {
|
||||
#ifdef ENABLE_AUDIO_SUPPORT
|
||||
globals->get_soundmgr()->stop(refname);
|
||||
globals->get_soundmgr()->remove(refname);
|
||||
_sgr->stop(refname);
|
||||
_sgr->remove(refname);
|
||||
#endif
|
||||
}
|
||||
_playing = false;
|
||||
|
@ -283,14 +299,14 @@ string FGATC::GenText(const string& m, int c) {
|
|||
|
||||
ostream& operator << (ostream& os, atc_type atc) {
|
||||
switch(atc) {
|
||||
case(AWOS): return(os << "AWOS");
|
||||
case(ATIS): return(os << "ATIS");
|
||||
case(GROUND): return(os << "GROUND");
|
||||
case(TOWER): return(os << "TOWER");
|
||||
case(AWOS): return(os << "AWOS");
|
||||
case(ATIS): return(os << "ATIS");
|
||||
case(GROUND): return(os << "GROUND");
|
||||
case(TOWER): return(os << "TOWER");
|
||||
case(APPROACH): return(os << "APPROACH");
|
||||
case(DEPARTURE): return(os << "DEPARTURE");
|
||||
case(ENROUTE): return(os << "ENROUTE");
|
||||
case(INVALID): return(os << "INVALID");
|
||||
case(ENROUTE): return(os << "ENROUTE");
|
||||
case(INVALID): return(os << "INVALID");
|
||||
}
|
||||
return(os << "ERROR - Unknown switch in atc_type operator << ");
|
||||
}
|
||||
|
@ -325,8 +341,8 @@ std::istream& operator >> ( std::istream& fin, ATCData& a )
|
|||
return fin >> skipeol;
|
||||
}
|
||||
|
||||
double lat, lon, elev;
|
||||
|
||||
double lat, lon, elev;
|
||||
|
||||
fin >> lat >> lon >> elev >> f >> a.range >> a.ident;
|
||||
a.geod = SGGeod::fromDegM(lon, lat, elev);
|
||||
a.name = "";
|
||||
|
@ -349,7 +365,7 @@ std::istream& operator >> ( std::istream& fin, ATCData& a )
|
|||
// cout << a.ident << endl;
|
||||
|
||||
// generate cartesian coordinates
|
||||
a.cart = SGVec3d::fromGeod(a.geod);
|
||||
a.cart = SGVec3d::fromGeod(a.geod);
|
||||
return fin >> skipeol;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,15 +24,19 @@
|
|||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include "ATCVoice.hxx"
|
||||
|
||||
class SGSampleGroup;
|
||||
|
||||
// Convert a frequency in MHz to tens of kHz
|
||||
// so we can use it e.g. as an index into commlist_freq
|
||||
//
|
||||
|
@ -76,7 +80,7 @@ enum atc_type {
|
|||
APPROACH,
|
||||
DEPARTURE,
|
||||
ENROUTE,
|
||||
INVALID /* must be last element; see ATC_NUM_TYPES */
|
||||
INVALID /* must be last element; see ATC_NUM_TYPES */
|
||||
};
|
||||
|
||||
const int ATC_NUM_TYPES = 1 + INVALID;
|
||||
|
@ -84,8 +88,8 @@ const int ATC_NUM_TYPES = 1 + INVALID;
|
|||
// DCL - new experimental ATC data store
|
||||
struct ATCData {
|
||||
atc_type type;
|
||||
SGGeod geod;
|
||||
SGVec3d cart;
|
||||
SGGeod geod;
|
||||
SGVec3d cart;
|
||||
unsigned short int freq;
|
||||
unsigned short int range;
|
||||
std::string ident;
|
||||
|
@ -114,7 +118,7 @@ public:
|
|||
FGATC();
|
||||
virtual ~FGATC();
|
||||
|
||||
virtual void Init()=0;
|
||||
virtual void Init()=0;
|
||||
|
||||
// Run the internal calculations
|
||||
// Derived classes should call this method from their own Update methods if they
|
||||
|
@ -176,15 +180,15 @@ protected:
|
|||
// Outputs the transmission either on screen or as audio depending on user preference
|
||||
// The refname is a string to identify this sample to the sound manager
|
||||
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
||||
void Render(std::string& msg, const double volume = 1.0,
|
||||
const std::string& refname = "", bool repeating = false);
|
||||
void Render(std::string& msg, const float volume = 1.0,
|
||||
const std::string& refname = "", bool repeating = false);
|
||||
|
||||
// Cease rendering all transmission from this station.
|
||||
// Requires the sound manager refname if audio, else "".
|
||||
void NoRender(const std::string& refname);
|
||||
|
||||
// Transmit a message when channel becomes free of other dialog
|
||||
void Transmit(int callback_code = 0);
|
||||
void Transmit(int callback_code = 0);
|
||||
|
||||
// Transmit a message if channel becomes free within timeout (seconds). timeout of zero implies no limit
|
||||
void ConditionalTransmit(double timeout, int callback_code = 0);
|
||||
|
@ -197,44 +201,46 @@ protected:
|
|||
SGGeod _geod;
|
||||
SGVec3d _cart;
|
||||
int freq;
|
||||
std::map<std::string,int> active_on;
|
||||
std::map<std::string,int> active_on;
|
||||
|
||||
int range;
|
||||
std::string ident; // Code of the airport its at.
|
||||
std::string name; // Name transmitted in the broadcast.
|
||||
std::string ident; // Code of the airport its at.
|
||||
std::string name; // Name transmitted in the broadcast.
|
||||
|
||||
|
||||
// Rendering related stuff
|
||||
bool _voice; // Flag - true if we are using voice
|
||||
bool _playing; // Indicates a message in progress
|
||||
bool _voiceOK; // Flag - true if at least one voice has loaded OK
|
||||
bool _voice; // Flag - true if we are using voice
|
||||
bool _playing; // Indicates a message in progress
|
||||
bool _voiceOK; // Flag - true if at least one voice has loaded OK
|
||||
FGATCVoice* _vPtr;
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr; // default sample group;
|
||||
|
||||
|
||||
bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog
|
||||
bool receiving; // Flag to indicate we are receiving a transmission
|
||||
bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog
|
||||
bool receiving; // Flag to indicate we are receiving a transmission
|
||||
|
||||
|
||||
double responseTime; // Time to take from end of request transmission to beginning of response
|
||||
// The idea is that this will be slightly random.
|
||||
double responseTime; // Time to take from end of request transmission to beginning of response
|
||||
// The idea is that this will be slightly random.
|
||||
|
||||
bool respond; // Flag to indicate now is the time to respond - ie set following the count down of the response timer.
|
||||
bool respond; // Flag to indicate now is the time to respond - ie set following the count down of the response timer.
|
||||
std::string responseID; // ID of the plane to respond to
|
||||
bool runResponseCounter; // Flag to indicate the response counter should be run
|
||||
double responseCounter; // counter to implement the above
|
||||
bool runResponseCounter; // Flag to indicate the response counter should be run
|
||||
double responseCounter; // counter to implement the above
|
||||
// Derived classes only need monitor this flag, and use the response ID, as long as they call FGATC::Update(...)
|
||||
bool _runReleaseCounter; // A timer for releasing the frequency after giving the message enough time to display
|
||||
bool responseReqd; // Flag to indicate we should be responding to a request/report
|
||||
bool responseReqd; // Flag to indicate we should be responding to a request/report
|
||||
double _releaseTime;
|
||||
double _releaseCounter;
|
||||
atc_type _type;
|
||||
bool _display; // Flag to indicate whether we should be outputting to the ATC display.
|
||||
std::string pending_transmission; // derived classes set this string before calling Transmit(...)
|
||||
bool _display; // Flag to indicate whether we should be outputting to the ATC display.
|
||||
std::string pending_transmission; // derived classes set this string before calling Transmit(...)
|
||||
|
||||
private:
|
||||
// Transmission timing stuff.
|
||||
double _timeout;
|
||||
bool _pending;
|
||||
bool _pending;
|
||||
|
||||
int _callback_code; // A callback code to be notified and processed by the derived classes
|
||||
// A value of zero indicates no callback required
|
||||
|
@ -242,6 +248,11 @@ private:
|
|||
bool _transmitting; // we are transmitting
|
||||
double _counter;
|
||||
double _max_count;
|
||||
|
||||
SGPropertyNode_ptr _volume;
|
||||
SGPropertyNode_ptr _enabled;
|
||||
SGPropertyNode_ptr _atc_external;
|
||||
SGPropertyNode_ptr _internal;
|
||||
};
|
||||
|
||||
std::istream& operator>> ( std::istream& fin, ATCData& a );
|
||||
|
|
|
@ -28,25 +28,17 @@
|
|||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/shared_array.hpp>
|
||||
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/math/sg_random.h>
|
||||
#include <simgear/sound/sample_openal.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strtok_r strtok_s
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
FGATCVoice::FGATCVoice() {
|
||||
|
@ -56,29 +48,29 @@ FGATCVoice::FGATCVoice() {
|
|||
|
||||
FGATCVoice::~FGATCVoice() {
|
||||
if (rawSoundData)
|
||||
free( rawSoundData );
|
||||
free( rawSoundData );
|
||||
delete SoundData;
|
||||
}
|
||||
|
||||
// Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce).
|
||||
// Return true if successful.
|
||||
bool FGATCVoice::LoadVoice(const string& voice) {
|
||||
// FIXME CLO: disabled to try to see if this is causing problemcs
|
||||
// return false;
|
||||
|
||||
std::ifstream fin;
|
||||
|
||||
SGPath path = globals->get_fg_root();
|
||||
string file = voice + ".wav";
|
||||
path.append( "ATC" );
|
||||
|
||||
string file = voice + ".wav";
|
||||
path.append( file );
|
||||
|
||||
SGSoundSample SoundData;
|
||||
rawSoundData = (char *)SoundData.load_file(path.c_str(), file.c_str());
|
||||
rawDataSize = SoundData.get_size();
|
||||
string full_path = path.str();
|
||||
int format, freq;
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
void *data;
|
||||
if (!smgr->load(full_path, &data, &format, &rawDataSize, &freq))
|
||||
return false;
|
||||
rawSoundData = (char*)data;
|
||||
#ifdef VOICE_TEST
|
||||
ALenum fmt = SoundData.get_format();
|
||||
cout << "ATCVoice: format: " << fmt
|
||||
cout << "ATCVoice: format: " << format
|
||||
<< " size: " << rawDataSize << endl;
|
||||
#endif
|
||||
path = globals->get_fg_root();
|
||||
|
@ -113,12 +105,12 @@ bool FGATCVoice::LoadVoice(const string& voice) {
|
|||
wd.offset = wrdOffset;
|
||||
wd.length = wrdLength;
|
||||
wordMap[wrdstr] = wd;
|
||||
string ws2 = wrdstr;
|
||||
for(string::iterator p = ws2.begin(); p != ws2.end(); p++){
|
||||
*p = tolower(*p);
|
||||
if (*p == '-') *p = '_';
|
||||
}
|
||||
if (wrdstr != ws2) wordMap[ws2] = wd;
|
||||
string ws2 = wrdstr;
|
||||
for(string::iterator p = ws2.begin(); p != ws2.end(); p++){
|
||||
*p = tolower(*p);
|
||||
if (*p == '-') *p = '_';
|
||||
}
|
||||
if (wrdstr != ws2) wordMap[ws2] = wd;
|
||||
|
||||
//cout << wrd << "\t\t" << wrdOffset << "\t\t" << wrdLength << '\n';
|
||||
//cout << i << '\n';
|
||||
|
@ -129,93 +121,82 @@ bool FGATCVoice::LoadVoice(const string& voice) {
|
|||
}
|
||||
|
||||
|
||||
typedef list < string > tokenList_type;
|
||||
typedef tokenList_type::iterator tokenList_iterator;
|
||||
|
||||
// Given a desired message, return a string containing the
|
||||
// sound-sample data
|
||||
string FGATCVoice::WriteMessage(const char* message, bool& dataOK) {
|
||||
void* FGATCVoice::WriteMessage(const string& message, size_t* len) {
|
||||
|
||||
// What should we do here?
|
||||
// First - parse the message into a list of tokens.
|
||||
// Sort the tokens into those we understand and those we don't.
|
||||
// Add all the raw lengths of the token sound data, allocate enough space, and fill it with the rqd data.
|
||||
tokenList_type tokenList;
|
||||
tokenList_iterator tokenListItr;
|
||||
|
||||
// TODO - at the moment we're effectively taking 3 passes through the data.
|
||||
// There is no need for this - 2 should be sufficient - we can probably ditch the tokenList.
|
||||
size_t n1 = 1+strlen(message);
|
||||
boost::shared_array<char> msg(new char[n1]);
|
||||
strncpy(msg.get(), message, n1); // strtok requires a non-const char*
|
||||
char* token;
|
||||
int numWords = 0;
|
||||
vector<char> sound;
|
||||
const char delimiters[] = " \t.,;:\"\n";
|
||||
char* context;
|
||||
token = strtok_r(msg.get(), delimiters, &context);
|
||||
while(token != NULL) {
|
||||
for (char *t = token; *t; t++) {
|
||||
*t = tolower(*t); // canonicalize the case, to
|
||||
if (*t == '-') *t = '_'; // match what's in the index
|
||||
}
|
||||
tokenList.push_back(token);
|
||||
++numWords;
|
||||
SG_LOG(SG_ATC, SG_DEBUG, "voice synth: token: '"
|
||||
<< token << "'");
|
||||
token = strtok_r(NULL, delimiters, &context);
|
||||
}
|
||||
|
||||
vector<WordData> wdptr;
|
||||
wdptr.reserve(numWords);
|
||||
unsigned int cumLength = 0;
|
||||
|
||||
tokenListItr = tokenList.begin();
|
||||
while(tokenListItr != tokenList.end()) {
|
||||
if(wordMap.find(*tokenListItr) == wordMap.end()) {
|
||||
// Oh dear - the token isn't in the sound file
|
||||
SG_LOG(SG_ATC, SG_ALERT, "voice synth: word '"
|
||||
<< *tokenListItr << "' not found");
|
||||
string::size_type token_start = message.find_first_not_of(delimiters);
|
||||
while(token_start != string::npos) {
|
||||
string::size_type token_end = message.find_first_of(delimiters, token_start);
|
||||
string token;
|
||||
if (token_end == string::npos) {
|
||||
token = message.substr(token_start);
|
||||
token_start = string::npos;
|
||||
} else {
|
||||
wdptr.push_back(wordMap[*tokenListItr]);
|
||||
cumLength += wdptr.back().length;
|
||||
token = message.substr(token_start, token_end - token_start);
|
||||
token_start = message.find_first_not_of(delimiters, token_end);
|
||||
}
|
||||
|
||||
for(string::iterator t = token.begin(); t != token.end(); t++) {
|
||||
// canonicalize the token, to match what's in the index
|
||||
*t = (*t == '-') ? '_' : tolower(*t);
|
||||
}
|
||||
SG_LOG(SG_ATC, SG_DEBUG, "voice synth: token: '"
|
||||
<< token << "'");
|
||||
|
||||
atc_word_map_const_iterator wordIt = wordMap.find(token);
|
||||
if(wordIt == wordMap.end()) {
|
||||
// Oh dear - the token isn't in the sound file
|
||||
SG_LOG(SG_ATC, SG_ALERT, "voice synth: word '"
|
||||
<< token << "' not found");
|
||||
} else {
|
||||
const WordData& word = wordIt->second;
|
||||
/*
|
||||
* Sanity check for corrupt/mismatched sound data input - avoids a seg fault
|
||||
* (As long as the calling function checks the return value!!)
|
||||
* This check should be left in even when the default Flightgear files are known
|
||||
* to be OK since it checks for mis-indexing of voice files by 3rd party developers.
|
||||
*/
|
||||
if((word.offset + word.length) > rawDataSize) {
|
||||
SG_LOG(SG_ATC, SG_ALERT, "ERROR - mismatch between ATC .wav and .vce file in ATCVoice.cxx\n");
|
||||
SG_LOG(SG_ATC, SG_ALERT, "Offset + length: " << word.offset + word.length
|
||||
<< " exceeds rawdata size: " << rawDataSize << endl);
|
||||
|
||||
*len = 0;
|
||||
return 0;
|
||||
}
|
||||
sound.insert(sound.end(), rawSoundData + word.offset, rawSoundData + word.offset + word.length);
|
||||
}
|
||||
++tokenListItr;
|
||||
}
|
||||
const size_t word = wdptr.size();
|
||||
|
||||
|
||||
// Check for no tokens found else slScheduler can be crashed
|
||||
if(!word) {
|
||||
dataOK = false;
|
||||
return "";
|
||||
*len = sound.size();
|
||||
if (*len == 0) {
|
||||
return 0;
|
||||
}
|
||||
boost::shared_array<char> tmpbuf(new char[cumLength]);
|
||||
unsigned int bufpos = 0;
|
||||
for(int i=0; i<word; ++i) {
|
||||
/*
|
||||
* Sanity check for corrupt/mismatched sound data input - avoids a seg fault
|
||||
* (As long as the calling function checks the return value!!)
|
||||
* This check should be left in even when the default Flightgear files are known
|
||||
* to be OK since it checks for mis-indexing of voice files by 3rd party developers.
|
||||
*/
|
||||
if((wdptr[i].offset + wdptr[i].length) > rawDataSize) {
|
||||
SG_LOG(SG_ATC, SG_ALERT, "ERROR - mismatch between ATC .wav and .vce file in ATCVoice.cxx\n");
|
||||
SG_LOG(SG_ATC, SG_ALERT, "Offset + length: " << wdptr[i].offset + wdptr[i].length
|
||||
<< " exceeds rawdata size: " << rawDataSize << endl);
|
||||
|
||||
dataOK = false;
|
||||
return "";
|
||||
}
|
||||
memcpy(tmpbuf.get() + bufpos, rawSoundData + wdptr[i].offset, wdptr[i].length);
|
||||
bufpos += wdptr[i].length;
|
||||
char* data = (char*)malloc(*len);
|
||||
if (data == 0) {
|
||||
SG_LOG(SG_ATC, SG_ALERT, "ERROR - could not allocate " << *len << " bytes of memory for ATIS sound\n");
|
||||
*len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// tmpbuf now contains the message starting at the beginning - but we want it to start at a random position.
|
||||
unsigned int offsetIn = (int)(cumLength * sg_random());
|
||||
if(offsetIn > cumLength) offsetIn = cumLength;
|
||||
|
||||
string front(tmpbuf.get(), offsetIn);
|
||||
string back(tmpbuf.get() + offsetIn, cumLength - offsetIn);
|
||||
// randomize start position
|
||||
unsigned int offsetIn = (unsigned int)(*len * sg_random());
|
||||
if (offsetIn > 0 && offsetIn < *len) {
|
||||
copy(sound.begin() + offsetIn, sound.end(), data);
|
||||
copy(sound.begin(), sound.begin() + offsetIn, data + *len - offsetIn);
|
||||
} else {
|
||||
copy(sound.begin(), sound.end(), data);
|
||||
}
|
||||
|
||||
dataOK = true;
|
||||
return back + front;
|
||||
return data;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define _FG_ATC_VOICE
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
@ -49,15 +50,15 @@ public:
|
|||
bool LoadVoice(const std::string& voice);
|
||||
|
||||
// Given a desired message, return a pointer to the data buffer and write the buffer length into len.
|
||||
// Sets dataOK = true if the returned buffer is valid.
|
||||
std::string WriteMessage(const char* message, bool& dataOK);
|
||||
// Sets len to something other than 0 if the returned buffer is valid.
|
||||
void* WriteMessage(const std::string& message, size_t *len);
|
||||
|
||||
private:
|
||||
|
||||
// the sound and word position data
|
||||
char* rawSoundData;
|
||||
unsigned int rawDataSize;
|
||||
SGSoundSample *SoundData;
|
||||
size_t rawDataSize;
|
||||
SGSharedPtr<SGSoundSample> SoundData;
|
||||
|
||||
// A map of words vs. byte position and length in rawSoundData
|
||||
atc_word_map_type wordMap;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_init.hxx>
|
||||
|
@ -226,12 +227,9 @@ fgLoadAircraft (const SGPropertyNode * arg)
|
|||
t = fgInitTime();
|
||||
globals->set_time_params( t );
|
||||
|
||||
// Reinitialize some subsystems
|
||||
//
|
||||
globals->get_viewmgr()->reinit();
|
||||
globals->get_controls()->reset_all();
|
||||
globals->get_aircraft_model()->reinit();
|
||||
globals->get_subsystem("fx")->reinit();
|
||||
globals->get_subsystem("xml-autopilot")->reinit();
|
||||
|
||||
fgReInitSubsystems();
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
// forward decls
|
||||
class SGPropertyNode;
|
||||
class SGSampleGroup;
|
||||
class FGMetar;
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,9 +68,10 @@ void FGClouds::init(void) {
|
|||
snd_lightning = new SGSoundSample(globals->get_fg_root().c_str(), "Sounds/thunder.wav");
|
||||
snd_lightning->set_max_dist(7000.0f);
|
||||
snd_lightning->set_reference_dist(3000.0f);
|
||||
SGSoundMgr *soundMgr = globals->get_soundmgr();
|
||||
soundMgr->add( snd_lightning, "thunder" );
|
||||
sgEnviro.set_soundMgr( soundMgr );
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
SGSampleGroup *sgr = smgr->find("weather", true);
|
||||
sgr->add( snd_lightning, "thunder" );
|
||||
sgEnviro.set_sampleGroup( sgr );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,10 +34,11 @@
|
|||
|
||||
using std::string;
|
||||
|
||||
class SGNewCloud;
|
||||
class SGSampleGroup;
|
||||
class SGCloudField;
|
||||
class SGNewCloud;
|
||||
class FGMetar;
|
||||
class FGEnvironmentCtrl;
|
||||
//class FGEnvironmentCtrl;
|
||||
|
||||
class FGClouds {
|
||||
|
||||
|
|
|
@ -69,7 +69,8 @@ ADF::ADF (SGPropertyNode *node )
|
|||
_transmitter_range_nm(0),
|
||||
_ident_count(0),
|
||||
_last_ident_time(0),
|
||||
_last_volume(-1)
|
||||
_last_volume(-1),
|
||||
_sgr(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -101,6 +102,10 @@ ADF::init ()
|
|||
_ident_node = node->getChild("ident", 0, true);
|
||||
_ident_audible_node = node->getChild("ident-audible", 0, true);
|
||||
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
_sgr = smgr->find("avionics", true);
|
||||
_sgr->tie_to_listener();
|
||||
|
||||
morse.init();
|
||||
|
||||
std::ostringstream temp;
|
||||
|
@ -156,6 +161,7 @@ ADF::update (double delta_time_sec)
|
|||
double range_nm = adjust_range(_transmitter_pos.getElevationFt(),
|
||||
altitude_m * SG_METER_TO_FEET,
|
||||
_transmitter_range_nm);
|
||||
|
||||
if (distance_nm <= range_nm) {
|
||||
|
||||
double bearing, az2, s;
|
||||
|
@ -171,9 +177,9 @@ ADF::update (double delta_time_sec)
|
|||
set_bearing(delta_time_sec, bearing);
|
||||
|
||||
// adf ident sound
|
||||
double volume;
|
||||
float volume;
|
||||
if ( _ident_audible_node->getBoolValue() )
|
||||
volume = _volume_node->getDoubleValue();
|
||||
volume = _volume_node->getFloatValue();
|
||||
else
|
||||
volume = 0.0;
|
||||
|
||||
|
@ -181,7 +187,7 @@ ADF::update (double delta_time_sec)
|
|||
_last_volume = volume;
|
||||
|
||||
SGSoundSample *sound;
|
||||
sound = globals->get_soundmgr()->find( _adf_ident );
|
||||
sound = _sgr->find( _adf_ident );
|
||||
if ( sound != NULL )
|
||||
sound->set_volume( volume );
|
||||
else
|
||||
|
@ -195,8 +201,8 @@ ADF::update (double delta_time_sec)
|
|||
}
|
||||
|
||||
if ( _ident_count < 4 ) {
|
||||
if ( !globals->get_soundmgr()->is_playing(_adf_ident) ) {
|
||||
globals->get_soundmgr()->play_once( _adf_ident );
|
||||
if ( !_sgr->is_playing(_adf_ident) && (volume > 0.05) ) {
|
||||
_sgr->play_once( _adf_ident );
|
||||
++_ident_count;
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +210,7 @@ ADF::update (double delta_time_sec)
|
|||
_in_range_node->setBoolValue(false);
|
||||
set_bearing(delta_time_sec, 90);
|
||||
_ident_node->setStringValue("");
|
||||
globals->get_soundmgr()->stop( _adf_ident );
|
||||
_sgr->stop( _adf_ident );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,16 +240,16 @@ ADF::search (double frequency_khz, double longitude_rad,
|
|||
_last_ident = ident;
|
||||
_ident_node->setStringValue(ident.c_str());
|
||||
|
||||
if ( globals->get_soundmgr()->exists( _adf_ident ) ) {
|
||||
if ( _sgr->exists( _adf_ident ) ) {
|
||||
// stop is required! -- remove alone wouldn't stop immediately
|
||||
globals->get_soundmgr()->stop( _adf_ident );
|
||||
globals->get_soundmgr()->remove( _adf_ident );
|
||||
_sgr->stop( _adf_ident );
|
||||
_sgr->remove( _adf_ident );
|
||||
}
|
||||
|
||||
SGSoundSample *sound;
|
||||
sound = morse.make_ident( ident, LO_FREQUENCY );
|
||||
sound->set_volume(_last_volume = 0);
|
||||
globals->get_soundmgr()->add( sound, _adf_ident );
|
||||
_sgr->add( sound, _adf_ident );
|
||||
|
||||
int offset = (int)(sg_random() * 30.0);
|
||||
_ident_count = offset / 4;
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
using std::string;
|
||||
|
||||
|
||||
class SGSampleGroup;
|
||||
|
||||
/**
|
||||
* Model an ADF radio.
|
||||
*
|
||||
|
@ -93,8 +95,10 @@ private:
|
|||
FGMorse morse;
|
||||
int _ident_count;
|
||||
time_t _last_ident_time;
|
||||
double _last_volume;
|
||||
float _last_volume;
|
||||
string _adf_ident;
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -105,7 +105,8 @@ FGKR_87::FGKR_87( SGPropertyNode *node ) :
|
|||
flight_timer(0.0),
|
||||
elapsed_timer(0.0),
|
||||
tmp_timer(0.0),
|
||||
_time_before_search_sec(0)
|
||||
_time_before_search_sec(0),
|
||||
_sgr(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -116,6 +117,9 @@ FGKR_87::~FGKR_87() {
|
|||
|
||||
|
||||
void FGKR_87::init () {
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
_sgr = smgr->find("avionics", true);
|
||||
_sgr->tie_to_listener();
|
||||
morse.init();
|
||||
}
|
||||
|
||||
|
@ -463,7 +467,7 @@ void FGKR_87::update( double dt_sec ) {
|
|||
// otherwise turn it off
|
||||
if ( vol_btn >= 0.01 && audio_btn ) {
|
||||
SGSoundSample *sound;
|
||||
sound = globals->get_soundmgr()->find( "adf-ident" );
|
||||
sound = _sgr->find( "adf-ident" );
|
||||
if ( sound != NULL ) {
|
||||
if ( !adf_btn ) {
|
||||
sound->set_volume( vol_btn );
|
||||
|
@ -480,13 +484,13 @@ void FGKR_87::update( double dt_sec ) {
|
|||
}
|
||||
if ( play_count < 4 ) {
|
||||
// play ADF ident
|
||||
if ( !globals->get_soundmgr()->is_playing("adf-ident") ) {
|
||||
globals->get_soundmgr()->play_once( "adf-ident" );
|
||||
if ( !_sgr->is_playing("adf-ident") && (vol_btn > 0.05) ) {
|
||||
_sgr->play_once( "adf-ident" );
|
||||
++play_count;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
globals->get_soundmgr()->stop( "adf-ident" );
|
||||
_sgr->stop( "adf-ident" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -527,13 +531,13 @@ void FGKR_87::search() {
|
|||
effective_range = kludgeRange(stn_elev, pos.getElevationM(), range);
|
||||
xyz = adf->cart();
|
||||
|
||||
if ( globals->get_soundmgr()->exists( "adf-ident" ) ) {
|
||||
globals->get_soundmgr()->remove( "adf-ident" );
|
||||
if ( _sgr->exists( "adf-ident" ) ) {
|
||||
_sgr->remove( "adf-ident" );
|
||||
}
|
||||
SGSoundSample *sound;
|
||||
sound = morse.make_ident( trans_ident, LO_FREQUENCY );
|
||||
sound->set_volume( 0.3 );
|
||||
globals->get_soundmgr()->add( sound, "adf-ident" );
|
||||
_sgr->add( sound, "adf-ident" );
|
||||
|
||||
int offset = (int)(sg_random() * 30.0);
|
||||
play_count = offset / 4;
|
||||
|
@ -551,7 +555,7 @@ void FGKR_87::search() {
|
|||
valid = false;
|
||||
ident = "";
|
||||
trans_ident = "";
|
||||
globals->get_soundmgr()->remove( "adf-ident" );
|
||||
_sgr->remove( "adf-ident" );
|
||||
last_ident = "";
|
||||
// cout << "not picking up adf. :-(" << endl;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <Navaids/navlist.hxx>
|
||||
#include <Sound/morse.hxx>
|
||||
|
||||
class SGSampleGroup;
|
||||
|
||||
class FGKR_87 : public SGSubsystem
|
||||
{
|
||||
|
@ -104,6 +105,8 @@ class FGKR_87 : public SGSubsystem
|
|||
// internal periodic station search timer
|
||||
double _time_before_search_sec;
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr;
|
||||
|
||||
public:
|
||||
|
||||
FGKR_87( SGPropertyNode *node );
|
||||
|
|
|
@ -49,7 +49,8 @@ FGMarkerBeacon::FGMarkerBeacon(SGPropertyNode *node) :
|
|||
inner_blink(false),
|
||||
name("marker-beacon"),
|
||||
num(0),
|
||||
_time_before_search_sec(0.0)
|
||||
_time_before_search_sec(0.0),
|
||||
_sgr(NULL)
|
||||
{
|
||||
SGPath path( globals->get_fg_root() );
|
||||
SGPath term = path;
|
||||
|
@ -99,7 +100,7 @@ FGMarkerBeacon::init ()
|
|||
|
||||
SGPropertyNode *node = fgGetNode(branch.c_str(), num, true );
|
||||
// Inputs
|
||||
sound_pause = fgGetNode("/sim/sound/pause", false);
|
||||
sound_working = fgGetNode("/sim/sound/working", true);
|
||||
lon_node = fgGetNode("/position/longitude-deg", true);
|
||||
lat_node = fgGetNode("/position/latitude-deg", true);
|
||||
alt_node = fgGetNode("/position/altitude-ft", true);
|
||||
|
@ -116,6 +117,10 @@ FGMarkerBeacon::init ()
|
|||
if (serviceable->getType() == simgear::props::NONE)
|
||||
serviceable->setBoolValue( true );
|
||||
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
_sgr = smgr->find("avionics", true);
|
||||
_sgr->tie_to_listener();
|
||||
|
||||
morse.init();
|
||||
beacon.init();
|
||||
blink.stamp();
|
||||
|
@ -171,7 +176,7 @@ FGMarkerBeacon::update(double dt)
|
|||
}
|
||||
|
||||
if ( has_power() && serviceable->getBoolValue()
|
||||
&& !sound_pause->getBoolValue()) {
|
||||
&& sound_working->getBoolValue()) {
|
||||
|
||||
// marker beacon blinking
|
||||
bool light_on = ( outer_blink || middle_blink || inner_blink );
|
||||
|
@ -300,9 +305,9 @@ void FGMarkerBeacon::search()
|
|||
if ( b == NULL || !inrange || !has_power() || !serviceable->getBoolValue() )
|
||||
{
|
||||
// cout << "no marker" << endl;
|
||||
globals->get_soundmgr()->stop( "outer-marker" );
|
||||
globals->get_soundmgr()->stop( "middle-marker" );
|
||||
globals->get_soundmgr()->stop( "inner-marker" );
|
||||
_sgr->stop( "outer-marker" );
|
||||
_sgr->stop( "middle-marker" );
|
||||
_sgr->stop( "inner-marker" );
|
||||
} else {
|
||||
|
||||
string current_sound_name;
|
||||
|
@ -312,63 +317,63 @@ void FGMarkerBeacon::search()
|
|||
current_sound_name = "outer-marker";
|
||||
// cout << "OUTER MARKER" << endl;
|
||||
if ( last_beacon != OUTER ) {
|
||||
if ( ! globals->get_soundmgr()->exists( current_sound_name ) ) {
|
||||
if ( ! _sgr->exists( current_sound_name ) ) {
|
||||
SGSoundSample *sound = beacon.get_outer();
|
||||
if ( sound ) {
|
||||
globals->get_soundmgr()->add( sound, current_sound_name );
|
||||
_sgr->add( sound, current_sound_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( audio_btn->getBoolValue() ) {
|
||||
if ( !globals->get_soundmgr()->is_playing(current_sound_name) ) {
|
||||
globals->get_soundmgr()->play_looped( current_sound_name );
|
||||
if ( !_sgr->is_playing(current_sound_name) ) {
|
||||
_sgr->play_looped( current_sound_name );
|
||||
}
|
||||
} else {
|
||||
globals->get_soundmgr()->stop( current_sound_name );
|
||||
_sgr->stop( current_sound_name );
|
||||
}
|
||||
} else if ( beacon_type == MIDDLE ) {
|
||||
middle_marker = true;
|
||||
current_sound_name = "middle-marker";
|
||||
// cout << "MIDDLE MARKER" << endl;
|
||||
if ( last_beacon != MIDDLE ) {
|
||||
if ( ! globals->get_soundmgr()->exists( current_sound_name ) ) {
|
||||
if ( ! _sgr->exists( current_sound_name ) ) {
|
||||
SGSoundSample *sound = beacon.get_middle();
|
||||
if ( sound ) {
|
||||
globals->get_soundmgr()->add( sound, current_sound_name );
|
||||
_sgr->add( sound, current_sound_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( audio_btn->getBoolValue() ) {
|
||||
if ( !globals->get_soundmgr()->is_playing(current_sound_name) ) {
|
||||
globals->get_soundmgr()->play_looped( current_sound_name );
|
||||
if ( !_sgr->is_playing(current_sound_name) ) {
|
||||
_sgr->play_looped( current_sound_name );
|
||||
}
|
||||
} else {
|
||||
globals->get_soundmgr()->stop( current_sound_name );
|
||||
_sgr->stop( current_sound_name );
|
||||
}
|
||||
} else if ( beacon_type == INNER ) {
|
||||
inner_marker = true;
|
||||
current_sound_name = "inner-marker";
|
||||
// cout << "INNER MARKER" << endl;
|
||||
if ( last_beacon != INNER ) {
|
||||
if ( ! globals->get_soundmgr()->exists( current_sound_name ) ) {
|
||||
if ( ! _sgr->exists( current_sound_name ) ) {
|
||||
SGSoundSample *sound = beacon.get_inner();
|
||||
if ( sound ) {
|
||||
globals->get_soundmgr()->add( sound, current_sound_name );
|
||||
_sgr->add( sound, current_sound_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( audio_btn->getBoolValue() ) {
|
||||
if ( !globals->get_soundmgr()->is_playing(current_sound_name) ) {
|
||||
globals->get_soundmgr()->play_looped( current_sound_name );
|
||||
if ( !_sgr->is_playing(current_sound_name) ) {
|
||||
_sgr->play_looped( current_sound_name );
|
||||
}
|
||||
} else {
|
||||
globals->get_soundmgr()->stop( current_sound_name );
|
||||
_sgr->stop( current_sound_name );
|
||||
}
|
||||
}
|
||||
// cout << "VOLUME " << audio_vol->getDoubleValue() << endl;
|
||||
SGSoundSample * mkr = globals->get_soundmgr()->find( current_sound_name );
|
||||
SGSoundSample * mkr = _sgr->find( current_sound_name );
|
||||
if (mkr)
|
||||
mkr->set_volume( audio_vol->getDoubleValue() );
|
||||
mkr->set_volume( audio_vol->getFloatValue() );
|
||||
}
|
||||
|
||||
if ( inrange ) {
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <Sound/beacon.hxx>
|
||||
#include <Sound/morse.hxx>
|
||||
|
||||
class SGSampleGroup;
|
||||
|
||||
class FGMarkerBeacon : public SGSubsystem
|
||||
{
|
||||
|
@ -54,7 +55,7 @@ class FGMarkerBeacon : public SGSubsystem
|
|||
SGPropertyNode_ptr audio_btn;
|
||||
SGPropertyNode_ptr audio_vol;
|
||||
SGPropertyNode_ptr serviceable;
|
||||
SGPropertyNode_ptr sound_pause;
|
||||
SGPropertyNode_ptr sound_working;
|
||||
|
||||
bool need_update;
|
||||
|
||||
|
@ -73,6 +74,8 @@ class FGMarkerBeacon : public SGSubsystem
|
|||
// internal periodic station search timer
|
||||
double _time_before_search_sec;
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr;
|
||||
|
||||
public:
|
||||
|
||||
enum fgMkrBeacType {
|
||||
|
|
|
@ -2106,34 +2106,17 @@ MK_VIII::VoicePlayer::Speaker::bind (SGPropertyNode *node)
|
|||
// uses xmlsound property names
|
||||
tie(node, "volume", &volume);
|
||||
tie(node, "pitch", &pitch);
|
||||
tie(node, "position/x", &position[0]);
|
||||
tie(node, "position/y", &position[1]);
|
||||
tie(node, "position/z", &position[2]);
|
||||
tie(node, "orientation/x", &orientation[0]);
|
||||
tie(node, "orientation/y", &orientation[1]);
|
||||
tie(node, "orientation/z", &orientation[2]);
|
||||
tie(node, "orientation/inner-cone", &inner_cone);
|
||||
tie(node, "orientation/outer-cone", &outer_cone);
|
||||
tie(node, "reference-dist", &reference_dist);
|
||||
tie(node, "max-dist", &max_dist);
|
||||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::Speaker::update_configuration ()
|
||||
{
|
||||
map<string, SGSoundSample *>::iterator iter;
|
||||
map< string, SGSharedPtr<SGSoundSample> >::iterator iter;
|
||||
for (iter = player->samples.begin(); iter != player->samples.end(); iter++)
|
||||
{
|
||||
SGSoundSample *sample = (*iter).second;
|
||||
|
||||
sample->set_pitch(pitch);
|
||||
sample->set_offset_pos(position);
|
||||
sample->set_orientation(orientation,
|
||||
inner_cone,
|
||||
outer_cone,
|
||||
outer_gain);
|
||||
sample->set_reference_dist(reference_dist);
|
||||
sample->set_max_dist(max_dist);
|
||||
}
|
||||
|
||||
if (player->voice)
|
||||
|
@ -2172,7 +2155,7 @@ MK_VIII::VoicePlayer::Voice::stop (bool now)
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::Voice::set_volume (double _volume)
|
||||
MK_VIII::VoicePlayer::Voice::set_volume (float _volume)
|
||||
{
|
||||
volume = _volume;
|
||||
volume_changed();
|
||||
|
@ -2209,15 +2192,6 @@ MK_VIII::VoicePlayer::~VoicePlayer ()
|
|||
for (iter1 = _voices.begin(); iter1 != _voices.end(); iter1++)
|
||||
delete *iter1;
|
||||
_voices.clear();
|
||||
|
||||
/* sound mgr already destroyed - samples already deleted
|
||||
map<string, SGSoundSample *>::iterator iter2;
|
||||
for (iter2 = samples.begin(); iter2 != samples.end(); iter2++)
|
||||
{
|
||||
bool status = globals->get_soundmgr()->remove((*iter2).first);
|
||||
assert(status);
|
||||
}
|
||||
*/
|
||||
samples.clear();
|
||||
}
|
||||
|
||||
|
@ -2226,6 +2200,10 @@ MK_VIII::VoicePlayer::init ()
|
|||
{
|
||||
#define STDPAUSE 0.75 // [SPEC] 6.4.4: "the standard 0.75 second delay"
|
||||
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
_sgr = smgr->find("avionics", true);
|
||||
_sgr->tie_to_listener();
|
||||
|
||||
make_voice(&voices.application_data_base_failed, "application-data-base-failed");
|
||||
make_voice(&voices.bank_angle, "bank-angle");
|
||||
make_voice(&voices.bank_angle_bank_angle, "bank-angle", "bank-angle");
|
||||
|
@ -2270,13 +2248,7 @@ MK_VIII::VoicePlayer::get_sample (const char *name)
|
|||
std::ostringstream refname;
|
||||
refname << mk->name << "[" << mk->num << "]" << "/" << name;
|
||||
|
||||
SGSoundMgr *soundmgr = globals->get_soundmgr();
|
||||
if (soundmgr->is_working() == false)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SGSoundSample *sample = soundmgr->find(refname.str());
|
||||
SGSoundSample *sample = _sgr->find(refname.str());
|
||||
if (! sample)
|
||||
{
|
||||
SGPath sample_path(globals->get_fg_root());
|
||||
|
@ -2293,7 +2265,7 @@ MK_VIII::VoicePlayer::get_sample (const char *name)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
soundmgr->add(sample, refname.str());
|
||||
_sgr->add(sample, refname.str());
|
||||
samples[refname.str()] = sample;
|
||||
}
|
||||
|
||||
|
@ -2338,7 +2310,7 @@ MK_VIII::VoicePlayer::stop (unsigned int flags)
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::set_volume (double _volume)
|
||||
MK_VIII::VoicePlayer::set_volume (float _volume)
|
||||
{
|
||||
volume = _volume;
|
||||
if (voice)
|
||||
|
@ -4130,7 +4102,7 @@ MK_VIII::Mode6Handler::leave_takeoff ()
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::Mode6Handler::set_volume (double volume)
|
||||
MK_VIII::Mode6Handler::set_volume (float volume)
|
||||
{
|
||||
mk_voice(minimums_minimums)->set_volume(volume);
|
||||
mk_voice(five_hundred_above)->set_volume(volume);
|
||||
|
|
|
@ -36,6 +36,8 @@ using std::vector;
|
|||
using std::deque;
|
||||
using std::map;
|
||||
|
||||
class SGSampleGroup;
|
||||
|
||||
#include <Airports/runways.hxx>
|
||||
#include <Airports/simple.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
@ -733,10 +735,10 @@ public:
|
|||
public:
|
||||
bool silence;
|
||||
|
||||
virtual inline void play (double volume) {}
|
||||
virtual inline void play (float volume) {}
|
||||
virtual inline void stop () {}
|
||||
virtual bool is_playing () = 0;
|
||||
virtual inline void set_volume (double volume) {}
|
||||
virtual inline void set_volume (float volume) {}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
@ -745,17 +747,17 @@ public:
|
|||
|
||||
class SampleElement : public Element
|
||||
{
|
||||
SGSoundSample *_sample;
|
||||
double _volume;
|
||||
SGSharedPtr<SGSoundSample> _sample;
|
||||
float _volume;
|
||||
|
||||
public:
|
||||
inline SampleElement (SGSoundSample *sample, double volume = 1.0)
|
||||
inline SampleElement (SGSharedPtr<SGSoundSample> sample, float volume = 1.0)
|
||||
: _sample(sample), _volume(volume) { silence = false; }
|
||||
|
||||
virtual inline void play (double volume) { if (_sample) { set_volume(volume); _sample->play_once(); } }
|
||||
virtual inline void play (float volume) { if (_sample && (volume > 0.05)) { set_volume(volume); _sample->play_once(); } }
|
||||
virtual inline void stop () { if (_sample) _sample->stop(); }
|
||||
virtual inline bool is_playing () { return _sample ? _sample->is_playing() : false; }
|
||||
virtual inline void set_volume (double volume) { if (_sample) _sample->set_volume(volume * _volume); }
|
||||
virtual inline void set_volume (float volume) { if (_sample) _sample->set_volume(volume * _volume); }
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
@ -771,7 +773,7 @@ public:
|
|||
inline SilenceElement (double duration)
|
||||
: _duration(duration) { silence = true; }
|
||||
|
||||
virtual inline void play (double volume) { start_time = globals->get_sim_time_sec(); }
|
||||
virtual inline void play (float volume) { start_time = globals->get_sim_time_sec(); }
|
||||
virtual inline bool is_playing () { return globals->get_sim_time_sec() - start_time < _duration; }
|
||||
};
|
||||
|
||||
|
@ -790,19 +792,19 @@ public:
|
|||
|
||||
void play ();
|
||||
void stop (bool now);
|
||||
void set_volume (double _volume);
|
||||
void set_volume (float _volume);
|
||||
void volume_changed ();
|
||||
void update ();
|
||||
|
||||
private:
|
||||
VoicePlayer *player;
|
||||
|
||||
double volume;
|
||||
float volume;
|
||||
|
||||
vector<Element *> elements;
|
||||
vector<Element *>::iterator iter;
|
||||
|
||||
inline double get_volume () const { return player->volume * player->speaker.volume * volume; }
|
||||
inline float get_volume () const { return player->volume * player->speaker.volume * volume; }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -811,10 +813,10 @@ public:
|
|||
|
||||
struct
|
||||
{
|
||||
double volume;
|
||||
float volume;
|
||||
} conf;
|
||||
|
||||
double volume;
|
||||
float volume;
|
||||
|
||||
Voice *voice;
|
||||
Voice *next_voice;
|
||||
|
@ -871,7 +873,7 @@ public:
|
|||
};
|
||||
void stop (unsigned int flags = 0);
|
||||
|
||||
void set_volume (double _volume);
|
||||
void set_volume (float _volume);
|
||||
void update ();
|
||||
|
||||
inline void bind (SGPropertyNode *node) { speaker.bind(node); }
|
||||
|
@ -887,13 +889,6 @@ public:
|
|||
VoicePlayer *player;
|
||||
|
||||
double pitch;
|
||||
float position[3];
|
||||
float orientation[3];
|
||||
float inner_cone;
|
||||
float outer_cone;
|
||||
float outer_gain;
|
||||
float reference_dist;
|
||||
float max_dist;
|
||||
|
||||
template <class T>
|
||||
inline void tie (SGPropertyNode *node, const char *name, T *ptr)
|
||||
|
@ -913,20 +908,13 @@ public:
|
|||
template <class T>
|
||||
inline T get_property (T *ptr) const { return *ptr; }
|
||||
|
||||
double volume;
|
||||
float volume;
|
||||
|
||||
inline Speaker (VoicePlayer *_player)
|
||||
: player(_player),
|
||||
pitch(1),
|
||||
inner_cone(360),
|
||||
outer_cone(360),
|
||||
outer_gain(0),
|
||||
reference_dist(3),
|
||||
max_dist(10),
|
||||
volume(1)
|
||||
{
|
||||
position[0] = 0; position[1] = 0; position[2] = 0;
|
||||
orientation[0] = 0; orientation[1] = 0; orientation[2] = 0;
|
||||
}
|
||||
|
||||
void bind (SGPropertyNode *node);
|
||||
|
@ -940,9 +928,10 @@ public:
|
|||
|
||||
MK_VIII *mk;
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr;
|
||||
Speaker speaker;
|
||||
|
||||
map<string, SGSoundSample *> samples;
|
||||
map< string, SGSharedPtr<SGSoundSample> > samples;
|
||||
vector<Voice *> _voices;
|
||||
|
||||
bool looped;
|
||||
|
@ -1465,7 +1454,7 @@ private:
|
|||
void power_off ();
|
||||
void enter_takeoff ();
|
||||
void leave_takeoff ();
|
||||
void set_volume (double volume);
|
||||
void set_volume (float volume);
|
||||
bool altitude_callouts_enabled ();
|
||||
void update ();
|
||||
|
||||
|
|
|
@ -156,7 +156,8 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) :
|
|||
_name(node->getStringValue("name", "nav")),
|
||||
_num(node->getIntValue("number", 0)),
|
||||
_time_before_search_sec(-1.0),
|
||||
_falseCoursesEnabled(true)
|
||||
_falseCoursesEnabled(true),
|
||||
_sgr(NULL)
|
||||
{
|
||||
SGPath path( globals->get_fg_root() );
|
||||
SGPath term = path;
|
||||
|
@ -184,6 +185,10 @@ FGNavRadio::~FGNavRadio()
|
|||
void
|
||||
FGNavRadio::init ()
|
||||
{
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
_sgr = smgr->find("avionics", true);
|
||||
_sgr->tie_to_listener();
|
||||
|
||||
morse.init();
|
||||
|
||||
string branch;
|
||||
|
@ -575,6 +580,8 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig
|
|||
if (!_gs || !inrange_node->getBoolValue()) {
|
||||
gs_dist_node->setDoubleValue( 0.0 );
|
||||
gs_inrange_node->setBoolValue(false);
|
||||
_gsNeedleDeflection = 0.0;
|
||||
_gsNeedleDeflectionNorm = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -584,6 +591,8 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig
|
|||
gs_inrange_node->setBoolValue(gsInRange);
|
||||
|
||||
if (!gsInRange) {
|
||||
_gsNeedleDeflection = 0.0;
|
||||
_gsNeedleDeflectionNorm = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -790,17 +799,17 @@ void FGNavRadio::updateAudio()
|
|||
|
||||
// play station ident via audio system if on + ident,
|
||||
// otherwise turn it off
|
||||
if (!power_btn_node->getBoolValue()
|
||||
if (!power_btn_node->getBoolValue()
|
||||
|| !(bus_power_node->getDoubleValue() > 1.0)
|
||||
|| !ident_btn_node->getBoolValue()
|
||||
|| !audio_btn_node->getBoolValue() ) {
|
||||
globals->get_soundmgr()->stop( nav_fx_name );
|
||||
globals->get_soundmgr()->stop( dme_fx_name );
|
||||
_sgr->stop( nav_fx_name );
|
||||
_sgr->stop( dme_fx_name );
|
||||
return;
|
||||
}
|
||||
|
||||
SGSoundSample *sound = globals->get_soundmgr()->find( nav_fx_name );
|
||||
double vol = vol_btn_node->getDoubleValue();
|
||||
SGSoundSample *sound = _sgr->find( nav_fx_name );
|
||||
double vol = vol_btn_node->getFloatValue();
|
||||
SG_CLAMP_RANGE(vol, 0.0, 1.0);
|
||||
|
||||
if ( sound != NULL ) {
|
||||
|
@ -809,7 +818,7 @@ void FGNavRadio::updateAudio()
|
|||
SG_LOG( SG_COCKPIT, SG_ALERT, "Can't find nav-vor-ident sound" );
|
||||
}
|
||||
|
||||
sound = globals->get_soundmgr()->find( dme_fx_name );
|
||||
sound = _sgr->find( dme_fx_name );
|
||||
if ( sound != NULL ) {
|
||||
sound->set_volume( vol );
|
||||
} else {
|
||||
|
@ -832,16 +841,16 @@ void FGNavRadio::updateAudio()
|
|||
play_count = ++play_count % NUM_IDENT_SLOTS;
|
||||
|
||||
// Previous ident is out of time; if still playing, cut it off:
|
||||
globals->get_soundmgr()->stop( nav_fx_name );
|
||||
globals->get_soundmgr()->stop( dme_fx_name );
|
||||
_sgr->stop( nav_fx_name );
|
||||
_sgr->stop( dme_fx_name );
|
||||
if (play_count == 0) { // the DME slot
|
||||
if (_dmeInRange && dme_serviceable_node->getBoolValue()) {
|
||||
// play DME ident
|
||||
globals->get_soundmgr()->play_once( dme_fx_name );
|
||||
if (vol > 0.05) _sgr->play_once( dme_fx_name );
|
||||
}
|
||||
} else { // NAV slot
|
||||
if (inrange_node->getBoolValue() && nav_serviceable_node->getBoolValue()) {
|
||||
globals->get_soundmgr()->play_once(nav_fx_name);
|
||||
if (vol > 0.05) _sgr->play_once(nav_fx_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -924,8 +933,9 @@ void FGNavRadio::search()
|
|||
_gs = NULL;
|
||||
_dme = NULL;
|
||||
nav_id_node->setStringValue("");
|
||||
globals->get_soundmgr()->remove( nav_fx_name );
|
||||
globals->get_soundmgr()->remove( dme_fx_name );
|
||||
|
||||
_sgr->remove( nav_fx_name );
|
||||
_sgr->remove( dme_fx_name );
|
||||
}
|
||||
|
||||
is_valid_node->setBoolValue(nav != NULL);
|
||||
|
@ -967,25 +977,25 @@ double FGNavRadio::localizerWidth(FGNavRecord* aLOC)
|
|||
|
||||
void FGNavRadio::audioNavidChanged()
|
||||
{
|
||||
if ( globals->get_soundmgr()->exists(nav_fx_name)) {
|
||||
globals->get_soundmgr()->remove(nav_fx_name);
|
||||
if (_sgr->exists(nav_fx_name)) {
|
||||
_sgr->remove(nav_fx_name);
|
||||
}
|
||||
|
||||
try {
|
||||
string trans_ident(_navaid->get_trans_ident());
|
||||
SGSoundSample* sound = morse.make_ident(trans_ident, LO_FREQUENCY);
|
||||
sound->set_volume( 0.3 );
|
||||
if (!globals->get_soundmgr()->add( sound, nav_fx_name )) {
|
||||
if (!_sgr->add( sound, nav_fx_name )) {
|
||||
SG_LOG(SG_COCKPIT, SG_WARN, "Failed to add v1-vor-ident sound");
|
||||
}
|
||||
|
||||
if ( globals->get_soundmgr()->exists( dme_fx_name ) ) {
|
||||
globals->get_soundmgr()->remove( dme_fx_name );
|
||||
if ( _sgr->exists( dme_fx_name ) ) {
|
||||
_sgr->remove( dme_fx_name );
|
||||
}
|
||||
|
||||
sound = morse.make_ident( trans_ident, HI_FREQUENCY );
|
||||
sound->set_volume( 0.3 );
|
||||
globals->get_soundmgr()->add( sound, dme_fx_name );
|
||||
_sgr->add( sound, dme_fx_name );
|
||||
|
||||
int offset = (int)(sg_random() * 30.0);
|
||||
play_count = offset / 4;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
// forward decls
|
||||
class SGInterpTable;
|
||||
|
||||
class SGSampleGroup;
|
||||
class FGNavRecord;
|
||||
typedef SGSharedPtr<FGNavRecord> FGNavRecordPtr;
|
||||
|
||||
|
@ -162,6 +163,8 @@ class FGNavRadio : public SGSubsystem
|
|||
|
||||
// realism setting, are false courses and GS lobes enabled?
|
||||
bool _falseCoursesEnabled;
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr;
|
||||
|
||||
bool updateWithPower(double aDt);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <simgear/structure/commands.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/event_mgr.hxx>
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
|
||||
#include <Cockpit/panel.hxx>
|
||||
#include <Cockpit/panel_io.hxx>
|
||||
|
@ -33,7 +34,7 @@
|
|||
#include <Scenery/tilemgr.hxx>
|
||||
#include <Scenery/scenery.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
#include <Sound/fg_fx.hxx>
|
||||
#include <Sound/sample_queue.hxx>
|
||||
#include <Time/sunsolver.hxx>
|
||||
#include <Time/tmp.hxx>
|
||||
|
||||
|
@ -1251,13 +1252,22 @@ do_set_cursor (const SGPropertyNode * arg)
|
|||
static bool
|
||||
do_play_audio_sample (const SGPropertyNode * arg)
|
||||
{
|
||||
FGFX *fx = (FGFX *)globals->get_subsystem("fx");
|
||||
string path = arg->getStringValue("path");
|
||||
string file = arg->getStringValue("file");
|
||||
double volume = arg->getDoubleValue("volume");
|
||||
float volume = arg->getFloatValue("volume");
|
||||
// cout << "playing " << path << " / " << file << endl;
|
||||
try {
|
||||
fx->play_message( path, file, volume );
|
||||
static FGSampleQueue *queue = 0;
|
||||
if ( !queue ) {
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
queue = new FGSampleQueue(smgr, "chatter");
|
||||
queue->tie_to_listener();
|
||||
}
|
||||
|
||||
SGSoundSample *msg = new SGSoundSample(path.c_str(), file.c_str());
|
||||
msg->set_volume( volume );
|
||||
queue->add( msg );
|
||||
|
||||
return true;
|
||||
|
||||
} catch (const sg_io_exception&) {
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include <simgear/misc/interpolator.hxx>
|
||||
#include <simgear/scene/material/matlib.hxx>
|
||||
#include <simgear/scene/model/particles.hxx>
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
#include <simgear/timing/lowleveltime.h>
|
||||
|
||||
|
@ -106,9 +107,6 @@
|
|||
#include <Scenery/scenery.hxx>
|
||||
#include <Scenery/tilemgr.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
#include <Sound/fg_fx.hxx>
|
||||
#include <Sound/beacon.hxx>
|
||||
#include <Sound/morse.hxx>
|
||||
#include <Sound/voice.hxx>
|
||||
#include <Systems/system_mgr.hxx>
|
||||
#include <Time/light.hxx>
|
||||
|
@ -138,9 +136,7 @@
|
|||
|
||||
using std::string;
|
||||
|
||||
class Sound;
|
||||
extern const char *default_root;
|
||||
float init_volume;
|
||||
|
||||
|
||||
// Scan the command line options for the specified option and return
|
||||
|
@ -1456,6 +1452,21 @@ bool fgInitSubsystems() {
|
|||
globals->get_event_mgr()->init();
|
||||
globals->get_event_mgr()->setRealtimeProperty(fgGetNode("/sim/time/delta-realtime-sec", true));
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Initialize the sound manager subsystem.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
globals->get_soundmgr()->bind();
|
||||
globals->get_soundmgr()->init(fgGetString("/sim/sound/device-name", NULL));
|
||||
|
||||
vector <const char*>devices =
|
||||
globals->get_soundmgr()->get_available_devices();
|
||||
for (unsigned int i=0; i<devices.size(); i++) {
|
||||
SGPropertyNode *p = fgGetNode("/sim/sound/devices/device", i, true);
|
||||
p->setStringValue(devices[i]);
|
||||
}
|
||||
devices.clear();
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Initialize the property interpolator subsystem. Put into the INIT
|
||||
// group because the "nasal" subsystem may need it at GENERAL take-down.
|
||||
|
@ -1524,7 +1535,6 @@ bool fgInitSubsystems() {
|
|||
// Initialize the ridgelift subsystem
|
||||
globals->add_subsystem("ridgelift", new FGRidgeLift);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Initialize the aircraft systems and instrumentation (before the
|
||||
// autopilot.)
|
||||
|
@ -1587,22 +1597,9 @@ bool fgInitSubsystems() {
|
|||
fgGetBool("/sim/rendering/bump-mapping", false);
|
||||
|
||||
#ifdef ENABLE_AUDIO_SUPPORT
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Initialize the sound subsystem.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
init_volume = fgGetFloat("/sim/sound/volume");
|
||||
fgSetFloat("/sim/sound/volume", 0.0f);
|
||||
globals->set_soundmgr(new SGSoundMgr);
|
||||
globals->get_soundmgr()->init();
|
||||
globals->get_soundmgr()->bind();
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Initialize the sound-effects subsystem.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
globals->add_subsystem("fx", new FGFX);
|
||||
globals->add_subsystem("voice", new FGVoiceMgr);
|
||||
#endif
|
||||
|
||||
|
@ -1685,6 +1682,7 @@ bool fgInitSubsystems() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
globals->add_subsystem("replay", new FGReplay);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Bind and initialize subsystems.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -222,12 +222,12 @@ setFreeze (bool f)
|
|||
frozen = f;
|
||||
|
||||
// Stop sound on a pause
|
||||
SGSoundMgr *s = globals->get_soundmgr();
|
||||
if ( s != NULL ) {
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
if ( smgr != NULL ) {
|
||||
if ( f ) {
|
||||
s->pause();
|
||||
} else if (!fgGetBool("/sim/sound/pause")) {
|
||||
s->resume();
|
||||
smgr->suspend();
|
||||
} else if (fgGetBool("/sim/sound/working")) {
|
||||
smgr->resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
|||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
|
@ -33,6 +32,7 @@
|
|||
#include <simgear/scene/material/matlib.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/structure/event_mgr.hxx>
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
|
||||
#include <Aircraft/controls.hxx>
|
||||
#include <Airports/runways.hxx>
|
||||
|
@ -73,6 +73,7 @@ FGGlobals::FGGlobals() :
|
|||
renderer( new FGRenderer ),
|
||||
subsystem_mgr( new SGSubsystemMgr ),
|
||||
event_mgr( new SGEventMgr ),
|
||||
soundmgr( new SGSoundMgr ),
|
||||
sim_time_sec( 0.0 ),
|
||||
fg_root( "" ),
|
||||
warp( 0 ),
|
||||
|
@ -83,7 +84,6 @@ FGGlobals::FGGlobals() :
|
|||
matlib( NULL ),
|
||||
route_mgr( NULL ),
|
||||
current_panel( NULL ),
|
||||
soundmgr( NULL ),
|
||||
ATC_mgr( NULL ),
|
||||
AI_mgr( NULL ),
|
||||
controls( NULL ),
|
||||
|
@ -127,6 +127,7 @@ FGGlobals::~FGGlobals()
|
|||
// deleted subsystems.
|
||||
subsystem_mgr->get_group(SGSubsystemMgr::GENERAL)->remove_subsystem("input");
|
||||
subsystem_mgr->get_group(SGSubsystemMgr::GENERAL)->remove_subsystem("gui");
|
||||
subsystem_mgr->unbind();
|
||||
delete subsystem_mgr;
|
||||
delete event_mgr;
|
||||
delete time_params;
|
||||
|
@ -135,7 +136,6 @@ FGGlobals::~FGGlobals()
|
|||
delete matlib;
|
||||
delete route_mgr;
|
||||
delete current_panel;
|
||||
delete soundmgr;
|
||||
|
||||
delete ATC_mgr;
|
||||
delete AI_mgr;
|
||||
|
@ -160,6 +160,9 @@ FGGlobals::~FGGlobals()
|
|||
delete channellist;
|
||||
delete airwaynet;
|
||||
delete multiplayer_mgr;
|
||||
|
||||
soundmgr->unbind();
|
||||
delete soundmgr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -261,6 +264,11 @@ FGGlobals::add_subsystem (const char * name,
|
|||
subsystem_mgr->add(name, subsystem, type, min_time_sec);
|
||||
}
|
||||
|
||||
SGSoundMgr *
|
||||
FGGlobals::get_soundmgr () const
|
||||
{
|
||||
return soundmgr;
|
||||
}
|
||||
|
||||
SGEventMgr *
|
||||
FGGlobals::get_event_mgr () const
|
||||
|
|
|
@ -49,10 +49,10 @@ class SGMagVar;
|
|||
class SGMaterialLib;
|
||||
class SGPropertyNode;
|
||||
class SGTime;
|
||||
class SGSoundMgr;
|
||||
class SGEventMgr;
|
||||
class SGSubsystemMgr;
|
||||
class SGSubsystem;
|
||||
class SGSoundMgr;
|
||||
|
||||
class FGAIMgr;
|
||||
class FGATCMgr;
|
||||
|
@ -94,6 +94,7 @@ private:
|
|||
FGRenderer *renderer;
|
||||
SGSubsystemMgr *subsystem_mgr;
|
||||
SGEventMgr *event_mgr;
|
||||
SGSoundMgr *soundmgr;
|
||||
|
||||
// Number of milliseconds elapsed since the start of the program.
|
||||
double sim_time_sec;
|
||||
|
@ -132,9 +133,6 @@ private:
|
|||
// 2D panel
|
||||
FGPanel *current_panel;
|
||||
|
||||
// sound manager
|
||||
SGSoundMgr *soundmgr;
|
||||
|
||||
// ATC manager
|
||||
FGATCMgr *ATC_mgr;
|
||||
|
||||
|
@ -202,6 +200,8 @@ public:
|
|||
|
||||
virtual SGEventMgr *get_event_mgr () const;
|
||||
|
||||
virtual SGSoundMgr *get_soundmgr () const;
|
||||
|
||||
inline double get_sim_time_sec () const { return sim_time_sec; }
|
||||
inline void inc_sim_time_sec (double dt) { sim_time_sec += dt; }
|
||||
inline void set_sim_time_sec (double t) { sim_time_sec = t; }
|
||||
|
@ -244,9 +244,6 @@ public:
|
|||
inline FGPanel *get_current_panel() const { return current_panel; }
|
||||
inline void set_current_panel( FGPanel *cp ) { current_panel = cp; }
|
||||
|
||||
inline SGSoundMgr *get_soundmgr() const { return soundmgr; }
|
||||
inline void set_soundmgr( SGSoundMgr *sm ) { soundmgr = sm; }
|
||||
|
||||
inline FGControls *get_controls() const { return controls; }
|
||||
inline void set_controls( FGControls *c ) { controls = c; }
|
||||
|
||||
|
@ -305,7 +302,7 @@ public:
|
|||
inline void set_tile_mgr ( FGTileMgr *t ) { tile_mgr = t; }
|
||||
|
||||
inline FGFontCache *get_fontcache() const { return fontcache; }
|
||||
|
||||
|
||||
inline FGNavList *get_navlist() const { return navlist; }
|
||||
inline void set_navlist( FGNavList *n ) { navlist = n; }
|
||||
inline FGNavList *get_loclist() const { return loclist; }
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#include <Model/acmodel.hxx>
|
||||
#include <Scenery/scenery.hxx>
|
||||
#include <Scenery/tilemgr.hxx>
|
||||
#include <Sound/fg_fx.hxx>
|
||||
#include <Sound/beacon.hxx>
|
||||
#include <Sound/morse.hxx>
|
||||
#include <Sound/fg_fx.hxx>
|
||||
|
@ -86,7 +87,6 @@
|
|||
|
||||
static double real_delta_time_sec = 0.0;
|
||||
double delta_time_sec = 0.0;
|
||||
extern float init_volume;
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
|
@ -104,6 +104,8 @@ long global_multi_loop;
|
|||
SGTimeStamp last_time_stamp;
|
||||
SGTimeStamp current_time_stamp;
|
||||
|
||||
void fgSetNewSoundDevice(const char *);
|
||||
|
||||
// The atexit() function handler should know when the graphical subsystem
|
||||
// is initialized.
|
||||
extern int _bootstrap_OSInit;
|
||||
|
@ -460,8 +462,8 @@ static void fgMainLoop( void ) {
|
|||
// we may want to move this to its own class at some point
|
||||
//
|
||||
double visibility_meters = fgGetDouble("/environment/visibility-m");
|
||||
|
||||
globals->get_tile_mgr()->prep_ssg_nodes( visibility_meters );
|
||||
|
||||
// update tile manager for view...
|
||||
SGVec3d viewPos = globals->get_current_view()->get_view_pos();
|
||||
SGGeod geodViewPos = SGGeod::fromCart(viewPos);
|
||||
|
@ -477,10 +479,26 @@ static void fgMainLoop( void ) {
|
|||
// update the view angle as late as possible, but before sound calculations
|
||||
globals->get_viewmgr()->update(real_delta_time_sec);
|
||||
|
||||
// Run audio scheduler
|
||||
// Update the sound manager last so it can use the CPU while the GPU
|
||||
// is processing the scenery (doubled the frame-rate for me) -EMH-
|
||||
#ifdef ENABLE_AUDIO_SUPPORT
|
||||
FGFX* fx = (FGFX*) globals->get_subsystem("fx");
|
||||
fx->update_fx_late(delta_time_sec);
|
||||
static SGPropertyNode *sound_enabled = fgGetNode("/sim/sound/enabled");
|
||||
static SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
static bool smgr_enabled = true;
|
||||
if (smgr_enabled != sound_enabled->getBoolValue()) {
|
||||
if (smgr_enabled == true) { // request to suspend
|
||||
smgr->suspend();
|
||||
} else {
|
||||
smgr->resume();
|
||||
}
|
||||
smgr_enabled = sound_enabled->getBoolValue();
|
||||
}
|
||||
|
||||
if (smgr_enabled == true) {
|
||||
static SGPropertyNode *volume = fgGetNode("/sim/sound/volume");
|
||||
smgr->set_volume(volume->getFloatValue());
|
||||
smgr->update(delta_time_sec);
|
||||
}
|
||||
#endif
|
||||
|
||||
// END Tile Manager udpates
|
||||
|
@ -488,8 +506,13 @@ static void fgMainLoop( void ) {
|
|||
if (!scenery_loaded && globals->get_tile_mgr()->isSceneryLoaded()
|
||||
&& cur_fdm_state->get_inited()) {
|
||||
fgSetBool("sim/sceneryloaded",true);
|
||||
fgSetFloat("/sim/sound/volume", init_volume);
|
||||
globals->get_soundmgr()->set_volume(init_volume);
|
||||
if (fgGetBool("/sim/sound/working")) {
|
||||
smgr->activate();
|
||||
} else {
|
||||
smgr->stop();
|
||||
}
|
||||
globals->get_props()->tie("/sim/sound/devices/name",
|
||||
SGRawValueFunctions<const char *>(0, fgSetNewSoundDevice), false);
|
||||
}
|
||||
|
||||
fgRequestRedraw();
|
||||
|
@ -497,6 +520,14 @@ static void fgMainLoop( void ) {
|
|||
SG_LOG( SG_ALL, SG_DEBUG, "" );
|
||||
}
|
||||
|
||||
void fgSetNewSoundDevice(const char *device)
|
||||
{
|
||||
globals->get_soundmgr()->suspend();
|
||||
globals->get_soundmgr()->stop();
|
||||
globals->get_soundmgr()->init(device);
|
||||
globals->get_soundmgr()->resume();
|
||||
}
|
||||
|
||||
// Operation for querying OpenGL parameters. This must be done in a
|
||||
// valid OpenGL context, potentially in another thread.
|
||||
namespace
|
||||
|
@ -627,6 +658,7 @@ static void fgIdleFunction ( void ) {
|
|||
|
||||
} else if ( idle_state == 5 ) {
|
||||
idle_state++;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Initialize the 3D aircraft model subsystem (has a dependency on
|
||||
// the scenery subsystem.)
|
||||
|
@ -723,13 +755,13 @@ static void fgIdleFunction ( void ) {
|
|||
SG_LOG( SG_GENERAL, SG_INFO,
|
||||
"Starting intro music: " << mp3file.str() );
|
||||
|
||||
#if defined( __CYGWIN__ )
|
||||
# if defined( __CYGWIN__ )
|
||||
string command = "start /m `cygpath -w " + mp3file.str() + "`";
|
||||
#elif defined( WIN32 )
|
||||
# elif defined( WIN32 )
|
||||
string command = "start /m " + mp3file.str();
|
||||
#else
|
||||
# else
|
||||
string command = "mpg123 " + mp3file.str() + "> /dev/null 2>&1";
|
||||
#endif
|
||||
# endif
|
||||
|
||||
system ( command.c_str() );
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/scene/material/mat.hxx>
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
|
||||
// #include <Include/general.hxx>
|
||||
// #include <Airports/simple.hxx>
|
||||
|
@ -81,7 +82,8 @@ enum
|
|||
FG_OPTIONS_ERROR = 2,
|
||||
FG_OPTIONS_EXIT = 3,
|
||||
FG_OPTIONS_VERBOSE_HELP = 4,
|
||||
FG_OPTIONS_SHOW_AIRCRAFT = 5
|
||||
FG_OPTIONS_SHOW_AIRCRAFT = 5,
|
||||
FG_OPTIONS_SHOW_SOUND_DEVICES = 6
|
||||
};
|
||||
|
||||
static double
|
||||
|
@ -186,7 +188,8 @@ fgSetDefaults ()
|
|||
fgSetBool("/sim/hud/enable3d", true);
|
||||
fgSetBool("/sim/hud/visibility", false);
|
||||
fgSetBool("/sim/panel/visibility", true);
|
||||
fgSetBool("/sim/sound/pause", false);
|
||||
fgSetBool("/sim/sound/enabled", true);
|
||||
fgSetBool("/sim/sound/working", true);
|
||||
|
||||
// Flight Model options
|
||||
fgSetString("/sim/flight-model", "jsb");
|
||||
|
@ -201,7 +204,7 @@ fgSetDefaults ()
|
|||
fgSetBool("/sim/rendering/shading", true);
|
||||
fgSetBool("/sim/rendering/skyblend", true);
|
||||
fgSetBool("/sim/rendering/textures", true);
|
||||
fgTie( "/sim/rendering/filtering", SGGetTextureFilter, SGSetTextureFilter, false);
|
||||
fgTie( "/sim/rendering/filtering", SGGetTextureFilter, SGSetTextureFilter, false);
|
||||
fgSetInt("/sim/rendering/filtering", 1);
|
||||
fgSetBool("/sim/rendering/wireframe", false);
|
||||
fgSetBool("/sim/rendering/horizon-effect", false);
|
||||
|
@ -1290,8 +1293,9 @@ struct OptionDesc {
|
|||
{"enable-hud", false, OPTION_BOOL, "/sim/hud/visibility", true, "", 0 },
|
||||
{"disable-panel", false, OPTION_BOOL, "/sim/panel/visibility", false, "", 0 },
|
||||
{"enable-panel", false, OPTION_BOOL, "/sim/panel/visibility", true, "", 0 },
|
||||
{"disable-sound", false, OPTION_BOOL, "/sim/sound/pause", true, "", 0 },
|
||||
{"enable-sound", false, OPTION_BOOL, "/sim/sound/pause", false, "", 0 },
|
||||
{"disable-sound", false, OPTION_BOOL, "/sim/sound/working", false, "", 0 },
|
||||
{"enable-sound", false, OPTION_BOOL, "/sim/sound/working", true, "", 0 },
|
||||
{"sound-device", true, OPTION_STRING, "/sim/sound/device-name", false, "", 0 },
|
||||
{"airport", true, OPTION_STRING, "/sim/presets/airport-id", false, "", 0 },
|
||||
{"runway", true, OPTION_FUNC, "", false, "", fgOptRunway },
|
||||
{"vor", true, OPTION_FUNC, "", false, "", fgOptVOR },
|
||||
|
@ -1501,6 +1505,8 @@ parse_option (const string& arg)
|
|||
return(FG_OPTIONS_VERBOSE_HELP);
|
||||
} else if ( arg.find( "--show-aircraft") == 0) {
|
||||
return(FG_OPTIONS_SHOW_AIRCRAFT);
|
||||
} else if ( arg.find( "--show-sound-devices") == 0) {
|
||||
return(FG_OPTIONS_SHOW_SOUND_DEVICES);
|
||||
} else if ( arg.find( "--prop:" ) == 0 ) {
|
||||
if (!set_property(arg.substr(7))) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Bad property assignment: " << arg );
|
||||
|
@ -1616,11 +1622,20 @@ fgParseArgs (int argc, char **argv)
|
|||
verbose = true;
|
||||
|
||||
else if (result == FG_OPTIONS_SHOW_AIRCRAFT) {
|
||||
fgOptLogLevel( "alert" );
|
||||
SGPath path( globals->get_fg_root() );
|
||||
path.append("Aircraft");
|
||||
fgShowAircraft(path, true);
|
||||
exit(0);
|
||||
fgOptLogLevel( "alert" );
|
||||
SGPath path( globals->get_fg_root() );
|
||||
path.append("Aircraft");
|
||||
fgShowAircraft(path, true);
|
||||
exit(0);
|
||||
|
||||
} else if (result == FG_OPTIONS_SHOW_SOUND_DEVICES) {
|
||||
SGSoundMgr smgr;
|
||||
vector <const char*>devices = smgr.get_available_devices();
|
||||
for (int i=0; i<devices.size(); i++) {
|
||||
printf("%i. \"%s\"\n", i, devices[i]);
|
||||
}
|
||||
devices.clear();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
else if (result == FG_OPTIONS_EXIT)
|
||||
|
|
|
@ -380,7 +380,7 @@ FGViewer::recalcLookFrom ()
|
|||
SGQuatd hlToBody = SGQuatd::fromYawPitchRollDeg(head, pitch, roll);
|
||||
|
||||
// The rotation offset, don't know why heading is negative here ...
|
||||
SGQuatd viewOffsetOr
|
||||
mViewOffsetOr
|
||||
= SGQuatd::fromYawPitchRollDeg(-_heading_offset_deg, _pitch_offset_deg,
|
||||
_roll_offset_deg);
|
||||
|
||||
|
@ -396,7 +396,7 @@ FGViewer::recalcLookFrom ()
|
|||
SGQuatd q(-0.5, -0.5, 0.5, 0.5);
|
||||
|
||||
_absolute_view_pos = position + (ec2body*q).backTransform(_offset_m);
|
||||
mViewOrientation = ec2body*viewOffsetOr*q;
|
||||
mViewOrientation = ec2body*mViewOffsetOr*q;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -437,7 +437,7 @@ FGViewer::recalcLookAt ()
|
|||
SGQuatd geodEyeHlOr = SGQuatd::fromLonLat(_position);
|
||||
|
||||
// the rotation offset, don't know why heading is negative here ...
|
||||
SGQuatd eyeOffsetOr =
|
||||
mViewOffsetOr =
|
||||
SGQuatd::fromYawPitchRollDeg(-_heading_offset_deg + 180, _pitch_offset_deg,
|
||||
_roll_offset_deg);
|
||||
|
||||
|
@ -445,7 +445,7 @@ FGViewer::recalcLookAt ()
|
|||
SGVec3d eyeOff(-_offset_m.z(), _offset_m.x(), -_offset_m.y());
|
||||
SGQuatd ec2eye = geodEyeHlOr*geodEyeOr;
|
||||
SGVec3d eyeCart = SGVec3d::fromGeod(_position);
|
||||
eyeCart += (ec2eye*eyeOffsetOr).backTransform(eyeOff);
|
||||
eyeCart += (ec2eye*mViewOffsetOr).backTransform(eyeOff);
|
||||
|
||||
SGVec3d atCart = SGVec3d::fromGeod(_target);
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ public:
|
|||
// orientation rotations listed below. This has the effect of the
|
||||
// eye moving around and "looking at" the object (model) from
|
||||
// different angles.
|
||||
virtual SGVec3d getOffset_m () const { return _offset_m; }
|
||||
virtual double getXOffset_m () const { return _offset_m.x(); }
|
||||
virtual double getYOffset_m () const { return _offset_m.y(); }
|
||||
virtual double getZOffset_m () const { return _offset_m.z(); }
|
||||
|
@ -201,6 +202,7 @@ public:
|
|||
const SGVec3d& get_view_pos() { if ( _dirty ) { recalc(); } return _absolute_view_pos; }
|
||||
const SGVec3d& getViewPosition() { if ( _dirty ) { recalc(); } return _absolute_view_pos; }
|
||||
const SGQuatd& getViewOrientation() { if ( _dirty ) { recalc(); } return mViewOrientation; }
|
||||
const SGQuatd& getViewOrientationOffset() { if ( _dirty ) { recalc(); } return mViewOffsetOr; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Part 4: View and frustrum data setters and getters
|
||||
|
@ -247,6 +249,7 @@ private:
|
|||
bool _dirty;
|
||||
|
||||
SGQuatd mViewOrientation;
|
||||
SGQuatd mViewOffsetOr;
|
||||
SGVec3d _absolute_view_pos;
|
||||
|
||||
SGGeod _position;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <string.h> // strcmp
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
#include <Model/acmodel.hxx>
|
||||
#include <Main/viewer.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
|
@ -42,6 +43,7 @@ FGViewMgr::FGViewMgr( void ) :
|
|||
config_list(fgGetNode("/sim", true)->getChildren("view")),
|
||||
current(0)
|
||||
{
|
||||
smgr = globals->get_soundmgr();
|
||||
}
|
||||
|
||||
// Destructor
|
||||
|
@ -214,6 +216,38 @@ FGViewMgr::bind ()
|
|||
n->tie("viewer-x-m", SGRawValuePointer<double>(&abs_viewer_position[0]));
|
||||
n->tie("viewer-y-m", SGRawValuePointer<double>(&abs_viewer_position[1]));
|
||||
n->tie("viewer-z-m", SGRawValuePointer<double>(&abs_viewer_position[2]));
|
||||
|
||||
// for automatic untying:
|
||||
#define x(str) ((void)tied_props.push_back(str), str)
|
||||
|
||||
fgTie(x("/sim/current-view/debug/orientation-w"), this,
|
||||
&FGViewMgr::getCurrentViewOrientation_w);
|
||||
fgTie(x("/sim/current-view/debug/orientation-x"), this,
|
||||
&FGViewMgr::getCurrentViewOrientation_x);
|
||||
fgTie(x("/sim/current-view/debug/orientation-y"), this,
|
||||
&FGViewMgr::getCurrentViewOrientation_y);
|
||||
fgTie(x("/sim/current-view/debug/orientation-z"), this,
|
||||
&FGViewMgr::getCurrentViewOrientation_z);
|
||||
|
||||
fgTie(x("/sim/current-view/debug/orientation_offset-w"), this,
|
||||
&FGViewMgr::getCurrentViewOrOffset_w);
|
||||
fgTie(x("/sim/current-view/debug/orientation_offset-x"), this,
|
||||
&FGViewMgr::getCurrentViewOrOffset_x);
|
||||
fgTie(x("/sim/current-view/debug/orientation_offset-y"), this,
|
||||
&FGViewMgr::getCurrentViewOrOffset_y);
|
||||
fgTie(x("/sim/current-view/debug/orientation_offset-z"), this,
|
||||
&FGViewMgr::getCurrentViewOrOffset_z);
|
||||
|
||||
fgTie(x("/sim/current-view/debug/frame-w"), this,
|
||||
&FGViewMgr::getCurrentViewFrame_w);
|
||||
fgTie(x("/sim/current-view/debug/frame-x"), this,
|
||||
&FGViewMgr::getCurrentViewFrame_x);
|
||||
fgTie(x("/sim/current-view/debug/frame-y"), this,
|
||||
&FGViewMgr::getCurrentViewFrame_y);
|
||||
fgTie(x("/sim/current-view/debug/frame-z"), this,
|
||||
&FGViewMgr::getCurrentViewFrame_z);
|
||||
|
||||
#undef x
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -234,16 +268,21 @@ FGViewMgr::unbind ()
|
|||
fgUntie("/sim/current-view/viewer-x-m");
|
||||
fgUntie("/sim/current-view/viewer-y-m");
|
||||
fgUntie("/sim/current-view/viewer-z-m");
|
||||
|
||||
list<const char*>::const_iterator it;
|
||||
for (it = tied_props.begin(); it != tied_props.end(); it++){
|
||||
fgUntie(*it);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
FGViewMgr::update (double dt)
|
||||
{
|
||||
FGViewer * view = get_current_view();
|
||||
if (view == 0)
|
||||
return;
|
||||
|
||||
FGViewer *loop_view = (FGViewer *)get_view(current);
|
||||
FGViewer *loop_view = (FGViewer *)get_current_view();
|
||||
if (loop_view == 0) return;
|
||||
|
||||
SGPropertyNode *n = config_list[current];
|
||||
double lon_deg, lat_deg, alt_ft, roll_deg, pitch_deg, heading_deg;
|
||||
|
||||
|
@ -289,10 +328,25 @@ FGViewMgr::update (double dt)
|
|||
setViewTargetYOffset_m(fgGetDouble("/sim/current-view/target-y-offset-m"));
|
||||
setViewTargetZOffset_m(fgGetDouble("/sim/current-view/target-z-offset-m"));
|
||||
|
||||
current_view_orientation = loop_view->getViewOrientation();
|
||||
current_view_or_offset = loop_view->getViewOrientationOffset();
|
||||
|
||||
// Update the current view
|
||||
do_axes();
|
||||
view->update(dt);
|
||||
loop_view->update(dt);
|
||||
abs_viewer_position = loop_view->getViewPosition();
|
||||
|
||||
// update audio listener values
|
||||
// set the viewer posotion in Cartesian coordinates in meters
|
||||
smgr->set_position( abs_viewer_position, loop_view->getPosition() );
|
||||
smgr->set_orientation( current_view_orientation );
|
||||
|
||||
// get the model velocity
|
||||
SGVec3d velocity = SGVec3d::zeros();
|
||||
if ( !stationary() ) {
|
||||
velocity = globals->get_aircraft_model()->getVelocity();
|
||||
}
|
||||
smgr->set_velocity( velocity );
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -562,6 +616,20 @@ FGViewMgr::setViewZOffset_m (double z)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
FGViewMgr::stationary () const
|
||||
{
|
||||
const FGViewer * view = get_current_view();
|
||||
if (view != 0) {
|
||||
if (((FGViewer *)view)->getXOffset_m() == 0.0 &&
|
||||
((FGViewer *)view)->getYOffset_m() == 0.0 &&
|
||||
((FGViewer *)view)->getZOffset_m() == 0.0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
double
|
||||
FGViewMgr::getViewTargetXOffset_m () const
|
||||
{
|
||||
|
@ -720,6 +788,101 @@ FGViewMgr::setViewAxisLat (double axis)
|
|||
axis_lat = axis;
|
||||
}
|
||||
|
||||
// reference frame orientation.
|
||||
// This is the view orientation you get when you have no
|
||||
// view offset, i.e. the offset operator is the identity.
|
||||
//
|
||||
// For example, in the familiar "cockpit lookfrom" view,
|
||||
// the reference frame is equal to the aircraft attitude,
|
||||
// i.e. it is the view looking towards 12:00 straight ahead.
|
||||
//
|
||||
// FIXME: Somebody needs to figure out what is the reference
|
||||
// frame view for the other view modes.
|
||||
//
|
||||
// Conceptually, this quat represents a rotation relative
|
||||
// to the ECEF reference orientation, as described at
|
||||
// http://www.av8n.com/physics/coords.htm#sec-orientation
|
||||
//
|
||||
// See the NOTE concerning reference orientations, below.
|
||||
//
|
||||
// The components of this quat are expressed in
|
||||
// the conventional aviation basis set,
|
||||
// i.e. x=forward, y=starboard, z=bottom
|
||||
double FGViewMgr::getCurrentViewFrame_w() const{
|
||||
return ((current_view_orientation*conj(fsb2sta())*conj(current_view_or_offset))).w();
|
||||
}
|
||||
double FGViewMgr::getCurrentViewFrame_x() const{
|
||||
return ((current_view_orientation*conj(fsb2sta())*conj(current_view_or_offset))).x();
|
||||
}
|
||||
double FGViewMgr::getCurrentViewFrame_y() const{
|
||||
return ((current_view_orientation*conj(fsb2sta())*conj(current_view_or_offset))).y();
|
||||
}
|
||||
double FGViewMgr::getCurrentViewFrame_z() const{
|
||||
return ((current_view_orientation*conj(fsb2sta())*conj(current_view_or_offset))).z();
|
||||
}
|
||||
|
||||
|
||||
// view offset.
|
||||
// This rotation takes you from the aforementioned
|
||||
// reference frame view orientation to whatever
|
||||
// actual current view orientation is.
|
||||
//
|
||||
// The components of this quaternion are expressed in
|
||||
// the conventional aviation basis set,
|
||||
// i.e. x=forward, y=starboard, z=bottom
|
||||
double FGViewMgr::getCurrentViewOrOffset_w() const{
|
||||
return current_view_or_offset.w();
|
||||
}
|
||||
double FGViewMgr::getCurrentViewOrOffset_x() const{
|
||||
return current_view_or_offset.x();
|
||||
}
|
||||
double FGViewMgr::getCurrentViewOrOffset_y() const{
|
||||
return current_view_or_offset.y();
|
||||
}
|
||||
double FGViewMgr::getCurrentViewOrOffset_z() const{
|
||||
return current_view_or_offset.z();
|
||||
}
|
||||
|
||||
|
||||
// current view orientation.
|
||||
// This is a rotation relative to the earth-centered (ec)
|
||||
// reference frame.
|
||||
//
|
||||
// NOTE: Here we remove a factor of fsb2sta so that
|
||||
// the components of this quat are displayed using the
|
||||
// conventional ECEF basis set. This is *not* the way
|
||||
// the view orientation is stored in the views[] array,
|
||||
// but is easier for non-graphics hackers to understand.
|
||||
// If we did not remove this factor of fsb2sta here and
|
||||
// in getCurrentViewFrame, that would be equivalent to
|
||||
// the following peculiar reference orientation:
|
||||
// Suppose you are over the Gulf of Guinea, at (lat,lon) = (0,0).
|
||||
// Then the reference frame orientation can be achieved via:
|
||||
// -- The aircraft X-axis (nose) headed south.
|
||||
// -- The aircraft Y-axis (starboard wingtip) pointing up.
|
||||
// -- The aircraft Z-axis (belly) pointing west.
|
||||
// To say the same thing in other words, and perhaps more to the
|
||||
// point: If we use the OpenGL camera orientation conventions,
|
||||
// i.e. Xprime=starboard, Yprime=top, Zprime=aft, then the
|
||||
// aforementioned peculiar reference orientation at (lat,lon)
|
||||
// = (0,0) can be described as:
|
||||
// -- aircraft Xprime axis (starboard) pointed up
|
||||
// -- aircraft Yprime axis (top) pointed east
|
||||
// -- aircraft Zprime axis (aft) pointed north
|
||||
// meaning the OpenGL axes are aligned with the ECEF axes.
|
||||
double FGViewMgr::getCurrentViewOrientation_w() const{
|
||||
return (current_view_orientation * conj(fsb2sta())).w();
|
||||
}
|
||||
double FGViewMgr::getCurrentViewOrientation_x() const{
|
||||
return (current_view_orientation * conj(fsb2sta())).x();
|
||||
}
|
||||
double FGViewMgr::getCurrentViewOrientation_y() const{
|
||||
return (current_view_orientation * conj(fsb2sta())).y();
|
||||
}
|
||||
double FGViewMgr::getCurrentViewOrientation_z() const{
|
||||
return (current_view_orientation * conj(fsb2sta())).z();
|
||||
}
|
||||
|
||||
void
|
||||
FGViewMgr::do_axes ()
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#define _VIEWMGR_HXX
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
@ -32,6 +33,7 @@
|
|||
|
||||
// forward decls
|
||||
class FGViewer;
|
||||
class SGSoundMgr;
|
||||
typedef SGSharedPtr<FGViewer> FGViewerPtr;
|
||||
|
||||
// Define a structure containing view information
|
||||
|
@ -75,6 +77,8 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
list<const char*> tied_props;
|
||||
|
||||
double axis_long;
|
||||
double axis_lat;
|
||||
|
||||
|
@ -116,6 +120,22 @@ private:
|
|||
int getView () const;
|
||||
void setView (int newview);
|
||||
|
||||
// quaternion accessors, for debugging:
|
||||
double getCurrentViewOrientation_w() const;
|
||||
double getCurrentViewOrientation_x() const;
|
||||
double getCurrentViewOrientation_y() const;
|
||||
double getCurrentViewOrientation_z() const;
|
||||
double getCurrentViewOrOffset_w() const;
|
||||
double getCurrentViewOrOffset_x() const;
|
||||
double getCurrentViewOrOffset_y() const;
|
||||
double getCurrentViewOrOffset_z() const;
|
||||
double getCurrentViewFrame_w() const;
|
||||
double getCurrentViewFrame_x() const;
|
||||
double getCurrentViewFrame_y() const;
|
||||
double getCurrentViewFrame_z() const;
|
||||
|
||||
bool stationary () const;
|
||||
|
||||
SGPropertyNode_ptr view_number;
|
||||
vector<SGPropertyNode_ptr> config_list;
|
||||
typedef std::vector<FGViewerPtr> viewer_list;
|
||||
|
@ -123,8 +143,20 @@ private:
|
|||
SGVec3d abs_viewer_position;
|
||||
|
||||
int current;
|
||||
SGQuatd current_view_orientation, current_view_or_offset;
|
||||
|
||||
SGSoundMgr *smgr;
|
||||
|
||||
};
|
||||
|
||||
// This takes the conventional aviation XYZ body system
|
||||
// i.e. x=forward, y=starboard, z=bottom
|
||||
// which is widely used in FGFS
|
||||
// and rotates it into the OpenGL camera system
|
||||
// i.e. Xprime=starboard, Yprime=top, Zprime=aft.
|
||||
inline const SGQuatd fsb2sta()
|
||||
{
|
||||
return SGQuatd(-0.5, -0.5, 0.5, 0.5);
|
||||
}
|
||||
|
||||
#endif // _VIEWMGR_HXX
|
||||
|
|
|
@ -22,19 +22,35 @@
|
|||
#include <Main/viewmgr.hxx>
|
||||
#include <Main/viewer.hxx>
|
||||
#include <Scenery/scenery.hxx>
|
||||
#include <Sound/fg_fx.hxx>
|
||||
|
||||
#include "model_panel.hxx"
|
||||
|
||||
#include "acmodel.hxx"
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of FGAircraftModel
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FGAircraftModel::FGAircraftModel ()
|
||||
: _aircraft(0)
|
||||
: _aircraft(0),
|
||||
_velocity(SGVec3d::zeros()),
|
||||
_fx(0),
|
||||
_lon(0),
|
||||
_lat(0),
|
||||
_alt(0),
|
||||
_pitch(0),
|
||||
_roll(0),
|
||||
_heading(0),
|
||||
_speed_n(0),
|
||||
_speed_e(0),
|
||||
_speed_d(0)
|
||||
{
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
_fx = new FGFX(smgr, "fx");
|
||||
_fx->init();
|
||||
}
|
||||
|
||||
FGAircraftModel::~FGAircraftModel ()
|
||||
|
@ -70,7 +86,15 @@ FGAircraftModel::init ()
|
|||
void
|
||||
FGAircraftModel::bind ()
|
||||
{
|
||||
// No-op
|
||||
_lon = fgGetNode("position/longitude-deg", true);
|
||||
_lat = fgGetNode("position/latitude-deg", true);
|
||||
_alt = fgGetNode("position/altitude-ft", true);
|
||||
_pitch = fgGetNode("orientation/pitch-deg", true);
|
||||
_roll = fgGetNode("orientation/roll-deg", true);
|
||||
_heading = fgGetNode("orientation/heading-deg", true);
|
||||
_speed_n = fgGetNode("velocities/speed-north-fps", true);
|
||||
_speed_e = fgGetNode("velocities/speed-east-fps", true);
|
||||
_speed_d = fgGetNode("velocities/speed-down-fps", true);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -91,13 +115,27 @@ FGAircraftModel::update (double dt)
|
|||
_aircraft->setVisible(true);
|
||||
}
|
||||
|
||||
_aircraft->setPosition(fgGetDouble("/position/longitude-deg"),
|
||||
fgGetDouble("/position/latitude-deg"),
|
||||
fgGetDouble("/position/altitude-ft"));
|
||||
_aircraft->setOrientation(fgGetDouble("/orientation/roll-deg"),
|
||||
fgGetDouble("/orientation/pitch-deg"),
|
||||
fgGetDouble("/orientation/heading-deg"));
|
||||
_aircraft->setPosition(_lon->getDoubleValue(),
|
||||
_lat->getDoubleValue(),
|
||||
_alt->getDoubleValue());
|
||||
_aircraft->setOrientation(_roll->getDoubleValue(),
|
||||
_pitch->getDoubleValue(),
|
||||
_heading->getDoubleValue());
|
||||
_aircraft->update();
|
||||
|
||||
// update model's audio sample values
|
||||
SGGeod position = _aircraft->getPosition();
|
||||
_fx->set_position_geod( position );
|
||||
|
||||
SGQuatd orient = SGQuatd::fromYawPitchRollDeg(_heading->getDoubleValue(),
|
||||
_pitch->getDoubleValue(),
|
||||
_roll->getDoubleValue());
|
||||
_fx->set_orientation( orient );
|
||||
|
||||
_velocity = SGVec3d( _speed_n->getDoubleValue(),
|
||||
_speed_e->getDoubleValue(),
|
||||
_speed_d->getDoubleValue() );
|
||||
_fx->set_velocity( _velocity );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ using std::vector;
|
|||
|
||||
// Don't pull in the headers, since we don't need them here.
|
||||
class SGModelPlacement;
|
||||
|
||||
class FGFX;
|
||||
|
||||
class FGAircraftModel : public SGSubsystem
|
||||
{
|
||||
|
@ -39,10 +39,23 @@ public:
|
|||
virtual void unbind ();
|
||||
virtual void update (double dt);
|
||||
virtual SGModelPlacement * get3DModel() { return _aircraft; }
|
||||
virtual SGVec3d& getVelocity() { return _velocity; }
|
||||
|
||||
private:
|
||||
|
||||
SGModelPlacement * _aircraft;
|
||||
SGVec3d _velocity;
|
||||
SGSharedPtr<FGFX> _fx;
|
||||
|
||||
SGPropertyNode_ptr _lon;
|
||||
SGPropertyNode_ptr _lat;
|
||||
SGPropertyNode_ptr _alt;
|
||||
SGPropertyNode_ptr _pitch;
|
||||
SGPropertyNode_ptr _roll;
|
||||
SGPropertyNode_ptr _heading;
|
||||
SGPropertyNode_ptr _speed_n;
|
||||
SGPropertyNode_ptr _speed_e;
|
||||
SGPropertyNode_ptr _speed_d;
|
||||
};
|
||||
|
||||
#endif // __ACMODEL_HXX
|
||||
|
|
|
@ -4,6 +4,7 @@ libSound_a_SOURCES = \
|
|||
beacon.cxx beacon.hxx \
|
||||
fg_fx.cxx fg_fx.hxx \
|
||||
morse.cxx morse.hxx \
|
||||
voice.cxx voice.hxx
|
||||
voice.cxx voice.hxx \
|
||||
sample_queue.cxx sample_queue.hxx
|
||||
|
||||
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
|
||||
|
|
|
@ -21,11 +21,11 @@
|
|||
// $Id$
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "beacon.hxx"
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
// constructor
|
||||
FGBeacon::FGBeacon()
|
||||
|
@ -39,17 +39,12 @@ FGBeacon::~FGBeacon() {
|
|||
|
||||
// allocate and initialize sound samples
|
||||
bool FGBeacon::init() {
|
||||
int i;
|
||||
int len;
|
||||
unsigned char *ptr;
|
||||
size_t i, len;
|
||||
|
||||
if (globals->get_soundmgr()->is_working() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char inner_buf[ INNER_SIZE ] ;
|
||||
unsigned char middle_buf[ MIDDLE_SIZE ] ;
|
||||
unsigned char outer_buf[ OUTER_SIZE ] ;
|
||||
const unsigned char* inner_buf = (const unsigned char*)malloc( INNER_SIZE );
|
||||
const unsigned char* middle_buf = (const unsigned char*)malloc(MIDDLE_SIZE);
|
||||
const unsigned char* outer_buf = (const unsigned char*)malloc( OUTER_SIZE );
|
||||
|
||||
// Make inner marker beacon sound
|
||||
len= (int)(INNER_DIT_LEN / 2.0 );
|
||||
|
@ -57,14 +52,14 @@ bool FGBeacon::init() {
|
|||
make_tone( inner_dit, INNER_FREQ, len, INNER_DIT_LEN,
|
||||
TRANSITION_BYTES );
|
||||
|
||||
ptr = inner_buf;
|
||||
ptr = (unsigned char*)inner_buf;
|
||||
for ( i = 0; i < 6; ++i ) {
|
||||
memcpy( ptr, inner_dit, INNER_DIT_LEN );
|
||||
ptr += INNER_DIT_LEN;
|
||||
}
|
||||
|
||||
try {
|
||||
inner = new SGSoundSample( inner_buf, INNER_SIZE, BYTES_PER_SECOND );
|
||||
inner = new SGSoundSample( &inner_buf, INNER_SIZE, BYTES_PER_SECOND );
|
||||
inner->set_reference_dist( 10.0 );
|
||||
inner->set_max_dist( 20.0 );
|
||||
|
||||
|
@ -79,12 +74,12 @@ bool FGBeacon::init() {
|
|||
make_tone( middle_dah, MIDDLE_FREQ, len, MIDDLE_DAH_LEN,
|
||||
TRANSITION_BYTES );
|
||||
|
||||
ptr = middle_buf;
|
||||
ptr = (unsigned char*)middle_buf;
|
||||
memcpy( ptr, middle_dit, MIDDLE_DIT_LEN );
|
||||
ptr += MIDDLE_DIT_LEN;
|
||||
memcpy( ptr, middle_dah, MIDDLE_DAH_LEN );
|
||||
|
||||
middle = new SGSoundSample( middle_buf, MIDDLE_SIZE, BYTES_PER_SECOND );
|
||||
middle = new SGSoundSample( &middle_buf, MIDDLE_SIZE, BYTES_PER_SECOND);
|
||||
middle->set_reference_dist( 10.0 );
|
||||
middle->set_max_dist( 20.0 );
|
||||
|
||||
|
@ -94,12 +89,12 @@ bool FGBeacon::init() {
|
|||
make_tone( outer_dah, OUTER_FREQ, len, OUTER_DAH_LEN,
|
||||
TRANSITION_BYTES );
|
||||
|
||||
ptr = outer_buf;
|
||||
ptr = (unsigned char*)outer_buf;
|
||||
memcpy( ptr, outer_dah, OUTER_DAH_LEN );
|
||||
ptr += OUTER_DAH_LEN;
|
||||
memcpy( ptr, outer_dah, OUTER_DAH_LEN );
|
||||
|
||||
outer = new SGSoundSample( outer_buf, OUTER_SIZE, BYTES_PER_SECOND);
|
||||
outer = new SGSoundSample( &outer_buf, OUTER_SIZE, BYTES_PER_SECOND );
|
||||
outer->set_reference_dist( 10.0 );
|
||||
outer->set_max_dist( 20.0 );
|
||||
} catch ( sg_io_exception &e ) {
|
||||
|
|
|
@ -31,43 +31,38 @@
|
|||
|
||||
#include "fg_fx.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/sound/xmlsound.hxx>
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <simgear/scene/model/placement.hxx>
|
||||
#include <Model/acmodel.hxx>
|
||||
#include <Main/viewer.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
#include <simgear/sound/xmlsound.hxx>
|
||||
|
||||
FGFX::FGFX () :
|
||||
last_visitor_pos(SGVec3d::zeros()),
|
||||
last_model_pos(SGVec3d::zeros()),
|
||||
last_pause( true ),
|
||||
last_volume( 0.0 ),
|
||||
_pause( fgGetNode("/sim/sound/pause") ),
|
||||
_volume( fgGetNode("/sim/sound/volume") )
|
||||
FGFX::FGFX ( SGSoundMgr *smgr, const string &refname ) :
|
||||
_enabled( fgGetNode("/sim/sound/effects/enabled", true) ),
|
||||
_volume( fgGetNode("/sim/sound/effects/volume", true) ),
|
||||
_avionics_enabled( fgGetNode("/sim/sound/avionics/enabled", true) ),
|
||||
_avionics_volume( fgGetNode("/sim/sound/avionics/volume", true) ),
|
||||
_avionics_external( fgGetNode("/sim/sound/avionics/external-view", true) ),
|
||||
_internal( fgGetNode("/sim/current-view/internal", true) )
|
||||
{
|
||||
SGSampleGroup::_smgr = smgr;
|
||||
SGSampleGroup::_refname = refname;
|
||||
SGSampleGroup::_smgr->add(this, refname);
|
||||
_avionics = _smgr->find("avionics", true);
|
||||
_avionics->tie_to_listener();
|
||||
}
|
||||
|
||||
|
||||
FGFX::~FGFX ()
|
||||
{
|
||||
unsigned int i;
|
||||
for ( i = 0; i < _sound.size(); i++ ) {
|
||||
for (unsigned int i = 0; i < _sound.size(); i++ ) {
|
||||
delete _sound[i];
|
||||
}
|
||||
_sound.clear();
|
||||
|
||||
while ( _samplequeue.size() > 0 ) {
|
||||
delete _samplequeue.front();
|
||||
_samplequeue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FGFX::init()
|
||||
{
|
||||
|
@ -99,8 +94,8 @@ FGFX::init()
|
|||
SGXmlSound *sound = new SGXmlSound();
|
||||
|
||||
try {
|
||||
sound->init(globals->get_props(), node->getChild(i),
|
||||
globals->get_soundmgr(), globals->get_fg_root());
|
||||
sound->init(globals->get_props(), node->getChild(i), this,
|
||||
_avionics, globals->get_fg_root());
|
||||
|
||||
_sound.push_back(sound);
|
||||
} catch ( sg_exception &e ) {
|
||||
|
@ -111,192 +106,40 @@ FGFX::init()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FGFX::reinit()
|
||||
{
|
||||
_sound.clear();
|
||||
init();
|
||||
_sound.clear();
|
||||
init();
|
||||
};
|
||||
|
||||
void
|
||||
FGFX::bind ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
FGFX::unbind ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
FGFX::update (double dt)
|
||||
{
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
bool active = _avionics_external->getBoolValue() ||
|
||||
_internal->getBoolValue();
|
||||
|
||||
if (smgr->is_working() == false) {
|
||||
return;
|
||||
}
|
||||
if ( active && _avionics_enabled->getBoolValue() )
|
||||
_avionics->resume(); // no-op if already in resumed state
|
||||
else
|
||||
_avionics->suspend();
|
||||
_avionics->set_volume( _avionics_volume->getFloatValue() );
|
||||
|
||||
// command sound manger
|
||||
bool pause = _pause->getBoolValue();
|
||||
if ( pause != last_pause ) {
|
||||
if ( pause ) {
|
||||
smgr->pause();
|
||||
} else {
|
||||
smgr->resume();
|
||||
}
|
||||
last_pause = pause;
|
||||
}
|
||||
if ( _enabled->getBoolValue() ) {
|
||||
set_volume( _volume->getDoubleValue() );
|
||||
resume();
|
||||
|
||||
// process mesage queue
|
||||
const string msgid = "Sequential Audio Message";
|
||||
bool is_playing = false;
|
||||
if ( smgr->exists( msgid ) ) {
|
||||
if ( smgr->is_playing( msgid ) ) {
|
||||
// still playing, do nothing
|
||||
is_playing = true;
|
||||
} else {
|
||||
// current message finished, stop and remove
|
||||
smgr->stop( msgid ); // removes source
|
||||
smgr->remove( msgid ); // removes buffer
|
||||
}
|
||||
}
|
||||
if ( !is_playing ) {
|
||||
// message queue idle, add next sound if we have one
|
||||
if ( _samplequeue.size() > 0 ) {
|
||||
smgr->add( _samplequeue.front(), msgid );
|
||||
_samplequeue.pop();
|
||||
smgr->play_once( msgid );
|
||||
}
|
||||
}
|
||||
|
||||
double volume = _volume->getDoubleValue();
|
||||
if ( volume != last_volume ) {
|
||||
smgr->set_volume( volume );
|
||||
last_volume = volume;
|
||||
}
|
||||
|
||||
if ( !pause ) {
|
||||
// update sound effects if not paused
|
||||
for ( unsigned int i = 0; i < _sound.size(); i++ ) {
|
||||
_sound[i]->update(dt);
|
||||
}
|
||||
|
||||
SGSampleGroup::update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FGFX::update_fx_late(double dt)
|
||||
{
|
||||
SGSoundMgr *smgr = globals->get_soundmgr();
|
||||
if (!smgr->is_working()) {
|
||||
return;
|
||||
}
|
||||
|
||||
smgr->update(dt);
|
||||
update_pos_and_orientation(smgr, dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* add a sound sample to the message queue which is played sequentially
|
||||
* in order.
|
||||
*/
|
||||
void
|
||||
FGFX::play_message( SGSoundSample *_sample )
|
||||
{
|
||||
_samplequeue.push( _sample );
|
||||
}
|
||||
void
|
||||
FGFX::play_message( const std::string& path, const std::string& fname, double volume )
|
||||
{
|
||||
if (globals->get_soundmgr()->is_working() == true) {
|
||||
SGSoundSample *sample;
|
||||
sample = new SGSoundSample( path.c_str(), fname.c_str() );
|
||||
sample->set_volume( volume );
|
||||
play_message( sample );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FGFX::update_pos_and_orientation(SGSoundMgr *smgr, double dt)
|
||||
{
|
||||
SGModelPlacement *model = globals->get_aircraft_model()->get3DModel();
|
||||
FGViewer *observer = globals->get_current_view();
|
||||
|
||||
// Right now we make a simplifying assumption that the primary
|
||||
// aircraft is the source of all sounds and that all sounds are
|
||||
// positioned in the aircraft base
|
||||
// EMH: Note: this is fine, to hear multiple aircraft simulataniously
|
||||
// we just have to trigger one instance of the FGFX class for every
|
||||
// aircraft
|
||||
|
||||
// get the orientation
|
||||
const SGQuatd view_or = observer->getViewOrientation();
|
||||
SGQuatd surf_or = SGQuatd::fromLonLat(observer->getPosition());
|
||||
|
||||
SGQuatd model_or = SGQuatd::fromYawPitchRollDeg(
|
||||
model->getHeadingDeg(),
|
||||
model->getPitchDeg(),
|
||||
model->getRollDeg());
|
||||
|
||||
// get the up and at vector in the aircraft base
|
||||
// (ok, the up vector is a down vector, but the coordinates
|
||||
// are finally calculated in a left hand system and openal
|
||||
// lives in a right hand system. Therefore we need to pass
|
||||
// the down vector to get correct stereo sound.)
|
||||
SGVec3d sgv_up
|
||||
= model_or.rotateBack(surf_or.rotateBack(view_or.rotate(SGVec3d(0,1,0))));
|
||||
SGVec3d sgv_at
|
||||
= model_or.rotateBack(surf_or.rotateBack(view_or.rotate(SGVec3d(0,0,1))));
|
||||
|
||||
// get the location data for the primary FDM (now hardcoded to ac model)...
|
||||
// EMH: to add multiple sound sources this should be replaced
|
||||
SGVec3d absolute_view_pos = SGVec3d::fromGeod(model->getPosition());
|
||||
|
||||
// calculate speed of visitor and model
|
||||
SGVec3d moved = last_visitor_pos - observer->get_view_pos();
|
||||
last_visitor_pos = observer->get_view_pos();
|
||||
SGVec3f listener_vel(model_or.rotateBack(surf_or.rotateBack(moved)));
|
||||
|
||||
moved = last_model_pos - absolute_view_pos;
|
||||
last_model_pos = absolute_view_pos;
|
||||
SGVec3f model_vel(model_or.rotateBack(surf_or.rotateBack(moved)));
|
||||
|
||||
if (dt > 0) {
|
||||
model_vel /= dt;
|
||||
listener_vel /= dt;
|
||||
}
|
||||
|
||||
// checking, if the listener pos has moved suddenly
|
||||
if (length(listener_vel) > 1000) {
|
||||
// check if the relative speed model vs listener has moved suddenly, too
|
||||
SGVec3f delta_vel = listener_vel - model_vel;
|
||||
if (length(delta_vel) > 1000)
|
||||
// a sane value
|
||||
smgr->set_listener_vel(model_vel.data());
|
||||
else
|
||||
smgr->set_listener_vel(listener_vel.data());
|
||||
} else {
|
||||
smgr->set_listener_vel( listener_vel.data());
|
||||
}
|
||||
|
||||
// set positional offset for sources
|
||||
SGVec3d dsource_pos_offset = observer->get_view_pos() - absolute_view_pos;
|
||||
dsource_pos_offset = model_or.rotateBack(surf_or.rotateBack(
|
||||
dsource_pos_offset
|
||||
));
|
||||
|
||||
smgr->set_source_pos_all( SGVec3f(dsource_pos_offset).data() );
|
||||
smgr->set_source_vel_all(model_vel.data() );
|
||||
|
||||
float orient[6];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
orient[i] = sgv_at[i];
|
||||
orient[i + 3] = sgv_up[i];
|
||||
}
|
||||
smgr->set_listener_orientation( orient );
|
||||
|
||||
// The listener is always positioned at the origin.
|
||||
smgr->set_listener_pos( SGVec3f::zeros().data() );
|
||||
else
|
||||
suspend();
|
||||
}
|
||||
|
||||
// end of fg_fx.cxx
|
||||
|
|
|
@ -26,73 +26,46 @@
|
|||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/sound/sample_group.hxx>
|
||||
|
||||
class SGXmlSound;
|
||||
class SGSoundSample;
|
||||
class SGSoundMgr;
|
||||
|
||||
/**
|
||||
* Generator for FlightGear sound effects.
|
||||
*
|
||||
* This module uses FGSoundMgr to generate sound effects based
|
||||
* on current flight conditions. The sound manager must be initialized
|
||||
* This module uses a FGSampleGroup class to generate sound effects based
|
||||
* on current flight conditions. The sound manager must be initialized
|
||||
* before this object is.
|
||||
*
|
||||
* Note: this module supports two separate sound mechanisms concurrently.
|
||||
*
|
||||
* 1. This module will load and play a set of sound effects defined in an
|
||||
* This module will load and play a set of sound effects defined in an
|
||||
* xml file and tie them to various property states.
|
||||
* 2. This modules also maintains a queue of 'message' audio files. These
|
||||
* are played sequentially with no overlap until the queue is finished.
|
||||
* This second mechanims is useful for things like tutorial messages or
|
||||
* background atc chatter.
|
||||
*/
|
||||
class FGFX : public SGSubsystem
|
||||
class FGFX : public SGSampleGroup
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
FGFX ();
|
||||
FGFX ( SGSoundMgr *smgr, const string &refname );
|
||||
virtual ~FGFX ();
|
||||
|
||||
virtual void init ();
|
||||
virtual void reinit ();
|
||||
virtual void bind ();
|
||||
virtual void unbind ();
|
||||
virtual void update (double dt);
|
||||
|
||||
/**
|
||||
* add a sound sample to the message queue which is played sequentially
|
||||
* in order.
|
||||
*/
|
||||
void play_message( SGSoundSample *_sample );
|
||||
void play_message( const std::string& path, const std::string& fname, double volume );
|
||||
|
||||
/**
|
||||
* Explicit late update hook, to avoid problems which occur if done during
|
||||
* normal SGSubsytem updating.
|
||||
*/
|
||||
void update_fx_late(double dt);
|
||||
|
||||
private:
|
||||
|
||||
void update_pos_and_orientation(SGSoundMgr *smgr, double dt);
|
||||
SGVec3d last_visitor_pos;
|
||||
SGVec3d last_model_pos;
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _avionics;
|
||||
std::vector<SGXmlSound *> _sound;
|
||||
std::queue<SGSoundSample *> _samplequeue;
|
||||
|
||||
bool last_pause;
|
||||
double last_volume;
|
||||
|
||||
SGPropertyNode_ptr _pause;
|
||||
SGPropertyNode_ptr _enabled;
|
||||
SGPropertyNode_ptr _volume;
|
||||
SGPropertyNode_ptr _avionics_enabled;
|
||||
SGPropertyNode_ptr _avionics_volume;
|
||||
SGPropertyNode_ptr _avionics_external;
|
||||
SGPropertyNode_ptr _internal;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -23,9 +23,6 @@
|
|||
|
||||
#include <simgear/constants.h>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "morse.hxx"
|
||||
|
||||
|
||||
|
@ -171,10 +168,6 @@ bool FGMorse::cust_init(const int freq ) {
|
|||
// make a SGSoundSample morse code transmission for the specified string
|
||||
SGSoundSample *FGMorse::make_ident( const string& id, const int freq ) {
|
||||
|
||||
if (globals->get_soundmgr()->is_working() == false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *idptr = (char *)id.c_str();
|
||||
|
||||
int length = 0;
|
||||
|
@ -226,10 +219,10 @@ SGSoundSample *FGMorse::make_ident( const string& id, const int freq ) {
|
|||
length += 2 * SPACE_SIZE;
|
||||
|
||||
// 2. Allocate space for the message
|
||||
unsigned char *buffer = new unsigned char[length];
|
||||
const unsigned char* buffer = (const unsigned char *)malloc(length);
|
||||
|
||||
// 3. Assemble the message;
|
||||
unsigned char *buf_ptr = buffer;
|
||||
unsigned char *buf_ptr = (unsigned char*)buffer;
|
||||
|
||||
for ( i = 0; i < (int)id.length(); ++i ) {
|
||||
if ( idptr[i] >= 'A' && idptr[i] <= 'Z' ) {
|
||||
|
@ -268,12 +261,9 @@ SGSoundSample *FGMorse::make_ident( const string& id, const int freq ) {
|
|||
buf_ptr += SPACE_SIZE;
|
||||
|
||||
// 4. create the simple sound and return
|
||||
SGSoundSample *sample = new SGSoundSample( buffer, length,
|
||||
SGSoundSample *sample = new SGSoundSample( &buffer, length,
|
||||
BYTES_PER_SECOND );
|
||||
|
||||
// clean up the buffer
|
||||
delete [] buffer;
|
||||
|
||||
sample->set_reference_dist( 10.0 );
|
||||
sample->set_max_dist( 20.0 );
|
||||
|
||||
|
|
108
src/Sound/sample_queue.cxx
Normal file
108
src/Sound/sample_queue.cxx
Normal file
|
@ -0,0 +1,108 @@
|
|||
// _samplequeue.cxx -- Sound effect management class implementation
|
||||
//
|
||||
// Started by David Megginson, October 2001
|
||||
// (Reuses some code from main.cxx, probably by Curtis Olson)
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable: 4786)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "sample_queue.hxx"
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
#include <simgear/sound/sample_openal.hxx>
|
||||
|
||||
FGSampleQueue::FGSampleQueue ( SGSoundMgr *smgr, const string &refname ) :
|
||||
last_enabled( true ),
|
||||
last_volume( 0.0 ),
|
||||
_enabled( fgGetNode("/sim/sound/chatter/enabled", true) ),
|
||||
_volume( fgGetNode("/sim/sound/chatter/volume", true) )
|
||||
{
|
||||
SGSampleGroup::_smgr = smgr;
|
||||
SGSampleGroup::_smgr->add(this, refname);
|
||||
SGSampleGroup::_refname = refname;
|
||||
_enabled->setBoolValue(true);
|
||||
_volume->setFloatValue(1.0);
|
||||
}
|
||||
|
||||
|
||||
FGSampleQueue::~FGSampleQueue ()
|
||||
{
|
||||
while ( _messages.size() > 0 ) {
|
||||
delete _messages.front();
|
||||
_messages.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FGSampleQueue::update (double dt)
|
||||
{
|
||||
// command sound manger
|
||||
bool new_enabled = _enabled->getBoolValue();
|
||||
if ( new_enabled != last_enabled ) {
|
||||
if ( new_enabled ) {
|
||||
resume();
|
||||
} else {
|
||||
suspend();
|
||||
}
|
||||
last_enabled = new_enabled;
|
||||
}
|
||||
|
||||
if ( new_enabled ) {
|
||||
double volume = _volume->getDoubleValue();
|
||||
if ( volume != last_volume ) {
|
||||
set_volume( volume );
|
||||
last_volume = volume;
|
||||
}
|
||||
|
||||
// process mesage queue
|
||||
const string msgid = "Sequential Audio Message";
|
||||
bool now_playing = false;
|
||||
if ( exists( msgid ) ) {
|
||||
now_playing = is_playing( msgid );
|
||||
if ( !now_playing ) {
|
||||
// current message finished, stop and remove
|
||||
stop( msgid ); // removes source
|
||||
remove( msgid ); // removes buffer
|
||||
}
|
||||
}
|
||||
|
||||
if ( !now_playing ) {
|
||||
// message queue idle, add next sound if we have one
|
||||
if ( _messages.size() > 0 ) {
|
||||
SGSampleGroup::add( _messages.front(), msgid );
|
||||
_messages.pop();
|
||||
play_once( msgid );
|
||||
}
|
||||
}
|
||||
|
||||
SGSampleGroup::update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
// end of _samplequeue.cxx
|
70
src/Sound/sample_queue.hxx
Normal file
70
src/Sound/sample_queue.hxx
Normal file
|
@ -0,0 +1,70 @@
|
|||
// sample_queue.hxx -- sample queue management class
|
||||
//
|
||||
// Started by David Megginson, October 2001
|
||||
// (Reuses some code from main.cxx, probably by Curtis Olson)
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifndef __FGSAMPLE_QUEUE_HXX
|
||||
#define __FGSAMPLE_QUEUE_HXX 1
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/sound/sample_group.hxx>
|
||||
|
||||
class SGSoundSample;
|
||||
|
||||
/**
|
||||
* FlightGear sample queue class
|
||||
*
|
||||
* This modules maintains a queue of 'message' audio files. These
|
||||
* are played sequentially with no overlap until the queue is finished.
|
||||
* This second mechanims is useful for things like tutorial messages or
|
||||
* background atc chatter.
|
||||
*/
|
||||
class FGSampleQueue : public SGSampleGroup
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
FGSampleQueue ( SGSoundMgr *smgr, const string &refname );
|
||||
virtual ~FGSampleQueue ();
|
||||
|
||||
virtual void update (double dt);
|
||||
|
||||
inline void add (SGSharedPtr<SGSoundSample> msg) { _messages.push(msg); }
|
||||
|
||||
private:
|
||||
|
||||
std::queue< SGSharedPtr<SGSoundSample> > _messages;
|
||||
|
||||
bool last_enabled;
|
||||
double last_volume;
|
||||
|
||||
SGPropertyNode_ptr _enabled;
|
||||
SGPropertyNode_ptr _volume;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// end of fg_fx.hxx
|
|
@ -35,7 +35,7 @@ FGVoiceMgr::FGVoiceMgr() :
|
|||
_host(fgGetString(VOICE "/host", "localhost")),
|
||||
_port(fgGetString(VOICE "/port", "1314")),
|
||||
_enabled(fgGetBool(VOICE "/enabled", false)),
|
||||
_pausedNode(fgGetNode("/sim/sound/pause", true))
|
||||
_pausedNode(fgGetNode("/sim/sound/working", true))
|
||||
{
|
||||
#if defined(ENABLE_THREADS)
|
||||
if (!_enabled)
|
||||
|
@ -83,7 +83,7 @@ void FGVoiceMgr::update(double)
|
|||
if (!_enabled)
|
||||
return;
|
||||
|
||||
_paused = _pausedNode->getBoolValue();
|
||||
_paused = !_pausedNode->getBoolValue();
|
||||
for (unsigned int i = 0; i < _voices.size(); i++) {
|
||||
_voices[i]->update();
|
||||
#if !defined(ENABLE_THREADS)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
bin_PROGRAMS = est-epsilon gl-info al-info
|
||||
bin_PROGRAMS = est-epsilon gl-info alcinfo
|
||||
|
||||
noinst_PROGRAMS = test-gethostname test-mktime test-text test-up test-env-map
|
||||
|
||||
|
@ -14,8 +14,8 @@ est_epsilon_LDADD = $(opengl_LIBS)
|
|||
gl_info_SOURCES = gl-info.c
|
||||
gl_info_LDADD = $(opengl_LIBS)
|
||||
|
||||
al_info_SOURCES = al-info.c
|
||||
al_info_LDADD = $(openal_LIBS)
|
||||
alcinfo_SOURCES = alcinfo.cxx
|
||||
alcinfo_LDADD = $(openal_LIBS)
|
||||
|
||||
test_env_map_SOURCES = test-env-map.cxx
|
||||
test_env_map_LDADD = $(opengl_LIBS)
|
||||
|
|
114
tests/al-info.c
114
tests/al-info.c
|
@ -1,114 +0,0 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <OpenAL/al.h>
|
||||
# include <OpenAL/alc.h>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
# include <AL/alc.h>
|
||||
# ifndef __CYGWIN__
|
||||
# include <AL/alext.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_VERSION_1_1
|
||||
# ifdef __APPLE__
|
||||
# include <OpenAL/altypes.h>
|
||||
# include <OpenAL/alctypes.h>
|
||||
#else
|
||||
# include <AL/altypes.h>
|
||||
# include <AL/alctypes.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define MAX_DATA 16
|
||||
|
||||
int main()
|
||||
{
|
||||
ALCint data[MAX_DATA];
|
||||
ALCdevice *device = NULL;
|
||||
ALCcontext *context = NULL;
|
||||
const ALchar *s;
|
||||
ALCenum error;
|
||||
|
||||
device = alcOpenDevice(NULL);
|
||||
if (device == NULL)
|
||||
{
|
||||
printf("No default audio device available.\n");
|
||||
return -1;
|
||||
}
|
||||
context = alcCreateContext(device, NULL);
|
||||
if (context == NULL)
|
||||
{
|
||||
printf("Could not create a valid context.\n");
|
||||
return -2;
|
||||
}
|
||||
alcMakeContextCurrent(context);
|
||||
|
||||
s = alGetString(AL_VENDOR);
|
||||
printf("AL_VENDOR = \"%s\"\n", s);
|
||||
|
||||
s = alGetString(AL_RENDERER);
|
||||
printf("AL_RENDERER = \"%s\"\n", s);
|
||||
|
||||
s = alGetString(AL_VERSION);
|
||||
printf("AL_VERSION = \"%s\"\n", s);
|
||||
|
||||
s = alGetString(AL_EXTENSIONS);
|
||||
printf("AL_EXTENSIONS = \"%s\"\n", s);
|
||||
|
||||
alcGetError(device);
|
||||
|
||||
printf("\n");
|
||||
if (alcIsExtensionPresent(NULL, (const ALchar *)"ALC_ENUMERATION_EXT") == AL_TRUE)
|
||||
{
|
||||
s = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||
printf("ALC_DEVICE_SPECIFIER = \"%s\"\n", s);
|
||||
}
|
||||
|
||||
alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, data);
|
||||
printf("ALC_MAJOR_VERSION = %i\n", *data);
|
||||
alcGetIntegerv(device, ALC_MINOR_VERSION, 1, data);
|
||||
printf("ALC_MINOR_VERSION = %i\n", *data);
|
||||
|
||||
s = alcGetString(device, ALC_EXTENSIONS);
|
||||
printf("ALC_EXTENSIONS = \"%s\"\n", s);
|
||||
|
||||
if ((error = alcGetError(device)))
|
||||
{
|
||||
printf("Error #%i occured\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
s = alcGetString(device, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||
printf("ALC_DEFAULT_DEVICE_SPECIFIER = \"%s\"\n", s);
|
||||
|
||||
if ((error = alcGetError(device)))
|
||||
{
|
||||
printf("Error #%i occured\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
#if 0
|
||||
alcGetIntegerv(device, ALC_ATTRIBUTES_SIZE, 1, &i);
|
||||
printf("ALC attributes(%i): ", i);
|
||||
|
||||
alcGetIntegerv(device, ALC_ALL_ATTRIBUTES, i, data);
|
||||
for (j=0; j<i; j++)
|
||||
{
|
||||
printf("%i ", data[j]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
if ((error = alcGetError(device)))
|
||||
{
|
||||
printf("Error #%i occured\n", error);
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
alcCloseDevice(device);
|
||||
|
||||
return 0;
|
||||
}
|
341
tests/alcinfo.cxx
Normal file
341
tests/alcinfo.cxx
Normal file
|
@ -0,0 +1,341 @@
|
|||
/* -*- mode: C; tab-width:8; c-basic-offset:8 -*-
|
||||
* vi:set ts=8:
|
||||
*
|
||||
* alcinfo.x
|
||||
*
|
||||
* alcinfo display info about a ALC extension and OpenAL renderer
|
||||
*
|
||||
* This file is in the Public Domain and comes with no warranty.
|
||||
* Erik Hofman <erik@ehofman.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <OpenAL/al.h>
|
||||
# include <OpenAL/alc.h>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
# include <AL/alc.h>
|
||||
# include <AL/alext.h>
|
||||
#endif
|
||||
|
||||
#ifndef AL_VERSION_1_1
|
||||
# ifdef __APPLE__
|
||||
# include <OpenAL/altypes.h>
|
||||
# include <OpenAL/alctypes.h>
|
||||
#else
|
||||
# include <AL/altypes.h>
|
||||
# include <AL/alctypes.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
# define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
||||
#endif
|
||||
|
||||
#define MAX_DATA 16
|
||||
static const int indentation = 4;
|
||||
static const int maxmimumWidth = 79;
|
||||
|
||||
void printExtensions (const char *, char, const char *);
|
||||
void displayDevices(const char *, const char *);
|
||||
char *getDeviceName(int, char **);
|
||||
void testForError(void *, const string&);
|
||||
void testForALCError(ALCdevice *);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ALCint data[MAX_DATA];
|
||||
ALCdevice *device = NULL;
|
||||
ALCcontext *context = NULL;
|
||||
ALenum error;
|
||||
char *s;
|
||||
|
||||
if (alcIsExtensionPresent(NULL, "ALC_enumeration_EXT") == AL_TRUE)
|
||||
{
|
||||
if (alcIsExtensionPresent(NULL, "ALC_enumerate_all_EXT") == AL_FALSE)
|
||||
s = (char *)alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||
else
|
||||
s = (char *)alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
||||
displayDevices("output", s);
|
||||
|
||||
s = (char *)alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
|
||||
displayDevices("input", s);
|
||||
}
|
||||
|
||||
s = getDeviceName(argc, argv);
|
||||
device = alcOpenDevice(s);
|
||||
testForError(device, "Audio device not available.");
|
||||
|
||||
context = alcCreateContext(device, NULL);
|
||||
testForError(context, "Unable to create a valid context.");
|
||||
|
||||
alcMakeContextCurrent(context);
|
||||
testForALCError(device);
|
||||
|
||||
s = (char *)alcGetString(device, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||
printf("default output device: %s\n", s);
|
||||
testForALCError(device);
|
||||
|
||||
error = alcIsExtensionPresent(device, "ALC_EXT_capture");
|
||||
if (error)
|
||||
{
|
||||
s = (char *)alcGetString(device, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
||||
printf("default input device: %s\n", s);
|
||||
testForALCError(device);
|
||||
}
|
||||
printf("capture support: %s\n", (error) ? "yes" : "no");
|
||||
|
||||
alcGetIntegerv(device, ALC_FREQUENCY, 1, data);
|
||||
printf("mixer frequency: %u hz\n", data[0]);
|
||||
testForALCError(device);
|
||||
|
||||
alcGetIntegerv(device, ALC_REFRESH, 1, data+1);
|
||||
printf("refresh rate : %u hz\n", data[0]/data[1]);
|
||||
testForALCError(device);
|
||||
|
||||
data[0] = 0;
|
||||
alcGetIntegerv(device, ALC_MONO_SOURCES, 1, data);
|
||||
error = alcGetError(device);
|
||||
if (error == AL_NONE) {
|
||||
printf("supported sources; mono: %u, ", data[0]);
|
||||
|
||||
data[0] = 0;
|
||||
alcGetIntegerv(device, ALC_STEREO_SOURCES, 1, data);
|
||||
printf("stereo: %u\n", data[0]);
|
||||
testForALCError(device);
|
||||
}
|
||||
|
||||
printf("ALC version: ");
|
||||
alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, data);
|
||||
printf("%i.", *data);
|
||||
alcGetIntegerv(device, ALC_MINOR_VERSION, 1, data);
|
||||
printf("%i\n", *data);
|
||||
testForALCError(device);
|
||||
|
||||
s = (char *)alcGetString(device, ALC_EXTENSIONS);
|
||||
printExtensions ("ALC extensions", ' ', s);
|
||||
testForALCError(device);
|
||||
|
||||
s = (char *)alGetString(AL_VENDOR);
|
||||
error = alGetError();
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
printf("Error #%x: %s\n", error, alGetString(error));
|
||||
else
|
||||
printf("OpenAL vendor string: %s\n", s);
|
||||
|
||||
s = (char *)alGetString(AL_RENDERER);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
printf("Error #%x: %s\n", error, alGetString(error));
|
||||
else
|
||||
printf("OpenAL renderer string: %s\n", s);
|
||||
|
||||
s = (char *)alGetString(AL_VERSION);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
printf("Error #%x: %s\n", error, alGetString(error));
|
||||
else if (!s)
|
||||
printf("Quering AL_VERSION returned NULL pointer!\n");
|
||||
else
|
||||
printf("OpenAL version string: %s\n", s);
|
||||
|
||||
s = (char *)alGetString(AL_EXTENSIONS);
|
||||
printExtensions ("OpenAL extensions", ' ', s);
|
||||
testForALCError(device);
|
||||
|
||||
/* alut testing mechanism */
|
||||
context = alcGetCurrentContext();
|
||||
if (context == NULL)
|
||||
{
|
||||
printf("Error: no current context\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alGetError () != AL_NO_ERROR)
|
||||
{
|
||||
printf("Alert: AL error on entry\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alcGetError (alcGetContextsDevice (context)) != ALC_NO_ERROR)
|
||||
{
|
||||
printf("Alert: ALC error on entry\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
/* end of alut test */
|
||||
|
||||
|
||||
if (alcMakeContextCurrent(NULL) == 0)
|
||||
printf("alcMakeContextCurrent failed.\n");
|
||||
|
||||
device = alcGetContextsDevice(context);
|
||||
alcDestroyContext(context);
|
||||
testForALCError(device);
|
||||
|
||||
if (alcCloseDevice(device) == 0)
|
||||
printf("alcCloseDevice failed.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
printChar (int c, int *width)
|
||||
{
|
||||
putchar (c);
|
||||
*width = (c == '\n') ? 0 : (*width + 1);
|
||||
}
|
||||
|
||||
void
|
||||
indent (int *width)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < indentation; i++)
|
||||
{
|
||||
printChar (' ', width);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printExtensions (const char *header, char separator, const char *extensions)
|
||||
{
|
||||
int width = 0, start = 0, end = 0;
|
||||
|
||||
printf ("%s:\n", header);
|
||||
if (extensions == NULL || extensions[0] == '\0')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
indent (&width);
|
||||
while (1)
|
||||
{
|
||||
if (extensions[end] == separator || extensions[end] == '\0')
|
||||
{
|
||||
if (width + end - start + 2 > maxmimumWidth)
|
||||
{
|
||||
printChar ('\n', &width);
|
||||
indent (&width);
|
||||
}
|
||||
while (start < end)
|
||||
{
|
||||
printChar (extensions[start], &width);
|
||||
start++;
|
||||
}
|
||||
if (extensions[end] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
start++;
|
||||
end++;
|
||||
if (extensions[end] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
printChar (',', &width);
|
||||
printChar (' ', &width);
|
||||
}
|
||||
end++;
|
||||
}
|
||||
printChar ('\n', &width);
|
||||
}
|
||||
|
||||
char *
|
||||
getCommandLineOption(int argc, char **argv, const string& option)
|
||||
{
|
||||
int slen = option.size();
|
||||
char *rv = 0;
|
||||
int i;
|
||||
|
||||
for (i=0; i<argc; i++)
|
||||
{
|
||||
if (strncmp(argv[i], option.c_str(), slen) == 0)
|
||||
{
|
||||
i++;
|
||||
if (i<argc) rv = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
char *
|
||||
getDeviceName(int argc, char **argv)
|
||||
{
|
||||
static char devname[255];
|
||||
int len = 255;
|
||||
char *s;
|
||||
|
||||
s = getCommandLineOption(argc, argv, "-d");
|
||||
if (s)
|
||||
{
|
||||
strncpy((char *)&devname, s, len);
|
||||
len -= strlen(s);
|
||||
|
||||
s = getCommandLineOption(argc, argv, "-r");
|
||||
if (s)
|
||||
{
|
||||
strncat((char *)&devname, " on ", len);
|
||||
len -= 4;
|
||||
|
||||
strncat((char *)&devname, s, len);
|
||||
}
|
||||
s = (char *)&devname;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
displayDevices(const char *type, const char *list)
|
||||
{
|
||||
ALCchar *ptr, *nptr;
|
||||
|
||||
ptr = (ALCchar *)list;
|
||||
printf("list of all available %s devices:\n", type);
|
||||
if (!list)
|
||||
{
|
||||
printf("none\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
nptr = ptr;
|
||||
while (*(nptr += strlen(ptr)+1) != 0)
|
||||
{
|
||||
printf(" %s\n", ptr);
|
||||
ptr = nptr;
|
||||
}
|
||||
printf(" %s\n", ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testForError(void *p, const string& s)
|
||||
{
|
||||
if (p == NULL)
|
||||
{
|
||||
printf("\nError: %s\n\n", s.c_str());
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testForALCError(ALCdevice *device)
|
||||
{
|
||||
ALenum error;
|
||||
error = alcGetError(device);
|
||||
if (error != ALC_NO_ERROR)
|
||||
printf("\nALC Error %x occurred: %s\n", error, alcGetString(device, error));
|
||||
}
|
Loading…
Reference in a new issue