diff --git a/Controller.cpp b/Controller.cpp index b2aac83..10f8bb3 100644 --- a/Controller.cpp +++ b/Controller.cpp @@ -30,8 +30,8 @@ #include "Controller.h" -#define AMBE_GAIN 16 //Encoder gain in dB (I use 16 here) -#define AMBE2_GAIN -24 //Encoder gain in dB (I use -24 here) +//#define AMBE_GAIN 16 //Encoder gain in dB (I use 16 here) +//#define AMBE2_GAIN -24 //Encoder gain in dB (I use -24 here) #define USRP_RXGAIN -6 #define USRP_TXGAIN 3 @@ -196,7 +196,7 @@ bool CController::InitVocoders() dstar_device = std::unique_ptr(new CDV3000(Encoding::dstar)); #ifdef USE_SW_AMBE2 md380_init(); - ambe_gain = calcGainVal(AMBE2_GAIN); + ambe_gain = calcGainVal(DMR_IN_GAIN); #else dmrsf_device = std::unique_ptr(new CDV3000(Encoding::dmrsf)); #endif @@ -213,7 +213,7 @@ bool CController::InitVocoders() if (dstar_device) { - if (dstar_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, calcGainVal(AMBE_GAIN))) + if (dstar_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, DSTAR_IN_GAIN, DSTAR_OUT_GAIN)) return true; deviceset.pop_front(); } @@ -225,7 +225,7 @@ bool CController::InitVocoders() #ifndef USE_SW_AMBE2 if (dmrsf_device) { - if (dmrsf_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, calcGainVal(AMBE2_GAIN))) + if (dmrsf_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, DMR_IN_GAIN, DMR_OUT_GAIN)) return true; deviceset.pop_front(); } diff --git a/Controller.h b/Controller.h index 1ffe3c2..5b2ae04 100644 --- a/Controller.h +++ b/Controller.h @@ -32,7 +32,6 @@ #include "UnixDgramSocket.h" #include "configure.h" - class CController { public: diff --git a/DV3000.cpp b/DV3000.cpp index 8da0a1b..687c945 100644 --- a/DV3000.cpp +++ b/DV3000.cpp @@ -40,7 +40,10 @@ extern CController Controller; CDV3000::CDV3000(Encoding t) : CDVDevice(t) {} -CDV3000::~CDV3000() {} +CDV3000::~CDV3000() +{ + waiting_packet.Shutdown(); +} void CDV3000::PushWaitingPacket(unsigned int /* channel */, std::shared_ptr packet) { @@ -52,7 +55,7 @@ std::shared_ptr CDV3000::PopWaitingPacket(unsigned int /* cha return waiting_packet.pop(); } -bool CDV3000::SendAudio(const uint8_t /*channel*/, const int16_t *audio, const int gain) const +bool CDV3000::SendAudio(const uint8_t /*channel*/, const int16_t *audio) const { // Create Audio packet based on input int8_ts SDV_Packet p; @@ -61,16 +64,9 @@ bool CDV3000::SendAudio(const uint8_t /*channel*/, const int16_t *audio, const i p.header.packet_type = PKT_SPEECH; p.field_id = PKT_SPEECHD; p.payload.audio3k.num_samples = 160U; - const uint32_t g = abs(gain); - - for (int i=0; i<160; i++){ - if(gain < 0){ - p.payload.audio3k.samples[i] = htons(audio[i] / g); - } - else{ - p.payload.audio3k.samples[i] = htons(audio[i] * g); - } - } + for (int i=0; i<160; i++) + p.payload.audio3k.samples[i] = htons(audio[i]); + // send audio packet to DV3000 const DWORD size = packet_size(p); DWORD written; @@ -121,39 +117,42 @@ bool CDV3000::SendData(const uint8_t /* channel */, const uint8_t *data) const void CDV3000::ProcessPacket(const SDV_Packet &p) { auto packet = PopWaitingPacket(PKT_CHANNEL0); - if (PKT_CHANNEL == p.header.packet_type) + if (packet) { - if (11!=ntohs(p.header.payload_length) || PKT_CHAND!=p.field_id || 72!=p.payload.ambe3k.num_bits) - dump("Improper ambe packet:", &p, packet_size(p)); - buffer_depth--; - if (Encoding::dstar == type) - packet->SetDStarData(p.payload.ambe3k.data); - else - packet->SetDMRData(p.payload.ambe3k.data); + if (PKT_CHANNEL == p.header.packet_type) + { + if (11!=ntohs(p.header.payload_length) || PKT_CHAND!=p.field_id || 72!=p.payload.ambe3k.num_bits) + dump("Improper ambe packet:", &p, packet_size(p)); + buffer_depth--; + if (Encoding::dstar == type) + packet->SetDStarData(p.payload.ambe3k.data); + else + packet->SetDMRData(p.payload.ambe3k.data); - } - else if (PKT_SPEECH == p.header.packet_type) - { - if (322!=ntohs(p.header.payload_length) || PKT_SPEECHD!=p.field_id || 160!=p.payload.audio3k.num_samples) - dump("Improper audio packet:", &p, packet_size(p)); - buffer_depth--; - packet->SetAudioSamples(p.payload.audio3k.samples, true); - } - else - { - dump("ReadDevice() ERROR: Read an unexpected device packet:", &p, packet_size(p)); - return; - } - if (Encoding::dstar == type) // is this a DMR or a DStar device? - { - Controller.dstar_mux.lock(); - Controller.RouteDstPacket(packet); - Controller.dstar_mux.unlock(); - } - else - { - Controller.dmrst_mux.lock(); - Controller.RouteDmrPacket(packet); - Controller.dmrst_mux.unlock(); + } + else if (PKT_SPEECH == p.header.packet_type) + { + if (322!=ntohs(p.header.payload_length) || PKT_SPEECHD!=p.field_id || 160!=p.payload.audio3k.num_samples) + dump("Improper audio packet:", &p, packet_size(p)); + buffer_depth--; + packet->SetAudioSamples(p.payload.audio3k.samples, true); + } + else + { + dump("ReadDevice() ERROR: Read an unexpected device packet:", &p, packet_size(p)); + return; + } + if (Encoding::dstar == type) // is this a DMR or a DStar device? + { + Controller.dstar_mux.lock(); + Controller.RouteDstPacket(packet); + Controller.dstar_mux.unlock(); + } + else + { + Controller.dmrst_mux.lock(); + Controller.RouteDmrPacket(packet); + Controller.dmrst_mux.unlock(); + } } } diff --git a/DV3000.h b/DV3000.h index 58b8567..51ab591 100644 --- a/DV3000.h +++ b/DV3000.h @@ -29,7 +29,7 @@ protected: void PushWaitingPacket(unsigned int channel, std::shared_ptr packet); std::shared_ptr PopWaitingPacket(unsigned int channel); void ProcessPacket(const SDV_Packet &p); - bool SendAudio(const uint8_t channel, const int16_t *audio, const int gain) const; + bool SendAudio(const uint8_t channel, const int16_t *audio) const; bool SendData(const uint8_t channel, const uint8_t *data) const; private: diff --git a/DV3003.cpp b/DV3003.cpp index 7994ff6..6a59668 100644 --- a/DV3003.cpp +++ b/DV3003.cpp @@ -40,7 +40,11 @@ extern CController Controller; CDV3003::CDV3003(Encoding t) : CDVDevice(t) {} -CDV3003::~CDV3003() {} +CDV3003::~CDV3003() +{ + for (int i=0; i<3; i++) + waiting_packet[i].Shutdown(); +} void CDV3003::PushWaitingPacket(unsigned int channel, std::shared_ptr packet) { @@ -52,7 +56,7 @@ std::shared_ptr CDV3003::PopWaitingPacket(unsigned int channe return waiting_packet[channel].pop(); } -bool CDV3003::SendAudio(const uint8_t channel, const int16_t *audio, const int gain) const +bool CDV3003::SendAudio(const uint8_t channel, const int16_t *audio) const { // Create Audio packet based on input int8_ts SDV_Packet p; @@ -62,16 +66,8 @@ bool CDV3003::SendAudio(const uint8_t channel, const int16_t *audio, const int g p.field_id = channel + PKT_CHANNEL0; p.payload.audio.speechd = PKT_SPEECHD; p.payload.audio.num_samples = 160U; - const uint32_t g = abs(gain); - - for (int i=0; i<160; i++){ - if(gain < 0){ - p.payload.audio3k.samples[i] = htons(audio[i]) / g; - } - else{ - p.payload.audio3k.samples[i] = htons(audio[i]) * g; - } - } + for (int i=0; i<160; i++) + p.payload.audio.samples[i] = htons(audio[i]); // send audio packet to DV3000 const DWORD size = packet_size(p); @@ -125,39 +121,42 @@ void CDV3003::ProcessPacket(const SDV_Packet &p) { unsigned int channel = p.field_id - PKT_CHANNEL0; auto packet = PopWaitingPacket(channel); - if (PKT_CHANNEL == p.header.packet_type) + if (packet) { - if (12!=ntohs(p.header.payload_length) || PKT_CHAND!=p.payload.ambe.chand || 72!=p.payload.ambe.num_bits) - dump("Improper ambe packet:", &p, packet_size(p)); - buffer_depth--; - if (Encoding::dstar == type) - packet->SetDStarData(p.payload.ambe.data); - else - packet->SetDMRData(p.payload.ambe.data); + if (PKT_CHANNEL == p.header.packet_type) + { + if (12!=ntohs(p.header.payload_length) || PKT_CHAND!=p.payload.ambe.chand || 72!=p.payload.ambe.num_bits) + dump("Improper ambe packet:", &p, packet_size(p)); + buffer_depth--; + if (Encoding::dstar == type) + packet->SetDStarData(p.payload.ambe.data); + else + packet->SetDMRData(p.payload.ambe.data); - } - else if (PKT_SPEECH == p.header.packet_type) - { - if (323!=ntohs(p.header.payload_length) || PKT_SPEECHD!=p.payload.audio.speechd || 160!=p.payload.audio.num_samples) - dump("Improper audio packet:", &p, packet_size(p)); - buffer_depth--; - packet->SetAudioSamples(p.payload.audio.samples, true); - } - else - { - dump("ReadDevice() ERROR: Read an unexpected device packet:", &p, packet_size(p)); - return; - } - if (Encoding::dstar == type) // is this a DMR or a DStar device? - { - Controller.dstar_mux.lock(); - Controller.RouteDstPacket(packet); - Controller.dstar_mux.unlock(); - } - else - { - Controller.dmrst_mux.lock(); - Controller.RouteDmrPacket(packet); - Controller.dmrst_mux.unlock(); + } + else if (PKT_SPEECH == p.header.packet_type) + { + if (323!=ntohs(p.header.payload_length) || PKT_SPEECHD!=p.payload.audio.speechd || 160!=p.payload.audio.num_samples) + dump("Improper audio packet:", &p, packet_size(p)); + buffer_depth--; + packet->SetAudioSamples(p.payload.audio.samples, true); + } + else + { + dump("ReadDevice() ERROR: Read an unexpected device packet:", &p, packet_size(p)); + return; + } + if (Encoding::dstar == type) // is this a DMR or a DStar device? + { + Controller.dstar_mux.lock(); + Controller.RouteDstPacket(packet); + Controller.dstar_mux.unlock(); + } + else + { + Controller.dmrst_mux.lock(); + Controller.RouteDmrPacket(packet); + Controller.dmrst_mux.unlock(); + } } } diff --git a/DV3003.h b/DV3003.h index 78b05fd..d19bb37 100644 --- a/DV3003.h +++ b/DV3003.h @@ -29,7 +29,7 @@ protected: void PushWaitingPacket(unsigned int channel, std::shared_ptr packet); std::shared_ptr PopWaitingPacket(unsigned int channel); void ProcessPacket(const SDV_Packet &p); - bool SendAudio(const uint8_t channel, const int16_t *audio, const int gain) const; + bool SendAudio(const uint8_t channel, const int16_t *audio) const; bool SendData(const uint8_t channel, const uint8_t *data) const; private: diff --git a/DVSIDevice.cpp b/DVSIDevice.cpp index a59b0d2..ecb826e 100644 --- a/DVSIDevice.cpp +++ b/DVSIDevice.cpp @@ -7,7 +7,7 @@ */ // tcd - a hybid transcoder using DVSI hardware and Codec2 software -// Copyright © 2022 Thomas A. Early N7TAE +// Copyright © 2022-2023 Thomas A. Early N7TAE // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ // along with this program. If not, see . #include +#include #include #include #include @@ -49,6 +50,7 @@ CDVDevice::~CDVDevice() void CDVDevice::CloseDevice() { + input_queue.Shutdown(); keep_running = false; if (ftHandle) { @@ -140,7 +142,7 @@ bool CDVDevice::checkResponse(SDV_Packet &p, uint8_t response) const return false; } -bool CDVDevice::OpenDevice(const std::string &serialno, const std::string &desc, Edvtype dvtype, int16_t g) +bool CDVDevice::OpenDevice(const std::string &serialno, const std::string &desc, Edvtype dvtype, int8_t in_gain, int8_t out_gain) { auto status = FT_OpenEx((PVOID)serialno.c_str(), FT_OPEN_BY_SERIAL_NUMBER, &ftHandle); if (FT_OK != status) @@ -149,7 +151,6 @@ bool CDVDevice::OpenDevice(const std::string &serialno, const std::string &desc, return true; } - gain = g; std::this_thread::sleep_for(std::chrono::milliseconds(50)); FT_Purge(ftHandle, FT_PURGE_RX | FT_PURGE_TX ); std::this_thread::sleep_for(std::chrono::milliseconds(50)); @@ -240,7 +241,7 @@ bool CDVDevice::OpenDevice(const std::string &serialno, const std::string &desc, const uint8_t limit = (Edvtype::dv3000 == dvtype) ? PKT_CHANNEL0 : PKT_CHANNEL2; for (uint8_t ch=PKT_CHANNEL0; ch<=limit; ch++) { - if (ConfigureVocoder(ch, type)) + if (ConfigureVocoder(ch, type, in_gain, out_gain)) return true; } return false; @@ -388,7 +389,7 @@ void CDVDevice::Start() readFuture = std::async(std::launch::async, &CDVDevice::ReadDevice, this); } -bool CDVDevice::ConfigureVocoder(uint8_t pkt_ch, Encoding type) +bool CDVDevice::ConfigureVocoder(uint8_t pkt_ch, Encoding type, int8_t in_gain, int8_t out_gain) { SDV_Packet controlPacket, responsePacket; const uint8_t ecmode[] { PKT_ECMODE, 0x0, 0x0 }; @@ -397,7 +398,7 @@ bool CDVDevice::ConfigureVocoder(uint8_t pkt_ch, Encoding type) const uint8_t dmr[] { PKT_RATEP, 0x04U, 0x31U, 0x07U, 0x54U, 0x24U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x6FU, 0x48U }; const uint8_t chfmt[] { PKT_CHANFMT, 0x0, 0x0 }; const uint8_t spfmt[] { PKT_SPCHFMT, 0x0, 0x0 }; - const uint8_t gain[] { PKT_GAIN, 0x0, 0x0 }; + const uint8_t gain[] { PKT_GAIN, uint8_t(in_gain), uint8_t(out_gain) }; const uint8_t init[] { PKT_INIT, 0x3U }; const uint8_t resp[] { 0x0, PKT_ECMODE, 0x0, PKT_DCMODE, 0x0, PKT_RATEP, 0x0, PKT_CHANFMT, 0x0, PKT_SPCHFMT, 0x0, PKT_GAIN, 0x0, PKT_INIT, 0x0 }; @@ -511,7 +512,12 @@ bool CDVDevice::GetResponse(SDV_Packet &packet) void CDVDevice::AddPacket(const std::shared_ptr packet) { - input_queue.push(packet); + auto size = input_queue.push(packet); + if (size > 200) + { + std::cerr << ((type==Encoding::dstar) ? "DStar" : "DMR/YSF") << " inQ size is overflowing! Shutting down..." << std::endl; + raise(SIGINT); + } } void CDVDevice::dump(const char *title, const void *pointer, int length) const @@ -563,40 +569,42 @@ void CDVDevice::FeedDevice() const auto n = modules.size(); while (keep_running) { - auto packet = input_queue.pop(); // blocks until there is something to pop - - - while (keep_running) // wait until there is room - { - if (buffer_depth < 2) - break; - - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - } + auto packet = input_queue.pop(); // blocks until there is something to pop, unless shutting down - if (keep_running) + if (packet) { - auto index = modules.find(packet->GetModule()); - // save the packet in the vocoder's queue while the vocoder does its magic - if (std::string::npos == index) + while (keep_running) // wait until there is room { - std::cerr << "Module '" << packet->GetModule() << "' is not configured on " << description << std::endl; - } - else - { - PushWaitingPacket(index, packet); + if (buffer_depth < 2) + break; - const bool needs_audio = (Encoding::dstar==type) ? packet->DStarIsSet() : packet->DMRIsSet(); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } - if (needs_audio) + if (keep_running) + { + auto index = modules.find(packet->GetModule()); + // save the packet in the vocoder's queue while the vocoder does its magic + if (std::string::npos == index) { - SendData(index, (Encoding::dstar==type) ? packet->GetDStarData() : packet->GetDMRData()); + std::cerr << "Module '" << packet->GetModule() << "' is not configured on " << description << std::endl; } else { - SendAudio(index, packet->GetAudioSamples(), gain); + PushWaitingPacket(index, packet); + + const bool needs_audio = (Encoding::dstar==type) ? packet->DStarIsSet() : packet->DMRIsSet(); + + if (needs_audio) + { + SendData(index, (Encoding::dstar==type) ? packet->GetDStarData() : packet->GetDMRData()); + } + else + { + SendAudio(index, packet->GetAudioSamples()); + } + buffer_depth++; } - buffer_depth++; } } } @@ -614,15 +622,15 @@ void CDVDevice::ReadDevice() if (FT_OK != status) { FTDI_Error("FT_GetQueueStatus", status); + std::cerr << "Shutting down..." << std::endl; + raise(SIGTERM); } - if (RxBytes) - { - break; - } - else + if (0 == RxBytes) { - std::this_thread::sleep_for(std::chrono::milliseconds(2)); + std::this_thread::sleep_for(std::chrono::milliseconds(3)); + if (! keep_running) + return; } } diff --git a/DVSIDevice.h b/DVSIDevice.h index 0756901..dd6e1b7 100644 --- a/DVSIDevice.h +++ b/DVSIDevice.h @@ -32,7 +32,7 @@ public: CDVDevice(Encoding t); virtual ~CDVDevice(); - bool OpenDevice(const std::string &serialno, const std::string &desc, Edvtype dvtype, int16_t g); + bool OpenDevice(const std::string &serialno, const std::string &desc, Edvtype dvtype, int8_t in_gain, int8_t out_gain); void Start(); void CloseDevice(); void AddPacket(const std::shared_ptr packet); @@ -46,10 +46,9 @@ protected: CPacketQueue input_queue; std::future feedFuture, readFuture; std::string description, productid; - int16_t gain; bool DiscoverFtdiDevices(); - bool ConfigureVocoder(uint8_t pkt_ch, Encoding type); + bool ConfigureVocoder(uint8_t pkt_ch, Encoding type, int8_t in_gain, int8_t out_gain); bool checkResponse(SDV_Packet &responsePacket, uint8_t response) const; bool GetResponse(SDV_Packet &packet); bool InitDevice(); @@ -62,6 +61,6 @@ protected: virtual void PushWaitingPacket(unsigned int channel, std::shared_ptr packet) = 0; virtual std::shared_ptr PopWaitingPacket(unsigned int channel) = 0; virtual void ProcessPacket(const SDV_Packet &p) = 0; - virtual bool SendAudio(const uint8_t channel, const int16_t *audio, const int gain) const = 0; + virtual bool SendAudio(const uint8_t channel, const int16_t *audio) const = 0; virtual bool SendData(const uint8_t channel, const uint8_t *data) const = 0; }; diff --git a/Main.cpp b/Main.cpp index 647caa4..d27f7f7 100644 --- a/Main.cpp +++ b/Main.cpp @@ -1,5 +1,5 @@ // tcd - a hybrid transcoder using DVSI hardware and Codec2 software -// Copyright © 2021 Thomas A. Early N7TAE +// Copyright © 2021,2023 Thomas A. Early N7TAE // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,7 +27,7 @@ int main() if (Controller.Start()) return EXIT_FAILURE; - std::cout << "Hybrid Transcoder version 0.0.1 successfully started" << std::endl; + std::cout << "Hybrid Transcoder version 0.0.5 successfully started" << std::endl; pause(); diff --git a/PacketQueue.h b/PacketQueue.h index 8da8aba..d45735e 100644 --- a/PacketQueue.h +++ b/PacketQueue.h @@ -30,27 +30,59 @@ class CPacketQueue { public: - // blocks until there's something to pop + CPacketQueue() : keep_running(true) {} + std::shared_ptr pop() { - std::unique_lock lock(m); - while (q.empty()) { - c.wait(lock); + std::shared_ptr rval; // the return value + + std::unique_lock lock(mx); + + while (keep_running && q.empty()) + cv.wait(lock); + + if (keep_running) + { + rval = q.front(); + q.pop(); } - auto pack = q.front(); - q.pop(); - return pack; + else + { + while (! q.empty()) + q.pop(); + } + + return rval; + } + + std::size_t push(std::shared_ptr item) + { + std::unique_lock lock(mx); + bool was_empty = q.empty(); + q.push(item); + + if (was_empty) + cv.notify_one(); + + return q.size(); + } + + bool IsEmpty() + { + std::lock_guard lock(mx); + return q.empty(); } - void push(std::shared_ptr packet) + void Shutdown() { - std::lock_guard lock(m); - q.push(packet); - c.notify_one(); + std::lock_guard lock(mx); + keep_running = false; + cv.notify_all(); } private: + std::mutex mx; + std::condition_variable cv; std::queue> q; - mutable std::mutex m; - std::condition_variable c; + std::atomic keep_running; };