Cut&paste voiceplayer into a separate module.
GPWS and TCAS are now completely separate - but use the same voice player.
This commit is contained in:
parent
59f2328551
commit
badec14d27
11 changed files with 671 additions and 544 deletions
projects
src
|
@ -1812,6 +1812,10 @@
|
||||||
RelativePath="..\..\src\sound\voice.hxx"
|
RelativePath="..\..\src\sound\voice.hxx"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\src\sound\voiceplayer.hxx"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\src\fdm\yasim\Wing.hpp"
|
RelativePath="..\..\src\fdm\yasim\Wing.hpp"
|
||||||
>
|
>
|
||||||
|
@ -3562,6 +3566,10 @@
|
||||||
RelativePath="..\..\src\sound\voice.cxx"
|
RelativePath="..\..\src\sound\voice.cxx"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\src\sound\voiceplayer.cxx"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\src\fdm\yasim\Wing.cpp"
|
RelativePath="..\..\src\fdm\yasim\Wing.cpp"
|
||||||
>
|
>
|
||||||
|
|
|
@ -3221,6 +3221,14 @@
|
||||||
RelativePath="..\..\..\src\Sound\voice.hxx"
|
RelativePath="..\..\..\src\Sound\voice.hxx"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\..\src\Sound\voiceplayer.cxx"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\..\src\Sound\voiceplayer.hxx"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="Lib_Time"
|
Name="Lib_Time"
|
||||||
|
|
|
@ -2133,255 +2133,6 @@ MK_VIII::IOHandler::set_present_status (bool value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// FGVoicePlayer //////////////////////////////////////////////////////////////
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::Speaker::bind (SGPropertyNode *node)
|
|
||||||
{
|
|
||||||
// uses xmlsound property names
|
|
||||||
tie(node, "volume", &volume);
|
|
||||||
tie(node, "pitch", &pitch);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::Speaker::update_configuration ()
|
|
||||||
{
|
|
||||||
map< string, SGSharedPtr<SGSoundSample> >::iterator iter;
|
|
||||||
for (iter = player->samples.begin(); iter != player->samples.end(); iter++)
|
|
||||||
{
|
|
||||||
SGSoundSample *sample = (*iter).second;
|
|
||||||
|
|
||||||
sample->set_pitch(pitch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player->voice)
|
|
||||||
player->voice->volume_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
FGVoicePlayer::Voice::~Voice ()
|
|
||||||
{
|
|
||||||
for (iter = elements.begin(); iter != elements.end(); iter++)
|
|
||||||
delete *iter; // we owned the element
|
|
||||||
elements.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::Voice::play ()
|
|
||||||
{
|
|
||||||
iter = elements.begin();
|
|
||||||
element = *iter;
|
|
||||||
|
|
||||||
element->play(get_volume());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::Voice::stop (bool now)
|
|
||||||
{
|
|
||||||
if (element)
|
|
||||||
{
|
|
||||||
if (now || element->silence)
|
|
||||||
{
|
|
||||||
element->stop();
|
|
||||||
element = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
iter = elements.end() - 1; // stop after the current element finishes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::Voice::set_volume (float _volume)
|
|
||||||
{
|
|
||||||
volume = _volume;
|
|
||||||
volume_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::Voice::volume_changed ()
|
|
||||||
{
|
|
||||||
if (element)
|
|
||||||
element->set_volume(get_volume());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::Voice::update ()
|
|
||||||
{
|
|
||||||
if (element)
|
|
||||||
{
|
|
||||||
if (! element->is_playing())
|
|
||||||
{
|
|
||||||
if (++iter == elements.end())
|
|
||||||
element = NULL;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
element = *iter;
|
|
||||||
element->play(get_volume());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FGVoicePlayer::~FGVoicePlayer ()
|
|
||||||
{
|
|
||||||
vector<Voice *>::iterator iter1;
|
|
||||||
for (iter1 = _voices.begin(); iter1 != _voices.end(); iter1++)
|
|
||||||
delete *iter1;
|
|
||||||
_voices.clear();
|
|
||||||
samples.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::bind (SGPropertyNode *node, const char* default_dir_prefix)
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
speaker.update_configuration();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::pause()
|
|
||||||
{
|
|
||||||
if (paused)
|
|
||||||
return;
|
|
||||||
|
|
||||||
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 = 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 sound sample \"" + filename + "\": " + e.getFormattedMessage());
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_sgr->add(sample, refname);
|
|
||||||
samples[refname] = sample;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sample;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::play (Voice *_voice, unsigned int flags)
|
|
||||||
{
|
|
||||||
if (!_voice)
|
|
||||||
return;
|
|
||||||
if (test_bits(flags, PLAY_NOW) || ! voice ||
|
|
||||||
(voice->element && voice->element->silence))
|
|
||||||
{
|
|
||||||
if (voice)
|
|
||||||
voice->stop(true);
|
|
||||||
|
|
||||||
voice = _voice;
|
|
||||||
looped = test_bits(flags, PLAY_LOOPED);
|
|
||||||
|
|
||||||
next_voice = NULL;
|
|
||||||
next_looped = false;
|
|
||||||
|
|
||||||
if (!paused)
|
|
||||||
voice->play();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
next_voice = _voice;
|
|
||||||
next_looped = test_bits(flags, PLAY_LOOPED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::stop (unsigned int flags)
|
|
||||||
{
|
|
||||||
if (voice)
|
|
||||||
{
|
|
||||||
voice->stop(test_bits(flags, STOP_NOW));
|
|
||||||
if (voice->element)
|
|
||||||
looped = false;
|
|
||||||
else
|
|
||||||
voice = NULL;
|
|
||||||
next_voice = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::set_volume (float _volume)
|
|
||||||
{
|
|
||||||
volume = _volume;
|
|
||||||
if (voice)
|
|
||||||
voice->volume_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FGVoicePlayer::update ()
|
|
||||||
{
|
|
||||||
if (voice)
|
|
||||||
{
|
|
||||||
voice->update();
|
|
||||||
|
|
||||||
if (next_voice)
|
|
||||||
{
|
|
||||||
if (! voice->element || voice->element->silence)
|
|
||||||
{
|
|
||||||
voice = next_voice;
|
|
||||||
looped = next_looped;
|
|
||||||
|
|
||||||
next_voice = NULL;
|
|
||||||
next_looped = false;
|
|
||||||
|
|
||||||
voice->play();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (! voice->element)
|
|
||||||
{
|
|
||||||
if (looped)
|
|
||||||
voice->play();
|
|
||||||
else
|
|
||||||
voice = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// MK_VIII::VoicePlayer ///////////////////////////////////////////////////////
|
// MK_VIII::VoicePlayer ///////////////////////////////////////////////////////
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
#include <simgear/props/props.hxx>
|
#include <simgear/props/props.hxx>
|
||||||
#include <simgear/props/tiedpropertylist.hxx>
|
#include <simgear/props/tiedpropertylist.hxx>
|
||||||
#include <simgear/sound/sample_openal.hxx>
|
|
||||||
#include <simgear/structure/subsystem_mgr.hxx>
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using std::deque;
|
using std::deque;
|
||||||
|
@ -41,298 +40,13 @@ class SGSampleGroup;
|
||||||
#include <Airports/runways.hxx>
|
#include <Airports/runways.hxx>
|
||||||
#include <Airports/simple.hxx>
|
#include <Airports/simple.hxx>
|
||||||
#include <Main/globals.hxx>
|
#include <Main/globals.hxx>
|
||||||
|
#include <Sound/voiceplayer.hxx>
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning( push )
|
# pragma warning( push )
|
||||||
# pragma warning( disable: 4355 )
|
# pragma warning( disable: 4355 )
|
||||||
#endif
|
#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 simgear::TiedPropertyList
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline void tie (SGPropertyNode *node, const SGRawValue<T> &raw_value)
|
|
||||||
{
|
|
||||||
Tie(node,raw_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 () {Untie();}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// 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 ////////////////////////////////////////////////////////////////////
|
// MK_VIII ////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -100,8 +100,6 @@
|
||||||
#include <simgear/sg_inlines.h>
|
#include <simgear/sg_inlines.h>
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
#include <simgear/math/sg_geodesy.hxx>
|
#include <simgear/math/sg_geodesy.hxx>
|
||||||
#include <simgear/math/sg_random.h>
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
|
||||||
#include <simgear/sound/soundmgr_openal.hxx>
|
#include <simgear/sound/soundmgr_openal.hxx>
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
|
@ -115,7 +113,6 @@ using std::string;
|
||||||
|
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
#include <Main/globals.hxx>
|
#include <Main/globals.hxx>
|
||||||
#include <AIModel/submodel.hxx>
|
|
||||||
#include "instrument_mgr.hxx"
|
#include "instrument_mgr.hxx"
|
||||||
#include "tcas.hxx"
|
#include "tcas.hxx"
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,8 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <simgear/props/props.hxx>
|
#include <simgear/props/props.hxx>
|
||||||
#include <simgear/sound/sample_openal.hxx>
|
|
||||||
#include <simgear/structure/subsystem_mgr.hxx>
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
#include "mk_viii.hxx" // FGVoicePlayer only
|
#include <Sound/voiceplayer.hxx>
|
||||||
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using std::deque;
|
using std::deque;
|
||||||
|
|
|
@ -6,6 +6,7 @@ set(SOURCES
|
||||||
morse.cxx
|
morse.cxx
|
||||||
sample_queue.cxx
|
sample_queue.cxx
|
||||||
voice.cxx
|
voice.cxx
|
||||||
|
voiceplayer.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
flightgear_component(Sound "${SOURCES}")
|
flightgear_component(Sound "${SOURCES}")
|
|
@ -5,6 +5,7 @@ libSound_a_SOURCES = \
|
||||||
fg_fx.cxx fg_fx.hxx \
|
fg_fx.cxx fg_fx.hxx \
|
||||||
morse.cxx morse.hxx \
|
morse.cxx morse.hxx \
|
||||||
voice.cxx voice.hxx \
|
voice.cxx voice.hxx \
|
||||||
sample_queue.cxx sample_queue.hxx
|
sample_queue.cxx sample_queue.hxx \
|
||||||
|
voiceplayer.cxx voiceplayer.hxx
|
||||||
|
|
||||||
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
|
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
|
||||||
|
|
|
@ -39,8 +39,8 @@ class SGSoundSample;
|
||||||
*
|
*
|
||||||
* This modules maintains a queue of 'message' audio files. These
|
* This modules maintains a queue of 'message' audio files. These
|
||||||
* are played sequentially with no overlap until the queue is finished.
|
* are played sequentially with no overlap until the queue is finished.
|
||||||
* This second mechanims is useful for things like tutorial messages or
|
* This second mechanisms is useful for things like tutorial messages or
|
||||||
* background atc chatter.
|
* background ATC chatter.
|
||||||
*/
|
*/
|
||||||
class FGSampleQueue : public SGSampleGroup
|
class FGSampleQueue : public SGSampleGroup
|
||||||
{
|
{
|
||||||
|
|
314
src/Sound/voiceplayer.cxx
Normal file
314
src/Sound/voiceplayer.cxx
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
// voiceplayer.cxx -- voice/sound sample player
|
||||||
|
//
|
||||||
|
// Written by Jean-Yves Lefort, started September 2005.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2005, 2006 Jean-Yves Lefort - jylefort@FreeBSD.org
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning( disable: 4355 )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
#include <simgear/sound/soundmgr_openal.hxx>
|
||||||
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
|
||||||
|
# include <Include/version.h>
|
||||||
|
#else
|
||||||
|
# include <Include/no_version.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "voiceplayer.hxx"
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// constants //////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// helpers ////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
#define ADD_VOICE(Var,Sample,Twice) \
|
||||||
|
{ make_voice(&Var);append(Var,Sample);\
|
||||||
|
if (Twice) append(Var,Sample); }
|
||||||
|
|
||||||
|
#define test_bits(_bits, _test) (((_bits) & (_test)) != 0)
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// FGVoicePlayer //////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::Speaker::bind (SGPropertyNode *node)
|
||||||
|
{
|
||||||
|
// uses xmlsound property names
|
||||||
|
tie(node, "volume", &volume);
|
||||||
|
tie(node, "pitch", &pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::Speaker::update_configuration ()
|
||||||
|
{
|
||||||
|
map< string, SGSharedPtr<SGSoundSample> >::iterator iter;
|
||||||
|
for (iter = player->samples.begin(); iter != player->samples.end(); iter++)
|
||||||
|
{
|
||||||
|
SGSoundSample *sample = (*iter).second;
|
||||||
|
|
||||||
|
sample->set_pitch(pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player->voice)
|
||||||
|
player->voice->volume_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
FGVoicePlayer::Voice::~Voice ()
|
||||||
|
{
|
||||||
|
for (iter = elements.begin(); iter != elements.end(); iter++)
|
||||||
|
delete *iter; // we owned the element
|
||||||
|
elements.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::Voice::play ()
|
||||||
|
{
|
||||||
|
iter = elements.begin();
|
||||||
|
element = *iter;
|
||||||
|
|
||||||
|
element->play(get_volume());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::Voice::stop (bool now)
|
||||||
|
{
|
||||||
|
if (element)
|
||||||
|
{
|
||||||
|
if (now || element->silence)
|
||||||
|
{
|
||||||
|
element->stop();
|
||||||
|
element = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
iter = elements.end() - 1; // stop after the current element finishes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::Voice::set_volume (float _volume)
|
||||||
|
{
|
||||||
|
volume = _volume;
|
||||||
|
volume_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::Voice::volume_changed ()
|
||||||
|
{
|
||||||
|
if (element)
|
||||||
|
element->set_volume(get_volume());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::Voice::update ()
|
||||||
|
{
|
||||||
|
if (element)
|
||||||
|
{
|
||||||
|
if (! element->is_playing())
|
||||||
|
{
|
||||||
|
if (++iter == elements.end())
|
||||||
|
element = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
element = *iter;
|
||||||
|
element->play(get_volume());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FGVoicePlayer::~FGVoicePlayer ()
|
||||||
|
{
|
||||||
|
vector<Voice *>::iterator iter1;
|
||||||
|
for (iter1 = _voices.begin(); iter1 != _voices.end(); iter1++)
|
||||||
|
delete *iter1;
|
||||||
|
_voices.clear();
|
||||||
|
samples.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::bind (SGPropertyNode *node, const char* default_dir_prefix)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
speaker.update_configuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::pause()
|
||||||
|
{
|
||||||
|
if (paused)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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 = 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 sound sample \"" + filename + "\": " + e.getFormattedMessage());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_sgr->add(sample, refname);
|
||||||
|
samples[refname] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::play (Voice *_voice, unsigned int flags)
|
||||||
|
{
|
||||||
|
if (!_voice)
|
||||||
|
return;
|
||||||
|
if (test_bits(flags, PLAY_NOW) || ! voice ||
|
||||||
|
(voice->element && voice->element->silence))
|
||||||
|
{
|
||||||
|
if (voice)
|
||||||
|
voice->stop(true);
|
||||||
|
|
||||||
|
voice = _voice;
|
||||||
|
looped = test_bits(flags, PLAY_LOOPED);
|
||||||
|
|
||||||
|
next_voice = NULL;
|
||||||
|
next_looped = false;
|
||||||
|
|
||||||
|
if (!paused)
|
||||||
|
voice->play();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next_voice = _voice;
|
||||||
|
next_looped = test_bits(flags, PLAY_LOOPED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::stop (unsigned int flags)
|
||||||
|
{
|
||||||
|
if (voice)
|
||||||
|
{
|
||||||
|
voice->stop(test_bits(flags, STOP_NOW));
|
||||||
|
if (voice->element)
|
||||||
|
looped = false;
|
||||||
|
else
|
||||||
|
voice = NULL;
|
||||||
|
next_voice = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::set_volume (float _volume)
|
||||||
|
{
|
||||||
|
volume = _volume;
|
||||||
|
if (voice)
|
||||||
|
voice->volume_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGVoicePlayer::update ()
|
||||||
|
{
|
||||||
|
if (voice)
|
||||||
|
{
|
||||||
|
voice->update();
|
||||||
|
|
||||||
|
if (next_voice)
|
||||||
|
{
|
||||||
|
if (! voice->element || voice->element->silence)
|
||||||
|
{
|
||||||
|
voice = next_voice;
|
||||||
|
looped = next_looped;
|
||||||
|
|
||||||
|
next_voice = NULL;
|
||||||
|
next_looped = false;
|
||||||
|
|
||||||
|
voice->play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (! voice->element)
|
||||||
|
{
|
||||||
|
if (looped)
|
||||||
|
voice->play();
|
||||||
|
else
|
||||||
|
voice = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
334
src/Sound/voiceplayer.hxx
Normal file
334
src/Sound/voiceplayer.hxx
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
// voiceplayer.hxx -- voice/sound sample player
|
||||||
|
//
|
||||||
|
// Written by Jean-Yves Lefort, started September 2005.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2005, 2006 Jean-Yves Lefort - jylefort@FreeBSD.org
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __SOUND_VOICEPLAYER_HXX
|
||||||
|
#define __SOUND_VOICEPLAYER_HXX
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <deque>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
#include <simgear/props/tiedpropertylist.hxx>
|
||||||
|
#include <simgear/sound/sample_openal.hxx>
|
||||||
|
using std::vector;
|
||||||
|
using std::deque;
|
||||||
|
using std::map;
|
||||||
|
|
||||||
|
class SGSampleGroup;
|
||||||
|
|
||||||
|
#include <Main/globals.hxx>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning( push )
|
||||||
|
# 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 simgear::TiedPropertyList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline void tie (SGPropertyNode *node, const SGRawValue<T> &raw_value)
|
||||||
|
{
|
||||||
|
Tie(node,raw_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 () {Untie();}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SOUND_VOICEPLAYER_HXX
|
Loading…
Add table
Reference in a new issue