]> git.mxchange.org Git - flightgear.git/commitdiff
Make voiceplayer independent
authorThorstenB <brehmt@gmail.com>
Wed, 29 Dec 2010 20:57:49 +0000 (21:57 +0100)
committerThorstenB <brehmt@gmail.com>
Fri, 25 Feb 2011 20:12:34 +0000 (21:12 +0100)
- 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

src/Instrumentation/mk_viii.cxx
src/Instrumentation/mk_viii.hxx

index 8fb871a4ab5d853a94b4e30faa4d7f901d0d4f66..065dc232ddf40f22d128408f4c77764a84da0989 100644 (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();
+  speaker.update_configuration();
+}
 
-  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");
+void
+FGVoicePlayer::pause()
+{
+    if (paused)
+        return;
 
-  for (unsigned i = 0; i < n_altitude_callouts; i++)
+    paused = true;
+    if (voice)
     {
-      std::ostringstream name;
-      name << "altitude-" << mk->mode6_handler.altitude_callout_definitions[i];
-      make_voice(&voices.altitude_callouts[i], name.str().c_str());
+        voice->stop(true);
     }
+}
 
-  speaker.update_configuration();
+void
+FGVoicePlayer::resume()
+{
+    if (!paused)
+        return;
+    paused = false;
+    if (voice)
+    {
+        voice->play();
+    }
 }
 
 SGSoundSample *
-MK_VIII::VoicePlayer::get_sample (const char *name)
+FGVoicePlayer::get_sample (const char *name)
 {
-  std::ostringstream refname;
-  refname << mk->name << "[" << mk->num << "]" << "/" << name;
+  string refname;
+  refname = dev_name + "/" + dir_prefix + name;
 
-  SGSoundSample *sample = _sgr->find(refname.str());
+  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
index e37fc972be2bfa2fa9bfe9ae2b862b9609bed99c..053b53d051a4016ffdb69e4c1f3b343a572e0642 100644 (file)
@@ -47,58 +47,304 @@ class SGSampleGroup;
 #  pragma warning( disable: 4355 )
 #endif
 
-///////////////////////////////////////////////////////////////////////////////
-// MK_VIII ////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+// FGVoicePlayer /////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
 
-class MK_VIII : public SGSubsystem
+class FGVoicePlayer
 {
-  // keep in sync with Mode6Handler::altitude_callout_definitions[]
-  static const unsigned n_altitude_callouts = 11;
+public:
 
-  /////////////////////////////////////////////////////////////////////////////
-  // MK_VIII::RawValueMethodsData /////////////////////////////////////////////
-  /////////////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////////////
+    // 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);
+      }
 
-  template <class C, class VT, class DT>
-  class RawValueMethodsData : public SGRawValue<VT>
+      PropertiesHandler() {};
+
+      void unbind ();
+    };
+
+  ///////////////////////////////////////////////////////////////////////////
+  // FGVoicePlayer::Voice ////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
+
+  class Voice
   {
   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) {}
+    /////////////////////////////////////////////////////////////////////////
+    // FGVoicePlayer::Voice::Element ////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////////
 
-    virtual VT getValue () const
+    class Element
     {
-      if (_getter)
-       return (_obj.*_getter)(_data);
-      else
-       return SGRawValue<VT>::DefaultValue();
-    }
-    virtual bool setValue (VT value)
+    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
     {
-      if (_setter)
-       {
-         (_obj.*_setter)(_data, value);
-         return true;
-       }
-      else
-       return false;
+        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));
     }
-    virtual SGRawValue<VT> *clone () const 
+
+  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)
     {
-      return new RawValueMethodsData<C,VT,DT>(_obj, _data, _getter, _setter);
     }
 
-  private:
-    C          &_obj;
-    DT         _data;
-    getter_t   _getter;
-    setter_t   _setter;
+    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 ////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class MK_VIII : public SGSubsystem
+{
+  // keep in sync with Mode6Handler::altitude_callout_definitions[]
+  static const unsigned n_altitude_callouts = 11;
+
   /////////////////////////////////////////////////////////////////////////////
   // 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:
@@ -716,250 +944,49 @@ 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 ////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
-
-    class Voice
-    {
-    public:
-
-      /////////////////////////////////////////////////////////////////////////
-      // MK_VIII::VoicePlayer::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) {}
-      };
-
-      /////////////////////////////////////////////////////////////////////////
-      // 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;
+      ~VoicePlayer() {}
+      void init ();
 
-      inline Speaker (VoicePlayer *_player)
-       : player(_player),
-         pitch(1),
-         volume(1)
+      struct
       {
-      }
-
-      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); }
+        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;
+      
   };
 
 private: