You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
8.8 KiB
C++

/*
Copyright (C) 2019-2021 Doug McLain
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "mode.h"
#include <iostream>
#ifdef Q_OS_WIN
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include "m17.h"
#include "ysf.h"
#include "dmr.h"
#include "p25.h"
#include "nxdn.h"
#include "ref.h"
#include "xrf.h"
#include "dcs.h"
#include "iax.h"
#ifdef USE_FLITE
extern "C" {
extern cst_voice * register_cmu_us_slt(const char *);
extern cst_voice * register_cmu_us_kal16(const char *);
extern cst_voice * register_cmu_us_awb(const char *);
}
#endif
Mode* Mode::create_mode(QString m)
{
Mode *mode = nullptr;
if(m == "M17"){
mode = new M17();
}
else if(m == "YSF" || m == "FCS"){
mode = new YSF();
}
else if(m == "DMR"){
mode = new DMR();
}
else if(m == "P25"){
mode = new P25();
}
else if(m == "NXDN"){
mode = new NXDN();
}
else if(m == "REF"){
mode = new REF();
}
else if(m == "XRF"){
mode = new XRF();
}
else if(m == "DCS"){
mode = new DCS();
}
else if(m == "IAX"){
mode = new IAX();
}
return mode;
}
Mode::Mode()
{
}
Mode::~Mode()
{
}
void Mode::init(QString callsign, uint32_t dmrid, uint16_t nxdnid, char module, QString refname, QString host, int port, bool ipv6, QString vocoder, QString modem, QString audioin, QString audioout, bool mdirect)
{
m_dmrid = dmrid;
m_nxdnid = nxdnid;
m_module = module;
m_refname = refname;
m_ipv6 = ipv6;
m_vocoder = vocoder;
m_modemport = modem;
m_audioin = audioin;
m_audioout = audioout;
m_mdirect = mdirect;
m_modem = nullptr;
m_ambedev = nullptr;
m_hwrx = false;
m_hwtx = false;
m_tx = false;
m_ttsid = 0;
m_rxwatchdog = 0;
m_modeinfo.callsign = callsign;
m_modeinfo.gwid = 0;
m_modeinfo.srcid = dmrid;
m_modeinfo.dstid = 0;
m_modeinfo.host = host;
m_modeinfo.port = port;
m_modeinfo.count = 0;
m_modeinfo.frame_number = 0;
m_modeinfo.frame_total = 0;
m_modeinfo.streamid = 0;
m_modeinfo.stream_state = STREAM_IDLE;
m_modeinfo.sw_vocoder_loaded = false;
m_modeinfo.hw_vocoder_loaded = false;
#ifdef Q_OS_WIN
m_rxtimerint = 19;
#else
m_rxtimerint = 20;
#endif
m_txtimerint = 19;
#ifdef USE_FLITE
flite_init();
voice_slt = register_cmu_us_slt(nullptr);
voice_kal = register_cmu_us_kal16(nullptr);
voice_awb = register_cmu_us_awb(nullptr);
#endif
}
void Mode::ambe_connect_status(bool s)
{
if(s){
#if !defined(Q_OS_IOS)
m_modeinfo.ambedesc = m_ambedev->get_ambe_description();
m_modeinfo.ambeprodid = m_ambedev->get_ambe_prodid();
m_modeinfo.ambeverstr = m_ambedev->get_ambe_verstring();
#endif
}
else{
m_modeinfo.ambeprodid = "Connect failed";
m_modeinfo.ambeverstr = "Connect failed";
}
emit update(m_modeinfo);
}
void Mode::mmdvm_connect_status(bool s)
{
if(s){
//m_modeinfo.mmdvmdesc = m_modem->get_mmdvm_description();
#if !defined(Q_OS_IOS)
m_modeinfo.mmdvm = m_modem->get_mmdvm_version();
#endif
}
else{
m_modeinfo.mmdvm = "Connect failed";
}
emit update(m_modeinfo);
}
void Mode::in_audio_vol_changed(qreal v)
{
m_audio->set_input_volume(v / m_attenuation);
}
void Mode::out_audio_vol_changed(qreal v)
{
m_audio->set_output_volume(v);
}
void Mode::agc_state_changed(int s)
{
m_audio->set_agc(s);
}
void Mode::begin_connect()
{
m_modeinfo.status = CONNECTING;
if((m_vocoder != "") && (m_mode != "M17")){
m_hwrx = true;
m_hwtx = true;
m_modeinfo.hw_vocoder_loaded = true;
#if !defined(Q_OS_IOS)
m_ambedev = new SerialAMBE(m_mode);
connect(m_ambedev, SIGNAL(connected(bool)), this, SLOT(ambe_connect_status(bool)));
connect(m_ambedev, SIGNAL(data_ready()), this, SLOT(get_ambe()));
connect(m_ambedev, SIGNAL(ambedev_ready()), this, SLOT(host_lookup()));
m_ambedev->connect_to_serial(m_vocoder);
#endif
}
else{
m_hwrx = false;
m_hwtx = false;
if(m_modemport == ""){
host_lookup();
}
}
if(m_modemport != ""){
#if !defined(Q_OS_IOS)
m_modem = new SerialModem(m_mode);
m_modem->set_modem_flags(m_rxInvert, m_txInvert, m_pttInvert, m_useCOSAsLockout, m_duplex);
m_modem->set_modem_params(m_baud, m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel, m_m17TXLevel);
connect(m_modem, SIGNAL(connected(bool)), this, SLOT(mmdvm_connect_status(bool)));
connect(m_modem, SIGNAL(modem_data_ready(QByteArray)), this, SLOT(process_modem_data(QByteArray)));
connect(m_modem, SIGNAL(modem_ready()), this, SLOT(host_lookup()));
m_modem->connect_to_serial(m_modemport);
#endif
}
}
void Mode::host_lookup()
{
if(m_mdirect && (m_mode == "M17")){ // MMDVM_DIRECT currently only supported by M17
mmdvm_direct_connect();
}
else if(m_ipv6 && (m_modeinfo.host != "none")){
qDebug() << "Host == " << m_modeinfo.host;
QList<QHostAddress> h;
QHostInfo i;
h.append(QHostAddress(m_modeinfo.host));
i.setAddresses(h);
hostname_lookup(i);
}
else{
QHostInfo::lookupHost(m_modeinfo.host, this, SLOT(hostname_lookup(QHostInfo)));
}
}
void Mode::toggle_tx(bool tx)
{
tx ? start_tx() : stop_tx();
}
void Mode::start_tx()
{
#if !defined(Q_OS_IOS)
if(m_hwtx){
m_ambedev->clear_queue();
}
#endif
m_txcodecq.clear();
m_tx = true;
m_txcnt = 0;
m_ttscnt = 0;
m_rxtimer->stop();
m_modeinfo.streamid = 0;
m_modeinfo.stream_state = TRANSMITTING;
#ifdef USE_FLITE
if(m_ttsid == 1){
tts_audio = flite_text_to_wave(m_ttstext.toStdString().c_str(), voice_kal);
}
else if(m_ttsid == 2){
tts_audio = flite_text_to_wave(m_ttstext.toStdString().c_str(), voice_awb);
}
else if(m_ttsid == 3){
tts_audio = flite_text_to_wave(m_ttstext.toStdString().c_str(), voice_slt);
}
#endif
if(!m_txtimer->isActive()){
if(m_ttsid == 0){
m_audio->set_input_buffer_size(640);
m_audio->start_capture();
//audioin->start(&audio_buffer);
}
m_txtimer->start(m_txtimerint);
}
}
void Mode::stop_tx()
{
m_tx = false;
}
bool Mode::load_vocoder_plugin()
{
#ifdef VOCODER_PLUGIN
QString config_path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_WIN)
config_path += "/dudetronics";
#endif
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
QString voc = config_path + "/vocoder_plugin." + QSysInfo::productType() + "." + QSysInfo::currentCpuArchitecture();
#else
QStringList l = QSysInfo::buildAbi().split('-');
//QString voc = config_path + "/vocoder_plugin." + QSysInfo::kernelType() + "." + QSysInfo::currentCpuArchitecture();
QString voc = config_path + "/vocoder_plugin." + QSysInfo::kernelType() + "." + l.at(0);
#endif
#if !defined(Q_OS_WIN)
//QString voc = "/mnt/data/src/mbe_vocoder/vocoder_plugin.linux.x86_64.so";
void* a = dlopen(voc.toLocal8Bit(), RTLD_LAZY);
if (!a) {
qDebug() << "Cannot load library: " << QString::fromLocal8Bit(dlerror());
return false;
}
dlerror();
create_t* create_a = (create_t*) dlsym(a, "create");
const char* dlsym_error = dlerror();
if (dlsym_error) {
qDebug() << "Cannot load symbol create: " << QString::fromLocal8Bit(dlsym_error);
return false;
}
m_mbevocoder = create_a();
qDebug() << voc + " loaded";
return true;
#else
HINSTANCE hinstvoclib;
hinstvoclib = LoadLibrary(reinterpret_cast<LPCWSTR>(voc.utf16()));
if (hinstvoclib != NULL) {
create_t* create_a = (create_t*)GetProcAddress(hinstvoclib, "create");
if (create_a != NULL) {
m_mbevocoder = create_a();
qDebug() << voc + " loaded";
return true;
}
else{
return false;
}
}
else{
return false;
}
#endif
#else
#if defined(Q_OS_IOS)
QString config_path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/dudetronics";
QString voc = config_path + "/vocoder_plugin." + QSysInfo::productType() + "." + QSysInfo::currentCpuArchitecture();
if(QFileInfo::exists(voc)){
m_mbevocoder = new VocoderPlugin();
return true;
}
else{
return false;
}
#else
m_mbevocoder = new VocoderPlugin();
return true;
#endif
#endif
}
void Mode::deleteLater()
{
if(m_modeinfo.status == CONNECTED_RW){
//m_udp->disconnect();
//m_ping_timer->stop();
send_disconnect();
delete m_audio;
#if !defined(Q_OS_IOS)
if(m_hwtx){
delete m_ambedev;
}
if(m_modem){
delete m_modem;
}
#endif
}
m_modeinfo.count = 0;
QObject::deleteLater();
}