From 395c31762789f3d027d854deb697e2ea56cf9675 Mon Sep 17 00:00:00 2001 From: ThorstenB Date: Sat, 13 Oct 2012 14:37:47 +0200 Subject: [PATCH] ATCVoice: support multiple voice files 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 | 8 +- src/ATCDCL/ATC.hxx | 3 +- src/ATCDCL/ATCProjection.cxx | 2 +- src/ATCDCL/ATCProjection.hxx | 2 +- src/ATCDCL/ATCVoice.cxx | 157 +++++++++++++++++++++++++++-------- src/ATCDCL/ATCVoice.hxx | 5 +- src/ATCDCL/ATISmgr.cxx | 18 +++- src/ATCDCL/ATISmgr.hxx | 2 + 8 files changed, 152 insertions(+), 45 deletions(-) diff --git a/src/ATCDCL/ATC.cxx b/src/ATCDCL/ATC.cxx index 89cb55273..9e868017b 100644 --- a/src/ATCDCL/ATC.cxx +++ b/src/ATCDCL/ATC.cxx @@ -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 { diff --git a/src/ATCDCL/ATC.hxx b/src/ATCDCL/ATC.hxx index e57ebabd7..6a0918a66 100644 --- a/src/ATCDCL/ATC.hxx +++ b/src/ATCDCL/ATC.hxx @@ -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 _sgr; // default sample group; diff --git a/src/ATCDCL/ATCProjection.cxx b/src/ATCDCL/ATCProjection.cxx index fb6b1bed4..ecc08cc80 100644 --- a/src/ATCDCL/ATCProjection.cxx +++ b/src/ATCDCL/ATCProjection.cxx @@ -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. // diff --git a/src/ATCDCL/ATCProjection.hxx b/src/ATCDCL/ATCProjection.hxx index fbc93a0d6..94dfb92b9 100644 --- a/src/ATCDCL/ATCProjection.hxx +++ b/src/ATCDCL/ATCProjection.hxx @@ -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. // diff --git a/src/ATCDCL/ATCVoice.cxx b/src/ATCDCL/ATCVoice.cxx index 14c4920e1..71f15e587 100644 --- a/src/ATCDCL/ATCVoice.cxx +++ b/src/ATCDCL/ATCVoice.cxx @@ -26,6 +26,7 @@ #include "ATCVoice.hxx" #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include +#include #include #include @@ -43,49 +45,124 @@ 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; iget_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); } diff --git a/src/ATCDCL/ATCVoice.hxx b/src/ATCDCL/ATCVoice.hxx index 5128cc55d..55fe68a7c 100644 --- a/src/ATCDCL/ATCVoice.hxx +++ b/src/ATCDCL/ATCVoice.hxx @@ -28,6 +28,7 @@ #include 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; diff --git a/src/ATCDCL/ATISmgr.cxx b/src/ATCDCL/ATISmgr.cxx index 1c5dc8ca7..b52d95675 100644 --- a/src/ATCDCL/ATISmgr.cxx +++ b/src/ATCDCL/ATISmgr.cxx @@ -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; diff --git a/src/ATCDCL/ATISmgr.hxx b/src/ATCDCL/ATISmgr.hxx index 716044f83..29aaef472 100644 --- a/src/ATCDCL/ATISmgr.hxx +++ b/src/ATCDCL/ATISmgr.hxx @@ -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