/* 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 . */ #include "mode.h" #include #ifdef Q_OS_WIN #include #else #include #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_mbevocoder = 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 m_debug = false; } 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 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(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 qDebug() << "new vocoder"; m_mbevocoder = new VocoderPlugin(); return true; #endif } void Mode::deleteLater() { if(m_modeinfo.status == CONNECTED_RW){ //m_udp->disconnect(); //m_ping_timer->stop(); send_disconnect(); delete m_audio; //if(m_mbevocoder != nullptr) delete m_mbevocoder; #if !defined(Q_OS_IOS) if(m_hwtx){ delete m_ambedev; } if(m_modem){ delete m_modem; } #endif } m_modeinfo.count = 0; QObject::deleteLater(); }