]> git.mxchange.org Git - flightgear.git/commitdiff
ATCVoice: support multiple voice files
authorThorstenB <brehmt@gmail.com>
Sat, 13 Oct 2012 12:37:47 +0000 (14:37 +0200)
committerThorstenB <brehmt@gmail.com>
Sat, 13 Oct 2012 13:45:15 +0000 (15:45 +0200)
so we can split ATIS voice files into separate files, i.e. for airport
names and phraseology, so we don't need to regenerate airport names when
extending/changing phraseology. Also allows to add custom airport names.
Enable switching voice files at run-time (different airports could have
different voices...).

src/ATCDCL/ATC.cxx
src/ATCDCL/ATC.hxx
src/ATCDCL/ATCProjection.cxx
src/ATCDCL/ATCProjection.hxx
src/ATCDCL/ATCVoice.cxx
src/ATCDCL/ATCVoice.hxx
src/ATCDCL/ATISmgr.cxx
src/ATCDCL/ATISmgr.hxx

index 89cb5527302f99208a9ff03d7acecbe6674790ac..9e868017b19111ccefb25fba399bc1aad156e36e 100644 (file)
@@ -41,7 +41,6 @@ FGATC::FGATC() :
     range(0),
     _voice(true),
     _playing(false),
-    _vPtr(NULL),
     _sgr(NULL),
     _type(INVALID),
     _display(false)
@@ -167,10 +166,9 @@ void FGATC::Render(std::string& msg, const float volume,
             _currentMsg = msg;
             size_t len;
             void* buf = NULL;
-            if (!_vPtr)
-                _vPtr = GetVoicePointer();
-            if (_vPtr)
-                buf = _vPtr->WriteMessage((char*)msg.c_str(), &len);
+            FGATCVoice* vPtr = GetVoicePointer();
+            if (vPtr)
+                buf = vPtr->WriteMessage((char*)msg.c_str(), &len);
             NoRender(refname);
             if(buf) {
                 try {
index e57ebabd7d8a245df0606164c2bfbe4f8d8af7b6..6a0918a663f357b5728b4aae664be26c899a61a6 100644 (file)
@@ -155,7 +155,7 @@ protected:
        // The refname is a string to identify this sample to the sound manager
        // The repeating flag indicates whether the message should be repeated continuously or played once.
        void Render(std::string& msg, const float volume = 1.0, 
-       const std::string& refname = "", bool repeating = false);
+                   const std::string& refname = "", bool repeating = false);
        
        // Cease rendering all transmission from this station.
        // Requires the sound manager refname if audio, else "".
@@ -176,7 +176,6 @@ protected:
        // Rendering related stuff
        bool _voice;    // Flag - true if we are using voice
        bool _playing;  // Indicates a message in progress
-       FGATCVoice* _vPtr;
 
        SGSharedPtr<SGSampleGroup> _sgr; // default sample group;
 
index fb6b1bed4cbefd4647ba4c15cf213c6d98fb0896..ecc08cc8002185e7f96d2a9ee827692214de5bfd 100644 (file)
@@ -1,4 +1,4 @@
-// ATCProjection.cxx - A convienience projection class for the ATC/AI system.
+// ATCProjection.cxx - A convenience projection class for the ATC/AI system.
 //
 // Written by David Luff, started 2002.
 //
index fbc93a0d624eabba117ee8ddac2651ee4d6e3dc5..94dfb92b9afd0306dd9ffa055606220995e5a255 100644 (file)
@@ -1,4 +1,4 @@
-// ATCProjection.hxx - A convienience projection class for the ATC/AI system.
+// ATCProjection.hxx - A convenience projection class for the ATC/AI system.
 //
 // Written by David Luff, started 2002.
 //
index 14c4920e11c6b198f4d81840785c46ed509024a6..71f15e5876bf7ec63f6077f4bc1d392be49c1c68 100644 (file)
@@ -26,6 +26,7 @@
 #include "ATCVoice.hxx"
 
 #include <stdlib.h>
+#include <string.h>
 #include <ctype.h>
 #include <fstream>
 #include <vector>
@@ -33,6 +34,7 @@
 
 #include <simgear/sound/soundmgr_openal.hxx>
 #include <simgear/sound/sample_openal.hxx>
+#include <simgear/misc/sg_dir.hxx>
 
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/debug/logstream.hxx>
 
 using namespace std;
 
-FGATCVoice::FGATCVoice() {
-  SoundData = 0;
-  rawSoundData = 0;
+FGATCVoice::FGATCVoice() :
+    rawSoundData(0),
+    rawDataSize(0),
+    SoundData(0)
+{
 }
 
 FGATCVoice::~FGATCVoice() {
     if (rawSoundData)
-       free( rawSoundData );
+        free( rawSoundData );
     delete SoundData;
 }
 
-// Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce).
+// Load all data for the requested voice.
 // Return true if successful.
-bool FGATCVoice::LoadVoice(const string& voice) {
-       std::ifstream fin;
+bool FGATCVoice::LoadVoice(const string& voicename)
+{
+    rawDataSize = 0;
+    if (rawSoundData)
+        free(rawSoundData);
+    rawSoundData = NULL;
+
+    // determine voice directory
+    SGPath voicepath = globals->get_fg_root();
+    voicepath.append( "ATC" );
+    voicepath.append( "voices" );
+    voicepath.append( voicename );
+
+    simgear::Dir d(voicepath);
+    if (!d.exists())
+    {
+        SG_LOG(SG_ATC, SG_ALERT, "Unable to load ATIS voice. No such directory: " << voicepath.str());
+        return false;
+    }
+
+    // load all files from the voice's directory
+    simgear::PathList paths = d.children(simgear::Dir::TYPE_FILE);
+    bool Ok = false;
+    for (unsigned int i=0; i<paths.size(); ++i)
+    {
+        if (paths[i].lower_extension() == "vce")
+            Ok |= AppendVoiceFile(voicepath, paths[i].file_base());
+    }
+
+    if (!Ok)
+    {
+        SG_LOG(SG_ATC, SG_ALERT, "Unable to load ATIS voice. Files are invalid or no files in directory: " << voicepath.str());
+    }
+
+    // ok when at least some files loaded fine
+    return Ok;
+}
+
+// load a voice file and append it to the current word database
+bool FGATCVoice::AppendVoiceFile(const SGPath& basepath, const string& file)
+{
+    size_t offset = 0;
+
+    SG_LOG(SG_ATC, SG_INFO, "Loading ATIS voice file: " << file);
+
+    // path to compressed voice file
+    SGPath path(basepath);
+    path.append(file + ".wav.gz");
+
+    // load wave data
+    SGSoundMgr *smgr = globals->get_soundmgr();
+    int format, freq;
+    void *data;
+    size_t size;
+    if (!smgr->load(path.str(), &data, &format, &size, &freq))
+        return false;
+
+    // append to existing data
+    if (!rawSoundData)
+        rawSoundData = (char*)data;
+    else
+    {
+        rawSoundData = (char*) realloc(rawSoundData, rawDataSize + size);
+        // new data starts behind existing sound data
+        offset = rawDataSize;
+        if (!rawSoundData)
+        {
+            SG_LOG(SG_ATC, SG_ALERT, "Out of memory. Cannot load file " << path.str());
+            rawDataSize = 0;
+            return false;
+        }
+        // append to existing sound data
+        memcpy(rawSoundData+offset, data, size);
+        free(data);
+        data = NULL;
+    }
+    rawDataSize += size;
 
-       SGPath path = globals->get_fg_root();
-       string file = voice + ".wav";
-       path.append( "ATC" );
-       path.append( file );
-       
-       string full_path = path.str();
-       int format, freq;
-       SGSoundMgr *smgr = globals->get_soundmgr();
-       void *data;
-       if (!smgr->load(full_path, &data, &format, &rawDataSize, &freq))
-           return false;
-       rawSoundData = (char*)data;
 #ifdef VOICE_TEST
        cout << "ATCVoice:  format: " << format
                        << "  size: " << rawDataSize << endl;
-#endif 
-       path = globals->get_fg_root();
-       string wordPath = "ATC/" + voice + ".vce";
-       path.append(wordPath);
+#endif
+
+       // load and parse index file (.vce)
+       return ParseVoiceIndex(basepath, file, offset);
+}
+
+// Load and parse a voice index file (.vce)
+bool FGATCVoice::ParseVoiceIndex(const SGPath& basepath, const string& file, size_t globaloffset)
+{
+       // path to voice index file
+       SGPath path(basepath);
+       path.append(file + ".vce");
        
        // Now load the word data
+       std::ifstream fin;
        fin.open(path.c_str(), ios::in);
        if(!fin) {
                SG_LOG(SG_ATC, SG_ALERT, "Unable to open input file " << path.c_str());
                return(false);
        }
-       SG_LOG(SG_ATC, SG_INFO, "Opened word data file " << wordPath << " OK...");
+       SG_LOG(SG_ATC, SG_INFO, "Opened word data file " << path.c_str() << " OK...");
+
        char numwds[10];
        char wrd[100];
        string wrdstr;
@@ -94,30 +171,44 @@ bool FGATCVoice::LoadVoice(const string& voice) {
        unsigned int wrdOffset;         // Offset into the raw sound data that the word sample begins
        unsigned int wrdLength;         // Length of the word sample in bytes
        WordData wd;
+
+       // first entry: number of words in the index
        fin >> numwds;
        unsigned int numwords = atoi(numwds);
        //cout << numwords << '\n';
+
+       // now load each word, its file offset and length
        for(unsigned int i=0; i < numwords; ++i) {
+           // read data
                fin >> wrd;
-               wrdstr = wrd;
                fin >> wrdOffsetStr;
                fin >> wrdLengthStr;
+
+               wrdstr    = wrd;
                wrdOffset = atoi(wrdOffsetStr);
                wrdLength = atoi(wrdLengthStr);
-               wd.offset = wrdOffset;
+
+               // store word in map
+               wd.offset = wrdOffset + globaloffset;
                wd.length = wrdLength;
                wordMap[wrdstr] = wd;
-               string ws2 = wrdstr;
-               for(string::iterator p = ws2.begin(); p != ws2.end(); p++){
-                 *p = tolower(*p);
-                 if (*p == '-') *p = '_';
-               }
-               if (wrdstr != ws2)  wordMap[ws2] = wd;
+
+               // post-process words
+               string ws2 = wrdstr;
+               for(string::iterator p = ws2.begin(); p != ws2.end(); p++){
+                   *p = tolower(*p);
+                   if (*p == '-')
+                       *p = '_';
+               }
+
+               // store alternative version of word (lowercase/no hyphen)
+               if (wrdstr != ws2)
+                   wordMap[ws2] = wd;
 
                //cout << wrd << "\t\t" << wrdOffset << "\t\t" << wrdLength << '\n';
                //cout << i << '\n';
        }
-       
+
        fin.close();
        return(true);
 }
index 5128cc55dda2fc0654d3405f59370b6e3946de69..55fe68a7c6547155ca0aefa50138ba48e474c95c 100644 (file)
@@ -28,6 +28,7 @@
 #include <string>
 
 class SGSoundSample;
+class SGPath;
 
 struct WordData {
        unsigned int offset;    // Offset of beginning of word sample into raw sound sample
@@ -47,13 +48,15 @@ public:
 
        // Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce).
        // Return true if successful.   
-       bool LoadVoice(const std::string& voice);
+       bool LoadVoice(const std::string& voicename);
        
        // Given a desired message, return a pointer to the data buffer and write the buffer length into len.
        // Sets len to something other than 0 if the returned buffer is valid.
        void* WriteMessage(const std::string& message, size_t *len);
 
 private:
+       bool AppendVoiceFile(const SGPath& basepath, const std::string& file);
+       bool ParseVoiceIndex(const SGPath& basepath, const std::string& file, size_t globaloffset);
 
        // the sound and word position data
        char* rawSoundData;
index 1c5dc8ca7936dc2a23b40219ce2f8009dacc2341..b52d9567574b75db4cbe4b1bd5088f694d6f97ff 100644 (file)
@@ -69,6 +69,19 @@ void FGATISMgr::init()
     }
 }
 
+void FGATISMgr::reinit()
+{
+#ifdef ENABLE_AUDIO_SUPPORT
+    if ((voiceName != "")&&
+        (voiceName != fgGetString("/sim/atis/voice", "default")))
+    {
+        voiceName = fgGetString("/sim/atis/voice", "default");
+        delete voice;
+        voice = NULL;
+        useVoice = true;
+    }
+#endif
+}
 
 void FGATISMgr::update(double dt)
 {
@@ -108,10 +121,11 @@ FGATCVoice* FGATISMgr::GetVoicePointer(const atc_type& type)
              */
             if (!voice && fgGetBool("/sim/sound/working")) {
                 voice = new FGATCVoice;
+                voiceName = fgGetString("/sim/atis/voice", "default");
                 try {
-                    useVoice = voice->LoadVoice("default");
+                    useVoice = voice->LoadVoice(voiceName);
                 } catch ( sg_io_exception & e) {
-                    SG_LOG(SG_ATC, SG_ALERT, "Unable to load default voice : "
+                    SG_LOG(SG_ATC, SG_ALERT, "Unable to load voice '" << voiceName << "': "
                                             << e.getFormattedMessage().c_str());
                     useVoice = false;
                     delete voice;
index 716044f83fdc3a59f8aa4426df0165154180023c..29aaef4722e60b569c0650fe33245a732202ed9e 100644 (file)
@@ -40,6 +40,7 @@ private:
 #ifdef ENABLE_AUDIO_SUPPORT
     bool useVoice;  // Flag - true if we are using voice
     FGATCVoice* voice;
+    std::string voiceName; // currently loaded voice name
 #endif
 
 public:
@@ -47,6 +48,7 @@ public:
     ~FGATISMgr();
 
     void init();
+    void reinit();
     void update(double dt);
 
     // Return a pointer to an appropriate voice for a given type of ATC