1
0
Fork 0

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:
ThorstenB 2010-12-29 21:57:49 +01:00
parent f938a23d7c
commit dd7c88b8cb
2 changed files with 450 additions and 379 deletions

View file

@ -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

View file

@ -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: