From c8ec37bd8595788a415c682b9eede01f1557036b Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sat, 26 Feb 2022 19:37:46 +0100 Subject: [PATCH] #20 using AmbeFilReader --- .vscode/tasks.json | 10 +- DGWTimeServer/TimeServerConfig.cpp | 3 +- DGWTimeServer/TimeServerDefs.h | 1 - DGWTimeServer/TimeServerThread.cpp | 324 ++++++++--------------------- DGWTimeServer/TimeServerThread.h | 65 ++---- 5 files changed, 107 insertions(+), 296 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 995a9f1..0545c42 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -65,7 +65,10 @@ "USE_GPSD=1", "DGWTimeServer/dgwtimeserver" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] }, { @@ -91,10 +94,7 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] } ] diff --git a/DGWTimeServer/TimeServerConfig.cpp b/DGWTimeServer/TimeServerConfig.cpp index d2c4fab..0a2f170 100644 --- a/DGWTimeServer/TimeServerConfig.cpp +++ b/DGWTimeServer/TimeServerConfig.cpp @@ -107,10 +107,9 @@ bool CTimeServerConfig::loadTimeServer(const CConfig & cfg) ret = cfg.getValue("timeserver", "address", m_timeServer.address, 0, 1024, "127.0.0.1") && ret; std::string format; - ret = cfg.getValue("timeserver", "format", format, "voiceandtext", {"voice", "text", "voiceandtext"}) && ret; + ret = cfg.getValue("timeserver", "format", format, "voice", {"voice", "text" }) && ret; if(format == "voice") m_timeServer.format = FORMAT_VOICE_TIME; else if(format == "text") m_timeServer.format = FORMAT_TEXT_TIME; - else if(format == "voiceandtext") m_timeServer.format = FORMAT_VOICE_ALL; std::string lang; ret = cfg.getValue("timeserver", "language", lang, "english_uk_1", {"english_uk_1", "english_uk_2", "english_us_1", "english_us_2", "deutsch_1", "deutsch_2", "francais", "nederlands", "svenska", "espanol", "norsk", "portugues"}) && ret;; diff --git a/DGWTimeServer/TimeServerDefs.h b/DGWTimeServer/TimeServerDefs.h index 8917675..b6910c2 100644 --- a/DGWTimeServer/TimeServerDefs.h +++ b/DGWTimeServer/TimeServerDefs.h @@ -44,6 +44,5 @@ enum INTERVAL { enum FORMAT { FORMAT_VOICE_TIME, - FORMAT_VOICE_ALL, FORMAT_TEXT_TIME }; diff --git a/DGWTimeServer/TimeServerThread.cpp b/DGWTimeServer/TimeServerThread.cpp index 696099f..be8d20e 100644 --- a/DGWTimeServer/TimeServerThread.cpp +++ b/DGWTimeServer/TimeServerThread.cpp @@ -46,32 +46,23 @@ m_addressStr(), m_language(LANG_ENGLISH_UK_1), m_format(FORMAT_VOICE_TIME), m_interval(INTERVAL_15MINS), -m_ambe(NULL), -m_ambeLength(0U), -m_index(), -m_seqNo(0U), -m_in(0U), -m_encoder(), -m_data(NULL), +m_data(), m_killed(false), -m_dataPath("") +m_dataPath(""), +m_ambeFileReader(nullptr) { CHeaderData::initialise(); - m_address.s_addr = INADDR_NONE; - - m_data = new CAMBEData*[MAX_FRAMES]; - - for (unsigned int i = 0U; i < MAX_FRAMES; i++) - m_data[i] = nullptr; + m_address.s_addr = INADDR_NONE; } CTimeServerThread::~CTimeServerThread() { - for (auto it = m_index.begin(); it != m_index.end(); it++) - delete it->second; + for(auto d : m_data) + delete d; - delete[] m_ambe; - delete[] m_data; + m_data.clear(); + + delete[] m_ambeFileReader; } void * CTimeServerThread::Entry() @@ -158,8 +149,8 @@ bool CTimeServerThread::setGateway(const std::string& callsign, const std::strin m_callsign.push_back(' '); m_addressStr.assign(address); - m_address = CUDPReaderWriter::lookup(address); m_dataPath.assign(dataPath); + m_address = CUDPReaderWriter::lookup(address); return true; } @@ -988,197 +979,103 @@ bool CTimeServerThread::loadAMBE() break; } - bool ret = readAMBE(m_dataPath, ambeFileName); - if (!ret) { - delete[] m_ambe; - m_ambe = NULL; - return false; - } + m_ambeFileReader = new CAMBEFileReader(m_dataPath + "/" + indxFileName, m_dataPath + "/" + ambeFileName); + bool ret = m_ambeFileReader->read(); - ret = readIndex(m_dataPath, indxFileName); if (!ret) { - delete[] m_ambe; - m_ambe = NULL; + delete[] m_ambeFileReader; + m_ambeFileReader = nullptr; return false; } return true; } -bool CTimeServerThread::readAMBE(const std::string& dir, const std::string& name) +void CTimeServerThread::buildAudio(const std::vector& words, CSlowDataEncoder& slowDataEncoder) { - std::string fileName = dir + "/" + name; - struct stat sbuf; - - if (stat(fileName.c_str(), &sbuf)) { - CLog::logInfo("File %s not readable\n", fileName.c_str()); - fileName.append("/data/"); - fileName += name; - if (stat(fileName.c_str(), &sbuf)) { - CLog::logInfo("File %s not readable\n", fileName.c_str()); - return false; - } - } - unsigned int fsize = sbuf.st_size; - - FILE *file = fopen(fileName.c_str(), "rb"); - if (NULL == file) { - CLog::logInfo("Cannot open %s for reading\n", fileName.c_str()); - return false; - } - - CLog::logInfo("Reading %s\n", fileName.c_str()); + unsigned int seqNo = 0U; - unsigned char buffer[VOICE_FRAME_LENGTH_BYTES]; + m_data.clear(); - size_t n = fread(buffer, sizeof(unsigned char), 4, file); - if (n != 4) { - CLog::logError("Unable to read the header from %s\n", fileName.c_str()); - fclose(file); - return false; - } + if(words.size() == 0U || m_ambeFileReader == nullptr) + CLog::logWarning("No words, falling back to text only"); - if (memcmp(buffer, "AMBE", 4)) { - CLog::logError("Invalid header from %s\n", fileName.c_str()); - fclose(file); - return false; - } + if(m_format == FORMAT_VOICE_TIME && words.size() != 0U) { + // Build the audio + m_ambeFileReader->lookup(" ", m_data); + m_ambeFileReader->lookup(" ", m_data); + m_ambeFileReader->lookup(" ", m_data); + m_ambeFileReader->lookup(" ", m_data); - // Length of the file minus the header - unsigned int length = fsize - 4U; + for (unsigned int i = 0U; i < words.size(); i++) + m_ambeFileReader->lookup(words.at(i), m_data); - // Hold the file data plus silence at the end - m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES]; - m_ambeLength = length / VOICE_FRAME_LENGTH_BYTES; + m_ambeFileReader->lookup(" ", m_data); + m_ambeFileReader->lookup(" ", m_data); + m_ambeFileReader->lookup(" ", m_data); + m_ambeFileReader->lookup(" ", m_data); - // Add silence to the beginning of the buffer - unsigned char* p = m_ambe; - for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES) - memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + for(unsigned int i = 0U; i < m_data.size(); i++) { + m_data[i]->setDestination(m_address, G2_DV_PORT); + m_data[i]->setSeq(seqNo); - n = fread(p, 1, length, file); - if (n != length) { - CLog::logError("Unable to read the AMBE data from %s\n", fileName.c_str()); - fclose(file); - delete[] m_ambe; - m_ambe = NULL; - return false; - } + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + m_data[i]->getData(buffer, DV_FRAME_LENGTH_BYTES); - fclose(file); + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (seqNo == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + slowDataEncoder.sync(); + } else { + slowDataEncoder.getInterleavedData(buffer + VOICE_FRAME_LENGTH_BYTES); + } - return true; -} + m_data[i]->setData(buffer, DV_FRAME_LENGTH_BYTES); -bool CTimeServerThread::readIndex(const std::string& dir, const std::string& name) -{ - std::string fileName = dir + "/" + name; - struct stat sbuf; - - if (stat(fileName.c_str(), &sbuf)) { - CLog::logInfo("File %s not readable\n", fileName.c_str()); - fileName.append("/data/"); - fileName += name; - if (stat(fileName.c_str(), &sbuf)) { - CLog::logInfo("File %s not readable\n", fileName.c_str()); - return false; + seqNo++; + if(seqNo >= 21U) seqNo = 0U; } } + else { + for (unsigned int i = 0U; i < 21U; i++, seqNo++) { + CAMBEData* dataOut = new CAMBEData; + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + dataOut->setDestination(m_address, G2_DV_PORT); + dataOut->setSeq(i); - FILE *file = fopen(fileName.c_str(), "r"); - if (NULL == file) { - CLog::logInfo("Cannot open %s for reading\n", fileName.c_str()); - return false; - } - - // Add a silence entry at the beginning - m_index[" "] = new CIndexRecord(" ", 0, SILENCE_LENGTH); - - CLog::logInfo("Reading %s\n", fileName.c_str()); - - char line[128]; - while (fgets(line, 128, file)) { - - if (strlen(line) && '#'!=line[0]) { - const std::string space(" \t\r\n"); - std::string name(strtok(line, space.c_str())); - std::string strt(strtok(NULL, space.c_str())); - std::string leng(strtok(NULL, space.c_str())); - - if (name.size() && strt.size() && leng.size()) { - unsigned long start = std::stoul(strt); - unsigned long length = std::stoul(leng); + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); - if (start >= m_ambeLength || (start + length) >= m_ambeLength) - CLog::logInfo("The start or end for *%s* is out of range, start: %lu, end: %lu\n", name.c_str(), start, start + length); - else - m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length); + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (i == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + slowDataEncoder.sync(); + } else { + slowDataEncoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); } - } - } - fclose(file); - - return true; -} - -bool CTimeServerThread::lookup(const std::string &id) -{ - CIndexRecord* info = m_index[id]; - if (info == NULL) { - // wxLogError(("Cannot find the AMBE index for *%s*"), id.c_str()); - return false; - } - - unsigned int start = info->getStart(); - unsigned int length = info->getLength(); - - for (unsigned int i = 0U; i < length; i++) { - unsigned char* dataIn = m_ambe + (start + i) * VOICE_FRAME_LENGTH_BYTES; - - CAMBEData* dataOut = new CAMBEData; - dataOut->setDestination(m_address, G2_DV_PORT); - dataOut->setSeq(m_seqNo); - - unsigned char buffer[DV_FRAME_LENGTH_BYTES]; - ::memcpy(buffer + 0U, dataIn, VOICE_FRAME_LENGTH_BYTES); - - // Insert sync bytes when the sequence number is zero, slow data otherwise - if (m_seqNo == 0U) { - ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); - m_encoder.sync(); + dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES); - } else { - m_encoder.getInterleavedData(buffer + VOICE_FRAME_LENGTH_BYTES); + m_data.push_back(dataOut); } - - dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES); - - m_seqNo++; - if (m_seqNo == 21U) - m_seqNo = 0U; - - m_data[m_in] = dataOut; - m_in++; } - return true; -} + if(seqNo >= 21U) { + seqNo = 0U; + } -void CTimeServerThread::end() -{ CAMBEData* dataOut = new CAMBEData; dataOut->setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); dataOut->setDestination(m_address, G2_DV_PORT); - dataOut->setSeq(m_seqNo); + dataOut->setSeq(seqNo); dataOut->setEnd(true); - m_data[m_in] = dataOut; - m_in++; + m_data.push_back(dataOut); } bool CTimeServerThread::send(const std::vector &words, unsigned int hour, unsigned int min) { + CSlowDataEncoder encoder; + CHeaderData header; header.setMyCall1(m_callsign); header.setRptCall1(m_callsignG); @@ -1241,78 +1138,26 @@ bool CTimeServerThread::send(const std::vector &words, unsigned int break; } - m_encoder.setHeaderData(header); - m_encoder.setTextData(slowData); + encoder.setHeaderData(header); + encoder.setTextData(slowData); - m_in = 0U; + buildAudio(words, encoder); - if (m_format != FORMAT_TEXT_TIME) { - std::string text = words.at(0U); - for (unsigned int i = 1U; i < words.size(); i++) { - text.push_back(' '); - text += words.at(i); - } + if (m_data.size() == 0U) { + CLog::logWarning(("Not sending, no audio files loaded")); + return false; + } + if(m_format == FORMAT_VOICE_TIME) { + std::string text = boost::algorithm::join(words, " "); boost::replace_all(text, "_", " "); - CLog::logInfo(("Sending voice \"%s\", sending text \"%s\""), text.c_str(), slowData.c_str()); - - m_seqNo = 0U; - - // Build the audio - lookup((" ")); - lookup((" ")); - lookup((" ")); - lookup((" ")); - - for (unsigned int i = 0U; i < words.size(); i++) - lookup(words.at(i)); - - lookup((" ")); - lookup((" ")); - lookup((" ")); - lookup((" ")); - - end(); - } else { - CLog::logInfo(("Sending text \"%s\""), slowData.c_str()); - - for (unsigned int i = 0U; i < 21U; i++) { - CAMBEData* dataOut = new CAMBEData; - dataOut->setDestination(m_address, G2_DV_PORT); - dataOut->setSeq(i); - - unsigned char buffer[DV_FRAME_LENGTH_BYTES]; - ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); - - // Insert sync bytes when the sequence number is zero, slow data otherwise - if (i == 0U) { - ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); - m_encoder.sync(); - } else { - m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); - } - - dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES); - - m_data[m_in] = dataOut; - m_in++; - } - - CAMBEData* dataOut = new CAMBEData; - dataOut->setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); - dataOut->setDestination(m_address, G2_DV_PORT); - dataOut->setSeq(0U); - dataOut->setEnd(true); - - m_data[m_in] = dataOut; - m_in++; + CLog::logInfo("Sending voice \"%s\", sending text \"%s\"", text.c_str(), slowData.c_str()); } - - if (m_in == 0U) { - CLog::logWarning(("Not sending, no audio files loaded")); - return false; + else { + CLog::logInfo("Sending text \"%s\"", slowData.c_str()); } + // Build id and socket lists std::vector ids; std::vector sockets; for(auto rpt : m_repeaters) { @@ -1321,6 +1166,7 @@ bool CTimeServerThread::send(const std::vector &words, unsigned int ids.push_back(CHeaderData::createId()); } + // open them all bool allOpen = std::all_of(sockets.begin(), sockets.end(), [](CUDPReaderWriter* s) { return s->open(); }); if(allOpen) { //send headers @@ -1332,7 +1178,7 @@ bool CTimeServerThread::send(const std::vector &words, unsigned int Sleep(5); } - // send audio + // send audio bool loop = true; unsigned int out = 0U; auto start = std::chrono::high_resolution_clock::now(); @@ -1353,7 +1199,7 @@ bool CTimeServerThread::send(const std::vector &words, unsigned int m_data[out] = nullptr; out++; - if (m_in == out) { + if (out >= m_data.size()) { loop = false; break; } @@ -1361,6 +1207,8 @@ bool CTimeServerThread::send(const std::vector &words, unsigned int } } + m_data.clear(); + for(auto socket : sockets) { socket->close(); delete socket; diff --git a/DGWTimeServer/TimeServerThread.h b/DGWTimeServer/TimeServerThread.h index 3abe883..36bb9c7 100644 --- a/DGWTimeServer/TimeServerThread.h +++ b/DGWTimeServer/TimeServerThread.h @@ -27,37 +27,7 @@ #include "HeaderData.h" #include "AMBEData.h" #include "Thread.h" - -class CIndexRecord { -public: - CIndexRecord(const std::string& name, unsigned int start, unsigned int length) : - m_name(name), - m_start(start), - m_length(length) - { - } - - std::string getName() const - { - return m_name; - } - - unsigned int getStart() const - { - return m_start; - } - - unsigned int getLength() const - { - return m_length; - } - -private: - std::string m_name; - unsigned int m_start; - unsigned int m_length; -}; - +#include "AMBEFileReader.h" class CTimeServerThread : public CThread { @@ -72,23 +42,19 @@ public: void kill(); private: - std::string m_callsign; + std::string m_callsign; std::vector m_repeaters; - std::string m_callsignG; - in_addr m_address; - std::string m_addressStr; - LANGUAGE m_language; - FORMAT m_format; - INTERVAL m_interval; - unsigned char* m_ambe; - unsigned int m_ambeLength; - std::unordered_map m_index; - unsigned int m_seqNo; - unsigned int m_in; - CSlowDataEncoder m_encoder; - CAMBEData** m_data; - bool m_killed; - std::string m_dataPath; + std::string m_callsignG; + in_addr m_address; + std::string m_addressStr; + LANGUAGE m_language; + FORMAT m_format; + INTERVAL m_interval; + CSlowDataEncoder m_encoder; + std::vector m_data; + bool m_killed; + std::string m_dataPath; + CAMBEFileReader * m_ambeFileReader; void sendTime(unsigned int hour, unsigned int min); @@ -110,9 +76,8 @@ private: bool sendData(CUDPReaderWriter& socket, const CAMBEData& data); bool loadAMBE(); - bool readAMBE(const std::string& dir, const std::string& name); - bool readIndex(const std::string& dir, const std::string& name); bool lookup(const std::string& id); - void end(); + + void buildAudio(const std::vector& words, CSlowDataEncoder& slowDataEncoder); };