Make voiceplayer independent
- former mk-viii voiceplayer is now an independent FGVoicePlayer - voiceplayer still sits in mk-viii module for now - add option to configure location/prefix of voice samples - add pause/resume to voiceplayer
This commit is contained in:
parent
f938a23d7c
commit
dd7c88b8cb
2 changed files with 450 additions and 379 deletions
|
@ -210,17 +210,6 @@ MK_VIII::PropertiesHandler::init ()
|
|||
mk_node(vs) = fgGetNode("/velocities/vertical-speed-fps", true);
|
||||
}
|
||||
|
||||
void
|
||||
MK_VIII::PropertiesHandler::unbind ()
|
||||
{
|
||||
vector<SGPropertyNode_ptr>::iterator iter;
|
||||
|
||||
for (iter = tied_properties.begin(); iter != tied_properties.end(); iter++)
|
||||
(*iter)->untie();
|
||||
|
||||
tied_properties.clear();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PowerHandler ///////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1858,7 +1847,8 @@ MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
|
|||
bool *input,
|
||||
bool *feed)
|
||||
{
|
||||
mk->properties_handler.tie(node, (string("inputs/discretes/") + name).c_str(), RawValueMethodsData<MK_VIII::IOHandler, bool, bool *>(*this, input, &MK_VIII::IOHandler::get_discrete_input, &MK_VIII::IOHandler::set_discrete_input));
|
||||
mk->properties_handler.tie(node, (string("inputs/discretes/") + name).c_str(),
|
||||
FGVoicePlayer::RawValueMethodsData<MK_VIII::IOHandler, bool, bool *>(*this, input, &MK_VIII::IOHandler::get_discrete_input, &MK_VIII::IOHandler::set_discrete_input));
|
||||
if (feed)
|
||||
mk->properties_handler.tie(node, (string("input-feeders/discretes/") + name).c_str(), SGRawValuePointer<bool>(feed));
|
||||
}
|
||||
|
@ -2144,11 +2134,22 @@ MK_VIII::IOHandler::set_present_status (bool value)
|
|||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// VoicePlayer ////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer //////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::Speaker::bind (SGPropertyNode *node)
|
||||
FGVoicePlayer::PropertiesHandler::unbind ()
|
||||
{
|
||||
vector<SGPropertyNode_ptr>::iterator iter;
|
||||
|
||||
for (iter = tied_properties.begin(); iter != tied_properties.end(); iter++)
|
||||
(*iter)->untie();
|
||||
|
||||
tied_properties.clear();
|
||||
}
|
||||
|
||||
void
|
||||
FGVoicePlayer::Speaker::bind (SGPropertyNode *node)
|
||||
{
|
||||
// uses xmlsound property names
|
||||
tie(node, "volume", &volume);
|
||||
|
@ -2156,7 +2157,7 @@ MK_VIII::VoicePlayer::Speaker::bind (SGPropertyNode *node)
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::Speaker::update_configuration ()
|
||||
FGVoicePlayer::Speaker::update_configuration ()
|
||||
{
|
||||
map< string, SGSharedPtr<SGSoundSample> >::iterator iter;
|
||||
for (iter = player->samples.begin(); iter != player->samples.end(); iter++)
|
||||
|
@ -2170,7 +2171,7 @@ MK_VIII::VoicePlayer::Speaker::update_configuration ()
|
|||
player->voice->volume_changed();
|
||||
}
|
||||
|
||||
MK_VIII::VoicePlayer::Voice::~Voice ()
|
||||
FGVoicePlayer::Voice::~Voice ()
|
||||
{
|
||||
for (iter = elements.begin(); iter != elements.end(); iter++)
|
||||
delete *iter; // we owned the element
|
||||
|
@ -2178,7 +2179,7 @@ MK_VIII::VoicePlayer::Voice::~Voice ()
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::Voice::play ()
|
||||
FGVoicePlayer::Voice::play ()
|
||||
{
|
||||
iter = elements.begin();
|
||||
element = *iter;
|
||||
|
@ -2187,7 +2188,7 @@ MK_VIII::VoicePlayer::Voice::play ()
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::Voice::stop (bool now)
|
||||
FGVoicePlayer::Voice::stop (bool now)
|
||||
{
|
||||
if (element)
|
||||
{
|
||||
|
@ -2202,21 +2203,21 @@ MK_VIII::VoicePlayer::Voice::stop (bool now)
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::Voice::set_volume (float _volume)
|
||||
FGVoicePlayer::Voice::set_volume (float _volume)
|
||||
{
|
||||
volume = _volume;
|
||||
volume_changed();
|
||||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::Voice::volume_changed ()
|
||||
FGVoicePlayer::Voice::volume_changed ()
|
||||
{
|
||||
if (element)
|
||||
element->set_volume(get_volume());
|
||||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::Voice::update ()
|
||||
FGVoicePlayer::Voice::update ()
|
||||
{
|
||||
if (element)
|
||||
{
|
||||
|
@ -2233,7 +2234,7 @@ MK_VIII::VoicePlayer::Voice::update ()
|
|||
}
|
||||
}
|
||||
|
||||
MK_VIII::VoicePlayer::~VoicePlayer ()
|
||||
FGVoicePlayer::~FGVoicePlayer ()
|
||||
{
|
||||
vector<Voice *>::iterator iter1;
|
||||
for (iter1 = _voices.begin(); iter1 != _voices.end(); iter1++)
|
||||
|
@ -2243,85 +2244,80 @@ MK_VIII::VoicePlayer::~VoicePlayer ()
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::init ()
|
||||
FGVoicePlayer::bind (SGPropertyNode *node, const char* default_dir_prefix)
|
||||
{
|
||||
#define STDPAUSE 0.75 // [SPEC] 6.4.4: "the standard 0.75 second delay"
|
||||
dir_prefix = node->getStringValue("voice/file-prefix", default_dir_prefix);
|
||||
speaker.bind(node);
|
||||
}
|
||||
|
||||
void
|
||||
FGVoicePlayer::init ()
|
||||
{
|
||||
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");
|
||||
make_voice(&voices.bank_angle_bank_angle_3, "bank-angle", "bank-angle", 3.0);
|
||||
make_voice(&voices.bank_angle_inop, "bank-angle-inop");
|
||||
make_voice(&voices.bank_angle_pause_bank_angle, "bank-angle", STDPAUSE, "bank-angle");
|
||||
make_voice(&voices.bank_angle_pause_bank_angle_3, "bank-angle", STDPAUSE, "bank-angle", 3.0);
|
||||
make_voice(&voices.callouts_inop, "callouts-inop");
|
||||
make_voice(&voices.configuration_type_invalid, "configuration-type-invalid");
|
||||
make_voice(&voices.dont_sink, "dont-sink");
|
||||
make_voice(&voices.dont_sink_pause_dont_sink, "dont-sink", STDPAUSE, "dont-sink");
|
||||
make_voice(&voices.five_hundred_above, "500-above");
|
||||
make_voice(&voices.glideslope, "glideslope");
|
||||
make_voice(&voices.glideslope_inop, "glideslope-inop");
|
||||
make_voice(&voices.gpws_inop, "gpws-inop");
|
||||
make_voice(&voices.hard_glideslope, "glideslope", "glideslope", 3.0);
|
||||
make_voice(&voices.minimums, "minimums");
|
||||
make_voice(&voices.minimums_minimums, "minimums", "minimums");
|
||||
make_voice(&voices.pull_up, "pull-up");
|
||||
make_voice(&voices.sink_rate, "sink-rate");
|
||||
make_voice(&voices.sink_rate_pause_sink_rate, "sink-rate", STDPAUSE, "sink-rate");
|
||||
make_voice(&voices.soft_glideslope, new Voice::SampleElement(get_sample("glideslope"), modify_amplitude(1.0, -6)));
|
||||
make_voice(&voices.terrain, "terrain");
|
||||
make_voice(&voices.terrain_pause_terrain, "terrain", STDPAUSE, "terrain");
|
||||
make_voice(&voices.too_low_flaps, "too-low-flaps");
|
||||
make_voice(&voices.too_low_gear, "too-low-gear");
|
||||
make_voice(&voices.too_low_terrain, "too-low-terrain");
|
||||
|
||||
for (unsigned i = 0; i < n_altitude_callouts; i++)
|
||||
{
|
||||
std::ostringstream name;
|
||||
name << "altitude-" << mk->mode6_handler.altitude_callout_definitions[i];
|
||||
make_voice(&voices.altitude_callouts[i], name.str().c_str());
|
||||
}
|
||||
|
||||
speaker.update_configuration();
|
||||
}
|
||||
|
||||
SGSoundSample *
|
||||
MK_VIII::VoicePlayer::get_sample (const char *name)
|
||||
void
|
||||
FGVoicePlayer::pause()
|
||||
{
|
||||
std::ostringstream refname;
|
||||
refname << mk->name << "[" << mk->num << "]" << "/" << name;
|
||||
if (paused)
|
||||
return;
|
||||
|
||||
SGSoundSample *sample = _sgr->find(refname.str());
|
||||
paused = true;
|
||||
if (voice)
|
||||
{
|
||||
voice->stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FGVoicePlayer::resume()
|
||||
{
|
||||
if (!paused)
|
||||
return;
|
||||
paused = false;
|
||||
if (voice)
|
||||
{
|
||||
voice->play();
|
||||
}
|
||||
}
|
||||
|
||||
SGSoundSample *
|
||||
FGVoicePlayer::get_sample (const char *name)
|
||||
{
|
||||
string refname;
|
||||
refname = dev_name + "/" + dir_prefix + name;
|
||||
|
||||
SGSoundSample *sample = _sgr->find(refname);
|
||||
if (! sample)
|
||||
{
|
||||
string filename = "Sounds/mk-viii/" + string(name) + ".wav";
|
||||
string filename = dir_prefix + string(name) + ".wav";
|
||||
try
|
||||
{
|
||||
sample = new SGSoundSample(filename.c_str(), SGPath());
|
||||
}
|
||||
catch (const sg_exception &e)
|
||||
{
|
||||
SG_LOG(SG_INSTR, SG_ALERT, "Error loading MK VIII sound sample \"" + filename + "\": " + e.getFormattedMessage());
|
||||
SG_LOG(SG_INSTR, SG_ALERT, "Error loading sound sample \"" + filename + "\": " + e.getFormattedMessage());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
_sgr->add(sample, refname.str());
|
||||
samples[refname.str()] = sample;
|
||||
_sgr->add(sample, refname);
|
||||
samples[refname] = sample;
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::play (Voice *_voice, unsigned int flags)
|
||||
FGVoicePlayer::play (Voice *_voice, unsigned int flags)
|
||||
{
|
||||
if (!_voice)
|
||||
return;
|
||||
if (test_bits(flags, PLAY_NOW) || ! voice || voice->element->silence)
|
||||
if (test_bits(flags, PLAY_NOW) || ! voice ||
|
||||
(voice->element && voice->element->silence))
|
||||
{
|
||||
if (voice)
|
||||
voice->stop(true);
|
||||
|
@ -2332,7 +2328,8 @@ MK_VIII::VoicePlayer::play (Voice *_voice, unsigned int flags)
|
|||
next_voice = NULL;
|
||||
next_looped = false;
|
||||
|
||||
voice->play();
|
||||
if (!paused)
|
||||
voice->play();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2342,7 +2339,7 @@ MK_VIII::VoicePlayer::play (Voice *_voice, unsigned int flags)
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::stop (unsigned int flags)
|
||||
FGVoicePlayer::stop (unsigned int flags)
|
||||
{
|
||||
if (voice)
|
||||
{
|
||||
|
@ -2356,7 +2353,7 @@ MK_VIII::VoicePlayer::stop (unsigned int flags)
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::set_volume (float _volume)
|
||||
FGVoicePlayer::set_volume (float _volume)
|
||||
{
|
||||
volume = _volume;
|
||||
if (voice)
|
||||
|
@ -2364,7 +2361,7 @@ MK_VIII::VoicePlayer::set_volume (float _volume)
|
|||
}
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::update ()
|
||||
FGVoicePlayer::update ()
|
||||
{
|
||||
if (voice)
|
||||
{
|
||||
|
@ -2396,6 +2393,53 @@ MK_VIII::VoicePlayer::update ()
|
|||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer ///////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
MK_VIII::VoicePlayer::init ()
|
||||
{
|
||||
FGVoicePlayer::init();
|
||||
|
||||
#define STDPAUSE 0.75 // [SPEC] 6.4.4: "the standard 0.75 second delay"
|
||||
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");
|
||||
make_voice(&voices.bank_angle_bank_angle_3, "bank-angle", "bank-angle", 3.0);
|
||||
make_voice(&voices.bank_angle_inop, "bank-angle-inop");
|
||||
make_voice(&voices.bank_angle_pause_bank_angle, "bank-angle", STDPAUSE, "bank-angle");
|
||||
make_voice(&voices.bank_angle_pause_bank_angle_3, "bank-angle", STDPAUSE, "bank-angle", 3.0);
|
||||
make_voice(&voices.callouts_inop, "callouts-inop");
|
||||
make_voice(&voices.configuration_type_invalid, "configuration-type-invalid");
|
||||
make_voice(&voices.dont_sink, "dont-sink");
|
||||
make_voice(&voices.dont_sink_pause_dont_sink, "dont-sink", STDPAUSE, "dont-sink");
|
||||
make_voice(&voices.five_hundred_above, "500-above");
|
||||
make_voice(&voices.glideslope, "glideslope");
|
||||
make_voice(&voices.glideslope_inop, "glideslope-inop");
|
||||
make_voice(&voices.gpws_inop, "gpws-inop");
|
||||
make_voice(&voices.hard_glideslope, "glideslope", "glideslope", 3.0);
|
||||
make_voice(&voices.minimums, "minimums");
|
||||
make_voice(&voices.minimums_minimums, "minimums", "minimums");
|
||||
make_voice(&voices.pull_up, "pull-up");
|
||||
make_voice(&voices.sink_rate, "sink-rate");
|
||||
make_voice(&voices.sink_rate_pause_sink_rate, "sink-rate", STDPAUSE, "sink-rate");
|
||||
make_voice(&voices.soft_glideslope, new Voice::SampleElement(get_sample("glideslope"), modify_amplitude(1.0, -6)));
|
||||
make_voice(&voices.terrain, "terrain");
|
||||
make_voice(&voices.terrain_pause_terrain, "terrain", STDPAUSE, "terrain");
|
||||
make_voice(&voices.too_low_flaps, "too-low-flaps");
|
||||
make_voice(&voices.too_low_gear, "too-low-gear");
|
||||
make_voice(&voices.too_low_terrain, "too-low-terrain");
|
||||
|
||||
for (unsigned i = 0; i < n_altitude_callouts; i++)
|
||||
{
|
||||
std::ostringstream name;
|
||||
name << "altitude-" << MK_VIII::Mode6Handler::altitude_callout_definitions[i];
|
||||
make_voice(&voices.altitude_callouts[i], name.str().c_str());
|
||||
}
|
||||
speaker.update_configuration();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SelfTestHandler ////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -4949,7 +4993,7 @@ MK_VIII::bind ()
|
|||
configuration_module.bind(node);
|
||||
power_handler.bind(node);
|
||||
io_handler.bind(node);
|
||||
voice_player.bind(node);
|
||||
voice_player.bind(node, "Sounds/mk-viii/");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -47,6 +47,295 @@ class SGSampleGroup;
|
|||
# pragma warning( disable: 4355 )
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer /////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class FGVoicePlayer
|
||||
{
|
||||
public:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// MK::RawValueMethodsData /////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class C, class VT, class DT>
|
||||
class RawValueMethodsData : public SGRawValue<VT>
|
||||
{
|
||||
public:
|
||||
typedef VT (C::*getter_t) (DT) const;
|
||||
typedef void (C::*setter_t) (DT, VT);
|
||||
|
||||
RawValueMethodsData (C &obj, DT data, getter_t getter = 0, setter_t setter = 0)
|
||||
: _obj(obj), _data(data), _getter(getter), _setter(setter) {}
|
||||
|
||||
virtual VT getValue () const
|
||||
{
|
||||
if (_getter)
|
||||
return (_obj.*_getter)(_data);
|
||||
else
|
||||
return SGRawValue<VT>::DefaultValue();
|
||||
}
|
||||
virtual bool setValue (VT value)
|
||||
{
|
||||
if (_setter)
|
||||
{
|
||||
(_obj.*_setter)(_data, value);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
virtual SGRawValue<VT> *clone () const
|
||||
{
|
||||
return new RawValueMethodsData<C,VT,DT>(_obj, _data, _getter, _setter);
|
||||
}
|
||||
|
||||
private:
|
||||
C &_obj;
|
||||
DT _data;
|
||||
getter_t _getter;
|
||||
setter_t _setter;
|
||||
};
|
||||
|
||||
class PropertiesHandler
|
||||
{
|
||||
public:
|
||||
vector<SGPropertyNode_ptr> tied_properties;
|
||||
|
||||
template <class T>
|
||||
inline void tie (SGPropertyNode *node, const SGRawValue<T> &raw_value)
|
||||
{
|
||||
node->tie(raw_value);
|
||||
tied_properties.push_back(node);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void tie (SGPropertyNode *node,
|
||||
const char *relative_path,
|
||||
const SGRawValue<T> &raw_value)
|
||||
{
|
||||
tie(node->getNode(relative_path, true), raw_value);
|
||||
}
|
||||
|
||||
PropertiesHandler() {};
|
||||
|
||||
void unbind ();
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer::Voice ////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Voice
|
||||
{
|
||||
public:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer::Voice::Element ////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Element
|
||||
{
|
||||
public:
|
||||
bool silence;
|
||||
|
||||
virtual inline void play (float volume) {}
|
||||
virtual inline void stop () {}
|
||||
virtual bool is_playing () = 0;
|
||||
virtual inline void set_volume (float volume) {}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer::Voice::SampleElement ///////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SampleElement : public Element
|
||||
{
|
||||
SGSharedPtr<SGSoundSample> _sample;
|
||||
float _volume;
|
||||
|
||||
public:
|
||||
inline SampleElement (SGSharedPtr<SGSoundSample> sample, float volume = 1.0)
|
||||
: _sample(sample), _volume(volume) { silence = false; }
|
||||
|
||||
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 (float volume) { if (_sample) _sample->set_volume(volume * _volume); }
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer::Voice::SilenceElement //////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SilenceElement : public Element
|
||||
{
|
||||
double _duration;
|
||||
double start_time;
|
||||
|
||||
public:
|
||||
inline SilenceElement (double duration)
|
||||
: _duration(duration) { silence = true; }
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer::Voice (continued) //////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Element *element;
|
||||
|
||||
inline Voice (FGVoicePlayer *_player)
|
||||
: element(NULL), player(_player), volume(1.0) {}
|
||||
|
||||
~Voice ();
|
||||
|
||||
inline void append (Element *_element) { elements.push_back(_element); }
|
||||
|
||||
void play ();
|
||||
void stop (bool now);
|
||||
void set_volume (float _volume);
|
||||
void volume_changed ();
|
||||
void update ();
|
||||
|
||||
private:
|
||||
FGVoicePlayer *player;
|
||||
|
||||
float volume;
|
||||
|
||||
vector<Element *> elements;
|
||||
vector<Element *>::iterator iter;
|
||||
|
||||
inline float get_volume () const { return player->volume * player->speaker.volume * volume; }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer (continued) ///////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct
|
||||
{
|
||||
float volume;
|
||||
} conf;
|
||||
|
||||
float volume;
|
||||
|
||||
Voice *voice;
|
||||
Voice *next_voice;
|
||||
bool paused;
|
||||
string dev_name;
|
||||
string dir_prefix;
|
||||
|
||||
inline FGVoicePlayer (PropertiesHandler* properties_handler, string _dev_name)
|
||||
: volume(1.0), voice(NULL), next_voice(NULL), paused(false),
|
||||
dev_name(_dev_name), dir_prefix(""),
|
||||
speaker(this,properties_handler) {}
|
||||
|
||||
~FGVoicePlayer ();
|
||||
|
||||
void init ();
|
||||
void pause();
|
||||
void resume();
|
||||
bool is_playing() { return (voice!=NULL);}
|
||||
|
||||
enum
|
||||
{
|
||||
PLAY_NOW = 1 << 0,
|
||||
PLAY_LOOPED = 1 << 1
|
||||
};
|
||||
void play (Voice *_voice, unsigned int flags = 0);
|
||||
|
||||
enum
|
||||
{
|
||||
STOP_NOW = 1 << 0
|
||||
};
|
||||
void stop (unsigned int flags = 0);
|
||||
|
||||
void set_volume (float _volume);
|
||||
void update ();
|
||||
|
||||
void bind (SGPropertyNode *node, const char* default_dir_prefix);
|
||||
|
||||
public:
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer::Speaker //////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Speaker
|
||||
{
|
||||
FGVoicePlayer *player;
|
||||
PropertiesHandler* properties_handler;
|
||||
|
||||
double pitch;
|
||||
|
||||
template <class T>
|
||||
inline void tie (SGPropertyNode *node, const char *name, T *ptr)
|
||||
{
|
||||
properties_handler->tie
|
||||
(node, (string("speaker/") + name).c_str(),
|
||||
RawValueMethodsData<FGVoicePlayer::Speaker,T,T*>
|
||||
(*this, ptr,
|
||||
&FGVoicePlayer::Speaker::get_property,
|
||||
&FGVoicePlayer::Speaker::set_property));
|
||||
}
|
||||
|
||||
public:
|
||||
template <class T>
|
||||
inline void set_property (T *ptr, T value) { *ptr = value; update_configuration(); }
|
||||
|
||||
template <class T>
|
||||
inline T get_property (T *ptr) const { return *ptr; }
|
||||
|
||||
float volume;
|
||||
|
||||
inline Speaker (FGVoicePlayer *_player,PropertiesHandler* _properties_handler)
|
||||
: player(_player),
|
||||
properties_handler(_properties_handler),
|
||||
pitch(1),
|
||||
volume(1)
|
||||
{
|
||||
}
|
||||
|
||||
void bind (SGPropertyNode *node);
|
||||
void update_configuration ();
|
||||
};
|
||||
|
||||
protected:
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// FGVoicePlayer (continued) ///////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr;
|
||||
Speaker speaker;
|
||||
|
||||
map< string, SGSharedPtr<SGSoundSample> > samples;
|
||||
vector<Voice *> _voices;
|
||||
|
||||
bool looped;
|
||||
bool next_looped;
|
||||
|
||||
SGSoundSample *get_sample (const char *name);
|
||||
|
||||
inline void append (Voice *voice, Voice::Element *element) { voice->append(element); }
|
||||
inline void append (Voice *voice, const char *sample_name) { voice->append(new Voice::SampleElement(get_sample(sample_name))); }
|
||||
inline void append (Voice *voice, double silence) { voice->append(new Voice::SilenceElement(silence)); }
|
||||
|
||||
inline void make_voice (Voice **voice) { *voice = new Voice(this); _voices.push_back(*voice); }
|
||||
|
||||
template <class T1>
|
||||
inline void make_voice (Voice **voice, T1 e1) { make_voice(voice); append(*voice, e1); }
|
||||
template <class T1, class T2>
|
||||
inline void make_voice (Voice **voice, T1 e1, T2 e2) { make_voice(voice, e1); append(*voice, e2); }
|
||||
template <class T1, class T2, class T3>
|
||||
inline void make_voice (Voice **voice, T1 e1, T2 e2, T3 e3) { make_voice(voice, e1, e2); append(*voice, e3); }
|
||||
template <class T1, class T2, class T3, class T4>
|
||||
inline void make_voice (Voice **voice, T1 e1, T2 e2, T3 e3, T4 e4) { make_voice(voice, e1, e2, e3); append(*voice, e4); }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII ////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -56,49 +345,6 @@ class MK_VIII : public SGSubsystem
|
|||
// keep in sync with Mode6Handler::altitude_callout_definitions[]
|
||||
static const unsigned n_altitude_callouts = 11;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::RawValueMethodsData /////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class C, class VT, class DT>
|
||||
class RawValueMethodsData : public SGRawValue<VT>
|
||||
{
|
||||
public:
|
||||
typedef VT (C::*getter_t) (DT) const;
|
||||
typedef void (C::*setter_t) (DT, VT);
|
||||
|
||||
RawValueMethodsData (C &obj, DT data, getter_t getter = 0, setter_t setter = 0)
|
||||
: _obj(obj), _data(data), _getter(getter), _setter(setter) {}
|
||||
|
||||
virtual VT getValue () const
|
||||
{
|
||||
if (_getter)
|
||||
return (_obj.*_getter)(_data);
|
||||
else
|
||||
return SGRawValue<VT>::DefaultValue();
|
||||
}
|
||||
virtual bool setValue (VT value)
|
||||
{
|
||||
if (_setter)
|
||||
{
|
||||
(_obj.*_setter)(_data, value);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
virtual SGRawValue<VT> *clone () const
|
||||
{
|
||||
return new RawValueMethodsData<C,VT,DT>(_obj, _data, _getter, _setter);
|
||||
}
|
||||
|
||||
private:
|
||||
C &_obj;
|
||||
DT _data;
|
||||
getter_t _getter;
|
||||
setter_t _setter;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::Parameter ///////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -184,12 +430,10 @@ class MK_VIII : public SGSubsystem
|
|||
// MK_VIII::PropertiesHandler ///////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class PropertiesHandler
|
||||
class PropertiesHandler : public FGVoicePlayer::PropertiesHandler
|
||||
{
|
||||
MK_VIII *mk;
|
||||
|
||||
vector<SGPropertyNode_ptr> tied_properties;
|
||||
|
||||
public:
|
||||
struct
|
||||
{
|
||||
|
@ -225,27 +469,11 @@ class MK_VIII : public SGSubsystem
|
|||
} external_properties;
|
||||
|
||||
inline PropertiesHandler (MK_VIII *device)
|
||||
: mk(device) {}
|
||||
: FGVoicePlayer::PropertiesHandler(), mk(device) {}
|
||||
|
||||
template <class T>
|
||||
inline void tie (SGPropertyNode *node, const SGRawValue<T> &raw_value)
|
||||
{
|
||||
node->tie(raw_value);
|
||||
tied_properties.push_back(node);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void tie (SGPropertyNode *node,
|
||||
const char *relative_path,
|
||||
const SGRawValue<T> &raw_value)
|
||||
{
|
||||
tie(node->getNode(relative_path, true), raw_value);
|
||||
}
|
||||
|
||||
PropertiesHandler() {};
|
||||
PropertiesHandler() : FGVoicePlayer::PropertiesHandler() {}
|
||||
|
||||
void init ();
|
||||
void unbind ();
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -717,249 +945,48 @@ public:
|
|||
bool *get_lamp_output (Lamp lamp);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer /////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class VoicePlayer
|
||||
class VoicePlayer : public FGVoicePlayer
|
||||
{
|
||||
public:
|
||||
VoicePlayer (MK_VIII *device) :
|
||||
FGVoicePlayer(&device->properties_handler, "mk-viii")
|
||||
{}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer::Voice ////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
~VoicePlayer() {}
|
||||
void init ();
|
||||
|
||||
class Voice
|
||||
{
|
||||
public:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer::Voice::Element ////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Element
|
||||
struct
|
||||
{
|
||||
public:
|
||||
bool silence;
|
||||
Voice *application_data_base_failed;
|
||||
Voice *bank_angle;
|
||||
Voice *bank_angle_bank_angle;
|
||||
Voice *bank_angle_bank_angle_3;
|
||||
Voice *bank_angle_inop;
|
||||
Voice *bank_angle_pause_bank_angle;
|
||||
Voice *bank_angle_pause_bank_angle_3;
|
||||
Voice *callouts_inop;
|
||||
Voice *configuration_type_invalid;
|
||||
Voice *dont_sink;
|
||||
Voice *dont_sink_pause_dont_sink;
|
||||
Voice *five_hundred_above;
|
||||
Voice *glideslope;
|
||||
Voice *glideslope_inop;
|
||||
Voice *gpws_inop;
|
||||
Voice *hard_glideslope;
|
||||
Voice *minimums;
|
||||
Voice *minimums_minimums;
|
||||
Voice *pull_up;
|
||||
Voice *sink_rate;
|
||||
Voice *sink_rate_pause_sink_rate;
|
||||
Voice *soft_glideslope;
|
||||
Voice *terrain;
|
||||
Voice *terrain_pause_terrain;
|
||||
Voice *too_low_flaps;
|
||||
Voice *too_low_gear;
|
||||
Voice *too_low_terrain;
|
||||
Voice *altitude_callouts[n_altitude_callouts];
|
||||
} voices;
|
||||
|
||||
virtual inline void play (float volume) {}
|
||||
virtual inline void stop () {}
|
||||
virtual bool is_playing () = 0;
|
||||
virtual inline void set_volume (float volume) {}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer::Voice::SampleElement ///////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SampleElement : public Element
|
||||
{
|
||||
SGSharedPtr<SGSoundSample> _sample;
|
||||
float _volume;
|
||||
|
||||
public:
|
||||
inline SampleElement (SGSharedPtr<SGSoundSample> sample, float volume = 1.0)
|
||||
: _sample(sample), _volume(volume) { silence = false; }
|
||||
|
||||
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 (float volume) { if (_sample) _sample->set_volume(volume * _volume); }
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer::Voice::SilenceElement //////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SilenceElement : public Element
|
||||
{
|
||||
double _duration;
|
||||
double start_time;
|
||||
|
||||
public:
|
||||
inline SilenceElement (double duration)
|
||||
: _duration(duration) { silence = true; }
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer::Voice (continued) //////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Element *element;
|
||||
|
||||
inline Voice (VoicePlayer *_player)
|
||||
: element(NULL), player(_player), volume(1.0) {}
|
||||
|
||||
~Voice ();
|
||||
|
||||
inline void append (Element *_element) { elements.push_back(_element); }
|
||||
|
||||
void play ();
|
||||
void stop (bool now);
|
||||
void set_volume (float _volume);
|
||||
void volume_changed ();
|
||||
void update ();
|
||||
|
||||
private:
|
||||
VoicePlayer *player;
|
||||
|
||||
float volume;
|
||||
|
||||
vector<Element *> elements;
|
||||
vector<Element *>::iterator iter;
|
||||
|
||||
inline float get_volume () const { return player->volume * player->speaker.volume * volume; }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer (continued) ///////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct
|
||||
{
|
||||
float volume;
|
||||
} conf;
|
||||
|
||||
float volume;
|
||||
|
||||
Voice *voice;
|
||||
Voice *next_voice;
|
||||
|
||||
struct
|
||||
{
|
||||
Voice *application_data_base_failed;
|
||||
Voice *bank_angle;
|
||||
Voice *bank_angle_bank_angle;
|
||||
Voice *bank_angle_bank_angle_3;
|
||||
Voice *bank_angle_inop;
|
||||
Voice *bank_angle_pause_bank_angle;
|
||||
Voice *bank_angle_pause_bank_angle_3;
|
||||
Voice *callouts_inop;
|
||||
Voice *configuration_type_invalid;
|
||||
Voice *dont_sink;
|
||||
Voice *dont_sink_pause_dont_sink;
|
||||
Voice *five_hundred_above;
|
||||
Voice *glideslope;
|
||||
Voice *glideslope_inop;
|
||||
Voice *gpws_inop;
|
||||
Voice *hard_glideslope;
|
||||
Voice *minimums;
|
||||
Voice *minimums_minimums;
|
||||
Voice *pull_up;
|
||||
Voice *sink_rate;
|
||||
Voice *sink_rate_pause_sink_rate;
|
||||
Voice *soft_glideslope;
|
||||
Voice *terrain;
|
||||
Voice *terrain_pause_terrain;
|
||||
Voice *too_low_flaps;
|
||||
Voice *too_low_gear;
|
||||
Voice *too_low_terrain;
|
||||
Voice *altitude_callouts[n_altitude_callouts];
|
||||
} voices;
|
||||
|
||||
inline VoicePlayer (MK_VIII *device)
|
||||
: voice(NULL), next_voice(NULL), mk(device), speaker(this) {}
|
||||
|
||||
~VoicePlayer ();
|
||||
|
||||
void init ();
|
||||
|
||||
enum
|
||||
{
|
||||
PLAY_NOW = 1 << 0,
|
||||
PLAY_LOOPED = 1 << 1
|
||||
};
|
||||
void play (Voice *_voice, unsigned int flags = 0);
|
||||
|
||||
enum
|
||||
{
|
||||
STOP_NOW = 1 << 0
|
||||
};
|
||||
void stop (unsigned int flags = 0);
|
||||
|
||||
void set_volume (float _volume);
|
||||
void update ();
|
||||
|
||||
inline void bind (SGPropertyNode *node) { speaker.bind(node); }
|
||||
|
||||
public:
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer::Speaker //////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Speaker
|
||||
{
|
||||
VoicePlayer *player;
|
||||
|
||||
double pitch;
|
||||
|
||||
template <class T>
|
||||
inline void tie (SGPropertyNode *node, const char *name, T *ptr)
|
||||
{
|
||||
player->mk->properties_handler.tie
|
||||
(node, (string("speaker/") + name).c_str(),
|
||||
RawValueMethodsData<MK_VIII::VoicePlayer::Speaker,T,T*>
|
||||
(*this, ptr,
|
||||
&MK_VIII::VoicePlayer::Speaker::get_property,
|
||||
&MK_VIII::VoicePlayer::Speaker::set_property));
|
||||
}
|
||||
|
||||
public:
|
||||
template <class T>
|
||||
inline void set_property (T *ptr, T value) { *ptr = value; update_configuration(); }
|
||||
|
||||
template <class T>
|
||||
inline T get_property (T *ptr) const { return *ptr; }
|
||||
|
||||
float volume;
|
||||
|
||||
inline Speaker (VoicePlayer *_player)
|
||||
: player(_player),
|
||||
pitch(1),
|
||||
volume(1)
|
||||
{
|
||||
}
|
||||
|
||||
void bind (SGPropertyNode *node);
|
||||
void update_configuration ();
|
||||
};
|
||||
|
||||
private:
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// MK_VIII::VoicePlayer (continued) ///////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MK_VIII *mk;
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr;
|
||||
Speaker speaker;
|
||||
|
||||
map< string, SGSharedPtr<SGSoundSample> > samples;
|
||||
vector<Voice *> _voices;
|
||||
|
||||
bool looped;
|
||||
bool next_looped;
|
||||
|
||||
SGSoundSample *get_sample (const char *name);
|
||||
|
||||
inline void append (Voice *voice, Voice::Element *element) { voice->append(element); }
|
||||
inline void append (Voice *voice, const char *sample_name) { voice->append(new Voice::SampleElement(get_sample(sample_name))); }
|
||||
inline void append (Voice *voice, double silence) { voice->append(new Voice::SilenceElement(silence)); }
|
||||
|
||||
inline void make_voice (Voice **voice) { *voice = new Voice(this); _voices.push_back(*voice); }
|
||||
|
||||
template <class T1>
|
||||
inline void make_voice (Voice **voice, T1 e1) { make_voice(voice); append(*voice, e1); }
|
||||
template <class T1, class T2>
|
||||
inline void make_voice (Voice **voice, T1 e1, T2 e2) { make_voice(voice, e1); append(*voice, e2); }
|
||||
template <class T1, class T2, class T3>
|
||||
inline void make_voice (Voice **voice, T1 e1, T2 e2, T3 e3) { make_voice(voice, e1, e2); append(*voice, e3); }
|
||||
template <class T1, class T2, class T3, class T4>
|
||||
inline void make_voice (Voice **voice, T1 e1, T2 e2, T3 e3, T4 e4) { make_voice(voice, e1, e2, e3); append(*voice, e4); }
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
Loading…
Reference in a new issue