diff --git a/Audio/de_DE.ambe b/Audio/de_DE.ambe new file mode 100644 index 0000000..e4ec9f9 Binary files /dev/null and b/Audio/de_DE.ambe differ diff --git a/Audio/de_DE.indx b/Audio/de_DE.indx new file mode 100644 index 0000000..41fb2d9 --- /dev/null +++ b/Audio/de_DE.indx @@ -0,0 +1,12 @@ +0 0 18 +1 18 24 +2 42 24 +3 66 24 +4 90 27 +5 117 29 +6 146 28 +7 174 31 +8 205 23 +9 228 25 +connected 253 77 +disconnected 330 72 diff --git a/Audio/en_GB.ambe b/Audio/en_GB.ambe new file mode 100644 index 0000000..5b33bad Binary files /dev/null and b/Audio/en_GB.ambe differ diff --git a/Audio/en_GB.indx b/Audio/en_GB.indx new file mode 100644 index 0000000..5d58500 --- /dev/null +++ b/Audio/en_GB.indx @@ -0,0 +1,12 @@ +0 0 50 +1 50 31 +2 81 30 +3 111 43 +4 154 42 +5 196 48 +6 244 40 +7 284 37 +8 321 32 +9 353 43 +connected 396 52 +disconnected 448 45 diff --git a/Conf.cpp b/Conf.cpp index 7a1846b..065573e 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -52,6 +52,7 @@ m_logFilePath(), m_logFileRoot(), m_voiceEnabled(true), m_voiceLanguage("en_GB"), +m_voiceDirectory(), m_dmrNetworkAddress(), m_dmrNetworkPort(0U), m_dmrNetworkLocal(0U), @@ -143,6 +144,8 @@ bool CConf::read() m_voiceEnabled = ::atoi(value) == 1; else if (::strcmp(key, "Language") == 0) m_voiceLanguage = value; + else if (::strcmp(key, "Directory") == 0) + m_voiceDirectory = value; } else if (section == SECTION_XLX_NETWORK) { if (::strcmp(key, "Address") == 0) m_xlxNetworkAddress = value; @@ -250,6 +253,11 @@ std::string CConf::getVoiceLanguage() const return m_voiceLanguage; } +std::string CConf::getVoiceDirectory() const +{ + return m_voiceDirectory; +} + std::string CConf::getXLXNetworkAddress() const { return m_xlxNetworkAddress; diff --git a/Conf.h b/Conf.h index 2a66c41..525dda3 100644 --- a/Conf.h +++ b/Conf.h @@ -50,6 +50,7 @@ public: // The Voice section bool getVoiceEnabled() const; std::string getVoiceLanguage() const; + std::string getVoiceDirectory() const; // The DMR Network section std::string getDMRNetworkAddress() const; @@ -80,6 +81,7 @@ private: bool m_voiceEnabled; std::string m_voiceLanguage; + std::string m_voiceDirectory; unsigned int m_logDisplayLevel; unsigned int m_logFileLevel; diff --git a/DMRGateway.cpp b/DMRGateway.cpp index 359dfde..f508203 100644 --- a/DMRGateway.cpp +++ b/DMRGateway.cpp @@ -250,13 +250,15 @@ int CDMRGateway::run() CVoice* voice = NULL; if (m_conf.getVoiceEnabled()) { - std::string language = m_conf.getVoiceLanguage(); + std::string language = m_conf.getVoiceLanguage(); + std::string directory = m_conf.getVoiceDirectory(); LogInfo("Voice Parameters"); LogInfo(" Enabled: yes"); LogInfo(" Language: %s", language.c_str()); + LogInfo(" Directory: %s", directory.c_str()); - voice = new CVoice(language, xlxSlot, xlxTG); + voice = new CVoice(directory, language, m_repeater->getId(), xlxSlot, xlxTG); bool ret = voice->open(); if (!ret) { diff --git a/DMRGateway.ini b/DMRGateway.ini index ddf5bb3..8bbf3d6 100644 --- a/DMRGateway.ini +++ b/DMRGateway.ini @@ -19,6 +19,7 @@ FileRoot=DMRGateway [Voice] Enabled=1 Language=en_GB +Directory=./Audio [XLX Network] Address=xlx950.epf.lu diff --git a/Voice.cpp b/Voice.cpp index 35bf22a..8cbebae 100644 --- a/Voice.cpp +++ b/Voice.cpp @@ -16,35 +16,190 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "DMRSlotType.h" +#include "DMRFullLC.h" +#include "DMREMB.h" #include "Voice.h" +#include "Sync.h" +#include "Log.h" -CVoice::CVoice(const std::string& language, unsigned int slot, unsigned int tg) : -m_language(language), +#include + +#include + +const unsigned char COLOR_CODE = 3U; + +CVoice::CVoice(const std::string& directory, const std::string& language, unsigned int id, unsigned int slot, unsigned int tg) : +m_indxFile(), +m_ambeFile(), m_slot(slot), -m_tg(tg), +m_lc(FLCO_GROUP, id, tg), +m_embeddedLC(), m_status(VS_NONE), m_timer(1000U, 1U), -m_stopWatch() +m_stopWatch(), +m_seqNo(0U), +m_streamId(0U), +m_sent(0U), +m_ambe(NULL), +m_data(), +m_it(), +m_start(), +m_length() { + m_embeddedLC.setLC(m_lc); + +#if defined(_WIN32) || defined(_WIN64) + m_indxFile = directory + "\\" + language + ".indx"; + m_ambeFile = directory + "\\" + language + ".ambe"; +#else + m_indxFile = directory + "/" + language + ".indx"; + m_ambeFile = directory + "/" + language + ".ambe"; +#endif } CVoice::~CVoice() { + for (std::vector::iterator it = m_data.begin(); it != m_data.end(); ++it) + delete *it; + + m_data.clear(); + + delete[] m_ambe; } bool CVoice::open() { + FILE* fpindx = ::fopen(m_indxFile.c_str(), "rt"); + if (fpindx == NULL) { + LogError("Unable to open the index file - %s", m_indxFile.c_str()); + return false; + } + + struct stat statStruct; + int ret = ::stat(m_ambeFile.c_str(), &statStruct); + if (ret != 0) { + LogError("Unable to stat the AMBE file - %s", m_ambeFile.c_str()); + ::fclose(fpindx); + return false; + } + + FILE* fpambe = ::fopen(m_ambeFile.c_str(), "rb"); + if (fpambe == NULL) { + LogError("Unable to open the AMBE file - %s", m_ambeFile.c_str()); + ::fclose(fpindx); + return false; + } + + m_ambe = new unsigned char[statStruct.st_size]; + + ::fread(m_ambe, 1U, statStruct.st_size, fpambe); + + char buffer[80U]; + while (::fgets(buffer, 80, fpindx) != NULL) { + char* p1 = ::strtok(buffer, "\t\r\n"); + char* p2 = ::strtok(NULL, "\t\r\n"); + char* p3 = ::strtok(NULL, "\t\r\n"); + + if (p1 != NULL && p2 != NULL && p3 != NULL) { + unsigned int start = ::atoi(p2) * 9U; + unsigned int length = ::atoi(p3) * 9U; + + m_start[p1] = start; + m_length[p1] = length; + } + } + + ::fclose(fpindx); + ::fclose(fpambe); + return true; } void CVoice::linkedTo(unsigned int id) { + for (std::vector::iterator it = m_data.begin(); it != m_data.end(); ++it) + delete *it; + + m_data.clear(); + + m_streamId = ::rand() + 1U; + m_seqNo = 0U; + + createHeaderTerminator(DT_VOICE_LC_HEADER); + createHeaderTerminator(DT_VOICE_LC_HEADER); + createHeaderTerminator(DT_VOICE_LC_HEADER); + + unsigned int length = 0U; + unsigned char* ambe = new unsigned char[10000U]; + + + delete[] ambe; + + createHeaderTerminator(DT_TERMINATOR_WITH_LC); + m_status = VS_WAITING; m_timer.start(); } void CVoice::unlinked() { + for (std::vector::iterator it = m_data.begin(); it != m_data.end(); ++it) + delete *it; + + m_data.clear(); + + m_streamId = ::rand() + 1U; + m_seqNo = 0U; + + createHeaderTerminator(DT_VOICE_LC_HEADER); + createHeaderTerminator(DT_VOICE_LC_HEADER); + createHeaderTerminator(DT_VOICE_LC_HEADER); + + unsigned int start = m_start["disconnected"]; + unsigned int length = m_length["disconnected"] / 9U; + + unsigned char buffer[DMR_FRAME_LENGTH_BYTES]; + + unsigned char* p = m_ambe + start; + for (unsigned int i = 0U; i < length; i++, p += 27U) { + CDMRData* data = new CDMRData; + + data->setSlotNo(m_slot); + data->setFLCO(FLCO_GROUP); + data->setSrcId(m_lc.getSrcId()); + data->setDstId(m_lc.getDstId()); + data->setN(); + data->setSeqNo(m_seqNo++); + data->setStreamId(m_streamId); + + ::memcpy(buffer + 0U, p + 0U, 9U); + ::memcpy(buffer + 9U, p + 9U, 9U); + ::memcpy(buffer + 15U, p + 9U, 9U); + ::memcpy(buffer + 24U, p + 18U, 9U); + + if (n == 0U) { + CSync::addDMRAudioSync(buffer); + data->setDataType(DT_VOICE_SYNC); + } else { + CDMREMB emb; + emb.setColorCode(COLOR_CODE); + emb.setPI(false); + emb.setLCSS(); + emb.getData(buffer); + + m_embeddedLC.getData(buffer, n); + + data->setDataType(DT_VOICE); + } + + data->setData(buffer); + + m_data.push_back(data); + } + + createHeaderTerminator(DT_TERMINATOR_WITH_LC); + m_status = VS_WAITING; m_timer.start(); } @@ -54,6 +209,25 @@ bool CVoice::read(CDMRData& data) if (m_status != VS_SENDING) return false; + unsigned int count = m_stopWatch.elapsed() / DMR_SLOT_TIME; + + if (m_sent < count) { + data = *(*m_it); + + ++m_sent; + ++m_it; + + if (m_it == m_data.end()) { + for (std::vector::iterator it = m_data.begin(); it != m_data.end(); ++it) + delete *it; + m_data.clear(); + m_timer.stop(); + m_status = VS_NONE; + } + + return true; + } + return false; } @@ -64,6 +238,38 @@ void CVoice::clock(unsigned int ms) if (m_status == VS_WAITING) { m_stopWatch.start(); m_status = VS_SENDING; + m_it = m_data.begin(); + m_sent = 0U; } } } + +void CVoice::createHeaderTerminator(unsigned char type) +{ + CDMRData* data = new CDMRData; + + data->setSlotNo(m_slot); + data->setFLCO(FLCO_GROUP); + data->setSrcId(m_lc.getSrcId()); + data->setDstId(m_lc.getDstId()); + data->setDataType(type); + data->setN(0U); + data->setSeqNo(m_seqNo++); + data->setStreamId(m_streamId); + + unsigned char buffer[DMR_FRAME_LENGTH_BYTES]; + + CDMRFullLC fullLC; + fullLC.encode(m_lc, buffer, type); + + CDMRSlotType slotType; + slotType.setColorCode(COLOR_CODE); + slotType.setDataType(type); + slotType.getData(buffer); + + CSync::addDMRDataSync(buffer, true); + + data->setData(buffer); + + m_data.push_back(data); +} diff --git a/Voice.h b/Voice.h index cd63657..abda07b 100644 --- a/Voice.h +++ b/Voice.h @@ -19,11 +19,15 @@ #if !defined(Voice_H) #define Voice_H +#include "DMREmbeddedData.h" #include "StopWatch.h" #include "DMRData.h" +#include "DMRLC.h" #include "Timer.h" +#include #include +#include enum VOICE_STATUS { VS_NONE, @@ -33,7 +37,7 @@ enum VOICE_STATUS { class CVoice { public: - CVoice(const std::string& language, unsigned int slot, unsigned int tg); + CVoice(const std::string& directory, const std::string& language, unsigned int id, unsigned int slot, unsigned int tg); ~CVoice(); bool open(); @@ -46,12 +50,24 @@ public: void clock(unsigned int ms); private: - std::string m_language; - unsigned int m_slot; - unsigned int m_tg; - VOICE_STATUS m_status; - CTimer m_timer; - CStopWatch m_stopWatch; + std::string m_indxFile; + std::string m_ambeFile; + unsigned int m_slot; + CDMRLC m_lc; + CDMREmbeddedData m_embeddedLC; + VOICE_STATUS m_status; + CTimer m_timer; + CStopWatch m_stopWatch; + unsigned int m_seqNo; + unsigned int m_streamId; + unsigned int m_sent; + unsigned char* m_ambe; + std::vector m_data; + std::vector::const_iterator m_it; + std::unordered_map m_start; + std::unordered_map m_length; + + void createHeaderTerminator(unsigned char type); }; #endif