From ff6d0a6a65d040967564205de44af26417407e5e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 4 Jan 2022 18:34:34 +0000 Subject: [PATCH 001/323] Merge settings branch --- CHANGELOG | 119 ++ WHATSNEW | 9 +- aboutbox.cpp | 10 +- rigcommander.cpp | 800 +++++++++++--- rigcommander.h | 110 +- rigctld.cpp | 589 +++++----- rigctld.h | 13 +- rigstate.h | 127 +++ udpserver.cpp | 2 + udpserver.h | 23 +- udpserversetup.cpp | 127 --- udpserversetup.h | 60 - wfmain.ui | 2385 +++++++++++++++++++++++++++------------- wfview.pro | 2 - wfview.sln | 27 +- wfview.vcxproj | 377 ++----- wfview.vcxproj.filters | 8 - wfview.vcxproj.user | 8 +- 18 files changed, 3027 insertions(+), 1769 deletions(-) create mode 100644 rigstate.h delete mode 100644 udpserversetup.cpp delete mode 100644 udpserversetup.h diff --git a/CHANGELOG b/CHANGELOG index dea2e59..22ac0c0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,124 @@ # CHANGELOG + +- 20211201 + + Another "minor" update for RX only rigs + + Disable certain TX commands for RX only rigs + +- 20211222 + + Add mutex within rigState to protect access + +- 20211119 + + Add more version info + + Add --version command line argument and WFVIEW_VERSION #define + +- 20211118 + + A little less green in our gray. + + Added size rules for audio source combo boxes. + + Fix silly bug in retransmit code + + Remove some extra logging from audio + +- 20211115 + + Add mutex for incoming audio on udp and server + + Force PA to use 48K Sample Rate if default is 44.1K + + Try using slot for audio again + +- 20211110 + + Server only tries mutex lock for 10ms before giving up. + + Check number of samples in opus packet before attempting decode + +- 20211109 + + Small changes to audio handler + +- 20211107 + + Add SSE2 enhancements to resampler + + Update audiohandler.h + + Enable SSE or NEON enhancements for resampler + + Fix resample ratio for input and output (hopefully!) + + Create resampleRatio + + Close PA stream after stopping it! + + Fix Linux rtaudio build + + Pulled out some debug code that isn't needed. + + Fixed manual rig ID issue with uninitialized variable. + +- 20211106 + + Added override allowing the user-specified CI-V address to also be used + as the Rig ID (model). This is useful for older radios that do not reply + to Rig ID queries. For radios using the default CI-V address, it should + "just work". + + Added PTT "snooping" to the commHandler, such that radios needing RTS + set for PTT will work. Includes replying to PTT queries with the RTS + status. There is currently no UI to manually select radios that need + RTS. + + deleted a lingering swapfile from one of my vim sessions + +- 20211105 + + Added RTS PTT support commHandler and rigCommander. RTS is only sent + from rigCommander at this time, the pty is not parsed. + + Added geometry constraints to the transceiver adjustments window, and + disable controls which do not function except for debug builds. + + + Changed IF/TPBF commands to be "unique priority" inserts. Added "more" + button for extended transceiver controls. + + Added an IF Shift-like control for radios with Twin PBF. + + Added support for IF Shift and Twin Pass-Band Filters. Currently + accessable only via the debug button. + +- 20211104 + Added IC-736 FM mode + + Added code to force IC-736 to rigID + +- 20211101 + + Use QT Audio by default + + remove unneeded audio signal handler + + Add portaudio support + + Make switching between audio apis easier (and tidy .pro file) + + Use buffered audio for Linux (was just Mac only) + + Adjust buffer size depending on latency setting + +- 20211031 + + Stuff audio buffer directly rather than signal/slot + - 20211022 Don't block until audio buffer has space diff --git a/WHATSNEW b/WHATSNEW index 33042e3..30289b7 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -10,10 +10,13 @@ The following highlights are in this 1.x-release: rigctl basic split support rigctl prevents switching off civ transceive added 25 kHz step - as a temporary measure sending multiple TX/FREQ change commands to the rig when we use rigctld. - people should use "fake it" in wsjtx as the split code is not reliable. - tidied up udp server function for better reliability + added some IC736 stuff + added portaudio (you need t change wfview.pro to select + and that lowers the latency to maybe less than 50 ms + added PBT and IF-shift + several bugs fixed + RTS as PTT for several radios like the 706/718/736… diff --git a/aboutbox.cpp b/aboutbox.cpp index dadd943..ba8de0f 100644 --- a/aboutbox.cpp +++ b/aboutbox.cpp @@ -19,10 +19,16 @@ aboutbox::aboutbox(QWidget *parent) : QString nacode = QString("

Networking, audio, rigctl server, and much more written by Phil Taylor, M0VSE"); QString doctest = QString("

Testing, documentation, bug fixes, and development mentorship from
Roeland Jansen, PA3MET, and Jim Nijkamp, PA8E."); - +#if defined(Q_OS_LINUX) QString ssCredit = QString("

Stylesheet qdarkstyle used under MIT license, stored in /usr/share/wfview/stylesheets/."); +#else + QString ssCredit = QString("

Stylesheet qdarkstyle used under MIT license."); +#endif QString website = QString("

Please visit https://wfview.org/ for the latest information."); + + QString donate = QString("

Join us on Patreon for a behind-the-scenes look at wfview development, nightly builds, and to support the software you love."); + QString docs = QString("

Be sure to check the User Manual and the Forum if you have any questions."); QString support = QString("

For support, please visit the official wfview support forum."); QString gitcodelink = QString("").arg(GITSHORT); @@ -78,7 +84,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."); // String it all together: QString aboutText = head + copyright + "\n" + nacode + "\n" + doctest + wfviewcommunityack; - aboutText.append(website + "\n"+ docs + support + contact +"\n"); + aboutText.append(website + "\n" + donate + "\n"+ docs + support + contact +"\n"); aboutText.append("\n" + ssCredit + "\n" + rsCredit + "\n"); #if defined(RTAUDIO) diff --git a/rigcommander.cpp b/rigcommander.cpp index ed75bf9..d497c0d 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -22,20 +22,12 @@ rigCommander::rigCommander() { - rigState.mutex = new QMutex(); - QMutexLocker locker(rigState.mutex); - rigState.filter = 0; - rigState.mode = 0; - rigState.ptt = 0; - rigState.currentVfo = 0; - rigState.duplex = dmSplitOff; - + state.set(SCOPEFUNC,true,false); } rigCommander::~rigCommander() { closeComm(); - delete rigState.mutex; } @@ -147,6 +139,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud connect(this, SIGNAL(discoveredRigID(rigCapabilities)), ptty, SLOT(receiveFoundRigID(rigCapabilities))); emit haveAfGain(rxSetup.localAFgain); + localVolume = rxSetup.localAFgain; } // data from the comm port to the program: @@ -224,6 +217,20 @@ void rigCommander::receiveBaudRate(quint32 baudrate) { emit haveBaudRate(baudrate); } +void rigCommander::setRTSforPTT(bool enabled) +{ + if(!usingNativeLAN) + { + useRTSforPTT_isSet = true; + useRTSforPTT_manual = enabled; + if(comm != NULL) + { + rigCaps.useRTSforPTT=enabled; + comm->setUseRTSforPTT(enabled); + } + } +} + void rigCommander::findRigs() { // This function sends data to 0x00 ("broadcast") to look for any connected rig. @@ -266,7 +273,26 @@ void rigCommander::powerOn() { QByteArray payload; - for(int i=0; i < 150; i++) + int numFE=150; + switch (this->rigBaudRate) { + case 57600: + numFE = 75; + break; + case 38400: + numFE = 50; + break; + case 19200: + numFE = 25; + break; + case 9600: + numFE = 13; + break; + case 4800: + numFE = 7; + break; + } + + for(int i=0; i < numFE; i++) { payload.append("\xFE"); } @@ -572,16 +598,10 @@ void rigCommander::setFrequency(unsigned char vfo, freqt freq) cmdPayload.append(freqPayload); if (vfo == 0) { - rigState.mutex->lock(); - rigState.vfoAFreq = freq; - rigState.mutex->unlock(); cmdPayload.prepend('\x00'); } else { - rigState.mutex->lock(); - rigState.vfoBFreq = freq; - rigState.mutex->unlock(); cmdPayload.prepend(vfo); cmdPayload.prepend('\x25'); } @@ -709,10 +729,6 @@ void rigCommander::setMode(mode_info m) payload.append(m.filter); prepDataAndSend(payload); - - QMutexLocker locker(rigState.mutex); - rigState.mode = m.reg; - rigState.filter = m.filter; } void rigCommander::setMode(unsigned char mode, unsigned char modeFilter) @@ -742,10 +758,6 @@ void rigCommander::setMode(unsigned char mode, unsigned char modeFilter) } prepDataAndSend(payload); - QMutexLocker locker(rigState.mutex); - rigState.mode = mode; - rigState.filter = modeFilter; - } } @@ -763,8 +775,6 @@ void rigCommander::setDataMode(bool dataOn, unsigned char filter) payload.append("\x00\x00", 2); // data mode off, bandwidth not defined per ICD. } prepDataAndSend(payload); - QMutexLocker locker(rigState.mutex); - rigState.datamode = dataOn; } void rigCommander::getFrequency() @@ -1008,9 +1018,9 @@ void rigCommander::getPTT() //{ // emit havePTTStatus(comm->rtsStatus()); //} else { - QByteArray payload; - payload.setRawData("\x1C\x00", 2); - prepDataAndSend(payload); + QByteArray payload; + payload.setRawData("\x1C\x00", 2); + prepDataAndSend(payload); //} } @@ -1031,8 +1041,6 @@ void rigCommander::setPTT(bool pttOn) QByteArray payload("\x1C\x00", 2); payload.append((char)pttOn); prepDataAndSend(payload); - QMutexLocker locker(rigState.mutex); - rigState.ptt = pttOn; } } @@ -1253,22 +1261,16 @@ void rigCommander::parseCommand() break; case '\x0F': emit haveDuplexMode((duplexMode)(unsigned char)payloadIn[1]); - rigState.mutex->lock(); - rigState.duplex = (duplexMode)(unsigned char)payloadIn[1]; - rigState.mutex->unlock(); + state.set(DUPLEX, (duplexMode)(unsigned char)payloadIn[1], false); break; case '\x11': emit haveAttenuator((unsigned char)payloadIn.at(1)); - rigState.mutex->lock(); - rigState.attenuator = (unsigned char)payloadIn.at(1); - rigState.mutex->unlock(); + state.set(ATTENUATOR, (quint8)payloadIn[1], false); break; case '\x12': emit haveAntenna((unsigned char)payloadIn.at(1), (bool)payloadIn.at(2)); - rigState.mutex->lock(); - rigState.antenna = (unsigned char)payloadIn.at(1); - rigState.rxAntenna = (bool)payloadIn.at(2); - rigState.mutex->unlock(); + state.set(ANTENNA, (quint8)payloadIn[1], false); + state.set(RXANTENNA, (bool)payloadIn[2], false); break; case '\x14': // read levels @@ -1372,24 +1374,21 @@ void rigCommander::parseLevels() // AF level - ignore if LAN connection. if (udp == Q_NULLPTR) { emit haveAfGain(level); - rigState.mutex->lock(); - rigState.afGain = level; - rigState.mutex->unlock(); + state.set(AFGAIN, level, false); + } + else { + state.set(AFGAIN, localVolume, false); } break; case '\x02': // RX RF Gain emit haveRfGain(level); - rigState.mutex->lock(); - rigState.rfGain = level; - rigState.mutex->unlock(); + state.set(RFGAIN, level, false); break; case '\x03': // Squelch level - emit haveSql(level); - rigState.mutex->lock(); - rigState.squelch = level; - rigState.mutex->unlock(); + emit haveSql(level); + state.set(SQUELCH, level, false); break; case '\x07': // Twin BPF Inner, or, IF-Shift level @@ -1408,16 +1407,12 @@ void rigCommander::parseLevels() case '\x0A': // TX RF level emit haveTxPower(level); - rigState.mutex->lock(); - rigState.txPower = level; - rigState.mutex->unlock(); + state.set(TXPOWER, level, false); break; case '\x0B': // Mic Gain emit haveMicGain(level); - rigState.mutex->lock(); - rigState.micGain = level; - rigState.mutex->unlock(); + state.set(MICGAIN, level, false); break; case '\x0C': // CW Keying Speed - ignore for now @@ -1428,9 +1423,7 @@ void rigCommander::parseLevels() case '\x0E': // compressor level emit haveCompLevel(level); - rigState.mutex->lock(); - rigState.compLevel = level; - rigState.mutex->unlock(); + state.set(COMPLEVEL, level, false); break; case '\x12': // NB level - ignore for now @@ -1438,23 +1431,17 @@ void rigCommander::parseLevels() case '\x15': // monitor level emit haveMonitorLevel(level); - rigState.mutex->lock(); - rigState.monitorLevel = level; - rigState.mutex->unlock(); + state.set(MONITORLEVEL, level, false); break; case '\x16': // VOX gain emit haveVoxGain(level); - rigState.mutex->lock(); - rigState.voxGain = level; - rigState.mutex->unlock(); + state.set(VOXGAIN, level, false); break; case '\x17': // anti-VOX gain emit haveAntiVoxGain(level); - rigState.mutex->lock(); - rigState.antiVoxGain = level; - rigState.mutex->unlock(); + state.set(ANTIVOXGAIN, level, false); break; default: @@ -1472,58 +1459,42 @@ void rigCommander::parseLevels() case '\x02': // S-Meter emit haveMeter(meterS, level); - rigState.mutex->lock(); - rigState.sMeter = level; - rigState.mutex->unlock(); + state.set(SMETER, level, false); break; case '\x04': // Center (IC-R8600) emit haveMeter(meterCenter, level); - rigState.mutex->lock(); - rigState.sMeter = level; - rigState.mutex->unlock(); + state.set(SMETER, level, false); break; case '\x11': // RF-Power meter emit haveMeter(meterPower, level); - rigState.mutex->lock(); - rigState.powerMeter = level; - rigState.mutex->unlock(); + state.set(POWERMETER, level, false); break; case '\x12': // SWR emit haveMeter(meterSWR, level); - rigState.mutex->lock(); - rigState.swrMeter = level; - rigState.mutex->unlock(); + state.set(SWRMETER, level, false); break; case '\x13': // ALC emit haveMeter(meterALC, level); - rigState.mutex->lock(); - rigState.alcMeter = level; - rigState.mutex->unlock(); + state.set(ALCMETER, level, false); break; case '\x14': // COMP dB reduction emit haveMeter(meterComp, level); - rigState.mutex->lock(); - rigState.compMeter = level; - rigState.mutex->unlock(); + state.set(COMPMETER, level, false); break; case '\x15': // VD (12V) emit haveMeter(meterVoltage, level); - rigState.mutex->lock(); - rigState.voltageMeter = level; - rigState.mutex->unlock(); + state.set(VOLTAGEMETER, level, false); break; case '\x16': // ID emit haveMeter(meterCurrent, level); - rigState.mutex->lock(); - rigState.currentMeter = level; - rigState.mutex->unlock(); + state.set(CURRENTMETER, level, false); break; default: @@ -1747,6 +1718,20 @@ void rigCommander::setModInputLevel(rigInput input, unsigned char level) } } +void rigCommander::setAfMute(bool gainOn) +{ + QByteArray payload("\x1a\x09"); + payload.append((quint8)gainOn); + prepDataAndSend(payload); +} + +void rigCommander::setDialLock(bool lockOn) +{ + QByteArray payload("\x16\x50"); + payload.append((quint8)lockOn); + prepDataAndSend(payload); +} + void rigCommander::getModInputLevel(rigInput input) { switch(input) @@ -1780,6 +1765,18 @@ void rigCommander::getModInputLevel(rigInput input) } } +void rigCommander::getAfMute() +{ + QByteArray payload("\x1a\x09"); + prepDataAndSend(payload); +} + +void rigCommander::getDialLock() +{ + QByteArray payload("\x16\x50"); + prepDataAndSend(payload); +} + QByteArray rigCommander::getUSBAddr() { QByteArray payload; @@ -2154,6 +2151,7 @@ void rigCommander::setAfGain(unsigned char level) } else { emit haveSetVolume(level); + localVolume = level; } } @@ -2390,6 +2388,7 @@ void rigCommander::parseRegister21() break; ritHz = f.Hz*((payloadIn.at(4)=='\x01')?-1:1); emit haveRitFrequency(ritHz); + state.set(RITVALUE, ritHz, false); break; case '\x01': // RIT on/off @@ -2399,6 +2398,7 @@ void rigCommander::parseRegister21() } else { emit haveRitEnabled(false); } + state.set(RITFUNC, (bool)payloadIn.at(02), false); break; case '\x02': // Delta TX setting on/off @@ -2416,6 +2416,8 @@ void rigCommander::parseATU() // [1]: 0x01 // [2]: 0 = off, 0x01 = on, 0x02 = tuning in-progress emit haveATUStatus((unsigned char) payloadIn[2]); + // This is a bool so any non-zero will mean enabled. + state.set(TUNERFUNC, (bool)payloadIn[2], false); } void rigCommander::parsePTT() @@ -2430,9 +2432,7 @@ void rigCommander::parsePTT() // PTT on emit havePTTStatus(true); } - QMutexLocker locker(rigState.mutex); - rigState.ptt = (bool)payloadIn[2]; - + state.set(PTT,(bool)payloadIn[2],false); } void rigCommander::parseRegisters1A() @@ -2450,7 +2450,6 @@ void rigCommander::parseRegisters1A() // "INDEX: 00 01 02 03 04 " // "DATA: 1a 06 01 03 fd " (data mode enabled, filter width 3 selected) - QMutexLocker locker(rigState.mutex); switch(payloadIn[01]) { @@ -2461,6 +2460,9 @@ void rigCommander::parseRegisters1A() // band stacking register parseBandStackReg(); break; + case '\x04': + state.set(AGC, (quint8)payloadIn[2], false); + break; case '\x06': // data mode // emit havedataMode( (bool) payloadIn[somebit]) @@ -2471,11 +2473,13 @@ void rigCommander::parseRegisters1A() // YY: filter selected, 01 through 03.; // if YY is 00 then XX was also set to 00 emit haveDataMode((bool)payloadIn[03]); - rigState.datamode = (bool)payloadIn[03]; + state.set(DATAMODE, (quint8)payloadIn[3], false); break; case '\x07': // IP+ status break; + case '\x09': + state.set(MUTEFUNC, (quint8)payloadIn[2], false); default: break; } @@ -2493,32 +2497,24 @@ void rigCommander::parseRegister1B() // "Repeater tone" tone = decodeTone(payloadIn); emit haveTone(tone); - rigState.mutex->lock(); - rigState.ctcss = tone; - rigState.mutex->unlock(); + state.set(CTCSS, tone, false); break; case '\x01': // "TSQL tone" tone = decodeTone(payloadIn); emit haveTSQL(tone); - rigState.mutex->lock(); - rigState.tsql = tone; - rigState.mutex->unlock(); + state.set(TSQL, tone, false); break; case '\x02': // DTCS (DCS) tone = decodeTone(payloadIn, tinv, rinv); emit haveDTCS(tone, tinv, rinv); - rigState.mutex->lock(); - rigState.dtcs = tone; - rigState.mutex->unlock(); + state.set(DTCS, tone, false); break; case '\x07': // "CSQL code (DV mode)" tone = decodeTone(payloadIn); - rigState.mutex->lock(); - rigState.csql = tone; - rigState.mutex->unlock(); + state.set(CSQL, tone, false); break; default: break; @@ -2539,9 +2535,52 @@ void rigCommander::parseRegister16() case '\x02': // Preamp emit havePreamp((unsigned char)payloadIn.at(2)); - rigState.mutex->lock(); - rigState.preamp = (unsigned char)payloadIn.at(2); - rigState.mutex->unlock(); + state.set(PREAMP, (quint8)payloadIn.at(2), false); + break; + case '\x22': + state.set(NBFUNC, payloadIn.at(2) != 0, false); + break; + case '\x40': + state.set(NRFUNC, payloadIn.at(2) != 0, false); + break; + case '\x41': // Auto notch + state.set(ANFFUNC, payloadIn.at(2) != 0, false); + break; + case '\x42': + state.set(TONEFUNC, payloadIn.at(2) != 0, false); + break; + case '\x43': + state.set(TSQLFUNC, payloadIn.at(2) != 0, false); + break; + case '\x44': + state.set(COMPFUNC, payloadIn.at(2) != 0, false); + break; + case '\x45': + state.set(MONFUNC, payloadIn.at(2) != 0, false); + break; + case '\x46': + state.set(VOXFUNC, payloadIn.at(2) != 0, false); + break; + case '\x47': + if (payloadIn.at(2) == '\00') { + state.set(FBKINFUNC, false, false); + state.set(SBKINFUNC, false, false); + } + else if (payloadIn.at(2) == '\01') { + state.set(FBKINFUNC, false, false); + state.set(SBKINFUNC, true, false); + + } + else if (payloadIn.at(2) == '\02') { + state.set(FBKINFUNC, true, false); + state.set(SBKINFUNC, false, false); + } + break; + case '\x48': // Manual Notch + state.set(MNFUNC, payloadIn.at(2) != 0, false); + break; + case '\x50': // Dial lock + state.set(LOCKFUNC, payloadIn.at(2) != 0, false); break; default: break; @@ -2847,6 +2886,7 @@ void rigCommander::parseWFData() break; case 0x10: // confirming scope is on + state.set(SCOPEFUNC, (bool)payloadIn[2], true); break; case 0x11: // confirming output enabled/disabled of wf data. @@ -3570,8 +3610,13 @@ void rigCommander::determineRigCaps() haveRigCaps = true; if(!usingNativeLAN) + { + if(useRTSforPTT_isSet) + { + rigCaps.useRTSforPTT = useRTSforPTT_manual; + } comm->setUseRTSforPTT(rigCaps.useRTSforPTT); - + } if(lookingForRig) { @@ -3815,49 +3860,52 @@ void rigCommander::parseFrequency() // printHex(payloadIn, false, true); frequencyMhz = 0.0; - if(payloadIn.length() == 7) + if (payloadIn.length() == 7) { // 7300 has these digits too, as zeros. // IC-705 or IC-9700 with higher frequency data available. - frequencyMhz += 100*(payloadIn[05] & 0x0f); - frequencyMhz += (1000*((payloadIn[05] & 0xf0) >> 4)); + frequencyMhz += 100 * (payloadIn[05] & 0x0f); + frequencyMhz += (1000 * ((payloadIn[05] & 0xf0) >> 4)); - freq.Hz += (payloadIn[05] & 0x0f) * 1E6 * 100; - freq.Hz += ((payloadIn[05] & 0xf0) >> 4) * 1E6 * 1000; + freq.Hz += (payloadIn[05] & 0x0f) * 1E6 * 100; + freq.Hz += ((payloadIn[05] & 0xf0) >> 4) * 1E6 * 1000; } freq.Hz += (payloadIn[04] & 0x0f) * 1E6; - freq.Hz += ((payloadIn[04] & 0xf0) >> 4) * 1E6 * 10; + freq.Hz += ((payloadIn[04] & 0xf0) >> 4) * 1E6 * 10; frequencyMhz += payloadIn[04] & 0x0f; - frequencyMhz += 10*((payloadIn[04] & 0xf0) >> 4); + frequencyMhz += 10 * ((payloadIn[04] & 0xf0) >> 4); // KHz land: - frequencyMhz += ((payloadIn[03] & 0xf0) >>4)/10.0 ; + frequencyMhz += ((payloadIn[03] & 0xf0) >> 4) / 10.0; frequencyMhz += (payloadIn[03] & 0x0f) / 100.0; frequencyMhz += ((payloadIn[02] & 0xf0) >> 4) / 1000.0; frequencyMhz += (payloadIn[02] & 0x0f) / 10000.0; - frequencyMhz += ((payloadIn[01] & 0xf0) >> 4) / 100000.0; - frequencyMhz += (payloadIn[01] & 0x0f) / 1000000.0; + frequencyMhz += ((payloadIn[01] & 0xf0) >> 4) / 100000.0; + frequencyMhz += (payloadIn[01] & 0x0f) / 1000000.0; freq.Hz += payloadIn[01] & 0x0f; - freq.Hz += ((payloadIn[01] & 0xf0) >> 4)* 10; + freq.Hz += ((payloadIn[01] & 0xf0) >> 4) * 10; - freq.Hz += (payloadIn[02] & 0x0f) * 100; - freq.Hz += ((payloadIn[02] & 0xf0) >> 4) * 1000; + freq.Hz += (payloadIn[02] & 0x0f) * 100; + freq.Hz += ((payloadIn[02] & 0xf0) >> 4) * 1000; - freq.Hz += (payloadIn[03] & 0x0f) * 10000; - freq.Hz += ((payloadIn[03] & 0xf0) >>4) * 100000; + freq.Hz += (payloadIn[03] & 0x0f) * 10000; + freq.Hz += ((payloadIn[03] & 0xf0) >> 4) * 100000; freq.MHzDouble = frequencyMhz; - - rigState.mutex->lock(); - rigState.vfoAFreq = freq; - rigState.mutex->unlock(); - + + if (state.getChar(CURRENTVFO) == 0) { + state.set(VFOAFREQ, freq.Hz, false); + } + else { + state.set(VFOBFREQ, freq.Hz, false); + } + emit haveFrequency(freq); } @@ -3935,11 +3983,9 @@ void rigCommander::parseMode() } else { filter = 0; } - rigState.mutex->lock(); - rigState.mode = (unsigned char)payloadIn[01]; - rigState.filter = filter; - rigState.mutex->unlock(); emit haveMode((unsigned char)payloadIn[01], filter); + state.set(MODE,(unsigned char)payloadIn[01],false); + state.set(FILTER,filter,false); } @@ -4014,6 +4060,144 @@ void rigCommander::setAntenna(unsigned char ant, bool rx) prepDataAndSend(payload); } +void rigCommander::setNb(bool enabled) { + QByteArray payload("\x16\x22"); + payload.append((unsigned char)enabled); + prepDataAndSend(payload); +} + +void rigCommander::getNb() +{ + QByteArray payload; + payload.setRawData("\x16\x22", 2); + prepDataAndSend(payload); +} + +void rigCommander::setNr(bool enabled) { + QByteArray payload("\x16\x40"); + payload.append((unsigned char)enabled); + prepDataAndSend(payload); +} + +void rigCommander::getNr() +{ + QByteArray payload; + payload.setRawData("\x16\x40", 2); + prepDataAndSend(payload); +} + +void rigCommander::setAutoNotch(bool enabled) +{ + QByteArray payload("\x16\x41"); + payload.append((unsigned char)enabled); + prepDataAndSend(payload); +} + +void rigCommander::getAutoNotch() +{ + QByteArray payload; + payload.setRawData("\x16\x41", 2); + prepDataAndSend(payload); +} + +void rigCommander::setToneEnabled(bool enabled) +{ + QByteArray payload("\x16\x42"); + payload.append((unsigned char)enabled); + prepDataAndSend(payload); +} + +void rigCommander::getToneEnabled() +{ + QByteArray payload; + payload.setRawData("\x16\x42", 2); + prepDataAndSend(payload); +} + +void rigCommander::setToneSql(bool enabled) +{ + QByteArray payload("\x16\x43"); + payload.append((unsigned char)enabled); + prepDataAndSend(payload); +} + +void rigCommander::getToneSql() +{ + QByteArray payload; + payload.setRawData("\x16\x43", 2); + prepDataAndSend(payload); +} + +void rigCommander::setCompressor(bool enabled) +{ + QByteArray payload("\x16\x44"); + payload.append((unsigned char)enabled); + prepDataAndSend(payload); +} + +void rigCommander::getCompressor() +{ + QByteArray payload; + payload.setRawData("\x16\x44", 2); + prepDataAndSend(payload); +} + +void rigCommander::setMonitor(bool enabled) +{ + QByteArray payload("\x16\x45"); + payload.append((unsigned char)enabled); + prepDataAndSend(payload); +} + +void rigCommander::getMonitor() +{ + QByteArray payload; + payload.setRawData("\x16\x45", 2); + prepDataAndSend(payload); +} + +void rigCommander::setVox(bool enabled) +{ + QByteArray payload("\x16\x46"); + payload.append((unsigned char)enabled); + prepDataAndSend(payload); +} + +void rigCommander::getVox() +{ + QByteArray payload; + payload.setRawData("\x16\x46", 2); + prepDataAndSend(payload); +} + +void rigCommander::setBreakIn(unsigned char type) { + QByteArray payload("\x16\x47"); + payload.append((unsigned char)type); + prepDataAndSend(payload); +} + +void rigCommander::getBreakIn() +{ + QByteArray payload; + payload.setRawData("\x16\x47", 2); + prepDataAndSend(payload); +} + +void rigCommander::setManualNotch(bool enabled) +{ + QByteArray payload("\x16\x48"); + payload.append((unsigned char)enabled); + prepDataAndSend(payload); +} + +void rigCommander::getManualNotch() +{ + QByteArray payload; + payload.setRawData("\x16\x48", 2); + prepDataAndSend(payload); +} + + void rigCommander::getRigID() { QByteArray payload; @@ -4086,7 +4270,345 @@ QByteArray rigCommander::stripData(const QByteArray &data, unsigned char cutPosi void rigCommander::sendState() { - emit stateInfo(&rigState); + emit stateInfo(&state); +} + +void rigCommander::stateUpdated() +{ + // A remote process has updated the rigState + // First we need to find which item(s) have been updated and send the command(s) to the rig. + + QMap::iterator i = state.map.begin(); + while (i != state.map.end()) { + if (!i.value()._valid || i.value()._updated) + { + i.value()._updated = false; + i.value()._valid = true; // Set value to valid as we have requested it (even if we haven't had a response) + qDebug(logRigCtlD()) << "Got new value:" << i.key() << "=" << i.value()._value; + switch (i.key()) { + case VFOAFREQ: + if (i.value()._valid) { + freqt freq; + freq.Hz = state.getInt64(VFOAFREQ); + setFrequency(0, freq); + setFrequency(0, freq); + setFrequency(0, freq); + } + getFrequency(); + break; + case VFOBFREQ: + if (i.value()._valid) { + freqt freq; + freq.Hz = state.getInt64(VFOBFREQ); + setFrequency(1, freq); + setFrequency(1, freq); + setFrequency(1, freq); + } + getFrequency(); + break; + case CURRENTVFO: + // Work on VFOB - how do we do this? + break; + case PTT: + if (i.value()._valid) { + setPTT(state.getBool(PTT)); + setPTT(state.getBool(PTT)); + setPTT(state.getBool(PTT)); + } + getPTT(); + break; + case MODE: + case FILTER: + if (i.value()._valid) { + setMode(state.getChar(MODE), state.getChar(FILTER)); + } + getMode(); + break; + case DUPLEX: + if (i.value()._valid) { + setDuplexMode(state.getDuplex(DUPLEX)); + } + getDuplexMode(); + break; + case DATAMODE: + if (i.value()._valid) { + setDataMode(state.getBool(DATAMODE), state.getChar(FILTER)); + } + getDuplexMode(); + break; + case ANTENNA: + case RXANTENNA: + if (i.value()._valid) { + setAntenna(state.getChar(ANTENNA), state.getBool(RXANTENNA)); + } + getAntenna(); + break; + case CTCSS: + if (i.value()._valid) { + setTone(state.getChar(CTCSS)); + } + getTone(); + break; + case TSQL: + if (i.value()._valid) { + setTSQL(state.getChar(TSQL)); + } + getTSQL(); + break; + case DTCS: + if (i.value()._valid) { + setDTCS(state.getChar(DTCS), false, false); // Not sure about this? + } + getDTCS(); + break; + case CSQL: + if (i.value()._valid) { + setTone(state.getChar(CSQL)); + } + getTone(); + break; + case PREAMP: + if (i.value()._valid) { + setPreamp(state.getChar(PREAMP)); + } + getPreamp(); + break; + case ATTENUATOR: + if (i.value()._valid) { + setAttenuator(state.getChar(ATTENUATOR)); + } + getAttenuator(); + break; + case AFGAIN: + if (i.value()._valid) { + setAfGain(state.getChar(AFGAIN)); + } + getAfGain(); + break; + case RFGAIN: + if (i.value()._valid) { + setRfGain(state.getChar(RFGAIN)); + } + getRfGain(); + break; + case SQUELCH: + if (i.value()._valid) { + setSquelch(state.getChar(SQUELCH)); + } + getSql(); + break; + case TXPOWER: + if (i.value()._valid) { + setTxPower(state.getChar(TXPOWER)); + } + getTxLevel(); + break; + case MICGAIN: + if (i.value()._valid) { + setMicGain(state.getChar(MICGAIN)); + } + getMicGain(); + break; + case COMPLEVEL: + if (i.value()._valid) { + setCompLevel(state.getChar(COMPLEVEL)); + } + getCompLevel(); + break; + case MONITORLEVEL: + if (i.value()._valid) { + setMonitorLevel(state.getChar(MONITORLEVEL)); + } + getMonitorLevel(); + break; + case VOXGAIN: + if (i.value()._valid) { + setVoxGain(state.getChar(VOXGAIN)); + } + getVoxGain(); + break; + case ANTIVOXGAIN: + if (i.value()._valid) { + setAntiVoxGain(state.getChar(ANTIVOXGAIN)); + } + getAntiVoxGain(); + break; + case NBFUNC: + if (i.value()._valid) { + setNb(state.getBool(NBFUNC)); + } + getNb(); + break; + case NRFUNC: + if (i.value()._valid) { + setNr(state.getBool(NRFUNC)); + } + getNr(); + break; + case ANFFUNC: + if (i.value()._valid) { + setAutoNotch(state.getBool(ANFFUNC)); + } + getAutoNotch(); + break; + case TONEFUNC: + if (i.value()._valid) { + setToneEnabled(state.getBool(TONEFUNC)); + } + getToneEnabled(); + break; + case TSQLFUNC: + if (i.value()._valid) { + setToneSql(state.getBool(TSQLFUNC)); + } + getToneSql(); + break; + case COMPFUNC: + if (i.value()._valid) { + setCompressor(state.getBool(COMPFUNC)); + } + getCompressor(); + break; + case MONFUNC: + if (i.value()._valid) { + setMonitor(state.getBool(MONFUNC)); + } + getMonitor(); + break; + case VOXFUNC: + if (i.value()._valid) { + setVox(state.getBool(VOXFUNC)); + } + getVox(); + break; + case SBKINFUNC: + if (i.value()._valid) { + setBreakIn(state.getBool(VOXFUNC)); + } + getVox(); + break; + case FBKINFUNC: + if (i.value()._valid) { + setBreakIn(state.getBool(VOXFUNC) << 1); + } + getBreakIn(); + break; + case MNFUNC: + if (i.value()._valid) { + setManualNotch(state.getBool(MNFUNC)); + } + getManualNotch(); + break; + case SCOPEFUNC: + if (i.value()._valid) { + if (state.getBool(SCOPEFUNC)) { + enableSpectOutput(); + } + else { + disableSpectOutput(); + } + } + break; + case RIGINPUT: + if (i.value()._valid) { + setModInput(state.getInput(RIGINPUT), state.getBool(DATAMODE)); + } + getModInput(state.getBool(DATAMODE)); + break; + case POWERONOFF: + if (i.value()._valid) { + if (state.getBool(POWERONOFF)) { + powerOn(); + } + else { + powerOff(); + } + } + break; + case RITVALUE: + if (i.value()._valid) { + setRitValue(state.getInt32(RITVALUE)); + } + getRitValue(); + break; + case RITFUNC: + if (i.value()._valid) { + setRitEnable(state.getBool(RITFUNC)); + } + getRitEnabled(); + break; + // All meters can only be updated from the rig end. + case SMETER: + case POWERMETER: + case ALCMETER: + case COMPMETER: + case VOLTAGEMETER: + case CURRENTMETER: + break; + case AGC: + break; + case MODINPUT: + break; + case FAGCFUNC: + break; + case AIPFUNC: + break; + case APFFUNC: + break; + case RFFUNC: // Should this set RF output power to 0? + break; + case AROFUNC: + break; + case MUTEFUNC: + if (i.value()._valid) { + setAfMute(state.getBool(MUTEFUNC)); + } + getAfMute(); + break; + case VSCFUNC: + break; + case REVFUNC: + break; + case SQLFUNC: + break; + case ABMFUNC: + break; + case BCFUNC: + break; + case MBCFUNC: + break; + case AFCFUNC: + break; + case SATMODEFUNC: + break; + case NBLEVEL: + break; + case NBDEPTH: + break; + case NBWIDTH: + break; + case NRLEVEL: + break; + case RESUMEFUNC: + break; + case TBURSTFUNC: + break; + case TUNERFUNC: + if (i.value()._valid) { + setATU(state.getBool(TUNERFUNC)); + } + getATUStatus(); + break; + case LOCKFUNC: + if (i.value()._valid) { + setDialLock(state.getBool(LOCKFUNC)); + } + getDialLock(); + break; + } + } + ++i; + } } void rigCommander::getDebug() diff --git a/rigcommander.h b/rigcommander.h index 90550e0..6da1744 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -2,10 +2,10 @@ #define RIGCOMMANDER_H #include -#include #include #include + #include "commhandler.h" #include "pttyhandler.h" #include "udphandler.h" @@ -13,6 +13,8 @@ #include "repeaterattributes.h" #include "freqmemory.h" +#include "rigstate.h" + // This file figures out what to send to the comm and also // parses returns into useful things. @@ -61,78 +63,6 @@ struct timekind { bool isMinus; }; -struct rigStateStruct { - QMutex *mutex; - freqt vfoAFreq; - freqt vfoBFreq; - unsigned char currentVfo; - bool ptt; - unsigned char mode; - unsigned char filter; - duplexMode duplex; - bool datamode; - unsigned char antenna; - bool rxAntenna; - // Tones - quint16 ctcss; - quint16 tsql; - quint16 dtcs; - quint16 csql; - // Levels - unsigned char preamp; - unsigned char attenuator; - unsigned char modInput; - unsigned char afGain; - unsigned char rfGain; - unsigned char squelch; - unsigned char txPower; - unsigned char micGain; - unsigned char compLevel; - unsigned char monitorLevel; - unsigned char voxGain; - unsigned char antiVoxGain; - // Meters - unsigned char sMeter; - unsigned char powerMeter; - unsigned char swrMeter; - unsigned char alcMeter; - unsigned char compMeter; - unsigned char voltageMeter; - unsigned char currentMeter; - // Functions - bool fagcFunc=false; - bool nbFunc=false; - bool compFunc=false; - bool voxFunc = false; - bool toneFunc = false; - bool tsqlFunc = false; - bool sbkinFunc = false; - bool fbkinFunc = false; - bool anfFunc = false; - bool nrFunc = false; - bool aipFunc = false; - bool apfFunc = false; - bool monFunc = false; - bool mnFunc = false; - bool rfFunc = false; - bool aroFunc = false; - bool muteFunc = false; - bool vscFunc = false; - bool revFunc = false; - bool sqlFunc = false; - bool abmFunc = false; - bool bcFunc = false; - bool mbcFunc = false; - bool ritFunc = false; - bool afcFunc = false; - bool satmodeFunc = false; - bool scopeFunc = false; - bool resumeFunc = false; - bool tburstFunc = false; - bool tunerFunc = false; - bool lockFunc = false; -}; - class rigCommander : public QObject { Q_OBJECT @@ -148,6 +78,8 @@ public slots: void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp); void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); void closeComm(); + void stateUpdated(); + void setRTSforPTT(bool enabled); // Power: void powerOn(); @@ -199,6 +131,26 @@ public slots: void setAttenuator(unsigned char att); void setPreamp(unsigned char pre); void setAntenna(unsigned char ant, bool rx); + void setNb(bool enabled); + void getNb(); + void setNr(bool enabled); + void getNr(); + void setAutoNotch(bool enabled); + void getAutoNotch(); + void setToneEnabled(bool enabled); + void getToneEnabled(); + void setToneSql(bool enabled); + void getToneSql(); + void setCompressor(bool enabled); + void getCompressor(); + void setMonitor(bool enabled); + void getMonitor(); + void setVox(bool enabled); + void getVox(); + void setBreakIn(unsigned char type); + void getBreakIn(); + void setManualNotch(bool enabled); + void getManualNotch(); // Repeater: void setDuplexMode(duplexMode dm); @@ -233,6 +185,8 @@ public slots: void getACCGain(unsigned char ab); void getModInput(bool dataOn); void getModInputLevel(rigInput input); + void getAfMute(); + void getDialLock(); // Set Levels: void setSquelch(unsigned char level); @@ -253,6 +207,8 @@ public slots: void setAntiVoxGain(unsigned char gain); void setModInput(rigInput input, bool dataOn); void setModInputLevel(rigInput input, unsigned char level); + void setAfMute(bool muteOn); + void setDialLock(bool lockOn); // NB, NR, IP+: void setIPP(bool enabled); @@ -406,7 +362,7 @@ signals: void haveAntenna(unsigned char ant,bool rx); // Rig State - void stateInfo(rigStateStruct* state); + void stateInfo(rigstate* state); // Housekeeping: void getMoreDebug(); @@ -480,7 +436,7 @@ private: struct rigCapabilities rigCaps; - rigStateStruct rigState; + rigstate state; bool haveRigCaps; model_kind model; @@ -497,6 +453,9 @@ private: unsigned char civAddr; unsigned char incomingCIVAddr; // place to store the incoming CIV. bool pttAllowed; + bool useRTSforPTT_isSet = false; + bool useRTSforPTT_manual = false; + QString rigSerialPort; quint32 rigBaudRate; @@ -509,6 +468,7 @@ private: QString password; QString serialPortError; + unsigned char localVolume=0; }; diff --git a/rigctld.cpp b/rigctld.cpp index c456cbd..daf779d 100644 --- a/rigctld.cpp +++ b/rigctld.cpp @@ -55,13 +55,9 @@ rigCtlD::~rigCtlD() qInfo(logRigCtlD()) << "closing rigctld"; } -//void rigCtlD::receiveFrequency(freqt freq) -//{ -// emit setFrequency(0, freq); -// emit setFrequency(0, freq); -//} -void rigCtlD::receiveStateInfo(rigStateStruct* state) + +void rigCtlD::receiveStateInfo(rigstate* state) { qInfo("Setting rig state"); rigState = state; @@ -79,6 +75,7 @@ int rigCtlD::startServer(qint16 port) { qInfo(logRigCtlD()) << "started on port " << port; } + return 0; } @@ -100,7 +97,7 @@ void rigCtlD::receiveRigCaps(rigCapabilities caps) this->rigCaps = caps; } -rigCtlClient::rigCtlClient(int socketId, rigCapabilities caps, rigStateStruct* state, rigCtlD* parent) : QObject(parent) +rigCtlClient::rigCtlClient(int socketId, rigCapabilities caps, rigstate* state, rigCtlD* parent) : QObject(parent) { commandBuffer.clear(); @@ -118,27 +115,37 @@ rigCtlClient::rigCtlClient(int socketId, rigCapabilities caps, rigStateStruct* s connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()), Qt::DirectConnection); connect(parent, SIGNAL(sendData(QString)), this, SLOT(sendData(QString)), Qt::DirectConnection); qInfo(logRigCtlD()) << " session connected: " << sessionId; + emit parent->stateUpdated(); // Get the current state. + } void rigCtlClient::socketReadyRead() { QByteArray data = socket->readAll(); commandBuffer.append(data); + QStringList commandList(commandBuffer.split('\n')); QString sep = "\n"; static int num = 0; - bool longReply = false; - char responseCode = 0; - QStringList response; - bool setCommand = false; - if (commandBuffer.endsWith('\n')) + + for (QString &commands : commandList) { - qDebug(logRigCtlD()) << sessionId << "command received" << commandBuffer; - commandBuffer.chop(1); // Remove \n character - if (commandBuffer.endsWith('\r')) + bool longReply = false; + char responseCode = 0; + QStringList response; + bool setCommand = false; + //commands.chop(1); // Remove \n character + if (commands.endsWith('\r')) { - commandBuffer.chop(1); // Remove \n character + commands.chop(1); // Remove \n character } + if (commands.isEmpty()) + { + continue; + } + + //qDebug(logRigCtlD()) << sessionId << "command received" << commands; + // We have a full line so process command. if (rigState == Q_NULLPTR) @@ -147,73 +154,87 @@ void rigCtlClient::socketReadyRead() return; } - if (commandBuffer[num] == ";" || commandBuffer[num] == "|" || commandBuffer[num] == ",") + if (commands[num] == ";" || commands[num] == "|" || commands[num] == ",") { - sep = commandBuffer[num].toLatin1(); + sep = commands[num].toLatin1(); num++; } - else if (commandBuffer[num] == "+") + else if (commands[num] == "+") { longReply = true; sep = "\n"; num++; } - else if (commandBuffer[num] == "#") + else if (commands[num] == "#") { - return; + continue; } - else if (commandBuffer[num].toLower() == "q") + else if (commands[num].toLower() == "q") { closeSocket(); return; } - if (commandBuffer[num] == "\\") + if (commands[num] == "\\") { num++; } - QStringList command = commandBuffer.mid(num).split(" "); + QStringList command = commands.mid(num).split(" "); - QMutexLocker locker(rigState->mutex); if (command[0] == 0xf0 || command[0] == "chk_vfo") { + chkVfoEecuted = true; QString resp; if (longReply) { resp.append(QString("ChkVFO: ")); } - resp.append(QString("%1").arg(rigState->currentVfo)); + resp.append(QString("%1").arg(rigState->getChar(CURRENTVFO))); response.append(resp); } else if (command[0] == "dump_state") { // Currently send "fake" state information until I can work out what is required! - response.append("1"); - response.append(QString("%1").arg(rigCaps.rigctlModel)); - response.append("0"); + response.append("1"); // rigctld protocol version + response.append(QString("%1").arg(rigCaps.rigctlModel)); + response.append("0"); // Print something + bandType lastBand; for (bandType band : rigCaps.bands) { - response.append(generateFreqRange(band)); + if (band != lastBand) + response.append(generateFreqRange(band)); + lastBand = band; } response.append("0 0 0 0 0 0 0"); if (rigCaps.hasTransmit) { for (bandType band : rigCaps.bands) - { - response.append(generateFreqRange(band)); + { + if (band != lastBand) + response.append(generateFreqRange(band)); + lastBand = band; } } response.append("0 0 0 0 0 0 0"); - response.append("0x1ff 1"); - response.append("0x1ff 0"); + + response.append(QString("0x%1 1").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 10").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 100").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 1000").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 2500").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 5000").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 6125").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 8333").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 10000").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 12500").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 25000").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 100000").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 250000").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 1000000").arg(getRadioModes(), 0, 16)); response.append("0 0"); - response.append("0x1e 2400"); - response.append("0x2 500"); - response.append("0x1 8000"); - response.append("0x1 2400"); - response.append("0x20 15000"); - response.append("0x20 8000"); - response.append("0x40 230000"); + response.append(QString("0x%1 1200").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 2400").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 3000").arg(getRadioModes(), 0, 16)); response.append("0 0"); response.append("9900"); response.append("9900"); @@ -252,12 +273,21 @@ void rigCtlClient::socketReadyRead() response.append(attens); + response.append("0xffffffffffffffff"); + response.append("0xffffffffffffffff"); + response.append("0xffffffffffffffff"); + response.append("0xffffffffffffffff"); + response.append("0xffffffffffffffff"); + response.append("0xffffffffffffffff"); + + /* response.append("0xffffffffffffffff"); response.append("0xffffffffffffffff"); response.append("0xfffffffff7ffffff"); response.append("0xfffffff083ffffff"); response.append("0xffffffffffffffff"); response.append("0xffffffffffffffbf"); + */ /* response.append("0x3effffff"); @@ -267,7 +297,20 @@ void rigCtlClient::socketReadyRead() response.append("0x7fffffff"); response.append("0x7fffffff"); */ - response.append("done"); + if (chkVfoEecuted) { + response.append(QString("vfo_ops=0x%1").arg(255, 0, 16)); + response.append(QString("ptt_type=0x%1").arg(rigCaps.hasTransmit, 0, 16)); + response.append(QString("has_set_vfo=0x%1").arg(1, 0, 16)); + response.append(QString("has_get_vfo=0x%1").arg(1, 0, 16)); + response.append(QString("has_set_freq=0x%1").arg(1, 0, 16)); + response.append(QString("has_get_freq=0x%1").arg(1, 0, 16)); + response.append(QString("has_set_conf=0x%1").arg(1, 0, 16)); + response.append(QString("has_get_conf=0x%1").arg(1, 0, 16)); + response.append(QString("has_power2mW=0x%1").arg(1, 0, 16)); + response.append(QString("has_mW2power=0x%1").arg(1, 0, 16)); + response.append(QString("timeout=0x%1").arg(1000, 0, 16)); + response.append("done"); + } } else if (command[0] == "f" || command[0] == "get_freq") @@ -276,12 +319,14 @@ void rigCtlClient::socketReadyRead() if (longReply) { resp.append(QString("Frequency: ")); } - if (rigState->currentVfo == 0) { - resp.append(QString("%1").arg(rigState->vfoAFreq.Hz)); + + if (rigState->getChar(CURRENTVFO)==0) { + resp.append(QString("%1").arg(rigState->getInt64(VFOAFREQ))); } else { - resp.append(QString("%1").arg(rigState->vfoBFreq.Hz)); + resp.append(QString("%1").arg(rigState->getInt64(VFOBFREQ))); } + response.append(resp); } else if (command[0] == "F" || command[0] == "set_freq") @@ -307,11 +352,12 @@ void rigCtlClient::socketReadyRead() if (ok) { freq.Hz = static_cast(newFreq); qDebug(logRigCtlD()) << QString("Set frequency: %1 (%2)").arg(freq.Hz).arg(command[1]); - emit parent->setFrequency(vfo, freq); - emit parent->setFrequency(vfo, freq); - emit parent->setFrequency(vfo, freq); - emit parent->setFrequency(vfo, freq); - emit parent->setFrequency(vfo, freq); + if (vfo == 0) { + rigState->set(VFOAFREQ, freq.Hz,true); + } + else { + rigState->set(VFOBFREQ, freq.Hz,true); + } } } else if (command[0] == "1" || command[0] == "dump_caps") @@ -341,7 +387,7 @@ void rigCtlClient::socketReadyRead() if (longReply) { resp.append(QString("PTT: ")); } - resp.append(QString("%1").arg(rigState->ptt)); + resp.append(QString("%1").arg(rigState->getBool(PTT))); response.append(resp); } else @@ -353,30 +399,27 @@ void rigCtlClient::socketReadyRead() { setCommand = true; if (rigCaps.hasPTTCommand) { - emit parent->setPTT(bool(command[1].toInt())); - emit parent->setPTT(bool(command[1].toInt())); - emit parent->setPTT(bool(command[1].toInt())); - emit parent->setPTT(bool(command[1].toInt())); - emit parent->setPTT(bool(command[1].toInt())); + rigState->set(PTT, (bool)command[1].toInt(), true); } else { responseCode = -1; } } - else if (command[0] == "v" || command[0] == "get_vfo") + else if (command[0] == "v" || command[0] == "v\nv" || command[0] == "get_vfo") { QString resp; if (longReply) { resp.append("VFO: "); } - - if (rigState->currentVfo == 0) { + + if (rigState->getChar(CURRENTVFO) == 0) { resp.append("VFOA"); } else { resp.append("VFOB"); } + response.append(resp); } else if (command.length() > 1 && (command[0] == "V" || command[0] == "set_vfo")) @@ -391,73 +434,82 @@ void rigCtlClient::socketReadyRead() response.append("MEM"); } else if (command[1] == "VFOB" || command[1] == "Sub") { - emit parent->setVFO(1); + rigState->set(CURRENTVFO, (quint8)1, true); } else { - emit parent->setVFO(0); + rigState->set(CURRENTVFO, (quint8)0, true); } } else if (command[0] == "s" || command[0] == "get_split_vfo") { + if (longReply) { - response.append(QString("Split: %1").arg(rigState->duplex)); + response.append(QString("Split: %1").arg(rigState->getChar(DUPLEX))); } else { - response.append(QString("%1").arg(rigState->duplex)); + response.append(QString("%1").arg(rigState->getChar(DUPLEX))); } - + QString resp; if (longReply) { resp.append("TX VFO: "); } - if (rigState->currentVfo == 0) + + if (rigState->getChar(CURRENTVFO) == 0) { resp.append(QString("%1").arg("VFOB")); } else { resp.append(QString("%1").arg("VFOA")); } + response.append(resp); } else if (command.length() > 1 && (command[0] == "S" || command[0] == "set_split_vfo")) { setCommand = true; + if (command[1] == "1") { - emit parent->setDuplexMode(dmSplitOn); - rigState->duplex = dmSplitOn; + rigState->set(DUPLEX, dmSplitOn, true); } else { - emit parent->setDuplexMode(dmSplitOff); - rigState->duplex = dmSplitOff; + rigState->set(DUPLEX, dmSplitOff, true); } } else if (command[0] == "\xf3" || command[0] == "get_vfo_info") { if (longReply) { - //response.append(QString("set_vfo: %1").arg(command[1])); - + if (command[1] == "?") { + if (rigState->getChar(CURRENTVFO) == 0) { + response.append(QString("set_vfo: VFOA")); + } + else + { + response.append(QString("set_vfo: VFOB")); + } + } if (command[1] == "VFOB") { - response.append(QString("Freq: %1").arg(rigState->vfoBFreq.Hz)); + response.append(QString("Freq: %1").arg(rigState->getInt64(VFOBFREQ))); } else { - response.append(QString("Freq: %1").arg(rigState->vfoAFreq.Hz)); + response.append(QString("Freq: %1").arg(rigState->getInt64(VFOAFREQ))); } - response.append(QString("Mode: %1").arg(getMode(rigState->mode, rigState->datamode))); - response.append(QString("Width: %1").arg(getFilter(rigState->mode, rigState->filter))); - response.append(QString("Split: %1").arg(rigState->duplex)); + response.append(QString("Mode: %1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); + response.append(QString("Width: %1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + response.append(QString("Split: %1").arg(rigState->getDuplex(DUPLEX))); response.append(QString("SatMode: %1").arg(0)); // Need to get satmode } else { if (command[1] == "VFOB") { - response.append(QString("%1").arg(rigState->vfoBFreq.Hz)); + response.append(QString("%1").arg(rigState->getInt64(VFOBFREQ))); } else { - response.append(QString("%1").arg(rigState->vfoAFreq.Hz)); + response.append(QString("%1").arg(rigState->getInt64(VFOAFREQ))); } - response.append(QString("%1").arg(getMode(rigState->mode, rigState->datamode))); - response.append(QString("%1").arg(getFilter(rigState->mode, rigState->filter))); + response.append(QString("%1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); + response.append(QString("%1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); } } else if (command[0] == "i" || command[0] == "get_split_freq") @@ -466,57 +518,54 @@ void rigCtlClient::socketReadyRead() if (longReply) { resp.append("TX VFO: "); } - if (rigState->currentVfo == 0) { - resp.append(QString("%1").arg(rigState->vfoBFreq.Hz)); + + if (rigState->getInt64(CURRENTVFO) == 0) { + resp.append(QString("%1").arg(rigState->getInt64(VFOBFREQ))); } else { - resp.append(QString("%1").arg(rigState->vfoAFreq.Hz)); + resp.append(QString("%1").arg(rigState->getInt64(VFOAFREQ))); } + response.append(resp); } else if (command.length() > 1 && (command[0] == "I" || command[0] == "set_split_freq")) { setCommand = true; - freqt freq; bool ok = false; double newFreq = 0.0f; newFreq = command[1].toDouble(&ok); if (ok) { - freq.Hz = static_cast(newFreq); - qDebug(logRigCtlD()) << QString("set_split_freq: %1 (%2)").arg(freq.Hz).arg(command[1]); - emit parent->setFrequency(1, freq); - emit parent->setFrequency(1, freq); - emit parent->setFrequency(1, freq); - emit parent->setFrequency(1, freq); - emit parent->setFrequency(1, freq); + qDebug(logRigCtlD()) << QString("set_split_freq: %1 (%2)").arg(newFreq).arg(command[1]); + rigState->set(VFOBFREQ, static_cast(newFreq),false); } } else if (command.length() > 2 && (command[0] == "X" || command[0] == "set_split_mode")) { setCommand = true; - } - else if (command.length() > 0 && (command[0] == "x" || command[0] == "get_split_mode")) - { - if (longReply) { - response.append(QString("TX Mode: %1").arg(getMode(rigState->mode, rigState->datamode))); - response.append(QString("TX Passband: %1").arg(getFilter(rigState->mode, rigState->filter))); - } - else { - response.append(QString("%1").arg(getMode(rigState->mode, rigState->datamode))); - response.append(QString("%1").arg(getFilter(rigState->mode, rigState->filter))); - } + } + + else if (command.length() > 0 && (command[0] == "x" || command[0] == "get_split_mode")) + { + if (longReply) { + response.append(QString("TX Mode: %1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); + response.append(QString("TX Passband: %1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + } + else { + response.append(QString("%1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); + response.append(QString("%1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + } } else if (command[0] == "m" || command[0] == "get_mode") { - if (longReply) { - response.append(QString("Mode: %1").arg(getMode(rigState->mode, rigState->datamode))); - response.append(QString("Passband: %1").arg(getFilter(rigState->mode, rigState->filter))); - } - else { - response.append(QString("%1").arg(getMode(rigState->mode, rigState->datamode))); - response.append(QString("%1").arg(getFilter(rigState->mode, rigState->filter))); - } + if (longReply) { + response.append(QString("TX Mode: %1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); + response.append(QString("TX Passband: %1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + } + else { + response.append(QString("%1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); + response.append(QString("%1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + } } else if (command[0] == "M" || command[0] == "set_mode") { @@ -540,16 +589,16 @@ void rigCtlClient::socketReadyRead() width = 2; else width = 1; - - emit parent->setMode(getMode(mode), width); + + rigState->set(MODE,getMode(mode),true); + rigState->set(FILTER,(quint8)width, true); if (mode.mid(0, 3) == "PKT") { - emit parent->setDataMode(true, width); - emit parent->setDataMode(true, width); + rigState->set(DATAMODE, true, true); } else { - emit parent->setDataMode(false, width); - emit parent->setDataMode(false, width); + rigState->set(DATAMODE, false, true); } + } else if (command[0] == "s" || command[0] == "get_split_vfo") { @@ -570,11 +619,12 @@ void rigCtlClient::socketReadyRead() if (longReply) { resp.append("RIT: "); } - resp.append(QString("%1").arg(0)); + resp.append(QString("%1").arg(rigState->getInt32(RITVALUE))); response.append(resp); - } + } else if (command[0] == "J" || command[0] == "set_rit") { + rigState->set(RITVALUE, command[1].toInt(),true); setCommand = true; } else if (command[0] == "y" || command[0] == "get_ant") @@ -585,34 +635,35 @@ void rigCtlClient::socketReadyRead() if (longReply) { response.append(QString("AntCurr: %1").arg(getAntName((unsigned char)command[1].toInt()))); response.append(QString("Option: %1").arg(0)); - response.append(QString("AntTx: %1").arg(getAntName(rigState->antenna))); - response.append(QString("AntRx: %1").arg(getAntName(rigState->antenna))); + response.append(QString("AntTx: %1").arg(getAntName(rigState->getChar(ANTENNA)))); + response.append(QString("AntRx: %1").arg(getAntName(rigState->getChar(ANTENNA)))); } else { response.append(QString("%1").arg(getAntName((unsigned char)command[1].toInt()))); response.append(QString("%1").arg(0)); - response.append(QString("%1").arg(getAntName(rigState->antenna))); - response.append(QString("%1").arg(getAntName(rigState->antenna))); + response.append(QString("%1").arg(getAntName(rigState->getChar(ANTENNA)))); + response.append(QString("%1").arg(getAntName(rigState->getChar(ANTENNA)))); } } } - else if (command[0] == "Y" || command[0] == "set_ant") + else if (command.length() > 1 && (command[0] == "Y" || command[0] == "set_ant")) { - qInfo(logRigCtlD()) << "set_ant:"; - setCommand = true; + setCommand = true; + qInfo(logRigCtlD()) << "set_ant:" << command[1]; + rigState->set(ANTENNA,antFromName(command[1]),true); } else if (command[0] == "z" || command[0] == "get_xit") { - QString resp; - if (longReply) { - resp.append("XIT: "); - } - resp.append(QString("%1").arg(0)); - response.append(resp); + QString resp; + if (longReply) { + resp.append("XIT: "); + } + resp.append(QString("%1").arg(0)); + response.append(resp); } else if (command[0] == "Z" || command[0] == "set_xit") { - setCommand = true; + setCommand = true; } else if (command.length() > 1 && (command[0] == "l" || command[0] == "get_level")) { @@ -623,52 +674,54 @@ void rigCtlClient::socketReadyRead() } if (command[1] == "STRENGTH") { + if (rigCaps.model == model7610) - value = getCalibratedValue(rigState->sMeter, IC7610_STR_CAL); + value = getCalibratedValue(rigState->getChar(SMETER), IC7610_STR_CAL); else if (rigCaps.model == model7850) - value = getCalibratedValue(rigState->sMeter, IC7850_STR_CAL); + value = getCalibratedValue(rigState->getChar(SMETER), IC7850_STR_CAL); else - value = getCalibratedValue(rigState->sMeter, IC7300_STR_CAL); + value = getCalibratedValue(rigState->getChar(SMETER), IC7300_STR_CAL); //qInfo(logRigCtlD()) << "Calibration IN:" << rigState->sMeter << "OUT" << value; resp.append(QString("%1").arg(value)); } + else if (command[1] == "AF") { - resp.append(QString("%1").arg((float)rigState->afGain / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(AFGAIN) / 255.0)); } else if (command[1] == "RF") { - resp.append(QString("%1").arg((float)rigState->rfGain / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(RFGAIN) / 255.0)); } else if (command[1] == "SQL") { - resp.append(QString("%1").arg((float)rigState->squelch / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(SQUELCH) / 255.0)); } else if (command[1] == "COMP") { - resp.append(QString("%1").arg((float)rigState->compLevel / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(COMPLEVEL) / 255.0)); } else if (command[1] == "MICGAIN") { - resp.append(QString("%1").arg((float)rigState->micGain / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(MICGAIN) / 255.0)); } else if (command[1] == "MON") { - resp.append(QString("%1").arg((float)rigState->monitorLevel / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(MONITORLEVEL) / 255.0)); } else if (command[1] == "VOXGAIN") { - resp.append(QString("%1").arg((float)rigState->voxGain / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(VOXGAIN) / 255.0)); } else if (command[1] == "ANTIVOX") { - resp.append(QString("%1").arg((float)rigState->antiVoxGain / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(ANTIVOXGAIN) / 255.0)); } else if (command[1] == "RFPOWER") { - resp.append(QString("%1").arg((float)rigState->txPower / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(TXPOWER) / 255.0)); } else if (command[1] == "PREAMP") { - resp.append(QString("%1").arg((float)rigState->preamp / 255.0)); + resp.append(QString("%1").arg(rigState->getChar(PREAMP)*10)); } else if (command[1] == "ATT") { - resp.append(QString("%1").arg((float)rigState->attenuator / 255.0)); + resp.append(QString("%1").arg(rigState->getChar(ATTENUATOR))); } else { resp.append(QString("%1").arg(value)); } - + response.append(resp); } else if (command.length() > 2 && (command[0] == "L" || command[0] == "set_level")) @@ -677,55 +730,49 @@ void rigCtlClient::socketReadyRead() setCommand = true; if (command[1] == "AF") { value = command[2].toFloat() * 255; - emit parent->setAfGain(value); - rigState->afGain = value; + rigState->set(AFGAIN, value, true); } else if (command[1] == "RF") { value = command[2].toFloat() * 255; - emit parent->setRfGain(value); - rigState->rfGain = value; + rigState->set(RFGAIN, value, true); } else if (command[1] == "SQL") { value = command[2].toFloat() * 255; - emit parent->setSql(value); - rigState->squelch = value; + rigState->set(SQUELCH, value, true); } else if (command[1] == "COMP") { value = command[2].toFloat() * 255; - emit parent->setCompLevel(value); - rigState->compLevel = value; + rigState->set(COMPLEVEL, value, true); } else if (command[1] == "MICGAIN") { value = command[2].toFloat() * 255; - emit parent->setMicGain(value); - rigState->micGain = value; + rigState->set(MICGAIN, value, true); } else if (command[1] == "MON") { value = command[2].toFloat() * 255; - emit parent->setMonitorLevel(value); - rigState->monitorLevel = value; + rigState->set(MONITORLEVEL, value, true); } else if (command[1] == "VOXGAIN") { value = command[2].toFloat() * 255; - emit parent->setVoxGain(value); - rigState->voxGain = value; + rigState->set(VOXGAIN, value, true); } else if (command[1] == "ANTIVOX") { value = command[2].toFloat() * 255; - emit parent->setAntiVoxGain(value); - rigState->antiVoxGain = value; + rigState->set(ANTIVOXGAIN, value, true); } else if (command[1] == "ATT") { - value = command[2].toFloat(); - emit parent->setAttenuator(value); - rigState->attenuator = value; + value = command[2].toInt(); + rigState->set(ATTENUATOR, value, true); } else if (command[1] == "PREAMP") { - value = command[2].toFloat()/10; - emit parent->setPreamp(value); - rigState->preamp = value; + value = command[2].toFloat() / 10; + rigState->set(PREAMP, value, true); } - + else if (command[1] == "AGC") { + value = command[2].toInt();; + rigState->set(AGC, value, true); + } + qInfo(logRigCtlD()) << "Setting:" << command[1] << command[2] << value; } @@ -736,271 +783,272 @@ void rigCtlClient::socketReadyRead() if (longReply) { resp.append(QString("Func Status: ")); } - if (command[1] == "FAGC") { - result=rigState->fagcFunc; + result=rigState->getBool(FAGCFUNC); } else if (command[1] == "NB") { - result=rigState->nbFunc; + result = rigState->getBool(NBFUNC); } else if (command[1] == "COMP") { - result=rigState->compFunc; + result=rigState->getBool(COMPFUNC); } else if (command[1] == "VOX") { - result = rigState->voxFunc; + result = rigState->getBool(VOXFUNC); } else if (command[1] == "TONE") { - result = rigState->toneFunc; + result = rigState->getBool(TONEFUNC); } else if (command[1] == "TSQL") { - result = rigState->tsqlFunc; + result = rigState->getBool(TSQLFUNC); } else if (command[1] == "SBKIN") { - result = rigState->sbkinFunc; + result = rigState->getBool(SBKINFUNC); } else if (command[1] == "FBKIN") { - result = rigState->fbkinFunc; + result = rigState->getBool(FBKINFUNC); } else if (command[1] == "ANF") { - result = rigState->anfFunc; + result = rigState->getBool (ANFFUNC); } else if (command[1] == "NR") { - result = rigState->nrFunc; + result = rigState->getBool(NRFUNC); } else if (command[1] == "AIP") { - result = rigState->aipFunc; + result = rigState->getBool(AIPFUNC); } else if (command[1] == "APF") { - result = rigState->apfFunc; + result = rigState->getBool(APFFUNC); } else if (command[1] == "MON") { - result = rigState->monFunc; + result = rigState->getBool(MONFUNC); } else if (command[1] == "MN") { - result = rigState->mnFunc; + result = rigState->getBool(MNFUNC); } else if (command[1] == "RF") { - result = rigState->rfFunc; + result = rigState->getBool(RFFUNC); } else if (command[1] == "ARO") { - result = rigState->aroFunc; + result = rigState->getBool(AROFUNC); } else if (command[1] == "MUTE") { - result = rigState->muteFunc; + result = rigState->getBool(MUTEFUNC); } else if (command[1] == "VSC") { - result = rigState->vscFunc; + result = rigState->getBool(VSCFUNC); } else if (command[1] == "REV") { - result = rigState->revFunc; + result = rigState->getBool(REVFUNC); } else if (command[1] == "SQL") { - result = rigState->sqlFunc; + result = rigState->getBool(SQLFUNC); } else if (command[1] == "ABM") { - result = rigState->abmFunc; + result = rigState->getBool(ABMFUNC); } else if (command[1] == "BC") { - result = rigState->bcFunc; + result = rigState->getBool(BCFUNC); } else if (command[1] == "MBC") { - result = rigState->mbcFunc; + result = rigState->getBool(MBCFUNC); } else if (command[1] == "RIT") { - result = rigState->ritFunc; + result = rigState->getBool(RITFUNC); } else if (command[1] == "AFC") { - result = rigState->afcFunc; + result = rigState->getBool(AFCFUNC); } else if (command[1] == "SATMODE") { - result = rigState->satmodeFunc; + result = rigState->getBool(SATMODEFUNC); } else if (command[1] == "SCOPE") { - result = rigState->scopeFunc; + result = rigState->getBool(SCOPEFUNC); } else if (command[1] == "RESUME") { - result = rigState->resumeFunc; + result = rigState->getBool(RESUMEFUNC); } else if (command[1] == "TBURST") { - result = rigState->tburstFunc; + result = rigState->getBool(TBURSTFUNC); } else if (command[1] == "TUNER") { - result = rigState->tunerFunc; + result = rigState->getBool(TUNERFUNC); } else if (command[1] == "LOCK") { - result = rigState->lockFunc; + result = rigState->getBool(LOCKFUNC); } else { qInfo(logRigCtlD()) << "Unimplemented func:" << command[0] << command[1]; } - + resp.append(QString("%1").arg(result)); response.append(resp); } else if (command.length() >2 && (command[0] == "U" || command[0] == "set_func")) { setCommand = true; + if (command[1] == "FAGC") { - rigState->fagcFunc = (bool)command[2].toInt(); + rigState->set(FAGCFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "NB") { - rigState->nbFunc = (bool)command[2].toInt(); + rigState->set(NBFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "COMP") { - rigState->compFunc = (bool)command[2].toInt(); + rigState->set(COMPFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "VOX") { - rigState->voxFunc = (bool)command[2].toInt(); + rigState->set(VOXFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "TONE") { - rigState->toneFunc = (bool)command[2].toInt(); + rigState->set(TONEFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "TSQL") { - rigState->tsqlFunc = (bool)command[2].toInt(); + rigState->set(TSQLFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "SBKIN") { - rigState->sbkinFunc = (bool)command[2].toInt(); + rigState->set(SBKINFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "FBKIN") { - rigState->fbkinFunc = (bool)command[2].toInt(); + rigState->set(FBKINFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "ANF") { - rigState->anfFunc = (bool)command[2].toInt(); + rigState->set(ANFFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "NR") { - rigState->nrFunc = (bool)command[2].toInt(); + rigState->set(NRFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "AIP") { - rigState->aipFunc = (bool)command[2].toInt(); + rigState->set(AIPFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "APF") { - rigState->apfFunc = (bool)command[2].toInt(); + rigState->set(APFFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "MON") { - rigState->monFunc = (bool)command[2].toInt(); + rigState->set(MONFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "MN") { - rigState->mnFunc = (bool)command[2].toInt(); + rigState->set(MNFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "RF") { - rigState->rfFunc = (bool)command[2].toInt(); + rigState->set(RFFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "ARO") { - rigState->aroFunc = (bool)command[2].toInt(); + rigState->set(AROFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "MUTE") { - rigState->muteFunc = (bool)command[2].toInt(); + rigState->set(MUTEFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "VSC") { - rigState->vscFunc = (bool)command[2].toInt(); + rigState->set(VSCFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "REV") { - rigState->revFunc = (bool)command[2].toInt(); + rigState->set(REVFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "SQL") { - rigState->sqlFunc = (bool)command[2].toInt(); + rigState->set(SQLFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "ABM") { - rigState->abmFunc = (bool)command[2].toInt(); + rigState->set(ABMFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "BC") { - rigState->bcFunc = (bool)command[2].toInt(); + rigState->set(BCFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "MBC") { - rigState->mbcFunc = (bool)command[2].toInt(); + rigState->set(MBCFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "RIT") { - rigState->ritFunc = (bool)command[2].toInt(); + rigState->set(RITFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "AFC") { - rigState->afcFunc = (bool)command[2].toInt(); + rigState->set(AFCFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "SATMODE") { - rigState->satmodeFunc = (bool)command[2].toInt(); + rigState->set(SATMODEFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "SCOPE") { - rigState->scopeFunc = (bool)command[2].toInt(); + rigState->set(SCOPEFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "RESUME") { - rigState->resumeFunc = (bool)command[2].toInt(); + rigState->set(RESUMEFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "TBURST") { - rigState->tburstFunc = (bool)command[2].toInt(); + rigState->set(TBURSTFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "TUNER") - { - rigState->tunerFunc = (bool)command[2].toInt(); + { + rigState->set(TUNERFUNC, (quint8)command[2].toInt(), true); } else if (command[1] == "LOCK") { - rigState->lockFunc = (bool)command[2].toInt(); + rigState->set(LOCKFUNC, (quint8)command[2].toInt(), true); } else { qInfo(logRigCtlD()) << "Unimplemented func:" << command[0] << command[1] << command[2]; } + qInfo(logRigCtlD()) << "Setting:" << command[1] << command[2]; } - else if (command.length() > 1 && (command[0] == 0x88 || command[0] == "get_powerstat")) + else if (command.length() > 0 && (command[0] == 0x88 || command[0] == "get_powerstat")) { QString resp; @@ -1013,17 +1061,17 @@ void rigCtlClient::socketReadyRead() } else if (command.length() > 1 && (command[0] == 0x87 || command[0] == "set_powerstat")) { - setCommand = true; - if (command[1] == "0") - { - emit parent->sendPowerOff(); + setCommand = true; + if (command[1] == "0") + { + rigState->set(POWERONOFF, false, true); } - else { - emit parent->sendPowerOn(); + else { + rigState->set(POWERONOFF, true, true); } } else { - qInfo(logRigCtlD()) << "Unimplemented command" << commandBuffer; + qInfo(logRigCtlD()) << "Unimplemented command" << commands; } if (longReply) { if (command.length() == 2) @@ -1034,6 +1082,12 @@ void rigCtlClient::socketReadyRead() sendData(QString("%1: %2 %3 %4%5").arg(command[0]).arg(command[1]).arg(command[2]).arg(command[3]).arg(sep)); } + if (setCommand) + { + // This was a set command so state has likely been updated. + emit parent->stateUpdated(); + } + if (setCommand || responseCode != 0 || longReply) { if (responseCode == 0) { response.append(QString("RPRT 0")); @@ -1048,15 +1102,15 @@ void rigCtlClient::socketReadyRead() if (str != "") sendData(QString("%1%2").arg(str).arg(sep)); } - if (sep != "\n") { sendData(QString("\n")); } - commandBuffer.clear(); - sep = " "; + sep = "\n"; num = 0; + } + commandBuffer.clear(); } void rigCtlClient::socketDisconnected() @@ -1073,7 +1127,7 @@ void rigCtlClient::closeSocket() void rigCtlClient::sendData(QString data) { - qDebug(logRigCtlD()) << "Sending:" << data; + //qDebug(logRigCtlD()) << "Sending:" << data; if (socket != Q_NULLPTR && socket->isValid() && socket->isOpen()) { socket->write(data.toLatin1()); @@ -1146,18 +1200,18 @@ QString rigCtlClient::getFilter(unsigned char mode, unsigned char filter) { QString rigCtlClient::getMode(unsigned char mode, bool datamode) { QString ret; - if (datamode) { - ret="PKT"; - } switch (mode) { case 0: + if (datamode) { ret = "PKT"; } ret.append("LSB"); break; case 1: + if (datamode) { ret = "PKT"; } ret.append("USB"); break; case 2: + if (datamode) { ret = "PKT"; } ret.append("AM"); break; case 3: @@ -1167,6 +1221,7 @@ QString rigCtlClient::getMode(unsigned char mode, bool datamode) { ret.append("RTTY"); break; case 5: + if (datamode) { ret = "PKT"; } ret.append("FM"); break; case 6: @@ -1179,12 +1234,15 @@ QString rigCtlClient::getMode(unsigned char mode, bool datamode) { ret.append("RTTYR"); break; case 12: + if (datamode) { ret = "PKT"; } ret.append("USB"); break; case 17: + if (datamode) { ret = "PKT"; } ret.append("LSB"); break; case 22: + if (datamode) { ret = "PKT"; } ret.append("FM"); break; } @@ -1324,7 +1382,7 @@ QString rigCtlClient::generateFreqRange(bandType band) QString ret = ""; if (lowFreq > 0 && highFreq > 0) { - ret = QString("%1 %2 0x%3 %4 %5 0x%6 0x%7").arg(lowFreq).arg(highFreq).arg(getRadioModes(),0,16).arg(-1).arg(-1).arg(0x16000003,0,16).arg(getAntennas(),0,16); + ret = QString("%1.000000 %2.000000 0x%3 %4 %5 0x%6 0x%7").arg(lowFreq).arg(highFreq).arg(getRadioModes(),0,16).arg(-1).arg(-1).arg(0x16000003,0,16).arg(getAntennas(),0,16); } return ret; } @@ -1349,13 +1407,18 @@ quint64 rigCtlClient::getRadioModes() QString curMode = mode.name; if (!strcmp(curMode.toLocal8Bit(), mode_str[i].str)) { + //qDebug(logRigCtlD()) << "Found mode:" << mode.name << mode_str[i].mode; modes |= mode_str[i].mode; } if (rigCaps.hasDataModes) { curMode = "PKT" + mode.name; if (!strcmp(curMode.toLocal8Bit(), mode_str[i].str)) { - modes |= mode_str[i].mode; + if (mode.name == "LSB" || mode.name == "USB" || mode.name == "FM" || mode.name == "AM") + { + // qDebug(logRigCtlD()) << "Found data mode:" << mode.name << mode_str[i].mode; + modes |= mode_str[i].mode; + } } } } @@ -1380,6 +1443,28 @@ QString rigCtlClient::getAntName(unsigned char ant) return ret; } +unsigned char rigCtlClient::antFromName(QString name) { + unsigned char ret; + + if (name == "ANT1") + ret = 0; + else if (name == "ANT2") + ret = 1; + else if (name == "ANT3") + ret = 2; + else if (name == "ANT4") + ret = 3; + else if (name == "ANT5") + ret = 4; + else if (name == "ANT_UNKNOWN") + ret = 30; + else if (name == "ANT_CURR") + ret = 31; + else + ret = 99; + return ret; +} + int rigCtlClient::getCalibratedValue(unsigned char meter,cal_table_t cal) { int interp; diff --git a/rigctld.h b/rigctld.h index 130ae5e..d86818d 100644 --- a/rigctld.h +++ b/rigctld.h @@ -19,6 +19,7 @@ #include #include "rigcommander.h" +#include "rigstate.h" #define CONSTANT_64BIT_FLAG(BIT) (1ull << (BIT)) @@ -343,7 +344,7 @@ signals: void setVFO(unsigned char vfo); void setSplit(unsigned char split); void setDuplexMode(duplexMode dm); - + void stateUpdated(); // Power void sendPowerOn(); void sendPowerOff(); @@ -368,11 +369,11 @@ signals: public slots: virtual void incomingConnection(qintptr socketDescriptor); void receiveRigCaps(rigCapabilities caps); - void receiveStateInfo(rigStateStruct* state); + void receiveStateInfo(rigstate* state); // void receiveFrequency(freqt freq); private: - rigStateStruct* rigState = Q_NULLPTR; + rigstate* rigState = Q_NULLPTR; }; @@ -382,7 +383,7 @@ class rigCtlClient : public QObject public: - explicit rigCtlClient(int socket, rigCapabilities caps, rigStateStruct *state, rigCtlD* parent = Q_NULLPTR); + explicit rigCtlClient(int socket, rigCapabilities caps, rigstate *state, rigCtlD* parent = Q_NULLPTR); int getSocketId(); @@ -399,8 +400,9 @@ protected: private: rigCapabilities rigCaps; - rigStateStruct* rigState = Q_NULLPTR; + rigstate* rigState = Q_NULLPTR; rigCtlD* parent; + bool chkVfoEecuted=false; QString getMode(unsigned char mode, bool datamode); unsigned char getMode(QString modeString); QString getFilter(unsigned char mode, unsigned char filter); @@ -408,6 +410,7 @@ private: unsigned char getAntennas(); quint64 getRadioModes(); QString getAntName(unsigned char ant); + unsigned char antFromName(QString name); int getCalibratedValue(unsigned char meter,cal_table_t cal); }; diff --git a/rigstate.h b/rigstate.h new file mode 100644 index 0000000..8722f9e --- /dev/null +++ b/rigstate.h @@ -0,0 +1,127 @@ +#ifndef RIGSTATEH +#define RIGSTATEH + +#include +#include +#include +#include +#include +#include + +#include "rigcommander.h" +#include "rigidentities.h" + +// Meters at the end as they are ALWAYS updated from the rig! +enum stateTypes { VFOAFREQ, VFOBFREQ, CURRENTVFO, PTT, MODE, FILTER, DUPLEX, DATAMODE, ANTENNA, RXANTENNA, CTCSS, TSQL, DTCS, CSQL, + PREAMP, AGC, ATTENUATOR, MODINPUT, AFGAIN, RFGAIN, SQUELCH, TXPOWER, MICGAIN, COMPLEVEL, MONITORLEVEL, VOXGAIN, ANTIVOXGAIN, + FAGCFUNC, NBFUNC, COMPFUNC, VOXFUNC, TONEFUNC, TSQLFUNC, SBKINFUNC, FBKINFUNC, ANFFUNC, NRFUNC, AIPFUNC, APFFUNC, MONFUNC, MNFUNC,RFFUNC, + AROFUNC, MUTEFUNC, VSCFUNC, REVFUNC, SQLFUNC, ABMFUNC, BCFUNC, MBCFUNC, RITFUNC, AFCFUNC, SATMODEFUNC, SCOPEFUNC, + NBLEVEL, NBDEPTH, NBWIDTH, NRLEVEL, RIGINPUT, POWERONOFF, RITVALUE, + RESUMEFUNC, TBURSTFUNC, TUNERFUNC, LOCKFUNC, SMETER, POWERMETER, SWRMETER, ALCMETER, COMPMETER, VOLTAGEMETER, CURRENTMETER +}; + +struct value { + quint64 _value=0; + bool _valid = false; + bool _updated = false; + QDateTime _dateUpdated; +}; + +class rigstate +{ + +public: + + void invalidate(stateTypes s) { map[s]._valid = false; } + bool isValid(stateTypes s) { return map[s]._valid; } + bool isUpdated(stateTypes s) { return map[s]._updated; } + QDateTime whenUpdated(stateTypes s) { return map[s]._dateUpdated; } + + void set(stateTypes s, quint64 x, bool u) { + if (x != map[s]._value) { + _mutex.lock(); + map[s]._value = (quint64)x; + map[s]._valid = true; + map[s]._updated = u; + map[s]._dateUpdated = QDateTime::currentDateTime(); + _mutex.unlock(); + } + } + void set(stateTypes s, qint32 x, bool u) { + if ((quint64)x != map[s]._value) { + _mutex.lock(); + map[s]._value = (quint64)x; + map[s]._valid = true; + map[s]._updated = u; + map[s]._dateUpdated = QDateTime::currentDateTime(); + _mutex.unlock(); + } + } + void set(stateTypes s, quint16 x, bool u) { + if ((quint64)x != map[s]._value) { + _mutex.lock(); + map[s]._value = (quint64)x; + map[s]._valid = true; + map[s]._updated = u; + map[s]._dateUpdated = QDateTime::currentDateTime(); + _mutex.unlock(); + } + } + void set(stateTypes s, quint8 x, bool u) { + if ((quint64)x != map[s]._value) { + _mutex.lock(); + map[s]._value = (quint64)x; + map[s]._valid = true; + map[s]._updated = u; + map[s]._dateUpdated = QDateTime::currentDateTime(); + _mutex.unlock(); + } + } + void set(stateTypes s, bool x, bool u) { + if ((quint64)x != map[s]._value) { + _mutex.lock(); + map[s]._value = (quint64)x; + map[s]._valid = true; + map[s]._updated = u; + map[s]._dateUpdated = QDateTime::currentDateTime(); + _mutex.unlock(); + } + } + void set(stateTypes s, duplexMode x, bool u) { + if ((quint64)x != map[s]._value) { + _mutex.lock(); + map[s]._value = (quint64)x; + map[s]._valid = true; + map[s]._updated = u; + map[s]._dateUpdated = QDateTime::currentDateTime(); + _mutex.unlock(); + } + } + + void set(stateTypes s, rigInput x, bool u) { + if ((quint64)x != map[s]._value) { + _mutex.lock(); + map[s]._value = (quint64)x; + map[s]._valid = true; + map[s]._updated = u; + map[s]._dateUpdated = QDateTime::currentDateTime(); + _mutex.unlock(); + } + } + + bool getBool(stateTypes s) { return map[s]._value != 0; } + quint8 getChar(stateTypes s) { return (quint8)map[s]._value; } + quint16 getInt16(stateTypes s) { return (qint16)map[s]._value; } + qint32 getInt32(stateTypes s) { return (qint32)map[s]._value; } + quint64 getInt64(stateTypes s) { return map[s]._value; } + duplexMode getDuplex(stateTypes s) { return(duplexMode)map[s]._value; } + rigInput getInput(stateTypes s) { return(rigInput)map[s]._value; } + QMap map; + + +private: + //std::map > values; + QMutex _mutex; +}; + +#endif \ No newline at end of file diff --git a/udpserver.cpp b/udpserver.cpp index 9f6527c..d7dd5c8 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -96,6 +96,8 @@ udpServer::~udpServer() udpAudio->close(); delete udpAudio; } + emit haveNetworkStatus(QString("")); + } diff --git a/udpserver.h b/udpserver.h index 1bfbacd..4da37de 100644 --- a/udpserver.h +++ b/udpserver.h @@ -19,7 +19,6 @@ #include -#include #include "packettypes.h" #include "rigidentities.h" #include "audiohandler.h" @@ -34,6 +33,28 @@ struct SEQBUFENTRY { quint8 retransmitCount; }; + +struct SERVERUSER { + QString username; + QString password; + quint8 userType; +}; + +struct SERVERCONFIG { + bool enabled; + bool lan; + quint16 controlPort; + quint16 civPort; + quint16 audioPort; + int audioOutput; + int audioInput; + quint8 resampleQuality; + quint32 baudRate; + + QList users; +}; + + class udpServer : public QObject { Q_OBJECT diff --git a/udpserversetup.cpp b/udpserversetup.cpp deleted file mode 100644 index 72b5996..0000000 --- a/udpserversetup.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "udpserversetup.h" -#include "ui_udpserversetup.h" -#include "logcategories.h" - -extern void passcode(QString in,QByteArray& out); - -udpServerSetup::udpServerSetup(QWidget* parent) : - QDialog(parent), - ui(new Ui::udpServerSetup) -{ - ui->setupUi(this); - addUserLine("", "", 0); // Create a blank row if we never receive config. - - // Get any stored config information from the main form. - SERVERCONFIG config; - emit serverConfig(config,false); // Just send blank server config. -} - -udpServerSetup::~udpServerSetup() -{ - delete ui; -} - -// Slot to receive config. -void udpServerSetup::receiveServerConfig(SERVERCONFIG conf) -{ - qInfo() << "Getting server config"; - - ui->enableCheckbox->setChecked(conf.enabled); - ui->controlPortText->setText(QString::number(conf.controlPort)); - ui->civPortText->setText(QString::number(conf.civPort)); - ui->audioPortText->setText(QString::number(conf.audioPort)); - - int row = 0; - - for (int i = 0; i < ui->usersTable->rowCount(); i++) - { - ui->usersTable->removeRow(i); - } - - foreach (SERVERUSER user, conf.users) - { - if (user.username != "" && user.password != "") - { - addUserLine(user.username, user.password, user.userType); - row++; - } - } - - if (row == 0) { - addUserLine("", "", 0); - } - -} - -void udpServerSetup::accept() -{ - qInfo() << "Server config stored"; - SERVERCONFIG config; - config.enabled = ui->enableCheckbox->isChecked(); - config.controlPort = ui->controlPortText->text().toInt(); - config.civPort = ui->civPortText->text().toInt(); - config.audioPort = ui->audioPortText->text().toInt(); - - config.users.clear(); - - for (int row = 0; row < ui->usersTable->model()->rowCount(); row++) - { - if (ui->usersTable->item(row, 0) != NULL) - { - SERVERUSER user; - user.username = ui->usersTable->item(row, 0)->text(); - QLineEdit* password = (QLineEdit*)ui->usersTable->cellWidget(row, 1); - user.password = password->text(); - QComboBox* comboBox = (QComboBox*)ui->usersTable->cellWidget(row, 2); - user.userType = comboBox->currentIndex(); - config.users.append(user); - - } - else { - ui->usersTable->removeRow(row); - } - } - - emit serverConfig(config,true); - this->hide(); -} - - -void udpServerSetup::on_usersTable_cellClicked(int row, int col) -{ - qInfo() << "Clicked on " << row << "," << col; - if (row == ui->usersTable->model()->rowCount() - 1 && ui->usersTable->item(row, 0) != NULL) { - addUserLine("", "", 0); - } -} - -void udpServerSetup::onPasswordChanged() -{ - int row = sender()->property("row").toInt(); - QLineEdit* password = (QLineEdit*)ui->usersTable->cellWidget(row, 1); - QByteArray pass; - passcode(password->text(), pass); - password->setText(pass); - qInfo() << "password row" << row << "changed"; -} - -void udpServerSetup::addUserLine(const QString& user, const QString& pass, const int& type) -{ - ui->usersTable->insertRow(ui->usersTable->rowCount()); - ui->usersTable->setItem(ui->usersTable->rowCount() - 1, 0, new QTableWidgetItem(user)); - ui->usersTable->setItem(ui->usersTable->rowCount() - 1, 1, new QTableWidgetItem()); - ui->usersTable->setItem(ui->usersTable->rowCount() - 1, 2, new QTableWidgetItem()); - - QLineEdit* password = new QLineEdit(); - password->setProperty("row", (int)ui->usersTable->rowCount() - 1); - password->setEchoMode(QLineEdit::PasswordEchoOnEdit); - password->setText(pass); - connect(password, SIGNAL(editingFinished()), this, SLOT(onPasswordChanged())); - ui->usersTable->setCellWidget(ui->usersTable->rowCount() - 1, 1, password); - - QComboBox* comboBox = new QComboBox(); - comboBox->insertItems(0, { "Full User","Full with no TX","Monitor only" }); - comboBox->setCurrentIndex(type); - ui->usersTable->setCellWidget(ui->usersTable->rowCount() - 1, 2, comboBox); - -} diff --git a/udpserversetup.h b/udpserversetup.h deleted file mode 100644 index 54d47c4..0000000 --- a/udpserversetup.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef UDPSERVERSETUP_H -#define UDPSERVERSETUP_H - -#include -#include -#include - -#include - - -struct SERVERUSER { - QString username; - QString password; - quint8 userType; -}; - -struct SERVERCONFIG { - bool enabled; - bool lan; - quint16 controlPort; - quint16 civPort; - quint16 audioPort; - int audioOutput; - int audioInput; - quint8 resampleQuality; - quint32 baudRate; - - QList users; -}; - -namespace Ui { - class udpServerSetup; -} - -class udpServerSetup : public QDialog -{ - Q_OBJECT - -public: - explicit udpServerSetup(QWidget* parent = 0); - ~udpServerSetup(); - -private slots: - void on_usersTable_cellClicked(int row, int col); - void onPasswordChanged(); - -public slots: - void receiveServerConfig(SERVERCONFIG conf); - -signals: - void serverConfig(SERVERCONFIG conf, bool store); - -private: - Ui::udpServerSetup* ui; - void accept(); - QList userTypes; - void addUserLine(const QString &user, const QString &pass, const int &type); -}; - -#endif // UDPSERVER_H diff --git a/wfmain.ui b/wfmain.ui index f5b6cbd..a938ee3 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -6,8 +6,8 @@ 0 0 - 948 - 554 + 946 + 569 @@ -2038,324 +2038,132 @@ Settings - + - - - - - Draw Peaks - - - - - - - When tuning, set lower digits to zero - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + + + 10 + - 0 + 10 - - - Use System Theme - - - - - - - Waterfall Dark Theme - - - - - - - Anti-Alias Waterfall - - - - - - - Interpolate Waterfall - - - true - - - - - - - Show full screen - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - - - <html><head/><body><p>Click here to adjust the frequency reference on the IC-9700.</p></body></html> - - - Adjust Reference - - - - - - - Satellite Ops - - - - - - - Modulation Input: - - - - - - - Modulation Input - - - Transmit modulation source - - - QComboBox::AdjustToContents - - - - - - - Data Mod Input: - - - - - - - Data Modulation Input - - - Transmit Data-mode modulation input source - - - QComboBox::AdjustToContents - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - - - PTT On - - - Ctrl+S - - - - - - - PTT Off - - - - - - - Enable PTT Controls - - - - - - - Secondary Meter Selection: - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - - - <html><head/><body><p>Select this option if the rig is pluged into the computer using a USB cable or a serial connection. </p></body></html> - - - Connect over USB (serial) - - - radioConnectionSerialNetworkGrp - - - - - - - Serial Device: - - - - - - - <html><head/><body><p>Select a serial port here. </p><p>Once selected, check &quot;Enable USB(serial), press &quot;Save Settings&quot;, exit, and re-start wfview. </p></body></html> - - - Serial Device Selector - - - - - - - Baud: - - - - - - - <html><head/><body><p>Baud rate selection menu. </p><p>For the IC-7300 select 115200. </p><p>Older rigs may require other settings. </p><p>Be sure to match what baud rate the rig is set to. Using the highese supported baud rate for the radio is recommended. </p><p>Please press &quot;Save Settings&quot; and re-launc wfview for this to take effect.</p></body></html> - - - baud rate - - - baud rate selection menu - - - - - - - <html><head/><body><p>Press here to set up the built-in rig server. The built-in server is intended to allow access over the network to a serial or USB-connected radio. </p></body></html> - - - Server Setup - - - - - - - <html><head/><body><p>If you are using an older (year 2010) radio, you may need to enable this option to manually specify the CI-V address. This option is also useful for radios that do not have CI-V Transceive enabled and thus will not answer our broadcast query for connected rigs on the CI-V bus.</p><p>If you have a modern radio with CI-V Transceive enabled, you should not need to check this box. </p><p>You will need to Save Settings and re-launch wfview for this to take effect. </p></body></html> - - - Manual Radio CI-V Address: - - - - - - - false - + - 50 + 150 0 - 50 + 150 16777215 - - <html><head/><body><p>Enter the address in as hexidecimal, without any prefix, just as the radio presents the address in the menu. </p><p>Here are some common examples:</p> + + + + + + 0 + + + + + + + + + + 140 + 0 + + + + Radio Connection + + + + + + Serial (USB) + + + buttonGroup + + + + + + + Network + + + buttonGroup + + + + + + + + + + + 300 + 0 + + + + + 300 + 16777215 + + + + CI-V and Model + + + + + + <html><head/><body><p>If you are using an older (year 2010) radio, you may need to enable this option to manually specify the CI-V address. This option is also useful for radios that do not have CI-V Transceive enabled and thus will not answer our broadcast query for connected rigs on the CI-V bus.</p><p>If you have a modern radio with CI-V Transceive enabled, you should not need to check this box. </p><p>You will need to Save Settings and re-launch wfview for this to take effect. </p></body></html> + + + Manual Radio CI-V Address: + + + + + + + <html><head/><body><p>Only check for older radios!</p><p>This checkbox forces wfview to trust that the CI-V address is also the model number of the radio. This is only useful for older radios that do not reply to our Rig ID requests (0x19 0x00). Do not check this box unless you have an older radio. </p></body></html> + + + Use CI-V address as Model ID too + + + + + + + false + + + + 50 + 0 + + + + + 50 + 16777215 + + + + <html><head/><body><p>Enter the address in as hexidecimal, without any prefix, just as the radio presents the address in the menu. </p><p>Here are some common examples:</p> <p>IC-706: 58 <br/>IC-756: 50 <br/>IC-756 Pro: 5C @@ -2367,496 +2175,1493 @@ <br/>IC-7300: 94 </p><p>This setting is typically needed for older radios and for radios that do not have CI-V Transceive enabled. </p> <p>After changing, press Save Settings and re-launch wfview.</p></body></html> - - - auto - - - - - - - <html><head/><body><p>Only check for older radios!</p><p>This checkbox forces wfview to trust that the CI-V address is also the model number of the radio. This is only useful for older radios that do not reply to our Rig ID requests (0x19 0x00). Do not check this box unless you have an older radio. </p></body></html> - - - Use as Model too - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - <html><head/><body><p>Connection to the radio is via network. </p><p>This means you are connecting to a radio with native ethernet or wifi, such as the IC-705, IC-7610, IC-7850, IC-R8600, or IC-9700</p><p>You should also select this option if you are connecting to another instance of wfview over a network. </p></body></html> - - - Connect over LAN - - - radioConnectionSerialNetworkGrp - - - - - - - Press here to initiate the network connection to the rig. - - - Connect - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::RightToLeft - - - Enable RigCtld - - - - - - - Port - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Virtual Serial Port - - - - - - - <html><head/><body><p>Use this to define a virtual serial port. </p><p><br/></p><p>On Windows, the virtual serial port can be used to connect to a serial port loopback device, through which other programs can connect to the radio. </p><p><br/></p><p>On Linux and macOS, the port defined here is a pseudo-terminal device, which may be connected to directly by any program designed for a serial connection. </p></body></html> - - - Virtual Serial Port Selector - + + + auto + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + Serial Connected Radios + + + + + + Serial Device: + + + + + + + + 180 + 0 + + + + + 180 + 16777215 + + + + + + + + Baud Rate + + + + + + + + 120 + 16777215 + + + + + + + + <html><head/><body><p>This feature is for older radios that respond best to an RTS serial port signal than a PTT command.</p><p><br/>For radios lacking PTT commands, this is automatic and transparent, but for radios which have a PTT command, you can check this box to override and force the PTT to be done using RTS. Do not check this box unless you really need this and have an appropriate adapter with RTS connected to the PTT line of the transceiver. </p></body></html> + + + Send RTS for PTT + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Network Connected Radios + + + + + + 0 + + + 0 + + + 0 + + + + + Hostname + + + + + + + + 256 + 0 + + + + + 256 + 16777215 + + + + + + + + Control Port + + + + + + + + 75 + 16777215 + + + + 50001 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Username + + + + + + + + 256 + 0 + + + + + 256 + 16777215 + + + + + + + + Password + + + + + + + + 180 + 0 + + + + + 180 + 16777215 + + + + QLineEdit::PasswordEchoOnEdit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + RX Latency (ms) + + + + + + + + 0 + 0 + + + + 30 + + + 500 + + + Qt::Horizontal + + + + + + + 0 + + + + + + + TX Latency (ms) + + + + + + + 30 + + + 500 + + + Qt::Horizontal + + + + + + + 0 + + + + + + + RX Codec + + + + + + + Receive Audio Codec Selector + + + + + + + TX Codec + + + + + + + Transmit Audio Codec Selector + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Sample Rate + + + + + + + Audio Sample Rate Selector + + + + 48000 + + + + + 24000 + + + + + 16000 + + + + + 8000 + + + + + + + + Audio Output + + + + + + + + 300 + 16777215 + + + + Audio Output Selector + + + + + + + Audio Input + + + + + + + + 300 + 16777215 + + + + Audio Input Selector + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + 0 + + + + + Connect + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Draw Peaks + + + + + + + When tuning, set lower digits to zero + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Waterfall Dark Theme + + + + + + + Interpolate Waterfall + + + true + + + + + + + Use System Theme + + + + + + + Anti-Alias Waterfall + + + + + + + Show full screen + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Secondary Meter Selection: + + + + + + + + + + Set up radio polling. The radio's meter is polled every-other interval. + + + Polling + + + + + + Polling + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + 0 + + + + + Enable PTT Controls + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Modulation Input: + + + + + + + Modulation Input + + + Transmit modulation source + + + QComboBox::AdjustToContents + + + + + + + Data Mod Input: + + + + + + + Data Modulation Input + + + Transmit Data-mode modulation input source + + + QComboBox::AdjustToContents + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Press here to set the clock of the radio. The command will be sent to the radio when the seconds go to zero. + + + Set Clock + + + + + + + Check this box to set the radio's clock to UTC. Otherwise, the clock will be set to the local timezone of this computer. + + + Use UTC + + + + + + + Satellite Ops + + + + + + + <html><head/><body><p>Click here to adjust the frequency reference on the IC-9700.</p></body></html> + + + Adjust Reference + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + Manual PTT Toggle + + + + + + + PTT On + + + Ctrl+S + + + + + + + PTT Off + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 279 + + + + + + + + + + QLayout::SetDefaultConstraint + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Enable + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Server Setup + + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Contol Port + + + + + + + + 0 + 0 + + + + + 130 + 25 + + + + + 130 + 25 + + + + 99999 + + + 50001 + + + true + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Civ Port + + + + + + + + 130 + 25 + + + + + 130 + 25 + + + + 99999 + + + 50002 + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Audio Port + + + + + + + + 130 + 25 + + + + + 130 + 25 + + + + 99999 + + + 50003 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + RX Audio Input + + + + + + + + 100 + 0 + + + + + 300 + 16777215 + + + + + + + + TX Audio Output + + + + + + + + 100 + 0 + + + + + 300 + 16777215 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + + + + + 400 + 160 + + + + + 750 + 330 + + + + QFrame::StyledPanel + + + 1 + + + 0 + + + 3 + + + false + + + 100 + + + false + + + true + + + false + + + true + + + false + + + + Username + + + + + Password + + + + + Admin + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + 0 + + + 0 + + + + + Qt::LeftToRight + + + Enable RigCtld + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + Port + + + + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + + + + + Qt::Horizontal + + + + 422 + 20 + + + + + + + + + + + + Virtual Serial Port + + + + + + + + 250 + 0 + + + + + 250 + 16777215 + + + + <html><head/><body><p>Use this to define a virtual serial port. </p><p><br/></p><p>On Windows, the virtual serial port can be used to connect to a serial port loopback device, through which other programs can connect to the radio. </p><p><br/></p><p>On Linux and macOS, the port defined here is a pseudo-terminal device, which may be connected to directly by any program designed for a serial connection. </p></body></html> + + + Virtual Serial Port Selector + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + This page contains experimental features. Use at your own risk. + + + + + + + + + + + <html><head/><body><p>This button runs debug functions, and is provided as a convenience for programmers. The functions executed are under:</p><p><span style=" color:#ffff55;">void</span><span style=" color:#55ff55;">wfmain</span><span style=" color:#aaaaaa;">::</span><span style=" font-weight:600;">on_debugBtn_clicked</span><span style=" color:#aaaaaa;">()</span></p><p>in wfmain.cpp.</p></body></html> + + + Debug + + + Ctrl+Alt+D + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 320 + + + + + + + - - - - - - Radio IP Address - - - - - - - - - - Radio Control Port - - - - - - - 50001 - - - - - - - - - - - Username - - - - - - - - - - Password - - - - - - - Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhSensitiveData - - - QLineEdit::PasswordEchoOnEdit - - - - - - - - - - - RX Latency (ms) - - - - - - - - 0 - 0 - - - - 30 - - - 500 - - - Qt::Horizontal - - - - - - - 0 - - - - - - - TX Latency (ms) - - - - - - - 30 - - - 500 - - - Qt::Horizontal - - - - - - - 0 - - - - - - - RX Codec - - - - - - - Receive Audio Codec Selector - - - - - - - TX Codec - - - - - - - Transmit Audio Codec Selector - - - - - - - - - - - Sample Rate - - - - - - - Audio Sample Rate Selector - - - - 48000 - - - - - 24000 - - - - - 16000 - - - - - 8000 - - - - - - - - Audio Output - - - - - - - - 300 - 16777215 - - - - Audio Output Selector - - - - - - - Audio Input - - - - - - - - 300 - 16777215 - - - - Audio Input Selector - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - - - <html><head/><body><p>This button runs debug functions, and is provided as a convenience for programmers. The functions executed are under:</p><p><span style=" color:#ffff55;">void</span><span style=" color:#55ff55;">wfmain</span><span style=" color:#aaaaaa;">::</span><span style=" font-weight:600;">on_debugBtn_clicked</span><span style=" color:#aaaaaa;">()</span></p><p>in wfmain.cpp.</p></body></html> - - - Debug - - - Ctrl+Alt+D - - - - - - - Set up radio polling. The radio's meter is polled every-other interval. - - - Polling - - - - - - Polling - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - About - - - - - - - Save Settings - - - - - - - - 75 - true - - - - Exit Program - - - - - - - - - 0 - - - - - Please note: Changing the built-in network server requires pressing "Save Settings", closing wfview, and re-opening. - - - - - - - - - 0 - - - - - <html><head></head><body><p>Please see the <a href="https://wfview.org/wfview-user-manual/settings-tab/">User Manual</a> for more information. </p></body></html> - - - Qt::RichText - - - true - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - + + + + 0 + + + 0 + + + 0 + + + + + About + + + + + + + Save Settings + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Exit Program + + + + + @@ -2865,7 +3670,7 @@ 0 0 - 948 + 946 22 @@ -2887,6 +3692,6 @@ - + diff --git a/wfview.pro b/wfview.pro index 95862aa..f105bb3 100644 --- a/wfview.pro +++ b/wfview.pro @@ -152,7 +152,6 @@ SOURCES += main.cpp\ audiohandler.cpp \ calibrationwindow.cpp \ satellitesetup.cpp \ - udpserversetup.cpp \ udpserver.cpp \ meter.cpp \ qledlabel.cpp \ @@ -174,7 +173,6 @@ HEADERS += wfmain.h \ audiohandler.h \ calibrationwindow.h \ satellitesetup.h \ - udpserversetup.h \ udpserver.h \ packettypes.h \ meter.h \ diff --git a/wfview.sln b/wfview.sln index 397c02c..ff4a8cd 100644 --- a/wfview.sln +++ b/wfview.sln @@ -7,33 +7,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfview", "wfview.vcxproj", EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 Release|x86 = Release|x86 - Template|Win32 = Template|Win32 - Template|x64 = Template|x64 - Template|x86 = Template|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|Win32.ActiveCfg = Debug|Win32 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|Win32.Build.0 = Debug|Win32 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x64.ActiveCfg = Debug|x64 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x64.Build.0 = Debug|x64 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x86.ActiveCfg = Debug|x64 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|Win32.ActiveCfg = Release|Win32 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|Win32.Build.0 = Release|Win32 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x64.ActiveCfg = Release|x64 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x64.Build.0 = Release|x64 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.ActiveCfg = Release|x64 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|Win32.ActiveCfg = Debug|Win32 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|Win32.Build.0 = Debug|Win32 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|x64.ActiveCfg = Release|x64 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|x64.Build.0 = Release|x64 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|x86.ActiveCfg = Release|x64 - {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|x86.Build.0 = Release|x64 + {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x86.ActiveCfg = Debug|Win32 + {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x86.Build.0 = Debug|Win32 + {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.ActiveCfg = Release|Win32 + {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wfview.vcxproj b/wfview.vcxproj index 030b9d6..21e1461 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,7 +16,8 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild + $(MSBuildProjectDirectory)\QtMsBuild + v142 @@ -36,7 +37,10 @@ debug\ wfview - + + + + @@ -44,11 +48,37 @@ - debug\debug\wfviewtruerelease\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport - + + + + + + debug\ + debug\ + wfview + true + + + release\ + release\ + wfview + true + false + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + + - .;..\qcustomplot;..\opus\include;resampler;rtaudio;release;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;resampler;release;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -57,14 +87,16 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";__WINDOWS_WASAPI__;GITSHORT="66912e1";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="62771f6";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - + + MultiThreadedDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -85,12 +117,31 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";__WINDOWS_WASAPI__;GITSHORT=\"66912e1\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"62771f6\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + - .;..\qcustomplot;..\opus\include;resampler;rtaudio;debug;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;resampler;debug;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -99,13 +150,14 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";__WINDOWS_WASAPI__;GITSHORT="66912e1";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="62771f6";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -124,9 +176,28 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";__WINDOWS_WASAPI__;GITSHORT=\"66912e1\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"62771f6\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + @@ -154,198 +225,53 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Document true @@ -362,119 +288,21 @@ release\moc_predefs.h;%(Outputs) true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -508,30 +336,16 @@ - - - - - - - - - - resourcesresources + resources + resources + - - - - - - - - - - stylestyle + style + style + @@ -550,6 +364,9 @@ - + + + + \ No newline at end of file diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index d78480d..46000f0 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -120,9 +120,6 @@ Source Files - - Source Files - Source Files @@ -203,9 +200,6 @@ Header Files - - Header Files - Header Files @@ -260,8 +254,6 @@ - - diff --git a/wfview.vcxproj.user b/wfview.vcxproj.user index dd9de9b..a638198 100644 --- a/wfview.vcxproj.user +++ b/wfview.vcxproj.user @@ -1,11 +1,15 @@  - PATH=$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(PATH) - PATH=$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(PATH) + + 2021-11-22T18:24:33.3752914Z + + + 2021-11-22T18:24:41.6960953Z + \ No newline at end of file From 8ec82e74069c3381ff8eb8495aff8e6bc904e26c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 4 Jan 2022 19:04:36 +0000 Subject: [PATCH 002/323] Various tidying in server and client code --- packettypes.h | 13 +++++++++++++ udphandler.h | 9 --------- udpserver.cpp | 33 ++++++++++++++++++--------------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/packettypes.h b/packettypes.h index 46cad4c..3a12b20 100644 --- a/packettypes.h +++ b/packettypes.h @@ -4,6 +4,19 @@ #pragma pack(push, 1) +// Various settings used by both client and server +#define PURGE_SECONDS 10 +#define TOKEN_RENEWAL 60000 +#define PING_PERIOD 500 +#define IDLE_PERIOD 100 +#define TXAUDIO_PERIOD 10 +#define AREYOUTHERE_PERIOD 500 +#define WATCHDOG_PERIOD 500 +#define RETRANSMIT_PERIOD 100 // How often to attempt retransmit +#define LOCK_PERIOD 10 // How long to try to lock mutex (ms) +#define STALE_CONNECTION 15 // Not heard from in this many seconds + + // Fixed Size Packets #define CONTROL_SIZE 0x10 #define WATCHDOG_SIZE 0x14 diff --git a/udphandler.h b/udphandler.h index 1a8e975..3357f32 100644 --- a/udphandler.h +++ b/udphandler.h @@ -24,15 +24,6 @@ #include "audiohandler.h" #include "packettypes.h" -#define PURGE_SECONDS 10 -#define TOKEN_RENEWAL 60000 -#define PING_PERIOD 100 -#define IDLE_PERIOD 100 -#define TXAUDIO_PERIOD 10 -#define AREYOUTHERE_PERIOD 500 -#define WATCHDOG_PERIOD 500 -#define RETRANSMIT_PERIOD 100 -#define LOCK_PERIOD 100 struct udpPreferences { QString ipAddress; diff --git a/udpserver.cpp b/udpserver.cpp index d7dd5c8..048800e 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1,8 +1,6 @@ #include "udpserver.h" #include "logcategories.h" -#define STALE_CONNECTION 15 -#define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms udpServer::udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudio) : config(config), outAudio(outAudio), @@ -96,6 +94,21 @@ udpServer::~udpServer() udpAudio->close(); delete udpAudio; } + + if (rxAudioThread != Q_NULLPTR) { + rxAudioThread->quit(); + rxAudioThread->wait(); + rxaudio = Q_NULLPTR; + rxAudioThread = Q_NULLPTR; + } + + if (txAudioThread != Q_NULLPTR) { + txAudioThread->quit(); + txAudioThread->wait(); + txaudio = Q_NULLPTR; + txAudioThread = Q_NULLPTR; + } + emit haveNetworkStatus(QString("")); } @@ -106,8 +119,6 @@ void udpServer::receiveRigCaps(rigCapabilities caps) this->rigCaps = caps; } -#define RETRANSMIT_PERIOD 100 - void udpServer::controlReceived() { // Received data on control port. @@ -596,7 +607,7 @@ void udpServer::audioReceived() current->pingTimer = new QTimer(); connect(current->pingTimer, &QTimer::timeout, this, std::bind(&udpServer::sendPing, this, &audioClients, current, (quint16)0x00, false)); - current->pingTimer->start(100); + current->pingTimer->start(PING_PERIOD); current->retransmitTimer = new QTimer(); connect(current->retransmitTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRetransmitRequest, this, current)); @@ -630,15 +641,9 @@ void udpServer::audioReceived() sendPing(&audioClients, current, in->seq, true); } else if (in->reply == 0x01) { - if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) + if (in->seq == current->pingSeq) { - // A Reply to our ping! - if (in->seq == current->pingSeq) { - current->pingSeq++; - } - else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; - } + current->pingSeq++; } } } @@ -1292,8 +1297,6 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) return; } -#define PURGE_SECONDS 60 - void udpServer::watchdog() { QDateTime now = QDateTime::currentDateTime(); From fce3a3d6be3fdab54d0549922880bde73fe34915 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 4 Jan 2022 19:50:38 +0000 Subject: [PATCH 003/323] Try to fix server --- audiohandler.cpp | 1 + udpserver.cpp | 25 +++++-------------------- udpserver.h | 4 ++-- wfmain.cpp | 10 ++++------ wfmain.h | 2 +- 5 files changed, 13 insertions(+), 29 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index d0381fa..61d9676 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -63,6 +63,7 @@ bool audioHandler::init(audioSetup setupIn) if (isInitialized) { return false; } + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; /* 0x01 uLaw 1ch 8bit diff --git a/udpserver.cpp b/udpserver.cpp index 048800e..7163fda 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1,17 +1,16 @@ #include "udpserver.h" #include "logcategories.h" -udpServer::udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudio) : - config(config), - outAudio(outAudio), - inAudio(inAudio) +udpServer::udpServer() { qInfo(logUdpServer()) << "Starting udp server"; } -void udpServer::init() +void udpServer::init(SERVERCONFIG conf, audioSetup out, audioSetup in) { - + this->config = conf; + this->outAudio = out; + this->inAudio = in; srand(time(NULL)); // Generate random key timeStarted.start(); // Convoluted way to find the external IP address, there must be a better way???? @@ -95,20 +94,6 @@ udpServer::~udpServer() delete udpAudio; } - if (rxAudioThread != Q_NULLPTR) { - rxAudioThread->quit(); - rxAudioThread->wait(); - rxaudio = Q_NULLPTR; - rxAudioThread = Q_NULLPTR; - } - - if (txAudioThread != Q_NULLPTR) { - txAudioThread->quit(); - txAudioThread->wait(); - txaudio = Q_NULLPTR; - txAudioThread = Q_NULLPTR; - } - emit haveNetworkStatus(QString("")); } diff --git a/udpserver.h b/udpserver.h index 4da37de..253e491 100644 --- a/udpserver.h +++ b/udpserver.h @@ -60,11 +60,11 @@ class udpServer : public QObject Q_OBJECT public: - udpServer(SERVERCONFIG config,audioSetup outAudio, audioSetup inAudio); + udpServer(); ~udpServer(); public slots: - void init(); + void init(SERVERCONFIG sc, audioSetup ai, audioSetup ao); void dataForServer(QByteArray); void receiveAudioData(const audioPacket &data); void receiveRigCaps(rigCapabilities caps); diff --git a/wfmain.cpp b/wfmain.cpp index efdec3e..bbe4923 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -964,13 +964,13 @@ void wfmain::setServerToPrefs() if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; - udp = new udpServer(serverConfig,serverTxSetup,serverRxSetup); + udp = new udpServer(); serverThread = new QThread(this); udp->moveToThread(serverThread); - connect(this, SIGNAL(initServer()), udp, SLOT(init())); + connect(this, SIGNAL(initServer(SERVERCONFIG, audioSetup, audioSetup)), udp, SLOT(init(SERVERCONFIG, audioSetup, audioSetup))); connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); if (!prefs.enableLAN && udp != Q_NULLPTR) { @@ -979,7 +979,7 @@ void wfmain::setServerToPrefs() serverThread->start(); - emit initServer(); + emit initServer(serverConfig, serverTxSetup, serverRxSetup); connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); ui->statusBar->showMessage(QString("Server enabled"), 1000); @@ -1638,8 +1638,7 @@ void wfmain::loadSettings() } ui->serverRXAudioInputCombo->blockSignals(false); - serverRxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); - serverRxSetup.resampleQuality = rxSetup.resampleQuality; + serverTxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); ui->serverTXAudioOutputCombo->blockSignals(true); serverTxSetup.name = settings->value("ServerAudioOutput", "").toString(); @@ -1659,7 +1658,6 @@ void wfmain::loadSettings() ui->serverTXAudioOutputCombo->blockSignals(false); serverTxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); - serverTxSetup.resampleQuality = rxSetup.resampleQuality; int row = 0; ui->serverUsersTable->setRowCount(0); diff --git a/wfmain.h b/wfmain.h index 498b6fe..b1b23c0 100644 --- a/wfmain.h +++ b/wfmain.h @@ -162,7 +162,7 @@ signals: void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); void sendCloseComm(); void sendChangeLatency(quint16 latency); - void initServer(); + void initServer(SERVERCONFIG sc, audioSetup ai, audioSetup ao); void sendRigCaps(rigCapabilities caps); void requestRigState(); void stateUpdated(); From 18646ab0cf5030ecf71fb7d7c8116159248860e4 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 4 Jan 2022 19:55:48 +0000 Subject: [PATCH 004/323] Add SERVERCONFIG metatype --- wfmain.cpp | 1 + wfmain.h | 1 + 2 files changed, 2 insertions(+) diff --git a/wfmain.cpp b/wfmain.cpp index bbe4923..1f874f5 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -40,6 +40,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType (); + qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType(); diff --git a/wfmain.h b/wfmain.h index b1b23c0..666e800 100644 --- a/wfmain.h +++ b/wfmain.h @@ -879,6 +879,7 @@ Q_DECLARE_METATYPE(struct mode_info) Q_DECLARE_METATYPE(struct udpPreferences) Q_DECLARE_METATYPE(struct audioPacket) Q_DECLARE_METATYPE(struct audioSetup) +Q_DECLARE_METATYPE(struct SERVERCONFIG) Q_DECLARE_METATYPE(struct timekind) Q_DECLARE_METATYPE(struct datekind) Q_DECLARE_METATYPE(enum rigInput) From 1611058f7755f078fb76f96fac7dde0edffd85d2 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 4 Jan 2022 21:26:03 +0000 Subject: [PATCH 005/323] Maybe fix audio crash? --- audiohandler.cpp | 2 +- udpserver.cpp | 5 +++++ wfmain.cpp | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 61d9676..9a86577 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -63,7 +63,6 @@ bool audioHandler::init(audioSetup setupIn) if (isInitialized) { return false; } - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; /* 0x01 uLaw 1ch 8bit @@ -77,6 +76,7 @@ bool audioHandler::init(audioSetup setupIn) setup = setupIn; setup.radioChan = 1; setup.bits = 8; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; if (setup.codec == 0x01 || setup.codec == 0x20) { setup.ulaw = true; diff --git a/udpserver.cpp b/udpserver.cpp index 7163fda..3cd2a42 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -8,6 +8,9 @@ udpServer::udpServer() void udpServer::init(SERVERCONFIG conf, audioSetup out, audioSetup in) { + + qInfo(logUdpServer()) << "Input audio device:" << in.name; + qInfo(logUdpServer()) << "Output audio device:" << out.name; this->config = conf; this->outAudio = out; this->inAudio = in; @@ -318,6 +321,7 @@ void udpServer::controlReceived() outAudio.codec = current->txCodec; outAudio.samplerate = current->txSampleRate; outAudio.latency = current->txBufferLen; + outAudio.isinput = false; txaudio = new audioHandler(); txAudioThread = new QThread(this); @@ -339,6 +343,7 @@ void udpServer::controlReceived() { inAudio.codec = current->rxCodec; inAudio.samplerate = current->rxSampleRate; + inAudio.isinput = true; rxaudio = new audioHandler(); diff --git a/wfmain.cpp b/wfmain.cpp index 1f874f5..2bda382 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -964,13 +964,15 @@ void wfmain::setServerToPrefs() if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; - + qInfo(logAudio()) << "Audio Input device " << serverRxSetup.name; + qInfo(logAudio()) << "Audio Output device " << serverTxSetup.name; udp = new udpServer(); serverThread = new QThread(this); udp->moveToThread(serverThread); + connect(this, SIGNAL(initServer(SERVERCONFIG, audioSetup, audioSetup)), udp, SLOT(init(SERVERCONFIG, audioSetup, audioSetup))); connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); @@ -983,6 +985,7 @@ void wfmain::setServerToPrefs() emit initServer(serverConfig, serverTxSetup, serverRxSetup); connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); + ui->statusBar->showMessage(QString("Server enabled"), 1000); } From a91450c90b3f6d92dbb7c68cbeae6d83d4c9adf5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 4 Jan 2022 21:30:48 +0000 Subject: [PATCH 006/323] Maybe fix server audio issue? --- udpserver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/udpserver.cpp b/udpserver.cpp index d7dd5c8..4bdeede 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -322,6 +322,7 @@ void udpServer::controlReceived() outAudio.codec = current->txCodec; outAudio.samplerate = current->txSampleRate; outAudio.latency = current->txBufferLen; + outAudio.isinput = false; txaudio = new audioHandler(); txAudioThread = new QThread(this); @@ -343,6 +344,7 @@ void udpServer::controlReceived() { inAudio.codec = current->rxCodec; inAudio.samplerate = current->rxSampleRate; + inAudio.isinput = true; rxaudio = new audioHandler(); From f77defd9d75f7a10d3c2bbac0a5b7a863e152be0 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 5 Jan 2022 14:40:40 +0000 Subject: [PATCH 007/323] Fix silly typo which was causing audio crash! --- wfmain.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index efdec3e..4ef1ecc 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1639,7 +1639,6 @@ void wfmain::loadSettings() ui->serverRXAudioInputCombo->blockSignals(false); serverRxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); - serverRxSetup.resampleQuality = rxSetup.resampleQuality; ui->serverTXAudioOutputCombo->blockSignals(true); serverTxSetup.name = settings->value("ServerAudioOutput", "").toString(); @@ -1653,13 +1652,12 @@ void wfmain::loadSettings() serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(serverAudioOutputIndex).toInt(); #else QVariant v = ui->serverTXAudioOutputCombo->currentData(); - serverRxSetup.port = v.value(); + serverTxSetup.port = v.value(); #endif } ui->serverTXAudioOutputCombo->blockSignals(false); serverTxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); - serverTxSetup.resampleQuality = rxSetup.resampleQuality; int row = 0; ui->serverUsersTable->setRowCount(0); From 9b8221b32e1cc8d28da00eae8603c2de4603fc26 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 5 Jan 2022 14:45:34 +0000 Subject: [PATCH 008/323] Ignore wrong ping seq --- udphandler.cpp | 4 ---- udpserver.cpp | 36 +++++++++--------------------------- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 7e88181..3911292 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1099,10 +1099,6 @@ void udpBase::dataReceived(QByteArray r) // This is response to OUR request so increment counter pingSendSeq++; } - else { - // Not sure what to do here, need to spend more time with the protocol but will try sending ping with same seq next time. - //qInfo(logUdp()) << this->metaObject()->className() << "Received out-of-sequence ping response. Sent:" << pingSendSeq << " received " << in->seq; - } } else { qInfo(logUdp()) << this->metaObject()->className() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16]; diff --git a/udpserver.cpp b/udpserver.cpp index 4bdeede..d67c23d 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -208,15 +208,9 @@ void udpServer::controlReceived() sendPing(&controlClients, current, in->seq, true); } else if (in->reply == 0x01) { - if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) - { - // A Reply to our ping! - if (in->seq == current->pingSeq) { - current->pingSeq++; - } - else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; - } + // A Reply to our ping! + if (in->seq == current->pingSeq) { + current->pingSeq++; } } } @@ -485,15 +479,9 @@ void udpServer::civReceived() sendPing(&civClients, current, in->seq, true); } else if (in->reply == 0x01) { - if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) - { - // A Reply to our ping! - if (in->seq == current->pingSeq) { - current->pingSeq++; - } - else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; - } + // A Reply to our ping! + if (in->seq == current->pingSeq) { + current->pingSeq++; } } } @@ -632,15 +620,9 @@ void udpServer::audioReceived() sendPing(&audioClients, current, in->seq, true); } else if (in->reply == 0x01) { - if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) - { - // A Reply to our ping! - if (in->seq == current->pingSeq) { - current->pingSeq++; - } - else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; - } + // A Reply to our ping! + if (in->seq == current->pingSeq) { + current->pingSeq++; } } } From 8bf107fadc68b62134c690f0372f8cb58832eafb Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 5 Jan 2022 14:53:12 +0000 Subject: [PATCH 009/323] Fix a few compile warnings --- rigctld.cpp | 2 +- wfmain.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rigctld.cpp b/rigctld.cpp index daf779d..e7c8c46 100644 --- a/rigctld.cpp +++ b/rigctld.cpp @@ -199,7 +199,7 @@ void rigCtlClient::socketReadyRead() response.append("1"); // rigctld protocol version response.append(QString("%1").arg(rigCaps.rigctlModel)); response.append("0"); // Print something - bandType lastBand; + bandType lastBand=(bandType)-1; for (bandType band : rigCaps.bands) { if (band != lastBand) diff --git a/wfmain.cpp b/wfmain.cpp index 4ef1ecc..0984aae 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1782,17 +1782,17 @@ void wfmain::on_serverEnableCheckbox_clicked(bool checked) void wfmain::on_serverControlPortText_textChanged(QString text) { - serverConfig.controlPort = ui->serverControlPortText->text().toInt(); + serverConfig.controlPort = text.toInt(); } void wfmain::on_serverCivPortText_textChanged(QString text) { - serverConfig.civPort = ui->serverCivPortText->text().toInt(); + serverConfig.civPort = text.toInt(); } void wfmain::on_serverAudioPortText_textChanged(QString text) { - serverConfig.audioPort = ui->serverAudioPortText->text().toInt(); + serverConfig.audioPort = text.toInt(); } void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) From 3035e5c712cfbd68dc27699864dc48375884608b Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 5 Jan 2022 15:27:46 +0000 Subject: [PATCH 010/323] Attempt to enable FEC, not sure if it works though? --- audiohandler.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index d0381fa..6b977c0 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -605,8 +605,12 @@ void audioHandler::incomingAudio(audioPacket inPacket) qInfo(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.samplerate / 50); return; } - nSamples = opus_decode(decoder, in, inPacket.data.size(), out, (setup.samplerate / 50), 0); - + if (inPacket.seq > lastSentSeq + 1) { + nSamples = opus_decode(decoder, in, inPacket.data.size(), out, (setup.samplerate / 50), 1); + } + else { + nSamples = opus_decode(decoder, in, inPacket.data.size(), out, (setup.samplerate / 50), 0); + } if (nSamples < 0) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decode failed:" << opus_strerror(nSamples) << "packet size" << inPacket.data.length(); @@ -700,12 +704,17 @@ void audioHandler::incomingAudio(audioPacket inPacket) } //qDebug(logAudio()) << "Adding packet to buffer:" << inPacket.seq << ": " << inPacket.data.length(); - lastSentSeq = inPacket.seq; if (!ringBuf->try_write(inPacket)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full! capacity:" << ringBuf->capacity() << "length" << ringBuf->size(); } + if (inPacket.seq > lastSentSeq + 1) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is"< Date: Thu, 6 Jan 2022 09:53:47 +0000 Subject: [PATCH 011/323] Spotted another error in audio settings. --- wfmain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wfmain.cpp b/wfmain.cpp index 0984aae..087f302 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1630,7 +1630,7 @@ void wfmain::loadSettings() #if defined(RTAUDIO) serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(serverAudioInputIndex).toInt(); #elif defined(PORTAUDIO) - serverRxSetup.port = ui->audioOutputCombo->itemData(serverAudioInputIndex).toInt(); + serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(serverAudioInputIndex).toInt(); #else QVariant v = ui->serverRXAudioInputCombo->currentData(); serverRxSetup.port = v.value(); From 43fb31e8d008973c750f75651e39bed8807011ea Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 6 Jan 2022 10:03:58 +0000 Subject: [PATCH 012/323] reconnect server signal/slots after restart --- wfmain.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 087f302..8d36f99 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -74,13 +74,6 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s rigConnections(); - if (serverConfig.enabled && udp != Q_NULLPTR) { - // Server - connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); - connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); - connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); - } - amTransmitting = false; connect(ui->txPowerSlider, &QSlider::sliderMoved, @@ -973,7 +966,7 @@ void wfmain::setServerToPrefs() connect(this, SIGNAL(initServer()), udp, SLOT(init())); connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); - if (!prefs.enableLAN && udp != Q_NULLPTR) { + if (!prefs.enableLAN) { connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(receiveStatusUpdate(QString))); } @@ -982,6 +975,10 @@ void wfmain::setServerToPrefs() emit initServer(); connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); + connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); + connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); + connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); + ui->statusBar->showMessage(QString("Server enabled"), 1000); } From 9284a494697fd0a784e66d8eabe4aed619bc1ab9 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 6 Jan 2022 10:08:36 +0000 Subject: [PATCH 013/323] Try again to fix server restart! --- wfmain.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 8d36f99..86fb8f9 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -66,14 +66,21 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s setUIToPrefs(); - setServerToPrefs(); - setInitialTiming(); openRig(); rigConnections(); +/* if (serverConfig.enabled && udp != Q_NULLPTR) { + // Server + connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); + connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); + connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); + } */ + + setServerToPrefs(); + amTransmitting = false; connect(ui->txPowerSlider, &QSlider::sliderMoved, @@ -966,6 +973,12 @@ void wfmain::setServerToPrefs() connect(this, SIGNAL(initServer()), udp, SLOT(init())); connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); + if (rig != Q_NULLPTR) { + connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); + connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); + connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); + } + if (!prefs.enableLAN) { connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(receiveStatusUpdate(QString))); } @@ -975,10 +988,6 @@ void wfmain::setServerToPrefs() emit initServer(); connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); - connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); - connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); - connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); - ui->statusBar->showMessage(QString("Server enabled"), 1000); } From 6c29eb12a25ae4ba830533b838341834bd4b5b26 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 6 Jan 2022 13:44:29 +0000 Subject: [PATCH 014/323] Improve FEC operation with opus --- audiohandler.cpp | 62 +++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 6b977c0..b141983 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -593,27 +593,29 @@ void audioHandler::incomingAudio(audioPacket inPacket) return; } + audioPacket livePacket = inPacket; + if (setup.codec == 0x40 || setup.codec == 0x80) { unsigned char* in = (unsigned char*)inPacket.data.data(); /* Decode the frame. */ QByteArray outPacket((setup.samplerate / 50) * sizeof(qint16) * setup.radioChan, (char)0xff); // Preset the output buffer size. qint16* out = (qint16*)outPacket.data(); - int nSamples = opus_packet_get_nb_samples(in, inPacket.data.size(),setup.samplerate); + int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.samplerate); if (nSamples != setup.samplerate / 50) { qInfo(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.samplerate / 50); return; } - if (inPacket.seq > lastSentSeq + 1) { - nSamples = opus_decode(decoder, in, inPacket.data.size(), out, (setup.samplerate / 50), 1); + if (livePacket.seq > lastSentSeq + 1) { + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 1); } else { - nSamples = opus_decode(decoder, in, inPacket.data.size(), out, (setup.samplerate / 50), 0); + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 0); } if (nSamples < 0) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decode failed:" << opus_strerror(nSamples) << "packet size" << inPacket.data.length(); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decode failed:" << opus_strerror(nSamples) << "packet size" << livePacket.data.length(); return; } else { @@ -622,22 +624,22 @@ void audioHandler::incomingAudio(audioPacket inPacket) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(qint16) * setup.radioChan << "outPacket:" << outPacket.size(); outPacket.resize(nSamples * sizeof(qint16) * setup.radioChan); } - //qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoded" << inPacket.data.size() << "bytes, into" << outPacket.length() << "bytes"; - inPacket.data.clear(); - inPacket.data = outPacket; // Replace incoming data with converted. + //qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoded" << livePacket.data.size() << "bytes, into" << outPacket.length() << "bytes"; + livePacket.data.clear(); + livePacket.data = outPacket; // Replace incoming data with converted. } } - //qDebug(logAudio()) << "Got" << setup.bits << "bits, length" << inPacket.data.length(); + //qDebug(logAudio()) << "Got" << setup.bits << "bits, length" << livePacket.data.length(); // Incoming data is 8bits? if (setup.bits == 8) { // Current packet is 8bit so need to create a new buffer that is 16bit - QByteArray outPacket((int)inPacket.data.length() * 2 * (devChannels / setup.radioChan), (char)0xff); + QByteArray outPacket((int)livePacket.data.length() * 2 * (devChannels / setup.radioChan), (char)0xff); qint16* out = (qint16*)outPacket.data(); - for (int f = 0; f < inPacket.data.length(); f++) + for (int f = 0; f < livePacket.data.length(); f++) { - int samp = (quint8)inPacket.data[f]; + int samp = (quint8)livePacket.data[f]; for (int g = setup.radioChan; g <= devChannels; g++) { if (setup.ulaw) @@ -646,30 +648,30 @@ void audioHandler::incomingAudio(audioPacket inPacket) *out++ = (qint16)((samp - 128) << 8) * this->volume; } } - inPacket.data.clear(); - inPacket.data = outPacket; // Replace incoming data with converted. + livePacket.data.clear(); + livePacket.data = outPacket; // Replace incoming data with converted. } else { // This is already a 16bit stream, do we need to convert to stereo? if (setup.radioChan == 1 && devChannels > 1) { // Yes - QByteArray outPacket(inPacket.data.length() * 2, (char)0xff); // Preset the output buffer size. - qint16* in = (qint16*)inPacket.data.data(); + QByteArray outPacket(livePacket.data.length() * 2, (char)0xff); // Preset the output buffer size. + qint16* in = (qint16*)livePacket.data.data(); qint16* out = (qint16*)outPacket.data(); - for (int f = 0; f < inPacket.data.length() / 2; f++) + for (int f = 0; f < livePacket.data.length() / 2; f++) { *out++ = (qint16)*in * this->volume; *out++ = (qint16)*in++ * this->volume; } - inPacket.data.clear(); - inPacket.data = outPacket; // Replace incoming data with converted. + livePacket.data.clear(); + livePacket.data = outPacket; // Replace incoming data with converted. } else { // We already have the same number of channels so just update volume. - qint16* in = (qint16*)inPacket.data.data(); - for (int f = 0; f < inPacket.data.length() / 2; f++) + qint16* in = (qint16*)livePacket.data.data(); + for (int f = 0; f < livePacket.data.length() / 2; f++) { *in = *in * this->volume; in++; @@ -681,17 +683,17 @@ void audioHandler::incomingAudio(audioPacket inPacket) /* We now have an array of 16bit samples in the NATIVE samplerate of the radio If the radio sample rate is below 48000, we need to resample. */ - //qDebug(logAudio()) << "Now 16 bit stereo, length" << inPacket.data.length(); + //qDebug(logAudio()) << "Now 16 bit stereo, length" << livePacket.data.length(); if (resampleRatio != 1.0) { // We need to resample // We have a stereo 16bit stream. - quint32 outFrames = ((inPacket.data.length() / 2 / devChannels) * resampleRatio); - quint32 inFrames = (inPacket.data.length() / 2 / devChannels); + quint32 outFrames = ((livePacket.data.length() / 2 / devChannels) * resampleRatio); + quint32 inFrames = (livePacket.data.length() / 2 / devChannels); QByteArray outPacket(outFrames * 4, (char)0xff); // Preset the output buffer size. - const qint16* in = (qint16*)inPacket.data.constData(); + const qint16* in = (qint16*)livePacket.data.constData(); qint16* out = (qint16*)outPacket.data(); int err = 0; @@ -699,17 +701,17 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (err) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } - inPacket.data.clear(); - inPacket.data = outPacket; // Replace incoming data with converted. + livePacket.data.clear(); + livePacket.data = outPacket; // Replace incoming data with converted. } - //qDebug(logAudio()) << "Adding packet to buffer:" << inPacket.seq << ": " << inPacket.data.length(); + //qDebug(logAudio()) << "Adding packet to buffer:" << livePacket.seq << ": " << livePacket.data.length(); - if (!ringBuf->try_write(inPacket)) + if (!ringBuf->try_write(livePacket)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full! capacity:" << ringBuf->capacity() << "length" << ringBuf->size(); } - if (inPacket.seq > lastSentSeq + 1) { + if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is"< Date: Fri, 7 Jan 2022 12:09:36 +0000 Subject: [PATCH 015/323] Tidying of audio handling --- audiohandler.cpp | 11 ++++++----- udpserver.cpp | 3 +-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index b141983..e79c913 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -601,17 +601,19 @@ void audioHandler::incomingAudio(audioPacket inPacket) /* Decode the frame. */ QByteArray outPacket((setup.samplerate / 50) * sizeof(qint16) * setup.radioChan, (char)0xff); // Preset the output buffer size. qint16* out = (qint16*)outPacket.data(); - int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.samplerate); - if (nSamples != setup.samplerate / 50) + int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(), setup.samplerate); + if (nSamples < 1) { qInfo(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.samplerate / 50); return; } if (livePacket.seq > lastSentSeq + 1) { - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 1); + opus_decoder_ctl(decoder, OPUS_GET_LAST_PACKET_DURATION(&nSamples)); + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq << "nSamples=" << nSamples; + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, nSamples, 1); } else { - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 0); + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, nSamples, 0); } if (nSamples < 0) { @@ -712,7 +714,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full! capacity:" << ringBuf->capacity() << "length" << ringBuf->size(); } if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is"<getNextAudioChunk(audio); - // Now we have the next audio chunk, we can release the mutex. - audioMutex.unlock(); int len = 0; while (len < audio.length()) { audioPacket partial; @@ -1468,6 +1466,7 @@ void udpServer::sendRxAudio() receiveAudioData(partial); len = len + partial.data.length(); } + audioMutex.unlock(); } else { qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio"; From 7dd325c3ae510bf89a3617b340be71d78b6d3523 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 7 Jan 2022 12:16:33 +0000 Subject: [PATCH 016/323] Hopefully fix opus issue in last commit? --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index e79c913..befbdda 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -613,7 +613,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) nSamples = opus_decode(decoder, in, livePacket.data.size(), out, nSamples, 1); } else { - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, nSamples, 0); + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 0); } if (nSamples < 0) { From 3036108d6f094fc28b07f79ab81dfb4c1c5dce08 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 7 Jan 2022 12:28:30 +0000 Subject: [PATCH 017/323] Revert "Hopefully fix opus issue in last commit?" This reverts commit 7dd325c3ae510bf89a3617b340be71d78b6d3523. --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index befbdda..e79c913 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -613,7 +613,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) nSamples = opus_decode(decoder, in, livePacket.data.size(), out, nSamples, 1); } else { - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 0); + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, nSamples, 0); } if (nSamples < 0) { From 6e44480d6b9a55d50a0d7b7afe009be6f9f0fa8e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 7 Jan 2022 12:28:43 +0000 Subject: [PATCH 018/323] Revert "Tidying of audio handling" This reverts commit 5a6ff8489249e260a76c496d6318281a6b65001f. --- audiohandler.cpp | 11 +++++------ udpserver.cpp | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index e79c913..b141983 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -601,19 +601,17 @@ void audioHandler::incomingAudio(audioPacket inPacket) /* Decode the frame. */ QByteArray outPacket((setup.samplerate / 50) * sizeof(qint16) * setup.radioChan, (char)0xff); // Preset the output buffer size. qint16* out = (qint16*)outPacket.data(); - int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(), setup.samplerate); - if (nSamples < 1) + int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.samplerate); + if (nSamples != setup.samplerate / 50) { qInfo(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.samplerate / 50); return; } if (livePacket.seq > lastSentSeq + 1) { - opus_decoder_ctl(decoder, OPUS_GET_LAST_PACKET_DURATION(&nSamples)); - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq << "nSamples=" << nSamples; - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, nSamples, 1); + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 1); } else { - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, nSamples, 0); + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 0); } if (nSamples < 0) { @@ -714,6 +712,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full! capacity:" << ringBuf->capacity() << "length" << ringBuf->size(); } if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is"<getNextAudioChunk(audio); + // Now we have the next audio chunk, we can release the mutex. + audioMutex.unlock(); int len = 0; while (len < audio.length()) { audioPacket partial; @@ -1466,7 +1468,6 @@ void udpServer::sendRxAudio() receiveAudioData(partial); len = len + partial.data.length(); } - audioMutex.unlock(); } else { qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio"; From db702123f9083f69cf8ac520256abf1f6d10e4f7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 7 Jan 2022 12:31:15 +0000 Subject: [PATCH 019/323] Keep audio mutex until all processing is done --- udpserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index d67c23d..ec58ac7 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1459,8 +1459,6 @@ void udpServer::sendRxAudio() { audio.clear(); rxaudio->getNextAudioChunk(audio); - // Now we have the next audio chunk, we can release the mutex. - audioMutex.unlock(); int len = 0; while (len < audio.length()) { audioPacket partial; @@ -1468,6 +1466,8 @@ void udpServer::sendRxAudio() receiveAudioData(partial); len = len + partial.data.length(); } + // Now we have the next audio chunk, we can release the mutex. + audioMutex.unlock(); } else { qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio"; From 82b655cc0986f18b91af5dce1219c243ae0729da Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 7 Jan 2022 14:42:32 +0000 Subject: [PATCH 020/323] Try to fix lock condition in server --- udpserver.cpp | 72 +++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index ec58ac7..96c0a2c 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1573,56 +1573,53 @@ void udpServer::sendRetransmitRequest(CLIENT* c) // We have at least 1 missing packet! qDebug(logUdp()) << "Missing Seq: size=" << c->rxSeqBuf.size() << "firstKey=" << c->rxSeqBuf.firstKey() << "lastKey=" << c->rxSeqBuf.lastKey() << "missing=" << c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() + 1; // We are missing packets so iterate through the buffer and add the missing ones to missing packet list - for (int i = 0; i < c->rxSeqBuf.keys().length() - 1; i++) { - for (quint16 j = c->rxSeqBuf.keys()[i] + 1; j < c->rxSeqBuf.keys()[i + 1]; j++) { - auto s = c->rxMissing.find(j); - if (s == c->rxMissing.end()) - { - // We haven't seen this missing packet before - qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j; - - if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + if (c->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + for (int i = 0; i < c->rxSeqBuf.keys().length() - 1; i++) { + for (quint16 j = c->rxSeqBuf.keys()[i] + 1; j < c->rxSeqBuf.keys()[i + 1]; j++) { + auto s = c->rxMissing.find(j); + if (s == c->rxMissing.end()) { - c->rxMissing.insert(j, 0); - c->missMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock missMutex()"; - } - - - if (c->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - if (c->rxSeqBuf.size() > 400) - { - c->rxSeqBuf.remove(0); - } - c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. - c->rxMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock rxMutex()"; - } - - } - else { - if (s.value() == 4) - { - // We have tried 4 times to request this packet, time to give up! + // We haven't seen this missing packet before + qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j; if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - s = c->rxMissing.erase(s); + c->rxMissing.insert(j, 0); c->missMutex.unlock(); } else { qInfo(logUdpServer()) << "Unable to lock missMutex()"; } + if (c->rxSeqBuf.size() > 400) + { + c->rxSeqBuf.remove(0); + c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. + } } + else { + if (s.value() == 4) + { + // We have tried 4 times to request this packet, time to give up! + if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + s = c->rxMissing.erase(s); + c->missMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock missMutex()"; + } + } + } } } + c->rxMutex.unlock(); } + else { + qInfo(logUdpServer()) << "Unable to lock rxMutex()"; + } + } } @@ -1667,9 +1664,10 @@ void udpServer::sendRetransmitRequest(CLIENT* c) { qDebug(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(); + missingSeqs.insert(0, p.packet, sizeof(p.packet)); + if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - missingSeqs.insert(0, p.packet, sizeof(p.packet)); c->socket->writeDatagram(missingSeqs, c->ipAddress, c->port); udpMutex.unlock(); } From 89ac99b3f9dd522fb8a803997d3ff66d7e002bb8 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 7 Jan 2022 14:52:33 +0000 Subject: [PATCH 021/323] Bit of tidying --- audiohandler.cpp | 6 +++++- udpserver.cpp | 5 ++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index b141983..7d43774 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -602,7 +602,11 @@ void audioHandler::incomingAudio(audioPacket inPacket) QByteArray outPacket((setup.samplerate / 50) * sizeof(qint16) * setup.radioChan, (char)0xff); // Preset the output buffer size. qint16* out = (qint16*)outPacket.data(); int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.samplerate); - if (nSamples != setup.samplerate / 50) + if (nSamples == -1) { + // No opus data yet? + return; + } + else if (nSamples != setup.samplerate / 50) { qInfo(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.samplerate / 50); return; diff --git a/udpserver.cpp b/udpserver.cpp index 96c0a2c..bf533df 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -3,6 +3,7 @@ #define STALE_CONNECTION 15 #define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms +#define AUDIO_SEND_PERIOD 20 // how often to call the RX audio send function in ms udpServer::udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudio) : config(config), outAudio(outAudio), @@ -356,7 +357,7 @@ void udpServer::controlReceived() rxAudioTimer = new QTimer(); rxAudioTimer->setTimerType(Qt::PreciseTimer); connect(rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); - rxAudioTimer->start(20); + rxAudioTimer->start(AUDIO_SEND_PERIOD); } } @@ -1276,8 +1277,6 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) return; } -#define PURGE_SECONDS 60 - void udpServer::watchdog() { QDateTime now = QDateTime::currentDateTime(); From f36f4db49795345bbbddee8c0b98302db4259f8e Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:19 +0100 Subject: [PATCH 022/323] Revert "Fix typo for portaudio/rtaudio builds" This reverts commit daf94543acd337e25d57282b29736ee5c61fbfc0. --- wfmain.cpp | 31 ++++++++++++++-------------- wfview.vcxproj.filters | 46 ++---------------------------------------- 2 files changed, 17 insertions(+), 60 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index efdec3e..33779db 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1800,9 +1800,9 @@ void wfmain::on_serverAudioPortText_textChanged(QString text) void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) { #if defined(RTAUDIO) - serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(value).toInt(); + serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); #elif defined(PORTAUDIO) - serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(value).toInt(); + serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); #else QVariant v = ui->serverRXAudioInputCombo->itemData(value); serverRxSetup.port = v.value(); @@ -1811,20 +1811,6 @@ void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) qDebug(logGui()) << "Changed default server audio input to:" << serverRxSetup.name; } -void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) -{ -#if defined(RTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#else - QVariant v = ui->serverTXAudioOutputCombo->itemData(value); - serverTxSetup.port = v.value(); -#endif - serverTxSetup.name = ui->serverTXAudioOutputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio output to:" << serverTxSetup.name; -} - void wfmain::on_serverUsersTable_cellChanged(int row, int column) { qInfo() << "Cell Changed:" << row << "," << column; @@ -1848,6 +1834,19 @@ void wfmain::on_serverUsersTable_cellChanged(int row, int column) } } +void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) +{ +#if defined(RTAUDIO) + serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); +#elif defined(PORTAUDIO) + serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); +#else + QVariant v = ui->serverTXAudioOutputCombo->itemData(value); + serverTxSetup.port = v.value(); +#endif + serverTxSetup.name = ui->serverTXAudioOutputCombo->itemText(value); + qDebug(logGui()) << "Changed default server audio output to:" << serverTxSetup.name; +} void wfmain::saveSettings() { diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index 46000f0..bfaabf2 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -208,55 +208,12 @@ - - - - - - - - - - Generated Files Generated Files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -411,6 +368,7 @@ Resource Files + @@ -424,6 +382,6 @@ - + \ No newline at end of file From 91f4269de8e51ad0f64c08e8c4707e9c225f91ad Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:27 +0100 Subject: [PATCH 023/323] Revert "Update server users table dynamically" This reverts commit cf1caf0a0f1caca88468ab3961f6c809844b6a3d. --- wfmain.cpp | 164 +++++++++++++++++++++++------------------------------ wfmain.h | 1 - 2 files changed, 70 insertions(+), 95 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 33779db..fd2ebce 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1662,6 +1662,7 @@ void wfmain::loadSettings() serverTxSetup.resampleQuality = rxSetup.resampleQuality; int row = 0; + ui->serverUsersTable->setRowCount(0); foreach(SERVERUSER user, serverConfig.users) @@ -1677,6 +1678,7 @@ void wfmain::loadSettings() serverAddUserLine("", "", 0); } + settings->endGroup(); // Memory channels @@ -1717,8 +1719,6 @@ void wfmain::loadSettings() void wfmain::serverAddUserLine(const QString& user, const QString& pass, const int& type) { - ui->serverUsersTable->blockSignals(true); - ui->serverUsersTable->insertRow(ui->serverUsersTable->rowCount()); ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 0, new QTableWidgetItem(user)); ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 1, new QTableWidgetItem()); @@ -1735,8 +1735,6 @@ void wfmain::serverAddUserLine(const QString& user, const QString& pass, const i comboBox->insertItems(0, { "Full User","Full with no TX","Monitor only" }); comboBox->setCurrentIndex(type); ui->serverUsersTable->setCellWidget(ui->serverUsersTable->rowCount() - 1, 2, comboBox); - ui->serverUsersTable->blockSignals(false); - } void wfmain::onServerPasswordChanged() @@ -1747,23 +1745,6 @@ void wfmain::onServerPasswordChanged() passcode(password->text(), pass); password->setText(pass); qInfo() << "password row" << row << "changed"; - serverConfig.users.clear(); - for (int rows = 0; rows < ui->serverUsersTable->model()->rowCount(); rows++) - { - if (ui->serverUsersTable->item(rows, 0) != NULL) - { - SERVERUSER user; - user.username = ui->serverUsersTable->item(rows, 0)->text(); - QLineEdit* password = (QLineEdit*)ui->serverUsersTable->cellWidget(rows, 1); - user.password = password->text(); - QComboBox* comboBox = (QComboBox*)ui->serverUsersTable->cellWidget(rows, 2); - user.userType = comboBox->currentIndex(); - serverConfig.users.append(user); - } - else { - ui->serverUsersTable->removeRow(rows); - } - } } void wfmain::on_serverUsersTable_cellClicked(int row, int col) @@ -1775,79 +1756,6 @@ void wfmain::on_serverUsersTable_cellClicked(int row, int col) } -void wfmain::on_serverEnableCheckbox_clicked(bool checked) -{ - ui->serverSetupGroup->setEnabled(checked); - serverConfig.enabled = checked; - setServerToPrefs(); -} - -void wfmain::on_serverControlPortText_textChanged(QString text) -{ - serverConfig.controlPort = ui->serverControlPortText->text().toInt(); -} - -void wfmain::on_serverCivPortText_textChanged(QString text) -{ - serverConfig.civPort = ui->serverCivPortText->text().toInt(); -} - -void wfmain::on_serverAudioPortText_textChanged(QString text) -{ - serverConfig.audioPort = ui->serverAudioPortText->text().toInt(); -} - -void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) -{ -#if defined(RTAUDIO) - serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); -#else - QVariant v = ui->serverRXAudioInputCombo->itemData(value); - serverRxSetup.port = v.value(); -#endif - serverRxSetup.name = ui->serverRXAudioInputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio input to:" << serverRxSetup.name; -} - -void wfmain::on_serverUsersTable_cellChanged(int row, int column) -{ - qInfo() << "Cell Changed:" << row << "," << column; - - serverConfig.users.clear(); - for (int rows = 0; rows < ui->serverUsersTable->model()->rowCount(); rows++) - { - if (ui->serverUsersTable->item(rows, 0) != NULL) - { - SERVERUSER user; - user.username = ui->serverUsersTable->item(rows, 0)->text(); - QLineEdit* password = (QLineEdit*)ui->serverUsersTable->cellWidget(rows, 1); - user.password = password->text(); - QComboBox* comboBox = (QComboBox*)ui->serverUsersTable->cellWidget(rows, 2); - user.userType = comboBox->currentIndex(); - serverConfig.users.append(user); - } - else { - ui->serverUsersTable->removeRow(rows); - } - } -} - -void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) -{ -#if defined(RTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#else - QVariant v = ui->serverTXAudioOutputCombo->itemData(value); - serverTxSetup.port = v.value(); -#endif - serverTxSetup.name = ui->serverTXAudioOutputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio output to:" << serverTxSetup.name; -} - void wfmain::saveSettings() { qInfo(logSystem()) << "Saving settings to " << settings->fileName(); @@ -1985,6 +1893,24 @@ void wfmain::saveSettings() settings->setValue("ServerAudioOutput", serverTxSetup.name); settings->setValue("ServerAudioInput", serverRxSetup.name); + serverConfig.users.clear(); + + for (int row = 0; row < ui->serverUsersTable->model()->rowCount(); row++) + { + if (ui->serverUsersTable->item(row, 0) != NULL) + { + SERVERUSER user; + user.username = ui->serverUsersTable->item(row, 0)->text(); + QLineEdit* password = (QLineEdit*)ui->serverUsersTable->cellWidget(row, 1); + user.password = password->text(); + QComboBox* comboBox = (QComboBox*)ui->serverUsersTable->cellWidget(row, 2); + user.userType = comboBox->currentIndex(); + serverConfig.users.append(user); + } + else { + ui->serverUsersTable->removeRow(row); + } + } for (int f = 0; f < serverConfig.users.count(); f++) { @@ -5734,6 +5660,56 @@ void wfmain::on_setClockBtn_clicked() setRadioTimeDatePrep(); } +void wfmain::on_serverEnableCheckbox_clicked(bool checked) +{ + ui->serverSetupGroup->setEnabled(checked); + serverConfig.enabled = checked; + setServerToPrefs(); +} + +void wfmain::on_serverControlPortText_textChanged(QString text) +{ + serverConfig.controlPort = ui->serverControlPortText->text().toInt(); +} + +void wfmain::on_serverCivPortText_textChanged(QString text) +{ + serverConfig.civPort = ui->serverCivPortText->text().toInt(); +} + +void wfmain::on_serverAudioPortText_textChanged(QString text) +{ + serverConfig.audioPort = ui->serverAudioPortText->text().toInt(); +} + +void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) +{ +#if defined(RTAUDIO) + serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); +#elif defined(PORTAUDIO) + serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); +#else + QVariant v = ui->serverRXAudioInputCombo->itemData(value); + serverRxSetup.port = v.value(); +#endif + serverRxSetup.name = ui->serverRXAudioInputCombo->itemText(value); + qDebug(logGui()) << "Changed default server audio input to:" << serverRxSetup.name; +} + +void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) +{ +#if defined(RTAUDIO) + serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); +#elif defined(PORTAUDIO) + serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); +#else + QVariant v = ui->serverTXAudioOutputCombo->itemData(value); + serverTxSetup.port = v.value(); +#endif + serverTxSetup.name = ui->serverTXAudioOutputCombo->itemText(value); + qDebug(logGui()) << "Changed default server audio output to:" << serverTxSetup.name; +} + // --- DEBUG FUNCTION --- void wfmain::on_debugBtn_clicked() { diff --git a/wfmain.h b/wfmain.h index 498b6fe..b9bd713 100644 --- a/wfmain.h +++ b/wfmain.h @@ -513,7 +513,6 @@ private slots: void on_serverTXAudioOutputCombo_currentIndexChanged(int value); void on_serverRXAudioInputCombo_currentIndexChanged(int value); void onServerPasswordChanged(); - void on_serverUsersTable_cellChanged(int row, int column); void on_useRTSforPTTchk_clicked(bool checked); From 9e87d35069e1abe6de3ceb70d54b38a53cd96c82 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:29 +0100 Subject: [PATCH 024/323] Revert "Clear server status when disabled" This reverts commit 2c82561ff57f444f21ffb02e5ec7afd58684e853. --- udpserver.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index d7dd5c8..9f6527c 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -96,8 +96,6 @@ udpServer::~udpServer() udpAudio->close(); delete udpAudio; } - emit haveNetworkStatus(QString("")); - } From d742c7564a3c020d1880b1dd925a9a2fb15e237b Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:30 +0100 Subject: [PATCH 025/323] Revert "Allow dynamic restarting of server" This reverts commit 3f06ab60612edc3d98988de7964b81046830fd54. --- wfmain.cpp | 86 ++++++++++++++++-------------------------- wfmain.h | 11 +++--- wfview.vcxproj.filters | 12 ++++++ 3 files changed, 51 insertions(+), 58 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index fd2ebce..ebb4634 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -953,14 +953,6 @@ void wfmain::setServerToPrefs() // Start server if enabled in config ui->serverSetupGroup->setEnabled(serverConfig.enabled); - if (serverThread != Q_NULLPTR) { - serverThread->quit(); - serverThread->wait(); - serverThread = Q_NULLPTR; - udp = Q_NULLPTR; - ui->statusBar->showMessage(QString("Server disabled"), 1000); - } - if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; @@ -982,7 +974,6 @@ void wfmain::setServerToPrefs() emit initServer(); connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); - ui->statusBar->showMessage(QString("Server enabled"), 1000); } } @@ -1885,6 +1876,10 @@ void wfmain::saveSettings() settings->beginGroup("Server"); + serverConfig.controlPort = ui->serverControlPortText->text().toInt(); + serverConfig.civPort = ui->serverCivPortText->text().toInt(); + serverConfig.audioPort = ui->serverAudioPortText->text().toInt(); + settings->setValue("ServerEnabled", serverConfig.enabled); settings->setValue("ServerControlPort", serverConfig.controlPort); settings->setValue("ServerCivPort", serverConfig.civPort); @@ -4556,6 +4551,35 @@ void wfmain::on_audioInputCombo_currentIndexChanged(int value) qDebug(logGui()) << "Changed default audio input to:" << txSetup.name; } + +void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) +{ +#if defined(RTAUDIO) + serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(value).toInt(); +#elif defined(PORTAUDIO) + serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(value).toInt(); +#else + QVariant v = ui->serverRXAudioInputCombo->itemData(value); + serverRxSetup.port = v.value(); +#endif + serverRxSetup.name = ui->serverRXAudioInputCombo->itemText(value); + qDebug(logGui()) << "Changed default server audio input to:" << serverRxSetup.name; +} + +void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) +{ +#if defined(RTAUDIO) + serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); +#elif defined(PORTAUDIO) + serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); +#else + QVariant v = ui->serverTXAudioOutputCombo->itemData(value); + serverTxSetup.port = v.value(); +#endif + serverTxSetup.name = ui->serverTXAudioOutputCombo->itemText(value); + qDebug(logGui()) << "Changed default server audio output to:" << serverTxSetup.name; +} + void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text) { //udpPrefs.audioRXSampleRate = text.toInt(); @@ -5664,50 +5688,6 @@ void wfmain::on_serverEnableCheckbox_clicked(bool checked) { ui->serverSetupGroup->setEnabled(checked); serverConfig.enabled = checked; - setServerToPrefs(); -} - -void wfmain::on_serverControlPortText_textChanged(QString text) -{ - serverConfig.controlPort = ui->serverControlPortText->text().toInt(); -} - -void wfmain::on_serverCivPortText_textChanged(QString text) -{ - serverConfig.civPort = ui->serverCivPortText->text().toInt(); -} - -void wfmain::on_serverAudioPortText_textChanged(QString text) -{ - serverConfig.audioPort = ui->serverAudioPortText->text().toInt(); -} - -void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) -{ -#if defined(RTAUDIO) - serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); -#else - QVariant v = ui->serverRXAudioInputCombo->itemData(value); - serverRxSetup.port = v.value(); -#endif - serverRxSetup.name = ui->serverRXAudioInputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio input to:" << serverRxSetup.name; -} - -void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) -{ -#if defined(RTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#else - QVariant v = ui->serverTXAudioOutputCombo->itemData(value); - serverTxSetup.port = v.value(); -#endif - serverTxSetup.name = ui->serverTXAudioOutputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio output to:" << serverTxSetup.name; } // --- DEBUG FUNCTION --- diff --git a/wfmain.h b/wfmain.h index b9bd713..09a8b86 100644 --- a/wfmain.h +++ b/wfmain.h @@ -385,6 +385,10 @@ private slots: void on_audioInputCombo_currentIndexChanged(int value); + void on_serverTXAudioOutputCombo_currentIndexChanged(int value); + + void on_serverRXAudioInputCombo_currentIndexChanged(int value); + void on_toFixedBtn_clicked(); void on_connectBtn_clicked(); @@ -506,12 +510,9 @@ private slots: void on_setClockBtn_clicked(); void on_serverEnableCheckbox_clicked(bool checked); + void on_serverUsersTable_cellClicked(int row, int col); - void on_serverControlPortText_textChanged(QString text); - void on_serverCivPortText_textChanged(QString text); - void on_serverAudioPortText_textChanged(QString text); - void on_serverTXAudioOutputCombo_currentIndexChanged(int value); - void on_serverRXAudioInputCombo_currentIndexChanged(int value); + void onServerPasswordChanged(); void on_useRTSforPTTchk_clicked(bool checked); diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index bfaabf2..051ed7d 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -120,6 +120,9 @@ Source Files + + Source Files + Source Files @@ -200,6 +203,9 @@ Header Files + + Header Files + Header Files @@ -369,6 +375,7 @@ Resource Files + @@ -384,4 +391,9 @@ + + + Generated Files + + \ No newline at end of file From a231cb086f7a9ccbede8f7d541d6c70b98c9ca00 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:32 +0100 Subject: [PATCH 026/323] Revert "Added forced manual RTS setting" This reverts commit 8dd42ba39224623f2881e760176637ebc1a2fa66. --- rigcommander.cpp | 21 +-------------------- rigcommander.h | 4 ---- wfmain.cpp | 17 +---------------- wfmain.h | 4 ---- wfmain.ui | 12 +----------- 5 files changed, 3 insertions(+), 55 deletions(-) diff --git a/rigcommander.cpp b/rigcommander.cpp index d497c0d..da5e894 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -217,20 +217,6 @@ void rigCommander::receiveBaudRate(quint32 baudrate) { emit haveBaudRate(baudrate); } -void rigCommander::setRTSforPTT(bool enabled) -{ - if(!usingNativeLAN) - { - useRTSforPTT_isSet = true; - useRTSforPTT_manual = enabled; - if(comm != NULL) - { - rigCaps.useRTSforPTT=enabled; - comm->setUseRTSforPTT(enabled); - } - } -} - void rigCommander::findRigs() { // This function sends data to 0x00 ("broadcast") to look for any connected rig. @@ -3610,13 +3596,8 @@ void rigCommander::determineRigCaps() haveRigCaps = true; if(!usingNativeLAN) - { - if(useRTSforPTT_isSet) - { - rigCaps.useRTSforPTT = useRTSforPTT_manual; - } comm->setUseRTSforPTT(rigCaps.useRTSforPTT); - } + if(lookingForRig) { diff --git a/rigcommander.h b/rigcommander.h index 6da1744..b1248a9 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -79,7 +79,6 @@ public slots: void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); void closeComm(); void stateUpdated(); - void setRTSforPTT(bool enabled); // Power: void powerOn(); @@ -453,9 +452,6 @@ private: unsigned char civAddr; unsigned char incomingCIVAddr; // place to store the incoming CIV. bool pttAllowed; - bool useRTSforPTT_isSet = false; - bool useRTSforPTT_manual = false; - QString rigSerialPort; quint32 rigBaudRate; diff --git a/wfmain.cpp b/wfmain.cpp index ebb4634..34db21d 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -423,7 +423,7 @@ void wfmain::makeRig() // Rig comm setup: connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString))); - connect(this, SIGNAL(setRTSforPTT(bool)), rig, SLOT(setRTSforPTT(bool))); + connect(rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); @@ -1312,7 +1312,6 @@ void wfmain::setDefPrefs() defPrefs.stylesheetPath = QString("qdarkstyle/style.qss"); defPrefs.radioCIVAddr = 0x00; // previously was 0x94 for 7300. defPrefs.CIVisRadioModel = false; - defPrefs.forceRTSasPTT = false; defPrefs.serialPortRadio = QString("auto"); defPrefs.serialPortBaud = 115200; defPrefs.enablePTT = false; @@ -1412,9 +1411,7 @@ void wfmain::loadSettings() ui->rigCIVaddrHexLine->setEnabled(false); } prefs.CIVisRadioModel = (bool)settings->value("CIVisRadioModel", defPrefs.CIVisRadioModel).toBool(); - prefs.forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool(); - ui->useRTSforPTTchk->setChecked(prefs.forceRTSasPTT); prefs.serialPortRadio = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString(); int serialIndex = ui->serialDeviceListCombo->findText(prefs.serialPortRadio); @@ -1775,7 +1772,6 @@ void wfmain::saveSettings() settings->beginGroup("Radio"); settings->setValue("RigCIVuInt", prefs.radioCIVAddr); settings->setValue("CIVisRadioModel", prefs.CIVisRadioModel); - settings->setValue("ForceRTSasPTT", prefs.forceRTSasPTT); settings->setValue("SerialPortRadio", prefs.serialPortRadio); settings->setValue("SerialPortBaud", prefs.serialPortBaud); settings->setValue("VirtualSerialPort", prefs.virtualSerialPort); @@ -3266,10 +3262,6 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) ui->tuneEnableChk->setEnabled(rigCaps.hasATU); ui->tuneNowBtn->setEnabled(rigCaps.hasATU); - ui->useRTSforPTTchk->blockSignals(true); - ui->useRTSforPTTchk->setChecked(rigCaps.useRTSforPTT); - ui->useRTSforPTTchk->blockSignals(false); - ui->connectBtn->setText("Disconnect"); // We must be connected now. prepareWf(ui->wfLengthSlider->value()); if(usingLAN) @@ -5505,7 +5497,6 @@ void wfmain::on_rigCIVaddrHexLine_editingFinished() } } - void wfmain::on_baudRateCombo_activated(int index) { bool ok = false; @@ -5519,12 +5510,6 @@ void wfmain::on_baudRateCombo_activated(int index) (void)index; } -void wfmain::on_useRTSforPTTchk_clicked(bool checked) -{ - emit setRTSforPTT(checked); - prefs.forceRTSasPTT = checked; -} - void wfmain::on_wfLengthSlider_valueChanged(int value) { prefs.wflength = (unsigned int)(value); diff --git a/wfmain.h b/wfmain.h index 09a8b86..a2bb8d4 100644 --- a/wfmain.h +++ b/wfmain.h @@ -52,7 +52,6 @@ signals: // Basic to rig: void setCIVAddr(unsigned char newRigCIVAddr); void setRigID(unsigned char rigID); - void setRTSforPTT(bool enabled); // Power void sendPowerOn(); @@ -515,8 +514,6 @@ private slots: void onServerPasswordChanged(); - void on_useRTSforPTTchk_clicked(bool checked); - private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); @@ -739,7 +736,6 @@ private: QString stylesheetPath; unsigned char radioCIVAddr; bool CIVisRadioModel; - bool forceRTSasPTT; QString serialPortRadio; quint32 serialPortBaud; bool enablePTT; diff --git a/wfmain.ui b/wfmain.ui index a938ee3..1849c09 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2066,7 +2066,7 @@ - 0 + 3 @@ -2250,16 +2250,6 @@ - - - - <html><head/><body><p>This feature is for older radios that respond best to an RTS serial port signal than a PTT command.</p><p><br/>For radios lacking PTT commands, this is automatic and transparent, but for radios which have a PTT command, you can check this box to override and force the PTT to be done using RTS. Do not check this box unless you really need this and have an appropriate adapter with RTS connected to the PTT line of the transceiver. </p></body></html> - - - Send RTS for PTT - - - From 41ec08ecba5481742a7e24c70149c84e40638d23 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:34 +0100 Subject: [PATCH 027/323] Revert "Minor typo with the new audio selection combo box, only on PORTAUDIO" This reverts commit d1165980a93ecd1839fc443e7f5bc9389cc0995d. --- wfmain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 34db21d..285f1d8 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -4547,9 +4547,9 @@ void wfmain::on_audioInputCombo_currentIndexChanged(int value) void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) { #if defined(RTAUDIO) - serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(value).toInt(); + serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); #elif defined(PORTAUDIO) - serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(value).toInt(); + serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); #else QVariant v = ui->serverRXAudioInputCombo->itemData(value); serverRxSetup.port = v.value(); From 54d08c5b8a9b27c2db7bbc9768df6601c05c3ed2 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:35 +0100 Subject: [PATCH 028/323] Revert "Edited some size constraints for better resizing and full-screen mode." This reverts commit c8b4992bab0a676852e1ca485cd5413a46415a9e. --- wfmain.ui | 133 ++++++++++++++++++++++++------------------------------ 1 file changed, 60 insertions(+), 73 deletions(-) diff --git a/wfmain.ui b/wfmain.ui index 1849c09..49b9e2d 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -6,8 +6,8 @@ 0 0 - 946 - 569 + 940 + 554 @@ -3103,13 +3103,13 @@ - 130 + 0 25 - 130 + 16777215 25 @@ -3124,6 +3124,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -3153,13 +3166,13 @@ - 130 + 0 25 - 130 + 16777215 25 @@ -3171,6 +3184,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -3194,13 +3220,13 @@ - 130 + 0 25 - 130 + 16777215 25 @@ -3212,19 +3238,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -3237,43 +3250,7 @@ - - - - 100 - 0 - - - - - 300 - 16777215 - - - - - - - - TX Audio Output - - - - - - - - 100 - 0 - - - - - 300 - 16777215 - - - + @@ -3288,8 +3265,31 @@ + + + + TX Audio Output + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -3377,19 +3377,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -3660,8 +3647,8 @@ 0 0 - 946 - 22 + 940 + 21 From d1d0194600f6c7145ecda4d75ec2e02a42ced07e Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:50 +0100 Subject: [PATCH 029/323] Revert "Integrate server setup into new settings pages" This reverts commit e1cdcad65baaef93b5d80a1492b86d302f21a0ea. --- udpserver.h | 23 +-- udpserversetup.cpp | 127 ++++++++++++++++ udpserversetup.h | 60 ++++++++ wfmain.cpp | 241 ++++++----------------------- wfmain.h | 20 +-- wfmain.ui | 366 ++++----------------------------------------- wfview.pro | 4 +- 7 files changed, 276 insertions(+), 565 deletions(-) create mode 100644 udpserversetup.cpp create mode 100644 udpserversetup.h diff --git a/udpserver.h b/udpserver.h index 4da37de..1bfbacd 100644 --- a/udpserver.h +++ b/udpserver.h @@ -19,6 +19,7 @@ #include +#include #include "packettypes.h" #include "rigidentities.h" #include "audiohandler.h" @@ -33,28 +34,6 @@ struct SEQBUFENTRY { quint8 retransmitCount; }; - -struct SERVERUSER { - QString username; - QString password; - quint8 userType; -}; - -struct SERVERCONFIG { - bool enabled; - bool lan; - quint16 controlPort; - quint16 civPort; - quint16 audioPort; - int audioOutput; - int audioInput; - quint8 resampleQuality; - quint32 baudRate; - - QList users; -}; - - class udpServer : public QObject { Q_OBJECT diff --git a/udpserversetup.cpp b/udpserversetup.cpp new file mode 100644 index 0000000..72b5996 --- /dev/null +++ b/udpserversetup.cpp @@ -0,0 +1,127 @@ +#include "udpserversetup.h" +#include "ui_udpserversetup.h" +#include "logcategories.h" + +extern void passcode(QString in,QByteArray& out); + +udpServerSetup::udpServerSetup(QWidget* parent) : + QDialog(parent), + ui(new Ui::udpServerSetup) +{ + ui->setupUi(this); + addUserLine("", "", 0); // Create a blank row if we never receive config. + + // Get any stored config information from the main form. + SERVERCONFIG config; + emit serverConfig(config,false); // Just send blank server config. +} + +udpServerSetup::~udpServerSetup() +{ + delete ui; +} + +// Slot to receive config. +void udpServerSetup::receiveServerConfig(SERVERCONFIG conf) +{ + qInfo() << "Getting server config"; + + ui->enableCheckbox->setChecked(conf.enabled); + ui->controlPortText->setText(QString::number(conf.controlPort)); + ui->civPortText->setText(QString::number(conf.civPort)); + ui->audioPortText->setText(QString::number(conf.audioPort)); + + int row = 0; + + for (int i = 0; i < ui->usersTable->rowCount(); i++) + { + ui->usersTable->removeRow(i); + } + + foreach (SERVERUSER user, conf.users) + { + if (user.username != "" && user.password != "") + { + addUserLine(user.username, user.password, user.userType); + row++; + } + } + + if (row == 0) { + addUserLine("", "", 0); + } + +} + +void udpServerSetup::accept() +{ + qInfo() << "Server config stored"; + SERVERCONFIG config; + config.enabled = ui->enableCheckbox->isChecked(); + config.controlPort = ui->controlPortText->text().toInt(); + config.civPort = ui->civPortText->text().toInt(); + config.audioPort = ui->audioPortText->text().toInt(); + + config.users.clear(); + + for (int row = 0; row < ui->usersTable->model()->rowCount(); row++) + { + if (ui->usersTable->item(row, 0) != NULL) + { + SERVERUSER user; + user.username = ui->usersTable->item(row, 0)->text(); + QLineEdit* password = (QLineEdit*)ui->usersTable->cellWidget(row, 1); + user.password = password->text(); + QComboBox* comboBox = (QComboBox*)ui->usersTable->cellWidget(row, 2); + user.userType = comboBox->currentIndex(); + config.users.append(user); + + } + else { + ui->usersTable->removeRow(row); + } + } + + emit serverConfig(config,true); + this->hide(); +} + + +void udpServerSetup::on_usersTable_cellClicked(int row, int col) +{ + qInfo() << "Clicked on " << row << "," << col; + if (row == ui->usersTable->model()->rowCount() - 1 && ui->usersTable->item(row, 0) != NULL) { + addUserLine("", "", 0); + } +} + +void udpServerSetup::onPasswordChanged() +{ + int row = sender()->property("row").toInt(); + QLineEdit* password = (QLineEdit*)ui->usersTable->cellWidget(row, 1); + QByteArray pass; + passcode(password->text(), pass); + password->setText(pass); + qInfo() << "password row" << row << "changed"; +} + +void udpServerSetup::addUserLine(const QString& user, const QString& pass, const int& type) +{ + ui->usersTable->insertRow(ui->usersTable->rowCount()); + ui->usersTable->setItem(ui->usersTable->rowCount() - 1, 0, new QTableWidgetItem(user)); + ui->usersTable->setItem(ui->usersTable->rowCount() - 1, 1, new QTableWidgetItem()); + ui->usersTable->setItem(ui->usersTable->rowCount() - 1, 2, new QTableWidgetItem()); + + QLineEdit* password = new QLineEdit(); + password->setProperty("row", (int)ui->usersTable->rowCount() - 1); + password->setEchoMode(QLineEdit::PasswordEchoOnEdit); + password->setText(pass); + connect(password, SIGNAL(editingFinished()), this, SLOT(onPasswordChanged())); + ui->usersTable->setCellWidget(ui->usersTable->rowCount() - 1, 1, password); + + QComboBox* comboBox = new QComboBox(); + comboBox->insertItems(0, { "Full User","Full with no TX","Monitor only" }); + comboBox->setCurrentIndex(type); + ui->usersTable->setCellWidget(ui->usersTable->rowCount() - 1, 2, comboBox); + +} diff --git a/udpserversetup.h b/udpserversetup.h new file mode 100644 index 0000000..54d47c4 --- /dev/null +++ b/udpserversetup.h @@ -0,0 +1,60 @@ +#ifndef UDPSERVERSETUP_H +#define UDPSERVERSETUP_H + +#include +#include +#include + +#include + + +struct SERVERUSER { + QString username; + QString password; + quint8 userType; +}; + +struct SERVERCONFIG { + bool enabled; + bool lan; + quint16 controlPort; + quint16 civPort; + quint16 audioPort; + int audioOutput; + int audioInput; + quint8 resampleQuality; + quint32 baudRate; + + QList users; +}; + +namespace Ui { + class udpServerSetup; +} + +class udpServerSetup : public QDialog +{ + Q_OBJECT + +public: + explicit udpServerSetup(QWidget* parent = 0); + ~udpServerSetup(); + +private slots: + void on_usersTable_cellClicked(int row, int col); + void onPasswordChanged(); + +public slots: + void receiveServerConfig(SERVERCONFIG conf); + +signals: + void serverConfig(SERVERCONFIG conf, bool store); + +private: + Ui::udpServerSetup* ui; + void accept(); + QList userTypes; + void addUserLine(const QString &user, const QString &pass, const int &type); +}; + +#endif // UDPSERVER_H diff --git a/wfmain.cpp b/wfmain.cpp index 285f1d8..ce769d2 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -27,8 +27,12 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s rpt = new repeaterSetup(); sat = new satelliteSetup(); trxadj = new transceiverAdjustments(); + srv = new udpServerSetup(); abtBox = new aboutbox(); + connect(this, SIGNAL(sendServerConfig(SERVERCONFIG)), srv, SLOT(receiveServerConfig(SERVERCONFIG))); + connect(srv, SIGNAL(serverConfig(SERVERCONFIG, bool)), this, SLOT(serverConfigRequested(SERVERCONFIG, bool))); + qRegisterMetaType(); // Needs to be registered early. qRegisterMetaType(); qRegisterMetaType(); @@ -952,11 +956,10 @@ void wfmain::setServerToPrefs() { // Start server if enabled in config - ui->serverSetupGroup->setEnabled(serverConfig.enabled); if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; - udp = new udpServer(serverConfig,serverTxSetup,serverRxSetup); + udp = new udpServer(serverConfig,rxSetup,txSetup); serverThread = new QThread(this); @@ -1058,12 +1061,10 @@ void wfmain::setAudioDevicesUI() if (info.outputChannels > 0) { qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); ui->audioOutputCombo->addItem(QString::fromStdString(info.name), i); - ui->serverTXAudioOutputCombo->addItem(QString::fromStdString(info.name), i); } if (info.inputChannels > 0) { qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name); ui->audioInputCombo->addItem(QString::fromStdString(info.name), i); - ui->serverRXAudioInputCombo->addItem(QString::fromStdString(info.name), i); } } @@ -1094,13 +1095,11 @@ void wfmain::setAudioDevicesUI() if (info->maxInputChannels > 0) { qInfo(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name; ui->audioInputCombo->addItem(info->name, i); - ui->serverRXAudioInputCombo->addItem(info->name, i); } if (info->maxOutputChannels > 0) { qInfo(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name; ui->audioOutputCombo->addItem(info->name, i); - ui->serverTXAudioOutputCombo->addItem(info->name, i); -} + } } #else @@ -1109,19 +1108,15 @@ void wfmain::setAudioDevicesUI() const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { ui->audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); } const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (const QAudioDeviceInfo& deviceInfo : audioInputs) { ui->audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - ui->serverRXAudioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); } // Set these to default audio devices initially. rxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); txSetup.port = QAudioDeviceInfo::defaultInputDevice(); - serverRxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); - serverTxSetup.port = QAudioDeviceInfo::defaultInputDevice(); #endif } @@ -1349,7 +1344,7 @@ void wfmain::loadSettings() prefs.drawPeaks = settings->value("DrawPeaks", defPrefs.drawPeaks).toBool(); prefs.wfAntiAlias = settings->value("WFAntiAlias", defPrefs.wfAntiAlias).toBool(); prefs.wfInterpolate = settings->value("WFInterpolate", defPrefs.wfInterpolate).toBool(); - prefs.wflength = (unsigned int)settings->value("WFLength", defPrefs.wflength).toInt(); + prefs.wflength = (unsigned int) settings->value("WFLength", defPrefs.wflength).toInt(); prefs.stylesheetPath = settings->value("StylesheetPath", defPrefs.stylesheetPath).toString(); ui->splitter->restoreState(settings->value("splitter").toByteArray()); @@ -1397,16 +1392,15 @@ void wfmain::loadSettings() // Radio and Comms: C-IV addr, port to use settings->beginGroup("Radio"); - prefs.radioCIVAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); - if (prefs.radioCIVAddr != 0) + prefs.radioCIVAddr = (unsigned char) settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); + if(prefs.radioCIVAddr!=0) { ui->rigCIVManualAddrChk->setChecked(true); ui->rigCIVaddrHexLine->blockSignals(true); ui->rigCIVaddrHexLine->setText(QString("%1").arg(prefs.radioCIVAddr, 2, 16)); ui->rigCIVaddrHexLine->setEnabled(true); ui->rigCIVaddrHexLine->blockSignals(false); - } - else { + } else { ui->rigCIVManualAddrChk->setChecked(false); ui->rigCIVaddrHexLine->setEnabled(false); } @@ -1419,9 +1413,9 @@ void wfmain::loadSettings() ui->serialDeviceListCombo->setCurrentIndex(serialIndex); } - prefs.serialPortBaud = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); + prefs.serialPortBaud = (quint32) settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); ui->baudRateCombo->blockSignals(true); - ui->baudRateCombo->setCurrentIndex(ui->baudRateCombo->findData(prefs.serialPortBaud)); + ui->baudRateCombo->setCurrentIndex( ui->baudRateCombo->findData(prefs.serialPortBaud) ); ui->baudRateCombo->blockSignals(false); if (prefs.serialPortBaud > 0) @@ -1437,10 +1431,10 @@ void wfmain::loadSettings() else { ui->vspCombo->addItem(prefs.virtualSerialPort); - ui->vspCombo->setCurrentIndex(ui->vspCombo->count() - 1); + ui->vspCombo->setCurrentIndex(ui->vspCombo->count()-1); } - prefs.localAFgain = (unsigned char)settings->value("localAFgain", defPrefs.localAFgain).toUInt(); + prefs.localAFgain = (unsigned char) settings->value("localAFgain", defPrefs.localAFgain).toUInt(); rxSetup.localAFgain = prefs.localAFgain; settings->endGroup(); @@ -1454,14 +1448,15 @@ void wfmain::loadSettings() settings->beginGroup("LAN"); prefs.enableLAN = settings->value("EnableLAN", defPrefs.enableLAN).toBool(); - if (prefs.enableLAN) + if(prefs.enableLAN) { ui->baudRateCombo->setEnabled(false); ui->serialDeviceListCombo->setEnabled(false); - } - else { + //ui->udpServerSetupBtn->setEnabled(false); + } else { ui->baudRateCombo->setEnabled(true); ui->serialDeviceListCombo->setEnabled(true); + //ui->udpServerSetupBtn->setEnabled(true); } ui->lanEnableBtn->setChecked(prefs.enableLAN); @@ -1477,15 +1472,15 @@ void wfmain::loadSettings() udpPrefs.ipAddress = settings->value("IPAddress", udpDefPrefs.ipAddress).toString(); ui->ipAddressTxt->setEnabled(ui->lanEnableBtn->isChecked()); ui->ipAddressTxt->setText(udpPrefs.ipAddress); - + udpPrefs.controlLANPort = settings->value("ControlLANPort", udpDefPrefs.controlLANPort).toInt(); ui->controlPortTxt->setEnabled(ui->lanEnableBtn->isChecked()); ui->controlPortTxt->setText(QString("%1").arg(udpPrefs.controlLANPort)); - + udpPrefs.username = settings->value("Username", udpDefPrefs.username).toString(); ui->usernameTxt->setEnabled(ui->lanEnableBtn->isChecked()); ui->usernameTxt->setText(QString("%1").arg(udpPrefs.username)); - + udpPrefs.password = settings->value("Password", udpDefPrefs.password).toString(); ui->passwordTxt->setEnabled(ui->lanEnableBtn->isChecked()); ui->passwordTxt->setText(QString("%1").arg(udpPrefs.password)); @@ -1601,72 +1596,6 @@ void wfmain::loadSettings() serverConfig.users.append(user); } - ui->serverEnableCheckbox->setChecked(serverConfig.enabled); - ui->serverControlPortText->setText(QString::number(serverConfig.controlPort)); - ui->serverCivPortText->setText(QString::number(serverConfig.civPort)); - ui->serverAudioPortText->setText(QString::number(serverConfig.audioPort)); - - serverRxSetup.isinput = true; - serverTxSetup.isinput = false; - - ui->serverRXAudioInputCombo->blockSignals(true); - serverRxSetup.name = settings->value("ServerAudioInput", "").toString(); - qInfo(logGui()) << "Got Server Audio Input: " << serverRxSetup.name; - int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(serverRxSetup.name); - if (serverAudioInputIndex != -1) { - ui->serverRXAudioInputCombo->setCurrentIndex(serverAudioInputIndex); -#if defined(RTAUDIO) - serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(serverAudioInputIndex).toInt(); -#elif defined(PORTAUDIO) - serverRxSetup.port = ui->audioOutputCombo->itemData(serverAudioInputIndex).toInt(); -#else - QVariant v = ui->serverRXAudioInputCombo->currentData(); - serverRxSetup.port = v.value(); -#endif - } - ui->serverRXAudioInputCombo->blockSignals(false); - - serverRxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); - serverRxSetup.resampleQuality = rxSetup.resampleQuality; - - ui->serverTXAudioOutputCombo->blockSignals(true); - serverTxSetup.name = settings->value("ServerAudioOutput", "").toString(); - qInfo(logGui()) << "Got Server Audio Output: " << serverTxSetup.name; - int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(serverTxSetup.name); - if (serverAudioOutputIndex != -1) { - ui->serverTXAudioOutputCombo->setCurrentIndex(serverAudioOutputIndex); -#if defined(RTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(serverAudioOutputIndex).toInt(); -#elif defined(PORTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(serverAudioOutputIndex).toInt(); -#else - QVariant v = ui->serverTXAudioOutputCombo->currentData(); - serverRxSetup.port = v.value(); -#endif - } - ui->serverTXAudioOutputCombo->blockSignals(false); - - serverTxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); - serverTxSetup.resampleQuality = rxSetup.resampleQuality; - - int row = 0; - - ui->serverUsersTable->setRowCount(0); - - foreach(SERVERUSER user, serverConfig.users) - { - if (user.username != "" && user.password != "") - { - serverAddUserLine(user.username, user.password, user.userType); - row++; - } - } - - if (row == 0) { - serverAddUserLine("", "", 0); - } - - settings->endGroup(); // Memory channels @@ -1686,7 +1615,7 @@ void wfmain::loadSettings() // Also annoying that the preference groups are not written in // the order they are specified here. - for (int i = 0; i < size; i++) + for(int i=0; i < size; i++) { settings->setArrayIndex(i); chan = settings->value("chan", 0).toInt(); @@ -1694,7 +1623,7 @@ void wfmain::loadSettings() mode = settings->value("mode", 0).toInt(); isSet = settings->value("isSet", false).toBool(); - if (isSet) + if(isSet) { mem.setPreset(chan, freq, (mode_kind)mode); } @@ -1703,45 +1632,10 @@ void wfmain::loadSettings() settings->endArray(); settings->endGroup(); + emit sendServerConfig(serverConfig); + } -void wfmain::serverAddUserLine(const QString& user, const QString& pass, const int& type) -{ - ui->serverUsersTable->insertRow(ui->serverUsersTable->rowCount()); - ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 0, new QTableWidgetItem(user)); - ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 1, new QTableWidgetItem()); - ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 2, new QTableWidgetItem()); - - QLineEdit* password = new QLineEdit(); - password->setProperty("row", (int)ui->serverUsersTable->rowCount() - 1); - password->setEchoMode(QLineEdit::PasswordEchoOnEdit); - password->setText(pass); - connect(password, SIGNAL(editingFinished()), this, SLOT(onServerPasswordChanged())); - ui->serverUsersTable->setCellWidget(ui->serverUsersTable->rowCount() - 1, 1, password); - - QComboBox* comboBox = new QComboBox(); - comboBox->insertItems(0, { "Full User","Full with no TX","Monitor only" }); - comboBox->setCurrentIndex(type); - ui->serverUsersTable->setCellWidget(ui->serverUsersTable->rowCount() - 1, 2, comboBox); -} - -void wfmain::onServerPasswordChanged() -{ - int row = sender()->property("row").toInt(); - QLineEdit* password = (QLineEdit*)ui->serverUsersTable->cellWidget(row, 1); - QByteArray pass; - passcode(password->text(), pass); - password->setText(pass); - qInfo() << "password row" << row << "changed"; -} - -void wfmain::on_serverUsersTable_cellClicked(int row, int col) -{ - qInfo() << "Clicked on " << row << "," << col; - if (row == ui->serverUsersTable->model()->rowCount() - 1 && ui->serverUsersTable->item(row, 0) != NULL) { - serverAddUserLine("", "", 0); - } -} void wfmain::saveSettings() @@ -1872,37 +1766,11 @@ void wfmain::saveSettings() settings->beginGroup("Server"); - serverConfig.controlPort = ui->serverControlPortText->text().toInt(); - serverConfig.civPort = ui->serverCivPortText->text().toInt(); - serverConfig.audioPort = ui->serverAudioPortText->text().toInt(); - settings->setValue("ServerEnabled", serverConfig.enabled); settings->setValue("ServerControlPort", serverConfig.controlPort); settings->setValue("ServerCivPort", serverConfig.civPort); settings->setValue("ServerAudioPort", serverConfig.audioPort); settings->setValue("ServerNumUsers", serverConfig.users.count()); - settings->setValue("ServerAudioOutput", serverTxSetup.name); - settings->setValue("ServerAudioInput", serverRxSetup.name); - - serverConfig.users.clear(); - - for (int row = 0; row < ui->serverUsersTable->model()->rowCount(); row++) - { - if (ui->serverUsersTable->item(row, 0) != NULL) - { - SERVERUSER user; - user.username = ui->serverUsersTable->item(row, 0)->text(); - QLineEdit* password = (QLineEdit*)ui->serverUsersTable->cellWidget(row, 1); - user.password = password->text(); - QComboBox* comboBox = (QComboBox*)ui->serverUsersTable->cellWidget(row, 2); - user.userType = comboBox->currentIndex(); - serverConfig.users.append(user); - } - else { - ui->serverUsersTable->removeRow(row); - } - } - for (int f = 0; f < serverConfig.users.count(); f++) { settings->setValue("ServerUsername_" + QString::number(f), serverConfig.users[f].username); @@ -1910,8 +1778,6 @@ void wfmain::saveSettings() settings->setValue("ServerUserType_" + QString::number(f), serverConfig.users[f].userType); } - qInfo() << "Server config stored"; - settings->endGroup(); @@ -4468,6 +4334,7 @@ void wfmain::on_serialEnableBtn_clicked(bool checked) ui->audioInputCombo->setEnabled(!checked); ui->baudRateCombo->setEnabled(checked); ui->serialDeviceListCombo->setEnabled(checked); + //ui->udpServerSetupBtn->setEnabled(true); } void wfmain::on_lanEnableBtn_clicked(bool checked) @@ -4489,6 +4356,7 @@ void wfmain::on_lanEnableBtn_clicked(bool checked) ui->audioInputCombo->setEnabled(checked); ui->baudRateCombo->setEnabled(!checked); ui->serialDeviceListCombo->setEnabled(!checked); + //ui->udpServerSetupBtn->setEnabled(false); if(checked) { showStatusBarText("After filling in values, press Save Settings."); @@ -4543,35 +4411,6 @@ void wfmain::on_audioInputCombo_currentIndexChanged(int value) qDebug(logGui()) << "Changed default audio input to:" << txSetup.name; } - -void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) -{ -#if defined(RTAUDIO) - serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - serverRxSetup.port = ui->serverRXaudioInputCombo->itemData(value).toInt(); -#else - QVariant v = ui->serverRXAudioInputCombo->itemData(value); - serverRxSetup.port = v.value(); -#endif - serverRxSetup.name = ui->serverRXAudioInputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio input to:" << serverRxSetup.name; -} - -void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) -{ -#if defined(RTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#else - QVariant v = ui->serverTXAudioOutputCombo->itemData(value); - serverTxSetup.port = v.value(); -#endif - serverTxSetup.name = ui->serverTXAudioOutputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio output to:" << serverTxSetup.name; -} - void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text) { //udpPrefs.audioRXSampleRate = text.toInt(); @@ -4634,6 +4473,10 @@ void wfmain::on_connectBtn_clicked() } } +void wfmain::on_udpServerSetupBtn_clicked() +{ + srv->show(); +} void wfmain::on_sqlSlider_valueChanged(int value) { issueCmd(cmdSetSql, (unsigned char)value); @@ -4989,6 +4832,22 @@ void wfmain::receiveSpectrumRefLevel(int level) changeSliderQuietly(ui->scopeRefLevelSlider, level); } +// Slot to send/receive server config. +// If store is true then write to config otherwise send current config by signal +void wfmain::serverConfigRequested(SERVERCONFIG conf, bool store) +{ + + if (!store) { + emit sendServerConfig(serverConfig); + } + else { + // Store config in file! + qInfo(logSystem()) << "Storing server config"; + serverConfig = conf; + } + +} + void wfmain::on_modInputCombo_activated(int index) { emit setModInput( (rigInput)ui->modInputCombo->currentData().toInt(), false ); @@ -5669,12 +5528,6 @@ void wfmain::on_setClockBtn_clicked() setRadioTimeDatePrep(); } -void wfmain::on_serverEnableCheckbox_clicked(bool checked) -{ - ui->serverSetupGroup->setEnabled(checked); - serverConfig.enabled = checked; -} - // --- DEBUG FUNCTION --- void wfmain::on_debugBtn_clicked() { diff --git a/wfmain.h b/wfmain.h index a2bb8d4..7c1a9f4 100644 --- a/wfmain.h +++ b/wfmain.h @@ -23,6 +23,7 @@ #include "repeatersetup.h" #include "satellitesetup.h" #include "transceiveradjustments.h" +#include "udpserversetup.h" #include "udpserver.h" #include "qledlabel.h" #include "rigctld.h" @@ -162,6 +163,7 @@ signals: void sendCloseComm(); void sendChangeLatency(quint16 latency); void initServer(); + void sendServerConfig(SERVERCONFIG conf); void sendRigCaps(rigCapabilities caps); void requestRigState(); void stateUpdated(); @@ -266,6 +268,7 @@ private slots: void handlePlotScroll(QWheelEvent *); void sendRadioCommandLoop(); void showStatusBarText(QString text); + void serverConfigRequested(SERVERCONFIG conf, bool store); void receiveBaudRate(quint32 baudrate); void setRadioTimeDateSend(); @@ -384,10 +387,6 @@ private slots: void on_audioInputCombo_currentIndexChanged(int value); - void on_serverTXAudioOutputCombo_currentIndexChanged(int value); - - void on_serverRXAudioInputCombo_currentIndexChanged(int value); - void on_toFixedBtn_clicked(); void on_connectBtn_clicked(); @@ -412,6 +411,7 @@ private slots: void on_dataModeBtn_toggled(bool checked); + void on_udpServerSetupBtn_clicked(); void on_transmitBtn_clicked(); @@ -508,12 +508,6 @@ private slots: void on_setClockBtn_clicked(); - void on_serverEnableCheckbox_clicked(bool checked); - - void on_serverUsersTable_cellClicked(int row, int col); - - void onServerPasswordChanged(); - private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); @@ -762,9 +756,6 @@ private: audioSetup rxSetup; audioSetup txSetup; - audioSetup serverRxSetup; - audioSetup serverTxSetup; - colors defaultColors; void setDefaultColors(); // populate with default values @@ -833,6 +824,7 @@ private: repeaterSetup *rpt; satelliteSetup *sat; transceiverAdjustments *trxadj; + udpServerSetup *srv; aboutbox *abtBox; @@ -865,8 +857,6 @@ private: rigstate* rigState = Q_NULLPTR; SERVERCONFIG serverConfig; - void serverAddUserLine(const QString& user, const QString& pass, const int& type); - }; Q_DECLARE_METATYPE(struct rigCapabilities) diff --git a/wfmain.ui b/wfmain.ui index 49b9e2d..77ac1c2 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -6,7 +6,7 @@ 0 0 - 940 + 948 554 @@ -2066,7 +2066,7 @@ - 3 + 0 @@ -3025,32 +3025,20 @@ - - QLayout::SetDefaultConstraint - - + - - - - 0 - 20 - - - - - 16777215 - 20 - + + + <html><head/><body><p>Press here to set up the built-in rig server. The built-in server is intended to allow access over the network to a serial or USB-connected radio. </p></body></html> - Enable + Server Setup - + Qt::Horizontal @@ -3065,323 +3053,35 @@ - + + + 0 + + + 0 + - - - Server Setup + + + Audio Sources here - - - - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - Contol Port - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 99999 - - - 50001 - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - Civ Port - - - - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 99999 - - - 50002 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - Audio Port - - - - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 99999 - - - 50003 - - - - - - - - - - - RX Audio Input - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - TX Audio Output - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - 0 - 0 - - - - - 400 - 160 - - - - - 750 - 330 - - - - QFrame::StyledPanel - - - 1 - - - 0 - - - 3 - - - false - - - 100 - - - false - - - true - - - false - - - true - - - false - - - - Username - - - - - Password - - - - - Admin - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + + Qt::Vertical + + + + 20 + 320 + + + + @@ -3647,8 +3347,8 @@ 0 0 - 940 - 21 + 948 + 22 diff --git a/wfview.pro b/wfview.pro index f105bb3..199fe33 100644 --- a/wfview.pro +++ b/wfview.pro @@ -49,7 +49,7 @@ DEFINES += PREFIX=\\\"$$PREFIX\\\" # Choose audio system, uses QTMultimedia if both are commented out. # DEFINES += RTAUDIO -# DEFINES += PORTAUDIO +DEFINES += PORTAUDIO contains(DEFINES, RTAUDIO) { # RTAudio defines @@ -152,6 +152,7 @@ SOURCES += main.cpp\ audiohandler.cpp \ calibrationwindow.cpp \ satellitesetup.cpp \ + udpserversetup.cpp \ udpserver.cpp \ meter.cpp \ qledlabel.cpp \ @@ -173,6 +174,7 @@ HEADERS += wfmain.h \ audiohandler.h \ calibrationwindow.h \ satellitesetup.h \ + udpserversetup.h \ udpserver.h \ packettypes.h \ meter.h \ From 9dd9293036d52c852ba5b2ce18fe7ed57a8760a1 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:52 +0100 Subject: [PATCH 030/323] Revert "Masked password input after focus change." This reverts commit 605a87dec9087b78c5c1420444b0c76ca0cb4d96. --- wfmain.ui | 3 --- 1 file changed, 3 deletions(-) diff --git a/wfmain.ui b/wfmain.ui index 77ac1c2..2b598a5 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2409,9 +2409,6 @@ 16777215 - - QLineEdit::PasswordEchoOnEdit - From 4f34e54b02ccd78373a16710b9c26b5f7aba7b45 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:53 +0100 Subject: [PATCH 031/323] Revert "A little more on the enable/disable UI elements. Should be good." This reverts commit 8e95919aa92b4d66b65ff848969bb096e50d47f5. --- wfmain.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index ce769d2..0a8f270 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -4330,8 +4330,6 @@ void wfmain::on_serialEnableBtn_clicked(bool checked) ui->txLatencySlider->setEnabled(!checked); ui->rxLatencyValue->setEnabled(!checked); ui->txLatencyValue->setEnabled(!checked); - ui->audioOutputCombo->setEnabled(!checked); - ui->audioInputCombo->setEnabled(!checked); ui->baudRateCombo->setEnabled(checked); ui->serialDeviceListCombo->setEnabled(checked); //ui->udpServerSetupBtn->setEnabled(true); @@ -4352,8 +4350,6 @@ void wfmain::on_lanEnableBtn_clicked(bool checked) ui->txLatencySlider->setEnabled(checked); ui->rxLatencyValue->setEnabled(checked); ui->txLatencyValue->setEnabled(checked); - ui->audioOutputCombo->setEnabled(checked); - ui->audioInputCombo->setEnabled(checked); ui->baudRateCombo->setEnabled(!checked); ui->serialDeviceListCombo->setEnabled(!checked); //ui->udpServerSetupBtn->setEnabled(false); From 809f67ad19490a18e8bac146f309d9c877d8bd8c Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:54 +0100 Subject: [PATCH 032/323] Revert "Changed the enable/disable for some network UI elements." This reverts commit 7977de42d9e4c7c4bb25a770e1bce949701e1f45. --- wfmain.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 0a8f270..744df09 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -4343,13 +4343,6 @@ void wfmain::on_lanEnableBtn_clicked(bool checked) ui->controlPortTxt->setEnabled(checked); ui->usernameTxt->setEnabled(checked); ui->passwordTxt->setEnabled(checked); - ui->audioRXCodecCombo->setEnabled(checked); - ui->audioTXCodecCombo->setEnabled(checked); - ui->audioSampleRateCombo->setEnabled(checked); - ui->rxLatencySlider->setEnabled(checked); - ui->txLatencySlider->setEnabled(checked); - ui->rxLatencyValue->setEnabled(checked); - ui->txLatencyValue->setEnabled(checked); ui->baudRateCombo->setEnabled(!checked); ui->serialDeviceListCombo->setEnabled(!checked); //ui->udpServerSetupBtn->setEnabled(false); From eaa7a8edad2cc8ef41fbe94ab226d898791ea7dc Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:55 +0100 Subject: [PATCH 033/323] Revert "Change to width of group box" This reverts commit c860fa77a161e52fcb04ab0693fd218cfa99a244. --- wfmain.ui | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/wfmain.ui b/wfmain.ui index 2b598a5..e35b81d 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2066,7 +2066,7 @@ - 0 + 4 @@ -2109,18 +2109,6 @@ - - - 300 - 0 - - - - - 300 - 16777215 - - CI-V and Model From 837cc5fa8b79aa249c3d0b0135d9c78d0107a6bf Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:57 +0100 Subject: [PATCH 034/323] Revert "Larger vsp combo box... seems like it resizes though?" This reverts commit 37a2c1caf398d2919e97ea209f8d61a73059cc3c. --- wfmain.ui | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wfmain.ui b/wfmain.ui index e35b81d..e685ed2 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2066,7 +2066,7 @@ - 4 + 2 @@ -3156,13 +3156,13 @@ - 250 + 180 0 - 250 + 180 16777215 From 91d90f2ae688ca13b769cf3c9ab2a4bab7487286 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:18:58 +0100 Subject: [PATCH 035/323] Revert "Changed the order of the mod source combos" This reverts commit 9a781c671ec8cb9aa8c7b818c3c2367cdd0d38e6. --- wfmain.ui | 56 +++++++++++++++++++++++++++--------------------------- wfview.pro | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/wfmain.ui b/wfmain.ui index e685ed2..7cf3ce3 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2066,7 +2066,7 @@ - 2 + 0 @@ -2841,22 +2841,19 @@ - + - Modulation Input: + Satellite Ops - - - Modulation Input + + + <html><head/><body><p>Click here to adjust the frequency reference on the IC-9700.</p></body></html> - - Transmit modulation source - - - QComboBox::AdjustToContents + + Adjust Reference @@ -2880,6 +2877,26 @@ + + + + Modulation Input: + + + + + + + Modulation Input + + + Transmit modulation source + + + QComboBox::AdjustToContents + + + @@ -2917,23 +2934,6 @@ - - - - Satellite Ops - - - - - - - <html><head/><body><p>Click here to adjust the frequency reference on the IC-9700.</p></body></html> - - - Adjust Reference - - - diff --git a/wfview.pro b/wfview.pro index 199fe33..95862aa 100644 --- a/wfview.pro +++ b/wfview.pro @@ -49,7 +49,7 @@ DEFINES += PREFIX=\\\"$$PREFIX\\\" # Choose audio system, uses QTMultimedia if both are commented out. # DEFINES += RTAUDIO -DEFINES += PORTAUDIO +# DEFINES += PORTAUDIO contains(DEFINES, RTAUDIO) { # RTAudio defines From 1667df89baeff36f1a13d3b27cfb2f36fa628be3 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:19:03 +0100 Subject: [PATCH 036/323] Revert "Slightly wider radio connection box" This reverts commit 7564239a68c90fc7863758b29c1cc3718dea7d61. --- wfmain.ui | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/wfmain.ui b/wfmain.ui index 7cf3ce3..69277c6 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2066,7 +2066,7 @@ - 0 + 2 @@ -2074,12 +2074,6 @@ - - - 140 - 0 - - Radio Connection From 942d7beb9996267bd5cd88f502422f65850ec08d Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:19:04 +0100 Subject: [PATCH 037/323] Revert "Added clock and UTC toggle." This reverts commit 314d78ad051081c0ced4c843a4574ccf743fab7b. --- wfmain.cpp | 16 ++-------------- wfmain.h | 2 -- wfmain.ui | 2 +- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 744df09..f8e38ed 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -4566,15 +4566,8 @@ void wfmain::setRadioTimeDatePrep() if(!waitingToSetTimeDate) { // 1: Find the current time and date - QDateTime now; - if(ui->useUTCChk->isChecked()) - { - now = QDateTime::currentDateTimeUtc(); - now.setTime(QTime::currentTime()); - } else { - now = QDateTime::currentDateTime(); - now.setTime(QTime::currentTime()); - } + QDateTime now = QDateTime::currentDateTime(); + now.setTime(QTime::currentTime()); int second = now.time().second(); @@ -5512,11 +5505,6 @@ void wfmain::receiveStateInfo(rigstate* state) rigState = state; } -void wfmain::on_setClockBtn_clicked() -{ - setRadioTimeDatePrep(); -} - // --- DEBUG FUNCTION --- void wfmain::on_debugBtn_clicked() { diff --git a/wfmain.h b/wfmain.h index 7c1a9f4..a22b7dc 100644 --- a/wfmain.h +++ b/wfmain.h @@ -506,8 +506,6 @@ private slots: void on_settingsList_currentRowChanged(int currentRow); - void on_setClockBtn_clicked(); - private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); diff --git a/wfmain.ui b/wfmain.ui index 69277c6..3750d72 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2066,7 +2066,7 @@ - 2 + 4 From 0a42db97c200ccbfd1be6b9d32896fade7ff45ed Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:19:06 +0100 Subject: [PATCH 038/323] Revert "Changed width of list, other minor tweaks." This reverts commit 6b30cb53bc0cac03cb599b9f5fbb9d30a5ca7de8. --- wfmain.cpp | 1 - wfmain.ui | 153 ++++++++++++++++++++++++++++------------------------- 2 files changed, 81 insertions(+), 73 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index f8e38ed..77e90f4 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -223,7 +223,6 @@ void wfmain::createSettingsListItems() ui->settingsList->addItem("External Control"); // 4 ui->settingsList->addItem("Experimental"); // 5 //ui->settingsList->addItem("Audio Processing"); // 6 - ui->settingsStack->setCurrentIndex(0); } void wfmain::on_settingsList_currentRowChanged(int currentRow) diff --git a/wfmain.ui b/wfmain.ui index 3750d72..cea22b3 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2049,15 +2049,9 @@ - - - 150 - 0 - - - 150 + 180 16777215 @@ -2066,7 +2060,7 @@ - 4 + 0 @@ -3067,74 +3061,89 @@ - - 0 - - - 0 - - - - Qt::LeftToRight - - - Enable RigCtld + + + Rigctld Server + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::LeftToRight + + + Enable RigCtld + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + Port + + + + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 20 - - - - - - - - Port - - - - - - - - 75 - 0 - - - - - 75 - 16777215 - - - - - - - - Qt::Horizontal - - - - 422 - 20 - - - - From ae5684238c55f8851b217c9d1f321fe91edab83d Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:19:09 +0100 Subject: [PATCH 039/323] Revert "Working preferences with a list." This reverts commit 26f15cc9dbe35b58fca01108b52c355d4a8f0ab4. --- wfmain.cpp | 8 --- wfmain.h | 2 - wfmain.ui | 144 +++++++++++++++-------------------------------------- 3 files changed, 40 insertions(+), 114 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 77e90f4..9deffe5 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -225,12 +225,6 @@ void wfmain::createSettingsListItems() //ui->settingsList->addItem("Audio Processing"); // 6 } -void wfmain::on_settingsList_currentRowChanged(int currentRow) -{ - ui->settingsStack->setCurrentIndex(currentRow); -} - - void wfmain::connectSettingsList() { @@ -632,8 +626,6 @@ void wfmain::setupPlots() void wfmain::setupMainUI() { - createSettingsListItems(); - ui->bandStkLastUsedBtn->setVisible(false); ui->bandStkVoiceBtn->setVisible(false); ui->bandStkDataBtn->setVisible(false); diff --git a/wfmain.h b/wfmain.h index a22b7dc..efef3cf 100644 --- a/wfmain.h +++ b/wfmain.h @@ -504,8 +504,6 @@ private slots: void receiveStateInfo(rigstate* state); - void on_settingsList_currentRowChanged(int currentRow); - private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); diff --git a/wfmain.ui b/wfmain.ui index cea22b3..702f54f 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2060,7 +2060,7 @@ - 0 + 2 @@ -2349,7 +2349,7 @@ - + 256 @@ -2372,7 +2372,7 @@ - + 180 @@ -2787,6 +2787,23 @@ 0 + + + + PTT Off + + + + + + + PTT On + + + Ctrl+S + + + @@ -2937,50 +2954,6 @@ - - - - 0 - - - - - Manual PTT Toggle - - - - - - - PTT On - - - Ctrl+S - - - - - - - PTT Off - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - @@ -2989,7 +2962,7 @@ 20 - 279 + 312 @@ -3208,62 +3181,25 @@ - - - - - - - This page contains experimental features. Use at your own risk. - - - - - - - - - - - <html><head/><body><p>This button runs debug functions, and is provided as a convenience for programmers. The functions executed are under:</p><p><span style=" color:#ffff55;">void</span><span style=" color:#55ff55;">wfmain</span><span style=" color:#aaaaaa;">::</span><span style=" font-weight:600;">on_debugBtn_clicked</span><span style=" color:#aaaaaa;">()</span></p><p>in wfmain.cpp.</p></body></html> - - - Debug - - - Ctrl+Alt+D - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 320 - - - - - + + + + 70 + 130 + 80 + 25 + + + + <html><head/><body><p>This button runs debug functions, and is provided as a convenience for programmers. The functions executed are under:</p><p><span style=" color:#ffff55;">void</span><span style=" color:#55ff55;">wfmain</span><span style=" color:#aaaaaa;">::</span><span style=" font-weight:600;">on_debugBtn_clicked</span><span style=" color:#aaaaaa;">()</span></p><p>in wfmain.cpp.</p></body></html> + + + Debug + + + Ctrl+Alt+D + + From b85c268f2c5f79d60ee8ec3044221b0c3b03600b Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:19:11 +0100 Subject: [PATCH 040/323] Revert "Added more pages." This reverts commit 700ac53b288fc987f009c9d421192f08ea63d1be. --- wfmain.cpp | 5 +- wfmain.ui | 237 +++-------------------------------------------------- 2 files changed, 13 insertions(+), 229 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 9deffe5..e7238db 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -221,8 +221,9 @@ void wfmain::createSettingsListItems() ui->settingsList->addItem("Radio Settings"); // 2 ui->settingsList->addItem("Radio Server"); // 3 ui->settingsList->addItem("External Control"); // 4 - ui->settingsList->addItem("Experimental"); // 5 - //ui->settingsList->addItem("Audio Processing"); // 6 + ui->settingsList->addItem("Audio Processing"); // 5 + ui->settingsList->addItem("Experimental"); // 6 + ui->settingsList->addItem("About"); // 7 } void wfmain::connectSettingsList() diff --git a/wfmain.ui b/wfmain.ui index 702f54f..1682cf3 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2060,7 +2060,7 @@ - 2 + 3 @@ -2920,20 +2920,14 @@ - - - Press here to set the clock of the radio. The command will be sent to the radio when the seconds go to zero. - + Set Clock - - - Check this box to set the radio's clock to UTC. Otherwise, the clock will be set to the local timezone of this computer. - + Use UTC @@ -2970,238 +2964,27 @@ - - - - - - - <html><head/><body><p>Press here to set up the built-in rig server. The built-in server is intended to allow access over the network to a serial or USB-connected radio. </p></body></html> - - - Server Setup - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - 0 - - - - - Audio Sources here - - - - - - - - - Qt::Vertical - - - - 20 - 320 - - - - - - - - - - - - - - Rigctld Server - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::LeftToRight - - - Enable RigCtld - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 20 - - - - - - - - Port - - - - - - - - 75 - 0 - - - - - 75 - 16777215 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - Virtual Serial Port - - - - - - - - 180 - 0 - - - - - 180 - 16777215 - - - - <html><head/><body><p>Use this to define a virtual serial port. </p><p><br/></p><p>On Windows, the virtual serial port can be used to connect to a serial port loopback device, through which other programs can connect to the radio. </p><p><br/></p><p>On Linux and macOS, the port defined here is a pseudo-terminal device, which may be connected to directly by any program designed for a serial connection. </p></body></html> - - - Virtual Serial Port Selector - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - + - 70 + 350 130 80 25 - <html><head/><body><p>This button runs debug functions, and is provided as a convenience for programmers. The functions executed are under:</p><p><span style=" color:#ffff55;">void</span><span style=" color:#55ff55;">wfmain</span><span style=" color:#aaaaaa;">::</span><span style=" font-weight:600;">on_debugBtn_clicked</span><span style=" color:#aaaaaa;">()</span></p><p>in wfmain.cpp.</p></body></html> + <html><head/><body><p>Press here to set up the built-in rig server. The built-in server is intended to allow access over the network to a serial or USB-connected radio. </p></body></html> - Debug - - - Ctrl+Alt+D + Server Setup + + + From 9cbcb3aa1cd407f0a3f7a2b8f500f5561052b5e1 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:19:12 +0100 Subject: [PATCH 041/323] Revert "Brought back the Save Settings button" This reverts commit 888824f9a4d0991786899dca86fddf1f5d3fc50a. --- wfmain.ui | 191 +----------------------------------------------------- 1 file changed, 3 insertions(+), 188 deletions(-) diff --git a/wfmain.ui b/wfmain.ui index 1682cf3..f97f01a 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2060,7 +2060,7 @@ - 3 + 0 @@ -2779,53 +2779,6 @@ - - - - 0 - - - 0 - - - - - PTT Off - - - - - - - PTT On - - - Ctrl+S - - - - - - - Enable PTT Controls - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - @@ -2841,146 +2794,8 @@ - - - - - - - - Satellite Ops - - - - - - - <html><head/><body><p>Click here to adjust the frequency reference on the IC-9700.</p></body></html> - - - Adjust Reference - - - - - - - Data Mod Input: - - - - - - - Data Modulation Input - - - Transmit Data-mode modulation input source - - - QComboBox::AdjustToContents - - - - - - - Modulation Input: - - - - - - - Modulation Input - - - Transmit modulation source - - - QComboBox::AdjustToContents - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Set Clock - - - - - - - Use UTC - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 312 - - - - - - - - - - - 350 - 130 - 80 - 25 - - - - <html><head/><body><p>Press here to set up the built-in rig server. The built-in server is intended to allow access over the network to a serial or USB-connected radio. </p></body></html> - - - Server Setup - - - + + From f8351ad6100452200db05c169c34253b8cce060c Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Wed, 12 Jan 2022 19:19:14 +0100 Subject: [PATCH 042/323] Revert "Beginning of the new Settings tab. Does not compile as-is yet." This reverts commit 0c807f54c2a752f760da65e431c09f06d04a7674. --- wfmain.cpp | 19 - wfmain.h | 4 - wfmain.ui | 1592 ++++++++++++++++++++++++++-------------------------- 3 files changed, 794 insertions(+), 821 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index e7238db..09cd9c5 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -213,25 +213,6 @@ void wfmain::openRig() } -void wfmain::createSettingsListItems() -{ - // Add items to the settings tab list widget - ui->settingsList->addItem("Radio Access"); // 0 - ui->settingsList->addItem("User Interface"); // 1 - ui->settingsList->addItem("Radio Settings"); // 2 - ui->settingsList->addItem("Radio Server"); // 3 - ui->settingsList->addItem("External Control"); // 4 - ui->settingsList->addItem("Audio Processing"); // 5 - ui->settingsList->addItem("Experimental"); // 6 - ui->settingsList->addItem("About"); // 7 -} - -void wfmain::connectSettingsList() -{ - -} - - void wfmain::rigConnections() { connect(this, SIGNAL(setCIVAddr(unsigned char)), rig, SLOT(setCIVAddr(unsigned char))); diff --git a/wfmain.h b/wfmain.h index efef3cf..3a889a4 100644 --- a/wfmain.h +++ b/wfmain.h @@ -510,10 +510,6 @@ private: QSettings *settings=Q_NULLPTR; void loadSettings(); void saveSettings(); - - void createSettingsListItems(); - void connectSettingsList(); - QCustomPlot *plot; // line plot QCustomPlot *wf; // waterfall image QCPItemLine * freqIndicatorLine; diff --git a/wfmain.ui b/wfmain.ui index f97f01a..f5b6cbd 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2038,108 +2038,324 @@ Settings - + - - - 10 - - - 10 - + - - - - 180 - 16777215 - + + + Draw Peaks - - - 0 + + + When tuning, set lower digits to zero - - - - - - - - Radio Connection - - - - - - Serial (USB) - - - buttonGroup - - - - - - - Network - - - buttonGroup - - - - - - - - - - CI-V and Model - - - - - - <html><head/><body><p>If you are using an older (year 2010) radio, you may need to enable this option to manually specify the CI-V address. This option is also useful for radios that do not have CI-V Transceive enabled and thus will not answer our broadcast query for connected rigs on the CI-V bus.</p><p>If you have a modern radio with CI-V Transceive enabled, you should not need to check this box. </p><p>You will need to Save Settings and re-launch wfview for this to take effect. </p></body></html> - - - Manual Radio CI-V Address: - - - - - - - <html><head/><body><p>Only check for older radios!</p><p>This checkbox forces wfview to trust that the CI-V address is also the model number of the radio. This is only useful for older radios that do not reply to our Rig ID requests (0x19 0x00). Do not check this box unless you have an older radio. </p></body></html> - - - Use CI-V address as Model ID too - - - - - - - false - - - - 50 - 0 - - - - - 50 - 16777215 - - - - <html><head/><body><p>Enter the address in as hexidecimal, without any prefix, just as the radio presents the address in the menu. </p><p>Here are some common examples:</p> + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + Use System Theme + + + + + + + Waterfall Dark Theme + + + + + + + Anti-Alias Waterfall + + + + + + + Interpolate Waterfall + + + true + + + + + + + Show full screen + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + <html><head/><body><p>Click here to adjust the frequency reference on the IC-9700.</p></body></html> + + + Adjust Reference + + + + + + + Satellite Ops + + + + + + + Modulation Input: + + + + + + + Modulation Input + + + Transmit modulation source + + + QComboBox::AdjustToContents + + + + + + + Data Mod Input: + + + + + + + Data Modulation Input + + + Transmit Data-mode modulation input source + + + QComboBox::AdjustToContents + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + PTT On + + + Ctrl+S + + + + + + + PTT Off + + + + + + + Enable PTT Controls + + + + + + + Secondary Meter Selection: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + <html><head/><body><p>Select this option if the rig is pluged into the computer using a USB cable or a serial connection. </p></body></html> + + + Connect over USB (serial) + + + radioConnectionSerialNetworkGrp + + + + + + + Serial Device: + + + + + + + <html><head/><body><p>Select a serial port here. </p><p>Once selected, check &quot;Enable USB(serial), press &quot;Save Settings&quot;, exit, and re-start wfview. </p></body></html> + + + Serial Device Selector + + + + + + + Baud: + + + + + + + <html><head/><body><p>Baud rate selection menu. </p><p>For the IC-7300 select 115200. </p><p>Older rigs may require other settings. </p><p>Be sure to match what baud rate the rig is set to. Using the highese supported baud rate for the radio is recommended. </p><p>Please press &quot;Save Settings&quot; and re-launc wfview for this to take effect.</p></body></html> + + + baud rate + + + baud rate selection menu + + + + + + + <html><head/><body><p>Press here to set up the built-in rig server. The built-in server is intended to allow access over the network to a serial or USB-connected radio. </p></body></html> + + + Server Setup + + + + + + + <html><head/><body><p>If you are using an older (year 2010) radio, you may need to enable this option to manually specify the CI-V address. This option is also useful for radios that do not have CI-V Transceive enabled and thus will not answer our broadcast query for connected rigs on the CI-V bus.</p><p>If you have a modern radio with CI-V Transceive enabled, you should not need to check this box. </p><p>You will need to Save Settings and re-launch wfview for this to take effect. </p></body></html> + + + Manual Radio CI-V Address: + + + + + + + false + + + + 50 + 0 + + + + + 50 + 16777215 + + + + <html><head/><body><p>Enter the address in as hexidecimal, without any prefix, just as the radio presents the address in the menu. </p><p>Here are some common examples:</p> <p>IC-706: 58 <br/>IC-756: 50 <br/>IC-756 Pro: 5C @@ -2151,716 +2367,496 @@ <br/>IC-7300: 94 </p><p>This setting is typically needed for older radios and for radios that do not have CI-V Transceive enabled. </p> <p>After changing, press Save Settings and re-launch wfview.</p></body></html> - - - auto - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - - - Serial Connected Radios - - - - - - Serial Device: - - - - - - - - 180 - 0 - - - - - 180 - 16777215 - - - - - - - - Baud Rate - - - - - - - - 120 - 16777215 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - - - Network Connected Radios - - - - - - 0 - - - 0 - - - 0 - - - - - Hostname - - - - - - - - 256 - 0 - - - - - 256 - 16777215 - - - - - - - - Control Port - - - - - - - - 75 - 16777215 - - - - 50001 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - 0 - - - 0 - - - - - Username - - - - - - - - 256 - 0 - - - - - 256 - 16777215 - - - - - - - - Password - - - - - - - - 180 - 0 - - - - - 180 - 16777215 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - RX Latency (ms) - - - - - - - - 0 - 0 - - - - 30 - - - 500 - - - Qt::Horizontal - - - - - - - 0 - - - - - - - TX Latency (ms) - - - - - - - 30 - - - 500 - - - Qt::Horizontal - - - - - - - 0 - - - - - - - RX Codec - - - - - - - Receive Audio Codec Selector - - - - - - - TX Codec - - - - - - - Transmit Audio Codec Selector - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Sample Rate - - - - - - - Audio Sample Rate Selector - - - - 48000 - - - - - 24000 - - - - - 16000 - - - - - 8000 - - - - - - - - Audio Output - - - - - - - - 300 - 16777215 - - - - Audio Output Selector - - - - - - - Audio Input - - - - - - - - 300 - 16777215 - - - - Audio Input Selector - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - 0 - - - - - Connect - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - Draw Peaks - - - - - - - When tuning, set lower digits to zero - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Waterfall Dark Theme - - - - - - - Interpolate Waterfall - - - true - - - - - - - Use System Theme - - - - - - - Anti-Alias Waterfall - - - - - - - Show full screen - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Secondary Meter Selection: - - - - - - - - - - Set up radio polling. The radio's meter is polled every-other interval. - - - Polling - - - - - - Polling - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - + + + auto + + + + + + + <html><head/><body><p>Only check for older radios!</p><p>This checkbox forces wfview to trust that the CI-V address is also the model number of the radio. This is only useful for older radios that do not reply to our Rig ID requests (0x19 0x00). Do not check this box unless you have an older radio. </p></body></html> + + + Use as Model too + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + <html><head/><body><p>Connection to the radio is via network. </p><p>This means you are connecting to a radio with native ethernet or wifi, such as the IC-705, IC-7610, IC-7850, IC-R8600, or IC-9700</p><p>You should also select this option if you are connecting to another instance of wfview over a network. </p></body></html> + + + Connect over LAN + + + radioConnectionSerialNetworkGrp + + + + + + + Press here to initiate the network connection to the rig. + + + Connect + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::RightToLeft + + + Enable RigCtld + + + + + + + Port + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Virtual Serial Port + + + + + + + <html><head/><body><p>Use this to define a virtual serial port. </p><p><br/></p><p>On Windows, the virtual serial port can be used to connect to a serial port loopback device, through which other programs can connect to the radio. </p><p><br/></p><p>On Linux and macOS, the port defined here is a pseudo-terminal device, which may be connected to directly by any program designed for a serial connection. </p></body></html> + + + Virtual Serial Port Selector + + + + + + + Radio IP Address + + + + + + + + + + Radio Control Port + + + + + + + 50001 + + + + + + + + + + + Username + + + + + + + + + + Password + + + + + + + Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhSensitiveData + + + QLineEdit::PasswordEchoOnEdit + + + + + + + + + + + RX Latency (ms) + + + + + + + + 0 + 0 + + + + 30 + + + 500 + + + Qt::Horizontal + + + + + + + 0 + + + + + + + TX Latency (ms) + + + + + + + 30 + + + 500 + + + Qt::Horizontal + + + + + + + 0 + + + + + + + RX Codec + + + + + + + Receive Audio Codec Selector + + + + + + + TX Codec + + + + + + + Transmit Audio Codec Selector + + + + + + + + + + + Sample Rate + + + + + + + Audio Sample Rate Selector + + + + 48000 + + + + + 24000 + + + + + 16000 + + + + + 8000 + + + + + + + + Audio Output + + + + + + + + 300 + 16777215 + + + + Audio Output Selector + + + + + + + Audio Input + + + + + + + + 300 + 16777215 + + + + Audio Input Selector + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + <html><head/><body><p>This button runs debug functions, and is provided as a convenience for programmers. The functions executed are under:</p><p><span style=" color:#ffff55;">void</span><span style=" color:#55ff55;">wfmain</span><span style=" color:#aaaaaa;">::</span><span style=" font-weight:600;">on_debugBtn_clicked</span><span style=" color:#aaaaaa;">()</span></p><p>in wfmain.cpp.</p></body></html> + + + Debug + + + Ctrl+Alt+D + + + + + + + Set up radio polling. The radio's meter is polled every-other interval. + + + Polling + + + + + + Polling + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + About + + + + + + + Save Settings + + + + + + + + 75 + true + + + + Exit Program + + + + + + + + + 0 + + + + + Please note: Changing the built-in network server requires pressing "Save Settings", closing wfview, and re-opening. + + + + + + + + + 0 + + + + + <html><head></head><body><p>Please see the <a href="https://wfview.org/wfview-user-manual/settings-tab/">User Manual</a> for more information. </p></body></html> + + + Qt::RichText + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + - - - - 0 - - - 0 - - - 0 - - - - - About - - - - - - - Save Settings - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - false - - - - Exit Program - - - - - @@ -2891,6 +2887,6 @@ - + From 5a92c071d1252abd575ed7fbfeee275f5fd2a965 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 12 Jan 2022 20:01:17 +0000 Subject: [PATCH 043/323] Set audio send period on server --- udpserver.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index bf533df..a72aeee 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -3,7 +3,7 @@ #define STALE_CONNECTION 15 #define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms -#define AUDIO_SEND_PERIOD 20 // how often to call the RX audio send function in ms +#define AUDIO_SEND_PERIOD 20 // udpServer::udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudio) : config(config), outAudio(outAudio), @@ -357,7 +357,7 @@ void udpServer::controlReceived() rxAudioTimer = new QTimer(); rxAudioTimer->setTimerType(Qt::PreciseTimer); connect(rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); - rxAudioTimer->start(AUDIO_SEND_PERIOD); + rxAudioTimer->start(20); } } @@ -1277,6 +1277,8 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) return; } +#define PURGE_SECONDS 60 + void udpServer::watchdog() { QDateTime now = QDateTime::currentDateTime(); From e00fa2622915404ed7ab2d966f654e9289a821c6 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 13 Jan 2022 11:17:13 +0000 Subject: [PATCH 044/323] trying to find the cause of server audio issue --- udpserver.cpp | 9 ++++++++- wfmain.cpp | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index a72aeee..12db9a0 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -38,7 +38,7 @@ void udpServer::init() uint32_t addr = localIP.toIPv4Address(); - qInfo(logUdpServer()) << " My IP Address: " << QHostAddress(addr).toString() << " My MAC Address: " << macAddress; + qInfo(logUdpServer()) << "My IP Address:" << QHostAddress(addr).toString() << "My MAC Address:" << macAddress; controlId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (config.controlPort & 0xffff); @@ -60,6 +60,13 @@ void udpServer::init() udpAudio->bind(config.audioPort); QUdpSocket::connect(udpAudio, &QUdpSocket::readyRead, this, &udpServer::audioReceived); +#if !defined(PORTAUDIO) && !defined(RTAUDIO) + qInfo(logUdpServer()) << "Server audio input:" << inAudio.port.deviceName(); + qInfo(logUdpServer()) << "Server audio output:" << outAudio.port.deviceName(); +#else + qInfo(logUdpServer()) << "Server audio input:" << inAudio.name; + qInfo(logUdpServer()) << "Server audio output:" << outAudio.name; +#endif wdTimer = new QTimer(); connect(wdTimer, &QTimer::timeout, this, &udpServer::watchdog); wdTimer->start(500); diff --git a/wfmain.cpp b/wfmain.cpp index 86fb8f9..48b44b9 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1135,8 +1135,8 @@ void wfmain::setAudioDevicesUI() // Set these to default audio devices initially. rxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); txSetup.port = QAudioDeviceInfo::defaultInputDevice(); - serverRxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); - serverTxSetup.port = QAudioDeviceInfo::defaultInputDevice(); + serverRxSetup.port = QAudioDeviceInfo::defaultInputDevice(); + serverTxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); #endif } From d4868ae14c531a60f154f6dde00afe42a71fd168 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 13 Jan 2022 19:34:34 +0000 Subject: [PATCH 045/323] Latency on server is wrong way round! --- audiohandler.cpp | 5 +++-- udpserver.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 7d43774..9f9909d 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -554,7 +554,7 @@ qint64 audioHandler::writeData(const char* data, qint64 nBytes) int send = qMin((int)(nBytes - sentlen), chunkBytes - tempBuf.sent); tempBuf.data.append(QByteArray::fromRawData(data + sentlen, send)); sentlen = sentlen + send; - tempBuf.seq = 0; // Not used in TX + tempBuf.seq = lastSentSeq; tempBuf.time = QTime::currentTime(); tempBuf.sent = tempBuf.sent + send; } @@ -568,6 +568,7 @@ qint64 audioHandler::writeData(const char* data, qint64 nBytes) } tempBuf.data.clear(); tempBuf.sent = 0; + lastSentSeq++; } } @@ -750,7 +751,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) if (currentLatency > setup.latency) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Packet " << hex << packet.seq << - " arrived too late (increase output latency!) " << + " arrived too late (increase latency!) " << dec << packet.time.msecsTo(QTime::currentTime()) << "ms"; // if (!ringBuf->try_read(packet)) // break; diff --git a/udpserver.cpp b/udpserver.cpp index 12db9a0..4c492a9 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -61,11 +61,11 @@ void udpServer::init() QUdpSocket::connect(udpAudio, &QUdpSocket::readyRead, this, &udpServer::audioReceived); #if !defined(PORTAUDIO) && !defined(RTAUDIO) - qInfo(logUdpServer()) << "Server audio input:" << inAudio.port.deviceName(); - qInfo(logUdpServer()) << "Server audio output:" << outAudio.port.deviceName(); + qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.port.deviceName(); + qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.port.deviceName(); #else - qInfo(logUdpServer()) << "Server audio input:" << inAudio.name; - qInfo(logUdpServer()) << "Server audio output:" << outAudio.name; + qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.name; + qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.name; #endif wdTimer = new QTimer(); connect(wdTimer, &QTimer::timeout, this, &udpServer::watchdog); @@ -323,7 +323,6 @@ void udpServer::controlReceived() { outAudio.codec = current->txCodec; outAudio.samplerate = current->txSampleRate; - outAudio.latency = current->txBufferLen; outAudio.isinput = false; txaudio = new audioHandler(); @@ -346,6 +345,7 @@ void udpServer::controlReceived() { inAudio.codec = current->rxCodec; inAudio.samplerate = current->rxSampleRate; + inAudio.latency = current->txBufferLen; inAudio.isinput = true; rxaudio = new audioHandler(); From 184af7a582496c1b21f115673077c63e7f196654 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 13 Jan 2022 19:54:43 +0000 Subject: [PATCH 046/323] Add extra debugging --- audiohandler.cpp | 4 +--- udpserver.cpp | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 9f9909d..66191b8 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -559,11 +559,9 @@ qint64 audioHandler::writeData(const char* data, qint64 nBytes) tempBuf.sent = tempBuf.sent + send; } else { - //ringBuf->write(tempBuf); - if (!ringBuf->try_write(tempBuf)) { - qDebug(logAudio()) << "outgoing audio buffer full!"; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << " audio buffer full!"; break; } tempBuf.data.clear(); diff --git a/udpserver.cpp b/udpserver.cpp index 4c492a9..c7c74cc 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1550,7 +1550,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) // This will run every 100ms so out-of-sequence packets will not trigger a retransmit request. QByteArray missingSeqs; - + QTime missingTime = QTime::currentTime(); if (!c->rxSeqBuf.empty() && c->rxSeqBuf.size() <= c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey()) { @@ -1691,6 +1691,9 @@ void udpServer::sendRetransmitRequest(CLIENT* c) qInfo(logUdpServer()) << "Unable to lock missMutex()"; } + if (missingTime.msecsTo(QTime::currentTime()) > 10) { + qInfo(logUdpServer()) << "Missing processing took" << missingTime.msecsTo(QTime::currentTime()) << "to run!!!!"; + } } From 4fa83b79f0ace1d3278b6bcd3a6e9b29ebd7d402 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 13 Jan 2022 20:16:14 +0000 Subject: [PATCH 047/323] More debugging to isolate audio stalling --- udpserver.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index c7c74cc..096db13 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1589,7 +1589,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (s == c->rxMissing.end()) { // We haven't seen this missing packet before - qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j; + qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j << dec << missingTime.msecsTo(QTime::currentTime()) << "ms"; if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { c->rxMissing.insert(j, 0); @@ -1631,6 +1631,10 @@ void udpServer::sendRetransmitRequest(CLIENT* c) } } + if (missingTime.msecsTo(QTime::currentTime()) > 10) { + qInfo(logUdpServer()) << "Initial missing processing has been running for" << missingTime.msecsTo(QTime::currentTime()) << "(ms)"; + } + if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it) @@ -1656,7 +1660,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. { p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); - qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq; + qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq << dec << missingTime.msecsTo(QTime::currentTime()) << "ms"; if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -1692,7 +1696,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) } if (missingTime.msecsTo(QTime::currentTime()) > 10) { - qInfo(logUdpServer()) << "Missing processing took" << missingTime.msecsTo(QTime::currentTime()) << "to run!!!!"; + qInfo(logUdpServer()) << "Missing processing took" << missingTime.msecsTo(QTime::currentTime()) << "(ms) to run!!!!"; } } From 1291990d54fd97e68e22364a1bb8b6d807e5472e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 13 Jan 2022 20:26:42 +0000 Subject: [PATCH 048/323] Reduce number of mutex locks during retransmit processing --- udpserver.cpp | 54 ++++++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index 096db13..b513517 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1583,50 +1583,42 @@ void udpServer::sendRetransmitRequest(CLIENT* c) // We are missing packets so iterate through the buffer and add the missing ones to missing packet list if (c->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - for (int i = 0; i < c->rxSeqBuf.keys().length() - 1; i++) { - for (quint16 j = c->rxSeqBuf.keys()[i] + 1; j < c->rxSeqBuf.keys()[i + 1]; j++) { - auto s = c->rxMissing.find(j); - if (s == c->rxMissing.end()) - { - // We haven't seen this missing packet before - qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j << dec << missingTime.msecsTo(QTime::currentTime()) << "ms"; - if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + for (int i = 0; i < c->rxSeqBuf.keys().length() - 1; i++) { + for (quint16 j = c->rxSeqBuf.keys()[i] + 1; j < c->rxSeqBuf.keys()[i + 1]; j++) { + auto s = c->rxMissing.find(j); + if (s == c->rxMissing.end()) { + // We haven't seen this missing packet before + qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j << dec << missingTime.msecsTo(QTime::currentTime()) << "ms"; c->rxMissing.insert(j, 0); - c->missMutex.unlock(); + + if (c->rxSeqBuf.size() > 400) + { + c->rxSeqBuf.remove(0); + c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. + } } else { - qInfo(logUdpServer()) << "Unable to lock missMutex()"; - } - - if (c->rxSeqBuf.size() > 400) - { - c->rxSeqBuf.remove(0); - c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. - } - } - else { - if (s.value() == 4) - { - // We have tried 4 times to request this packet, time to give up! - if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + if (s.value() == 4) { + // We have tried 4 times to request this packet, time to give up! s = c->rxMissing.erase(s); - c->missMutex.unlock(); } - else { - qInfo(logUdpServer()) << "Unable to lock missMutex()"; - } - } } } } + else { + qInfo(logUdpServer()) << "Unable to lock missMutex()"; + } c->rxMutex.unlock(); } else { qInfo(logUdpServer()) << "Unable to lock rxMutex()"; } + c->missMutex.unlock(); } } @@ -1660,7 +1652,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. { p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); - qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq << dec << missingTime.msecsTo(QTime::currentTime()) << "ms"; + qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq; if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -1694,10 +1686,6 @@ void udpServer::sendRetransmitRequest(CLIENT* c) else { qInfo(logUdpServer()) << "Unable to lock missMutex()"; } - - if (missingTime.msecsTo(QTime::currentTime()) > 10) { - qInfo(logUdpServer()) << "Missing processing took" << missingTime.msecsTo(QTime::currentTime()) << "(ms) to run!!!!"; - } } From 99ca97370b5d79f4c0c8c690cba97dad7bec3e59 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 13 Jan 2022 21:12:27 +0000 Subject: [PATCH 049/323] Correctly remove items from the buffers! --- udphandler.cpp | 4 ++-- udpserver.cpp | 31 +++++++++++++++++++++---------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 3911292..476dff6 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1229,8 +1229,8 @@ void udpBase::sendRetransmitRequest() // We have at least 1 missing packet! qDebug(logUdp()) << "Missing Seq: size=" << rxSeqBuf.size() << "firstKey=" << rxSeqBuf.firstKey() << "lastKey=" << rxSeqBuf.lastKey() << "missing=" << rxSeqBuf.lastKey() - rxSeqBuf.firstKey() - rxSeqBuf.size() + 1; // We are missing packets so iterate through the buffer and add the missing ones to missing packet list + missingMutex.lock(); for (int i = 0; i < rxSeqBuf.keys().length() - 1; i++) { - missingMutex.lock(); for (quint16 j = rxSeqBuf.keys()[i] + 1; j < rxSeqBuf.keys()[i + 1]; j++) { auto s = rxMissing.find(j); if (s == rxMissing.end()) @@ -1258,8 +1258,8 @@ void udpBase::sendRetransmitRequest() } } - missingMutex.unlock(); } + missingMutex.unlock(); } } rxBufferMutex.unlock(); diff --git a/udpserver.cpp b/udpserver.cpp index b513517..50cf933 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -833,7 +833,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) { if (current->rxSeqBuf.size() > 400) { - current->rxSeqBuf.remove(0); + current->rxSeqBuf.remove(current->rxSeqBuf.firstKey()); } current->rxSeqBuf.insert(in->seq, QTime::currentTime()); current->rxMutex.unlock(); @@ -1128,7 +1128,7 @@ void udpServer::sendCapabilities(CLIENT* c) { if (c->txSeqBuf.size() > 400) { - c->txSeqBuf.remove(0); + c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } c->txSeqBuf.insert(p.seq, s); c->txSeq++; @@ -1199,7 +1199,7 @@ void udpServer::sendConnectionInfo(CLIENT* c) { if (c->txSeqBuf.size() > 400) { - c->txSeqBuf.remove(0); + c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } c->txSeqBuf.insert(p.seq, s); c->txSeq++; @@ -1257,7 +1257,7 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) { if (c->txSeqBuf.size() > 400) { - c->txSeqBuf.remove(0); + c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } c->txSeqBuf.insert(p.seq, s); c->txSeq++; @@ -1375,7 +1375,7 @@ void udpServer::sendStatus(CLIENT* c) { if (c->txSeqBuf.size() > 400) { - c->txSeqBuf.remove(0); + c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } c->txSeq++; c->txSeqBuf.insert(p.seq, s); @@ -1430,7 +1430,7 @@ void udpServer::dataForServer(QByteArray d) { if (client->txSeqBuf.size() > 400) { - client->txSeqBuf.remove(0); + client->txSeqBuf.remove(client->txSeqBuf.firstKey()); } client->txSeqBuf.insert(p.seq, s); client->txSeq++; @@ -1512,7 +1512,7 @@ void udpServer::receiveAudioData(const audioPacket& d) { if (client->txSeqBuf.size() > 400) { - client->txSeqBuf.remove(0); + client->txSeqBuf.remove(client->txSeqBuf.firstKey()); } client->txSeqBuf.insert(p.seq, s); client->txSeq++; @@ -1584,9 +1584,20 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (c->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { + { for (int i = 0; i < c->rxSeqBuf.keys().length() - 1; i++) { for (quint16 j = c->rxSeqBuf.keys()[i] + 1; j < c->rxSeqBuf.keys()[i + 1]; j++) { + + /* + if (c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() == 0 && c->type == "AUDIO" && + (c->txCodec == 0x40 || c->txCodec == 0x80)) + { + // Single missing audio packet ignore it! + qDebug(logUdpServer()) << "Single missing audio packet"; + c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. + break; + } + */ auto s = c->rxMissing.find(j); if (s == c->rxMissing.end()) { @@ -1596,9 +1607,9 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (c->rxSeqBuf.size() > 400) { - c->rxSeqBuf.remove(0); - c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. + c->rxSeqBuf.remove(c->rxSeqBuf.firstKey()); } + c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. } else { if (s.value() == 4) From 4696fe68246400feb28a23003fcc8a6d90f3869f Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 13 Jan 2022 21:50:43 +0000 Subject: [PATCH 050/323] Set buffer size with a #define --- packettypes.h | 2 ++ udphandler.cpp | 8 ++++---- udpserver.cpp | 16 ++++++++-------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packettypes.h b/packettypes.h index 46cad4c..ae19b3f 100644 --- a/packettypes.h +++ b/packettypes.h @@ -22,6 +22,8 @@ #define AUDIO_SIZE 0x18 #define DATA_SIZE 0x15 +#define BUFSIZE 50 // Number of packets to buffer + // 0x10 length control packet (connect/disconnect/idle.) typedef union control_packet { struct { diff --git a/udphandler.cpp b/udphandler.cpp index 476dff6..eaa58de 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1154,7 +1154,7 @@ void udpBase::dataReceived(QByteArray r) { rxBufferMutex.lock(); if (rxSeqBuf.isEmpty()) { - if (rxSeqBuf.size() > 400) + if (rxSeqBuf.size() > BUFSIZE) { rxSeqBuf.erase(rxSeqBuf.begin()); } @@ -1178,7 +1178,7 @@ void udpBase::dataReceived(QByteArray r) { // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. rxSeqBuf.insert(in->seq, QTime::currentTime()); - if (rxSeqBuf.size() > 400) + if (rxSeqBuf.size() > BUFSIZE) { rxSeqBuf.erase(rxSeqBuf.begin()); } @@ -1242,7 +1242,7 @@ void udpBase::sendRetransmitRequest() rxMissing.erase(rxMissing.begin()); } rxMissing.insert(j, 0); - if (rxSeqBuf.size() > 400) + if (rxSeqBuf.size() > BUFSIZE) { rxSeqBuf.erase(rxSeqBuf.begin()); } @@ -1388,7 +1388,7 @@ void udpBase::sendTrackedPacket(QByteArray d) congestion = 0; } txSeqBuf.insert(sendSeq,s); - if (txSeqBuf.size() > 400) + if (txSeqBuf.size() > BUFSIZE) { txSeqBuf.erase(txSeqBuf.begin()); } diff --git a/udpserver.cpp b/udpserver.cpp index 50cf933..bff231d 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -831,7 +831,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. if (current->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (current->rxSeqBuf.size() > 400) + if (current->rxSeqBuf.size() > BUFSIZE) { current->rxSeqBuf.remove(current->rxSeqBuf.firstKey()); } @@ -1126,7 +1126,7 @@ void udpServer::sendCapabilities(CLIENT* c) s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > 400) + if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } @@ -1197,7 +1197,7 @@ void udpServer::sendConnectionInfo(CLIENT* c) if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > 400) + if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } @@ -1255,7 +1255,7 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > 400) + if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } @@ -1373,7 +1373,7 @@ void udpServer::sendStatus(CLIENT* c) s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > 400) + if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } @@ -1428,7 +1428,7 @@ void udpServer::dataForServer(QByteArray d) if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (client->txSeqBuf.size() > 400) + if (client->txSeqBuf.size() > BUFSIZE) { client->txSeqBuf.remove(client->txSeqBuf.firstKey()); } @@ -1510,7 +1510,7 @@ void udpServer::receiveAudioData(const audioPacket& d) s.data = t; if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (client->txSeqBuf.size() > 400) + if (client->txSeqBuf.size() > BUFSIZE) { client->txSeqBuf.remove(client->txSeqBuf.firstKey()); } @@ -1605,7 +1605,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j << dec << missingTime.msecsTo(QTime::currentTime()) << "ms"; c->rxMissing.insert(j, 0); - if (c->rxSeqBuf.size() > 400) + if (c->rxSeqBuf.size() > BUFSIZE) { c->rxSeqBuf.remove(c->rxSeqBuf.firstKey()); } From ce0544ac6a6dabc2f32b40b8b5232da64a1183d8 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 13 Jan 2022 21:53:53 +0000 Subject: [PATCH 051/323] Ignore single missing audio packet --- udpserver.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index bff231d..461f225 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1588,16 +1588,18 @@ void udpServer::sendRetransmitRequest(CLIENT* c) for (int i = 0; i < c->rxSeqBuf.keys().length() - 1; i++) { for (quint16 j = c->rxSeqBuf.keys()[i] + 1; j < c->rxSeqBuf.keys()[i + 1]; j++) { - /* + if (c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() == 0 && c->type == "AUDIO" && (c->txCodec == 0x40 || c->txCodec == 0x80)) { // Single missing audio packet ignore it! - qDebug(logUdpServer()) << "Single missing audio packet"; + qDebug(logUdpServer()) << "Single missing audio packet will be handled by FEC (" << hex << j << ")"; c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. - break; + c->rxMutex.unlock(); + c->missMutex.unlock(); + return; } - */ + auto s = c->rxMissing.find(j); if (s == c->rxMissing.end()) { From e00b598fd9d592414ae4fe81f893dc2bfd7d6821 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 14:10:21 +0000 Subject: [PATCH 052/323] Hopefully improve retransmit search --- udphandler.cpp | 53 ++++++++++++++++++++------------------- udpserver.cpp | 67 +++++++++++++++++++++++++------------------------- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index eaa58de..fc7553e 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1230,34 +1230,37 @@ void udpBase::sendRetransmitRequest() qDebug(logUdp()) << "Missing Seq: size=" << rxSeqBuf.size() << "firstKey=" << rxSeqBuf.firstKey() << "lastKey=" << rxSeqBuf.lastKey() << "missing=" << rxSeqBuf.lastKey() - rxSeqBuf.firstKey() - rxSeqBuf.size() + 1; // We are missing packets so iterate through the buffer and add the missing ones to missing packet list missingMutex.lock(); - for (int i = 0; i < rxSeqBuf.keys().length() - 1; i++) { - for (quint16 j = rxSeqBuf.keys()[i] + 1; j < rxSeqBuf.keys()[i + 1]; j++) { - auto s = rxMissing.find(j); - if (s == rxMissing.end()) - { - // We haven't seen this missing packet before - qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << rxMissing.size() << "): " << j; - if (rxMissing.size() > 25) - { - rxMissing.erase(rxMissing.begin()); - } - rxMissing.insert(j, 0); - if (rxSeqBuf.size() > BUFSIZE) - { - rxSeqBuf.erase(rxSeqBuf.begin()); - } - rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. - packetsLost++; - } - else { - if (s.value() == 4) - { - // We have tried 4 times to request this packet, time to give up! - s = rxMissing.erase(s); - } + auto i = std::adjacent_find(rxSeqBuf.keys().begin(), rxSeqBuf.keys().end(), [](int l, int r) {return l + 1 < r; }); + while (i != rxSeqBuf.keys().end()) + { + quint16 j = 1 + *i; + + auto s = rxMissing.find(j); + if (s == rxMissing.end()) + { + // We haven't seen this missing packet before + qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << rxMissing.size() << "): " << j; + if (rxMissing.size() > 25) + { + rxMissing.erase(rxMissing.begin()); + } + rxMissing.insert(j, 0); + if (rxSeqBuf.size() > BUFSIZE) + { + rxSeqBuf.erase(rxSeqBuf.begin()); + } + rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. + packetsLost++; + } + else { + if (s.value() == 4) + { + // We have tried 4 times to request this packet, time to give up! + s = rxMissing.erase(s); } } + ++i; } missingMutex.unlock(); } diff --git a/udpserver.cpp b/udpserver.cpp index 461f225..d1dc439 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1584,43 +1584,45 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (c->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - for (int i = 0; i < c->rxSeqBuf.keys().length() - 1; i++) { - for (quint16 j = c->rxSeqBuf.keys()[i] + 1; j < c->rxSeqBuf.keys()[i + 1]; j++) { + { + auto i = std::adjacent_find(c->rxSeqBuf.keys().begin(), c->rxSeqBuf.keys().end(), [](int l, int r) {return l + 1 < r; }); + while (i != c->rxSeqBuf.keys().end()) + { + quint16 j = 1 + *i; - - if (c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() == 0 && c->type == "AUDIO" && - (c->txCodec == 0x40 || c->txCodec == 0x80)) - { - // Single missing audio packet ignore it! - qDebug(logUdpServer()) << "Single missing audio packet will be handled by FEC (" << hex << j << ")"; - c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. - c->rxMutex.unlock(); - c->missMutex.unlock(); - return; - } - - auto s = c->rxMissing.find(j); - if (s == c->rxMissing.end()) - { - // We haven't seen this missing packet before - qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j << dec << missingTime.msecsTo(QTime::currentTime()) << "ms"; - c->rxMissing.insert(j, 0); + if (c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() == 0 && c->type == "AUDIO" && + (c->txCodec == 0x40 || c->txCodec == 0x80)) + { + // Single missing audio packet ignore it! + qDebug(logUdpServer()) << "Single missing audio packet will be handled by FEC (" << hex << j << ")"; + c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer so it doesn't try to retransmit + c->missMutex.unlock(); + c->rxMutex.unlock(); + return; + } - if (c->rxSeqBuf.size() > BUFSIZE) - { - c->rxSeqBuf.remove(c->rxSeqBuf.firstKey()); - } - c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. + auto s = c->rxMissing.find(j); + if (s == c->rxMissing.end()) + { + // We haven't seen this missing packet before + qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j << dec << missingTime.msecsTo(QTime::currentTime()) << "ms"; + c->rxMissing.insert(j, 0); + + if (c->rxSeqBuf.size() > BUFSIZE) + { + c->rxSeqBuf.remove(c->rxSeqBuf.firstKey()); } - else { - if (s.value() == 4) - { - // We have tried 4 times to request this packet, time to give up! - s = c->rxMissing.erase(s); - } + c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. + } + else { + if (s.value() == 4) + { + // We have tried 4 times to request this packet, time to give up! + s = c->rxMissing.erase(s); } } + ++i; + } } else { @@ -1632,7 +1634,6 @@ void udpServer::sendRetransmitRequest(CLIENT* c) qInfo(logUdpServer()) << "Unable to lock rxMutex()"; } c->missMutex.unlock(); - } } From 317cbd640a7159b941ce13aa227480b415153825 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 15:49:35 +0000 Subject: [PATCH 053/323] Minor fix to retransmit handling --- udphandler.cpp | 6 +++++- udpserver.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index fc7553e..d32329f 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1235,6 +1235,11 @@ void udpBase::sendRetransmitRequest() while (i != rxSeqBuf.keys().end()) { quint16 j = 1 + *i; + ++i; + if (i == rxSeqBuf.keys().end()) + { + continue; + } auto s = rxMissing.find(j); if (s == rxMissing.end()) @@ -1260,7 +1265,6 @@ void udpBase::sendRetransmitRequest() s = rxMissing.erase(s); } } - ++i; } missingMutex.unlock(); } diff --git a/udpserver.cpp b/udpserver.cpp index d1dc439..a2fa8aa 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1589,7 +1589,11 @@ void udpServer::sendRetransmitRequest(CLIENT* c) while (i != c->rxSeqBuf.keys().end()) { quint16 j = 1 + *i; - + ++i; + if (i == c->rxSeqBuf.keys().end()) + { + continue; + } if (c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() == 0 && c->type == "AUDIO" && (c->txCodec == 0x40 || c->txCodec == 0x80)) { @@ -1621,7 +1625,6 @@ void udpServer::sendRetransmitRequest(CLIENT* c) s = c->rxMissing.erase(s); } } - ++i; } } From 94a89dea33c53f69f3dcb37f7c11f56941e062f9 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 16:00:05 +0000 Subject: [PATCH 054/323] Clear audio buffer if too many packets are delayed --- audiohandler.cpp | 15 ++++++++++++++- audiohandler.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 66191b8..87d89c0 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -439,13 +439,13 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) if (!isReady) { isReady = true; } + audioPacket packet; if (ringBuf->size()>0) { // Output buffer is ALWAYS 16 bit. //qDebug(logAudio()) << "Read: nFrames" << nFrames << "nBytes" << nBytes; while (sentlen < nBytes) { - audioPacket packet; if (!ringBuf->try_read(packet)) { qDebug(logAudio()) << "No more data available but buffer is not full! sentlen:" << sentlen << " nBytes:" << nBytes ; @@ -479,6 +479,7 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) } currentLatency = packet.time.msecsTo(QTime::currentTime()); } + delayedPackets++; } int send = qMin((int)nBytes - sentlen, packet.data.length()); @@ -510,6 +511,12 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) if (nBytes > sentlen) { memset(buffer+sentlen,0,nBytes-sentlen); } + + if (delayedPackets > 10) { + while (ringBuf->try_read(packet)); // Empty buffer + delayedPackets = 0; + } + #if defined(RTAUDIO) return 0; #elif defined(PORTAUDIO) @@ -751,6 +758,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Packet " << hex << packet.seq << " arrived too late (increase latency!) " << dec << packet.time.msecsTo(QTime::currentTime()) << "ms"; + delayedPackets++; // if (!ringBuf->try_read(packet)) // break; // currentLatency = packet.time.msecsTo(QTime::currentTime()); @@ -851,6 +859,11 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) ret = packet.data; //qDebug(logAudio()) << "Now radio format, length" << packet.data.length(); + if (delayedPackets > 10) { + while (ringBuf->try_read(packet)); // Empty buffer + delayedPackets = 0; + } + } diff --git a/audiohandler.h b/audiohandler.h index e67e2ef..cdc608b 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -195,6 +195,8 @@ private: quint8 radioSampleBits; quint8 radioChannels; + int delayedPackets=0; + QMapaudioBuffer; double resampleRatio; From 5a9342d9a9cdfd0c46251509056c9fce8d0c4ef4 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 16:21:32 +0000 Subject: [PATCH 055/323] Clear audio buffer if it's full --- audiohandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/audiohandler.cpp b/audiohandler.cpp index 87d89c0..b28350f 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -720,6 +720,8 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (!ringBuf->try_write(livePacket)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full! capacity:" << ringBuf->capacity() << "length" << ringBuf->size(); + while (ringBuf->try_read(inPacket)); // Empty buffer + return; } if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is"< Date: Fri, 14 Jan 2022 16:23:01 +0000 Subject: [PATCH 056/323] Set latency for tx and rx --- udpserver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/udpserver.cpp b/udpserver.cpp index a2fa8aa..6dbbcd9 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -324,6 +324,7 @@ void udpServer::controlReceived() outAudio.codec = current->txCodec; outAudio.samplerate = current->txSampleRate; outAudio.isinput = false; + inAudio.latency = current->txBufferLen; txaudio = new audioHandler(); txAudioThread = new QThread(this); From 4a200006eb357b9aa8f6eef3f99214dc4f7293c7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 18:57:20 +0000 Subject: [PATCH 057/323] Extra audio debugging --- audiohandler.cpp | 10 +++++++++- wfmain.cpp | 6 +++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index b28350f..38d50eb 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -63,7 +63,15 @@ bool audioHandler::init(audioSetup setupIn) if (isInitialized) { return false; } - + qDebug(logAudio()) << "Creating" << (setupIn.isinput ? "Input" : "Output") << "audio device:" << setupIn.name << + ", bits" << setupIn.bits << + ", codec" << setupIn.codec << + ", latency" << setupIn.latency << + ", localAFGain" << setupIn.localAFgain << + ", radioChan" << setupIn.radioChan << + ", resampleQuality" << setupIn.resampleQuality << + ", samplerate" << setupIn.samplerate << + ", uLaw" << setupIn.ulaw; /* 0x01 uLaw 1ch 8bit 0x02 PCM 1ch 8bit diff --git a/wfmain.cpp b/wfmain.cpp index 48b44b9..ac49c24 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1625,6 +1625,7 @@ void wfmain::loadSettings() ui->serverAudioPortText->setText(QString::number(serverConfig.audioPort)); serverRxSetup.isinput = true; + serverTxSetup.isinput = false; ui->serverRXAudioInputCombo->blockSignals(true); @@ -1644,7 +1645,8 @@ void wfmain::loadSettings() } ui->serverRXAudioInputCombo->blockSignals(false); - serverRxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); + serverRxSetup.resampleQuality = rxSetup.resampleQuality; + serverTxSetup.resampleQuality = serverRxSetup.resampleQuality; ui->serverTXAudioOutputCombo->blockSignals(true); serverTxSetup.name = settings->value("ServerAudioOutput", "").toString(); @@ -1663,8 +1665,6 @@ void wfmain::loadSettings() } ui->serverTXAudioOutputCombo->blockSignals(false); - serverTxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); - int row = 0; ui->serverUsersTable->setRowCount(0); From d9a611e118244216b1ecd7388ff239e5d13553ee Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 19:40:25 +0000 Subject: [PATCH 058/323] Set latency for tx and rx --- audiohandler.cpp | 20 +++++++++++--------- packettypes.h | 2 +- udpserver.cpp | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 38d50eb..a5f0d1d 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -63,15 +63,6 @@ bool audioHandler::init(audioSetup setupIn) if (isInitialized) { return false; } - qDebug(logAudio()) << "Creating" << (setupIn.isinput ? "Input" : "Output") << "audio device:" << setupIn.name << - ", bits" << setupIn.bits << - ", codec" << setupIn.codec << - ", latency" << setupIn.latency << - ", localAFGain" << setupIn.localAFgain << - ", radioChan" << setupIn.radioChan << - ", resampleQuality" << setupIn.resampleQuality << - ", samplerate" << setupIn.samplerate << - ", uLaw" << setupIn.ulaw; /* 0x01 uLaw 1ch 8bit 0x02 PCM 1ch 8bit @@ -95,6 +86,17 @@ bool audioHandler::init(audioSetup setupIn) setup.bits = 16; } + qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name << + ", bits" << setup.bits << + ", codec" << setup.codec << + ", latency" << setup.latency << + ", localAFGain" << setup.localAFgain << + ", radioChan" << setup.radioChan << + ", resampleQuality" << setup.resampleQuality << + ", samplerate" << setup.samplerate << + ", uLaw" << setup.ulaw; + + ringBuf = new wilt::Ring(setupIn.latency / 8 + 1); // Should be customizable. tempBuf.sent = 0; diff --git a/packettypes.h b/packettypes.h index ae19b3f..f75093e 100644 --- a/packettypes.h +++ b/packettypes.h @@ -343,7 +343,7 @@ typedef union capabilities_packet { quint32 baudrate; // 0x9c quint16 capf; // 0xa0 char unusedi; // 0xa2 - quint16 capg; // 0xa3 + quint16 txbuffer; // 0xa3 char unusedj[3]; // 0xa5 }; char packet[CAPABILITIES_SIZE]; diff --git a/udpserver.cpp b/udpserver.cpp index 6dbbcd9..bf16a21 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -324,7 +324,7 @@ void udpServer::controlReceived() outAudio.codec = current->txCodec; outAudio.samplerate = current->txSampleRate; outAudio.isinput = false; - inAudio.latency = current->txBufferLen; + outAudio.latency = current->txBufferLen; txaudio = new audioHandler(); txAudioThread = new QThread(this); From ed7d88eb1ec5aae0c0570bbadf5361fb1645e2ad Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 19:43:48 +0000 Subject: [PATCH 059/323] Fix that didn't work! --- packettypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packettypes.h b/packettypes.h index f75093e..1bb126a 100644 --- a/packettypes.h +++ b/packettypes.h @@ -343,7 +343,7 @@ typedef union capabilities_packet { quint32 baudrate; // 0x9c quint16 capf; // 0xa0 char unusedi; // 0xa2 - quint16 txbuffer; // 0xa3 + quint16 capg; // 0xa3 char unusedj[3]; // 0xa5 }; char packet[CAPABILITIES_SIZE]; From 2eace96be9754e7b403d0400dee058882378bc07 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 19:57:12 +0000 Subject: [PATCH 060/323] Set tx/rx gain in server --- wfmain.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wfmain.cpp b/wfmain.cpp index ac49c24..f5e7555 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1628,6 +1628,10 @@ void wfmain::loadSettings() serverTxSetup.isinput = false; + serverRxSetup.localAFgain = 255; + + serverTxSetup.localAFgain = 255; + ui->serverRXAudioInputCombo->blockSignals(true); serverRxSetup.name = settings->value("ServerAudioInput", "").toString(); qInfo(logGui()) << "Got Server Audio Input: " << serverRxSetup.name; From 918238835d664883fc3dd7f929d339d7bcd99e3e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 20:12:50 +0000 Subject: [PATCH 061/323] Catch excessive missing packets --- udphandler.cpp | 13 +++++++++++++ udpserver.cpp | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/udphandler.cpp b/udphandler.cpp index d32329f..a06933b 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1232,6 +1232,7 @@ void udpBase::sendRetransmitRequest() missingMutex.lock(); auto i = std::adjacent_find(rxSeqBuf.keys().begin(), rxSeqBuf.keys().end(), [](int l, int r) {return l + 1 < r; }); + int missCounter = 0; while (i != rxSeqBuf.keys().end()) { quint16 j = 1 + *i; @@ -1241,6 +1242,18 @@ void udpBase::sendRetransmitRequest() continue; } + missCounter++; + + if (missCounter > 20) { + // More than 20 packets missing, something horrific has happened! + qDebug(logUdp()) << this->metaObject()->className() << ": Too many missing packets, clearing buffer"; + rxSeqBuf.clear(); + rxMissing.clear(); + missingMutex.unlock(); + rxBufferMutex.unlock(); + return; + } + auto s = rxMissing.find(j); if (s == rxMissing.end()) { diff --git a/udpserver.cpp b/udpserver.cpp index bf16a21..29df2be 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1586,6 +1586,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) { if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { + int missCounter = 0; auto i = std::adjacent_find(c->rxSeqBuf.keys().begin(), c->rxSeqBuf.keys().end(), [](int l, int r) {return l + 1 < r; }); while (i != c->rxSeqBuf.keys().end()) { @@ -1606,6 +1607,17 @@ void udpServer::sendRetransmitRequest(CLIENT* c) return; } + missCounter++; + + if (missCounter > 20) { + // More than 20 packets missing, something horrific has happened! + qDebug(logUdpServer()) << ": Too many missing packets, clearing buffer"; + c->rxSeqBuf.clear(); + c->rxMissing.clear(); + c->missMutex.unlock(); + c->rxMutex.unlock(); + return; + } auto s = c->rxMissing.find(j); if (s == c->rxMissing.end()) { From b4884e773a059ce6245aa45e9474259a2dec90d1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 20:23:59 +0000 Subject: [PATCH 062/323] Ensure buffer is at least 1/2 filled before starting --- audiohandler.cpp | 15 ++++++++++++++- audiohandler.h | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index a5f0d1d..825d84e 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -449,6 +449,15 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) if (!isReady) { isReady = true; } + if (!audioBuffered) { +#if defined(RTAUDIO) + return 0; +#elif defined(PORTAUDIO) + return 0; +#else + return nBytes; +#endif + } audioPacket packet; if (ringBuf->size()>0) { @@ -458,7 +467,7 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) { if (!ringBuf->try_read(packet)) { - qDebug(logAudio()) << "No more data available but buffer is not full! sentlen:" << sentlen << " nBytes:" << nBytes ; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "buffer is empty, sentlen:" << sentlen << " nBytes:" << nBytes ; break; } currentLatency = packet.time.msecsTo(QTime::currentTime()); @@ -739,6 +748,10 @@ void audioHandler::incomingAudio(audioPacket inPacket) incomingAudio(inPacket); // Call myself again to run the packet a second time (FEC) } lastSentSeq = inPacket.seq; + if (ringBuf->size() > ringBuf->capacity() / 2) + { + audioBuffered = true; + } return; } diff --git a/audiohandler.h b/audiohandler.h index cdc608b..4df25bc 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -165,7 +165,7 @@ private: void reinit(); bool isInitialized=false; bool isReady = false; - + bool audioBuffered = false; #if defined(RTAUDIO) RtAudio* audio = Q_NULLPTR; int audioDevice = 0; From 7d5ec8a0658fa4fcc643748dfb935efdaff7f829 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 20:29:27 +0000 Subject: [PATCH 063/323] Check when audio has been buffered --- audiohandler.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 825d84e..adad9fd 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -743,13 +743,14 @@ void audioHandler::incomingAudio(audioPacket inPacket) return; } if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is"<size() > ringBuf->capacity() / 2) + if (!audioBuffered && ringBuf->size() > ringBuf->capacity() / 2) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio buffering complete, capacity:" <capacity() << ", used:" << ringBuf->size(); audioBuffered = true; } return; From dcb47127405efebebf579a086aec1506b245e911 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 20:41:56 +0000 Subject: [PATCH 064/323] Move buffering code --- audiohandler.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index adad9fd..a134ada 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -617,6 +617,11 @@ void audioHandler::incomingAudio(audioPacket inPacket) qDebug(logAudio()) << "Packet received when stream was not ready"; return; } + if (!audioBuffered && ringBuf->size() > ringBuf->capacity() / 2) + { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio buffering complete, capacity:" << ringBuf->capacity() << ", used:" << ringBuf->size(); + audioBuffered = true; + } audioPacket livePacket = inPacket; @@ -748,11 +753,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) incomingAudio(inPacket); // Call myself again to run the packet a second time (FEC) } lastSentSeq = inPacket.seq; - if (!audioBuffered && ringBuf->size() > ringBuf->capacity() / 2) - { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio buffering complete, capacity:" <capacity() << ", used:" << ringBuf->size(); - audioBuffered = true; - } return; } From 03799b2cda34735497bbd73a86d9f26ae6a9a5ef Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 20:52:25 +0000 Subject: [PATCH 065/323] More fixes --- audiohandler.cpp | 1 + packettypes.h | 1 + udphandler.h | 1 - udpserver.cpp | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index a134ada..9b5db5b 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -761,6 +761,7 @@ void audioHandler::changeLatency(const quint16 newSize) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency; setup.latency = newSize; delete ringBuf; + audioBuffered = false; ringBuf = new wilt::Ring(setup.latency / 8 + 1); // Should be customizable. } diff --git a/packettypes.h b/packettypes.h index 1bb126a..6c1e9d7 100644 --- a/packettypes.h +++ b/packettypes.h @@ -23,6 +23,7 @@ #define DATA_SIZE 0x15 #define BUFSIZE 50 // Number of packets to buffer +#define TXAUDIO_PERIOD 20 // 0x10 length control packet (connect/disconnect/idle.) typedef union control_packet { diff --git a/udphandler.h b/udphandler.h index 1a8e975..c077f30 100644 --- a/udphandler.h +++ b/udphandler.h @@ -28,7 +28,6 @@ #define TOKEN_RENEWAL 60000 #define PING_PERIOD 100 #define IDLE_PERIOD 100 -#define TXAUDIO_PERIOD 10 #define AREYOUTHERE_PERIOD 500 #define WATCHDOG_PERIOD 500 #define RETRANSMIT_PERIOD 100 diff --git a/udpserver.cpp b/udpserver.cpp index 29df2be..c2ec9e6 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -365,7 +365,7 @@ void udpServer::controlReceived() rxAudioTimer = new QTimer(); rxAudioTimer->setTimerType(Qt::PreciseTimer); connect(rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); - rxAudioTimer->start(20); + rxAudioTimer->start(TXAUDIO_PERIOD); } } From b8f6feacbf6cbd272b727ddf6833c7b801b4797e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 20:55:41 +0000 Subject: [PATCH 066/323] Try to send outgoing audio every 10ms --- packettypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packettypes.h b/packettypes.h index 6c1e9d7..943fbbb 100644 --- a/packettypes.h +++ b/packettypes.h @@ -23,7 +23,7 @@ #define DATA_SIZE 0x15 #define BUFSIZE 50 // Number of packets to buffer -#define TXAUDIO_PERIOD 20 +#define TXAUDIO_PERIOD 10 // 0x10 length control packet (connect/disconnect/idle.) typedef union control_packet { From 3abb4bc58464bdd8c24c47e8e2f826f8c0f40adc Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 14 Jan 2022 20:58:06 +0000 Subject: [PATCH 067/323] Revert "Try to send outgoing audio every 10ms" This reverts commit b8f6feacbf6cbd272b727ddf6833c7b801b4797e. --- packettypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packettypes.h b/packettypes.h index 943fbbb..6c1e9d7 100644 --- a/packettypes.h +++ b/packettypes.h @@ -23,7 +23,7 @@ #define DATA_SIZE 0x15 #define BUFSIZE 50 // Number of packets to buffer -#define TXAUDIO_PERIOD 10 +#define TXAUDIO_PERIOD 20 // 0x10 length control packet (connect/disconnect/idle.) typedef union control_packet { From a75c6e0fdf2025b1d0ef69b347b75941e1fd4493 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 15 Jan 2022 16:31:50 +0000 Subject: [PATCH 068/323] Zero audio to stop blip at startup --- audiohandler.cpp | 5 +- wfmain.cpp | 2 + wfview.vcxproj | 366 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 273 insertions(+), 100 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 9b5db5b..ea731fb 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -450,6 +450,7 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) isReady = true; } if (!audioBuffered) { + memset(buffer, 0, nBytes); #if defined(RTAUDIO) return 0; #elif defined(PORTAUDIO) @@ -528,8 +529,8 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) // fill the rest of the buffer with silence if (nBytes > sentlen) { - memset(buffer+sentlen,0,nBytes-sentlen); - } + memset(buffer + sentlen, 0, nBytes - sentlen); + } if (delayedPackets > 10) { while (ringBuf->try_read(packet)); // Empty buffer diff --git a/wfmain.cpp b/wfmain.cpp index f5e7555..09a9e54 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1460,6 +1460,8 @@ void wfmain::loadSettings() prefs.localAFgain = (unsigned char)settings->value("localAFgain", defPrefs.localAFgain).toUInt(); rxSetup.localAFgain = prefs.localAFgain; + txSetup.localAFgain = 255; + settings->endGroup(); // Misc. user settings (enable PTT, draw peaks, etc) diff --git a/wfview.vcxproj b/wfview.vcxproj index 21e1461..9357e18 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,8 +16,7 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild - + $(MSBuildProjectDirectory)\QtMsBuild v142 @@ -37,10 +36,7 @@ debug\ wfview - - - - + @@ -48,34 +44,8 @@ - - - - - - debug\ - debug\ - wfview - true - - - release\ - release\ - wfview - true - false - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - - + debug\debug\wfviewtruerelease\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport + .;..\qcustomplot;..\opus\include;resampler;release;/include;%(AdditionalIncludeDirectories) @@ -87,16 +57,14 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="62771f6";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="01ea44d";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - - + MultiThreadedDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -117,28 +85,9 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"62771f6\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"01ea44d\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h .;..\qcustomplot;..\opus\include;resampler;debug;/include;%(AdditionalIncludeDirectories) @@ -150,14 +99,13 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="62771f6";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="01ea44d";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -176,28 +124,9 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"62771f6\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"01ea44d\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -220,58 +149,192 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + Document true @@ -288,21 +351,117 @@ release\moc_predefs.h;%(Outputs) true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -336,16 +495,30 @@ - resources - resources - + + + + + + + + + + resourcesresources - style - style - + + + + + + + + + + stylestyle @@ -364,9 +537,6 @@ - - - - + \ No newline at end of file From 914477d1dcc72b4b93c80dac739132352cb3b6b5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 16 Jan 2022 18:47:13 +0000 Subject: [PATCH 069/323] Tidy code after merge --- audiohandler.cpp | 13 +++---------- packettypes.h | 5 ++--- udphandler.h | 8 -------- udpserver.cpp | 7 +------ udpserver.h | 4 ++-- wfmain.cpp | 6 +++--- wfmain.h | 2 +- 7 files changed, 12 insertions(+), 33 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 0eac89d..2ace4b7 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -407,7 +407,7 @@ void audioHandler::start() void audioHandler::setVolume(unsigned char volume) { - //this->volume = (qreal)volume/255.0; + this->volume = audiopot[volume]; qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; @@ -494,12 +494,6 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Packet " << hex << packet.seq << " arrived too late (increase output latency!) " << dec << packet.time.msecsTo(QTime::currentTime()) << "ms"; - while (currentLatency > setup.latency/2) { - if (!ringBuf->try_read(packet)) { - break; - } - currentLatency = packet.time.msecsTo(QTime::currentTime()); - } delayedPackets++; } @@ -534,6 +528,7 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) } if (delayedPackets > 10) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Too many delayed packets, flushing buffer"; while (ringBuf->try_read(packet)); // Empty buffer delayedPackets = 0; } @@ -788,9 +783,6 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) " arrived too late (increase latency!) " << dec << packet.time.msecsTo(QTime::currentTime()) << "ms"; delayedPackets++; - // if (!ringBuf->try_read(packet)) - // break; - // currentLatency = packet.time.msecsTo(QTime::currentTime()); } //qDebug(logAudio) << "Chunksize" << this->chunkSize << "Packet size" << packet.data.length(); @@ -889,6 +881,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) ret = packet.data; //qDebug(logAudio()) << "Now radio format, length" << packet.data.length(); if (delayedPackets > 10) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Too many delayed packets, flushing buffer"; while (ringBuf->try_read(packet)); // Empty buffer delayedPackets = 0; } diff --git a/packettypes.h b/packettypes.h index 6fd98aa..b21a4f1 100644 --- a/packettypes.h +++ b/packettypes.h @@ -9,12 +9,13 @@ #define TOKEN_RENEWAL 60000 #define PING_PERIOD 500 #define IDLE_PERIOD 100 -#define TXAUDIO_PERIOD 10 #define AREYOUTHERE_PERIOD 500 #define WATCHDOG_PERIOD 500 #define RETRANSMIT_PERIOD 100 // How often to attempt retransmit #define LOCK_PERIOD 10 // How long to try to lock mutex (ms) #define STALE_CONNECTION 15 // Not heard from in this many seconds +#define BUFSIZE 50 // Number of packets to buffer +#define TXAUDIO_PERIOD 20 // Fixed Size Packets @@ -35,8 +36,6 @@ #define AUDIO_SIZE 0x18 #define DATA_SIZE 0x15 -#define BUFSIZE 50 // Number of packets to buffer -#define TXAUDIO_PERIOD 20 // 0x10 length control packet (connect/disconnect/idle.) typedef union control_packet { diff --git a/udphandler.h b/udphandler.h index c077f30..3357f32 100644 --- a/udphandler.h +++ b/udphandler.h @@ -24,14 +24,6 @@ #include "audiohandler.h" #include "packettypes.h" -#define PURGE_SECONDS 10 -#define TOKEN_RENEWAL 60000 -#define PING_PERIOD 100 -#define IDLE_PERIOD 100 -#define AREYOUTHERE_PERIOD 500 -#define WATCHDOG_PERIOD 500 -#define RETRANSMIT_PERIOD 100 -#define LOCK_PERIOD 100 struct udpPreferences { QString ipAddress; diff --git a/udpserver.cpp b/udpserver.cpp index b171d36..c170917 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -12,14 +12,9 @@ udpServer::udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudi qInfo(logUdpServer()) << "Starting udp server"; } -void udpServer::init(SERVERCONFIG conf, audioSetup out, audioSetup in) +void udpServer::init() { - qInfo(logUdpServer()) << "Input audio device:" << in.name; - qInfo(logUdpServer()) << "Output audio device:" << out.name; - this->config = conf; - this->outAudio = out; - this->inAudio = in; srand(time(NULL)); // Generate random key timeStarted.start(); // Convoluted way to find the external IP address, there must be a better way???? diff --git a/udpserver.h b/udpserver.h index 253e491..e309923 100644 --- a/udpserver.h +++ b/udpserver.h @@ -60,11 +60,11 @@ class udpServer : public QObject Q_OBJECT public: - udpServer(); + udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudio); ~udpServer(); public slots: - void init(SERVERCONFIG sc, audioSetup ai, audioSetup ao); + void init(); void dataForServer(QByteArray); void receiveAudioData(const audioPacket &data); void receiveRigCaps(rigCapabilities caps); diff --git a/wfmain.cpp b/wfmain.cpp index df3c15b..53d4f79 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -966,14 +966,14 @@ void wfmain::setServerToPrefs() serverConfig.lan = prefs.enableLAN; qInfo(logAudio()) << "Audio Input device " << serverRxSetup.name; qInfo(logAudio()) << "Audio Output device " << serverTxSetup.name; - udp = new udpServer(); + udp = new udpServer(serverConfig, serverTxSetup, serverRxSetup); serverThread = new QThread(this); udp->moveToThread(serverThread); - connect(this, SIGNAL(initServer(SERVERCONFIG, audioSetup, audioSetup)), udp, SLOT(init(SERVERCONFIG, audioSetup, audioSetup))); + connect(this, SIGNAL(initServer()), udp, SLOT(init())); connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); if (rig != Q_NULLPTR) { @@ -988,7 +988,7 @@ void wfmain::setServerToPrefs() serverThread->start(); - emit initServer(serverConfig, serverTxSetup, serverRxSetup); + emit initServer(); connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); diff --git a/wfmain.h b/wfmain.h index 666e800..236af59 100644 --- a/wfmain.h +++ b/wfmain.h @@ -162,7 +162,7 @@ signals: void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); void sendCloseComm(); void sendChangeLatency(quint16 latency); - void initServer(SERVERCONFIG sc, audioSetup ai, audioSetup ao); + void initServer(); void sendRigCaps(rigCapabilities caps); void requestRigState(); void stateUpdated(); From b691398f6a9dcf93568697adbb527ba2dfb8ecd9 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 16 Jan 2022 18:54:41 +0000 Subject: [PATCH 070/323] Increase audio buffer size --- audiohandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 2ace4b7..4b87d36 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -98,7 +98,7 @@ bool audioHandler::init(audioSetup setupIn) ", uLaw" << setup.ulaw; - ringBuf = new wilt::Ring(setupIn.latency / 8 + 1); // Should be customizable. + ringBuf = new wilt::Ring(setupIn.latency / 4 + 1); // Should be customizable. tempBuf.sent = 0; @@ -759,7 +759,7 @@ void audioHandler::changeLatency(const quint16 newSize) setup.latency = newSize; delete ringBuf; audioBuffered = false; - ringBuf = new wilt::Ring(setup.latency / 8 + 1); // Should be customizable. + ringBuf = new wilt::Ring(setup.latency / 4 + 1); // Should be customizable. } int audioHandler::getLatency() From b87e0de05d73a26927c2182445d8528486dc5bfb Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 16 Jan 2022 19:04:44 +0000 Subject: [PATCH 071/323] Force refilling buffer once it has been flushed. --- audiohandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/audiohandler.cpp b/audiohandler.cpp index 4b87d36..cd17ef7 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -531,6 +531,7 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Too many delayed packets, flushing buffer"; while (ringBuf->try_read(packet)); // Empty buffer delayedPackets = 0; + audioBuffered = false; } #if defined(RTAUDIO) @@ -884,6 +885,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Too many delayed packets, flushing buffer"; while (ringBuf->try_read(packet)); // Empty buffer delayedPackets = 0; + audioBuffered = false; } } From 28812be8bef2544bf1d2fdc2155d722461ed34d7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 16 Jan 2022 19:17:32 +0000 Subject: [PATCH 072/323] Buffer for correct latency --- audiohandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index cd17ef7..fc3dc94 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -98,7 +98,7 @@ bool audioHandler::init(audioSetup setupIn) ", uLaw" << setup.ulaw; - ringBuf = new wilt::Ring(setupIn.latency / 4 + 1); // Should be customizable. + ringBuf = new wilt::Ring(setupIn.latency / 20 + 1); // Should be customizable. tempBuf.sent = 0; @@ -760,7 +760,7 @@ void audioHandler::changeLatency(const quint16 newSize) setup.latency = newSize; delete ringBuf; audioBuffered = false; - ringBuf = new wilt::Ring(setup.latency / 4 + 1); // Should be customizable. + ringBuf = new wilt::Ring(setup.latency / 20 + 1); // Should be customizable. } int audioHandler::getLatency() From 387b26187de42a78a33edb1d79f915736e9a4ccb Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 17 Jan 2022 17:23:55 +0000 Subject: [PATCH 073/323] Initial server commit --- audiohandler.cpp | 2 +- main.cpp | 57 +- servermain.cpp | 1825 ++++++++++++++++++++++++++++++++++++++ servermain.h | 510 +++++++++++ wfmain.h | 5 + wfserver.pro | 158 ++++ wfserver.vcxproj | 313 +++++++ wfserver.vcxproj.filters | 284 ++++++ wfserver.vcxproj.user | 10 + wfserver_resource.rc | 37 + wfview.sln | 6 + wfview.vcxproj | 8 +- 12 files changed, 3186 insertions(+), 29 deletions(-) create mode 100644 servermain.cpp create mode 100644 servermain.h create mode 100644 wfserver.pro create mode 100644 wfserver.vcxproj create mode 100644 wfserver.vcxproj.filters create mode 100644 wfserver.vcxproj.user create mode 100644 wfserver_resource.rc diff --git a/audiohandler.cpp b/audiohandler.cpp index fc3dc94..56d7b67 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -98,7 +98,7 @@ bool audioHandler::init(audioSetup setupIn) ", uLaw" << setup.ulaw; - ringBuf = new wilt::Ring(setupIn.latency / 20 + 1); // Should be customizable. + ringBuf = new wilt::Ring(setup.latency / 20 + 1); // Should be customizable. tempBuf.sent = 0; diff --git a/main.cpp b/main.cpp index bae6e35..0adc1ad 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,10 @@ +//#undef WFSERVER +#ifdef WFSERVER +#include +#else #include +#endif + #include #include "wfmain.h" #include "logcategories.h" @@ -14,7 +20,12 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt int main(int argc, char *argv[]) { +#ifdef WFSERVER + QCoreApplication a(argc, argv); +#else QApplication a(argc, argv); +#endif + //a.setStyle( "Fusion" ); a.setOrganizationName("wfview"); @@ -38,12 +49,19 @@ int main(int argc, char *argv[]) const QString helpText = QString("\nUsage: -p --port /dev/port, -h --host remotehostname, -c --civ 0xAddr, -l --logfile filename.log, -s --settings filename.ini, -d --debug, -v --version\n"); // TODO... +#ifdef WFSERVER + const QString version = QString("wfserver version: %1 (Git:%2 on %3 at %4 by %5@%6)\nOperating System: %7 (%8)\nBuild Qt Version %9. Current Qt Version: %10\n") + .arg(QString(WFVIEW_VERSION)) + .arg(GITSHORT).arg(__DATE__).arg(__TIME__).arg(UNAME).arg(HOST) + .arg(QSysInfo::prettyProductName()).arg(QSysInfo::buildCpuArchitecture()) + .arg(QT_VERSION_STR).arg(qVersion()); +#else const QString version = QString("wfview version: %1 (Git:%2 on %3 at %4 by %5@%6)\nOperating System: %7 (%8)\nBuild Qt Version %9. Current Qt Version: %10\n") - .arg(QString(WFVIEW_VERSION)) - .arg(GITSHORT).arg(__DATE__).arg(__TIME__).arg(UNAME).arg(HOST) - .arg(QSysInfo::prettyProductName()).arg(QSysInfo::buildCpuArchitecture()) - .arg(QT_VERSION_STR).arg(qVersion()); - + .arg(QString(WFVIEW_VERSION)) + .arg(GITSHORT).arg(__DATE__).arg(__TIME__).arg(UNAME).arg(HOST) + .arg(QSysInfo::prettyProductName()).arg(QSysInfo::buildCpuArchitecture()) + .arg(QT_VERSION_STR).arg(qVersion()); +#endif for(int c=1; cserialPortCL = serialPortCL; + this->hostCL = hostCL; + + qRegisterMetaType(); // Needs to be registered early. + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType(); + + //signal(SIGINT, handleCtrlC); + + haveRigCaps = false; + + setDefPrefs(); + + getSettingsFilePath(settingsFile); + + loadSettings(); // Look for saved preferences + + setInitialTiming(); + + openRig(); + + rigConnections(); + + setServerToPrefs(); + + amTransmitting = false; + +} + +servermain::~servermain() +{ + rigThread->quit(); + rigThread->wait(); + if (serverThread != Q_NULLPTR) { + serverThread->quit(); + serverThread->wait(); + } + if (rigCtl != Q_NULLPTR) { + delete rigCtl; + } + delete settings; + +#if defined(PORTAUDIO) + Pa_Terminate(); +#endif + +} + +void servermain::closeEvent(QCloseEvent *event) +{ + // Are you sure? +} + +void servermain::openRig() +{ + // This function is intended to handle opening a connection to the rig. + // the connection can be either serial or network, + // and this function is also responsible for initiating the search for a rig model and capabilities. + // Any errors, such as unable to open connection or unable to open port, are to be reported to the user. + + //TODO: if(hasRunPreviously) + + //TODO: if(useNetwork){... + + // } else { + + // if (prefs.fileWasNotFound) { + // showRigSettings(); // rig setting dialog box for network/serial, CIV, hostname, port, baud rate, serial device, etc + // TODO: How do we know if the setting was loaded? + + + // TODO: Use these if they are found + if(!serialPortCL.isEmpty()) + { + qDebug(logSystem()) << "Serial port specified by user: " << serialPortCL; + } else { + qDebug(logSystem()) << "Serial port not specified. "; + } + + if(!hostCL.isEmpty()) + { + qDebug(logSystem()) << "Remote host name specified by user: " << hostCL; + } + + + makeRig(); + + if (prefs.enableLAN) + { + usingLAN = true; + // We need to setup the tx/rx audio: + emit sendCommSetup(prefs.radioCIVAddr, udpPrefs, rxSetup, txSetup, prefs.virtualSerialPort); + } else { + if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) + { + findSerialPort(); + + } else { + if(serialPortCL.isEmpty()) + { + serialPortRig = prefs.serialPortRadio; + } else { + serialPortRig = serialPortCL; + } + } + usingLAN = false; + emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort); + } + + + +} + +void servermain::rigConnections() +{ + connect(this, SIGNAL(setCIVAddr(unsigned char)), rig, SLOT(setCIVAddr(unsigned char))); + + connect(this, SIGNAL(sendPowerOn()), rig, SLOT(powerOn())); + connect(this, SIGNAL(sendPowerOff()), rig, SLOT(powerOff())); + + connect(rig, SIGNAL(haveFrequency(freqt)), this, SLOT(receiveFreq(freqt))); + connect(this, SIGNAL(getFrequency()), rig, SLOT(getFrequency())); + connect(this, SIGNAL(getMode()), rig, SLOT(getMode())); + connect(this, SIGNAL(getDataMode()), rig, SLOT(getDataMode())); + connect(this, SIGNAL(setDataMode(bool, unsigned char)), rig, SLOT(setDataMode(bool, unsigned char))); + connect(this, SIGNAL(getBandStackReg(char,char)), rig, SLOT(getBandStackReg(char,char))); + connect(rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool))); + connect(this, SIGNAL(setPTT(bool)), rig, SLOT(setPTT(bool))); + connect(this, SIGNAL(getPTT()), rig, SLOT(getPTT())); + connect(rig, SIGNAL(haveBandStackReg(freqt,char,char,bool)), this, SLOT(receiveBandStackReg(freqt,char,char,bool))); + connect(this, SIGNAL(setRitEnable(bool)), rig, SLOT(setRitEnable(bool))); + connect(this, SIGNAL(setRitValue(int)), rig, SLOT(setRitValue(int))); + connect(rig, SIGNAL(haveRitEnabled(bool)), this, SLOT(receiveRITStatus(bool))); + connect(rig, SIGNAL(haveRitFrequency(int)), this, SLOT(receiveRITValue(int))); + connect(this, SIGNAL(getRitEnabled()), rig, SLOT(getRitEnabled())); + connect(this, SIGNAL(getRitValue()), rig, SLOT(getRitValue())); + + connect(this, SIGNAL(getDebug()), rig, SLOT(getDebug())); + + connect(this, SIGNAL(spectOutputDisable()), rig, SLOT(disableSpectOutput())); + connect(this, SIGNAL(spectOutputEnable()), rig, SLOT(enableSpectOutput())); + connect(this, SIGNAL(scopeDisplayDisable()), rig, SLOT(disableSpectrumDisplay())); + connect(this, SIGNAL(scopeDisplayEnable()), rig, SLOT(enableSpectrumDisplay())); + connect(rig, SIGNAL(haveDataMode(bool)), this, SLOT(receiveDataModeStatus(bool))); + + + connect(this, SIGNAL(getDuplexMode()), rig, SLOT(getDuplexMode())); + connect(this, SIGNAL(getTone()), rig, SLOT(getTone())); + connect(this, SIGNAL(getTSQL()), rig, SLOT(getTSQL())); + connect(this, SIGNAL(getRptAccessMode()), rig, SLOT(getRptAccessMode())); + //connect(this, SIGNAL(setDuplexMode(duplexMode)), rig, SLOT(setDuplexMode(duplexMode))); + //connect(rig, SIGNAL(haveDuplexMode(duplexMode)), this, SLOT(receiveDuplexMode(duplexMode))); + + connect(this, SIGNAL(getModInput(bool)), rig, SLOT(getModInput(bool))); + connect(rig, SIGNAL(haveModInput(rigInput,bool)), this, SLOT(receiveModInput(rigInput, bool))); + connect(this, SIGNAL(setModInput(rigInput, bool)), rig, SLOT(setModInput(rigInput,bool))); + + connect(rig, SIGNAL(haveSpectrumData(QByteArray, double, double)), this, SLOT(receiveSpectrumData(QByteArray, double, double))); + connect(rig, SIGNAL(haveSpectrumMode(spectrumMode)), this, SLOT(receiveSpectrumMode(spectrumMode))); + connect(this, SIGNAL(setScopeMode(spectrumMode)), rig, SLOT(setSpectrumMode(spectrumMode))); + connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode())); + + connect(this, SIGNAL(setFrequency(unsigned char, freqt)), rig, SLOT(setFrequency(unsigned char, freqt))); + connect(this, SIGNAL(setScopeEdge(char)), rig, SLOT(setScopeEdge(char))); + connect(this, SIGNAL(setScopeSpan(char)), rig, SLOT(setScopeSpan(char))); + //connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode())); + connect(this, SIGNAL(getScopeEdge()), rig, SLOT(getScopeEdge())); + connect(this, SIGNAL(getScopeSpan()), rig, SLOT(getScopeSpan())); + connect(rig, SIGNAL(haveScopeSpan(freqt,bool)), this, SLOT(receiveSpectrumSpan(freqt,bool))); + connect(this, SIGNAL(setScopeFixedEdge(double,double,unsigned char)), rig, SLOT(setSpectrumBounds(double,double,unsigned char))); + + connect(this, SIGNAL(setMode(unsigned char, unsigned char)), rig, SLOT(setMode(unsigned char, unsigned char))); + connect(this, SIGNAL(setMode(mode_info)), rig, SLOT(setMode(mode_info))); + + // Levels (read and write) + // Levels: Query: + connect(this, SIGNAL(getLevels()), rig, SLOT(getLevels())); + connect(this, SIGNAL(getRfGain()), rig, SLOT(getRfGain())); + connect(this, SIGNAL(getAfGain()), rig, SLOT(getAfGain())); + connect(this, SIGNAL(getSql()), rig, SLOT(getSql())); + connect(this, SIGNAL(getIfShift()), rig, SLOT(getIFShift())); + connect(this, SIGNAL(getTPBFInner()), rig, SLOT(getTPBFInner())); + connect(this, SIGNAL(getTPBFOuter()), rig, SLOT(getTPBFOuter())); + connect(this, SIGNAL(getTxPower()), rig, SLOT(getTxLevel())); + connect(this, SIGNAL(getMicGain()), rig, SLOT(getMicGain())); + connect(this, SIGNAL(getSpectrumRefLevel()), rig, SLOT(getSpectrumRefLevel())); + connect(this, SIGNAL(getModInputLevel(rigInput)), rig, SLOT(getModInputLevel(rigInput))); + + + // Levels: Set: + connect(this, SIGNAL(setRfGain(unsigned char)), rig, SLOT(setRfGain(unsigned char))); + connect(this, SIGNAL(setAfGain(unsigned char)), rig, SLOT(setAfGain(unsigned char))); + connect(this, SIGNAL(setSql(unsigned char)), rig, SLOT(setSquelch(unsigned char))); + connect(this, SIGNAL(setIFShift(unsigned char)), rig, SLOT(setIFShift(unsigned char))); + connect(this, SIGNAL(setTPBFInner(unsigned char)), rig, SLOT(setTPBFInner(unsigned char))); + connect(this, SIGNAL(setTPBFOuter(unsigned char)), rig, SLOT(setTPBFOuter(unsigned char))); + connect(this, SIGNAL(setTxPower(unsigned char)), rig, SLOT(setTxPower(unsigned char))); + connect(this, SIGNAL(setMicGain(unsigned char)), rig, SLOT(setMicGain(unsigned char))); + connect(this, SIGNAL(setMonitorLevel(unsigned char)), rig, SLOT(setMonitorLevel(unsigned char))); + connect(this, SIGNAL(setVoxGain(unsigned char)), rig, SLOT(setVoxGain(unsigned char))); + connect(this, SIGNAL(setAntiVoxGain(unsigned char)), rig, SLOT(setAntiVoxGain(unsigned char))); + connect(this, SIGNAL(setSpectrumRefLevel(int)), rig, SLOT(setSpectrumRefLevel(int))); + connect(this, SIGNAL(setModLevel(rigInput, unsigned char)), rig, SLOT(setModInputLevel(rigInput, unsigned char))); + + // Levels: handle return on query: + connect(rig, SIGNAL(haveRfGain(unsigned char)), this, SLOT(receiveRfGain(unsigned char))); + connect(rig, SIGNAL(haveAfGain(unsigned char)), this, SLOT(receiveAfGain(unsigned char))); + connect(rig, SIGNAL(haveSql(unsigned char)), this, SLOT(receiveSql(unsigned char))); + connect(rig, SIGNAL(haveTxPower(unsigned char)), this, SLOT(receiveTxPower(unsigned char))); + connect(rig, SIGNAL(haveMicGain(unsigned char)), this, SLOT(receiveMicGain(unsigned char))); + connect(rig, SIGNAL(haveSpectrumRefLevel(int)), this, SLOT(receiveSpectrumRefLevel(int))); + connect(rig, SIGNAL(haveACCGain(unsigned char,unsigned char)), this, SLOT(receiveACCGain(unsigned char,unsigned char))); + connect(rig, SIGNAL(haveUSBGain(unsigned char)), this, SLOT(receiveUSBGain(unsigned char))); + connect(rig, SIGNAL(haveLANGain(unsigned char)), this, SLOT(receiveLANGain(unsigned char))); + + //Metering: + connect(this, SIGNAL(getMeters(meterKind)), rig, SLOT(getMeters(meterKind))); + connect(rig, SIGNAL(haveMeter(meterKind,unsigned char)), this, SLOT(receiveMeter(meterKind,unsigned char))); + + // Rig and ATU info: + connect(this, SIGNAL(startATU()), rig, SLOT(startATU())); + connect(this, SIGNAL(setATU(bool)), rig, SLOT(setATU(bool))); + connect(this, SIGNAL(getATUStatus()), rig, SLOT(getATUStatus())); + connect(this, SIGNAL(getRigID()), rig, SLOT(getRigID())); + connect(rig, SIGNAL(haveATUStatus(unsigned char)), this, SLOT(receiveATUStatus(unsigned char))); + connect(rig, SIGNAL(haveRigID(rigCapabilities)), this, SLOT(receiveRigID(rigCapabilities))); + connect(this, SIGNAL(setAttenuator(unsigned char)), rig, SLOT(setAttenuator(unsigned char))); + connect(this, SIGNAL(setPreamp(unsigned char)), rig, SLOT(setPreamp(unsigned char))); + connect(this, SIGNAL(setAntenna(unsigned char, bool)), rig, SLOT(setAntenna(unsigned char, bool))); + connect(this, SIGNAL(getPreamp()), rig, SLOT(getPreamp())); + connect(rig, SIGNAL(havePreamp(unsigned char)), this, SLOT(receivePreamp(unsigned char))); + connect(this, SIGNAL(getAttenuator()), rig, SLOT(getAttenuator())); + connect(rig, SIGNAL(haveAttenuator(unsigned char)), this, SLOT(receiveAttenuator(unsigned char))); + connect(this, SIGNAL(getAntenna()), rig, SLOT(getAntenna())); + connect(rig, SIGNAL(haveAntenna(unsigned char,bool)), this, SLOT(receiveAntennaSel(unsigned char,bool))); + + + // Speech (emitted from rig speaker) + connect(this, SIGNAL(sayAll()), rig, SLOT(sayAll())); + connect(this, SIGNAL(sayFrequency()), rig, SLOT(sayFrequency())); + connect(this, SIGNAL(sayMode()), rig, SLOT(sayMode())); + + + // Date and Time: + connect(this, SIGNAL(setTime(timekind)), rig, SLOT(setTime(timekind))); + connect(this, SIGNAL(setDate(datekind)), rig, SLOT(setDate(datekind))); + connect(this, SIGNAL(setUTCOffset(timekind)), rig, SLOT(setUTCOffset(timekind))); + +} + + +void servermain::makeRig() +{ + if (rigThread == Q_NULLPTR) + { + rig = new rigCommander(); + rigThread = new QThread(this); + + // Thread: + rig->moveToThread(rigThread); + connect(rigThread, SIGNAL(started()), rig, SLOT(process())); + connect(rigThread, SIGNAL(finished()), rig, SLOT(deleteLater())); + rigThread->start(); + + // Rig status and Errors: + connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); + connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString))); + + // Rig comm setup: + connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); + connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString))); + connect(this, SIGNAL(setRTSforPTT(bool)), rig, SLOT(setRTSforPTT(bool))); + + connect(rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); + + connect(this, SIGNAL(sendCloseComm()), rig, SLOT(closeComm())); + connect(this, SIGNAL(sendChangeLatency(quint16)), rig, SLOT(changeLatency(quint16))); + connect(this, SIGNAL(getRigCIV()), rig, SLOT(findRigs())); + connect(this, SIGNAL(setRigID(unsigned char)), rig, SLOT(setRigID(unsigned char))); + connect(rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities))); + connect(rig, SIGNAL(commReady()), this, SLOT(receiveCommReady())); + + connect(this, SIGNAL(requestRigState()), rig, SLOT(sendState())); + connect(this, SIGNAL(stateUpdated()), rig, SLOT(stateUpdated())); + connect(rig, SIGNAL(stateInfo(rigstate*)), this, SLOT(receiveStateInfo(rigstate*))); + if (rigCtl != Q_NULLPTR) { + connect(rig, SIGNAL(stateInfo(rigstate*)), rigCtl, SLOT(receiveStateInfo(rigstate*))); + connect(rigCtl, SIGNAL(stateUpdated()), rig, SLOT(stateUpdated())); + } + } +} + +void servermain::removeRig() +{ + if (rigThread != Q_NULLPTR) + { + if (rigCtl != Q_NULLPTR) { + rigCtl->disconnect(); + } + rigThread->disconnect(); + + rig->disconnect(); + + delete rigThread; + delete rig; + rig = Q_NULLPTR; + } + +} + + +void servermain::findSerialPort() +{ + // Find the ICOM radio connected, or, if none, fall back to OS default. + // qInfo(logSystem()) << "Searching for serial port..."; + QDirIterator it73("/dev/serial/by-id", QStringList() << "*IC-7300*", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it97("/dev/serial", QStringList() << "*IC-9700*A*", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it785x("/dev/serial", QStringList() << "*IC-785*A*", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it705("/dev/serial", QStringList() << "*IC-705*A", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it7610("/dev/serial", QStringList() << "*IC-7610*A", QDir::Files, QDirIterator::Subdirectories); + QDirIterator itR8600("/dev/serial", QStringList() << "*IC-R8600*A", QDir::Files, QDirIterator::Subdirectories); + + if(!it73.filePath().isEmpty()) + { + // IC-7300 + serialPortRig = it73.filePath(); // first + } else if(!it97.filePath().isEmpty()) + { + // IC-9700 + serialPortRig = it97.filePath(); + } else if(!it785x.filePath().isEmpty()) + { + // IC-785x + serialPortRig = it785x.filePath(); + } else if(!it705.filePath().isEmpty()) + { + // IC-705 + serialPortRig = it705.filePath(); + } else if(!it7610.filePath().isEmpty()) + { + // IC-7610 + serialPortRig = it7610.filePath(); + } else if(!itR8600.filePath().isEmpty()) + { + // IC-R8600 + serialPortRig = itR8600.filePath(); + } else { + //fall back: + qInfo(logSystem()) << "Could not find Icom serial port. Falling back to OS default. Use --port to specify, or modify preferences."; +#ifdef Q_OS_MAC + serialPortRig = QString("/dev/tty.SLAB_USBtoUART"); +#endif +#ifdef Q_OS_LINUX + serialPortRig = QString("/dev/ttyUSB0"); +#endif +#ifdef Q_OS_WIN + serialPortRig = QString("COM1"); +#endif + } +} + +void servermain::receiveCommReady() +{ + qInfo(logSystem()) << "Received CommReady!! "; + if(!usingLAN) + { + // usingLAN gets set when we emit the sendCommSetup signal. + // If we're not using the LAN, then we're on serial, and + // we already know the baud rate and can calculate the timing parameters. + calculateTimingParameters(); + } + if(prefs.radioCIVAddr == 0) + { + // tell rigCommander to broadcast a request for all rig IDs. + // qInfo(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)"; + emit getRigCIV(); + issueDelayedCommand(cmdGetRigCIV); + delayedCommand->start(); + } else { + // don't bother, they told us the CIV they want, stick with it. + // We still query the rigID to find the model, but at least we know the CIV. + qInfo(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << prefs.radioCIVAddr; + if(prefs.CIVisRadioModel) + { + qInfo(logSystem()) << "Skipping Rig ID query, using user-supplied model from CI-V address: " << prefs.radioCIVAddr; + emit setRigID(prefs.radioCIVAddr); + } else { + emit getRigID(); + getInitialRigState(); + } + } +} + + +void servermain::receiveFoundRigID(rigCapabilities rigCaps) +{ + // Entry point for unknown rig being identified at the start of the program. + //now we know what the rig ID is: + //qInfo(logSystem()) << "In wfview, we now have a reply to our request for rig identity sent to CIV BROADCAST."; + + if(rig->usingLAN()) + { + usingLAN = true; + } else { + usingLAN = false; + } + + receiveRigID(rigCaps); + getInitialRigState(); + + return; +} + +void servermain::receiveSerialPortError(QString port, QString errorText) +{ + qInfo(logSystem()) << "servermain: received serial port error for port: " << port << " with message: " << errorText; + + // TODO: Dialog box, exit, etc +} + + +void servermain::getSettingsFilePath(QString settingsFile) +{ + if (settingsFile.isNull()) { + settings = new QSettings(); + } + else + { + QString file = settingsFile; + QFile info(settingsFile); + QString path=""; + if (!QFileInfo(info).isAbsolute()) + { + path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + if (path.isEmpty()) + { + path = QDir::homePath(); + } + path = path + "/"; + file = info.fileName(); + } + + qInfo(logSystem()) << "Loading settings from:" << path + file; + settings = new QSettings(path + file, QSettings::Format::IniFormat); + } +} + + +void servermain::setInitialTiming() +{ + loopTickCounter = 0; + delayedCmdIntervalLAN_ms = 70; // interval for regular delayed commands, including initial rig/UI state queries + delayedCmdIntervalSerial_ms = 100; // interval for regular delayed commands, including initial rig/UI state queries + delayedCmdStartupInterval_ms = 250; // interval for rigID polling + delayedCommand = new QTimer(this); + delayedCommand->setInterval(delayedCmdStartupInterval_ms); // 250ms until we find rig civ and id, then 100ms. + delayedCommand->setSingleShot(false); + connect(delayedCommand, SIGNAL(timeout()), this, SLOT(sendRadioCommandLoop())); + + // TODO: Remove this: +// periodicPollingTimer = new QTimer(this); +// periodicPollingTimer->setInterval(10); +// periodicPollingTimer->setSingleShot(false); + //connect(periodicPollingTimer, SIGNAL(timeout()), this, SLOT(sendRadioCommandLoop())); + + pttTimer = new QTimer(this); + pttTimer->setInterval(180*1000); // 3 minute max transmit time in ms + pttTimer->setSingleShot(true); + connect(pttTimer, SIGNAL(timeout()), this, SLOT(handlePttLimit())); + + timeSync = new QTimer(this); + connect(timeSync, SIGNAL(timeout()), this, SLOT(setRadioTimeDateSend())); + waitingToSetTimeDate = false; + lastFreqCmdTime_ms = QDateTime::currentMSecsSinceEpoch() - 5000; // 5 seconds ago +} + +void servermain::setServerToPrefs() +{ + + // Start server if enabled in config + if (serverThread != Q_NULLPTR) { + serverThread->quit(); + serverThread->wait(); + serverThread = Q_NULLPTR; + udp = Q_NULLPTR; + } + + if (serverConfig.enabled) { + serverConfig.lan = prefs.enableLAN; + qInfo(logAudio()) << "Audio Input device " << serverRxSetup.name; + qInfo(logAudio()) << "Audio Output device " << serverTxSetup.name; + udp = new udpServer(serverConfig, serverTxSetup, serverRxSetup); + + serverThread = new QThread(this); + + udp->moveToThread(serverThread); + + + connect(this, SIGNAL(initServer()), udp, SLOT(init())); + connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); + + if (rig != Q_NULLPTR) { + connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); + connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); + connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); + } + + if (!prefs.enableLAN) { + connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(receiveStatusUpdate(QString))); + } + + serverThread->start(); + + emit initServer(); + + connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); + + } +} + + +void servermain::setDefPrefs() +{ + defPrefs.useFullScreen = false; + defPrefs.useDarkMode = true; + defPrefs.useSystemTheme = false; + defPrefs.drawPeaks = true; + defPrefs.wfAntiAlias = false; + defPrefs.wfInterpolate = true; + defPrefs.radioCIVAddr = 0x00; // previously was 0x94 for 7300. + defPrefs.CIVisRadioModel = false; + defPrefs.forceRTSasPTT = false; + defPrefs.serialPortRadio = QString("auto"); + defPrefs.serialPortBaud = 115200; + defPrefs.enablePTT = false; + defPrefs.niceTS = true; + defPrefs.enableRigCtlD = false; + defPrefs.rigCtlPort = 4533; + defPrefs.virtualSerialPort = QString("none"); + defPrefs.localAFgain = 255; + defPrefs.wflength = 160; + defPrefs.confirmExit = true; + defPrefs.confirmPowerOff = true; + defPrefs.meter2Type = meterNone; + + udpDefPrefs.ipAddress = QString(""); + udpDefPrefs.controlLANPort = 50001; + udpDefPrefs.serialLANPort = 50002; + udpDefPrefs.audioLANPort = 50003; + udpDefPrefs.username = QString(""); + udpDefPrefs.password = QString(""); + udpDefPrefs.clientName = QHostInfo::localHostName(); +} + +void servermain::loadSettings() +{ + qInfo(logSystem()) << "Loading settings from " << settings->fileName(); + + // Radio and Comms: C-IV addr, port to use + settings->beginGroup("Radio"); + prefs.radioCIVAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); + prefs.CIVisRadioModel = (bool)settings->value("CIVisRadioModel", defPrefs.CIVisRadioModel).toBool(); + prefs.forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool(); + prefs.serialPortRadio = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString(); + prefs.serialPortBaud = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); + + if (prefs.serialPortBaud > 0) + { + serverConfig.baudRate = prefs.serialPortBaud; + } + + prefs.virtualSerialPort = settings->value("VirtualSerialPort", defPrefs.virtualSerialPort).toString(); + + prefs.localAFgain = (unsigned char)settings->value("localAFgain", defPrefs.localAFgain).toUInt(); + rxSetup.localAFgain = prefs.localAFgain; + txSetup.localAFgain = 255; + + settings->endGroup(); + + // Misc. user settings (enable PTT, draw peaks, etc) + settings->beginGroup("Controls"); + prefs.enablePTT = settings->value("EnablePTT", defPrefs.enablePTT).toBool(); + prefs.niceTS = settings->value("NiceTS", defPrefs.niceTS).toBool(); + settings->endGroup(); + + settings->beginGroup("LAN"); + + prefs.enableLAN = settings->value("EnableLAN", defPrefs.enableLAN).toBool(); + + prefs.enableRigCtlD = settings->value("EnableRigCtlD", defPrefs.enableRigCtlD).toBool(); + prefs.rigCtlPort = settings->value("RigCtlPort", defPrefs.rigCtlPort).toInt(); + udpPrefs.ipAddress = settings->value("IPAddress", udpDefPrefs.ipAddress).toString(); + udpPrefs.controlLANPort = settings->value("ControlLANPort", udpDefPrefs.controlLANPort).toInt(); + udpPrefs.username = settings->value("Username", udpDefPrefs.username).toString(); + udpPrefs.password = settings->value("Password", udpDefPrefs.password).toString(); + + rxSetup.isinput = false; + txSetup.isinput = true; + + rxSetup.latency = settings->value("AudioRXLatency", "150").toInt(); + txSetup.latency = settings->value("AudioTXLatency", "150").toInt(); + rxSetup.samplerate = settings->value("AudioRXSampleRate", "48000").toInt(); + txSetup.samplerate = rxSetup.samplerate; + rxSetup.codec = settings->value("AudioRXCodec", "4").toInt(); + txSetup.codec = settings->value("AudioTXCodec", "4").toInt(); + rxSetup.name = settings->value("AudioOutput", "").toString(); + qInfo(logGui()) << "Got Audio Output: " << rxSetup.name; + + txSetup.name = settings->value("AudioInput", "").toString(); + // qInfo(logGui()) << "Got Audio Input: " << txSetup.name; int audioInputIndex = ui->audioInputCombo->findText(txSetup.name); + + rxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); + txSetup.resampleQuality = rxSetup.resampleQuality; + + udpPrefs.clientName = settings->value("ClientName", udpDefPrefs.clientName).toString(); + + settings->endGroup(); + + + settings->beginGroup("Server"); + serverConfig.enabled = settings->value("ServerEnabled", false).toBool(); + serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt(); + serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt(); + serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt(); + int numUsers = settings->value("ServerNumUsers", 2).toInt(); + serverConfig.users.clear(); + for (int f = 0; f < numUsers; f++) + { + SERVERUSER user; + user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); + user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); + user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); + serverConfig.users.append(user); + } + + serverRxSetup.isinput = true; + serverTxSetup.isinput = false; + serverRxSetup.localAFgain = 255; + serverTxSetup.localAFgain = 255; + + serverRxSetup.name = settings->value("ServerAudioInput", "").toString(); + qInfo(logGui()) << "Got Server Audio Input: " << serverRxSetup.name; + + serverRxSetup.resampleQuality = rxSetup.resampleQuality; + serverTxSetup.resampleQuality = serverRxSetup.resampleQuality; + + serverTxSetup.name = settings->value("ServerAudioOutput", "").toString(); + qInfo(logGui()) << "Got Server Audio Output: " << serverTxSetup.name; + + settings->endGroup(); + + // Memory channels + + settings->beginGroup("Memory"); + int size = settings->beginReadArray("Channel"); + int chan = 0; + double freq; + unsigned char mode; + bool isSet; + + // Annoying: QSettings will write the array to the + // preference file starting the array at 1 and ending at 100. + // Thus, we are writing the channel number each time. + // It is also annoying that they get written with their array + // numbers in alphabetical order without zero padding. + // Also annoying that the preference groups are not written in + // the order they are specified here. + + for (int i = 0; i < size; i++) + { + settings->setArrayIndex(i); + chan = settings->value("chan", 0).toInt(); + freq = settings->value("freq", 12.345).toDouble(); + mode = settings->value("mode", 0).toInt(); + isSet = settings->value("isSet", false).toBool(); + + if (isSet) + { + mem.setPreset(chan, freq, (mode_kind)mode); + } + } + + settings->endArray(); + settings->endGroup(); + + + + + +#if defined(RTAUDIO) + +#if defined(Q_OS_LINUX) + RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); +#elif defined(Q_OS_WIN) + RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); +#elif defined(Q_OS_MACX) + RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE); +#endif + + // Enumerate audio devices, need to do before settings are loaded. + std::map apiMap; + apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; + apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; + apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound"; + apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI"; + apiMap[RtAudio::UNIX_JACK] = "Jack Client"; + apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; + apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; + apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; + apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; + + std::vector< RtAudio::Api > apis; + RtAudio::getCompiledApi(apis); + + qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion()); + + qInfo(logAudio()) << "Compiled APIs:"; + for (unsigned int i = 0; i < apis.size(); i++) { + qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]); + } + + RtAudio::DeviceInfo info; + + qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]); + + unsigned int devices = audio->getDeviceCount(); + qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; + + for (unsigned int i = 1; i < devices; i++) { + info = audio->getDeviceInfo(i); + if (info.outputChannels > 0) { + if (QString::fromStdString(info.name) == rxSetup.name) { + rxSetup.port = i; + } + if (QString::fromStdString(info.name) == serverTxSetup.name) { + serverTxSetup.port = i; + } + } + if (info.inputChannels > 0) { + if (QString::fromStdString(info.name) == txSetup.name) { + txSetup.port = i; + } + if (QString::fromStdString(info.name) == serverRxSetup.name) { + serverRxSetup.port = i; + } + } + } + + delete audio; + +#elif defined(PORTAUDIO) + // Use PortAudio device enumeration + + PaError err; + + err = Pa_Initialize(); + + if (err != paNoError) + { + qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio"; + } + + qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText; + + int numDevices; + numDevices = Pa_GetDeviceCount(); + qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; + + const PaDeviceInfo* info; + for (int i = 0; i < numDevices; i++) + { + info = Pa_GetDeviceInfo(i); + if (info->maxInputChannels > 0) { + if (QString::fromStdString(info.name) == rxSetup.name) { + rxSetup.port = i; + } + if (QString::fromStdString(info.name) == serverTxSetup.name) { + serverTxSetup.port = i; + } + } + if (info->maxOutputChannels > 0) { + if (QString::fromStdString(info.name) == txSetup.name) { + txSetup.port = i; + } + if (QString::fromStdString(info.name) == serverRxSetup.name) { + serverRxSetup.port = i; + } + } + } +#else + + // If no external library is configured, use QTMultimedia + // + // Set these to default audio devices initially. + rxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); + txSetup.port = QAudioDeviceInfo::defaultInputDevice(); + serverRxSetup.port = QAudioDeviceInfo::defaultInputDevice(); + serverTxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); + + + // Enumerate audio devices, need to do before settings are loaded. + const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); + for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { + if (deviceInfo.deviceName() == rxSetup.name) { + rxSetup.port = deviceInfo; + qInfo(logGui()) << "Setting Audio Output: " << rxSetup.name; + } + if (deviceInfo.deviceName() == serverTxSetup.name) { + serverTxSetup.port = deviceInfo; + qInfo(logGui()) << "Setting Server Audio Input: " << serverTxSetup.name; + } + } + + const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + for (const QAudioDeviceInfo& deviceInfo : audioInputs) { + if (deviceInfo.deviceName() == txSetup.name) { + txSetup.port = deviceInfo; + qInfo(logGui()) << "Setting Audio Input: " << txSetup.name; + } + if (deviceInfo.deviceName() == serverRxSetup.name) { + serverRxSetup.port = deviceInfo; + qInfo(logGui()) << "Setting Server Audio Output: " << serverRxSetup.name; + } + } + +#endif + +} + + +quint64 servermain::roundFrequency(quint64 frequency, unsigned int tsHz) +{ + return frequency; +} + +quint64 servermain::roundFrequencyWithStep(quint64 frequency, int steps, unsigned int tsHz) +{ + quint64 rounded = 0; + + if(steps > 0) + { + frequency = frequency + (quint64)(steps*tsHz); + } else { + frequency = frequency - std::min((quint64)(abs(steps)*tsHz), frequency); + } + + return frequency; +} + + +void servermain:: getInitialRigState() +{ + // Initial list of queries to the radio. + // These are made when the program starts up + // and are used to adjust the UI to match the radio settings + // the polling interval is set at 200ms. Faster is possible but slower + // computers will glitch occassionally. + + issueDelayedCommand(cmdGetFreq); + issueDelayedCommand(cmdGetMode); + + issueDelayedCommand(cmdNone); + + issueDelayedCommand(cmdGetFreq); + issueDelayedCommand(cmdGetMode); + + // From left to right in the UI: + if (rigCaps.hasTransmit) + { + issueDelayedCommand(cmdGetDataMode); + issueDelayedCommand(cmdGetModInput); + issueDelayedCommand(cmdGetModDataInput); + } + issueDelayedCommand(cmdGetRxGain); + issueDelayedCommand(cmdGetAfGain); + issueDelayedCommand(cmdGetSql); + + if (rigCaps.hasTransmit) + { + issueDelayedCommand(cmdGetTxPower); + issueDelayedCommand(cmdGetCurrentModLevel); // level for currently selected mod sources + } + + issueDelayedCommand(cmdGetSpectrumRefLevel); + issueDelayedCommand(cmdGetDuplexMode); + + if(rigCaps.hasSpectrum) + { + issueDelayedCommand(cmdDispEnable); + issueDelayedCommand(cmdSpecOn); + } + + if (rigCaps.hasTransmit) + { + issueDelayedCommand(cmdGetModInput); + issueDelayedCommand(cmdGetModDataInput); + } + + if(rigCaps.hasCTCSS) + { + issueDelayedCommand(cmdGetTone); + issueDelayedCommand(cmdGetTSQL); + } + if(rigCaps.hasDTCS) + { + issueDelayedCommand(cmdGetDTCS); + } + issueDelayedCommand(cmdGetRptAccessMode); + + if(rigCaps.hasAntennaSel) + { + issueDelayedCommand(cmdGetAntenna); + } + if(rigCaps.hasAttenuator) + { + issueDelayedCommand(cmdGetAttenuator); + } + if(rigCaps.hasPreamp) + { + issueDelayedCommand(cmdGetPreamp); + } + + issueDelayedCommand(cmdGetRitEnabled); + issueDelayedCommand(cmdGetRitValue); + + if(rigCaps.hasIFShift) + issueDelayedCommand(cmdGetIFShift); + if(rigCaps.hasTBPF) + { + issueDelayedCommand(cmdGetTPBFInner); + issueDelayedCommand(cmdGetTPBFOuter); + } + + if(rigCaps.hasSpectrum) + { + issueDelayedCommand(cmdGetSpectrumMode); + issueDelayedCommand(cmdGetSpectrumSpan); + } + + issueDelayedCommand(cmdNone); + issueDelayedCommand(cmdStartRegularPolling); + + if(rigCaps.hasATU) + { + issueDelayedCommand(cmdGetATUStatus); + } + + delayedCommand->start(); +} + + +void servermain::doCmd(commandtype cmddata) +{ + cmds cmd = cmddata.cmd; + std::shared_ptr data = cmddata.data; + + // This switch is for commands with parameters. + // the "default" for non-parameter commands is to call doCmd(cmd). + switch (cmd) + { + case cmdSetFreq: + { + lastFreqCmdTime_ms = QDateTime::currentMSecsSinceEpoch(); + freqt f = (*std::static_pointer_cast(data)); + emit setFrequency(0,f); + break; + } + case cmdSetMode: + { + mode_info m = (*std::static_pointer_cast(data)); + emit setMode(m); + break; + } + case cmdSetTxPower: + { + unsigned char txpower = (*std::static_pointer_cast(data)); + emit setTxPower(txpower); + break; + } + case cmdSetMicGain: + { + unsigned char micgain = (*std::static_pointer_cast(data)); + emit setTxPower(micgain); + break; + } + case cmdSetRxRfGain: + { + unsigned char rfgain = (*std::static_pointer_cast(data)); + emit setRfGain(rfgain); + break; + } + case cmdSetModLevel: + { + unsigned char modlevel = (*std::static_pointer_cast(data)); + rigInput currentIn; + if(usingDataMode) + { + currentIn = currentModDataSrc; + } else { + currentIn = currentModSrc; + } + emit setModLevel(currentIn, modlevel); + break; + } + case cmdSetAfGain: + { + unsigned char afgain = (*std::static_pointer_cast(data)); + emit setAfGain(afgain); + break; + } + case cmdSetSql: + { + unsigned char sqlLevel = (*std::static_pointer_cast(data)); + emit setSql(sqlLevel); + break; + } + case cmdSetIFShift: + { + unsigned char IFShiftLevel = (*std::static_pointer_cast(data)); + emit setIFShift(IFShiftLevel); + break; + } + case cmdSetTPBFInner: + { + unsigned char innterLevel = (*std::static_pointer_cast(data)); + emit setTPBFInner(innterLevel); + break; + } + case cmdSetTPBFOuter: + { + unsigned char outerLevel = (*std::static_pointer_cast(data)); + emit setTPBFOuter(outerLevel); + break; + } + case cmdSetPTT: + { + bool pttrequest = (*std::static_pointer_cast(data)); + emit setPTT(pttrequest); + break; + } + case cmdSetATU: + { + bool atuOn = (*std::static_pointer_cast(data)); + emit setATU(atuOn); + break; + } + case cmdSetUTCOffset: + { + timekind u = (*std::static_pointer_cast(data)); + emit setUTCOffset(u); + break; + } + case cmdSetTime: + { + timekind t = (*std::static_pointer_cast(data)); + emit setTime(t); + break; + } + case cmdSetDate: + { + datekind d = (*std::static_pointer_cast(data)); + emit setDate(d); + break; + } + default: + doCmd(cmd); + break; + } + +} + + +void servermain::doCmd(cmds cmd) +{ + +} + + +void servermain::sendRadioCommandLoop() +{ + // Called by the periodicPollingTimer, see setInitialTiming() + + if(!(loopTickCounter % 2)) + { + // if ther's a command waiting, run it. + if(!delayedCmdQue.empty()) + { + commandtype cmddata = delayedCmdQue.front(); + delayedCmdQue.pop_front(); + doCmd(cmddata); + } else if(!(loopTickCounter % 10)) + { + // pick from useful queries to make now and then + if(haveRigCaps && !slowPollCmdQueue.empty()) + { + + int nCmds = slowPollCmdQueue.size(); + cmds sCmd = slowPollCmdQueue[(slowCmdNum++)%nCmds]; + doCmd(sCmd); + } + } + } else { + // odd-number ticks: + // s-meter or other metering + if(haveRigCaps && !periodicCmdQueue.empty()) + { + int nCmds = periodicCmdQueue.size(); + cmds pcmd = periodicCmdQueue[ (pCmdNum++)%nCmds ]; + doCmd(pcmd); + } + } + loopTickCounter++; +} + +void servermain::issueDelayedCommand(cmds cmd) +{ + // Append to end of command queue + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = NULL; + delayedCmdQue.push_back(cmddata); +} + +void servermain::issueDelayedCommandPriority(cmds cmd) +{ + // Places the new command at the top of the queue + // Use only when needed. + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = NULL; + delayedCmdQue.push_front(cmddata); +} + +void servermain::issueDelayedCommandUnique(cmds cmd) +{ + // Use this function to insert commands where + // multiple (redundant) commands don't make sense. + + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = NULL; + + // The following is both expensive and not that great, + // since it does not check if the arguments are the same. + bool found = false; + for(unsigned int i=0; i < delayedCmdQue.size(); i++) + { + if(delayedCmdQue.at(i).cmd == cmd) + { + found = true; + break; + } + } + + if(!found) + { + delayedCmdQue.push_front(cmddata); + } + +// if( std::find(delayedCmdQue.begin(), delayedCmdQue.end(), cmddata ) == delayedCmdQue.end()) +// { +// delayedCmdQue.push_front(cmddata); +// } + +} + +void servermain::issueCmd(cmds cmd, mode_info m) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new mode_info(m)); + delayedCmdQue.push_back(cmddata); +} + +void servermain::issueCmd(cmds cmd, freqt f) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new freqt(f)); + delayedCmdQue.push_back(cmddata); +} + +void servermain::issueCmd(cmds cmd, timekind t) +{ + qDebug(logSystem()) << "Issuing timekind command with data: " << t.hours << " hours, " << t.minutes << " minutes, " << t.isMinus << " isMinus"; + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new timekind(t)); + delayedCmdQue.push_front(cmddata); +} + +void servermain::issueCmd(cmds cmd, datekind d) +{ + qDebug(logSystem()) << "Issuing datekind command with data: " << d.day << " day, " << d.month << " month, " << d.year << " year."; + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new datekind(d)); + delayedCmdQue.push_front(cmddata); +} + +void servermain::issueCmd(cmds cmd, int i) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new int(i)); + delayedCmdQue.push_back(cmddata); +} + +void servermain::issueCmd(cmds cmd, char c) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new char(c)); + delayedCmdQue.push_back(cmddata); +} + +void servermain::issueCmd(cmds cmd, bool b) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new bool(b)); + delayedCmdQue.push_back(cmddata); +} + +void servermain::issueCmd(cmds cmd, unsigned char c) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new unsigned char(c)); + delayedCmdQue.push_back(cmddata); +} + +void servermain::issueCmdUniquePriority(cmds cmd, bool b) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new bool(b)); + delayedCmdQue.push_front(cmddata); + removeSimilarCommand(cmd); +} + +void servermain::issueCmdUniquePriority(cmds cmd, unsigned char c) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new unsigned char(c)); + delayedCmdQue.push_front(cmddata); + removeSimilarCommand(cmd); +} + +void servermain::issueCmdUniquePriority(cmds cmd, char c) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new char(c)); + delayedCmdQue.push_front(cmddata); + removeSimilarCommand(cmd); +} + +void servermain::issueCmdUniquePriority(cmds cmd, freqt f) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new freqt(f)); + delayedCmdQue.push_front(cmddata); + removeSimilarCommand(cmd); +} + +void servermain::removeSimilarCommand(cmds cmd) +{ + // pop anything out that is of the same kind of command: + // pop anything out that is of the same kind of command: + // Start at 1 since we put one in at zero that we want to keep. + for(unsigned int i=1; i < delayedCmdQue.size(); i++) + { + if(delayedCmdQue.at(i).cmd == cmd) + { + //delayedCmdQue[i].cmd = cmdNone; + delayedCmdQue.erase(delayedCmdQue.begin()+i); + // i -= 1; + } + } +} + +void servermain::receiveRigID(rigCapabilities rigCaps) +{ + // Note: We intentionally request rigID several times + // because without rigID, we can't do anything with the waterfall. + if(haveRigCaps) + { + return; + } else { + + qDebug(logSystem()) << "Rig name: " << rigCaps.modelName; + qDebug(logSystem()) << "Has LAN capabilities: " << rigCaps.hasLan; + qDebug(logSystem()) << "Rig ID received into servermain: spectLenMax: " << rigCaps.spectLenMax; + qDebug(logSystem()) << "Rig ID received into servermain: spectAmpMax: " << rigCaps.spectAmpMax; + qDebug(logSystem()) << "Rig ID received into servermain: spectSeqMax: " << rigCaps.spectSeqMax; + qDebug(logSystem()) << "Rig ID received into servermain: hasSpectrum: " << rigCaps.hasSpectrum; + + this->rigCaps = rigCaps; + this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true. + haveRigCaps = true; + // Added so that server receives rig capabilities. + emit sendRigCaps(rigCaps); + + } +} + +void servermain::initPeriodicCommands() +{ + // This function places periodic polling commands into a queue. + // The commands are run using a timer, + // and the timer is started by the delayed command cmdStartPeriodicTimer. + + insertPeriodicCommand(cmdGetTxRxMeter, 128); + + insertSlowPeriodicCommand(cmdGetFreq, 128); + insertSlowPeriodicCommand(cmdGetMode, 128); + if(rigCaps.hasTransmit) + insertSlowPeriodicCommand(cmdGetPTT, 128); + insertSlowPeriodicCommand(cmdGetTxPower, 128); + insertSlowPeriodicCommand(cmdGetRxGain, 128); + if(rigCaps.hasAttenuator) + insertSlowPeriodicCommand(cmdGetAttenuator, 128); + if(rigCaps.hasTransmit) + insertSlowPeriodicCommand(cmdGetPTT, 128); + if(rigCaps.hasPreamp) + insertSlowPeriodicCommand(cmdGetPreamp, 128); + if (rigCaps.hasRXAntenna) { + insertSlowPeriodicCommand(cmdGetAntenna, 128); + } + insertSlowPeriodicCommand(cmdGetDuplexMode, 128); +} + +void servermain::insertPeriodicCommand(cmds cmd, unsigned char priority) +{ + // TODO: meaningful priority + // These commands get run at the fastest pace possible + // Typically just metering. + if(priority < 10) + { + periodicCmdQueue.push_front(cmd); + } else { + periodicCmdQueue.push_back(cmd); + } +} + +void servermain::insertPeriodicCommandUnique(cmds cmd) +{ + // Use this function to insert a non-duplicate command + // into the fast periodic polling queue, typically + // meter commands where high refresh rates are desirable. + + removePeriodicCommand(cmd); + periodicCmdQueue.push_front(cmd); +} + +void servermain::removePeriodicCommand(cmds cmd) +{ + while(true) + { + auto it = std::find(this->periodicCmdQueue.begin(), this->periodicCmdQueue.end(), cmd); + if(it != periodicCmdQueue.end()) + { + periodicCmdQueue.erase(it); + } else { + break; + } + } +} + + +void servermain::insertSlowPeriodicCommand(cmds cmd, unsigned char priority) +{ + // TODO: meaningful priority + // These commands are run every 20 "ticks" of the primary radio command loop + // Basically 20 times less often than the standard peridic command + if(priority < 10) + { + slowPollCmdQueue.push_front(cmd); + } else { + slowPollCmdQueue.push_back(cmd); + } +} + +void servermain::receiveFreq(freqt freqStruct) +{ + + qint64 tnow_ms = QDateTime::currentMSecsSinceEpoch(); + if(tnow_ms - lastFreqCmdTime_ms > delayedCommand->interval() * 2) + { + freq = freqStruct; + } else { + qDebug(logSystem()) << "Rejecting stale frequency: " << freqStruct.Hz << " Hz, delta time ms = " << tnow_ms - lastFreqCmdTime_ms\ + << ", tnow_ms " << tnow_ms << ", last: " << lastFreqCmdTime_ms; + } +} + +void servermain::receivePTTstatus(bool pttOn) +{ + // This is the only place where amTransmitting and the transmit button text should be changed: + //qInfo(logSystem()) << "PTT status: " << pttOn; + amTransmitting = pttOn; +} + +void servermain::changeTxBtn() +{ +} + + +void servermain::receiveDataModeStatus(bool dataEnabled) +{ +} + +void servermain::checkFreqSel() +{ + if(freqTextSelected) + { + freqTextSelected = false; + } +} + +void servermain::changeMode(mode_kind mode) +{ + bool dataOn = false; + if(((unsigned char) mode >> 4) == 0x08) + { + dataOn = true; + mode = (mode_kind)((int)mode & 0x0f); + } + + changeMode(mode, dataOn); +} + +void servermain::changeMode(mode_kind mode, bool dataOn) +{ +} + +void servermain::receiveBandStackReg(freqt freqGo, char mode, char filter, bool dataOn) +{ + // read the band stack and apply by sending out commands + + qInfo(logSystem()) << __func__ << "BSR received into main: Freq: " << freqGo.Hz << ", mode: " << (unsigned int)mode << ", filter: " << (unsigned int)filter << ", data mode: " << dataOn; + //emit setFrequency(0,freq); + issueCmd(cmdSetFreq, freqGo); + setModeVal = (unsigned char) mode; + setFilterVal = (unsigned char) filter; + + issueDelayedCommand(cmdSetModeFilter); + freq = freqGo; + + if(dataOn) + { + issueDelayedCommand(cmdSetDataModeOn); + } else { + issueDelayedCommand(cmdSetDataModeOff); + } + //issueDelayedCommand(cmdGetFreq); + //issueDelayedCommand(cmdGetMode); + +} + +void servermain::bandStackBtnClick() +{ +} + +void servermain::receiveRfGain(unsigned char level) +{ +} + +void servermain::receiveAfGain(unsigned char level) +{ +} + +void servermain::receiveSql(unsigned char level) +{ +} + +void servermain::receiveIFShift(unsigned char level) +{ +} + +void servermain::receiveTBPFInner(unsigned char level) +{ +} + +void servermain::receiveTBPFOuter(unsigned char level) +{ +} + +void servermain::setRadioTimeDatePrep() +{ + if(!waitingToSetTimeDate) + { + // 1: Find the current time and date + QDateTime now; + now = QDateTime::currentDateTime(); + now.setTime(QTime::currentTime()); + + int second = now.time().second(); + + // 2: Find how many mseconds until next minute + int msecdelay = QTime::currentTime().msecsTo( QTime::currentTime().addSecs(60-second) ); + + // 3: Compute time and date at one minute later + QDateTime setpoint = now.addMSecs(msecdelay); // at HMS or posibly HMS + some ms. Never under though. + + // 4: Prepare data structs for the time at one minute later + timesetpoint.hours = (unsigned char)setpoint.time().hour(); + timesetpoint.minutes = (unsigned char)setpoint.time().minute(); + datesetpoint.day = (unsigned char)setpoint.date().day(); + datesetpoint.month = (unsigned char)setpoint.date().month(); + datesetpoint.year = (uint16_t)setpoint.date().year(); + unsigned int utcOffsetSeconds = (unsigned int)abs(setpoint.offsetFromUtc()); + bool isMinus = setpoint.offsetFromUtc() < 0; + utcsetting.hours = utcOffsetSeconds / 60 / 60; + utcsetting.minutes = (utcOffsetSeconds - (utcsetting.hours*60*60) ) / 60; + utcsetting.isMinus = isMinus; + + timeSync->setInterval(msecdelay); + timeSync->setSingleShot(true); + + // 5: start one-shot timer for the delta computed in #2. + timeSync->start(); + waitingToSetTimeDate = true; + } +} + +void servermain::setRadioTimeDateSend() +{ + // Issue priority commands for UTC offset, date, and time + // UTC offset must come first, otherwise the radio may "help" and correct for any changes. + + issueCmd(cmdSetTime, timesetpoint); + issueCmd(cmdSetDate, datesetpoint); + issueCmd(cmdSetUTCOffset, utcsetting); + waitingToSetTimeDate = false; +} + + +void servermain::receiveTxPower(unsigned char power) +{ +} + +void servermain::receiveMicGain(unsigned char gain) +{ + processModLevel(inputMic, gain); +} + +void servermain::processModLevel(rigInput source, unsigned char level) +{ + rigInput currentIn; + if(usingDataMode) + { + currentIn = currentModDataSrc; + } else { + currentIn = currentModSrc; + } + + switch(source) + { + case inputMic: + micGain = level; + break; + case inputACC: + accGain = level; + break; + + case inputACCA: + accAGain = level; + break; + + case inputACCB: + accBGain = level; + break; + + case inputUSB: + usbGain = level; + break; + + case inputLAN: + lanGain = level; + break; + + default: + break; + } + +} + +void servermain::receiveModInput(rigInput input, bool dataOn) +{ +} + +void servermain::receiveACCGain(unsigned char level, unsigned char ab) +{ + if(ab==1) + { + processModLevel(inputACCB, level); + } else { + if(rigCaps.model == model7850) + { + processModLevel(inputACCA, level); + } else { + processModLevel(inputACC, level); + } + } +} + +void servermain::receiveUSBGain(unsigned char level) +{ + processModLevel(inputUSB, level); +} + +void servermain::receiveLANGain(unsigned char level) +{ + processModLevel(inputLAN, level); +} + +void servermain::receiveMeter(meterKind inMeter, unsigned char level) +{ + +} + +void servermain::receiveCompLevel(unsigned char compLevel) +{ + (void)compLevel; +} + +void servermain::receiveMonitorGain(unsigned char monitorGain) +{ + (void)monitorGain; +} + +void servermain::receiveVoxGain(unsigned char voxGain) +{ + (void)voxGain; +} + +void servermain::receiveAntiVoxGain(unsigned char antiVoxGain) +{ + (void)antiVoxGain; +} + + +void servermain::receiveSpectrumRefLevel(int level) +{ +} + +void servermain::changeModLabelAndSlider(rigInput source) +{ + changeModLabel(source, true); +} + +void servermain::changeModLabel(rigInput input) +{ + changeModLabel(input, false); +} + +void servermain::changeModLabel(rigInput input, bool updateLevel) +{ +} + +void servermain::processChangingCurrentModLevel(unsigned char level) +{ + // slider moved, so find the current mod and issue the level set command. + issueCmd(cmdSetModLevel, level); +} +void servermain::receivePreamp(unsigned char pre) +{ +} + +void servermain::receiveAttenuator(unsigned char att) +{ +} + +void servermain::receiveAntennaSel(unsigned char ant, bool rx) +{ +} + + +void servermain::calculateTimingParameters() +{ + // Function for calculating polling parameters. + // Requires that we know the "baud rate" of the actual + // radio connection. + + // baud on the serial port reflects the actual rig connection, + // even if a client-server connection is being used. + // Computed time for a 10 byte message, with a safety factor of 2. + + if (prefs.serialPortBaud == 0) + { + prefs.serialPortBaud = 9600; + qInfo(logSystem()) << "WARNING: baud rate received was zero. Assuming 9600 baud, performance may suffer."; + } + + unsigned int usPerByte = 9600*1000 / prefs.serialPortBaud; + unsigned int msMinTiming=usPerByte * 10*2/1000; + if(msMinTiming < 25) + msMinTiming = 25; + + if(haveRigCaps && rigCaps.hasFDcomms) + { + delayedCommand->setInterval( msMinTiming); // 20 byte message + } else { + delayedCommand->setInterval( msMinTiming * 3); // 20 byte message + } + + + qInfo(logSystem()) << "Delay command interval timing: " << delayedCommand->interval() << "ms"; + + // Normal: + delayedCmdIntervalLAN_ms = delayedCommand->interval(); + delayedCmdIntervalSerial_ms = delayedCommand->interval(); + + // startup initial state: + delayedCmdStartupInterval_ms = delayedCommand->interval() * 3; +} + +void servermain::receiveBaudRate(quint32 baud) +{ + qInfo() << "Received serial port baud rate from remote server:" << baud; + prefs.serialPortBaud = baud; + calculateTimingParameters(); +} + +void servermain::powerRigOn() +{ + emit sendPowerOn(); + + delayedCommand->setInterval(3000); // 3 seconds + issueDelayedCommand(cmdQueNormalSpeed); + issueDelayedCommand(cmdStartRegularPolling); // s-meter, etc + delayedCommand->start(); +} + +void servermain::powerRigOff() +{ + delayedCommand->stop(); + delayedCmdQue.clear(); + + emit sendPowerOff(); +} + + +void servermain::receiveRITStatus(bool ritEnabled) +{ +} + +void servermain::receiveRITValue(int ritValHz) +{ +} + +servermain::cmds servermain::meterKindToMeterCommand(meterKind m) +{ + cmds c; + switch(m) + { + case meterNone: + c = cmdNone; + break; + case meterS: + c = cmdGetSMeter; + break; + case meterCenter: + c = cmdGetCenterMeter; + break; + case meterPower: + c = cmdGetPowerMeter; + break; + case meterSWR: + c = cmdGetSWRMeter; + break; + case meterALC: + c = cmdGetALCMeter; + break; + case meterComp: + c = cmdGetCompMeter; + break; + case meterCurrent: + c = cmdGetIdMeter; + break; + case meterVoltage: + c = cmdGetVdMeter; + break; + default: + c = cmdNone; + break; + } + + return c; +} + + +void servermain::handleCtrlC(int sig) { + if (sig == 2) { + QCoreApplication::quit(); + //exit(EXIT_FAILURE); + } +} diff --git a/servermain.h b/servermain.h new file mode 100644 index 0000000..4d79004 --- /dev/null +++ b/servermain.h @@ -0,0 +1,510 @@ +#ifndef WFMAIN_H +#define WFMAIN_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "logcategories.h" +#include "commhandler.h" +#include "rigcommander.h" +#include "rigstate.h" +#include "freqmemory.h" +#include "rigidentities.h" +#include "repeaterattributes.h" + +#include "udpserver.h" +#include "rigctld.h" +#include "signal.h" + +#include + +#include +#include + +namespace Ui { +class wfmain; +} + +class servermain : public QObject +{ + Q_OBJECT + +public: + servermain(const QString serialPortCL, const QString hostCL, const QString settingsFile); + QString serialPortCL; + QString hostCL; + ~servermain(); + +signals: + // Basic to rig: + void setCIVAddr(unsigned char newRigCIVAddr); + void setRigID(unsigned char rigID); + void setRTSforPTT(bool enabled); + + // Power + void sendPowerOn(); + void sendPowerOff(); + + // Frequency, mode, band: + void getFrequency(); + void setFrequency(unsigned char vfo, freqt freq); + void getMode(); + void setMode(unsigned char modeIndex, unsigned char modeFilter); + void setMode(mode_info); + void setDataMode(bool dataOn, unsigned char filter); + void getDataMode(); + void getModInput(bool dataOn); + void setModInput(rigInput input, bool dataOn); + void getBandStackReg(char band, char regCode); + void getDebug(); + void getRitEnabled(); + void getRitValue(); + void setRitValue(int ritValue); + void setRitEnable(bool ritEnabled); + + // Repeater: + void getDuplexMode(); + void getTone(); + void getTSQL(); + void getDTCS(); + void getRptAccessMode(); + + // Level get: + void getLevels(); // get all levels + void getRfGain(); + void getAfGain(); + void getSql(); + void getIfShift(); + void getTPBFInner(); + void getTPBFOuter(); + void getTxPower(); + void getMicGain(); + void getSpectrumRefLevel(); + void getModInputLevel(rigInput input); + + // Level set: + void setRfGain(unsigned char level); + void setAfGain(unsigned char level); + void setSql(unsigned char level); + void setIFShift(unsigned char level); + void setTPBFInner(unsigned char level); + void setTPBFOuter(unsigned char level); + + void setIFShiftWindow(unsigned char level); + void setTPBFInnerWindow(unsigned char level); + void setTPBFOuterWindow(unsigned char level); + void setMicGain(unsigned char); + void setCompLevel(unsigned char); + void setTxPower(unsigned char); + void setMonitorLevel(unsigned char); + void setVoxGain(unsigned char); + void setAntiVoxGain(unsigned char); + void setSpectrumRefLevel(int); + + void setModLevel(rigInput input, unsigned char level); + void setACCGain(unsigned char level); + void setACCAGain(unsigned char level); + void setACCBGain(unsigned char level); + void setUSBGain(unsigned char level); + void setLANGain(unsigned char level); + + void getMeters(meterKind meter); + + + // PTT, ATU, ATT, Antenna, Preamp: + void getPTT(); + void setPTT(bool pttOn); + void getAttenuator(); + void getPreamp(); + void getAntenna(); + void setAttenuator(unsigned char att); + void setPreamp(unsigned char pre); + void setAntenna(unsigned char ant, bool rx); + void startATU(); + void setATU(bool atuEnabled); + void getATUStatus(); + + // Time and date: + void setTime(timekind t); + void setDate(datekind d); + void setUTCOffset(timekind t); + + void getRigID(); // this is the model of the rig + void getRigCIV(); // get the rig's CIV addr + void spectOutputEnable(); + void spectOutputDisable(); + void scopeDisplayEnable(); + void scopeDisplayDisable(); + void setScopeMode(spectrumMode spectMode); + void setScopeSpan(char span); + void setScopeEdge(char edge); + void setScopeFixedEdge(double startFreq, double endFreq, unsigned char edgeNumber); + void getScopeMode(); + void getScopeEdge(); + void getScopeSpan(); + void sayFrequency(); + void sayMode(); + void sayAll(); + void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp); + void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); + void sendCloseComm(); + void sendChangeLatency(quint16 latency); + void initServer(); + void sendRigCaps(rigCapabilities caps); + void requestRigState(); + void stateUpdated(); + +private slots: + + void receiveCommReady(); + void receiveFreq(freqt); + void receivePTTstatus(bool pttOn); + void receiveDataModeStatus(bool dataOn); + void receiveBandStackReg(freqt f, char mode, char filter, bool dataOn); // freq, mode, (filter,) datamode + void receiveRITStatus(bool ritEnabled); + void receiveRITValue(int ritValHz); + void receiveModInput(rigInput input, bool dataOn); + //void receiveDuplexMode(duplexMode dm); + + + + // Levels: + void receiveRfGain(unsigned char level); + void receiveAfGain(unsigned char level); + void receiveSql(unsigned char level); + void receiveIFShift(unsigned char level); + void receiveTBPFInner(unsigned char level); + void receiveTBPFOuter(unsigned char level); + // 'change' from data in transceiver controls window: + void receiveTxPower(unsigned char power); + void receiveMicGain(unsigned char gain); + void receiveCompLevel(unsigned char compLevel); + void receiveMonitorGain(unsigned char monitorGain); + void receiveVoxGain(unsigned char voxGain); + void receiveAntiVoxGain(unsigned char antiVoxGain); + void receiveSpectrumRefLevel(int level); + void receiveACCGain(unsigned char level, unsigned char ab); + void receiveUSBGain(unsigned char level); + void receiveLANGain(unsigned char level); + + // Meters: + void receiveMeter(meterKind meter, unsigned char level); +// void receiveSMeter(unsigned char level); +// void receivePowerMeter(unsigned char level); +// void receiveALCMeter(unsigned char level); +// void receiveCompMeter(unsigned char level); + + + void receivePreamp(unsigned char pre); + void receiveAttenuator(unsigned char att); + void receiveAntennaSel(unsigned char ant, bool rx); + void receiveRigID(rigCapabilities rigCaps); + void receiveFoundRigID(rigCapabilities rigCaps); + void receiveSerialPortError(QString port, QString errorText); + void sendRadioCommandLoop(); + void receiveBaudRate(quint32 baudrate); + + void setRadioTimeDateSend(); + + +private: + Ui::wfmain *ui; + void closeEvent(QCloseEvent *event); + QSettings *settings=Q_NULLPTR; + void loadSettings(); + + void getInitialRigState(); + + void openRig(); + void powerRigOff(); + void powerRigOn(); + QStringList portList; + QString serialPortRig; + + rigCommander * rig=Q_NULLPTR; + QThread* rigThread = Q_NULLPTR; + QTimer * delayedCommand; + QTimer * pttTimer; + uint16_t loopTickCounter; + uint16_t slowCmdNum; + + void makeRig(); + void rigConnections(); + void removeRig(); + void findSerialPort(); + + void setServerToPrefs(); + void setInitialTiming(); + void getSettingsFilePath(QString settingsFile); + + QStringList modes; + int currentModeIndex; + QStringList spans; + QStringList edges; + QStringList commPorts; + + quint16 spectWidth; + quint16 wfLength; + bool spectrumDrawLock; + + QByteArray spectrumPeaks; + + QVector wfimage; + unsigned int wfLengthMax; + + bool onFullscreen; + bool drawPeaks; + bool freqTextSelected; + void checkFreqSel(); + + double oldLowerFreq; + double oldUpperFreq; + freqt freq; + float tsKnobMHz; + + unsigned char setModeVal=0; + unsigned char setFilterVal=0; + + enum cmds {cmdNone, cmdGetRigID, cmdGetRigCIV, cmdGetFreq, cmdSetFreq, cmdGetMode, cmdSetMode, + cmdGetDataMode, cmdSetModeFilter, cmdSetDataModeOn, cmdSetDataModeOff, cmdGetRitEnabled, cmdGetRitValue, + cmdSpecOn, cmdSpecOff, cmdDispEnable, cmdDispDisable, cmdGetRxGain, cmdSetRxRfGain, cmdGetAfGain, cmdSetAfGain, + cmdGetSql, cmdSetSql, cmdGetIFShift, cmdSetIFShift, cmdGetTPBFInner, cmdSetTPBFInner, + cmdGetTPBFOuter, cmdSetTPBFOuter, cmdGetATUStatus, + cmdSetATU, cmdStartATU, cmdGetSpectrumMode, + cmdGetSpectrumSpan, cmdScopeCenterMode, cmdScopeFixedMode, cmdGetPTT, cmdSetPTT, + cmdGetTxPower, cmdSetTxPower, cmdGetMicGain, cmdSetMicGain, cmdSetModLevel, + cmdGetSpectrumRefLevel, cmdGetDuplexMode, cmdGetModInput, cmdGetModDataInput, + cmdGetCurrentModLevel, cmdStartRegularPolling, cmdStopRegularPolling, cmdQueNormalSpeed, + cmdGetVdMeter, cmdGetIdMeter, cmdGetSMeter, cmdGetCenterMeter, cmdGetPowerMeter, + cmdGetSWRMeter, cmdGetALCMeter, cmdGetCompMeter, cmdGetTxRxMeter, + cmdGetTone, cmdGetTSQL, cmdGetDTCS, cmdGetRptAccessMode, cmdGetPreamp, cmdGetAttenuator, cmdGetAntenna, + cmdSetTime, cmdSetDate, cmdSetUTCOffset}; + + struct commandtype { + cmds cmd; + std::shared_ptr data; + }; + + std::deque delayedCmdQue; // rapid que for commands to the radio + std::deque periodicCmdQueue; // rapid que for metering + std::deque slowPollCmdQueue; // slow, regular checking for UI sync + void doCmd(cmds cmd); + void doCmd(commandtype cmddata); + + void issueCmd(cmds cmd, freqt f); + void issueCmd(cmds cmd, mode_info m); + void issueCmd(cmds cmd, timekind t); + void issueCmd(cmds cmd, datekind d); + void issueCmd(cmds cmd, int i); + void issueCmd(cmds cmd, unsigned char c); + void issueCmd(cmds cmd, char c); + void issueCmd(cmds cmd, bool b); + + // These commands pop_front and remove similar commands: + void issueCmdUniquePriority(cmds cmd, bool b); + void issueCmdUniquePriority(cmds cmd, unsigned char c); + void issueCmdUniquePriority(cmds cmd, char c); + void issueCmdUniquePriority(cmds cmd, freqt f); + + void removeSimilarCommand(cmds cmd); + + qint64 lastFreqCmdTime_ms; + + int pCmdNum = 0; + int delayedCmdIntervalLAN_ms = 100; + int delayedCmdIntervalSerial_ms = 100; + int delayedCmdStartupInterval_ms = 100; + bool runPeriodicCommands; + bool usingLAN = false; + + // Radio time sync: + QTimer *timeSync; + bool waitingToSetTimeDate; + void setRadioTimeDatePrep(); + timekind timesetpoint; + timekind utcsetting; + datekind datesetpoint; + + freqMemory mem; + struct colors { + QColor Dark_PlotBackground; + QColor Dark_PlotAxisPen; + QColor Dark_PlotLegendTextColor; + QColor Dark_PlotLegendBorderPen; + QColor Dark_PlotLegendBrush; + QColor Dark_PlotTickLabel; + QColor Dark_PlotBasePen; + QColor Dark_PlotTickPen; + QColor Dark_PeakPlotLine; + QColor Dark_TuningLine; + + QColor Light_PlotBackground; + QColor Light_PlotAxisPen; + QColor Light_PlotLegendTextColor; + QColor Light_PlotLegendBorderPen; + QColor Light_PlotLegendBrush; + QColor Light_PlotTickLabel; + QColor Light_PlotBasePen; + QColor Light_PlotTickPen; + QColor Light_PeakPlotLine; + QColor Light_TuningLine; + + } colorScheme; + + struct preferences { + bool useFullScreen; + bool useDarkMode; + bool useSystemTheme; + bool drawPeaks; + bool wfAntiAlias; + bool wfInterpolate; + QString stylesheetPath; + unsigned char radioCIVAddr; + bool CIVisRadioModel; + bool forceRTSasPTT; + QString serialPortRadio; + quint32 serialPortBaud; + bool enablePTT; + bool niceTS; + bool enableLAN; + bool enableRigCtlD; + quint16 rigCtlPort; + colors colorScheme; + QString virtualSerialPort; + unsigned char localAFgain; + unsigned int wflength; + int wftheme; + bool confirmExit; + bool confirmPowerOff; + meterKind meter2Type; + // plot scheme + } prefs; + + preferences defPrefs; + udpPreferences udpPrefs; + udpPreferences udpDefPrefs; + + // Configuration for audio output and input. + audioSetup rxSetup; + audioSetup txSetup; + + audioSetup serverRxSetup; + audioSetup serverTxSetup; + + colors defaultColors; + + void setDefaultColors(); // populate with default values + void useColors(); // set the plot up + void setDefPrefs(); // populate default values to default prefs + void setTuningSteps(); + + quint64 roundFrequency(quint64 frequency, unsigned int tsHz); + quint64 roundFrequencyWithStep(quint64 oldFreq, int steps,\ + unsigned int tsHz); + + void changeTxBtn(); + void issueDelayedCommand(cmds cmd); + void issueDelayedCommandPriority(cmds cmd); + void issueDelayedCommandUnique(cmds cmd); + + void processModLevel(rigInput source, unsigned char level); + + void processChangingCurrentModLevel(unsigned char level); + + void changeModLabel(rigInput source); + void changeModLabel(rigInput source, bool updateLevel); + + void changeModLabelAndSlider(rigInput source); + + // Fast command queue: + void initPeriodicCommands(); + void insertPeriodicCommand(cmds cmd, unsigned char priority); + void insertPeriodicCommandUnique(cmds cmd); + void removePeriodicCommand(cmds cmd); + + void insertSlowPeriodicCommand(cmds cmd, unsigned char priority); + void calculateTimingParameters(); + + void changeMode(mode_kind mode); + void changeMode(mode_kind mode, bool dataOn); + + cmds meterKindToMeterCommand(meterKind m); + + int oldFreqDialVal; + + rigCapabilities rigCaps; + rigInput currentModSrc = inputUnknown; + rigInput currentModDataSrc = inputUnknown; + mode_kind currentMode = modeUSB; + mode_info currentModeInfo; + + bool haveRigCaps; + bool amTransmitting; + bool usingDataMode = false; + + unsigned char micGain=0; + unsigned char accAGain=0; + unsigned char accBGain=0; + unsigned char accGain=0; + unsigned char usbGain=0; + unsigned char lanGain=0; + + + udpServer* udp = Q_NULLPTR; + rigCtlD* rigCtl = Q_NULLPTR; + QThread* serverThread = Q_NULLPTR; + + void bandStackBtnClick(); + bool waitingForBandStackRtn; + char bandStkBand; + char bandStkRegCode; + + bool freqLock; + + float tsPlus; + float tsPlusShift; + float tsPlusControl; + float tsPage; + float tsPageShift; + float tsWfScroll; + + unsigned int tsPlusHz; + unsigned int tsPlusShiftHz; + unsigned int tsPlusControlHz; + unsigned int tsPageHz; + unsigned int tsPageShiftHz; + unsigned int tsWfScrollHz; + unsigned int tsKnobHz; + + rigstate* rigState = Q_NULLPTR; + + SERVERCONFIG serverConfig; + + static void handleCtrlC(int sig); +}; + +Q_DECLARE_METATYPE(struct rigCapabilities) +Q_DECLARE_METATYPE(struct freqt) +Q_DECLARE_METATYPE(struct mode_info) +Q_DECLARE_METATYPE(struct udpPreferences) +Q_DECLARE_METATYPE(struct audioPacket) +Q_DECLARE_METATYPE(struct audioSetup) +Q_DECLARE_METATYPE(struct SERVERCONFIG) +Q_DECLARE_METATYPE(struct timekind) +Q_DECLARE_METATYPE(struct datekind) +Q_DECLARE_METATYPE(enum rigInput) +Q_DECLARE_METATYPE(enum meterKind) +Q_DECLARE_METATYPE(enum spectrumMode) +Q_DECLARE_METATYPE(rigstate*) + + +#endif // WFMAIN_H diff --git a/wfmain.h b/wfmain.h index 236af59..6a8bd71 100644 --- a/wfmain.h +++ b/wfmain.h @@ -1,3 +1,7 @@ +#ifdef WFSERVER +#include "servermain.h" +#else + #ifndef WFMAIN_H #define WFMAIN_H @@ -889,3 +893,4 @@ Q_DECLARE_METATYPE(rigstate*) #endif // WFMAIN_H +#endif \ No newline at end of file diff --git a/wfserver.pro b/wfserver.pro new file mode 100644 index 0000000..993c9cc --- /dev/null +++ b/wfserver.pro @@ -0,0 +1,158 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-05-26T16:57:32 +# +#------------------------------------------------- + +QT += core serialport network multimedia + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport + +TARGET = wfserver +TEMPLATE = app + +CONFIG += console + +DEFINES += WFVIEW_VERSION=\\\"1.2d\\\" + +DEFINES += WFSERVER + +CONFIG(debug, release|debug) { +# For Debug builds only: +QMAKE_CXXFLAGS += -faligned-new + +} else { +# For Release builds only: +linux:QMAKE_CXXFLAGS += -s +QMAKE_CXXFLAGS += -fvisibility=hidden +QMAKE_CXXFLAGS += -fvisibility-inlines-hidden +QMAKE_CXXFLAGS += -faligned-new +linux:QMAKE_LFLAGS += -O2 -s +} + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += QCUSTOMPLOT_COMPILE_LIBRARY + + +# These defines are used for the resampler +equals(QT_ARCH, i386): DEFINES += USE_SSE +equals(QT_ARCH, i386): DEFINES += USE_SSE2 +equals(QT_ARCH, arm): DEFINES += USE_NEON +DEFINES += OUTSIDE_SPEEX +DEFINES += RANDOM_PREFIX=wf + +isEmpty(PREFIX) { + PREFIX = /usr/local +} + +DEFINES += PREFIX=\\\"$$PREFIX\\\" + +# Choose audio system, uses QTMultimedia if both are commented out. +# DEFINES += RTAUDIO +# DEFINES += PORTAUDIO + +contains(DEFINES, RTAUDIO) { + # RTAudio defines + win32:DEFINES += __WINDOWS_WASAPI__ + #win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries + linux:DEFINES += __LINUX_ALSA__ + #linux:DEFINES += __LINUX_OSS__ + #linux:DEFINES += __LINUX_PULSE__ + macx:DEFINES += __MACOSX_CORE__ + win32:SOURCES += ../rtaudio/RTAudio.cpp + win32:HEADERS += ../rtaudio/RTAUdio.h + !linux:INCLUDEPATH += ../rtaudio + linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread +} + +contains(DEFINES, PORTAUDIO) { + CONFIG(debug, release|debug) { + win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 + } else { + win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 + } + win32:INCLUDEPATH += ../portaudio/include + !win32:LIBS += -lportaudio +} + +macx:INCLUDEPATH += /usr/local/include /opt/local/include +macx:LIBS += -L/usr/local/lib -L/opt/local/lib + +macx:ICON = ../wfview/resources/wfview.icns +win32:RC_ICONS = ../wfview/resources/wfview.ico +QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.13 +QMAKE_TARGET_BUNDLE_PREFIX = org.wfview +MY_ENTITLEMENTS.name = CODE_SIGN_ENTITLEMENTS +MY_ENTITLEMENTS.value = ../wfview/resources/wfview.entitlements +QMAKE_MAC_XCODE_SETTINGS += MY_ENTITLEMENTS +QMAKE_INFO_PLIST = ../wfview/resources/Info.plist + +!win32:DEFINES += HOST=\\\"`hostname`\\\" UNAME=\\\"`whoami`\\\" + +!win32:DEFINES += GITSHORT="\\\"$(shell git -C $$PWD rev-parse --short HEAD)\\\"" +win32:DEFINES += GITSHORT=\\\"$$system(git -C $$PWD rev-parse --short HEAD)\\\" + +win32:DEFINES += HOST=\\\"wfview.org\\\" +win32:DEFINES += UNAME=\\\"build\\\" + + +RESOURCES += qdarkstyle/style.qrc \ + resources/resources.qrc + +unix:target.path = $$PREFIX/bin +INSTALLS += target + +# Do not do this, it will hang on start: +# CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT + +CONFIG(debug, release|debug) { + win32:LIBS += -L../opus/win32/VS2015/Win32/Debug/ -lopus +} else { + win32:LIBS += -L../opus/win32/VS2015/Win32/Release/ -lopus +} + +linux:LIBS += -L./ -l$$QCPLIB -lopus +macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread -lopus + +!linux:INCLUDEPATH += ../opus/include + +INCLUDEPATH += resampler + +SOURCES += main.cpp\ + servermain.cpp \ + commhandler.cpp \ + rigcommander.cpp \ + freqmemory.cpp \ + rigidentities.cpp \ + udphandler.cpp \ + logcategories.cpp \ + audiohandler.cpp \ + udpserver.cpp \ + pttyhandler.cpp \ + resampler/resample.c \ + rigctld.cpp \ + ring/ring.cpp + +HEADERS += servermain.h \ + commhandler.h \ + rigcommander.h \ + freqmemory.h \ + rigidentities.h \ + udphandler.h \ + logcategories.h \ + audiohandler.h \ + udpserver.h \ + packettypes.h \ + pttyhandler.h \ + resampler/speex_resampler.h \ + resampler/arch.h \ + resampler/resample_sse.h \ + repeaterattributes.h \ + rigctld.h \ + ulaw.h \ + ring/ring.h \ + audiotaper.h diff --git a/wfserver.vcxproj b/wfserver.vcxproj new file mode 100644 index 0000000..925094b --- /dev/null +++ b/wfserver.vcxproj @@ -0,0 +1,313 @@ + + + + + Release + Win32 + + + Debug + Win32 + + + + {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56} + wfserver + QtVS_v304 + 10.0.19041.0 + 10.0.19041.0 + $(MSBuildProjectDirectory)\QtMsBuild + + + + v142 + release\ + false + NotSet + Application + release\ + wfserver + + + v142 + debug\ + false + NotSet + Application + debug\ + wfserver + + + + + + + + + + + + + + + + + + debug\ + debug\ + wfserver + true + + + release\ + release\ + wfserver + true + false + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + + + + + .;..\opus\include;resampler;release;/include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + release\ + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + release\ + MaxSpeed + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="28812be";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + false + + + MultiThreadedDLL + true + true + Level3 + true + + + ..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies) + ..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + true + $(OutDir)\wfserver.exe + true + Console + true + + + Unsigned + None + 0 + + + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"28812be\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + + + .;..\opus\include;resampler;debug;/include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + debug\ + false + ProgramDatabase + 4577;4467;%(DisableSpecificWarnings) + Sync + debug\ + Disabled + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="28812be";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + true + + + ..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies) + ..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\wfserver.exe + true + Console + true + + + Unsigned + None + 0 + + + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"28812be\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -faligned-new -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + Generate moc_predefs.h + debug\moc_predefs.h;%(Outputs) + + + Document + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -fvisibility=hidden -fvisibility-inlines-hidden -faligned-new -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h + Generate moc_predefs.h + release\moc_predefs.h;%(Outputs) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + resources + resources + + + + + + style + style + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wfserver.vcxproj.filters b/wfserver.vcxproj.filters new file mode 100644 index 0000000..e2466f8 --- /dev/null +++ b/wfserver.vcxproj.filters @@ -0,0 +1,284 @@ + + + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Generated Files + + + Generated Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + + \ No newline at end of file diff --git a/wfserver.vcxproj.user b/wfserver.vcxproj.user new file mode 100644 index 0000000..65dc681 --- /dev/null +++ b/wfserver.vcxproj.user @@ -0,0 +1,10 @@ + + + + + 2022-01-17T12:12:10.6444561Z + + + 2022-01-17T12:12:12.8619428Z + + \ No newline at end of file diff --git a/wfserver_resource.rc b/wfserver_resource.rc new file mode 100644 index 0000000..d437633 --- /dev/null +++ b/wfserver_resource.rc @@ -0,0 +1,37 @@ +#include + +IDI_ICON1 ICON DISCARDABLE "C:\\Users\\Phil\\source\\repos\\wfview\\resources\\wfview.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,0,0,0 + PRODUCTVERSION 0,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "\0" + VALUE "FileVersion", "0.0.0.0\0" + VALUE "LegalCopyright", "\0" + VALUE "OriginalFilename", "wfserver.exe\0" + VALUE "ProductName", "wfserver\0" + VALUE "ProductVersion", "0.0.0.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END + END +/* End of Version info */ + diff --git a/wfview.sln b/wfview.sln index ff4a8cd..d1ee00c 100644 --- a/wfview.sln +++ b/wfview.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.30804.86 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfview", "wfview.vcxproj", "{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfserver", "wfserver.vcxproj", "{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 @@ -15,6 +17,10 @@ Global {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x86.Build.0 = Debug|Win32 {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.ActiveCfg = Release|Win32 {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.Build.0 = Release|Win32 + {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.ActiveCfg = Debug|Win32 + {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.Build.0 = Debug|Win32 + {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.ActiveCfg = Release|Win32 + {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wfview.vcxproj b/wfview.vcxproj index 9357e18..bd3677c 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="01ea44d";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="28812be";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -85,7 +85,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"01ea44d\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"28812be\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -99,7 +99,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="01ea44d";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="28812be";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -124,7 +124,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"01ea44d\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"28812be\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h From 4ebfe457c75721f99cd16067bf0bc5503edb9380 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 18 Jan 2022 00:11:15 +0000 Subject: [PATCH 074/323] Tidying of server code --- main.cpp | 2 + servermain.cpp | 670 ++++++++++++++++--------------------------------- servermain.h | 64 +---- 3 files changed, 220 insertions(+), 516 deletions(-) diff --git a/main.cpp b/main.cpp index 0adc1ad..ed95e7f 100644 --- a/main.cpp +++ b/main.cpp @@ -142,11 +142,13 @@ int main(int argc, char *argv[]) #ifdef WFSERVER servermain *w = new servermain(serialPortCL, hostCL, settingsFile); + #else a.setWheelScrollLines(1); // one line per wheel click wfmain w(serialPortCL, hostCL, settingsFile); w.show(); + #endif return a.exec(); diff --git a/servermain.cpp b/servermain.cpp index 9caa3b9..aa0de88 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -3,6 +3,7 @@ #include "commhandler.h" #include "rigidentities.h" #include "logcategories.h" +#include // This code is copyright 2017-2020 Elliott H. Liggett // All rights reserved @@ -28,7 +29,7 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q qRegisterMetaType (); qRegisterMetaType(); - //signal(SIGINT, handleCtrlC); + signal(SIGINT, handleCtrlC); haveRigCaps = false; @@ -69,11 +70,6 @@ servermain::~servermain() } -void servermain::closeEvent(QCloseEvent *event) -{ - // Are you sure? -} - void servermain::openRig() { // This function is intended to handle opening a connection to the rig. @@ -141,45 +137,18 @@ void servermain::rigConnections() connect(this, SIGNAL(sendPowerOn()), rig, SLOT(powerOn())); connect(this, SIGNAL(sendPowerOff()), rig, SLOT(powerOff())); - connect(rig, SIGNAL(haveFrequency(freqt)), this, SLOT(receiveFreq(freqt))); - connect(this, SIGNAL(getFrequency()), rig, SLOT(getFrequency())); - connect(this, SIGNAL(getMode()), rig, SLOT(getMode())); - connect(this, SIGNAL(getDataMode()), rig, SLOT(getDataMode())); - connect(this, SIGNAL(setDataMode(bool, unsigned char)), rig, SLOT(setDataMode(bool, unsigned char))); - connect(this, SIGNAL(getBandStackReg(char,char)), rig, SLOT(getBandStackReg(char,char))); connect(rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool))); connect(this, SIGNAL(setPTT(bool)), rig, SLOT(setPTT(bool))); connect(this, SIGNAL(getPTT()), rig, SLOT(getPTT())); - connect(rig, SIGNAL(haveBandStackReg(freqt,char,char,bool)), this, SLOT(receiveBandStackReg(freqt,char,char,bool))); - connect(this, SIGNAL(setRitEnable(bool)), rig, SLOT(setRitEnable(bool))); - connect(this, SIGNAL(setRitValue(int)), rig, SLOT(setRitValue(int))); - connect(rig, SIGNAL(haveRitEnabled(bool)), this, SLOT(receiveRITStatus(bool))); - connect(rig, SIGNAL(haveRitFrequency(int)), this, SLOT(receiveRITValue(int))); - connect(this, SIGNAL(getRitEnabled()), rig, SLOT(getRitEnabled())); - connect(this, SIGNAL(getRitValue()), rig, SLOT(getRitValue())); - connect(this, SIGNAL(getDebug()), rig, SLOT(getDebug())); - connect(this, SIGNAL(spectOutputDisable()), rig, SLOT(disableSpectOutput())); - connect(this, SIGNAL(spectOutputEnable()), rig, SLOT(enableSpectOutput())); - connect(this, SIGNAL(scopeDisplayDisable()), rig, SLOT(disableSpectrumDisplay())); - connect(this, SIGNAL(scopeDisplayEnable()), rig, SLOT(enableSpectrumDisplay())); connect(rig, SIGNAL(haveDataMode(bool)), this, SLOT(receiveDataModeStatus(bool))); - connect(this, SIGNAL(getDuplexMode()), rig, SLOT(getDuplexMode())); connect(this, SIGNAL(getTone()), rig, SLOT(getTone())); connect(this, SIGNAL(getTSQL()), rig, SLOT(getTSQL())); connect(this, SIGNAL(getRptAccessMode()), rig, SLOT(getRptAccessMode())); - //connect(this, SIGNAL(setDuplexMode(duplexMode)), rig, SLOT(setDuplexMode(duplexMode))); - //connect(rig, SIGNAL(haveDuplexMode(duplexMode)), this, SLOT(receiveDuplexMode(duplexMode))); - - connect(this, SIGNAL(getModInput(bool)), rig, SLOT(getModInput(bool))); - connect(rig, SIGNAL(haveModInput(rigInput,bool)), this, SLOT(receiveModInput(rigInput, bool))); - connect(this, SIGNAL(setModInput(rigInput, bool)), rig, SLOT(setModInput(rigInput,bool))); - - connect(rig, SIGNAL(haveSpectrumData(QByteArray, double, double)), this, SLOT(receiveSpectrumData(QByteArray, double, double))); - connect(rig, SIGNAL(haveSpectrumMode(spectrumMode)), this, SLOT(receiveSpectrumMode(spectrumMode))); + connect(this, SIGNAL(setScopeMode(spectrumMode)), rig, SLOT(setSpectrumMode(spectrumMode))); connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode())); @@ -189,7 +158,6 @@ void servermain::rigConnections() //connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode())); connect(this, SIGNAL(getScopeEdge()), rig, SLOT(getScopeEdge())); connect(this, SIGNAL(getScopeSpan()), rig, SLOT(getScopeSpan())); - connect(rig, SIGNAL(haveScopeSpan(freqt,bool)), this, SLOT(receiveSpectrumSpan(freqt,bool))); connect(this, SIGNAL(setScopeFixedEdge(double,double,unsigned char)), rig, SLOT(setSpectrumBounds(double,double,unsigned char))); connect(this, SIGNAL(setMode(unsigned char, unsigned char)), rig, SLOT(setMode(unsigned char, unsigned char))); @@ -225,37 +193,18 @@ void servermain::rigConnections() connect(this, SIGNAL(setSpectrumRefLevel(int)), rig, SLOT(setSpectrumRefLevel(int))); connect(this, SIGNAL(setModLevel(rigInput, unsigned char)), rig, SLOT(setModInputLevel(rigInput, unsigned char))); - // Levels: handle return on query: - connect(rig, SIGNAL(haveRfGain(unsigned char)), this, SLOT(receiveRfGain(unsigned char))); - connect(rig, SIGNAL(haveAfGain(unsigned char)), this, SLOT(receiveAfGain(unsigned char))); - connect(rig, SIGNAL(haveSql(unsigned char)), this, SLOT(receiveSql(unsigned char))); - connect(rig, SIGNAL(haveTxPower(unsigned char)), this, SLOT(receiveTxPower(unsigned char))); - connect(rig, SIGNAL(haveMicGain(unsigned char)), this, SLOT(receiveMicGain(unsigned char))); - connect(rig, SIGNAL(haveSpectrumRefLevel(int)), this, SLOT(receiveSpectrumRefLevel(int))); - connect(rig, SIGNAL(haveACCGain(unsigned char,unsigned char)), this, SLOT(receiveACCGain(unsigned char,unsigned char))); - connect(rig, SIGNAL(haveUSBGain(unsigned char)), this, SLOT(receiveUSBGain(unsigned char))); - connect(rig, SIGNAL(haveLANGain(unsigned char)), this, SLOT(receiveLANGain(unsigned char))); - - //Metering: - connect(this, SIGNAL(getMeters(meterKind)), rig, SLOT(getMeters(meterKind))); - connect(rig, SIGNAL(haveMeter(meterKind,unsigned char)), this, SLOT(receiveMeter(meterKind,unsigned char))); - // Rig and ATU info: connect(this, SIGNAL(startATU()), rig, SLOT(startATU())); connect(this, SIGNAL(setATU(bool)), rig, SLOT(setATU(bool))); connect(this, SIGNAL(getATUStatus()), rig, SLOT(getATUStatus())); connect(this, SIGNAL(getRigID()), rig, SLOT(getRigID())); - connect(rig, SIGNAL(haveATUStatus(unsigned char)), this, SLOT(receiveATUStatus(unsigned char))); connect(rig, SIGNAL(haveRigID(rigCapabilities)), this, SLOT(receiveRigID(rigCapabilities))); connect(this, SIGNAL(setAttenuator(unsigned char)), rig, SLOT(setAttenuator(unsigned char))); connect(this, SIGNAL(setPreamp(unsigned char)), rig, SLOT(setPreamp(unsigned char))); connect(this, SIGNAL(setAntenna(unsigned char, bool)), rig, SLOT(setAntenna(unsigned char, bool))); connect(this, SIGNAL(getPreamp()), rig, SLOT(getPreamp())); - connect(rig, SIGNAL(havePreamp(unsigned char)), this, SLOT(receivePreamp(unsigned char))); connect(this, SIGNAL(getAttenuator()), rig, SLOT(getAttenuator())); - connect(rig, SIGNAL(haveAttenuator(unsigned char)), this, SLOT(receiveAttenuator(unsigned char))); connect(this, SIGNAL(getAntenna()), rig, SLOT(getAntenna())); - connect(rig, SIGNAL(haveAntenna(unsigned char,bool)), this, SLOT(receiveAntennaSel(unsigned char,bool))); // Speech (emitted from rig speaker) @@ -263,7 +212,6 @@ void servermain::rigConnections() connect(this, SIGNAL(sayFrequency()), rig, SLOT(sayFrequency())); connect(this, SIGNAL(sayMode()), rig, SLOT(sayMode())); - // Date and Time: connect(this, SIGNAL(setTime(timekind)), rig, SLOT(setTime(timekind))); connect(this, SIGNAL(setDate(datekind)), rig, SLOT(setDate(datekind))); @@ -382,6 +330,12 @@ void servermain::findSerialPort() } } +void servermain::receiveStatusUpdate(QString text) +{ + std::cout << text.toLocal8Bit().toStdString() << "\n"; +} + + void servermain::receiveCommReady() { qInfo(logSystem()) << "Received CommReady!! "; @@ -752,17 +706,11 @@ void servermain::loadSettings() for (unsigned int i = 1; i < devices; i++) { info = audio->getDeviceInfo(i); if (info.outputChannels > 0) { - if (QString::fromStdString(info.name) == rxSetup.name) { - rxSetup.port = i; - } if (QString::fromStdString(info.name) == serverTxSetup.name) { serverTxSetup.port = i; } } if (info.inputChannels > 0) { - if (QString::fromStdString(info.name) == txSetup.name) { - txSetup.port = i; - } if (QString::fromStdString(info.name) == serverRxSetup.name) { serverRxSetup.port = i; } @@ -794,17 +742,11 @@ void servermain::loadSettings() { info = Pa_GetDeviceInfo(i); if (info->maxInputChannels > 0) { - if (QString::fromStdString(info.name) == rxSetup.name) { - rxSetup.port = i; - } if (QString::fromStdString(info.name) == serverTxSetup.name) { serverTxSetup.port = i; } } if (info->maxOutputChannels > 0) { - if (QString::fromStdString(info.name) == txSetup.name) { - txSetup.port = i; - } if (QString::fromStdString(info.name) == serverRxSetup.name) { serverRxSetup.port = i; } @@ -824,10 +766,6 @@ void servermain::loadSettings() // Enumerate audio devices, need to do before settings are loaded. const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { - if (deviceInfo.deviceName() == rxSetup.name) { - rxSetup.port = deviceInfo; - qInfo(logGui()) << "Setting Audio Output: " << rxSetup.name; - } if (deviceInfo.deviceName() == serverTxSetup.name) { serverTxSetup.port = deviceInfo; qInfo(logGui()) << "Setting Server Audio Input: " << serverTxSetup.name; @@ -836,10 +774,6 @@ void servermain::loadSettings() const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (const QAudioDeviceInfo& deviceInfo : audioInputs) { - if (deviceInfo.deviceName() == txSetup.name) { - txSetup.port = deviceInfo; - qInfo(logGui()) << "Setting Audio Input: " << txSetup.name; - } if (deviceInfo.deviceName() == serverRxSetup.name) { serverRxSetup.port = deviceInfo; qInfo(logGui()) << "Setting Server Audio Output: " << serverRxSetup.name; @@ -851,25 +785,6 @@ void servermain::loadSettings() } -quint64 servermain::roundFrequency(quint64 frequency, unsigned int tsHz) -{ - return frequency; -} - -quint64 servermain::roundFrequencyWithStep(quint64 frequency, int steps, unsigned int tsHz) -{ - quint64 rounded = 0; - - if(steps > 0) - { - frequency = frequency + (quint64)(steps*tsHz); - } else { - frequency = frequency - std::min((quint64)(abs(steps)*tsHz), frequency); - } - - return frequency; -} - void servermain:: getInitialRigState() { @@ -971,6 +886,13 @@ void servermain:: getInitialRigState() delayedCommand->start(); } +void servermain::receivePTTstatus(bool pttOn) +{ + // This is the only place where amTransmitting and the transmit button text should be changed: + //qInfo(logSystem()) << "PTT status: " << pttOn; + amTransmitting = pttOn; +} + void servermain::doCmd(commandtype cmddata) { @@ -1095,10 +1017,203 @@ void servermain::doCmd(commandtype cmddata) void servermain::doCmd(cmds cmd) { - + // Use this function to take action upon a command. + switch (cmd) + { + case cmdNone: + //qInfo(logSystem()) << "NOOP"; + break; + case cmdGetRigID: + emit getRigID(); + break; + case cmdGetRigCIV: + // if(!know rig civ already) + if (!haveRigCaps) + { + emit getRigCIV(); + issueDelayedCommand(cmdGetRigCIV); // This way, we stay here until we get an answer. + } + break; + case cmdGetFreq: + emit getFrequency(); + break; + case cmdGetMode: + emit getMode(); + break; + case cmdGetDataMode: + if (rigCaps.hasDataModes) + emit getDataMode(); + break; + case cmdSetModeFilter: + emit setMode(setModeVal, setFilterVal); + break; + case cmdSetDataModeOff: + break; + case cmdSetDataModeOn: + break; + case cmdGetRitEnabled: + emit getRitEnabled(); + break; + case cmdGetRitValue: + emit getRitValue(); + break; + case cmdGetModInput: + emit getModInput(false); + break; + case cmdGetModDataInput: + emit getModInput(true); + break; + case cmdGetCurrentModLevel: + // TODO: Add delay between these queries + emit getModInputLevel(currentModSrc); + emit getModInputLevel(currentModDataSrc); + break; + case cmdGetDuplexMode: + emit getDuplexMode(); + break; + case cmdGetTone: + emit getTone(); + break; + case cmdGetTSQL: + emit getTSQL(); + break; + case cmdGetDTCS: + emit getDTCS(); + break; + case cmdGetRptAccessMode: + emit getRptAccessMode(); + break; + case cmdDispEnable: + emit scopeDisplayEnable(); + break; + case cmdDispDisable: + emit scopeDisplayDisable(); + break; + case cmdGetSpectrumMode: + emit getScopeMode(); + break; + case cmdGetSpectrumSpan: + emit getScopeSpan(); + break; + case cmdSpecOn: + emit spectOutputEnable(); + break; + case cmdSpecOff: + emit spectOutputDisable(); + break; + case cmdGetRxGain: + emit getRfGain(); + break; + case cmdGetAfGain: + emit getAfGain(); + break; + case cmdGetSql: + emit getSql(); + break; + case cmdGetIFShift: + emit getIfShift(); + break; + case cmdGetTPBFInner: + emit getTPBFInner(); + break; + case cmdGetTPBFOuter: + emit getTPBFOuter(); + break; + case cmdGetTxPower: + emit getTxPower(); + break; + case cmdGetMicGain: + emit getMicGain(); + break; + case cmdGetSpectrumRefLevel: + emit getSpectrumRefLevel(); + break; + case cmdGetATUStatus: + if (rigCaps.hasATU) + emit getATUStatus(); + break; + case cmdStartATU: + if (rigCaps.hasATU) + emit startATU(); + break; + case cmdGetAttenuator: + emit getAttenuator(); + break; + case cmdGetPreamp: + emit getPreamp(); + break; + case cmdGetAntenna: + emit getAntenna(); + break; + case cmdScopeCenterMode: + emit setScopeMode(spectModeCenter); + break; + case cmdScopeFixedMode: + emit setScopeMode(spectModeFixed); + break; + case cmdGetPTT: + emit getPTT(); + break; + case cmdGetTxRxMeter: + if (amTransmitting) + emit getMeters(meterPower); + else + emit getMeters(meterS); + break; + case cmdGetSMeter: + if (!amTransmitting) + emit getMeters(meterS); + break; + case cmdGetCenterMeter: + if (!amTransmitting) + emit getMeters(meterCenter); + break; + case cmdGetPowerMeter: + if (amTransmitting) + emit getMeters(meterPower); + break; + case cmdGetSWRMeter: + if (amTransmitting) + emit getMeters(meterSWR); + break; + case cmdGetIdMeter: + emit getMeters(meterCurrent); + break; + case cmdGetVdMeter: + emit getMeters(meterVoltage); + break; + case cmdGetALCMeter: + if (amTransmitting) + emit getMeters(meterALC); + break; + case cmdGetCompMeter: + if (amTransmitting) + emit getMeters(meterComp); + break; + case cmdStartRegularPolling: + runPeriodicCommands = true; + break; + case cmdStopRegularPolling: + runPeriodicCommands = false; + break; + case cmdQueNormalSpeed: + if (usingLAN) + { + delayedCommand->setInterval(delayedCmdIntervalLAN_ms); + } + else { + delayedCommand->setInterval(delayedCmdIntervalSerial_ms); + } + break; + default: + qInfo(logSystem()) << __PRETTY_FUNCTION__ << "WARNING: Command fell through of type: " << (unsigned int)cmd; + break; + } } + + void servermain::sendRadioCommandLoop() { // Called by the periodicPollingTimer, see setInitialTiming() @@ -1336,24 +1451,6 @@ void servermain::initPeriodicCommands() // The commands are run using a timer, // and the timer is started by the delayed command cmdStartPeriodicTimer. - insertPeriodicCommand(cmdGetTxRxMeter, 128); - - insertSlowPeriodicCommand(cmdGetFreq, 128); - insertSlowPeriodicCommand(cmdGetMode, 128); - if(rigCaps.hasTransmit) - insertSlowPeriodicCommand(cmdGetPTT, 128); - insertSlowPeriodicCommand(cmdGetTxPower, 128); - insertSlowPeriodicCommand(cmdGetRxGain, 128); - if(rigCaps.hasAttenuator) - insertSlowPeriodicCommand(cmdGetAttenuator, 128); - if(rigCaps.hasTransmit) - insertSlowPeriodicCommand(cmdGetPTT, 128); - if(rigCaps.hasPreamp) - insertSlowPeriodicCommand(cmdGetPreamp, 128); - if (rigCaps.hasRXAntenna) { - insertSlowPeriodicCommand(cmdGetAntenna, 128); - } - insertSlowPeriodicCommand(cmdGetDuplexMode, 128); } void servermain::insertPeriodicCommand(cmds cmd, unsigned char priority) @@ -1407,302 +1504,15 @@ void servermain::insertSlowPeriodicCommand(cmds cmd, unsigned char priority) } } -void servermain::receiveFreq(freqt freqStruct) + +void servermain::handlePttLimit() { - - qint64 tnow_ms = QDateTime::currentMSecsSinceEpoch(); - if(tnow_ms - lastFreqCmdTime_ms > delayedCommand->interval() * 2) - { - freq = freqStruct; - } else { - qDebug(logSystem()) << "Rejecting stale frequency: " << freqStruct.Hz << " Hz, delta time ms = " << tnow_ms - lastFreqCmdTime_ms\ - << ", tnow_ms " << tnow_ms << ", last: " << lastFreqCmdTime_ms; - } + // transmission time exceeded! + std::cout << "Transmit timeout at 3 minutes. Sending PTT OFF command now.\n"; + issueCmdUniquePriority(cmdSetPTT, false); + issueDelayedCommand(cmdGetPTT); } -void servermain::receivePTTstatus(bool pttOn) -{ - // This is the only place where amTransmitting and the transmit button text should be changed: - //qInfo(logSystem()) << "PTT status: " << pttOn; - amTransmitting = pttOn; -} - -void servermain::changeTxBtn() -{ -} - - -void servermain::receiveDataModeStatus(bool dataEnabled) -{ -} - -void servermain::checkFreqSel() -{ - if(freqTextSelected) - { - freqTextSelected = false; - } -} - -void servermain::changeMode(mode_kind mode) -{ - bool dataOn = false; - if(((unsigned char) mode >> 4) == 0x08) - { - dataOn = true; - mode = (mode_kind)((int)mode & 0x0f); - } - - changeMode(mode, dataOn); -} - -void servermain::changeMode(mode_kind mode, bool dataOn) -{ -} - -void servermain::receiveBandStackReg(freqt freqGo, char mode, char filter, bool dataOn) -{ - // read the band stack and apply by sending out commands - - qInfo(logSystem()) << __func__ << "BSR received into main: Freq: " << freqGo.Hz << ", mode: " << (unsigned int)mode << ", filter: " << (unsigned int)filter << ", data mode: " << dataOn; - //emit setFrequency(0,freq); - issueCmd(cmdSetFreq, freqGo); - setModeVal = (unsigned char) mode; - setFilterVal = (unsigned char) filter; - - issueDelayedCommand(cmdSetModeFilter); - freq = freqGo; - - if(dataOn) - { - issueDelayedCommand(cmdSetDataModeOn); - } else { - issueDelayedCommand(cmdSetDataModeOff); - } - //issueDelayedCommand(cmdGetFreq); - //issueDelayedCommand(cmdGetMode); - -} - -void servermain::bandStackBtnClick() -{ -} - -void servermain::receiveRfGain(unsigned char level) -{ -} - -void servermain::receiveAfGain(unsigned char level) -{ -} - -void servermain::receiveSql(unsigned char level) -{ -} - -void servermain::receiveIFShift(unsigned char level) -{ -} - -void servermain::receiveTBPFInner(unsigned char level) -{ -} - -void servermain::receiveTBPFOuter(unsigned char level) -{ -} - -void servermain::setRadioTimeDatePrep() -{ - if(!waitingToSetTimeDate) - { - // 1: Find the current time and date - QDateTime now; - now = QDateTime::currentDateTime(); - now.setTime(QTime::currentTime()); - - int second = now.time().second(); - - // 2: Find how many mseconds until next minute - int msecdelay = QTime::currentTime().msecsTo( QTime::currentTime().addSecs(60-second) ); - - // 3: Compute time and date at one minute later - QDateTime setpoint = now.addMSecs(msecdelay); // at HMS or posibly HMS + some ms. Never under though. - - // 4: Prepare data structs for the time at one minute later - timesetpoint.hours = (unsigned char)setpoint.time().hour(); - timesetpoint.minutes = (unsigned char)setpoint.time().minute(); - datesetpoint.day = (unsigned char)setpoint.date().day(); - datesetpoint.month = (unsigned char)setpoint.date().month(); - datesetpoint.year = (uint16_t)setpoint.date().year(); - unsigned int utcOffsetSeconds = (unsigned int)abs(setpoint.offsetFromUtc()); - bool isMinus = setpoint.offsetFromUtc() < 0; - utcsetting.hours = utcOffsetSeconds / 60 / 60; - utcsetting.minutes = (utcOffsetSeconds - (utcsetting.hours*60*60) ) / 60; - utcsetting.isMinus = isMinus; - - timeSync->setInterval(msecdelay); - timeSync->setSingleShot(true); - - // 5: start one-shot timer for the delta computed in #2. - timeSync->start(); - waitingToSetTimeDate = true; - } -} - -void servermain::setRadioTimeDateSend() -{ - // Issue priority commands for UTC offset, date, and time - // UTC offset must come first, otherwise the radio may "help" and correct for any changes. - - issueCmd(cmdSetTime, timesetpoint); - issueCmd(cmdSetDate, datesetpoint); - issueCmd(cmdSetUTCOffset, utcsetting); - waitingToSetTimeDate = false; -} - - -void servermain::receiveTxPower(unsigned char power) -{ -} - -void servermain::receiveMicGain(unsigned char gain) -{ - processModLevel(inputMic, gain); -} - -void servermain::processModLevel(rigInput source, unsigned char level) -{ - rigInput currentIn; - if(usingDataMode) - { - currentIn = currentModDataSrc; - } else { - currentIn = currentModSrc; - } - - switch(source) - { - case inputMic: - micGain = level; - break; - case inputACC: - accGain = level; - break; - - case inputACCA: - accAGain = level; - break; - - case inputACCB: - accBGain = level; - break; - - case inputUSB: - usbGain = level; - break; - - case inputLAN: - lanGain = level; - break; - - default: - break; - } - -} - -void servermain::receiveModInput(rigInput input, bool dataOn) -{ -} - -void servermain::receiveACCGain(unsigned char level, unsigned char ab) -{ - if(ab==1) - { - processModLevel(inputACCB, level); - } else { - if(rigCaps.model == model7850) - { - processModLevel(inputACCA, level); - } else { - processModLevel(inputACC, level); - } - } -} - -void servermain::receiveUSBGain(unsigned char level) -{ - processModLevel(inputUSB, level); -} - -void servermain::receiveLANGain(unsigned char level) -{ - processModLevel(inputLAN, level); -} - -void servermain::receiveMeter(meterKind inMeter, unsigned char level) -{ - -} - -void servermain::receiveCompLevel(unsigned char compLevel) -{ - (void)compLevel; -} - -void servermain::receiveMonitorGain(unsigned char monitorGain) -{ - (void)monitorGain; -} - -void servermain::receiveVoxGain(unsigned char voxGain) -{ - (void)voxGain; -} - -void servermain::receiveAntiVoxGain(unsigned char antiVoxGain) -{ - (void)antiVoxGain; -} - - -void servermain::receiveSpectrumRefLevel(int level) -{ -} - -void servermain::changeModLabelAndSlider(rigInput source) -{ - changeModLabel(source, true); -} - -void servermain::changeModLabel(rigInput input) -{ - changeModLabel(input, false); -} - -void servermain::changeModLabel(rigInput input, bool updateLevel) -{ -} - -void servermain::processChangingCurrentModLevel(unsigned char level) -{ - // slider moved, so find the current mod and issue the level set command. - issueCmd(cmdSetModLevel, level); -} -void servermain::receivePreamp(unsigned char pre) -{ -} - -void servermain::receiveAttenuator(unsigned char att) -{ -} - -void servermain::receiveAntennaSel(unsigned char ant, bool rx) -{ -} - - void servermain::calculateTimingParameters() { // Function for calculating polling parameters. @@ -1768,54 +1578,6 @@ void servermain::powerRigOff() } -void servermain::receiveRITStatus(bool ritEnabled) -{ -} - -void servermain::receiveRITValue(int ritValHz) -{ -} - -servermain::cmds servermain::meterKindToMeterCommand(meterKind m) -{ - cmds c; - switch(m) - { - case meterNone: - c = cmdNone; - break; - case meterS: - c = cmdGetSMeter; - break; - case meterCenter: - c = cmdGetCenterMeter; - break; - case meterPower: - c = cmdGetPowerMeter; - break; - case meterSWR: - c = cmdGetSWRMeter; - break; - case meterALC: - c = cmdGetALCMeter; - break; - case meterComp: - c = cmdGetCompMeter; - break; - case meterCurrent: - c = cmdGetIdMeter; - break; - case meterVoltage: - c = cmdGetVdMeter; - break; - default: - c = cmdNone; - break; - } - - return c; -} - void servermain::handleCtrlC(int sig) { if (sig == 2) { diff --git a/servermain.h b/servermain.h index 4d79004..5437c8d 100644 --- a/servermain.h +++ b/servermain.h @@ -166,59 +166,18 @@ signals: private slots: void receiveCommReady(); - void receiveFreq(freqt); void receivePTTstatus(bool pttOn); - void receiveDataModeStatus(bool dataOn); - void receiveBandStackReg(freqt f, char mode, char filter, bool dataOn); // freq, mode, (filter,) datamode - void receiveRITStatus(bool ritEnabled); - void receiveRITValue(int ritValHz); - void receiveModInput(rigInput input, bool dataOn); - //void receiveDuplexMode(duplexMode dm); - - - // Levels: - void receiveRfGain(unsigned char level); - void receiveAfGain(unsigned char level); - void receiveSql(unsigned char level); - void receiveIFShift(unsigned char level); - void receiveTBPFInner(unsigned char level); - void receiveTBPFOuter(unsigned char level); - // 'change' from data in transceiver controls window: - void receiveTxPower(unsigned char power); - void receiveMicGain(unsigned char gain); - void receiveCompLevel(unsigned char compLevel); - void receiveMonitorGain(unsigned char monitorGain); - void receiveVoxGain(unsigned char voxGain); - void receiveAntiVoxGain(unsigned char antiVoxGain); - void receiveSpectrumRefLevel(int level); - void receiveACCGain(unsigned char level, unsigned char ab); - void receiveUSBGain(unsigned char level); - void receiveLANGain(unsigned char level); - - // Meters: - void receiveMeter(meterKind meter, unsigned char level); -// void receiveSMeter(unsigned char level); -// void receivePowerMeter(unsigned char level); -// void receiveALCMeter(unsigned char level); -// void receiveCompMeter(unsigned char level); - - - void receivePreamp(unsigned char pre); - void receiveAttenuator(unsigned char att); - void receiveAntennaSel(unsigned char ant, bool rx); void receiveRigID(rigCapabilities rigCaps); void receiveFoundRigID(rigCapabilities rigCaps); void receiveSerialPortError(QString port, QString errorText); void sendRadioCommandLoop(); void receiveBaudRate(quint32 baudrate); - void setRadioTimeDateSend(); - + void handlePttLimit(); + void receiveStatusUpdate(QString text); private: - Ui::wfmain *ui; - void closeEvent(QCloseEvent *event); QSettings *settings=Q_NULLPTR; void loadSettings(); @@ -402,29 +361,12 @@ private: colors defaultColors; - void setDefaultColors(); // populate with default values - void useColors(); // set the plot up void setDefPrefs(); // populate default values to default prefs - void setTuningSteps(); - quint64 roundFrequency(quint64 frequency, unsigned int tsHz); - quint64 roundFrequencyWithStep(quint64 oldFreq, int steps,\ - unsigned int tsHz); - - void changeTxBtn(); void issueDelayedCommand(cmds cmd); void issueDelayedCommandPriority(cmds cmd); void issueDelayedCommandUnique(cmds cmd); - void processModLevel(rigInput source, unsigned char level); - - void processChangingCurrentModLevel(unsigned char level); - - void changeModLabel(rigInput source); - void changeModLabel(rigInput source, bool updateLevel); - - void changeModLabelAndSlider(rigInput source); - // Fast command queue: void initPeriodicCommands(); void insertPeriodicCommand(cmds cmd, unsigned char priority); @@ -434,8 +376,6 @@ private: void insertSlowPeriodicCommand(cmds cmd, unsigned char priority); void calculateTimingParameters(); - void changeMode(mode_kind mode); - void changeMode(mode_kind mode, bool dataOn); cmds meterKindToMeterCommand(meterKind m); From 55baec6100095dd45669a0774caf42ead6bc720b Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 18 Jan 2022 00:14:37 +0000 Subject: [PATCH 075/323] Only show server status message once --- servermain.cpp | 5 ++++- servermain.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/servermain.cpp b/servermain.cpp index aa0de88..b35e810 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -332,7 +332,10 @@ void servermain::findSerialPort() void servermain::receiveStatusUpdate(QString text) { - std::cout << text.toLocal8Bit().toStdString() << "\n"; + if (text != lastMessage) { + std::cout << text.toLocal8Bit().toStdString() << "\n"; + lastMessage = text; + } } diff --git a/servermain.h b/servermain.h index 5437c8d..080f32a 100644 --- a/servermain.h +++ b/servermain.h @@ -195,6 +195,7 @@ private: QTimer * pttTimer; uint16_t loopTickCounter; uint16_t slowCmdNum; + QString lastMessage=""; void makeRig(); void rigConnections(); From bcadb2176bda078a7f03db8f03e44de030ce4cc4 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 18 Jan 2022 00:18:52 +0000 Subject: [PATCH 076/323] Remove unnecessary signal/slots --- servermain.cpp | 10 +++++----- servermain.h | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index b35e810..e35afdb 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -142,7 +142,6 @@ void servermain::rigConnections() connect(this, SIGNAL(getPTT()), rig, SLOT(getPTT())); connect(this, SIGNAL(getDebug()), rig, SLOT(getDebug())); - connect(rig, SIGNAL(haveDataMode(bool)), this, SLOT(receiveDataModeStatus(bool))); connect(this, SIGNAL(getDuplexMode()), rig, SLOT(getDuplexMode())); connect(this, SIGNAL(getTone()), rig, SLOT(getTone())); @@ -448,10 +447,6 @@ void servermain::setInitialTiming() pttTimer->setSingleShot(true); connect(pttTimer, SIGNAL(timeout()), this, SLOT(handlePttLimit())); - timeSync = new QTimer(this); - connect(timeSync, SIGNAL(timeout()), this, SLOT(setRadioTimeDateSend())); - waitingToSetTimeDate = false; - lastFreqCmdTime_ms = QDateTime::currentMSecsSinceEpoch() - 5000; // 5 seconds ago } void servermain::setServerToPrefs() @@ -1580,6 +1575,11 @@ void servermain::powerRigOff() emit sendPowerOff(); } +void servermain::receiveStateInfo(rigstate* state) +{ + qInfo("Setting rig state for wfmain"); + rigState = state; +} void servermain::handleCtrlC(int sig) { diff --git a/servermain.h b/servermain.h index 080f32a..e96f19d 100644 --- a/servermain.h +++ b/servermain.h @@ -176,6 +176,7 @@ private slots: void handlePttLimit(); void receiveStatusUpdate(QString text); + void receiveStateInfo(rigstate* state); private: QSettings *settings=Q_NULLPTR; From 9f059c9e7376c8540f798d4560f9d93763434799 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 18 Jan 2022 08:58:02 +0000 Subject: [PATCH 077/323] Fix portaudio build --- servermain.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index e35afdb..4b7774c 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -704,12 +704,12 @@ void servermain::loadSettings() for (unsigned int i = 1; i < devices; i++) { info = audio->getDeviceInfo(i); if (info.outputChannels > 0) { - if (QString::fromStdString(info.name) == serverTxSetup.name) { + if (serverTxSetup.name == info->name) { serverTxSetup.port = i; } } if (info.inputChannels > 0) { - if (QString::fromStdString(info.name) == serverRxSetup.name) { + if (serverRxSetup.name == info->name) { serverRxSetup.port = i; } } @@ -740,12 +740,12 @@ void servermain::loadSettings() { info = Pa_GetDeviceInfo(i); if (info->maxInputChannels > 0) { - if (QString::fromStdString(info.name) == serverTxSetup.name) { + if (serverTxSetup.name == info->name) { serverTxSetup.port = i; } } if (info->maxOutputChannels > 0) { - if (QString::fromStdString(info.name) == serverRxSetup.name) { + if (serverRxSetup.name == info->name) { serverRxSetup.port = i; } } From 4e086ac2200620d1c7b3d14c7b89d3457444cb91 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 18 Jan 2022 09:03:41 +0000 Subject: [PATCH 078/323] Fix building both wfview and wfserver together --- main.cpp | 11 +-- wfmain.h | 2 +- wfserver.pro | 2 +- wfserver.vcxproj | 202 +++++++++++++++++++++++---------------- wfserver.vcxproj.filters | 22 ++++- wfview.pro | 2 + wfview.vcxproj | 8 +- 7 files changed, 152 insertions(+), 97 deletions(-) diff --git a/main.cpp b/main.cpp index ed95e7f..f5e91c7 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,4 @@ -//#undef WFSERVER -#ifdef WFSERVER +#ifdef BUILD_WFSERVER #include #else #include @@ -20,7 +19,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt int main(int argc, char *argv[]) { -#ifdef WFSERVER +#ifdef BUILD_WFSERVER QCoreApplication a(argc, argv); #else QApplication a(argc, argv); @@ -49,7 +48,7 @@ int main(int argc, char *argv[]) const QString helpText = QString("\nUsage: -p --port /dev/port, -h --host remotehostname, -c --civ 0xAddr, -l --logfile filename.log, -s --settings filename.ini, -d --debug, -v --version\n"); // TODO... -#ifdef WFSERVER +#ifdef BUILD_WFSERVER const QString version = QString("wfserver version: %1 (Git:%2 on %3 at %4 by %5@%6)\nOperating System: %7 (%8)\nBuild Qt Version %9. Current Qt Version: %10\n") .arg(QString(WFVIEW_VERSION)) .arg(GITSHORT).arg(__DATE__).arg(__TIME__).arg(UNAME).arg(HOST) @@ -140,7 +139,7 @@ int main(int argc, char *argv[]) qDebug(logSystem()) << QString("remote host as set by parser: %1").arg(hostCL); qDebug(logSystem()) << QString("CIV as set by parser: %1").arg(civCL); -#ifdef WFSERVER +#ifdef BUILD_WFSERVER servermain *w = new servermain(serialPortCL, hostCL, settingsFile); #else @@ -189,7 +188,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt } // Write to the output category of the message and the message itself out << context.category << ": " << msg << "\n"; -#ifdef WFSERVER +#ifdef BUILD_WFSERVER std::cout << msg.toLocal8Bit().toStdString() << "\n"; #endif out.flush(); // Clear the buffered data diff --git a/wfmain.h b/wfmain.h index 6a8bd71..34c3ea6 100644 --- a/wfmain.h +++ b/wfmain.h @@ -1,4 +1,4 @@ -#ifdef WFSERVER +#ifdef BUILD_WFSERVER #include "servermain.h" #else diff --git a/wfserver.pro b/wfserver.pro index 993c9cc..8d6dbed 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -15,7 +15,7 @@ CONFIG += console DEFINES += WFVIEW_VERSION=\\\"1.2d\\\" -DEFINES += WFSERVER +DEFINES += BUILD_WFSERVER CONFIG(debug, release|debug) { # For Debug builds only: diff --git a/wfserver.vcxproj b/wfserver.vcxproj index 925094b..e78371d 100644 --- a/wfserver.vcxproj +++ b/wfserver.vcxproj @@ -16,8 +16,7 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild - + $(MSBuildProjectDirectory)\QtMsBuild v142 @@ -37,10 +36,7 @@ debug\ wfserver - - - - + @@ -48,34 +44,8 @@ - - - - - - debug\ - debug\ - wfserver - true - - - release\ - release\ - wfserver - true - false - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - - + debug\debug\wfservertruerelease\release\wfservertruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport + .;..\opus\include;resampler;release;/include;%(AdditionalIncludeDirectories) @@ -87,16 +57,14 @@ Sync release\ MaxSpeed - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="28812be";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - - + MultiThreadedDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories) @@ -117,23 +85,9 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"28812be\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp .;..\opus\include;resampler;debug;/include;%(AdditionalIncludeDirectories) @@ -145,14 +99,13 @@ Sync debug\ Disabled - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="28812be";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories) @@ -171,23 +124,9 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"28812be\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp @@ -207,33 +146,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document true @@ -250,6 +257,22 @@ release\moc_predefs.h;%(Outputs) true + + + + + + + + + + + + + + + + @@ -282,16 +305,30 @@ - resources - resources - + + + + + + + + + + resourcesresources - style - style - + + + + + + + + + + stylestyle @@ -305,9 +342,6 @@ - - - - + \ No newline at end of file diff --git a/wfserver.vcxproj.filters b/wfserver.vcxproj.filters index e2466f8..47cfe64 100644 --- a/wfserver.vcxproj.filters +++ b/wfserver.vcxproj.filters @@ -140,12 +140,32 @@ + + + + Generated Files Generated Files + + + + + + + + + + + + + + + + @@ -279,6 +299,6 @@ - + \ No newline at end of file diff --git a/wfview.pro b/wfview.pro index f105bb3..b431678 100644 --- a/wfview.pro +++ b/wfview.pro @@ -13,6 +13,8 @@ TEMPLATE = app DEFINES += WFVIEW_VERSION=\\\"1.2d\\\" +DEFINES += BUILD_WFVIEW + CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new diff --git a/wfview.vcxproj b/wfview.vcxproj index bd3677c..0ddaebd 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="28812be";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -85,7 +85,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"28812be\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -99,7 +99,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="28812be";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -124,7 +124,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"28812be\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h From e4cc2962b3cbdd5305f5118345323051591c088f Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 18 Jan 2022 09:06:23 +0000 Subject: [PATCH 079/323] Fix wfserver linux build --- wfserver.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wfserver.pro b/wfserver.pro index 8d6dbed..26808da 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -115,7 +115,7 @@ CONFIG(debug, release|debug) { win32:LIBS += -L../opus/win32/VS2015/Win32/Release/ -lopus } -linux:LIBS += -L./ -l$$QCPLIB -lopus +linux:LIBS += -L./ -lopus macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread -lopus !linux:INCLUDEPATH += ../opus/include From 440429be9f1abf457e9a033d72d7faaf238166c1 Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Thu, 20 Jan 2022 23:26:46 -0800 Subject: [PATCH 080/323] Adjusted window size for radios without spectrum. Thanks K5TUX. --- wfmain.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 09a9e54..00a1909 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -865,13 +865,11 @@ void wfmain::updateSizes(int tabIndex) ui->tabWidget->widget(0)->setMaximumSize(ui->tabWidget->widget(0)->minimumSizeHint()); ui->tabWidget->widget(0)->adjustSize(); // tab this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - this->setMaximumSize(QSize(929, 270)); - this->setMinimumSize(QSize(929, 270)); + this->setMaximumSize(QSize(1024,350)); + this->setMinimumSize(QSize(1024,350)); resize(minimumSize()); adjustSize(); // main window - adjustSize(); - } else if(tabIndex==0 && rigCaps.hasSpectrum) { // At main tab (0) and we have spectrum: ui->tabWidget->widget(0)->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); @@ -882,8 +880,9 @@ void wfmain::updateSizes(int tabIndex) // At some other tab, with or without spectrum: ui->tabWidget->widget(tabIndex)->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - this->setMinimumSize(QSize(994, 455)); // not large enough for settings tab + this->setMinimumSize(QSize(1024, 600)); // not large enough for settings tab this->setMaximumSize(QSize(65535,65535)); + adjustSize(); } } else { ui->tabWidget->widget(tabIndex)->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); @@ -5751,9 +5750,21 @@ void wfmain::on_debugBtn_clicked() qInfo(logSystem()) << "Debug button pressed."; // issueDelayedCommand(cmdGetRigID); //emit getRigCIV(); - trxadj->show(); + //trxadj->show(); //setRadioTimeDatePrep(); //wf->setInteraction(QCP::iRangeZoom, true); //wf->setInteraction(QCP::iRangeDrag, true); + bool ok; + int height = QInputDialog::getInt(this, "wfview Radio Polling Setup", "Poll Timing Interval (ms)", 350, 1, 500, 1, &ok ); + + this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + this->setMaximumSize(QSize(1025,height)); + this->setMinimumSize(QSize(1025,height)); + //this->setMaximumSize(QSize(929, 270)); + //this->setMinimumSize(QSize(929, 270)); + + resize(minimumSize()); + adjustSize(); // main window + adjustSize(); } From fcc7c9a5dc49a0c59e2a30bd67d5121d6c819c94 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 21 Jan 2022 19:23:32 +0000 Subject: [PATCH 081/323] Lots of changes, mainly for multi-radio support --- packettypes.h | 99 +++++++----- rigcommander.cpp | 16 ++ rigcommander.h | 4 + selectradio.cpp | 42 +++++ selectradio.h | 36 +++++ selectradio.ui | 87 ++++++++++ servermain.cpp | 1 + udphandler.cpp | 79 ++++++--- udphandler.h | 8 +- udpserver.cpp | 79 +++++---- udpserver.h | 3 +- wfmain.cpp | 12 +- wfmain.h | 5 +- wfview.pro | 4 +- wfview.vcxproj | 357 +++++++++++------------------------------ wfview.vcxproj.filters | 56 ++----- 16 files changed, 472 insertions(+), 416 deletions(-) create mode 100644 selectradio.cpp create mode 100644 selectradio.h create mode 100644 selectradio.ui diff --git a/packettypes.h b/packettypes.h index b21a4f1..328b8df 100644 --- a/packettypes.h +++ b/packettypes.h @@ -1,6 +1,7 @@ #ifndef PACKETTYPES_H #define PACKETTYPES_H #include +#include #pragma pack(push, 1) @@ -29,7 +30,8 @@ #define LOGIN_RESPONSE_SIZE 0x60 #define LOGIN_SIZE 0x80 #define CONNINFO_SIZE 0x90 -#define CAPABILITIES_SIZE 0xA8 +#define CAPABILITIES_SIZE 0x42 +#define RADIO_CAP_SIZE 0x66 // Variable size packets + payload #define CIV_SIZE 0x15 @@ -172,9 +174,8 @@ typedef union token_packet { quint32 token; // 0x1c char unusedd[7]; // 0x20 quint16 commoncap; // 0x27 - char unuseddd[2]; // 0x29 - char identa; // 0x2b - quint32 identb; // 0x2c + char unuseddd; // 0x29 + char macaddress[6]; // 0x2a quint32 response; // 0x30 char unusede[12]; // 0x34 }; @@ -201,9 +202,8 @@ typedef union status_packet { char unusedd[6]; // 0x20 quint16 unknown; // 0x26 char unusede; // 0x28 - char unusedf[2]; // 0x29 - char identa; // 0x2b - quint32 identb; // 0x2c + char unusedf; // 0x29 + quint8 macaddress[6]; // 0x2a quint32 error; // 0x30 char unusedg[12]; // 0x34 char disc; // 0x40 @@ -286,14 +286,18 @@ typedef union conninfo_packet { char unusedb; // 0x19 quint16 tokrequest; // 0x1a quint32 token; // 0x1c - quint16 authstartid; // 0x20 - char unusedd[5]; // 0x22 - quint32 commoncap; // 0x27 - char identa; // 0x2b - quint32 identb; // 0x2c - char unusedf[16]; // 0x30 - char name[16]; // 0x40 - char unusedg[16]; // 0x50 + union { + struct { + quint16 authstartid; // 0x20 + char unusedg[5]; // 0x22 + quint16 commoncap; // 0x27 + char unusedh; // 0x29 + char macaddress[6]; // 0x2a + }; + GUID guid; // 0x20 + }; + char unusedab[16]; // 0x30 + char name[32]; // 0x40 union { // This contains differences between the send/receive packet struct { // Receive quint32 busy; // 0x60 @@ -321,6 +325,42 @@ typedef union conninfo_packet { char packet[CONNINFO_SIZE]; } *conninfo_packet_t; + +// 0x64 length radio capabilities part of cap packet. +/* + };*/ +typedef union radio_cap_packet { + struct + { + union { + struct { + char unusede[7]; // 0x0 + quint16 commoncap; // 0x0 + char unused; // 0x0 + char macaddress[6]; // 0x0 + }; + GUID guid; // 0x0 + }; + char name[32]; // 0x10 + char audio[32]; // 0x30 + quint16 conntype; // 0x50 + char civ; // 0x52 + quint16 rxsample; // 0x53 + quint16 txsample; // 0x55 + quint8 enablea; // 0x57 + quint8 enableb; // 0x58 + quint8 enablec; // 0x59 + quint32 baudrate; // 0x5a + quint16 capf; // 0x5e + char unusedi; // 0x60 + quint16 capg; // 0x61 + char unusedj[3]; // 0x66 + }; + char packet[RADIO_CAP_SIZE]; +} *radio_cap_packet_t; + + + // 0xA8 length capabilities packet typedef union capabilities_packet { struct @@ -330,41 +370,22 @@ typedef union capabilities_packet { quint16 seq; // 0x06 quint32 sentid; // 0x08 quint32 rcvdid; // 0x0c - char unuseda[3]; // 0x10 - quint16 code; // 0x13 - quint16 res; // 0x15 - quint8 innerseq; // 0x17 + char unuseda[2]; // 0x10 + quint16 payloadsize; // 0x12 + quint16 res; // 0x14 + quint16 innerseq; // 0x16 char unusedb; // 0x18 char unusedc; // 0x19 quint16 tokrequest; // 0x1a quint32 token; // 0x1c char unusedd[33]; // 0x20 - char capa; // 0x41 - char unusede[7]; // 0x42 - quint16 commoncap; // 0x49 - char unused; // 0x4b - char macaddress[6]; // 0x4c - char name[32]; // 0x52 - char audio[32]; // 0x72 - quint16 conntype; // 0x92 - char civ; // 0x94 - quint16 rxsample; // 0x95 - quint16 txsample; // 0x97 - quint8 enablea; // 0x99 - quint8 enableb; // 0x9a - quint8 enablec; // 0x9b - quint32 baudrate; // 0x9c - quint16 capf; // 0xa0 - char unusedi; // 0xa2 - quint16 capg; // 0xa3 - char unusedj[3]; // 0xa5 + char numradios; // 0x41 }; char packet[CAPABILITIES_SIZE]; } *capabilities_packet_t; - #pragma pack(pop) diff --git a/rigcommander.cpp b/rigcommander.cpp index d497c0d..bab57b4 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -138,6 +138,8 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud connect(this, SIGNAL(discoveredRigID(rigCapabilities)), ptty, SLOT(receiveFoundRigID(rigCapabilities))); + connect(udp, SIGNAL(requestRadioSelection(QList)), this, SLOT(radioSelection(QList))); + connect(udp, SIGNAL(setRadioUsage(int, QString, QString)), this, SLOT(radioUsage(int, QString, QString))); emit haveAfGain(rxSetup.localAFgain); localVolume = rxSetup.localAFgain; } @@ -4273,6 +4275,20 @@ void rigCommander::sendState() emit stateInfo(&state); } +void rigCommander::radioSelection(QList radios) +{ + for (const radio_cap_packet radio : radios) + { + qInfo(logSystem()) << "Radio Name" << radio.name; + } + emit requestRadioSelection(radios); +} + +void rigCommander::radioUsage(int radio, QString user, QString ip) { + emit setRadioUsage(radio, user, ip); +} + + void rigCommander::stateUpdated() { // A remote process has updated the rigState diff --git a/rigcommander.h b/rigcommander.h index 6da1744..c22c7a3 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -273,6 +273,8 @@ public slots: // Housekeeping: void handleStatusUpdate(const QString text); + void radioSelection(QList radios); + void radioUsage(int radio, QString name, QString ip); void sendState(); void getDebug(); @@ -365,6 +367,8 @@ signals: void stateInfo(rigstate* state); // Housekeeping: + void requestRadioSelection(QList radios); + void setRadioUsage(int radio, QString user, QString ip); void getMoreDebug(); void finished(); diff --git a/selectradio.cpp b/selectradio.cpp new file mode 100644 index 0000000..a815190 --- /dev/null +++ b/selectradio.cpp @@ -0,0 +1,42 @@ +#include "logcategories.h" +#include "selectradio.h" +#include "ui_selectradio.h" + + +selectRadio::selectRadio(QWidget* parent) : + QDialog(parent), + ui(new Ui::selectRadio) +{ + ui->setupUi(this); +} + +selectRadio::~selectRadio() +{ + delete ui; +} + +void selectRadio::populate(QList radios) +{ + ui->table->clearContents(); + for (int row = 0; row < radios.count(); row++) { + ui->table->insertRow(ui->table->rowCount()); + ui->table->setItem(ui->table->rowCount() - 1, 0, new QTableWidgetItem(QString(radios[row].name))); + ui->table->setItem(ui->table->rowCount() - 1, 1, new QTableWidgetItem(QString("%1").arg((unsigned char)radios[row].civ, 2, 16, QLatin1Char('0')).toUpper())); + ui->table->setItem(ui->table->rowCount() - 1, 2, new QTableWidgetItem(QString::number(qFromBigEndian(radios[row].baudrate)))); + } +} + +void selectRadio::setInUse(int radio, QString user, QString ip) +{ + ui->table->setItem(radio, 3, new QTableWidgetItem(user)); + ui->table->setItem(radio, 4, new QTableWidgetItem(ip)); +} + +void selectRadio::on_table_cellClicked(int row, int col) { + qInfo() << "Clicked on " << row << "," << col; + ui->table->selectRow(row); + emit selectedRadio(row); +} +void selectRadio::on_table_sectionClicked(int index) { + qInfo() << "Section Clicked" << index; +} diff --git a/selectradio.h b/selectradio.h new file mode 100644 index 0000000..d823670 --- /dev/null +++ b/selectradio.h @@ -0,0 +1,36 @@ +#ifndef SELECTRADIO_H +#define SELECTRADIO_H + +#include +#include +#include +#include +#include "packettypes.h" + +namespace Ui { + class selectRadio; +} + +class selectRadio : public QDialog +{ + Q_OBJECT + +public: + explicit selectRadio(QWidget* parent = 0); + ~selectRadio(); + void populate(QList radios); + +public slots: + void on_table_cellClicked(int row, int col); + void on_table_sectionClicked(int index); + void setInUse(int radio, QString user, QString ip); + +signals: + void selectedRadio(int radio); + +private: + Ui::selectRadio* ui; + +}; + +#endif // SELECTRADIO_H diff --git a/selectradio.ui b/selectradio.ui new file mode 100644 index 0000000..e73b123 --- /dev/null +++ b/selectradio.ui @@ -0,0 +1,87 @@ + + + selectRadio + + + + 0 + 0 + 680 + 498 + + + + Select Radio From List + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + true + + + + Rig Name + + + + + CI-V + + + + + Baud Rate + + + + + Current User + + + + + User IP Address + + + + + + + + + + + diff --git a/servermain.cpp b/servermain.cpp index 4b7774c..5e0a844 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -28,6 +28,7 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType(); + qRegisterMetaType>() ; signal(SIGINT, handleCtrlC); diff --git a/udphandler.cpp b/udphandler.cpp index a06933b..651322a 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -322,8 +322,29 @@ void udpHandler::dataReceived() } case (CONNINFO_SIZE): { + // Once connected, the server will send a conninfo packet for each radio that is connected + conninfo_packet_t in = (conninfo_packet_t)r.constData(); - if (in->type != 0x01) { + QHostAddress ip = QHostAddress(qToBigEndian(in->ipaddress)); + + qInfo(logUdp()) << "Got Connection status for:" << in->name << "Computer" << in->computer << "User" << in->username << "IP" << ip.toString() << "GUID" << in->guid; + + // First we need to find this radio in our capabilities packet, there aren't many so just step through + for (int f = 0; f < radios.length(); f++) + { + if ((radios[f].commoncap == 0x8010 && + radios[f].macaddress[0] == in->macaddress[0] && + radios[f].macaddress[1] == in->macaddress[1] && + radios[f].macaddress[2] == in->macaddress[2] && + radios[f].macaddress[3] == in->macaddress[3] && + radios[f].macaddress[4] == in->macaddress[4] && + radios[f].macaddress[5] == in->macaddress[5]) || + (QUuid)radios[f].guid == (QUuid)in->guid) + { + emit setRadioUsage(f,QString(in->computer),ip.toString()); + } + } + if (in->type != 0x01 && numRadios == 1) { devName = in->name; QHostAddress ip = QHostAddress(qToBigEndian(in->ipaddress)); @@ -367,8 +388,7 @@ void udpHandler::dataReceived() { emit haveNetworkStatus(devName + " available"); - identa = in->identa; - identb = in->identb; + memcpy(macaddress, in->macaddress, 6); sendRequestStream(); } @@ -383,28 +403,50 @@ void udpHandler::dataReceived() break; } - case (CAPABILITIES_SIZE): + default: { - capabilities_packet_t in = (capabilities_packet_t)r.constData(); - if (in->type != 0x01) + if ((r.length() - CAPABILITIES_SIZE) % RADIO_CAP_SIZE != 0) { - audioType = in->audio; - devName = in->name; - civId = in->civ; - rxSampleRates = in->rxsample; - txSampleRates = in->txsample; - emit haveBaudRate(qFromBigEndian(in->baudrate)); - //replyId = r.mid(0x42, 16); - qInfo(logUdp()) << this->metaObject()->className() << "Received radio capabilities, Name:" << - devName << " Audio:" << - audioType << "CIV:" << hex << civId; + qInfo(logUdp()) << this->metaObject()->className() << "Packet received" << r.length() << "not recognised"; + break; + } - if (txSampleRates < 2) + int baudrate = 0; + + capabilities_packet_t in = (capabilities_packet_t)r.constData(); + numRadios = in->numradios; + + for (int f = CAPABILITIES_SIZE; f < r.length(); f = f + RADIO_CAP_SIZE) { + radio_cap_packet rad; + const char* tmpRad = r.constData(); + memcpy(&rad, tmpRad+f, RADIO_CAP_SIZE); + devName = rad.name; + audioType = rad.audio; + civId = rad.civ; + rxSampleRates = rad.rxsample; + txSampleRates = rad.txsample; + radios.append(rad); + } + for(const radio_cap_packet radio : radios) + { + qInfo(logUdp()) << this->metaObject()->className() << "Received radio capabilities, Name:" << + radio.name << " Audio:" << + radio.audio << "CIV:" << hex << (unsigned char)radio.civ << + "CAPF" << radio.capf; + if (radio.txsample < 2) { // TX not supported qInfo(logUdp()) << this->metaObject()->className() << "TX audio is disabled"; } + if (radio.commoncap != 0x8010) { + // GUID not MAC address + qInfo(logUdp()) << this->metaObject()->className() << "Radio GUID" << radio.guid; + } + + baudrate = qFromBigEndian(radio.baudrate); } + emit requestRadioSelection(radios); + emit haveBaudRate(baudrate); break; } @@ -432,8 +474,7 @@ void udpHandler::sendRequestStream() p.code = 0x0180; p.res = 0x03; p.commoncap = 0x8010; - p.identa = identa; - p.identb = identb; + memcpy(p.macaddress, macaddress, 6); p.innerseq = authSeq++; p.tokrequest = tokRequest; p.token = token; diff --git a/udphandler.h b/udphandler.h index 3357f32..28cbd8e 100644 --- a/udphandler.h +++ b/udphandler.h @@ -209,6 +209,8 @@ public: udpCivData* civ = Q_NULLPTR; udpAudio* audio = Q_NULLPTR; + unsigned char numRadios; + QList radios; public slots: void receiveDataFromUserToRig(QByteArray); // This slot will send data on to @@ -226,7 +228,8 @@ signals: void haveSetVolume(unsigned char value); void haveNetworkStatus(QString); void haveBaudRate(quint32 baudrate); - + void requestRadioSelection(QList radios); + void setRadioUsage(int, QString name, QString mac); private: void sendAreYouThere(); @@ -261,8 +264,7 @@ private: quint16 tokRequest; quint32 token; // These are for stream ident info. - char identa; - quint32 identb; + quint8 macaddress[8]; QByteArray usernameEncoded; QByteArray passwordEncoded; diff --git a/udpserver.cpp b/udpserver.cpp index c170917..45ac479 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -229,8 +229,7 @@ void udpServer::controlReceived() token_packet_t in = (token_packet_t)r.constData(); current->rxSeq = in->seq; current->authInnerSeq = in->innerseq; - current->identa = in->identa; - current->identb = in->identb; + memcpy(current->macaddress, in->macaddress, 6); if (in->res == 0x02) { // Request for new token qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received create token request"; @@ -301,8 +300,8 @@ void udpServer::controlReceived() current->txSampleRate = qFromBigEndian(in->txsample); current->txBufferLen = qFromBigEndian(in->txbuffer); current->authInnerSeq = in->innerseq; - current->identa = in->identa; - current->identb = in->identb; + + memcpy(current->macaddress, in->macaddress, 6); sendStatus(current); current->authInnerSeq = 0x00; sendConnectionInfo(current); @@ -1040,7 +1039,9 @@ void udpServer::sendCapabilities(CLIENT* c) qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending Capabilities :" << c->txSeq; capabilities_packet p; + radio_cap_packet r; memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + memset(r.packet, 0x0, sizeof(r)); // We can't be sure it is initialized with 0x00! p.len = sizeof(p); p.type = 0x00; p.seq = c->txSeq; @@ -1049,28 +1050,26 @@ void udpServer::sendCapabilities(CLIENT* c) p.innerseq = c->authInnerSeq; p.tokrequest = c->tokenRx; p.token = c->tokenTx; - p.code = 0x0298; - p.res = 0x02; - p.capa = 0x01; - p.commoncap = c->commonCap; + p.payloadsize = sizeof(p)-0x0f; + p.res = 0x0202; + p.numradios = 0x01; + r.commoncap = c->commonCap; - memcpy(p.macaddress, macAddress.toLocal8Bit(), 6); + memcpy(r.macaddress, macAddress.toLocal8Bit(), 6); // IRU seems to expect an "Icom" mac address so replace the first 3 octets of our Mac with one in their range! - memcpy(p.macaddress, QByteArrayLiteral("\x00\x90\xc7").constData(), 3); - - - memcpy(p.name, rigCaps.modelName.toLocal8Bit(), rigCaps.modelName.length()); - memcpy(p.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11); + memcpy(r.macaddress, QByteArrayLiteral("\x00\x90\xc7").constData(), 3); + memcpy(r.name, rigCaps.modelName.toLocal8Bit(), rigCaps.modelName.length()); + memcpy(r.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11); if (rigCaps.hasWiFi && !rigCaps.hasEthernet) { - p.conntype = 0x0707; // 0x0707 for wifi rig. + r.conntype = 0x0707; // 0x0707 for wifi rig. } else { - p.conntype = 0x073f; // 0x073f for ethernet rig. + r.conntype = 0x073f; // 0x073f for ethernet rig. } - p.civ = rigCaps.civ; - p.baudrate = (quint32)qToBigEndian(config.baudRate); + r.civ = rigCaps.civ; + r.baudrate = (quint32)qToBigEndian(config.baudRate); /* 0x80 = 12K only 0x40 = 44.1K only @@ -1082,49 +1081,48 @@ void udpServer::sendCapabilities(CLIENT* c) 0x01 = 8K only */ if (rxaudio == Q_NULLPTR) { - p.rxsample = 0x8b01; // all rx sample frequencies supported + r.rxsample = 0x8b01; // all rx sample frequencies supported } else { if (rxSampleRate == 48000) { - p.rxsample = 0x0800; // fixed rx sample frequency + r.rxsample = 0x0800; // fixed rx sample frequency } else if (rxSampleRate == 32000) { - p.rxsample = 0x0400; + r.rxsample = 0x0400; } else if (rxSampleRate == 24000) { - p.rxsample = 0x0001; + r.rxsample = 0x0001; } else if (rxSampleRate == 16000) { - p.rxsample = 0x0200; + r.rxsample = 0x0200; } else if (rxSampleRate == 12000) { - p.rxsample = 0x8000; + r.rxsample = 0x8000; } } if (txaudio == Q_NULLPTR) { - p.txsample = 0x8b01; // all tx sample frequencies supported - p.enablea = 0x01; // 0x01 enables TX 24K mode? + r.txsample = 0x8b01; // all tx sample frequencies supported + r.enablea = 0x01; // 0x01 enables TX 24K mode? qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Client will have TX audio"; } else { qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Disable tx audio for client"; - p.txsample = 0; + r.txsample = 0; } // I still don't know what these are? - p.enableb = 0x01; // 0x01 doesn't seem to do anything? - p.enablec = 0x01; // 0x01 doesn't seem to do anything? - p.capf = 0x5001; - p.capg = 0x0190; - - + r.enableb = 0x01; // 0x01 doesn't seem to do anything? + r.enablec = 0x01; // 0x01 doesn't seem to do anything? + r.capf = 0x5001; + r.capg = 0x0190; SEQBUFENTRY s; s.seqNum = p.seq; s.timeSent = QTime::currentTime(); s.retransmitCount = 0; s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + s.data.append(QByteArray::fromRawData((const char*)r.packet, sizeof(r))); if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { if (c->txSeqBuf.size() > BUFSIZE) @@ -1141,7 +1139,7 @@ void udpServer::sendCapabilities(CLIENT* c) if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); + c->socket->writeDatagram((const char*)s.data, s.data.length(), c->ipAddress, c->port); udpMutex.unlock(); } else { @@ -1172,8 +1170,7 @@ void udpServer::sendConnectionInfo(CLIENT* c) p.token = c->tokenTx; p.code = 0x0380; p.commoncap = c->commonCap; - p.identa = c->identa; - p.identb = c->identb; + memcpy(p.macaddress, c->macaddress, 6); // 0x1a-0x1f is authid (random number? // memcpy(p + 0x40, QByteArrayLiteral("IC-7851").constData(), 7); @@ -1185,8 +1182,7 @@ void udpServer::sendConnectionInfo(CLIENT* c) p.busy = 0x01; memcpy(p.computer, c->clientName.constData(), c->clientName.length()); p.ipaddress = qToBigEndian(c->ipAddress.toIPv4Address()); - p.identa = c->identa; - p.identb = c->identb; + memcpy(p.macaddress, c->macaddress, 6); } @@ -1242,8 +1238,7 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) p.tokrequest = c->tokenRx; p.token = c->tokenTx; p.code = 0x0230; - p.identa = c->identa; - p.identb = c->identb; + memcpy(p.macaddress, c->macaddress, 6); p.commoncap = c->commonCap; p.res = type; @@ -1356,8 +1351,8 @@ void udpServer::sendStatus(CLIENT* c) p.res = 0x03; p.unknown = 0x1000; p.unusede = (char)0x80; - p.identa = c->identa; - p.identb = c->identb; + memcpy(p.macaddress, c->macaddress, 6); + p.civport = qToBigEndian(c->civPort); p.audioport = qToBigEndian(c->audioPort); diff --git a/udpserver.h b/udpserver.h index e309923..a93f56e 100644 --- a/udpserver.h +++ b/udpserver.h @@ -104,8 +104,7 @@ private: quint16 authSeq; quint16 innerSeq; quint16 sendAudioSeq; - quint8 identa; - quint32 identb; + quint8 macaddress[4]; quint16 tokenRx; quint32 tokenTx; quint32 commonCap; diff --git a/wfmain.cpp b/wfmain.cpp index 53d4f79..8317676 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -28,6 +28,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s sat = new satelliteSetup(); trxadj = new transceiverAdjustments(); abtBox = new aboutbox(); + selRad = new selectRadio(); qRegisterMetaType(); // Needs to be registered early. qRegisterMetaType(); @@ -44,7 +45,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType(); - + qRegisterMetaType>(); haveRigCaps = false; @@ -420,6 +421,8 @@ void wfmain::makeRig() // Rig status and Errors: connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString))); + connect(rig, SIGNAL(requestRadioSelection(QList)), this, SLOT(radioSelection(QList))); + connect(rig, SIGNAL(setRadioUsage(int, QString, QString)), selRad, SLOT(setInUse(int, QString, QString))); // Rig comm setup: connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); @@ -1156,6 +1159,7 @@ void wfmain::setSerialDevicesUI() ui->serialDeviceListCombo->addItem(QString("/dev/")+serialPortInfo.portName(), i++); #else ui->serialDeviceListCombo->addItem(serialPortInfo.portName(), i++); + qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); #endif } #if defined(Q_OS_LINUX) || defined(Q_OS_MAC) @@ -5749,6 +5753,12 @@ void wfmain::on_setClockBtn_clicked() setRadioTimeDatePrep(); } +void wfmain::radioSelection(QList radios) +{ + selRad->populate(radios); + selRad->setVisible(true); +} + // --- DEBUG FUNCTION --- void wfmain::on_debugBtn_clicked() { diff --git a/wfmain.h b/wfmain.h index 34c3ea6..23c8900 100644 --- a/wfmain.h +++ b/wfmain.h @@ -31,6 +31,7 @@ #include "qledlabel.h" #include "rigctld.h" #include "aboutbox.h" +#include "selectradio.h" #include #include @@ -272,6 +273,7 @@ private slots: void sendRadioCommandLoop(); void showStatusBarText(QString text); void receiveBaudRate(quint32 baudrate); + void radioSelection(QList radios); void setRadioTimeDateSend(); @@ -842,7 +844,7 @@ private: satelliteSetup *sat; transceiverAdjustments *trxadj; aboutbox *abtBox; - + selectRadio *selRad; udpServer* udp = Q_NULLPTR; rigCtlD* rigCtl = Q_NULLPTR; @@ -890,6 +892,7 @@ Q_DECLARE_METATYPE(enum rigInput) Q_DECLARE_METATYPE(enum meterKind) Q_DECLARE_METATYPE(enum spectrumMode) Q_DECLARE_METATYPE(rigstate*) +Q_DECLARE_METATYPE(QList) #endif // WFMAIN_H diff --git a/wfview.pro b/wfview.pro index b431678..fe040fb 100644 --- a/wfview.pro +++ b/wfview.pro @@ -163,6 +163,7 @@ SOURCES += main.cpp\ rigctld.cpp \ ring/ring.cpp \ transceiveradjustments.cpp \ + selectradio.cpp \ aboutbox.cpp HEADERS += wfmain.h \ @@ -190,13 +191,14 @@ HEADERS += wfmain.h \ ring/ring.h \ transceiveradjustments.h \ audiotaper.h \ + selectradio.h \ aboutbox.h FORMS += wfmain.ui \ calibrationwindow.ui \ satellitesetup.ui \ - udpserversetup.ui \ + selectradio.ui \ repeatersetup.ui \ transceiveradjustments.ui \ aboutbox.ui diff --git a/wfview.vcxproj b/wfview.vcxproj index 0ddaebd..359ba8e 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,7 +16,8 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild + $(MSBuildProjectDirectory)\QtMsBuild + v142 @@ -36,7 +37,10 @@ debug\ wfview - + + + + @@ -44,8 +48,34 @@ - debug\debug\wfviewtruerelease\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport - + + + + + + debug\ + debug\ + wfview + true + + + release\ + release\ + wfview + true + false + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + + .;..\qcustomplot;..\opus\include;resampler;release;/include;%(AdditionalIncludeDirectories) @@ -59,12 +89,14 @@ MaxSpeed _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - + + MultiThreadedDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -87,7 +119,26 @@ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + .;..\qcustomplot;..\opus\include;resampler;debug;/include;%(AdditionalIncludeDirectories) @@ -105,7 +156,8 @@ true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -126,7 +178,26 @@ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + @@ -146,6 +217,7 @@ + @@ -153,188 +225,51 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - Document true @@ -351,117 +286,22 @@ release\moc_predefs.h;%(Outputs) true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -495,30 +335,16 @@ - - - - - - - - - - resourcesresources + resources + resources + - - - - - - - - - - stylestyle + style + style + @@ -537,6 +363,9 @@ - + + + + \ No newline at end of file diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index 46000f0..d28d498 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -123,6 +123,9 @@ Source Files + + Generated Files + @@ -206,57 +209,17 @@ Header Files + + Generated Files + - - - - - - - - - - Generated Files Generated Files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -280,6 +243,9 @@ Form Files + + Form Files + @@ -411,6 +377,8 @@ Resource Files + + @@ -424,6 +392,6 @@ - + \ No newline at end of file From 96de9c55fa3e355b1f8019d7513b83166a96227e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 21 Jan 2022 23:58:18 +0000 Subject: [PATCH 082/323] More work on multi-radio support --- rigcommander.cpp | 10 +- rigcommander.h | 6 +- selectradio.cpp | 23 ++- selectradio.h | 4 +- selectradio.ui | 2 +- udphandler.cpp | 104 ++++++----- udphandler.h | 7 +- wfmain.cpp | 17 +- wfmain.h | 2 + wfmain.ui | 9 +- wfview.pro | 4 +- wfview.sln | 6 - wfview.vcxproj | 385 ++++++++++++++++++++++++++++++----------- wfview.vcxproj.filters | 91 ++++++---- wfview.vcxproj.user | 4 +- 15 files changed, 460 insertions(+), 214 deletions(-) diff --git a/rigcommander.cpp b/rigcommander.cpp index bab57b4..be5ac0e 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -139,7 +139,8 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud connect(this, SIGNAL(discoveredRigID(rigCapabilities)), ptty, SLOT(receiveFoundRigID(rigCapabilities))); connect(udp, SIGNAL(requestRadioSelection(QList)), this, SLOT(radioSelection(QList))); - connect(udp, SIGNAL(setRadioUsage(int, QString, QString)), this, SLOT(radioUsage(int, QString, QString))); + connect(udp, SIGNAL(setRadioUsage(int, bool, QString, QString)), this, SLOT(radioUsage(int, bool, QString, QString))); + connect(this, SIGNAL(selectedRadio(int)), udp, SLOT(setCurrentRadio(int))); emit haveAfGain(rxSetup.localAFgain); localVolume = rxSetup.localAFgain; } @@ -4284,10 +4285,13 @@ void rigCommander::radioSelection(QList radios) emit requestRadioSelection(radios); } -void rigCommander::radioUsage(int radio, QString user, QString ip) { - emit setRadioUsage(radio, user, ip); +void rigCommander::radioUsage(int radio, bool busy, QString user, QString ip) { + emit setRadioUsage(radio, busy, user, ip); } +void rigCommander::setCurrentRadio(int radio) { + emit selectedRadio(radio); +} void rigCommander::stateUpdated() { diff --git a/rigcommander.h b/rigcommander.h index c22c7a3..d867341 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -274,7 +274,8 @@ public slots: // Housekeeping: void handleStatusUpdate(const QString text); void radioSelection(QList radios); - void radioUsage(int radio, QString name, QString ip); + void radioUsage(int radio, bool busy, QString name, QString ip); + void setCurrentRadio(int radio); void sendState(); void getDebug(); @@ -368,7 +369,8 @@ signals: // Housekeeping: void requestRadioSelection(QList radios); - void setRadioUsage(int radio, QString user, QString ip); + void setRadioUsage(int radio, bool busy, QString user, QString ip); + void selectedRadio(int radio); void getMoreDebug(); void finished(); diff --git a/selectradio.cpp b/selectradio.cpp index a815190..94c4429 100644 --- a/selectradio.cpp +++ b/selectradio.cpp @@ -18,16 +18,24 @@ selectRadio::~selectRadio() void selectRadio::populate(QList radios) { ui->table->clearContents(); + for (int row = ui->table->rowCount() - 1;row>=0; row--) { + ui->table->removeRow(row); + } + for (int row = 0; row < radios.count(); row++) { ui->table->insertRow(ui->table->rowCount()); - ui->table->setItem(ui->table->rowCount() - 1, 0, new QTableWidgetItem(QString(radios[row].name))); - ui->table->setItem(ui->table->rowCount() - 1, 1, new QTableWidgetItem(QString("%1").arg((unsigned char)radios[row].civ, 2, 16, QLatin1Char('0')).toUpper())); - ui->table->setItem(ui->table->rowCount() - 1, 2, new QTableWidgetItem(QString::number(qFromBigEndian(radios[row].baudrate)))); + ui->table->setItem(row, 0, new QTableWidgetItem(QString(radios[row].name))); + ui->table->setItem(row, 1, new QTableWidgetItem(QString("%1").arg((unsigned char)radios[row].civ, 2, 16, QLatin1Char('0')).toUpper())); + ui->table->setItem(row, 2, new QTableWidgetItem(QString::number(qFromBigEndian(radios[row].baudrate)))); } } -void selectRadio::setInUse(int radio, QString user, QString ip) +void selectRadio::setInUse(int radio, bool busy, QString user, QString ip) { + if ((radio > 0)&& !this->isVisible()) { + qInfo() << "setInUse: radio:" << radio <<"busy" << busy << "user" << user << "ip"<setVisible(true); + } ui->table->setItem(radio, 3, new QTableWidgetItem(user)); ui->table->setItem(radio, 4, new QTableWidgetItem(ip)); } @@ -36,7 +44,10 @@ void selectRadio::on_table_cellClicked(int row, int col) { qInfo() << "Clicked on " << row << "," << col; ui->table->selectRow(row); emit selectedRadio(row); + this->setVisible(false); } -void selectRadio::on_table_sectionClicked(int index) { - qInfo() << "Section Clicked" << index; + + +void selectRadio::on_cancelButton_clicked() { + this->setVisible(false); } diff --git a/selectradio.h b/selectradio.h index d823670..3175616 100644 --- a/selectradio.h +++ b/selectradio.h @@ -22,8 +22,8 @@ public: public slots: void on_table_cellClicked(int row, int col); - void on_table_sectionClicked(int index); - void setInUse(int radio, QString user, QString ip); + void setInUse(int radio, bool busy, QString user, QString ip); + void on_cancelButton_clicked(); signals: void selectedRadio(int radio); diff --git a/selectradio.ui b/selectradio.ui index e73b123..10e347a 100644 --- a/selectradio.ui +++ b/selectradio.ui @@ -49,7 +49,7 @@ true - true + false diff --git a/udphandler.cpp b/udphandler.cpp index 651322a..7795494 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -327,12 +327,12 @@ void udpHandler::dataReceived() conninfo_packet_t in = (conninfo_packet_t)r.constData(); QHostAddress ip = QHostAddress(qToBigEndian(in->ipaddress)); - qInfo(logUdp()) << "Got Connection status for:" << in->name << "Computer" << in->computer << "User" << in->username << "IP" << ip.toString() << "GUID" << in->guid; + qInfo(logUdp()) << "Got Connection status for:" << in->name << "Busy:" << in->busy << "Computer" << in->computer << "IP" << ip.toString() << "GUID" << in->guid; // First we need to find this radio in our capabilities packet, there aren't many so just step through for (int f = 0; f < radios.length(); f++) { - if ((radios[f].commoncap == 0x8010 && + if ((radios[f].commoncap == 0x8010 && radios[f].macaddress[0] == in->macaddress[0] && radios[f].macaddress[1] == in->macaddress[1] && radios[f].macaddress[2] == in->macaddress[2] && @@ -341,14 +341,12 @@ void udpHandler::dataReceived() radios[f].macaddress[5] == in->macaddress[5]) || (QUuid)radios[f].guid == (QUuid)in->guid) { - emit setRadioUsage(f,QString(in->computer),ip.toString()); + emit setRadioUsage(f, (bool)in->busy, QString(in->computer), ip.toString()); } } - if (in->type != 0x01 && numRadios == 1) { + if (in->type != 0x01 && !streamOpened) { - devName = in->name; - QHostAddress ip = QHostAddress(qToBigEndian(in->ipaddress)); - if (!streamOpened && in->busy) + if (!streamOpened && in->busy && numRadios == 1 ) { if (in->ipaddress != 0x00 && strcmp(in->computer, compName.toLocal8Bit())) { @@ -356,32 +354,7 @@ void udpHandler::dataReceived() sendControl(false, 0x00, in->seq); // Respond with an idle } else { - civ = new udpCivData(localIP, radioIP, civPort); - - // TX is not supported - if (txSampleRates <2 ) { - txSetup.samplerate = 0; - txSetup.codec = 0; - } - - audio = new udpAudio(localIP, radioIP, audioPort, rxSetup, txSetup); - - QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); - QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); - QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16))); - QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char))); - - streamOpened = true; - - emit haveNetworkStatus(devName); - - qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName; - - // Stuff can change in the meantime because of a previous login... - remoteId = in->sentid; - myId = in->rcvdid; - tokRequest = in->tokrequest; - token = in->token; + setCurrentRadio(0); } } else if (!streamOpened && !in->busy) @@ -389,7 +362,6 @@ void udpHandler::dataReceived() emit haveNetworkStatus(devName + " available"); memcpy(macaddress, in->macaddress, 6); - sendRequestStream(); } else if (streamOpened) @@ -411,7 +383,6 @@ void udpHandler::dataReceived() break; } - int baudrate = 0; capabilities_packet_t in = (capabilities_packet_t)r.constData(); numRadios = in->numradios; @@ -420,11 +391,6 @@ void udpHandler::dataReceived() radio_cap_packet rad; const char* tmpRad = r.constData(); memcpy(&rad, tmpRad+f, RADIO_CAP_SIZE); - devName = rad.name; - audioType = rad.audio; - civId = rad.civ; - rxSampleRates = rad.rxsample; - txSampleRates = rad.txsample; radios.append(rad); } for(const radio_cap_packet radio : radios) @@ -442,11 +408,8 @@ void udpHandler::dataReceived() // GUID not MAC address qInfo(logUdp()) << this->metaObject()->className() << "Radio GUID" << radio.guid; } - - baudrate = qFromBigEndian(radio.baudrate); } emit requestRadioSelection(radios); - emit haveBaudRate(baudrate); break; } @@ -460,6 +423,52 @@ void udpHandler::dataReceived() } +void udpHandler::setCurrentRadio(int radio) { + if (!streamOpened) { + qInfo(logUdp()) << "Got Radio" << radio; + int baudrate = qFromBigEndian(radios[radio].baudrate); + emit haveBaudRate(baudrate); + + if (radios[radio].commoncap == 0x8010) { + memcpy(macaddress, radios[radio].macaddress, 6); + useGuid = false; + } + else { + useGuid = true; + guid = radios[radio].guid; + } + + devName =radios[radio].name; + audioType = radios[radio].audio; + civId = radios[radio].civ; + rxSampleRates = radios[radio].rxsample; + txSampleRates = radios[radio].txsample; + + civ = new udpCivData(localIP, radioIP, civPort); + + // TX is not supported + if (txSampleRates < 2) { + txSetup.samplerate = 0; + txSetup.codec = 0; + } + + audio = new udpAudio(localIP, radioIP, audioPort, rxSetup, txSetup); + + QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); + QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); + QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16))); + QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char))); + + streamOpened = true; + emit haveNetworkStatus(devName); + + qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName; + + sendRequestStream(); + } +} + + void udpHandler::sendRequestStream() { @@ -473,8 +482,13 @@ void udpHandler::sendRequestStream() p.rcvdid = remoteId; p.code = 0x0180; p.res = 0x03; - p.commoncap = 0x8010; - memcpy(p.macaddress, macaddress, 6); + if (!useGuid) { + p.commoncap = 0x8010; + memcpy(p.macaddress, macaddress, 6); + } + else { + p.guid = guid; + } p.innerseq = authSeq++; p.tokrequest = tokRequest; p.token = token; diff --git a/udphandler.h b/udphandler.h index 28cbd8e..8f1e094 100644 --- a/udphandler.h +++ b/udphandler.h @@ -219,6 +219,8 @@ public slots: void changeLatency(quint16 value); void setVolume(unsigned char value); void init(); + void setCurrentRadio(int radio); + signals: void haveDataFromPort(QByteArray data); // emit this when we have data, connect to rigcommander @@ -229,7 +231,7 @@ signals: void haveNetworkStatus(QString); void haveBaudRate(quint32 baudrate); void requestRadioSelection(QList radios); - void setRadioUsage(int, QString name, QString mac); + void setRadioUsage(int, bool busy, QString name, QString mac); private: void sendAreYouThere(); @@ -265,7 +267,8 @@ private: quint32 token; // These are for stream ident info. quint8 macaddress[8]; - + GUID guid; + bool useGuid = false; QByteArray usernameEncoded; QByteArray passwordEncoded; diff --git a/wfmain.cpp b/wfmain.cpp index a3d866b..8ff92af 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -422,8 +422,8 @@ void wfmain::makeRig() connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString))); connect(rig, SIGNAL(requestRadioSelection(QList)), this, SLOT(radioSelection(QList))); - connect(rig, SIGNAL(setRadioUsage(int, QString, QString)), selRad, SLOT(setInUse(int, QString, QString))); - + connect(rig, SIGNAL(setRadioUsage(int, bool, QString, QString)), selRad, SLOT(setInUse(int, bool, QString, QString))); + connect(selRad, SIGNAL(selectedRadio(int)), rig, SLOT(setCurrentRadio(int))); // Rig comm setup: connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString))); @@ -5755,7 +5755,18 @@ void wfmain::on_setClockBtn_clicked() void wfmain::radioSelection(QList radios) { selRad->populate(radios); - selRad->setVisible(true); +} + +void wfmain::on_radioStatusBtn_clicked() +{ + if (selRad->isVisible()) + { + selRad->hide(); + } + else + { + selRad->show(); + } } // --- DEBUG FUNCTION --- diff --git a/wfmain.h b/wfmain.h index 23c8900..ec31871 100644 --- a/wfmain.h +++ b/wfmain.h @@ -523,6 +523,8 @@ private slots: void on_useRTSforPTTchk_clicked(bool checked); + void on_radioStatusBtn_clicked(); + private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); diff --git a/wfmain.ui b/wfmain.ui index a938ee3..ffd25f3 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -3634,6 +3634,13 @@ + + + + Radio Status + + + @@ -3671,7 +3678,7 @@ 0 0 946 - 22 + 21 diff --git a/wfview.pro b/wfview.pro index fe040fb..9128711 100644 --- a/wfview.pro +++ b/wfview.pro @@ -105,9 +105,9 @@ unix:target.path = $$PREFIX/bin INSTALLS += target # Why doesn't this seem to do anything? -DISTFILES += resources/wfview.png \ +unix:DISTFILES += resources/wfview.png \ resources/install.sh -DISTFILES += resources/wfview.desktop +unix:DISTFILES += resources/wfview.desktop unix:applications.files = resources/wfview.desktop unix:applications.path = $$PREFIX/share/applications diff --git a/wfview.sln b/wfview.sln index d1ee00c..ff4a8cd 100644 --- a/wfview.sln +++ b/wfview.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 16.0.30804.86 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfview", "wfview.vcxproj", "{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfserver", "wfserver.vcxproj", "{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 @@ -17,10 +15,6 @@ Global {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x86.Build.0 = Debug|Win32 {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.ActiveCfg = Release|Win32 {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.Build.0 = Release|Win32 - {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.ActiveCfg = Debug|Win32 - {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.Build.0 = Debug|Win32 - {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.ActiveCfg = Release|Win32 - {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wfview.vcxproj b/wfview.vcxproj index 359ba8e..37ea711 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,8 +16,7 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild - + $(MSBuildProjectDirectory)\QtMsBuild v142 @@ -37,10 +36,7 @@ debug\ wfview - - - - + @@ -48,34 +44,8 @@ - - - - - - debug\ - debug\ - wfview - true - - - release\ - release\ - wfview - true - false - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - - + debug\debug\wfviewtruerelease\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport + .;..\qcustomplot;..\opus\include;resampler;release;/include;%(AdditionalIncludeDirectories) @@ -87,16 +57,14 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="41e90bb";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - - + MultiThreadedDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -117,28 +85,9 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"41e90bb\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h .;..\qcustomplot;..\opus\include;resampler;debug;/include;%(AdditionalIncludeDirectories) @@ -150,14 +99,13 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="41e90bb";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -176,28 +124,9 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"41e90bb\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -225,51 +154,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + Document true @@ -286,22 +362,119 @@ release\moc_predefs.h;%(Outputs) true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + @@ -335,16 +508,30 @@ - resources - resources - + + + + + + + + + + resourcesresources - style - style - + + + + + + + + + + stylestyle @@ -355,17 +542,9 @@ - - - - - - - - - + \ No newline at end of file diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index d28d498..a756bf0 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -45,16 +45,6 @@ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx - - {B83CAF91-C7BF-462F-B76C-EA11631F866C} - * - false - - - {B83CAF91-C7BF-462F-B76C-EA11631F866C} - * - false - @@ -111,6 +101,9 @@ Source Files + + Source Files + Source Files @@ -123,9 +116,6 @@ Source Files - - Generated Files - @@ -191,6 +181,9 @@ Header Files + + Header Files + Header Files @@ -209,17 +202,59 @@ Header Files - - Generated Files - + + + + + + + + + + Generated Files Generated Files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -234,18 +269,15 @@ Form Files + + Form Files + Form Files - - Form Files - Form Files - - Form Files - @@ -377,21 +409,8 @@ Resource Files - - - - Distribution Files - - - Distribution Files - - - Distribution Files - - - - + \ No newline at end of file diff --git a/wfview.vcxproj.user b/wfview.vcxproj.user index a638198..f5fbdbc 100644 --- a/wfview.vcxproj.user +++ b/wfview.vcxproj.user @@ -7,9 +7,9 @@ PATH=$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(PATH) - 2021-11-22T18:24:33.3752914Z + 2022-01-21T23:07:22.7167866Z - 2021-11-22T18:24:41.6960953Z + 2022-01-21T23:07:24.1030588Z \ No newline at end of file From 39540612c7d86f7b4c2e3e00b0aca33c2d4643d8 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 22 Jan 2022 15:12:36 +0000 Subject: [PATCH 083/323] More multi-radio support (nearly working!) --- audiohandler.cpp | 24 ++++++-- audiohandler.h | 2 + packettypes.h | 9 +++ rigcommander.cpp | 10 +--- rigcommander.h | 4 +- selectradio.cpp | 8 +++ selectradio.h | 2 + selectradio.ui | 70 ++++++++++++++++++++++ udphandler.cpp | 143 ++++++++++++++++++++++++++++---------------- udphandler.h | 19 +++++- udpserver.cpp | 4 +- udpserver.h | 3 +- wfmain.cpp | 13 ++-- wfmain.h | 3 +- wfview.vcxproj.user | 4 +- 15 files changed, 241 insertions(+), 77 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 56d7b67..53ff131 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -664,6 +664,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) //qDebug(logAudio()) << "Got" << setup.bits << "bits, length" << livePacket.data.length(); // Incoming data is 8bits? + int tempAmplitude=0; if (setup.bits == 8) { // Current packet is 8bit so need to create a new buffer that is 16bit @@ -672,12 +673,15 @@ void audioHandler::incomingAudio(audioPacket inPacket) for (int f = 0; f < livePacket.data.length(); f++) { int samp = (quint8)livePacket.data[f]; + tempAmplitude = qMax(tempAmplitude, abs(samp)); for (int g = setup.radioChan; g <= devChannels; g++) { - if (setup.ulaw) + if (setup.ulaw) { *out++ = ulaw_decode[samp] * this->volume; - else + } + else { *out++ = (qint16)((samp - 128) << 8) * this->volume; + } } } livePacket.data.clear(); @@ -693,6 +697,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) qint16* out = (qint16*)outPacket.data(); for (int f = 0; f < livePacket.data.length() / 2; f++) { + tempAmplitude = qMax(tempAmplitude, (int)(abs(*in) / 256)); *out++ = (qint16)*in * this->volume; *out++ = (qint16)*in++ * this->volume; } @@ -705,13 +710,14 @@ void audioHandler::incomingAudio(audioPacket inPacket) qint16* in = (qint16*)livePacket.data.data(); for (int f = 0; f < livePacket.data.length() / 2; f++) { - *in = *in * this->volume; + tempAmplitude = qMax(tempAmplitude, (int)(abs(*in) / 256)); + *in = *in * this->volume; in++; } } } - + amplitude = tempAmplitude; /* We now have an array of 16bit samples in the NATIVE samplerate of the radio If the radio sample rate is below 48000, we need to resample. */ @@ -810,6 +816,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) //qDebug(logAudio()) << "Now resampled, length" << packet.data.length(); + int tempAmplitude = 0; // Do we need to convert mono to stereo? if (setup.radioChan == 1 && devChannels > 1) { @@ -819,6 +826,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) qint16* out = (qint16*)outPacket.data(); for (int f = 0; f < outPacket.length()/2; f++) { + tempAmplitude = qMax(tempAmplitude, (int)(abs(*in) / 256)); *out++ = *in++; in++; // Skip each even channel. } @@ -852,6 +860,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) } else if (setup.bits == 8) { + // Do we need to convert 16-bit to 8-bit? QByteArray outPacket((int)packet.data.length() / 2, (char)0xff); qint16* in = (qint16*)packet.data.data(); @@ -874,10 +883,12 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) int compressedByte = (((sample + 32768) >> 8) & 0xff); outPacket[f] = (quint8)compressedByte; } + tempAmplitude = qMax(tempAmplitude, abs(outPacket[f])); } packet.data.clear(); packet.data = outPacket; // Copy output packet back to input buffer. } + amplitude = tempAmplitude; ret = packet.data; //qDebug(logAudio()) << "Now radio format, length" << packet.data.length(); @@ -890,7 +901,6 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) } - return; } @@ -973,4 +983,8 @@ void audioHandler::stop() #endif +quint16 audioHandler::getAmplitude() +{ + return amplitude; +} diff --git a/audiohandler.h b/audiohandler.h index 4df25bc..ab8b1bb 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -107,6 +107,7 @@ public: #endif void getNextAudioChunk(QByteArray &data); + quint16 getAmplitude(); public slots: bool init(audioSetup setup); @@ -206,6 +207,7 @@ private: volatile bool ready = false; audioPacket tempBuf; quint16 currentLatency; + quint16 amplitude = 0; qreal volume=1.0; int devChannels; audioSetup setup; diff --git a/packettypes.h b/packettypes.h index 328b8df..41c89aa 100644 --- a/packettypes.h +++ b/packettypes.h @@ -5,6 +5,15 @@ #pragma pack(push, 1) +#ifndef Q_OS_WIN +typedef struct _GUID { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +} GUID; +#endif + // Various settings used by both client and server #define PURGE_SECONDS 10 #define TOKEN_RENEWAL 60000 diff --git a/rigcommander.cpp b/rigcommander.cpp index be5ac0e..fbef41a 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -131,7 +131,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud // Connect for errors/alerts connect(udp, SIGNAL(haveNetworkError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString))); - connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(handleStatusUpdate(QString))); + connect(udp, SIGNAL(haveNetworkStatus(networkStatus)), this, SLOT(handleStatusUpdate(networkStatus))); connect(ptty, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString))); connect(this, SIGNAL(getMoreDebug()), ptty, SLOT(debugThis())); @@ -206,9 +206,9 @@ void rigCommander::handleSerialPortError(const QString port, const QString error emit haveSerialPortError(port, errorText); } -void rigCommander::handleStatusUpdate(const QString text) +void rigCommander::handleStatusUpdate(const networkStatus status) { - emit haveStatusUpdate(text); + emit haveStatusUpdate(status); } bool rigCommander::usingLAN() @@ -4278,10 +4278,6 @@ void rigCommander::sendState() void rigCommander::radioSelection(QList radios) { - for (const radio_cap_packet radio : radios) - { - qInfo(logSystem()) << "Radio Name" << radio.name; - } emit requestRadioSelection(radios); } diff --git a/rigcommander.h b/rigcommander.h index d867341..f5a184c 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -272,7 +272,7 @@ public slots: void sayAll(); // Housekeeping: - void handleStatusUpdate(const QString text); + void handleStatusUpdate(const networkStatus status); void radioSelection(QList radios); void radioUsage(int radio, bool busy, QString name, QString ip); void setCurrentRadio(int radio); @@ -283,7 +283,7 @@ signals: // Communication: void commReady(); void haveSerialPortError(const QString port, const QString errorText); - void haveStatusUpdate(const QString text); + void haveStatusUpdate(const networkStatus status); void dataForComm(const QByteArray &outData); void toggleRTS(bool rtsOn); diff --git a/selectradio.cpp b/selectradio.cpp index 94c4429..f569edd 100644 --- a/selectradio.cpp +++ b/selectradio.cpp @@ -51,3 +51,11 @@ void selectRadio::on_table_cellClicked(int row, int col) { void selectRadio::on_cancelButton_clicked() { this->setVisible(false); } + +void selectRadio::audioOutputLevel(quint16 level) { + ui->afLevel->setValue(level); +} + +void selectRadio::audioInputLevel(quint16 level) { + ui->modLevel->setValue(level); +} diff --git a/selectradio.h b/selectradio.h index 3175616..fbe3412 100644 --- a/selectradio.h +++ b/selectradio.h @@ -19,6 +19,8 @@ public: explicit selectRadio(QWidget* parent = 0); ~selectRadio(); void populate(QList radios); + void audioOutputLevel(quint16 level); + void audioInputLevel(quint16 level); public slots: void on_table_cellClicked(int row, int col); diff --git a/selectradio.ui b/selectradio.ui index 10e347a..5716e2a 100644 --- a/selectradio.ui +++ b/selectradio.ui @@ -78,6 +78,76 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + AF + + + + + + + 255 + + + 0 + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + MOD + + + + + + + 255 + + + 0 + + + false + + + + + diff --git a/udphandler.cpp b/udphandler.cpp index 7795494..9f33678 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -170,20 +170,21 @@ void udpHandler::dataReceived() if (in->type == 0x07 && in->reply == 0x01 && streamOpened) { // This is a response to our ping request so measure latency - latency += lastPingSentTime.msecsTo(QDateTime::currentDateTime()); - latency /= 2; - quint32 totalsent = packetsSent; - quint32 totallost = packetsLost; + status.networkLatency += lastPingSentTime.msecsTo(QDateTime::currentDateTime()); + status.networkLatency /= 2; + status.packetsSent = packetsSent; + status.packetsLost = packetsLost; if (audio != Q_NULLPTR) { - totalsent = totalsent + audio->packetsSent; - totallost = totallost + audio->packetsLost; + status.packetsSent = status.packetsSent + audio->packetsSent; + status.packetsLost = status.packetsLost + audio->packetsLost; } if (civ != Q_NULLPTR) { - totalsent = totalsent + civ->packetsSent; - totallost = totallost + civ->packetsLost; + status.packetsSent = status.packetsSent + civ->packetsSent; + status.packetsLost = status.packetsLost + civ->packetsLost; } QString tempLatency; + status.rxLatency = audio->audioLatency; if (rxSetup.latency > audio->audioLatency) { tempLatency = QString("%1 ms").arg(audio->audioLatency,3); @@ -195,7 +196,14 @@ void udpHandler::dataReceived() if (txSetup.codec == 0) { txString = "(no tx)"; } - emit haveNetworkStatus(QString("
%1 rx latency: %2 / rtt: %3 ms / loss: %4/%5
").arg(txString).arg(tempLatency).arg(latency, 3).arg(totallost, 3).arg(totalsent, 3)); + status.message = QString("
%1 rx latency: %2 / rtt: %3 ms / loss: %4/%5
").arg(txString).arg(tempLatency).arg(latency, 3).arg(status.packetsLost, 3).arg(status.packetsSent, 3); + + if (audio != Q_NULLPTR) { + status.rxAudioLevel = audio->getRxAmplitude(); + status.txAudioLevel = audio->getTxAmplitude(); + } + emit haveNetworkStatus(status); + } break; } @@ -295,7 +303,7 @@ void udpHandler::dataReceived() if (in->error == 0xfeffffff) { - emit haveNetworkStatus("Invalid Username/Password"); + status.message = "Invalid Username/Password"; qInfo(logUdp()) << this->metaObject()->className() << ": Invalid Username/Password"; } else if (!isAuthenticated) @@ -303,7 +311,7 @@ void udpHandler::dataReceived() if (in->tokrequest == tokRequest) { - emit haveNetworkStatus("Radio Login OK!"); + status.message="Radio Login OK!"; qInfo(logUdp()) << this->metaObject()->className() << ": Received matching token response to our request"; token = in->token; sendToken(0x02); @@ -346,32 +354,49 @@ void udpHandler::dataReceived() } if (in->type != 0x01 && !streamOpened) { - if (!streamOpened && in->busy && numRadios == 1 ) + if (in->busy && numRadios == 1) { if (in->ipaddress != 0x00 && strcmp(in->computer, compName.toLocal8Bit())) { - emit haveNetworkStatus(devName + " in use by: " + in->computer + " (" + ip.toString() + ")"); + status.message = devName + " in use by: " + in->computer + " (" + ip.toString() + ")"; sendControl(false, 0x00, in->seq); // Respond with an idle } else { setCurrentRadio(0); } } - else if (!streamOpened && !in->busy) + else if (!in->busy && numRadios == 1) { - emit haveNetworkStatus(devName + " available"); + status.message = devName + " available"; - memcpy(macaddress, in->macaddress, 6); - sendRequestStream(); - } - else if (streamOpened) - /* If another client connects/disconnects from the server, the server will emit - a CONNINFO packet, send our details to confirm we still want the stream */ - { - // Received while stream is open. - sendRequestStream(); + if (civPort == 0) { + qInfo(logUdp()) << this->metaObject()->className() << "civPort not yet configured!"; + } + + if (radios[0].commoncap == 0x8010) { + memcpy(macaddress, radios[0].macaddress, 6); + useGuid = false; + } + else { + useGuid = true; + guid = radios[0].guid; + } + + devName = radios[0].name; + audioType = radios[0].audio; + civId = radios[0].civ; + rxSampleRates = radios[0].rxsample; + txSampleRates = radios[0].txsample; + sendRequestStream(); // Send initial stream request. } } + else if (streamOpened) + /* If another client connects/disconnects from the server, the server will emit + a CONNINFO packet, send our details to confirm we still want the stream */ + { + // Received while stream is open. + sendRequestStream(); + } break; } @@ -393,7 +418,7 @@ void udpHandler::dataReceived() memcpy(&rad, tmpRad+f, RADIO_CAP_SIZE); radios.append(rad); } - for(const radio_cap_packet radio : radios) + for(const radio_cap_packet &radio : radios) { qInfo(logUdp()) << this->metaObject()->className() << "Received radio capabilities, Name:" << radio.name << " Audio:" << @@ -417,32 +442,32 @@ void udpHandler::dataReceived() udpBase::dataReceived(r); // Call parent function to process the rest. r.clear(); datagram.clear(); - } return; } void udpHandler::setCurrentRadio(int radio) { + qInfo(logUdp()) << "Got Radio" << radio; + int baudrate = qFromBigEndian(radios[radio].baudrate); + emit haveBaudRate(baudrate); + + if (radios[radio].commoncap == 0x8010) { + memcpy(macaddress, radios[radio].macaddress, 6); + useGuid = false; + } + else { + useGuid = true; + guid = radios[radio].guid; + } + + devName =radios[radio].name; + audioType = radios[radio].audio; + civId = radios[radio].civ; + rxSampleRates = radios[radio].rxsample; + txSampleRates = radios[radio].txsample; + if (!streamOpened) { - qInfo(logUdp()) << "Got Radio" << radio; - int baudrate = qFromBigEndian(radios[radio].baudrate); - emit haveBaudRate(baudrate); - - if (radios[radio].commoncap == 0x8010) { - memcpy(macaddress, radios[radio].macaddress, 6); - useGuid = false; - } - else { - useGuid = true; - guid = radios[radio].guid; - } - - devName =radios[radio].name; - audioType = radios[radio].audio; - civId = radios[radio].civ; - rxSampleRates = radios[radio].rxsample; - txSampleRates = radios[radio].txsample; civ = new udpCivData(localIP, radioIP, civPort); @@ -451,21 +476,17 @@ void udpHandler::setCurrentRadio(int radio) { txSetup.samplerate = 0; txSetup.codec = 0; } - audio = new udpAudio(localIP, radioIP, audioPort, rxSetup, txSetup); - QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16))); QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char))); streamOpened = true; - emit haveNetworkStatus(devName); - - qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName; - - sendRequestStream(); } + qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName; + + sendRequestStream(); } @@ -515,7 +536,7 @@ void udpHandler::sendAreYouThere() if (areYouThereCounter == 20) { qInfo(logUdp()) << this->metaObject()->className() << ": Radio not responding."; - emit haveNetworkStatus("Radio not responding!"); + status.message = "Radio not responding!"; } qInfo(logUdp()) << this->metaObject()->className() << ": Sending Are You There..."; @@ -961,6 +982,24 @@ void udpAudio::setVolume(unsigned char value) emit haveSetVolume(value); } +quint16 udpAudio::getRxAmplitude() { + if (rxaudio != Q_NULLPTR) { + return rxaudio->getAmplitude(); + } + else { + return 0; + } +} + +quint16 udpAudio::getTxAmplitude() { + if (txaudio != Q_NULLPTR) { + return txaudio->getAmplitude(); + } + else { + return 0; + } +} + void udpAudio::dataReceived() { while (udp->hasPendingDatagrams()) { diff --git a/udphandler.h b/udphandler.h index 8f1e094..298047f 100644 --- a/udphandler.h +++ b/udphandler.h @@ -35,6 +35,20 @@ struct udpPreferences { QString clientName; }; +struct networkStatus { + quint8 rxAudioBufferPercent; + quint8 txAudioBufferPercent; + quint8 rxAudioLevel; + quint8 txAudioLevel; + quint16 rxLatency; + quint16 txLatency; + quint32 packetsSent; + quint32 packetsLost; + quint16 rtt; + quint32 networkLatency; + QString message; +}; + void passcode(QString in, QByteArray& out); QByteArray parseNullTerminatedString(QByteArray c, int s); @@ -158,6 +172,8 @@ public: ~udpAudio(); int audioLatency = 0; + quint16 getRxAmplitude(); + quint16 getTxAmplitude(); signals: void haveAudioData(audioPacket data); @@ -228,7 +244,7 @@ signals: void haveNetworkError(QString, QString); void haveChangeLatency(quint16 value); void haveSetVolume(unsigned char value); - void haveNetworkStatus(QString); + void haveNetworkStatus(networkStatus); void haveBaudRate(quint32 baudrate); void requestRadioSelection(QList radios); void setRadioUsage(int, bool busy, QString name, QString mac); @@ -279,6 +295,7 @@ private: quint8 civId = 0; quint16 rxSampleRates = 0; quint16 txSampleRates = 0; + networkStatus status; }; diff --git a/udpserver.cpp b/udpserver.cpp index 45ac479..d313249 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -105,7 +105,7 @@ udpServer::~udpServer() delete udpAudio; } - emit haveNetworkStatus(QString("")); + //emit haveNetworkStatus(QString("")); } @@ -1329,7 +1329,7 @@ void udpServer::watchdog() } } - emit haveNetworkStatus(QString("
Server connections: Control:%1 CI-V:%2 Audio:%3
").arg(controlClients.size()).arg(civClients.size()).arg(audioClients.size())); + //emit haveNetworkStatus(QString("
Server connections: Control:%1 CI-V:%2 Audio:%3
").arg(controlClients.size()).arg(civClients.size()).arg(audioClients.size())); } void udpServer::sendStatus(CLIENT* c) diff --git a/udpserver.h b/udpserver.h index a93f56e..4d5730b 100644 --- a/udpserver.h +++ b/udpserver.h @@ -21,6 +21,7 @@ #include "packettypes.h" #include "rigidentities.h" +#include "udphandler.h" #include "audiohandler.h" extern void passcode(QString in,QByteArray& out); @@ -72,7 +73,7 @@ public slots: signals: void haveDataFromServer(QByteArray); void haveAudioData(audioPacket data); - void haveNetworkStatus(QString); + void haveNetworkStatus(networkStatus); void setupTxAudio(audioSetup); void setupRxAudio(audioSetup); diff --git a/wfmain.cpp b/wfmain.cpp index 8ff92af..557af65 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -46,6 +46,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType (); qRegisterMetaType(); qRegisterMetaType>(); + qRegisterMetaType(); haveRigCaps = false; @@ -420,7 +421,7 @@ void wfmain::makeRig() // Rig status and Errors: connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); - connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString))); + connect(rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); connect(rig, SIGNAL(requestRadioSelection(QList)), this, SLOT(radioSelection(QList))); connect(rig, SIGNAL(setRadioUsage(int, bool, QString, QString)), selRad, SLOT(setInUse(int, bool, QString, QString))); connect(selRad, SIGNAL(selectedRadio(int)), rig, SLOT(setCurrentRadio(int))); @@ -579,9 +580,13 @@ void wfmain::receiveSerialPortError(QString port, QString errorText) // TODO: Dialog box, exit, etc } -void wfmain::receiveStatusUpdate(QString text) +void wfmain::receiveStatusUpdate(networkStatus status) { - this->rigStatus->setText(text); + + this->rigStatus->setText(status.message); + selRad->audioOutputLevel(status.rxAudioLevel); + selRad->audioInputLevel(status.txAudioLevel); + //qInfo(logSystem()) << "Got Status Update" << status.rxAudioLevel; } void wfmain::setupPlots() @@ -985,7 +990,7 @@ void wfmain::setServerToPrefs() } if (!prefs.enableLAN) { - connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(receiveStatusUpdate(QString))); + connect(udp, SIGNAL(haveNetworkStatus(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); } serverThread->start(); diff --git a/wfmain.h b/wfmain.h index ec31871..1d89626 100644 --- a/wfmain.h +++ b/wfmain.h @@ -263,7 +263,7 @@ private slots: void receiveRigID(rigCapabilities rigCaps); void receiveFoundRigID(rigCapabilities rigCaps); void receiveSerialPortError(QString port, QString errorText); - void receiveStatusUpdate(QString errorText); + void receiveStatusUpdate(networkStatus status); void handlePlotClick(QMouseEvent *); void handlePlotDoubleClick(QMouseEvent *); void handleWFClick(QMouseEvent *); @@ -895,6 +895,7 @@ Q_DECLARE_METATYPE(enum meterKind) Q_DECLARE_METATYPE(enum spectrumMode) Q_DECLARE_METATYPE(rigstate*) Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(struct networkStatus) #endif // WFMAIN_H diff --git a/wfview.vcxproj.user b/wfview.vcxproj.user index f5fbdbc..8cf5a5b 100644 --- a/wfview.vcxproj.user +++ b/wfview.vcxproj.user @@ -7,9 +7,9 @@ PATH=$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(PATH) - 2022-01-21T23:07:22.7167866Z + 2022-01-22T12:48:05.0658578Z - 2022-01-21T23:07:24.1030588Z + 2022-01-22T12:48:06.5160626Z \ No newline at end of file From 88d2124f358fcb021fcbd1c4f69175891c0c34e7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 22 Jan 2022 15:28:41 +0000 Subject: [PATCH 084/323] Add MetaType for radio_cap_packet --- wfmain.cpp | 1 + wfmain.h | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 557af65..c40299e 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -45,6 +45,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType(); diff --git a/wfmain.h b/wfmain.h index 1d89626..196f6e8 100644 --- a/wfmain.h +++ b/wfmain.h @@ -23,6 +23,7 @@ #include "rigidentities.h" #include "repeaterattributes.h" +#include "packettypes.h" #include "calibrationwindow.h" #include "repeatersetup.h" #include "satellitesetup.h" @@ -890,12 +891,13 @@ Q_DECLARE_METATYPE(struct audioSetup) Q_DECLARE_METATYPE(struct SERVERCONFIG) Q_DECLARE_METATYPE(struct timekind) Q_DECLARE_METATYPE(struct datekind) +Q_DECLARE_METATYPE(union radio_cap_packet) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(struct networkStatus) Q_DECLARE_METATYPE(enum rigInput) Q_DECLARE_METATYPE(enum meterKind) Q_DECLARE_METATYPE(enum spectrumMode) Q_DECLARE_METATYPE(rigstate*) -Q_DECLARE_METATYPE(QList) -Q_DECLARE_METATYPE(struct networkStatus) #endif // WFMAIN_H From 3ff6e7180a0914462466a07af27df41a811de89d Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 22 Jan 2022 15:32:53 +0000 Subject: [PATCH 085/323] Add radio_cap_packet MetaType for server --- servermain.cpp | 2 +- servermain.h | 1 + wfmain.cpp | 1 - wfmain.h | 3 +-- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index 5e0a844..115bead 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -28,7 +28,7 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType(); - qRegisterMetaType>() ; + qRegisterMetaType>(); signal(SIGINT, handleCtrlC); diff --git a/servermain.h b/servermain.h index e96f19d..7a870ff 100644 --- a/servermain.h +++ b/servermain.h @@ -444,6 +444,7 @@ Q_DECLARE_METATYPE(struct SERVERCONFIG) Q_DECLARE_METATYPE(struct timekind) Q_DECLARE_METATYPE(struct datekind) Q_DECLARE_METATYPE(enum rigInput) +Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(enum meterKind) Q_DECLARE_METATYPE(enum spectrumMode) Q_DECLARE_METATYPE(rigstate*) diff --git a/wfmain.cpp b/wfmain.cpp index c40299e..557af65 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -45,7 +45,6 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType(); - qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType(); diff --git a/wfmain.h b/wfmain.h index 196f6e8..4832a18 100644 --- a/wfmain.h +++ b/wfmain.h @@ -891,12 +891,11 @@ Q_DECLARE_METATYPE(struct audioSetup) Q_DECLARE_METATYPE(struct SERVERCONFIG) Q_DECLARE_METATYPE(struct timekind) Q_DECLARE_METATYPE(struct datekind) -Q_DECLARE_METATYPE(union radio_cap_packet) -Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(struct networkStatus) Q_DECLARE_METATYPE(enum rigInput) Q_DECLARE_METATYPE(enum meterKind) Q_DECLARE_METATYPE(enum spectrumMode) +Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(rigstate*) From 45e074783acc3668894f5b4231fcb2201212abcd Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 22 Jan 2022 18:32:08 +0000 Subject: [PATCH 086/323] Hopefully fix linux compile --- udphandler.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 9f33678..08bae80 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -6,8 +6,8 @@ udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) : controlPort(prefs.controlLANPort), - civPort(0), - audioPort(0), + civPort(50002), + audioPort(50003), rxSetup(rx), txSetup(tx) { @@ -335,7 +335,7 @@ void udpHandler::dataReceived() conninfo_packet_t in = (conninfo_packet_t)r.constData(); QHostAddress ip = QHostAddress(qToBigEndian(in->ipaddress)); - qInfo(logUdp()) << "Got Connection status for:" << in->name << "Busy:" << in->busy << "Computer" << in->computer << "IP" << ip.toString() << "GUID" << in->guid; + qInfo(logUdp()) << "Got Connection status for:" << in->name << "Busy:" << in->busy << "Computer" << in->computer << "IP" << ip.toString(); // First we need to find this radio in our capabilities packet, there aren't many so just step through for (int f = 0; f < radios.length(); f++) @@ -347,7 +347,10 @@ void udpHandler::dataReceived() radios[f].macaddress[3] == in->macaddress[3] && radios[f].macaddress[4] == in->macaddress[4] && radios[f].macaddress[5] == in->macaddress[5]) || - (QUuid)radios[f].guid == (QUuid)in->guid) + (radios[f].guid.Data1 == in->guid.Data1 && + radios[f].guid.Data2 == in->guid.Data2 && + radios[f].guid.Data3 == in->guid.Data3 && + radios[f].guid.Data4 == in->guid.Data4)) { emit setRadioUsage(f, (bool)in->busy, QString(in->computer), ip.toString()); } @@ -430,8 +433,7 @@ void udpHandler::dataReceived() qInfo(logUdp()) << this->metaObject()->className() << "TX audio is disabled"; } if (radio.commoncap != 0x8010) { - // GUID not MAC address - qInfo(logUdp()) << this->metaObject()->className() << "Radio GUID" << radio.guid; + useGuid = true; } } emit requestRadioSelection(radios); From 1c8bc62fcca067e0c394531a4d71ded971971979 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 22 Jan 2022 18:49:07 +0000 Subject: [PATCH 087/323] More linux build fixes --- packettypes.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packettypes.h b/packettypes.h index 41c89aa..d71cfdb 100644 --- a/packettypes.h +++ b/packettypes.h @@ -7,10 +7,10 @@ #ifndef Q_OS_WIN typedef struct _GUID { - unsigned long Data1; - unsigned short Data2; - unsigned short Data3; - unsigned char Data4[8]; + quint32 Data1; + quint16 Data2; + quint16 Data3; + quint8 Data4[8]; } GUID; #endif @@ -363,7 +363,7 @@ typedef union radio_cap_packet { quint16 capf; // 0x5e char unusedi; // 0x60 quint16 capg; // 0x61 - char unusedj[3]; // 0x66 + char unusedj[3]; // 0x63 }; char packet[RADIO_CAP_SIZE]; } *radio_cap_packet_t; From ff47fbd82f7eb6e57c30640764055e675f00c0db Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 22 Jan 2022 22:53:06 +0000 Subject: [PATCH 088/323] Make radio selection visible when there is more than 1 radio. --- selectradio.cpp | 3 +++ selectradio.ui | 4 ++-- wfview.vcxproj.user | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/selectradio.cpp b/selectradio.cpp index f569edd..5ea1b56 100644 --- a/selectradio.cpp +++ b/selectradio.cpp @@ -28,6 +28,9 @@ void selectRadio::populate(QList radios) ui->table->setItem(row, 1, new QTableWidgetItem(QString("%1").arg((unsigned char)radios[row].civ, 2, 16, QLatin1Char('0')).toUpper())); ui->table->setItem(row, 2, new QTableWidgetItem(QString::number(qFromBigEndian(radios[row].baudrate)))); } + if (radios.count() > 1) { + this->setVisible(true); + } } void selectRadio::setInUse(int radio, bool busy, QString user, QString ip) diff --git a/selectradio.ui b/selectradio.ui index 5716e2a..46be235 100644 --- a/selectradio.ui +++ b/selectradio.ui @@ -6,8 +6,8 @@ 0 0 - 680 - 498 + 590 + 206 diff --git a/wfview.vcxproj.user b/wfview.vcxproj.user index 8cf5a5b..ba3b589 100644 --- a/wfview.vcxproj.user +++ b/wfview.vcxproj.user @@ -7,7 +7,7 @@ PATH=$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(PATH) - 2022-01-22T12:48:05.0658578Z + 2022-01-22T22:51:25.6471709Z 2022-01-22T12:48:06.5160626Z From 264ad231c0a40e59bbab1bdd98b088f72212d893 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 23 Jan 2022 15:06:31 +0000 Subject: [PATCH 089/323] Now supports multiple radios on OEM server --- servermain.cpp | 37 ++++++++++++---- udphandler.cpp | 107 +++++++++++++++++++++++------------------------ udphandler.h | 11 +++-- wfmain.cpp | 60 ++++++++++++++++++++------ wfserver.vcxproj | 8 ++-- wfview.sln | 6 +++ 6 files changed, 148 insertions(+), 81 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index 115bead..e0be201 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -598,15 +598,36 @@ void servermain::loadSettings() serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt(); serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt(); serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt(); - int numUsers = settings->value("ServerNumUsers", 2).toInt(); + serverConfig.users.clear(); - for (int f = 0; f < numUsers; f++) - { - SERVERUSER user; - user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); - user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); - user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); - serverConfig.users.append(user); + + int numUsers = settings->beginReadArray("Users"); + if (numUsers > 0) { + { + for (int f = 0; f < numUsers; f++) + { + settings->setArrayIndex(f); + SERVERUSER user; + user.username = settings->value("Username", "").toString(); + user.password = settings->value("Password", "").toString(); + user.userType = settings->value("UserType", 0).toInt(); + serverConfig.users.append(user); + } + } + settings->endArray(); + } + else { + /* Support old way of storing users*/ + settings->endArray(); + numUsers = settings->value("ServerNumUsers", 2).toInt(); + for (int f = 0; f < numUsers; f++) + { + SERVERUSER user; + user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); + user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); + user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); + serverConfig.users.append(user); + } } serverRxSetup.isinput = true; diff --git a/udphandler.cpp b/udphandler.cpp index 08bae80..8671997 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -6,8 +6,10 @@ udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) : controlPort(prefs.controlLANPort), - civPort(50002), - audioPort(50003), + civPort(0), + audioPort(0), + civLocalPort(0), + audioLocalPort(0), rxSetup(rx), txSetup(tx) { @@ -55,7 +57,7 @@ udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) : void udpHandler::init() { - udpBase::init(); // Perform UDP socket initialization. + udpBase::init(0); // Perform UDP socket initialization. // Connect socket to my dataReceived function. QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpHandler::dataReceived); @@ -219,7 +221,7 @@ void udpHandler::dataReceived() gotAuthOK = true; if (!streamOpened) { - sendRequestStream(); + sendRequestStream(); } } @@ -272,6 +274,27 @@ void udpHandler::dataReceived() else { civPort = qFromBigEndian(in->civport); audioPort = qFromBigEndian(in->audioport); + if (!streamOpened) { + + civ = new udpCivData(localIP, radioIP, civPort, civLocalPort); + + // TX is not supported + if (txSampleRates < 2) { + txSetup.samplerate = 0; + txSetup.codec = 0; + } + audio = new udpAudio(localIP, radioIP, audioPort, audioLocalPort, rxSetup, txSetup); + + QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); + QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); + QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16))); + QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char))); + + streamOpened = true; + } + qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName; + + } } break; @@ -365,32 +388,13 @@ void udpHandler::dataReceived() sendControl(false, 0x00, in->seq); // Respond with an idle } else { - setCurrentRadio(0); } } else if (!in->busy && numRadios == 1) { status.message = devName + " available"; - - if (civPort == 0) { - qInfo(logUdp()) << this->metaObject()->className() << "civPort not yet configured!"; - } - - if (radios[0].commoncap == 0x8010) { - memcpy(macaddress, radios[0].macaddress, 6); - useGuid = false; - } - else { - useGuid = true; - guid = radios[0].guid; - } - - devName = radios[0].name; - audioType = radios[0].audio; - civId = radios[0].civ; - rxSampleRates = radios[0].rxsample; - txSampleRates = radios[0].txsample; - sendRequestStream(); // Send initial stream request. + + setCurrentRadio(0); } } else if (streamOpened) @@ -398,7 +402,7 @@ void udpHandler::dataReceived() a CONNINFO packet, send our details to confirm we still want the stream */ { // Received while stream is open. - sendRequestStream(); + //sendRequestStream(); } break; } @@ -451,6 +455,21 @@ void udpHandler::dataReceived() void udpHandler::setCurrentRadio(int radio) { qInfo(logUdp()) << "Got Radio" << radio; + qInfo(logUdp()) << "Find available local ports"; + + /* This seems to be the only way to find some available local ports. + The problem is we need to know the local AND remote ports but send the + local port to the server first and it replies with the remote ports! */ + if (civLocalPort == 0 || audioLocalPort == 0) { + QUdpSocket* tempudp = new QUdpSocket(); + tempudp->bind(); // Bind to random port. + civLocalPort = tempudp->localPort(); + tempudp->close(); + tempudp->bind(); + audioLocalPort = tempudp->localPort(); + tempudp->close(); + delete tempudp; + } int baudrate = qFromBigEndian(radios[radio].baudrate); emit haveBaudRate(baudrate); @@ -469,25 +488,6 @@ void udpHandler::setCurrentRadio(int radio) { rxSampleRates = radios[radio].rxsample; txSampleRates = radios[radio].txsample; - if (!streamOpened) { - - civ = new udpCivData(localIP, radioIP, civPort); - - // TX is not supported - if (txSampleRates < 2) { - txSetup.samplerate = 0; - txSetup.codec = 0; - } - audio = new udpAudio(localIP, radioIP, audioPort, rxSetup, txSetup); - QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); - QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); - QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16))); - QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char))); - - streamOpened = true; - } - qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName; - sendRequestStream(); } @@ -525,8 +525,8 @@ void udpHandler::sendRequestStream() memcpy(&p.username, usernameEncoded.constData(), usernameEncoded.length()); p.rxsample = qToBigEndian((quint32)rxSetup.samplerate); p.txsample = qToBigEndian((quint32)txSetup.samplerate); - p.civport = qToBigEndian((quint32)civPort); - p.audioport = qToBigEndian((quint32)audioPort); + p.civport = qToBigEndian((quint32)civLocalPort); + p.audioport = qToBigEndian((quint32)audioLocalPort); p.txbuffer = qToBigEndian((quint32)txSetup.latency); p.convert = 1; sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p))); @@ -597,14 +597,14 @@ void udpHandler::sendToken(uint8_t magic) // Class that manages all Civ Data to/from the rig -udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort) +udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort,quint16 localPort=0) { qInfo(logUdp()) << "Starting udpCivData"; localIP = local; port = civPort; radioIP = ip; - udpBase::init(); // Perform connection + udpBase::init(localPort); // Perform connection QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpCivData::dataReceived); @@ -793,7 +793,7 @@ void udpCivData::dataReceived() // Audio stream -udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, audioSetup rxSetup, audioSetup txSetup) +udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 lport, audioSetup rxSetup, audioSetup txSetup) { qInfo(logUdp()) << "Starting udpAudio"; this->localIP = local; @@ -804,7 +804,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, audio enableTx = false; } - init(); // Perform connection + init(lport); // Perform connection QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); @@ -1065,11 +1065,11 @@ void udpAudio::dataReceived() -void udpBase::init() +void udpBase::init(quint16 lport) { timeStarted.start(); udp = new QUdpSocket(this); - udp->bind(); // Bind to random port. + udp->bind(lport); // Bind to random port. localPort = udp->localPort(); qInfo(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port; uint32_t addr = localIP.toIPv4Address(); @@ -1078,7 +1078,6 @@ void udpBase::init() retransmitTimer = new QTimer(); connect(retransmitTimer, &QTimer::timeout, this, &udpBase::sendRetransmitRequest); retransmitTimer->start(RETRANSMIT_PERIOD); - } udpBase::~udpBase() diff --git a/udphandler.h b/udphandler.h index 298047f..bdf44cf 100644 --- a/udphandler.h +++ b/udphandler.h @@ -60,7 +60,9 @@ class udpBase : public QObject public: ~udpBase(); - void init(); + void init(quint16 local); + + void reconnect(); void dataReceived(QByteArray r); void sendPing(); @@ -142,7 +144,7 @@ class udpCivData : public udpBase Q_OBJECT public: - udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort); + udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, quint16 lport); ~udpCivData(); QMutex serialmutex; @@ -168,7 +170,7 @@ class udpAudio : public udpBase Q_OBJECT public: - udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, audioSetup rxSetup, audioSetup txSetup); + udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 lport, audioSetup rxSetup, audioSetup txSetup); ~udpAudio(); int audioLatency = 0; @@ -271,6 +273,9 @@ private: quint16 civPort; quint16 audioPort; + quint16 civLocalPort; + quint16 audioLocalPort; + audioSetup rxSetup; audioSetup txSetup; diff --git a/wfmain.cpp b/wfmain.cpp index 557af65..c9bfef0 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1622,16 +1622,38 @@ void wfmain::loadSettings() serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt(); serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt(); serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt(); - int numUsers = settings->value("ServerNumUsers", 2).toInt(); + serverConfig.users.clear(); - for (int f = 0; f < numUsers; f++) - { - SERVERUSER user; - user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); - user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); - user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); - serverConfig.users.append(user); + + int numUsers = settings->beginReadArray("Users"); + if (numUsers > 0) { + { + for (int f = 0; f < numUsers; f++) + { + settings->setArrayIndex(f); + SERVERUSER user; + user.username = settings->value("Username", "").toString(); + user.password = settings->value("Password", "").toString(); + user.userType = settings->value("UserType", 0).toInt(); + serverConfig.users.append(user); + } + } + settings->endArray(); } + else { + /* Support old way of storing users*/ + settings->endArray(); + numUsers = settings->value("ServerNumUsers", 2).toInt(); + for (int f = 0; f < numUsers; f++) + { + SERVERUSER user; + user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); + user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); + user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); + serverConfig.users.append(user); + } + } + ui->serverEnableCheckbox->setChecked(serverConfig.enabled); ui->serverControlPortText->setText(QString::number(serverConfig.controlPort)); @@ -2004,18 +2026,32 @@ void wfmain::saveSettings() settings->setValue("ServerControlPort", serverConfig.controlPort); settings->setValue("ServerCivPort", serverConfig.civPort); settings->setValue("ServerAudioPort", serverConfig.audioPort); - settings->setValue("ServerNumUsers", serverConfig.users.count()); settings->setValue("ServerAudioOutput", serverTxSetup.name); settings->setValue("ServerAudioInput", serverRxSetup.name); + /* Remove old format users*/ + int numUsers = settings->value("ServerNumUsers", 0).toInt(); + if (numUsers > 0) { + settings->remove("ServerNumUsers"); + for (int f = 0; f < numUsers; f++) + { + settings->remove("ServerUsername_" + QString::number(f)); + settings->remove("ServerPassword_" + QString::number(f)); + settings->remove("ServerUserType_" + QString::number(f)); + } + } + + settings->beginWriteArray("Users"); for (int f = 0; f < serverConfig.users.count(); f++) { - settings->setValue("ServerUsername_" + QString::number(f), serverConfig.users[f].username); - settings->setValue("ServerPassword_" + QString::number(f), serverConfig.users[f].password); - settings->setValue("ServerUserType_" + QString::number(f), serverConfig.users[f].userType); + settings->setArrayIndex(f); + settings->setValue("Username", serverConfig.users[f].username); + settings->setValue("Password", serverConfig.users[f].password); + settings->setValue("UserType", serverConfig.users[f].userType); } + settings->endArray(); qInfo() << "Server config stored"; settings->endGroup(); diff --git a/wfserver.vcxproj b/wfserver.vcxproj index e78371d..6fb6b46 100644 --- a/wfserver.vcxproj +++ b/wfserver.vcxproj @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ff47fbd";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -85,7 +85,7 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ff47fbd\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp @@ -99,7 +99,7 @@ Sync debug\ Disabled - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ff47fbd";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -124,7 +124,7 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ff47fbd\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp diff --git a/wfview.sln b/wfview.sln index ff4a8cd..d1ee00c 100644 --- a/wfview.sln +++ b/wfview.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.30804.86 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfview", "wfview.vcxproj", "{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfserver", "wfserver.vcxproj", "{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 @@ -15,6 +17,10 @@ Global {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x86.Build.0 = Debug|Win32 {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.ActiveCfg = Release|Win32 {326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.Build.0 = Release|Win32 + {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.ActiveCfg = Debug|Win32 + {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.Build.0 = Debug|Win32 + {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.ActiveCfg = Release|Win32 + {00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 87a36426cf0c25d4cae95f6eb3ffd282605dac56 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 23 Jan 2022 16:43:58 +0000 Subject: [PATCH 090/323] Fix some compile warnings --- main.cpp | 1 + rigcommander.cpp | 1 + wfserver.vcxproj.user | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index f5e91c7..89f6792 100644 --- a/main.cpp +++ b/main.cpp @@ -141,6 +141,7 @@ int main(int argc, char *argv[]) #ifdef BUILD_WFSERVER servermain *w = new servermain(serialPortCL, hostCL, settingsFile); + Q_UNUSED(w); // Prevent warning! #else a.setWheelScrollLines(1); // one line per wheel click diff --git a/rigcommander.cpp b/rigcommander.cpp index fbef41a..37df970 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -4555,6 +4555,7 @@ void rigCommander::stateUpdated() break; // All meters can only be updated from the rig end. case SMETER: + case SWRMETER: case POWERMETER: case ALCMETER: case COMPMETER: diff --git a/wfserver.vcxproj.user b/wfserver.vcxproj.user index 65dc681..04ba52a 100644 --- a/wfserver.vcxproj.user +++ b/wfserver.vcxproj.user @@ -2,9 +2,9 @@ - 2022-01-17T12:12:10.6444561Z + 2022-01-23T13:00:12.7440722Z - 2022-01-17T12:12:12.8619428Z + 2022-01-23T13:00:16.3164650Z \ No newline at end of file From 5ae3549ba50e19784bbd0e4499352de525d42260 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 23 Jan 2022 17:54:40 +0000 Subject: [PATCH 091/323] Treat GUID as 16 bytes rather than trying to be clever! --- packettypes.h | 14 ++------------ rigcommander.cpp | 8 ++++---- rigcommander.h | 8 ++++---- selectradio.cpp | 11 ++++++++++- selectradio.h | 4 ++-- udphandler.cpp | 27 ++++++++------------------- udphandler.h | 7 ++++--- wfmain.cpp | 5 ++--- 8 files changed, 36 insertions(+), 48 deletions(-) diff --git a/packettypes.h b/packettypes.h index d71cfdb..c8e4c60 100644 --- a/packettypes.h +++ b/packettypes.h @@ -1,19 +1,9 @@ #ifndef PACKETTYPES_H #define PACKETTYPES_H #include -#include #pragma pack(push, 1) -#ifndef Q_OS_WIN -typedef struct _GUID { - quint32 Data1; - quint16 Data2; - quint16 Data3; - quint8 Data4[8]; -} GUID; -#endif - // Various settings used by both client and server #define PURGE_SECONDS 10 #define TOKEN_RENEWAL 60000 @@ -303,7 +293,7 @@ typedef union conninfo_packet { char unusedh; // 0x29 char macaddress[6]; // 0x2a }; - GUID guid; // 0x20 + quint8 guid[16]; // 0x20 }; char unusedab[16]; // 0x30 char name[32]; // 0x40 @@ -348,7 +338,7 @@ typedef union radio_cap_packet { char unused; // 0x0 char macaddress[6]; // 0x0 }; - GUID guid; // 0x0 + quint8 guid[16]; // 0x0 }; char name[32]; // 0x10 char audio[32]; // 0x30 diff --git a/rigcommander.cpp b/rigcommander.cpp index 37df970..6e461bb 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -139,8 +139,8 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud connect(this, SIGNAL(discoveredRigID(rigCapabilities)), ptty, SLOT(receiveFoundRigID(rigCapabilities))); connect(udp, SIGNAL(requestRadioSelection(QList)), this, SLOT(radioSelection(QList))); - connect(udp, SIGNAL(setRadioUsage(int, bool, QString, QString)), this, SLOT(radioUsage(int, bool, QString, QString))); - connect(this, SIGNAL(selectedRadio(int)), udp, SLOT(setCurrentRadio(int))); + connect(udp, SIGNAL(setRadioUsage(quint8, quint8, QString, QString)), this, SLOT(radioUsage(quint8, quint8, QString, QString))); + connect(this, SIGNAL(selectedRadio(quint8)), udp, SLOT(setCurrentRadio(quint8))); emit haveAfGain(rxSetup.localAFgain); localVolume = rxSetup.localAFgain; } @@ -4281,11 +4281,11 @@ void rigCommander::radioSelection(QList radios) emit requestRadioSelection(radios); } -void rigCommander::radioUsage(int radio, bool busy, QString user, QString ip) { +void rigCommander::radioUsage(quint8 radio, quint8 busy, QString user, QString ip) { emit setRadioUsage(radio, busy, user, ip); } -void rigCommander::setCurrentRadio(int radio) { +void rigCommander::setCurrentRadio(quint8 radio) { emit selectedRadio(radio); } diff --git a/rigcommander.h b/rigcommander.h index f5a184c..d930dac 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -274,8 +274,8 @@ public slots: // Housekeeping: void handleStatusUpdate(const networkStatus status); void radioSelection(QList radios); - void radioUsage(int radio, bool busy, QString name, QString ip); - void setCurrentRadio(int radio); + void radioUsage(quint8 radio, quint8 busy, QString name, QString ip); + void setCurrentRadio(quint8 radio); void sendState(); void getDebug(); @@ -369,8 +369,8 @@ signals: // Housekeeping: void requestRadioSelection(QList radios); - void setRadioUsage(int radio, bool busy, QString user, QString ip); - void selectedRadio(int radio); + void setRadioUsage(quint8 radio, quint8 busy, QString user, QString ip); + void selectedRadio(quint8 radio); void getMoreDebug(); void finished(); diff --git a/selectradio.cpp b/selectradio.cpp index 5ea1b56..43cf093 100644 --- a/selectradio.cpp +++ b/selectradio.cpp @@ -33,7 +33,7 @@ void selectRadio::populate(QList radios) } } -void selectRadio::setInUse(int radio, bool busy, QString user, QString ip) +void selectRadio::setInUse(quint8 radio, quint8 busy, QString user, QString ip) { if ((radio > 0)&& !this->isVisible()) { qInfo() << "setInUse: radio:" << radio <<"busy" << busy << "user" << user << "ip"<table->setItem(radio, 3, new QTableWidgetItem(user)); ui->table->setItem(radio, 4, new QTableWidgetItem(ip)); + for (int f = 0; f < 5; f++) { + if (busy == 1) + ui->table->item(radio, f)->setBackground(Qt::darkGreen); + else if (busy == 2) + ui->table->item(radio, f)->setBackground(Qt::red); + else + ui->table->item(radio, f)->setBackground(Qt::black); + } + } void selectRadio::on_table_cellClicked(int row, int col) { diff --git a/selectradio.h b/selectradio.h index fbe3412..5e585ab 100644 --- a/selectradio.h +++ b/selectradio.h @@ -24,11 +24,11 @@ public: public slots: void on_table_cellClicked(int row, int col); - void setInUse(int radio, bool busy, QString user, QString ip); + void setInUse(quint8 radio, quint8 busy, QString user, QString ip); void on_cancelButton_clicked(); signals: - void selectedRadio(int radio); + void selectedRadio(quint8 radio); private: Ui::selectRadio* ui; diff --git a/udphandler.cpp b/udphandler.cpp index 8671997..870e568 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -361,7 +361,7 @@ void udpHandler::dataReceived() qInfo(logUdp()) << "Got Connection status for:" << in->name << "Busy:" << in->busy << "Computer" << in->computer << "IP" << ip.toString(); // First we need to find this radio in our capabilities packet, there aren't many so just step through - for (int f = 0; f < radios.length(); f++) + for (unsigned char f = 0; f < radios.length(); f++) { if ((radios[f].commoncap == 0x8010 && radios[f].macaddress[0] == in->macaddress[0] && @@ -370,12 +370,9 @@ void udpHandler::dataReceived() radios[f].macaddress[3] == in->macaddress[3] && radios[f].macaddress[4] == in->macaddress[4] && radios[f].macaddress[5] == in->macaddress[5]) || - (radios[f].guid.Data1 == in->guid.Data1 && - radios[f].guid.Data2 == in->guid.Data2 && - radios[f].guid.Data3 == in->guid.Data3 && - radios[f].guid.Data4 == in->guid.Data4)) + !memcmp(radios[f].guid,in->guid,sizeof(in->guid))) { - emit setRadioUsage(f, (bool)in->busy, QString(in->computer), ip.toString()); + emit setRadioUsage(f, in->busy, QString(in->computer), ip.toString()); } } if (in->type != 0x01 && !streamOpened) { @@ -431,14 +428,6 @@ void udpHandler::dataReceived() radio.name << " Audio:" << radio.audio << "CIV:" << hex << (unsigned char)radio.civ << "CAPF" << radio.capf; - if (radio.txsample < 2) - { - // TX not supported - qInfo(logUdp()) << this->metaObject()->className() << "TX audio is disabled"; - } - if (radio.commoncap != 0x8010) { - useGuid = true; - } } emit requestRadioSelection(radios); break; @@ -453,7 +442,7 @@ void udpHandler::dataReceived() } -void udpHandler::setCurrentRadio(int radio) { +void udpHandler::setCurrentRadio(quint8 radio) { qInfo(logUdp()) << "Got Radio" << radio; qInfo(logUdp()) << "Find available local ports"; @@ -474,12 +463,12 @@ void udpHandler::setCurrentRadio(int radio) { emit haveBaudRate(baudrate); if (radios[radio].commoncap == 0x8010) { - memcpy(macaddress, radios[radio].macaddress, 6); + memcpy(&macaddress, radios[radio].macaddress, sizeof(macaddress)); useGuid = false; } else { useGuid = true; - guid = radios[radio].guid; + memcpy(&guid, radios[radio].guid, sizeof(guid)); } devName =radios[radio].name; @@ -507,10 +496,10 @@ void udpHandler::sendRequestStream() p.res = 0x03; if (!useGuid) { p.commoncap = 0x8010; - memcpy(p.macaddress, macaddress, 6); + memcpy(&p.macaddress, macaddress, 6); } else { - p.guid = guid; + memcpy(&p.guid, guid, sizeof(p.guid)); } p.innerseq = authSeq++; p.tokrequest = tokRequest; diff --git a/udphandler.h b/udphandler.h index bdf44cf..ee6ea24 100644 --- a/udphandler.h +++ b/udphandler.h @@ -11,6 +11,7 @@ #include #include #include +#include // Allow easy endian-ness conversions #include @@ -237,7 +238,7 @@ public slots: void changeLatency(quint16 value); void setVolume(unsigned char value); void init(); - void setCurrentRadio(int radio); + void setCurrentRadio(quint8 radio); signals: @@ -249,7 +250,7 @@ signals: void haveNetworkStatus(networkStatus); void haveBaudRate(quint32 baudrate); void requestRadioSelection(QList radios); - void setRadioUsage(int, bool busy, QString name, QString mac); + void setRadioUsage(quint8, quint8 busy, QString name, QString mac); private: void sendAreYouThere(); @@ -288,7 +289,7 @@ private: quint32 token; // These are for stream ident info. quint8 macaddress[8]; - GUID guid; + quint8 guid[16]; bool useGuid = false; QByteArray usernameEncoded; QByteArray passwordEncoded; diff --git a/wfmain.cpp b/wfmain.cpp index c9bfef0..13523c8 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -423,8 +423,8 @@ void wfmain::makeRig() connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); connect(rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); connect(rig, SIGNAL(requestRadioSelection(QList)), this, SLOT(radioSelection(QList))); - connect(rig, SIGNAL(setRadioUsage(int, bool, QString, QString)), selRad, SLOT(setInUse(int, bool, QString, QString))); - connect(selRad, SIGNAL(selectedRadio(int)), rig, SLOT(setCurrentRadio(int))); + connect(rig, SIGNAL(setRadioUsage(quint8, quint8, QString, QString)), selRad, SLOT(setInUse(quint8, quint8, QString, QString))); + connect(selRad, SIGNAL(selectedRadio(quint8)), rig, SLOT(setCurrentRadio(quint8))); // Rig comm setup: connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString))); @@ -1422,7 +1422,6 @@ void wfmain::loadSettings() prefs.colorScheme.Light_TuningLine = QColor::fromRgba(settings->value("Light_TuningLine", defaultColors.Light_TuningLine.rgba()).toUInt()); settings->endGroup(); - // Radio and Comms: C-IV addr, port to use settings->beginGroup("Radio"); prefs.radioCIVAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); From 4a1be30c40d0d611806bc63a13f2c7f540153ea5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 26 Jan 2022 09:49:52 +0000 Subject: [PATCH 092/323] Non-working standalone server --- main.cpp | 37 +- rigcommander.cpp | 21 +- rigcommander.h | 7 +- rigidentities.h | 2 + servermain.cpp | 1591 ++++++++++------------------------------------ servermain.h | 167 +---- udpserver.cpp | 631 ++++++++++-------- udpserver.h | 44 +- 8 files changed, 799 insertions(+), 1701 deletions(-) diff --git a/main.cpp b/main.cpp index 89f6792..c232ca8 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,11 @@ #include #endif +#ifdef Q_OS_WIN +#include +#include +#endif + #include #include "wfmain.h" #include "logcategories.h" @@ -15,6 +20,29 @@ QScopedPointer m_logFile; QMutex logMutex; bool debugMode=false; +#ifdef BUILD_WFSERVER + servermain* w=Q_NULLPTR; + + #ifdef Q_OS_WIN + bool __stdcall cleanup(DWORD sig) + #else + static void cleanup(int sig) + #endif + { + Q_UNUSED(sig) + qDebug() << "Exiting via SIGNAL"; + if (w!=Q_NULLPTR) w->deleteLater(); + qApp->quit(); + + #ifdef Q_OS_WIN + return true; + #else + return; + #endif + } + +#endif + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); int main(int argc, char *argv[]) @@ -140,9 +168,12 @@ int main(int argc, char *argv[]) qDebug(logSystem()) << QString("CIV as set by parser: %1").arg(civCL); #ifdef BUILD_WFSERVER - servermain *w = new servermain(serialPortCL, hostCL, settingsFile); - Q_UNUSED(w); // Prevent warning! - +#ifdef Q_OS_WIN + SetConsoleCtrlHandler((PHANDLER_ROUTINE)cleanup, TRUE); +#else + signal(SIGINT, cleanup); +#endif + w = new servermain(serialPortCL, hostCL, settingsFile); #else a.setWheelScrollLines(1); // one line per wheel click wfmain w(serialPortCL, hostCL, settingsFile); diff --git a/rigcommander.cpp b/rigcommander.cpp index 6e461bb..4094da5 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -22,11 +22,20 @@ rigCommander::rigCommander() { - state.set(SCOPEFUNC,true,false); + qInfo(logRig()) << "creating instance of rigCommander()"; + state.set(SCOPEFUNC, true, false); +} + +rigCommander::rigCommander(quint8 guid[16]) +{ + qInfo(logRig()) << "creating instance of rigCommander()"; + state.set(SCOPEFUNC, true, false); + memcpy(this->guid, guid, sizeof(this->guid)); } rigCommander::~rigCommander() { + qInfo(logRig()) << "closing instance of rigCommander()"; closeComm(); } @@ -40,13 +49,14 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu // civAddr = 0x94; // address of the radio. Decimal is 148. civAddr = rigCivAddr; // address of the radio. Decimal is 148. usingNativeLAN = false; - + //qInfo(logRig()) << "Opening connection to Rig:" << hex << (unsigned char)rigCivAddr << "on serial port" << rigSerialPort << "at baud rate" << rigBaudRate; // --- setup(); // --- this->rigSerialPort = rigSerialPort; this->rigBaudRate = rigBaudRate; + rigCaps.baudRate = rigBaudRate; comm = new commHandler(rigSerialPort, rigBaudRate); ptty = new pttyHandler(vsp); @@ -217,6 +227,7 @@ bool rigCommander::usingLAN() } void rigCommander::receiveBaudRate(quint32 baudrate) { + rigCaps.baudRate = baudrate; emit haveBaudRate(baudrate); } @@ -3612,6 +3623,9 @@ void rigCommander::determineRigCaps() } haveRigCaps = true; + // Copy received guid so we can recognise this radio. + memcpy(rigCaps.guid, this->guid, sizeof(rigCaps.guid)); + if(!usingNativeLAN) { if(useRTSforPTT_isSet) @@ -4676,6 +4690,9 @@ void rigCommander::dataFromServer(QByteArray data) emit dataForComm(data); } +quint8* rigCommander::getGUID() { + return guid; +} diff --git a/rigcommander.h b/rigcommander.h index d930dac..e343555 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -69,13 +69,16 @@ class rigCommander : public QObject public: rigCommander(); + rigCommander(quint8 guid[16]); ~rigCommander(); bool usingLAN(); + quint8* getGUID(); + public slots: void process(); - void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp); + void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp); void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); void closeComm(); void stateUpdated(); @@ -475,7 +478,7 @@ private: QString serialPortError; unsigned char localVolume=0; - + quint8 guid[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; }; diff --git a/rigidentities.h b/rigidentities.h index cae1b75..222a73c 100644 --- a/rigidentities.h +++ b/rigidentities.h @@ -138,6 +138,8 @@ struct rigCapabilities { std::vector modes; QByteArray transceiveCommand; + quint8 guid[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + quint32 baudRate; }; diff --git a/servermain.cpp b/servermain.cpp index e0be201..2ab49e8 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -13,16 +13,16 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q this->serialPortCL = serialPortCL; this->hostCL = hostCL; - qRegisterMetaType(); // Needs to be registered early. - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType (); // Needs to be registered early. + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType (); @@ -30,10 +30,6 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q qRegisterMetaType(); qRegisterMetaType>(); - signal(SIGINT, handleCtrlC); - - haveRigCaps = false; - setDefPrefs(); getSettingsFilePath(settingsFile); @@ -44,8 +40,6 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q openRig(); - rigConnections(); - setServerToPrefs(); amTransmitting = false; @@ -54,15 +48,19 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q servermain::~servermain() { - rigThread->quit(); - rigThread->wait(); + for (RIGCONFIG& radio : serverConfig.rigs) + { + if (radio.rigThread != Q_NULLPTR) + { + radio.rigThread->quit(); + radio.rigThread->wait(); + } + } if (serverThread != Q_NULLPTR) { serverThread->quit(); serverThread->wait(); } - if (rigCtl != Q_NULLPTR) { - delete rigCtl; - } + delete settings; #if defined(PORTAUDIO) @@ -78,205 +76,104 @@ void servermain::openRig() // and this function is also responsible for initiating the search for a rig model and capabilities. // Any errors, such as unable to open connection or unable to open port, are to be reported to the user. - //TODO: if(hasRunPreviously) - - //TODO: if(useNetwork){... - - // } else { - - // if (prefs.fileWasNotFound) { - // showRigSettings(); // rig setting dialog box for network/serial, CIV, hostname, port, baud rate, serial device, etc - // TODO: How do we know if the setting was loaded? - - - // TODO: Use these if they are found - if(!serialPortCL.isEmpty()) - { - qDebug(logSystem()) << "Serial port specified by user: " << serialPortCL; - } else { - qDebug(logSystem()) << "Serial port not specified. "; - } - - if(!hostCL.isEmpty()) - { - qDebug(logSystem()) << "Remote host name specified by user: " << hostCL; - } - makeRig(); - if (prefs.enableLAN) + if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) { - usingLAN = true; - // We need to setup the tx/rx audio: - emit sendCommSetup(prefs.radioCIVAddr, udpPrefs, rxSetup, txSetup, prefs.virtualSerialPort); + findSerialPort(); + } else { - if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) + if(serialPortCL.isEmpty()) { - findSerialPort(); - + serialPortRig = prefs.serialPortRadio; } else { - if(serialPortCL.isEmpty()) - { - serialPortRig = prefs.serialPortRadio; - } else { - serialPortRig = serialPortCL; - } + serialPortRig = serialPortCL; + } + } + for (RIGCONFIG& radio : serverConfig.rigs) + { + //qInfo(logSystem()) << "Opening rig"; + if (radio.rigThread != Q_NULLPTR) + { + //qInfo(logSystem()) << "Got rig"; + QMetaObject::invokeMethod(radio.rig, [=]() { + radio.rig->commSetup(radio.civAddr, radio.serialPort, radio.baudRate, QString("none")); + }, Qt::QueuedConnection); } - usingLAN = false; - emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort); } - - - } -void servermain::rigConnections() -{ - connect(this, SIGNAL(setCIVAddr(unsigned char)), rig, SLOT(setCIVAddr(unsigned char))); - - connect(this, SIGNAL(sendPowerOn()), rig, SLOT(powerOn())); - connect(this, SIGNAL(sendPowerOff()), rig, SLOT(powerOff())); - - connect(rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool))); - connect(this, SIGNAL(setPTT(bool)), rig, SLOT(setPTT(bool))); - connect(this, SIGNAL(getPTT()), rig, SLOT(getPTT())); - connect(this, SIGNAL(getDebug()), rig, SLOT(getDebug())); - - - connect(this, SIGNAL(getDuplexMode()), rig, SLOT(getDuplexMode())); - connect(this, SIGNAL(getTone()), rig, SLOT(getTone())); - connect(this, SIGNAL(getTSQL()), rig, SLOT(getTSQL())); - connect(this, SIGNAL(getRptAccessMode()), rig, SLOT(getRptAccessMode())); - - connect(this, SIGNAL(setScopeMode(spectrumMode)), rig, SLOT(setSpectrumMode(spectrumMode))); - connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode())); - - connect(this, SIGNAL(setFrequency(unsigned char, freqt)), rig, SLOT(setFrequency(unsigned char, freqt))); - connect(this, SIGNAL(setScopeEdge(char)), rig, SLOT(setScopeEdge(char))); - connect(this, SIGNAL(setScopeSpan(char)), rig, SLOT(setScopeSpan(char))); - //connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode())); - connect(this, SIGNAL(getScopeEdge()), rig, SLOT(getScopeEdge())); - connect(this, SIGNAL(getScopeSpan()), rig, SLOT(getScopeSpan())); - connect(this, SIGNAL(setScopeFixedEdge(double,double,unsigned char)), rig, SLOT(setSpectrumBounds(double,double,unsigned char))); - - connect(this, SIGNAL(setMode(unsigned char, unsigned char)), rig, SLOT(setMode(unsigned char, unsigned char))); - connect(this, SIGNAL(setMode(mode_info)), rig, SLOT(setMode(mode_info))); - - // Levels (read and write) - // Levels: Query: - connect(this, SIGNAL(getLevels()), rig, SLOT(getLevels())); - connect(this, SIGNAL(getRfGain()), rig, SLOT(getRfGain())); - connect(this, SIGNAL(getAfGain()), rig, SLOT(getAfGain())); - connect(this, SIGNAL(getSql()), rig, SLOT(getSql())); - connect(this, SIGNAL(getIfShift()), rig, SLOT(getIFShift())); - connect(this, SIGNAL(getTPBFInner()), rig, SLOT(getTPBFInner())); - connect(this, SIGNAL(getTPBFOuter()), rig, SLOT(getTPBFOuter())); - connect(this, SIGNAL(getTxPower()), rig, SLOT(getTxLevel())); - connect(this, SIGNAL(getMicGain()), rig, SLOT(getMicGain())); - connect(this, SIGNAL(getSpectrumRefLevel()), rig, SLOT(getSpectrumRefLevel())); - connect(this, SIGNAL(getModInputLevel(rigInput)), rig, SLOT(getModInputLevel(rigInput))); - - - // Levels: Set: - connect(this, SIGNAL(setRfGain(unsigned char)), rig, SLOT(setRfGain(unsigned char))); - connect(this, SIGNAL(setAfGain(unsigned char)), rig, SLOT(setAfGain(unsigned char))); - connect(this, SIGNAL(setSql(unsigned char)), rig, SLOT(setSquelch(unsigned char))); - connect(this, SIGNAL(setIFShift(unsigned char)), rig, SLOT(setIFShift(unsigned char))); - connect(this, SIGNAL(setTPBFInner(unsigned char)), rig, SLOT(setTPBFInner(unsigned char))); - connect(this, SIGNAL(setTPBFOuter(unsigned char)), rig, SLOT(setTPBFOuter(unsigned char))); - connect(this, SIGNAL(setTxPower(unsigned char)), rig, SLOT(setTxPower(unsigned char))); - connect(this, SIGNAL(setMicGain(unsigned char)), rig, SLOT(setMicGain(unsigned char))); - connect(this, SIGNAL(setMonitorLevel(unsigned char)), rig, SLOT(setMonitorLevel(unsigned char))); - connect(this, SIGNAL(setVoxGain(unsigned char)), rig, SLOT(setVoxGain(unsigned char))); - connect(this, SIGNAL(setAntiVoxGain(unsigned char)), rig, SLOT(setAntiVoxGain(unsigned char))); - connect(this, SIGNAL(setSpectrumRefLevel(int)), rig, SLOT(setSpectrumRefLevel(int))); - connect(this, SIGNAL(setModLevel(rigInput, unsigned char)), rig, SLOT(setModInputLevel(rigInput, unsigned char))); - - // Rig and ATU info: - connect(this, SIGNAL(startATU()), rig, SLOT(startATU())); - connect(this, SIGNAL(setATU(bool)), rig, SLOT(setATU(bool))); - connect(this, SIGNAL(getATUStatus()), rig, SLOT(getATUStatus())); - connect(this, SIGNAL(getRigID()), rig, SLOT(getRigID())); - connect(rig, SIGNAL(haveRigID(rigCapabilities)), this, SLOT(receiveRigID(rigCapabilities))); - connect(this, SIGNAL(setAttenuator(unsigned char)), rig, SLOT(setAttenuator(unsigned char))); - connect(this, SIGNAL(setPreamp(unsigned char)), rig, SLOT(setPreamp(unsigned char))); - connect(this, SIGNAL(setAntenna(unsigned char, bool)), rig, SLOT(setAntenna(unsigned char, bool))); - connect(this, SIGNAL(getPreamp()), rig, SLOT(getPreamp())); - connect(this, SIGNAL(getAttenuator()), rig, SLOT(getAttenuator())); - connect(this, SIGNAL(getAntenna()), rig, SLOT(getAntenna())); - - - // Speech (emitted from rig speaker) - connect(this, SIGNAL(sayAll()), rig, SLOT(sayAll())); - connect(this, SIGNAL(sayFrequency()), rig, SLOT(sayFrequency())); - connect(this, SIGNAL(sayMode()), rig, SLOT(sayMode())); - - // Date and Time: - connect(this, SIGNAL(setTime(timekind)), rig, SLOT(setTime(timekind))); - connect(this, SIGNAL(setDate(datekind)), rig, SLOT(setDate(datekind))); - connect(this, SIGNAL(setUTCOffset(timekind)), rig, SLOT(setUTCOffset(timekind))); - -} - - void servermain::makeRig() { - if (rigThread == Q_NULLPTR) + for (RIGCONFIG& radio : serverConfig.rigs) { - rig = new rigCommander(); - rigThread = new QThread(this); + if (radio.rigThread == Q_NULLPTR) + { + qInfo(logSystem()) << "Creating new rigThread()"; + radio.rig = new rigCommander(radio.guid); + radio.rigThread = new QThread(this); - // Thread: - rig->moveToThread(rigThread); - connect(rigThread, SIGNAL(started()), rig, SLOT(process())); - connect(rigThread, SIGNAL(finished()), rig, SLOT(deleteLater())); - rigThread->start(); + // Thread: + radio.rig->moveToThread(radio.rigThread); + connect(radio.rigThread, SIGNAL(started()), radio.rig, SLOT(process())); + connect(radio.rigThread, SIGNAL(finished()), radio.rig, SLOT(deleteLater())); + radio.rigThread->start(); + // Rig status and Errors: + connect(radio.rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); + connect(radio.rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); - // Rig status and Errors: - connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); - connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString))); + // Rig comm setup: + //connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), radio.rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); + //connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32, QString)), radio.rig, SLOT(commSetup(unsigned char, QString, quint32, QString))); + connect(this, SIGNAL(setRTSforPTT(bool)), radio.rig, SLOT(setRTSforPTT(bool))); - // Rig comm setup: - connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); - connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString))); - connect(this, SIGNAL(setRTSforPTT(bool)), rig, SLOT(setRTSforPTT(bool))); + connect(radio.rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); - connect(rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); + connect(this, SIGNAL(sendCloseComm()), radio.rig, SLOT(closeComm())); + connect(this, SIGNAL(sendChangeLatency(quint16)), radio.rig, SLOT(changeLatency(quint16))); + //connect(this, SIGNAL(getRigCIV()), radio.rig, SLOT(findRigs())); + //connect(this, SIGNAL(setRigID(unsigned char)), radio.rig, SLOT(setRigID(unsigned char))); + connect(radio.rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities))); + connect(radio.rig, SIGNAL(commReady()), this, SLOT(receiveCommReady())); - connect(this, SIGNAL(sendCloseComm()), rig, SLOT(closeComm())); - connect(this, SIGNAL(sendChangeLatency(quint16)), rig, SLOT(changeLatency(quint16))); - connect(this, SIGNAL(getRigCIV()), rig, SLOT(findRigs())); - connect(this, SIGNAL(setRigID(unsigned char)), rig, SLOT(setRigID(unsigned char))); - connect(rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities))); - connect(rig, SIGNAL(commReady()), this, SLOT(receiveCommReady())); + connect(this, SIGNAL(requestRigState()), radio.rig, SLOT(sendState())); + connect(this, SIGNAL(stateUpdated()), radio.rig, SLOT(stateUpdated())); + connect(radio.rig, SIGNAL(stateInfo(rigstate*)), this, SLOT(receiveStateInfo(rigstate*))); + + //Other connections + connect(this, SIGNAL(setCIVAddr(unsigned char)), radio.rig, SLOT(setCIVAddr(unsigned char))); + + connect(radio.rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool))); + connect(this, SIGNAL(setPTT(bool)), radio.rig, SLOT(setPTT(bool))); + connect(this, SIGNAL(getPTT()), radio.rig, SLOT(getPTT())); + connect(this, SIGNAL(getDebug()), radio.rig, SLOT(getDebug())); + if (radio.rigThread->isRunning()) { + qInfo(logSystem()) << "Rig thread is running"; + } + else { + qInfo(logSystem()) << "Rig thread is not running"; + } - connect(this, SIGNAL(requestRigState()), rig, SLOT(sendState())); - connect(this, SIGNAL(stateUpdated()), rig, SLOT(stateUpdated())); - connect(rig, SIGNAL(stateInfo(rigstate*)), this, SLOT(receiveStateInfo(rigstate*))); - if (rigCtl != Q_NULLPTR) { - connect(rig, SIGNAL(stateInfo(rigstate*)), rigCtl, SLOT(receiveStateInfo(rigstate*))); - connect(rigCtl, SIGNAL(stateUpdated()), rig, SLOT(stateUpdated())); } + } + } void servermain::removeRig() { - if (rigThread != Q_NULLPTR) + for (RIGCONFIG& radio : serverConfig.rigs) { - if (rigCtl != Q_NULLPTR) { - rigCtl->disconnect(); + if (radio.rigThread != Q_NULLPTR) + { + radio.rigThread->disconnect(); + radio.rig->disconnect(); + delete radio.rigThread; + delete radio.rig; + radio.rig = Q_NULLPTR; } - rigThread->disconnect(); - - rig->disconnect(); - - delete rigThread; - delete rig; - rig = Q_NULLPTR; } - } @@ -330,43 +227,45 @@ void servermain::findSerialPort() } } -void servermain::receiveStatusUpdate(QString text) +void servermain::receiveStatusUpdate(networkStatus status) { - if (text != lastMessage) { - std::cout << text.toLocal8Bit().toStdString() << "\n"; - lastMessage = text; + if (status.message != lastMessage) { + std::cout << status.message.toLocal8Bit().toStdString() << "\n"; + lastMessage = status.message; } } void servermain::receiveCommReady() { - qInfo(logSystem()) << "Received CommReady!! "; - if(!usingLAN) + rigCommander* sender = qobject_cast(QObject::sender()); + + // Use the GUID to determine which radio the response is from + + for (RIGCONFIG& radio : serverConfig.rigs) { - // usingLAN gets set when we emit the sendCommSetup signal. - // If we're not using the LAN, then we're on serial, and - // we already know the baud rate and can calculate the timing parameters. - calculateTimingParameters(); - } - if(prefs.radioCIVAddr == 0) - { - // tell rigCommander to broadcast a request for all rig IDs. - // qInfo(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)"; - emit getRigCIV(); - issueDelayedCommand(cmdGetRigCIV); - delayedCommand->start(); - } else { - // don't bother, they told us the CIV they want, stick with it. - // We still query the rigID to find the model, but at least we know the CIV. - qInfo(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << prefs.radioCIVAddr; - if(prefs.CIVisRadioModel) + if (sender != Q_NULLPTR && radio.rig != Q_NULLPTR && !memcmp(sender->getGUID(), radio.guid, sizeof(radio.guid))) { - qInfo(logSystem()) << "Skipping Rig ID query, using user-supplied model from CI-V address: " << prefs.radioCIVAddr; - emit setRigID(prefs.radioCIVAddr); - } else { - emit getRigID(); - getInitialRigState(); + + qInfo(logSystem()) << "Received CommReady!! "; + if (radio.civAddr == 0) + { + // tell rigCommander to broadcast a request for all rig IDs. + // qInfo(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)"; + if (!radio.rigAvailable) { + QMetaObject::invokeMethod(radio.rig, [=]() { + radio.rig->findRigs(); + }, Qt::QueuedConnection); + } + } + else { + // don't bother, they told us the CIV they want, stick with it. + // We still query the rigID to find the model, but at least we know the CIV. + qInfo(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << radio.civAddr; + QMetaObject::invokeMethod(radio.rig, [=]() { + radio.rig->setRigID(radio.civAddr); + }, Qt::QueuedConnection); + } } } } @@ -376,18 +275,45 @@ void servermain::receiveFoundRigID(rigCapabilities rigCaps) { // Entry point for unknown rig being identified at the start of the program. //now we know what the rig ID is: - //qInfo(logSystem()) << "In wfview, we now have a reply to our request for rig identity sent to CIV BROADCAST."; - if(rig->usingLAN()) + rigCommander* sender = qobject_cast(QObject::sender()); + + // Use the GUID to determine which radio the response is from + for (RIGCONFIG& radio : serverConfig.rigs) { - usingLAN = true; - } else { - usingLAN = false; + + if (sender != Q_NULLPTR && radio.rig != Q_NULLPTR && !radio.rigAvailable && !memcmp(sender->getGUID(), radio.guid, sizeof(radio.guid))) + { + + qDebug(logSystem()) << "Rig name: " << rigCaps.modelName; + qDebug(logSystem()) << "Has LAN capabilities: " << rigCaps.hasLan; + qDebug(logSystem()) << "Rig ID received into servermain: spectLenMax: " << rigCaps.spectLenMax; + qDebug(logSystem()) << "Rig ID received into servermain: spectAmpMax: " << rigCaps.spectAmpMax; + qDebug(logSystem()) << "Rig ID received into servermain: spectSeqMax: " << rigCaps.spectSeqMax; + qDebug(logSystem()) << "Rig ID received into servermain: hasSpectrum: " << rigCaps.hasSpectrum; + qDebug(logSystem()).noquote() << QString("Rig ID received into servermain: GUID: {%1%2%3%4-%5%6-%7%8-%9%10-%11%12%13%14%15%16}") + .arg(rigCaps.guid[0], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[1], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[2], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[3], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[4], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[5], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[6], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[7], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[8], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[9], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[10], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[11], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[12], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[13], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[14], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[15], 2, 16, QLatin1Char('0')) + ; + radio.rigCaps = rigCaps; + // Added so that server receives rig capabilities. + emit sendRigCaps(rigCaps); + } } - - receiveRigID(rigCaps); - getInitialRigState(); - return; } @@ -429,25 +355,10 @@ void servermain::getSettingsFilePath(QString settingsFile) void servermain::setInitialTiming() { loopTickCounter = 0; - delayedCmdIntervalLAN_ms = 70; // interval for regular delayed commands, including initial rig/UI state queries - delayedCmdIntervalSerial_ms = 100; // interval for regular delayed commands, including initial rig/UI state queries - delayedCmdStartupInterval_ms = 250; // interval for rigID polling - delayedCommand = new QTimer(this); - delayedCommand->setInterval(delayedCmdStartupInterval_ms); // 250ms until we find rig civ and id, then 100ms. - delayedCommand->setSingleShot(false); - connect(delayedCommand, SIGNAL(timeout()), this, SLOT(sendRadioCommandLoop())); - - // TODO: Remove this: -// periodicPollingTimer = new QTimer(this); -// periodicPollingTimer->setInterval(10); -// periodicPollingTimer->setSingleShot(false); - //connect(periodicPollingTimer, SIGNAL(timeout()), this, SLOT(sendRadioCommandLoop())); - pttTimer = new QTimer(this); pttTimer->setInterval(180*1000); // 3 minute max transmit time in ms pttTimer->setSingleShot(true); connect(pttTimer, SIGNAL(timeout()), this, SLOT(handlePttLimit())); - } void servermain::setServerToPrefs() @@ -461,63 +372,49 @@ void servermain::setServerToPrefs() udp = Q_NULLPTR; } - if (serverConfig.enabled) { - serverConfig.lan = prefs.enableLAN; - qInfo(logAudio()) << "Audio Input device " << serverRxSetup.name; - qInfo(logAudio()) << "Audio Output device " << serverTxSetup.name; - udp = new udpServer(serverConfig, serverTxSetup, serverRxSetup); + udp = new udpServer(&serverConfig, serverTxSetup, serverRxSetup); - serverThread = new QThread(this); + serverThread = new QThread(this); - udp->moveToThread(serverThread); + udp->moveToThread(serverThread); - connect(this, SIGNAL(initServer()), udp, SLOT(init())); - connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); + connect(this, SIGNAL(initServer()), udp, SLOT(init())); + connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); - if (rig != Q_NULLPTR) { - connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); - connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); - connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); + // Step through all radios and connect them to the server, + // server will then use GUID to determine which actual radio it belongs to. + + for (RIGCONFIG& radio : serverConfig.rigs) + { + if (radio.rigThread != Q_NULLPTR) + { + + if (radio.rig != Q_NULLPTR) { + connect(radio.rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); + connect(radio.rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); + //connect(udp, SIGNAL(haveDataFromServer(QByteArray)), radio.rig, SLOT(dataFromServer(QByteArray))); + connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); + } } - - if (!prefs.enableLAN) { - connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(receiveStatusUpdate(QString))); - } - - serverThread->start(); - - emit initServer(); - - connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); - } + + connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(receiveStatusUpdate(QString))); + + serverThread->start(); + + emit initServer(); } void servermain::setDefPrefs() { - defPrefs.useFullScreen = false; - defPrefs.useDarkMode = true; - defPrefs.useSystemTheme = false; - defPrefs.drawPeaks = true; - defPrefs.wfAntiAlias = false; - defPrefs.wfInterpolate = true; defPrefs.radioCIVAddr = 0x00; // previously was 0x94 for 7300. defPrefs.CIVisRadioModel = false; defPrefs.forceRTSasPTT = false; defPrefs.serialPortRadio = QString("auto"); defPrefs.serialPortBaud = 115200; - defPrefs.enablePTT = false; - defPrefs.niceTS = true; - defPrefs.enableRigCtlD = false; - defPrefs.rigCtlPort = 4533; - defPrefs.virtualSerialPort = QString("none"); defPrefs.localAFgain = 255; - defPrefs.wflength = 160; - defPrefs.confirmExit = true; - defPrefs.confirmPowerOff = true; - defPrefs.meter2Type = meterNone; udpDefPrefs.ipAddress = QString(""); udpDefPrefs.controlLANPort = 50001; @@ -532,157 +429,12 @@ void servermain::loadSettings() { qInfo(logSystem()) << "Loading settings from " << settings->fileName(); - // Radio and Comms: C-IV addr, port to use - settings->beginGroup("Radio"); - prefs.radioCIVAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); - prefs.CIVisRadioModel = (bool)settings->value("CIVisRadioModel", defPrefs.CIVisRadioModel).toBool(); - prefs.forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool(); - prefs.serialPortRadio = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString(); - prefs.serialPortBaud = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); - - if (prefs.serialPortBaud > 0) - { - serverConfig.baudRate = prefs.serialPortBaud; - } - - prefs.virtualSerialPort = settings->value("VirtualSerialPort", defPrefs.virtualSerialPort).toString(); - - prefs.localAFgain = (unsigned char)settings->value("localAFgain", defPrefs.localAFgain).toUInt(); - rxSetup.localAFgain = prefs.localAFgain; - txSetup.localAFgain = 255; - - settings->endGroup(); - - // Misc. user settings (enable PTT, draw peaks, etc) - settings->beginGroup("Controls"); - prefs.enablePTT = settings->value("EnablePTT", defPrefs.enablePTT).toBool(); - prefs.niceTS = settings->value("NiceTS", defPrefs.niceTS).toBool(); - settings->endGroup(); - - settings->beginGroup("LAN"); - - prefs.enableLAN = settings->value("EnableLAN", defPrefs.enableLAN).toBool(); - - prefs.enableRigCtlD = settings->value("EnableRigCtlD", defPrefs.enableRigCtlD).toBool(); - prefs.rigCtlPort = settings->value("RigCtlPort", defPrefs.rigCtlPort).toInt(); - udpPrefs.ipAddress = settings->value("IPAddress", udpDefPrefs.ipAddress).toString(); - udpPrefs.controlLANPort = settings->value("ControlLANPort", udpDefPrefs.controlLANPort).toInt(); - udpPrefs.username = settings->value("Username", udpDefPrefs.username).toString(); - udpPrefs.password = settings->value("Password", udpDefPrefs.password).toString(); - - rxSetup.isinput = false; - txSetup.isinput = true; - - rxSetup.latency = settings->value("AudioRXLatency", "150").toInt(); - txSetup.latency = settings->value("AudioTXLatency", "150").toInt(); - rxSetup.samplerate = settings->value("AudioRXSampleRate", "48000").toInt(); - txSetup.samplerate = rxSetup.samplerate; - rxSetup.codec = settings->value("AudioRXCodec", "4").toInt(); - txSetup.codec = settings->value("AudioTXCodec", "4").toInt(); - rxSetup.name = settings->value("AudioOutput", "").toString(); - qInfo(logGui()) << "Got Audio Output: " << rxSetup.name; - - txSetup.name = settings->value("AudioInput", "").toString(); - // qInfo(logGui()) << "Got Audio Input: " << txSetup.name; int audioInputIndex = ui->audioInputCombo->findText(txSetup.name); - - rxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); - txSetup.resampleQuality = rxSetup.resampleQuality; - - udpPrefs.clientName = settings->value("ClientName", udpDefPrefs.clientName).toString(); - - settings->endGroup(); - - - settings->beginGroup("Server"); - serverConfig.enabled = settings->value("ServerEnabled", false).toBool(); - serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt(); - serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt(); - serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt(); - - serverConfig.users.clear(); - - int numUsers = settings->beginReadArray("Users"); - if (numUsers > 0) { - { - for (int f = 0; f < numUsers; f++) - { - settings->setArrayIndex(f); - SERVERUSER user; - user.username = settings->value("Username", "").toString(); - user.password = settings->value("Password", "").toString(); - user.userType = settings->value("UserType", 0).toInt(); - serverConfig.users.append(user); - } - } + int numRadios=settings->beginReadArray("Radios"); + int tempNum = numRadios; + if (numRadios == 0) { settings->endArray(); + numRadios = 1; } - else { - /* Support old way of storing users*/ - settings->endArray(); - numUsers = settings->value("ServerNumUsers", 2).toInt(); - for (int f = 0; f < numUsers; f++) - { - SERVERUSER user; - user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); - user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); - user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); - serverConfig.users.append(user); - } - } - - serverRxSetup.isinput = true; - serverTxSetup.isinput = false; - serverRxSetup.localAFgain = 255; - serverTxSetup.localAFgain = 255; - - serverRxSetup.name = settings->value("ServerAudioInput", "").toString(); - qInfo(logGui()) << "Got Server Audio Input: " << serverRxSetup.name; - - serverRxSetup.resampleQuality = rxSetup.resampleQuality; - serverTxSetup.resampleQuality = serverRxSetup.resampleQuality; - - serverTxSetup.name = settings->value("ServerAudioOutput", "").toString(); - qInfo(logGui()) << "Got Server Audio Output: " << serverTxSetup.name; - - settings->endGroup(); - - // Memory channels - - settings->beginGroup("Memory"); - int size = settings->beginReadArray("Channel"); - int chan = 0; - double freq; - unsigned char mode; - bool isSet; - - // Annoying: QSettings will write the array to the - // preference file starting the array at 1 and ending at 100. - // Thus, we are writing the channel number each time. - // It is also annoying that they get written with their array - // numbers in alphabetical order without zero padding. - // Also annoying that the preference groups are not written in - // the order they are specified here. - - for (int i = 0; i < size; i++) - { - settings->setArrayIndex(i); - chan = settings->value("chan", 0).toInt(); - freq = settings->value("freq", 12.345).toDouble(); - mode = settings->value("mode", 0).toInt(); - isSet = settings->value("isSet", false).toBool(); - - if (isSet) - { - mem.setPreset(chan, freq, (mode_kind)mode); - } - } - - settings->endArray(); - settings->endGroup(); - - - - #if defined(RTAUDIO) @@ -723,22 +475,6 @@ void servermain::loadSettings() unsigned int devices = audio->getDeviceCount(); qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; - for (unsigned int i = 1; i < devices; i++) { - info = audio->getDeviceInfo(i); - if (info.outputChannels > 0) { - if (serverTxSetup.name == info->name) { - serverTxSetup.port = i; - } - } - if (info.inputChannels > 0) { - if (serverRxSetup.name == info->name) { - serverRxSetup.port = i; - } - } - } - - delete audio; - #elif defined(PORTAUDIO) // Use PortAudio device enumeration @@ -758,154 +494,165 @@ void servermain::loadSettings() qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; const PaDeviceInfo* info; - for (int i = 0; i < numDevices; i++) - { - info = Pa_GetDeviceInfo(i); - if (info->maxInputChannels > 0) { - if (serverTxSetup.name == info->name) { - serverTxSetup.port = i; - } - } - if (info->maxOutputChannels > 0) { - if (serverRxSetup.name == info->name) { - serverRxSetup.port = i; - } - } - } + #else - // If no external library is configured, use QTMultimedia - // - // Set these to default audio devices initially. - rxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); - txSetup.port = QAudioDeviceInfo::defaultInputDevice(); - serverRxSetup.port = QAudioDeviceInfo::defaultInputDevice(); - serverTxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); - - - // Enumerate audio devices, need to do before settings are loaded. const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); - for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { - if (deviceInfo.deviceName() == serverTxSetup.name) { - serverTxSetup.port = deviceInfo; - qInfo(logGui()) << "Setting Server Audio Input: " << serverTxSetup.name; - } - } - const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); - for (const QAudioDeviceInfo& deviceInfo : audioInputs) { - if (deviceInfo.deviceName() == serverRxSetup.name) { - serverRxSetup.port = deviceInfo; - qInfo(logGui()) << "Setting Server Audio Output: " << serverRxSetup.name; + +#endif + for (int i = 0; i < numRadios; i++) { + if (tempNum == 0) { + settings->beginGroup("Radio"); + } + else { + settings->setArrayIndex(i); + } + RIGCONFIG tempPrefs; + tempPrefs.civAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); + tempPrefs.forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool(); + tempPrefs.serialPort = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString(); + tempPrefs.rigName = settings->value("RigName", "").toString(); + tempPrefs.baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); + + if (tempPrefs.rigName=="") + { + foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) + { + //qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); + if (serialPortInfo.portName() == tempPrefs.serialPort && !serialPortInfo.serialNumber().isEmpty()) + { + tempPrefs.rigName = serialPortInfo.serialNumber(); + } + } + } + QString guid = settings->value("GUID", "").toString(); + if (guid.isEmpty()) { + guid = QUuid::createUuid().toString(); + settings->setValue("GUID", guid); + } + memcpy(tempPrefs.guid, QUuid::fromString(guid).toRfc4122().constData(), sizeof(tempPrefs.guid)); + + tempPrefs.rxAudioSetup.isinput = true; + tempPrefs.txAudioSetup.isinput = false; + tempPrefs.rxAudioSetup.localAFgain = 255; + tempPrefs.txAudioSetup.localAFgain = 255; + tempPrefs.rxAudioSetup.resampleQuality = 4; + tempPrefs.txAudioSetup.resampleQuality = 4; + + tempPrefs.rxAudioSetup.name = settings->value("AudioInput", "").toString(); + tempPrefs.txAudioSetup.name = settings->value("AudioOutput", "").toString(); + + // Find the actual audio devices +#if defined(RTAUDIO) + for (unsigned int i = 1; i < devices; i++) { + info = audio->getDeviceInfo(i); + if (info.outputChannels > 0) { + if (tempPrefs.txAudio.name == info->name) { + tempPrefs.txAudio.port = i; + } + } + if (info.inputChannels > 0) { + if (tempPrefs.rxAudio.name == info->name) { + tempPrefs.rxAudio.port = i; + } + } + } +#elif defined(PORTAUDIO) + for (int i = 0; i < numDevices; i++) + { + info = Pa_GetDeviceInfo(i); + if (info->maxInputChannels > 0) { + if (tempPrefs.txAudio.name == info->name) { + tempPrefs.txAudio.port = i; + } + } + if (info->maxOutputChannels > 0) { + if (tempPrefs.rxAudio.name == info->name) { + tempPrefs.rxAudio.port = i; + } + } + } +#else + + /* If no external library is configured, use QTMultimedia + // Set these to default audio devices initially. + */ + + //qInfo(logAudio()) << "Looking for audio output devices"; + for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { + if (deviceInfo.deviceName() == tempPrefs.txAudioSetup.name) { + tempPrefs.txAudioSetup.port = deviceInfo; + } + } + + //qInfo(logAudio()) << "Looking for audio input devices"; + for (const QAudioDeviceInfo& deviceInfo : audioInputs) { + if (deviceInfo.deviceName() == tempPrefs.rxAudioSetup.name) { + tempPrefs.rxAudioSetup.port = deviceInfo; + } + } +#endif + tempPrefs.rig = Q_NULLPTR; + tempPrefs.rigThread = Q_NULLPTR; + serverConfig.rigs.append(tempPrefs); + if (tempNum == 0) { + settings->endGroup(); + } + } + if (tempNum > 0) { + settings->endArray(); + } + + + settings->beginGroup("Server"); + serverConfig.enabled = settings->value("ServerEnabled", false).toBool(); + serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt(); + serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt(); + serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt(); + + serverConfig.users.clear(); + + int numUsers = settings->beginReadArray("Users"); + if (numUsers > 0) { + { + for (int f = 0; f < numUsers; f++) + { + settings->setArrayIndex(f); + SERVERUSER user; + user.username = settings->value("Username", "").toString(); + user.password = settings->value("Password", "").toString(); + user.userType = settings->value("UserType", 0).toInt(); + serverConfig.users.append(user); + + } + } + settings->endArray(); + } + else { + /* Support old way of storing users just to get them loaded*/ + settings->endArray(); + numUsers = settings->value("ServerNumUsers", 2).toInt(); + for (int f = 0; f < numUsers; f++) + { + SERVERUSER user; + user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); + user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); + user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); + serverConfig.users.append(user); } } + settings->endGroup(); + settings->sync(); + +#if defined(RTAUDIO) + delete audio; #endif } - -void servermain:: getInitialRigState() -{ - // Initial list of queries to the radio. - // These are made when the program starts up - // and are used to adjust the UI to match the radio settings - // the polling interval is set at 200ms. Faster is possible but slower - // computers will glitch occassionally. - - issueDelayedCommand(cmdGetFreq); - issueDelayedCommand(cmdGetMode); - - issueDelayedCommand(cmdNone); - - issueDelayedCommand(cmdGetFreq); - issueDelayedCommand(cmdGetMode); - - // From left to right in the UI: - if (rigCaps.hasTransmit) - { - issueDelayedCommand(cmdGetDataMode); - issueDelayedCommand(cmdGetModInput); - issueDelayedCommand(cmdGetModDataInput); - } - issueDelayedCommand(cmdGetRxGain); - issueDelayedCommand(cmdGetAfGain); - issueDelayedCommand(cmdGetSql); - - if (rigCaps.hasTransmit) - { - issueDelayedCommand(cmdGetTxPower); - issueDelayedCommand(cmdGetCurrentModLevel); // level for currently selected mod sources - } - - issueDelayedCommand(cmdGetSpectrumRefLevel); - issueDelayedCommand(cmdGetDuplexMode); - - if(rigCaps.hasSpectrum) - { - issueDelayedCommand(cmdDispEnable); - issueDelayedCommand(cmdSpecOn); - } - - if (rigCaps.hasTransmit) - { - issueDelayedCommand(cmdGetModInput); - issueDelayedCommand(cmdGetModDataInput); - } - - if(rigCaps.hasCTCSS) - { - issueDelayedCommand(cmdGetTone); - issueDelayedCommand(cmdGetTSQL); - } - if(rigCaps.hasDTCS) - { - issueDelayedCommand(cmdGetDTCS); - } - issueDelayedCommand(cmdGetRptAccessMode); - - if(rigCaps.hasAntennaSel) - { - issueDelayedCommand(cmdGetAntenna); - } - if(rigCaps.hasAttenuator) - { - issueDelayedCommand(cmdGetAttenuator); - } - if(rigCaps.hasPreamp) - { - issueDelayedCommand(cmdGetPreamp); - } - - issueDelayedCommand(cmdGetRitEnabled); - issueDelayedCommand(cmdGetRitValue); - - if(rigCaps.hasIFShift) - issueDelayedCommand(cmdGetIFShift); - if(rigCaps.hasTBPF) - { - issueDelayedCommand(cmdGetTPBFInner); - issueDelayedCommand(cmdGetTPBFOuter); - } - - if(rigCaps.hasSpectrum) - { - issueDelayedCommand(cmdGetSpectrumMode); - issueDelayedCommand(cmdGetSpectrumSpan); - } - - issueDelayedCommand(cmdNone); - issueDelayedCommand(cmdStartRegularPolling); - - if(rigCaps.hasATU) - { - issueDelayedCommand(cmdGetATUStatus); - } - - delayedCommand->start(); -} - void servermain::receivePTTstatus(bool pttOn) { // This is the only place where amTransmitting and the transmit button text should be changed: @@ -913,700 +660,32 @@ void servermain::receivePTTstatus(bool pttOn) amTransmitting = pttOn; } - -void servermain::doCmd(commandtype cmddata) -{ - cmds cmd = cmddata.cmd; - std::shared_ptr data = cmddata.data; - - // This switch is for commands with parameters. - // the "default" for non-parameter commands is to call doCmd(cmd). - switch (cmd) - { - case cmdSetFreq: - { - lastFreqCmdTime_ms = QDateTime::currentMSecsSinceEpoch(); - freqt f = (*std::static_pointer_cast(data)); - emit setFrequency(0,f); - break; - } - case cmdSetMode: - { - mode_info m = (*std::static_pointer_cast(data)); - emit setMode(m); - break; - } - case cmdSetTxPower: - { - unsigned char txpower = (*std::static_pointer_cast(data)); - emit setTxPower(txpower); - break; - } - case cmdSetMicGain: - { - unsigned char micgain = (*std::static_pointer_cast(data)); - emit setTxPower(micgain); - break; - } - case cmdSetRxRfGain: - { - unsigned char rfgain = (*std::static_pointer_cast(data)); - emit setRfGain(rfgain); - break; - } - case cmdSetModLevel: - { - unsigned char modlevel = (*std::static_pointer_cast(data)); - rigInput currentIn; - if(usingDataMode) - { - currentIn = currentModDataSrc; - } else { - currentIn = currentModSrc; - } - emit setModLevel(currentIn, modlevel); - break; - } - case cmdSetAfGain: - { - unsigned char afgain = (*std::static_pointer_cast(data)); - emit setAfGain(afgain); - break; - } - case cmdSetSql: - { - unsigned char sqlLevel = (*std::static_pointer_cast(data)); - emit setSql(sqlLevel); - break; - } - case cmdSetIFShift: - { - unsigned char IFShiftLevel = (*std::static_pointer_cast(data)); - emit setIFShift(IFShiftLevel); - break; - } - case cmdSetTPBFInner: - { - unsigned char innterLevel = (*std::static_pointer_cast(data)); - emit setTPBFInner(innterLevel); - break; - } - case cmdSetTPBFOuter: - { - unsigned char outerLevel = (*std::static_pointer_cast(data)); - emit setTPBFOuter(outerLevel); - break; - } - case cmdSetPTT: - { - bool pttrequest = (*std::static_pointer_cast(data)); - emit setPTT(pttrequest); - break; - } - case cmdSetATU: - { - bool atuOn = (*std::static_pointer_cast(data)); - emit setATU(atuOn); - break; - } - case cmdSetUTCOffset: - { - timekind u = (*std::static_pointer_cast(data)); - emit setUTCOffset(u); - break; - } - case cmdSetTime: - { - timekind t = (*std::static_pointer_cast(data)); - emit setTime(t); - break; - } - case cmdSetDate: - { - datekind d = (*std::static_pointer_cast(data)); - emit setDate(d); - break; - } - default: - doCmd(cmd); - break; - } - -} - - -void servermain::doCmd(cmds cmd) -{ - // Use this function to take action upon a command. - switch (cmd) - { - case cmdNone: - //qInfo(logSystem()) << "NOOP"; - break; - case cmdGetRigID: - emit getRigID(); - break; - case cmdGetRigCIV: - // if(!know rig civ already) - if (!haveRigCaps) - { - emit getRigCIV(); - issueDelayedCommand(cmdGetRigCIV); // This way, we stay here until we get an answer. - } - break; - case cmdGetFreq: - emit getFrequency(); - break; - case cmdGetMode: - emit getMode(); - break; - case cmdGetDataMode: - if (rigCaps.hasDataModes) - emit getDataMode(); - break; - case cmdSetModeFilter: - emit setMode(setModeVal, setFilterVal); - break; - case cmdSetDataModeOff: - break; - case cmdSetDataModeOn: - break; - case cmdGetRitEnabled: - emit getRitEnabled(); - break; - case cmdGetRitValue: - emit getRitValue(); - break; - case cmdGetModInput: - emit getModInput(false); - break; - case cmdGetModDataInput: - emit getModInput(true); - break; - case cmdGetCurrentModLevel: - // TODO: Add delay between these queries - emit getModInputLevel(currentModSrc); - emit getModInputLevel(currentModDataSrc); - break; - case cmdGetDuplexMode: - emit getDuplexMode(); - break; - case cmdGetTone: - emit getTone(); - break; - case cmdGetTSQL: - emit getTSQL(); - break; - case cmdGetDTCS: - emit getDTCS(); - break; - case cmdGetRptAccessMode: - emit getRptAccessMode(); - break; - case cmdDispEnable: - emit scopeDisplayEnable(); - break; - case cmdDispDisable: - emit scopeDisplayDisable(); - break; - case cmdGetSpectrumMode: - emit getScopeMode(); - break; - case cmdGetSpectrumSpan: - emit getScopeSpan(); - break; - case cmdSpecOn: - emit spectOutputEnable(); - break; - case cmdSpecOff: - emit spectOutputDisable(); - break; - case cmdGetRxGain: - emit getRfGain(); - break; - case cmdGetAfGain: - emit getAfGain(); - break; - case cmdGetSql: - emit getSql(); - break; - case cmdGetIFShift: - emit getIfShift(); - break; - case cmdGetTPBFInner: - emit getTPBFInner(); - break; - case cmdGetTPBFOuter: - emit getTPBFOuter(); - break; - case cmdGetTxPower: - emit getTxPower(); - break; - case cmdGetMicGain: - emit getMicGain(); - break; - case cmdGetSpectrumRefLevel: - emit getSpectrumRefLevel(); - break; - case cmdGetATUStatus: - if (rigCaps.hasATU) - emit getATUStatus(); - break; - case cmdStartATU: - if (rigCaps.hasATU) - emit startATU(); - break; - case cmdGetAttenuator: - emit getAttenuator(); - break; - case cmdGetPreamp: - emit getPreamp(); - break; - case cmdGetAntenna: - emit getAntenna(); - break; - case cmdScopeCenterMode: - emit setScopeMode(spectModeCenter); - break; - case cmdScopeFixedMode: - emit setScopeMode(spectModeFixed); - break; - case cmdGetPTT: - emit getPTT(); - break; - case cmdGetTxRxMeter: - if (amTransmitting) - emit getMeters(meterPower); - else - emit getMeters(meterS); - break; - case cmdGetSMeter: - if (!amTransmitting) - emit getMeters(meterS); - break; - case cmdGetCenterMeter: - if (!amTransmitting) - emit getMeters(meterCenter); - break; - case cmdGetPowerMeter: - if (amTransmitting) - emit getMeters(meterPower); - break; - case cmdGetSWRMeter: - if (amTransmitting) - emit getMeters(meterSWR); - break; - case cmdGetIdMeter: - emit getMeters(meterCurrent); - break; - case cmdGetVdMeter: - emit getMeters(meterVoltage); - break; - case cmdGetALCMeter: - if (amTransmitting) - emit getMeters(meterALC); - break; - case cmdGetCompMeter: - if (amTransmitting) - emit getMeters(meterComp); - break; - case cmdStartRegularPolling: - runPeriodicCommands = true; - break; - case cmdStopRegularPolling: - runPeriodicCommands = false; - break; - case cmdQueNormalSpeed: - if (usingLAN) - { - delayedCommand->setInterval(delayedCmdIntervalLAN_ms); - } - else { - delayedCommand->setInterval(delayedCmdIntervalSerial_ms); - } - break; - default: - qInfo(logSystem()) << __PRETTY_FUNCTION__ << "WARNING: Command fell through of type: " << (unsigned int)cmd; - break; - } -} - - - - -void servermain::sendRadioCommandLoop() -{ - // Called by the periodicPollingTimer, see setInitialTiming() - - if(!(loopTickCounter % 2)) - { - // if ther's a command waiting, run it. - if(!delayedCmdQue.empty()) - { - commandtype cmddata = delayedCmdQue.front(); - delayedCmdQue.pop_front(); - doCmd(cmddata); - } else if(!(loopTickCounter % 10)) - { - // pick from useful queries to make now and then - if(haveRigCaps && !slowPollCmdQueue.empty()) - { - - int nCmds = slowPollCmdQueue.size(); - cmds sCmd = slowPollCmdQueue[(slowCmdNum++)%nCmds]; - doCmd(sCmd); - } - } - } else { - // odd-number ticks: - // s-meter or other metering - if(haveRigCaps && !periodicCmdQueue.empty()) - { - int nCmds = periodicCmdQueue.size(); - cmds pcmd = periodicCmdQueue[ (pCmdNum++)%nCmds ]; - doCmd(pcmd); - } - } - loopTickCounter++; -} - -void servermain::issueDelayedCommand(cmds cmd) -{ - // Append to end of command queue - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = NULL; - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueDelayedCommandPriority(cmds cmd) -{ - // Places the new command at the top of the queue - // Use only when needed. - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = NULL; - delayedCmdQue.push_front(cmddata); -} - -void servermain::issueDelayedCommandUnique(cmds cmd) -{ - // Use this function to insert commands where - // multiple (redundant) commands don't make sense. - - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = NULL; - - // The following is both expensive and not that great, - // since it does not check if the arguments are the same. - bool found = false; - for(unsigned int i=0; i < delayedCmdQue.size(); i++) - { - if(delayedCmdQue.at(i).cmd == cmd) - { - found = true; - break; - } - } - - if(!found) - { - delayedCmdQue.push_front(cmddata); - } - -// if( std::find(delayedCmdQue.begin(), delayedCmdQue.end(), cmddata ) == delayedCmdQue.end()) -// { -// delayedCmdQue.push_front(cmddata); -// } - -} - -void servermain::issueCmd(cmds cmd, mode_info m) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new mode_info(m)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, freqt f) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new freqt(f)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, timekind t) -{ - qDebug(logSystem()) << "Issuing timekind command with data: " << t.hours << " hours, " << t.minutes << " minutes, " << t.isMinus << " isMinus"; - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new timekind(t)); - delayedCmdQue.push_front(cmddata); -} - -void servermain::issueCmd(cmds cmd, datekind d) -{ - qDebug(logSystem()) << "Issuing datekind command with data: " << d.day << " day, " << d.month << " month, " << d.year << " year."; - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new datekind(d)); - delayedCmdQue.push_front(cmddata); -} - -void servermain::issueCmd(cmds cmd, int i) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new int(i)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, char c) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new char(c)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, bool b) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new bool(b)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, unsigned char c) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new unsigned char(c)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmdUniquePriority(cmds cmd, bool b) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new bool(b)); - delayedCmdQue.push_front(cmddata); - removeSimilarCommand(cmd); -} - -void servermain::issueCmdUniquePriority(cmds cmd, unsigned char c) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new unsigned char(c)); - delayedCmdQue.push_front(cmddata); - removeSimilarCommand(cmd); -} - -void servermain::issueCmdUniquePriority(cmds cmd, char c) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new char(c)); - delayedCmdQue.push_front(cmddata); - removeSimilarCommand(cmd); -} - -void servermain::issueCmdUniquePriority(cmds cmd, freqt f) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new freqt(f)); - delayedCmdQue.push_front(cmddata); - removeSimilarCommand(cmd); -} - -void servermain::removeSimilarCommand(cmds cmd) -{ - // pop anything out that is of the same kind of command: - // pop anything out that is of the same kind of command: - // Start at 1 since we put one in at zero that we want to keep. - for(unsigned int i=1; i < delayedCmdQue.size(); i++) - { - if(delayedCmdQue.at(i).cmd == cmd) - { - //delayedCmdQue[i].cmd = cmdNone; - delayedCmdQue.erase(delayedCmdQue.begin()+i); - // i -= 1; - } - } -} - -void servermain::receiveRigID(rigCapabilities rigCaps) -{ - // Note: We intentionally request rigID several times - // because without rigID, we can't do anything with the waterfall. - if(haveRigCaps) - { - return; - } else { - - qDebug(logSystem()) << "Rig name: " << rigCaps.modelName; - qDebug(logSystem()) << "Has LAN capabilities: " << rigCaps.hasLan; - qDebug(logSystem()) << "Rig ID received into servermain: spectLenMax: " << rigCaps.spectLenMax; - qDebug(logSystem()) << "Rig ID received into servermain: spectAmpMax: " << rigCaps.spectAmpMax; - qDebug(logSystem()) << "Rig ID received into servermain: spectSeqMax: " << rigCaps.spectSeqMax; - qDebug(logSystem()) << "Rig ID received into servermain: hasSpectrum: " << rigCaps.hasSpectrum; - - this->rigCaps = rigCaps; - this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true. - haveRigCaps = true; - // Added so that server receives rig capabilities. - emit sendRigCaps(rigCaps); - - } -} - -void servermain::initPeriodicCommands() -{ - // This function places periodic polling commands into a queue. - // The commands are run using a timer, - // and the timer is started by the delayed command cmdStartPeriodicTimer. - -} - -void servermain::insertPeriodicCommand(cmds cmd, unsigned char priority) -{ - // TODO: meaningful priority - // These commands get run at the fastest pace possible - // Typically just metering. - if(priority < 10) - { - periodicCmdQueue.push_front(cmd); - } else { - periodicCmdQueue.push_back(cmd); - } -} - -void servermain::insertPeriodicCommandUnique(cmds cmd) -{ - // Use this function to insert a non-duplicate command - // into the fast periodic polling queue, typically - // meter commands where high refresh rates are desirable. - - removePeriodicCommand(cmd); - periodicCmdQueue.push_front(cmd); -} - -void servermain::removePeriodicCommand(cmds cmd) -{ - while(true) - { - auto it = std::find(this->periodicCmdQueue.begin(), this->periodicCmdQueue.end(), cmd); - if(it != periodicCmdQueue.end()) - { - periodicCmdQueue.erase(it); - } else { - break; - } - } -} - - -void servermain::insertSlowPeriodicCommand(cmds cmd, unsigned char priority) -{ - // TODO: meaningful priority - // These commands are run every 20 "ticks" of the primary radio command loop - // Basically 20 times less often than the standard peridic command - if(priority < 10) - { - slowPollCmdQueue.push_front(cmd); - } else { - slowPollCmdQueue.push_back(cmd); - } -} - - void servermain::handlePttLimit() { - // transmission time exceeded! + // ptt time exceeded! std::cout << "Transmit timeout at 3 minutes. Sending PTT OFF command now.\n"; - issueCmdUniquePriority(cmdSetPTT, false); - issueDelayedCommand(cmdGetPTT); -} - -void servermain::calculateTimingParameters() -{ - // Function for calculating polling parameters. - // Requires that we know the "baud rate" of the actual - // radio connection. - - // baud on the serial port reflects the actual rig connection, - // even if a client-server connection is being used. - // Computed time for a 10 byte message, with a safety factor of 2. - - if (prefs.serialPortBaud == 0) - { - prefs.serialPortBaud = 9600; - qInfo(logSystem()) << "WARNING: baud rate received was zero. Assuming 9600 baud, performance may suffer."; - } - - unsigned int usPerByte = 9600*1000 / prefs.serialPortBaud; - unsigned int msMinTiming=usPerByte * 10*2/1000; - if(msMinTiming < 25) - msMinTiming = 25; - - if(haveRigCaps && rigCaps.hasFDcomms) - { - delayedCommand->setInterval( msMinTiming); // 20 byte message - } else { - delayedCommand->setInterval( msMinTiming * 3); // 20 byte message - } - - - qInfo(logSystem()) << "Delay command interval timing: " << delayedCommand->interval() << "ms"; - - // Normal: - delayedCmdIntervalLAN_ms = delayedCommand->interval(); - delayedCmdIntervalSerial_ms = delayedCommand->interval(); - - // startup initial state: - delayedCmdStartupInterval_ms = delayedCommand->interval() * 3; + emit setPTT(false); + emit getPTT(); } void servermain::receiveBaudRate(quint32 baud) { qInfo() << "Received serial port baud rate from remote server:" << baud; prefs.serialPortBaud = baud; - calculateTimingParameters(); } void servermain::powerRigOn() { emit sendPowerOn(); - - delayedCommand->setInterval(3000); // 3 seconds - issueDelayedCommand(cmdQueNormalSpeed); - issueDelayedCommand(cmdStartRegularPolling); // s-meter, etc - delayedCommand->start(); } void servermain::powerRigOff() { - delayedCommand->stop(); - delayedCmdQue.clear(); - emit sendPowerOff(); } void servermain::receiveStateInfo(rigstate* state) { - qInfo("Setting rig state for wfmain"); - rigState = state; -} - - -void servermain::handleCtrlC(int sig) { - if (sig == 2) { - QCoreApplication::quit(); - //exit(EXIT_FAILURE); - } + qInfo("Received rig state for wfmain"); + //rigState = state; } diff --git a/servermain.h b/servermain.h index 7a870ff..cea805c 100644 --- a/servermain.h +++ b/servermain.h @@ -168,30 +168,24 @@ private slots: void receiveCommReady(); void receivePTTstatus(bool pttOn); - void receiveRigID(rigCapabilities rigCaps); void receiveFoundRigID(rigCapabilities rigCaps); void receiveSerialPortError(QString port, QString errorText); - void sendRadioCommandLoop(); void receiveBaudRate(quint32 baudrate); void handlePttLimit(); - void receiveStatusUpdate(QString text); + void receiveStatusUpdate(networkStatus status); void receiveStateInfo(rigstate* state); private: QSettings *settings=Q_NULLPTR; void loadSettings(); - void getInitialRigState(); - void openRig(); void powerRigOff(); void powerRigOn(); QStringList portList; QString serialPortRig; - rigCommander * rig=Q_NULLPTR; - QThread* rigThread = Q_NULLPTR; QTimer * delayedCommand; QTimer * pttTimer; uint16_t loopTickCounter; @@ -199,7 +193,6 @@ private: QString lastMessage=""; void makeRig(); - void rigConnections(); void removeRig(); void findSerialPort(); @@ -235,119 +228,19 @@ private: unsigned char setModeVal=0; unsigned char setFilterVal=0; - enum cmds {cmdNone, cmdGetRigID, cmdGetRigCIV, cmdGetFreq, cmdSetFreq, cmdGetMode, cmdSetMode, - cmdGetDataMode, cmdSetModeFilter, cmdSetDataModeOn, cmdSetDataModeOff, cmdGetRitEnabled, cmdGetRitValue, - cmdSpecOn, cmdSpecOff, cmdDispEnable, cmdDispDisable, cmdGetRxGain, cmdSetRxRfGain, cmdGetAfGain, cmdSetAfGain, - cmdGetSql, cmdSetSql, cmdGetIFShift, cmdSetIFShift, cmdGetTPBFInner, cmdSetTPBFInner, - cmdGetTPBFOuter, cmdSetTPBFOuter, cmdGetATUStatus, - cmdSetATU, cmdStartATU, cmdGetSpectrumMode, - cmdGetSpectrumSpan, cmdScopeCenterMode, cmdScopeFixedMode, cmdGetPTT, cmdSetPTT, - cmdGetTxPower, cmdSetTxPower, cmdGetMicGain, cmdSetMicGain, cmdSetModLevel, - cmdGetSpectrumRefLevel, cmdGetDuplexMode, cmdGetModInput, cmdGetModDataInput, - cmdGetCurrentModLevel, cmdStartRegularPolling, cmdStopRegularPolling, cmdQueNormalSpeed, - cmdGetVdMeter, cmdGetIdMeter, cmdGetSMeter, cmdGetCenterMeter, cmdGetPowerMeter, - cmdGetSWRMeter, cmdGetALCMeter, cmdGetCompMeter, cmdGetTxRxMeter, - cmdGetTone, cmdGetTSQL, cmdGetDTCS, cmdGetRptAccessMode, cmdGetPreamp, cmdGetAttenuator, cmdGetAntenna, - cmdSetTime, cmdSetDate, cmdSetUTCOffset}; - - struct commandtype { - cmds cmd; - std::shared_ptr data; - }; - - std::deque delayedCmdQue; // rapid que for commands to the radio - std::deque periodicCmdQueue; // rapid que for metering - std::deque slowPollCmdQueue; // slow, regular checking for UI sync - void doCmd(cmds cmd); - void doCmd(commandtype cmddata); - - void issueCmd(cmds cmd, freqt f); - void issueCmd(cmds cmd, mode_info m); - void issueCmd(cmds cmd, timekind t); - void issueCmd(cmds cmd, datekind d); - void issueCmd(cmds cmd, int i); - void issueCmd(cmds cmd, unsigned char c); - void issueCmd(cmds cmd, char c); - void issueCmd(cmds cmd, bool b); - - // These commands pop_front and remove similar commands: - void issueCmdUniquePriority(cmds cmd, bool b); - void issueCmdUniquePriority(cmds cmd, unsigned char c); - void issueCmdUniquePriority(cmds cmd, char c); - void issueCmdUniquePriority(cmds cmd, freqt f); - - void removeSimilarCommand(cmds cmd); - - qint64 lastFreqCmdTime_ms; - - int pCmdNum = 0; - int delayedCmdIntervalLAN_ms = 100; - int delayedCmdIntervalSerial_ms = 100; - int delayedCmdStartupInterval_ms = 100; - bool runPeriodicCommands; bool usingLAN = false; - // Radio time sync: - QTimer *timeSync; - bool waitingToSetTimeDate; - void setRadioTimeDatePrep(); - timekind timesetpoint; - timekind utcsetting; - datekind datesetpoint; - - freqMemory mem; - struct colors { - QColor Dark_PlotBackground; - QColor Dark_PlotAxisPen; - QColor Dark_PlotLegendTextColor; - QColor Dark_PlotLegendBorderPen; - QColor Dark_PlotLegendBrush; - QColor Dark_PlotTickLabel; - QColor Dark_PlotBasePen; - QColor Dark_PlotTickPen; - QColor Dark_PeakPlotLine; - QColor Dark_TuningLine; - - QColor Light_PlotBackground; - QColor Light_PlotAxisPen; - QColor Light_PlotLegendTextColor; - QColor Light_PlotLegendBorderPen; - QColor Light_PlotLegendBrush; - QColor Light_PlotTickLabel; - QColor Light_PlotBasePen; - QColor Light_PlotTickPen; - QColor Light_PeakPlotLine; - QColor Light_TuningLine; - - } colorScheme; - struct preferences { - bool useFullScreen; - bool useDarkMode; - bool useSystemTheme; - bool drawPeaks; - bool wfAntiAlias; - bool wfInterpolate; - QString stylesheetPath; unsigned char radioCIVAddr; bool CIVisRadioModel; bool forceRTSasPTT; QString serialPortRadio; quint32 serialPortBaud; - bool enablePTT; - bool niceTS; - bool enableLAN; - bool enableRigCtlD; - quint16 rigCtlPort; - colors colorScheme; - QString virtualSerialPort; unsigned char localAFgain; - unsigned int wflength; - int wftheme; - bool confirmExit; - bool confirmPowerOff; - meterKind meter2Type; - // plot scheme + audioSetup rxAudio; + audioSetup txAudio; + rigCapabilities rigCaps; + bool haveRigCaps = false; } prefs; preferences defPrefs; @@ -361,35 +254,9 @@ private: audioSetup serverRxSetup; audioSetup serverTxSetup; - colors defaultColors; void setDefPrefs(); // populate default values to default prefs - void issueDelayedCommand(cmds cmd); - void issueDelayedCommandPriority(cmds cmd); - void issueDelayedCommandUnique(cmds cmd); - - // Fast command queue: - void initPeriodicCommands(); - void insertPeriodicCommand(cmds cmd, unsigned char priority); - void insertPeriodicCommandUnique(cmds cmd); - void removePeriodicCommand(cmds cmd); - - void insertSlowPeriodicCommand(cmds cmd, unsigned char priority); - void calculateTimingParameters(); - - - cmds meterKindToMeterCommand(meterKind m); - - int oldFreqDialVal; - - rigCapabilities rigCaps; - rigInput currentModSrc = inputUnknown; - rigInput currentModDataSrc = inputUnknown; - mode_kind currentMode = modeUSB; - mode_info currentModeInfo; - - bool haveRigCaps; bool amTransmitting; bool usingDataMode = false; @@ -405,33 +272,9 @@ private: rigCtlD* rigCtl = Q_NULLPTR; QThread* serverThread = Q_NULLPTR; - void bandStackBtnClick(); - bool waitingForBandStackRtn; - char bandStkBand; - char bandStkRegCode; - - bool freqLock; - - float tsPlus; - float tsPlusShift; - float tsPlusControl; - float tsPage; - float tsPageShift; - float tsWfScroll; - - unsigned int tsPlusHz; - unsigned int tsPlusShiftHz; - unsigned int tsPlusControlHz; - unsigned int tsPageHz; - unsigned int tsPageShiftHz; - unsigned int tsWfScrollHz; - unsigned int tsKnobHz; - rigstate* rigState = Q_NULLPTR; SERVERCONFIG serverConfig; - - static void handleCtrlC(int sig); }; Q_DECLARE_METATYPE(struct rigCapabilities) diff --git a/udpserver.cpp b/udpserver.cpp index d313249..11ddee5 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -4,8 +4,8 @@ #define STALE_CONNECTION 15 #define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms #define AUDIO_SEND_PERIOD 20 // -udpServer::udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudio) : - config(config), +udpServer::udpServer(SERVERCONFIG* config, audioSetup outAudio, audioSetup inAudio) : + config(*config), outAudio(outAudio), inAudio(inAudio) { @@ -15,6 +15,31 @@ udpServer::udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudi void udpServer::init() { + for (RIGCONFIG rig : config.rigs) + { + qDebug(logUdpServer()) << "CIV:" << rig.civAddr; + qDebug(logUdpServer()) << "Model:" << rig.modelName; + qDebug(logUdpServer()) << "Name:" << rig.rigName; + qDebug(logUdpServer()) << "CIV:" << rig.civAddr; + qDebug(logUdpServer()).noquote() << QString("GUID: {%1%2%3%4-%5%6-%7%8-%9%10-%11%12%13%14%15%16}") + .arg(rig.guid[0], 2, 16, QLatin1Char('0')) + .arg(rig.guid[1], 2, 16, QLatin1Char('0')) + .arg(rig.guid[2], 2, 16, QLatin1Char('0')) + .arg(rig.guid[3], 2, 16, QLatin1Char('0')) + .arg(rig.guid[4], 2, 16, QLatin1Char('0')) + .arg(rig.guid[5], 2, 16, QLatin1Char('0')) + .arg(rig.guid[6], 2, 16, QLatin1Char('0')) + .arg(rig.guid[7], 2, 16, QLatin1Char('0')) + .arg(rig.guid[8], 2, 16, QLatin1Char('0')) + .arg(rig.guid[9], 2, 16, QLatin1Char('0')) + .arg(rig.guid[10], 2, 16, QLatin1Char('0')) + .arg(rig.guid[11], 2, 16, QLatin1Char('0')) + .arg(rig.guid[12], 2, 16, QLatin1Char('0')) + .arg(rig.guid[13], 2, 16, QLatin1Char('0')) + .arg(rig.guid[14], 2, 16, QLatin1Char('0')) + .arg(rig.guid[15], 2, 16, QLatin1Char('0')) + ; + } srand(time(NULL)); // Generate random key timeStarted.start(); // Convoluted way to find the external IP address, there must be a better way???? @@ -112,7 +137,17 @@ udpServer::~udpServer() void udpServer::receiveRigCaps(rigCapabilities caps) { - this->rigCaps = caps; + for (RIGCONFIG &rig: config.rigs) { + if (!memcmp(rig.guid, caps.guid, sizeof(rig.guid))) { + // Matching rig, fill-in missing details + rig.rigAvailable = true; + rig.modelName = caps.modelName; + rig.civAddr = caps.civ; + if (rig.rigName=="") { + rig.rigName = caps.modelName; + } + } + } } void udpServer::controlReceived() @@ -164,8 +199,6 @@ void udpServer::controlReceived() connect(current->retransmitTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRetransmitRequest, this, current)); current->retransmitTimer->start(RETRANSMIT_PERIOD); - - current->commonCap = 0x8010; qInfo(logUdpServer()) << current->ipAddress.toString() << ": New Control connection created"; if (connMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) @@ -234,7 +267,9 @@ void udpServer::controlReceived() // Request for new token qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received create token request"; sendCapabilities(current); - sendConnectionInfo(current); + for (RIGCONFIG& radio : config.rigs) { + sendConnectionInfo(current, radio.guid); + } } else if (in->res == 0x01) { // Token disconnect @@ -245,7 +280,12 @@ void udpServer::controlReceived() // Disconnect audio/civ sendTokenResponse(current, in->res); current->isStreaming = false; - sendConnectionInfo(current); + for (RIGCONFIG& radio : config.rigs) { + if (!memcmp(radio.guid, current->guid, sizeof(radio.guid))) + { + sendConnectionInfo(current, radio.guid); + } + } } else { qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received token request"; @@ -301,10 +341,10 @@ void udpServer::controlReceived() current->txBufferLen = qFromBigEndian(in->txbuffer); current->authInnerSeq = in->innerseq; - memcpy(current->macaddress, in->macaddress, 6); + memcpy(current->guid, in->guid, sizeof(in->guid)); sendStatus(current); current->authInnerSeq = 0x00; - sendConnectionInfo(current); + sendConnectionInfo(current,in->guid); qInfo(logUdpServer()) << current->ipAddress.toString() << ": rxCodec:" << current->rxCodec << " txCodec:" << current->txCodec << " rxSampleRate" << current->rxSampleRate << " txSampleRate" << current->txSampleRate << @@ -316,55 +356,62 @@ void udpServer::controlReceived() audioSetup setup; setup.resampleQuality = config.resampleQuality; + for (RIGCONFIG& radio : config.rigs) { + if (!memcmp(radio.guid, current->guid, sizeof(radio.guid)) && radio.txaudio == Q_NULLPTR) + { + radio.txAudioSetup.codec = current->txCodec; + radio.txAudioSetup.samplerate = current->txSampleRate; + radio.txAudioSetup.isinput = false; + radio.txAudioSetup.latency = current->txBufferLen; + outAudio.isinput = false; - if (txaudio == Q_NULLPTR) - { - outAudio.codec = current->txCodec; - outAudio.samplerate = current->txSampleRate; - outAudio.isinput = false; - outAudio.latency = current->txBufferLen; - outAudio.isinput = false; + radio.txaudio = new audioHandler(); + radio.txAudioThread = new QThread(this); - txaudio = new audioHandler(); - txAudioThread = new QThread(this); + radio.txaudio->moveToThread(radio.txAudioThread); - txaudio->moveToThread(txAudioThread); + radio.txAudioThread->start(QThread::TimeCriticalPriority); - txAudioThread->start(QThread::TimeCriticalPriority); + //connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); + connect(radio.txAudioThread, SIGNAL(finished()), radio.txaudio, SLOT(deleteLater())); - connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); - connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); + QMetaObject::invokeMethod(radio.txaudio, [=]() { + radio.txaudio->init(radio.txAudioSetup); + }, Qt::QueuedConnection); - emit setupTxAudio(outAudio); - hasTxAudio = datagram.senderAddress(); + emit setupTxAudio(outAudio); + hasTxAudio = datagram.senderAddress(); - connect(this, SIGNAL(haveAudioData(audioPacket)), txaudio, SLOT(incomingAudio(audioPacket))); + connect(this, SIGNAL(haveAudioData(audioPacket)), radio.txaudio, SLOT(incomingAudio(audioPacket))); - } - if (rxaudio == Q_NULLPTR) - { - inAudio.codec = current->rxCodec; - inAudio.samplerate = current->rxSampleRate; - inAudio.latency = current->txBufferLen; - inAudio.isinput = true; + } + if (!memcmp(radio.guid, current->guid, sizeof(radio.guid)) && radio.rxaudio == Q_NULLPTR) + { + radio.rxAudioSetup.codec = current->rxCodec; + radio.rxAudioSetup.samplerate = current->rxSampleRate; + radio.rxAudioSetup.latency = current->txBufferLen; + radio.rxAudioSetup.isinput = true; - rxaudio = new audioHandler(); + radio.rxaudio = new audioHandler(); - rxAudioThread = new QThread(this); + radio.rxAudioThread = new QThread(this); - rxaudio->moveToThread(rxAudioThread); + radio.rxaudio->moveToThread(radio.rxAudioThread); - rxAudioThread->start(QThread::TimeCriticalPriority); + radio.rxAudioThread->start(QThread::TimeCriticalPriority); - connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); - connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); + //connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); + connect(radio.rxAudioThread, SIGNAL(finished()), radio.rxaudio, SLOT(deleteLater())); - emit setupRxAudio(inAudio); + QMetaObject::invokeMethod(radio.rxaudio, [=]() { + radio.rxaudio->init(radio.rxAudioSetup); + }, Qt::QueuedConnection); - rxAudioTimer = new QTimer(); - rxAudioTimer->setTimerType(Qt::PreciseTimer); - connect(rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); - rxAudioTimer->start(TXAUDIO_PERIOD); + radio.rxAudioTimer = new QTimer(); + radio.rxAudioTimer->setTimerType(Qt::PreciseTimer); + connect(radio.rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); + radio.rxAudioTimer->start(TXAUDIO_PERIOD); + } } } @@ -420,6 +467,7 @@ void udpServer::civReceived() { current->controlClient = client; client->civClient = current; + memcpy(current->guid, client->guid, sizeof(current->guid)); } } } @@ -518,9 +566,18 @@ void udpServer::civReceived() qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId; qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId; } else if (r.length() > lastFE+2 && (quint8)r[lastFE+2] != 0xE1) { qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected invalid remote CI-V:" << hex << (quint8)r[lastFE+2]; - } + } + + for (RIGCONFIG& radio : config.rigs) { + if (!memcmp(radio.guid, current->guid, sizeof(radio.guid))) + { + // Only send to the rig that it belongs to! + QMetaObject::invokeMethod(radio.rig, [=]() { + radio.rig->dataFromServer(r.mid(0x15));; + }, Qt::DirectConnection); + } + } - emit haveDataFromServer(r.mid(0x15)); } else { qInfo(logUdpServer()) << current->ipAddress.toString() << ": Datalen mismatch " << quint16(in->datalen + 0x15) << ":" << (quint16)in->len; @@ -571,6 +628,7 @@ void udpServer::audioReceived() { current->controlClient = client; client->audioClient = current; + memcpy(current->guid, client->guid, sizeof(current->guid)); } } } @@ -743,7 +801,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) } default: { - //break; + //break } } @@ -1036,13 +1094,8 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed) void udpServer::sendCapabilities(CLIENT* c) { - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending Capabilities :" << c->txSeq; - capabilities_packet p; - radio_cap_packet r; memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - memset(r.packet, 0x0, sizeof(r)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); p.type = 0x00; p.seq = c->txSeq; p.sentid = c->myId; @@ -1050,79 +1103,86 @@ void udpServer::sendCapabilities(CLIENT* c) p.innerseq = c->authInnerSeq; p.tokrequest = c->tokenRx; p.token = c->tokenTx; - p.payloadsize = sizeof(p)-0x0f; p.res = 0x0202; - p.numradios = 0x01; - r.commoncap = c->commonCap; - - memcpy(r.macaddress, macAddress.toLocal8Bit(), 6); - // IRU seems to expect an "Icom" mac address so replace the first 3 octets of our Mac with one in their range! - memcpy(r.macaddress, QByteArrayLiteral("\x00\x90\xc7").constData(), 3); - memcpy(r.name, rigCaps.modelName.toLocal8Bit(), rigCaps.modelName.length()); - memcpy(r.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11); - - if (rigCaps.hasWiFi && !rigCaps.hasEthernet) { - r.conntype = 0x0707; // 0x0707 for wifi rig. - } - else { - r.conntype = 0x073f; // 0x073f for ethernet rig. - } - - r.civ = rigCaps.civ; - r.baudrate = (quint32)qToBigEndian(config.baudRate); - /* - 0x80 = 12K only - 0x40 = 44.1K only - 0x20 = 22.05K only - 0x10 = 11.025K only - 0x08 = 48K only - 0x04 = 32K only - 0x02 = 16K only - 0x01 = 8K only - */ - if (rxaudio == Q_NULLPTR) { - r.rxsample = 0x8b01; // all rx sample frequencies supported - } - else { - if (rxSampleRate == 48000) { - r.rxsample = 0x0800; // fixed rx sample frequency - } - else if (rxSampleRate == 32000) { - r.rxsample = 0x0400; - } - else if (rxSampleRate == 24000) { - r.rxsample = 0x0001; - } - else if (rxSampleRate == 16000) { - r.rxsample = 0x0200; - } - else if (rxSampleRate == 12000) { - r.rxsample = 0x8000; - } - } - - if (txaudio == Q_NULLPTR) { - r.txsample = 0x8b01; // all tx sample frequencies supported - r.enablea = 0x01; // 0x01 enables TX 24K mode? - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Client will have TX audio"; - } - else { - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Disable tx audio for client"; - r.txsample = 0; - } - - // I still don't know what these are? - r.enableb = 0x01; // 0x01 doesn't seem to do anything? - r.enablec = 0x01; // 0x01 doesn't seem to do anything? - r.capf = 0x5001; - r.capg = 0x0190; + p.numradios = config.rigs.count(); SEQBUFENTRY s; s.seqNum = p.seq; s.timeSent = QTime::currentTime(); s.retransmitCount = 0; - s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - s.data.append(QByteArray::fromRawData((const char*)r.packet, sizeof(r))); + + for (RIGCONFIG &rig : config.rigs) { + qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending Capabilities :" << c->txSeq << "for" << rig.modelName; + radio_cap_packet r; + memset(r.packet, 0x0, sizeof(r)); // We can't be sure it is initialized with 0x00! + + memcpy(r.guid, rig.guid, sizeof(r.guid)); + memcpy(r.name, rig.rigName.toLocal8Bit(), sizeof(r.name)); + memcpy(r.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11); + + if (rig.hasWiFi && !rig.hasEthernet) { + r.conntype = 0x0707; // 0x0707 for wifi rig. + } + else { + r.conntype = 0x073f; // 0x073f for ethernet rig. + } + + r.civ = rig.civAddr; + r.baudrate = (quint32)qToBigEndian(rig.baudRate); + /* + 0x80 = 12K only + 0x40 = 44.1K only + 0x20 = 22.05K only + 0x10 = 11.025K only + 0x08 = 48K only + 0x04 = 32K only + 0x02 = 16K only + 0x01 = 8K only + */ + if (rig.rxaudio == Q_NULLPTR) { + r.rxsample = 0x8b01; // all rx sample frequencies supported + } + else { + if (rxSampleRate == 48000) { + r.rxsample = 0x0800; // fixed rx sample frequency + } + else if (rxSampleRate == 32000) { + r.rxsample = 0x0400; + } + else if (rxSampleRate == 24000) { + r.rxsample = 0x0001; + } + else if (rxSampleRate == 16000) { + r.rxsample = 0x0200; + } + else if (rxSampleRate == 12000) { + r.rxsample = 0x8000; + } + } + + if (rig.txaudio == Q_NULLPTR) { + r.txsample = 0x8b01; // all tx sample frequencies supported + r.enablea = 0x01; // 0x01 enables TX 24K mode? + qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Client will have TX audio"; + } + else { + qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Disable tx audio for client"; + r.txsample = 0; + } + + // I still don't know what these are? + r.enableb = 0x01; // 0x01 doesn't seem to do anything? + r.enablec = 0x01; // 0x01 doesn't seem to do anything? + r.capf = 0x5001; + r.capg = 0x0190; + s.data.append(QByteArray::fromRawData((const char*)r.packet, sizeof(r))); + } + + p.len = sizeof(p)+s.data.length(); + p.payloadsize = sizeof(p)+s.data.length() - 0x0f; + + s.data.insert(0,QByteArray::fromRawData((const char*)p.packet, sizeof(p))); + if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { if (c->txSeqBuf.size() > BUFSIZE) @@ -1155,68 +1215,79 @@ void udpServer::sendCapabilities(CLIENT* c) // When client has requested civ/audio connection, this will contain their details // Also used to display currently connected used information. -void udpServer::sendConnectionInfo(CLIENT* c) +void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[16]) { - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending ConnectionInfo :" << c->txSeq; - conninfo_packet p; - memset(p.packet, 0x0, sizeof(p)); - p.len = sizeof(p); - p.type = 0x00; - p.seq = c->txSeq; - p.sentid = c->myId; - p.rcvdid = c->remoteId; - //p.innerseq = c->authInnerSeq; // Innerseq not used in user packet - p.tokrequest = c->tokenRx; - p.token = c->tokenTx; - p.code = 0x0380; - p.commoncap = c->commonCap; - memcpy(p.macaddress, c->macaddress, 6); - - // 0x1a-0x1f is authid (random number? - // memcpy(p + 0x40, QByteArrayLiteral("IC-7851").constData(), 7); - - memcpy(p.packet + 0x40, rigCaps.modelName.toLocal8Bit(), rigCaps.modelName.length()); - - // This is the current streaming client (should we support multiple clients?) - if (c->isStreaming) { - p.busy = 0x01; - memcpy(p.computer, c->clientName.constData(), c->clientName.length()); - p.ipaddress = qToBigEndian(c->ipAddress.toIPv4Address()); - memcpy(p.macaddress, c->macaddress, 6); - } - - - SEQBUFENTRY s; - s.seqNum = p.seq; - s.timeSent = QTime::currentTime(); - s.retransmitCount = 0; - s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - - if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - if (c->txSeqBuf.size() > BUFSIZE) + for (RIGCONFIG& radio : config.rigs) { + if (!memcmp(guid, radio.guid, sizeof(guid))) { - c->txSeqBuf.remove(c->txSeqBuf.firstKey()); + qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending ConnectionInfo :" << c->txSeq; + conninfo_packet p; + memset(p.packet, 0x0, sizeof(p)); + p.len = sizeof(p); + p.type = 0x00; + p.seq = c->txSeq; + p.sentid = c->myId; + p.rcvdid = c->remoteId; + //p.innerseq = c->authInnerSeq; // Innerseq not used in user packet + p.tokrequest = c->tokenRx; + p.token = c->tokenTx; + p.code = 0x0380; + memcpy(p.guid, radio.guid, sizeof(p.guid)); + memcpy(p.name, radio.rigName.toLocal8Bit(), sizeof(p.name)); + + if (radio.rigAvailable) { + if (c->isStreaming) { + p.busy = 0x01; + } + else { + p.busy = 0x00; + } + } + else + { + p.busy = 0x02; + } + + // This is the current streaming client (should we support multiple clients?) + if (c->isStreaming) { + memcpy(p.computer, c->clientName.constData(), c->clientName.length()); + p.ipaddress = qToBigEndian(c->ipAddress.toIPv4Address()); + + } + + + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + + if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + if (c->txSeqBuf.size() > BUFSIZE) + { + c->txSeqBuf.remove(c->txSeqBuf.firstKey()); + } + c->txSeqBuf.insert(p.seq, s); + c->txSeq++; + c->txMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock txMutex()"; + } + + + if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); + udpMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock udpMutex()"; + } + } - c->txSeqBuf.insert(p.seq, s); - c->txSeq++; - c->txMutex.unlock(); } - else { - qInfo(logUdpServer()) << "Unable to lock txMutex()"; - } - - - if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); - udpMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock udpMutex()"; - } - - if (c->idleTimer != Q_NULLPTR) c->idleTimer->start(100); @@ -1395,84 +1466,108 @@ void udpServer::sendStatus(CLIENT* c) void udpServer::dataForServer(QByteArray d) { - //qInfo(logUdpServer()) << "Server got:" << d; - foreach(CLIENT * client, civClients) + rigCommander* sender = qobject_cast(QObject::sender()); + + if (sender == Q_NULLPTR) { - int lastFE = d.lastIndexOf((quint8)0xfe); - if (client != Q_NULLPTR && client->connected && d.length() > lastFE + 2 && - ((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId || - (quint8)d[lastFE + 1] == 0x00 || (quint8)d[lastFE + 2]==0x00 || (quint8)d[lastFE + 1] == 0xE1 || (quint8)d[lastFE + 2] == 0xE1)) { - data_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = (quint16)d.length() + sizeof(p); - p.seq = client->txSeq; - p.sentid = client->myId; - p.rcvdid = client->remoteId; - p.reply = (char)0xc1; - p.datalen = (quint16)d.length(); - p.sendseq = client->innerSeq; - QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - t.append(d); - - SEQBUFENTRY s; - s.seqNum = p.seq; - s.timeSent = QTime::currentTime(); - s.retransmitCount = 0; - s.data = t; - - if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - if (client->txSeqBuf.size() > BUFSIZE) - { - client->txSeqBuf.remove(client->txSeqBuf.firstKey()); - } - client->txSeqBuf.insert(p.seq, s); - client->txSeq++; - client->innerSeq++; - client->txMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock txMutex()"; - } - - - if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - client->socket->writeDatagram(t, client->ipAddress, client->port); - udpMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock udpMutex()"; - } - - } else { - qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE+1] << ":" << hex << (quint8)d[lastFE+2]; - } + return; } + for (CLIENT* client : civClients) + { + if (client == Q_NULLPTR || !client->connected) + { + continue; + } + for (RIGCONFIG& radio : config.rigs) + { + + if (memcmp(radio.guid, client->guid, sizeof(radio.guid))) + { + continue; + } + + int lastFE = d.lastIndexOf((quint8)0xfe); + // Use the GUID to determine which radio the response is from + qInfo(logUdpServer()) << "Server got CIV data from" << radio.rigName << "length" << d.length(); + if (client->connected && d.length() > lastFE + 2 && + ((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId || + (quint8)d[lastFE + 1] == 0x00 || (quint8)d[lastFE + 2] == 0x00 || (quint8)d[lastFE + 1] == 0xE1 || (quint8)d[lastFE + 2] == 0xE1)) + { + data_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = (quint16)d.length() + sizeof(p); + p.seq = client->txSeq; + p.sentid = client->myId; + p.rcvdid = client->remoteId; + p.reply = (char)0xc1; + p.datalen = (quint16)d.length(); + p.sendseq = client->innerSeq; + QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + t.append(d); + + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = t; + + if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + if (client->txSeqBuf.size() > BUFSIZE) + { + client->txSeqBuf.remove(client->txSeqBuf.firstKey()); + } + client->txSeqBuf.insert(p.seq, s); + client->txSeq++; + client->innerSeq++; + client->txMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock txMutex()"; + } + + + if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + client->socket->writeDatagram(t, client->ipAddress, client->port); + udpMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock udpMutex()"; + } + } + else { + qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE + 1] << ":" << hex << (quint8)d[lastFE + 2]; + } + } + } return; } void udpServer::sendRxAudio() { QByteArray audio; - if (rxaudio) { - if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - audio.clear(); - rxaudio->getNextAudioChunk(audio); - int len = 0; - while (len < audio.length()) { - audioPacket partial; - partial.data = audio.mid(len, 1364); - receiveAudioData(partial); - len = len + partial.data.length(); + for (RIGCONFIG &rig : config.rigs) { + + if (rig.rxaudio != Q_NULLPTR) { + if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + audio.clear(); + rig.rxaudio->getNextAudioChunk(audio); + int len = 0; + while (len < audio.length()) { + audioPacket partial; + partial.data = audio.mid(len, 1364); + receiveAudioData(partial); + len = len + partial.data.length(); + } + // Now we have the next audio chunk, we can release the mutex. + audioMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio"; } - // Now we have the next audio chunk, we can release the mutex. - audioMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio"; } } } @@ -1721,6 +1816,10 @@ void udpServer::sendRetransmitRequest(CLIENT* c) void udpServer::deleteConnection(QList* l, CLIENT* c) { + quint8 guid[16]; + memcpy(guid, c->guid, sizeof(guid)); + int len = l->length(); + qInfo(logUdpServer()) << "Deleting" << c->type << "connection to: " << c->ipAddress.toString() << ":" << QString::number(c->port); if (c->idleTimer != Q_NULLPTR) { c->idleTimer->stop(); @@ -1774,6 +1873,7 @@ void udpServer::deleteConnection(QList* l, CLIENT* c) if (client != Q_NULLPTR && client == c) { qInfo(logUdpServer()) << "Found" << client->type << "connection to: " << client->ipAddress.toString() << ":" << QString::number(client->port); it = l->erase(it); + len--; } else { ++it; @@ -1788,27 +1888,30 @@ void udpServer::deleteConnection(QList* l, CLIENT* c) qInfo(logUdpServer()) << "Unable to lock connMutex()"; } - if (l->length() == 0) { + if (len == 0) { + for (RIGCONFIG& radio : config.rigs) { + if (!memcmp(radio.guid, guid, sizeof(radio.guid))) + { + if (radio.rxAudioTimer != Q_NULLPTR) { + radio.rxAudioTimer->stop(); + delete radio.rxAudioTimer; + radio.rxAudioTimer = Q_NULLPTR; + } - if (rxAudioTimer != Q_NULLPTR) { - rxAudioTimer->stop(); - delete rxAudioTimer; - rxAudioTimer = Q_NULLPTR; + if (radio.rxAudioThread != Q_NULLPTR) { + radio.rxAudioThread->quit(); + radio.rxAudioThread->wait(); + radio.rxaudio = Q_NULLPTR; + radio.rxAudioThread = Q_NULLPTR; + } + + if (radio.txAudioThread != Q_NULLPTR) { + radio.txAudioThread->quit(); + radio.txAudioThread->wait(); + radio.txaudio = Q_NULLPTR; + radio.txAudioThread = Q_NULLPTR; + } + } } - - if (rxAudioThread != Q_NULLPTR) { - rxAudioThread->quit(); - rxAudioThread->wait(); - rxaudio = Q_NULLPTR; - rxAudioThread = Q_NULLPTR; - } - - if (txAudioThread != Q_NULLPTR) { - txAudioThread->quit(); - txAudioThread->wait(); - txaudio = Q_NULLPTR; - txAudioThread = Q_NULLPTR; - } - } } diff --git a/udpserver.h b/udpserver.h index 4d5730b..2269732 100644 --- a/udpserver.h +++ b/udpserver.h @@ -23,6 +23,7 @@ #include "rigidentities.h" #include "udphandler.h" #include "audiohandler.h" +#include "rigcommander.h" extern void passcode(QString in,QByteArray& out); extern QByteArray parseNullTerminatedString(QByteArray c, int s); @@ -41,6 +42,32 @@ struct SERVERUSER { quint8 userType; }; +struct RIGCONFIG { + QString serialPort; + quint32 baudRate; + unsigned char civAddr; + bool civIsRadioModel; + bool forceRTSasPTT; + bool hasWiFi = false; + bool hasEthernet=false; + audioSetup rxAudioSetup; + audioSetup txAudioSetup; + QString modelName; + QString rigName; + quint8 guid[16]; + bool rigAvailable=false; + rigCapabilities rigCaps; + rigCommander* rig = Q_NULLPTR; + QThread* rigThread = Q_NULLPTR; + audioHandler* rxaudio = Q_NULLPTR; + QThread* rxAudioThread = Q_NULLPTR; + audioHandler* txaudio = Q_NULLPTR; + QThread* txAudioThread = Q_NULLPTR; + QTimer* rxAudioTimer = Q_NULLPTR; + +}; + + struct SERVERCONFIG { bool enabled; bool lan; @@ -51,8 +78,8 @@ struct SERVERCONFIG { int audioInput; quint8 resampleQuality; quint32 baudRate; - QList users; + QList rigs; }; @@ -61,7 +88,7 @@ class udpServer : public QObject Q_OBJECT public: - udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudio); + udpServer(SERVERCONFIG* config, audioSetup outAudio, audioSetup inAudio); ~udpServer(); public slots: @@ -140,6 +167,7 @@ private: CLIENT* controlClient = Q_NULLPTR; CLIENT* civClient = Q_NULLPTR; CLIENT* audioClient = Q_NULLPTR; + quint8 guid[16]; }; void controlReceived(); @@ -151,7 +179,7 @@ private: void sendControl(CLIENT* c, quint8 type, quint16 seq); void sendLoginResponse(CLIENT* c, bool allowed); void sendCapabilities(CLIENT* c); - void sendConnectionInfo(CLIENT* c); + void sendConnectionInfo(CLIENT* c,quint8 guid[16]); void sendTokenResponse(CLIENT* c,quint8 type); void sendStatus(CLIENT* c); void sendRetransmitRequest(CLIENT* c); @@ -171,8 +199,6 @@ private: quint32 civId = 0; quint32 audioId = 0; - quint8 rigciv = 0xa2; - QMutex udpMutex; // Used for critical operations. QMutex connMutex; QMutex audioMutex; @@ -180,19 +206,13 @@ private: QList controlClients = QList(); QList civClients = QList(); QList audioClients = QList(); + QTime timeStarted; - rigCapabilities rigCaps; - audioHandler* rxaudio = Q_NULLPTR; - QThread* rxAudioThread = Q_NULLPTR; - - audioHandler* txaudio = Q_NULLPTR; - QThread* txAudioThread = Q_NULLPTR; audioSetup outAudio; audioSetup inAudio; - QTimer* rxAudioTimer=Q_NULLPTR; quint16 rxSampleRate = 0; quint16 txSampleRate = 0; quint8 rxCodec = 0; From 2f4fe061b3c38896cdf5b4f34ea8a9e24e37ea1d Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 19:11:16 +0000 Subject: [PATCH 093/323] Improve missing packet handling --- packettypes.h | 2 +- servermain.cpp | 222 +++++++++++---------- udphandler.cpp | 42 +++- udpserver.cpp | 528 ++++++++++++++++++++++--------------------------- udpserver.h | 4 +- 5 files changed, 389 insertions(+), 409 deletions(-) diff --git a/packettypes.h b/packettypes.h index c8e4c60..f2f46af 100644 --- a/packettypes.h +++ b/packettypes.h @@ -14,7 +14,7 @@ #define RETRANSMIT_PERIOD 100 // How often to attempt retransmit #define LOCK_PERIOD 10 // How long to try to lock mutex (ms) #define STALE_CONNECTION 15 // Not heard from in this many seconds -#define BUFSIZE 50 // Number of packets to buffer +#define BUFSIZE 500 // Number of packets to buffer #define TXAUDIO_PERIOD 20 diff --git a/servermain.cpp b/servermain.cpp index 2ab49e8..fd34fc5 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -48,14 +48,16 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q servermain::~servermain() { - for (RIGCONFIG& radio : serverConfig.rigs) + for (RIGCONFIG* radio : serverConfig.rigs) { - if (radio.rigThread != Q_NULLPTR) + if (radio->rigThread != Q_NULLPTR) { - radio.rigThread->quit(); - radio.rigThread->wait(); + radio->rigThread->quit(); + radio->rigThread->wait(); } + delete radio; // This has been created by new in loadSettings(); } + serverConfig.rigs.clear(); if (serverThread != Q_NULLPTR) { serverThread->quit(); serverThread->wait(); @@ -79,26 +81,14 @@ void servermain::openRig() makeRig(); - if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) - { - findSerialPort(); - - } else { - if(serialPortCL.isEmpty()) - { - serialPortRig = prefs.serialPortRadio; - } else { - serialPortRig = serialPortCL; - } - } - for (RIGCONFIG& radio : serverConfig.rigs) + for (RIGCONFIG* radio : serverConfig.rigs) { //qInfo(logSystem()) << "Opening rig"; - if (radio.rigThread != Q_NULLPTR) + if (radio->rigThread != Q_NULLPTR) { //qInfo(logSystem()) << "Got rig"; - QMetaObject::invokeMethod(radio.rig, [=]() { - radio.rig->commSetup(radio.civAddr, radio.serialPort, radio.baudRate, QString("none")); + QMetaObject::invokeMethod(radio->rig, [=]() { + radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none")); }, Qt::QueuedConnection); } } @@ -106,49 +96,49 @@ void servermain::openRig() void servermain::makeRig() { - for (RIGCONFIG& radio : serverConfig.rigs) + for (RIGCONFIG* radio : serverConfig.rigs) { - if (radio.rigThread == Q_NULLPTR) + if (radio->rigThread == Q_NULLPTR) { qInfo(logSystem()) << "Creating new rigThread()"; - radio.rig = new rigCommander(radio.guid); - radio.rigThread = new QThread(this); + radio->rig = new rigCommander(radio->guid); + radio->rigThread = new QThread(this); // Thread: - radio.rig->moveToThread(radio.rigThread); - connect(radio.rigThread, SIGNAL(started()), radio.rig, SLOT(process())); - connect(radio.rigThread, SIGNAL(finished()), radio.rig, SLOT(deleteLater())); - radio.rigThread->start(); + radio->rig->moveToThread(radio->rigThread); + connect(radio->rigThread, SIGNAL(started()), radio->rig, SLOT(process())); + connect(radio->rigThread, SIGNAL(finished()), radio->rig, SLOT(deleteLater())); + radio->rigThread->start(); // Rig status and Errors: - connect(radio.rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); - connect(radio.rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); + connect(radio->rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); + connect(radio->rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); // Rig comm setup: - //connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), radio.rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); - //connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32, QString)), radio.rig, SLOT(commSetup(unsigned char, QString, quint32, QString))); - connect(this, SIGNAL(setRTSforPTT(bool)), radio.rig, SLOT(setRTSforPTT(bool))); + //connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), radio->rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); + //connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32, QString)), radio->rig, SLOT(commSetup(unsigned char, QString, quint32, QString))); + connect(this, SIGNAL(setRTSforPTT(bool)), radio->rig, SLOT(setRTSforPTT(bool))); - connect(radio.rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); + connect(radio->rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); - connect(this, SIGNAL(sendCloseComm()), radio.rig, SLOT(closeComm())); - connect(this, SIGNAL(sendChangeLatency(quint16)), radio.rig, SLOT(changeLatency(quint16))); - //connect(this, SIGNAL(getRigCIV()), radio.rig, SLOT(findRigs())); - //connect(this, SIGNAL(setRigID(unsigned char)), radio.rig, SLOT(setRigID(unsigned char))); - connect(radio.rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities))); - connect(radio.rig, SIGNAL(commReady()), this, SLOT(receiveCommReady())); + connect(this, SIGNAL(sendCloseComm()), radio->rig, SLOT(closeComm())); + connect(this, SIGNAL(sendChangeLatency(quint16)), radio->rig, SLOT(changeLatency(quint16))); + //connect(this, SIGNAL(getRigCIV()), radio->rig, SLOT(findRigs())); + //connect(this, SIGNAL(setRigID(unsigned char)), radio->rig, SLOT(setRigID(unsigned char))); + connect(radio->rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities))); + connect(radio->rig, SIGNAL(commReady()), this, SLOT(receiveCommReady())); - connect(this, SIGNAL(requestRigState()), radio.rig, SLOT(sendState())); - connect(this, SIGNAL(stateUpdated()), radio.rig, SLOT(stateUpdated())); - connect(radio.rig, SIGNAL(stateInfo(rigstate*)), this, SLOT(receiveStateInfo(rigstate*))); + connect(this, SIGNAL(requestRigState()), radio->rig, SLOT(sendState())); + connect(this, SIGNAL(stateUpdated()), radio->rig, SLOT(stateUpdated())); + connect(radio->rig, SIGNAL(stateInfo(rigstate*)), this, SLOT(receiveStateInfo(rigstate*))); //Other connections - connect(this, SIGNAL(setCIVAddr(unsigned char)), radio.rig, SLOT(setCIVAddr(unsigned char))); + connect(this, SIGNAL(setCIVAddr(unsigned char)), radio->rig, SLOT(setCIVAddr(unsigned char))); - connect(radio.rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool))); - connect(this, SIGNAL(setPTT(bool)), radio.rig, SLOT(setPTT(bool))); - connect(this, SIGNAL(getPTT()), radio.rig, SLOT(getPTT())); - connect(this, SIGNAL(getDebug()), radio.rig, SLOT(getDebug())); - if (radio.rigThread->isRunning()) { + connect(radio->rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool))); + connect(this, SIGNAL(setPTT(bool)), radio->rig, SLOT(setPTT(bool))); + connect(this, SIGNAL(getPTT()), radio->rig, SLOT(getPTT())); + connect(this, SIGNAL(getDebug()), radio->rig, SLOT(getDebug())); + if (radio->rigThread->isRunning()) { qInfo(logSystem()) << "Rig thread is running"; } else { @@ -163,15 +153,15 @@ void servermain::makeRig() void servermain::removeRig() { - for (RIGCONFIG& radio : serverConfig.rigs) + for (RIGCONFIG* radio : serverConfig.rigs) { - if (radio.rigThread != Q_NULLPTR) + if (radio->rigThread != Q_NULLPTR) { - radio.rigThread->disconnect(); - radio.rig->disconnect(); - delete radio.rigThread; - delete radio.rig; - radio.rig = Q_NULLPTR; + radio->rigThread->disconnect(); + radio->rig->disconnect(); + delete radio->rigThread; + delete radio->rig; + radio->rig = Q_NULLPTR; } } } @@ -242,28 +232,28 @@ void servermain::receiveCommReady() // Use the GUID to determine which radio the response is from - for (RIGCONFIG& radio : serverConfig.rigs) + for (RIGCONFIG* radio : serverConfig.rigs) { - if (sender != Q_NULLPTR && radio.rig != Q_NULLPTR && !memcmp(sender->getGUID(), radio.guid, sizeof(radio.guid))) + if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !memcmp(sender->getGUID(), radio->guid, sizeof(radio->guid))) { qInfo(logSystem()) << "Received CommReady!! "; - if (radio.civAddr == 0) + if (radio->civAddr == 0) { // tell rigCommander to broadcast a request for all rig IDs. // qInfo(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)"; - if (!radio.rigAvailable) { - QMetaObject::invokeMethod(radio.rig, [=]() { - radio.rig->findRigs(); + if (!radio->rigAvailable) { + QMetaObject::invokeMethod(radio->rig, [=]() { + radio->rig->findRigs(); }, Qt::QueuedConnection); } } else { // don't bother, they told us the CIV they want, stick with it. // We still query the rigID to find the model, but at least we know the CIV. - qInfo(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << radio.civAddr; - QMetaObject::invokeMethod(radio.rig, [=]() { - radio.rig->setRigID(radio.civAddr); + qInfo(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << radio->civAddr; + QMetaObject::invokeMethod(radio->rig, [=]() { + radio->rig->setRigID(radio->civAddr); }, Qt::QueuedConnection); } } @@ -279,10 +269,10 @@ void servermain::receiveFoundRigID(rigCapabilities rigCaps) rigCommander* sender = qobject_cast(QObject::sender()); // Use the GUID to determine which radio the response is from - for (RIGCONFIG& radio : serverConfig.rigs) + for (RIGCONFIG* radio : serverConfig.rigs) { - if (sender != Q_NULLPTR && radio.rig != Q_NULLPTR && !radio.rigAvailable && !memcmp(sender->getGUID(), radio.guid, sizeof(radio.guid))) + if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !radio->rigAvailable && !memcmp(sender->getGUID(), radio->guid, sizeof(radio->guid))) { qDebug(logSystem()) << "Rig name: " << rigCaps.modelName; @@ -309,7 +299,7 @@ void servermain::receiveFoundRigID(rigCapabilities rigCaps) .arg(rigCaps.guid[14], 2, 16, QLatin1Char('0')) .arg(rigCaps.guid[15], 2, 16, QLatin1Char('0')) ; - radio.rigCaps = rigCaps; + radio->rigCaps = rigCaps; // Added so that server receives rig capabilities. emit sendRigCaps(rigCaps); } @@ -372,7 +362,7 @@ void servermain::setServerToPrefs() udp = Q_NULLPTR; } - udp = new udpServer(&serverConfig, serverTxSetup, serverRxSetup); + udp = new udpServer(serverConfig, serverTxSetup, serverRxSetup); serverThread = new QThread(this); @@ -385,15 +375,15 @@ void servermain::setServerToPrefs() // Step through all radios and connect them to the server, // server will then use GUID to determine which actual radio it belongs to. - for (RIGCONFIG& radio : serverConfig.rigs) + for (RIGCONFIG* radio : serverConfig.rigs) { - if (radio.rigThread != Q_NULLPTR) + if (radio->rigThread != Q_NULLPTR) { - if (radio.rig != Q_NULLPTR) { - connect(radio.rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); - connect(radio.rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); - //connect(udp, SIGNAL(haveDataFromServer(QByteArray)), radio.rig, SLOT(dataFromServer(QByteArray))); + if (radio->rig != Q_NULLPTR) { + connect(radio->rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); + connect(radio->rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); + //connect(udp, SIGNAL(haveDataFromServer(QByteArray)), radio->rig, SLOT(dataFromServer(QByteArray))); connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); } } @@ -508,21 +498,21 @@ void servermain::loadSettings() else { settings->setArrayIndex(i); } - RIGCONFIG tempPrefs; - tempPrefs.civAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); - tempPrefs.forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool(); - tempPrefs.serialPort = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString(); - tempPrefs.rigName = settings->value("RigName", "").toString(); - tempPrefs.baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); + RIGCONFIG* tempPrefs = new RIGCONFIG(); + tempPrefs->civAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); + tempPrefs->forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool(); + tempPrefs->serialPort = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString(); + tempPrefs->rigName = settings->value("RigName", "").toString(); + tempPrefs->baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); - if (tempPrefs.rigName=="") + if (tempPrefs->rigName=="") { foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) { //qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); - if (serialPortInfo.portName() == tempPrefs.serialPort && !serialPortInfo.serialNumber().isEmpty()) + if (serialPortInfo.portName() == tempPrefs->serialPort && !serialPortInfo.serialNumber().isEmpty()) { - tempPrefs.rigName = serialPortInfo.serialNumber(); + tempPrefs->rigName = serialPortInfo.serialNumber(); } } } @@ -531,30 +521,33 @@ void servermain::loadSettings() guid = QUuid::createUuid().toString(); settings->setValue("GUID", guid); } - memcpy(tempPrefs.guid, QUuid::fromString(guid).toRfc4122().constData(), sizeof(tempPrefs.guid)); + memcpy(tempPrefs->guid, QUuid::fromString(guid).toRfc4122().constData(), sizeof(tempPrefs->guid)); - tempPrefs.rxAudioSetup.isinput = true; - tempPrefs.txAudioSetup.isinput = false; - tempPrefs.rxAudioSetup.localAFgain = 255; - tempPrefs.txAudioSetup.localAFgain = 255; - tempPrefs.rxAudioSetup.resampleQuality = 4; - tempPrefs.txAudioSetup.resampleQuality = 4; - - tempPrefs.rxAudioSetup.name = settings->value("AudioInput", "").toString(); - tempPrefs.txAudioSetup.name = settings->value("AudioOutput", "").toString(); + tempPrefs->rxAudioSetup.isinput = true; + tempPrefs->txAudioSetup.isinput = false; + tempPrefs->rxAudioSetup.localAFgain = 255; + tempPrefs->txAudioSetup.localAFgain = 255; + tempPrefs->rxAudioSetup.resampleQuality = 4; + tempPrefs->txAudioSetup.resampleQuality = 4; + tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "").toString(); + tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "").toString(); + bool rxDeviceFound = false; + bool txDeviceFound = false; // Find the actual audio devices #if defined(RTAUDIO) for (unsigned int i = 1; i < devices; i++) { info = audio->getDeviceInfo(i); if (info.outputChannels > 0) { - if (tempPrefs.txAudio.name == info->name) { - tempPrefs.txAudio.port = i; + if (tempPrefs->txAudio.name == info->name) { + tempPrefs->txAudio.port = i; + txDeviceFound = true; } } if (info.inputChannels > 0) { - if (tempPrefs.rxAudio.name == info->name) { - tempPrefs.rxAudio.port = i; + if (tempPrefs->rxAudio.name == info->name) { + tempPrefs->rxAudio.port = i; + rxDeviceFound = true; } } } @@ -563,14 +556,16 @@ void servermain::loadSettings() { info = Pa_GetDeviceInfo(i); if (info->maxInputChannels > 0) { - if (tempPrefs.txAudio.name == info->name) { - tempPrefs.txAudio.port = i; + if (tempPrefs->txAudio.name == info->name) { + tempPrefs->txAudio.port = i; + txDeviceFound = true; } } if (info->maxOutputChannels > 0) { - if (tempPrefs.rxAudio.name == info->name) { - tempPrefs.rxAudio.port = i; - } + if (tempPrefs->rxAudio.name == info->name) { + tempPrefs->rxAudio.port = i; + rxDeviceFound = true; + } } } #else @@ -581,20 +576,29 @@ void servermain::loadSettings() //qInfo(logAudio()) << "Looking for audio output devices"; for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { - if (deviceInfo.deviceName() == tempPrefs.txAudioSetup.name) { - tempPrefs.txAudioSetup.port = deviceInfo; + if (deviceInfo.deviceName() == tempPrefs->txAudioSetup.name) { + tempPrefs->txAudioSetup.port = deviceInfo; + txDeviceFound = true; } } //qInfo(logAudio()) << "Looking for audio input devices"; for (const QAudioDeviceInfo& deviceInfo : audioInputs) { - if (deviceInfo.deviceName() == tempPrefs.rxAudioSetup.name) { - tempPrefs.rxAudioSetup.port = deviceInfo; + if (deviceInfo.deviceName() == tempPrefs->rxAudioSetup.name) { + tempPrefs->rxAudioSetup.port = deviceInfo; + rxDeviceFound = true; } } #endif - tempPrefs.rig = Q_NULLPTR; - tempPrefs.rigThread = Q_NULLPTR; + + if (!txDeviceFound) { + qInfo() << "Cannot find txAudioDevice" << tempPrefs->txAudioSetup.name; + } + if (!rxDeviceFound) { + qInfo() << "Cannot find rxAudioDevice" << tempPrefs->rxAudioSetup.name; + } + tempPrefs->rig = Q_NULLPTR; + tempPrefs->rigThread = Q_NULLPTR; serverConfig.rigs.append(tempPrefs); if (tempNum == 0) { settings->endGroup(); diff --git a/udphandler.cpp b/udphandler.cpp index 870e568..88e449b 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1246,7 +1246,6 @@ void udpBase::dataReceived(QByteArray r) } else { - //std::sort(rxSeqBuf.begin(), rxSeqBuf.end()); if (in->seq < rxSeqBuf.firstKey()) { qInfo(logUdp()) << this->metaObject()->className() << ": ******* seq number has rolled over ****** previous highest: " << hex << rxSeqBuf.lastKey() << " current: " << hex << in->seq; @@ -1261,10 +1260,43 @@ void udpBase::dataReceived(QByteArray r) if (!rxSeqBuf.contains(in->seq)) { // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. - rxSeqBuf.insert(in->seq, QTime::currentTime()); - if (rxSeqBuf.size() > BUFSIZE) - { - rxSeqBuf.erase(rxSeqBuf.begin()); + + + if (in->seq > rxSeqBuf.lastKey() + 1) { + // We are likely missing packets then! + missingMutex.lock(); + int missCounter = 0; + for (quint16 f = rxSeqBuf.lastKey() + 1; f < in->seq; f++) + { + if (missCounter > 50) { + // More than 50 packets missing, something horrific has happened! + qDebug(logUdp()) << "Too many missing packets, full reset!"; + rxSeqBuf.clear(); + rxMissing.clear(); + missingMutex.unlock(); + break; + } + qDebug(logUdp()) << "Detected missing packet" << f; + + if (rxSeqBuf.size() > BUFSIZE) + { + rxSeqBuf.erase(rxSeqBuf.begin()); + } + rxSeqBuf.insert(f, QTime::currentTime()); + if (!rxMissing.contains(f)) + { + rxMissing.insert(f, 0); + } + } + missingMutex.unlock(); + } + else { + if (rxSeqBuf.size() > BUFSIZE) + { + rxSeqBuf.erase(rxSeqBuf.begin()); + } + rxSeqBuf.insert(in->seq, QTime::currentTime()); + } } else { diff --git a/udpserver.cpp b/udpserver.cpp index 11ddee5..9d111f5 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -4,8 +4,8 @@ #define STALE_CONNECTION 15 #define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms #define AUDIO_SEND_PERIOD 20 // -udpServer::udpServer(SERVERCONFIG* config, audioSetup outAudio, audioSetup inAudio) : - config(*config), +udpServer::udpServer(SERVERCONFIG& config, audioSetup outAudio, audioSetup inAudio) : + config(config), outAudio(outAudio), inAudio(inAudio) { @@ -15,29 +15,29 @@ udpServer::udpServer(SERVERCONFIG* config, audioSetup outAudio, audioSetup inAud void udpServer::init() { - for (RIGCONFIG rig : config.rigs) + for (RIGCONFIG* rig : config.rigs) { - qDebug(logUdpServer()) << "CIV:" << rig.civAddr; - qDebug(logUdpServer()) << "Model:" << rig.modelName; - qDebug(logUdpServer()) << "Name:" << rig.rigName; - qDebug(logUdpServer()) << "CIV:" << rig.civAddr; + qDebug(logUdpServer()) << "CIV:" << rig->civAddr; + qDebug(logUdpServer()) << "Model:" << rig->modelName; + qDebug(logUdpServer()) << "Name:" << rig->rigName; + qDebug(logUdpServer()) << "CIV:" << rig->civAddr; qDebug(logUdpServer()).noquote() << QString("GUID: {%1%2%3%4-%5%6-%7%8-%9%10-%11%12%13%14%15%16}") - .arg(rig.guid[0], 2, 16, QLatin1Char('0')) - .arg(rig.guid[1], 2, 16, QLatin1Char('0')) - .arg(rig.guid[2], 2, 16, QLatin1Char('0')) - .arg(rig.guid[3], 2, 16, QLatin1Char('0')) - .arg(rig.guid[4], 2, 16, QLatin1Char('0')) - .arg(rig.guid[5], 2, 16, QLatin1Char('0')) - .arg(rig.guid[6], 2, 16, QLatin1Char('0')) - .arg(rig.guid[7], 2, 16, QLatin1Char('0')) - .arg(rig.guid[8], 2, 16, QLatin1Char('0')) - .arg(rig.guid[9], 2, 16, QLatin1Char('0')) - .arg(rig.guid[10], 2, 16, QLatin1Char('0')) - .arg(rig.guid[11], 2, 16, QLatin1Char('0')) - .arg(rig.guid[12], 2, 16, QLatin1Char('0')) - .arg(rig.guid[13], 2, 16, QLatin1Char('0')) - .arg(rig.guid[14], 2, 16, QLatin1Char('0')) - .arg(rig.guid[15], 2, 16, QLatin1Char('0')) + .arg(rig->guid[0], 2, 16, QLatin1Char('0')) + .arg(rig->guid[1], 2, 16, QLatin1Char('0')) + .arg(rig->guid[2], 2, 16, QLatin1Char('0')) + .arg(rig->guid[3], 2, 16, QLatin1Char('0')) + .arg(rig->guid[4], 2, 16, QLatin1Char('0')) + .arg(rig->guid[5], 2, 16, QLatin1Char('0')) + .arg(rig->guid[6], 2, 16, QLatin1Char('0')) + .arg(rig->guid[7], 2, 16, QLatin1Char('0')) + .arg(rig->guid[8], 2, 16, QLatin1Char('0')) + .arg(rig->guid[9], 2, 16, QLatin1Char('0')) + .arg(rig->guid[10], 2, 16, QLatin1Char('0')) + .arg(rig->guid[11], 2, 16, QLatin1Char('0')) + .arg(rig->guid[12], 2, 16, QLatin1Char('0')) + .arg(rig->guid[13], 2, 16, QLatin1Char('0')) + .arg(rig->guid[14], 2, 16, QLatin1Char('0')) + .arg(rig->guid[15], 2, 16, QLatin1Char('0')) ; } srand(time(NULL)); // Generate random key @@ -85,13 +85,6 @@ void udpServer::init() udpAudio->bind(config.audioPort); QUdpSocket::connect(udpAudio, &QUdpSocket::readyRead, this, &udpServer::audioReceived); -#if !defined(PORTAUDIO) && !defined(RTAUDIO) - qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.port.deviceName(); - qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.port.deviceName(); -#else - qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.name; - qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.name; -#endif wdTimer = new QTimer(); connect(wdTimer, &QTimer::timeout, this, &udpServer::watchdog); wdTimer->start(500); @@ -137,14 +130,14 @@ udpServer::~udpServer() void udpServer::receiveRigCaps(rigCapabilities caps) { - for (RIGCONFIG &rig: config.rigs) { - if (!memcmp(rig.guid, caps.guid, sizeof(rig.guid))) { + for (RIGCONFIG* rig: config.rigs) { + if (!memcmp(rig->guid, caps.guid, sizeof(rig->guid))) { // Matching rig, fill-in missing details - rig.rigAvailable = true; - rig.modelName = caps.modelName; - rig.civAddr = caps.civ; - if (rig.rigName=="") { - rig.rigName = caps.modelName; + rig->rigAvailable = true; + rig->modelName = caps.modelName; + rig->civAddr = caps.civ; + if (rig->rigName=="") { + rig->rigName = caps.modelName; } } } @@ -267,8 +260,8 @@ void udpServer::controlReceived() // Request for new token qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received create token request"; sendCapabilities(current); - for (RIGCONFIG& radio : config.rigs) { - sendConnectionInfo(current, radio.guid); + for (RIGCONFIG* radio : config.rigs) { + sendConnectionInfo(current, radio->guid); } } else if (in->res == 0x01) { @@ -280,10 +273,10 @@ void udpServer::controlReceived() // Disconnect audio/civ sendTokenResponse(current, in->res); current->isStreaming = false; - for (RIGCONFIG& radio : config.rigs) { - if (!memcmp(radio.guid, current->guid, sizeof(radio.guid))) + for (RIGCONFIG* radio : config.rigs) { + if (!memcmp(radio->guid, current->guid, sizeof(radio->guid))) { - sendConnectionInfo(current, radio.guid); + sendConnectionInfo(current, radio->guid); } } } @@ -350,68 +343,71 @@ void udpServer::controlReceived() " txSampleRate" << current->txSampleRate << " txBufferLen" << current->txBufferLen; - if (!config.lan) { - // Radio is connected by USB/Serial and we assume that audio is connected as well. - // Create audio TX/RX threads if they don't already exist (first client chooses samplerate/codec) - audioSetup setup; - setup.resampleQuality = config.resampleQuality; - for (RIGCONFIG& radio : config.rigs) { - if (!memcmp(radio.guid, current->guid, sizeof(radio.guid)) && radio.txaudio == Q_NULLPTR) - { - radio.txAudioSetup.codec = current->txCodec; - radio.txAudioSetup.samplerate = current->txSampleRate; - radio.txAudioSetup.isinput = false; - radio.txAudioSetup.latency = current->txBufferLen; - outAudio.isinput = false; + audioSetup setup; + setup.resampleQuality = config.resampleQuality; + for (RIGCONFIG* radio : config.rigs) { + if (!memcmp(radio->guid, current->guid, sizeof(radio->guid)) && radio->txaudio == Q_NULLPTR) + { + radio->txAudioSetup.codec = current->txCodec; + radio->txAudioSetup.samplerate = current->txSampleRate; + radio->txAudioSetup.isinput = false; + radio->txAudioSetup.latency = current->txBufferLen; + outAudio.isinput = false; - radio.txaudio = new audioHandler(); - radio.txAudioThread = new QThread(this); + radio->txaudio = new audioHandler(); + radio->txAudioThread = new QThread(this); - radio.txaudio->moveToThread(radio.txAudioThread); + radio->txaudio->moveToThread(radio->txAudioThread); - radio.txAudioThread->start(QThread::TimeCriticalPriority); + radio->txAudioThread->start(QThread::TimeCriticalPriority); - //connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); - connect(radio.txAudioThread, SIGNAL(finished()), radio.txaudio, SLOT(deleteLater())); + //connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); + connect(radio->txAudioThread, SIGNAL(finished()), radio->txaudio, SLOT(deleteLater())); - QMetaObject::invokeMethod(radio.txaudio, [=]() { - radio.txaudio->init(radio.txAudioSetup); - }, Qt::QueuedConnection); + QMetaObject::invokeMethod(radio->txaudio, [=]() { + radio->txaudio->init(radio->txAudioSetup); + }, Qt::QueuedConnection); - emit setupTxAudio(outAudio); - hasTxAudio = datagram.senderAddress(); + emit setupTxAudio(outAudio); + hasTxAudio = datagram.senderAddress(); - connect(this, SIGNAL(haveAudioData(audioPacket)), radio.txaudio, SLOT(incomingAudio(audioPacket))); + connect(this, SIGNAL(haveAudioData(audioPacket)), radio->txaudio, SLOT(incomingAudio(audioPacket))); - } - if (!memcmp(radio.guid, current->guid, sizeof(radio.guid)) && radio.rxaudio == Q_NULLPTR) - { - radio.rxAudioSetup.codec = current->rxCodec; - radio.rxAudioSetup.samplerate = current->rxSampleRate; - radio.rxAudioSetup.latency = current->txBufferLen; - radio.rxAudioSetup.isinput = true; + } + if (!memcmp(radio->guid, current->guid, sizeof(radio->guid)) && radio->rxaudio == Q_NULLPTR) + { + #if !defined(PORTAUDIO) && !defined(RTAUDIO) + qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio input(RX) :" << radio->rxAudioSetup.port.deviceName(); + qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio output(TX) :" << radio->txAudioSetup.port.deviceName(); + #else + qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.name; + qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.name; + #endif - radio.rxaudio = new audioHandler(); + radio->rxAudioSetup.codec = current->rxCodec; + radio->rxAudioSetup.samplerate = current->rxSampleRate; + radio->rxAudioSetup.latency = current->txBufferLen; + radio->rxAudioSetup.isinput = true; - radio.rxAudioThread = new QThread(this); + radio->rxaudio = new audioHandler(); - radio.rxaudio->moveToThread(radio.rxAudioThread); + radio->rxAudioThread = new QThread(this); - radio.rxAudioThread->start(QThread::TimeCriticalPriority); + radio->rxaudio->moveToThread(radio->rxAudioThread); - //connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); - connect(radio.rxAudioThread, SIGNAL(finished()), radio.rxaudio, SLOT(deleteLater())); + radio->rxAudioThread->start(QThread::TimeCriticalPriority); - QMetaObject::invokeMethod(radio.rxaudio, [=]() { - radio.rxaudio->init(radio.rxAudioSetup); - }, Qt::QueuedConnection); + connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater())); - radio.rxAudioTimer = new QTimer(); - radio.rxAudioTimer->setTimerType(Qt::PreciseTimer); - connect(radio.rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); - radio.rxAudioTimer->start(TXAUDIO_PERIOD); - } + QMetaObject::invokeMethod(radio->rxaudio, [=]() { + radio->rxaudio->init(radio->rxAudioSetup); + }, Qt::QueuedConnection); + + radio->rxAudioTimer = new QTimer(); + radio->rxAudioTimer->setTimerType(Qt::PreciseTimer); + connect(radio->rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); + radio->rxAudioTimer->start(TXAUDIO_PERIOD); } } @@ -568,12 +564,12 @@ void udpServer::civReceived() qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected invalid remote CI-V:" << hex << (quint8)r[lastFE+2]; } - for (RIGCONFIG& radio : config.rigs) { - if (!memcmp(radio.guid, current->guid, sizeof(radio.guid))) + for (RIGCONFIG* radio : config.rigs) { + if (!memcmp(radio->guid, current->guid, sizeof(radio->guid))) { // Only send to the rig that it belongs to! - QMetaObject::invokeMethod(radio.rig, [=]() { - radio.rig->dataFromServer(r.mid(0x15));; + QMetaObject::invokeMethod(radio->rig, [=]() { + radio->rig->dataFromServer(r.mid(0x15));; }, Qt::DirectConnection); } } @@ -886,14 +882,53 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) if (!current->rxSeqBuf.contains(in->seq)) { - // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. if (current->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (current->rxSeqBuf.size() > BUFSIZE) - { - current->rxSeqBuf.remove(current->rxSeqBuf.firstKey()); + // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. + int missCounter = 0; + if (in->seq > current->rxSeqBuf.lastKey() + 1) { + // We are likely missing packets then! + if (current->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + + for (quint16 f = current->rxSeqBuf.lastKey() + 1; f < in->seq; f++) + { + if (missCounter > 50) { + // More than 50 packets missing, something horrific has happened! + qDebug(logUdpServer()) << "Too many missing packets, full reset!"; + current->rxSeqBuf.clear(); + current->rxMissing.clear(); + current->missMutex.unlock(); + break; + } + + qInfo(logUdpServer()) << "Detected missing packet" << f; + + if (current->rxSeqBuf.size() > BUFSIZE) + { + current->rxSeqBuf.remove(current->rxSeqBuf.firstKey()); + } + current->rxSeqBuf.insert(f, QTime::currentTime()); + + if (!current->rxMissing.contains(f)) + { + current->rxMissing.insert(f, 0); + } + } + current->missMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock missMutex()"; + } + } + else { + + if (current->rxSeqBuf.size() > BUFSIZE) + { + current->rxSeqBuf.remove(current->rxSeqBuf.firstKey()); + } + current->rxSeqBuf.insert(in->seq, QTime::currentTime()); } - current->rxSeqBuf.insert(in->seq, QTime::currentTime()); current->rxMutex.unlock(); } else { @@ -944,6 +979,11 @@ void udpServer::sendControl(CLIENT* c, quint8 type, quint16 seq) s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { + if (c->txSeqBuf.size() > BUFSIZE) + { + c->txSeqBuf.remove(c->txSeqBuf.firstKey()); + } + c->txSeqBuf.insert(seq, s); c->txSeq++; c->txMutex.unlock(); @@ -1069,6 +1109,11 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed) s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { + if (c->txSeqBuf.size() > BUFSIZE) + { + c->txSeqBuf.remove(c->txSeqBuf.firstKey()); + } + c->txSeqBuf.insert(c->txSeq, s); c->txSeq++; c->txMutex.unlock(); @@ -1111,24 +1156,24 @@ void udpServer::sendCapabilities(CLIENT* c) s.timeSent = QTime::currentTime(); s.retransmitCount = 0; - for (RIGCONFIG &rig : config.rigs) { - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending Capabilities :" << c->txSeq << "for" << rig.modelName; + for (RIGCONFIG* rig : config.rigs) { + qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending Capabilities :" << c->txSeq << "for" << rig->modelName; radio_cap_packet r; memset(r.packet, 0x0, sizeof(r)); // We can't be sure it is initialized with 0x00! - memcpy(r.guid, rig.guid, sizeof(r.guid)); - memcpy(r.name, rig.rigName.toLocal8Bit(), sizeof(r.name)); + memcpy(r.guid, rig->guid, sizeof(r.guid)); + memcpy(r.name, rig->rigName.toLocal8Bit(), sizeof(r.name)); memcpy(r.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11); - if (rig.hasWiFi && !rig.hasEthernet) { - r.conntype = 0x0707; // 0x0707 for wifi rig. + if (rig->hasWiFi && !rig->hasEthernet) { + r.conntype = 0x0707; // 0x0707 for wifi rig-> } else { - r.conntype = 0x073f; // 0x073f for ethernet rig. + r.conntype = 0x073f; // 0x073f for ethernet rig-> } - r.civ = rig.civAddr; - r.baudrate = (quint32)qToBigEndian(rig.baudRate); + r.civ = rig->civAddr; + r.baudrate = (quint32)qToBigEndian(rig->baudRate); /* 0x80 = 12K only 0x40 = 44.1K only @@ -1139,7 +1184,7 @@ void udpServer::sendCapabilities(CLIENT* c) 0x02 = 16K only 0x01 = 8K only */ - if (rig.rxaudio == Q_NULLPTR) { + if (rig->rxaudio == Q_NULLPTR) { r.rxsample = 0x8b01; // all rx sample frequencies supported } else { @@ -1160,7 +1205,7 @@ void udpServer::sendCapabilities(CLIENT* c) } } - if (rig.txaudio == Q_NULLPTR) { + if (rig->txaudio == Q_NULLPTR) { r.txsample = 0x8b01; // all tx sample frequencies supported r.enablea = 0x01; // 0x01 enables TX 24K mode? qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Client will have TX audio"; @@ -1217,8 +1262,8 @@ void udpServer::sendCapabilities(CLIENT* c) // Also used to display currently connected used information. void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[16]) { - for (RIGCONFIG& radio : config.rigs) { - if (!memcmp(guid, radio.guid, sizeof(guid))) + for (RIGCONFIG* radio : config.rigs) { + if (!memcmp(guid, radio->guid, sizeof(guid))) { qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending ConnectionInfo :" << c->txSeq; conninfo_packet p; @@ -1232,10 +1277,10 @@ void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[16]) p.tokrequest = c->tokenRx; p.token = c->tokenTx; p.code = 0x0380; - memcpy(p.guid, radio.guid, sizeof(p.guid)); - memcpy(p.name, radio.rigName.toLocal8Bit(), sizeof(p.name)); + memcpy(p.guid, radio->guid, sizeof(p.guid)); + memcpy(p.name, radio->rigName.toLocal8Bit(), sizeof(p.name)); - if (radio.rigAvailable) { + if (radio->rigAvailable) { if (c->isStreaming) { p.busy = 0x01; } @@ -1465,7 +1510,6 @@ void udpServer::sendStatus(CLIENT* c) void udpServer::dataForServer(QByteArray d) { - rigCommander* sender = qobject_cast(QObject::sender()); if (sender == Q_NULLPTR) @@ -1479,67 +1523,63 @@ void udpServer::dataForServer(QByteArray d) { continue; } - for (RIGCONFIG& radio : config.rigs) + // Use the GUID to determine which radio the response is from + if (memcmp(sender->getGUID(), client->guid, sizeof(client->guid))) { - - if (memcmp(radio.guid, client->guid, sizeof(radio.guid))) + continue; // Rig guid doesn't match the one requested by the client. + } + + int lastFE = d.lastIndexOf((quint8)0xfe); + //qInfo(logUdpServer()) << "Server got CIV data from" << radio->rigName << "length" << d.length(); + if (client->connected && d.length() > lastFE + 2 && + ((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId || + (quint8)d[lastFE + 1] == 0x00 || (quint8)d[lastFE + 2] == 0x00 || (quint8)d[lastFE + 1] == 0xE1 || (quint8)d[lastFE + 2] == 0xE1)) + { + data_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = (quint16)d.length() + sizeof(p); + p.seq = client->txSeq; + p.sentid = client->myId; + p.rcvdid = client->remoteId; + p.reply = (char)0xc1; + p.datalen = (quint16)d.length(); + p.sendseq = client->innerSeq; + QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + t.append(d); + + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = t; + + if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - continue; - } - - int lastFE = d.lastIndexOf((quint8)0xfe); - // Use the GUID to determine which radio the response is from - qInfo(logUdpServer()) << "Server got CIV data from" << radio.rigName << "length" << d.length(); - if (client->connected && d.length() > lastFE + 2 && - ((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId || - (quint8)d[lastFE + 1] == 0x00 || (quint8)d[lastFE + 2] == 0x00 || (quint8)d[lastFE + 1] == 0xE1 || (quint8)d[lastFE + 2] == 0xE1)) - { - data_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = (quint16)d.length() + sizeof(p); - p.seq = client->txSeq; - p.sentid = client->myId; - p.rcvdid = client->remoteId; - p.reply = (char)0xc1; - p.datalen = (quint16)d.length(); - p.sendseq = client->innerSeq; - QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - t.append(d); - - SEQBUFENTRY s; - s.seqNum = p.seq; - s.timeSent = QTime::currentTime(); - s.retransmitCount = 0; - s.data = t; - - if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + if (client->txSeqBuf.size() > BUFSIZE) { - if (client->txSeqBuf.size() > BUFSIZE) - { - client->txSeqBuf.remove(client->txSeqBuf.firstKey()); - } - client->txSeqBuf.insert(p.seq, s); - client->txSeq++; - client->innerSeq++; - client->txMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock txMutex()"; - } - - - if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - client->socket->writeDatagram(t, client->ipAddress, client->port); - udpMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock udpMutex()"; + client->txSeqBuf.remove(client->txSeqBuf.firstKey()); } + client->txSeqBuf.insert(p.seq, s); + client->txSeq++; + client->innerSeq++; + client->txMutex.unlock(); } else { - qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE + 1] << ":" << hex << (quint8)d[lastFE + 2]; + qInfo(logUdpServer()) << "Unable to lock txMutex()"; } + + + if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + client->socket->writeDatagram(t, client->ipAddress, client->port); + udpMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock udpMutex()"; + } + } + else { + qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE + 1] << ":" << hex << (quint8)d[lastFE + 2]; } } return; @@ -1548,13 +1588,13 @@ void udpServer::dataForServer(QByteArray d) void udpServer::sendRxAudio() { QByteArray audio; - for (RIGCONFIG &rig : config.rigs) { + for (RIGCONFIG* rig : config.rigs) { - if (rig.rxaudio != Q_NULLPTR) { + if (rig->rxaudio != Q_NULLPTR) { if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { audio.clear(); - rig.rxaudio->getNextAudioChunk(audio); + rig->rxaudio->getNextAudioChunk(audio); int len = 0; while (len < audio.length()) { audioPacket partial; @@ -1641,122 +1681,26 @@ void udpServer::sendRetransmitRequest(CLIENT* c) QByteArray missingSeqs; QTime missingTime = QTime::currentTime(); - if (!c->rxSeqBuf.empty() && c->rxSeqBuf.size() <= c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey()) - { - if ((c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size()) > 20) - { - // Too many packets to process, flush buffers and start again! - qDebug(logUdp()) << "Too many missing packets, flushing buffer: " << c->rxSeqBuf.lastKey() << "missing=" << c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() + 1; - if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - c->rxMissing.clear(); - c->missMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock missMutex()"; - } - - if (c->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - c->rxSeqBuf.clear(); - c->rxMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock rxMutex()"; - } - - } - else { - // We have at least 1 missing packet! - qDebug(logUdp()) << "Missing Seq: size=" << c->rxSeqBuf.size() << "firstKey=" << c->rxSeqBuf.firstKey() << "lastKey=" << c->rxSeqBuf.lastKey() << "missing=" << c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() + 1; - // We are missing packets so iterate through the buffer and add the missing ones to missing packet list - if (c->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - int missCounter = 0; - auto i = std::adjacent_find(c->rxSeqBuf.keys().begin(), c->rxSeqBuf.keys().end(), [](int l, int r) {return l + 1 < r; }); - while (i != c->rxSeqBuf.keys().end()) - { - quint16 j = 1 + *i; - ++i; - if (i == c->rxSeqBuf.keys().end()) - { - continue; - } - if (c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() == 0 && c->type == "AUDIO" && - (c->txCodec == 0x40 || c->txCodec == 0x80)) - { - // Single missing audio packet ignore it! - qDebug(logUdpServer()) << "Single missing audio packet will be handled by FEC (" << hex << j << ")"; - c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer so it doesn't try to retransmit - c->missMutex.unlock(); - c->rxMutex.unlock(); - return; - } - - missCounter++; - - if (missCounter > 20) { - // More than 20 packets missing, something horrific has happened! - qDebug(logUdpServer()) << ": Too many missing packets, clearing buffer"; - c->rxSeqBuf.clear(); - c->rxMissing.clear(); - c->missMutex.unlock(); - c->rxMutex.unlock(); - return; - } - auto s = c->rxMissing.find(j); - if (s == c->rxMissing.end()) - { - // We haven't seen this missing packet before - qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j << dec << missingTime.msecsTo(QTime::currentTime()) << "ms"; - c->rxMissing.insert(j, 0); - - if (c->rxSeqBuf.size() > BUFSIZE) - { - c->rxSeqBuf.remove(c->rxSeqBuf.firstKey()); - } - c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. - } - else { - if (s.value() == 4) - { - // We have tried 4 times to request this packet, time to give up! - s = c->rxMissing.erase(s); - } - } - - } - } - else { - qInfo(logUdpServer()) << "Unable to lock missMutex()"; - } - c->rxMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock rxMutex()"; - } - c->missMutex.unlock(); - } - } - - if (missingTime.msecsTo(QTime::currentTime()) > 10) { - qInfo(logUdpServer()) << "Initial missing processing has been running for" << missingTime.msecsTo(QTime::currentTime()) << "(ms)"; - } - if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it) { - if (it.value() < 10) + if (it.value() < 4) { missingSeqs.append(it.key() & 0xff); missingSeqs.append(it.key() >> 8 & 0xff); missingSeqs.append(it.key() & 0xff); missingSeqs.append(it.key() >> 8 & 0xff); it.value()++; + } + + else { + // We have tried 4 times to request this packet, time to give up! + qDebug(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; + it = c->rxMissing.erase(it); } + + } if (missingSeqs.length() != 0) @@ -1889,27 +1833,27 @@ void udpServer::deleteConnection(QList* l, CLIENT* c) } if (len == 0) { - for (RIGCONFIG& radio : config.rigs) { - if (!memcmp(radio.guid, guid, sizeof(radio.guid))) + for (RIGCONFIG* radio : config.rigs) { + if (!memcmp(radio->guid, guid, sizeof(radio->guid))) { - if (radio.rxAudioTimer != Q_NULLPTR) { - radio.rxAudioTimer->stop(); - delete radio.rxAudioTimer; - radio.rxAudioTimer = Q_NULLPTR; + if (radio->rxAudioTimer != Q_NULLPTR) { + radio->rxAudioTimer->stop(); + delete radio->rxAudioTimer; + radio->rxAudioTimer = Q_NULLPTR; } - if (radio.rxAudioThread != Q_NULLPTR) { - radio.rxAudioThread->quit(); - radio.rxAudioThread->wait(); - radio.rxaudio = Q_NULLPTR; - radio.rxAudioThread = Q_NULLPTR; + if (radio->rxAudioThread != Q_NULLPTR) { + radio->rxAudioThread->quit(); + radio->rxAudioThread->wait(); + radio->rxaudio = Q_NULLPTR; + radio->rxAudioThread = Q_NULLPTR; } - if (radio.txAudioThread != Q_NULLPTR) { - radio.txAudioThread->quit(); - radio.txAudioThread->wait(); - radio.txaudio = Q_NULLPTR; - radio.txAudioThread = Q_NULLPTR; + if (radio->txAudioThread != Q_NULLPTR) { + radio->txAudioThread->quit(); + radio->txAudioThread->wait(); + radio->txaudio = Q_NULLPTR; + radio->txAudioThread = Q_NULLPTR; } } } diff --git a/udpserver.h b/udpserver.h index 2269732..feea307 100644 --- a/udpserver.h +++ b/udpserver.h @@ -79,7 +79,7 @@ struct SERVERCONFIG { quint8 resampleQuality; quint32 baudRate; QList users; - QList rigs; + QList rigs; }; @@ -88,7 +88,7 @@ class udpServer : public QObject Q_OBJECT public: - udpServer(SERVERCONFIG* config, audioSetup outAudio, audioSetup inAudio); + udpServer(SERVERCONFIG& config, audioSetup outAudio, audioSetup inAudio); ~udpServer(); public slots: From bce66393fb063b19f5c0052bb4a50d3726ec6c57 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 22:44:30 +0000 Subject: [PATCH 094/323] Remove unnecessary code --- udphandler.cpp | 71 -------------------------------------------------- 1 file changed, 71 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 88e449b..804826a 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1329,77 +1329,6 @@ void udpBase::sendRetransmitRequest() QByteArray missingSeqs; - rxBufferMutex.lock(); - if (!rxSeqBuf.empty() && rxSeqBuf.size() <= rxSeqBuf.lastKey() - rxSeqBuf.firstKey()) - { - if ((rxSeqBuf.lastKey() - rxSeqBuf.firstKey() - rxSeqBuf.size()) > 20) - { - // Too many packets to process, flush buffers and start again! - qDebug(logUdp()) << "Too many missing packets, flushing buffer: " << rxSeqBuf.lastKey() << "missing=" << rxSeqBuf.lastKey() - rxSeqBuf.firstKey() - rxSeqBuf.size() + 1; - rxMissing.clear(); - missingMutex.lock(); - rxSeqBuf.clear(); - missingMutex.unlock(); - } - else { - // We have at least 1 missing packet! - qDebug(logUdp()) << "Missing Seq: size=" << rxSeqBuf.size() << "firstKey=" << rxSeqBuf.firstKey() << "lastKey=" << rxSeqBuf.lastKey() << "missing=" << rxSeqBuf.lastKey() - rxSeqBuf.firstKey() - rxSeqBuf.size() + 1; - // We are missing packets so iterate through the buffer and add the missing ones to missing packet list - missingMutex.lock(); - auto i = std::adjacent_find(rxSeqBuf.keys().begin(), rxSeqBuf.keys().end(), [](int l, int r) {return l + 1 < r; }); - - int missCounter = 0; - while (i != rxSeqBuf.keys().end()) - { - quint16 j = 1 + *i; - ++i; - if (i == rxSeqBuf.keys().end()) - { - continue; - } - - missCounter++; - - if (missCounter > 20) { - // More than 20 packets missing, something horrific has happened! - qDebug(logUdp()) << this->metaObject()->className() << ": Too many missing packets, clearing buffer"; - rxSeqBuf.clear(); - rxMissing.clear(); - missingMutex.unlock(); - rxBufferMutex.unlock(); - return; - } - - auto s = rxMissing.find(j); - if (s == rxMissing.end()) - { - // We haven't seen this missing packet before - qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << rxMissing.size() << "): " << j; - if (rxMissing.size() > 25) - { - rxMissing.erase(rxMissing.begin()); - } - rxMissing.insert(j, 0); - if (rxSeqBuf.size() > BUFSIZE) - { - rxSeqBuf.erase(rxSeqBuf.begin()); - } - rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. - packetsLost++; - } - else { - if (s.value() == 4) - { - // We have tried 4 times to request this packet, time to give up! - s = rxMissing.erase(s); - } - } - } - missingMutex.unlock(); - } - } - rxBufferMutex.unlock(); - missingMutex.lock(); for (auto it = rxMissing.begin(); it != rxMissing.end(); ++it) { From 77b7462f74c1072bb23f947b5d1cb34923335b53 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 22:51:28 +0000 Subject: [PATCH 095/323] Restore partial QT5.9 compatibility --- udphandler.cpp | 1 + udpserver.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/udphandler.cpp b/udphandler.cpp index 804826a..15da084 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1306,6 +1306,7 @@ void udpBase::dataReceived(QByteArray r) if (s != rxMissing.end()) { qDebug(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << hex << in->seq; + s = rxMissing.erase(s); } missingMutex.unlock(); diff --git a/udpserver.cpp b/udpserver.cpp index 9d111f5..8bae034 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -365,9 +365,14 @@ void udpServer::controlReceived() //connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); connect(radio->txAudioThread, SIGNAL(finished()), radio->txaudio, SLOT(deleteLater())); + // Not sure how we make this work in QT5.9? +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) QMetaObject::invokeMethod(radio->txaudio, [=]() { radio->txaudio->init(radio->txAudioSetup); }, Qt::QueuedConnection); +#else + #warn "QT 5.9 is not fully supported" +#endif emit setupTxAudio(outAudio); hasTxAudio = datagram.senderAddress(); @@ -400,9 +405,13 @@ void udpServer::controlReceived() connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater())); +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) QMetaObject::invokeMethod(radio->rxaudio, [=]() { radio->rxaudio->init(radio->rxAudioSetup); }, Qt::QueuedConnection); +#else + #warn "QT 5.9 is not fully supported" +#endif radio->rxAudioTimer = new QTimer(); radio->rxAudioTimer->setTimerType(Qt::PreciseTimer); @@ -568,9 +577,15 @@ void udpServer::civReceived() if (!memcmp(radio->guid, current->guid, sizeof(radio->guid))) { // Only send to the rig that it belongs to! + +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) QMetaObject::invokeMethod(radio->rig, [=]() { radio->rig->dataFromServer(r.mid(0x15));; }, Qt::DirectConnection); +#else + #warn "QT 5.9 is not fully supported" +#endif + } } From 3d1e04c556f5246b6628e81c93ad0abbfae32ec7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 22:52:50 +0000 Subject: [PATCH 096/323] Fix compile issue --- udpserver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index 8bae034..7b3ad4f 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -371,7 +371,7 @@ void udpServer::controlReceived() radio->txaudio->init(radio->txAudioSetup); }, Qt::QueuedConnection); #else - #warn "QT 5.9 is not fully supported" + #warning "QT 5.9 is not fully supported" #endif emit setupTxAudio(outAudio); @@ -410,7 +410,7 @@ void udpServer::controlReceived() radio->rxaudio->init(radio->rxAudioSetup); }, Qt::QueuedConnection); #else - #warn "QT 5.9 is not fully supported" + #warning "QT 5.9 is not fully supported" #endif radio->rxAudioTimer = new QTimer(); @@ -583,7 +583,7 @@ void udpServer::civReceived() radio->rig->dataFromServer(r.mid(0x15));; }, Qt::DirectConnection); #else - #warn "QT 5.9 is not fully supported" + #warning "QT 5.9 is not fully supported" #endif } From d29c1ddba771bb951ac13fe36c92c69d2058cfd2 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 23:12:07 +0000 Subject: [PATCH 097/323] More retransmit fixes --- udphandler.cpp | 3 +++ udpserver.cpp | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 15da084..1adbbd1 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1327,6 +1327,9 @@ void udpBase::sendRetransmitRequest() { // Find all gaps in received packets and then send requests for them. // This will run every 100ms so out-of-sequence packets will not trigger a retransmit request. + if (rxMissing.isEmpty()) { + return; + } QByteArray missingSeqs; diff --git a/udpserver.cpp b/udpserver.cpp index 7b3ad4f..95d2920 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1693,7 +1693,12 @@ void udpServer::sendRetransmitRequest(CLIENT* c) // Find all gaps in received packets and then send requests for them. // This will run every 100ms so out-of-sequence packets will not trigger a retransmit request. + if (c->rxMissing.isEmpty()) { + return; + } + QByteArray missingSeqs; + QTime missingTime = QTime::currentTime(); if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) @@ -1714,8 +1719,6 @@ void udpServer::sendRetransmitRequest(CLIENT* c) qDebug(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; it = c->rxMissing.erase(it); } - - } if (missingSeqs.length() != 0) From fe7bdf134538e382549b6dbc592e4820a12bc041 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 23:24:54 +0000 Subject: [PATCH 098/323] Flush buffers on excessive missing --- udphandler.cpp | 18 ++++++++++-------- udpserver.cpp | 19 +++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 1adbbd1..596afc3 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1268,14 +1268,6 @@ void udpBase::dataReceived(QByteArray r) int missCounter = 0; for (quint16 f = rxSeqBuf.lastKey() + 1; f < in->seq; f++) { - if (missCounter > 50) { - // More than 50 packets missing, something horrific has happened! - qDebug(logUdp()) << "Too many missing packets, full reset!"; - rxSeqBuf.clear(); - rxMissing.clear(); - missingMutex.unlock(); - break; - } qDebug(logUdp()) << "Detected missing packet" << f; if (rxSeqBuf.size() > BUFSIZE) @@ -1330,6 +1322,16 @@ void udpBase::sendRetransmitRequest() if (rxMissing.isEmpty()) { return; } + else if (rxMissing.size() > 100) { + qDebug(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers"; + missingMutex.lock(); + rxBufferMutex.lock(); + qDebug(logUdp()) << "Too many missing packets, full reset!"; + rxSeqBuf.clear(); + rxMissing.clear(); + missingMutex.unlock(); + return; + } QByteArray missingSeqs; diff --git a/udpserver.cpp b/udpserver.cpp index 95d2920..a922367 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -908,14 +908,6 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) for (quint16 f = current->rxSeqBuf.lastKey() + 1; f < in->seq; f++) { - if (missCounter > 50) { - // More than 50 packets missing, something horrific has happened! - qDebug(logUdpServer()) << "Too many missing packets, full reset!"; - current->rxSeqBuf.clear(); - current->rxMissing.clear(); - current->missMutex.unlock(); - break; - } qInfo(logUdpServer()) << "Detected missing packet" << f; @@ -1696,6 +1688,17 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (c->rxMissing.isEmpty()) { return; } + else if (c->rxMissing.size() > 100) { + qDebug(logUdp()) << "Too many missing packets," << c->rxMissing.size() << "flushing all buffers"; + c->missMutex.lock(); + c->rxMutex.lock(); + qDebug(logUdp()) << "Too many missing packets, full reset!"; + c->rxSeqBuf.clear(); + c->rxMissing.clear(); + c->missMutex.unlock(); + return; + } + QByteArray missingSeqs; From 3568c80fcc695a0cfeba22a9c68190b4df03ed84 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 23:28:34 +0000 Subject: [PATCH 099/323] Fix mutex --- udphandler.cpp | 7 ++++--- udpserver.cpp | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 596afc3..f4156f2 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1325,11 +1325,12 @@ void udpBase::sendRetransmitRequest() else if (rxMissing.size() > 100) { qDebug(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers"; missingMutex.lock(); - rxBufferMutex.lock(); - qDebug(logUdp()) << "Too many missing packets, full reset!"; - rxSeqBuf.clear(); rxMissing.clear(); missingMutex.unlock(); + + rxBufferMutex.lock(); + rxSeqBuf.clear(); + rxBufferMutex.unlock(); return; } diff --git a/udpserver.cpp b/udpserver.cpp index a922367..d1059dd 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1690,10 +1690,10 @@ void udpServer::sendRetransmitRequest(CLIENT* c) } else if (c->rxMissing.size() > 100) { qDebug(logUdp()) << "Too many missing packets," << c->rxMissing.size() << "flushing all buffers"; - c->missMutex.lock(); c->rxMutex.lock(); - qDebug(logUdp()) << "Too many missing packets, full reset!"; c->rxSeqBuf.clear(); + c->rxMutex.unlock(); + c->missMutex.lock(); c->rxMissing.clear(); c->missMutex.unlock(); return; From 9d2801a5054e78665fe1d8b77475529aa8d4fce1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 23:39:45 +0000 Subject: [PATCH 100/323] Improve congestion calculation --- udphandler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index f4156f2..2768e2f 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1124,7 +1124,7 @@ void udpBase::dataReceived(QByteArray r) { // Single packet request packetsLost++; - congestion ++; + congestion = packetsSent/packetsLost * 100; txBufferMutex.lock(); QMap::iterator match = txSeqBuf.find(in->seq); if (match != txSeqBuf.end()) { @@ -1230,7 +1230,7 @@ void udpBase::dataReceived(QByteArray r) udpMutex.unlock(); match++; packetsLost++; - congestion++; + congestion=packetsSent/packetsLost * 100; } } } @@ -1331,7 +1331,7 @@ void udpBase::sendRetransmitRequest() rxBufferMutex.lock(); rxSeqBuf.clear(); rxBufferMutex.unlock(); - return; + return } QByteArray missingSeqs; From be7dbbb39586967593441e5f684229c6b3ea1444 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 23:40:29 +0000 Subject: [PATCH 101/323] Missing ; --- udphandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udphandler.cpp b/udphandler.cpp index 2768e2f..1a5b795 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1331,7 +1331,7 @@ void udpBase::sendRetransmitRequest() rxBufferMutex.lock(); rxSeqBuf.clear(); rxBufferMutex.unlock(); - return + return; } QByteArray missingSeqs; From 6427be05b824e9d97e05324e17f369cbe027d781 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 23:47:17 +0000 Subject: [PATCH 102/323] Update udphandler.cpp --- udphandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 1a5b795..c41c9c7 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1124,7 +1124,7 @@ void udpBase::dataReceived(QByteArray r) { // Single packet request packetsLost++; - congestion = packetsSent/packetsLost * 100; + congestion = (packetsSent/packetsLost) * 100; txBufferMutex.lock(); QMap::iterator match = txSeqBuf.find(in->seq); if (match != txSeqBuf.end()) { @@ -1230,7 +1230,7 @@ void udpBase::dataReceived(QByteArray r) udpMutex.unlock(); match++; packetsLost++; - congestion=packetsSent/packetsLost * 100; + congestion=(packetsSent/packetsLost) * 100; } } } From c0fabb3999f20044339f4686e85d55f9f449a8b3 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 27 Jan 2022 23:51:25 +0000 Subject: [PATCH 103/323] Update udphandler.cpp --- udphandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index c41c9c7..8890694 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1124,7 +1124,7 @@ void udpBase::dataReceived(QByteArray r) { // Single packet request packetsLost++; - congestion = (packetsSent/packetsLost) * 100; + congestion = static_cast(packetsSent) / packetsLost * 100; txBufferMutex.lock(); QMap::iterator match = txSeqBuf.find(in->seq); if (match != txSeqBuf.end()) { @@ -1230,7 +1230,7 @@ void udpBase::dataReceived(QByteArray r) udpMutex.unlock(); match++; packetsLost++; - congestion=(packetsSent/packetsLost) * 100; + congestion = static_cast(packetsSent) / packetsLost * 100; } } } From 30d5858663edd0ab5433bbe9f0a38b7345356fa5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 28 Jan 2022 09:43:53 +0000 Subject: [PATCH 104/323] Max missing packets slightly less than buffer size --- packettypes.h | 1 + udphandler.cpp | 2 +- udpserver.cpp | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packettypes.h b/packettypes.h index f2f46af..ef471e6 100644 --- a/packettypes.h +++ b/packettypes.h @@ -15,6 +15,7 @@ #define LOCK_PERIOD 10 // How long to try to lock mutex (ms) #define STALE_CONNECTION 15 // Not heard from in this many seconds #define BUFSIZE 500 // Number of packets to buffer +#define MAX_MISSING 400 // Make the maximum number of possible missing packets less than total buffer size! #define TXAUDIO_PERIOD 20 diff --git a/udphandler.cpp b/udphandler.cpp index 8890694..160d52f 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1322,7 +1322,7 @@ void udpBase::sendRetransmitRequest() if (rxMissing.isEmpty()) { return; } - else if (rxMissing.size() > 100) { + else if (rxMissing.size() > MAX_MISSING) { qDebug(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers"; missingMutex.lock(); rxMissing.clear(); diff --git a/udpserver.cpp b/udpserver.cpp index d1059dd..a79c4ee 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -823,11 +823,12 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) { for (quint16 i = 0x10; i < r.length(); i = i + 2) { - auto match = std::find_if(current->txSeqBuf.begin(), current->txSeqBuf.end(), [&cs = in->seq](SEQBUFENTRY& s) { + quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; + auto match = std::find_if(current->txSeqBuf.begin(), current->txSeqBuf.end(), [&cs = seq](SEQBUFENTRY& s) { return s.seqNum == cs; }); if (match == current->txSeqBuf.end()) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested packet " << hex << in->seq << " not found"; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested packet " << hex << seq << " not found"; // Just send idle packet. sendControl(current, 0, in->seq); } @@ -1688,7 +1689,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (c->rxMissing.isEmpty()) { return; } - else if (c->rxMissing.size() > 100) { + else if (c->rxMissing.size() > MAX_MISSING) { qDebug(logUdp()) << "Too many missing packets," << c->rxMissing.size() << "flushing all buffers"; c->rxMutex.lock(); c->rxSeqBuf.clear(); From dd9be7c99ceb0576c5de837bf44b526c2118db50 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 28 Jan 2022 09:52:26 +0000 Subject: [PATCH 105/323] Tuning lost packets --- packettypes.h | 2 +- udpserver.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packettypes.h b/packettypes.h index ef471e6..23ce742 100644 --- a/packettypes.h +++ b/packettypes.h @@ -15,7 +15,7 @@ #define LOCK_PERIOD 10 // How long to try to lock mutex (ms) #define STALE_CONNECTION 15 // Not heard from in this many seconds #define BUFSIZE 500 // Number of packets to buffer -#define MAX_MISSING 400 // Make the maximum number of possible missing packets less than total buffer size! +#define MAX_MISSING 100 // Make the maximum number of possible missing packets much less than total buffer size! #define TXAUDIO_PERIOD 20 diff --git a/udpserver.cpp b/udpserver.cpp index a79c4ee..09a89af 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1709,7 +1709,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) { for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it) { - if (it.value() < 4) + if (&it != Q_NULLPTR && it.value() < 4) { missingSeqs.append(it.key() & 0xff); missingSeqs.append(it.key() >> 8 & 0xff); From 9ae8bc660c2c22b4c887e33997f58921fabb8540 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 29 Jan 2022 22:50:58 +0000 Subject: [PATCH 106/323] Lots more fixes and tweaks --- audiohandler.h | 4 ++++ commhandler.cpp | 17 ++++++++++++---- packettypes.h | 7 ++++--- rigcommander.cpp | 6 +++--- rigcommander.h | 4 ++-- rigidentities.h | 3 ++- servermain.cpp | 40 +++++++++++++++++++++++++++++-------- servermain.h | 1 + udphandler.cpp | 14 ++++++------- udphandler.h | 10 +++++----- udpserver.cpp | 51 +++++++++++++++++++++++++++++------------------- udpserver.h | 8 ++++---- wfmain.cpp | 49 ++++++++++++++++++++++++++++++---------------- 13 files changed, 140 insertions(+), 74 deletions(-) diff --git a/audiohandler.h b/audiohandler.h index ab8b1bb..d12ff05 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -25,6 +25,9 @@ #include #endif + +#include "packettypes.h" + typedef signed short MY_TYPE; #define FORMAT RTAUDIO_SINT16 #define SCALE 32767.0 @@ -61,6 +64,7 @@ struct audioPacket { QTime time; quint16 sent; QByteArray data; + quint8 guid[GUIDLEN]; }; struct audioSetup { diff --git a/commhandler.cpp b/commhandler.cpp index e678317..72a1cd8 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -60,7 +60,10 @@ commHandler::commHandler(QString portName, quint32 baudRate) commHandler::~commHandler() { - this->closePort(); + if (isConnected) { + this->closePort(); + } + delete port; } void commHandler::setupComm() @@ -78,9 +81,16 @@ void commHandler::receiveDataFromUserToRig(const QByteArray &data) void commHandler::sendDataOut(const QByteArray &writeData) { - mutex.lock(); + // Recycle port to attempt reconnection. + if (!this->isConnected) { + closePort(); + openPort(); + } + if (!this->isConnected) { + return; + } qint64 bytesWritten; if(PTTviaRTS) @@ -230,7 +240,6 @@ void commHandler::setUseRTSforPTT(bool PTTviaRTS) void commHandler::openPort() { bool success; - // port->open(); success = port->open(QIODevice::ReadWrite); if(success) { @@ -253,7 +262,7 @@ void commHandler::closePort() if(port) { port->close(); - delete port; + //delete port; } isConnected = false; } diff --git a/packettypes.h b/packettypes.h index 23ce742..cdb7c9c 100644 --- a/packettypes.h +++ b/packettypes.h @@ -15,8 +15,9 @@ #define LOCK_PERIOD 10 // How long to try to lock mutex (ms) #define STALE_CONNECTION 15 // Not heard from in this many seconds #define BUFSIZE 500 // Number of packets to buffer -#define MAX_MISSING 100 // Make the maximum number of possible missing packets much less than total buffer size! +#define MAX_MISSING 50 // Make the maximum number of possible missing packets much less than total buffer size! #define TXAUDIO_PERIOD 20 +#define GUIDLEN 16 // Fixed Size Packets @@ -294,7 +295,7 @@ typedef union conninfo_packet { char unusedh; // 0x29 char macaddress[6]; // 0x2a }; - quint8 guid[16]; // 0x20 + quint8 guid[GUIDLEN]; // 0x20 }; char unusedab[16]; // 0x30 char name[32]; // 0x40 @@ -339,7 +340,7 @@ typedef union radio_cap_packet { char unused; // 0x0 char macaddress[6]; // 0x0 }; - quint8 guid[16]; // 0x0 + quint8 guid[GUIDLEN]; // 0x0 }; char name[32]; // 0x10 char audio[32]; // 0x30 diff --git a/rigcommander.cpp b/rigcommander.cpp index 4094da5..4ece2ea 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -26,11 +26,11 @@ rigCommander::rigCommander() state.set(SCOPEFUNC, true, false); } -rigCommander::rigCommander(quint8 guid[16]) +rigCommander::rigCommander(quint8 guid[GUIDLEN]) { qInfo(logRig()) << "creating instance of rigCommander()"; state.set(SCOPEFUNC, true, false); - memcpy(this->guid, guid, sizeof(this->guid)); + memcpy(this->guid, guid, GUIDLEN); } rigCommander::~rigCommander() @@ -3624,7 +3624,7 @@ void rigCommander::determineRigCaps() haveRigCaps = true; // Copy received guid so we can recognise this radio. - memcpy(rigCaps.guid, this->guid, sizeof(rigCaps.guid)); + memcpy(rigCaps.guid, this->guid, GUIDLEN); if(!usingNativeLAN) { diff --git a/rigcommander.h b/rigcommander.h index e343555..00754a2 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -69,7 +69,7 @@ class rigCommander : public QObject public: rigCommander(); - rigCommander(quint8 guid[16]); + rigCommander(quint8 guid[GUIDLEN]); ~rigCommander(); bool usingLAN(); @@ -478,7 +478,7 @@ private: QString serialPortError; unsigned char localVolume=0; - quint8 guid[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + quint8 guid[GUIDLEN] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; }; diff --git a/rigidentities.h b/rigidentities.h index 222a73c..7df3e19 100644 --- a/rigidentities.h +++ b/rigidentities.h @@ -7,6 +7,7 @@ #include #include "freqmemory.h" +#include "packettypes.h" // Credit for parts of CIV list: // http://www.docksideradio.com/Icom%20Radio%20Hex%20Addresses.htm @@ -138,7 +139,7 @@ struct rigCapabilities { std::vector modes; QByteArray transceiveCommand; - quint8 guid[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + quint8 guid[GUIDLEN] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; quint32 baudRate; }; diff --git a/servermain.cpp b/servermain.cpp index fd34fc5..c83ee18 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -234,7 +234,7 @@ void servermain::receiveCommReady() for (RIGCONFIG* radio : serverConfig.rigs) { - if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !memcmp(sender->getGUID(), radio->guid, sizeof(radio->guid))) + if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !memcmp(sender->getGUID(), radio->guid, GUIDLEN)) { qInfo(logSystem()) << "Received CommReady!! "; @@ -243,9 +243,11 @@ void servermain::receiveCommReady() // tell rigCommander to broadcast a request for all rig IDs. // qInfo(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)"; if (!radio->rigAvailable) { - QMetaObject::invokeMethod(radio->rig, [=]() { - radio->rig->findRigs(); - }, Qt::QueuedConnection); + if (radio->connectTimer == Q_NULLPTR) { + radio->connectTimer = new QTimer(); + connect(radio->connectTimer, &QTimer::timeout, this, std::bind(&servermain::connectToRig, this, radio)); + } + radio->connectTimer->start(500); } } else { @@ -260,6 +262,18 @@ void servermain::receiveCommReady() } } +void servermain::connectToRig(RIGCONFIG* rig) +{ + if (!rig->rigAvailable) { + //qDebug(logSystem()) << "Searching for rig on" << rig->serialPort; + QMetaObject::invokeMethod(rig->rig, [=]() { + rig->rig->findRigs(); + }, Qt::QueuedConnection); + } + else { + rig->connectTimer->stop(); + } +} void servermain::receiveFoundRigID(rigCapabilities rigCaps) { @@ -272,7 +286,7 @@ void servermain::receiveFoundRigID(rigCapabilities rigCaps) for (RIGCONFIG* radio : serverConfig.rigs) { - if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !radio->rigAvailable && !memcmp(sender->getGUID(), radio->guid, sizeof(radio->guid))) + if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !radio->rigAvailable && !memcmp(sender->getGUID(), radio->guid, GUIDLEN)) { qDebug(logSystem()) << "Rig name: " << rigCaps.modelName; @@ -521,7 +535,7 @@ void servermain::loadSettings() guid = QUuid::createUuid().toString(); settings->setValue("GUID", guid); } - memcpy(tempPrefs->guid, QUuid::fromString(guid).toRfc4122().constData(), sizeof(tempPrefs->guid)); + memcpy(tempPrefs->guid, QUuid::fromString(guid).toRfc4122().constData(), GUIDLEN); tempPrefs->rxAudioSetup.isinput = true; tempPrefs->txAudioSetup.isinput = false; @@ -576,7 +590,12 @@ void servermain::loadSettings() //qInfo(logAudio()) << "Looking for audio output devices"; for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { - if (deviceInfo.deviceName() == tempPrefs->txAudioSetup.name) { + if (deviceInfo.deviceName() == tempPrefs->txAudioSetup.name +#ifdef Q_OS_WIN + && deviceInfo.realm() == "wasapi" +#endif + ) { + qDebug(logSystem()) << "Audio output: " << deviceInfo.deviceName() << "Realm:" << deviceInfo.realm(); tempPrefs->txAudioSetup.port = deviceInfo; txDeviceFound = true; } @@ -584,7 +603,12 @@ void servermain::loadSettings() //qInfo(logAudio()) << "Looking for audio input devices"; for (const QAudioDeviceInfo& deviceInfo : audioInputs) { - if (deviceInfo.deviceName() == tempPrefs->rxAudioSetup.name) { + if (deviceInfo.deviceName() == tempPrefs->rxAudioSetup.name +#ifdef Q_OS_WIN + && deviceInfo.realm() == "wasapi" +#endif + ) { + qDebug(logSystem()) << "Audio input: " << deviceInfo.deviceName() << "Realm:" << deviceInfo.realm(); tempPrefs->rxAudioSetup.port = deviceInfo; rxDeviceFound = true; } diff --git a/servermain.h b/servermain.h index cea805c..6e5fcc5 100644 --- a/servermain.h +++ b/servermain.h @@ -175,6 +175,7 @@ private slots: void handlePttLimit(); void receiveStatusUpdate(networkStatus status); void receiveStateInfo(rigstate* state); + void connectToRig(RIGCONFIG* rig); private: QSettings *settings=Q_NULLPTR; diff --git a/udphandler.cpp b/udphandler.cpp index 160d52f..2bb5a88 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -198,7 +198,7 @@ void udpHandler::dataReceived() if (txSetup.codec == 0) { txString = "(no tx)"; } - status.message = QString("
%1 rx latency: %2 / rtt: %3 ms / loss: %4/%5
").arg(txString).arg(tempLatency).arg(latency, 3).arg(status.packetsLost, 3).arg(status.packetsSent, 3); + status.message = QString("
%1 rx latency: %2 / rtt: %3 ms / loss: %4/%5
").arg(txString).arg(tempLatency).arg(status.networkLatency, 3).arg(status.packetsLost, 3).arg(status.packetsSent, 3); if (audio != Q_NULLPTR) { status.rxAudioLevel = audio->getRxAmplitude(); @@ -370,7 +370,7 @@ void udpHandler::dataReceived() radios[f].macaddress[3] == in->macaddress[3] && radios[f].macaddress[4] == in->macaddress[4] && radios[f].macaddress[5] == in->macaddress[5]) || - !memcmp(radios[f].guid,in->guid,sizeof(in->guid))) + !memcmp(radios[f].guid,in->guid, GUIDLEN)) { emit setRadioUsage(f, in->busy, QString(in->computer), ip.toString()); } @@ -408,7 +408,7 @@ void udpHandler::dataReceived() { if ((r.length() - CAPABILITIES_SIZE) % RADIO_CAP_SIZE != 0) { - qInfo(logUdp()) << this->metaObject()->className() << "Packet received" << r.length() << "not recognised"; + // Likely a retransmit request? break; } @@ -468,7 +468,7 @@ void udpHandler::setCurrentRadio(quint8 radio) { } else { useGuid = true; - memcpy(&guid, radios[radio].guid, sizeof(guid)); + memcpy(&guid, radios[radio].guid, GUIDLEN); } devName =radios[radio].name; @@ -499,7 +499,7 @@ void udpHandler::sendRequestStream() memcpy(&p.macaddress, macaddress, 6); } else { - memcpy(&p.guid, guid, sizeof(p.guid)); + memcpy(&p.guid, guid, GUIDLEN); } p.innerseq = authSeq++; p.tokrequest = tokRequest; @@ -1265,10 +1265,10 @@ void udpBase::dataReceived(QByteArray r) if (in->seq > rxSeqBuf.lastKey() + 1) { // We are likely missing packets then! missingMutex.lock(); - int missCounter = 0; + //int missCounter = 0; for (quint16 f = rxSeqBuf.lastKey() + 1; f < in->seq; f++) { - qDebug(logUdp()) << "Detected missing packet" << f; + //qDebug(logUdp()) << "Detected missing packet" << f; if (rxSeqBuf.size() > BUFSIZE) { diff --git a/udphandler.h b/udphandler.h index ee6ea24..ca3aa2a 100644 --- a/udphandler.h +++ b/udphandler.h @@ -43,10 +43,10 @@ struct networkStatus { quint8 txAudioLevel; quint16 rxLatency; quint16 txLatency; - quint32 packetsSent; - quint32 packetsLost; - quint16 rtt; - quint32 networkLatency; + quint32 packetsSent=0; + quint32 packetsLost=0; + quint16 rtt=0; + quint32 networkLatency=0; QString message; }; @@ -289,7 +289,7 @@ private: quint32 token; // These are for stream ident info. quint8 macaddress[8]; - quint8 guid[16]; + quint8 guid[GUIDLEN]; bool useGuid = false; QByteArray usernameEncoded; QByteArray passwordEncoded; diff --git a/udpserver.cpp b/udpserver.cpp index 09a89af..8ec7255 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -40,7 +40,7 @@ void udpServer::init() .arg(rig->guid[15], 2, 16, QLatin1Char('0')) ; } - srand(time(NULL)); // Generate random key + srand(time(NULL)); // Generate random timeStarted.start(); // Convoluted way to find the external IP address, there must be a better way???? QString localhostname = QHostInfo::localHostName(); @@ -109,7 +109,7 @@ udpServer::~udpServer() deleteConnection(&audioClients, client); } - // Now all connections are deleted, close and delete the sockets. + // Now all connections are deleted, close and delete the sockets if (udpControl != Q_NULLPTR) { udpControl->close(); delete udpControl; @@ -131,18 +131,19 @@ udpServer::~udpServer() void udpServer::receiveRigCaps(rigCapabilities caps) { for (RIGCONFIG* rig: config.rigs) { - if (!memcmp(rig->guid, caps.guid, sizeof(rig->guid))) { + if (!memcmp(rig->guid, caps.guid, GUIDLEN)) { // Matching rig, fill-in missing details rig->rigAvailable = true; rig->modelName = caps.modelName; rig->civAddr = caps.civ; - if (rig->rigName=="") { + if (rig->rigName == "") { rig->rigName = caps.modelName; } } } } + void udpServer::controlReceived() { // Received data on control port. @@ -274,7 +275,7 @@ void udpServer::controlReceived() sendTokenResponse(current, in->res); current->isStreaming = false; for (RIGCONFIG* radio : config.rigs) { - if (!memcmp(radio->guid, current->guid, sizeof(radio->guid))) + if (!memcmp(radio->guid, current->guid, GUIDLEN)) { sendConnectionInfo(current, radio->guid); } @@ -334,7 +335,7 @@ void udpServer::controlReceived() current->txBufferLen = qFromBigEndian(in->txbuffer); current->authInnerSeq = in->innerseq; - memcpy(current->guid, in->guid, sizeof(in->guid)); + memcpy(current->guid, in->guid, GUIDLEN); sendStatus(current); current->authInnerSeq = 0x00; sendConnectionInfo(current,in->guid); @@ -347,7 +348,7 @@ void udpServer::controlReceived() audioSetup setup; setup.resampleQuality = config.resampleQuality; for (RIGCONFIG* radio : config.rigs) { - if (!memcmp(radio->guid, current->guid, sizeof(radio->guid)) && radio->txaudio == Q_NULLPTR) + if (!memcmp(radio->guid, current->guid, GUIDLEN) && radio->txaudio == Q_NULLPTR) { radio->txAudioSetup.codec = current->txCodec; radio->txAudioSetup.samplerate = current->txSampleRate; @@ -380,7 +381,7 @@ void udpServer::controlReceived() connect(this, SIGNAL(haveAudioData(audioPacket)), radio->txaudio, SLOT(incomingAudio(audioPacket))); } - if (!memcmp(radio->guid, current->guid, sizeof(radio->guid)) && radio->rxaudio == Q_NULLPTR) + if (!memcmp(radio->guid, current->guid, GUIDLEN) && radio->rxaudio == Q_NULLPTR) { #if !defined(PORTAUDIO) && !defined(RTAUDIO) qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio input(RX) :" << radio->rxAudioSetup.port.deviceName(); @@ -472,7 +473,7 @@ void udpServer::civReceived() { current->controlClient = client; client->civClient = current; - memcpy(current->guid, client->guid, sizeof(current->guid)); + memcpy(current->guid, client->guid, GUIDLEN); } } } @@ -639,7 +640,7 @@ void udpServer::audioReceived() { current->controlClient = client; client->audioClient = current; - memcpy(current->guid, client->guid, sizeof(current->guid)); + memcpy(current->guid, client->guid, GUIDLEN); } } } @@ -901,7 +902,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) if (current->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. - int missCounter = 0; + //int missCounter = 0; if (in->seq > current->rxSeqBuf.lastKey() + 1) { // We are likely missing packets then! if (current->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) @@ -1169,7 +1170,7 @@ void udpServer::sendCapabilities(CLIENT* c) radio_cap_packet r; memset(r.packet, 0x0, sizeof(r)); // We can't be sure it is initialized with 0x00! - memcpy(r.guid, rig->guid, sizeof(r.guid)); + memcpy(r.guid, rig->guid, GUIDLEN); memcpy(r.name, rig->rigName.toLocal8Bit(), sizeof(r.name)); memcpy(r.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11); @@ -1268,10 +1269,10 @@ void udpServer::sendCapabilities(CLIENT* c) // When client has requested civ/audio connection, this will contain their details // Also used to display currently connected used information. -void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[16]) +void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[GUIDLEN]) { for (RIGCONFIG* radio : config.rigs) { - if (!memcmp(guid, radio->guid, sizeof(guid))) + if (!memcmp(guid, radio->guid, GUIDLEN)) { qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending ConnectionInfo :" << c->txSeq; conninfo_packet p; @@ -1285,7 +1286,7 @@ void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[16]) p.tokrequest = c->tokenRx; p.token = c->tokenTx; p.code = 0x0380; - memcpy(p.guid, radio->guid, sizeof(p.guid)); + memcpy(p.guid, radio->guid, GUIDLEN); memcpy(p.name, radio->rigName.toLocal8Bit(), sizeof(p.name)); if (radio->rigAvailable) { @@ -1532,7 +1533,7 @@ void udpServer::dataForServer(QByteArray d) continue; } // Use the GUID to determine which radio the response is from - if (memcmp(sender->getGUID(), client->guid, sizeof(client->guid))) + if (memcmp(sender->getGUID(), client->guid, GUIDLEN)) { continue; // Rig guid doesn't match the one requested by the client. } @@ -1606,6 +1607,7 @@ void udpServer::sendRxAudio() int len = 0; while (len < audio.length()) { audioPacket partial; + memcpy(partial.guid, rig->guid, GUIDLEN); partial.data = audio.mid(len, 1364); receiveAudioData(partial); len = len + partial.data.length(); @@ -1624,10 +1626,19 @@ void udpServer::sendRxAudio() void udpServer::receiveAudioData(const audioPacket& d) { + rigCommander* sender = qobject_cast(QObject::sender()); + quint8 guid[GUIDLEN]; + if (sender != Q_NULLPTR) + { + memcpy(guid, sender->getGUID(), GUIDLEN); + } + else { + memcpy(guid, d.guid, GUIDLEN); + } //qInfo(logUdpServer()) << "Server got:" << d.data.length(); foreach(CLIENT * client, audioClients) { - if (client != Q_NULLPTR && client->connected) { + if (client != Q_NULLPTR && client->connected && !memcmp(client->guid,guid, GUIDLEN)) { audio_packet p; memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! p.len = sizeof(p) + d.data.length(); @@ -1782,8 +1793,8 @@ void udpServer::sendRetransmitRequest(CLIENT* c) void udpServer::deleteConnection(QList* l, CLIENT* c) { - quint8 guid[16]; - memcpy(guid, c->guid, sizeof(guid)); + quint8 guid[GUIDLEN]; + memcpy(guid, c->guid, GUIDLEN); int len = l->length(); qInfo(logUdpServer()) << "Deleting" << c->type << "connection to: " << c->ipAddress.toString() << ":" << QString::number(c->port); @@ -1856,7 +1867,7 @@ void udpServer::deleteConnection(QList* l, CLIENT* c) if (len == 0) { for (RIGCONFIG* radio : config.rigs) { - if (!memcmp(radio->guid, guid, sizeof(radio->guid))) + if (!memcmp(radio->guid, guid, GUIDLEN)) { if (radio->rxAudioTimer != Q_NULLPTR) { radio->rxAudioTimer->stop(); diff --git a/udpserver.h b/udpserver.h index feea307..9f769df 100644 --- a/udpserver.h +++ b/udpserver.h @@ -54,7 +54,7 @@ struct RIGCONFIG { audioSetup txAudioSetup; QString modelName; QString rigName; - quint8 guid[16]; + quint8 guid[GUIDLEN]; bool rigAvailable=false; rigCapabilities rigCaps; rigCommander* rig = Q_NULLPTR; @@ -64,7 +64,7 @@ struct RIGCONFIG { audioHandler* txaudio = Q_NULLPTR; QThread* txAudioThread = Q_NULLPTR; QTimer* rxAudioTimer = Q_NULLPTR; - + QTimer* connectTimer = Q_NULLPTR; }; @@ -167,7 +167,7 @@ private: CLIENT* controlClient = Q_NULLPTR; CLIENT* civClient = Q_NULLPTR; CLIENT* audioClient = Q_NULLPTR; - quint8 guid[16]; + quint8 guid[GUIDLEN]; }; void controlReceived(); @@ -179,7 +179,7 @@ private: void sendControl(CLIENT* c, quint8 type, quint16 seq); void sendLoginResponse(CLIENT* c, bool allowed); void sendCapabilities(CLIENT* c); - void sendConnectionInfo(CLIENT* c,quint8 guid[16]); + void sendConnectionInfo(CLIENT* c,quint8 guid[GUIDLEN]); void sendTokenResponse(CLIENT* c,quint8 type); void sendStatus(CLIENT* c); void sendRetransmitRequest(CLIENT* c); diff --git a/wfmain.cpp b/wfmain.cpp index 13523c8..48af1df 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -446,6 +446,11 @@ void wfmain::makeRig() connect(rig, SIGNAL(stateInfo(rigstate*)), rigCtl, SLOT(receiveStateInfo(rigstate*))); connect(rigCtl, SIGNAL(stateUpdated()), rig, SLOT(stateUpdated())); } + // Create link for server so it can have easy access to rig. + if (serverConfig.rigs.first() != Q_NULLPTR) { + serverConfig.rigs.first()->rig = rig; + serverConfig.rigs.first()->rigThread = rigThread; + } } } @@ -1163,7 +1168,7 @@ void wfmain::setSerialDevicesUI() ui->serialDeviceListCombo->addItem(QString("/dev/")+serialPortInfo.portName(), i++); #else ui->serialDeviceListCombo->addItem(serialPortInfo.portName(), i++); - qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); + //qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); #endif } #if defined(Q_OS_LINUX) || defined(Q_OS_MAC) @@ -1659,18 +1664,29 @@ void wfmain::loadSettings() ui->serverCivPortText->setText(QString::number(serverConfig.civPort)); ui->serverAudioPortText->setText(QString::number(serverConfig.audioPort)); - serverRxSetup.isinput = true; + RIGCONFIG* rigTemp = new RIGCONFIG(); + rigTemp->rxAudioSetup.isinput = true; + rigTemp->txAudioSetup.isinput = true; + rigTemp->rxAudioSetup.localAFgain = 255; + rigTemp->txAudioSetup.localAFgain = 255; + rigTemp->rxAudioSetup.resampleQuality = 4; + rigTemp->txAudioSetup.resampleQuality = 4; - serverTxSetup.isinput = false; + rigTemp->baudRate = prefs.serialPortBaud; + rigTemp->civAddr = prefs.radioCIVAddr; + rigTemp->serialPort = prefs.serialPortBaud; - serverRxSetup.localAFgain = 255; - - serverTxSetup.localAFgain = 255; + QString guid = settings->value("GUID", "").toString(); + if (guid.isEmpty()) { + guid = QUuid::createUuid().toString(); + settings->setValue("GUID", guid); + } + memcpy(rigTemp->guid, QUuid::fromString(guid).toRfc4122().constData(), GUIDLEN); ui->serverRXAudioInputCombo->blockSignals(true); - serverRxSetup.name = settings->value("ServerAudioInput", "").toString(); - qInfo(logGui()) << "Got Server Audio Input: " << serverRxSetup.name; - int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(serverRxSetup.name); + rigTemp->rxAudioSetup.name = settings->value("ServerAudioInput", "").toString(); + qInfo(logGui()) << "Got Server Audio Input: " << rigTemp->rxAudioSetup.name; + int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(rigTemp->rxAudioSetup.name); if (serverAudioInputIndex != -1) { ui->serverRXAudioInputCombo->setCurrentIndex(serverAudioInputIndex); #if defined(RTAUDIO) @@ -1679,18 +1695,15 @@ void wfmain::loadSettings() serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(serverAudioInputIndex).toInt(); #else QVariant v = ui->serverRXAudioInputCombo->currentData(); - serverRxSetup.port = v.value(); + rigTemp->rxAudioSetup.port = v.value(); #endif } ui->serverRXAudioInputCombo->blockSignals(false); - serverRxSetup.resampleQuality = rxSetup.resampleQuality; - serverTxSetup.resampleQuality = serverRxSetup.resampleQuality; - ui->serverTXAudioOutputCombo->blockSignals(true); - serverTxSetup.name = settings->value("ServerAudioOutput", "").toString(); - qInfo(logGui()) << "Got Server Audio Output: " << serverTxSetup.name; - int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(serverTxSetup.name); + rigTemp->txAudioSetup.name = settings->value("ServerAudioOutput", "").toString(); + qInfo(logGui()) << "Got Server Audio Output: " << rigTemp->txAudioSetup.name; + int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(rigTemp->txAudioSetup.name); if (serverAudioOutputIndex != -1) { ui->serverTXAudioOutputCombo->setCurrentIndex(serverAudioOutputIndex); #if defined(RTAUDIO) @@ -1699,11 +1712,13 @@ void wfmain::loadSettings() serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(serverAudioOutputIndex).toInt(); #else QVariant v = ui->serverTXAudioOutputCombo->currentData(); - serverTxSetup.port = v.value(); + rigTemp->txAudioSetup.port = v.value(); #endif } ui->serverTXAudioOutputCombo->blockSignals(false); + serverConfig.rigs.append(rigTemp); + int row = 0; ui->serverUsersTable->setRowCount(0); From 46cd6c1e5336af032e0c3bdde5095317a4fa656a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 30 Jan 2022 10:29:23 +0000 Subject: [PATCH 107/323] Hopefully fix Linux compile error --- packettypes.h | 2 +- servermain.cpp | 5 +++-- udphandler.cpp | 2 +- udpserver.cpp | 21 +++++++++++---------- wfmain.cpp | 20 ++++++++++++++++---- 5 files changed, 32 insertions(+), 18 deletions(-) diff --git a/packettypes.h b/packettypes.h index cdb7c9c..51888ce 100644 --- a/packettypes.h +++ b/packettypes.h @@ -16,7 +16,7 @@ #define STALE_CONNECTION 15 // Not heard from in this many seconds #define BUFSIZE 500 // Number of packets to buffer #define MAX_MISSING 50 // Make the maximum number of possible missing packets much less than total buffer size! -#define TXAUDIO_PERIOD 20 +#define AUDIO_PERIOD 20 #define GUIDLEN 16 diff --git a/servermain.cpp b/servermain.cpp index c83ee18..ea3d29d 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -595,7 +595,7 @@ void servermain::loadSettings() && deviceInfo.realm() == "wasapi" #endif ) { - qDebug(logSystem()) << "Audio output: " << deviceInfo.deviceName() << "Realm:" << deviceInfo.realm(); + qDebug(logSystem()) << "Audio output: " << deviceInfo.deviceName(); tempPrefs->txAudioSetup.port = deviceInfo; txDeviceFound = true; } @@ -608,7 +608,7 @@ void servermain::loadSettings() && deviceInfo.realm() == "wasapi" #endif ) { - qDebug(logSystem()) << "Audio input: " << deviceInfo.deviceName() << "Realm:" << deviceInfo.realm(); + qDebug(logSystem()) << "Audio input: " << deviceInfo.deviceName(); tempPrefs->rxAudioSetup.port = deviceInfo; rxDeviceFound = true; } @@ -715,5 +715,6 @@ void servermain::powerRigOff() void servermain::receiveStateInfo(rigstate* state) { qInfo("Received rig state for wfmain"); + Q_UNUSED(state); //rigState = state; } diff --git a/udphandler.cpp b/udphandler.cpp index 2bb5a88..0f6f6c3 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1005,7 +1005,7 @@ void udpAudio::dataReceived() control_packet_t in = (control_packet_t)r.constData(); if (in->type == 0x04 && enableTx) { - txAudioTimer->start(TXAUDIO_PERIOD); + txAudioTimer->start(AUDIO_PERIOD); } break; diff --git a/udpserver.cpp b/udpserver.cpp index 8ec7255..c983033 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -363,7 +363,7 @@ void udpServer::controlReceived() radio->txAudioThread->start(QThread::TimeCriticalPriority); - //connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); + connect(this, SIGNAL(setupTxAudio(audioSetup)), radio->txaudio, SLOT(init(audioSetup))); connect(radio->txAudioThread, SIGNAL(finished()), radio->txaudio, SLOT(deleteLater())); // Not sure how we make this work in QT5.9? @@ -372,10 +372,9 @@ void udpServer::controlReceived() radio->txaudio->init(radio->txAudioSetup); }, Qt::QueuedConnection); #else - #warning "QT 5.9 is not fully supported" + emit setupTxAudio(radio->txAudioSetup) + #warning "QT 5.9 is not fully supported multiple rigs will NOT work!" #endif - - emit setupTxAudio(outAudio); hasTxAudio = datagram.senderAddress(); connect(this, SIGNAL(haveAudioData(audioPacket)), radio->txaudio, SLOT(incomingAudio(audioPacket))); @@ -404,6 +403,7 @@ void udpServer::controlReceived() radio->rxAudioThread->start(QThread::TimeCriticalPriority); + connect(this, SIGNAL(setupRxAudio(audioSetup)), radio->rxaudio, SLOT(init(audioSetup))); connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater())); #if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) @@ -411,13 +411,14 @@ void udpServer::controlReceived() radio->rxaudio->init(radio->rxAudioSetup); }, Qt::QueuedConnection); #else - #warning "QT 5.9 is not fully supported" + #warning "QT 5.9 is not fully supported multiple rigs will NOT work!" + setupRxAudio(radio->rxAudioSetup); #endif radio->rxAudioTimer = new QTimer(); radio->rxAudioTimer->setTimerType(Qt::PreciseTimer); connect(radio->rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); - radio->rxAudioTimer->start(TXAUDIO_PERIOD); + radio->rxAudioTimer->start(AUDIO_PERIOD); } } @@ -578,13 +579,13 @@ void udpServer::civReceived() if (!memcmp(radio->guid, current->guid, sizeof(radio->guid))) { // Only send to the rig that it belongs to! - #if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) QMetaObject::invokeMethod(radio->rig, [=]() { radio->rig->dataFromServer(r.mid(0x15));; }, Qt::DirectConnection); #else - #warning "QT 5.9 is not fully supported" + #warning "QT 5.9 is not fully supported, multiple rigs will NOT work!" + emit haveDataFromServer(r.mid(0x15)); #endif } @@ -1714,13 +1715,13 @@ void udpServer::sendRetransmitRequest(CLIENT* c) QByteArray missingSeqs; - QTime missingTime = QTime::currentTime(); + //QTime missingTime = QTime::currentTime(); if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it) { - if (&it != Q_NULLPTR && it.value() < 4) + if (&it.key() != Q_NULLPTR && it.value() < 4) { missingSeqs.append(it.key() & 0xff); missingSeqs.append(it.key() >> 8 & 0xff); diff --git a/wfmain.cpp b/wfmain.cpp index 48af1df..d2955e7 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1139,14 +1139,26 @@ void wfmain::setAudioDevicesUI() // Enumerate audio devices, need to do before settings are loaded. const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { - ui->audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); +#ifdef Q_OS_WIN + if (deviceInfo.realm() == "wasapi") { +#endif + ui->audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); + ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); +#ifdef Q_OS_WIN + } +#endif } const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (const QAudioDeviceInfo& deviceInfo : audioInputs) { - ui->audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - ui->serverRXAudioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); +#ifdef Q_OS_WIN + if (deviceInfo.realm() == "wasapi") { +#endif + ui->audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); + ui->serverRXAudioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); +#ifdef Q_OS_WIN + } +#endif } // Set these to default audio devices initially. rxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); From 156d03706aec8083cffe331c44936e9cc183ee39 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 30 Jan 2022 10:52:40 +0000 Subject: [PATCH 108/323] Update udpserver.cpp --- udpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udpserver.cpp b/udpserver.cpp index c983033..31ee926 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -372,7 +372,7 @@ void udpServer::controlReceived() radio->txaudio->init(radio->txAudioSetup); }, Qt::QueuedConnection); #else - emit setupTxAudio(radio->txAudioSetup) + emit setupTxAudio(radio->txAudioSetup); #warning "QT 5.9 is not fully supported multiple rigs will NOT work!" #endif hasTxAudio = datagram.senderAddress(); From 21a8bbc1a62aec595cca0b4b8fe7b9ddf5a46776 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 30 Jan 2022 10:55:12 +0000 Subject: [PATCH 109/323] Qt 5.9 compatibility --- servermain.cpp | 3 ++- wfmain.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/servermain.cpp b/servermain.cpp index ea3d29d..c354aee 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -535,8 +535,9 @@ void servermain::loadSettings() guid = QUuid::createUuid().toString(); settings->setValue("GUID", guid); } +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) memcpy(tempPrefs->guid, QUuid::fromString(guid).toRfc4122().constData(), GUIDLEN); - +#endif tempPrefs->rxAudioSetup.isinput = true; tempPrefs->txAudioSetup.isinput = false; tempPrefs->rxAudioSetup.localAFgain = 255; diff --git a/wfmain.cpp b/wfmain.cpp index d2955e7..0931669 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1693,7 +1693,9 @@ void wfmain::loadSettings() guid = QUuid::createUuid().toString(); settings->setValue("GUID", guid); } +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) memcpy(rigTemp->guid, QUuid::fromString(guid).toRfc4122().constData(), GUIDLEN); +#endif ui->serverRXAudioInputCombo->blockSignals(true); rigTemp->rxAudioSetup.name = settings->value("ServerAudioInput", "").toString(); From 824a91b1258707ba00f86b50957ea4676d519aa9 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 2 Feb 2022 16:02:40 +0000 Subject: [PATCH 110/323] Few more fixes --- servermain.cpp | 18 +++++++++--------- udpserver.cpp | 8 ++++---- udpserver.h | 2 ++ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index c354aee..0cf2795 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -403,7 +403,7 @@ void servermain::setServerToPrefs() } } - connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(receiveStatusUpdate(QString))); + connect(udp, SIGNAL(haveNetworkStatus(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); serverThread->start(); @@ -554,14 +554,14 @@ void servermain::loadSettings() for (unsigned int i = 1; i < devices; i++) { info = audio->getDeviceInfo(i); if (info.outputChannels > 0) { - if (tempPrefs->txAudio.name == info->name) { - tempPrefs->txAudio.port = i; + if (tempPrefs->txAudioSetup.name == info->name) { + tempPrefs->txAudioSetup.port = i; txDeviceFound = true; } } if (info.inputChannels > 0) { - if (tempPrefs->rxAudio.name == info->name) { - tempPrefs->rxAudio.port = i; + if (tempPrefs->rxAudioSetup.name == info->name) { + tempPrefs->rxAudioSetup.port = i; rxDeviceFound = true; } } @@ -571,14 +571,14 @@ void servermain::loadSettings() { info = Pa_GetDeviceInfo(i); if (info->maxInputChannels > 0) { - if (tempPrefs->txAudio.name == info->name) { - tempPrefs->txAudio.port = i; + if (tempPrefs->txAudioSetup.name == info->name) { + tempPrefs->txAudioSetup.port = i; txDeviceFound = true; } } if (info->maxOutputChannels > 0) { - if (tempPrefs->rxAudio.name == info->name) { - tempPrefs->rxAudio.port = i; + if (tempPrefs->rxAudioSetup.name == info->name) { + tempPrefs->rxAudioSetup.port = i; rxDeviceFound = true; } } diff --git a/udpserver.cpp b/udpserver.cpp index 31ee926..03fd882 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -122,9 +122,8 @@ udpServer::~udpServer() udpAudio->close(); delete udpAudio; } - - //emit haveNetworkStatus(QString("")); - + status.message = QString(""); + emit haveNetworkStatus(status); } @@ -1455,7 +1454,8 @@ void udpServer::watchdog() } } - //emit haveNetworkStatus(QString("
Server connections: Control:%1 CI-V:%2 Audio:%3
").arg(controlClients.size()).arg(civClients.size()).arg(audioClients.size())); + status.message = QString("
Server connections: Control:%1 CI-V:%2 Audio:%3
").arg(controlClients.size()).arg(civClients.size()).arg(audioClients.size()); + emit haveNetworkStatus(status); } void udpServer::sendStatus(CLIENT* c) diff --git a/udpserver.h b/udpserver.h index 9f769df..ce5fa88 100644 --- a/udpserver.h +++ b/udpserver.h @@ -220,6 +220,8 @@ private: QHostAddress hasTxAudio; QTimer* wdTimer; + + networkStatus status; }; From feb5d13d135f4fe590d34a50122f5a8db152eea6 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 2 Feb 2022 16:07:03 +0000 Subject: [PATCH 111/323] Add some debugging to find audio/serial ports --- servermain.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/servermain.cpp b/servermain.cpp index 0cf2795..76a8dec 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -523,7 +523,7 @@ void servermain::loadSettings() { foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) { - //qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); + qDebug(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); if (serialPortInfo.portName() == tempPrefs->serialPort && !serialPortInfo.serialNumber().isEmpty()) { tempPrefs->rigName = serialPortInfo.serialNumber(); @@ -591,6 +591,7 @@ void servermain::loadSettings() //qInfo(logAudio()) << "Looking for audio output devices"; for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { + qDebug(logSystem()) << "Found Audio output: " << deviceInfo.deviceName(); if (deviceInfo.deviceName() == tempPrefs->txAudioSetup.name #ifdef Q_OS_WIN && deviceInfo.realm() == "wasapi" @@ -604,6 +605,7 @@ void servermain::loadSettings() //qInfo(logAudio()) << "Looking for audio input devices"; for (const QAudioDeviceInfo& deviceInfo : audioInputs) { + qDebug(logSystem()) << "Found Audio input: " << deviceInfo.deviceName(); if (deviceInfo.deviceName() == tempPrefs->rxAudioSetup.name #ifdef Q_OS_WIN && deviceInfo.realm() == "wasapi" From 468cfa13034db29a7edd44d0cc742169b3742c0e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 2 Feb 2022 16:26:40 +0000 Subject: [PATCH 112/323] Add metahandler --- servermain.cpp | 1 + servermain.h | 1 + 2 files changed, 2 insertions(+) diff --git a/servermain.cpp b/servermain.cpp index 76a8dec..488515f 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -29,6 +29,7 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q qRegisterMetaType (); qRegisterMetaType(); qRegisterMetaType>(); + qRegisterMetaType(); setDefPrefs(); diff --git a/servermain.h b/servermain.h index 6e5fcc5..c9639cc 100644 --- a/servermain.h +++ b/servermain.h @@ -287,6 +287,7 @@ Q_DECLARE_METATYPE(struct audioSetup) Q_DECLARE_METATYPE(struct SERVERCONFIG) Q_DECLARE_METATYPE(struct timekind) Q_DECLARE_METATYPE(struct datekind) +Q_DECLARE_METATYPE(struct networkStatus) Q_DECLARE_METATYPE(enum rigInput) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(enum meterKind) From 38274cc3c8a70ac0b65f27d2a57a35b059929754 Mon Sep 17 00:00:00 2001 From: Philip Kubat Date: Mon, 7 Feb 2022 20:31:29 -0500 Subject: [PATCH 113/323] Added IC-746 --- rigcommander.cpp | 28 ++++++++++++++++++++++++++++ rigidentities.cpp | 7 +++++-- rigidentities.h | 1 + 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/rigcommander.cpp b/rigcommander.cpp index d497c0d..feea2e1 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -3492,6 +3492,34 @@ void rigCommander::determineRigCaps() createMode(modeCW, 0x03, "CW"), createMode(modeCW_R, 0x07, "CW-R"), }; break; + case model746: + rigCaps.modelName = QString("IC-746"); + rigCaps.rigctlModel = 3023; + rigCaps.hasSpectrum = false; + rigCaps.inputs.clear(); + rigCaps.hasLan = false; + rigCaps.hasEthernet = false; + rigCaps.hasWiFi = false; + rigCaps.hasFDcomms = false; + rigCaps.hasATU = true; + rigCaps.hasTBPF = true; + rigCaps.hasIFShift = true; + rigCaps.hasCTCSS = true; + rigCaps.hasDTCS = true; + rigCaps.hasAntennaSel = true; + rigCaps.preamps.push_back('\x01'); + rigCaps.preamps.push_back('\x02'); + rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x20'}); + // There are two HF and VHF ant, 12-01 adn 12-02 select the HF, the VHF is auto selected + // this incorrectly shows up as 2 and 3 in the drop down. + rigCaps.antennas = {0x01, 0x02}; + rigCaps.bands = standardHF; + rigCaps.bands.push_back(band2m); + rigCaps.bands.push_back(bandGen); + rigCaps.modes = commonModes; + rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); + break; + case model756pro: rigCaps.modelName = QString("IC-756 Pro"); rigCaps.rigctlModel = 3027; diff --git a/rigidentities.cpp b/rigidentities.cpp index a48a44d..1538429 100644 --- a/rigidentities.cpp +++ b/rigidentities.cpp @@ -58,8 +58,8 @@ model_kind determineRadioModel(unsigned char rigID) case model736: rig = model736; break; - case model910h: - rig = model910h; + case model746: + rig = model746; break; case model756pro: rig = model756pro; @@ -70,6 +70,9 @@ model_kind determineRadioModel(unsigned char rigID) case model756proiii: rig = model756proiii; break; + case model910h: + rig = model910h; + break; case model9100: rig = model9100; break; diff --git a/rigidentities.h b/rigidentities.h index cae1b75..3f6fa6d 100644 --- a/rigidentities.h +++ b/rigidentities.h @@ -30,6 +30,7 @@ enum model_kind { model706 = 0x58, model718 = 0x5E, model736 = 0x40, + model746 = 0x56, model756pro = 0x5C, model756proii = 0x64, model756proiii = 0x6E, From ece5933d55c62a34c6d49168f9e2ebd7f0824ad7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 18 Mar 2022 15:41:11 +0000 Subject: [PATCH 114/323] Some extra debugging --- audiohandler.cpp | 32 ++++++++++++++++++++++---------- udphandler.cpp | 5 +---- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 53ff131..c9be30c 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -98,7 +98,7 @@ bool audioHandler::init(audioSetup setupIn) ", uLaw" << setup.ulaw; - ringBuf = new wilt::Ring(setup.latency / 20 + 1); // Should be customizable. + ringBuf = new wilt::Ring(setup.latency + 1); // Should be customizable. tempBuf.sent = 0; @@ -390,7 +390,8 @@ void audioHandler::start() #ifndef Q_OS_WIN this->open(QIODevice::WriteOnly); #else - this->open(QIODevice::WriteOnly | QIODevice::Unbuffered); + this->open(QIODevice::WriteOnly); + //this->open(QIODevice::WriteOnly | QIODevice::Unbuffered); #endif audioInput->start(this); } @@ -398,7 +399,8 @@ void audioHandler::start() #ifndef Q_OS_WIN this->open(QIODevice::ReadOnly); #else - this->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + //this->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + this->open(QIODevice::ReadOnly); #endif audioOutput->start(this); } @@ -464,7 +466,6 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) if (ringBuf->size()>0) { // Output buffer is ALWAYS 16 bit. - //qDebug(logAudio()) << "Read: nFrames" << nFrames << "nBytes" << nBytes; while (sentlen < nBytes) { if (!ringBuf->try_read(packet)) @@ -472,6 +473,7 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "buffer is empty, sentlen:" << sentlen << " nBytes:" << nBytes ; break; } + //qDebug(logAudio()) << "Packet size:" << packet.data.length() << "nBytes (requested)" << nBytes << "remaining" << nBytes-sentlen; currentLatency = packet.time.msecsTo(QTime::currentTime()); // This shouldn't be required but if we did output a partial packet @@ -509,29 +511,29 @@ qint64 audioHandler::readData(char* buffer, qint64 nBytes) break; } - /* + if (packet.seq <= lastSeq) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Duplicate/early audio packet: " << hex << lastSeq << " got " << hex << packet.seq; } else if (packet.seq != lastSeq + 1) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Missing audio packet(s) from: " << hex << lastSeq + 1 << " to " << hex << packet.seq - 1; } - */ + lastSeq = packet.seq; } } - //qDebug(logAudio()) << "looking for: " << nBytes << " got: " << sentlen; // fill the rest of the buffer with silence if (nBytes > sentlen) { + qDebug(logAudio()) << "looking for: " << nBytes << " got: " << sentlen; memset(buffer + sentlen, 0, nBytes - sentlen); } if (delayedPackets > 10) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Too many delayed packets, flushing buffer"; - while (ringBuf->try_read(packet)); // Empty buffer + //while (ringBuf->try_read(packet)); // Empty buffer delayedPackets = 0; - audioBuffered = false; + //audioBuffered = false; } #if defined(RTAUDIO) @@ -610,6 +612,11 @@ void audioHandler::incomingAudio(audioPacket inPacket) // Regardless of the radio stream format, the buffered audio will ALWAYS be // 16bit sample interleaved stereo 48K (or whatever the native sample rate is) + if (inPacket.time.msecsTo(QTime::currentTime()) > 20) + { + qInfo(logUdp()) << "Audio took" << inPacket.time.msecsTo(QTime::currentTime()) << "ms to arrive!"; + } + if (!isInitialized && !isReady) { qDebug(logAudio()) << "Packet received when stream was not ready"; @@ -757,6 +764,11 @@ void audioHandler::incomingAudio(audioPacket inPacket) incomingAudio(inPacket); // Call myself again to run the packet a second time (FEC) } lastSentSeq = inPacket.seq; + //if (inPacket.time.msecsTo(QTime::currentTime()) > 20) + //{ + //qDebug(logAudio()) << "After processing, audio took" << inPacket.time.msecsTo(QTime::currentTime()) << "ms to arrive!"; + //} + return; } @@ -766,7 +778,7 @@ void audioHandler::changeLatency(const quint16 newSize) setup.latency = newSize; delete ringBuf; audioBuffered = false; - ringBuf = new wilt::Ring(setup.latency / 20 + 1); // Should be customizable. + ringBuf = new wilt::Ring(setup.latency + 1); // Should be customizable. } int audioHandler::getLatency() diff --git a/udphandler.cpp b/udphandler.cpp index 0f6f6c3..ee8a954 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1261,15 +1261,12 @@ void udpBase::dataReceived(QByteArray r) { // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. - if (in->seq > rxSeqBuf.lastKey() + 1) { // We are likely missing packets then! missingMutex.lock(); //int missCounter = 0; for (quint16 f = rxSeqBuf.lastKey() + 1; f < in->seq; f++) { - //qDebug(logUdp()) << "Detected missing packet" << f; - if (rxSeqBuf.size() > BUFSIZE) { rxSeqBuf.erase(rxSeqBuf.begin()); @@ -1367,7 +1364,7 @@ void udpBase::sendRetransmitRequest() } else { - qDebug(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(); + qDebug(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':'); missingMutex.lock(); missingSeqs.insert(0, p.packet, sizeof(p.packet)); missingMutex.unlock(); From 72663310c5ddfadcf8aec1477e9488678f069edd Mon Sep 17 00:00:00 2001 From: Russ Woodman - K5TUX Date: Sat, 19 Mar 2022 14:05:46 -0500 Subject: [PATCH 115/323] Fixed broken implementation of "set_level RFPOWER" in rigctld --- rigcommander.cpp | 6 +++--- rigctld.cpp | 6 +++++- rigstate.h | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/rigcommander.cpp b/rigcommander.cpp index d497c0d..e53a2d6 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -1407,7 +1407,7 @@ void rigCommander::parseLevels() case '\x0A': // TX RF level emit haveTxPower(level); - state.set(TXPOWER, level, false); + state.set(RFPOWER, level, false); break; case '\x0B': // Mic Gain @@ -4397,9 +4397,9 @@ void rigCommander::stateUpdated() } getSql(); break; - case TXPOWER: + case RFPOWER: if (i.value()._valid) { - setTxPower(state.getChar(TXPOWER)); + setTxPower(state.getChar(RFPOWER)); } getTxLevel(); break; diff --git a/rigctld.cpp b/rigctld.cpp index e7c8c46..a3eadae 100644 --- a/rigctld.cpp +++ b/rigctld.cpp @@ -710,7 +710,7 @@ void rigCtlClient::socketReadyRead() resp.append(QString("%1").arg((float)rigState->getChar(ANTIVOXGAIN) / 255.0)); } else if (command[1] == "RFPOWER") { - resp.append(QString("%1").arg((float)rigState->getChar(TXPOWER) / 255.0)); + resp.append(QString("%1").arg((float)rigState->getChar(RFPOWER) / 255.0)); } else if (command[1] == "PREAMP") { resp.append(QString("%1").arg(rigState->getChar(PREAMP)*10)); @@ -736,6 +736,10 @@ void rigCtlClient::socketReadyRead() value = command[2].toFloat() * 255; rigState->set(RFGAIN, value, true); } + else if (command[1] == "RFPOWER") { + value = command[2].toFloat() * 255; + rigState->set(RFPOWER, value, true); + } else if (command[1] == "SQL") { value = command[2].toFloat() * 255; rigState->set(SQUELCH, value, true); diff --git a/rigstate.h b/rigstate.h index 8722f9e..13c66cf 100644 --- a/rigstate.h +++ b/rigstate.h @@ -13,7 +13,7 @@ // Meters at the end as they are ALWAYS updated from the rig! enum stateTypes { VFOAFREQ, VFOBFREQ, CURRENTVFO, PTT, MODE, FILTER, DUPLEX, DATAMODE, ANTENNA, RXANTENNA, CTCSS, TSQL, DTCS, CSQL, - PREAMP, AGC, ATTENUATOR, MODINPUT, AFGAIN, RFGAIN, SQUELCH, TXPOWER, MICGAIN, COMPLEVEL, MONITORLEVEL, VOXGAIN, ANTIVOXGAIN, + PREAMP, AGC, ATTENUATOR, MODINPUT, AFGAIN, RFGAIN, SQUELCH, RFPOWER, MICGAIN, COMPLEVEL, MONITORLEVEL, VOXGAIN, ANTIVOXGAIN, FAGCFUNC, NBFUNC, COMPFUNC, VOXFUNC, TONEFUNC, TSQLFUNC, SBKINFUNC, FBKINFUNC, ANFFUNC, NRFUNC, AIPFUNC, APFFUNC, MONFUNC, MNFUNC,RFFUNC, AROFUNC, MUTEFUNC, VSCFUNC, REVFUNC, SQLFUNC, ABMFUNC, BCFUNC, MBCFUNC, RITFUNC, AFCFUNC, SATMODEFUNC, SCOPEFUNC, NBLEVEL, NBDEPTH, NBWIDTH, NRLEVEL, RIGINPUT, POWERONOFF, RITVALUE, @@ -124,4 +124,4 @@ private: QMutex _mutex; }; -#endif \ No newline at end of file +#endif From bfd9ddea52a2e92976239f928fa954a2792b354a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 23 Mar 2022 10:12:42 +0000 Subject: [PATCH 116/323] Initial tcpserver support (needs work) --- rigcommander.cpp | 17 +++++++++++++ rigcommander.h | 2 ++ tcpserver.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++ tcpserver.h | 26 ++++++++++++++++++++ wfview.pro | 2 ++ wfview.vcxproj | 21 +++++++++++++--- wfview.vcxproj.filters | 8 ++++++ 7 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 tcpserver.cpp create mode 100644 tcpserver.h diff --git a/rigcommander.cpp b/rigcommander.cpp index 4ece2ea..f545979 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -61,13 +61,20 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu comm = new commHandler(rigSerialPort, rigBaudRate); ptty = new pttyHandler(vsp); + tcp = new tcpServer(); + // data from the comm port to the program: connect(comm, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray))); // data from the ptty to the rig: connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray))); + + // data from the tcp port to the rig: + connect(tcp, SIGNAL(readyRead(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray))); + // data from the program to the comm port: connect(this, SIGNAL(dataForComm(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray))); + connect(this, SIGNAL(dataForComm(QByteArray)), tcp, SLOT(dataToPort(QByteArray))); connect(this, SIGNAL(toggleRTS(bool)), comm, SLOT(setRTS(bool))); // data from the rig to the ptty: @@ -100,6 +107,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud setup(); // --- + if (udp == Q_NULLPTR) { udp = new udpHandler(prefs,rxSetup,txSetup); @@ -120,12 +128,18 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud ptty = new pttyHandler(vsp); + tcp = new tcpServer(this); + // Data from UDP to the program connect(udp, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray))); // data from the rig to the ptty: connect(udp, SIGNAL(haveDataFromPort(QByteArray)), ptty, SLOT(receiveDataFromRigToPtty(QByteArray))); + // data from the rig to tcp: + connect(udp, SIGNAL(haveDataFromPort(QByteArray)), tcp, SLOT(dataToPort(QByteArray))); + + // Audio from UDP connect(udp, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); @@ -135,6 +149,9 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud // data from the ptty to the rig: connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); + // data from the tcp port to the Rig: + connect(tcp, SIGNAL(haveDataFromPort(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); + connect(this, SIGNAL(haveChangeLatency(quint16)), udp, SLOT(changeLatency(quint16))); connect(this, SIGNAL(haveSetVolume(unsigned char)), udp, SLOT(setVolume(unsigned char))); connect(udp, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); diff --git a/rigcommander.h b/rigcommander.h index 00754a2..dc10f40 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -12,6 +12,7 @@ #include "rigidentities.h" #include "repeaterattributes.h" #include "freqmemory.h" +#include "tcpserver.h" #include "rigstate.h" @@ -425,6 +426,7 @@ private: commHandler* comm = Q_NULLPTR; pttyHandler* ptty = Q_NULLPTR; + tcpServer* tcp = Q_NULLPTR; udpHandler* udp=Q_NULLPTR; QThread* udpHandlerThread = Q_NULLPTR; diff --git a/tcpserver.cpp b/tcpserver.cpp new file mode 100644 index 0000000..85c0c4a --- /dev/null +++ b/tcpserver.cpp @@ -0,0 +1,56 @@ +#include "tcpserver.h" + +#include "logcategories.h" + +tcpServer::tcpServer(QObject* parent) : QObject(parent) +{ + server = new QTcpServer(this); + connect(server, SIGNAL(newConnection()), this, SLOT(newConnection())); + if (!server->listen(QHostAddress::Any, 5010)) + { + qDebug() << "TCP Server could not start"; + } + else + { + qDebug() << "TCP Server started!"; + } +} + +tcpServer::~tcpServer() +{ + if (socket != Q_NULLPTR) + { + socket->close(); + delete socket; + } + server->close(); + delete server; + +} + +void tcpServer::newConnection() +{ + qDebug(logSystem()) << QString("Incoming Connection"); + socket = server->nextPendingConnection(); + connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); +} + +void tcpServer::readyRead() { + QByteArray data; + if (socket->bytesAvailable()) { + data=socket->readAll(); + emit haveDataFromPort(data); + } + //qDebug(logSystem()) << QString("Data IN!"); + +} + + +void tcpServer::dataToPort(QByteArray data) { + //qDebug(logSystem()) << QString("TCP Send"); + + if (socket != Q_NULLPTR) { + socket->write(data); + socket->flush(); + } +} \ No newline at end of file diff --git a/tcpserver.h b/tcpserver.h new file mode 100644 index 0000000..2b17730 --- /dev/null +++ b/tcpserver.h @@ -0,0 +1,26 @@ +#ifndef TCPSERVER_H +#define TCPSERVER_H + +#include +#include +#include +#include +class tcpServer : + public QObject +{ + Q_OBJECT +public: + explicit tcpServer(QObject* parent = 0); + ~tcpServer(); +public slots: + void dataToPort(QByteArray data); + void readyRead(); + void newConnection(); +signals: + void haveDataFromPort(QByteArray data); // emit this when we have data, connect to rigcommander +private: + QTcpServer* server; + QTcpSocket* socket = Q_NULLPTR; +}; + +#endif \ No newline at end of file diff --git a/wfview.pro b/wfview.pro index 9128711..7fc84cf 100644 --- a/wfview.pro +++ b/wfview.pro @@ -164,6 +164,7 @@ SOURCES += main.cpp\ ring/ring.cpp \ transceiveradjustments.cpp \ selectradio.cpp \ + tcpserver.cpp \ aboutbox.cpp HEADERS += wfmain.h \ @@ -192,6 +193,7 @@ HEADERS += wfmain.h \ transceiveradjustments.h \ audiotaper.h \ selectradio.h \ + tcpserver.h \ aboutbox.h diff --git a/wfview.vcxproj b/wfview.vcxproj index 37ea711..ac20f37 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="41e90bb";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ece5933";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -85,7 +85,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"41e90bb\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ece5933\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -99,7 +99,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="41e90bb";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ece5933";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -124,7 +124,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"41e90bb\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ece5933\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -147,6 +147,7 @@ + @@ -293,6 +294,16 @@ + + + + + + + + + + @@ -394,6 +405,8 @@ + + diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index a756bf0..bc556d8 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -104,6 +104,9 @@ Source Files + + Source Files + Source Files @@ -187,6 +190,9 @@ Header Files + + Header Files + Header Files @@ -252,6 +258,8 @@ + + From c5cf0fdf57ccf2f4419ff0d1e45f68e77224e7b4 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 23 Mar 2022 13:19:05 +0000 Subject: [PATCH 117/323] Slightly better tcp server implementation (still needs UI adding) --- logcategories.cpp | 1 + logcategories.h | 1 + rigcommander.cpp | 5 ++- tcpserver.cpp | 100 ++++++++++++++++++++++++++++++++-------------- tcpserver.h | 54 ++++++++++++++++++++----- 5 files changed, 121 insertions(+), 40 deletions(-) diff --git a/logcategories.cpp b/logcategories.cpp index 96d0a97..5719ef1 100644 --- a/logcategories.cpp +++ b/logcategories.cpp @@ -8,3 +8,4 @@ Q_LOGGING_CATEGORY(logAudio, "audio") Q_LOGGING_CATEGORY(logUdp, "udp") Q_LOGGING_CATEGORY(logUdpServer, "udp.server") Q_LOGGING_CATEGORY(logRigCtlD, "rigctld") +Q_LOGGING_CATEGORY(logTcpServer, "tcpserver") diff --git a/logcategories.h b/logcategories.h index 2bd780b..5f7362a 100644 --- a/logcategories.h +++ b/logcategories.h @@ -11,6 +11,7 @@ Q_DECLARE_LOGGING_CATEGORY(logAudio) Q_DECLARE_LOGGING_CATEGORY(logUdp) Q_DECLARE_LOGGING_CATEGORY(logUdpServer) Q_DECLARE_LOGGING_CATEGORY(logRigCtlD) +Q_DECLARE_LOGGING_CATEGORY(logTcpServer) #if defined(Q_OS_WIN) && !defined(__PRETTY_FUNCTION__) diff --git a/rigcommander.cpp b/rigcommander.cpp index f545979..d1132ee 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -129,6 +129,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud ptty = new pttyHandler(vsp); tcp = new tcpServer(this); + tcp->startServer(5010); // Data from UDP to the program connect(udp, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray))); @@ -137,7 +138,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud connect(udp, SIGNAL(haveDataFromPort(QByteArray)), ptty, SLOT(receiveDataFromRigToPtty(QByteArray))); // data from the rig to tcp: - connect(udp, SIGNAL(haveDataFromPort(QByteArray)), tcp, SLOT(dataToPort(QByteArray))); + connect(udp, SIGNAL(haveDataFromPort(QByteArray)), tcp, SLOT(sendData(QByteArray))); // Audio from UDP @@ -150,7 +151,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); // data from the tcp port to the Rig: - connect(tcp, SIGNAL(haveDataFromPort(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); + connect(tcp, SIGNAL(haveData(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); connect(this, SIGNAL(haveChangeLatency(quint16)), udp, SLOT(changeLatency(quint16))); connect(this, SIGNAL(haveSetVolume(unsigned char)), udp, SLOT(setVolume(unsigned char))); diff --git a/tcpserver.cpp b/tcpserver.cpp index 85c0c4a..d1fee65 100644 --- a/tcpserver.cpp +++ b/tcpserver.cpp @@ -2,55 +2,97 @@ #include "logcategories.h" -tcpServer::tcpServer(QObject* parent) : QObject(parent) +tcpServer::tcpServer(QObject* parent) : QTcpServer(parent) { - server = new QTcpServer(this); - connect(server, SIGNAL(newConnection()), this, SLOT(newConnection())); - if (!server->listen(QHostAddress::Any, 5010)) - { - qDebug() << "TCP Server could not start"; - } - else - { - qDebug() << "TCP Server started!"; - } } tcpServer::~tcpServer() { - if (socket != Q_NULLPTR) - { - socket->close(); - delete socket; + qInfo(logTcpServer()) << "closing tcpServer"; +} + +int tcpServer::startServer(qint16 port) { + + if (!this->listen(QHostAddress::Any, port)) { + qInfo(logTcpServer()) << "could not start on port " << port; + return -1; + } + else + { + qInfo(logTcpServer()) << "started on port " << port; } - server->close(); - delete server; + return 0; } -void tcpServer::newConnection() +void tcpServer::incomingConnection(qintptr socket) { + tcpServerClient* client = new tcpServerClient(socket, this); + connect(this, SIGNAL(onStopped()), client, SLOT(closeSocket())); +} + +void tcpServer::stopServer() { - qDebug(logSystem()) << QString("Incoming Connection"); - socket = server->nextPendingConnection(); - connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); + qInfo(logTcpServer()) << "stopping server"; + emit onStopped(); } -void tcpServer::readyRead() { + +void tcpServer::receiveDataFromClient(QByteArray data) +{ + emit haveData(data); +} + +void tcpServer::sendData(QByteArray data) { + + emit sendDataToClient(data); + +} + +tcpServerClient::tcpServerClient(int socketId, tcpServer* parent) : QObject(parent) +{ + sessionId = socketId; + socket = new QTcpSocket(this); + this->parent = parent; + if (!socket->setSocketDescriptor(sessionId)) + { + qInfo(logTcpServer()) << " error binding socket: " << sessionId; + return; + } + connect(socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead()), Qt::DirectConnection); + connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()), Qt::DirectConnection); + connect(parent, SIGNAL(sendDataToClient(QByteArray)), this, SLOT(receiveDataToClient(QByteArray)), Qt::DirectConnection); + connect(this, SIGNAL(sendDataFromClient(QByteArray)), parent, SLOT(receiveDataFromClient(QByteArray)), Qt::DirectConnection); + qInfo(logTcpServer()) << " session connected: " << sessionId; + +} + +void tcpServerClient::socketReadyRead() { QByteArray data; if (socket->bytesAvailable()) { data=socket->readAll(); - emit haveDataFromPort(data); + emit sendDataFromClient(data); } - //qDebug(logSystem()) << QString("Data IN!"); - } +void tcpServerClient::socketDisconnected() { + qInfo(logTcpServer()) << sessionId << "disconnected"; + socket->deleteLater(); + this->deleteLater(); +} -void tcpServer::dataToPort(QByteArray data) { - //qDebug(logSystem()) << QString("TCP Send"); +void tcpServerClient::closeSocket() +{ + socket->close(); +} - if (socket != Q_NULLPTR) { +void tcpServerClient::receiveDataToClient(QByteArray data) { + + if (socket != Q_NULLPTR && socket->isValid() && socket->isOpen()) + { socket->write(data); - socket->flush(); + } + else + { + qInfo(logTcpServer()) << "socket not open!"; } } \ No newline at end of file diff --git a/tcpserver.h b/tcpserver.h index 2b17730..0fb8fda 100644 --- a/tcpserver.h +++ b/tcpserver.h @@ -1,26 +1,62 @@ #ifndef TCPSERVER_H #define TCPSERVER_H -#include +#include +#include #include #include -#include -class tcpServer : - public QObject +#include +#include + +#include +#include +#include + +class tcpServer : public QTcpServer { Q_OBJECT + public: - explicit tcpServer(QObject* parent = 0); + explicit tcpServer(QObject* parent = Q_NULLPTR); ~tcpServer(); + int startServer(qint16 port); + void stopServer(); + public slots: - void dataToPort(QByteArray data); - void readyRead(); - void newConnection(); + virtual void incomingConnection(qintptr socketDescriptor); + void receiveDataFromClient(QByteArray data); + void sendData(QByteArray data); signals: - void haveDataFromPort(QByteArray data); // emit this when we have data, connect to rigcommander + void onStarted(); + void onStopped(); + void haveData(QByteArray data); // emit this when we have data from tcp client, connect to rigcommander + void sendDataToClient(QByteArray data); + private: QTcpServer* server; QTcpSocket* socket = Q_NULLPTR; }; +class tcpServerClient : public QObject +{ + Q_OBJECT + +public: + explicit tcpServerClient(int socket, tcpServer* parent = Q_NULLPTR); +public slots: + void socketReadyRead(); + void socketDisconnected(); + void closeSocket(); + void receiveDataToClient(QByteArray); + +signals: + void sendDataFromClient(QByteArray data); +protected: + int sessionId; + QTcpSocket* socket = Q_NULLPTR; + +private: + tcpServer* parent; +}; + #endif \ No newline at end of file From 32f438aa01bdbc5c56387dbbd4e566d6f30b5541 Mon Sep 17 00:00:00 2001 From: Daniele Forsi Date: Wed, 23 Mar 2022 15:45:51 +0000 Subject: [PATCH 118/323] Fix spelling errors --- CHANGELOG | 20 ++++++++++---------- README.md | 8 ++++---- USERMANUAL/wfview-1.0.txt | 2 +- audiohandler.cpp | 2 +- calibrationwindow.ui | 2 +- commhandler.cpp | 6 +++--- freqmemory.cpp | 2 +- main.cpp | 2 +- meter.cpp | 4 ++-- pttyhandler.cpp | 4 ++-- pttyhandler.h | 2 +- repeatersetup.cpp | 2 +- resampler/resample.c | 2 +- rigcommander.cpp | 6 +++--- rigidentities.cpp | 2 +- ring/ring.h | 6 +++--- udphandler.cpp | 2 +- udpserversetup.ui | 2 +- wfmain.cpp | 12 ++++++------ wfmain.ui | 8 ++++---- 20 files changed, 48 insertions(+), 48 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 22ac0c0..32189f9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -94,7 +94,7 @@ Added an IF Shift-like control for radios with Twin PBF. Added support for IF Shift and Twin Pass-Band Filters. Currently - accessable only via the debug button. + accessible only via the debug button. - 20211104 Added IC-736 FM mode @@ -494,7 +494,7 @@ Just helps keep the UI in sync with changes taking place at the rig. The polling is slow enough that it doesn't impact anything. But quick enough - that it catches discrepencies pretty quickly. + that it catches discrepancies pretty quickly. - 20210619 @@ -535,7 +535,7 @@ Changed collision detection code so that we can more easily see what message was missed. - Added collision detection for serial commands. Collisions are aparently + Added collision detection for serial commands. Collisions are apparently frequent for true 1-wire CI-V radios. We now calculate polling rates immediately upon receiveCommReady for @@ -689,7 +689,7 @@ - 20210601 - Stop deleting audio after last client disconects + Stop deleting audio after last client disconnects Change udpserver packet handling to be similar to udphandler @@ -807,7 +807,7 @@ Add debugging and fix silly error in audiooutput combobox - reenable audio buffers + re-enable audio buffers Attempt to fix crash @@ -830,7 +830,7 @@ Fix for stuttering audio on mac - Fixed missing break in switchs. + Fixed missing break in switches. Typo in message about CI-V @@ -866,7 +866,7 @@ Fixed issue where unknown rigs were identified as 0xff. All rigs are now identified using rigCaps.modelID, populated with the raw response from the Rig ID query. Enumerations of known rig types continue to fall into - rigCaps.model, and unknwn rigs will match to rigCaps.model=modelUnknown, + rigCaps.model, and unknown rigs will match to rigCaps.model=modelUnknown, as before. Serial baud rate is in the UI now. Added some enable/disable code to @@ -1055,7 +1055,7 @@ Additional bands added, Airband, WFM - added 630/2200m for 705; probably like the 7300 hardly useable for TX though. Also added auomatic switching to the view pane after non-BSR bands are selected + added 630/2200m for 705; probably like the 7300 hardly usable for TX though. Also added auomatic switching to the view pane after non-BSR bands are selected added 630/2200m to 7300/7610/785x @@ -1131,7 +1131,7 @@ - 20210411 Added ATU feature on 7610. Now grabs scope state on startup. Found bug, did not fix, in the - frequency parsing code. Worked aroud it by using doubles for now. + frequency parsing code. Worked around it by using doubles for now. - 20210410 Preamp and attenuator are now queried on startup. Additionally, the @@ -1258,7 +1258,7 @@ S-meter ballistics: Turned up the speed. Once you see fast meters, there's no going back. - Tested at 5ms without any issues, comitting at 10ms for now. Note that + Tested at 5ms without any issues, committing at 10ms for now. Note that 50ms in the first 'fixed' meter code is the same as 25ms now due to how the command queue is structured. So 10ms is only a bit faster than before. diff --git a/README.md b/README.md index 074905f..2e69d58 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Other models to be tested/added (including the IC-705).. website - [WFVIEW](https://wfview.org/) wfview.org -wfview supports viewing the spectrum display waterfall and most normal radio controls. Using wfview, the radio can be operated using the mouse, or just the keyboard (great for those with visual impairments), or even a touch screen display. The gorgous waterfall spectrum can be displayed on a monitor of any size, and can even projected onto a wall for a presentation. Even a VNC session can make use of wfview for interesting remote rig posibilities. wfview runs on humble hardware, ranging from the $35 Raspberry Pi, to laptops, to desktops. wfview is designed to run on GNU Linux, but can probably be adapted to run on other operating systems. In fact we do have working example in windows as well. +wfview supports viewing the spectrum display waterfall and most normal radio controls. Using wfview, the radio can be operated using the mouse, or just the keyboard (great for those with visual impairments), or even a touch screen display. The gorgous waterfall spectrum can be displayed on a monitor of any size, and can even projected onto a wall for a presentation. Even a VNC session can make use of wfview for interesting remote rig possibilities. wfview runs on humble hardware, ranging from the $35 Raspberry Pi, to laptops, to desktops. wfview is designed to run on GNU Linux, but can probably be adapted to run on other operating systems. In fact we do have working example in windows as well. @@ -29,16 +29,16 @@ wfview is copyright 2017-2020 Elliott H. Liggett. All rights reserved. wfview so 2. Double-click anywhere on the bandscope or waterfall to tune the radio. 3. Entry of frequency is permitted under the "Frequency" tab. Buttons are provided for touch-screen control 4. Bandscope parameters (span and mode) are adjustable. -5. Full [keyboard](https://gitlab.com/eliggett/wfview/-/wikis/Keystrokes) and mouse control. Operate in whichever way you like. Most radio functions can be operated from a numberic keypad! This also enables those with visual impairments to use the IC-7300. +5. Full [keyboard](https://gitlab.com/eliggett/wfview/-/wikis/Keystrokes) and mouse control. Operate in whichever way you like. Most radio functions can be operated from a numeric keypad! This also enables those with visual impairments to use the IC-7300. 6. 100 user memories stored in plain text on the computer 7. Stylable GUI using CSS 8. pseudo-terminal device, which allows for secondary program to control the radio while wfview is running -9. works for radios that support the ethernet interface with compareable waterfall speeds as on the radio itself. +9. works for radios that support the ethernet interface with comparable waterfall speeds as on the radio itself. ### Build Requirements: 1. gcc / g++ / make 2. qmake -3. qt5 (proably the package named "qt5-default") +3. qt5 (probably the package named "qt5-default") 4. libqt5serialport5-dev 5. libqcustomplot-dev diff --git a/USERMANUAL/wfview-1.0.txt b/USERMANUAL/wfview-1.0.txt index 75049df..2dd8b6c 100644 --- a/USERMANUAL/wfview-1.0.txt +++ b/USERMANUAL/wfview-1.0.txt @@ -42,7 +42,7 @@ on/off switch for the scope/waterfall wf theme Currently fixed selections how the scope and waterfall look like -with colors. At som epoint we may add the ability to accept the +with colors. At some point we may add the ability to accept the RGB values like the rig does. diff --git a/audiohandler.cpp b/audiohandler.cpp index d0381fa..5b02386 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -1,5 +1,5 @@ /* - This class handles both RX and TX audio, each is created as a seperate instance of the class + This class handles both RX and TX audio, each is created as a separate instance of the class but as the setup/handling if output (RX) and input (TX) devices is so similar I have combined them. */ diff --git a/calibrationwindow.ui b/calibrationwindow.ui index bb68a7e..5d8228f 100644 --- a/calibrationwindow.ui +++ b/calibrationwindow.ui @@ -173,7 +173,7 @@ false
- <html><head/><body><p>Load the calibration data fromthe indicated slot in the preference file. </p></body></html> + <html><head/><body><p>Load the calibration data from the indicated slot in the preference file. </p></body></html> Load diff --git a/commhandler.cpp b/commhandler.cpp index e678317..950e7e7 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -3,7 +3,7 @@ #include -// Copytight 2017-2020 Elliott H. Liggett +// Copyright 2017-2020 Elliott H. Liggett commHandler::commHandler() { @@ -182,13 +182,13 @@ void commHandler::receiveDataIn() if(rolledBack) { - // qInfo(logSerial()) << "Rolled back and was successfull. Length: " << inPortData.length(); + // qInfo(logSerial()) << "Rolled back and was successful. Length: " << inPortData.length(); //printHex(inPortData, false, true); rolledBack = false; } } else { // did not receive the entire thing so roll back: - // qInfo(logSerial()) << "Rolling back transaction. End not detected. Lenth: " << inPortData.length(); + // qInfo(logSerial()) << "Rolling back transaction. End not detected. Length: " << inPortData.length(); //printHex(inPortData, false, true); port->rollbackTransaction(); rolledBack = true; diff --git a/freqmemory.cpp b/freqmemory.cpp index 9a137c0..660a8f8 100644 --- a/freqmemory.cpp +++ b/freqmemory.cpp @@ -1,7 +1,7 @@ #include "freqmemory.h" #include "logcategories.h" -// Copytight 2017-2020 Elliott H. Liggett +// Copyright 2017-2020 Elliott H. Liggett freqMemory::freqMemory() { diff --git a/main.cpp b/main.cpp index bae6e35..78b7997 100644 --- a/main.cpp +++ b/main.cpp @@ -3,7 +3,7 @@ #include "wfmain.h" #include "logcategories.h" -// Copytight 2017-2021 Elliott H. Liggett +// Copyright 2017-2021 Elliott H. Liggett // Smart pointer to log file QScopedPointer m_logFile; diff --git a/meter.cpp b/meter.cpp index f316a6b..bc606ef 100644 --- a/meter.cpp +++ b/meter.cpp @@ -51,7 +51,7 @@ meter::meter(QWidget *parent) : QWidget(parent) void meter::clearMeterOnPTTtoggle() { // When a meter changes type, such as the fixed S -- TxPo meter, - // there is automatic clearing. However, some meters do not switch on thier own, + // there is automatic clearing. However, some meters do not switch on their own, // and thus we are providing this clearing method. We are careful // not to clear meters that don't make sense to clear (such as Vd and Id) @@ -110,7 +110,7 @@ void meter::paintEvent(QPaintEvent *) { QPainter painter(this); // This next line sets up a canvis within the - // space of the widget, and gives it corrdinates. + // space of the widget, and gives it coordinates. // The end effect, is that the drawing functions will all // scale to the window size. painter.setRenderHint(QPainter::SmoothPixmapTransform); diff --git a/pttyhandler.cpp b/pttyhandler.cpp index bea880d..1249537 100644 --- a/pttyhandler.cpp +++ b/pttyhandler.cpp @@ -245,14 +245,14 @@ void pttyHandler::receiveDataIn(int fd) { if (rolledBack) { - // qInfo(logSerial()) << "Rolled back and was successfull. Length: " << inPortData.length(); + // qInfo(logSerial()) << "Rolled back and was successful. Length: " << inPortData.length(); //printHex(inPortData, false, true); rolledBack = false; } } else { // did not receive the entire thing so roll back: - // qInfo(logSerial()) << "Rolling back transaction. End not detected. Lenth: " << inPortData.length(); + // qInfo(logSerial()) << "Rolling back transaction. End not detected. Length: " << inPortData.length(); //printHex(inPortData, false, true); rolledBack = true; #ifdef Q_OS_WIN diff --git a/pttyhandler.h b/pttyhandler.h index 73aa80e..3c18206 100644 --- a/pttyhandler.h +++ b/pttyhandler.h @@ -62,7 +62,7 @@ private: bool rolledBack; int ptfd; // pseudo-terminal file desc. - int ptKeepAlive=0; // Used to keep the pty alive after client disconects. + int ptKeepAlive=0; // Used to keep the pty alive after client disconnects. bool havePt; QString ptDevSlave; diff --git a/repeatersetup.cpp b/repeatersetup.cpp index 2439f15..78c3d4a 100644 --- a/repeatersetup.cpp +++ b/repeatersetup.cpp @@ -25,7 +25,7 @@ repeaterSetup::repeaterSetup(QWidget *parent) : repeaterSetup::~repeaterSetup() { - // Trying this for more consistant destruction + // Trying this for more consistent destruction rig.inputs.clear(); rig.preamps.clear(); rig.attenuators.clear(); diff --git a/resampler/resample.c b/resampler/resample.c index 5144434..cab633c 100644 --- a/resampler/resample.c +++ b/resampler/resample.c @@ -104,7 +104,7 @@ static void speex_free(void* ptr) { free(ptr); } #include "resample_neon.h" #endif -/* Numer of elements to allocate on the stack */ +/* Number of elements to allocate on the stack */ #ifdef VAR_ARRAYS #define FIXED_STACK_ALLOC 8192 #else diff --git a/rigcommander.cpp b/rigcommander.cpp index d497c0d..0fe0c23 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -4,7 +4,7 @@ #include "rigidentities.h" #include "logcategories.h" -// Copytight 2017-2020 Elliott H. Liggett +// Copyright 2017-2020 Elliott H. Liggett // This file parses data from the radio and also forms commands to the radio. // The radio physical interface is handled by the commHandler() instance "comm" @@ -3730,7 +3730,7 @@ void rigCommander::parseSpectrum() spectrumEndFreq = fEnd.MHzDouble; if(scopeMode == spectModeCenter) { - // "center" mode, start is actuall center, end is bandwidth. + // "center" mode, start is actual center, end is bandwidth. spectrumStartFreq -= spectrumEndFreq; spectrumEndFreq = spectrumStartFreq + 2*(spectrumEndFreq); // emit haveSpectrumCenterSpan(span); @@ -4029,7 +4029,7 @@ void rigCommander::getPreamp() void rigCommander::getAntenna() { - // This one might neet some thought + // This one might need some thought // as it seems each antenna has to be checked. // Maybe 0x12 alone will do it. QByteArray payload("\x12"); diff --git a/rigidentities.cpp b/rigidentities.cpp index a48a44d..1a94836 100644 --- a/rigidentities.cpp +++ b/rigidentities.cpp @@ -1,7 +1,7 @@ #include "rigidentities.h" #include "logcategories.h" -// Copytight 2017-2021 Elliott H. Liggett +// Copyright 2017-2021 Elliott H. Liggett model_kind determineRadioModel(unsigned char rigID) { diff --git a/ring/ring.h b/ring/ring.h index cec5852..0f5be22 100644 --- a/ring/ring.h +++ b/ring/ring.h @@ -52,11 +52,11 @@ namespace wilt // The class works by allocating the array and storing two pointers (for the // beginning and end of the allocated space). Two atomic pointers are used to // track the beginning and end of the currently used storage space. To - // facilitate concurrent reads and writes, theres a read buffer pointer before + // facilitate concurrent reads and writes, there's a read buffer pointer before // the read pointer for data currently being read, and a corresponding write // buffer pointer beyond the write pointer for data currently being written. // These buffer pointers cannot overlap. Just using these pointers suffer from - // some minute inefficiencies and a few ABA problems. Therfore, atomic + // some minute inefficiencies and a few ABA problems. Therefore, atomic // integers are used to store the currently used and currently free sizes. // // It allows multiple readers and multiple writers by implementing a reserve- @@ -71,7 +71,7 @@ namespace wilt // done yet, the reader must wait until the read buffer pointer points to // where the read started. Only, then is the read buffer pointer updated, and // the free size increased. So while this implementation is lock-free, it is - // not wait-free. This same principle works the same when writing (ammended + // not wait-free. This same principle works the same when writing (amended // for the appropriate pointers). // // If two readers try to read at the same time and there is only enough data diff --git a/udphandler.cpp b/udphandler.cpp index 7e88181..5478528 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -550,7 +550,7 @@ udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort) // Start sending are you there packets - will be stopped once "I am here" received // send ping packets every 100 ms (maybe change to less frequent?) pingTimer->start(PING_PERIOD); - // Send idle packets every 100ms, this timer will be reset everytime a non-idle packet is sent. + // Send idle packets every 100ms, this timer will be reset every time a non-idle packet is sent. idleTimer->start(IDLE_PERIOD); areYouThereTimer->start(AREYOUTHERE_PERIOD); } diff --git a/udpserversetup.ui b/udpserversetup.ui index f3d8d63..9379320 100644 --- a/udpserversetup.ui +++ b/udpserversetup.ui @@ -139,7 +139,7 @@ - Contol Port + Control Port diff --git a/wfmain.cpp b/wfmain.cpp index efdec3e..96e8a3a 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -2449,7 +2449,7 @@ void wfmain:: getInitialRigState() // These are made when the program starts up // and are used to adjust the UI to match the radio settings // the polling interval is set at 200ms. Faster is possible but slower - // computers will glitch occassionally. + // computers will glitch occasionally. issueDelayedCommand(cmdGetFreq); issueDelayedCommand(cmdGetMode); @@ -3316,7 +3316,7 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) ui->antennaSelCombo->setDisabled(false); for(unsigned int i=0; i < rigCaps.antennas.size(); i++) { - inName = QString("%1").arg(rigCaps.antennas.at(i)+1, 0, 16); // adding 1 to have the combobox start with ant 1 insted of 0 + inName = QString("%1").arg(rigCaps.antennas.at(i)+1, 0, 16); // adding 1 to have the combobox start with ant 1 instead of 0 ui->antennaSelCombo->addItem(inName, rigCaps.antennas.at(i)); } } else { @@ -3447,7 +3447,7 @@ void wfmain::insertSlowPeriodicCommand(cmds cmd, unsigned char priority) { // TODO: meaningful priority // These commands are run every 20 "ticks" of the primary radio command loop - // Basically 20 times less often than the standard peridic command + // Basically 20 times less often than the standard periodic command if(priority < 10) { slowPollCmdQueue.push_front(cmd); @@ -4007,7 +4007,7 @@ void wfmain::changeMode(mode_kind mode, bool dataOn) void wfmain::on_modeSelectCombo_activated(int index) { // The "acticvated" signal means the user initiated a mode change. - // This function is not called if code initated the change. + // This function is not called if code initiated the change. mode_info mode; unsigned char newMode = static_cast(ui->modeSelectCombo->itemData(index).toUInt()); @@ -4110,7 +4110,7 @@ void wfmain::on_freqDial_valueChanged(int value) } // With the number of steps and direction of steps established, - // we can now adjust the frequeny: + // we can now adjust the frequency: f.Hz = roundFrequencyWithStep(freq.Hz, delta, tsKnobHz); f.MHzDouble = f.Hz / (double)1E6; @@ -4809,7 +4809,7 @@ void wfmain::setRadioTimeDatePrep() int msecdelay = QTime::currentTime().msecsTo( QTime::currentTime().addSecs(60-second) ); // 3: Compute time and date at one minute later - QDateTime setpoint = now.addMSecs(msecdelay); // at HMS or posibly HMS + some ms. Never under though. + QDateTime setpoint = now.addMSecs(msecdelay); // at HMS or possibly HMS + some ms. Never under though. // 4: Prepare data structs for the time at one minute later timesetpoint.hours = (unsigned char)setpoint.time().hour(); diff --git a/wfmain.ui b/wfmain.ui index a938ee3..cd6e255 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -108,7 +108,7 @@ - <html><head/><body><p>Press button to convert center mode spectrum to fixed mode, preserving the range. This allows you to tune without the spectrum moving, in the same currently-visible range that you see now. </p><p><br/></p><p>The currently-selected edge slot will be overriden.</p></body></html> + <html><head/><body><p>Press button to convert center mode spectrum to fixed mode, preserving the range. This allows you to tune without the spectrum moving, in the same currently-visible range that you see now. </p><p><br/></p><p>The currently-selected edge slot will be overridden.</p></body></html> ToFixed @@ -148,7 +148,7 @@ Waterfall display color theme - Selects the theme for the color waterfall dispaly + Selects the theme for the color waterfall display @@ -2163,7 +2163,7 @@ - <html><head/><body><p>Enter the address in as hexidecimal, without any prefix, just as the radio presents the address in the menu. </p><p>Here are some common examples:</p> + <html><head/><body><p>Enter the address in as hexadecimal, without any prefix, just as the radio presents the address in the menu. </p><p>Here are some common examples:</p> <p>IC-706: 58 <br/>IC-756: 50 <br/>IC-756 Pro: 5C @@ -3099,7 +3099,7 @@ - Contol Port + Control Port From acf4c1bf63df2f81333d369267e198f6723f9c64 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 23 Mar 2022 17:27:47 +0000 Subject: [PATCH 119/323] Start of test to include Eigen --- audiohandler.h | 6 ++++++ wfview.pro | 17 ++++++++++++++--- wfview.vcxproj | 12 ++++++------ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/audiohandler.h b/audiohandler.h index d12ff05..7064d5e 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -25,6 +25,7 @@ #include #endif +#include #include "packettypes.h" @@ -34,6 +35,11 @@ typedef signed short MY_TYPE; #define LOG100 4.60517018599 +typedef Eigen::Matrix VectorXuint8; +typedef Eigen::Matrix VectorXint8; +typedef Eigen::Matrix VectorXint16; +typedef Eigen::Matrix VectorXint32; + #include #include #include diff --git a/wfview.pro b/wfview.pro index 7fc84cf..31370d5 100644 --- a/wfview.pro +++ b/wfview.pro @@ -37,12 +37,21 @@ DEFINES += QCUSTOMPLOT_COMPILE_LIBRARY # These defines are used for the resampler -equals(QT_ARCH, i386): DEFINES += USE_SSE -equals(QT_ARCH, i386): DEFINES += USE_SSE2 +equals(QT_ARCH, i386): win32:DEFINES += USE_SSE +equals(QT_ARCH, i386): win32:DEFINES += USE_SSE2 +equals(QT_ARCH, x86_64): DEFINES += USE_SSE +equals(QT_ARCH, x86_64): DEFINES += USE_SSE2 equals(QT_ARCH, arm): DEFINES += USE_NEON DEFINES += OUTSIDE_SPEEX DEFINES += RANDOM_PREFIX=wf +# These defines are used for the Eigen library +DEFINES += EIGEN_MPL2_ONLY +DEFINES += EIGEN_DONT_VECTORIZE #Clear vector flags +equals(QT_ARCH, i386): win32:DEFINES += EIGEN_VECTORIZE_SSE3 +equals(QT_ARCH, x86_64): DEFINES += EIGEN_VECTORIZE_SSE3 + + isEmpty(PREFIX) { PREFIX = /usr/local } @@ -77,7 +86,7 @@ contains(DEFINES, PORTAUDIO) { !win32:LIBS += -lportaudio } -macx:INCLUDEPATH += /usr/local/include /opt/local/include +macx:INCLUDEPATH += /usr/local/include /opt/local/include macx:LIBS += -L/usr/local/lib -L/opt/local/lib macx:ICON = ../wfview/resources/wfview.icns @@ -141,6 +150,8 @@ macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread -lopus !linux:INCLUDEPATH += ../opus/include +!linux:INCLUDEPATH += ../eigen/Eigen + INCLUDEPATH += resampler SOURCES += main.cpp\ diff --git a/wfview.vcxproj b/wfview.vcxproj index ac20f37..0868b4b 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -48,7 +48,7 @@ - .;..\qcustomplot;..\opus\include;resampler;release;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;..\eigen\Eigen;resampler;release;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ece5933";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="c5cf0fd";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -85,12 +85,12 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ece5933\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"c5cf0fd\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h - .;..\qcustomplot;..\opus\include;resampler;debug;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;..\eigen\Eigen;resampler;debug;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -99,7 +99,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ece5933";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="c5cf0fd";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -124,7 +124,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ece5933\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"c5cf0fd\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h From 389f661c79ab248596539eb5bd8c5dc84824a218 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 3 Apr 2022 20:16:52 +0100 Subject: [PATCH 120/323] Working (in Windows) audio output --- audiohandler.cpp | 262 ++++++++++++++++++++--------------------- audiohandler.h | 226 ++++++++++++++++++++++++++++++++--- rigcommander.cpp | 2 +- tcpserver.cpp | 3 +- tcpserver.h | 3 +- udphandler.cpp | 14 +-- udpserver.cpp | 4 +- wfmain.cpp | 15 +-- wfview.pro | 10 +- wfview.vcxproj | 13 +- wfview.vcxproj.filters | 3 + 11 files changed, 380 insertions(+), 175 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 7424b1e..9a10329 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -73,28 +73,30 @@ bool audioHandler::init(audioSetup setupIn) */ setup = setupIn; - setup.radioChan = 1; - setup.bits = 8; + setup.format.setChannelCount(1); + setup.format.setSampleSize(8); + setup.format.setSampleType(QAudioFormat::UnSignedInt); qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; if (setup.codec == 0x01 || setup.codec == 0x20) { setup.ulaw = true; } if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20 || setup.codec == 0x80) { - setup.radioChan = 2; + setup.format.setChannelCount(2); } if (setup.codec == 0x04 || setup.codec == 0x10 || setup.codec == 0x40 || setup.codec == 0x80) { - setup.bits = 16; + setup.format.setSampleSize(16); + setup.format.setSampleType(QAudioFormat::SignedInt); } qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name << - ", bits" << setup.bits << + ", bits" << setup.format.sampleSize() << ", codec" << setup.codec << ", latency" << setup.latency << ", localAFGain" << setup.localAFgain << - ", radioChan" << setup.radioChan << + ", radioChan" << setup.format.channelCount() << ", resampleQuality" << setup.resampleQuality << - ", samplerate" << setup.samplerate << + ", samplerate" << setup.format.sampleRate() << ", uLaw" << setup.ulaw; @@ -325,11 +327,9 @@ bool audioHandler::init(audioSetup setupIn) else { audioOutput = new QAudioOutput(setup.port, format, this); -#ifdef Q_OS_MAC - audioOutput->setBufferSize(chunkSize*4); -#endif + audioOutput->setBufferSize(getAudioSize(setup.latency, format)); - connect(audioOutput, SIGNAL(notify()), SLOT(notified())); + //connect(audioOutput, SIGNAL(notify()), SLOT(notified())); connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); isInitialized = true; } @@ -339,10 +339,10 @@ bool audioHandler::init(audioSetup setupIn) int resample_error = 0; int opus_err = 0; if (setup.isinput) { - resampler = wf_resampler_init(devChannels, nativeSampleRate, setup.samplerate, setup.resampleQuality, &resample_error); + resampler = wf_resampler_init(devChannels, nativeSampleRate, setup.format.sampleRate(), setup.resampleQuality, &resample_error); if (setup.codec == 0x40 || setup.codec == 0x80) { // Opus codec - encoder = opus_encoder_create(setup.samplerate, setup.radioChan, OPUS_APPLICATION_AUDIO, &opus_err); + encoder = opus_encoder_create(setup.format.sampleRate(), setup.format.channelCount(), OPUS_APPLICATION_AUDIO, &opus_err); opus_encoder_ctl(encoder, OPUS_SET_LSB_DEPTH(16)); opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1)); opus_encoder_ctl(encoder, OPUS_SET_DTX(1)); @@ -351,10 +351,13 @@ bool audioHandler::init(audioSetup setupIn) } } else { - resampler = wf_resampler_init(devChannels, setup.samplerate, this->nativeSampleRate, setup.resampleQuality, &resample_error); + + //resampBufs = new r8b::CFixedBuffer[format.channelCount()]; + //resamps = new r8b::CPtrKeeper[format.channelCount()]; + resampler = wf_resampler_init(devChannels, setup.format.sampleRate(), this->nativeSampleRate, setup.resampleQuality, &resample_error); if (setup.codec == 0x40 || setup.codec == 0x80) { // Opus codec - decoder = opus_decoder_create(setup.samplerate, setup.radioChan, &opus_err); + decoder = opus_decoder_create(setup.format.sampleRate(), setup.format.sampleRate(), &opus_err); qInfo(logAudio()) << "Creating opus decoder: " << opus_strerror(opus_err); } } @@ -400,9 +403,18 @@ void audioHandler::start() this->open(QIODevice::ReadOnly); #else //this->open(QIODevice::ReadOnly | QIODevice::Unbuffered); - this->open(QIODevice::ReadOnly); + //this->open(QIODevice::ReadOnly); #endif - audioOutput->start(this); + audioDevice = audioOutput->start(); + if (!audioDevice) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio device failed to start()"; + return; + } + connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); + connect(audioDevice, &QIODevice::destroyed, this, &QAudioOutput::deleteLater, Qt::UniqueConnection); + audioBuffered = true; + } } #endif @@ -612,45 +624,30 @@ void audioHandler::incomingAudio(audioPacket inPacket) // Regardless of the radio stream format, the buffered audio will ALWAYS be // 16bit sample interleaved stereo 48K (or whatever the native sample rate is) - if (inPacket.time.msecsTo(QTime::currentTime()) > 20) - { - qInfo(logUdp()) << "Audio took" << inPacket.time.msecsTo(QTime::currentTime()) << "ms to arrive!"; - } - - if (!isInitialized && !isReady) - { - qDebug(logAudio()) << "Packet received when stream was not ready"; - return; - } - if (!audioBuffered && ringBuf->size() > ringBuf->capacity() / 2) - { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio buffering complete, capacity:" << ringBuf->capacity() << ", used:" << ringBuf->size(); - audioBuffered = true; - } - audioPacket livePacket = inPacket; if (setup.codec == 0x40 || setup.codec == 0x80) { + /* Opus data */ unsigned char* in = (unsigned char*)inPacket.data.data(); /* Decode the frame. */ - QByteArray outPacket((setup.samplerate / 50) * sizeof(qint16) * setup.radioChan, (char)0xff); // Preset the output buffer size. + QByteArray outPacket((setup.format.sampleRate() / 50) * sizeof(qint16) * setup.format.channelCount(), (char)0xff); // Preset the output buffer size. qint16* out = (qint16*)outPacket.data(); - int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.samplerate); + int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.format.sampleRate()); if (nSamples == -1) { // No opus data yet? return; } - else if (nSamples != setup.samplerate / 50) + else if (nSamples != setup.format.sampleRate() / 50) { - qInfo(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.samplerate / 50); + qInfo(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.format.sampleRate() / 50); return; } if (livePacket.seq > lastSentSeq + 1) { - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 1); + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.format.sampleRate() / 50), 1); } else { - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.samplerate / 50), 0); + nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.format.sampleRate() / 50), 0); } if (nSamples < 0) { @@ -658,10 +655,10 @@ void audioHandler::incomingAudio(audioPacket inPacket) return; } else { - if (int(nSamples * sizeof(qint16) * setup.radioChan) != outPacket.size()) + if (int(nSamples * sizeof(qint16) * setup.format.channelCount()) != outPacket.size()) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(qint16) * setup.radioChan << "outPacket:" << outPacket.size(); - outPacket.resize(nSamples * sizeof(qint16) * setup.radioChan); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(qint16) * setup.format.channelCount() << "outPacket:" << outPacket.size(); + outPacket.resize(nSamples * sizeof(qint16) * setup.format.channelCount()); } //qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoded" << livePacket.data.size() << "bytes, into" << outPacket.length() << "bytes"; livePacket.data.clear(); @@ -669,105 +666,100 @@ void audioHandler::incomingAudio(audioPacket inPacket) } } - //qDebug(logAudio()) << "Got" << setup.bits << "bits, length" << livePacket.data.length(); - // Incoming data is 8bits? - int tempAmplitude=0; - if (setup.bits == 8) + // Process uLaw + if (setup.ulaw) { // Current packet is 8bit so need to create a new buffer that is 16bit - QByteArray outPacket((int)livePacket.data.length() * 2 * (devChannels / setup.radioChan), (char)0xff); + QByteArray outPacket((int)livePacket.data.length() * 2, (char)0xff); qint16* out = (qint16*)outPacket.data(); for (int f = 0; f < livePacket.data.length(); f++) { - int samp = (quint8)livePacket.data[f]; - tempAmplitude = qMax(tempAmplitude, abs(samp)); - for (int g = setup.radioChan; g <= devChannels; g++) - { - if (setup.ulaw) { - *out++ = ulaw_decode[samp] * this->volume; - } - else { - *out++ = (qint16)((samp - 128) << 8) * this->volume; - } - } + *out++ = ulaw_decode[(quint8)livePacket.data[f]]; } livePacket.data.clear(); livePacket.data = outPacket; // Replace incoming data with converted. + setup.format.setSampleSize(16); + setup.format.setSampleType(QAudioFormat::SignedInt); + // Buffer now contains 16bit signed samples. } - else - { - // This is already a 16bit stream, do we need to convert to stereo? - if (setup.radioChan == 1 && devChannels > 1) { - // Yes - QByteArray outPacket(livePacket.data.length() * 2, (char)0xff); // Preset the output buffer size. - qint16* in = (qint16*)livePacket.data.data(); + + + + if (!livePacket.data.isEmpty()) { + + Eigen::VectorXf samplesF; + if (setup.format.sampleSize() == 16) + { + VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); + samplesF = samplesI.cast(); + } + else + { + VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } + + // Set the max amplitude found in the vector + amplitude = samplesF.array().abs().maxCoeff(); + + // Set the volume + samplesF *= volume; + + // Convert mono to stereo + if (setup.format.channelCount() == 1) { + Eigen::VectorXf samplesTemp(samplesF.size() * 2); + Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; + Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; + samplesF = samplesTemp; + } + + + if (format.sampleType() == QAudioFormat::SignedInt) + { + VectorXint16 samplesI = samplesF.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + } + else + { + livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + } + + + if (resampleRatio != 1.0) { + + // We need to resample + // We have a stereo 16bit stream. + quint32 outFrames = ((livePacket.data.length() / 2 / devChannels) * resampleRatio); + quint32 inFrames = (livePacket.data.length() / 2 / devChannels); + QByteArray outPacket(outFrames * 4, (char)0xff); // Preset the output buffer size. + + const qint16* in = (qint16*)livePacket.data.constData(); qint16* out = (qint16*)outPacket.data(); - for (int f = 0; f < livePacket.data.length() / 2; f++) - { - tempAmplitude = qMax(tempAmplitude, (int)(abs(*in) / 256)); - *out++ = (qint16)*in * this->volume; - *out++ = (qint16)*in++ * this->volume; + + int err = 0; + err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames); + if (err) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } livePacket.data.clear(); livePacket.data = outPacket; // Replace incoming data with converted. } - else - { - // We already have the same number of channels so just update volume. - qint16* in = (qint16*)livePacket.data.data(); - for (int f = 0; f < livePacket.data.length() / 2; f++) - { - tempAmplitude = qMax(tempAmplitude, (int)(abs(*in) / 256)); - *in = *in * this->volume; - in++; - } + + + //qDebug(logAudio()) << "Adding packet to buffer:" << livePacket.seq << ": " << livePacket.data.length(); + + currentLatency = livePacket.time.msecsTo(QTime::currentTime()); + + audioDevice->write(livePacket.data); + + if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq; + lastSentSeq = inPacket.seq; + incomingAudio(inPacket); // Call myself again to run the packet a second time (FEC) } - } - amplitude = tempAmplitude; - /* We now have an array of 16bit samples in the NATIVE samplerate of the radio - If the radio sample rate is below 48000, we need to resample. - */ - //qDebug(logAudio()) << "Now 16 bit stereo, length" << livePacket.data.length(); - - if (resampleRatio != 1.0) { - - // We need to resample - // We have a stereo 16bit stream. - quint32 outFrames = ((livePacket.data.length() / 2 / devChannels) * resampleRatio); - quint32 inFrames = (livePacket.data.length() / 2 / devChannels); - QByteArray outPacket(outFrames * 4, (char)0xff); // Preset the output buffer size. - - const qint16* in = (qint16*)livePacket.data.constData(); - qint16* out = (qint16*)outPacket.data(); - - int err = 0; - err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames); - if (err) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - livePacket.data.clear(); - livePacket.data = outPacket; // Replace incoming data with converted. - } - - //qDebug(logAudio()) << "Adding packet to buffer:" << livePacket.seq << ": " << livePacket.data.length(); - - if (!ringBuf->try_write(livePacket)) - { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full! capacity:" << ringBuf->capacity() << "length" << ringBuf->size(); - while (ringBuf->try_read(inPacket)); // Empty buffer - return; - } - if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq; lastSentSeq = inPacket.seq; - incomingAudio(inPacket); // Call myself again to run the packet a second time (FEC) } - lastSentSeq = inPacket.seq; - //if (inPacket.time.msecsTo(QTime::currentTime()) > 20) - //{ - //qDebug(logAudio()) << "After processing, audio took" << inPacket.time.msecsTo(QTime::currentTime()) << "ms to arrive!"; - //} return; } @@ -776,9 +768,18 @@ void audioHandler::changeLatency(const quint16 newSize) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency; setup.latency = newSize; - delete ringBuf; - audioBuffered = false; - ringBuf = new wilt::Ring(setup.latency + 1); // Should be customizable. + //delete ringBuf; + //audioBuffered = false; + //ringBuf = new wilt::Ring(setup.latency + 1); // Should be customizable. + if (!setup.isinput) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Current buffer size is" << audioOutput->bufferSize() << " " << getAudioDuration(audioOutput->bufferSize(), format) << "ms)"; + audioOutput->stop(); + audioOutput->setBufferSize(getAudioSize(setup.latency, format)); + audioDevice = audioOutput->start(); + connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); + connect(audioDevice, &QIODevice::destroyed, this, &QAudioOutput::deleteLater, Qt::UniqueConnection); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "New buffer size is" << audioOutput->bufferSize() << " " << getAudioDuration(audioOutput->bufferSize(), format) << "ms)"; + } } int audioHandler::getLatency() @@ -830,7 +831,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) int tempAmplitude = 0; // Do we need to convert mono to stereo? - if (setup.radioChan == 1 && devChannels > 1) + if (setup.format.channelCount() == 1 && devChannels > 1) { // Strip out right channel? QByteArray outPacket(packet.data.length()/2, (char)0xff); @@ -857,7 +858,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size unsigned char* out = (unsigned char*)outPacket.data(); - int nbBytes = opus_encode(encoder, in, (setup.samplerate / 50), out, outPacket.length()); + int nbBytes = opus_encode(encoder, in, (setup.format.sampleRate() / 50), out, outPacket.length()); if (nbBytes < 0) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); @@ -870,7 +871,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) } } - else if (setup.bits == 8) + else if (setup.format.sampleSize() == 8) { // Do we need to convert 16-bit to 8-bit? @@ -908,7 +909,6 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Too many delayed packets, flushing buffer"; while (ringBuf->try_read(packet)); // Empty buffer delayedPackets = 0; - audioBuffered = false; } } @@ -997,6 +997,6 @@ void audioHandler::stop() quint16 audioHandler::getAmplitude() { - return amplitude; + return *reinterpret_cast(&litude); } diff --git a/audiohandler.h b/audiohandler.h index 7064d5e..d1bfa01 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -25,20 +25,10 @@ #include #endif -#include - #include "packettypes.h" typedef signed short MY_TYPE; #define FORMAT RTAUDIO_SINT16 -#define SCALE 32767.0 - -#define LOG100 4.60517018599 - -typedef Eigen::Matrix VectorXuint8; -typedef Eigen::Matrix VectorXint8; -typedef Eigen::Matrix VectorXint16; -typedef Eigen::Matrix VectorXint32; #include #include @@ -55,6 +45,10 @@ typedef Eigen::Matrix VectorXint32; #endif #include "audiotaper.h" +#include + +//#include +//#include #include @@ -75,13 +69,14 @@ struct audioPacket { struct audioSetup { QString name; - quint8 bits; - quint8 radioChan; - quint16 samplerate; +// quint8 bits; +// quint8 radioChan; +// quint16 samplerate; quint16 latency; quint8 codec; - bool ulaw; + bool ulaw = false; bool isinput; + QAudioFormat format; // Use this for all audio APIs #if defined(RTAUDIO) || defined(PORTAUDIO) int port; #else @@ -190,17 +185,22 @@ private: #else QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; + QIODevice* audioDevice=Q_NULLPTR; QAudioFormat format; QAudioDeviceInfo deviceInfo; #endif SpeexResamplerState* resampler = Q_NULLPTR; + //r8b::CFixedBuffer* resampBufs; + //r8b::CPtrKeeper* resamps; + quint16 audioLatency; unsigned int chunkSize; bool chunkAvailable; quint32 lastSeq; quint32 lastSentSeq=0; + qint64 elapsedMs = 0; quint16 nativeSampleRate=0; quint8 radioSampleBits; @@ -217,7 +217,7 @@ private: volatile bool ready = false; audioPacket tempBuf; quint16 currentLatency; - quint16 amplitude = 0; + float amplitude; qreal volume=1.0; int devChannels; audioSetup setup; @@ -225,4 +225,200 @@ private: OpusDecoder* decoder=Q_NULLPTR; }; + +// Various audio handling functions declared inline + +static inline qint64 getAudioSize(qint64 timeInMs, const QAudioFormat& format) +{ + qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(10000) * timeInMs)); + + if (value % (format.channelCount() * (format.sampleSize() / 8)) != 0) + value += (format.channelCount() * (format.sampleSize() / 8) - value % (format.channelCount() * (format.sampleSize() / 8))); + + return value; +} + +static inline qint64 getAudioDuration(qint64 bytes, const QAudioFormat& format) +{ + return qint64(qFloor(bytes / (format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000)))); +} + +typedef Eigen::Matrix VectorXuint8; +typedef Eigen::Matrix VectorXint8; +typedef Eigen::Matrix VectorXint16; +typedef Eigen::Matrix VectorXint32; + +static inline QByteArray samplesToInt(const QByteArray& data, const QAudioFormat& supported_format) +{ + QByteArray input = data; + + switch (supported_format.sampleSize()) + { + case 8: + { + switch (supported_format.sampleType()) + { + case QAudioFormat::UnSignedInt: + { + Eigen::Ref samples_float = Eigen::Map(reinterpret_cast(input.data()), input.size() / int(sizeof(float))); + + Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits::max()); + + VectorXuint8 samples_int = samples_int_tmp.cast(); + + QByteArray raw = QByteArray(reinterpret_cast(samples_int.data()), int(samples_int.size()) * int(sizeof(quint8))); + + return raw; + } + case QAudioFormat::SignedInt: + { + Eigen::Ref samples_float = Eigen::Map(reinterpret_cast(input.data()), input.size() / int(sizeof(float))); + + Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits::max()); + + VectorXint8 samples_int = samples_int_tmp.cast(); + + QByteArray raw = QByteArray(reinterpret_cast(samples_int.data()), int(samples_int.size()) * int(sizeof(qint8))); + + return raw; + } + default: + break; + } + + break; + } + case 16: + { + switch (supported_format.sampleType()) + { + case QAudioFormat::SignedInt: + { + Eigen::Ref samples_float = Eigen::Map(reinterpret_cast(input.data()), input.size() / int(sizeof(float))); + + Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits::max()); + + VectorXint16 samples_int = samples_int_tmp.cast(); + + QByteArray raw = QByteArray(reinterpret_cast(samples_int.data()), int(samples_int.size()) * int(sizeof(qint16))); + + return raw; + } + default: + break; + } + + break; + } + case 32: + { + switch (supported_format.sampleType()) + { + case QAudioFormat::SignedInt: + { + Eigen::Ref samples_float = Eigen::Map(reinterpret_cast(input.data()), input.size() / int(sizeof(float))); + + Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits::max()); + + VectorXint32 samples_int = samples_int_tmp.cast(); + + QByteArray raw = QByteArray(reinterpret_cast(samples_int.data()), int(samples_int.size()) * int(sizeof(qint32))); + + return raw; + } + default: + break; + } + + break; + } + default: + break; + } + + return QByteArray(); +} + +static inline QByteArray samplesToFloat(const QByteArray& data, const QAudioFormat& supported_format) +{ + QByteArray input = data; + + switch (supported_format.sampleSize()) + { + case 8: + { + switch (supported_format.sampleType()) + { + case QAudioFormat::UnSignedInt: + { + QByteArray raw = input; + + Eigen::Ref samples_int = Eigen::Map(reinterpret_cast(raw.data()), raw.size() / int(sizeof(quint8))); + + Eigen::VectorXf samples_float = samples_int.cast() / float(std::numeric_limits::max()); + + return QByteArray(reinterpret_cast(samples_float.data()), int(samples_float.size()) * int(sizeof(float))); + } + case QAudioFormat::SignedInt: + { + QByteArray raw = input; + + Eigen::Ref samples_int = Eigen::Map(reinterpret_cast(raw.data()), raw.size() / int(sizeof(qint8))); + + Eigen::VectorXf samples_float = samples_int.cast() / float(std::numeric_limits::max()); + + return QByteArray(reinterpret_cast(samples_float.data()), int(samples_float.size()) * int(sizeof(float))); + } + default: + break; + } + + break; + } + case 16: + { + switch (supported_format.sampleType()) + { + case QAudioFormat::SignedInt: + { + QByteArray raw = input; + + Eigen::Ref samples_int = Eigen::Map(reinterpret_cast(raw.data()), raw.size() / int(sizeof(qint16))); + + Eigen::VectorXf samples_float = samples_int.cast() / float(std::numeric_limits::max()); + + return QByteArray(reinterpret_cast(samples_float.data()), int(samples_float.size()) * int(sizeof(float))); + } + default: + break; + } + + break; + } + case 32: + { + switch (supported_format.sampleType()) + { + case QAudioFormat::SignedInt: + { + QByteArray raw = input; + + Eigen::Ref samples_int = Eigen::Map(reinterpret_cast(raw.data()), raw.size() / int(sizeof(qint32))); + + Eigen::VectorXf samples_float = samples_int.cast() / float(std::numeric_limits::max()); + + return QByteArray(reinterpret_cast(samples_float.data()), int(samples_float.size()) * int(sizeof(float))); + } + default: + break; + } + + break; + } + default: + break; + } + + return QByteArray(); +} #endif // AUDIOHANDLER_H diff --git a/rigcommander.cpp b/rigcommander.cpp index 082b4f5..f0380dd 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -151,7 +151,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); // data from the tcp port to the Rig: - connect(tcp, SIGNAL(haveData(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); + connect(tcp, SIGNAL(receiveData(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); connect(this, SIGNAL(haveChangeLatency(quint16)), udp, SLOT(changeLatency(quint16))); connect(this, SIGNAL(haveSetVolume(unsigned char)), udp, SLOT(setVolume(unsigned char))); diff --git a/tcpserver.cpp b/tcpserver.cpp index d1fee65..8dea503 100644 --- a/tcpserver.cpp +++ b/tcpserver.cpp @@ -28,6 +28,7 @@ int tcpServer::startServer(qint16 port) { void tcpServer::incomingConnection(qintptr socket) { tcpServerClient* client = new tcpServerClient(socket, this); connect(this, SIGNAL(onStopped()), client, SLOT(closeSocket())); + emit newClient(socket); // Signal par } void tcpServer::stopServer() @@ -39,7 +40,7 @@ void tcpServer::stopServer() void tcpServer::receiveDataFromClient(QByteArray data) { - emit haveData(data); + emit receiveData(data); } void tcpServer::sendData(QByteArray data) { diff --git a/tcpserver.h b/tcpserver.h index 0fb8fda..e699a90 100644 --- a/tcpserver.h +++ b/tcpserver.h @@ -29,8 +29,9 @@ public slots: signals: void onStarted(); void onStopped(); - void haveData(QByteArray data); // emit this when we have data from tcp client, connect to rigcommander + void receiveData(QByteArray data); // emit this when we have data from tcp client, connect to rigcommander void sendDataToClient(QByteArray data); + void newClient(int socketId); private: QTcpServer* server; diff --git a/udphandler.cpp b/udphandler.cpp index 9ed326d..3fbfd41 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -20,8 +20,8 @@ udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) : this->password = prefs.password; this->compName = prefs.clientName.mid(0,8) + "-wfview"; - qInfo(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxSetup.latency << " tx latency:" << txSetup.latency << " rx sample rate: " << rxSetup.samplerate << - " rx codec: " << rxSetup.codec << " tx sample rate: " << txSetup.samplerate << " tx codec: " << txSetup.codec; + qInfo(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxSetup.latency << " tx latency:" << txSetup.latency << " rx sample rate: " << rxSetup.format.sampleRate() << + " rx codec: " << rxSetup.codec << " tx sample rate: " << txSetup.format.sampleRate() << " tx codec: " << txSetup.codec; // Try to set the IP address, if it is a hostname then perform a DNS lookup. if (!radioIP.setAddress(prefs.ipAddress)) @@ -280,7 +280,7 @@ void udpHandler::dataReceived() // TX is not supported if (txSampleRates < 2) { - txSetup.samplerate = 0; + txSetup.format.setSampleRate(0); txSetup.codec = 0; } audio = new udpAudio(localIP, radioIP, audioPort, audioLocalPort, rxSetup, txSetup); @@ -512,8 +512,8 @@ void udpHandler::sendRequestStream() } p.rxcodec = rxSetup.codec; memcpy(&p.username, usernameEncoded.constData(), usernameEncoded.length()); - p.rxsample = qToBigEndian((quint32)rxSetup.samplerate); - p.txsample = qToBigEndian((quint32)txSetup.samplerate); + p.rxsample = qToBigEndian((quint32)rxSetup.format.sampleRate()); + p.txsample = qToBigEndian((quint32)txSetup.format.sampleRate()); p.civport = qToBigEndian((quint32)civLocalPort); p.audioport = qToBigEndian((quint32)audioLocalPort); p.txbuffer = qToBigEndian((quint32)txSetup.latency); @@ -789,7 +789,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint this->port = audioPort; this->radioIP = ip; - if (txSetup.samplerate == 0) { + if (txSetup.format.sampleRate() == 0) { enableTx = false; } @@ -812,7 +812,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); - txSetup.radioChan = 1; + txSetup.format.setChannelCount(1); // TX Audio is always single channel. txaudio = new audioHandler(); txAudioThread = new QThread(this); diff --git a/udpserver.cpp b/udpserver.cpp index 03fd882..56fee68 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -350,7 +350,7 @@ void udpServer::controlReceived() if (!memcmp(radio->guid, current->guid, GUIDLEN) && radio->txaudio == Q_NULLPTR) { radio->txAudioSetup.codec = current->txCodec; - radio->txAudioSetup.samplerate = current->txSampleRate; + radio->txAudioSetup.format.setSampleRate(current->txSampleRate); radio->txAudioSetup.isinput = false; radio->txAudioSetup.latency = current->txBufferLen; outAudio.isinput = false; @@ -390,7 +390,7 @@ void udpServer::controlReceived() #endif radio->rxAudioSetup.codec = current->rxCodec; - radio->rxAudioSetup.samplerate = current->rxSampleRate; + radio->rxAudioSetup.format.setSampleRate(current->rxSampleRate); radio->rxAudioSetup.latency = current->txBufferLen; radio->rxAudioSetup.isinput = true; diff --git a/wfmain.cpp b/wfmain.cpp index 3ceb5db..3bcca57 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1552,10 +1552,11 @@ void wfmain::loadSettings() ui->txLatencySlider->setTracking(false); // Stop it sending value on every change. ui->audioSampleRateCombo->blockSignals(true); - rxSetup.samplerate = settings->value("AudioRXSampleRate", "48000").toInt(); - txSetup.samplerate = rxSetup.samplerate; + rxSetup.format.setSampleRate(settings->value("AudioRXSampleRate", "48000").toInt()); + txSetup.format.setSampleRate(rxSetup.format.sampleRate()); + ui->audioSampleRateCombo->setEnabled(ui->lanEnableBtn->isChecked()); - int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(rxSetup.samplerate)); + int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(rxSetup.format.sampleRate())); if (audioSampleRateIndex != -1) { ui->audioSampleRateCombo->setCurrentIndex(audioSampleRateIndex); } @@ -1974,9 +1975,9 @@ void wfmain::saveSettings() settings->setValue("Password", udpPrefs.password); settings->setValue("AudioRXLatency", rxSetup.latency); settings->setValue("AudioTXLatency", txSetup.latency); - settings->setValue("AudioRXSampleRate", rxSetup.samplerate); + settings->setValue("AudioRXSampleRate", rxSetup.format.sampleRate()); settings->setValue("AudioRXCodec", rxSetup.codec); - settings->setValue("AudioTXSampleRate", txSetup.samplerate); + settings->setValue("AudioTXSampleRate", txSetup.format.sampleRate()); settings->setValue("AudioTXCodec", txSetup.codec); settings->setValue("AudioOutput", rxSetup.name); settings->setValue("AudioInput", txSetup.name); @@ -4721,8 +4722,8 @@ void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text) { //udpPrefs.audioRXSampleRate = text.toInt(); //udpPrefs.audioTXSampleRate = text.toInt(); - rxSetup.samplerate = text.toInt(); - txSetup.samplerate = text.toInt(); + rxSetup.format.setSampleRate(text.toInt()); + txSetup.format.setSampleRate(text.toInt()); } void wfmain::on_audioRXCodecCombo_currentIndexChanged(int value) diff --git a/wfview.pro b/wfview.pro index 31370d5..48ef479 100644 --- a/wfview.pro +++ b/wfview.pro @@ -150,7 +150,8 @@ macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread -lopus !linux:INCLUDEPATH += ../opus/include -!linux:INCLUDEPATH += ../eigen/Eigen +!linux:INCLUDEPATH += ../eigen +!linux:INCLUDEPATH += ../r8brain-free-src INCLUDEPATH += resampler @@ -175,8 +176,8 @@ SOURCES += main.cpp\ ring/ring.cpp \ transceiveradjustments.cpp \ selectradio.cpp \ - tcpserver.cpp \ - aboutbox.cpp + tcpserver.cpp \ + aboutbox.cpp HEADERS += wfmain.h \ commhandler.h \ @@ -204,7 +205,8 @@ HEADERS += wfmain.h \ transceiveradjustments.h \ audiotaper.h \ selectradio.h \ - tcpserver.h \ + tcpserver.h \ + audiocommon.h \ aboutbox.h diff --git a/wfview.vcxproj b/wfview.vcxproj index 0868b4b..612592e 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -48,7 +48,7 @@ - .;..\qcustomplot;..\opus\include;..\eigen\Eigen;resampler;release;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="c5cf0fd";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="8ec62fe";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -85,12 +85,12 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"c5cf0fd\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"8ec62fe\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h - .;..\qcustomplot;..\opus\include;..\eigen\Eigen;resampler;debug;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -99,7 +99,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="c5cf0fd";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="8ec62fe";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -124,7 +124,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"c5cf0fd\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"8ec62fe\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -165,6 +165,7 @@ + diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index bc556d8..d21b5b3 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -127,6 +127,9 @@ Header Files + + Header Files + Header Files From 83c494ecc1dafb3bbd134615ae80cc23626d1e1d Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 4 Apr 2022 00:01:08 +0100 Subject: [PATCH 121/323] Remove rtaudio/portaudio for now --- audiohandler.cpp | 518 ++++------------------------------------------- audiohandler.h | 69 +------ 2 files changed, 39 insertions(+), 548 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 9a10329..38a85b5 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -8,9 +8,6 @@ #include "logcategories.h" #include "ulaw.h" -#if defined(Q_OS_WIN) && defined(PORTAUDIO) -#include -#endif audioHandler::audioHandler(QObject* parent) @@ -22,28 +19,23 @@ audioHandler::~audioHandler() { if (isInitialized) { -#if defined(RTAUDIO) - - try { - audio->abortStream(); - audio->closeStream(); - } - catch (RtAudioError& e) { - qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); - } - delete audio; -#elif defined(PORTAUDIO) - Pa_StopStream(audio); - Pa_CloseStream(audio); -#else stop(); -#endif } if (ringBuf != Q_NULLPTR) { delete ringBuf; } + if (audioInput != Q_NULLPTR) { + audioInput = Q_NULLPTR; + delete audioInput; + } + if (audioOutput != Q_NULLPTR) { + delete audioOutput; + audioOutput = Q_NULLPTR; + } + + if (resampler != Q_NULLPTR) { speex_resampler_destroy(resampler); qDebug(logAudio()) << "Resampler closed"; @@ -109,181 +101,6 @@ bool audioHandler::init(audioSetup setupIn) this->setVolume(setup.localAFgain); } -#if defined(RTAUDIO) -#if !defined(Q_OS_MACX) - options.flags = ((!RTAUDIO_HOG_DEVICE) | (RTAUDIO_MINIMIZE_LATENCY)); -#endif - -#if defined(Q_OS_LINUX) - audio = new RtAudio(RtAudio::Api::LINUX_ALSA); -#elif defined(Q_OS_WIN) - audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); -#elif defined(Q_OS_MACX) - audio = new RtAudio(RtAudio::Api::MACOSX_CORE); -#endif - - if (setup.port > 0) { - aParams.deviceId = setup.port; - } - else if (setup.isinput) { - aParams.deviceId = audio->getDefaultInputDevice(); - } - else { - aParams.deviceId = audio->getDefaultOutputDevice(); - } - aParams.firstChannel = 0; - - try { - info = audio->getDeviceInfo(aParams.deviceId); - } - catch (RtAudioError& e) { - qInfo(logAudio()) << "Device error:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); - return isInitialized; - } - - if (info.probed) - { - // Always use the "preferred" sample rate - // We can always resample if needed - this->nativeSampleRate = info.preferredSampleRate; - - // Per channel chunk size. - this->chunkSize = (this->nativeSampleRate / 50); - - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed"; - if (info.nativeFormats == 0) - { - qInfo(logAudio()) << " No natively supported data formats!"; - return false; - } - else { - qDebug(logAudio()) << " Supported formats:" << - (info.nativeFormats & RTAUDIO_SINT8 ? "8-bit int," : "") << - (info.nativeFormats & RTAUDIO_SINT16 ? "16-bit int," : "") << - (info.nativeFormats & RTAUDIO_SINT24 ? "24-bit int," : "") << - (info.nativeFormats & RTAUDIO_SINT32 ? "32-bit int," : "") << - (info.nativeFormats & RTAUDIO_FLOAT32 ? "32-bit float," : "") << - (info.nativeFormats & RTAUDIO_FLOAT64 ? "64-bit float," : ""); - - qInfo(logAudio()) << " Preferred sample rate:" << info.preferredSampleRate; - if (setup.isinput) { - devChannels = info.inputChannels; - } - else { - devChannels = info.outputChannels; - } - qInfo(logAudio()) << " Channels:" << devChannels; - if (devChannels > 2) { - devChannels = 2; - } - aParams.nChannels = devChannels; - } - - qInfo(logAudio()) << " chunkSize: " << chunkSize; - try { - if (setup.isinput) { - audio->openStream(NULL, &aParams, RTAUDIO_SINT16, this->nativeSampleRate, &this->chunkSize, &staticWrite, this, &options); - } - else { - audio->openStream(&aParams, NULL, RTAUDIO_SINT16, this->nativeSampleRate, &this->chunkSize, &staticRead, this, &options); - } - audio->startStream(); - isInitialized = true; - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened"; - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "detected latency:" << audio->getStreamLatency(); - } - catch (RtAudioError& e) { - qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); - } - } - else - { - qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!"; - } - - -#elif defined(PORTAUDIO) - - PaError err; - -#ifdef Q_OS_WIN - CoInitialize(0); -#endif - - memset(&aParams, 0,sizeof(PaStreamParameters)); - - if (setup.port > 0) { - aParams.device = setup.port; - } - else if (setup.isinput) { - aParams.device = Pa_GetDefaultInputDevice(); - } - else { - aParams.device = Pa_GetDefaultOutputDevice(); - } - - info = Pa_GetDeviceInfo(aParams.device); - - aParams.channelCount = 2; - aParams.hostApiSpecificStreamInfo = NULL; - aParams.sampleFormat = paInt16; - if (setup.isinput) { - aParams.suggestedLatency = info->defaultLowInputLatency; - } - else { - aParams.suggestedLatency = info->defaultLowOutputLatency; - } - - aParams.hostApiSpecificStreamInfo = NULL; - - // Always use the "preferred" sample rate (unless it is 44100) - // We can always resample if needed - if (info->defaultSampleRate == 44100) { - this->nativeSampleRate = 48000; - } - else { - this->nativeSampleRate = info->defaultSampleRate; - } - // Per channel chunk size. - this->chunkSize = (this->nativeSampleRate / 50); - - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << info->name << "(" << aParams.device << ") successfully probed"; - if (setup.isinput) { - devChannels = info->maxInputChannels; - } - else { - devChannels = info->maxOutputChannels; - } - if (devChannels > 2) { - devChannels = 2; - } - aParams.channelCount = devChannels; - - qInfo(logAudio()) << " Channels:" << devChannels; - qInfo(logAudio()) << " chunkSize: " << chunkSize; - qInfo(logAudio()) << " sampleRate: " << nativeSampleRate; - - if (setup.isinput) { - err=Pa_OpenStream(&audio, &aParams, 0, this->nativeSampleRate, this->chunkSize, paNoFlag, &audioHandler::staticWrite, (void*)this); - } - else { - err=Pa_OpenStream(&audio, 0, &aParams, this->nativeSampleRate, this->chunkSize, paNoFlag, &audioHandler::staticRead, (void*)this); - } - - if (err == paNoError) { - err = Pa_StartStream(audio); - } - if (err == paNoError) { - isInitialized = true; - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened"; - } - else { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "failed to open device" << Pa_GetErrorText(err); - } - - -#else - format.setSampleSize(16); format.setChannelCount(2); format.setSampleRate(INTERNAL_SAMPLE_RATE); @@ -320,21 +137,16 @@ bool audioHandler::init(audioSetup setupIn) if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); - connect(audioInput, SIGNAL(notify()), SLOT(notified())); - connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); + //connect(audioInput, SIGNAL(notify()), SLOT(notified())); isInitialized = true; } else { audioOutput = new QAudioOutput(setup.port, format, this); audioOutput->setBufferSize(getAudioSize(setup.latency, format)); - - //connect(audioOutput, SIGNAL(notify()), SLOT(notified())); - connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); isInitialized = true; } -#endif // Setup resampler and opus if they are needed. int resample_error = 0; int opus_err = 0; @@ -370,16 +182,10 @@ bool audioHandler::init(audioSetup setupIn) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "thread id" << QThread::currentThreadId(); -#if !defined (RTAUDIO) && !defined(PORTAUDIO) - if (isInitialized) { - this->start(); - } -#endif - + this->start(); return isInitialized; } -#if !defined (RTAUDIO) && !defined(PORTAUDIO) void audioHandler::start() { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "start() running"; @@ -390,196 +196,30 @@ void audioHandler::start() } if (setup.isinput) { -#ifndef Q_OS_WIN - this->open(QIODevice::WriteOnly); -#else - this->open(QIODevice::WriteOnly); - //this->open(QIODevice::WriteOnly | QIODevice::Unbuffered); -#endif - audioInput->start(this); + audioDevice = audioInput->start(); + connect(audioInput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); + connect(audioDevice, &QIODevice::destroyed, this, &QAudioInput::deleteLater, Qt::UniqueConnection); } else { -#ifndef Q_OS_WIN - this->open(QIODevice::ReadOnly); -#else - //this->open(QIODevice::ReadOnly | QIODevice::Unbuffered); - //this->open(QIODevice::ReadOnly); -#endif audioDevice = audioOutput->start(); - if (!audioDevice) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio device failed to start()"; - return; - } connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); connect(audioDevice, &QIODevice::destroyed, this, &QAudioOutput::deleteLater, Qt::UniqueConnection); - audioBuffered = true; - + } + if (!audioDevice) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio device failed to start()"; + return; } } -#endif void audioHandler::setVolume(unsigned char volume) { - this->volume = audiopot[volume]; - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } - -/// -/// This function processes the incoming audio FROM the radio and pushes it into the playback buffer *data -/// -/// -/// -/// -#if defined(RTAUDIO) -int audioHandler::readData(void* outputBuffer, void* inputBuffer, - unsigned int nFrames, double streamTime, RtAudioStreamStatus status) -{ - Q_UNUSED(inputBuffer); - Q_UNUSED(streamTime); - if (status == RTAUDIO_OUTPUT_UNDERFLOW) - qDebug(logAudio()) << "Underflow detected"; - int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels - quint8* buffer = (quint8*)outputBuffer; -#elif defined(PORTAUDIO) - -int audioHandler::readData(const void* inputBuffer, void* outputBuffer, - unsigned long nFrames, const PaStreamCallbackTimeInfo * streamTime, PaStreamCallbackFlags status) -{ - Q_UNUSED(inputBuffer); - Q_UNUSED(streamTime); - Q_UNUSED(status); - int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels - quint8* buffer = (quint8*)outputBuffer; -#else -qint64 audioHandler::readData(char* buffer, qint64 nBytes) -{ -#endif - // Calculate output length, always full samples - int sentlen = 0; - if (!isReady) { - isReady = true; - } - if (!audioBuffered) { - memset(buffer, 0, nBytes); -#if defined(RTAUDIO) - return 0; -#elif defined(PORTAUDIO) - return 0; -#else - return nBytes; -#endif - } - audioPacket packet; - if (ringBuf->size()>0) - { - // Output buffer is ALWAYS 16 bit. - while (sentlen < nBytes) - { - if (!ringBuf->try_read(packet)) - { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "buffer is empty, sentlen:" << sentlen << " nBytes:" << nBytes ; - break; - } - //qDebug(logAudio()) << "Packet size:" << packet.data.length() << "nBytes (requested)" << nBytes << "remaining" << nBytes-sentlen; - currentLatency = packet.time.msecsTo(QTime::currentTime()); - - // This shouldn't be required but if we did output a partial packet - // This will add the remaining packet data to the output buffer. - if (tempBuf.sent != tempBuf.data.length()) - { - int send = qMin((int)nBytes - sentlen, tempBuf.data.length() - tempBuf.sent); - memcpy(buffer + sentlen, tempBuf.data.constData() + tempBuf.sent, send); - tempBuf.sent = tempBuf.sent + send; - sentlen = sentlen + send; - if (tempBuf.sent != tempBuf.data.length()) - { - // We still don't have enough buffer space for this? - break; - } - //qDebug(logAudio()) << "Adding partial:" << send; - } - - if (currentLatency > setup.latency) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Packet " << hex << packet.seq << - " arrived too late (increase output latency!) " << - dec << packet.time.msecsTo(QTime::currentTime()) << "ms"; - delayedPackets++; - } - - int send = qMin((int)nBytes - sentlen, packet.data.length()); - memcpy(buffer + sentlen, packet.data.constData(), send); - sentlen = sentlen + send; - if (send < packet.data.length()) - { - //qDebug(logAudio()) << "Asking for partial, sent:" << send << "packet length" << packet.data.length(); - tempBuf = packet; - tempBuf.sent = tempBuf.sent + send; - lastSeq = packet.seq; - break; - } - - - if (packet.seq <= lastSeq) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Duplicate/early audio packet: " << hex << lastSeq << " got " << hex << packet.seq; - } - else if (packet.seq != lastSeq + 1) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Missing audio packet(s) from: " << hex << lastSeq + 1 << " to " << hex << packet.seq - 1; - } - - lastSeq = packet.seq; - } - } - - // fill the rest of the buffer with silence - if (nBytes > sentlen) { - qDebug(logAudio()) << "looking for: " << nBytes << " got: " << sentlen; - memset(buffer + sentlen, 0, nBytes - sentlen); - } - - if (delayedPackets > 10) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Too many delayed packets, flushing buffer"; - //while (ringBuf->try_read(packet)); // Empty buffer - delayedPackets = 0; - //audioBuffered = false; - } - -#if defined(RTAUDIO) - return 0; -#elif defined(PORTAUDIO) - return 0; -#else - return nBytes; -#endif -} - -#if defined(RTAUDIO) -int audioHandler::writeData(void* outputBuffer, void* inputBuffer, - unsigned int nFrames, double streamTime, RtAudioStreamStatus status) -{ - Q_UNUSED(outputBuffer); - Q_UNUSED(streamTime); - Q_UNUSED(status); - int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels - const char* data = (const char*)inputBuffer; -#elif defined(PORTAUDIO) -int audioHandler::writeData(const void* inputBuffer, void* outputBuffer, - unsigned long nFrames, const PaStreamCallbackTimeInfo * streamTime, - PaStreamCallbackFlags status) -{ - Q_UNUSED(outputBuffer); - Q_UNUSED(streamTime); - Q_UNUSED(status); - int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels - const char* data = (const char*)inputBuffer; -#else qint64 audioHandler::writeData(const char* data, qint64 nBytes) { -#endif if (!isReady) { isReady = true; } @@ -609,13 +249,7 @@ qint64 audioHandler::writeData(const char* data, qint64 nBytes) } //qDebug(logAudio()) << "sentlen" << sentlen; -#if defined(RTAUDIO) - return 0; -#elif defined(PORTAUDIO) - return 0; -#else return nBytes; -#endif } void audioHandler::incomingAudio(audioPacket inPacket) @@ -666,7 +300,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) } } - // Process uLaw + // Process uLaw. if (setup.ulaw) { // Current packet is 8bit so need to create a new buffer that is 16bit @@ -691,7 +325,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (setup.format.sampleSize() == 16) { VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); - samplesF = samplesI.cast(); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); } else { @@ -701,7 +335,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) // Set the max amplitude found in the vector amplitude = samplesF.array().abs().maxCoeff(); - + qDebug(logAudio()) << "Current amplitude" << QString::number(amplitude, 'f', 4) << getAmplitude() ; // Set the volume samplesF *= volume; @@ -716,7 +350,8 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (format.sampleType() == QAudioFormat::SignedInt) { - VectorXint16 samplesI = samplesF.cast(); + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint16 samplesI = samplesITemp.cast(); livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); } else @@ -747,8 +382,8 @@ void audioHandler::incomingAudio(audioPacket inPacket) //qDebug(logAudio()) << "Adding packet to buffer:" << livePacket.seq << ": " << livePacket.data.length(); - - currentLatency = livePacket.time.msecsTo(QTime::currentTime()); + + currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + getAudioDuration(audioOutput->bufferSize()-audioOutput->bytesFree(),format); audioDevice->write(livePacket.data); @@ -766,20 +401,16 @@ void audioHandler::incomingAudio(audioPacket inPacket) void audioHandler::changeLatency(const quint16 newSize) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency; setup.latency = newSize; - //delete ringBuf; - //audioBuffered = false; - //ringBuf = new wilt::Ring(setup.latency + 1); // Should be customizable. + if (!setup.isinput) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Current buffer size is" << audioOutput->bufferSize() << " " << getAudioDuration(audioOutput->bufferSize(), format) << "ms)"; - audioOutput->stop(); + stop(); audioOutput->setBufferSize(getAudioSize(setup.latency, format)); - audioDevice = audioOutput->start(); - connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); - connect(audioDevice, &QIODevice::destroyed, this, &QAudioOutput::deleteLater, Qt::UniqueConnection); - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "New buffer size is" << audioOutput->bufferSize() << " " << getAudioDuration(audioOutput->bufferSize(), format) << "ms)"; + start(); } + } int audioHandler::getLatency() @@ -793,18 +424,11 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) { audioPacket packet; packet.sent = 0; - - if (isInitialized && ringBuf != Q_NULLPTR && ringBuf->try_read(packet)) - { - currentLatency = packet.time.msecsTo(QTime::currentTime()); - - if (currentLatency > setup.latency) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Packet " << hex << packet.seq << - " arrived too late (increase latency!) " << - dec << packet.time.msecsTo(QTime::currentTime()) << "ms"; - delayedPackets++; - } - + if (audioDevice != Q_NULLPTR) { + packet.data = audioDevice->readAll(); + } + if (packet.data.length() > 0) + { //qDebug(logAudio) << "Chunksize" << this->chunkSize << "Packet size" << packet.data.length(); // Packet will arrive as stereo interleaved 16bit 48K if (resampleRatio != 1.0) @@ -904,12 +528,6 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) amplitude = tempAmplitude; ret = packet.data; - //qDebug(logAudio()) << "Now radio format, length" << packet.data.length(); - if (delayedPackets > 10) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Too many delayed packets, flushing buffer"; - while (ringBuf->try_read(packet)); // Empty buffer - delayedPackets = 0; - } } @@ -918,85 +536,23 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) } -#if !defined (RTAUDIO) && !defined(PORTAUDIO) - -qint64 audioHandler::bytesAvailable() const -{ - return 0; -} - -bool audioHandler::isSequential() const -{ - return true; -} - -void audioHandler::notified() -{ -} - - -void audioHandler::stateChanged(QAudio::State state) -{ - // Process the state - switch (state) - { - case QAudio::IdleState: - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in idle state: " << audioBuffer.size() << " packets in buffer"; - if (audioOutput != Q_NULLPTR && audioOutput->error() == QAudio::UnderrunError) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "buffer underrun"; - //audioOutput->suspend(); - } - break; - } - case QAudio::ActiveState: - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in active state: " << audioBuffer.size() << " packets in buffer"; - break; - } - case QAudio::SuspendedState: - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in suspended state: " << audioBuffer.size() << " packets in buffer"; - break; - } - case QAudio::StoppedState: - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in stopped state: " << audioBuffer.size() << " packets in buffer"; - break; - } - default: { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unhandled audio state: " << audioBuffer.size() << " packets in buffer"; - } - } -} - void audioHandler::stop() { if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) { // Stop audio output audioOutput->stop(); - this->stop(); - this->close(); - delete audioOutput; - audioOutput = Q_NULLPTR; } if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) { // Stop audio output audioInput->stop(); - this->stop(); - this->close(); - delete audioInput; - audioInput = Q_NULLPTR; } isInitialized = false; } -#endif quint16 audioHandler::getAmplitude() { - return *reinterpret_cast(&litude); + return static_cast(amplitude * 255.0); } diff --git a/audiohandler.h b/audiohandler.h index d1bfa01..be23b6d 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -69,29 +69,18 @@ struct audioPacket { struct audioSetup { QString name; -// quint8 bits; -// quint8 radioChan; -// quint16 samplerate; quint16 latency; quint8 codec; bool ulaw = false; bool isinput; QAudioFormat format; // Use this for all audio APIs -#if defined(RTAUDIO) || defined(PORTAUDIO) - int port; -#else QAudioDeviceInfo port; -#endif quint8 resampleQuality; unsigned char localAFgain; }; // For QtMultimedia, use a native QIODevice -#if !defined(PORTAUDIO) && !defined(RTAUDIO) -class audioHandler : public QIODevice -#else class audioHandler : public QObject -#endif { Q_OBJECT @@ -101,15 +90,8 @@ public: int getLatency(); -#if !defined (RTAUDIO) && !defined(PORTAUDIO) - bool setDevice(QAudioDeviceInfo deviceInfo); - void start(); - void flush(); void stop(); - qint64 bytesAvailable() const; - bool isSequential() const; -#endif void getNextAudioChunk(QByteArray &data); quint16 getAmplitude(); @@ -121,10 +103,6 @@ public slots: void incomingAudio(const audioPacket data); private slots: -#if !defined (RTAUDIO) && !defined(PORTAUDIO) - void notified(); - void stateChanged(QAudio::State state); -#endif signals: void audioMessage(QString message); @@ -134,61 +112,18 @@ signals: private: -#if defined(RTAUDIO) - int readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status); - - static int staticRead(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) { - return static_cast(userData)->readData(outputBuffer, inputBuffer, nFrames, streamTime, status); - } - - int writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status); - - static int staticWrite(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) { - return static_cast(userData)->writeData(outputBuffer, inputBuffer, nFrames, streamTime, status); - } -#elif defined(PORTAUDIO) - int readData(const void* inputBuffer, void* outputBuffer, - unsigned long nFrames, - const PaStreamCallbackTimeInfo* streamTime, - PaStreamCallbackFlags status); - static int staticRead(const void* inputBuffer, void* outputBuffer, unsigned long nFrames, const PaStreamCallbackTimeInfo* streamTime, PaStreamCallbackFlags status, void* userData) { - return ((audioHandler*)userData)->readData(inputBuffer, outputBuffer, nFrames, streamTime, status); - } - - int writeData(const void* inputBuffer, void* outputBuffer, - unsigned long nFrames, - const PaStreamCallbackTimeInfo* streamTime, - PaStreamCallbackFlags status); - static int staticWrite(const void* inputBuffer, void* outputBuffer, unsigned long nFrames, const PaStreamCallbackTimeInfo* streamTime, PaStreamCallbackFlags status, void* userData) { - return ((audioHandler*)userData)->writeData(inputBuffer, outputBuffer, nFrames, streamTime, status); - } - -#else - qint64 readData(char* data, qint64 nBytes); qint64 writeData(const char* data, qint64 nBytes); -#endif - void reinit(); bool isInitialized=false; bool isReady = false; bool audioBuffered = false; -#if defined(RTAUDIO) - RtAudio* audio = Q_NULLPTR; - int audioDevice = 0; - RtAudio::StreamParameters aParams; - RtAudio::StreamOptions options; - RtAudio::DeviceInfo info; -#elif defined(PORTAUDIO) - PaStream* audio = Q_NULLPTR; - PaStreamParameters aParams; - const PaDeviceInfo *info; -#else + QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; QIODevice* audioDevice=Q_NULLPTR; QAudioFormat format; QAudioDeviceInfo deviceInfo; -#endif + SpeexResamplerState* resampler = Q_NULLPTR; //r8b::CFixedBuffer* resampBufs; From 5c2d6e57b2df88217359b00396f7a18212775f44 Mon Sep 17 00:00:00 2001 From: M0VSE Date: Mon, 4 Apr 2022 00:23:23 +0100 Subject: [PATCH 122/323] Fix linux compile --- audiohandler.cpp | 2 -- audiohandler.h | 9 +++++++++ wfview.pro | 1 - 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 38a85b5..c4243f0 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -92,8 +92,6 @@ bool audioHandler::init(audioSetup setupIn) ", uLaw" << setup.ulaw; - ringBuf = new wilt::Ring(setup.latency + 1); // Should be customizable. - tempBuf.sent = 0; if(!setup.isinput) diff --git a/audiohandler.h b/audiohandler.h index be23b6d..87ea3df 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -45,7 +45,11 @@ typedef signed short MY_TYPE; #endif #include "audiotaper.h" +#ifdef Q_OS_LINUX +#include +#else #include +#endif //#include //#include @@ -165,7 +169,12 @@ private: static inline qint64 getAudioSize(qint64 timeInMs, const QAudioFormat& format) { +#ifdef Q_OS_LINUX + qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000) * timeInMs)); +#else qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(10000) * timeInMs)); +#endif + if (value % (format.channelCount() * (format.sampleSize() / 8)) != 0) value += (format.channelCount() * (format.sampleSize() / 8) - value % (format.channelCount() * (format.sampleSize() / 8))); diff --git a/wfview.pro b/wfview.pro index 48ef479..3d62c22 100644 --- a/wfview.pro +++ b/wfview.pro @@ -206,7 +206,6 @@ HEADERS += wfmain.h \ audiotaper.h \ selectradio.h \ tcpserver.h \ - audiocommon.h \ aboutbox.h From cf7a947beb7751371e77958c5c6fde495cade0ba Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 4 Apr 2022 00:37:07 +0100 Subject: [PATCH 123/323] Remove debugging message --- audiohandler.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index c4243f0..0e50cb7 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -333,7 +333,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) // Set the max amplitude found in the vector amplitude = samplesF.array().abs().maxCoeff(); - qDebug(logAudio()) << "Current amplitude" << QString::number(amplitude, 'f', 4) << getAmplitude() ; // Set the volume samplesF *= volume; From 64b4ef2019d11c2b67bdbbd2c626ba6be47c1a88 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 4 Apr 2022 11:12:06 +0100 Subject: [PATCH 124/323] Few more fixes and move windows builds into separate directories --- .gitignore | 4 + audiohandler.cpp | 61 ++----- audiohandler.h | 2 - wfserver.pro | 13 ++ wfserver.vcxproj | 209 ++++++++++------------ wfserver.vcxproj.filters | 28 +-- wfview.pro | 3 +- wfview.vcxproj | 374 ++++++++++----------------------------- wfview.vcxproj.filters | 49 +---- 9 files changed, 216 insertions(+), 527 deletions(-) diff --git a/.gitignore b/.gitignore index 3edd80b..e96cd60 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,8 @@ .qmake.stash debug release +wfview-debug +wfserver-debug +wfview-release +wfserver-release ui_* \ No newline at end of file diff --git a/audiohandler.cpp b/audiohandler.cpp index 0e50cb7..7041c23 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -126,8 +126,6 @@ bool audioHandler::init(audioSetup setupIn) devChannels = format.channelCount(); nativeSampleRate = format.sampleRate(); - // chunk size is always relative to Internal Sample Rate. - this->chunkSize = (nativeSampleRate / 50); qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Internal: sample rate" << format.sampleRate() << "channel count" << format.channelCount(); @@ -188,11 +186,6 @@ void audioHandler::start() { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "start() running"; - if ((audioOutput == Q_NULLPTR || audioOutput->state() != QAudio::StoppedState) && - (audioInput == Q_NULLPTR || audioInput->state() != QAudio::StoppedState)) { - return; - } - if (setup.isinput) { audioDevice = audioInput->start(); connect(audioInput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); @@ -212,44 +205,10 @@ void audioHandler::start() void audioHandler::setVolume(unsigned char volume) { this->volume = audiopot[volume]; - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } -qint64 audioHandler::writeData(const char* data, qint64 nBytes) -{ - if (!isReady) { - isReady = true; - } - int sentlen = 0; - //qDebug(logAudio()) << "nFrames" << nFrames << "nBytes" << nBytes; - int chunkBytes = chunkSize * devChannels * 2; - while (sentlen < nBytes) { - if (tempBuf.sent != chunkBytes) - { - int send = qMin((int)(nBytes - sentlen), chunkBytes - tempBuf.sent); - tempBuf.data.append(QByteArray::fromRawData(data + sentlen, send)); - sentlen = sentlen + send; - tempBuf.seq = lastSentSeq; - tempBuf.time = QTime::currentTime(); - tempBuf.sent = tempBuf.sent + send; - } - else { - if (!ringBuf->try_write(tempBuf)) - { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << " audio buffer full!"; - break; - } - tempBuf.data.clear(); - tempBuf.sent = 0; - lastSentSeq++; - } - } - - //qDebug(logAudio()) << "sentlen" << sentlen; - return nBytes; -} - void audioHandler::incomingAudio(audioPacket inPacket) { // No point buffering audio until stream is actually running. @@ -272,7 +231,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) } else if (nSamples != setup.format.sampleRate() / 50) { - qInfo(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.format.sampleRate() / 50); + qDebug(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.format.sampleRate() / 50); return; } if (livePacket.seq > lastSentSeq + 1) { @@ -289,7 +248,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) else { if (int(nSamples * sizeof(qint16) * setup.format.channelCount()) != outPacket.size()) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(qint16) * setup.format.channelCount() << "outPacket:" << outPacket.size(); + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(qint16) * setup.format.channelCount() << "outPacket:" << outPacket.size(); outPacket.resize(nSamples * sizeof(qint16) * setup.format.channelCount()); } //qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoded" << livePacket.data.size() << "bytes, into" << outPacket.length() << "bytes"; @@ -382,8 +341,9 @@ void audioHandler::incomingAudio(audioPacket inPacket) currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + getAudioDuration(audioOutput->bufferSize()-audioOutput->bytesFree(),format); - audioDevice->write(livePacket.data); - + if (audioDevice != Q_NULLPTR) { + audioDevice->write(livePacket.data); + } if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq; lastSentSeq = inPacket.seq; @@ -399,7 +359,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) void audioHandler::changeLatency(const quint16 newSize) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency; setup.latency = newSize; if (!setup.isinput) { @@ -426,7 +386,6 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) } if (packet.data.length() > 0) { - //qDebug(logAudio) << "Chunksize" << this->chunkSize << "Packet size" << packet.data.length(); // Packet will arrive as stereo interleaved 16bit 48K if (resampleRatio != 1.0) { @@ -442,8 +401,6 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) if (err) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } - //qInfo(logAudio()) << "Resampler run " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - //qInfo(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length(); packet.data.clear(); packet.data = outPacket; // Copy output packet back to input buffer. } @@ -535,6 +492,8 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) void audioHandler::stop() { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "stop() running"; + if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) { // Stop audio output audioOutput->stop(); @@ -544,7 +503,7 @@ void audioHandler::stop() // Stop audio output audioInput->stop(); } - isInitialized = false; + audioDevice = Q_NULLPTR; } diff --git a/audiohandler.h b/audiohandler.h index 87ea3df..5647f92 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -116,7 +116,6 @@ signals: private: - qint64 writeData(const char* data, qint64 nBytes); bool isInitialized=false; bool isReady = false; @@ -134,7 +133,6 @@ private: //r8b::CPtrKeeper* resamps; quint16 audioLatency; - unsigned int chunkSize; bool chunkAvailable; quint32 lastSeq; diff --git a/wfserver.pro b/wfserver.pro index 26808da..0d7de1f 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -20,6 +20,7 @@ DEFINES += BUILD_WFSERVER CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new +WIN32:DESTDIR = wfview-release } else { # For Release builds only: @@ -28,6 +29,7 @@ QMAKE_CXXFLAGS += -fvisibility=hidden QMAKE_CXXFLAGS += -fvisibility-inlines-hidden QMAKE_CXXFLAGS += -faligned-new linux:QMAKE_LFLAGS += -O2 -s +WIN32:DESTDIR = wfview-debug } # The following define makes your compiler emit warnings if you use @@ -49,6 +51,12 @@ isEmpty(PREFIX) { PREFIX = /usr/local } +# These defines are used for the Eigen library +DEFINES += EIGEN_MPL2_ONLY +DEFINES += EIGEN_DONT_VECTORIZE #Clear vector flags +equals(QT_ARCH, i386): win32:DEFINES += EIGEN_VECTORIZE_SSE3 +equals(QT_ARCH, x86_64): DEFINES += EIGEN_VECTORIZE_SSE3 + DEFINES += PREFIX=\\\"$$PREFIX\\\" # Choose audio system, uses QTMultimedia if both are commented out. @@ -120,6 +128,9 @@ macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread -lopus !linux:INCLUDEPATH += ../opus/include +!linux:INCLUDEPATH += ../eigen +!linux:INCLUDEPATH += ../r8brain-free-src + INCLUDEPATH += resampler SOURCES += main.cpp\ @@ -135,6 +146,7 @@ SOURCES += main.cpp\ pttyhandler.cpp \ resampler/resample.c \ rigctld.cpp \ + tcpserver.cpp \ ring/ring.cpp HEADERS += servermain.h \ @@ -155,4 +167,5 @@ HEADERS += servermain.h \ rigctld.h \ ulaw.h \ ring/ring.h \ + tcpserver.h \ audiotaper.h diff --git a/wfserver.vcxproj b/wfserver.vcxproj index 6fb6b46..177b88d 100644 --- a/wfserver.vcxproj +++ b/wfserver.vcxproj @@ -16,7 +16,8 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild + $(MSBuildProjectDirectory)\QtMsBuild + v142 @@ -36,7 +37,10 @@ debug\ wfserver - + + + + @@ -44,11 +48,37 @@ - debug\debug\wfservertruerelease\release\wfservertruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport - + + + + + + wfserver-debug\ + wfserver-debug\ + wfserver + true + + + release\ + release\ + wfserver + true + false + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + + - .;..\opus\include;resampler;release;/include;%(AdditionalIncludeDirectories) + .;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -57,14 +87,16 @@ Sync release\ MaxSpeed - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ff47fbd";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="cf7a947";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - + + MultiThreadedDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories) @@ -85,12 +117,26 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ff47fbd\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"cf7a947\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + - .;..\opus\include;resampler;debug;/include;%(AdditionalIncludeDirectories) + .;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -99,13 +145,14 @@ Sync debug\ Disabled - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ff47fbd";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="cf7a947";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories) @@ -124,9 +171,23 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ff47fbd\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"cf7a947\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + @@ -140,107 +201,42 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - Document true @@ -257,22 +253,6 @@ release\moc_predefs.h;%(Outputs) true - - - - - - - - - - - - - - - - @@ -305,30 +285,16 @@ - - - - - - - - - - resourcesresources + resources + resources + - - - - - - - - - - stylestyle + style + style + @@ -342,6 +308,9 @@ - + + + + \ No newline at end of file diff --git a/wfserver.vcxproj.filters b/wfserver.vcxproj.filters index 47cfe64..3e9270e 100644 --- a/wfserver.vcxproj.filters +++ b/wfserver.vcxproj.filters @@ -73,6 +73,9 @@ Source Files + + Source Files + Source Files @@ -129,6 +132,9 @@ Header Files + + Header Files + Header Files @@ -140,32 +146,12 @@ - - - - Generated Files Generated Files - - - - - - - - - - - - - - - - @@ -299,6 +285,6 @@ - + \ No newline at end of file diff --git a/wfview.pro b/wfview.pro index 3d62c22..28f75e9 100644 --- a/wfview.pro +++ b/wfview.pro @@ -18,7 +18,7 @@ DEFINES += BUILD_WFVIEW CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new - +WIN32:DESTDIR = wfview-release } else { # For Release builds only: linux:QMAKE_CXXFLAGS += -s @@ -26,6 +26,7 @@ QMAKE_CXXFLAGS += -fvisibility=hidden QMAKE_CXXFLAGS += -fvisibility-inlines-hidden QMAKE_CXXFLAGS += -faligned-new linux:QMAKE_LFLAGS += -O2 -s +WIN32:DESTDIR = wfview-debug } # The following define makes your compiler emit warnings if you use diff --git a/wfview.vcxproj b/wfview.vcxproj index 612592e..2954a6f 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,7 +16,8 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild + $(MSBuildProjectDirectory)\QtMsBuild + v142 @@ -36,7 +37,10 @@ debug\ wfview - + + + + @@ -44,8 +48,34 @@ - debug\debug\wfviewtruerelease\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport - + + + + + + wfview-debug\ + wfview-debug\ + wfview + true + + + release\ + release\ + wfview + true + false + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) @@ -59,12 +89,14 @@ MaxSpeed _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="8ec62fe";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - + + MultiThreadedDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -87,7 +119,26 @@ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"8ec62fe\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) @@ -105,7 +156,8 @@ true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -126,7 +178,26 @@ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"8ec62fe\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + @@ -155,209 +226,55 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Document true @@ -374,121 +291,21 @@ release\moc_predefs.h;%(Outputs) true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -522,30 +339,16 @@ - - - - - - - - - - resourcesresources + resources + resources + - - - - - - - - - - stylestyle + style + style + @@ -559,6 +362,9 @@ - + + + + \ No newline at end of file diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index d21b5b3..d3be9c0 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -213,59 +213,12 @@ - - - - - - - - - - Generated Files Generated Files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -422,6 +375,6 @@ - + \ No newline at end of file From 2cc875ff7cc3b245e99d41c79d64907fcb42fa6e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 4 Apr 2022 11:47:55 +0100 Subject: [PATCH 125/323] Try to fix linux audio stopping on latency change --- audiohandler.cpp | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 7041c23..bf91135 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -189,12 +189,10 @@ void audioHandler::start() if (setup.isinput) { audioDevice = audioInput->start(); connect(audioInput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); - connect(audioDevice, &QIODevice::destroyed, this, &QAudioInput::deleteLater, Qt::UniqueConnection); } else { audioDevice = audioOutput->start(); connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); - connect(audioDevice, &QIODevice::destroyed, this, &QAudioOutput::deleteLater, Qt::UniqueConnection); } if (!audioDevice) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio device failed to start()"; @@ -202,6 +200,23 @@ void audioHandler::start() } } + +void audioHandler::stop() +{ + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "stop() running"; + + if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) { + // Stop audio output + audioOutput->stop(); + } + + if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) { + // Stop audio output + audioInput->stop(); + } + audioDevice = Q_NULLPTR; +} + void audioHandler::setVolume(unsigned char volume) { this->volume = audiopot[volume]; @@ -490,22 +505,6 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) } -void audioHandler::stop() -{ - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "stop() running"; - - if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) { - // Stop audio output - audioOutput->stop(); - } - - if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) { - // Stop audio output - audioInput->stop(); - } - audioDevice = Q_NULLPTR; -} - quint16 audioHandler::getAmplitude() { From ac70ba5f8324e12c71e2ab54f999d0c4a32694aa Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 4 Apr 2022 11:52:59 +0100 Subject: [PATCH 126/323] Add debug to make sure buffer size is really changing --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index bf91135..d926e6d 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -374,7 +374,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) void audioHandler::changeLatency(const quint16 newSize) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency; setup.latency = newSize; if (!setup.isinput) { @@ -382,6 +381,7 @@ void audioHandler::changeLatency(const quint16 newSize) audioOutput->setBufferSize(getAudioSize(setup.latency, format)); start(); } + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << getAudioDuration(audioOutput->bufferSize(),format) <<"ms"; } From 45ac1fbe1c48b90d369820fdde72f52d6eb17ae1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 4 Apr 2022 19:22:11 +0100 Subject: [PATCH 127/323] Remove ringbuffer as no longer needed --- audiohandler.cpp | 43 ++-- audiohandler.h | 3 - ring/LICENSE | 21 -- ring/README.md | 10 - ring/ring.cpp | 290 --------------------------- ring/ring.h | 440 ----------------------------------------- wfserver.pro | 2 - wfview.pro | 2 - wfview.vcxproj | 385 +++++++++++++++++++++++++++--------- wfview.vcxproj.filters | 58 +++++- 10 files changed, 366 insertions(+), 888 deletions(-) delete mode 100644 ring/LICENSE delete mode 100644 ring/README.md delete mode 100644 ring/ring.cpp delete mode 100644 ring/ring.h diff --git a/audiohandler.cpp b/audiohandler.cpp index d926e6d..db4a68b 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -22,10 +22,6 @@ audioHandler::~audioHandler() stop(); } - if (ringBuf != Q_NULLPTR) { - delete ringBuf; - } - if (audioInput != Q_NULLPTR) { audioInput = Q_NULLPTR; delete audioInput; @@ -99,12 +95,15 @@ bool audioHandler::init(audioSetup setupIn) this->setVolume(setup.localAFgain); } - format.setSampleSize(16); +/* format.setSampleSize(16); format.setChannelCount(2); format.setSampleRate(INTERNAL_SAMPLE_RATE); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); - format.setSampleType(QAudioFormat::SignedInt); + format.setSampleType(QAudioFormat::SignedInt); */ + format = setup.port.preferredFormat(); + qDebug(logAudio()) << "Preferred Format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() << + "Sample Rate" << format.sampleRate() << "Codec" << format.codec() << "Sample Type" << format.sampleType(); if (setup.port.isNull()) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins."; @@ -139,7 +138,6 @@ bool audioHandler::init(audioSetup setupIn) else { audioOutput = new QAudioOutput(setup.port, format, this); - audioOutput->setBufferSize(getAudioSize(setup.latency, format)); isInitialized = true; } @@ -191,6 +189,8 @@ void audioHandler::start() connect(audioInput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); } else { + // Buffer size must be set before audio is started. + audioOutput->setBufferSize(getAudioSize(setup.latency, format)); audioDevice = audioOutput->start(); connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); } @@ -294,23 +294,26 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (!livePacket.data.isEmpty()) { Eigen::VectorXf samplesF; - if (setup.format.sampleSize() == 16) + if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) { VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); samplesF = samplesI.cast() / float(std::numeric_limits::max()); } - else + else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) { VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); samplesF = samplesI.cast() / float(std::numeric_limits::max());; } + /* samplesF is now an Eigen Vector of the current samples in float format */ + // Set the max amplitude found in the vector + // Should it be before or after volume is applied? amplitude = samplesF.array().abs().maxCoeff(); // Set the volume samplesF *= volume; - // Convert mono to stereo + // Convert mono to stereo if required if (setup.format.channelCount() == 1) { Eigen::VectorXf samplesTemp(samplesF.size() * 2); Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; @@ -319,16 +322,31 @@ void audioHandler::incomingAudio(audioPacket inPacket) } - if (format.sampleType() == QAudioFormat::SignedInt) + if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXuint8 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + } + if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) { Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); VectorXint16 samplesI = samplesITemp.cast(); livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); } - else + else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint32 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); + } + else if (format.sampleType() == QAudioFormat::Float) { livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); } + else { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); + } if (resampleRatio != 1.0) { @@ -378,7 +396,6 @@ void audioHandler::changeLatency(const quint16 newSize) if (!setup.isinput) { stop(); - audioOutput->setBufferSize(getAudioSize(setup.latency, format)); start(); } qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << getAudioDuration(audioOutput->bufferSize(),format) <<"ms"; diff --git a/audiohandler.h b/audiohandler.h index 5647f92..8532d69 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -36,7 +36,6 @@ typedef signed short MY_TYPE; #include #include "resampler/speex_resampler.h" -#include "ring/ring.h" #ifdef Q_OS_WIN #include "opus.h" @@ -149,8 +148,6 @@ private: double resampleRatio; - wilt::Ring *ringBuf=Q_NULLPTR; - volatile bool ready = false; audioPacket tempBuf; quint16 currentLatency; diff --git a/ring/LICENSE b/ring/LICENSE deleted file mode 100644 index b9a4d39..0000000 --- a/ring/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Trevor Wilson - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/ring/README.md b/ring/README.md deleted file mode 100644 index 2cdbbe9..0000000 --- a/ring/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Ring Library - -## Overview - -This library provides source for a multi-producer multi-consumer lock-free ring buffer. It provides a very simple interface for writing and reading from the buffer. The source includes a `Ring_` class, that provides the raw implementation and C-like facilities, as well as a templated `Ring` class for typed reads and writes. - - -## Contact - -If you have any questions, concerns, or recommendations please feel free to e-mail me at kmdreko@gmail.com. If you notice a bug or defect, create an issue to report it. diff --git a/ring/ring.cpp b/ring/ring.cpp deleted file mode 100644 index d50555d..0000000 --- a/ring/ring.cpp +++ /dev/null @@ -1,290 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// FILE: ring.cpp -// DATE: 2016-02-25 -// AUTH: Trevor Wilson -// DESC: Implements a lock-free, multi-consumer, multi-producer ring buffer -// class - -//////////////////////////////////////////////////////////////////////////////// -// Copyright (c) 2016 Trevor Wilson -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "ring.h" -using namespace wilt; - -#include -// - std::memcpy - -Ring_::Ring_() - : beg_(nullptr) - , end_(nullptr) -{ - std::atomic_init(&used_, static_cast(0)); - std::atomic_init(&free_, static_cast(0)); - std::atomic_init(&rbuf_, static_cast(0)); - std::atomic_init(&rptr_, static_cast(0)); - std::atomic_init(&wptr_, static_cast(0)); - std::atomic_init(&wbuf_, static_cast(0)); -} - -Ring_::Ring_(std::size_t size) - : beg_(new char[size]) - , end_(beg_ + size) -{ - std::atomic_init(&used_, static_cast(0)); - std::atomic_init(&free_, static_cast(size)); - std::atomic_init(&rbuf_, beg_); - std::atomic_init(&rptr_, beg_); - std::atomic_init(&wptr_, beg_); - std::atomic_init(&wbuf_, beg_); -} - -Ring_::Ring_(Ring_&& ring) - : beg_(ring.beg_) - , end_(ring.end_) -{ - std::atomic_init(&used_, ring.used_.load()); - std::atomic_init(&free_, ring.free_.load()); - std::atomic_init(&rbuf_, ring.rbuf_.load()); - std::atomic_init(&rptr_, ring.rptr_.load()); - std::atomic_init(&wptr_, ring.wptr_.load()); - std::atomic_init(&wbuf_, ring.wbuf_.load()); - - ring.beg_ = nullptr; - ring.end_ = nullptr; - - ring.used_.store(0); - ring.free_.store(0); - ring.rbuf_.store(nullptr); - ring.rptr_.store(nullptr); - ring.wptr_.store(nullptr); - ring.wbuf_.store(nullptr); -} - -Ring_& Ring_::operator= (Ring_&& ring) -{ - delete[] beg_; - - beg_ = ring.beg_; - end_ = ring.end_; - - used_.store(ring.used_.load()); - free_.store(ring.free_.load()); - rbuf_.store(ring.rbuf_.load()); - rptr_.store(ring.rptr_.load()); - wptr_.store(ring.wptr_.load()); - wbuf_.store(ring.wbuf_.load()); - - ring.beg_ = nullptr; - ring.end_ = nullptr; - - ring.used_.store(0); - ring.free_.store(0); - ring.rbuf_.store(nullptr); - ring.rptr_.store(nullptr); - ring.wptr_.store(nullptr); - ring.wbuf_.store(nullptr); - - return *this; -} - -Ring_::~Ring_() -{ - delete[] beg_; -} - -std::size_t Ring_::size() const -{ - // The 'used' space can be negative in an over-reserved case, but it can be - // clamped to 0 for simplicity. - - auto s = used_.load(); - return s < 0 ? 0 : static_cast(s); -} - -std::size_t Ring_::capacity() const -{ - return static_cast(end_ - beg_); -} - -void Ring_::read(void* data, std::size_t length) noexcept -{ - auto block = acquire_read_block_(length); - - copy_read_block_(block, (char*)data, length); - release_read_block_(block, length); -} - -void Ring_::write(const void* data, std::size_t length) noexcept -{ - auto block = acquire_write_block_(length); - - copy_write_block_(block, (const char*)data, length); - release_write_block_(block, length); -} - -bool Ring_::try_read(void* data, std::size_t length) noexcept -{ - auto block = try_acquire_read_block_(length); - if (block == nullptr) - return false; - - copy_read_block_(block, (char*)data, length); - release_read_block_(block, length); - - return true; -} - -bool Ring_::try_write(const void* data, std::size_t length) noexcept -{ - auto block = try_acquire_write_block_(length); - if (block == nullptr) - return false; - - copy_write_block_(block, (const char*)data, length); - release_write_block_(block, length); - - return true; -} - -char* Ring_::normalize_(char* ptr) -{ - return ptr < end_ ? ptr : ptr - capacity(); -} - -char* Ring_::acquire_read_block_(std::size_t length) -{ - auto size = static_cast(length); - while (true) // loop while conflict - { - auto old_rptr = rptr_.load(std::memory_order_consume); // read rptr - while (used_.load(std::memory_order_consume) < size) // check for data - ; // spin until success - - auto new_rptr = normalize_(old_rptr + size); // get block end - used_.fetch_sub(size); // reserve - if (rptr_.compare_exchange_strong(old_rptr, new_rptr)) // try commit - return old_rptr; // committed - - used_.fetch_add(size, std::memory_order_relaxed); // un-reserve - } -} - -char* Ring_::try_acquire_read_block_(std::size_t length) -{ - auto size = static_cast(length); - while (true) // loop while conflict - { - auto old_rptr = rptr_.load(std::memory_order_consume); // read rptr - if (used_.load(std::memory_order_consume) < size) // check for data - return nullptr; // return failure - - auto new_rptr = normalize_(old_rptr + size); // get block end - used_.fetch_sub(size); // reserve - if (rptr_.compare_exchange_strong(old_rptr, new_rptr)) // try commit - return old_rptr; // committed - - used_.fetch_add(size, std::memory_order_relaxed); // un-reserve - } -} - -void Ring_::copy_read_block_(const char* block, char* data, std::size_t length) -{ - if (block + length < end_) - { - std::memcpy(data, block, length); - } - else - { - auto first = end_ - block; - std::memcpy(data, block, first); - std::memcpy(data + first, beg_, length - first); - } -} - -void Ring_::release_read_block_(char* old_rptr, std::size_t length) -{ - auto new_rptr = normalize_(old_rptr + length); // get block end - while (rbuf_.load() != old_rptr) // check for earlier reads - ; // spin until reads complete - - rbuf_.store(new_rptr); // finish commit - free_.fetch_add(length, std::memory_order_relaxed); // add to free space -} - -char* Ring_::acquire_write_block_(std::size_t length) -{ - auto size = static_cast(length); - while (true) // loop while conflict - { - auto old_wbuf = wbuf_.load(std::memory_order_consume); // read wbuf - while (free_.load(std::memory_order_consume) < size) // check for space - ; // spin until success - - auto new_wbuf = normalize_(old_wbuf + size); // get block end - free_.fetch_sub(size); // reserve - if (wbuf_.compare_exchange_strong(old_wbuf, new_wbuf)) // try commit - return old_wbuf; // committed - - free_.fetch_add(size, std::memory_order_relaxed); // un-reserve - } -} - -char* Ring_::try_acquire_write_block_(std::size_t length) -{ - auto size = static_cast(length); - while (true) // loop while conflict - { - auto old_wbuf = wbuf_.load(std::memory_order_consume); // read wbuf - if (free_.load(std::memory_order_consume) < size) // check for space - return nullptr; // return failure - - auto new_wbuf = normalize_(old_wbuf + size); // get block end - free_.fetch_sub(size); // reserve - if (wbuf_.compare_exchange_strong(old_wbuf, new_wbuf)) // try commit - return old_wbuf; // committed - - free_.fetch_add(size, std::memory_order_relaxed); // un-reserve - } -} - -void Ring_::copy_write_block_(char* block, const char* data, std::size_t length) -{ - if (block + length < end_) - { - std::memcpy(block, data, length); - } - else - { - auto first = end_ - block; - std::memcpy(block, data, first); - std::memcpy(beg_, data + first, length - first); - } -} - -void Ring_::release_write_block_(char* old_wbuf, std::size_t length) -{ - auto new_wbuf = normalize_(old_wbuf + length); // get block end - while (wptr_.load() != old_wbuf) // wait for earlier writes - ; // spin until writes complete - - wptr_.store(new_wbuf); // finish commit - used_.fetch_add(length, std::memory_order_relaxed); // add to used space -} diff --git a/ring/ring.h b/ring/ring.h deleted file mode 100644 index 0f5be22..0000000 --- a/ring/ring.h +++ /dev/null @@ -1,440 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// FILE: ring.h -// DATE: 2016-02-25 -// AUTH: Trevor Wilson -// DESC: Defines a lock-free, multi-consumer, multi-producer ring buffer class - -//////////////////////////////////////////////////////////////////////////////// -// Copyright (c) 2016 Trevor Wilson -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef WILT_RING_H -#define WILT_RING_H - -#include -// - std::atomic -#include -// - std::size_t -// - std::ptrdiff_t -#include -// - ::new(ptr) -#include -// - std::is_nothrow_copy_constructible -// - std::is_nothrow_move_constructible -// - std::is_nothrow_move_assignable -// - std::is_nothrow_destructible -#include -// - std::move - -namespace wilt -{ - ////////////////////////////////////////////////////////////////////////////// - // This structure aims to access elements in a ring buffer from multiple - // concurrent readers and writers in a lock-free manner. - // - // The class works by allocating the array and storing two pointers (for the - // beginning and end of the allocated space). Two atomic pointers are used to - // track the beginning and end of the currently used storage space. To - // facilitate concurrent reads and writes, there's a read buffer pointer before - // the read pointer for data currently being read, and a corresponding write - // buffer pointer beyond the write pointer for data currently being written. - // These buffer pointers cannot overlap. Just using these pointers suffer from - // some minute inefficiencies and a few ABA problems. Therefore, atomic - // integers are used to store the currently used and currently free sizes. - // - // It allows multiple readers and multiple writers by implementing a reserve- - // commit system. A thread wanting to read will check the used size to see if - // there's enough data. If there is, it subtracts from the used size to - // 'reserve' the read. It then does a compare-exchange to 'commit' by - // increasing the read pointer. If that fails, then it backs out ('un- - // reserves') by adding back to the used size and tries again. If it - // succeeds, then it proceeds to read the data. In order to complete, the - // reader must update the read buffer pointer to where it just finished - // reading from. However, because other readers that started before may not be - // done yet, the reader must wait until the read buffer pointer points to - // where the read started. Only, then is the read buffer pointer updated, and - // the free size increased. So while this implementation is lock-free, it is - // not wait-free. This same principle works the same when writing (amended - // for the appropriate pointers). - // - // If two readers try to read at the same time and there is only enough data - // for one of them. The used size MAY be negative because they both 'reserve' - // the data. This is an over-reserved state. But the compare-exchange will - // only allow one reader to 'commit' to the read and the other will 'un- - // reserve' the read. - // - // |beg |rptr used=5 |wbuf - unused - // |----|----|++++|====|====|====|====|====|++++|----| + modifying - // free=3 |rbuf |wptr |end = used - // - // The diagram above shows a buffer of size 10 storing 5 bytes with a reader - // reading one byte and one writer reading one byte. - // - // Out of the box, the class works by reading and writing raw bytes from POD - // data types and arrays. A wrapper could allow for a nicer interface for - // pushing and popping elements. As it stands, this structure cannot be easily - // modified to store types of variable size. - - class Ring_ - { - private: - //////////////////////////////////////////////////////////////////////////// - // TYPE DEFINITIONS - //////////////////////////////////////////////////////////////////////////// - - typedef char* data_ptr; - typedef std::atomic size_type; - typedef std::atomic atom_ptr; - - private: - //////////////////////////////////////////////////////////////////////////// - // PRIVATE MEMBERS - //////////////////////////////////////////////////////////////////////////// - // Beginning and end pointers don't need to be atomic because they don't - // change. used_ and free_ can be negative in certain cases (and that's ok). - - data_ptr beg_; // pointer to beginning of data block - data_ptr end_; // pointer to end of data block - - alignas(64) - size_type used_; // size of unreserved used space - - alignas(64) - size_type free_; // size of unreserved free space - - alignas(64) - atom_ptr rbuf_; // pointer to beginning of data being read - atom_ptr rptr_; // pointer to beginning of data - - alignas(64) - atom_ptr wptr_; // pointer to end of data - atom_ptr wbuf_; // pointer to end of data being written - - public: - //////////////////////////////////////////////////////////////////////////// - // CONSTRUCTORS AND DESTRUCTORS - //////////////////////////////////////////////////////////////////////////// - - // Constructs a ring without a buffer (capacity() == 0) - Ring_(); - - // Constructs a ring with a buffer with a size - Ring_(std::size_t size); - - // Moves the buffer between rings, assumes no concurrent operations - Ring_(Ring_&& ring); - - // Moves the buffer between rings, assumes no concurrent operations on - // either ring. Deallocates the buffer - Ring_& operator= (Ring_&& ring); - - // No copying - Ring_(const Ring_&) = delete; - Ring_& operator= (const Ring_&) = delete; - - // Deallocates the buffer - ~Ring_(); - - public: - //////////////////////////////////////////////////////////////////////////// - // QUERY FUNCTIONS - //////////////////////////////////////////////////////////////////////////// - // Functions only report on the state of the ring - - // Returns the current amount of non-reserved used space (amount of written - // data that a read hasn't yet reserved). Over-reserved scenarios mean this - // number is not the ultimate source of truth with concurrent operations, - // but its the closest safe approximation. This, of course, doesn't report - // writes that have not completed. - std::size_t size() const; - - // Maximum amount of data that can be held - std::size_t capacity() const; - - public: - //////////////////////////////////////////////////////////////////////////// - // ACCESSORS AND MODIFIERS - //////////////////////////////////////////////////////////////////////////// - // All operations assume object has not been moved. Blocking operations run - // until operation is completed. Non-blocking operations fail if there is - // not enough space - - void read(void* data, std::size_t length) noexcept; - void write(const void* data, std::size_t length) noexcept; - bool try_read(void* data, std::size_t length) noexcept; - bool try_write(const void* data, std::size_t length) noexcept; - - protected: - //////////////////////////////////////////////////////////////////////////// - // PROTECTED FUNCTIONS - //////////////////////////////////////////////////////////////////////////// - // Helper functions - - // Wraps a pointer within the array. Assumes 'beg_ <= ptr < end_+capacity()' - char* normalize_(char*); - - char* acquire_read_block_(std::size_t length); - char* try_acquire_read_block_(std::size_t length); - void copy_read_block_(const char* block, char* data, std::size_t length); - void release_read_block_(char* block, std::size_t length); - - char* acquire_write_block_(std::size_t length); - char* try_acquire_write_block_(std::size_t length); - void copy_write_block_(char* block, const char* data, std::size_t length); - void release_write_block_(char* block, std::size_t length); - - char* begin_alloc_() { return beg_; } - const char* begin_alloc_() const { return beg_; } - char* end_alloc_() { return end_; } - const char* end_alloc_() const { return end_; } - char* begin_data_() { return rptr_; } - const char* begin_data_() const { return rptr_; } - char* end_data_() { return wptr_; } - const char* end_data_() const { return wptr_; } - - }; // class Ring_ - - template - class Ring : protected Ring_ - { - public: - //////////////////////////////////////////////////////////////////////////// - // CONSTRUCTORS AND DESTRUCTORS - //////////////////////////////////////////////////////////////////////////// - - // Constructs a ring without a buffer (capacity() == 0) - Ring(); - - // Constructs a ring with a buffer with a size - Ring(std::size_t size); - - // Moves the buffer between rings, assumes no concurrent operations - Ring(Ring&& ring); - - // Moves the buffer between rings, assumes no concurrent operations on - // either ring. Deallocates the buffer - Ring& operator= (Ring&& ring); - - // No copying - Ring(const Ring_&) = delete; - Ring& operator= (const Ring_&) = delete; - - // Deallocates the buffer, destructs stored data. - ~Ring(); - - public: - //////////////////////////////////////////////////////////////////////////// - // QUERY FUNCTIONS - //////////////////////////////////////////////////////////////////////////// - // Functions only report on the state of the ring - - // Returns the current amount of non-reserved used space (amount of written - // data that a read hasn't yet reserved). Over-reserved scenarios mean this - // number is not the ultimate source of truth with concurrent operations, - // but its the closest safe approximation. This, of course, doesn't report - // writes that have not completed. - std::size_t size() const; - - // Maximum amount of data that can be held - std::size_t capacity() const; - - public: - //////////////////////////////////////////////////////////////////////////// - // ACCESSORS AND MODIFIERS - //////////////////////////////////////////////////////////////////////////// - // All operations assume object has not been moved. Blocking operations run - // until operation is completed. Non-blocking operations fail if there is - // not enough space - - void read(T& data) noexcept; // blocking read - void write(const T& data) noexcept; // blocking write - void write(T&& data) noexcept; // blocking write - bool try_read(T& data) noexcept; // non-blocking read - bool try_write(const T& data) noexcept; // non-blocking write - bool try_write(T&& data) noexcept; // non-blocking write - - private: - //////////////////////////////////////////////////////////////////////////// - // PRIVATE HELPER FUNCTIONS - //////////////////////////////////////////////////////////////////////////// - - void destruct_(); - - }; // class Ring - - template - Ring::Ring() - : Ring_() - { } - - template - Ring::Ring(std::size_t size) - : Ring_(size * sizeof(T)) - { } - - template - Ring::Ring(Ring&& ring) - : Ring_(std::move(ring)) - { } - - template - Ring& Ring::operator= (Ring&& ring) - { - destruct_(); - - Ring_::operator= (ring); - - return *this; - } - - template - Ring::~Ring() - { - destruct_(); - } - - template - void Ring::destruct_() - { - if (size() == 0) - return; - - auto itr = begin_data_(); - auto end = end_data_(); - do - { - auto t = reinterpret_cast(itr); - t->~T(); - - itr = normalize_(itr + sizeof(T)); - } while (itr != end); - } - - template - std::size_t Ring::size() const - { - return Ring_::size() / sizeof(T); - } - - template - std::size_t Ring::capacity() const - { - return Ring_::capacity() / sizeof(T); - } - - template - void Ring::read(T& data) noexcept - { - static_assert(std::is_nothrow_move_assignable::value, "T move assignment must not throw"); - static_assert(std::is_nothrow_destructible::value, "T destructor must not throw"); - - auto block = acquire_read_block_(sizeof(T)); - - // critical section - auto t = reinterpret_cast(block); - data = std::move(*t); - t->~T(); - - release_read_block_(block, sizeof(T)); - } - - template - void Ring::write(const T& data) noexcept - { - static_assert(std::is_nothrow_copy_constructible::value, "T copy constructor must not throw"); - - auto block = acquire_write_block_(sizeof(T)); - - // critical section - new(block) T(data); - - release_write_block_(block, sizeof(T)); - } - - template - void Ring::write(T&& data) noexcept - { - static_assert(std::is_nothrow_move_constructible::value, "T move constructor must not throw"); - - auto block = acquire_write_block_(sizeof(T)); - - // critical section - new(block) T(std::move(data)); - - release_write_block_(block, sizeof(T)); - } - - template - bool Ring::try_read(T& data) noexcept - { - static_assert(std::is_nothrow_move_assignable::value, "T move assignment must not throw"); - static_assert(std::is_nothrow_destructible::value, "T destructor must not throw"); - - auto block = try_acquire_read_block_(sizeof(T)); - if (block == nullptr) - return false; - - // critical section - auto t = reinterpret_cast(block); - data = std::move(*t); - t->~T(); - - release_read_block_(block, sizeof(T)); - - return true; - } - - template - bool Ring::try_write(const T& data) noexcept - { - static_assert(std::is_nothrow_copy_constructible::value, "T copy constructor must not throw"); - - auto block = try_acquire_write_block_(sizeof(T)); - if (block == nullptr) - return false; - - // critical section - new(block) T(data); - - release_write_block_(block, sizeof(T)); - - return true; - } - - template - bool Ring::try_write(T&& data) noexcept - { - static_assert(std::is_nothrow_move_constructible::value, "T move constructor must not throw"); - - auto block = try_acquire_write_block_(sizeof(T)); - if (block == nullptr) - return false; - - // critical section - new(block) T(std::move(data)); - - release_write_block_(block, sizeof(T)); - - return true; - } - -} // namespace wilt - -#endif // !WILT_RING_H \ No newline at end of file diff --git a/wfserver.pro b/wfserver.pro index 0d7de1f..fe8be7e 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -147,7 +147,6 @@ SOURCES += main.cpp\ resampler/resample.c \ rigctld.cpp \ tcpserver.cpp \ - ring/ring.cpp HEADERS += servermain.h \ commhandler.h \ @@ -166,6 +165,5 @@ HEADERS += servermain.h \ repeaterattributes.h \ rigctld.h \ ulaw.h \ - ring/ring.h \ tcpserver.h \ audiotaper.h diff --git a/wfview.pro b/wfview.pro index 28f75e9..3d94b70 100644 --- a/wfview.pro +++ b/wfview.pro @@ -174,7 +174,6 @@ SOURCES += main.cpp\ resampler/resample.c \ repeatersetup.cpp \ rigctld.cpp \ - ring/ring.cpp \ transceiveradjustments.cpp \ selectradio.cpp \ tcpserver.cpp \ @@ -202,7 +201,6 @@ HEADERS += wfmain.h \ repeaterattributes.h \ rigctld.h \ ulaw.h \ - ring/ring.h \ transceiveradjustments.h \ audiotaper.h \ selectradio.h \ diff --git a/wfview.vcxproj b/wfview.vcxproj index 2954a6f..a495694 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,8 +16,7 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild - + $(MSBuildProjectDirectory)\QtMsBuild v142 @@ -37,10 +36,7 @@ debug\ wfview - - - - + @@ -48,34 +44,8 @@ - - - - - - wfview-debug\ - wfview-debug\ - wfview - true - - - release\ - release\ - wfview - true - false - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - - + debug\debug\wfviewtruerelease\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) @@ -87,16 +57,14 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="8ec62fe";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="ac70ba5";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - - + MultiThreadedDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -117,28 +85,9 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"8ec62fe\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"ac70ba5\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) @@ -150,14 +99,13 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="8ec62fe";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="ac70ba5";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -176,28 +124,9 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"8ec62fe\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"ac70ba5\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -215,7 +144,6 @@ - @@ -226,55 +154,207 @@ + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document true @@ -291,21 +371,121 @@ release\moc_predefs.h;%(Outputs) true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -339,16 +519,30 @@ - resources - resources - + + + + + + + + + + resourcesresources - style - style - + + + + + + + + + + stylestyle @@ -362,9 +556,6 @@ - - - - + \ No newline at end of file diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index d3be9c0..b7c023b 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -95,9 +95,6 @@ Source Files - - Source Files - Source Files @@ -127,9 +124,6 @@ Header Files - - Header Files - Header Files @@ -181,9 +175,6 @@ Header Files - - Header Files - Header Files @@ -213,12 +204,59 @@ + + + + + + + + + + Generated Files Generated Files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -375,6 +413,6 @@ - + \ No newline at end of file From 38fdec3da6f69bf95f902048624cdab843488d5a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 5 Apr 2022 16:47:43 +0100 Subject: [PATCH 128/323] More tidying and use float resampler --- audiohandler.cpp | 353 +++++++++++++++++++++++++---------------------- audiohandler.h | 59 +++----- 2 files changed, 211 insertions(+), 201 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index db4a68b..61991f9 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -66,12 +66,23 @@ bool audioHandler::init(audioSetup setupIn) setup.format.setSampleType(QAudioFormat::UnSignedInt); qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; + if (setup.port.isNull()) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins."; + return false; + } + if (setup.codec == 0x01 || setup.codec == 0x20) { + /* Althought uLaw is 8bit unsigned, it is 16bit signed once decoded*/ setup.ulaw = true; + setup.format.setSampleSize(16); + setup.format.setSampleType(QAudioFormat::SignedInt); + } if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20 || setup.codec == 0x80) { setup.format.setChannelCount(2); } + if (setup.codec == 0x04 || setup.codec == 0x10 || setup.codec == 0x40 || setup.codec == 0x80) { setup.format.setSampleSize(16); setup.format.setSampleType(QAudioFormat::SignedInt); @@ -95,25 +106,9 @@ bool audioHandler::init(audioSetup setupIn) this->setVolume(setup.localAFgain); } -/* format.setSampleSize(16); - format.setChannelCount(2); - format.setSampleRate(INTERNAL_SAMPLE_RATE); - format.setCodec("audio/pcm"); - format.setByteOrder(QAudioFormat::LittleEndian); - format.setSampleType(QAudioFormat::SignedInt); */ format = setup.port.preferredFormat(); - qDebug(logAudio()) << "Preferred Format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() << + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Preferred Format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() << "Sample Rate" << format.sampleRate() << "Codec" << format.codec() << "Sample Type" << format.sampleType(); - if (setup.port.isNull()) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins."; - return false; - } - else if (!setup.port.isFormatSupported(format)) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Format not supported, choosing nearest supported format - which may not work!"; - format=setup.port.nearestFormat(format); - } if (format.channelCount() > 2) { format.setChannelCount(2); } @@ -123,29 +118,22 @@ bool audioHandler::init(audioSetup setupIn) return false; } - devChannels = format.channelCount(); - nativeSampleRate = format.sampleRate(); - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Internal: sample rate" << format.sampleRate() << "channel count" << format.channelCount(); // We "hopefully" now have a valid format that is supported so try connecting if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); - //connect(audioInput, SIGNAL(notify()), SLOT(notified())); - isInitialized = true; } else { audioOutput = new QAudioOutput(setup.port, format, this); - - isInitialized = true; } // Setup resampler and opus if they are needed. int resample_error = 0; int opus_err = 0; if (setup.isinput) { - resampler = wf_resampler_init(devChannels, nativeSampleRate, setup.format.sampleRate(), setup.resampleQuality, &resample_error); + resampler = wf_resampler_init(format.channelCount(), format.sampleRate(), setup.format.sampleRate(), setup.resampleQuality, &resample_error); if (setup.codec == 0x40 || setup.codec == 0x80) { // Opus codec encoder = opus_encoder_create(setup.format.sampleRate(), setup.format.channelCount(), OPUS_APPLICATION_AUDIO, &opus_err); @@ -160,7 +148,7 @@ bool audioHandler::init(audioSetup setupIn) //resampBufs = new r8b::CFixedBuffer[format.channelCount()]; //resamps = new r8b::CPtrKeeper[format.channelCount()]; - resampler = wf_resampler_init(devChannels, setup.format.sampleRate(), this->nativeSampleRate, setup.resampleQuality, &resample_error); + resampler = wf_resampler_init(format.channelCount(), setup.format.sampleRate(), format.sampleRate(), setup.resampleQuality, &resample_error); if (setup.codec == 0x40 || setup.codec == 0x80) { // Opus codec decoder = opus_decoder_create(setup.format.sampleRate(), setup.format.sampleRate(), &opus_err); @@ -177,7 +165,7 @@ bool audioHandler::init(audioSetup setupIn) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "thread id" << QThread::currentThreadId(); this->start(); - return isInitialized; + return true; } void audioHandler::start() @@ -284,8 +272,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) } livePacket.data.clear(); livePacket.data = outPacket; // Replace incoming data with converted. - setup.format.setSampleSize(16); - setup.format.setSampleType(QAudioFormat::SignedInt); // Buffer now contains 16bit signed samples. } @@ -304,6 +290,10 @@ void audioHandler::incomingAudio(audioPacket inPacket) VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); samplesF = samplesI.cast() / float(std::numeric_limits::max());; } + else { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); + } + /* samplesF is now an Eigen Vector of the current samples in float format */ @@ -322,6 +312,25 @@ void audioHandler::incomingAudio(audioPacket inPacket) } + if (resampleRatio != 1.0) { + + // We need to resample + // We have a stereo 16bit stream. + livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + quint32 outFrames = ((livePacket.data.length() / sizeof(float) / format.channelCount()) * resampleRatio); + quint32 inFrames = (livePacket.data.length() / sizeof(float) / format.channelCount()); + QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. + const float* in = (float*)livePacket.data.data(); + float* out = (float*)outPacket.data(); + + int err = 0; + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + if (err) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + } + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } + if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) { Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); @@ -349,25 +358,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) } - if (resampleRatio != 1.0) { - - // We need to resample - // We have a stereo 16bit stream. - quint32 outFrames = ((livePacket.data.length() / 2 / devChannels) * resampleRatio); - quint32 inFrames = (livePacket.data.length() / 2 / devChannels); - QByteArray outPacket(outFrames * 4, (char)0xff); // Preset the output buffer size. - - const qint16* in = (qint16*)livePacket.data.constData(); - qint16* out = (qint16*)outPacket.data(); - - int err = 0; - err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames); - if (err) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - livePacket.data.clear(); - livePacket.data = outPacket; // Replace incoming data with converted. - } //qDebug(logAudio()) << "Adding packet to buffer:" << livePacket.seq << ": " << livePacket.data.length(); @@ -389,6 +379,157 @@ void audioHandler::incomingAudio(audioPacket inPacket) return; } + +void audioHandler::getNextAudioChunk(QByteArray& ret) +{ + audioPacket livePacket; + livePacket.sent = 0; + if (audioDevice != Q_NULLPTR) { + livePacket.data = audioDevice->readAll(); + if (livePacket.data.length() > 0) + { + Eigen::VectorXf samplesF; + if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32) + { + VectorXint32 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); + } + else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) + { + VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); + } + else if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) + { + VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } + else if (format.sampleType() == QAudioFormat::Float) + { + samplesF = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(float))); + } + else { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType() << format.sampleSize(); + } + + /* samplesF is now an Eigen Vector of the current samples in float format */ + + // Set the max amplitude found in the vector + amplitude = samplesF.array().abs().maxCoeff(); + + // Channel count should now match the device that audio is going to (rig) + + if (resampleRatio != 1.0) { + + // We need to resample + // We have a stereo 16bit stream. + livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); + quint32 inFrames = (samplesF.size() / format.channelCount()); + + QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. + const float* in = (float*)livePacket.data.data(); + float* out = (float*)outPacket.data(); + + int err = 0; + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + if (err) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + } + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } + + // If we need to drop one of the audio channels, do it now + if (format.channelCount() == 2 && setup.format.channelCount() == 1) { + Eigen::VectorXf samplesTemp(samplesF.size() / 2); + samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); + samplesF = samplesTemp; + } + + if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXuint8 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + } + if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint16 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + } + else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint32 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); + } + else if (setup.format.sampleType() == QAudioFormat::Float) + { + livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + } + else { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); + } + + //qDebug(logAudio()) << "Now mono, length" << packet.data.length(); + + if (setup.ulaw) + { + QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff); + qint16* in = (qint16*)livePacket.data.data(); + for (int f = 0; f < outPacket.length(); f++) + { + qint16 sample = *in++; + if (setup.ulaw) { + int sign = (sample >> 8) & 0x80; + if (sign) + sample = (short)-sample; + if (sample > cClip) + sample = cClip; + sample = (short)(sample + cBias); + int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; + int mantissa = (sample >> (exponent + 3)) & 0x0F; + int compressedByte = ~(sign | (exponent << 4) | mantissa); + outPacket[f] = (quint8)compressedByte; + } + } + livePacket.data.clear(); + livePacket.data = outPacket; // Copy output packet back to input buffer. + } + + else if (setup.codec == 0x40 || setup.codec == 0x80) + { + //Are we using the opus codec? + qint16* in = (qint16*)livePacket.data.data(); + + /* Encode the frame. */ + QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size + unsigned char* out = (unsigned char*)outPacket.data(); + + int nbBytes = opus_encode(encoder, in, (setup.format.sampleRate() / 50), out, outPacket.length()); + if (nbBytes < 0) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); + return; + } + else { + outPacket.resize(nbBytes); + livePacket.data.clear(); + livePacket.data = outPacket; // Replace incoming data with converted. + } + + } + + ret = livePacket.data; + + } + } + return; + +} + + void audioHandler::changeLatency(const quint16 newSize) { @@ -398,7 +539,7 @@ void audioHandler::changeLatency(const quint16 newSize) stop(); start(); } - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << getAudioDuration(audioOutput->bufferSize(),format) <<"ms"; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << getAudioDuration(audioOutput->bufferSize(), format) << "ms"; } @@ -409,120 +550,6 @@ int audioHandler::getLatency() -void audioHandler::getNextAudioChunk(QByteArray& ret) -{ - audioPacket packet; - packet.sent = 0; - if (audioDevice != Q_NULLPTR) { - packet.data = audioDevice->readAll(); - } - if (packet.data.length() > 0) - { - // Packet will arrive as stereo interleaved 16bit 48K - if (resampleRatio != 1.0) - { - quint32 outFrames = ((packet.data.length() / 2 / devChannels) * resampleRatio); - quint32 inFrames = (packet.data.length() / 2 / devChannels); - QByteArray outPacket((int)outFrames * 2 * devChannels, (char)0xff); - - const qint16* in = (qint16*)packet.data.constData(); - qint16* out = (qint16*)outPacket.data(); - - int err = 0; - err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames); - if (err) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - packet.data.clear(); - packet.data = outPacket; // Copy output packet back to input buffer. - } - - //qDebug(logAudio()) << "Now resampled, length" << packet.data.length(); - - int tempAmplitude = 0; - // Do we need to convert mono to stereo? - if (setup.format.channelCount() == 1 && devChannels > 1) - { - // Strip out right channel? - QByteArray outPacket(packet.data.length()/2, (char)0xff); - const qint16* in = (qint16*)packet.data.constData(); - qint16* out = (qint16*)outPacket.data(); - for (int f = 0; f < outPacket.length()/2; f++) - { - tempAmplitude = qMax(tempAmplitude, (int)(abs(*in) / 256)); - *out++ = *in++; - in++; // Skip each even channel. - } - packet.data.clear(); - packet.data = outPacket; // Copy output packet back to input buffer. - } - - //qDebug(logAudio()) << "Now mono, length" << packet.data.length(); - - if (setup.codec == 0x40 || setup.codec == 0x80) - { - //Are we using the opus codec? - qint16* in = (qint16*)packet.data.data(); - - /* Encode the frame. */ - QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size - unsigned char* out = (unsigned char*)outPacket.data(); - - int nbBytes = opus_encode(encoder, in, (setup.format.sampleRate() / 50), out, outPacket.length()); - if (nbBytes < 0) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); - return; - } - else { - outPacket.resize(nbBytes); - packet.data.clear(); - packet.data = outPacket; // Replace incoming data with converted. - } - - } - else if (setup.format.sampleSize() == 8) - { - - // Do we need to convert 16-bit to 8-bit? - QByteArray outPacket((int)packet.data.length() / 2, (char)0xff); - qint16* in = (qint16*)packet.data.data(); - for (int f = 0; f < outPacket.length(); f++) - { - qint16 sample = *in++; - if (setup.ulaw) { - int sign = (sample >> 8) & 0x80; - if (sign) - sample = (short)-sample; - if (sample > cClip) - sample = cClip; - sample = (short)(sample + cBias); - int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - int compressedByte = ~(sign | (exponent << 4) | mantissa); - outPacket[f] = (quint8)compressedByte; - } - else { - int compressedByte = (((sample + 32768) >> 8) & 0xff); - outPacket[f] = (quint8)compressedByte; - } - tempAmplitude = qMax(tempAmplitude, abs(outPacket[f])); - } - packet.data.clear(); - packet.data = outPacket; // Copy output packet back to input buffer. - } - amplitude = tempAmplitude; - - ret = packet.data; - - } - - return; - -} - - - quint16 audioHandler::getAmplitude() { return static_cast(amplitude * 255.0); diff --git a/audiohandler.h b/audiohandler.h index 8532d69..45b0219 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -1,63 +1,52 @@ #ifndef AUDIOHANDLER_H #define AUDIOHANDLER_H +/* QT Headers */ #include - #include #include #include #include +#include +#include +#include +#include +#include -#if defined(RTAUDIO) -#ifdef Q_OS_WIN -#include "RtAudio.h" -#else -#include "rtaudio/RtAudio.h" -#endif -#elif defined (PORTAUDIO) -#include "portaudio.h" -//#error "PORTAUDIO is not currently supported" -#else +/* QT Audio Headers */ #include #include #include #include #include -#endif - -#include "packettypes.h" - -typedef signed short MY_TYPE; -#define FORMAT RTAUDIO_SINT16 - -#include -#include -#include -#include +/* Current resampler code */ #include "resampler/speex_resampler.h" +/* Potential new resampler */ +//#include +//#include + + +/* Opus */ #ifdef Q_OS_WIN #include "opus.h" #else #include "opus/opus.h" #endif -#include "audiotaper.h" +/* Eigen */ #ifdef Q_OS_LINUX #include #else #include #endif -//#include -//#include +/* wfview Packet types */ +#include "packettypes.h" -#include - -//#define BUFFER_SIZE (32*1024) - -#define INTERNAL_SAMPLE_RATE 48000 +/* Logarithmic taper for volume control */ +#include "audiotaper.h" #define MULAW_BIAS 33 #define MULAW_MAX 0x1fff @@ -132,20 +121,13 @@ private: //r8b::CPtrKeeper* resamps; quint16 audioLatency; - bool chunkAvailable; quint32 lastSeq; quint32 lastSentSeq=0; qint64 elapsedMs = 0; - quint16 nativeSampleRate=0; - quint8 radioSampleBits; - quint8 radioChannels; - int delayedPackets=0; - QMapaudioBuffer; - double resampleRatio; volatile bool ready = false; @@ -153,8 +135,9 @@ private: quint16 currentLatency; float amplitude; qreal volume=1.0; - int devChannels; + audioSetup setup; + OpusEncoder* encoder=Q_NULLPTR; OpusDecoder* decoder=Q_NULLPTR; }; From 47772a4925a451a853a06519c23def7be39e5f84 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 5 Apr 2022 19:02:29 +0100 Subject: [PATCH 129/323] Few more audio fixes --- audiohandler.cpp | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 61991f9..8a82278 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -316,11 +316,10 @@ void audioHandler::incomingAudio(audioPacket inPacket) // We need to resample // We have a stereo 16bit stream. - livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); - quint32 outFrames = ((livePacket.data.length() / sizeof(float) / format.channelCount()) * resampleRatio); - quint32 inFrames = (livePacket.data.length() / sizeof(float) / format.channelCount()); + quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); + quint32 inFrames = (samplesF.size() / format.channelCount()); QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. - const float* in = (float*)livePacket.data.data(); + const float* in = (float*)samplesF.data(); float* out = (float*)outPacket.data(); int err = 0; @@ -357,11 +356,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); } - - - - //qDebug(logAudio()) << "Adding packet to buffer:" << livePacket.seq << ": " << livePacket.data.length(); - currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + getAudioDuration(audioOutput->bufferSize()-audioOutput->bytesFree(),format); if (audioDevice != Q_NULLPTR) { @@ -423,12 +417,11 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) // We need to resample // We have a stereo 16bit stream. - livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); quint32 inFrames = (samplesF.size() / format.channelCount()); QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. - const float* in = (float*)livePacket.data.data(); + const float* in = (float*)samplesF.data(); float* out = (float*)outPacket.data(); int err = 0; @@ -472,8 +465,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); } - //qDebug(logAudio()) << "Now mono, length" << packet.data.length(); - + /* Need to find a floating point uLaw encoder!*/ if (setup.ulaw) { QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff); @@ -497,7 +489,6 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) livePacket.data.clear(); livePacket.data = outPacket; // Copy output packet back to input buffer. } - else if (setup.codec == 0x40 || setup.codec == 0x80) { //Are we using the opus codec? @@ -518,11 +509,8 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) livePacket.data.clear(); livePacket.data = outPacket; // Replace incoming data with converted. } - } - ret = livePacket.data; - } } return; From 833371530ac4f06b92c70aca27514d551f1548e6 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 6 Apr 2022 19:39:43 +0100 Subject: [PATCH 130/323] Enable High DPI Scaling --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 2cf73d7..e95aea6 100644 --- a/main.cpp +++ b/main.cpp @@ -58,6 +58,7 @@ int main(int argc, char *argv[]) a.setOrganizationName("wfview"); a.setOrganizationDomain("wfview.org"); a.setApplicationName("wfview"); + Qt::AA_EnableHighDpiScaling; #ifdef QT_DEBUG debugMode = true; @@ -177,7 +178,6 @@ int main(int argc, char *argv[]) #else a.setWheelScrollLines(1); // one line per wheel click wfmain w(serialPortCL, hostCL, settingsFile); - w.show(); #endif From cfc22dcb30b84dcaa999b3a64e78c9986ecec30d Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 6 Apr 2022 19:40:14 +0100 Subject: [PATCH 131/323] Allow for 8bit signed (in case computer uses it!) --- audiohandler.cpp | 196 ++++++++++---------- wfserver.pro | 4 +- wfserver.vcxproj | 214 +++++++++++++--------- wfserver.vcxproj.filters | 30 ++- wfview.vcxproj | 387 ++++++++++----------------------------- wfview.vcxproj.filters | 52 +----- 6 files changed, 358 insertions(+), 525 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 8a82278..ad0939f 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -186,6 +186,7 @@ void audioHandler::start() qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio device failed to start()"; return; } + } @@ -214,9 +215,6 @@ void audioHandler::setVolume(unsigned char volume) void audioHandler::incomingAudio(audioPacket inPacket) { - // No point buffering audio until stream is actually running. - // Regardless of the radio stream format, the buffered audio will ALWAYS be - // 16bit sample interleaved stereo 48K (or whatever the native sample rate is) audioPacket livePacket = inPacket; @@ -297,8 +295,10 @@ void audioHandler::incomingAudio(audioPacket inPacket) /* samplesF is now an Eigen Vector of the current samples in float format */ + // Set the max amplitude found in the vector // Should it be before or after volume is applied? + amplitude = samplesF.array().abs().maxCoeff(); // Set the volume samplesF *= volume; @@ -398,6 +398,11 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); samplesF = samplesI.cast() / float(std::numeric_limits::max());; } + else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8) + { + VectorXint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } else if (format.sampleType() == QAudioFormat::Float) { samplesF = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(float))); @@ -409,108 +414,115 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) /* samplesF is now an Eigen Vector of the current samples in float format */ // Set the max amplitude found in the vector - amplitude = samplesF.array().abs().maxCoeff(); - - // Channel count should now match the device that audio is going to (rig) + if (samplesF.size() > 0) { + amplitude = samplesF.array().abs().maxCoeff(); + // Channel count should now match the device that audio is going to (rig) - if (resampleRatio != 1.0) { + if (resampleRatio != 1.0) { - // We need to resample - // We have a stereo 16bit stream. - quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); - quint32 inFrames = (samplesF.size() / format.channelCount()); - - QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. - const float* in = (float*)samplesF.data(); - float* out = (float*)outPacket.data(); + // We need to resample + // We have a stereo 16bit stream. + quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); + quint32 inFrames = (samplesF.size() / format.channelCount()); - int err = 0; - err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); - if (err) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } + QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. + const float* in = (float*)samplesF.data(); + float* out = (float*)outPacket.data(); - // If we need to drop one of the audio channels, do it now - if (format.channelCount() == 2 && setup.format.channelCount() == 1) { - Eigen::VectorXf samplesTemp(samplesF.size() / 2); - samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); - samplesF = samplesTemp; - } - - if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXuint8 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); - } - if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint16 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); - } - else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint32 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); - } - else if (setup.format.sampleType() == QAudioFormat::Float) - { - livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); - } - else { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); - } - - /* Need to find a floating point uLaw encoder!*/ - if (setup.ulaw) - { - QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff); - qint16* in = (qint16*)livePacket.data.data(); - for (int f = 0; f < outPacket.length(); f++) - { - qint16 sample = *in++; - if (setup.ulaw) { - int sign = (sample >> 8) & 0x80; - if (sign) - sample = (short)-sample; - if (sample > cClip) - sample = cClip; - sample = (short)(sample + cBias); - int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - int compressedByte = ~(sign | (exponent << 4) | mantissa); - outPacket[f] = (quint8)compressedByte; + int err = 0; + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + if (err) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); } - livePacket.data.clear(); - livePacket.data = outPacket; // Copy output packet back to input buffer. - } - else if (setup.codec == 0x40 || setup.codec == 0x80) - { - //Are we using the opus codec? - qint16* in = (qint16*)livePacket.data.data(); - /* Encode the frame. */ - QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size - unsigned char* out = (unsigned char*)outPacket.data(); + // If we need to drop one of the audio channels, do it now + if (format.channelCount() == 2 && setup.format.channelCount() == 1) { + Eigen::VectorXf samplesTemp(samplesF.size() / 2); + samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); + samplesF = samplesTemp; + } - int nbBytes = opus_encode(encoder, in, (setup.format.sampleRate() / 50), out, outPacket.length()); - if (nbBytes < 0) + if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); - return; + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint8 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); + } + else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXuint8 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + } + else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint16 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + } + else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint32 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); + } + else if (setup.format.sampleType() == QAudioFormat::Float) + { + livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); } else { - outPacket.resize(nbBytes); - livePacket.data.clear(); - livePacket.data = outPacket; // Replace incoming data with converted. + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); } + + /* Need to find a floating point uLaw encoder!*/ + if (setup.ulaw) + { + QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff); + qint16* in = (qint16*)livePacket.data.data(); + for (int f = 0; f < outPacket.length(); f++) + { + qint16 sample = *in++; + if (setup.ulaw) { + int sign = (sample >> 8) & 0x80; + if (sign) + sample = (short)-sample; + if (sample > cClip) + sample = cClip; + sample = (short)(sample + cBias); + int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; + int mantissa = (sample >> (exponent + 3)) & 0x0F; + int compressedByte = ~(sign | (exponent << 4) | mantissa); + outPacket[f] = (quint8)compressedByte; + } + } + livePacket.data.clear(); + livePacket.data = outPacket; // Copy output packet back to input buffer. + } + else if (setup.codec == 0x40 || setup.codec == 0x80) + { + //Are we using the opus codec? + qint16* in = (qint16*)livePacket.data.data(); + + /* Encode the frame. */ + QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size + unsigned char* out = (unsigned char*)outPacket.data(); + + int nbBytes = opus_encode(encoder, in, (setup.format.sampleRate() / 50), out, outPacket.length()); + if (nbBytes < 0) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); + return; + } + else { + outPacket.resize(nbBytes); + livePacket.data.clear(); + livePacket.data = outPacket; // Replace incoming data with converted. + } + } + ret = livePacket.data; } - ret = livePacket.data; } } return; diff --git a/wfserver.pro b/wfserver.pro index fe8be7e..74215d5 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -20,7 +20,7 @@ DEFINES += BUILD_WFSERVER CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new -WIN32:DESTDIR = wfview-release +WIN32:DESTDIR = wfview-debug } else { # For Release builds only: @@ -29,7 +29,7 @@ QMAKE_CXXFLAGS += -fvisibility=hidden QMAKE_CXXFLAGS += -fvisibility-inlines-hidden QMAKE_CXXFLAGS += -faligned-new linux:QMAKE_LFLAGS += -O2 -s -WIN32:DESTDIR = wfview-debug +WIN32:DESTDIR = wfview-release } # The following define makes your compiler emit warnings if you use diff --git a/wfserver.vcxproj b/wfserver.vcxproj index 177b88d..cbfc961 100644 --- a/wfserver.vcxproj +++ b/wfserver.vcxproj @@ -16,8 +16,7 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild - + $(MSBuildProjectDirectory)\QtMsBuild v142 @@ -37,10 +36,7 @@ debug\ wfserver - - - - + @@ -48,34 +44,8 @@ - - - - - - wfserver-debug\ - wfserver-debug\ - wfserver - true - - - release\ - release\ - wfserver - true - false - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - - + debug\debug\wfservertruerelease\release\wfservertruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport + .;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) @@ -87,16 +57,14 @@ Sync release\ MaxSpeed - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="cf7a947";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - - + MultiThreadedDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories) @@ -117,23 +85,9 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"cf7a947\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp .;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) @@ -145,14 +99,13 @@ Sync debug\ Disabled - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="cf7a947";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories) @@ -171,23 +124,9 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"cf7a947\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp @@ -199,7 +138,6 @@ - @@ -208,35 +146,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document true @@ -253,6 +266,24 @@ release\moc_predefs.h;%(Outputs) true + + + + + + + + + + + + + + + + + + @@ -285,16 +316,30 @@ - resources - resources - + + + + + + + + + + resourcesresources - style - style - + + + + + + + + + + stylestyle @@ -308,9 +353,6 @@ - - - - + \ No newline at end of file diff --git a/wfserver.vcxproj.filters b/wfserver.vcxproj.filters index 3e9270e..80cfac4 100644 --- a/wfserver.vcxproj.filters +++ b/wfserver.vcxproj.filters @@ -67,9 +67,6 @@ Source Files - - Source Files - Source Files @@ -123,9 +120,6 @@ Header Files - - Header Files - Header Files @@ -146,12 +140,34 @@ + + + + Generated Files Generated Files + + + + + + + + + + + + + + + + + + @@ -285,6 +301,6 @@ - + \ No newline at end of file diff --git a/wfview.vcxproj b/wfview.vcxproj index a495694..bf6d12b 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,7 +16,8 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild + $(MSBuildProjectDirectory)\QtMsBuild + v142 @@ -36,7 +37,10 @@ debug\ wfview - + + + + @@ -44,11 +48,37 @@ - debug\debug\wfviewtruerelease\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport - + + + + + + debug\ + debug\ + wfview + true + + + release\ + release\ + wfview + true + false + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + + - .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;..\kissfft;resampler;release;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -57,14 +87,16 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="ac70ba5";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - + + MultiThreadedDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -85,12 +117,31 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"ac70ba5\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + - .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;..\kissfft;resampler;debug;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -99,13 +150,14 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="ac70ba5";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -124,10 +176,30 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"ac70ba5\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + + @@ -154,207 +226,53 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Document true @@ -371,121 +289,21 @@ release\moc_predefs.h;%(Outputs) true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -519,30 +337,16 @@ - - - - - - - - - - resourcesresources + resources + resources + - - - - - - - - - - stylestyle + style + style + @@ -556,6 +360,9 @@ - + + + + \ No newline at end of file diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index b7c023b..d0e5df8 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -116,6 +116,9 @@ Source Files + + Source Files + @@ -204,59 +207,12 @@ - - - - - - - - - - Generated Files Generated Files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -413,6 +369,6 @@ - + \ No newline at end of file From f00051ecd47cc998b01f3536b3326f91ec0a4c49 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 6 Apr 2022 21:52:22 +0100 Subject: [PATCH 132/323] Use signal/slot for audio signal reporting --- audiohandler.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++- audiohandler.h | 7 +++++-- udphandler.cpp | 49 ++++++++++++++++++++++++---------------------- udphandler.h | 12 ++++++++++-- 4 files changed, 91 insertions(+), 28 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index ad0939f..d6f1172 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -124,9 +124,11 @@ bool audioHandler::init(audioSetup setupIn) if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); + connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); } else { audioOutput = new QAudioOutput(setup.port, format, this); + connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); } // Setup resampler and opus if they are needed. @@ -357,7 +359,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) } currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + getAudioDuration(audioOutput->bufferSize()-audioOutput->bytesFree(),format); - if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); } @@ -370,6 +371,8 @@ void audioHandler::incomingAudio(audioPacket inPacket) lastSentSeq = inPacket.seq; } + emit haveLevels(getAmplitude(), setup.latency, currentLatency,isUnderrun); + return; } @@ -525,6 +528,9 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) } } } + + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + return; } @@ -555,3 +561,46 @@ quint16 audioHandler::getAmplitude() return static_cast(amplitude * 255.0); } + + +void audioHandler::stateChanged(QAudio::State state) +{ + // Process the state + switch (state) + { + case QAudio::IdleState: + { + isUnderrun = true; + break; + } + case QAudio::ActiveState: + { + //qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio started!"; + if (underTimer == Q_NULLPTR) { + underTimer = new QTimer(); + underTimer->setSingleShot(true); + connect(underTimer, &QTimer::timeout, this, &audioHandler::clearUnderrun); + underTimer->start(500); + } + break; + } + case QAudio::SuspendedState: + { + break; + } + case QAudio::StoppedState: + { + break; + } + default: { + } + break; + } +} + +void audioHandler::clearUnderrun() +{ + isUnderrun = false; + delete underTimer; + underTimer = Q_NULLPTR; +} \ No newline at end of file diff --git a/audiohandler.h b/audiohandler.h index 45b0219..e10aeac 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -95,16 +95,18 @@ public slots: void incomingAudio(const audioPacket data); private slots: + void stateChanged(QAudio::State state); + void clearUnderrun(); signals: void audioMessage(QString message); void sendLatency(quint16 newSize); void haveAudioData(const QByteArray& data); - + void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under); private: - + bool isUnderrun = false; bool isInitialized=false; bool isReady = false; bool audioBuffered = false; @@ -140,6 +142,7 @@ private: OpusEncoder* encoder=Q_NULLPTR; OpusDecoder* decoder=Q_NULLPTR; + QTimer * underTimer=Q_NULLPTR; }; diff --git a/udphandler.cpp b/udphandler.cpp index 3fbfd41..509584b 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -135,6 +135,20 @@ void udpHandler::receiveDataFromUserToRig(QByteArray data) } } +void udpHandler::getRxLevels(quint16 amplitude,quint16 latency,quint16 current, bool under) { + status.rxAudioLevel = amplitude; + status.rxLatency = latency; + status.rxCurrentLatency = current; + status.rxUnderrun = under; +} + +void udpHandler::getTxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under) { + status.txAudioLevel = amplitude; + status.txLatency = latency; + status.txCurrentLatency = current; + status.txUnderrun = under; +} + void udpHandler::dataReceived() { while (udp->hasPendingDatagrams()) { @@ -186,13 +200,12 @@ void udpHandler::dataReceived() } QString tempLatency; - status.rxLatency = audio->audioLatency; - if (rxSetup.latency > audio->audioLatency) + if (status.rxLatency > status.rxCurrentLatency && !status.rxUnderrun) { - tempLatency = QString("%1 ms").arg(audio->audioLatency,3); + tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); } else { - tempLatency = QString("%1 ms").arg(audio->audioLatency,3); + tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); } QString txString=""; if (txSetup.codec == 0) { @@ -200,10 +213,6 @@ void udpHandler::dataReceived() } status.message = QString("
%1 rx latency: %2 / rtt: %3 ms / loss: %4/%5
").arg(txString).arg(tempLatency).arg(status.networkLatency, 3).arg(status.packetsLost, 3).arg(status.packetsSent, 3); - if (audio != Q_NULLPTR) { - status.rxAudioLevel = audio->getRxAmplitude(); - status.txAudioLevel = audio->getTxAmplitude(); - } emit haveNetworkStatus(status); } @@ -289,6 +298,8 @@ void udpHandler::dataReceived() QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16))); QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char))); + QObject::connect(audio, SIGNAL(haveRxLevels(quint16, quint16, quint16,bool)), this, SLOT(getRxLevels(quint16, quint16,quint16,bool))); + QObject::connect(audio, SIGNAL(haveTxLevels(quint16, quint16,quint16,bool)), this, SLOT(getTxLevels(quint16, quint16,quint16,bool))); streamOpened = true; } @@ -810,6 +821,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); + connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16,bool))); connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); txSetup.format.setChannelCount(1); // TX Audio is always single channel. @@ -822,6 +834,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint txAudioThread->start(QThread::TimeCriticalPriority); connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); + connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool))); connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); @@ -973,22 +986,13 @@ void udpAudio::setVolume(unsigned char value) emit haveSetVolume(value); } -quint16 udpAudio::getRxAmplitude() { - if (rxaudio != Q_NULLPTR) { - return rxaudio->getAmplitude(); - } - else { - return 0; - } +void udpAudio::getRxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under) { + + emit haveRxLevels(amplitude,latency, current, under); } -quint16 udpAudio::getTxAmplitude() { - if (txaudio != Q_NULLPTR) { - return txaudio->getAmplitude(); - } - else { - return 0; - } +void udpAudio::getTxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under) { + emit haveTxLevels(amplitude,latency, current, under); } void udpAudio::dataReceived() @@ -1040,7 +1044,6 @@ void udpAudio::dataReceived() // Need to do more testing but latency appears fine. //rxaudio->incomingAudio(tempAudio); emit haveAudioData(tempAudio); - audioLatency = rxaudio->getLatency(); } break; } diff --git a/udphandler.h b/udphandler.h index ca3aa2a..30757d3 100644 --- a/udphandler.h +++ b/udphandler.h @@ -43,6 +43,10 @@ struct networkStatus { quint8 txAudioLevel; quint16 rxLatency; quint16 txLatency; + bool rxUnderrun; + bool txUnderrun; + quint16 rxCurrentLatency; + quint16 txCurrentLatency; quint32 packetsSent=0; quint32 packetsLost=0; quint16 rtt=0; @@ -175,8 +179,6 @@ public: ~udpAudio(); int audioLatency = 0; - quint16 getRxAmplitude(); - quint16 getTxAmplitude(); signals: void haveAudioData(audioPacket data); @@ -186,10 +188,14 @@ signals: void haveChangeLatency(quint16 value); void haveSetVolume(unsigned char value); + void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); public slots: void changeLatency(quint16 value); void setVolume(unsigned char value); + void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); private: @@ -239,6 +245,8 @@ public slots: void setVolume(unsigned char value); void init(); void setCurrentRadio(quint8 radio); + void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); signals: From 0f7a5566fc10ca9393bc5d4760f0a0b0e07cb710 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 6 Apr 2022 22:02:43 +0100 Subject: [PATCH 133/323] Fix underrun handler --- audiohandler.cpp | 16 ++++++++++------ audiohandler.h | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index d6f1172..968ec39 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -166,7 +166,12 @@ bool audioHandler::init(audioSetup setupIn) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "thread id" << QThread::currentThreadId(); + underTimer = new QTimer(); + underTimer->setSingleShot(true); + connect(underTimer, &QTimer::timeout, this, &audioHandler::clearUnderrun); + this->start(); + return true; } @@ -571,15 +576,15 @@ void audioHandler::stateChanged(QAudio::State state) case QAudio::IdleState: { isUnderrun = true; + if (underTimer->isActive()) { + underTimer->stop(); + } break; } case QAudio::ActiveState: { //qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio started!"; - if (underTimer == Q_NULLPTR) { - underTimer = new QTimer(); - underTimer->setSingleShot(true); - connect(underTimer, &QTimer::timeout, this, &audioHandler::clearUnderrun); + if (!underTimer->isActive()) { underTimer->start(500); } break; @@ -601,6 +606,5 @@ void audioHandler::stateChanged(QAudio::State state) void audioHandler::clearUnderrun() { isUnderrun = false; - delete underTimer; - underTimer = Q_NULLPTR; + underTimer->stop(); } \ No newline at end of file diff --git a/audiohandler.h b/audiohandler.h index e10aeac..e0584e8 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -142,7 +142,7 @@ private: OpusEncoder* encoder=Q_NULLPTR; OpusDecoder* decoder=Q_NULLPTR; - QTimer * underTimer=Q_NULLPTR; + QTimer* underTimer; }; From 53cae9173dd601eccbf6610758f7d28eab2ca6e6 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 7 Apr 2022 16:03:50 +0100 Subject: [PATCH 134/323] Use floating point opus encoder/decoder --- audiohandler.cpp | 105 +++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 45 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 968ec39..da6607b 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -153,7 +153,7 @@ bool audioHandler::init(audioSetup setupIn) resampler = wf_resampler_init(format.channelCount(), setup.format.sampleRate(), format.sampleRate(), setup.resampleQuality, &resample_error); if (setup.codec == 0x40 || setup.codec == 0x80) { // Opus codec - decoder = opus_decoder_create(setup.format.sampleRate(), setup.format.sampleRate(), &opus_err); + decoder = opus_decoder_create(setup.format.sampleRate(), setup.format.channelCount(), &opus_err); qInfo(logAudio()) << "Creating opus decoder: " << opus_strerror(opus_err); } } @@ -224,47 +224,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) { audioPacket livePacket = inPacket; - - if (setup.codec == 0x40 || setup.codec == 0x80) { - /* Opus data */ - unsigned char* in = (unsigned char*)inPacket.data.data(); - - /* Decode the frame. */ - QByteArray outPacket((setup.format.sampleRate() / 50) * sizeof(qint16) * setup.format.channelCount(), (char)0xff); // Preset the output buffer size. - qint16* out = (qint16*)outPacket.data(); - int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.format.sampleRate()); - if (nSamples == -1) { - // No opus data yet? - return; - } - else if (nSamples != setup.format.sampleRate() / 50) - { - qDebug(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.format.sampleRate() / 50); - return; - } - if (livePacket.seq > lastSentSeq + 1) { - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.format.sampleRate() / 50), 1); - } - else { - nSamples = opus_decode(decoder, in, livePacket.data.size(), out, (setup.format.sampleRate() / 50), 0); - } - if (nSamples < 0) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decode failed:" << opus_strerror(nSamples) << "packet size" << livePacket.data.length(); - return; - } - else { - if (int(nSamples * sizeof(qint16) * setup.format.channelCount()) != outPacket.size()) - { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(qint16) * setup.format.channelCount() << "outPacket:" << outPacket.size(); - outPacket.resize(nSamples * sizeof(qint16) * setup.format.channelCount()); - } - //qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoded" << livePacket.data.size() << "bytes, into" << outPacket.length() << "bytes"; - livePacket.data.clear(); - livePacket.data = outPacket; // Replace incoming data with converted. - } - } - // Process uLaw. if (setup.ulaw) { @@ -281,6 +240,47 @@ void audioHandler::incomingAudio(audioPacket inPacket) } + /* Opus data */ + if (setup.codec == 0x40 || setup.codec == 0x80) { + unsigned char* in = (unsigned char*)inPacket.data.data(); + + //Decode the frame. + QByteArray outPacket((setup.format.sampleRate() / 50) * sizeof(float) * setup.format.channelCount(), (char)0xff); // Preset the output buffer size. + float* out = (float*)outPacket.data(); + int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.format.sampleRate()); + if (nSamples == -1) { + // No opus data yet? + return; + } + else if (nSamples != setup.format.sampleRate() / 50) + { + qDebug(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.format.sampleRate() / 50); + return; + } + if (livePacket.seq > lastSentSeq + 1) { + nSamples = opus_decode_float(decoder, Q_NULLPTR,0, out, (setup.format.sampleRate() / 50), 1); + } + else { + nSamples = opus_decode_float(decoder, in, livePacket.data.size(), out, (setup.format.sampleRate() / 50), 0); + } + if (nSamples < 0) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decode failed:" << opus_strerror(nSamples) << "packet size" << livePacket.data.length(); + return; + } + else { + if (int(nSamples * sizeof(float) * setup.format.channelCount()) != outPacket.size()) + { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(float) * setup.format.channelCount() << "outPacket:" << outPacket.size(); + outPacket.resize(nSamples * sizeof(float) * setup.format.channelCount()); + } + //qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoded" << livePacket.data.size() << "bytes, into" << outPacket.length() << "bytes"; + livePacket.data.clear(); + livePacket.data = outPacket; // Replace incoming data with converted. + } + setup.format.setSampleType(QAudioFormat::Float); + } + if (!livePacket.data.isEmpty()) { @@ -295,7 +295,11 @@ void audioHandler::incomingAudio(audioPacket inPacket) VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); samplesF = samplesI.cast() / float(std::numeric_limits::max());; } - else { + else if (setup.format.sampleType() == QAudioFormat::Float) { + samplesF = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(float))); + } + else + { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); } @@ -330,7 +334,13 @@ void audioHandler::incomingAudio(audioPacket inPacket) float* out = (float*)outPacket.data(); int err = 0; - err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + if (format.channelCount() == 1) { + err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); + } + else { + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + } + if (err) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } @@ -438,7 +448,12 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) float* out = (float*)outPacket.data(); int err = 0; - err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + if (format.channelCount() == 1) { + err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); + } + else { + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + } if (err) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } From a21a44df865549ec916c107ee1ec7263f8425e87 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 7 Apr 2022 17:35:58 +0100 Subject: [PATCH 135/323] Opus issue --- audiohandler.cpp | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index da6607b..1c00642 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -467,6 +467,29 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) samplesF = samplesTemp; } + + if (setup.codec == 0x40 || setup.codec == 0x80) + { + //Are we using the opus codec? + float* in = (float*)samplesF.data(); + + /* Encode the frame. */ + QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size + unsigned char* out = (unsigned char*)outPacket.data(); + + int nbBytes = opus_encode_float(encoder, in, (samplesF.size()/setup.format.channelCount()), out, outPacket.length()); + if (nbBytes < 0) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); + return; + } + else { + outPacket.resize(nbBytes); + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } + } + + if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) { Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); @@ -523,27 +546,6 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) livePacket.data.clear(); livePacket.data = outPacket; // Copy output packet back to input buffer. } - else if (setup.codec == 0x40 || setup.codec == 0x80) - { - //Are we using the opus codec? - qint16* in = (qint16*)livePacket.data.data(); - - /* Encode the frame. */ - QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size - unsigned char* out = (unsigned char*)outPacket.data(); - - int nbBytes = opus_encode(encoder, in, (setup.format.sampleRate() / 50), out, outPacket.length()); - if (nbBytes < 0) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); - return; - } - else { - outPacket.resize(nbBytes); - livePacket.data.clear(); - livePacket.data = outPacket; // Replace incoming data with converted. - } - } ret = livePacket.data; } } From d543f1474d4150471166e5adb8004bd5855b15a1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 7 Apr 2022 18:35:44 +0100 Subject: [PATCH 136/323] Update audiohandler.cpp --- audiohandler.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 1c00642..3a9d55a 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -83,11 +83,15 @@ bool audioHandler::init(audioSetup setupIn) setup.format.setChannelCount(2); } - if (setup.codec == 0x04 || setup.codec == 0x10 || setup.codec == 0x40 || setup.codec == 0x80) { + if (setup.codec == 0x04 || setup.codec == 0x10) { setup.format.setSampleSize(16); setup.format.setSampleType(QAudioFormat::SignedInt); } + if (setup.codec == 0x40 || setup.codec == 0x80) { + setup.format.setSampleType(QAudioFormat::Float); + } + qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name << ", bits" << setup.format.sampleSize() << ", codec" << setup.codec << @@ -245,23 +249,23 @@ void audioHandler::incomingAudio(audioPacket inPacket) unsigned char* in = (unsigned char*)inPacket.data.data(); //Decode the frame. - QByteArray outPacket((setup.format.sampleRate() / 50) * sizeof(float) * setup.format.channelCount(), (char)0xff); // Preset the output buffer size. + QByteArray outPacket((getAudioSize(20, setup.format)) * sizeof(float) * setup.format.channelCount(), (char)0xff); // Preset the output buffer size. float* out = (float*)outPacket.data(); int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.format.sampleRate()); if (nSamples == -1) { // No opus data yet? return; } - else if (nSamples != setup.format.sampleRate() / 50) + else if (nSamples != getAudioSize(20, setup.format)) { - qDebug(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.format.sampleRate() / 50); - return; + qDebug(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (getAudioSize(20, setup.format)); + //return; } if (livePacket.seq > lastSentSeq + 1) { - nSamples = opus_decode_float(decoder, Q_NULLPTR,0, out, (setup.format.sampleRate() / 50), 1); + nSamples = opus_decode_float(decoder, Q_NULLPTR,0, out, getAudioSize(20, setup.format), 1); } else { - nSamples = opus_decode_float(decoder, in, livePacket.data.size(), out, (setup.format.sampleRate() / 50), 0); + nSamples = opus_decode_float(decoder, in, livePacket.data.size(), out, (getAudioSize(20, setup.format)), 0); } if (nSamples < 0) { @@ -397,7 +401,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) audioPacket livePacket; livePacket.sent = 0; if (audioDevice != Q_NULLPTR) { - livePacket.data = audioDevice->readAll(); + livePacket.data = audioDevice->read(setup.format.bytesForDuration(20000)); // 20000uS is 20ms if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; From e4d5efb3d586ce16376e2db6d9d1a46c4169be28 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 7 Apr 2022 18:37:47 +0100 Subject: [PATCH 137/323] Update audiohandler.cpp --- audiohandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 3a9d55a..e8e61e0 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -401,7 +401,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) audioPacket livePacket; livePacket.sent = 0; if (audioDevice != Q_NULLPTR) { - livePacket.data = audioDevice->read(setup.format.bytesForDuration(20000)); // 20000uS is 20ms + livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format. if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; @@ -484,7 +484,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) int nbBytes = opus_encode_float(encoder, in, (samplesF.size()/setup.format.channelCount()), out, outPacket.length()); if (nbBytes < 0) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); return; } else { From 2a889488c4cbb09366dd85cf024531ad071aa5ab Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 7 Apr 2022 18:48:47 +0100 Subject: [PATCH 138/323] Update audiohandler.cpp --- audiohandler.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index e8e61e0..fac9df4 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -249,23 +249,23 @@ void audioHandler::incomingAudio(audioPacket inPacket) unsigned char* in = (unsigned char*)inPacket.data.data(); //Decode the frame. - QByteArray outPacket((getAudioSize(20, setup.format)) * sizeof(float) * setup.format.channelCount(), (char)0xff); // Preset the output buffer size. + QByteArray outPacket((960) * sizeof(float) * setup.format.channelCount(), (char)0xff); // Preset the output buffer size. float* out = (float*)outPacket.data(); int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.format.sampleRate()); if (nSamples == -1) { // No opus data yet? return; } - else if (nSamples != getAudioSize(20, setup.format)) + else if (nSamples != 960) { - qDebug(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (getAudioSize(20, setup.format)); + qDebug(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << 960; //return; } if (livePacket.seq > lastSentSeq + 1) { - nSamples = opus_decode_float(decoder, Q_NULLPTR,0, out, getAudioSize(20, setup.format), 1); + nSamples = opus_decode_float(decoder, Q_NULLPTR,0, out, 960, 1); } else { - nSamples = opus_decode_float(decoder, in, livePacket.data.size(), out, (getAudioSize(20, setup.format)), 0); + nSamples = opus_decode_float(decoder, in, livePacket.data.size(), out, 960, 0); } if (nSamples < 0) { @@ -400,6 +400,11 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) { audioPacket livePacket; livePacket.sent = 0; + // Don't start sending until we have setup.latency of audio buffered + if (audioDevice->bytesAvailable() < format.bytesForDuration((setup.latency*1000))) + { + return; + } if (audioDevice != Q_NULLPTR) { livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format. if (livePacket.data.length() > 0) From c027413ee2b44e9174107f8811614bdddc11d0f7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 7 Apr 2022 18:52:43 +0100 Subject: [PATCH 139/323] Update audiohandler.cpp --- audiohandler.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index fac9df4..eaddfcb 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -400,12 +400,8 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) { audioPacket livePacket; livePacket.sent = 0; - // Don't start sending until we have setup.latency of audio buffered - if (audioDevice->bytesAvailable() < format.bytesForDuration((setup.latency*1000))) - { - return; - } - if (audioDevice != Q_NULLPTR) { + // Don't start sending until we have at least 1/2 of setup.latency of audio buffered + if (audioDevice != Q_NULLPTR && audioDevice->bytesAvailable() > format.bytesForDuration(setup.latency * 500)) { livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format. if (livePacket.data.length() > 0) { From 86237afd8b1922a0efeaadaa325ecb7c58b920d8 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 7 Apr 2022 18:57:42 +0100 Subject: [PATCH 140/323] Update audiohandler.cpp --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index eaddfcb..5b9c618 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -401,7 +401,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) audioPacket livePacket; livePacket.sent = 0; // Don't start sending until we have at least 1/2 of setup.latency of audio buffered - if (audioDevice != Q_NULLPTR && audioDevice->bytesAvailable() > format.bytesForDuration(setup.latency * 500)) { + if (audioDevice != Q_NULLPTR && audioDevice->bytesAvailable() > format.bytesForDuration(setup.latency)) { livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format. if (livePacket.data.length() > 0) { From 4f5e4addb6316ceb7f0df28b2c5f2b6a36401417 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 7 Apr 2022 19:04:49 +0100 Subject: [PATCH 141/323] Update audiohandler.cpp --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 5b9c618..88247f4 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -401,7 +401,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) audioPacket livePacket; livePacket.sent = 0; // Don't start sending until we have at least 1/2 of setup.latency of audio buffered - if (audioDevice != Q_NULLPTR && audioDevice->bytesAvailable() > format.bytesForDuration(setup.latency)) { + if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR && audioInput->bytesReady() > format.bytesForDuration(setup.latency)) { livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format. if (livePacket.data.length() > 0) { From 2ca376a3c19eeb524c714534bd4d7db329e458e8 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 7 Apr 2022 19:23:38 +0100 Subject: [PATCH 142/323] Opus now should work with any native sample rate/type --- audiohandler.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 88247f4..b9755df 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -249,24 +249,22 @@ void audioHandler::incomingAudio(audioPacket inPacket) unsigned char* in = (unsigned char*)inPacket.data.data(); //Decode the frame. - QByteArray outPacket((960) * sizeof(float) * setup.format.channelCount(), (char)0xff); // Preset the output buffer size. - float* out = (float*)outPacket.data(); int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.format.sampleRate()); if (nSamples == -1) { // No opus data yet? return; } - else if (nSamples != 960) - { - qDebug(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << 960; - //return; - } + + QByteArray outPacket(nSamples*sizeof(float)*setup.format.channelCount(), (char)0xff); // Preset the output buffer size. + float* out = (float*)outPacket.data(); + if (livePacket.seq > lastSentSeq + 1) { - nSamples = opus_decode_float(decoder, Q_NULLPTR,0, out, 960, 1); + nSamples = opus_decode_float(decoder, Q_NULLPTR,0, out, nSamples, 1); } else { - nSamples = opus_decode_float(decoder, in, livePacket.data.size(), out, 960, 0); + nSamples = opus_decode_float(decoder, in, livePacket.data.size(), out, nSamples, 0); } + if (nSamples < 0) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decode failed:" << opus_strerror(nSamples) << "packet size" << livePacket.data.length(); @@ -401,7 +399,8 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) audioPacket livePacket; livePacket.sent = 0; // Don't start sending until we have at least 1/2 of setup.latency of audio buffered - if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR && audioInput->bytesReady() > format.bytesForDuration(setup.latency)) { + // For some reason the audioDevice->bytesAvailable() function always returns 0? + if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR && audioInput->bytesReady() > format.bytesForDuration(setup.latency*500)) { livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format. if (livePacket.data.length() > 0) { From 23b37f27543197dc9d55bf5069518248dd3e8e87 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 8 Apr 2022 09:53:02 +0100 Subject: [PATCH 143/323] Attempt to fix weird retransmission bug --- packettypes.h | 2 +- udphandler.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packettypes.h b/packettypes.h index 51888ce..d8a17d3 100644 --- a/packettypes.h +++ b/packettypes.h @@ -15,7 +15,7 @@ #define LOCK_PERIOD 10 // How long to try to lock mutex (ms) #define STALE_CONNECTION 15 // Not heard from in this many seconds #define BUFSIZE 500 // Number of packets to buffer -#define MAX_MISSING 50 // Make the maximum number of possible missing packets much less than total buffer size! +#define MAX_MISSING 50 // More than this indicates serious network problem #define AUDIO_PERIOD 20 #define GUIDLEN 16 diff --git a/udphandler.cpp b/udphandler.cpp index 509584b..86f2aeb 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1246,17 +1246,21 @@ void udpBase::dataReceived(QByteArray r) rxSeqBuf.erase(rxSeqBuf.begin()); } rxSeqBuf.insert(in->seq, QTime::currentTime()); - } + } else { - if (in->seq < rxSeqBuf.firstKey()) + if (in->seq < rxSeqBuf.firstKey() || in->seq - rxSeqBuf.lastKey() > MAX_MISSING) { - qInfo(logUdp()) << this->metaObject()->className() << ": ******* seq number has rolled over ****** previous highest: " << hex << rxSeqBuf.lastKey() << " current: " << hex << in->seq; + qInfo(logUdp()) << this->metaObject()->className() << "Large seq number gap detected, previous highest: " << hex << rxSeqBuf.lastKey() << " current: " << hex << in->seq; //seqPrefix++; // Looks like it has rolled over so clear buffer and start again. rxSeqBuf.clear(); - rxMissing.clear(); + // Add this packet to the incoming buffer + rxSeqBuf.insert(in->seq, QTime::currentTime()); rxBufferMutex.unlock(); + missingMutex.lock(); + rxMissing.clear(); + missingMutex.unlock(); return; } @@ -1268,6 +1272,7 @@ void udpBase::dataReceived(QByteArray r) // We are likely missing packets then! missingMutex.lock(); //int missCounter = 0; + // Sanity check! for (quint16 f = rxSeqBuf.lastKey() + 1; f < in->seq; f++) { if (rxSeqBuf.size() > BUFSIZE) From 87b1a9f84d2367a3b33895fc4baed96e0bf2fe4b Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 8 Apr 2022 09:58:58 +0100 Subject: [PATCH 144/323] Change some log messages --- udphandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 86f2aeb..812b44c 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1219,14 +1219,14 @@ void udpBase::dataReceived(QByteArray r) quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; QMap::iterator match = txSeqBuf.find(seq); if (match == txSeqBuf.end()) { - qDebug(logUdp()) << this->metaObject()->className() << ": Requested packet " << hex << seq << " not found"; + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << hex << seq << " not found"; // Just send idle packet. sendControl(false, 0, seq); } else { // Found matching entry? // Send "untracked" as it has already been sent once. - qDebug(logUdp()) << this->metaObject()->className() << ": Sending retransmit (range) of " << hex << match->seqNum; + qDebug(logUdp()) << this->metaObject()->className() << ": Remote has requested retransmit of " << hex << match->seqNum; match->retransmitCount++; udpMutex.lock(); udp->writeDatagram(match->data, radioIP, port); From 0aaf5de9764bbd71d8ee51a321a9c1ace7951003 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 8 Apr 2022 11:22:33 +0100 Subject: [PATCH 145/323] Hopefully fix silly retransmit bug --- udphandler.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 812b44c..2854ca9 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1269,18 +1269,19 @@ void udpBase::dataReceived(QByteArray r) // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. if (in->seq > rxSeqBuf.lastKey() + 1) { + qInfo(logUdp()) << this->metaObject()->className() << "1 or more missing packets detected, previous: " << hex << rxSeqBuf.lastKey() << " current: " << hex << in->seq; // We are likely missing packets then! missingMutex.lock(); //int missCounter = 0; // Sanity check! - for (quint16 f = rxSeqBuf.lastKey() + 1; f < in->seq; f++) + for (quint16 f = rxSeqBuf.lastKey() + 1; f <= in->seq; f++) { if (rxSeqBuf.size() > BUFSIZE) { rxSeqBuf.erase(rxSeqBuf.begin()); } rxSeqBuf.insert(f, QTime::currentTime()); - if (!rxMissing.contains(f)) + if (f != in->seq && !rxMissing.contains(f)) { rxMissing.insert(f, 0); } From 2ee20d9415c8e290e6603d25c4bdaee9a7e3aa72 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 8 Apr 2022 12:12:31 +0100 Subject: [PATCH 146/323] Fix max build --- audiohandler.h | 2 +- wfview.pro | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/audiohandler.h b/audiohandler.h index e0584e8..31675d3 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -36,7 +36,7 @@ #endif /* Eigen */ -#ifdef Q_OS_LINUX +#ifndef Q_OS_WIN #include #else #include diff --git a/wfview.pro b/wfview.pro index 3d94b70..c72f27c 100644 --- a/wfview.pro +++ b/wfview.pro @@ -151,8 +151,8 @@ macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread -lopus !linux:INCLUDEPATH += ../opus/include -!linux:INCLUDEPATH += ../eigen -!linux:INCLUDEPATH += ../r8brain-free-src +win32:INCLUDEPATH += ../eigen +win32:INCLUDEPATH += ../r8brain-free-src INCLUDEPATH += resampler From 1f25ca67e8cff514b74c318f863cac6cf63dd54b Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 8 Apr 2022 14:40:05 +0100 Subject: [PATCH 147/323] Fixes to TCP server and attempt auto port detection on non-linux! --- rigcommander.cpp | 37 ++++++++------- rigcommander.h | 4 +- servermain.cpp | 5 +- servermain.h | 5 +- wfmain.cpp | 118 ++++++++++++++++++++++++++++------------------- wfmain.h | 5 +- 6 files changed, 101 insertions(+), 73 deletions(-) diff --git a/rigcommander.cpp b/rigcommander.cpp index f0380dd..8cdde8b 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -40,7 +40,7 @@ rigCommander::~rigCommander() } -void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp) +void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp,quint16 tcpPort) { // construct // TODO: Bring this parameter and the comm port from the UI. @@ -61,7 +61,11 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu comm = new commHandler(rigSerialPort, rigBaudRate); ptty = new pttyHandler(vsp); - tcp = new tcpServer(); + if (tcpPort > 0) { + tcp = new tcpServer(this); + tcp->startServer(tcpPort); + } + // data from the comm port to the program: connect(comm, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray))); @@ -69,12 +73,13 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu // data from the ptty to the rig: connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray))); - // data from the tcp port to the rig: - connect(tcp, SIGNAL(readyRead(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray))); - // data from the program to the comm port: connect(this, SIGNAL(dataForComm(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray))); - connect(this, SIGNAL(dataForComm(QByteArray)), tcp, SLOT(dataToPort(QByteArray))); + if (tcpPort > 0) { + // data from the tcp port to the rig: + connect(tcp, SIGNAL(receiveData(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray))); + connect(comm, SIGNAL(haveDataFromPort(QByteArray)), tcp, SLOT(sendData(QByteArray))); + } connect(this, SIGNAL(toggleRTS(bool)), comm, SLOT(setRTS(bool))); // data from the rig to the ptty: @@ -93,7 +98,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu } -void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp) +void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcpPort) { // construct // TODO: Bring this parameter and the comm port from the UI. @@ -128,19 +133,16 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud ptty = new pttyHandler(vsp); - tcp = new tcpServer(this); - tcp->startServer(5010); - + if (tcpPort > 0) { + tcp = new tcpServer(this); + tcp->startServer(tcpPort); + } // Data from UDP to the program connect(udp, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray))); // data from the rig to the ptty: connect(udp, SIGNAL(haveDataFromPort(QByteArray)), ptty, SLOT(receiveDataFromRigToPtty(QByteArray))); - // data from the rig to tcp: - connect(udp, SIGNAL(haveDataFromPort(QByteArray)), tcp, SLOT(sendData(QByteArray))); - - // Audio from UDP connect(udp, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); @@ -150,8 +152,11 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud // data from the ptty to the rig: connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); - // data from the tcp port to the Rig: - connect(tcp, SIGNAL(receiveData(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); + if (tcpPort > 0) { + // data from the tcp port to the rig: + connect(tcp, SIGNAL(receiveData(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray))); + connect(udp, SIGNAL(haveDataFromPort(QByteArray)), tcp, SLOT(sendData(QByteArray))); + } connect(this, SIGNAL(haveChangeLatency(quint16)), udp, SLOT(changeLatency(quint16))); connect(this, SIGNAL(haveSetVolume(unsigned char)), udp, SLOT(setVolume(unsigned char))); diff --git a/rigcommander.h b/rigcommander.h index dc10f40..c95d8d0 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -79,8 +79,8 @@ public: public slots: void process(); - void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp); - void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); + void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp, quint16 tcp); + void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcp); void closeComm(); void stateUpdated(); void setRTSforPTT(bool enabled); diff --git a/servermain.cpp b/servermain.cpp index 488515f..286cec5 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -89,7 +89,7 @@ void servermain::openRig() { //qInfo(logSystem()) << "Got rig"; QMetaObject::invokeMethod(radio->rig, [=]() { - radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none")); + radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none"),prefs.tcpPort); }, Qt::QueuedConnection); } } @@ -115,8 +115,6 @@ void servermain::makeRig() connect(radio->rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); // Rig comm setup: - //connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), radio->rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); - //connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32, QString)), radio->rig, SLOT(commSetup(unsigned char, QString, quint32, QString))); connect(this, SIGNAL(setRTSforPTT(bool)), radio->rig, SLOT(setRTSforPTT(bool))); connect(radio->rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); @@ -420,6 +418,7 @@ void servermain::setDefPrefs() defPrefs.serialPortRadio = QString("auto"); defPrefs.serialPortBaud = 115200; defPrefs.localAFgain = 255; + defPrefs.tcpPort = 0; udpDefPrefs.ipAddress = QString(""); udpDefPrefs.controlLANPort = 50001; diff --git a/servermain.h b/servermain.h index c9639cc..99d3ea7 100644 --- a/servermain.h +++ b/servermain.h @@ -154,8 +154,8 @@ signals: void sayFrequency(); void sayMode(); void sayAll(); - void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp); - void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); + void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp, quint16 tcp); + void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcp); void sendCloseComm(); void sendChangeLatency(quint16 latency); void initServer(); @@ -242,6 +242,7 @@ private: audioSetup txAudio; rigCapabilities rigCaps; bool haveRigCaps = false; + quint16 tcpPort; } prefs; preferences defPrefs; diff --git a/wfmain.cpp b/wfmain.cpp index 3bcca57..e98fe89 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -188,13 +188,12 @@ void wfmain::openRig() ui->lanEnableBtn->setChecked(true); usingLAN = true; // We need to setup the tx/rx audio: - emit sendCommSetup(prefs.radioCIVAddr, udpPrefs, rxSetup, txSetup, prefs.virtualSerialPort); + emit sendCommSetup(prefs.radioCIVAddr, udpPrefs, rxSetup, txSetup, prefs.virtualSerialPort, prefs.tcpPort); } else { ui->serialEnableBtn->setChecked(true); if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) { findSerialPort(); - } else { if(serialPortCL.isEmpty()) { @@ -204,7 +203,7 @@ void wfmain::openRig() } } usingLAN = false; - emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort); + emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort, prefs.tcpPort); ui->statusBar->showMessage(QString("Connecting to rig using serial port ").append(serialPortRig), 1000); } @@ -426,8 +425,8 @@ void wfmain::makeRig() connect(rig, SIGNAL(setRadioUsage(quint8, quint8, QString, QString)), selRad, SLOT(setInUse(quint8, quint8, QString, QString))); connect(selRad, SIGNAL(selectedRadio(quint8)), rig, SLOT(setCurrentRadio(quint8))); // Rig comm setup: - connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); - connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString))); + connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString, quint16)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString, quint16))); + connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString, quint16)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString, quint16))); connect(this, SIGNAL(setRTSforPTT(bool)), rig, SLOT(setRTSforPTT(bool))); connect(rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); @@ -477,49 +476,68 @@ void wfmain::findSerialPort() { // Find the ICOM radio connected, or, if none, fall back to OS default. // qInfo(logSystem()) << "Searching for serial port..."; - QDirIterator it73("/dev/serial/by-id", QStringList() << "*IC-7300*", QDir::Files, QDirIterator::Subdirectories); - QDirIterator it97("/dev/serial", QStringList() << "*IC-9700*A*", QDir::Files, QDirIterator::Subdirectories); - QDirIterator it785x("/dev/serial", QStringList() << "*IC-785*A*", QDir::Files, QDirIterator::Subdirectories); - QDirIterator it705("/dev/serial", QStringList() << "*IC-705*A", QDir::Files, QDirIterator::Subdirectories); - QDirIterator it7610("/dev/serial", QStringList() << "*IC-7610*A", QDir::Files, QDirIterator::Subdirectories); - QDirIterator itR8600("/dev/serial", QStringList() << "*IC-R8600*A", QDir::Files, QDirIterator::Subdirectories); + bool found = false; + // First try to find first Icom port: + foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) + { + if (serialPortInfo.serialNumber().left(3) == "IC-") { + qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) + serialPortRig = (QString("/dev/") + serialPortInfo.portName()); +#else + serialPortRig = serialPortInfo.portName(); +#endif + found = true; + break; + } + } - if(!it73.filePath().isEmpty()) - { - // IC-7300 - serialPortRig = it73.filePath(); // first - } else if(!it97.filePath().isEmpty()) - { - // IC-9700 - serialPortRig = it97.filePath(); - } else if(!it785x.filePath().isEmpty()) - { - // IC-785x - serialPortRig = it785x.filePath(); - } else if(!it705.filePath().isEmpty()) - { - // IC-705 - serialPortRig = it705.filePath(); - } else if(!it7610.filePath().isEmpty()) - { - // IC-7610 - serialPortRig = it7610.filePath(); - } else if(!itR8600.filePath().isEmpty()) - { - // IC-R8600 - serialPortRig = itR8600.filePath(); - } else { - //fall back: - qInfo(logSystem()) << "Could not find Icom serial port. Falling back to OS default. Use --port to specify, or modify preferences."; + if (!found) { + QDirIterator it73("/dev/serial/by-id", QStringList() << "*IC-7300*", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it97("/dev/serial", QStringList() << "*IC-9700*A*", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it785x("/dev/serial", QStringList() << "*IC-785*A*", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it705("/dev/serial", QStringList() << "*IC-705*A", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it7610("/dev/serial", QStringList() << "*IC-7610*A", QDir::Files, QDirIterator::Subdirectories); + QDirIterator itR8600("/dev/serial", QStringList() << "*IC-R8600*A", QDir::Files, QDirIterator::Subdirectories); + + if(!it73.filePath().isEmpty()) + { + // IC-7300 + serialPortRig = it73.filePath(); // first + } else if(!it97.filePath().isEmpty()) + { + // IC-9700 + serialPortRig = it97.filePath(); + } else if(!it785x.filePath().isEmpty()) + { + // IC-785x + serialPortRig = it785x.filePath(); + } else if(!it705.filePath().isEmpty()) + { + // IC-705 + serialPortRig = it705.filePath(); + } else if(!it7610.filePath().isEmpty()) + { + // IC-7610 + serialPortRig = it7610.filePath(); + } else if(!itR8600.filePath().isEmpty()) + { + // IC-R8600 + serialPortRig = itR8600.filePath(); + } + else { + //fall back: + qInfo(logSystem()) << "Could not find Icom serial port. Falling back to OS default. Use --port to specify, or modify preferences."; #ifdef Q_OS_MAC - serialPortRig = QString("/dev/tty.SLAB_USBtoUART"); + serialPortRig = QString("/dev/tty.SLAB_USBtoUART"); #endif #ifdef Q_OS_LINUX - serialPortRig = QString("/dev/ttyUSB0"); + serialPortRig = QString("/dev/ttyUSB0"); #endif #ifdef Q_OS_WIN - serialPortRig = QString("COM1"); + serialPortRig = QString("COM1"); #endif + } } } @@ -1177,7 +1195,7 @@ void wfmain::setSerialDevicesUI() { portList.append(serialPortInfo.portName()); #if defined(Q_OS_LINUX) || defined(Q_OS_MAC) - ui->serialDeviceListCombo->addItem(QString("/dev/")+serialPortInfo.portName(), i++); + ui->serialDeviceListCombo->addItem(QString("/dev/") + serialPortInfo.portName(), i++); #else ui->serialDeviceListCombo->addItem(serialPortInfo.portName(), i++); //qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); @@ -1202,18 +1220,18 @@ void wfmain::setSerialDevicesUI() #ifdef Q_OS_MAC QString vspName = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/rig-pty"; #else - QString vspName=QDir::homePath()+"/rig-pty"; + QString vspName = QDir::homePath() + "/rig-pty"; #endif - for (i=1;i<8;i++) { + for (i = 1; i < 8; i++) { ui->vspCombo->addItem(vspName + QString::number(i)); - if (QFile::exists(vspName+QString::number(i))) { - auto * model = qobject_cast(ui->vspCombo->model()); - auto *item = model->item(ui->vspCombo->count()-1); + if (QFile::exists(vspName + QString::number(i))) { + auto* model = qobject_cast(ui->vspCombo->model()); + auto* item = model->item(ui->vspCombo->count() - 1); item->setEnabled(false); } } - ui->vspCombo->addItem(vspName+QString::number(i)); + ui->vspCombo->addItem(vspName + QString::number(i)); ui->vspCombo->addItem(QString("None"), i++); #endif @@ -1370,6 +1388,7 @@ void wfmain::setDefPrefs() defPrefs.confirmExit = true; defPrefs.confirmPowerOff = true; defPrefs.meter2Type = meterNone; + defPrefs.tcpPort = 0; udpDefPrefs.ipAddress = QString(""); udpDefPrefs.controlLANPort = 50001; @@ -1515,6 +1534,8 @@ void wfmain::loadSettings() ui->lanEnableBtn->setChecked(prefs.enableLAN); ui->connectBtn->setEnabled(true); + prefs.tcpPort = settings->value("TcpServerPort", defPrefs.tcpPort).toInt(); + prefs.enableRigCtlD = settings->value("EnableRigCtlD", defPrefs.enableRigCtlD).toBool(); ui->enableRigctldChk->setChecked(prefs.enableRigCtlD); prefs.rigCtlPort = settings->value("RigCtlPort", defPrefs.rigCtlPort).toInt(); @@ -1966,6 +1987,7 @@ void wfmain::saveSettings() settings->beginGroup("LAN"); settings->setValue("EnableLAN", prefs.enableLAN); settings->setValue("EnableRigCtlD", prefs.enableRigCtlD); + settings->setValue("TcpServerPort", prefs.tcpPort); settings->setValue("RigCtlPort", prefs.rigCtlPort); settings->setValue("IPAddress", udpPrefs.ipAddress); settings->setValue("ControlLANPort", udpPrefs.controlLANPort); diff --git a/wfmain.h b/wfmain.h index 4832a18..6a092a4 100644 --- a/wfmain.h +++ b/wfmain.h @@ -164,8 +164,8 @@ signals: void sayFrequency(); void sayMode(); void sayAll(); - void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp); - void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); + void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp, quint16 tcp); + void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcp); void sendCloseComm(); void sendChangeLatency(quint16 latency); void initServer(); @@ -764,6 +764,7 @@ private: bool confirmExit; bool confirmPowerOff; meterKind meter2Type; + quint16 tcpPort; // plot scheme } prefs; From 03b324be1ec61aef2aa00697846e5709f65c4d9f Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 8 Apr 2022 15:28:46 +0100 Subject: [PATCH 148/323] Set HighDpiScaling attribute --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index e95aea6..e7e74b0 100644 --- a/main.cpp +++ b/main.cpp @@ -58,7 +58,7 @@ int main(int argc, char *argv[]) a.setOrganizationName("wfview"); a.setOrganizationDomain("wfview.org"); a.setApplicationName("wfview"); - Qt::AA_EnableHighDpiScaling; + a.setAttribute(Qt::AA_EnableHighDpiScaling); #ifdef QT_DEBUG debugMode = true; From 9e269d774ae0e3f68fecc8e97bafbbc1b7d6a5b5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 8 Apr 2022 15:51:59 +0100 Subject: [PATCH 149/323] Add some more startup logging and improve windows startup speed. --- wfmain.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index e98fe89..901860b 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -67,12 +67,16 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s loadSettings(); // Look for saved preferences setTuningSteps(); // TODO: Combine into preferences + qDebug(logSystem()) << "Running setUIToPrefs()"; setUIToPrefs(); + qDebug(logSystem()) << "Running setInititalTiming()"; setInitialTiming(); + qDebug(logSystem()) << "Running openRig()"; openRig(); + qDebug(logSystem()) << "Running rigConnections()"; rigConnections(); /* if (serverConfig.enabled && udp != Q_NULLPTR) { @@ -1155,6 +1159,7 @@ void wfmain::setAudioDevicesUI() // If no external library is configured, use QTMultimedia // Enumerate audio devices, need to do before settings are loaded. + qDebug(logSystem()) << "Finding audio input devices"; const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { #ifdef Q_OS_WIN @@ -1164,9 +1169,11 @@ void wfmain::setAudioDevicesUI() ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); #ifdef Q_OS_WIN } + #endif } + qDebug(logSystem()) << "Finding audio output devices"; const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (const QAudioDeviceInfo& deviceInfo : audioInputs) { #ifdef Q_OS_WIN @@ -1179,10 +1186,12 @@ void wfmain::setAudioDevicesUI() #endif } // Set these to default audio devices initially. + qDebug(logSystem()) << "Audio devices done."; rxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); txSetup.port = QAudioDeviceInfo::defaultInputDevice(); - serverRxSetup.port = QAudioDeviceInfo::defaultInputDevice(); - serverTxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); + serverRxSetup.port = txSetup.port; + serverTxSetup.port = rxSetup.port; + qDebug(logSystem()) << "Audio set to default device initially"; #endif } From 87c0850c5b89c665b0c92b28c627bdca23dbb505 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 8 Apr 2022 18:42:37 +0100 Subject: [PATCH 150/323] Fixed retransmit bug in server code. --- udpserver.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index 56fee68..9c3fd7d 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -908,20 +908,21 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) if (current->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - for (quint16 f = current->rxSeqBuf.lastKey() + 1; f < in->seq; f++) + for (quint16 f = current->rxSeqBuf.lastKey() + 1; f <= in->seq; f++) { - qInfo(logUdpServer()) << "Detected missing packet" << f; - if (current->rxSeqBuf.size() > BUFSIZE) { current->rxSeqBuf.remove(current->rxSeqBuf.firstKey()); } current->rxSeqBuf.insert(f, QTime::currentTime()); - if (!current->rxMissing.contains(f)) - { - current->rxMissing.insert(f, 0); + if (f != in->seq) { + qInfo(logUdpServer()) << "Detected missing packet" << f; + if (!current->rxMissing.contains(f)) + { + current->rxMissing.insert(f, 0); + } } } current->missMutex.unlock(); From a108f1ca997a201925026b7daba50d6a8db17f83 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 8 Apr 2022 18:55:41 +0100 Subject: [PATCH 151/323] TX Audio fixes. --- audiohandler.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index b9755df..7996808 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -400,8 +400,13 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) livePacket.sent = 0; // Don't start sending until we have at least 1/2 of setup.latency of audio buffered // For some reason the audioDevice->bytesAvailable() function always returns 0? - if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR && audioInput->bytesReady() > format.bytesForDuration(setup.latency*500)) { - livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format. + if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR && audioInput->bytesReady() > format.bytesForDuration(setup.latency)) { + if (setup.codec == 0x40 || setup.codec == 0x80) { + livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format. + } + else { + livePacket.data = audioDevice->readAll(); // 20000uS is 20ms in NATIVE format. + } if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; From 50c8b4e5455d462e6a7b5e571c4e854d9f6f1f2f Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 10 Apr 2022 23:13:51 +0100 Subject: [PATCH 152/323] Change audio output to use single/slot --- audiohandler.cpp | 35 +++++++++----- audiohandler.h | 11 +++-- udphandler.cpp | 108 ++++++++++++++++++------------------------- udphandler.h | 3 +- udpserver.cpp | 118 ++++++++++++++++++++--------------------------- udpserver.h | 2 +- 6 files changed, 127 insertions(+), 150 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 7996808..2008b7e 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -31,6 +31,12 @@ audioHandler::~audioHandler() audioOutput = Q_NULLPTR; } + if (audioTimer != Q_NULLPTR) { + audioTimer->stop(); + delete audioTimer; + audioTimer = Q_NULLPTR; + } + if (resampler != Q_NULLPTR) { speex_resampler_destroy(resampler); @@ -128,6 +134,11 @@ bool audioHandler::init(audioSetup setupIn) if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Starting audio timer"; + audioTimer = new QTimer(); + audioTimer->setTimerType(Qt::PreciseTimer); + connect(audioTimer, &QTimer::timeout, this, &audioHandler::getNextAudioChunk); + connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); } else { @@ -186,6 +197,7 @@ void audioHandler::start() if (setup.isinput) { audioDevice = audioInput->start(); connect(audioInput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); + audioTimer->start(setup.blockSize); } else { // Buffer size must be set before audio is started. @@ -394,19 +406,17 @@ void audioHandler::incomingAudio(audioPacket inPacket) } -void audioHandler::getNextAudioChunk(QByteArray& ret) +void audioHandler::getNextAudioChunk() { audioPacket livePacket; + livePacket.time= QTime::currentTime(); livePacket.sent = 0; - // Don't start sending until we have at least 1/2 of setup.latency of audio buffered - // For some reason the audioDevice->bytesAvailable() function always returns 0? - if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR && audioInput->bytesReady() > format.bytesForDuration(setup.latency)) { - if (setup.codec == 0x40 || setup.codec == 0x80) { - livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format. - } - else { - livePacket.data = audioDevice->readAll(); // 20000uS is 20ms in NATIVE format. - } + memcpy(&livePacket.guid, setup.guid, GUIDLEN); + + if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR) { + + livePacket.data = audioDevice->readAll(); + if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; @@ -555,12 +565,13 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) livePacket.data.clear(); livePacket.data = outPacket; // Copy output packet back to input buffer. } - ret = livePacket.data; + emit haveAudioData(livePacket); + //ret = livePacket.data; } } + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); } - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); return; diff --git a/audiohandler.h b/audiohandler.h index 31675d3..128b270 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -69,6 +69,8 @@ struct audioSetup { QAudioDeviceInfo port; quint8 resampleQuality; unsigned char localAFgain; + quint16 blockSize=20; // Each 'block' of audio is 20ms long by default. + quint8 guid[GUIDLEN]; }; // For QtMultimedia, use a native QIODevice @@ -85,7 +87,6 @@ public: void start(); void stop(); - void getNextAudioChunk(QByteArray &data); quint16 getAmplitude(); public slots: @@ -96,14 +97,14 @@ public slots: private slots: void stateChanged(QAudio::State state); - void clearUnderrun(); + void clearUnderrun(); + void getNextAudioChunk(); signals: void audioMessage(QString message); void sendLatency(quint16 newSize); - void haveAudioData(const QByteArray& data); + void haveAudioData(const audioPacket& data); void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under); - private: bool isUnderrun = false; @@ -114,9 +115,9 @@ private: QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; QIODevice* audioDevice=Q_NULLPTR; + QTimer* audioTimer = Q_NULLPTR; QAudioFormat format; QAudioDeviceInfo deviceInfo; - SpeexResamplerState* resampler = Q_NULLPTR; //r8b::CFixedBuffer* resampBufs; diff --git a/udphandler.cpp b/udphandler.cpp index 2854ca9..25f4cee 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -826,17 +826,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint txSetup.format.setChannelCount(1); // TX Audio is always single channel. - txaudio = new audioHandler(); - txAudioThread = new QThread(this); - txaudio->moveToThread(txAudioThread); - - txAudioThread->start(QThread::TimeCriticalPriority); - - connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); - connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool))); - - connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); sendControl(false, 0x03, 0x00); // First connect packet @@ -845,6 +835,18 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint pingTimer->start(PING_PERIOD); // send ping packets every 100ms if (enableTx) { + txaudio = new audioHandler(); + txAudioThread = new QThread(this); + + txaudio->moveToThread(txAudioThread); + + txAudioThread->start(QThread::TimeCriticalPriority); + + connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); + connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); + connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool))); + + connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); emit setupTxAudio(txSetup); } @@ -854,10 +856,6 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog); watchdogTimer->start(WATCHDOG_PERIOD); - txAudioTimer = new QTimer(); - txAudioTimer->setTimerType(Qt::PreciseTimer); - connect(txAudioTimer, &QTimer::timeout, this, &udpAudio::sendTxAudio); - areYouThereTimer = new QTimer(); connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0)); areYouThereTimer->start(AREYOUTHERE_PERIOD); @@ -889,13 +887,6 @@ udpAudio::~udpAudio() watchdogTimer = Q_NULLPTR; } - if (txAudioTimer != Q_NULLPTR) - { - qDebug(logUdp()) << "Stopping txaudio timer"; - txAudioTimer->stop(); - delete txAudioTimer; - } - if (rxAudioThread != Q_NULLPTR) { qDebug(logUdp()) << "Stopping rxaudio thread"; rxAudioThread->quit(); @@ -935,44 +926,41 @@ void udpAudio::sendTxAudio() if (txaudio == Q_NULLPTR) { return; } - QByteArray audio; - if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - txaudio->getNextAudioChunk(audio); - // Now we have the next audio chunk, we can release the mutex. - audioMutex.unlock(); - if (audio.length() > 0) { - int counter = 1; - int len = 0; +} - while (len < audio.length()) { - QByteArray partial = audio.mid(len, 1364); - audio_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p) + partial.length(); - p.sentid = myId; - p.rcvdid = remoteId; - if (partial.length() == 0xa0) { - p.ident = 0x9781; - } - else { - p.ident = 0x0080; // TX audio is always this? - } - p.datalen = (quint16)qToBigEndian((quint16)partial.length()); - p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN! - QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - tx.append(partial); - len = len + partial.length(); - //qInfo(logUdp()) << "Sending audio packet length: " << tx.length(); - sendTrackedPacket(tx); - sendAudioSeq++; - counter++; - } - } +void udpAudio::receiveAudioData(audioPacket audio) { + // I really can't see how this could be possible but a quick sanity check! + if (txaudio == Q_NULLPTR) { + return; } - else { - qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio"; + if (audio.data.length() > 0) { + int counter = 1; + int len = 0; + + while (len < audio.data.length()) { + QByteArray partial = audio.data.mid(len, 1364); + audio_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p) + partial.length(); + p.sentid = myId; + p.rcvdid = remoteId; + if (partial.length() == 0xa0) { + p.ident = 0x9781; + } + else { + p.ident = 0x0080; // TX audio is always this? + } + p.datalen = (quint16)qToBigEndian((quint16)partial.length()); + p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN! + QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + tx.append(partial); + len = len + partial.length(); + //qInfo(logUdp()) << "Sending audio packet length: " << tx.length(); + sendTrackedPacket(tx); + sendAudioSeq++; + counter++; + } } } @@ -1006,12 +994,7 @@ void udpAudio::dataReceived() { case (16): // Response to control packet handled in udpBase { - control_packet_t in = (control_packet_t)r.constData(); - if (in->type == 0x04 && enableTx) - { - txAudioTimer->start(AUDIO_PERIOD); - } - + //control_packet_t in = (control_packet_t)r.constData(); break; } default: @@ -1024,7 +1007,6 @@ void udpAudio::dataReceived() */ control_packet_t in = (control_packet_t)r.constData(); - if (in->type != 0x01 && in->len >= 0x20) { if (in->seq == 0) diff --git a/udphandler.h b/udphandler.h index 30757d3..3731bc1 100644 --- a/udphandler.h +++ b/udphandler.h @@ -196,6 +196,7 @@ public slots: void setVolume(unsigned char value); void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void receiveAudioData(audioPacket audio); private: @@ -211,7 +212,7 @@ private: audioHandler* txaudio = Q_NULLPTR; QThread* txAudioThread = Q_NULLPTR; - QTimer* txAudioTimer=Q_NULLPTR; + QTimer* txAudioTimer = Q_NULLPTR; bool enableTx = true; QMutex audioMutex; diff --git a/udpserver.cpp b/udpserver.cpp index 9c3fd7d..a92f218 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -353,6 +353,7 @@ void udpServer::controlReceived() radio->txAudioSetup.format.setSampleRate(current->txSampleRate); radio->txAudioSetup.isinput = false; radio->txAudioSetup.latency = current->txBufferLen; + outAudio.isinput = false; radio->txaudio = new audioHandler(); @@ -393,6 +394,7 @@ void udpServer::controlReceived() radio->rxAudioSetup.format.setSampleRate(current->rxSampleRate); radio->rxAudioSetup.latency = current->txBufferLen; radio->rxAudioSetup.isinput = true; + memcpy(radio->rxAudioSetup.guid, radio->guid, GUIDLEN); radio->rxaudio = new audioHandler(); @@ -404,6 +406,7 @@ void udpServer::controlReceived() connect(this, SIGNAL(setupRxAudio(audioSetup)), radio->rxaudio, SLOT(init(audioSetup))); connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater())); + connect(radio->rxaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); #if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) QMetaObject::invokeMethod(radio->rxaudio, [=]() { @@ -414,10 +417,6 @@ void udpServer::controlReceived() setupRxAudio(radio->rxAudioSetup); #endif - radio->rxAudioTimer = new QTimer(); - radio->rxAudioTimer->setTimerType(Qt::PreciseTimer); - connect(radio->rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); - radio->rxAudioTimer->start(AUDIO_PERIOD); } } @@ -1596,30 +1595,12 @@ void udpServer::dataForServer(QByteArray d) return; } -void udpServer::sendRxAudio() +void udpServer::sendRxAudio(const audioPacket& audio) { - QByteArray audio; + audioHandler* sender = qobject_cast(QObject::sender()); for (RIGCONFIG* rig : config.rigs) { - if (rig->rxaudio != Q_NULLPTR) { - if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - audio.clear(); - rig->rxaudio->getNextAudioChunk(audio); - int len = 0; - while (len < audio.length()) { - audioPacket partial; - memcpy(partial.guid, rig->guid, GUIDLEN); - partial.data = audio.mid(len, 1364); - receiveAudioData(partial); - len = len + partial.data.length(); - } - // Now we have the next audio chunk, we can release the mutex. - audioMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio"; - } + if (sender != Q_NULLPTR && rig->rxaudio == sender) { } } } @@ -1637,52 +1618,58 @@ void udpServer::receiveAudioData(const audioPacket& d) else { memcpy(guid, d.guid, GUIDLEN); } - //qInfo(logUdpServer()) << "Server got:" << d.data.length(); +//qInfo(logUdpServer()) << "Server got:" << d.data.length(); foreach(CLIENT * client, audioClients) { - if (client != Q_NULLPTR && client->connected && !memcmp(client->guid,guid, GUIDLEN)) { - audio_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p) + d.data.length(); - p.sentid = client->myId; - p.rcvdid = client->remoteId; - p.ident = 0x0080; // audio is always this? - p.datalen = (quint16)qToBigEndian((quint16)d.data.length()); - p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN! - p.seq = client->txSeq; - QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - t.append(d.data); + int len = 0; + while (len < d.data.length()) { + QByteArray partial; + partial = d.data.mid(len, 1364); + len = len + partial.length(); + if (client != Q_NULLPTR && client->connected && !memcmp(client->guid, guid, GUIDLEN)) { + audio_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p) + partial.length(); + p.sentid = client->myId; + p.rcvdid = client->remoteId; + p.ident = 0x0080; // audio is always this? + p.datalen = (quint16)qToBigEndian((quint16)partial.length()); + p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN! + p.seq = client->txSeq; + QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + t.append(d.data); - SEQBUFENTRY s; - s.seqNum = p.seq; - s.timeSent = QTime::currentTime(); - s.retransmitCount = 0; - s.data = t; - if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - if (client->txSeqBuf.size() > BUFSIZE) + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = t; + if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - client->txSeqBuf.remove(client->txSeqBuf.firstKey()); + if (client->txSeqBuf.size() > BUFSIZE) + { + client->txSeqBuf.remove(client->txSeqBuf.firstKey()); + } + client->txSeqBuf.insert(p.seq, s); + client->txSeq++; + client->sendAudioSeq++; + client->txMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock txMutex()"; } - client->txSeqBuf.insert(p.seq, s); - client->txSeq++; - client->sendAudioSeq++; - client->txMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock txMutex()"; - } - if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - client->socket->writeDatagram(t, client->ipAddress, client->port); - udpMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock udpMutex()"; - } + if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + client->socket->writeDatagram(t, client->ipAddress, client->port); + udpMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock udpMutex()"; + } + } } } @@ -1871,11 +1858,6 @@ void udpServer::deleteConnection(QList* l, CLIENT* c) for (RIGCONFIG* radio : config.rigs) { if (!memcmp(radio->guid, guid, GUIDLEN)) { - if (radio->rxAudioTimer != Q_NULLPTR) { - radio->rxAudioTimer->stop(); - delete radio->rxAudioTimer; - radio->rxAudioTimer = Q_NULLPTR; - } if (radio->rxAudioThread != Q_NULLPTR) { radio->rxAudioThread->quit(); diff --git a/udpserver.h b/udpserver.h index ce5fa88..70ec542 100644 --- a/udpserver.h +++ b/udpserver.h @@ -96,6 +96,7 @@ public slots: void dataForServer(QByteArray); void receiveAudioData(const audioPacket &data); void receiveRigCaps(rigCapabilities caps); + void sendRxAudio(const audioPacket &audio); signals: void haveDataFromServer(QByteArray); @@ -184,7 +185,6 @@ private: void sendStatus(CLIENT* c); void sendRetransmitRequest(CLIENT* c); void watchdog(); - void sendRxAudio(); void deleteConnection(QList *l, CLIENT* c); SERVERCONFIG config; From e068bc5745d934030235547d4374544428d31840 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 10 Apr 2022 23:19:45 +0100 Subject: [PATCH 153/323] Fix for opus? --- audiohandler.cpp | 9 +++++++-- udpserver.cpp | 11 ----------- udpserver.h | 1 - 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 2008b7e..dda3393 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -415,8 +415,13 @@ void audioHandler::getNextAudioChunk() if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR) { - livePacket.data = audioDevice->readAll(); - + if (setup.codec == 0x40 || setup.codec == 0x80) { + livePacket.data = audioDevice->read(format.bytesForDuration(setup.blockSize*1000)); + } + else + { + livePacket.data = audioDevice->readAll(); + } if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; diff --git a/udpserver.cpp b/udpserver.cpp index a92f218..e5e2528 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1595,17 +1595,6 @@ void udpServer::dataForServer(QByteArray d) return; } -void udpServer::sendRxAudio(const audioPacket& audio) -{ - audioHandler* sender = qobject_cast(QObject::sender()); - for (RIGCONFIG* rig : config.rigs) { - - if (sender != Q_NULLPTR && rig->rxaudio == sender) { - } - } -} - - void udpServer::receiveAudioData(const audioPacket& d) { diff --git a/udpserver.h b/udpserver.h index 70ec542..8173929 100644 --- a/udpserver.h +++ b/udpserver.h @@ -96,7 +96,6 @@ public slots: void dataForServer(QByteArray); void receiveAudioData(const audioPacket &data); void receiveRigCaps(rigCapabilities caps); - void sendRxAudio(const audioPacket &audio); signals: void haveDataFromServer(QByteArray); From 0a711e5c1fef87322eebd2aa697240860edbb4fb Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 00:05:54 +0100 Subject: [PATCH 154/323] Try a different method --- audiohandler.cpp | 313 ++++++++++++++++++++++------------------------- audiohandler.h | 1 - 2 files changed, 149 insertions(+), 165 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index dda3393..4ebca12 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -31,12 +31,6 @@ audioHandler::~audioHandler() audioOutput = Q_NULLPTR; } - if (audioTimer != Q_NULLPTR) { - audioTimer->stop(); - delete audioTimer; - audioTimer = Q_NULLPTR; - } - if (resampler != Q_NULLPTR) { speex_resampler_destroy(resampler); @@ -109,8 +103,6 @@ bool audioHandler::init(audioSetup setupIn) ", uLaw" << setup.ulaw; - tempBuf.sent = 0; - if(!setup.isinput) { this->setVolume(setup.localAFgain); @@ -135,9 +127,6 @@ bool audioHandler::init(audioSetup setupIn) if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Starting audio timer"; - audioTimer = new QTimer(); - audioTimer->setTimerType(Qt::PreciseTimer); - connect(audioTimer, &QTimer::timeout, this, &audioHandler::getNextAudioChunk); connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); } @@ -196,8 +185,8 @@ void audioHandler::start() if (setup.isinput) { audioDevice = audioInput->start(); - connect(audioInput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); - audioTimer->start(setup.blockSize); + connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); + connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); } else { // Buffer size must be set before audio is started. @@ -238,7 +227,7 @@ void audioHandler::setVolume(unsigned char volume) void audioHandler::incomingAudio(audioPacket inPacket) { - + audioPacket livePacket = inPacket; // Process uLaw. if (setup.ulaw) @@ -408,175 +397,171 @@ void audioHandler::incomingAudio(audioPacket inPacket) void audioHandler::getNextAudioChunk() { + tempBuf.data = tempBuf.data + audioDevice->readAll(); + if (tempBuf.data.length() <= format.bytesForDuration(setup.blockSize * 1000)) { + return; + } + audioPacket livePacket; livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - - if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR) { - - if (setup.codec == 0x40 || setup.codec == 0x80) { - livePacket.data = audioDevice->read(format.bytesForDuration(setup.blockSize*1000)); - } - else + livePacket.data = tempBuf.data.left(format.bytesForDuration(setup.blockSize*1000)); + tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); + //qDebug(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length(); + if (livePacket.data.length() > 0) + { + Eigen::VectorXf samplesF; + if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32) { - livePacket.data = audioDevice->readAll(); + VectorXint32 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); } - if (livePacket.data.length() > 0) + else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) { - Eigen::VectorXf samplesF; - if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32) - { - VectorXint32 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) - { - VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) - { - VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8) - { - VectorXint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (format.sampleType() == QAudioFormat::Float) - { - samplesF = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(float))); - } - else { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType() << format.sampleSize(); - } + VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); + } + else if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) + { + VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } + else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8) + { + VectorXint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } + else if (format.sampleType() == QAudioFormat::Float) + { + samplesF = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(float))); + } + else { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType() << format.sampleSize(); + } - /* samplesF is now an Eigen Vector of the current samples in float format */ + /* samplesF is now an Eigen Vector of the current samples in float format */ - // Set the max amplitude found in the vector - if (samplesF.size() > 0) { - amplitude = samplesF.array().abs().maxCoeff(); - // Channel count should now match the device that audio is going to (rig) + // Set the max amplitude found in the vector + if (samplesF.size() > 0) { + amplitude = samplesF.array().abs().maxCoeff(); + // Channel count should now match the device that audio is going to (rig) - if (resampleRatio != 1.0) { + if (resampleRatio != 1.0) { - // We need to resample - // We have a stereo 16bit stream. - quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); - quint32 inFrames = (samplesF.size() / format.channelCount()); + // We need to resample + // We have a stereo 16bit stream. + quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); + quint32 inFrames = (samplesF.size() / format.channelCount()); - QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. - const float* in = (float*)samplesF.data(); - float* out = (float*)outPacket.data(); + QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. + const float* in = (float*)samplesF.data(); + float* out = (float*)outPacket.data(); - int err = 0; - if (format.channelCount() == 1) { - err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); - } - else { - err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); - } - if (err) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } - - // If we need to drop one of the audio channels, do it now - if (format.channelCount() == 2 && setup.format.channelCount() == 1) { - Eigen::VectorXf samplesTemp(samplesF.size() / 2); - samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); - samplesF = samplesTemp; - } - - - if (setup.codec == 0x40 || setup.codec == 0x80) - { - //Are we using the opus codec? - float* in = (float*)samplesF.data(); - - /* Encode the frame. */ - QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size - unsigned char* out = (unsigned char*)outPacket.data(); - - int nbBytes = opus_encode_float(encoder, in, (samplesF.size()/setup.format.channelCount()), out, outPacket.length()); - if (nbBytes < 0) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); - return; - } - else { - outPacket.resize(nbBytes); - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } - } - - - if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint8 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); - } - else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXuint8 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); - } - else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint16 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); - } - else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint32 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); - } - else if (setup.format.sampleType() == QAudioFormat::Float) - { - livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + int err = 0; + if (format.channelCount() == 1) { + err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); } else { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); } - - /* Need to find a floating point uLaw encoder!*/ - if (setup.ulaw) - { - QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff); - qint16* in = (qint16*)livePacket.data.data(); - for (int f = 0; f < outPacket.length(); f++) - { - qint16 sample = *in++; - if (setup.ulaw) { - int sign = (sample >> 8) & 0x80; - if (sign) - sample = (short)-sample; - if (sample > cClip) - sample = cClip; - sample = (short)(sample + cBias); - int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - int compressedByte = ~(sign | (exponent << 4) | mantissa); - outPacket[f] = (quint8)compressedByte; - } - } - livePacket.data.clear(); - livePacket.data = outPacket; // Copy output packet back to input buffer. + if (err) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } - emit haveAudioData(livePacket); - //ret = livePacket.data; + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); } - } - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); - } + // If we need to drop one of the audio channels, do it now + if (format.channelCount() == 2 && setup.format.channelCount() == 1) { + Eigen::VectorXf samplesTemp(samplesF.size() / 2); + samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); + samplesF = samplesTemp; + } + + + if (setup.codec == 0x40 || setup.codec == 0x80) + { + //Are we using the opus codec? + float* in = (float*)samplesF.data(); + + /* Encode the frame. */ + QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size + unsigned char* out = (unsigned char*)outPacket.data(); + + int nbBytes = opus_encode_float(encoder, in, (samplesF.size()/setup.format.channelCount()), out, outPacket.length()); + if (nbBytes < 0) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); + return; + } + else { + outPacket.resize(nbBytes); + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } + } + + + if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint8 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); + } + else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXuint8 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + } + else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint16 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + } + else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint32 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); + } + else if (setup.format.sampleType() == QAudioFormat::Float) + { + livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + } + else { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); + } + + /* Need to find a floating point uLaw encoder!*/ + if (setup.ulaw) + { + QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff); + qint16* in = (qint16*)livePacket.data.data(); + for (int f = 0; f < outPacket.length(); f++) + { + qint16 sample = *in++; + if (setup.ulaw) { + int sign = (sample >> 8) & 0x80; + if (sign) + sample = (short)-sample; + if (sample > cClip) + sample = cClip; + sample = (short)(sample + cBias); + int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; + int mantissa = (sample >> (exponent + 3)) & 0x0F; + int compressedByte = ~(sign | (exponent << 4) | mantissa); + outPacket[f] = (quint8)compressedByte; + } + } + livePacket.data.clear(); + livePacket.data = outPacket; // Copy output packet back to input buffer. + } + emit haveAudioData(livePacket); + //ret = livePacket.data; + } + } + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); return; diff --git a/audiohandler.h b/audiohandler.h index 128b270..ace1e5f 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -115,7 +115,6 @@ private: QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; QIODevice* audioDevice=Q_NULLPTR; - QTimer* audioTimer = Q_NULLPTR; QAudioFormat format; QAudioDeviceInfo deviceInfo; SpeexResamplerState* resampler = Q_NULLPTR; From 109bdb28125322ab233b2fcbf35bfd9efa48723d Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 00:23:08 +0100 Subject: [PATCH 155/323] Update audiohandler.cpp --- audiohandler.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 4ebca12..98fb87c 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -397,8 +397,9 @@ void audioHandler::incomingAudio(audioPacket inPacket) void audioHandler::getNextAudioChunk() { - tempBuf.data = tempBuf.data + audioDevice->readAll(); - if (tempBuf.data.length() <= format.bytesForDuration(setup.blockSize * 1000)) { + tempBuf.data.append(audioDevice->readAll()); + + if (tempBuf.data.length() <= format.bytesForDuration(setup.blockSize * 2000)) { return; } @@ -406,8 +407,8 @@ void audioHandler::getNextAudioChunk() livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - livePacket.data = tempBuf.data.left(format.bytesForDuration(setup.blockSize*1000)); - tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); + livePacket.data = tempBuf.data.left(format.bytesForDuration(setup.blockSize*2000)); + tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 2000)); //qDebug(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length(); if (livePacket.data.length() > 0) { From 4ca21b84cb512a16423beceec2607d61be9128eb Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 00:33:39 +0100 Subject: [PATCH 156/323] More opus changes --- audiohandler.cpp | 8 ++++---- audiohandler.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 98fb87c..61bb905 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -399,7 +399,7 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - if (tempBuf.data.length() <= format.bytesForDuration(setup.blockSize * 2000)) { + if (tempBuf.data.length() <= getAudioSize(setup.latency,format)) { return; } @@ -407,9 +407,9 @@ void audioHandler::getNextAudioChunk() livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - livePacket.data = tempBuf.data.left(format.bytesForDuration(setup.blockSize*2000)); - tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 2000)); - //qDebug(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length(); + livePacket.data = tempBuf.data.left(getAudioSize(setup.blockSize,format)); + tempBuf.data.remove(0, getAudioSize(setup.blockSize,format)); + qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length(); if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; diff --git a/audiohandler.h b/audiohandler.h index ace1e5f..edbbd82 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -153,7 +153,7 @@ static inline qint64 getAudioSize(qint64 timeInMs, const QAudioFormat& format) #ifdef Q_OS_LINUX qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000) * timeInMs)); #else - qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(10000) * timeInMs)); + qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000) * timeInMs)); #endif From 5ba0a382a984fa584c25b089985169ca52c19376 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 09:49:25 +0100 Subject: [PATCH 157/323] Update audiohandler.cpp --- audiohandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 61bb905..3aaf036 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -399,7 +399,7 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - if (tempBuf.data.length() <= getAudioSize(setup.latency,format)) { + if (tempBuf.data.length() < getAudioSize(setup.blockSize,format)) { return; } @@ -407,7 +407,7 @@ void audioHandler::getNextAudioChunk() livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - livePacket.data = tempBuf.data.left(getAudioSize(setup.blockSize,format)); + livePacket.data = tempBuf.data.mid(0,getAudioSize(setup.blockSize,format)); tempBuf.data.remove(0, getAudioSize(setup.blockSize,format)); qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length(); if (livePacket.data.length() > 0) From ecbac9b48c6fca757136b384c207ef3c9a69a9d5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 09:53:58 +0100 Subject: [PATCH 158/323] Update audiohandler.cpp --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 3aaf036..6facb50 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -409,7 +409,6 @@ void audioHandler::getNextAudioChunk() memcpy(&livePacket.guid, setup.guid, GUIDLEN); livePacket.data = tempBuf.data.mid(0,getAudioSize(setup.blockSize,format)); tempBuf.data.remove(0, getAudioSize(setup.blockSize,format)); - qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length(); if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; @@ -479,6 +478,7 @@ void audioHandler::getNextAudioChunk() samplesF = samplesTemp; } + qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length() << "resampled" << samplesF.size(); if (setup.codec == 0x40 || setup.codec == 0x80) { From 3815ac67e74c2296c7cd09bff8e057f06a21d6af Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 09:59:21 +0100 Subject: [PATCH 159/323] Update audiohandler.cpp --- audiohandler.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/audiohandler.cpp b/audiohandler.cpp index 6facb50..3f1008c 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -402,6 +402,9 @@ void audioHandler::getNextAudioChunk() if (tempBuf.data.length() < getAudioSize(setup.blockSize,format)) { return; } + else if (tempBuf.data.length() > getAudioSize(setup.latency, format)) { + tempBuf.data.clear(); + } audioPacket livePacket; livePacket.time= QTime::currentTime(); From 3296e16195e5e7612934528c558ab566d8c66e50 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 11:26:52 +0100 Subject: [PATCH 160/323] Found bug in server audio --- audiohandler.cpp | 9 +++------ audiohandler.h | 2 +- udpserver.cpp | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 3f1008c..fa0c9a0 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -399,19 +399,16 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - if (tempBuf.data.length() < getAudioSize(setup.blockSize,format)) { + if (tempBuf.data.length() < getAudioSize(setup.blockSize,format)*10) { return; } - else if (tempBuf.data.length() > getAudioSize(setup.latency, format)) { - tempBuf.data.clear(); - } audioPacket livePacket; livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - livePacket.data = tempBuf.data.mid(0,getAudioSize(setup.blockSize,format)); - tempBuf.data.remove(0, getAudioSize(setup.blockSize,format)); + livePacket.data = tempBuf.data.mid(0,getAudioSize(setup.blockSize,format)*10); + tempBuf.data.remove(0, getAudioSize(setup.blockSize,format)*10); if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; diff --git a/audiohandler.h b/audiohandler.h index edbbd82..ace1e5f 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -153,7 +153,7 @@ static inline qint64 getAudioSize(qint64 timeInMs, const QAudioFormat& format) #ifdef Q_OS_LINUX qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000) * timeInMs)); #else - qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000) * timeInMs)); + qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(10000) * timeInMs)); #endif diff --git a/udpserver.cpp b/udpserver.cpp index e5e2528..fc33ce4 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1626,7 +1626,7 @@ void udpServer::receiveAudioData(const audioPacket& d) p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN! p.seq = client->txSeq; QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - t.append(d.data); + t.append(partial); SEQBUFENTRY s; s.seqNum = p.seq; From 74de3087c8ee1015791d765145d18bab6341cd3f Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 11:28:32 +0100 Subject: [PATCH 161/323] Update audiohandler.cpp --- audiohandler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index fa0c9a0..6facb50 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -399,7 +399,7 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - if (tempBuf.data.length() < getAudioSize(setup.blockSize,format)*10) { + if (tempBuf.data.length() < getAudioSize(setup.blockSize,format)) { return; } @@ -407,8 +407,8 @@ void audioHandler::getNextAudioChunk() livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - livePacket.data = tempBuf.data.mid(0,getAudioSize(setup.blockSize,format)*10); - tempBuf.data.remove(0, getAudioSize(setup.blockSize,format)*10); + livePacket.data = tempBuf.data.mid(0,getAudioSize(setup.blockSize,format)); + tempBuf.data.remove(0, getAudioSize(setup.blockSize,format)); if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; From c0a1000c9a5d0e26935dc01dbff1a3e0af0821e3 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 11:35:33 +0100 Subject: [PATCH 162/323] Update audiohandler.cpp --- audiohandler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 6facb50..7534bbe 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -399,7 +399,7 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - if (tempBuf.data.length() < getAudioSize(setup.blockSize,format)) { + if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)) { return; } @@ -407,8 +407,8 @@ void audioHandler::getNextAudioChunk() livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - livePacket.data = tempBuf.data.mid(0,getAudioSize(setup.blockSize,format)); - tempBuf.data.remove(0, getAudioSize(setup.blockSize,format)); + livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); + tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; From 9d63c36e51ba780f83a78e0100dc06f11e065e0d Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 11:44:53 +0100 Subject: [PATCH 163/323] Update audiohandler.cpp --- audiohandler.cpp | 279 ++++++++++++++++++++++++----------------------- 1 file changed, 140 insertions(+), 139 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 7534bbe..57dab34 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -407,163 +407,164 @@ void audioHandler::getNextAudioChunk() livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); - tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); - if (livePacket.data.length() > 0) - { - Eigen::VectorXf samplesF; - if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32) + while (tempBuf.data.length() > 0) { + livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); + tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); + if (livePacket.data.length() > 0) { - VectorXint32 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) - { - VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) - { - VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8) - { - VectorXint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (format.sampleType() == QAudioFormat::Float) - { - samplesF = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(float))); - } - else { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType() << format.sampleSize(); - } - - /* samplesF is now an Eigen Vector of the current samples in float format */ - - // Set the max amplitude found in the vector - if (samplesF.size() > 0) { - amplitude = samplesF.array().abs().maxCoeff(); - // Channel count should now match the device that audio is going to (rig) - - if (resampleRatio != 1.0) { - - // We need to resample - // We have a stereo 16bit stream. - quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); - quint32 inFrames = (samplesF.size() / format.channelCount()); - - QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. - const float* in = (float*)samplesF.data(); - float* out = (float*)outPacket.data(); - - int err = 0; - if (format.channelCount() == 1) { - err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); - } - else { - err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); - } - if (err) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } - - // If we need to drop one of the audio channels, do it now - if (format.channelCount() == 2 && setup.format.channelCount() == 1) { - Eigen::VectorXf samplesTemp(samplesF.size() / 2); - samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); - samplesF = samplesTemp; - } - - qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length() << "resampled" << samplesF.size(); - - if (setup.codec == 0x40 || setup.codec == 0x80) + Eigen::VectorXf samplesF; + if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32) { - //Are we using the opus codec? - float* in = (float*)samplesF.data(); - - /* Encode the frame. */ - QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size - unsigned char* out = (unsigned char*)outPacket.data(); - - int nbBytes = opus_encode_float(encoder, in, (samplesF.size()/setup.format.channelCount()), out, outPacket.length()); - if (nbBytes < 0) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); - return; - } - else { - outPacket.resize(nbBytes); - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } + VectorXint32 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); } - - - if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) + else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint8 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); + VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); } - else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) + else if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXuint8 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; } - else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) + else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8) { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint16 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + VectorXint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; } - else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) + else if (format.sampleType() == QAudioFormat::Float) { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint32 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); - } - else if (setup.format.sampleType() == QAudioFormat::Float) - { - livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + samplesF = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(float))); } else { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType() << format.sampleSize(); } - /* Need to find a floating point uLaw encoder!*/ - if (setup.ulaw) - { - QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff); - qint16* in = (qint16*)livePacket.data.data(); - for (int f = 0; f < outPacket.length(); f++) + /* samplesF is now an Eigen Vector of the current samples in float format */ + + // Set the max amplitude found in the vector + if (samplesF.size() > 0) { + amplitude = samplesF.array().abs().maxCoeff(); + // Channel count should now match the device that audio is going to (rig) + + if (resampleRatio != 1.0) { + + // We need to resample + // We have a stereo 16bit stream. + quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); + quint32 inFrames = (samplesF.size() / format.channelCount()); + + QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. + const float* in = (float*)samplesF.data(); + float* out = (float*)outPacket.data(); + + int err = 0; + if (format.channelCount() == 1) { + err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); + } + else { + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + } + if (err) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + } + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } + + // If we need to drop one of the audio channels, do it now + if (format.channelCount() == 2 && setup.format.channelCount() == 1) { + Eigen::VectorXf samplesTemp(samplesF.size() / 2); + samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); + samplesF = samplesTemp; + } + + qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length() << "resampled" << samplesF.size(); + + if (setup.codec == 0x40 || setup.codec == 0x80) { - qint16 sample = *in++; - if (setup.ulaw) { - int sign = (sample >> 8) & 0x80; - if (sign) - sample = (short)-sample; - if (sample > cClip) - sample = cClip; - sample = (short)(sample + cBias); - int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - int compressedByte = ~(sign | (exponent << 4) | mantissa); - outPacket[f] = (quint8)compressedByte; + //Are we using the opus codec? + float* in = (float*)samplesF.data(); + + /* Encode the frame. */ + QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size + unsigned char* out = (unsigned char*)outPacket.data(); + + int nbBytes = opus_encode_float(encoder, in, (samplesF.size() / setup.format.channelCount()), out, outPacket.length()); + if (nbBytes < 0) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); + return; + } + else { + outPacket.resize(nbBytes); + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); } } - livePacket.data.clear(); - livePacket.data = outPacket; // Copy output packet back to input buffer. - } - emit haveAudioData(livePacket); - //ret = livePacket.data; - } - } - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + + if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint8 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); + } + else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXuint8 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + } + else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint16 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + } + else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint32 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); + } + else if (setup.format.sampleType() == QAudioFormat::Float) + { + livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + } + else { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); + } + + /* Need to find a floating point uLaw encoder!*/ + if (setup.ulaw) + { + QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff); + qint16* in = (qint16*)livePacket.data.data(); + for (int f = 0; f < outPacket.length(); f++) + { + qint16 sample = *in++; + if (setup.ulaw) { + int sign = (sample >> 8) & 0x80; + if (sign) + sample = (short)-sample; + if (sample > cClip) + sample = cClip; + sample = (short)(sample + cBias); + int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; + int mantissa = (sample >> (exponent + 3)) & 0x0F; + int compressedByte = ~(sign | (exponent << 4) | mantissa); + outPacket[f] = (quint8)compressedByte; + } + } + livePacket.data.clear(); + livePacket.data = outPacket; // Copy output packet back to input buffer. + } + emit haveAudioData(livePacket); + //ret = livePacket.data; + } + } + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + } return; } From 718d54ee3b6b3094ffec5379f479c0f8a133195c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 11:49:48 +0100 Subject: [PATCH 164/323] Update audiohandler.cpp --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 57dab34..cfcf843 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -407,7 +407,7 @@ void audioHandler::getNextAudioChunk() livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - while (tempBuf.data.length() > 0) { + while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) { livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); if (livePacket.data.length() > 0) From 1e94a97964f2a6f626cd899cd2d71db2a810ae88 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 11:52:57 +0100 Subject: [PATCH 165/323] Add time to wfserver debugging --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index e7e74b0..81448f6 100644 --- a/main.cpp +++ b/main.cpp @@ -221,7 +221,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt // Write to the output category of the message and the message itself out << context.category << ": " << msg << "\n"; #ifdef BUILD_WFSERVER - std::cout << msg.toLocal8Bit().toStdString() << "\n"; + std::cout << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ") << msg.toLocal8Bit().toStdString() << "\n"; #endif out.flush(); // Clear the buffered data } From af2be449193392dd3c9119a4b63dad3d4568c323 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 11:54:06 +0100 Subject: [PATCH 166/323] Update main.cpp --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 81448f6..8217ee9 100644 --- a/main.cpp +++ b/main.cpp @@ -221,7 +221,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt // Write to the output category of the message and the message itself out << context.category << ": " << msg << "\n"; #ifdef BUILD_WFSERVER - std::cout << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ") << msg.toLocal8Bit().toStdString() << "\n"; + std::cout << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ").toLocal8Bit().toStdString() << msg.toLocal8Bit().toStdString() << "\n"; #endif out.flush(); // Clear the buffered data } From 2b588ffce00ff690026bb2c11cb603d3d851b3b1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 11:59:38 +0100 Subject: [PATCH 167/323] Switch back to using a timer. --- audiohandler.cpp | 12 +++++++++++- audiohandler.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index cfcf843..f404d5c 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -31,6 +31,12 @@ audioHandler::~audioHandler() audioOutput = Q_NULLPTR; } + if (audioTimer != Q_NULLPTR) { + audioTimer->stop(); + delete audioTimer; + audioTimer = Q_NULLPTR; + } + if (resampler != Q_NULLPTR) { speex_resampler_destroy(resampler); @@ -127,6 +133,9 @@ bool audioHandler::init(audioSetup setupIn) if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Starting audio timer"; + audioTimer = new QTimer(); + audioTimer->setTimerType(Qt::PreciseTimer); + connect(audioTimer, &QTimer::timeout, this, &audioHandler::getNextAudioChunk); connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); } @@ -186,7 +195,8 @@ void audioHandler::start() if (setup.isinput) { audioDevice = audioInput->start(); connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); - connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); + //connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); + audioTimer->start(setup.blockSize); } else { // Buffer size must be set before audio is started. diff --git a/audiohandler.h b/audiohandler.h index ace1e5f..128b270 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -115,6 +115,7 @@ private: QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; QIODevice* audioDevice=Q_NULLPTR; + QTimer* audioTimer = Q_NULLPTR; QAudioFormat format; QAudioDeviceInfo deviceInfo; SpeexResamplerState* resampler = Q_NULLPTR; From b7dee4f5f4bcd372d0ecb237059a39f3b83f0270 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 12:04:40 +0100 Subject: [PATCH 168/323] Update audiohandler.cpp --- audiohandler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index f404d5c..425fc7d 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -409,7 +409,7 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)) { + if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)*format.channelCount()) { return; } @@ -417,9 +417,9 @@ void audioHandler::getNextAudioChunk() livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) { - livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); - tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); + while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)*format.channelCount()) { + livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)*format.channelCount()); + tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)*format.channelCount()); if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; From 7542cc3f56753dc31930b1f25ae076d9e02cddee Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 12:11:10 +0100 Subject: [PATCH 169/323] Update audiohandler.cpp --- audiohandler.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 425fc7d..67922c0 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -409,7 +409,7 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)*format.channelCount()) { + if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)) { return; } @@ -417,9 +417,10 @@ void audioHandler::getNextAudioChunk() livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); - while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)*format.channelCount()) { - livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)*format.channelCount()); - tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)*format.channelCount()); + while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) { + livePacket.data.clear(); + livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); + tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; From 55fdf52436d91a3ccda2ea1d667602a0890f1a97 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 12:13:48 +0100 Subject: [PATCH 170/323] Update audiohandler.cpp --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 67922c0..803aea9 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -490,7 +490,7 @@ void audioHandler::getNextAudioChunk() samplesF = samplesTemp; } - qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length() << "resampled" << samplesF.size(); + //qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length() << "resampled" << samplesF.size(); if (setup.codec == 0x40 || setup.codec == 0x80) { From b67b43d0cb72da49abfbc067c932479747c243fd Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Mon, 11 Apr 2022 14:20:24 +0200 Subject: [PATCH 171/323] added eigen3 for suse build --- INSTALL.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 970ddab..589eab7 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -70,12 +70,12 @@ sudo usermod -aG dialout $USER ### opensuse/sles/tumbleweed install --- -install wfview on suse 15.x sles 15.x or tumbleweed; this was done on a clean install/updated OS. +install wfview on suse 15.3 & up, sles 15.x or tumbleweed; this was done on a clean install/updated OS. we need to add packages to be able to build the stuff. - sudo zypper in --type pattern devel_basis -- sudo zypper in libQt5Widgets-devel libqt5-qtbase-common-devel libqt5-qtserialport-devel libQt5SerialPort5 qcustomplot-devel libqcustomplot2 libQt5PrintSupport-devel libqt5-qtmultimedia-devel lv2-devel libopus-devel +- sudo zypper in libQt5Widgets-devel libqt5-qtbase-common-devel libqt5-qtserialport-devel libQt5SerialPort5 qcustomplot-devel libqcustomplot2 libQt5PrintSupport-devel libqt5-qtmultimedia-devel lv2-devel libopus-devel eigen3-devel optional (mainly for development specifics): get and install qt5: @@ -94,9 +94,9 @@ in this case, use your homedir: - mkdir -p ~/src/build && cd src - git clone https://gitlab.com/eliggett/wfview.git - cd build -- qmake-qt5 ../wfview/wfview.pro +- qmake-qt5 ../wfview/wfview.pro (wfserver.pro if you build the server version) - make -j -- sudo ./install.sh +- sudo make install wfview is now installed in /usr/local/bin From e8343963873eeb98231794998c17afd9c619f08c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 11 Apr 2022 23:24:56 +0100 Subject: [PATCH 172/323] Force 16 bit samples if 24 is requested. --- audiohandler.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/audiohandler.cpp b/audiohandler.cpp index 803aea9..12df8bb 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -126,6 +126,11 @@ bool audioHandler::init(audioSetup setupIn) return false; } + if (format.sampleSize() == 24) { + // We can't convert this easily + format.setSampleSize(16); + } + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Internal: sample rate" << format.sampleRate() << "channel count" << format.channelCount(); // We "hopefully" now have a valid format that is supported so try connecting From 4fe9b2cba7e28fa55205ac9bda98cb49536743d1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 13 Apr 2022 12:55:59 +0100 Subject: [PATCH 173/323] Move wfserver configuration and add simple keyboard handler --- keyboard.cpp | 26 +++++ keyboard.h | 11 +++ main.cpp | 12 ++- wfserver.pro | 4 +- wfserver.vcxproj | 206 ++++++++++++++++----------------------- wfserver.vcxproj.filters | 32 ++---- wfserver.vcxproj.user | 4 +- wfview.vcxproj.user | 4 +- wfview_resource.aps | Bin 0 -> 157368 bytes 9 files changed, 143 insertions(+), 156 deletions(-) create mode 100644 keyboard.cpp create mode 100644 keyboard.h create mode 100644 wfview_resource.aps diff --git a/keyboard.cpp b/keyboard.cpp new file mode 100644 index 0000000..ccddeab --- /dev/null +++ b/keyboard.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include +#include "keyboard.h" + +keyboard::keyboard(void) +{ +} + +keyboard::~keyboard(void) +{ +} + +void keyboard::run() +{ + while (true) + { + char key = getchar(); + if (key == 'q') { + QCoreApplication::exit(0); + } + } + return; +} \ No newline at end of file diff --git a/keyboard.h b/keyboard.h new file mode 100644 index 0000000..1fab712 --- /dev/null +++ b/keyboard.h @@ -0,0 +1,11 @@ +#include +#include +#include +class keyboard : public QThread +{ + Q_OBJECT +public: + keyboard(void); + ~keyboard(void); + void run(); +}; diff --git a/main.cpp b/main.cpp index 8217ee9..7debf6e 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,6 @@ #ifdef BUILD_WFSERVER #include +#include "keyboard.h" #else #include #endif @@ -47,18 +48,21 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt int main(int argc, char *argv[]) { + #ifdef BUILD_WFSERVER QCoreApplication a(argc, argv); + a.setOrganizationName("wfview"); + a.setOrganizationDomain("wfview.org"); + a.setApplicationName("wfserver"); + keyboard* kb = new keyboard(); + kb->start(); #else QApplication a(argc, argv); -#endif - - //a.setStyle( "Fusion" ); - a.setOrganizationName("wfview"); a.setOrganizationDomain("wfview.org"); a.setApplicationName("wfview"); a.setAttribute(Qt::AA_EnableHighDpiScaling); +#endif #ifdef QT_DEBUG debugMode = true; diff --git a/wfserver.pro b/wfserver.pro index 74215d5..009fa1f 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -147,6 +147,7 @@ SOURCES += main.cpp\ resampler/resample.c \ rigctld.cpp \ tcpserver.cpp \ + keyboard.cpp HEADERS += servermain.h \ commhandler.h \ @@ -166,4 +167,5 @@ HEADERS += servermain.h \ rigctld.h \ ulaw.h \ tcpserver.h \ - audiotaper.h + audiotaper.h \ + keyboard.h diff --git a/wfserver.vcxproj b/wfserver.vcxproj index cbfc961..e619017 100644 --- a/wfserver.vcxproj +++ b/wfserver.vcxproj @@ -16,7 +16,8 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild
+ $(MSBuildProjectDirectory)\QtMsBuild +
v142 @@ -36,7 +37,10 @@ debug\ wfserver - + + + + @@ -44,8 +48,34 @@ - debug\debug\wfservertruerelease\release\wfservertruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport - + + + + + + debug\ + debug\ + wfserver + true + + + release\ + release\ + wfserver + true + false + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + + .;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) @@ -59,12 +89,14 @@ MaxSpeed _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - + + MultiThreadedDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories) @@ -87,7 +119,21 @@ _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + .;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) @@ -105,7 +151,8 @@ true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories) @@ -126,11 +173,26 @@ _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + @@ -144,112 +206,37 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Document true @@ -266,24 +253,6 @@ release\moc_predefs.h;%(Outputs) true - - - - - - - - - - - - - - - - - - @@ -316,30 +285,16 @@ - - - - - - - - - - resourcesresources + resources + resources + - - - - - - - - - - stylestyle + style + style + @@ -353,6 +308,9 @@ - + + + + \ No newline at end of file diff --git a/wfserver.vcxproj.filters b/wfserver.vcxproj.filters index 80cfac4..1a18943 100644 --- a/wfserver.vcxproj.filters +++ b/wfserver.vcxproj.filters @@ -79,6 +79,9 @@ Source Files + + Source Files + @@ -140,34 +143,12 @@ - - - - Generated Files Generated Files - - - - - - - - - - - - - - - - - - @@ -301,6 +282,11 @@ - + + + + + Header Files + \ No newline at end of file diff --git a/wfserver.vcxproj.user b/wfserver.vcxproj.user index 04ba52a..f4da9e5 100644 --- a/wfserver.vcxproj.user +++ b/wfserver.vcxproj.user @@ -2,9 +2,9 @@ - 2022-01-23T13:00:12.7440722Z + 2022-04-13T11:33:50.3607712Z - 2022-01-23T13:00:16.3164650Z + 2022-04-13T11:33:53.0745117Z \ No newline at end of file diff --git a/wfview.vcxproj.user b/wfview.vcxproj.user index ba3b589..1f25d96 100644 --- a/wfview.vcxproj.user +++ b/wfview.vcxproj.user @@ -7,9 +7,9 @@ PATH=$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(PATH) - 2022-01-22T22:51:25.6471709Z + 2022-04-13T11:33:25.4527691Z - 2022-01-22T12:48:06.5160626Z + 2022-04-13T11:33:27.0112037Z \ No newline at end of file diff --git a/wfview_resource.aps b/wfview_resource.aps new file mode 100644 index 0000000000000000000000000000000000000000..3e7faabf1b7a39ac7283357c587acab60d93588a GIT binary patch literal 157368 zcmd?y2b^C;xj+6Bf?~U3xkx7gm14m(LZT=x$p#kEOreQHN+^PW0coNjMeMy?JNAP8 zdKJZjy@Q}22q+*Zc2V!W>Q%nK&wI|!?su0>vYST!um8SYv*&x}%sFS~nWsO`%xRjY znV8b)v?V`>{LReSqf+*34r=zvvC|pXfBpXV>;KWbe)%TNtS26}^wi}mPCablamSx{ z*r_K!cg0c54_mSPl#@?A?2KbhKYsZc{fA{MmbVvoUvZTCFWF^W|NF-eJbThZ2A{oa z+PpF4uPJTIruozMfe_0!3p(zZs+s)t$Z zomb@d@ww;8&4)TK&-J^ST{jk(tjWA|c5KFXwrNfXe%+LD{n(cQ}SEcD`kH3yUqui z>j$3y={*1W&262tn>&KbuM7Y3tk=49MHM6`A+BS8N0E5H^taL z+5Dtq{?&CCa8zoYwr>tz?%Vva^Tg)QohS7dzrpE{!0W>1mhdb1**@F7p7Hs-B z1H&!W`(Q`$g7B&bhF6`~nb3S8WBF6(CC$$ZE$HhNfzRiHQ(yu|DjbZ-cs`$N9}4X_ z*6}%hHRC=%xQ6!N(Pj~_a8-*lqc#|NLDoPHh@IGmSpeI_`BFQW(aGCwd{nrlywtUM_)?4r=it8>rG z!{08>@0aEXm&i~&3~b@h8G*rvbB_0sm5yX29Q;h+0iN#pdg%P0(ev(Qn5@Z!L}yiyM7MfhXIk*(Nf|>=#BX&f6&T`%>QYjD?A=tM#7`vb>M{NmShg5Hh=Bx*8ESwt=Yk$ z{|KFeA(`jd=ob9k^SaE-;koDX=3{~3L)R6KtjTl2tL_usYFTH8=5yhxe=0P9@4}xG zLT6xsAI(V}+F1~u^?~qO_yiutgNELkaaKO9PI29s(G&RxhsaNK`?efU54_))`|v$9 z{jumG|6A;t`>x4Y?#+8b3iru4mxK<#KY%-zWIWFZt%4a@b!^6nZ&%n<_&K60aH(~0 z@;Pabo@b`GAMK)7_p_`UNvqCTI4Z0&cd$0pn-@S%gz>AUgj zL%HY3&?=b#hn`V>|2lmB_QJc~5S%6-!RVIo@c(O`ac|MuXz*xwn}S!z#rFA1b6LhP z$g9nFoqzb^x4Az*e28wz$4SQF*KBZY6m}$ie7OH=zufbY;3U|>&5xy2oz1h|OBZ=o z;Cp%e&Yk8#!~41+&LjKAMYnod%6~<-YWaz=u=mhR)s5f}+MURmVU4h|bOmP4V0DL$I^yVimTRg(saD{)LZ$m117t;v1rm^39M9i$nA5A3Tx& z;^M%6MV>n+eBd3;w*y0=wGI9k8Ms+=tK-ABzB7b2*tC43aN~=?2lxXgoY(mW13Dw! zi5x^bt|{7~Bf6*Gd=KIdjOo=HbSU;Hu^ufP=UbmJA=Vo%Y{@i%y;@C%~ZXGccx zrP6=ZzcBpoug!je;|Bi=KK)x~tLDtkV?!%dx2p6(R=qnkz(36HxFGnzc6(X+``W^!xDe4>GLMN;e+W-{51ce0}JQZcjgWXO7|!Z_WLDpm3*nB4c^hA&@vs>^XOObj2&q{ zw0(W>4ej3DEWS5rZLFvj9pb+6{k<@9>9)jGf1Yx4aIGDKu~mGkM`MIb_%>aOeMlcg zr)U?9D=(_}(InbIPs8a9T<~%_5!jK17epTLDTvz~ z6ncd_2L#6KrOKDUo<2tBzcB6j6kI#wyk)Mq2S_B>2^9j;@0vpRoJUpLdu8 z{tU7bjM)rJf^SzOx1!TLcunSdL*5e^_=L_A;$sJ&mt+j){fx}Hxu+)$hYOt^AFK6# z8~4Y9gKz`y=VK<T`ugp1F4Ni1xeic3^b__l9+>95ms<5eej_isThVID!I@Wq*$M7u}bA^}Vf8{xBU2y2GulA!l z&u|~yx**SgePrOL1808Ii=vyzC*iAjORnD>|Np<5{Wl~`#)?|W4c#PowIFnGb%`7B zSyni8!=U~K3-klF#yXNe!u|N%#eoBxoL}K>fsga&1!u%Mj>>$|RjUr-9xyyUeCh?c zZVuqL+@m|1*93+eBrX=)rHM{3rL$f0k>J#=#YU}i$&n8jZ`R)mlZqGOdVD76&bVsf zCnf9YO6tHyjP&gpFFVKm>=xq%Uq^LdKn}8hj>`RXcJRJ2bM?0kZ_kVcx5BR;5;(p! zc1|0k!PkewtQ&rQ(?kCwbi+QWFz9XrcH%JRfDXGLM+JNoddTYn>u!Dso^X|4Uc3X` zUmQFnN3U)EGkCQ@;$qQln)s~_h%Ed{@rM{gH(v3%kx3)L=#uoA?T6mp9~^jBV8BLx zS)nhvHEk?{9?n(~|AS9(>vg&3wf%JJ*$V%#xS98Zi&(`+^X?zzo`B75&0*G$gN$e-D1Nc=gkwztY8Hrb49OMVPIk^TLn@cq9uhX;=9 z{V)E|2gepXEBy6_$mBbNS8d-bZlMi0Fr2R&v%bC|Fk`R0CiK8pjE)Wne!e92^e6N~ zPF6pt`}hIj4Ic#_{mP8{kiY`3KQ%ZCj>|GPbUXZ$opVQG5qHNIvH@T+R-h-hc9X#H zgy7X3#rMYtfOhBzcm-dw`G!w)0sc;KsrcXsE^iCnv9sAo+NyI6jlMESrHCg|%0peouT`Fru4ZoI-X(s$5x$V|txTii7 zUdqX$--0E6i3h$q<>!fAcH(DSkM!BurReN?XFfk&_`(UnX>b|NulU$&UTEz*I2b2C z!s~9C(TE zjC9>I!4}@&jpl%FhYpM$`6T29nlt%La#Q(zy#MEA-N4fIidXkbZp`DG-7>fT6`S-A zMK2s4H>Y3WH=oTI=-BLDM?4rF=`&c8=j=W7#IL~KbDf^m6u^$W!y7`bc!wdWIIl-Y5LxU@Fw0?|y;g4{BMBph# zhL%0+`jr1Iaj{LtI@Zy)4=WtsnUwsABSUNZMz>*y%}XD;`IX;%N_acoazw@pU(kgd zzN3S4V)xHU{p)g$Cg2oaL>E6Y_4v+N!6h~yS;*!+A>$QmV5`7cuvE|=Jmg1~bIZnr z%Xr;QSxfPMv0c|A4<9Y;YP-(ElFPnR@-g?ySm71AqH}l3`f=~2Y_TbYpFKi)ttGpBI5U$BH z?`qy17;aw2u&$4thcpijJf0Ug++J*rvocos`E?y69ZNm~|Js55eb>jcgCk@UpPcv? z8E>5pK83ef_W&Q=tGzKBueIZRGuDlOoBR0&$x69x{0`{uq?D)UH~A|!)f|DjJiq^L zUJ{sWx~^fdrqAm5XuU??LR3{apRc#s*Iu z>7!y0V1ky|!em%?K6}M4c+1ujd&3jh49hZC){}`<&ky|Q75o$On5=`c_WHM3bKGfc zW3kTvkbG?8$Az!FJ9^#i8OzTKt)3GTxYGEqHVbYbAaj>T(_3vS_O^ewu( zJcrhI*RIXQ3)z0)EB}Lk2JY~SiA|tI@S(fo+2{^zj?4Lq(EfXZS73Qnp8v<@(!gY+ zSQ~CqA_I5ISU(Z?)O99*4o{c++s5ngpXY|oj!zvs1P{Vz;6&v)c$j+U{G6`AG4;m& zf$;gShgW|!`pUO*ta#;!F4=!42To`lzMdbr9+UU6WN$&y=3Tx@ROxX%9y zUhUFspMJkH_7=VQrszFta9=M55KO?wyUh2eb_^!MUF5{8nLDuYoIh^HRy}j5oi9X%?e_7KkH)e9y z^Ierb|FzJZ{?lpbpm0Oq#R2&b@C<$=F$C8XdgaBb!;`CB17F}49oD%0zACZzk2GiG z_jiWim1mF<-T{Zb50A5cijUfw1iDI_=lr{(e~DlGJ$tsShdjUF)ws;*yajx?tpb>r%^zrKGMVB_0M}}V|iolnj;lNy{yD!aq$v-%TFM7ARsWcCEcsQH+rnz2XDNxA2oz#D&}OR@3X!zQ8g zqhIal@AB8g9^}Q~aq@ELdDRySR(|tGT%CLWH~U9y0H5({Vff(WZqJOqH!uB$Pn8y} zRjN4Rh@a>?pB|d3IA{LJfAFcgmiAyuFH`r!$UX4=L3FrD;RCOYpX_~sB|VjmIg;Mx zx%THlqt@oJsnIvNf*1TC&%Zsf%zrgIt!|8Cxn6P@Hg6_|_q;Ce{8_R4hRH@nP<|Y;36Lueki8V9aF`-@cAF)eSd1+8@zhRSjN0Q+GdT!c9Gj3jK193Utos6 zi6GLBC?dKP&yg8{}z$BYpN`X>(WjABAWg{YCcJOYVT^J>M+!L$?ykYI9)m z&%qqd3M=*;zrmp?d~Wo1{!lvj`(pRJCqA5q#s{z{dVoAh`qG<%Tk?R;4ovw8>5lv; zXw4cV@B+6_&{ppe6ANvNE z&>vfc?R9kKoZn90^%<|CKfx(Z;LkS%clXOab2}uq zw_WO|2Y1=n{D1P4yYsK$nCDyHYppqbf^A55KQD6zztx`;IRC8dF}BX)Vx3L1Ze;If zMR50q$non-oXNQP@5GDo@`_L0NA!r7$!`@8<{LR9{l2<+Q^vS`Z0_e|@5pxrkHq!h zz|S+TUlqMkobQEsKH13kMVF*=@fXW$JSNY<3;7xNAMnE$rVW{iZqcQDEd2j9ssBaf z?s~9iMvGs;t8p3I{LB%X=hwyN#j6y)ax{Q1kcW6L*`uHnFpvvNhk^%u1?bJ38)wC> z9|W&%4X^rJ_BPnPnVG$QUYhv!CGj2DOY*mY*{_NWfWw~S+2BRy^8Fl}!Y?f!Ma;xH z1bWu<3fPvbQiSDSZd-o#Sy3Vogs$4Gn zPs+1J)B49=GGEr2z}0VMU()qp&#Vr1^?G1<-}LV}g(l=avTx9%+@Hh3N5Bz0;gUJ8 z1mpPTlt4jg{p^adxI*xdgf-F?^aqqhc*gSvuupxLT3fg`l*o6%W;28(wv1_CEPnay+xsQ25ou5+j)q+PSLAU=3KJi(J6X>gPpb>q2apwKH5);@7%4gj7%U*R`hbGPltOj)jumwl? z?p2>)Bco6G|MV$x0WacX;PbaH0e=Qt35@B!^fdXD@>)Ng=XZk7>wwnAikW4s^OAS` z>gMx-TWib8`KfUV@FNfDpys>cQ$6C{WFI`Buj5~Gh4>u&#&^)tjlruwwtF>|cTUVc zQd?xtpGQZBIV5ni_Xqwi&lZicyPuo>kjwDN-06o{r1!B49MLy@R^H4{npXveYrSW3 zXMAGK+coEhU)@>ePXGA|T%UG;wQz4|?c^8$FS|%=V}}sp1vClz123M2`d$xP*tayyTkB z_rpu97x~@b-g1$HkI3G4J+Zql>g*9b#s|DK=uQCeu@~FyhuK2z8*L%U#t2q z_i?a zuO8H&gID89Ec?i2MxK570MB9*@hR}%z$Jw($PY^Y=fg5?zEZKQRwobx|3$$^{0xs2 zm%uOiWiHF}{@T1DFd6vP!0@rIuk+y5eWDLPx7Zc!-V{GCv8`^r5zEch_4`Hl#V6nu`%F%PIAFJ~4zK7@VlVPY@iVbO zu>id1WxQ9f{waznNV~20F3<=$LHomS45;LAWKq z)O+Z$bWMJF?>Be!cXsR#;xq0v+pjPDS=nbP~HVz_lNk!`rom#F|VrL=R}e#)RR73TkK9bQG6Zd6rR`p zsJd|rU(!b2A)Af-<}-nZZ!fr>Z07baHer)>7@Kt-n4I~Onl}eu2kmI~2pshcehRue z{6S;JOiqe<)9=W+Q}dhej@~6l4&BKCq379)6b)WcavulaUi+~=kHDYU&12HHD@t5J z&JZ5j4Kw(~9>W{D`4t?)H~0a>EYLMy13kXtm7{jo1gEae8l<}i=_WWeE;8Yq*xh$T zm-$KX!al9-{ycEXd*vGOed0;3=^sC~938QIe8D~&{EGagZ%eyBuHBx=!K+QeQx1!* za!a8xyb~WcR&t3RI2=Ck2cL#ZVuNgaYeCLU{pBTRWk`Nl`Ci2XlJC7N{^3srKJ+&_ zr7=D;_rg>B3oTdv=iTyc4h~G%Fl<7&=bhTRpB^U0BcD{gx^D&F|B?8{TF}~9aj@Xk zS+ULE6o2URGZwl4xm;~%bV1kR8)m0;+k@~09*G~~DRQyIrr0Lke2qW!hrzX*;uq|a z^T8?m{_L0Ijp12ei+uTc;Y;jzehzRmH}olfDsql5S{}IHc+lV>AhgL@48e8K0jC)8vYsM|F zYp0CysNmf<;xoRc=-%Q~d=z32^i{mlT=28<*~#N$GmyRf7yR(}D0nN@yYktx7vzMP z6T17K^G%&~V9$&ezk*leV|P6x@BVS&rDtY5@ZDJW+Ybppp!3LqqMPe`wh~fXt#_89RF7tC7#l4zT8s zACz4qmja%F3xBbgsyXp4`Yj&7UuquIe=BqHyJClD*NqMH7hCLs>B|Mhw@8k?H}fJ+ zQgtizDvuaHptrK=^_{H4hrohgjx9uoGPZ7Fo45v7NW(oGyjV!N{7_Za*^E^XrL2SZl&3q>hZeBYwk9Y>_p9$w+-) zRb7GMzS$$?vg{M~_0Sf4fa7wV$Uk%BrMEUpHpH?+CrzRA6Fms%=IFRh+6v@WM~YNVveq zY=mVZD9z_1*$p+UJviX3IW2)-?T9ej)4PXF;fc}+hkxkvxjJ&&XL9lY8kI?WNWZ?7)>$8Rf* z@GW@{{$ai=PC4RP=n!tf^*05c?fQj_Gwu=BF9ff~CAVSo$oOZ3Pq#5ZzQ2ppfBt2> z2mXkwiCtNnq!>5;&9~C#F1PtZ?z4B198&fqJZ0aob1u!Z?n+#C1LXM?7`{HT|Es~} zF9!$k7ja%T7C6zb=r3dp8Hr}e1AYMGw8oO1#?K(eOh(o@Ks)SWyyScN{kN6p2?wt> zPnngRlqrGfM{`g69WU?UXJF%?UvXDHZFaYQwR^A0Pi^bPbm%nr?FWOOcQ=n74K8)J ziEg!N^qS}9e!lBFicKKrgrC;_N%#d@YiW2=H(piT!avx8^n#nCQ~qP+`$t56)7>AC z?+f$~ChWtsfbKsJk2E{dc z+im$4W~X^r_juRG@AyydA3T0u@M$m)nBFZnv&Jc`%YpwjrjG~dvlt@1i@X=B=f8rN zm1p6>d=Fso-N5%>$&Vv=NB9d)jZ1lGY{Gp)e;*5sf0JjnIr8iwGKGIp92cDE>Er^P zLkv=$2|rJ}7T@~(o1+U|n>^ex?U}55i3x8Tx$>dV`7g^H)0^el*`I@KmM4m5@~;oq zv)~qAJexvX@O#lIJIOs-*|_apH!-oot@0h(#gUid$$W>_0@AJIGF+7Lvvc_qy_SCizos8p7e?3TN92?6EIA_9hVv$d6i=CtCKqctJuiC^@0(Sh$?`8up( z8%+q$`uD^rmuJ6`_vamAy<+(E8@!#om0xI{>3n>k)=%5(f}e&z7QN~}Uy1Kh{5AO* zV~C3tm`rTu26ui^{2kt9je;Ce@i%j3Zs}Qg<#1UDR%|r7J73zTil2MyRZUKOq~kje zh)uj#{KMYoema_UJmdp=_2lM4(Vn>|>M$sDpv<-*dh#GcTNKG7G!imk)ef-iDX=sU*O>V|OOC#7`bmGxZm zHNKE*e~Axa75k~iuPMg=z~s(s8QD88a^$-5eCwU)3G6^~C7+E?l3hA$^;6%GVM;;@1D&_^;@nw8U4j4TcFOYj>?VA`P8B%Eh zF90WVC`K*E2oJbDe&{jK+UOuR*eWpGBx5)sGNz4z&>`?Ea5I$05A^%gxGQ=-9*pv2GavC;?9{aGw3VYM9?-l)+ZyjGibNp8N z)cOPeQ2aCImtIX~@F((r=%ZMUwVR(QHt2-m?^@Y;=f2T(j*I`0y@lSr53Z0m_!J$F zKF&{Np3qz^;Dj&mLy&>+>L0N~5b!GgvR?t3+CK946&WXgFF(~U%X$jB1Y52<9}Ao- zzB%G&ZT$) z$T$7uXJF5YZ^<99N0o81&0I%2Y%aJ%)>j{V_jp~?mJSW~{+w^jtq#7~%`tfO;Lg8g zZSkDw?)-psDe&M|s5u1iiajO=g>DV*VrG1X_$It^PPcehuKm4v>gwKI0TZ9c#L(0i zgUi1v`Y>OT_!}Nhw`21fzcKSQ;RhA39G&YYne*0+`){LrcVdxSB-iWg;ITai#NB^Y zd=2u8p75C(ywXvr*?-*XvLDX0ODBkyl z#FYQB`tNHL9c#zrVr`T4i@W9f;MQQmEBt)89@EmH?}oN}N0CqT5Bi393%SWZFQ4)k zquXPwz+`UL>(lTa(VO!FHtkk^>jz_cdjYvDNOGTwwD0><9np$Y44*x)<}~1wm=I~3&{Tb!=Fhb*-#1`H^c{y*)GkzAlwD$IAMJJKh2gYjqh!*S7p4-JfN2+#jT(f#Bcl5cPV zp3n#7)v<-hGJLAijo-#b$HJ$`JFw@osxr{=rz!tEdS6`R!KSgh7sc*=TYOYx8ylGq z>=(t3fLC|~yrG}!AAOyi^O;FeqhcvEr8kt&FDU!6*4f^gVm1$lF<#Ipo)(SCI>Fja;B_n``#=+tc5_B`~T68yfqi{d*vy}d-4u)RxEJ1euY=?kAEY+`py`AXFDd}ecR;bT#z+I7YCQf zaQIMjL(sCd(R_Att;lCKFMdS6;a%(;dZpYoal>lI;8SQsjM}=#Uz9zQCyZiB#?&Oa zPLEB$J{laqzTg92D4mo|#h)Nfz!p@%ip~mGDlhWA1Aa?#haP}u@V)VOvrFs`%7*z( za$oKqeXKL|%1^jk{UQ&^9&nb=KyGPQd72ypzNSAX zuDuGpaw+#bCceYV3lHG8BX`AY%q6@vE_SATF!(LUm@lUCDze8lIW=D@vF0)9#-XXr z!lw@oyuT3mxBmK8CfpFX-WWTeT_*z9?DNFeAns}(89Fh)Eq|&UBXkGv*cj%F4&xra zKs@8-`1bD}olP-X#40e{Cws8IBJpkCfTCNq@sBo!3|H7XVkKf->{Na3*01!{8UnIU z{*PD<*oIbM_FAA`GN%ry^QJuEi&G1=4e72%`&wLdCyL99}4h`FT4`%TXgyB9;G zo7&Gszu7=y*X*(nqe1KruUlHJ4`j4BH2}jGCSLO8=39|r^wFE6m)}-=*7&B} zPkJMNwm2bO0`9OCyLBwGuhqxIplCWx`ffoI3EIm|h`=zGn_gI6Qw6!gPneAaEeD7ad6 zw>yhn_Popu{zJ!+w<4Y*euLh+Z9)AwGVo$Uz9hJK?byH9R=z=J@3G#D{mr3yz60}c zWD32}Z*ssqz$5+!b_@9?r-TkdAH%QkPPrc95ZcqvTtgdtCce}C`*r&bn9%0<%*C;Rkdj`mtCP`BvBKnHT(lj>T3KYllPlE}aoQ^GV=WY+rhqXW`F3PfTzP*76nJ zF){n*ZWUkkA<@t9eLAdp69*=9(QuV_;$U!@yfW|bPkhe2v+3mFU7LBjyP32yS(r6l zTjzP7D}DvB2l}_XEBdi9vGpC{Hy;9@fAwdZL%frH@BQqaHO>9v7v3^<`pL;l^e*}x z-vynKK1Y|-4|17}-s0V_^4{AD9+J)Y72Q)_0G$gjK<9ig{3Cp*-^g6vSiS+X^;l?r zRc$+)#RvDM*t3_VZ}7hAO!OG>WAR(-^T-o;g}?K?;bZ*FeBAgi`rw0xkG_{{zWM3e zJgpyfeidG`25XN?KGu$*+pU{(vmeOind@H!&UXy(2>8U_f$#9A?x_WKatQdG@H6%s z-!~uHuaf8YfA`GU?B`YXjM+1DcU2iXKLR=Doi*pI&0TE26~$*JADC_jr@)B*u0MF$ zRV9CJjdE2Z`!`K4z+;0mua3TcUFJc)QtLNBCu|1ewcblCgRh6)BAz8CPX4nS$9RIg6t=*Z1C#$q;Kq8s*j(-|+nsD_oKI+H1pC*m`o3<=(Ja_{OU3jStwf zfqsq;fjeB{dqYq1Vyb_l;uc=YE@L~}oARzz+cTq`v-lU9;K=Og?Dh+6@yeXAJM@VR z#OL|cz)H-7pN8JZ#s+KlM5P1xNVnvJw7wQz@uQf>zhp16HT>p5;5$CL`@X@QLxVS$ z75LHf$R70D7T-Fyo}t|@ot!ofaHCt$sf|Pa$Txx?ca{AwhOCJJx8T)%qT9Tp;Hmk; zkI=nbMRp0e@V$~L=9KPh9hGN$2i!t?)}WDtKPfiC_|eWuce}FwYn$-uCuL8N7l(iH z-?2xke-qu4G2qT`rJmd;Gw@b1UVRa#g!}Skx_JlrNv_b#F3J7BSvPwoXWyOs1*eY7 zT=0Dk?pcCAvSaCgWH@@os}w#+_PTf-+O0TMd8_<0cvaVRi{soB|IOb*bC|&z{KXF< z7Q1Ko)&-g8%Zq#lFa0r(coBUIJlnO_?Rr>oJN{j`MBiZ_k>7ahA7a0Ent?TZwOd)& z{J_x9hoU3>xr`mp6X%v2Al8mYi6ihI^AWQZy}!aof6yTN5D)m>=>2uI58cPm(-v7{ zHa~fBAB@g)d&bb_x!9M9J<3-gcb9yBRpv_0h&UU4yzV!{7WPd6G{#pAw-hwOrX+9J z$v2d>6f3o7y8E@N--&JR9KQ3(JoCn)U!w7f&+r7jqX%(KIxm>P8S=q*Ug`CGSCy~Q zv&0tYGvpF}!alhyc=dm=IoA}gCI+v3n{?aw8%~aI*t#jYG5$%Hp;w_t^v9P=?_-CP z#o{?|4&RkyiC*;6p2`1A4o+8}{EV-Ahu8;K zwj30mbz^vFn|D>?UhHOeg8e+{UPlF%{O@>)KC?IEN#iqo6V}Gunf+V3Vk-qETgMmq zq})F%{kIl_je_p+XEKAY5&xDmi{9}*F+2Hlm1n`Lxq0umi;cBbdqb4{k?gy5QEc`v z=(r^8eC*AH1X1#U0=0IC=#&x;_niTuFKC5K2Kja$@`uW{J*N;E?ZMyt$P4VIga24 zhBbEpZJRh27G|&2KOHY=2(+ZwyBUT2pZU&Wz?!u?Ig8+-Se4 zKu@*ynT!(C6l-Pw(F6Ge@vhd+xU<-`{8z>&zw;wykFi9H+P}c$p`o4k6&*%gL5_s| z0P!NY#jjX>?&iyyc>Y&D7`W8Uuh@dOC)YF&UE9Cxqdz{+-aUQzLU?8SePa3i`Udyy zBLk247wk)cC$rV9Z{a85FOrKzZt@$`P4F}{C|4gpz<2qYeiYcQ#W!H$w|Ye2@xI8o zc1;$Z3OD%w#q0S&;5Xb63kNqoYdQH~2FBewJACH@r7!X`^CQB;SLbNf4?lY5g8>OuFv!-a=6B$`Pr?pBDbtt#e4Y^v;^T}Ml#Q8tE|Gz`xxzk*kM-26jqb3S@H*n9Ji zyPNEovi5($t8ryb`}y%Ny)y6Os}V;A8@y#f@PW-Qc2jL}u^jp!-zLAaoEh!V)8(Ph zKMk(s&;C|V4v%AZ{IS>q>~pfb&1Imkwck#mui)9nPmgK8tu(mb&ffWk!aJI;WPY|= z`%wp{$ijz2w>&kzVc*N63s;?%4lIX<&xh~O-cj@=HY`0rPBy<5Io{?GwRwQ_9q|fw zu4kA>eB#>3+P`J3$QsZ?*>~r}W@=>e6@|Z$3uH1G%>JQY(F^Ggcs*Yn-O4`q)n{aW zw81}(QJ?6KWGS5=U6|j?#vXHB*<56uYLonx2j#g>j=k`e$Z+4C{b`{~HfZHj@*>Gc zM|vJUD^~<-w^gnm-1BW^b1_nV@M?T?jAfBqZ46R;ncrNji2Yz~BfQraz6A0N zpF)>(6-Tmx{ii?tUF0XfAskajKm1&t`>S=g-lN1~AD;Y?y|Vw<7b3&q1s#y=BZtIN z;1|9xM!?n)TeY4DuOctxG@ubY?9$-mpGzEIyXb9A=H`stW3Q@K#_~bZ6WN;bl-OeS zpfEo4ZH#P7HfPnZW(Kbg&%3T`PFWM0A8EeSMYp_P#(#9??dr^Z`yEw&SA1C^qvfaZ z;n}x=4p4nP;yZHW*f#Lac*IZSId}$sWn3*kxTW*s!96Nh)}N8;BTF6_oPAxMDG#pQ z8=SAB;*q(qmopq;D|kOSsGsaXwl-gU)!V_B%tXIbJVc!ts#_ z+s1BvdEVE4uig4D^vSozcY}`k-o!1)0B}T?)dy!R#=&p-`>f6E#NV<>_C@Fmjy)|n z!lsq;1gFdy{9<#WYq*6cS4a5M?u*g$YgeiJ&k?h)>H>+RaYBe)2s>`Qum zvA5PJ=DM;mrSD|nBf@iD8v67d()N2<^eQ@^cp#e_Ucr055_Se2Bu)m_VzTUAvGS|a z-@i6TCayg@a^uEA$KrotwQLhddpY8Na+bv56*{}QVmH7&G8oVDJ-ydQr|hibZ}Vi9 zlV|bZ(9_A8m#fR%fVmg|JqiCI2k8a$L44a-%@f_1&(u0qyqe#|-U4iQ{bwVBC0{ao z(w@ox%032T(6Ndhabot>_)2n5KNTFpH}OL6hu7`5ep=muJl03?5BS7KqhI`I;9nLZ7f!mAy@m1uJ%7OAEg( z>x}3lid;`IDm3%G7VkV(Hy** zlk%y^=n?ibS=pFIQ=fgf`LXVs{vHrK`F7~H)!p$<^Z`%kO=9x&UUs5b1UuH8qIdJl zr^WvQ@8~>qNAyRJ;kR%1!M{1{5KbI3UcEB8*58Od4TsU3waDZi8rMg0KRNDVeQZ(o z6S-ke8F6$u<$P`Khf~%|(DCX0{B+7!)7Sql`_ipZ%ynh3&imz2-Y2Cm^ZV_R+lQCb zy*I#-ehwF^-<_?Y|8$O;k4GlZJII7f!}pgZ@4$X!U_h>eu~;IThP~{Wd_<0XKlmbE z@v^k>Zhn7wbx@91mOYWit!xZyS|7aHBywZ-;QOb;uUg*#yrRF*OZctW6ZjK3$A66% z7$e?BhZ0NVv*P1p>(UjfzNn4ye?4>er?PHo)OJVuwrOH$Cl&mK!=8oL=!}|T2m@S=&?u2K7ss4X9xi_y!f9WUIv(m-!IyhzhjdOMtyy6q` zTm>&Tc6th*1l>vw$FB$XI$oDU2g>N~+eA*iDX?yRi{gO#N9PqQq|4C1<;>!(c(Oc1 z^J|XCc6nU<-E3Pvdc1+ZMn0x>ho1=#yR*cgM(rm_Y<%vh4Wp=)ZuSDzf=T0PRQ z@J@Vy3>07GYpXhYZReg^;HK}s-rY=DmvF7miVPeVel<6^@`+-vvSaxP`E1E3vJ9Wa zuf%O?{;IVce9!Vr=-|eN25yYcM!X5_@S(CD`K0Lqaun?qglF;PtLF!TSM~vuuYpJK zH9RqR^|IjS-D~bwtYaMMSnz6GVp;ENPAf7EKJoAJ`LKoI7CA^~5)WV(k|}UX+>qYL zXNtDKz}U?nUC~;9wz6DAe#oB}zvHOB0aN;QboO%oQtTdkOyYT-t9Un?-O>Bdq4%L- z`X65ho=WHA1M=>Qf9~h2a}WMT2L3jCgs!LWRF`!++ayjnDSFK5!3Q|Ow}5BhNo*ka zp}*pS&dtAZp&du-SNKNxROnOI@0btz1>NiQfxEqY=(=Pm{Ys1rpR<3Sd}q7~uE`hh zu6=@6A1b*f_sHuS2~XCfE^~O_;QHaAi>nH6u5wy@z}(QA;2PVCeC8*@i|V>J@f`62 z_2$bQqA@uQ-hfUJm6Og)ApPB zR>m6aH@T{@c3;aJZj#ve!NIK`7kx^sft_OC5q=cBi`>Vn$$mN(-vPZ`%n-k|Hiz%T zx)Ucsr^}dVr+xT)uF^8p3?@A8Sw*tG$ryS{0bO-#LtmXecxB5wsc4L;s@8|`G3#ei4b``{UtAHeDcSpMvwhmU~y-$+r+*275H?> zG`1qV5btA$^Y?(EK9UR1OJ2nWPj`)xK8oBfMfH~r)Fdqt<=VLS<;@$KE zxO-dXe0`2P@Jy_5X2$lN;7BX;<)axZoT69Ksq~2*&!5K*6yu@4(An)*hF|cTuxZg6 zo2%t>ZCnxWzN7Ho)#mvmXYE0;_g)^HYIQ3xW)FfdJP{l84)~$KIkYIYj~4KH1tlge_$ytUJ&Pw8k9k2aV%2yq{mXTF7d;3Z%sD@R*cQJG+%)gkCm-qV zReuAf^ylfxh5SO+hSu+|zym(0x<7|LRM`m53L671)%X`0mSX}R^{JNH-uk{@m)OWQ ztAo+nUeA~w6j}Jfz~R==;T_Qf+wYcG^UF6wZqcFeB0RR*a_GpKVe8b;DLYC3eczXk zNv~vQ$=gN4@QQxPj>7ZkUt&jeIeHhGdunv6OUiomwa(*R9b7W*`$z7s2p+XDsy3gs z^|^~Lqp$YcwJpw&IdFzqzx>STgI^tIne?cy(dc&wefI zj;c?sKA%28Hq`tHc#L0pH$1bxh8&=$$ZKK`!DluqT<`AVRjK<^$!A+<-@jcMoa1-* z?Y1K#r*12H1X(10!KWmKAnr`pf*11p?Bm0(qkr(4H#^hX zmpWds5|5C(L8gF-So4?4H|f@?ziyCBIvpGxLnERDBHYiPN*q?9X&#(HB=brr_1M=uwAKHw z!awmzc!z%Z*1?~yQDeLIW}&;vZzoT*#k;RjYOLl%A!qm@$W^$< z{`zU+K^tP+4~gzRA#=GjeI@_tdHe)m0LSEpk>_I3__mx)>qqFC;K43KKXuONGHe(2 ziFp?9WFN~Tx?|Nblag!s@!;OK)0e6{(KGNOG26PfgN;|O!!c_+>Nh|2%RA{7=s^ra z9Ed(s<>9yD3;J_x&J6+I+0iw&XcojLcU7?~sx5;r(Gk!lz9a_&|G}p&4v)cWox2X6 z))(PpuDu}hK9q`Acov^FdjNf(lX?4}b+fK>b>x`*(I)Gl_sYC| zF=M&4_%i7x`eL0R{I{M^T%Fv*pYSc~N94=mC6!;nE%VG~qc8H+$Zs;Q@^P+;AM&3~ zzGi#RKT{kSZXNvlK%Up~7S974zCyk-a)A89!(8)Tx*U01b3o8F`(1oj%z)h?SB72! z7eAWcKg_pBM!7HDJ$pK~n(&k+HsNl;s}F@YwcoSjuY@=F61y7yh*$8BpdV`i(Goo3 zf1+221@foUzu+W$MEqU5>aSoE+JjO~zj@+wWUtG?w2#A^OAf%+V;jMmx6WSC+h(8l z4;A@A*Y-^SzH+!<=g*N0v=$71V0Y9!6W3}n*Zd9e5N?`tIgIQfzN}v-w|)dZQSfSh z_D8uaz8-tC*XKF%iSg~jVe1wvE_)Y!j$aU;s(O37N7rv7!*4Gz)&@@{Kk(ekN53!K znnQK)&hX)fM0dX+Jn81ld+X!W7jlZtN=L$9*>L=DbX<8bbSgTxyej@d`)2U9@L{7B zJ|F&PwFst^zyrJ)nyl+YHYFuD&T=*RwXedwe zZ2nOAfoIiyyVS!MdOMl7B<~}C@h);4T;X0VbSuUYwbB-S39_k2S3Zx|n4<|6Oth~f(7qUyY2TKt`CVtyC-;b^Dk zqR?OXW=_d9x*7jBU58&n{ycj8cKHU(gNFAoc_o`gMm;|`@Z%!;@I22W=fo$`YxOZy z-3q;XH@}})hddPeuR;czH#k*sUL5C&_(Oj;wteY__jRo2WiNn-C%0km=vW7(k2RKI z?G>F!4hy?b?i-z+Z?O8C<&@DU==bD^b&}eU1$18WQ(i0`jNCjSc*GW#J43#(3B|kr zBgg;UFgj(0Prr_}eQ@fvWnTH__=oWU`VHJiU*aZW=xBfrjYsew;3IYZ>bDcfN&HL< z!ToULiahV;=y9FqQT5%9MF#Ghwb-A_TDk66vwQFy{%QLNRUCsa_$J+n{z12G_Z_e{ zy!|${exg^nSMiE`y)EmI*5Dg7-TfWw@1kQpG~fMrLVQPc&l~Vl@kq1>zsbD%KD?gpD`o?xRd4s(e9<@BHA6oe;9H*$ zp53^9_n5C^8h(rp`=H3eR|SXKc%F5q@*e4h@PfUEKHvp^G`k!Q(H)IZE+F}Uud#9Y zO6gGYoAERD3K+D0ur}5}mbCorcaa6}4$NbvZP>r8)4hN6fH#HSt)GSO)(o}rTXRWn zvoXjcwh2Fr`DUNFUh%5pgLeED^acJ^yxy2k$?^91VTa_yCqLh7Yxj|> zHy2+IJyi@95328k!|KHZYwmW{-SGgjL0qu*?}MS4A4b-8txMlfP|jW*P4tbv@T4C{ zUfo%AATpevKyEvoLmZPVW83+6_SGFy@iaW9>Oea?=TdqZ#cJaUDt zcvSH9%Iu%i1@jFy?k$S{?%3?dZGSRr&|6zXt_7UBFub9)U)#O>@LKkkTz2!@jaPUJ zzm_qxZ;Tr}voOAXy+$!*d{*cM@IMiT*mOL0ylah+0Mq*Keh>9JXo=Z z_;~n&$pwBA?ZjsIOZiplz5LbWB0nShoF2kTpU9fq1$6nbv0XP4}M{xmV?rBV6F?!t>gFQGM6G-P8QdY5$YF zqto?`_a|j-_+`yo!dF_q6@9GYgXh69x`G@_>l>~6<7*&a>0NSP%m;bG|IA(hU-=}1 z`2M4OSNDqK+K=cz>xRYp`rWxt@)h@qOkmUSHS_DCKmI|qNEbqjU`p>$CtjdfC&FLA z_Cxb>VHG+#J&a96r&Z)J*lP-mJo~#@$GD!p12P&|g-t#nv~+HXsgvvcaB>LvcIA@G zpOu3KulPRc>vSo;m`l@t;EVQ9C1)yLeJ#K5DBpa4$Y@}(KH7vY z-#0nBFNz-j+t5Ip_Xx-2O~Mara`>yr0sFn`J3Xt$(#S_Vn?C_9!8iIa9MuMH)dIid zI>=G_RvGu0_Bvjd`Tc z*W|Xza}&2`5Aw}fFUBuQuO}zbD%x)MSM|N8_M52oGqd(Wp2Qmhqg#qjdGC&UR@P*_ zB{u2#=@a`DucgDZHvaXIJ*}MQFR~Ain1uWe`wkkf^&)gkV-^>td*W?;7i2ve`)=m? zHzlrq-@tl&^oC~_oWkdP^O4S9-|2g`V2o#4Cx<^)+~bd8qw*=T(_5Ts_bRgX1nt8q zy1qI3#rjzzF|91U;7jkvuFXP@J-zz>oddP+88{eEIa=2ZQx}J$W-* z1V3IGSo+4ReD$9f84q^&KYg4{i1))cwl}*49MLfU9C=Tl6gLD5Fh_^tJme(VVI2wD ze`jLs|7ae+zQ?t?=O?D~@bJ%97Cc~kDDoKbZR0~<_%_`}?wGl)e$mQD&NGW}W{Q-N3XV*8@l@9zg?+@tz7+3NbfHrK^#O3ikcvkf@J}|Ij$K4rS>{++I=@LY3*k)vgbt0+vy6{@7mAgqnVSNn-@netTg9sU5sfo&nV;GD!E_BX1?B* zez7m?lljch$-?l0p5(I539jMApDld5-AAe~yuvrxUmKW^q1NrnHKTWvdmjs&+Wq8i zj*OHSackngm&ATPIJ(%oQ{J2Mp768x1a9vS9$p&QsC!>v{ORz?E5b9s93KDa!0Q9i z-QE+Nc~A7GkL0=E3{G7Wef@^e!42u-_3^=77e4u=;Qa^F{?pN|=CczvhQIi9CWVi_ zIe7Dl@S)4&zx{S#{G-srm4WkB>HD=A&z0f3-;ZD7`q1}}qE~%C@cd5X`*(tmpDJ(y zg9SXG4P@4PW_Sc>0GU_bMqJ!)$b)2cxE#p&)8{5S&;Y7Pu=_+_skx}kI6Uoc8*Q6Tm02eN}O%~{%hLx zM2_wf8}^a0^>)nO-fcNI#}%o6M)crG$<;g{^Yws@2CwYrGc$dk(L5o2+dci9pZ=eb z??qpbba$QkItS=_IublexRw@|6@*kjeEw%as)rH*fVkE zUGj{_C-3=y_+X!zb!I=v+VPEro+iZ4^3<$f=*t{FC3|H)F~@z=&x6zVLsAxI9J^;N zFE~6h`1i<+_p$l5+7mKHQ`mTxF-pEVxam3CX_xMR+DN&Vv!ciTH_s*X!}PRjBW z`>4-NIVRU9r|p)BqugtD_C08KV&FR=dHlY!Iw82RL+qCc*-v~zXlO$4X+qXnOvs+U zI|L3B16Oi$LU3h5_7a;A{F{)v3GvNLNM6QvxwnPYmN}o3a$d?Ixp()JU9%U*#xw2- z$$6iUIhz<#!w$Hc6cgVM~cL)r&&-k|uKI~B7@bp}NM#`krZ<(@Hf7v$U-66jp znQOCC9+%(SmS=3AvBC{y%My#-I@gu0Qg+Kbk4iZxzqjwIcdeE!a~-VB2^gQ9a!!AF zUh3i2q5apNpX1Y0whUib=X+cX$CuHbhnH@iJ{_5Y7N(}}aH5WHm;9cW`oq(1evZ%1 z`S~dqq`WZaOHv+{^2C%KQ`Zf@xjBD+?mH&snJG`r`Ms`NRk)-f!y zDZ7^Y2Cr|Oy2qqEE(Ol-pK@5r@o9Hf3fQ;spO|wrF)@2F>emk0k1X-eI=*}Rm_EU) zo|MO?Oiy`2`VN*Y{2beHqFXd$T<4|k@D%WagDni}abn?Z=H~3+(?K~-NqI!%=*Bee zZPTaYQw~Z0PfNiA$Q8fw=ri-%oZ{1a<=og$&2PLN4(**IevDr%O@Vt)N-^JfI=-)6 zH(s@HLW?`bj|bjE;kQld`%>nmew+Slp5xitu1G=CFUa|G(hjd}+wBnF$%8i{`0g0} zYGT2o1JcLmr<|Viz8ts9ai6(QRvA;P4LF`|qI}5qu{k zH@4*`C#TO~cV^DfBtEr!;Cyh7OLBg0O667Hy?@T(-3~dTDYyhy6^7tq?pnF(JFeN+ z=HBMd_5JDBW+~gG&pT&~uWdaSZ8`^|9dqs-hvc_DbmJ8q)rX@~k4Kt^=a+MIuw$X$EwbO| z#slApfiD68#^`&(?9%%>^GO)#oXgL_=OB)m8}L3Y1^(fWk4^#ehi9$TM)TL% zA--qdXliNi*c_jgazgs4?=4MC3{JE<2!7V$R;x#VDV>Ad1Q#;l@DyV{C&$wQ-z6!} zNZBWK8xK8Ah~Cr6EMu#%Z&ls}2dAYNBi?sZ3LM=xMctH?z0-c<;aA(nmo}&$!WaD{|I8!# zNVb#ll~2|BT~b$hn|AJTUgv-K5&p6}@sw_yaqo;2usSg1q!hH*-KP6l+Aq%a3UhNn z*E==Gvr@?DjR(FvhF|R%p4`fV1Jl1VQ(lmQmgtFS5iB zI~2a?dBc4Qc-D@A+w44p{=|pz$gcO)_jz|~1Mp7_>bc;r(6`LN!aQr^!K(?O+g1-E zOuON~b;kVQtQp*B{v!YJ zXuJac;2Td#**#^y^!=Qa3-TKed1`)F|7D$PNBn~h>fR@(kf(SY-UoI&m*4QJ)yul? zt-rNJZ_bCptF7}6vi8UnK16+`w>>TG=^*+?CeUxUj_lcJ{?ecE=`XvcThFL?VcZ92 z4Ckhx3;YTHVz=y_`{*9k-%#(VFdKT@A^Ph^62k%fjN_r{JG{b&7pLF}_}1~cMmMCt z%*b!L`p~h!0r-GN(63q@^Y9!Wm9}ibR;O%rv61wv;m=$7dFy}m9=ZrR#~=RYE>2VLtIyjZdF0!dyo)U}p3Re+w9)+0 zrPE)$dQgX;3%0zwTmE(b-ZIz6Qnm`r?y;&B?}V{dIe%?v~VzTl>0y<}vYybdqJ7 zzxH`+T^C!c`+0x!Xm#iLac&vMXzL6;v7I6J4%E38(0yL+PQIT?>*1YGGC6HmFJ!3=Su1dyoaa_2!+dJ>*svGZpwJu|E?~uB{YkYUzEooosQt#dgd0uxNc=Xqe>uC{CjHnPp5*0DjaA)*A$3=bTo+hR zC^yx6)2`NyPhI1Ax0JfQwd=3jd}Vbr;8vc|-7ff1>c;&Oh|||h)Udh<{dMOSAhqpE z-9zxSyubh6A$1vKnF7dF+x6G&T@Vhv=H7eM<^2P7o}it&bDBIUeZ6Phxoute&S1O2 zI(N1Ap4$>)+Ns;CJa2fNC-n2LB6qZF>p(kGTHZC33-mXb921K!}9n4hq?^+9^+lpI)pU*-XV1s{!J>4 z9^CXbvs0F(%xV@i^9L52PI}1wyS_y?E`hN(o*lGpCN`Js;%vJ_awe8%JR~n`!)BA3 z!F#sbWLmBs*euSo4{UmyMfufN-r!x?{4~G)eR0!2hEUr3OGW>`z59y*_VtYENyBD(^WZ?>j#ComARQYVMck)OH7^-HNpAzP@|j zS4&eizZ=)gO}k_Bwi8PaPRYBC;MnH4JiBS0(mXKFT+m-YUzqz(ZjNrAn|_}*WNh8{8N{?QhN10;k7RHR#?k~^!0*8HWyL_x z2BB`8W^75Wg6*m4{}DsrcK=O3vFRy~8CT%3Uvp5iPg-}faMK_BsdK$!soAkP{+y= zZ%N;R`O6mfE;_JxQL}6Rg*iR*`sN-o@ySn~IHaP#*>26YBRpc^0sAgX>m`epE?zS5 zpy6-u$jQ4kPhQ#mz5D1%->lxnt9!&#R`Q761AFH$nY&=${^zXjF(IXU0K-TQ+;8^k zZhh)XZe7^BXwIw!OXn}?0_aNM(6f?9Ebg7PbW!i(C7GY43s={VV9AI>Skyaj!IIuN ztA*sOmE6D2f(1)@_Lp~EJ#4gO5H+#u~1#?&1h2E9iKf7=7yw$yX&Pwi` zwIC4e9R>XMUC9G_7A{T@pMT)$ zx_!V(Zk;tZB)od$ox76T4_v(3`OY5eBjU@FMLl!o^vzmb$LFu)_P)il7Wb|0=7`o2 z-@SOrf<-<1u6C{$uH^Q43l}dwaJ4;u+Dh&Yt{ptPcj4Rxhs+xV$}C#RQx01Fq%9^C zh=zU`w48p~!dc7Gg?w>zkdo__{T9scEf=RoQyF&K;zNQc^CBw;iLn2+eu1(_Ge}+i zSN83h7w$g286=wi3$w#%mMmCU-ZgpBr06Td-lm5mc6$&W?W2aZ>VEJc0}mZ!$^F*{ z5T|{}J*8jQb9QUqIKqQ;aCIWI>md`*IcMUK@!a#3?YxYpVV$PknzxV8v;O&AAD!E+ zdCy2Mto>OnKo-`PyEPve;Xy6Btghpw)hNK!1^wNckB;!VevDq-``TuscwtKiyEUI1 z;az3q6;}t*aR4CASDJ=OMO6!PSkybKXG!nu{G2tnXHgHmty#A0=;cS9cd_}0 zee?;ZFJIAWm-;q!ivRPMEStY1*2I~UriE}PwA~)Klm=4{aB+`ZoOsWR2QO?dPR5#s zJoKQ&lcu)UrZhVZzt%T@x@*(((BwD`x$nTf=uY+GqlR6~Lub@$j~;ey?t=OI?zdpk z{Ca)oVb?R1J=<%On_cdCt^c`GSV}`Wl>7FXyR^5xb*e8M55K%*zo;wwEtor7o1Tm* zc@V?fOrBIPW=qNbi+#Pz`g*673sZ7e%#iL2Q_F>^xsZ*LhF+NBSyLxX@tNuFE7Qs? z=5gxq3zOY4ZPMg0{2{mOQEoB2dknu&ZrLNZOdob-56{{ocP7r&{rc(UStfPH@C%C< zmZwe6(`MfDS{cvu;L8(-UfZ)gaL+vONkcEpC>Lhr!d^o!%q$mX=0fb0?!J*q{j(GE zW5|`ksfk4~cm-7qo|TD;5FM<^b)y=rC`}?w2Wv``DP0w%NyO@~CR4jA`g%=lBoQ*`uqXuUDI>n8Vsk@2cqQ%_K&Z9BdL~pE;xJynRY^zF|!k_jc8k$3!<9 z(k80+;CWi?(L{k7tZ5m`;JIf^j_+f5li?uNYRL$#hC*4Zr6aT&3TUkk9HG@vP-~T> zkHM~n=MA3p^#;#)SX!BCVzYtJ0kqOj2Jr)YGEQvG5`nvE&nmUF?@= zlfrx_#RW9lqu#5e@nOu6a zV%Ww0-b^mNId$OTpj0lMd2aWGfxb*BeK~#Twf?S5DP1{Z=(Q^8OHa-mdab`BQ%Xn9 z8n`y70+fzCulquOM z^THulbY<|Ov-5{tpEBh7l;(nA*QXA-KDD`U*!5{cu1{+&s@DgovweTt(TnTVq6!V| z*-N^w;U?Xer!+4edU<%iUN-dd@NT_);PRjzRc7v$T^FhnRW7`$>q3_XRj$3d>)IgI zw*tB}cuiM>0RdgEymrWyWlQ!e_z<-#f0LWn4ZXT(aUrkv>Xhd7!>@MTJGFVk@T&!z z+Iy$58VBGxdD5~)iwi!rSNCY%H2i8&lG>}&oBtSkb@I&py1KPz^X8$K58S6;Rmq5E zG;fLdG5dfh>3CZ_y^9$S-91V3w>8az#r@Zo&FfiAWuKEjFlpZ1G>aDwyXb<;48X-p z_Zi`;>&<%y*lw6={9*kX)K6=FYj^J1!P|?`lTf|-?QaDivo_`MI+y_N3%`S z%52I^iw|11U*GIZoC(WGjGlw$FIY6MXKr8? zBQI40r;i+HQ99FiXz%QpD~}qi_1;oDwTUU0TT4$;6XPYe+rZ;{W-aMEFjE%GC1zWH zb*z=xX>Kx7H%(`m^mP@bne{6!N}hE8T5><+xxrU4tr*KYrG8cFV32~U~%94eFN{9F&W2#CHwU* zI&{H;c>!+Bm(;ZF^g+9+O=2Cj-O~A`<+P?JAh|fGRon?PDBd|wl){r| z(y((nYi@7PA{DbJs;O9%C_rn{&u*fXre^VeeRD9POw!TqJqr@JTA+iOljWrzTiA@6ys_4|-sK zRjrBgmzo3^mK6X_o=)RS&HhX0?_Zv?r+{iI=EMn9o-%{x*FL3fF*AW$6?yaieO^7Y ziH4Vo^3-tORP`j_sH%7Ite%Bs2>S@BrY7hV?ph!*D^XKb^B2tTs-2xEs@k4evw9bn zcD<5uQZsKs{7_|lUNI-pTD6N8_RR8zI3%KsrDEZtz6B;@%4Eq=shHn;u;)yf(nRaZ zdGCB4k110rS~=ZsL2SO9PorGrd~R=!*aDKSh>2WZ{PjO)ApoMr6M#0 z6{pOgLFIgL@7&&5rNvA-Q)<$oB7>&%P?A#7dvJnn;S9NJpXNpFU2(aEw90+6nrKUD z);B*~u++?MUR-Ks=h^Ljy-l>HwArtBZt30}YEsVUhJf?jJ@#p$Z{&PY|M@H$M$Y3e zCXx1-O{d8D;<>%O9;P24dv%KouI9KAEd`pbCjhpVGXetBUF| zeQNX0uBvIOrZtyzRZ%jg@6o)gtBPZ7`gB5n;PKpE)AuCm2dXG)(`S(H169c;ICJ{U z=6$7VUie_&!nud2>uKI!>Uw6I?&_E7pCEcDb20j_0gqsfiRglwYjbP z%95yy3ld(>m1)f{y00V>NCM2RAW8<2APA_K`Tu_B-1OF4)wBHZ zdA{{|zS(DH>pj17&pkKad+xdS{#ypZ9EzP@WlsJ*OUE!?(-0MURi^P&4!1ka9<~t? ztSbMR0o@2vsnN9)feyD@s~BOItdFnM)kPQJm}&h|26gSVhFM>R z?s{uXB#IfjWzsLxA15U|voxUNx@r9go#m?T*Dl!SGqpS(88 z(hf8jR=_nWD@t+pjzbN$xyF=q1D{`VHKygTv7(o3^vE_Nmn(&dqa{I!bFvYLmtc-d z#)=FAZKc8vs!^5W2}w1s$(0F-9STeWR|V3MZmrj0*V$;uW}w!va#P~GJSY$=y&fm$ z^Dxi{8rE4D!$~d5i9oHjSD51YlAH{*%4&~8J!X|#f!0`VaZ)rtDcga@q0-i;^HXw4 z$v)y~+0pM>$F!TDkzFM;E^~xg*)u|?(dJ5Seojs+frAIXD{_r~>xvxTRk>Ebv4}(O zN{*UbXCX94=j(F4ezG)tq0KkshO^`d<~A@SAdK4k9-|bqa~Z8Dk5w96C@`B~^k6K> zS$q0x)Py8Kp3Y&Dh_daN(tZ^*M1TB+Cq6C^C-ltg=rX@DidbCq=IYNK-$Yu_#m zh_uC01WXUliy)0JMlQ?ql|0NoEiZ8FO|~isD9MYAvs39ds)rVIZ4LUQyjbaQTdB2M zFi81@JFqGF9m8OC@>VFy!op7Jpy9tXL`7a=A&@gL4><@GD8u?P<*7FgXDkH}T$NWU zy~jtZTT4=TFSrt_teX63^;XmF&{Zy$n%_2c$?o-_J`zX;#=PZ*)Q+C>Aecd(e` zzCuwxIYjSb)dG9$Um3ZE^-i-^X<-N@y)2({X<0d1uk<*FMhYwPH%3)!>m^uRn%NLg zRX(jyn(&5vUTMwcmDOGgrb=sEUL}fzYsVKx@o0QgMfswFy_&81z+aLtDao0_KqQalm9u1dzSFNP!=JY(U$w7QbVF?9CHWc&eXT3V zim&UJs@*vX3;#D%ELXT_PHs*-F)>q>Zz|C39feVlJ)ke+8N8c31iA8^!RvF1z!RN= zuH}%X9@d#}D}-7fD(lR=e8(guL|AIRtHkA6GN8g>^Usc&4yiEQe9tpiTJUFf!ze5` z|KjM2O^isWGb|uja{kqk zeP$TsVQhd4F=q@lXa0AyDa$zybejiJ;G~?J zg}lHiIWG&O0;lDC2fB@hC~!t@mW8~)S-}Y#O_dLmXgDXgaG=|!hytr}>n!92*5o!> zAl0xgw^dMEADlXQ>ZPC5FHFns6dAOzVb1>vC80N+Z(=wgmHsC)h-vwGPt3}mk-I9u zwj9)$lM9uI*jg2zsyX?Ev&A)e>`FLpZb-{P<>n3tM#N5!0|Pd+)FD5znNV(r|Dn95BlQDNd$PVlq_?%&s*%eX!}%2oMK( zRR}ic`9MX_6vy%+=28m^p&qz2$JAj@XfcFKGauVSO$DGT_uuFYtQ$3$AkWB>30%Rj znT2L$Ss@)(GJ)o#jjGt}V!MJPb3)bI`IM4}1{ipi$%9kCsumo|WG5xZp08?UULkpbAWG@B0s3|#}l2cLB za*aZMfUxaGtSx5b+7$4jX63q+oQj&0>kZ;^t?`22|egb4Z6%ayNHzJ zWr}dMmjvXE=LlV$m+0wcst$;VPC3Y(UX zr+^nVBcDjgS_G!poP07x;2mSOx)Rolf0ZJc+BjL2Po?09CNx=-zg8B_BDX5>ZE3PD zf0Krfz0YJrKAj>-$22)FpE+CVOMRCi$x5e$rRZnVU>%fK1ILRskywg;E)T-%PS^M) zEK5J1hdZ!^z)oiU0oJKsP!^q5!g6HGN0pAH+E)UWbYEDj{av6lfVmJ&l(1O)Y667m zDP6Tp;-q{nFvOWw2`jg+2fCkWm9TF6MxZ9k^Ah&ozZHOFu3o|}_IFai)e>xFe^<#m z2rt5Dt8>s2R&C#l^jJf%F8ddyFV}`cOvt~dND`(b|Dni{Ue{SSlVwe*TEyimT&?KY z@*Z59+&ZSTJ|SnPNEnwkj>|a;)K{Dx(u%ABx6dJZzuJHyflei80GiX-P6f3O6B&q*wTDjbIxCpcLhHN?E~hq1`9X-`w7q zkTsgiWlTiwprlp!`kACjxua5`E;cHo4Wv9VC3i}Y3QgNIkZA8%o|u*&Q`8MCFp{rS z+s+76#$@Ej4TWuR2%42UE2OLZA#6^5LNT}I7(y%ZlZrB5hM=n4MIrbM*Q%zc+kZgT z)a0iWv=Osb#sAeS3Q%2sS|PXc8IszNpHWoj0objRRYw`qfV(Rl z&XnFx?P9FRMT&$3#&M%utV>zJ`jMq%Et=674yc}G88Zx46?C&1mU)@=EV^cBUapQh ziM8K!!K`G?Gn&EE&mL$1aASWNbCi2|F5F>&c}*MZb>045HrE6ga?5hxJWs26m?u@a z<6OnOt)iy=GUgxmQ(iPM+&I!|U>!k&c|%t<G%2mrsJo=MTYiCN5HtT^P1&d9SNPl8G^XWY{Pfn08F{#J;#91; z9L&lilu&VN3Aj{0k_1gLTJxj$VJSuXeKfzAt!THu>V6w%SsvrQ9rL6-mIRlfnDjp` zep--ed3+?fP&4ubqhW5@5w!SU<0rNZSK)Jk6b8MYNV@L6f*O>65~=Jbt){tTJlRvV znt7cp#-|W-h7WWkPvfWU8lu5IonLBDN4xy>bELb}YkMDgR7FA67)4N;g$HRmy}i5#=?JjbCm3OiPqx3k`J8|{VXz~LV*!Oo?= zD8H8>pbg`61nSSrm7I8?m*o{hB*!}`zkiOflj(QiTxaU}0*&m$Ob_Ox(L0B(_y1_z-dXqtZ(DS9#i&XGoO=Mb_nz0)x35>1O3 z<+}__T=F$==ws#G#=t70v3poQ-V;Gp#Dn_rUbOnd*b-?q7naRq4rPoLNkY~N?F#8H z6S}MYM6ibWV1iifBY>{(Lk8d=IyV(r+B747*r#VUla~l6=eP^qCJ61Pd}D-!{xP zsbJKzFxUz*dh|i{z(1myGh*0Y><9_mH{2ij9x}1pC)&ca!);Dkz?PhSE=A)FFyn$H9(fJ4q#1lwTrrqH>Bi>M_xvv@EE zWiZT4bdu`gYst+6tg2uvV%2hSw&WHH`xfGA9V2bYEiI%QkXTD=PMcdPf_{ztvsGUU z@*@hsiiwrHdcd&C8}y?J`5}mbOox`-S`j~15wUc)3^6Oz(SAy~VvTgEP^?QhyQfuB!P%CSJ!n4Z!N#R{5mjl@Go-UMt!rQ@ zfnmxsEMX?@nzM#ReQU{#M|stnOQH`w3w+k&hBfMD;G+Okpq9*e-1@K-M+0fey#j6~ zk+8K70ccw-xwmJCqlwk)mfYV%VQ4bFTT<~zrw6BoO5NFw0JBxLr0Vfjuam_op(V9| z!;?3&IRThWz9n_fqIwPUn3#s=aYIS83WTP);90nhgiYFzf7*StWYOd3nbYjoey1f( z507jssCuzjtmc9gOQIkxAKoo>T@-9+qXWlk5ahXPS&$_!489Cl0cbz&x-{u{Y#GK) ztPikGs~pADP%E?V5HE{N2;Ss?j>SS;>V-b_3eRXlP|7ODcg7F&uwV0^3}} zOdnCHwew}!UU{z6x_**j&8;X~N=)W2s7$i0MB5f|9)q)TN{QILsXKqJ33;%RaJ5pm z>xh^T>?qMHg`;Akw5wF?NqfIOs4aU+?83N;+|Y=itejSA^=Pl56Spu;V1CLqSuj){ z=7L#P!bk&PtmU&O30^ID6mj(Vx1U5&=~ou1n>dM2=MB2WobShTx31rpO(B)foShhGKY#cDZm-# zvg;b$81csngcFY%0Lr%HPZZ!)quv2n=T0r$UChXpx>}tvTlmlMUaZF>JE%^%tk{YC{VE*ua1*|f5z?;KiUvk18|-uxflv zfw(jwKRdCOd{U9j9NCTGpE6-~DwYA*f3@VVm8^sECAewSJ7RkH(+cZ;L);lbK=%DD z`JAFj{RF)wUmz6+dMwXT@!YSk7~N$#E2J4E|IX-{+yk2V{%XRbS?C$`<7kgxKP>3{yCA+AyYaKHY^=)O4u@IU|Akl3_<#gFfKG)fG0@m~z{^$g+s z&wo8vy0t!+EXYeRblR{JIyN8AQ@S7W=W*|#oUf#~qMV<=;p3YrF)k+OQ9fJ-S;c z85?(=h!!QcRAStUpD!Y1w^Cws3z(;C#*Zkmhiw-+`!HceB$Hgy3{U=}6eE zl^&d~amIK;Zlg4x#T0;K-A;*1F!`hYdB^7YBGPtyML1PPwz+k{d=crpg95P*GkrMz za7U6c1YskP7g|FTlpFsP<<1&{OH(aGUt|ZAtQJ+4tuM0+&XabPM^<={qFkDR^YAYi zX%&+**rb(MmV-vD)04pwOHwdmXCV@ca(5%P<|A=J?%_Di(2IB`akYohR3%boDTXx?PIGxa4Q$j&DH_!!#;9c}8P%o6s5oxtBv^8ciYpn7 z>e6FW+$LhwrI;V^eV8^PM&@dpio#!F#z-#yrc!Yv&ZsVaqfX15QC)T#bxQ7KR2RQd zC*|Ho#kH2P>~M>~#YWb~3NEN)=Za+&=?D3@FiY=qyxH{Gd zBuz=%NU%Qfdsi_BO+?_2}+xh%Z#?n`Z$dycGSMPAE=Z$`DLSEZmK@N zoLYeuUDyKvG%+w#mR2z9XL?I=%xEq+vu)idu*O;*P0EH*s@U|@P*XB63N}Ja&a@mi z3I+mr`%0kz8#U4BOcAROz#_ zb)GbFM;FqGgKx%fX}ZRMBu&>Eg=tc`a^To z)NB$TYQ&6U4|j|%*;F!(<7oz4>Itf%Pd93sdQF|z;n#`lIhkkkmoti&Z9wiKUDy8-gB?V1Q&lkjCPcLZ9Lzx8NN-+D>D;sq~NIyhiviNy5>U-f}9 zbhrU}{ub6ORt#t<@#E`0TqLo+;QoVeC;}TwCZloT!QU&uOe6vZOV*5h)5nDlj>c?v zmb5-A-|?{wA#?IwMQmX=k9~bVjjvbad*?|N`;M#K#c**1`$zt1$@dk+%3>6b?o!TY zV4Zh`{Vaw=uX&b2?AG!y*05(QtV`?Rc+s_{bM?<|i7kiotgq=>c@3iEON=;l6AYVt6J1KBDD+n9D*x*!zdDvM>IdFWKEzeYShq>(RTs5%$*st70Lzr zZKox1aixAagGNbyUO$6X1J~Oq!P#A4Rll2kw^y;7?q%X?8>KR#SVAH;C-0z>7J(qS zU~=rBB0|(N67Q}kx?DShG%fc~LcA$qM(zoUj?A_MVgi!r8^stGpIl+1dexZiETF=CPFgD2D(xtFJ6E<(38XIGn_m5YIN zdx??BYPwX6uF3EwUK~&}Do15ZhOz7H&WE8!OU*Es*D7XG>PGZOxmabTalYX4*WAYD zAth&Pu}%uLoat}6gjldjef2ZWDa5I=DJbn5QrYGryx7Xa5P^dL>ITJP#4$Ox+(9pQyFuh%$`m9P2GPs@PD+HG%U}mn$&5+oCejasINQtkQM7@o~BD z+|awV5~boUGS#j-PPqo z?(Pxo`k~8%6yRaQY$9xSf3=}#O)Yf-^4lTS+n!K#r9y*Y2DV=Jq{6HOLoj`PTC!!b zUC3lKG*-dC?cLB)4KU+g_4Zdviwz}hDE3r;A0Y^TANKvX#dzCB${rm^d6@Oh8&(Tz^RFY z$WR?JmV0m@z=zkhAWt}7S{M`4Ar;yE45Q+r6<6Dk+t2izFo}g7Eu{W8jqa`n4J(uh zmLrGxpIA#F>pDM+A>(MuXbTGMhmrayv7h;CAFcx)@a!ZqV4o`hK#BOT9SWcc)qx7 z;`(}nQ*(~BtT!5v+HI!$-m6Uy(;&lg(e zcnx)2%OKewcJxbEozpq{?~gcQG^Ma|eKZxZY<@Rd>ptcHM$m>klitjX3W=QfxP#Ci zB*HLv`a}d&r63R3C)0>7>m~c~NZ?;N5H{W##&JK{!Q|*u4nY^e?7$@)*#8cq;>8e5 z;omqqcC>htKnQ}^KkXpxh#Y{U@Gbd_1CSP&aNSxTN8VfV*$|6nuG?Wm4y?)N9fa%d zy#MUPG5La{;%r{bu66lhj6o9(M`Jf?ZOE4#<}UzZ-y?sU68qtHBA8phl0o#=c{qo* zaI=+G71&?@?tEHnF~FD#rP)dBRearuOV~z)*+0}4f<^g;feQ`n$k67_`s7w`WGKns z8v{mQewXE&_U%p0piRoRjHbyZ*OdIj`Er52lX-!xZ=XE5zPEG2y%VvB5ix1Fay#7M z50b^{at3GrjEdb>e`74`p4`EaoAtV%Va>`NjmQfz)RT!r9+LfIM&?-pe^&HWA=5g zG+dS;DcyTJu26JdNFdzDafA}|9D#7L;|K-mIRfFnj)RA5!T^Jobw5Wxg7+3NJx%5^ zXkYiogD)7H(>lP)&U#QY2vd|eO(e#OfP1AG3_F2!n?kv+fK7Qq8iwkWmMb0W@DP+$ zV&1@^lBHm+8hBKLl0&+!@oBwSG>CHsUJl2>6EU_SAX1QvwaCZ6vs@V%@{} zGID&yXx@7U*?g%{Fe!)G9`~th|DrN+CKIoXpl9Rk^K#6}omx=$G7qGs!z?^XpqD8$ zlCMA{F6)p%hg^~1y+xzPxmHOYXr#1fUo%oLU{1<0qhySivTnp^2*Td7ky2Za3_@6U zVd$(JHxidqUO8kbUKgR#W^%_|-(N|=dd7D~2x>)2c7Bk>n?V>abk;h6Y<`eY)BIvh z##M9mH32_Ikj)%F$#TpL+kHW5RUyYJ7e!XKAF!CcCp0MrvCC#t-+^7 zRx#W}BTtdfg^+}4OLetgKN_w~Ey)dr=oB@{KpZ$t0IwVEa1V8KboDArla74+6-TEh z5T;C4_{hSC89kIV^6ueALP6ll@GhFLF(dyT;hEz&&is3%$O`fg1_lISgmA1jl5dM7 z>N{-Y!=4Xz>D9P7%eO*eSXTnzhQ?3E%Tn-a4;4kv{SA9M zEW-_qO^Xsie~!_$;0`i4IWVld1Q+DF#$ZK0%wY*E@C}rh6_?2{o69171G~jjO|e31 ztw1roP=tti5QMF3rc7RBP}=PwP4YWN&h=DGnEY-4ZP*UO42tt5hQ&&Z%J)*x4kLQ3 zFHEBezGgxa_H2UzrN{?ONYvx7k-5aDO-N4`*nHoFq^tq5%!f`?0;bHkRL6`DOb(tr zQ98y%P_5@TRc84BiegX%w*^mCva(G`Q(7hp&V(`#P)Ck6|V7TJQ{}uyr zSVYZ7i}0h+5gOQpyw!s=R0Ifd;YM9_sZaveipOieV37yR>4Iy`g#!r zP0G6s$>t>M?t2XI&cNyN1Lgf*LpgWK))N%>`wSa8x`mR)YcSLr9ceO!=BnlW2Iv$p zEJaw(A22{yNr=a7S+Jn--V0@M&4+i9E?Ysj%zDa9{h&c~D{_+-md}Tb$^{d;{2)wg z$A=9|Ol?{_KH|yC7ztq|aK=9^oR4}enw_*{K4x?rzT*0c%Asg+EZ6(-zzHL8=z2Y9 zg5uC8W0))ix4pW=qXC8W=@69GC@d0taaRpR_L%@uE^{W5S25!RC1E3i0(a|%WIhJoprhgH52V_%D${-)7#Wq|d*AzYVb zTGy=ngF|S@VZZC!KNpYRPB8700*L(mj)9p4Kl1duMowo1%*TH;GVRfR82Be6CHgQg z|JkTk&w^(QfbSWgiSu-3?Y|gZGs2$_K&xSw@~;MI_jCk~a5`~ecK@3TQ^8{=^^k>Z zQvdFP@V0#%^V7>ZoZ(lS)qfbCv+Zi*`cDIsu0Y$>e6>^OhWhV^}s3Q9fQIxX@A z9ujjSd5Q1u5Mf<+?;&V+cdiQnPZl>bndQSfOJJ>YkrTFhi+nwYfHAmbN|$T^KU&1-|2NO^4cSz>ljSUk> z?yq5g$~l@Lq4zk8UQk{5`nrxNCKsFFzoaO9s75kiCwxK~+z~ret#Hf;&r#syx(_~* z0o&6hFyMyq`Tj!d=!#pF;^JaYP4T*zWMQ#}JvCk>SH{h7cfi}hMz)K2 zVX!!u@)c-yb%f{xl3}?AZtK5OV5-z%s2CD*58Sf^Gp!S|x;&DnT%gU?@$mJqY$THf znrS;Wm%>Re6QEBthC`GmBnWYGzB}{zjv8snJw^bUdN(`NOSm4 z>wCzW_1A80ZSA?t=feSb8*LRmv>oxLu}g`~W!=ovi}+@a)6Avh%6zAJsDgW`Py@<1 z5aG!!*v^_2c4NhL+O3Sa)kRBGDz3VRxyq?&p4h2Wt<9DqIwTsgNSQ8{cYRK+ z@zL9qm=wj#Y;lbyGlN%KI51>!qDwpK$T(t90ggCvJK^P?i2fa=**~<(v(ap+c+&8f zJ&3CTP{o+0sdZY|R6MSwfP1wLHj{jIHznF8a&x|Eh9+EZJ%l4NEb}mz)EYFij>ZoQ zor!4Ut#XVThz*x&KG0KfHLt4`NaC8O!o^0rl_?tPG*d*bjyPjbV;fIt`0eB@W72QBKi3r_}ihGRg7-r>kI^ zb5tY-(cl7I*yW6b8Nmu>Tel|WxTgewa5 z+KZ_T8VgMTj?6Gm%j+~28i{-Fno$_zAK{3XV0*|l1Z-zM(oyj`GsXt(FOJEh0pBM5{ z#NhVgd9DIQwAtby}~tEhmQvC{*2s)LqrbOD9qf*hBp1v>5$ zfi~rVJUu0$1LN~Ylk0qDSyO^b7|7b{2DG-lRm7?hqmJ}^N5Yu|*ag6b zlaMd)G^}4-ZqCUIJrNpe&f{pEa{u>X!!;$cu&B-u~l24tF06~y+r%lI`l zq9(+26kW#<^GY8E9g)@moCp1bNL$2(BbsMZ$JBxuM2U4Sp0uJ<~h2#s{Z0y}imM zO;%J~?EZ#8TB=;`Y+h5x`S`TsT+;Vvar*g9frOV<81W<@p*F5Y3w4FGH8q(s-X1RZ z1*JGpW2sVbc-3k$?&$tgAGC>Gy@h3#>WscjZ30O}CuY>Qc-}7hG}7?)#algXwR6a& z`8X}5792#(_P`0RkGDJGDpH42rfA<2@(xcsgoU{8UP?5mYIybE%63-o@=&MFgkr(= zZcn+qsskb%O5Wp0hfxWZ-5`s})O$U-+34{$WUU{iHQG)!x+4ud-VC9*n;ceCBjIg6 zzVnD{z5l|IE?aEtuvx~@Hb**IIi#Z#?h5~aCo$9U5cyyF?{){bf`B%?Mq8;0zkIdD zG~mUL4`H(u=MGppnR)wvMS8pu09z)o%XofxtcD92Fl8+9F=db021p8*`Ka;`yo&p1 z@s@j>48p+USyr3!QLKZk96GvZ4=n z?q!_VRsk>vg?Ma%(Z>rW&Gw3RyNCf+p}?Sv4RkxJJgmzPrv>aoLp;q+6E~TG!%BJjpJ-Jb}E?p!iqEm_i(!s zdRoFWv?2|`?6HZ>%I$L4QQU5=;2fWi9tzlHzr&Dgd`Ef+yab*PCifU0lpaEPDKzX= z47^So))0JXRgb&_-JmIJ2wkmc84hU28iJ~oMK*WJJx)v35PT^XK&zzAtD;#DsAGfVrQ>9@%8v>07dwjamQ43n+&?Ro)#%W6#jA#jr8324>y0+#; zp8R}*hGj2q>DLvo3Ak&@qATE3zq{qpVFfQtSZrn7uj>Ed4cLJL2_JZ=#0;FGJuj#{ zK0um@v$XYvFuEkqbRC5R^$Ts-1#QV~$%GZq3vi2W_&$ckcaf&RfM?g=ecyu=`HpyC za_exq_xS}mGW-`)4qcPecAhM+!E;fbNBz7*uUw+(F-Bx!7GWDcguhHqnWo2K25wSR zu!*fr5qFy8IWV)Ya1g@l2BidbYy3=(mJ^)o`hHFS#V$F9E^p?UOqiTWpbrZ$Z3C9+ zuw@10&MPRwS-;Zk~~A&cyNQsp@hR(nEKAtWHq}L8N9KnkGqW84cxYb zshxQ+>67n1KjAT~rl$T-YKpR)$ZD$P3AS{#?O0sx1P?~2B433MvXz{zwU_2#R5A2K zR*+@spNZ#}~@GMLfOXfI1RoFxrcT&PLS^6hBK;EifUxqO)14+)2vH3y>6 zq6Yh1yCR2YGX~bzJVz59C{7{4Z`kG|Q}Td>iMBml7T0L_iEuv)IxC;DB(>s!370eZ zr33d`gw^)fUvBD*H4el@IhOL6F);LO>j|bNK~(GxmhqOg8wtvmm&QCuFmEO7^KF9) zZjeFQ;;D|v5e$a#$x_pyaa@^jz;@`BH}I+i3}!@XU=~GV+Dw_8O%Wr$vjakj2aRWl zc~p|C6E+$lF&hYamgGc&274rgpUlzIO12eIZ?Np^4^w?R!p?G;X78yKH4`S*L2fls zk_V4~ecy;?wUfdyb@mTc@b6+AKz9=c8a|O{uoH$!_Fe+h2-;s*#b!3USJVA;iU*5m zKNv=lH`gRAW*beDcWsI~BPjx1myp#?+CQZKi;7<#IbbzK5$fuzC^w{ZHJ-9~VMH~B zACkbS85MCpG=Wk}Dta0ACR&+G@+%2f=p$KCvK>QF9u^s+i51fmXKgig9-cB`%;NOL zk6cjc5yNn`vx@6&;99{ofwRP-m>)UBMtY7a^!IKH51TS9sru^(Y zj(ab0aTW~>NWpXRIF4)Rf(W;qMbKN!z3JQv{I`b~(84iq=)pt~v+sEckJ-{QYxUshws1a1SXK<%Q#zx}$uEmY3hC0KVg5Et-*6y&FB!{}3R{yu%=2Z|^sma}A(oQ7I*+E}%kr9p0rQf@%+&F_m@>NWiDQs5WmgnFDiA&CSKPPX` zGx!dAP2Q0Qam5=1>+-HV9%d{X^6osk+QAN+yCb+bGT#^qbxbBVi z!&XI6J`zFQB*ojb*eVSJe^EY~GPo^M>@5wgXG`+2QG9MIrjL7kP*WDvsy-ql;o2sj_r%gF#cBws5`W& zz|~lgH2Vit@$CWc$Z48@`l4ejB;Js=w5HSd=d(96XKdca+mb8}5)yxUjk0 zPlp-2Ha&zU>*v2qAnrlO5w<8_9mdj;uOwfKaM-Kv`(NzAO7iuDCG1=G@tP~Qu*rhs z7m-D`uyN-a4rkQ+hv?akM@yCI?-M3&YNyk1Jf{~?-QOOunftBCqWj&MD*g`%*oS+P#llM_W=525VAgmT#>O*LQ2*~5r? z%!ZwxoHGn^dj_YsVdbOiF5DO7T*u>hxTC9QKXUQWSpr`bAQP}Ogz+6M2TAntS<;BIby zFAh$ea`Z`KaM~Wn4c)SS6-tNkLyddsr$q-X?ya8|8~pSiXEc|JHMS~>vUb` zw^MCH7VO*UHlPnXai`pIdav)SZJ2mC4K3@ZNr$UxTR%;Bsl>Yxz-+SN4SuP9n&{Fb z9m#>3jp zm+5zXeCMPKJ4v5we7dkx^tQ&Q3p-6WYkaz}GxV?CS>JPEXX#swPZxHMPSyByVb|zR zjZYW$7(J;s*SB5Rb=lHS7j}dG(?DI=0Uf9D>B1hT$Mo*rx`l<$Ec}}BX<^}@2Zv^S zT3C3b!Iv4I78YJ(@MFfOWhI>IZqQH5Nt`Koh<;iz9ZR)|x?{W5R`oX>J@%oK+z+M1O%uldy(+{r2@HK`!v;@Vc=xWSw(+}?IPqJ^z zI`}ZdyO_A?2Up&w*th8i$IPeNx6^Np{=?*V`W>V5Fu$FC>+~ANH!7^gzd?s#emngJ zbQR{e)9*O_giq|c>ITp4=jf-Q@a%oAewy-d%7^nVm`!)MmZ?3xMyFhUJH^x=m)}mab$a6R z+o`reCtQ9z-3Ii(<+oGrIGu0TPaZSzaHM~wewy@|BE4aOn(&ztyaXNY&e9%ZIo(QC7qq4&nUmG zIA+)AFv@SI*fDyG^4n>)PFGQWJJmMmD#~xC+kkGO{C3J6r<3UJ`i_amOI07&Pm>NG z>QCsW3CG!>PwJ=1o-4~=>8FXF!!rZ=X_DvgynudMvgh!yfPPxC=kTfkvVZS{#aqz> z=e$p^jTZ^wJ%~phaboOCB6?(Z2$O`MTv;F1my`8~vFt{Iv+zH6a4h0<_Z0&AdGXO)kVj>5-69<`^ zQz?!EpbWCTySIOs37DgK|6l~}dEj8-o(HH<(KJ8U z?{A$xaY8=|1-UDv)R4@*oCu?i3coX0+gMv)AMEa$NaPjr0m2SKSaT~U38YbC5G-iY zcQRDd{xxeSHaAT6fqs3!Oh%5y1dLUnXbNnf$|na(kUUC}lWW%>8~i}3^r^x2)`{!; z1$r_v(Kn1r_67rm%5A4I5J4hz0svFmF%o?c_?_k%x0Q)M4a7$uga-Fkk)AQ2XoK zr?;$R4i*b&Q`l7W(d`U&PoLQ9$LgwGW76tf{5ifGr%;rurE4v6luFph^eQn^#%Mt( ziI|@%_lL?2?2)EX=Tf?lE;-&CuiTB!nU_cPTy*&=mMx@EOF_=(4{B(E8>b`EJXSC> zIz)b^G-?FcarF`8GHGlyBRG@DawYBBTS2gW>my4vjX69v!ZF-Qq$8_Xh!{J=K4Pak zyZr5~<7=BIPGdYCQ*}o2#z@%>9O$>V`d-XIKVY%L(?JD|7@NXA?j#vED*mzjSsloB zW<6dHJ#ZU{*JU`#36zz7X35y?OP32S>=FL9w6V66?<1!O_ z)5EdkbUj0~IsUvJIhHdg|E*9A-HFXjG2P6L5G2QFe4nG!Pd_9TxC7-yBi}WEsk6 z%OOsk(V+hgv36`Y&Wae$oRLgEp0w9kw#7uI<8&XHnEx}ze8XU8dwl=pV$L%CfH8AW z+F9GW5=)AUFTUr22~J3`BZj1pnmifRMS+je#m*T;GWk8A!ZJmQjVUF@g3k{Mj4Q=Q z#Ts#D6y>3odQ4L8&)pLU)7E)+QECYx%@`dPa;Vy6_hw{POnRi7_G)C?h4IJ3+qyLA9#} z>sO;6**m?HU-D)^CoDsAg6pZmr+b1Lq-U25nO}_y6bCOXOcgP$B7>WIqAO?r0F@r8GcBw;fgejk4cUS#9G%$91b-~>M&0t zc^GoiALjKPCC+zZ8rB7P3In(vI2R_{GkZ1{@~H@^`L)IT84eo}r*tH^@J~Z{7HfVW zGO7!QkJ#g8205~2I>=``iM}+m^R&o5O7d3Tbim2da1_7oqTqCPd^8aq71hZbXB)$n z+b%K~d|lPfr^sV2q7jxoq%7tV2GJKdsi(KD-rBx4-_J759viEEM&gT|M4yMlb%7E+ zHcFz$v|```Gezk65+^lg*C@FW9VI!US=lf38jq^FsxE<161B2tg}scz*dvXo$wu~q z@HMV3lQCv0`o2H(Qpc2i43J3Aig=9|;VVfl11ZFVPK>KT8C6jxQ8yILYrSAD)qVJp z!c&Gs&zK9tqF?Vt`${oVtOv)545b8R-xMT{ENo_hvX7R;-#TA-JtV1t#SoNAd8btvaxgmw# zMjZJH^7kQ*oseny3Slusj*R?Bc6xs^$Yf08q!Dv++W6^}I~9Aif_yt8X>?_cd74z& zoW!*LekV#rCEr&^hbMYc&`4dso0Gb)zE1Lx&N=BMSjStCe~d98!(2a+X?ig)G8XWR zzBGP+FC&R@>@UqYl3d70G_)Z95~aG-?ISgCBvL6TBbmDXYfkDJ^mUSRI_IPt2h2_( zhEv+Hj;pbTI(;pxLCOqdVbqC>_z{fz&tcC#SG=_w>g=Cbs+39Sh}g2D%@3j2IT^*0 zNy&&hNX3lf2hFdQ5YlxgtxU8`?U|#FT;ro3moGU5mNE)tCOczN?s>*XK0N7J&cxv- zH~ZB@&$DkFx%ya;l}t=A6ZVTMa*dBI@@Tn1J)6W|nu$*p+ApefA~vEjk4R7_6G8AH z*e{a6G(I_Fy&+aJ(OtU(vzPiT>=$3;8XuwZjC24w9Ao6*IKETWu`~4iF879~@wpsK zd)(NWW&5Z&D7!}afUHhu=%pBZR5z8;ZFB|t|D|0&tKAv;i14F?%`L6munxqq zE@S?8HtN_BjjbOP5!v=pVO$2~mH0ndCA{wYs^FRSQNXjLc~Fp-XCsSR?i*cX+b6=v zl#TKg*(mq#RG7abdX;SH`bC}}9t-mOAr4M($2U4dGl>8xHl$IR<|}oEF==rHc}*tv z{hJpx7}2^i!bj)i0j?UOsu>Ml>oq8;V_oPn$CT<2SDGIR#aPDntZzh&&?jjO`M9#U zizSnYeNvjWjl3m^ZdCF5s6vMdLfm}IMsvn(u(u*IPAtZiWW-#WK!{nEjm+0kbU!Im zQhJJHQ~N!qz&_KjNe0k8X*X zJd6Us%_T6!fuY zIy(!PdP*L(YfqslQfS8RKUv~fc(a$Tim)(EU3MsU*Xqacq?BvQXK zNsT;1dXAL-tB@Y1nyS^P?qHb1Ne*Nr-FNFwMnRsGlRPeWgBT5KT)7yGoQcIq1$lBV zrg3?cjmfxjG5LIQ%q&?W739^qn5e^zi!E~IVvJBOeLm*bgqY19HolZRJ{(IZm_LOm zx{PmapK_aum&sW-hX?%TmTT0ZIf3NFz>ryxBeztp{tYAe`Zq*QHiXL^@774>EzgY@ z3i7|m3tLo9END(DjamhHpko}N0Zmy!DANk35Y0UNAD!8$wVkz-_~{VNiQ-;sL9Psf z*$0f$163S|)5eRru@Nw0wC}-n>jTu6&;=IcDj)BNY+{OHK8;CILAIlavAS>l0LtgI zM82P599pq*S)M8!swhxl6MP=y)c~OjQY{oFU;k0mGGwXK-_*Y}a|9zKZ1rntVZ2Cnpt}2bmdWX8qk2TGy>?IzOE_6zdCD9kczR2P$!V$vPDbSH&7 zCMV96Pahf9VZWR*Ei+xuka{<^qM5ef9Sf_yrPLx)@|s_wWZfRKti#hS@z@wE|R_SMYtWvGv!ZVLR+ z#rN$|`i$)&LcGa5DHerlgq_2z$TM^oNJ+NKaIHDX{@9|iL6;Z4Ujv{NiqA9wH?*qa z$Uxu0`wralG&d`AWJ~UF|3#4NmVHq$`8bDLk=#ncsO(hUw;lO(jcQ3A!~Y9%PFAqh zCLm|3x?{R7l3hj&XF);E9~IY`O2^gje0)qsP}kxdn}LLFzBDd_e{_^?XUt-Ye_SR9 z?Fw>Riq95mE{C(QpPkSa92Z9DxqjA*%JPpC7g*@P>Lg}XnguyPap=eNka13k2VB^W zKa5r`stmW!PffqcFDo{xO<>F*xI~u3weO^`^N@XVnK=u zbZTeq%9CpcuU*^O!qo@|Po3Vq3c4@h1Ww96Q0KNB^Y$t5E`xV@8>fm-o*L{yMjy2k zmH13itc(e^UsRXk!EW56xPXgUai=^U16jap6XVkGuq<-}7rWQ`%I4`gKNbhAf`mQB#n;I*~XKkyHzv{A2G!?R*y-sfx{|5;^-J*q8JvuPj;H{ zQCUjbK9S&hBaAP4QY4I`dJZI)icw5HHQAu(3DM|mCg{9!a?*5~=s+iBg)>K}ik#KC zXn}XM_xl)gw%6D8wlP?7OiwmlBINb3u{DG!@M`wHxuxbqt7~UAq>_;H425c=pZS0u z@p@aEu(+Lsmr(*|++#!p$jfkP&j{g#39haN{=7EUo&v*=K;J?$f_d#hf9KSCf3UT; zbA4W9fFMlg9w#^$xtR<4{Jov+6BhOqPq!WHtklp!;}-3H|LVcc*1&;z!71t#9WNDk z*zEMS52-+i=eg|ul^dJmln&4t1uxW_kjJCdkhQ+OfkRK44$g;$tJn}jR<;8Ryxcp= zTHD!K82a@}uaur%;Jwsd*w*%zr;n4Dundb#paO5P?yqiPK?^(Y);f!syDFq}-5^xpWyk&bodKqHv^1=^v-WrNPOA_=}2( zdx?`s*t72w5#c1VFht(?=x{-Ce;${y)G)C+ftl6z7E-%6xNeUJ21X}yKW?Xt3+pUL z8MTln+P_1^%3lBA-TO66A(053TyP`-aOA4NR{x+aDh^z5@qu|fVzJV}?P>k)YOU7j zcKf`*3ok0Q@Gfrc(18mtJTQ{^=yGkbgBRE@Z%{rvS&e|Fzi)^08O;9&Ro8c)%MQ*6wKhkr`JyG_D|rB-agip+xe=j0rPCWmM{}`(Oh@Y)OT^g zmgYY57-!;T5z+K;PJR~+8fX#=&OT$h+FeCKXmw1kVmM;}7j@;twQJWSN>>tjCGKPA z@*lxVj5l_0;n)t367v>goz3oUT!Z$KlCj*tBv-*vOA*8R*2+$AZPJp0#*bC2AE!)Q zu6=2<+$VrbP+XYVWoV**8%wls!yt#AeAF@Sgl$ZO#(~Y6VNJHSEoF%NJ|p8E zm+)Nd#`uHAA7a0MHg;GZc3c_mm-2*dOw3!`W78huu8)ZOOnI&`lj#SKH_7uw#>}qW z3<1r~r7cZv#&vy&!9_@3Z$Bu=KYKyDYu5~R_mLCuRE!c~OXFP&7TaJK6X|^g0Szpv zEKi5HyLfTC7)6M*2cO=AEObF92aq-GWg+8hYZ#?067yOAjsO9T#J3=g3f zL#XDXnI_$b<;DR+LUN=I=0i4XZEi0N_Ub3sDjRF3MokjNusKDv-_1A0A)#oIU7zM5 zXiOCu;&QQ#(WM|gFC9JN?%vv#UxB9vLsm@>J9++siGuL(z19}Yr7#uPPe#Jw#9$}Z zUrK@7@w_CO+Ef(ouKcwj;s!2fe_~35v2J&;hb!8jlu{elo!Y@Q?N9bJOsn*24L51e zx2(wE%SYE~v8Oa+8m1=Ifs+KA+SWD-=r?>Mo4f5}55nYN|Colje0151KVYcyQgNz$ z4fPeXy?h_Yl8JZ_r7Pp8x(zqY-HJ-ufXcrM;jT&?Ke5d#G0yl z0flW@JEDS75xW5J# zTP#_kNSLNa*J`s`hf7>B$&dAUlwJEPsHg`|a z{7?IRe|zt$v0X`?(Z#0SWQrbac2@dJm-O)#TBC!4y9JIpD$`~%aQVS0j97BRInG#% z8S6Qm({j6LcW2$d)iLybN3iW4#=XC|R2mN>;jxoWFWe%0I}t$creQ;ia(gAN;ypXQ zM}jB7aSJux{J4XX=jWUKE-c;^yfMV5Tjm>$`H9JDy(K>$@m^=O=C3ct74s7l2tP5= zlDh;JYBS%eEOeJD_IRgWW?Y_c$xk^R#9duo;0^Wmr`Y1FBTUFVS9o|5xKZiq3q*#O zT7ox79A4?+pxQ@)zxMz;h1g7&v_2!68<(iokT_v`v1J=;62OD zN-Ha)1dh9cqus>|L5G&xNA%S=r+$l%o)D_HyOt=ICkKt(?w0H-@`;HOgS7Yz3i$!p zMf?*JWdrVRz;e&zz)DZ{dl>adbJ-rXrtMyVdm7NMX0i(*E^+{Z)Z0h8K6-{iX8)2y zSC&!$xjYd7uNOm%ZxyTlQ{-2feer^Wel2A{iqhR z3u;XVv|Iw}!!pmH+l-;hJs-KpnR}2u3w*vevfS=%P-SH$QyK4LY6%{9Av+%E>NRV)XS^B%&9t~31GF?!+q%Q* zox^A*xEj9EiD8k;jReOVVbyj8m&}Joh6Kzf40tFFkL@iatqe(7NEqtP?g}1DjslRR z#ROk!qZ^4RQr1jRyib3m-O;yuE&rxla!G=BkK+AyeP7F9B>hkVH}SkX>^hZdD~ST_ zxRvnWxiot~*(A)hWGO+h#g>T2jp@q?*j!#&?JYI%SY?ty)fOMK(cEVayG{;|f#veL z*5W%i%E?m44j~egEsJi7A8yoo_H3@HilXSHIQrB^@90XBRFuUkInW_CyWPbKdMkFT zmfb~pfMWgHY6m@dqwDHjQ66Y?vNo4P@swoE=+)Kv`Chw?j@j4jNxo}yxQS?RcYF%ojc(*JI)Hu(tkz&{URQC;GZYmMmUtC zFFV4gwB-BWe=o@x`)+}^sYwFBr#H&G)8DBuHl z;p{pLG1C~+uYkl&)HlAgbgg2C@V$p$S6~30z`sqP>j-%oJbZDA)Qj|QQSdkl#>t|7 zUjdGjz^~27yNK-y`r4oWd%EpHk2U1Q0IBdguAwPyqEBG{FmJ9$I&Qk!o`Rn1cuQ*y zWjgpDp!F_t`fB{;zt=$HotsSVruCFHVi&rt-z2p=4c#o~c5cEo^QPO5tfbe`x1Gd) z>*&{Z(9SQyzc-C`Zu+r}3~q8-SwFsoJiF=T*9r82yEmB@`#)=JNqfJWR=@l|D<8Ak zb}^S2poi&dAHx~d4_`xPyJpzUI&#b-1RXl$I9Z34dnCJnrJ zhB=v^`yJP@ZCBAP4RHm+*MRrHzYPhT%Zicn%YT-?_Z{(ct8tLQT z|9+kSUyb@jRTFD6(e+nRV4Y2$pF z{oV@ZfL!%mfv;_C@9V&>0Lo{E+2?YdcpUY06O?NmlJX97Tm(+`A9$k}f3xfL_kogK zuj3`qX1&>O*4oQdkpaH{eb}Ijxh;^|y{^F4*v{@1D_3ovxMElDZn;A5M7iSHLtgj`XUPlk@%`_g zgTIDP+&1EO1a|7+S-?h@1LdUrKLs^@SNU%n<)O65$3Lg;ewKU*f1};wJxZtE`UC&a ZHgetS*$Gdyjd&iG$H76$6U)Q%{C`YwdG`PS literal 0 HcmV?d00001 From 2ff36e26b729abe3fbf9c3feab3ee154cd50192b Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Thu, 14 Apr 2022 08:20:48 +0200 Subject: [PATCH 174/323] bumped versions for wfserver and wfview to 1.2e --- wfserver.pro | 2 +- wfview.pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wfserver.pro b/wfserver.pro index 009fa1f..a6b2341 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -13,7 +13,7 @@ TEMPLATE = app CONFIG += console -DEFINES += WFVIEW_VERSION=\\\"1.2d\\\" +DEFINES += WFVIEW_VERSION=\\\"1.2e\\\" DEFINES += BUILD_WFSERVER diff --git a/wfview.pro b/wfview.pro index c72f27c..61d2218 100644 --- a/wfview.pro +++ b/wfview.pro @@ -11,7 +11,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport TARGET = wfview TEMPLATE = app -DEFINES += WFVIEW_VERSION=\\\"1.2d\\\" +DEFINES += WFVIEW_VERSION=\\\"1.2e\\\" DEFINES += BUILD_WFVIEW From c1f9358d38bac2bd222cc4de1f174ec19ce80645 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Thu, 14 Apr 2022 18:02:11 +0200 Subject: [PATCH 175/323] updated WHATSNEW and CHANGELOG --- CHANGELOG | 28 ++++++++++++++++++++++++++++ WHATSNEW | 15 +++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 32189f9..25ba84e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,34 @@ # CHANGELOG +- 20220414 + this is a cherrypicked CHANGELOG add as there are loads of changes due to different + audio transport ideas etc. For a full changelog exec: git log" + + About box updated to include Patreon site + + added 500 Hz step for VFO + + Added clock and UTC toggle. + + Added forced manual RTS setting + + Add RIT function and other rigctl fixes + + Adjusted window size for radios without spectrum. Thanks K5TUX. + + Allow dynamic restarting of server + + New Settings tab + + Enable High DPI Scaling + + More multi-radio support + + many rigctl improvements/cmpat adds + + N1MM+ TCP connection added + - 20211201 Another "minor" update for RX only rigs diff --git a/WHATSNEW b/WHATSNEW index 30289b7..45ee36c 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -20,3 +20,18 @@ The following highlights are in this 1.x-release: added PBT and IF-shift several bugs fixed RTS as PTT for several radios like the 706/718/736… ++ New major change is the audio transport mechanism. Lower latencies. ++ Author: Daniele Forsi /Author: Daniele Forsi ++ Author: Russ Woodman - K5TUX ++ About box updated to include Patreon site ++ added 500 Hz step for VFO ++ Added clock and UTC toggle. ++ Added forced manual RTS setting ++ Add RIT function and other rigctl fixes ++ Adjusted window size for radios without spectrum. Thanks K5TUX. ++ Allow dynamic restarting of server ++ New Settings tab ++ Enable High DPI Scaling ++ More multi-radio support (nearly working!) ++ N1MM+ TCP add. + From 4ba435845b16bef619ce835ea4fcfa238ff54124 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Mon, 18 Apr 2022 14:13:53 +0200 Subject: [PATCH 176/323] added contributors and whatsnew changes --- CONTRIBUTING.md | 9 +++++++++ WHATSNEW | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 901803c..7804ed1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,3 +6,12 @@ the following people currently contribute to this Project: - Jim PA8E - Phil M0VSE - Roeland PA3MET + + +Also contributions by: + +Daniele Forsi +Russ Woodman - K5TUX +(and some others I will add too) + + diff --git a/WHATSNEW b/WHATSNEW index 45ee36c..68c1e88 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -21,8 +21,6 @@ The following highlights are in this 1.x-release: several bugs fixed RTS as PTT for several radios like the 706/718/736… + New major change is the audio transport mechanism. Lower latencies. -+ Author: Daniele Forsi /Author: Daniele Forsi -+ Author: Russ Woodman - K5TUX + About box updated to include Patreon site + added 500 Hz step for VFO + Added clock and UTC toggle. From d86f8958b2724e7c186e735852a85efe12de3498 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 18 Apr 2022 20:29:50 +0100 Subject: [PATCH 177/323] Add support for splitting/combining waterfall/scope data --- commhandler.cpp | 66 ++++++- commhandler.h | 11 +- rigcommander.cpp | 6 +- rigcommander.h | 2 +- servermain.h | 2 +- udphandler.cpp | 118 ++++++++++++- udphandler.h | 10 +- wfmain.cpp | 35 +++- wfmain.h | 6 +- wfmain.ui | 93 +++++++++- wfview.vcxproj | 387 ++++++++++++++++++++++++++++++----------- wfview.vcxproj.filters | 52 +++++- wfview.vcxproj.user | 4 +- 13 files changed, 665 insertions(+), 127 deletions(-) diff --git a/commhandler.cpp b/commhandler.cpp index 29f9c71..f234509 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -31,7 +31,7 @@ commHandler::commHandler() connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn())); } -commHandler::commHandler(QString portName, quint32 baudRate) +commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat) { //constructor // grab baud rate and other comm port details @@ -40,6 +40,11 @@ commHandler::commHandler(QString portName, quint32 baudRate) port = new QSerialPort(); + if (wfFormat == 1) { // Single waterfall packet + combineWf = true; + qDebug(logSerial()) << "*********** Combine Waterfall Mode Enabled!"; + } + // TODO: The following should become arguments and/or functions // Add signal/slot everywhere for comm port setup. // Consider how to "re-setup" and how to save the state for next time. @@ -175,7 +180,7 @@ void commHandler::receiveDataIn() } - if(inPortData.startsWith("\xFE\xFE")) + if (inPortData.startsWith("\xFE\xFE")) { if(inPortData.contains("\xFC")) { @@ -188,6 +193,63 @@ void commHandler::receiveDataIn() { // good! port->commitTransaction(); + + //payloadIn = data.right(data.length() - 4); + + // Do we need to combine waterfall into single packet? + int combined = 0; + if (combineWf) { + int pos = inPortData.indexOf(QByteArrayLiteral("\x27\x00\x00")); + int fdPos = inPortData.mid(pos).indexOf(QByteArrayLiteral("\xfd")); + //printHex(inPortData, false, true); + while (pos > -1 && fdPos > -1) { + combined++; + spectrumDivisionNumber = 0; + spectrumDivisionNumber = inPortData[pos + 3] & 0x0f; + spectrumDivisionNumber += ((inPortData[pos + 3] & 0xf0) >> 4) * 10; + + if (spectrumDivisionNumber == 1) { + // This is the first waveform data. + spectrumDivisionMax = 0; + spectrumDivisionMax = inPortData[pos + 4] & 0x0f; + spectrumDivisionMax += ((inPortData[pos + 4] & 0xf0) >> 4) * 10; + spectrumData.clear(); + spectrumData = inPortData.mid(pos - 4, fdPos+4); // Don't include terminating FD + spectrumData[8] = spectrumData[7]; // Make max = current; + //qDebug() << "New Spectrum seq:" << spectrumDivisionNumber << "pos = " << pos << "len" << fdPos; + + } + else if (spectrumDivisionNumber > lastSpectrum && spectrumDivisionNumber <= spectrumDivisionMax) { + spectrumData.insert(spectrumData.length(), inPortData.mid(pos + 4, fdPos-5)); + //qDebug() << "Added spectrum seq:" << spectrumDivisionNumber << "len" << fdPos-5; + //printHex(inPortData.mid((pos+4),fdPos - (pos+5)), false, true); + } + else { + qDebug() << "Invalid Spectrum Division received" << spectrumDivisionNumber << "last Spectrum" << lastSpectrum; + } + + lastSpectrum = spectrumDivisionNumber; + + if (spectrumDivisionNumber == spectrumDivisionMax) { + //qDebug() << "Got Spectrum! length=" << spectrumData.length(); + spectrumData.append("\xfd"); // Need to add FD on the end. + //printHex(spectrumData, false, true); + emit haveDataFromPort(spectrumData); + lastSpectrum = 0; + } + inPortData = inPortData.remove(pos-4, fdPos+5); + pos = inPortData.indexOf(QByteArrayLiteral("\x27\x00\x00")); + fdPos = inPortData.mid(pos).indexOf(QByteArrayLiteral("\xfd")); + } + // If we still have data left, let the main function deal with it, any spectrum data has been removed + if (inPortData.length() == 0) + { + return; + } + // qDebug() << "Got extra data!"; + //printHex(inPortData, false, true); + } + // emit haveDataFromPort(inPortData); if(rolledBack) diff --git a/commhandler.h b/commhandler.h index c76e23f..4baeacd 100644 --- a/commhandler.h +++ b/commhandler.h @@ -16,7 +16,7 @@ class commHandler : public QObject public: commHandler(); - commHandler(QString portName, quint32 baudRate); + commHandler(QString portName, quint32 baudRate, quint8 wfFormat); bool serialError; bool rtsStatus(); @@ -74,7 +74,14 @@ private: bool isConnected; // port opened mutable QMutex mutex; void printHex(const QByteArray &pdata, bool printVert, bool printHoriz); - + bool combineWf = false; + QByteArray spectrumData; + quint8 spectrumDivisionNumber; + quint8 spectrumDivisionMax; + quint8 spectrumCenterOrFixed; + quint8 spectrumInformation; + quint8 spectrumOutOfRange; + quint8 lastSpectrum = 0; }; #endif // COMMHANDLER_H diff --git a/rigcommander.cpp b/rigcommander.cpp index 8cdde8b..c99cb1c 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -40,7 +40,7 @@ rigCommander::~rigCommander() } -void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp,quint16 tcpPort) +void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp,quint16 tcpPort, quint8 wf) { // construct // TODO: Bring this parameter and the comm port from the UI. @@ -49,6 +49,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu // civAddr = 0x94; // address of the radio. Decimal is 148. civAddr = rigCivAddr; // address of the radio. Decimal is 148. usingNativeLAN = false; + //qInfo(logRig()) << "Opening connection to Rig:" << hex << (unsigned char)rigCivAddr << "on serial port" << rigSerialPort << "at baud rate" << rigBaudRate; // --- setup(); @@ -58,7 +59,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu this->rigBaudRate = rigBaudRate; rigCaps.baudRate = rigBaudRate; - comm = new commHandler(rigSerialPort, rigBaudRate); + comm = new commHandler(rigSerialPort, rigBaudRate,wf); ptty = new pttyHandler(vsp); if (tcpPort > 0) { @@ -107,7 +108,6 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud // civAddr = 0x94; // address of the radio. Decimal is 148. civAddr = rigCivAddr; // address of the radio. Decimal is 148. usingNativeLAN = true; - // --- setup(); // --- diff --git a/rigcommander.h b/rigcommander.h index c95d8d0..dc1b153 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -79,7 +79,7 @@ public: public slots: void process(); - void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp, quint16 tcp); + void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp, quint16 tcp, quint8 wf); void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcp); void closeComm(); void stateUpdated(); diff --git a/servermain.h b/servermain.h index 99d3ea7..af9150c 100644 --- a/servermain.h +++ b/servermain.h @@ -154,7 +154,7 @@ signals: void sayFrequency(); void sayMode(); void sayAll(); - void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp, quint16 tcp); + void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp, quint16 tcp, quint8 wf); void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcp); void sendCloseComm(); void sendChangeLatency(quint16 latency); diff --git a/udphandler.cpp b/udphandler.cpp index 25f4cee..e3c55fb 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -13,13 +13,15 @@ udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) : rxSetup(rx), txSetup(tx) { - - this->port = this->controlPort; this->username = prefs.username; this->password = prefs.password; this->compName = prefs.clientName.mid(0,8) + "-wfview"; + if (prefs.waterfallFormat == 2) + { + splitWf = true; + } qInfo(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxSetup.latency << " tx latency:" << txSetup.latency << " rx sample rate: " << rxSetup.format.sampleRate() << " rx codec: " << rxSetup.codec << " tx sample rate: " << txSetup.format.sampleRate() << " tx codec: " << txSetup.codec; @@ -285,7 +287,7 @@ void udpHandler::dataReceived() audioPort = qFromBigEndian(in->audioport); if (!streamOpened) { - civ = new udpCivData(localIP, radioIP, civPort, civLocalPort); + civ = new udpCivData(localIP, radioIP, civPort, civLocalPort,splitWf); // TX is not supported if (txSampleRates < 2) { @@ -597,12 +599,13 @@ void udpHandler::sendToken(uint8_t magic) // Class that manages all Civ Data to/from the rig -udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort,quint16 localPort=0) +udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 localPort=0 ) { qInfo(logUdp()) << "Starting udpCivData"; localIP = local; port = civPort; radioIP = ip; + splitWaterfall = splitWf; udpBase::init(localPort); // Perform connection @@ -774,8 +777,77 @@ void udpCivData::dataReceived() if (quint16(in->datalen + 0x15) == (quint16)in->len) { //if (r.mid(0x15).length() != 157) - emit receive(r.mid(0x15)); + // Find data length + int pos = r.indexOf(QByteArrayLiteral("\x27\x00\x00"))+2; + int len = r.mid(pos).indexOf(QByteArrayLiteral("\xfd")); + if (splitWaterfall && pos > 1 && len > 100) { + // We need to split waterfall data into its component parts + // There are only 2 types that we are currently aware of + int numDivisions = 0; + if (len == 490) // IC705, IC9700, IC7300(LAN) + { + numDivisions = 11; + } + else if (len == 704) // IC7610, IC7851, ICR8600 + { + numDivisions = 15; + } + else { + qInfo(logUdp()) << "Unknown spectrum size" << len; + break; + } + // (sequence #1) includes center/fixed mode at [05]. No pixels. + // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 " + // "DATA: 27 00 00 01 11 01 00 00 00 14 00 00 00 35 14 00 00 fd " + // (sequences 2-10, 50 pixels) + // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 " + // "DATA: 27 00 00 07 11 27 13 15 01 00 22 21 09 08 06 19 0e 20 23 25 2c 2d 17 27 29 16 14 1b 1b 21 27 1a 18 17 1e 21 1b 24 21 22 23 13 19 23 2f 2d 25 25 0a 0e 1e 20 1f 1a 0c fd " + // ^--^--(seq 7/11) + // ^-- start waveform data 0x00 to 0xA0, index 05 to 54 + // (sequence #11) + // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 " + // "DATA: 27 00 00 11 11 0b 13 21 23 1a 1b 22 1e 1a 1d 13 21 1d 26 28 1f 19 1a 18 09 2c 2c 2c 1a 1b fd " + + int divSize = (len / numDivisions)+6; + QByteArray wfPacket; + for (int i = 0; i < numDivisions; i++) { + + wfPacket = r.mid(pos - 6, 9); // First part of packet + + wfPacket = r.mid(pos - 6, 9); // First part of packet + char tens = ((i + 1) / 10); + char units = ((i + 1) - (10 * tens)); + wfPacket[7] = units | (tens << 4); + + tens = (numDivisions / 10); + units = (numDivisions - (10 * tens)); + wfPacket[8] = units | (tens << 4); + + if (i == 0) { + //Just send initial data, first BCD encode the max number: + wfPacket.append(r.mid(pos + 3, 12)); + } + else + { + wfPacket.append(r.mid((pos + 15) + ((i-1) * divSize),divSize)); + } + if (i < numDivisions-1) { + wfPacket.append('\xfd'); + } + //printHex(wfPacket, false, true); + + emit receive(wfPacket); + wfPacket.clear(); + + } + //qDebug(logUdp()) << "Waterfall packet len" << len << "Num Divisions" << numDivisions << "Division Size" << divSize; + } + else { + // Not waterfall data or split not enabled. + emit receive(r.mid(0x15)); + } //qDebug(logUdp()) << "Got incoming CIV datagram" << r.mid(0x15).length(); + } } @@ -1537,6 +1609,42 @@ void udpBase::purgeOldEntries() } } +void udpBase::printHex(const QByteArray& pdata) +{ + printHex(pdata, false, true); +} + +void udpBase::printHex(const QByteArray& pdata, bool printVert, bool printHoriz) +{ + qDebug(logUdp()) << "---- Begin hex dump -----:"; + QString sdata("DATA: "); + QString index("INDEX: "); + QStringList strings; + + for (int i = 0; i < pdata.length(); i++) + { + strings << QString("[%1]: %2").arg(i, 8, 10, QChar('0')).arg((unsigned char)pdata[i], 2, 16, QChar('0')); + sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0'))); + index.append(QString("%1 ").arg(i, 2, 10, QChar('0'))); + } + + if (printVert) + { + for (int i = 0; i < strings.length(); i++) + { + //sdata = QString(strings.at(i)); + qDebug(logUdp()) << strings.at(i); + } + } + + if (printHoriz) + { + qDebug(logUdp()) << index; + qDebug(logUdp()) << sdata; + } + qDebug(logUdp()) << "----- End hex dump -----"; +} + /// /// passcode function used to generate secure (ish) code /// diff --git a/udphandler.h b/udphandler.h index 3731bc1..08b3c39 100644 --- a/udphandler.h +++ b/udphandler.h @@ -34,6 +34,7 @@ struct udpPreferences { QString username; QString password; QString clientName; + quint8 waterfallFormat; }; struct networkStatus { @@ -75,6 +76,10 @@ public: void sendControl(bool tracked,quint8 id, quint16 seq); + void printHex(const QByteArray& pdata); + void printHex(const QByteArray& pdata, bool printVert, bool printHoriz); + + QTime timeStarted; QUdpSocket* udp=Q_NULLPTR; @@ -142,14 +147,13 @@ private: }; - // Class for all (pseudo) serial communications class udpCivData : public udpBase { Q_OBJECT public: - udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, quint16 lport); + udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 lport); ~udpCivData(); QMutex serialmutex; @@ -166,6 +170,7 @@ private: void sendOpenClose(bool close); QTimer* startCivDataTimer = Q_NULLPTR; + bool splitWaterfall = false; }; @@ -311,6 +316,7 @@ private: quint16 rxSampleRates = 0; quint16 txSampleRates = 0; networkStatus status; + bool splitWf = false; }; diff --git a/wfmain.cpp b/wfmain.cpp index 901860b..e57ac59 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -192,6 +192,7 @@ void wfmain::openRig() ui->lanEnableBtn->setChecked(true); usingLAN = true; // We need to setup the tx/rx audio: + udpPrefs.waterfallFormat = prefs.waterfallFormat; emit sendCommSetup(prefs.radioCIVAddr, udpPrefs, rxSetup, txSetup, prefs.virtualSerialPort, prefs.tcpPort); } else { ui->serialEnableBtn->setChecked(true); @@ -207,7 +208,7 @@ void wfmain::openRig() } } usingLAN = false; - emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort, prefs.tcpPort); + emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort, prefs.tcpPort,prefs.waterfallFormat); ui->statusBar->showMessage(QString("Connecting to rig using serial port ").append(serialPortRig), 1000); } @@ -430,7 +431,7 @@ void wfmain::makeRig() connect(selRad, SIGNAL(selectedRadio(quint8)), rig, SLOT(setCurrentRadio(quint8))); // Rig comm setup: connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString, quint16)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString, quint16))); - connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString, quint16)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString, quint16))); + connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString, quint16,quint8)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString, quint16,quint8))); connect(this, SIGNAL(setRTSforPTT(bool)), rig, SLOT(setRTSforPTT(bool))); connect(rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); @@ -1398,6 +1399,7 @@ void wfmain::setDefPrefs() defPrefs.confirmPowerOff = true; defPrefs.meter2Type = meterNone; defPrefs.tcpPort = 0; + defPrefs.waterfallFormat = 0; udpDefPrefs.ipAddress = QString(""); udpDefPrefs.controlLANPort = 50001; @@ -1543,8 +1545,6 @@ void wfmain::loadSettings() ui->lanEnableBtn->setChecked(prefs.enableLAN); ui->connectBtn->setEnabled(true); - prefs.tcpPort = settings->value("TcpServerPort", defPrefs.tcpPort).toInt(); - prefs.enableRigCtlD = settings->value("EnableRigCtlD", defPrefs.enableRigCtlD).toBool(); ui->enableRigctldChk->setChecked(prefs.enableRigCtlD); prefs.rigCtlPort = settings->value("RigCtlPort", defPrefs.rigCtlPort).toInt(); @@ -1552,6 +1552,14 @@ void wfmain::loadSettings() // Call the function to start rigctld if enabled. on_enableRigctldChk_clicked(prefs.enableRigCtlD); + prefs.tcpPort = settings->value("TcpServerPort", defPrefs.tcpPort).toInt(); + ui->tcpServerPortTxt->setText(QString("%1").arg(prefs.tcpPort)); + + prefs.waterfallFormat = settings->value("WaterfallFormat", defPrefs.waterfallFormat).toInt(); + ui->waterfallFormatCombo->blockSignals(true); + ui->waterfallFormatCombo->setCurrentIndex(prefs.waterfallFormat); + ui->waterfallFormatCombo->blockSignals(false); + udpPrefs.ipAddress = settings->value("IPAddress", udpDefPrefs.ipAddress).toString(); ui->ipAddressTxt->setEnabled(ui->lanEnableBtn->isChecked()); ui->ipAddressTxt->setText(udpPrefs.ipAddress); @@ -1998,6 +2006,7 @@ void wfmain::saveSettings() settings->setValue("EnableRigCtlD", prefs.enableRigCtlD); settings->setValue("TcpServerPort", prefs.tcpPort); settings->setValue("RigCtlPort", prefs.rigCtlPort); + settings->setValue("tcpServerPort", prefs.tcpPort); settings->setValue("IPAddress", udpPrefs.ipAddress); settings->setValue("ControlLANPort", udpPrefs.controlLANPort); settings->setValue("SerialLANPort", udpPrefs.serialLANPort); @@ -2014,6 +2023,8 @@ void wfmain::saveSettings() settings->setValue("AudioInput", txSetup.name); settings->setValue("ResampleQuality", rxSetup.resampleQuality); settings->setValue("ClientName", udpPrefs.clientName); + settings->setValue("WaterfallFormat", prefs.waterfallFormat); + settings->endGroup(); // Memory channels @@ -5832,6 +5843,22 @@ void wfmain::on_rigctldPortTxt_editingFinished() } } +void wfmain::on_tcpServerPortTxt_editingFinished() +{ + + bool okconvert = false; + unsigned int port = ui->tcpServerPortTxt->text().toUInt(&okconvert); + if (okconvert) + { + prefs.tcpPort = port; + } +} + +void wfmain::on_waterfallFormatCombo_activated(int index) +{ + prefs.waterfallFormat = index; +} + void wfmain::on_moreControlsBtn_clicked() { trxadj->show(); diff --git a/wfmain.h b/wfmain.h index 6a092a4..fa1b23d 100644 --- a/wfmain.h +++ b/wfmain.h @@ -164,7 +164,7 @@ signals: void sayFrequency(); void sayMode(); void sayAll(); - void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp, quint16 tcp); + void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp, quint16 tcp, quint8 wf); void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcp); void sendCloseComm(); void sendChangeLatency(quint16 latency); @@ -497,10 +497,13 @@ private slots: void on_meter2selectionCombo_activated(int index); + void on_waterfallFormatCombo_activated(int index); + void on_enableRigctldChk_clicked(bool checked); void on_rigctldPortTxt_editingFinished(); + void on_tcpServerPortTxt_editingFinished(); void on_moreControlsBtn_clicked(); @@ -765,6 +768,7 @@ private: bool confirmPowerOff; meterKind meter2Type; quint16 tcpPort; + quint8 waterfallFormat; // plot scheme } prefs; diff --git a/wfmain.ui b/wfmain.ui index 51ff81c..e1b24a7 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -6,7 +6,7 @@ 0 0 - 946 + 940 569 @@ -2066,7 +2066,7 @@ - 0 + 4 @@ -3527,6 +3527,93 @@ + + + + + + TCP Server Port + + + + + + + true + + + + 0 + 0 + + + + + + + + Enter port for TCP server, 0 = disabled (restart required if changed) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Waterfall Format + + + + + + + + Default + + + + + Single (network) + + + + + Multi (serial) + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -3677,7 +3764,7 @@ 0 0 - 946 + 940 21 diff --git a/wfview.vcxproj b/wfview.vcxproj index bf6d12b..5c2bc26 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,8 +16,7 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild - + $(MSBuildProjectDirectory)\QtMsBuild v142 @@ -37,10 +36,7 @@ debug\ wfview - - - - + @@ -48,37 +44,11 @@ - - - - - - debug\ - debug\ - wfview - true - - - release\ - release\ - wfview - true - false - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - - + debug\debug\wfviewtruerelease\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport + - .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;..\kissfft;resampler;release;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -87,16 +57,14 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="c1f9358";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - - + MultiThreadedDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -117,31 +85,12 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"c1f9358\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h - .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;..\kissfft;resampler;debug;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -150,14 +99,13 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="c1f9358";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true - + true ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -176,30 +124,10 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"c1f9358\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h - @@ -226,53 +154,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document true @@ -289,21 +371,121 @@ release\moc_predefs.h;%(Outputs) true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -337,16 +519,30 @@ - resources - resources - + + + + + + + + + + resourcesresources - style - style - + + + + + + + + + + stylestyle @@ -360,9 +556,6 @@ - - - - + \ No newline at end of file diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index d0e5df8..b7c023b 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -116,9 +116,6 @@ Source Files - - Source Files - @@ -207,12 +204,59 @@ + + + + + + + + + + Generated Files Generated Files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -369,6 +413,6 @@ - + \ No newline at end of file diff --git a/wfview.vcxproj.user b/wfview.vcxproj.user index 1f25d96..2d805e3 100644 --- a/wfview.vcxproj.user +++ b/wfview.vcxproj.user @@ -7,9 +7,9 @@ PATH=$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(PATH) - 2022-04-13T11:33:25.4527691Z + 2022-04-18T13:23:03.5252168Z - 2022-04-13T11:33:27.0112037Z + 2022-04-18T13:23:05.0598803Z \ No newline at end of file From 86502a5c3a5d69563e16926cc79d19ea95ca6947 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 20 Apr 2022 13:35:23 +0100 Subject: [PATCH 178/323] Various compatibility improvements --- audiohandler.cpp | 14 ++++ main.cpp | 2 +- packettypes.h | 115 +++++++++++++++------------ rigcommander.cpp | 8 +- servermain.cpp | 4 +- udphandler.cpp | 48 +++++++---- udphandler.h | 2 +- udpserver.cpp | 170 ++++++++++++++++++++++----------------- udpserver.h | 23 ++++-- wfmain.cpp | 203 +++++++++-------------------------------------- wfmain.h | 2 - 11 files changed, 273 insertions(+), 318 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 12df8bb..8daa19b 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -125,6 +125,13 @@ bool audioHandler::init(audioSetup setupIn) qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup."; return false; } + if (format.channelCount() == 1 && setup.format.channelCount() == 2) { + format.setChannelCount(2); + if (!setup.port.isFormatSupported(format)) { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request stereo input!"; + format.setChannelCount(1); + } + } if (format.sampleSize() == 24) { // We can't convert this easily @@ -494,6 +501,13 @@ void audioHandler::getNextAudioChunk() samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); samplesF = samplesTemp; } + else if (format.channelCount() == 1 && setup.format.channelCount() == 2) { + // Convert mono to stereo if required + Eigen::VectorXf samplesTemp(samplesF.size() * 2); + Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; + Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; + samplesF = samplesTemp; + } //qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length() << "resampled" << samplesF.size(); diff --git a/main.cpp b/main.cpp index 7debf6e..2dc33e0 100644 --- a/main.cpp +++ b/main.cpp @@ -57,11 +57,11 @@ int main(int argc, char *argv[]) keyboard* kb = new keyboard(); kb->start(); #else + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication a(argc, argv); a.setOrganizationName("wfview"); a.setOrganizationDomain("wfview.org"); a.setApplicationName("wfview"); - a.setAttribute(Qt::AA_EnableHighDpiScaling); #endif #ifdef QT_DEBUG diff --git a/packettypes.h b/packettypes.h index d8a17d3..4a61e68 100644 --- a/packettypes.h +++ b/packettypes.h @@ -121,6 +121,7 @@ typedef union audio_packet { quint16 seq; // 0x06 quint32 sentid; // 0x08 quint32 rcvdid; // 0x0c + quint16 ident; // 0x10 quint16 sendseq; // 0x12 quint16 unused; // 0x14 @@ -165,18 +166,24 @@ typedef union token_packet { quint16 seq; // 0x06 quint32 sentid; // 0x08 quint32 rcvdid; // 0x0c - char unuseda[3]; // 0x10 - quint16 code; // 0x13 - quint16 res; // 0x15 - quint8 innerseq; // 0x17 - char unusedb; // 0x18 - char unusedc; // 0x19 + char unuseda[2]; // 0x10 + quint16 payloadsize; // 0x12 + quint8 requestreply; // 0x13 + quint8 requesttype; // 0x14 + quint16 innerseq; // 0x16 + char unusedb[2]; // 0x18 quint16 tokrequest; // 0x1a quint32 token; // 0x1c - char unusedd[7]; // 0x20 - quint16 commoncap; // 0x27 - char unuseddd; // 0x29 - char macaddress[6]; // 0x2a + union { + struct { + quint16 authstartid; // 0x20 + char unusedg[5]; // 0x22 + quint16 commoncap; // 0x27 + char unusedh; // 0x29 + quint8 macaddress[6]; // 0x2a + }; + quint8 guid[GUIDLEN]; // 0x20 + }; quint32 response; // 0x30 char unusede[12]; // 0x34 }; @@ -192,19 +199,24 @@ typedef union status_packet { quint16 seq; // 0x06 quint32 sentid; // 0x08 quint32 rcvdid; // 0x0c - char unuseda[3]; // 0x10 - quint16 code; // 0x13 - quint16 res; // 0x15 - quint8 innerseq; // 0x17 - char unusedb; // 0x18 - char unusedc; // 0x19 + char unuseda[2]; // 0x10 + quint16 payloadsize; // 0x12 + quint8 requestreply; // 0x13 + quint8 requesttype; // 0x14 + quint16 innerseq; // 0x16 + char unusedb[2]; // 0x18 quint16 tokrequest; // 0x1a quint32 token; // 0x1c - char unusedd[6]; // 0x20 - quint16 unknown; // 0x26 - char unusede; // 0x28 - char unusedf; // 0x29 - quint8 macaddress[6]; // 0x2a + union { + struct { + quint16 authstartid; // 0x20 + char unusedd[5]; // 0x22 + quint16 commoncap; // 0x27 + char unusede; // 0x29 + quint8 macaddress[6]; // 0x2a + }; + quint8 guid[GUIDLEN]; // 0x20 + }; quint32 error; // 0x30 char unusedg[12]; // 0x34 char disc; // 0x40 @@ -226,12 +238,12 @@ typedef union login_response_packet { quint16 seq; // 0x06 quint32 sentid; // 0x08 quint32 rcvdid; // 0x0c - char unuseda[3]; // 0x10 - quint16 code; // 0x13 - quint16 res; // 0x15 - quint8 innerseq; // 0x17 - char unusedb; // 0x18 - char unusedc; // 0x19 + char unuseda[2]; // 0x10 + quint16 payloadsize; // 0x12 + quint8 requestreply; // 0x13 + quint8 requesttype; // 0x14 + quint16 innerseq; // 0x16 + char unusedb[2]; // 0x18 quint16 tokrequest; // 0x1a quint32 token; // 0x1c quint16 authstartid; // 0x20 @@ -253,12 +265,12 @@ typedef union login_packet { quint16 seq; // 0x06 quint32 sentid; // 0x08 quint32 rcvdid; // 0x0c - char unuseda[3]; // 0x10 - quint16 code; // 0x13 - quint16 res; // 0x15 - quint8 innerseq; // 0x17 - char unusedaa; // 0x18; - char unusedb; // 0x19 + char unuseda[2]; // 0x10 + quint16 payloadsize; // 0x12 + quint8 requestreply; // 0x13 + quint8 requesttype; // 0x14 + quint16 innerseq; // 0x16 + char unusedb[2]; // 0x18 quint16 tokrequest; // 0x1a quint32 token; // 0x1c char unusedc[32]; // 0x20 @@ -279,12 +291,12 @@ typedef union conninfo_packet { quint16 seq; // 0x06 quint32 sentid; // 0x08 quint32 rcvdid; // 0x0c - char unuseda[3]; // 0x10 - quint16 code; // 0x13 - quint16 res; // 0x15 - quint8 innerseq; // 0x17 - char unusedaa; // 0x18 - char unusedb; // 0x19 + char unuseda[2]; // 0x10 + quint16 payloadsize; // 0x12 + quint8 requestreply; // 0x13 + quint8 requesttype; // 0x14 + quint16 innerseq; // 0x16 + char unusedb[2]; // 0x18 quint16 tokrequest; // 0x1a quint32 token; // 0x1c union { @@ -293,7 +305,7 @@ typedef union conninfo_packet { char unusedg[5]; // 0x22 quint16 commoncap; // 0x27 char unusedh; // 0x29 - char macaddress[6]; // 0x2a + quint8 macaddress[6]; // 0x2a }; quint8 guid[GUIDLEN]; // 0x20 }; @@ -328,19 +340,18 @@ typedef union conninfo_packet { // 0x64 length radio capabilities part of cap packet. -/* - };*/ + typedef union radio_cap_packet { struct { union { struct { - char unusede[7]; // 0x0 - quint16 commoncap; // 0x0 - char unused; // 0x0 - char macaddress[6]; // 0x0 + quint8 unusede[7]; // 0x00 + quint16 commoncap; // 0x07 + quint8 unused; // 0x09 + quint8 macaddress[6]; // 0x0a }; - quint8 guid[GUIDLEN]; // 0x0 + quint8 guid[GUIDLEN]; // 0x0 }; char name[32]; // 0x10 char audio[32]; // 0x30 @@ -373,14 +384,14 @@ typedef union capabilities_packet { quint32 rcvdid; // 0x0c char unuseda[2]; // 0x10 quint16 payloadsize; // 0x12 - quint16 res; // 0x14 + quint8 requestreply; // 0x13 + quint8 requesttype; // 0x14 quint16 innerseq; // 0x16 - char unusedb; // 0x18 - char unusedc; // 0x19 + char unusedb[2]; // 0x18 quint16 tokrequest; // 0x1a quint32 token; // 0x1c - char unusedd[33]; // 0x20 - char numradios; // 0x41 + char unusedd[32]; // 0x20 + quint16 numradios; // 0x40 }; char packet[CAPABILITIES_SIZE]; } *capabilities_packet_t; diff --git a/rigcommander.cpp b/rigcommander.cpp index c99cb1c..45cdb52 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -1493,6 +1493,9 @@ void rigCommander::parseLevels() { switch(payloadIn[1]) { + case '\x01': + // noise or s-meter sequelch status + break; case '\x02': // S-Meter emit haveMeter(meterS, level); @@ -1503,6 +1506,9 @@ void rigCommander::parseLevels() emit haveMeter(meterCenter, level); state.set(SMETER, level, false); break; + case '\x05': + // Various squelch (tone etc.) + break; case '\x11': // RF-Power meter emit haveMeter(meterPower, level); @@ -4709,7 +4715,7 @@ void rigCommander::printHex(const QByteArray &pdata, bool printVert, bool printH void rigCommander::dataFromServer(QByteArray data) { - //qInfo(logRig()) << "emit dataForComm()"; + //qInfo(logRig()) << "***************** emit dataForComm()" << data; emit dataForComm(data); } diff --git a/servermain.cpp b/servermain.cpp index 286cec5..cafa08d 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -89,7 +89,7 @@ void servermain::openRig() { //qInfo(logSystem()) << "Got rig"; QMetaObject::invokeMethod(radio->rig, [=]() { - radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none"),prefs.tcpPort); + radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none"),prefs.tcpPort,0); }, Qt::QueuedConnection); } } @@ -375,7 +375,7 @@ void servermain::setServerToPrefs() udp = Q_NULLPTR; } - udp = new udpServer(serverConfig, serverTxSetup, serverRxSetup); + udp = new udpServer(serverConfig); serverThread = new QThread(this); diff --git a/udphandler.cpp b/udphandler.cpp index e3c55fb..f8a5904 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -223,7 +223,7 @@ void udpHandler::dataReceived() case (TOKEN_SIZE): // Response to Token request { token_packet_t in = (token_packet_t)r.constData(); - if (in->res == 0x05 && in->type != 0x01) + if (in->requesttype == 0x05 && in->requestreply == 0x02 && in->type != 0x01) { if (in->response == 0x0000) { @@ -374,8 +374,9 @@ void udpHandler::dataReceived() qInfo(logUdp()) << "Got Connection status for:" << in->name << "Busy:" << in->busy << "Computer" << in->computer << "IP" << ip.toString(); // First we need to find this radio in our capabilities packet, there aren't many so just step through - for (unsigned char f = 0; f < radios.length(); f++) + for (unsigned char f = 0; f < radios.size(); f++) { + if ((radios[f].commoncap == 0x8010 && radios[f].macaddress[0] == in->macaddress[0] && radios[f].macaddress[1] == in->macaddress[1] && @@ -386,11 +387,15 @@ void udpHandler::dataReceived() !memcmp(radios[f].guid,in->guid, GUIDLEN)) { emit setRadioUsage(f, in->busy, QString(in->computer), ip.toString()); + qDebug(logUdp()) << "Set radio usage num:" << f << in->name << "Busy:" << in->busy << "Computer" << in->computer << "IP" << ip.toString(); } } - if (in->type != 0x01 && !streamOpened) { - if (in->busy && numRadios == 1) + if (!streamOpened && radios.size()==1) { + + qDebug(logUdp()) << "Single radio available, can I connect to it?"; + + if (in->busy) { if (in->ipaddress != 0x00 && strcmp(in->computer, compName.toLocal8Bit())) { @@ -400,17 +405,19 @@ void udpHandler::dataReceived() else { } } - else if (!in->busy && numRadios == 1) + else if (!in->busy) { + qDebug(logUdp()) << "Attempting to connect to radio"; status.message = devName + " available"; setCurrentRadio(0); } } else if (streamOpened) - /* If another client connects/disconnects from the server, the server will emit + /* If another client connects/disconnects from the server, the server will emit a CONNINFO packet, send our details to confirm we still want the stream */ { + //qDebug(logUdp()) << "I am already connected????"; // Received while stream is open. //sendRequestStream(); } @@ -440,9 +447,16 @@ void udpHandler::dataReceived() qInfo(logUdp()) << this->metaObject()->className() << "Received radio capabilities, Name:" << radio.name << " Audio:" << radio.audio << "CIV:" << hex << (unsigned char)radio.civ << + "MAC:" << radio.macaddress[0] << + ":" << radio.macaddress[1] << + ":" << radio.macaddress[2] << + ":" << radio.macaddress[3] << + ":" << radio.macaddress[4] << + ":" << radio.macaddress[5] << "CAPF" << radio.capf; } emit requestRadioSelection(radios); + break; } @@ -505,8 +519,10 @@ void udpHandler::sendRequestStream() p.len = sizeof(p); p.sentid = myId; p.rcvdid = remoteId; - p.code = 0x0180; - p.res = 0x03; + p.payloadsize = qToBigEndian((quint16)(sizeof(p) - 0x10)); + p.requesttype = 0x03; + p.requestreply = 0x01; + if (!useGuid) { p.commoncap = 0x8010; memcpy(&p.macaddress, macaddress, 6); @@ -514,7 +530,7 @@ void udpHandler::sendRequestStream() else { memcpy(&p.guid, guid, GUIDLEN); } - p.innerseq = authSeq++; + p.innerseq = qToBigEndian(authSeq++); p.tokrequest = tokRequest; p.token = token; memcpy(&p.name, devName.toLocal8Bit().constData(), devName.length()); @@ -565,8 +581,11 @@ void udpHandler::sendLogin() // Only used on control stream. p.len = sizeof(p); p.sentid = myId; p.rcvdid = remoteId; - p.code = 0x0170; // Not sure what this is? - p.innerseq = authSeq++; + p.payloadsize = qToBigEndian((quint16)(sizeof(p) - 0x10)); + p.requesttype = 0x00; + p.requestreply = 0x01; + + p.innerseq = qToBigEndian(authSeq++); p.tokrequest = tokRequest; memcpy(p.username, usernameEncoded.constData(), usernameEncoded.length()); memcpy(p.password, passwordEncoded.constData(), passwordEncoded.length()); @@ -585,9 +604,10 @@ void udpHandler::sendToken(uint8_t magic) p.len = sizeof(p); p.sentid = myId; p.rcvdid = remoteId; - p.code = 0x0130; // Not sure what this is? - p.res = magic; - p.innerseq = authSeq++; + p.payloadsize = qToBigEndian((quint16)(sizeof(p) - 0x10)); + p.requesttype = magic; + p.requestreply = 0x01; + p.innerseq = qToBigEndian(authSeq++); p.tokrequest = tokRequest; p.token = token; diff --git a/udphandler.h b/udphandler.h index 08b3c39..3538337 100644 --- a/udphandler.h +++ b/udphandler.h @@ -85,7 +85,7 @@ public: QUdpSocket* udp=Q_NULLPTR; uint32_t myId = 0; uint32_t remoteId = 0; - uint8_t authSeq = 0x00; + uint16_t authSeq = 0x30; uint16_t sendSeqB = 0; uint16_t sendSeq = 1; uint16_t lastReceivedSeq = 1; diff --git a/udpserver.cpp b/udpserver.cpp index fc33ce4..fa6b85d 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -4,10 +4,8 @@ #define STALE_CONNECTION 15 #define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms #define AUDIO_SEND_PERIOD 20 // -udpServer::udpServer(SERVERCONFIG& config, audioSetup outAudio, audioSetup inAudio) : - config(config), - outAudio(outAudio), - inAudio(inAudio) +udpServer::udpServer(SERVERCONFIG* config) : + config(config) { qInfo(logUdpServer()) << "Starting udp server"; } @@ -15,7 +13,7 @@ udpServer::udpServer(SERVERCONFIG& config, audioSetup outAudio, audioSetup inAud void udpServer::init() { - for (RIGCONFIG* rig : config.rigs) + for (RIGCONFIG* rig : config->rigs) { qDebug(logUdpServer()) << "CIV:" << rig->civAddr; qDebug(logUdpServer()) << "Model:" << rig->modelName; @@ -53,36 +51,40 @@ void udpServer::init() } } + QString macTemp; foreach(QNetworkInterface netInterface, QNetworkInterface::allInterfaces()) { // Return only the first non-loopback MAC Address if (!(netInterface.flags() & QNetworkInterface::IsLoopBack)) { - macAddress = netInterface.hardwareAddress(); + macTemp = netInterface.hardwareAddress(); } } + memcpy(&macAddress, macTemp.toLocal8Bit(), 6); + memcpy(&macAddress, QByteArrayLiteral("\x00\x90\xc7").constData(), 3); + uint32_t addr = localIP.toIPv4Address(); - qInfo(logUdpServer()) << "My IP Address:" << QHostAddress(addr).toString() << "My MAC Address:" << macAddress; + qInfo(logUdpServer()) << "My IP Address:" << QHostAddress(addr).toString(); - controlId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (config.controlPort & 0xffff); - civId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (config.civPort & 0xffff); - audioId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (config.audioPort & 0xffff); + controlId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (config->controlPort & 0xffff); + civId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (config->civPort & 0xffff); + audioId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (config->audioPort & 0xffff); - qInfo(logUdpServer()) << "Server Binding Control to: " << config.controlPort; + qInfo(logUdpServer()) << "Server Binding Control to: " << config->controlPort; udpControl = new QUdpSocket(this); - udpControl->bind(config.controlPort); + udpControl->bind(config->controlPort); QUdpSocket::connect(udpControl, &QUdpSocket::readyRead, this, &udpServer::controlReceived); - qInfo(logUdpServer()) << "Server Binding CIV to: " << config.civPort; + qInfo(logUdpServer()) << "Server Binding CIV to: " << config->civPort; udpCiv = new QUdpSocket(this); - udpCiv->bind(config.civPort); + udpCiv->bind(config->civPort); QUdpSocket::connect(udpCiv, &QUdpSocket::readyRead, this, &udpServer::civReceived); - qInfo(logUdpServer()) << "Server Binding Audio to: " << config.audioPort; + qInfo(logUdpServer()) << "Server Binding Audio to: " << config->audioPort; udpAudio = new QUdpSocket(this); - udpAudio->bind(config.audioPort); + udpAudio->bind(config->audioPort); QUdpSocket::connect(udpAudio, &QUdpSocket::readyRead, this, &udpServer::audioReceived); wdTimer = new QTimer(); @@ -129,8 +131,8 @@ udpServer::~udpServer() void udpServer::receiveRigCaps(rigCapabilities caps) { - for (RIGCONFIG* rig: config.rigs) { - if (!memcmp(rig->guid, caps.guid, GUIDLEN)) { + for (RIGCONFIG* rig: config->rigs) { + if (!memcmp(rig->guid, caps.guid, GUIDLEN) || config->rigs.size()==1) { // Matching rig, fill-in missing details rig->rigAvailable = true; rig->modelName = caps.modelName; @@ -173,8 +175,8 @@ void udpServer::controlReceived() current->timeConnected = QDateTime::currentDateTime(); current->ipAddress = datagram.senderAddress(); current->port = datagram.senderPort(); - current->civPort = config.civPort; - current->audioPort = config.audioPort; + current->civPort = config->civPort; + current->audioPort = config->audioPort; current->myId = controlId; current->remoteId = qFromLittleEndian(r.mid(8, 4)); current->socket = udpControl; @@ -194,8 +196,16 @@ void udpServer::controlReceived() qInfo(logUdpServer()) << current->ipAddress.toString() << ": New Control connection created"; + if (connMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { + // Quick hack to replace the GUID with a MAC address. + if (config->rigs.size() == 1) { + memset(config->rigs.first()->guid, 0, GUIDLEN); + config->rigs.first()->commoncap = (quint16)0x8010; + memcpy(config->rigs.first()->macaddress, macAddress, 6); + memcpy(current->guid, config->rigs.first()->guid, GUIDLEN); + } controlClients.append(current); connMutex.unlock(); } @@ -255,26 +265,26 @@ void udpServer::controlReceived() token_packet_t in = (token_packet_t)r.constData(); current->rxSeq = in->seq; current->authInnerSeq = in->innerseq; - memcpy(current->macaddress, in->macaddress, 6); - if (in->res == 0x02) { + memcpy(current->guid, in->guid, GUIDLEN); + if (in->requesttype == 0x02 && in->requestreply == 0x01) { // Request for new token qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received create token request"; sendCapabilities(current); - for (RIGCONFIG* radio : config.rigs) { + for (RIGCONFIG* radio : config->rigs) { sendConnectionInfo(current, radio->guid); } } - else if (in->res == 0x01) { + else if (in->requesttype == 0x01 && in->requestreply == 0x01) { // Token disconnect qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received token disconnect request"; - sendTokenResponse(current, in->res); + sendTokenResponse(current, in->requesttype); } - else if (in->res == 0x04) { + else if (in->requesttype == 0x04 && in->requestreply == 0x01) { // Disconnect audio/civ - sendTokenResponse(current, in->res); + sendTokenResponse(current, in->requesttype); current->isStreaming = false; - for (RIGCONFIG* radio : config.rigs) { - if (!memcmp(radio->guid, current->guid, GUIDLEN)) + for (RIGCONFIG* radio : config->rigs) { + if (!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size() == 1) { sendConnectionInfo(current, radio->guid); } @@ -282,7 +292,7 @@ void udpServer::controlReceived() } else { qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received token request"; - sendTokenResponse(current, in->res); + sendTokenResponse(current, in->requesttype); } break; } @@ -290,7 +300,7 @@ void udpServer::controlReceived() { login_packet_t in = (login_packet_t)r.constData(); qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received 'login'"; - foreach(SERVERUSER user, config.users) + foreach(SERVERUSER user, config->users) { QByteArray usercomp; passcode(user.username, usercomp); @@ -345,9 +355,9 @@ void udpServer::controlReceived() audioSetup setup; - setup.resampleQuality = config.resampleQuality; - for (RIGCONFIG* radio : config.rigs) { - if (!memcmp(radio->guid, current->guid, GUIDLEN) && radio->txaudio == Q_NULLPTR) + setup.resampleQuality = config->resampleQuality; + for (RIGCONFIG* radio : config->rigs) { + if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size()==1) && radio->txaudio == Q_NULLPTR ) { radio->txAudioSetup.codec = current->txCodec; radio->txAudioSetup.format.setSampleRate(current->txSampleRate); @@ -380,7 +390,7 @@ void udpServer::controlReceived() connect(this, SIGNAL(haveAudioData(audioPacket)), radio->txaudio, SLOT(incomingAudio(audioPacket))); } - if (!memcmp(radio->guid, current->guid, GUIDLEN) && radio->rxaudio == Q_NULLPTR) + if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size() == 1) && radio->rxaudio == Q_NULLPTR) { #if !defined(PORTAUDIO) && !defined(RTAUDIO) qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio input(RX) :" << radio->rxAudioSetup.port.deviceName(); @@ -404,7 +414,6 @@ void udpServer::controlReceived() radio->rxAudioThread->start(QThread::TimeCriticalPriority); - connect(this, SIGNAL(setupRxAudio(audioSetup)), radio->rxaudio, SLOT(init(audioSetup))); connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater())); connect(radio->rxaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); @@ -414,6 +423,7 @@ void udpServer::controlReceived() }, Qt::QueuedConnection); #else #warning "QT 5.9 is not fully supported multiple rigs will NOT work!" + connect(this, SIGNAL(setupRxAudio(audioSetup)), radio->rxaudio, SLOT(init(audioSetup))); setupRxAudio(radio->rxAudioSetup); #endif @@ -573,10 +583,11 @@ void udpServer::civReceived() qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected invalid remote CI-V:" << hex << (quint8)r[lastFE+2]; } - for (RIGCONFIG* radio : config.rigs) { - if (!memcmp(radio->guid, current->guid, sizeof(radio->guid))) + for (RIGCONFIG* radio : config->rigs) { + if (!memcmp(radio->guid, current->guid, sizeof(radio->guid)) || config->rigs.size()==1) { // Only send to the rig that it belongs to! + //qDebug(logUdpServer()) << "Sending data" << r.mid(0x15); #if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) QMetaObject::invokeMethod(radio->rig, [=]() { radio->rig->dataFromServer(r.mid(0x15));; @@ -1095,7 +1106,9 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed) p.innerseq = c->authInnerSeq; p.tokrequest = c->tokenRx; p.token = c->tokenTx; - p.code = 0x0250; + p.payloadsize = qToBigEndian((quint16)(sizeof(p) - 0x10)); + p.requesttype = 0x00; + p.requestreply = 0x01; if (!allowed) { @@ -1108,7 +1121,8 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed) c->retransmitTimer->stop(); } else { - strcpy(p.connection, "WFVIEW"); + //strcpy(p.connection, "WFVIEW"); + strcpy(p.connection, "FTTH"); } SEQBUFENTRY s; @@ -1157,20 +1171,21 @@ void udpServer::sendCapabilities(CLIENT* c) p.innerseq = c->authInnerSeq; p.tokrequest = c->tokenRx; p.token = c->tokenTx; - p.res = 0x0202; - p.numradios = config.rigs.count(); - + p.requesttype = 0x02; + p.requestreply = 0x02; + p.numradios = qToBigEndian((quint16)config->rigs.size()); SEQBUFENTRY s; s.seqNum = p.seq; s.timeSent = QTime::currentTime(); s.retransmitCount = 0; - for (RIGCONFIG* rig : config.rigs) { + for (RIGCONFIG* rig : config->rigs) { qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending Capabilities :" << c->txSeq << "for" << rig->modelName; radio_cap_packet r; memset(r.packet, 0x0, sizeof(r)); // We can't be sure it is initialized with 0x00! - + memcpy(r.guid, rig->guid, GUIDLEN); + memcpy(r.name, rig->rigName.toLocal8Bit(), sizeof(r.name)); memcpy(r.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11); @@ -1233,7 +1248,7 @@ void udpServer::sendCapabilities(CLIENT* c) } p.len = sizeof(p)+s.data.length(); - p.payloadsize = sizeof(p)+s.data.length() - 0x0f; + p.payloadsize = qToBigEndian((quint16)(sizeof(p) + s.data.length() - 0x10)); s.data.insert(0,QByteArray::fromRawData((const char*)p.packet, sizeof(p))); @@ -1271,8 +1286,8 @@ void udpServer::sendCapabilities(CLIENT* c) // Also used to display currently connected used information. void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[GUIDLEN]) { - for (RIGCONFIG* radio : config.rigs) { - if (!memcmp(guid, radio->guid, GUIDLEN)) + for (RIGCONFIG* radio : config->rigs) { + if (!memcmp(guid, radio->guid, GUIDLEN) || config->rigs.size()==1) { qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending ConnectionInfo :" << c->txSeq; conninfo_packet p; @@ -1285,7 +1300,10 @@ void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[GUIDLEN]) //p.innerseq = c->authInnerSeq; // Innerseq not used in user packet p.tokrequest = c->tokenRx; p.token = c->tokenTx; - p.code = 0x0380; + p.payloadsize = qToBigEndian((quint16)(sizeof(p) - 0x10)); + p.requesttype = 0x00; + p.requestreply = 0x03; + memcpy(p.guid, radio->guid, GUIDLEN); memcpy(p.name, radio->rigName.toLocal8Bit(), sizeof(p.name)); @@ -1362,11 +1380,11 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) p.innerseq = c->authInnerSeq; p.tokrequest = c->tokenRx; p.token = c->tokenTx; - p.code = 0x0230; - memcpy(p.macaddress, c->macaddress, 6); - p.commoncap = c->commonCap; - p.res = type; + p.payloadsize = qToBigEndian((quint16)(sizeof(p) - 0x10)); + memcpy(p.guid, c->guid, GUIDLEN); + p.requesttype = type; + p.requestreply = 0x02; SEQBUFENTRY s; s.seqNum = p.seq; @@ -1473,11 +1491,10 @@ void udpServer::sendStatus(CLIENT* c) p.innerseq = c->authInnerSeq; p.tokrequest = c->tokenRx; p.token = c->tokenTx; - p.code = 0x0240; - p.res = 0x03; - p.unknown = 0x1000; - p.unusede = (char)0x80; - memcpy(p.macaddress, c->macaddress, 6); + p.payloadsize = qToBigEndian((quint16)(sizeof(p) - 0x10)); + p.requestreply = 0x02; + p.requesttype = 0x03; + memcpy(p.guid, c->guid, GUIDLEN); // May be MAC address OR guid. p.civport = qToBigEndian(c->civPort); @@ -1534,7 +1551,7 @@ void udpServer::dataForServer(QByteArray d) continue; } // Use the GUID to determine which radio the response is from - if (memcmp(sender->getGUID(), client->guid, GUIDLEN)) + if (memcmp(sender->getGUID(), client->guid, GUIDLEN) && config->rigs.size()>1) { continue; // Rig guid doesn't match the one requested by the client. } @@ -1571,7 +1588,7 @@ void udpServer::dataForServer(QByteArray d) } client->txSeqBuf.insert(p.seq, s); client->txSeq++; - client->innerSeq++; + //client->innerSeq = (qToBigEndian(qFromBigEndian(client->innerSeq) + 1)); client->txMutex.unlock(); } else { @@ -1607,7 +1624,7 @@ void udpServer::receiveAudioData(const audioPacket& d) else { memcpy(guid, d.guid, GUIDLEN); } -//qInfo(logUdpServer()) << "Server got:" << d.data.length(); + //qInfo(logUdpServer()) << "Server got:" << d.data.length(); foreach(CLIENT * client, audioClients) { int len = 0; @@ -1615,7 +1632,7 @@ void udpServer::receiveAudioData(const audioPacket& d) QByteArray partial; partial = d.data.mid(len, 1364); len = len + partial.length(); - if (client != Q_NULLPTR && client->connected && !memcmp(client->guid, guid, GUIDLEN)) { + if (client != Q_NULLPTR && client->connected && (!memcmp(client->guid, guid, GUIDLEN) || config->rigs.size()== 1)) { audio_packet p; memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! p.len = sizeof(p) + partial.length(); @@ -1698,19 +1715,22 @@ void udpServer::sendRetransmitRequest(CLIENT* c) { for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it) { - if (&it.key() != Q_NULLPTR && it.value() < 4) + if (&it.key() != Q_NULLPTR) { - missingSeqs.append(it.key() & 0xff); - missingSeqs.append(it.key() >> 8 & 0xff); - missingSeqs.append(it.key() & 0xff); - missingSeqs.append(it.key() >> 8 & 0xff); - it.value()++; - } + if (it.value() < 4) + { + missingSeqs.append(it.key() & 0xff); + missingSeqs.append(it.key() >> 8 & 0xff); + missingSeqs.append(it.key() & 0xff); + missingSeqs.append(it.key() >> 8 & 0xff); + it.value()++; + } - else { - // We have tried 4 times to request this packet, time to give up! - qDebug(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; - it = c->rxMissing.erase(it); + else { + // We have tried 4 times to request this packet, time to give up! + qDebug(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; + it = c->rxMissing.erase(it); + } } } @@ -1844,8 +1864,8 @@ void udpServer::deleteConnection(QList* l, CLIENT* c) } if (len == 0) { - for (RIGCONFIG* radio : config.rigs) { - if (!memcmp(radio->guid, guid, GUIDLEN)) + for (RIGCONFIG* radio : config->rigs) { + if (!memcmp(radio->guid, guid, GUIDLEN) || config->rigs.size() == 1) { if (radio->rxAudioThread != Q_NULLPTR) { diff --git a/udpserver.h b/udpserver.h index 8173929..b7ca33d 100644 --- a/udpserver.h +++ b/udpserver.h @@ -42,6 +42,7 @@ struct SERVERUSER { quint8 userType; }; + struct RIGCONFIG { QString serialPort; quint32 baudRate; @@ -54,7 +55,17 @@ struct RIGCONFIG { audioSetup txAudioSetup; QString modelName; QString rigName; - quint8 guid[GUIDLEN]; +#pragma pack(push, 1) + union { + struct { + quint8 unused[7]; // 0x22 + quint16 commoncap; // 0x27 + quint8 unusedb; // 0x29 + quint8 macaddress[6]; // 0x2a + }; + quint8 guid[GUIDLEN]; // 0x20 + }; +#pragma pack(pop) bool rigAvailable=false; rigCapabilities rigCaps; rigCommander* rig = Q_NULLPTR; @@ -88,7 +99,7 @@ class udpServer : public QObject Q_OBJECT public: - udpServer(SERVERCONFIG& config, audioSetup outAudio, audioSetup inAudio); + udpServer(SERVERCONFIG* config); ~udpServer(); public slots: @@ -128,11 +139,11 @@ private: quint16 connSeq; quint16 pingSeq; quint32 rxPingTime; // 32bit as has other info - quint8 authInnerSeq; + quint16 authInnerSeq; quint16 authSeq; quint16 innerSeq; quint16 sendAudioSeq; - quint8 macaddress[4]; + quint8 macaddress[6]; quint16 tokenRx; quint32 tokenTx; quint32 commonCap; @@ -186,13 +197,13 @@ private: void watchdog(); void deleteConnection(QList *l, CLIENT* c); - SERVERCONFIG config; + SERVERCONFIG *config; QUdpSocket* udpControl = Q_NULLPTR; QUdpSocket* udpCiv = Q_NULLPTR; QUdpSocket* udpAudio = Q_NULLPTR; QHostAddress localIP; - QString macAddress; + quint8 macAddress[6]; quint32 controlId = 0; quint32 civId = 0; diff --git a/wfmain.cpp b/wfmain.cpp index e57ac59..61bd474 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -111,10 +111,6 @@ wfmain::~wfmain() delete ui; delete settings; -#if defined(PORTAUDIO) - Pa_Terminate(); -#endif - } void wfmain::closeEvent(QCloseEvent *event) @@ -999,9 +995,9 @@ void wfmain::setServerToPrefs() if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; - qInfo(logAudio()) << "Audio Input device " << serverRxSetup.name; - qInfo(logAudio()) << "Audio Output device " << serverTxSetup.name; - udp = new udpServer(serverConfig, serverTxSetup, serverRxSetup); + qInfo(logAudio()) << "Audio Input device " << serverConfig.rigs.first()->rxAudioSetup.name; + qInfo(logAudio()) << "Audio Output device " << serverConfig.rigs.first()->txAudioSetup.name; + udp = new udpServer(&serverConfig); serverThread = new QThread(this); @@ -1067,100 +1063,8 @@ void wfmain::setUIToPrefs() void wfmain::setAudioDevicesUI() { -#if defined(RTAUDIO) - -#if defined(Q_OS_LINUX) - RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); -#elif defined(Q_OS_WIN) - RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); -#elif defined(Q_OS_MACX) - RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE); -#endif - - // Enumerate audio devices, need to do before settings are loaded. - std::map apiMap; - apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; - apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; - apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound"; - apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI"; - apiMap[RtAudio::UNIX_JACK] = "Jack Client"; - apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; - apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; - apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; - apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; - - std::vector< RtAudio::Api > apis; - RtAudio::getCompiledApi(apis); - - qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion()); - - qInfo(logAudio()) << "Compiled APIs:"; - for (unsigned int i = 0; i < apis.size(); i++) { - qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]); - } - - RtAudio::DeviceInfo info; - - qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]); - - unsigned int devices = audio->getDeviceCount(); - qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; - - for (unsigned int i = 1; i < devices; i++) { - info = audio->getDeviceInfo(i); - if (info.outputChannels > 0) { - qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); - ui->audioOutputCombo->addItem(QString::fromStdString(info.name), i); - ui->serverTXAudioOutputCombo->addItem(QString::fromStdString(info.name), i); - } - if (info.inputChannels > 0) { - qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name); - ui->audioInputCombo->addItem(QString::fromStdString(info.name), i); - ui->serverRXAudioInputCombo->addItem(QString::fromStdString(info.name), i); - } - } - - delete audio; - -#elif defined(PORTAUDIO) - // Use PortAudio device enumeration - - PaError err; - - err = Pa_Initialize(); - - if (err != paNoError) - { - qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio"; - } - - qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText; - - int numDevices; - numDevices = Pa_GetDeviceCount(); - qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; - - const PaDeviceInfo* info; - for (int i = 0; i < numDevices; i++) - { - info = Pa_GetDeviceInfo(i); - if (info->maxInputChannels > 0) { - qInfo(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name; - ui->audioInputCombo->addItem(info->name, i); - ui->serverRXAudioInputCombo->addItem(info->name, i); - } - if (info->maxOutputChannels > 0) { - qInfo(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name; - ui->audioOutputCombo->addItem(info->name, i); - ui->serverTXAudioOutputCombo->addItem(info->name, i); -} - } -#else - -// If no external library is configured, use QTMultimedia - // Enumerate audio devices, need to do before settings are loaded. - qDebug(logSystem()) << "Finding audio input devices"; + qDebug(logSystem()) << "Finding audio output devices"; const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { #ifdef Q_OS_WIN @@ -1174,7 +1078,7 @@ void wfmain::setAudioDevicesUI() #endif } - qDebug(logSystem()) << "Finding audio output devices"; + qDebug(logSystem()) << "Finding audio input devices"; const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (const QAudioDeviceInfo& deviceInfo : audioInputs) { #ifdef Q_OS_WIN @@ -1190,10 +1094,7 @@ void wfmain::setAudioDevicesUI() qDebug(logSystem()) << "Audio devices done."; rxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); txSetup.port = QAudioDeviceInfo::defaultInputDevice(); - serverRxSetup.port = txSetup.port; - serverTxSetup.port = rxSetup.port; qDebug(logSystem()) << "Audio set to default device initially"; -#endif } void wfmain::setSerialDevicesUI() @@ -1637,14 +1538,10 @@ void wfmain::loadSettings() int audioOutputIndex = ui->audioOutputCombo->findText(rxSetup.name); if (audioOutputIndex != -1) { ui->audioOutputCombo->setCurrentIndex(audioOutputIndex); -#if defined(RTAUDIO) - rxSetup.port = ui->audioOutputCombo->itemData(audioOutputIndex).toInt(); -#elif defined(PORTAUDIO) - rxSetup.port = ui->audioOutputCombo->itemData(audioOutputIndex).toInt(); -#else + QVariant v = ui->audioOutputCombo->currentData(); rxSetup.port = v.value(); -#endif + } ui->audioOutputCombo->blockSignals(false); @@ -1654,14 +1551,10 @@ void wfmain::loadSettings() int audioInputIndex = ui->audioInputCombo->findText(txSetup.name); if (audioInputIndex != -1) { ui->audioInputCombo->setCurrentIndex(audioInputIndex); -#if defined(RTAUDIO) - txSetup.port = ui->audioInputCombo->itemData(audioInputIndex).toInt(); -#elif defined(PORTAUDIO) - txSetup.port = ui->audioInputCombo->itemData(audioInputIndex).toInt(); -#else + QVariant v = ui->audioInputCombo->currentData(); txSetup.port = v.value(); -#endif + } ui->audioInputCombo->blockSignals(false); @@ -1717,7 +1610,7 @@ void wfmain::loadSettings() RIGCONFIG* rigTemp = new RIGCONFIG(); rigTemp->rxAudioSetup.isinput = true; - rigTemp->txAudioSetup.isinput = true; + rigTemp->txAudioSetup.isinput = false; rigTemp->rxAudioSetup.localAFgain = 255; rigTemp->txAudioSetup.localAFgain = 255; rigTemp->rxAudioSetup.resampleQuality = 4; @@ -1742,14 +1635,10 @@ void wfmain::loadSettings() int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(rigTemp->rxAudioSetup.name); if (serverAudioInputIndex != -1) { ui->serverRXAudioInputCombo->setCurrentIndex(serverAudioInputIndex); -#if defined(RTAUDIO) - serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(serverAudioInputIndex).toInt(); -#elif defined(PORTAUDIO) - serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(serverAudioInputIndex).toInt(); -#else + QVariant v = ui->serverRXAudioInputCombo->currentData(); rigTemp->rxAudioSetup.port = v.value(); -#endif + } ui->serverRXAudioInputCombo->blockSignals(false); @@ -1759,17 +1648,12 @@ void wfmain::loadSettings() int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(rigTemp->txAudioSetup.name); if (serverAudioOutputIndex != -1) { ui->serverTXAudioOutputCombo->setCurrentIndex(serverAudioOutputIndex); -#if defined(RTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(serverAudioOutputIndex).toInt(); -#elif defined(PORTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(serverAudioOutputIndex).toInt(); -#else + QVariant v = ui->serverTXAudioOutputCombo->currentData(); rigTemp->txAudioSetup.port = v.value(); -#endif + } ui->serverTXAudioOutputCombo->blockSignals(false); - serverConfig.rigs.append(rigTemp); int row = 0; @@ -1910,30 +1794,25 @@ void wfmain::on_serverAudioPortText_textChanged(QString text) void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) { -#if defined(RTAUDIO) - serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - serverRxSetup.port = ui->serverRXAudioInputCombo->itemData(value).toInt(); -#else - QVariant v = ui->serverRXAudioInputCombo->itemData(value); - serverRxSetup.port = v.value(); -#endif - serverRxSetup.name = ui->serverRXAudioInputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio input to:" << serverRxSetup.name; + if (serverConfig.rigs.size() > 0) + { + QVariant v = ui->serverRXAudioInputCombo->itemData(value); + serverConfig.rigs.first()->rxAudioSetup.port = v.value(); + + serverConfig.rigs.first()->rxAudioSetup.name = ui->serverRXAudioInputCombo->itemText(value); + qDebug(logGui()) << "Changed default server audio input to:" << serverConfig.rigs.first()->rxAudioSetup.name; + } } void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) { -#if defined(RTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - serverTxSetup.port = ui->serverTXAudioOutputCombo->itemData(value).toInt(); -#else - QVariant v = ui->serverTXAudioOutputCombo->itemData(value); - serverTxSetup.port = v.value(); -#endif - serverTxSetup.name = ui->serverTXAudioOutputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio output to:" << serverTxSetup.name; + if (serverConfig.rigs.size() > 0) { + QVariant v = ui->serverTXAudioOutputCombo->itemData(value); + serverConfig.rigs.first()->txAudioSetup.port = v.value(); + + serverConfig.rigs.first()->txAudioSetup.name = ui->serverTXAudioOutputCombo->itemText(value); + qDebug(logGui()) << "Changed default server audio output to:" << serverConfig.rigs.first()->txAudioSetup.name; + } } void wfmain::on_serverUsersTable_cellChanged(int row, int column) @@ -2097,8 +1976,8 @@ void wfmain::saveSettings() settings->setValue("ServerControlPort", serverConfig.controlPort); settings->setValue("ServerCivPort", serverConfig.civPort); settings->setValue("ServerAudioPort", serverConfig.audioPort); - settings->setValue("ServerAudioOutput", serverTxSetup.name); - settings->setValue("ServerAudioInput", serverRxSetup.name); + settings->setValue("ServerAudioOutput", serverConfig.rigs.first()->txAudioSetup.name); + settings->setValue("ServerAudioInput", serverConfig.rigs.first()->rxAudioSetup.name); /* Remove old format users*/ int numUsers = settings->value("ServerNumUsers", 0).toInt(); @@ -3343,6 +3222,12 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) this->rigCaps = rigCaps; rigName->setText(rigCaps.modelName); + if (serverConfig.enabled) { + serverConfig.rigs.first()->modelName = rigCaps.modelName; + serverConfig.rigs.first()->rigName = rigCaps.modelName; + serverConfig.rigs.first()->civAddr = rigCaps.civ; + serverConfig.rigs.first()->baudRate = rigCaps.baudRate; + } setWindowTitle(rigCaps.modelName); this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true. haveRigCaps = true; @@ -4734,28 +4619,18 @@ void wfmain::on_passwordTxt_textChanged(QString text) void wfmain::on_audioOutputCombo_currentIndexChanged(int value) { -#if defined(RTAUDIO) - rxSetup.port = ui->audioOutputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - rxSetup.port = ui->audioOutputCombo->itemData(value).toInt(); -#else QVariant v = ui->audioOutputCombo->itemData(value); rxSetup.port = v.value(); -#endif + rxSetup.name = ui->audioOutputCombo->itemText(value); qDebug(logGui()) << "Changed default audio output to:" << rxSetup.name; } void wfmain::on_audioInputCombo_currentIndexChanged(int value) { -#if defined(RTAUDIO) - txSetup.port = ui->audioInputCombo->itemData(value).toInt(); -#elif defined(PORTAUDIO) - txSetup.port = ui->audioInputCombo->itemData(value).toInt(); -#else QVariant v = ui->audioInputCombo->itemData(value); txSetup.port = v.value(); -#endif + txSetup.name = ui->audioInputCombo->itemText(value); qDebug(logGui()) << "Changed default audio input to:" << txSetup.name; } diff --git a/wfmain.h b/wfmain.h index fa1b23d..474c165 100644 --- a/wfmain.h +++ b/wfmain.h @@ -780,8 +780,6 @@ private: audioSetup rxSetup; audioSetup txSetup; - audioSetup serverRxSetup; - audioSetup serverTxSetup; colors defaultColors; From 6716a57d5b6f1bea1c6709e6a20c6489745af4f8 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 20 Apr 2022 13:43:45 +0100 Subject: [PATCH 179/323] Fix wfserver --- servermain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servermain.cpp b/servermain.cpp index cafa08d..a63cbd1 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -375,7 +375,7 @@ void servermain::setServerToPrefs() udp = Q_NULLPTR; } - udp = new udpServer(serverConfig); + udp = new udpServer(&serverConfig); serverThread = new QThread(this); From b510b70f041ca43f3259f793518b58686e440961 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 26 Apr 2022 08:40:58 +0100 Subject: [PATCH 180/323] Add WFVIEW response type back in --- udpserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index fa6b85d..8c21178 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1121,8 +1121,8 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed) c->retransmitTimer->stop(); } else { - //strcpy(p.connection, "WFVIEW"); - strcpy(p.connection, "FTTH"); + strcpy(p.connection, "WFVIEW"); + //strcpy(p.connection, "FTTH"); } SEQBUFENTRY s; From 382f0951e58643612dfff6af6de906b815f58dd3 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 28 Apr 2022 10:02:51 +0100 Subject: [PATCH 181/323] Try to support all possible native audio formats --- audiohandler.cpp | 47 +++++++++++++++++++++++++++++++++++++---------- wfview.vcxproj | 8 ++++---- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 8daa19b..5830559 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -79,12 +79,9 @@ bool audioHandler::init(audioSetup setupIn) } if (setup.codec == 0x01 || setup.codec == 0x20) { - /* Althought uLaw is 8bit unsigned, it is 16bit signed once decoded*/ setup.ulaw = true; - setup.format.setSampleSize(16); - setup.format.setSampleType(QAudioFormat::SignedInt); - } + if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20 || setup.codec == 0x80) { setup.format.setChannelCount(2); } @@ -134,8 +131,12 @@ bool audioHandler::init(audioSetup setupIn) } if (format.sampleSize() == 24) { - // We can't convert this easily - format.setSampleSize(16); + // We can't convert this easily so use 32 bit instead. + format.setSampleSize(32); + if (!setup.port.isFormatSupported(format)) { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "24 bit requested and 32 bit audio not supported, try 16 bit instead"; + format.setSampleSize(16); + } } qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Internal: sample rate" << format.sampleRate() << "channel count" << format.channelCount(); @@ -264,6 +265,9 @@ void audioHandler::incomingAudio(audioPacket inPacket) livePacket.data.clear(); livePacket.data = outPacket; // Replace incoming data with converted. // Buffer now contains 16bit signed samples. + setup.format.setSampleSize(16); + setup.format.setSampleType(QAudioFormat::SignedInt); + } @@ -310,11 +314,21 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (!livePacket.data.isEmpty()) { Eigen::VectorXf samplesF; - if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) + if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) + { + VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); + } + else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) { VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); samplesF = samplesI.cast() / float(std::numeric_limits::max()); } + else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) + { + VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) { VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); @@ -339,14 +353,22 @@ void audioHandler::incomingAudio(audioPacket inPacket) // Set the volume samplesF *= volume; - // Convert mono to stereo if required - if (setup.format.channelCount() == 1) { + + if (setup.format.channelCount() == 2 && format.channelCount() == 1) { + // If we need to drop one of the audio channels, do it now + Eigen::VectorXf samplesTemp(samplesF.size() / 2); + samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); + samplesF = samplesTemp; + } + else if (setup.format.channelCount() == 1 && format.channelCount() == 2) { + // Convert mono to stereo if required Eigen::VectorXf samplesTemp(samplesF.size() * 2); Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; samplesF = samplesTemp; } + // We now have format.channelCount() (native) channels of audio. if (resampleRatio != 1.0) { @@ -378,6 +400,12 @@ void audioHandler::incomingAudio(audioPacket inPacket) VectorXuint8 samplesI = samplesITemp.cast(); livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); } + if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint8 samplesI = samplesITemp.cast(); + livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); + } if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) { Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); @@ -469,7 +497,6 @@ void audioHandler::getNextAudioChunk() // Set the max amplitude found in the vector if (samplesF.size() > 0) { amplitude = samplesF.array().abs().maxCoeff(); - // Channel count should now match the device that audio is going to (rig) if (resampleRatio != 1.0) { diff --git a/wfview.vcxproj b/wfview.vcxproj index 5c2bc26..5a843a9 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="c1f9358";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="b510b70";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -85,7 +85,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"c1f9358\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"b510b70\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -99,7 +99,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="c1f9358";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="b510b70";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -124,7 +124,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"c1f9358\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"b510b70\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h From a9c21871b2c4ea4be2199abe9cf09494e9f9b95c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 28 Apr 2022 10:05:51 +0100 Subject: [PATCH 182/323] Update audiohandler.cpp --- audiohandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 5830559..e63f1a0 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -316,7 +316,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) Eigen::VectorXf samplesF; if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) { - VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); + VectorXint32 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); samplesF = samplesI.cast() / float(std::numeric_limits::max()); } else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) @@ -326,7 +326,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) } else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) { - VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); + VectorXint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); samplesF = samplesI.cast() / float(std::numeric_limits::max());; } else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) From ff17a74df8954e3cfac42c1e8c37990573542a45 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 28 Apr 2022 10:32:55 +0100 Subject: [PATCH 183/323] Make missing packets qInfo instead of qDebug --- udphandler.cpp | 8 ++++---- udpserver.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index f8a5904..a4fe408 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1377,7 +1377,7 @@ void udpBase::dataReceived(QByteArray r) QMap::iterator s = rxMissing.find(in->seq); if (s != rxMissing.end()) { - qDebug(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << hex << in->seq; + qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << hex << in->seq; s = rxMissing.erase(s); } @@ -1403,7 +1403,7 @@ void udpBase::sendRetransmitRequest() return; } else if (rxMissing.size() > MAX_MISSING) { - qDebug(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers"; + qInfo(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers"; missingMutex.lock(); rxMissing.clear(); missingMutex.unlock(); @@ -1440,14 +1440,14 @@ void udpBase::sendRetransmitRequest() if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. { p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); - qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq; + qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq; udpMutex.lock(); udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); udpMutex.unlock(); } else { - qDebug(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':'); + qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':'); missingMutex.lock(); missingSeqs.insert(0, p.packet, sizeof(p.packet)); missingMutex.unlock(); diff --git a/udpserver.cpp b/udpserver.cpp index 8c21178..73f8c9c 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1696,7 +1696,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) return; } else if (c->rxMissing.size() > MAX_MISSING) { - qDebug(logUdp()) << "Too many missing packets," << c->rxMissing.size() << "flushing all buffers"; + qInfo(logUdp()) << "Too many missing packets," << c->rxMissing.size() << "flushing all buffers"; c->rxMutex.lock(); c->rxSeqBuf.clear(); c->rxMutex.unlock(); @@ -1728,7 +1728,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) else { // We have tried 4 times to request this packet, time to give up! - qDebug(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; + qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; it = c->rxMissing.erase(it); } } @@ -1745,7 +1745,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. { p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); - qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq; + qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq; if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -1759,7 +1759,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) } else { - qDebug(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(); + qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(); missingSeqs.insert(0, p.packet, sizeof(p.packet)); From d0df7004ac0e0dde841db7cbdbe8f3b579eea119 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 28 Apr 2022 10:46:47 +0100 Subject: [PATCH 184/323] Improve hex encoding --- udphandler.cpp | 18 ++++++++++-------- udpserver.cpp | 25 ++++++++++++++----------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index a4fe408..1b5907f 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -446,7 +446,7 @@ void udpHandler::dataReceived() { qInfo(logUdp()) << this->metaObject()->className() << "Received radio capabilities, Name:" << radio.name << " Audio:" << - radio.audio << "CIV:" << hex << (unsigned char)radio.civ << + radio.audio << "CIV:" << QString("0x%1").arg((unsigned char)radio.civ,0, 16) << "MAC:" << radio.macaddress[0] << ":" << radio.macaddress[1] << ":" << radio.macaddress[2] << @@ -1208,7 +1208,7 @@ void udpBase::dataReceived(QByteArray r) // Found matching entry? // Send "untracked" as it has already been sent once. // Don't constantly retransmit the same packet, give-up eventually - qDebug(logUdp()) << this->metaObject()->className() << ": Sending retransmit of " << hex << match->seqNum; + qDebug(logUdp()) << this->metaObject()->className() << ": Sending retransmit of " << QString("0x%1").arg(match->seqNum,0,16); match->retransmitCount++; udpMutex.lock(); udp->writeDatagram(match->data, radioIP, port); @@ -1293,14 +1293,14 @@ void udpBase::dataReceived(QByteArray r) quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; QMap::iterator match = txSeqBuf.find(seq); if (match == txSeqBuf.end()) { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << hex << seq << " not found"; + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(seq,0,16) << " not found"; // Just send idle packet. sendControl(false, 0, seq); } else { // Found matching entry? // Send "untracked" as it has already been sent once. - qDebug(logUdp()) << this->metaObject()->className() << ": Remote has requested retransmit of " << hex << match->seqNum; + qDebug(logUdp()) << this->metaObject()->className() << ": Remote has requested retransmit of " << QString("0x%1").arg(match->seqNum,0,16); match->retransmitCount++; udpMutex.lock(); udp->writeDatagram(match->data, radioIP, port); @@ -1325,7 +1325,8 @@ void udpBase::dataReceived(QByteArray r) { if (in->seq < rxSeqBuf.firstKey() || in->seq - rxSeqBuf.lastKey() > MAX_MISSING) { - qInfo(logUdp()) << this->metaObject()->className() << "Large seq number gap detected, previous highest: " << hex << rxSeqBuf.lastKey() << " current: " << hex << in->seq; + qInfo(logUdp()) << this->metaObject()->className() << "Large seq number gap detected, previous highest: " << + QString("0x%1").arg(rxSeqBuf.lastKey(),0,16) << " current: " << QString("0x%1").arg(in->seq,0,16); //seqPrefix++; // Looks like it has rolled over so clear buffer and start again. rxSeqBuf.clear(); @@ -1343,7 +1344,8 @@ void udpBase::dataReceived(QByteArray r) // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. if (in->seq > rxSeqBuf.lastKey() + 1) { - qInfo(logUdp()) << this->metaObject()->className() << "1 or more missing packets detected, previous: " << hex << rxSeqBuf.lastKey() << " current: " << hex << in->seq; + qInfo(logUdp()) << this->metaObject()->className() << "1 or more missing packets detected, previous: " << + QString("0x%1").arg(rxSeqBuf.lastKey(),0,16) << " current: " << QString("0x%1").arg(in->seq,0,16); // We are likely missing packets then! missingMutex.lock(); //int missCounter = 0; @@ -1377,7 +1379,7 @@ void udpBase::dataReceived(QByteArray r) QMap::iterator s = rxMissing.find(in->seq); if (s != rxMissing.end()) { - qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << hex << in->seq; + qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << QString("0x%1").arg(in->seq,0,16); s = rxMissing.erase(s); } @@ -1440,7 +1442,7 @@ void udpBase::sendRetransmitRequest() if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. { p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); - qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq; + qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq,0,16); udpMutex.lock(); udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); udpMutex.unlock(); diff --git a/udpserver.cpp b/udpserver.cpp index 73f8c9c..84792a3 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -573,14 +573,15 @@ void udpServer::civReceived() if (current->civId == 0 && r.length() > lastFE + 2 && (quint8)r[lastFE+2] != 0xE1 && (quint8)r[lastFE + 2] > (quint8)0xdf && (quint8)r[lastFE + 2] < (quint8)0xef) { // This is (should be) the remotes CIV id. current->civId = (quint8)r[lastFE + 2]; - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected remote CI-V:" << hex << current->civId; + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected remote CI-V:" << QString("0x%1").arg(current->civId,0,16); } else if (current->civId != 0 && r.length() > lastFE + 2 && (quint8)r[lastFE+2] != 0xE1 && (quint8)r[lastFE + 2] != current->civId) { current->civId = (quint8)r[lastFE + 2]; - qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId; qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId; + qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << QString("0x%1").arg(current->civId,0,16); + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << QString("0x%1").arg(current->civId,0,16); } else if (r.length() > lastFE+2 && (quint8)r[lastFE+2] != 0xE1) { - qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected invalid remote CI-V:" << hex << (quint8)r[lastFE+2]; + qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected invalid remote CI-V:" << QString("0x%1").arg((quint8)r[lastFE+2],0,16); } for (RIGCONFIG* radio : config->rigs) { @@ -796,13 +797,13 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) else if (in->type == 0x01) { // Single packet request - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'retransmit' request for " << in->seq; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'retransmit' request for " << QString("0x%1").arg(in->seq,0,16); QMap::iterator match = current->txSeqBuf.find(in->seq); if (match != current->txSeqBuf.end() && match->retransmitCount < 5) { // Found matching entry? // Don't constantly retransmit the same packet, give-up eventually - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending retransmit of " << hex << match->seqNum; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending retransmit of " << QString("0x%1").arg(match->seqNum,0,16); match->retransmitCount++; if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -839,7 +840,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) return s.seqNum == cs; }); if (match == current->txSeqBuf.end()) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested packet " << hex << seq << " not found"; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested packet " << QString("0x%1").arg(seq,0,16) << " not found"; // Just send idle packet. sendControl(current, 0, in->seq); } @@ -847,7 +848,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) { // Found matching entry? // Send "untracked" as it has already been sent once. - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending retransmit of " << hex << match->seqNum; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending retransmit of " << QString("0x%1").arg(match->seqNum,0,16); match->retransmitCount++; if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -884,7 +885,8 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) if (in->seq < current->rxSeqBuf.firstKey()) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): ******* seq number may have rolled over ****** previous highest: " << hex << current->rxSeqBuf.lastKey() << " current: " << hex << in->seq; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): ******* seq number may have rolled over ****** previous highest: " << + QString("0x%1").arg(current->rxSeqBuf.lastKey(),0,16) << " current: " << QString("0x%1").arg(in->seq,0,16); // Looks like it has rolled over so clear buffer and start again. if (current->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -962,7 +964,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) QMap::iterator s = current->rxMissing.find(in->seq); if (s != current->rxMissing.end()) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Missing SEQ has been received! " << hex << in->seq; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Missing SEQ has been received! " << QString("0x%1").arg(in->seq,0,16); s = current->rxMissing.erase(s); } current->missMutex.unlock(); @@ -1606,7 +1608,8 @@ void udpServer::dataForServer(QByteArray d) } } else { - qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE + 1] << ":" << hex << (quint8)d[lastFE + 2]; + qInfo(logUdpServer()) << "Got data for different ID" << + QString("0x%1").arg((quint8)d[lastFE + 1],0,16) << ":" << QString("0x%1").arg((quint8)d[lastFE + 2],0,16); } } return; @@ -1745,7 +1748,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. { p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); - qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq; + qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq,0,16); if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { From 5b50127d0bbfd78f98df59614d65267b60944ebe Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 28 Apr 2022 10:52:46 +0100 Subject: [PATCH 185/323] Replace deprecated hex modifier --- pttyhandler.cpp | 4 ++-- rigcommander.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pttyhandler.cpp b/pttyhandler.cpp index 1249537..0639bc0 100644 --- a/pttyhandler.cpp +++ b/pttyhandler.cpp @@ -214,12 +214,12 @@ void pttyHandler::receiveDataIn(int fd) { if (civId == 0 && inPortData.length() > lastFE + 2 && (quint8)inPortData[lastFE + 2] > (quint8)0xdf && (quint8)inPortData[lastFE + 2] < (quint8)0xef) { // This is (should be) the remotes CIV id. civId = (quint8)inPortData[lastFE + 2]; - qInfo(logSerial()) << "pty detected remote CI-V:" << hex << civId; + qInfo(logSerial()) << "pty detected remote CI-V:" << QString("0x%1").arg(civId,0,16); } else if (civId != 0 && inPortData.length() > lastFE + 2 && (quint8)inPortData[lastFE + 2] != civId) { civId = (quint8)inPortData[lastFE + 2]; - qInfo(logSerial()) << "pty remote CI-V changed:" << hex << (quint8)civId; + qInfo(logSerial()) << "pty remote CI-V changed:" << QString("0x%1").arg((quint8)civId,0,16); } // filter C-IV transceive command before forwarding on. if (inPortData.contains(rigCaps.transceiveCommand)) diff --git a/rigcommander.cpp b/rigcommander.cpp index 45cdb52..282ca82 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -50,7 +50,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu civAddr = rigCivAddr; // address of the radio. Decimal is 148. usingNativeLAN = false; - //qInfo(logRig()) << "Opening connection to Rig:" << hex << (unsigned char)rigCivAddr << "on serial port" << rigSerialPort << "at baud rate" << rigBaudRate; + //qInfo(logRig()) << "Opening connection to Rig:" << QString("0x%1").arg((unsigned char)rigCivAddr,0,16) << "on serial port" << rigSerialPort << "at baud rate" << rigBaudRate; // --- setup(); // --- @@ -3675,7 +3675,7 @@ void rigCommander::determineRigCaps() payloadPrefix.append(civAddr); payloadPrefix.append((char)compCivAddr); // if there is a compile-time error, remove the following line, the "hex" part is the issue: - qInfo(logRig()) << "Using incomingCIVAddr: (int): " << this->civAddr << " hex: " << hex << this->civAddr; + qInfo(logRig()) << "Using incomingCIVAddr: (int): " << this->civAddr << " hex: " << QString("0x%1").arg(this->civAddr,0,16); emit discoveredRigID(rigCaps); } else { if(!foundRig) From 2525641e76d8ea027e4b221f3746496fa9a0bcc9 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 28 Apr 2022 12:32:40 +0100 Subject: [PATCH 186/323] Update audiohandler.cpp --- audiohandler.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index e63f1a0..2a9e3b8 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -236,6 +236,7 @@ void audioHandler::stop() if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) { // Stop audio output + audioTimer->stop(); audioInput->stop(); } audioDevice = Q_NULLPTR; @@ -316,22 +317,22 @@ void audioHandler::incomingAudio(audioPacket inPacket) Eigen::VectorXf samplesF; if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) { - VectorXint32 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); samplesF = samplesI.cast() / float(std::numeric_limits::max()); } else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) { - VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); samplesF = samplesI.cast() / float(std::numeric_limits::max()); } else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) { - VectorXint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); samplesF = samplesI.cast() / float(std::numeric_limits::max());; } else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) { - VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); samplesF = samplesI.cast() / float(std::numeric_limits::max());; } else if (setup.format.sampleType() == QAudioFormat::Float) { @@ -466,22 +467,22 @@ void audioHandler::getNextAudioChunk() Eigen::VectorXf samplesF; if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32) { - VectorXint32 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); samplesF = samplesI.cast() / float(std::numeric_limits::max()); } else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) { - VectorXint16 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); samplesF = samplesI.cast() / float(std::numeric_limits::max()); } else if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) { - VectorXuint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); samplesF = samplesI.cast() / float(std::numeric_limits::max());; } else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8) { - VectorXint8 samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); samplesF = samplesI.cast() / float(std::numeric_limits::max());; } else if (format.sampleType() == QAudioFormat::Float) From bb6c615b4ccfc126e26f6de1eac345e3ccc51557 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 11:22:05 +0100 Subject: [PATCH 187/323] Add some more debugging when audio is delayed --- audiohandler.cpp | 65 +++++++++++++++++++++++++++++++++++++++--------- audiohandler.h | 17 +++++++------ udphandler.cpp | 2 +- 3 files changed, 64 insertions(+), 20 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 2a9e3b8..45cc2d4 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -212,9 +212,18 @@ void audioHandler::start() audioTimer->start(setup.blockSize); } else { - // Buffer size must be set before audio is started. - audioOutput->setBufferSize(getAudioSize(setup.latency, format)); + /* OK I don't understand what is happening here? + On Windows, I set the buffer size and when the stream is started, the buffer size is multiplied by 10? + this doesn't happen on Linux/ + We want the buffer sizes to be slightly more than the setup.latency value + */ +#ifdef Q_OS_WIN + audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 100)); +#else + audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 1000)); +#endif audioDevice = audioOutput->start(); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "bufferSize needed" << format.bytesForDuration((quint32)setup.latency * 1000) << "got" << audioOutput->bufferSize(); connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); } if (!audioDevice) { @@ -244,6 +253,13 @@ void audioHandler::stop() void audioHandler::setVolume(unsigned char volume) { + /*float volumeLevelLinear = float(0.5); //cut amplitude in half + float volumeLevelDb = float(10 * (qLn(double(volumeLevelLinear)) / qLn(10))); + float volumeLinear = (volume / float(100)); + this->volume = volumeLinear * float(qPow(10, (qreal(volumeLevelDb)) / 20)); + + this->volume = qMin(this->volume, float(1)); + */ this->volume = audiopot[volume]; qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } @@ -251,7 +267,12 @@ void audioHandler::setVolume(unsigned char volume) void audioHandler::incomingAudio(audioPacket inPacket) { - + + + if (lastReceived.msecsTo(QTime::currentTime()) > 30) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; + } + lastReceived = QTime::currentTime(); audioPacket livePacket = inPacket; // Process uLaw. if (setup.ulaw) @@ -427,7 +448,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); } - currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + getAudioDuration(audioOutput->bufferSize()-audioOutput->bytesFree(),format); if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); } @@ -437,8 +457,12 @@ void audioHandler::incomingAudio(audioPacket inPacket) incomingAudio(inPacket); // Call myself again to run the packet a second time (FEC) } + currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); lastSentSeq = inPacket.seq; } + else { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Received audio packet is empty?"; + } emit haveLevels(getAmplitude(), setup.latency, currentLatency,isUnderrun); @@ -454,14 +478,18 @@ void audioHandler::getNextAudioChunk() return; } + audioPacket livePacket; livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) { + QTime startProcessing = QTime::currentTime(); livePacket.data.clear(); livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); + //qDebug(logAudio()) << "Got bytes " << format.bytesForDuration(setup.blockSize * 1000); + if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; @@ -618,6 +646,11 @@ void audioHandler::getNextAudioChunk() livePacket.data = outPacket; // Copy output packet back to input buffer. } emit haveAudioData(livePacket); + if (lastReceived.msecsTo(QTime::currentTime()) > 30) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" <bufferSize(), format) << "ms"; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << format.durationForBytes(audioOutput->bufferSize()/1000) << "ms"; } @@ -662,18 +695,22 @@ void audioHandler::stateChanged(QAudio::State state) { case QAudio::IdleState: { - isUnderrun = true; - if (underTimer->isActive()) { - underTimer->stop(); + if (!setup.isinput) + { + qDebug(logAudio()) << "Output Underrun detected" << "Buffer size" << audioOutput->bufferSize() << "Bytes free" << audioOutput->bytesFree(); + if (isUnderrun && audioOutput->bytesFree() > audioOutput->bufferSize()/2) { + audioOutput->suspend(); + } } + isUnderrun = true; + if (!underTimer->isActive()) { + underTimer->start(setup.latency/2); + } + break; } case QAudio::ActiveState: { - //qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio started!"; - if (!underTimer->isActive()) { - underTimer->start(500); - } break; } case QAudio::SuspendedState: @@ -694,4 +731,8 @@ void audioHandler::clearUnderrun() { isUnderrun = false; underTimer->stop(); + if (!setup.isinput) { + qDebug(logAudio()) << "clearUnderrun() " << "Buffer size" << audioOutput->bufferSize() << "Bytes free" << audioOutput->bytesFree(); + audioOutput->resume(); + } } \ No newline at end of file diff --git a/audiohandler.h b/audiohandler.h index 128b270..7a09af7 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -12,6 +12,7 @@ #include #include #include +#include /* QT Audio Headers */ #include @@ -111,7 +112,7 @@ private: bool isInitialized=false; bool isReady = false; bool audioBuffered = false; - + QTime lastReceived; QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; QIODevice* audioDevice=Q_NULLPTR; @@ -137,7 +138,7 @@ private: audioPacket tempBuf; quint16 currentLatency; float amplitude; - qreal volume=1.0; + float volume=1.0; audioSetup setup; @@ -149,6 +150,12 @@ private: // Various audio handling functions declared inline +typedef Eigen::Matrix VectorXuint8; +typedef Eigen::Matrix VectorXint8; +typedef Eigen::Matrix VectorXint16; +typedef Eigen::Matrix VectorXint32; + +/* static inline qint64 getAudioSize(qint64 timeInMs, const QAudioFormat& format) { #ifdef Q_OS_LINUX @@ -169,11 +176,6 @@ static inline qint64 getAudioDuration(qint64 bytes, const QAudioFormat& format) return qint64(qFloor(bytes / (format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000)))); } -typedef Eigen::Matrix VectorXuint8; -typedef Eigen::Matrix VectorXint8; -typedef Eigen::Matrix VectorXint16; -typedef Eigen::Matrix VectorXint32; - static inline QByteArray samplesToInt(const QByteArray& data, const QAudioFormat& supported_format) { QByteArray input = data; @@ -347,4 +349,5 @@ static inline QByteArray samplesToFloat(const QByteArray& data, const QAudioForm return QByteArray(); } +*/ #endif // AUDIOHANDLER_H diff --git a/udphandler.cpp b/udphandler.cpp index 1b5907f..a036a98 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -202,7 +202,7 @@ void udpHandler::dataReceived() } QString tempLatency; - if (status.rxLatency > status.rxCurrentLatency && !status.rxUnderrun) + if (status.rxLatency * 1.3 > (status.rxCurrentLatency) && !status.rxUnderrun) { tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); } From 7130f71335487e38bc98a6dd905ac6dff0cd0f55 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 11:29:34 +0100 Subject: [PATCH 188/323] Update audiohandler.cpp --- audiohandler.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 45cc2d4..90b6083 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -269,10 +269,8 @@ void audioHandler::incomingAudio(audioPacket inPacket) { - if (lastReceived.msecsTo(QTime::currentTime()) > 30) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; - } - lastReceived = QTime::currentTime(); + QTime startProcessing = QTime::currentTime(); + audioPacket livePacket = inPacket; // Process uLaw. if (setup.ulaw) @@ -450,6 +448,11 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); + if (lastReceived.msecsTo(QTime::currentTime()) > 30) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); + } + lastReceived = QTime::currentTime(); + } if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq; @@ -647,7 +650,7 @@ void audioHandler::getNextAudioChunk() } emit haveAudioData(livePacket); if (lastReceived.msecsTo(QTime::currentTime()) > 30) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" < Date: Mon, 2 May 2022 11:32:28 +0100 Subject: [PATCH 189/323] Update audiohandler.cpp --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 90b6083..e5aceec 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -209,7 +209,7 @@ void audioHandler::start() audioDevice = audioInput->start(); connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); //connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); - audioTimer->start(setup.blockSize); + audioTimer->start(setup.blockSize/2); } else { /* OK I don't understand what is happening here? From 26ea3bc3dcd496e561a32f2f3c8aa05e343794c6 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 11:47:44 +0100 Subject: [PATCH 190/323] Reduce audio polling time --- audiohandler.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index e5aceec..46219f4 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -209,7 +209,7 @@ void audioHandler::start() audioDevice = audioInput->start(); connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); //connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); - audioTimer->start(setup.blockSize/2); + audioTimer->start(setup.blockSize/4); } else { /* OK I don't understand what is happening here? @@ -448,7 +448,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); - if (lastReceived.msecsTo(QTime::currentTime()) > 30) { + if (lastReceived.msecsTo(QTime::currentTime()) > 50) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); } lastReceived = QTime::currentTime(); @@ -477,17 +477,12 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)) { - return; - } - - - audioPacket livePacket; - livePacket.time= QTime::currentTime(); - livePacket.sent = 0; - memcpy(&livePacket.guid, setup.guid, GUIDLEN); - while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) { + while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { QTime startProcessing = QTime::currentTime(); + audioPacket livePacket; + livePacket.time = QTime::currentTime(); + livePacket.sent = 0; + memcpy(&livePacket.guid, setup.guid, GUIDLEN); livePacket.data.clear(); livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); @@ -649,7 +644,7 @@ void audioHandler::getNextAudioChunk() livePacket.data = outPacket; // Copy output packet back to input buffer. } emit haveAudioData(livePacket); - if (lastReceived.msecsTo(QTime::currentTime()) > 30) { + if (lastReceived.msecsTo(QTime::currentTime()) > 50) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); } lastReceived = QTime::currentTime(); From 1ba97134081b11b474ffe7c611f0dac500fc3dfb Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 11:51:12 +0100 Subject: [PATCH 191/323] Revert "Reduce audio polling time" This reverts commit 26ea3bc3dcd496e561a32f2f3c8aa05e343794c6. --- audiohandler.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 46219f4..e5aceec 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -209,7 +209,7 @@ void audioHandler::start() audioDevice = audioInput->start(); connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); //connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); - audioTimer->start(setup.blockSize/4); + audioTimer->start(setup.blockSize/2); } else { /* OK I don't understand what is happening here? @@ -448,7 +448,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); - if (lastReceived.msecsTo(QTime::currentTime()) > 50) { + if (lastReceived.msecsTo(QTime::currentTime()) > 30) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); } lastReceived = QTime::currentTime(); @@ -477,12 +477,17 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { + if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)) { + return; + } + + + audioPacket livePacket; + livePacket.time= QTime::currentTime(); + livePacket.sent = 0; + memcpy(&livePacket.guid, setup.guid, GUIDLEN); + while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) { QTime startProcessing = QTime::currentTime(); - audioPacket livePacket; - livePacket.time = QTime::currentTime(); - livePacket.sent = 0; - memcpy(&livePacket.guid, setup.guid, GUIDLEN); livePacket.data.clear(); livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); @@ -644,7 +649,7 @@ void audioHandler::getNextAudioChunk() livePacket.data = outPacket; // Copy output packet back to input buffer. } emit haveAudioData(livePacket); - if (lastReceived.msecsTo(QTime::currentTime()) > 50) { + if (lastReceived.msecsTo(QTime::currentTime()) > 30) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); } lastReceived = QTime::currentTime(); From 8f3a8d38d4c8b7e1ea65f93ffed5fab0806a8a17 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 11:51:36 +0100 Subject: [PATCH 192/323] Revert "Update audiohandler.cpp" This reverts commit 60824c2055aecd89614cf9bdc8c9c8233c195ac4. --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index e5aceec..90b6083 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -209,7 +209,7 @@ void audioHandler::start() audioDevice = audioInput->start(); connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); //connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); - audioTimer->start(setup.blockSize/2); + audioTimer->start(setup.blockSize); } else { /* OK I don't understand what is happening here? From ab36165d9db4eb9b878b8cdc087ac9cee3f98717 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 11:52:09 +0100 Subject: [PATCH 193/323] Revert "Update audiohandler.cpp" This reverts commit 7130f71335487e38bc98a6dd905ac6dff0cd0f55. --- audiohandler.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 90b6083..45cc2d4 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -269,8 +269,10 @@ void audioHandler::incomingAudio(audioPacket inPacket) { - QTime startProcessing = QTime::currentTime(); - + if (lastReceived.msecsTo(QTime::currentTime()) > 30) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; + } + lastReceived = QTime::currentTime(); audioPacket livePacket = inPacket; // Process uLaw. if (setup.ulaw) @@ -448,11 +450,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); - if (lastReceived.msecsTo(QTime::currentTime()) > 30) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); - } - lastReceived = QTime::currentTime(); - } if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq; @@ -650,7 +647,7 @@ void audioHandler::getNextAudioChunk() } emit haveAudioData(livePacket); if (lastReceived.msecsTo(QTime::currentTime()) > 30) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" < Date: Mon, 2 May 2022 11:52:23 +0100 Subject: [PATCH 194/323] Revert "Add some more debugging when audio is delayed" This reverts commit bb6c615b4ccfc126e26f6de1eac345e3ccc51557. --- audiohandler.cpp | 63 +++++++++--------------------------------------- audiohandler.h | 17 ++++++------- udphandler.cpp | 2 +- 3 files changed, 19 insertions(+), 63 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 45cc2d4..2a9e3b8 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -212,18 +212,9 @@ void audioHandler::start() audioTimer->start(setup.blockSize); } else { - /* OK I don't understand what is happening here? - On Windows, I set the buffer size and when the stream is started, the buffer size is multiplied by 10? - this doesn't happen on Linux/ - We want the buffer sizes to be slightly more than the setup.latency value - */ -#ifdef Q_OS_WIN - audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 100)); -#else - audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 1000)); -#endif + // Buffer size must be set before audio is started. + audioOutput->setBufferSize(getAudioSize(setup.latency, format)); audioDevice = audioOutput->start(); - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "bufferSize needed" << format.bytesForDuration((quint32)setup.latency * 1000) << "got" << audioOutput->bufferSize(); connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); } if (!audioDevice) { @@ -253,13 +244,6 @@ void audioHandler::stop() void audioHandler::setVolume(unsigned char volume) { - /*float volumeLevelLinear = float(0.5); //cut amplitude in half - float volumeLevelDb = float(10 * (qLn(double(volumeLevelLinear)) / qLn(10))); - float volumeLinear = (volume / float(100)); - this->volume = volumeLinear * float(qPow(10, (qreal(volumeLevelDb)) / 20)); - - this->volume = qMin(this->volume, float(1)); - */ this->volume = audiopot[volume]; qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } @@ -267,12 +251,7 @@ void audioHandler::setVolume(unsigned char volume) void audioHandler::incomingAudio(audioPacket inPacket) { - - - if (lastReceived.msecsTo(QTime::currentTime()) > 30) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; - } - lastReceived = QTime::currentTime(); + audioPacket livePacket = inPacket; // Process uLaw. if (setup.ulaw) @@ -448,6 +427,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); } + currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + getAudioDuration(audioOutput->bufferSize()-audioOutput->bytesFree(),format); if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); } @@ -457,12 +437,8 @@ void audioHandler::incomingAudio(audioPacket inPacket) incomingAudio(inPacket); // Call myself again to run the packet a second time (FEC) } - currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); lastSentSeq = inPacket.seq; } - else { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Received audio packet is empty?"; - } emit haveLevels(getAmplitude(), setup.latency, currentLatency,isUnderrun); @@ -478,18 +454,14 @@ void audioHandler::getNextAudioChunk() return; } - audioPacket livePacket; livePacket.time= QTime::currentTime(); livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) { - QTime startProcessing = QTime::currentTime(); livePacket.data.clear(); livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); - //qDebug(logAudio()) << "Got bytes " << format.bytesForDuration(setup.blockSize * 1000); - if (livePacket.data.length() > 0) { Eigen::VectorXf samplesF; @@ -646,11 +618,6 @@ void audioHandler::getNextAudioChunk() livePacket.data = outPacket; // Copy output packet back to input buffer. } emit haveAudioData(livePacket); - if (lastReceived.msecsTo(QTime::currentTime()) > 30) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" <bufferSize()/1000) << "ms"; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << getAudioDuration(audioOutput->bufferSize(), format) << "ms"; } @@ -695,22 +662,18 @@ void audioHandler::stateChanged(QAudio::State state) { case QAudio::IdleState: { - if (!setup.isinput) - { - qDebug(logAudio()) << "Output Underrun detected" << "Buffer size" << audioOutput->bufferSize() << "Bytes free" << audioOutput->bytesFree(); - if (isUnderrun && audioOutput->bytesFree() > audioOutput->bufferSize()/2) { - audioOutput->suspend(); - } - } isUnderrun = true; - if (!underTimer->isActive()) { - underTimer->start(setup.latency/2); + if (underTimer->isActive()) { + underTimer->stop(); } - break; } case QAudio::ActiveState: { + //qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio started!"; + if (!underTimer->isActive()) { + underTimer->start(500); + } break; } case QAudio::SuspendedState: @@ -731,8 +694,4 @@ void audioHandler::clearUnderrun() { isUnderrun = false; underTimer->stop(); - if (!setup.isinput) { - qDebug(logAudio()) << "clearUnderrun() " << "Buffer size" << audioOutput->bufferSize() << "Bytes free" << audioOutput->bytesFree(); - audioOutput->resume(); - } } \ No newline at end of file diff --git a/audiohandler.h b/audiohandler.h index 7a09af7..128b270 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -12,7 +12,6 @@ #include #include #include -#include /* QT Audio Headers */ #include @@ -112,7 +111,7 @@ private: bool isInitialized=false; bool isReady = false; bool audioBuffered = false; - QTime lastReceived; + QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; QIODevice* audioDevice=Q_NULLPTR; @@ -138,7 +137,7 @@ private: audioPacket tempBuf; quint16 currentLatency; float amplitude; - float volume=1.0; + qreal volume=1.0; audioSetup setup; @@ -150,12 +149,6 @@ private: // Various audio handling functions declared inline -typedef Eigen::Matrix VectorXuint8; -typedef Eigen::Matrix VectorXint8; -typedef Eigen::Matrix VectorXint16; -typedef Eigen::Matrix VectorXint32; - -/* static inline qint64 getAudioSize(qint64 timeInMs, const QAudioFormat& format) { #ifdef Q_OS_LINUX @@ -176,6 +169,11 @@ static inline qint64 getAudioDuration(qint64 bytes, const QAudioFormat& format) return qint64(qFloor(bytes / (format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000)))); } +typedef Eigen::Matrix VectorXuint8; +typedef Eigen::Matrix VectorXint8; +typedef Eigen::Matrix VectorXint16; +typedef Eigen::Matrix VectorXint32; + static inline QByteArray samplesToInt(const QByteArray& data, const QAudioFormat& supported_format) { QByteArray input = data; @@ -349,5 +347,4 @@ static inline QByteArray samplesToFloat(const QByteArray& data, const QAudioForm return QByteArray(); } -*/ #endif // AUDIOHANDLER_H diff --git a/udphandler.cpp b/udphandler.cpp index a036a98..1b5907f 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -202,7 +202,7 @@ void udpHandler::dataReceived() } QString tempLatency; - if (status.rxLatency * 1.3 > (status.rxCurrentLatency) && !status.rxUnderrun) + if (status.rxLatency > status.rxCurrentLatency && !status.rxUnderrun) { tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); } From fb0d662b4037e79cbdae7551fc4a22eeccd4f836 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 11:56:40 +0100 Subject: [PATCH 195/323] Add debugging back after revert. --- audiohandler.cpp | 13 ++++++++++++- audiohandler.h | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 2a9e3b8..10a7d61 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -251,7 +251,8 @@ void audioHandler::setVolume(unsigned char volume) void audioHandler::incomingAudio(audioPacket inPacket) { - + QTime startProcessing = QTime::currentTime(); + audioPacket livePacket = inPacket; // Process uLaw. if (setup.ulaw) @@ -430,6 +431,10 @@ void audioHandler::incomingAudio(audioPacket inPacket) currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + getAudioDuration(audioOutput->bufferSize()-audioOutput->bytesFree(),format); if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); + if (lastReceived.msecsTo(QTime::currentTime()) > 50) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); + } + lastReceived = QTime::currentTime(); } if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq; @@ -448,6 +453,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) void audioHandler::getNextAudioChunk() { + tempBuf.data.append(audioDevice->readAll()); if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)) { @@ -459,6 +465,7 @@ void audioHandler::getNextAudioChunk() livePacket.sent = 0; memcpy(&livePacket.guid, setup.guid, GUIDLEN); while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) { + QTime startProcessing = QTime::currentTime(); livePacket.data.clear(); livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); @@ -618,6 +625,10 @@ void audioHandler::getNextAudioChunk() livePacket.data = outPacket; // Copy output packet back to input buffer. } emit haveAudioData(livePacket); + if (lastReceived.msecsTo(QTime::currentTime()) > 50) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); + } + //ret = livePacket.data; } } diff --git a/audiohandler.h b/audiohandler.h index 128b270..cf173ae 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -119,7 +119,7 @@ private: QAudioFormat format; QAudioDeviceInfo deviceInfo; SpeexResamplerState* resampler = Q_NULLPTR; - + QTime lastReceived; //r8b::CFixedBuffer* resampBufs; //r8b::CPtrKeeper* resamps; From 7bf2f54255838dbfc115170c80d23a16379ec708 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 12:55:58 +0100 Subject: [PATCH 196/323] Use notify instead of timer for audio input --- audiohandler.cpp | 24 ++++-- audiohandler.h | 193 ----------------------------------------------- 2 files changed, 16 insertions(+), 201 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 10a7d61..2e4aa39 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -145,10 +145,13 @@ bool audioHandler::init(audioSetup setupIn) if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); + audioInput->setNotifyInterval(setup.blockSize); qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Starting audio timer"; - audioTimer = new QTimer(); - audioTimer->setTimerType(Qt::PreciseTimer); - connect(audioTimer, &QTimer::timeout, this, &audioHandler::getNextAudioChunk); + + //audioTimer = new QTimer(); + //audioTimer->setTimerType(Qt::PreciseTimer); + //connect(audioTimer, &QTimer::timeout, this, &audioHandler::getNextAudioChunk); + connect(audioInput, SIGNAL(notify()), this, SLOT(getNextAudioChunk()),Qt::DirectConnection); connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); } @@ -209,11 +212,16 @@ void audioHandler::start() audioDevice = audioInput->start(); connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); //connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); - audioTimer->start(setup.blockSize); + //audioTimer->start(setup.blockSize); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Notify interval set to" << audioInput->notifyInterval() << "requested" << setup.blockSize; } else { // Buffer size must be set before audio is started. - audioOutput->setBufferSize(getAudioSize(setup.latency, format)); +#ifdef Q_OS_WIN + audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 100)); +#else + audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 1000)); +#endif audioDevice = audioOutput->start(); connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); } @@ -236,7 +244,7 @@ void audioHandler::stop() if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) { // Stop audio output - audioTimer->stop(); + //audioTimer->stop(); audioInput->stop(); } audioDevice = Q_NULLPTR; @@ -428,7 +436,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); } - currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + getAudioDuration(audioOutput->bufferSize()-audioOutput->bytesFree(),format); + currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize()-audioOutput->bytesFree())/1000); if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); if (lastReceived.msecsTo(QTime::currentTime()) > 50) { @@ -648,7 +656,7 @@ void audioHandler::changeLatency(const quint16 newSize) stop(); start(); } - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << getAudioDuration(audioOutput->bufferSize(), format) << "ms"; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << format.durationForBytes(audioOutput->bufferSize())/1000 << "ms"; } diff --git a/audiohandler.h b/audiohandler.h index cf173ae..395b126 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -149,202 +149,9 @@ private: // Various audio handling functions declared inline -static inline qint64 getAudioSize(qint64 timeInMs, const QAudioFormat& format) -{ -#ifdef Q_OS_LINUX - qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000) * timeInMs)); -#else - qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(10000) * timeInMs)); -#endif - - - if (value % (format.channelCount() * (format.sampleSize() / 8)) != 0) - value += (format.channelCount() * (format.sampleSize() / 8) - value % (format.channelCount() * (format.sampleSize() / 8))); - - return value; -} - -static inline qint64 getAudioDuration(qint64 bytes, const QAudioFormat& format) -{ - return qint64(qFloor(bytes / (format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000)))); -} - typedef Eigen::Matrix VectorXuint8; typedef Eigen::Matrix VectorXint8; typedef Eigen::Matrix VectorXint16; typedef Eigen::Matrix VectorXint32; -static inline QByteArray samplesToInt(const QByteArray& data, const QAudioFormat& supported_format) -{ - QByteArray input = data; - - switch (supported_format.sampleSize()) - { - case 8: - { - switch (supported_format.sampleType()) - { - case QAudioFormat::UnSignedInt: - { - Eigen::Ref samples_float = Eigen::Map(reinterpret_cast(input.data()), input.size() / int(sizeof(float))); - - Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits::max()); - - VectorXuint8 samples_int = samples_int_tmp.cast(); - - QByteArray raw = QByteArray(reinterpret_cast(samples_int.data()), int(samples_int.size()) * int(sizeof(quint8))); - - return raw; - } - case QAudioFormat::SignedInt: - { - Eigen::Ref samples_float = Eigen::Map(reinterpret_cast(input.data()), input.size() / int(sizeof(float))); - - Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits::max()); - - VectorXint8 samples_int = samples_int_tmp.cast(); - - QByteArray raw = QByteArray(reinterpret_cast(samples_int.data()), int(samples_int.size()) * int(sizeof(qint8))); - - return raw; - } - default: - break; - } - - break; - } - case 16: - { - switch (supported_format.sampleType()) - { - case QAudioFormat::SignedInt: - { - Eigen::Ref samples_float = Eigen::Map(reinterpret_cast(input.data()), input.size() / int(sizeof(float))); - - Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits::max()); - - VectorXint16 samples_int = samples_int_tmp.cast(); - - QByteArray raw = QByteArray(reinterpret_cast(samples_int.data()), int(samples_int.size()) * int(sizeof(qint16))); - - return raw; - } - default: - break; - } - - break; - } - case 32: - { - switch (supported_format.sampleType()) - { - case QAudioFormat::SignedInt: - { - Eigen::Ref samples_float = Eigen::Map(reinterpret_cast(input.data()), input.size() / int(sizeof(float))); - - Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits::max()); - - VectorXint32 samples_int = samples_int_tmp.cast(); - - QByteArray raw = QByteArray(reinterpret_cast(samples_int.data()), int(samples_int.size()) * int(sizeof(qint32))); - - return raw; - } - default: - break; - } - - break; - } - default: - break; - } - - return QByteArray(); -} - -static inline QByteArray samplesToFloat(const QByteArray& data, const QAudioFormat& supported_format) -{ - QByteArray input = data; - - switch (supported_format.sampleSize()) - { - case 8: - { - switch (supported_format.sampleType()) - { - case QAudioFormat::UnSignedInt: - { - QByteArray raw = input; - - Eigen::Ref samples_int = Eigen::Map(reinterpret_cast(raw.data()), raw.size() / int(sizeof(quint8))); - - Eigen::VectorXf samples_float = samples_int.cast() / float(std::numeric_limits::max()); - - return QByteArray(reinterpret_cast(samples_float.data()), int(samples_float.size()) * int(sizeof(float))); - } - case QAudioFormat::SignedInt: - { - QByteArray raw = input; - - Eigen::Ref samples_int = Eigen::Map(reinterpret_cast(raw.data()), raw.size() / int(sizeof(qint8))); - - Eigen::VectorXf samples_float = samples_int.cast() / float(std::numeric_limits::max()); - - return QByteArray(reinterpret_cast(samples_float.data()), int(samples_float.size()) * int(sizeof(float))); - } - default: - break; - } - - break; - } - case 16: - { - switch (supported_format.sampleType()) - { - case QAudioFormat::SignedInt: - { - QByteArray raw = input; - - Eigen::Ref samples_int = Eigen::Map(reinterpret_cast(raw.data()), raw.size() / int(sizeof(qint16))); - - Eigen::VectorXf samples_float = samples_int.cast() / float(std::numeric_limits::max()); - - return QByteArray(reinterpret_cast(samples_float.data()), int(samples_float.size()) * int(sizeof(float))); - } - default: - break; - } - - break; - } - case 32: - { - switch (supported_format.sampleType()) - { - case QAudioFormat::SignedInt: - { - QByteArray raw = input; - - Eigen::Ref samples_int = Eigen::Map(reinterpret_cast(raw.data()), raw.size() / int(sizeof(qint32))); - - Eigen::VectorXf samples_float = samples_int.cast() / float(std::numeric_limits::max()); - - return QByteArray(reinterpret_cast(samples_float.data()), int(samples_float.size()) * int(sizeof(float))); - } - default: - break; - } - - break; - } - default: - break; - } - - return QByteArray(); -} #endif // AUDIOHANDLER_H From 09ea7a2c26b21da6e32c1cb8cc2d4f3811a0c937 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 13:03:27 +0100 Subject: [PATCH 197/323] Try readyRead instead of notify --- audiohandler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 2e4aa39..d00fd69 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -145,13 +145,13 @@ bool audioHandler::init(audioSetup setupIn) if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); - audioInput->setNotifyInterval(setup.blockSize); + //audioInput->setNotifyInterval(setup.blockSize); qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Starting audio timer"; //audioTimer = new QTimer(); //audioTimer->setTimerType(Qt::PreciseTimer); //connect(audioTimer, &QTimer::timeout, this, &audioHandler::getNextAudioChunk); - connect(audioInput, SIGNAL(notify()), this, SLOT(getNextAudioChunk()),Qt::DirectConnection); + //connect(audioInput, SIGNAL(notify()), this, SLOT(getNextAudioChunk()),Qt::DirectConnection); connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); } @@ -211,9 +211,9 @@ void audioHandler::start() if (setup.isinput) { audioDevice = audioInput->start(); connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); - //connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); + connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); //audioTimer->start(setup.blockSize); - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Notify interval set to" << audioInput->notifyInterval() << "requested" << setup.blockSize; + //qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Notify interval set to" << audioInput->notifyInterval() << "requested" << setup.blockSize; } else { // Buffer size must be set before audio is started. From 92288aa768ea95cae495bf693b603089940e6a99 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 13:08:42 +0100 Subject: [PATCH 198/323] Increase late audio timeout to 100ms --- audiohandler.cpp | 21 ++------------------- audiohandler.h | 3 +-- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index d00fd69..26ec64c 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -31,12 +31,6 @@ audioHandler::~audioHandler() audioOutput = Q_NULLPTR; } - if (audioTimer != Q_NULLPTR) { - audioTimer->stop(); - delete audioTimer; - audioTimer = Q_NULLPTR; - } - if (resampler != Q_NULLPTR) { speex_resampler_destroy(resampler); @@ -145,13 +139,6 @@ bool audioHandler::init(audioSetup setupIn) if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); - //audioInput->setNotifyInterval(setup.blockSize); - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Starting audio timer"; - - //audioTimer = new QTimer(); - //audioTimer->setTimerType(Qt::PreciseTimer); - //connect(audioTimer, &QTimer::timeout, this, &audioHandler::getNextAudioChunk); - //connect(audioInput, SIGNAL(notify()), this, SLOT(getNextAudioChunk()),Qt::DirectConnection); connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); } @@ -212,8 +199,6 @@ void audioHandler::start() audioDevice = audioInput->start(); connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); - //audioTimer->start(setup.blockSize); - //qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Notify interval set to" << audioInput->notifyInterval() << "requested" << setup.blockSize; } else { // Buffer size must be set before audio is started. @@ -244,7 +229,6 @@ void audioHandler::stop() if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) { // Stop audio output - //audioTimer->stop(); audioInput->stop(); } audioDevice = Q_NULLPTR; @@ -439,7 +423,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize()-audioOutput->bytesFree())/1000); if (audioDevice != Q_NULLPTR) { audioDevice->write(livePacket.data); - if (lastReceived.msecsTo(QTime::currentTime()) > 50) { + if (lastReceived.msecsTo(QTime::currentTime()) > 100) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); } lastReceived = QTime::currentTime(); @@ -633,7 +617,7 @@ void audioHandler::getNextAudioChunk() livePacket.data = outPacket; // Copy output packet back to input buffer. } emit haveAudioData(livePacket); - if (lastReceived.msecsTo(QTime::currentTime()) > 50) { + if (lastReceived.msecsTo(QTime::currentTime()) > 100) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); } @@ -712,5 +696,4 @@ void audioHandler::stateChanged(QAudio::State state) void audioHandler::clearUnderrun() { isUnderrun = false; - underTimer->stop(); } \ No newline at end of file diff --git a/audiohandler.h b/audiohandler.h index 395b126..20929d8 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -8,10 +8,10 @@ #include #include #include -#include #include #include #include +#include /* QT Audio Headers */ #include @@ -115,7 +115,6 @@ private: QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; QIODevice* audioDevice=Q_NULLPTR; - QTimer* audioTimer = Q_NULLPTR; QAudioFormat format; QAudioDeviceInfo deviceInfo; SpeexResamplerState* resampler = Q_NULLPTR; From 43f423f1ad6d24dbac1d9365254da59b127eeb59 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 13:53:49 +0100 Subject: [PATCH 199/323] Tidy getnextaudiochunk() --- audiohandler.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 26ec64c..38b1f75 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -448,15 +448,11 @@ void audioHandler::getNextAudioChunk() tempBuf.data.append(audioDevice->readAll()); - if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)) { - return; - } - - audioPacket livePacket; - livePacket.time= QTime::currentTime(); - livePacket.sent = 0; - memcpy(&livePacket.guid, setup.guid, GUIDLEN); - while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) { + while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { + audioPacket livePacket; + livePacket.time = QTime::currentTime(); + livePacket.sent = 0; + memcpy(&livePacket.guid, setup.guid, GUIDLEN); QTime startProcessing = QTime::currentTime(); livePacket.data.clear(); livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); From fb99a572726ad4c3b45bc22082d9f2c45d446b53 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 15:58:41 +0100 Subject: [PATCH 200/323] Tidy some connections --- audiohandler.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 38b1f75..7c58cef 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -184,7 +184,7 @@ bool audioHandler::init(audioSetup setupIn) underTimer = new QTimer(); underTimer->setSingleShot(true); - connect(underTimer, &QTimer::timeout, this, &audioHandler::clearUnderrun); + connect(underTimer, SIGNAL(timeout()), this, SLOT(clearUnderrun())); this->start(); @@ -197,8 +197,8 @@ void audioHandler::start() if (setup.isinput) { audioDevice = audioInput->start(); - connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); - connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk); + connect(audioInput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); + connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk), Qt::UniqueConnection); } else { // Buffer size must be set before audio is started. @@ -208,7 +208,7 @@ void audioHandler::start() audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 1000)); #endif audioDevice = audioOutput->start(); - connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection); + connect(audioOutput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); } if (!audioDevice) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio device failed to start()"; @@ -445,7 +445,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) void audioHandler::getNextAudioChunk() { - tempBuf.data.append(audioDevice->readAll()); while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { @@ -616,7 +615,7 @@ void audioHandler::getNextAudioChunk() if (lastReceived.msecsTo(QTime::currentTime()) > 100) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); } - + lastReceived = QTime::currentTime(); //ret = livePacket.data; } } From 76e52c42db9b9c2f36474933767adf90b980664a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 16:01:54 +0100 Subject: [PATCH 201/323] Add parens --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 7c58cef..f47f59f 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -198,7 +198,7 @@ void audioHandler::start() if (setup.isinput) { audioDevice = audioInput->start(); connect(audioInput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); - connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk), Qt::UniqueConnection); + connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); } else { // Buffer size must be set before audio is started. From a929b2b8a87dc2c40eedbe3b2d3db6e3a570940c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 16:11:35 +0100 Subject: [PATCH 202/323] Name threads to ease debugging --- rigcommander.cpp | 1 + servermain.cpp | 2 ++ udphandler.cpp | 2 ++ udpserver.cpp | 3 +++ wfmain.cpp | 2 ++ 5 files changed, 10 insertions(+) diff --git a/rigcommander.cpp b/rigcommander.cpp index 282ca82..a053810 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -118,6 +118,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud udp = new udpHandler(prefs,rxSetup,txSetup); udpHandlerThread = new QThread(this); + udpHandlerThread->setObjectName("udpHandler()"); udp->moveToThread(udpHandlerThread); diff --git a/servermain.cpp b/servermain.cpp index a63cbd1..2db93d3 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -104,6 +104,7 @@ void servermain::makeRig() qInfo(logSystem()) << "Creating new rigThread()"; radio->rig = new rigCommander(radio->guid); radio->rigThread = new QThread(this); + radio->rigThread->setObjectName("rigCommander()"); // Thread: radio->rig->moveToThread(radio->rigThread); @@ -378,6 +379,7 @@ void servermain::setServerToPrefs() udp = new udpServer(&serverConfig); serverThread = new QThread(this); + serverThread->setObjectName("udpServer()"); udp->moveToThread(serverThread); diff --git a/udphandler.cpp b/udphandler.cpp index 1b5907f..abcc9b0 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -902,6 +902,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint rxaudio = new audioHandler(); rxAudioThread = new QThread(this); + rxAudioThread->setObjectName("rxAudio()"); rxaudio->moveToThread(rxAudioThread); @@ -929,6 +930,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint if (enableTx) { txaudio = new audioHandler(); txAudioThread = new QThread(this); + rxAudioThread->setObjectName("txAudio()"); txaudio->moveToThread(txAudioThread); diff --git a/udpserver.cpp b/udpserver.cpp index 84792a3..365cc03 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -368,6 +368,8 @@ void udpServer::controlReceived() radio->txaudio = new audioHandler(); radio->txAudioThread = new QThread(this); + radio->txAudioThread->setObjectName("server txAudio()"); + radio->txaudio->moveToThread(radio->txAudioThread); @@ -409,6 +411,7 @@ void udpServer::controlReceived() radio->rxaudio = new audioHandler(); radio->rxAudioThread = new QThread(this); + radio->rxAudioThread->setObjectName("server rxAudio()"); radio->rxaudio->moveToThread(radio->rxAudioThread); diff --git a/wfmain.cpp b/wfmain.cpp index 61bd474..32b9e31 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -412,6 +412,8 @@ void wfmain::makeRig() { rig = new rigCommander(); rigThread = new QThread(this); + rigThread->setObjectName("rigCommander()"); + // Thread: rig->moveToThread(rigThread); From 8217b12bc290f98537c60c24fa01c1859c38eb02 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 16:13:47 +0100 Subject: [PATCH 203/323] Update udpserver.cpp --- udpserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index 365cc03..6d6cb5c 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -368,7 +368,7 @@ void udpServer::controlReceived() radio->txaudio = new audioHandler(); radio->txAudioThread = new QThread(this); - radio->txAudioThread->setObjectName("server txAudio()"); + radio->txAudioThread->setObjectName("txAudio()"); radio->txaudio->moveToThread(radio->txAudioThread); @@ -411,7 +411,7 @@ void udpServer::controlReceived() radio->rxaudio = new audioHandler(); radio->rxAudioThread = new QThread(this); - radio->rxAudioThread->setObjectName("server rxAudio()"); + radio->rxAudioThread->setObjectName("rxAudio()"); radio->rxaudio->moveToThread(radio->rxAudioThread); From 95cace832b56461408404c6b460fe3dc56737481 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 16:41:28 +0100 Subject: [PATCH 204/323] Try a reduced complexity on Opus encoder --- audiohandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/audiohandler.cpp b/audiohandler.cpp index f47f59f..ee68043 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -159,6 +159,7 @@ bool audioHandler::init(audioSetup setupIn) opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1)); opus_encoder_ctl(encoder, OPUS_SET_DTX(1)); opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(5)); + opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(5)); // Reduce complexity to maybe lower CPU? qInfo(logAudio()) << "Creating opus encoder: " << opus_strerror(opus_err); } } From 8d75d01e2c2ded4688787c3bf40fd4ecf27f07bb Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 2 May 2022 17:15:41 +0100 Subject: [PATCH 205/323] Set Opus complexity to 7 --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index ee68043..95a443a 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -159,7 +159,7 @@ bool audioHandler::init(audioSetup setupIn) opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1)); opus_encoder_ctl(encoder, OPUS_SET_DTX(1)); opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(5)); - opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(5)); // Reduce complexity to maybe lower CPU? + opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(7)); // Reduce complexity to maybe lower CPU? qInfo(logAudio()) << "Creating opus encoder: " << opus_strerror(opus_err); } } From 2b04b84eb158a68598a508303df5927ca8c6a173 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 3 May 2022 10:37:41 +0100 Subject: [PATCH 206/323] Fix for uLaw encoding --- audiohandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/audiohandler.cpp b/audiohandler.cpp index 95a443a..9a25cc7 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -74,6 +74,8 @@ bool audioHandler::init(audioSetup setupIn) if (setup.codec == 0x01 || setup.codec == 0x20) { setup.ulaw = true; + setup.format.setSampleSize(16); + setup.format.setSampleType(QAudioFormat::SignedInt); } if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20 || setup.codec == 0x80) { From 105d1782ed83558bb80b85d8df65795117318eda Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 3 May 2022 11:21:08 +0100 Subject: [PATCH 207/323] Remove unnecessary code --- audiohandler.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 9a25cc7..8eba4d2 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -261,10 +261,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) } livePacket.data.clear(); livePacket.data = outPacket; // Replace incoming data with converted. - // Buffer now contains 16bit signed samples. - setup.format.setSampleSize(16); - setup.format.setSampleType(QAudioFormat::SignedInt); - } @@ -304,7 +300,6 @@ void audioHandler::incomingAudio(audioPacket inPacket) livePacket.data.clear(); livePacket.data = outPacket; // Replace incoming data with converted. } - setup.format.setSampleType(QAudioFormat::Float); } From df2fb20d780dc5b084e61dfcdc651318bf7759b0 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Tue, 3 May 2022 14:57:34 +0200 Subject: [PATCH 208/323] WHATSNEW --- WHATSNEW | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/WHATSNEW b/WHATSNEW index 68c1e88..a3a3334 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -15,7 +15,6 @@ The following highlights are in this 1.x-release: people should use "fake it" in wsjtx as the split code is not reliable. tidied up udp server function for better reliability added some IC736 stuff - added portaudio (you need t change wfview.pro to select and that lowers the latency to maybe less than 50 ms added PBT and IF-shift several bugs fixed @@ -30,6 +29,13 @@ The following highlights are in this 1.x-release: + Allow dynamic restarting of server + New Settings tab + Enable High DPI Scaling -+ More multi-radio support (nearly working!) -+ N1MM+ TCP add. ++ More multi-radio support (mostly working!) ++ Split LAN waterfall data for N1MM+ Spectrum Scope support: There is a combobox that allows you to select + split/combine (or default). Split only makes sense for LAN and Combine for USB. ++ portaudio removed ++ added radio status display with meters for audio (speaker/mic) + +Note: + +We know about high CPU usage on RPi. From f42fbc2e54af93cb647d1f08cfbd9f7fe5d9f9c0 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 4 May 2022 15:43:34 +0100 Subject: [PATCH 209/323] Try using float for all audio. Not tested on Linux/Mac --- audiohandler.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 8eba4d2..d395003 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -90,7 +90,7 @@ bool audioHandler::init(audioSetup setupIn) if (setup.codec == 0x40 || setup.codec == 0x80) { setup.format.setSampleType(QAudioFormat::Float); } - + qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name << ", bits" << setup.format.sampleSize() << ", codec" << setup.codec << @@ -134,8 +134,14 @@ bool audioHandler::init(audioSetup setupIn) format.setSampleSize(16); } } + format.setSampleType(QAudioFormat::Float); + if (!setup.port.isFormatSupported(format)) { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempt to select Float failed, reverting to SignedInt"; + format.setSampleType(QAudioFormat::SignedInt); + } - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Internal: sample rate" << format.sampleRate() << "channel count" << format.channelCount(); + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() << + "Sample Rate" << format.sampleRate() << "Codec" << format.codec() << "Sample Type" << format.sampleType(); // We "hopefully" now have a valid format that is supported so try connecting From bdca404457c1c1dd9dcafc4645126e913657afe1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 4 May 2022 20:21:29 +0100 Subject: [PATCH 210/323] Make udpserver and client behave the same way for retransmit --- udphandler.cpp | 51 +++++++++++++++++++++----------------------------- udpserver.cpp | 19 ++++++++++--------- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index abcc9b0..cc21e9d 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -202,7 +202,7 @@ void udpHandler::dataReceived() } QString tempLatency; - if (status.rxLatency > status.rxCurrentLatency && !status.rxUnderrun) + if (status.rxCurrentLatency < status.rxLatency*1.2 && !status.rxUnderrun) { tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); } @@ -1199,7 +1199,7 @@ void udpBase::dataReceived(QByteArray r) case (CONTROL_SIZE): // Empty response used for simple comms and retransmit requests. { control_packet_t in = (control_packet_t)r.constData(); - if (in->type == 0x01) + if (in->type == 0x01 && in->len == 0x10) { // Single packet request packetsLost++; @@ -1210,7 +1210,7 @@ void udpBase::dataReceived(QByteArray r) // Found matching entry? // Send "untracked" as it has already been sent once. // Don't constantly retransmit the same packet, give-up eventually - qDebug(logUdp()) << this->metaObject()->className() << ": Sending retransmit of " << QString("0x%1").arg(match->seqNum,0,16); + qDebug(logUdp()) << this->metaObject()->className() << ": Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum,0,16); match->retransmitCount++; udpMutex.lock(); udp->writeDatagram(match->data, radioIP, port); @@ -1302,7 +1302,7 @@ void udpBase::dataReceived(QByteArray r) else { // Found matching entry? // Send "untracked" as it has already been sent once. - qDebug(logUdp()) << this->metaObject()->className() << ": Remote has requested retransmit of " << QString("0x%1").arg(match->seqNum,0,16); + qDebug(logUdp()) << this->metaObject()->className() << ": Sending (multiple packet) retransmit of " << QString("0x%1").arg(match->seqNum,0,16); match->retransmitCount++; udpMutex.lock(); udp->writeDatagram(match->data, radioIP, port); @@ -1423,20 +1423,28 @@ void udpBase::sendRetransmitRequest() missingMutex.lock(); for (auto it = rxMissing.begin(); it != rxMissing.end(); ++it) { - if (it.value() < 10) - { - missingSeqs.append(it.key() & 0xff); - missingSeqs.append(it.key() >> 8 & 0xff); - missingSeqs.append(it.key() & 0xff); - missingSeqs.append(it.key() >> 8 & 0xff); - it.value()++; + if (&it.key() != Q_NULLPTR) { + if (it.value() < 4) + { + missingSeqs.append(it.key() & 0xff); + missingSeqs.append(it.key() >> 8 & 0xff); + missingSeqs.append(it.key() & 0xff); + missingSeqs.append(it.key() >> 8 & 0xff); + it.value()++; + } + else { + qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; + it = rxMissing.erase(it); + } } } missingMutex.unlock(); + if (missingSeqs.length() != 0) { control_packet p; memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p); p.type = 0x01; p.seq = 0x0000; p.sentid = myId; @@ -1453,7 +1461,8 @@ void udpBase::sendRetransmitRequest() { qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':'); missingMutex.lock(); - missingSeqs.insert(0, p.packet, sizeof(p.packet)); + p.len = sizeof(p)+missingSeqs.size(); + missingSeqs.insert(0, p.packet, sizeof(p)); missingMutex.unlock(); udpMutex.lock(); @@ -1506,24 +1515,6 @@ void udpBase::sendPing() return; } -void udpBase::sendRetransmitRange(quint16 first, quint16 second, quint16 third, quint16 fourth) -{ - retransmit_range_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.type = 0x00; - p.sentid = myId; - p.rcvdid = remoteId; - p.first = first; - p.second = second; - p.third = third; - p.fourth = fourth; - udpMutex.lock(); - udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); - udpMutex.unlock(); - return; -} - void udpBase::sendTrackedPacket(QByteArray d) { diff --git a/udpserver.cpp b/udpserver.cpp index 6d6cb5c..d7c771e 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -796,17 +796,15 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) if (current->idleTimer != Q_NULLPTR && !current->idleTimer->isActive()) { current->idleTimer->start(100); } - } // This is a retransmit request - else if (in->type == 0x01) + } // This is a single packet retransmit request + else if (in->type == 0x01 && in->len == 0x10) { - // Single packet request - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'retransmit' request for " << QString("0x%1").arg(in->seq,0,16); QMap::iterator match = current->txSeqBuf.find(in->seq); if (match != current->txSeqBuf.end() && match->retransmitCount < 5) { // Found matching entry? // Don't constantly retransmit the same packet, give-up eventually - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending retransmit of " << QString("0x%1").arg(match->seqNum,0,16); + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16); match->retransmitCount++; if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -820,6 +818,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) } else { // Just send an idle! + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (single) packet " << QString("0x%1").arg(in->seq, 0, 16) << "not found"; sendControl(current, 0x00, in->seq); } } @@ -843,7 +842,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) return s.seqNum == cs; }); if (match == current->txSeqBuf.end()) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested packet " << QString("0x%1").arg(seq,0,16) << " not found"; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (multiple) packet " << QString("0x%1").arg(seq,0,16) << " not found"; // Just send idle packet. sendControl(current, 0, in->seq); } @@ -851,7 +850,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) { // Found matching entry? // Send "untracked" as it has already been sent once. - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending retransmit of " << QString("0x%1").arg(match->seqNum,0,16); + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending (multiple packet) retransmit of " << QString("0x%1").arg(match->seqNum,0,16); match->retransmitCount++; if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -933,9 +932,9 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) current->rxSeqBuf.insert(f, QTime::currentTime()); if (f != in->seq) { - qInfo(logUdpServer()) << "Detected missing packet" << f; if (!current->rxMissing.contains(f)) { + qInfo(logUdpServer()) << "Detected new missing packet" << f; current->rxMissing.insert(f, 0); } } @@ -1750,6 +1749,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) p.rcvdid = c->remoteId; if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. { + p.len = sizeof(p); p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq,0,16); @@ -1767,7 +1767,8 @@ void udpServer::sendRetransmitRequest(CLIENT* c) { qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(); - missingSeqs.insert(0, p.packet, sizeof(p.packet)); + p.len = sizeof(p) + missingSeqs.size(); + missingSeqs.insert(0, p.packet, sizeof(p)); if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { From 3aefa6893964506a45cbe3e0c466accd80de66c5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 4 May 2022 20:37:58 +0100 Subject: [PATCH 211/323] More fixes to make client and server behave in same way --- udphandler.cpp | 4 ---- udpserver.cpp | 17 ++++++++--------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index cc21e9d..865ae76 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1317,10 +1317,6 @@ void udpBase::dataReceived(QByteArray r) { rxBufferMutex.lock(); if (rxSeqBuf.isEmpty()) { - if (rxSeqBuf.size() > BUFSIZE) - { - rxSeqBuf.erase(rxSeqBuf.begin()); - } rxSeqBuf.insert(in->seq, QTime::currentTime()); } else diff --git a/udpserver.cpp b/udpserver.cpp index d7c771e..8fff956 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -885,10 +885,10 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) else { - if (in->seq < current->rxSeqBuf.firstKey()) + if (in->seq < current->rxSeqBuf.firstKey() || in->seq - current->rxSeqBuf.lastKey() > MAX_MISSING) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): ******* seq number may have rolled over ****** previous highest: " << - QString("0x%1").arg(current->rxSeqBuf.lastKey(),0,16) << " current: " << QString("0x%1").arg(in->seq,0,16); + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "Large seq number gap detected, previous highest: " << + QString("0x%1").arg(current->rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16); // Looks like it has rolled over so clear buffer and start again. if (current->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -918,6 +918,8 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. //int missCounter = 0; if (in->seq > current->rxSeqBuf.lastKey() + 1) { + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "1 or more missing packets detected, previous: " << + QString("0x%1").arg(current->rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16); // We are likely missing packets then! if (current->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { @@ -931,12 +933,9 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) } current->rxSeqBuf.insert(f, QTime::currentTime()); - if (f != in->seq) { - if (!current->rxMissing.contains(f)) - { - qInfo(logUdpServer()) << "Detected new missing packet" << f; - current->rxMissing.insert(f, 0); - } + if (f != in->seq && !current->rxMissing.contains(f)) + { + current->rxMissing.insert(f, 0); } } current->missMutex.unlock(); From 85ba0fb56967fdae1ae1ce6d180009c12089ad03 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 4 May 2022 22:29:05 +0100 Subject: [PATCH 212/323] When using Float, force 32bit as Mac doesn't like it otherwise --- audiohandler.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index d395003..4a51aab 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -126,6 +126,17 @@ bool audioHandler::init(audioSetup setupIn) } } + if (format.sampleType()==QAudioFormat::SignedInt) { + format.setSampleType(QAudioFormat::Float); + format.setSampleSize(32); + if (!setup.port.isFormatSupported(format)) { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempt to select 32bit Float failed, reverting to SignedInt"; + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleSize(16); + } + + } + if (format.sampleSize() == 24) { // We can't convert this easily so use 32 bit instead. format.setSampleSize(32); @@ -134,11 +145,6 @@ bool audioHandler::init(audioSetup setupIn) format.setSampleSize(16); } } - format.setSampleType(QAudioFormat::Float); - if (!setup.port.isFormatSupported(format)) { - qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempt to select Float failed, reverting to SignedInt"; - format.setSampleType(QAudioFormat::SignedInt); - } qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() << "Sample Rate" << format.sampleRate() << "Codec" << format.codec() << "Sample Type" << format.sampleType(); @@ -695,4 +701,4 @@ void audioHandler::stateChanged(QAudio::State state) void audioHandler::clearUnderrun() { isUnderrun = false; -} \ No newline at end of file +} From cac93198a63cebdb2ab8695aaa8f9702da94d371 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 00:06:50 +0100 Subject: [PATCH 213/323] Debugging retransmission (again!) --- udphandler.cpp | 14 ++++++++------ udpserver.cpp | 14 +++++--------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 865ae76..ae66207 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1205,17 +1205,20 @@ void udpBase::dataReceived(QByteArray r) packetsLost++; congestion = static_cast(packetsSent) / packetsLost * 100; txBufferMutex.lock(); - QMap::iterator match = txSeqBuf.find(in->seq); + auto match = txSeqBuf.find(in->seq); if (match != txSeqBuf.end()) { // Found matching entry? // Send "untracked" as it has already been sent once. // Don't constantly retransmit the same packet, give-up eventually - qDebug(logUdp()) << this->metaObject()->className() << ": Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum,0,16); + qDebug(logUdp()) << this->metaObject()->className() << ": Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16); match->retransmitCount++; udpMutex.lock(); udp->writeDatagram(match->data, radioIP, port); udpMutex.unlock(); } + else { + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(in->seq, 0, 16) << " not found, have " << txSeqBuf.begin()->seqNum << "to" << txSeqBuf.end()->seqNum;; + } txBufferMutex.unlock(); } if (in->type == 0x04) { @@ -1293,9 +1296,9 @@ void udpBase::dataReceived(QByteArray r) for (quint16 i = 0x10; i < r.length(); i = i + 2) { quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; - QMap::iterator match = txSeqBuf.find(seq); + auto match = txSeqBuf.find(seq); if (match == txSeqBuf.end()) { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(seq,0,16) << " not found"; + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(seq, 0, 16) << " not found, have " << txSeqBuf.begin()->seqNum << "to" << txSeqBuf.end()->seqNum;; // Just send idle packet. sendControl(false, 0, seq); } @@ -1307,7 +1310,6 @@ void udpBase::dataReceived(QByteArray r) udpMutex.lock(); udp->writeDatagram(match->data, radioIP, port); udpMutex.unlock(); - match++; packetsLost++; congestion = static_cast(packetsSent) / packetsLost * 100; } @@ -1374,7 +1376,7 @@ void udpBase::dataReceived(QByteArray r) else { // This is probably one of our missing packets! missingMutex.lock(); - QMap::iterator s = rxMissing.find(in->seq); + auto s = rxMissing.find(in->seq); if (s != rxMissing.end()) { qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << QString("0x%1").arg(in->seq,0,16); diff --git a/udpserver.cpp b/udpserver.cpp index 8fff956..206898e 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -799,7 +799,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) } // This is a single packet retransmit request else if (in->type == 0x01 && in->len == 0x10) { - QMap::iterator match = current->txSeqBuf.find(in->seq); + auto match = current->txSeqBuf.find(in->seq); if (match != current->txSeqBuf.end() && match->retransmitCount < 5) { // Found matching entry? @@ -818,7 +818,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) } else { // Just send an idle! - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (single) packet " << QString("0x%1").arg(in->seq, 0, 16) << "not found"; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (single) packet " << QString("0x%1").arg(in->seq, 0, 16) << "not found, have " << current->txSeqBuf.begin()->seqNum << "to" << current->txSeqBuf.end()->seqNum; sendControl(current, 0x00, in->seq); } } @@ -838,11 +838,9 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) for (quint16 i = 0x10; i < r.length(); i = i + 2) { quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; - auto match = std::find_if(current->txSeqBuf.begin(), current->txSeqBuf.end(), [&cs = seq](SEQBUFENTRY& s) { - return s.seqNum == cs; - }); + auto match = current->txSeqBuf.find(seq); if (match == current->txSeqBuf.end()) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (multiple) packet " << QString("0x%1").arg(seq,0,16) << " not found"; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (multiple) packet " << QString("0x%1").arg(seq,0,16) << " not found , have " << current->txSeqBuf.begin()->seqNum << "to" << current->txSeqBuf.end()->seqNum;; // Just send idle packet. sendControl(current, 0, in->seq); } @@ -860,8 +858,6 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) else { qInfo(logUdpServer()) << "Unable to lock udpMutex()"; } - - match++; } } } @@ -962,7 +958,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) // Check whether this is one of our missing ones! if (current->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - QMap::iterator s = current->rxMissing.find(in->seq); + auto s = current->rxMissing.find(in->seq); if (s != current->rxMissing.end()) { qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Missing SEQ has been received! " << QString("0x%1").arg(in->seq,0,16); From 24841c5da9f1f9df80cc8a3231aa99198ba30366 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 00:23:42 +0100 Subject: [PATCH 214/323] Fix debug message --- udphandler.cpp | 4 ++-- udpserver.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index ae66207..d373525 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1217,7 +1217,7 @@ void udpBase::dataReceived(QByteArray r) udpMutex.unlock(); } else { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(in->seq, 0, 16) << " not found, have " << txSeqBuf.begin()->seqNum << "to" << txSeqBuf.end()->seqNum;; + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(in->seq, 0, 16) << " not found, have " << txSeqBuf.firstKey() << "to" << txSeqBuf.lastKey(); } txBufferMutex.unlock(); } @@ -1298,7 +1298,7 @@ void udpBase::dataReceived(QByteArray r) quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; auto match = txSeqBuf.find(seq); if (match == txSeqBuf.end()) { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(seq, 0, 16) << " not found, have " << txSeqBuf.begin()->seqNum << "to" << txSeqBuf.end()->seqNum;; + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(seq, 0, 16) << " not found, have " << txSeqBuf.firstKey() << "to" << txSeqBuf.lastKey(); // Just send idle packet. sendControl(false, 0, seq); } diff --git a/udpserver.cpp b/udpserver.cpp index 206898e..6a1cfd1 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -818,7 +818,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) } else { // Just send an idle! - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (single) packet " << QString("0x%1").arg(in->seq, 0, 16) << "not found, have " << current->txSeqBuf.begin()->seqNum << "to" << current->txSeqBuf.end()->seqNum; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (single) packet " << QString("0x%1").arg(in->seq, 0, 16) << "not found, have " << current->txSeqBuf.firstKey() << "to" << current->txSeqBuf.lastKey(); sendControl(current, 0x00, in->seq); } } @@ -840,7 +840,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; auto match = current->txSeqBuf.find(seq); if (match == current->txSeqBuf.end()) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (multiple) packet " << QString("0x%1").arg(seq,0,16) << " not found , have " << current->txSeqBuf.begin()->seqNum << "to" << current->txSeqBuf.end()->seqNum;; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (multiple) packet " << QString("0x%1").arg(seq,0,16) << " not found , have " << current->txSeqBuf.firstKey() << "to" << current->txSeqBuf.lastKey(); // Just send idle packet. sendControl(current, 0, in->seq); } From 4b19ed522fabf600d04fe5cea3f842caf8f43b8d Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 00:31:55 +0100 Subject: [PATCH 215/323] Hopefully fix occasional retransmit bug --- udphandler.cpp | 2 +- udpserver.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index d373525..81812e8 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1421,7 +1421,7 @@ void udpBase::sendRetransmitRequest() missingMutex.lock(); for (auto it = rxMissing.begin(); it != rxMissing.end(); ++it) { - if (&it.key() != Q_NULLPTR) { + if (it.key() != NULL) { if (it.value() < 4) { missingSeqs.append(it.key() & 0xff); diff --git a/udpserver.cpp b/udpserver.cpp index 6a1cfd1..2da30db 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1715,7 +1715,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) { for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it) { - if (&it.key() != Q_NULLPTR) + if (it.key() != NULL) { if (it.value() < 4) { From 7b5f29f4fe122d5bcd3106efdafe9d9c21f1e855 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 01:49:21 +0100 Subject: [PATCH 216/323] More retransmit debugging --- udphandler.cpp | 9 +++++++-- udpserver.cpp | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 81812e8..fc7ecbf 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1421,7 +1421,8 @@ void udpBase::sendRetransmitRequest() missingMutex.lock(); for (auto it = rxMissing.begin(); it != rxMissing.end(); ++it) { - if (it.key() != NULL) { + if (it.key() != 0x0) + { if (it.value() < 4) { missingSeqs.append(it.key() & 0xff); @@ -1429,11 +1430,15 @@ void udpBase::sendRetransmitRequest() missingSeqs.append(it.key() & 0xff); missingSeqs.append(it.key() >> 8 & 0xff); it.value()++; + it++; } else { qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; it = rxMissing.erase(it); - } + } + } else { + qInfo(logUdp()) << this->metaObject()->className() << ": found empty key in missing buffer"; + it++; } } missingMutex.unlock(); diff --git a/udpserver.cpp b/udpserver.cpp index 2da30db..3a178af 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1715,7 +1715,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) { for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it) { - if (it.key() != NULL) + if (it.key() != 0x00) { if (it.value() < 4) { @@ -1724,6 +1724,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) missingSeqs.append(it.key() & 0xff); missingSeqs.append(it.key() >> 8 & 0xff); it.value()++; + it++; } else { @@ -1731,6 +1732,9 @@ void udpServer::sendRetransmitRequest(CLIENT* c) qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; it = c->rxMissing.erase(it); } + } else { + qInfo(logUdp()) << this->metaObject()->className() << ": found empty key in missing buffer"; + it++; } } From 069c5368d870c7695b4423dc0ce21c154b6575a4 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 02:01:20 +0100 Subject: [PATCH 217/323] More debugging --- udphandler.cpp | 3 ++- udpserver.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index fc7ecbf..65ce54d 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1419,7 +1419,8 @@ void udpBase::sendRetransmitRequest() QByteArray missingSeqs; missingMutex.lock(); - for (auto it = rxMissing.begin(); it != rxMissing.end(); ++it) + auto it = rxMissing.begin(); + while (it != rxMissing.end()) { if (it.key() != 0x0) { diff --git a/udpserver.cpp b/udpserver.cpp index 3a178af..2158451 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1713,7 +1713,8 @@ void udpServer::sendRetransmitRequest(CLIENT* c) if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it) + auto it = c->rxMissing.begin(); + while (it != c->rxMissing.end()) { if (it.key() != 0x00) { From f662d1f7cd9d00b9d54ab978f10e72ddc225212b Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 08:57:50 +0100 Subject: [PATCH 218/323] Found another retransmit bug in server --- udphandler.cpp | 13 +++++++--- udpserver.cpp | 66 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 65ce54d..405a04f 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1217,7 +1217,10 @@ void udpBase::dataReceived(QByteArray r) udpMutex.unlock(); } else { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(in->seq, 0, 16) << " not found, have " << txSeqBuf.firstKey() << "to" << txSeqBuf.lastKey(); + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet" + << QString("0x%1").arg(in->seq, 0, 16) << + "not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) << + "to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16); } txBufferMutex.unlock(); } @@ -1298,7 +1301,10 @@ void udpBase::dataReceived(QByteArray r) quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; auto match = txSeqBuf.find(seq); if (match == txSeqBuf.end()) { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet " << QString("0x%1").arg(seq, 0, 16) << " not found, have " << txSeqBuf.firstKey() << "to" << txSeqBuf.lastKey(); + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet" + << QString("0x%1").arg(seq, 0, 16) << + "not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) << + "to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16); // Just send idle packet. sendControl(false, 0, seq); } @@ -1538,11 +1544,12 @@ void udpBase::sendTrackedPacket(QByteArray d) txSeqBuf.clear(); congestion = 0; } - txSeqBuf.insert(sendSeq,s); if (txSeqBuf.size() > BUFSIZE) { txSeqBuf.erase(txSeqBuf.begin()); } + txSeqBuf.insert(sendSeq, s); + txBufferMutex.unlock(); } else { qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked"; diff --git a/udpserver.cpp b/udpserver.cpp index 2158451..14df13d 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -818,7 +818,10 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) } else { // Just send an idle! - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (single) packet " << QString("0x%1").arg(in->seq, 0, 16) << "not found, have " << current->txSeqBuf.firstKey() << "to" << current->txSeqBuf.lastKey(); + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << + "): Requested (single) packet " << QString("0x%1").arg(in->seq, 0, 16) << + "not found, have " << QString("0x%1").arg(current->txSeqBuf.firstKey(), 0, 16) << + "to" << QString("0x%1").arg(current->txSeqBuf.lastKey(), 0, 16); sendControl(current, 0x00, in->seq); } } @@ -840,7 +843,11 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; auto match = current->txSeqBuf.find(seq); if (match == current->txSeqBuf.end()) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested (multiple) packet " << QString("0x%1").arg(seq,0,16) << " not found , have " << current->txSeqBuf.firstKey() << "to" << current->txSeqBuf.lastKey(); + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << + "): Requested (multiple) packet " << QString("0x%1").arg(seq,0,16) << + "not found, have " << QString("0x%1").arg(current->txSeqBuf.firstKey(), 0, 16) << + "to" << QString("0x%1").arg(current->txSeqBuf.lastKey(), 0, 16); + // Just send idle packet. sendControl(current, 0, in->seq); } @@ -998,12 +1005,16 @@ void udpServer::sendControl(CLIENT* c, quint8 type, quint16 seq) s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > BUFSIZE) + if (c->txSeq == 0) { + c->txSeqBuf.clear(); + } + else + if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } - c->txSeqBuf.insert(seq, s); + c->txSeqBuf.insert(c->txSeq, s); c->txSeq++; c->txMutex.unlock(); } @@ -1131,7 +1142,10 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed) s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > BUFSIZE) + if (c->txSeq == 0) { + c->txSeqBuf.clear(); + } + else if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } @@ -1253,11 +1267,14 @@ void udpServer::sendCapabilities(CLIENT* c) if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > BUFSIZE) + if (c->txSeq == 0) { + c->txSeqBuf.clear(); + } + else if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } - c->txSeqBuf.insert(p.seq, s); + c->txSeqBuf.insert(c->txSeq, s); c->txSeq++; c->txMutex.unlock(); } @@ -1335,11 +1352,14 @@ void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[GUIDLEN]) if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > BUFSIZE) + if (c->txSeq == 0) { + c->txSeqBuf.clear(); + } + else if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } - c->txSeqBuf.insert(p.seq, s); + c->txSeqBuf.insert(c->txSeq, s); c->txSeq++; c->txMutex.unlock(); } @@ -1393,11 +1413,14 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > BUFSIZE) + if (c->txSeq == 0) { + c->txSeqBuf.clear(); + } + else if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } - c->txSeqBuf.insert(p.seq, s); + c->txSeqBuf.insert(c->txSeq, s); c->txSeq++; c->txMutex.unlock(); } @@ -1509,12 +1532,15 @@ void udpServer::sendStatus(CLIENT* c) s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (c->txSeqBuf.size() > BUFSIZE) + if (c->txSeq == 0) { + c->txSeqBuf.clear(); + } + else if (c->txSeqBuf.size() > BUFSIZE) { c->txSeqBuf.remove(c->txSeqBuf.firstKey()); } c->txSeq++; - c->txSeqBuf.insert(p.seq, s); + c->txSeqBuf.insert(c->txSeq, s); c->txMutex.unlock(); } else { @@ -1581,11 +1607,14 @@ void udpServer::dataForServer(QByteArray d) if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (client->txSeqBuf.size() > BUFSIZE) + if (client->txSeq == 0) { + client->txSeqBuf.clear(); + } + else if (client->txSeqBuf.size() > BUFSIZE) { client->txSeqBuf.remove(client->txSeqBuf.firstKey()); } - client->txSeqBuf.insert(p.seq, s); + client->txSeqBuf.insert(client->txSeq, s); client->txSeq++; //client->innerSeq = (qToBigEndian(qFromBigEndian(client->innerSeq) + 1)); client->txMutex.unlock(); @@ -1652,11 +1681,14 @@ void udpServer::receiveAudioData(const audioPacket& d) s.data = t; if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { - if (client->txSeqBuf.size() > BUFSIZE) + if (client->txSeq == 0) { + client->txSeqBuf.clear(); + } + else if (client->txSeqBuf.size() > BUFSIZE) { client->txSeqBuf.remove(client->txSeqBuf.firstKey()); } - client->txSeqBuf.insert(p.seq, s); + client->txSeqBuf.insert(client->txSeq, s); client->txSeq++; client->sendAudioSeq++; client->txMutex.unlock(); From 7924d2b87f5908365222cf734c5738cc334bb90a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 09:01:04 +0100 Subject: [PATCH 219/323] Fix retransmit missing message --- udphandler.cpp | 2 +- udpserver.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index 405a04f..f19e6fd 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1440,7 +1440,7 @@ void udpBase::sendRetransmitRequest() it++; } else { - qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; + qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << QString("0x%1").arg(it.key(), 0, 16) << "deleting"; it = rxMissing.erase(it); } } else { diff --git a/udpserver.cpp b/udpserver.cpp index 14df13d..9c8db8c 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1762,7 +1762,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c) else { // We have tried 4 times to request this packet, time to give up! - qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting"; + qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << QString("0x%1").arg(it.key(), 0, 16) << "deleting"; it = c->rxMissing.erase(it); } } else { From bf5f81992761149da556bebaabd321070cec3792 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 11:16:59 +0100 Subject: [PATCH 220/323] found another small retransmit bug --- udpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udpserver.cpp b/udpserver.cpp index 9c8db8c..3b30467 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -999,7 +999,7 @@ void udpServer::sendControl(CLIENT* c, quint8 type, quint16 seq) { p.seq = c->txSeq; SEQBUFENTRY s; - s.seqNum = seq; + s.seqNum = c->txSeq; s.timeSent = QTime::currentTime(); s.retransmitCount = 0; s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); From 3bc0b4840a62d454a40a597cc21ba4179bf8da81 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Thu, 5 May 2022 13:41:31 +0200 Subject: [PATCH 221/323] WHATSNEW --- WHATSNEW | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/WHATSNEW b/WHATSNEW index a3a3334..9d9eca8 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -36,6 +36,9 @@ The following highlights are in this 1.x-release: + added radio status display with meters for audio (speaker/mic) -Note: +Notes: + +- We know about high CPU usage on RPi. +- the audio stack has been rewritten multiple times to get the best out of it and have the lowest possible latencies. + (At least three times) -We know about high CPU usage on RPi. From 8b958d572caf34769e939cd623d82cb53cfd4978 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 18:09:16 +0100 Subject: [PATCH 222/323] Fix some compile warnings and try to support sharing lan connected rigs --- udphandler.cpp | 5 +++-- udphandler.h | 2 +- udpserver.cpp | 9 +++++---- udpserver.h | 4 ++-- wfmain.cpp | 37 +++++++++++++++++++------------------ 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/udphandler.cpp b/udphandler.cpp index f19e6fd..3280530 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -1135,7 +1135,7 @@ void udpAudio::dataReceived() void udpBase::init(quint16 lport) { - timeStarted.start(); + //timeStarted.start(); udp = new QUdpSocket(this); udp->bind(lport); // Bind to random port. localPort = udp->localPort(); @@ -1517,7 +1517,8 @@ void udpBase::sendPing() p.sentid = myId; p.rcvdid = remoteId; p.seq = pingSendSeq; - p.time = timeStarted.msecsSinceStartOfDay(); + QTime now=QTime::currentTime(); + p.time = (quint32)now.msecsSinceStartOfDay(); lastPingSentTime = QDateTime::currentDateTime(); udpMutex.lock(); udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); diff --git a/udphandler.h b/udphandler.h index 3538337..3e51ed9 100644 --- a/udphandler.h +++ b/udphandler.h @@ -80,7 +80,7 @@ public: void printHex(const QByteArray& pdata, bool printVert, bool printHoriz); - QTime timeStarted; + //QTime timeStarted; QUdpSocket* udp=Q_NULLPTR; uint32_t myId = 0; diff --git a/udpserver.cpp b/udpserver.cpp index 3b30467..76b6be3 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -39,7 +39,7 @@ void udpServer::init() ; } srand(time(NULL)); // Generate random - timeStarted.start(); + //timeStarted.start(); // Convoluted way to find the external IP address, there must be a better way???? QString localhostname = QHostInfo::localHostName(); QList hostList = QHostInfo::fromName(localhostname).addresses(); @@ -357,7 +357,7 @@ void udpServer::controlReceived() audioSetup setup; setup.resampleQuality = config->resampleQuality; for (RIGCONFIG* radio : config->rigs) { - if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size()==1) && radio->txaudio == Q_NULLPTR ) + if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size()==1) && radio->txaudio == Q_NULLPTR && !config->lan) { radio->txAudioSetup.codec = current->txCodec; radio->txAudioSetup.format.setSampleRate(current->txSampleRate); @@ -392,7 +392,7 @@ void udpServer::controlReceived() connect(this, SIGNAL(haveAudioData(audioPacket)), radio->txaudio, SLOT(incomingAudio(audioPacket))); } - if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size() == 1) && radio->rxaudio == Q_NULLPTR) + if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size() == 1) && radio->rxaudio == Q_NULLPTR && !config->lan) { #if !defined(PORTAUDIO) && !defined(RTAUDIO) qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio input(RX) :" << radio->rxAudioSetup.port.deviceName(); @@ -1072,7 +1072,8 @@ void udpServer::sendPing(QList* l, CLIENT* c, quint16 seq, bool reply) pingTime = c->rxPingTime; } else { - pingTime = (quint32)timeStarted.msecsSinceStartOfDay(); + QTime now=QTime::currentTime(); + pingTime = (quint32)now.msecsSinceStartOfDay(); seq = c->pingSeq; // Don't increment pingseq until we receive a reply. } diff --git a/udpserver.h b/udpserver.h index b7ca33d..9fd4f4a 100644 --- a/udpserver.h +++ b/udpserver.h @@ -217,7 +217,7 @@ private: QList civClients = QList(); QList audioClients = QList(); - QTime timeStarted; + //QTime timeStarted; audioSetup outAudio; @@ -235,4 +235,4 @@ private: }; -#endif // UDPSERVER_H \ No newline at end of file +#endif // UDPSERVER_H diff --git a/wfmain.cpp b/wfmain.cpp index 32b9e31..d2f4468 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -79,12 +79,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qDebug(logSystem()) << "Running rigConnections()"; rigConnections(); -/* if (serverConfig.enabled && udp != Q_NULLPTR) { - // Server - connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); - connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); - connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); - } */ + setServerToPrefs(); @@ -997,8 +992,7 @@ void wfmain::setServerToPrefs() if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; - qInfo(logAudio()) << "Audio Input device " << serverConfig.rigs.first()->rxAudioSetup.name; - qInfo(logAudio()) << "Audio Output device " << serverConfig.rigs.first()->txAudioSetup.name; + udp = new udpServer(&serverConfig); serverThread = new QThread(this); @@ -1011,12 +1005,17 @@ void wfmain::setServerToPrefs() if (rig != Q_NULLPTR) { connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); + // Need to add a signal/slot for audio from the client to rig. + //connect(udp, SIGNAL(haveAudioData(audioPacket)), rig, SLOT(receiveAudioData(audioPacket))); connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); } - if (!prefs.enableLAN) { + if (serverConfig.lan) { connect(udp, SIGNAL(haveNetworkStatus(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); + } else { + qInfo(logAudio()) << "Audio Input device " << serverConfig.rigs.first()->rxAudioSetup.name; + qInfo(logAudio()) << "Audio Output device " << serverConfig.rigs.first()->txAudioSetup.name; } serverThread->start(); @@ -1435,15 +1434,13 @@ void wfmain::loadSettings() settings->beginGroup("LAN"); prefs.enableLAN = settings->value("EnableLAN", defPrefs.enableLAN).toBool(); - if (prefs.enableLAN) - { - ui->baudRateCombo->setEnabled(false); - ui->serialDeviceListCombo->setEnabled(false); - } - else { - ui->baudRateCombo->setEnabled(true); - ui->serialDeviceListCombo->setEnabled(true); - } + + // If LAN is enabled, server gets its audio straight from the LAN + ui->serverRXAudioInputCombo->setEnabled(!prefs.enableLAN); + ui->serverTXAudioOutputCombo->setEnabled(!prefs.enableLAN); + + ui->baudRateCombo->setEnabled(!prefs.enableLAN); + ui->serialDeviceListCombo->setEnabled(!prefs.enableLAN); ui->lanEnableBtn->setChecked(prefs.enableLAN); ui->connectBtn->setEnabled(true); @@ -4572,6 +4569,8 @@ void wfmain::on_serialEnableBtn_clicked(bool checked) ui->audioInputCombo->setEnabled(!checked); ui->baudRateCombo->setEnabled(checked); ui->serialDeviceListCombo->setEnabled(checked); + ui->serverRXAudioInputCombo->setEnabled(checked); + ui->serverTXAudioOutputCombo->setEnabled(checked); } void wfmain::on_lanEnableBtn_clicked(bool checked) @@ -4593,6 +4592,8 @@ void wfmain::on_lanEnableBtn_clicked(bool checked) ui->audioInputCombo->setEnabled(checked); ui->baudRateCombo->setEnabled(!checked); ui->serialDeviceListCombo->setEnabled(!checked); + ui->serverRXAudioInputCombo->setEnabled(!checked); + ui->serverTXAudioOutputCombo->setEnabled(!checked); if(checked) { showStatusBarText("After filling in values, press Save Settings."); From 5bd29096e5ea04468bbb6534db7b7964c7a5d927 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 5 May 2022 18:48:45 +0100 Subject: [PATCH 223/323] Found issue with splitwaterfall function --- rigcommander.cpp | 2 +- udphandler.cpp | 7 ++++++- udpserver.cpp | 9 +++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/rigcommander.cpp b/rigcommander.cpp index a053810..d4768f8 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -108,7 +108,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud // civAddr = 0x94; // address of the radio. Decimal is 148. civAddr = rigCivAddr; // address of the radio. Decimal is 148. usingNativeLAN = true; - // --- + // -- setup(); // --- diff --git a/udphandler.cpp b/udphandler.cpp index 3280530..0a615d7 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -22,6 +22,10 @@ udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) : { splitWf = true; } + else + { + splitWf = false; + } qInfo(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxSetup.latency << " tx latency:" << txSetup.latency << " rx sample rate: " << rxSetup.format.sampleRate() << " rx codec: " << rxSetup.codec << " tx sample rate: " << txSetup.format.sampleRate() << " tx codec: " << txSetup.codec; @@ -287,7 +291,7 @@ void udpHandler::dataReceived() audioPort = qFromBigEndian(in->audioport); if (!streamOpened) { - civ = new udpCivData(localIP, radioIP, civPort, civLocalPort,splitWf); + civ = new udpCivData(localIP, radioIP, civPort, splitWf, civLocalPort); // TX is not supported if (txSampleRates < 2) { @@ -800,6 +804,7 @@ void udpCivData::dataReceived() // Find data length int pos = r.indexOf(QByteArrayLiteral("\x27\x00\x00"))+2; int len = r.mid(pos).indexOf(QByteArrayLiteral("\xfd")); + //splitWaterfall = false; if (splitWaterfall && pos > 1 && len > 100) { // We need to split waterfall data into its component parts // There are only 2 types that we are currently aware of diff --git a/udpserver.cpp b/udpserver.cpp index 76b6be3..b0e2078 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -1494,9 +1494,10 @@ void udpServer::watchdog() qInfo(logUdpServer()) << "Current client is NULL!"; } } - - status.message = QString("
Server connections: Control:%1 CI-V:%2 Audio:%3
").arg(controlClients.size()).arg(civClients.size()).arg(audioClients.size()); - emit haveNetworkStatus(status); + if (!config->lan) { + status.message = QString("
Server connections: Control:%1 CI-V:%2 Audio:%3
").arg(controlClients.size()).arg(civClients.size()).arg(audioClients.size()); + emit haveNetworkStatus(status); + } } void udpServer::sendStatus(CLIENT* c) @@ -1583,7 +1584,7 @@ void udpServer::dataForServer(QByteArray d) } int lastFE = d.lastIndexOf((quint8)0xfe); - //qInfo(logUdpServer()) << "Server got CIV data from" << radio->rigName << "length" << d.length(); + //qInfo(logUdpServer()) << "Server got CIV data from" << config->rigs.first()->rigName << "length" << d.length() << d.toHex(); if (client->connected && d.length() > lastFE + 2 && ((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId || (quint8)d[lastFE + 1] == 0x00 || (quint8)d[lastFE + 2] == 0x00 || (quint8)d[lastFE + 1] == 0xE1 || (quint8)d[lastFE + 2] == 0xE1)) From 78f16b7cff51b49b9a681f5344d7e89c0ec308d5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 6 May 2022 23:10:46 +0100 Subject: [PATCH 224/323] Add experimental audio converter --- audioconverter.cpp | 293 ++++++++++++++++++++++++ audioconverter.h | 68 ++++++ audiohandler.cpp | 505 ++++++++--------------------------------- audiohandler.h | 41 ++-- logcategories.cpp | 1 + logcategories.h | 1 + wfview.vcxproj | 376 ++++++++---------------------- wfview.vcxproj.filters | 55 +---- 8 files changed, 584 insertions(+), 756 deletions(-) create mode 100644 audioconverter.cpp create mode 100644 audioconverter.h diff --git a/audioconverter.cpp b/audioconverter.cpp new file mode 100644 index 0000000..3545752 --- /dev/null +++ b/audioconverter.cpp @@ -0,0 +1,293 @@ +#include "audioconverter.h" +#include "logcategories.h" +#include "ulaw.h" + +audioConverter::audioConverter(){ +} + +bool audioConverter::init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 opusComplexity, quint8 resampleQuality) +{ + + this->inFormat = inFormat; + this->outFormat = outFormat; + this->opusComplexity = opusComplexity; + this->resampleQuality = resampleQuality; + + qInfo(logAudioConverter) << "Starting audioConverter() Input:" << inFormat.channelCount() << "Channels of" << inFormat.codec() << inFormat.sampleRate() << inFormat.sampleType() << inFormat.sampleSize() << + "Output:" << outFormat.channelCount() << "Channels of" << outFormat.codec() << outFormat.sampleRate() << outFormat.sampleType() << outFormat.sampleSize(); + + + if (inFormat.codec() == "audio/opus") + { + // Create instance of opus decoder + int opus_err = 0; + opusDecoder = opus_decoder_create(inFormat.sampleRate(), inFormat.channelCount(), &opus_err); + qInfo(logAudioConverter()) << "Creating opus decoder: " << opus_strerror(opus_err); + } + + if (outFormat.codec() == "audio/opus") + { + // Create instance of opus encoder + int opus_err = 0; + opusEncoder = opus_encoder_create(outFormat.sampleRate(), outFormat.channelCount(), OPUS_APPLICATION_AUDIO, &opus_err); + opus_encoder_ctl(opusEncoder, OPUS_SET_LSB_DEPTH(16)); + opus_encoder_ctl(opusEncoder, OPUS_SET_INBAND_FEC(1)); + opus_encoder_ctl(opusEncoder, OPUS_SET_DTX(1)); + opus_encoder_ctl(opusEncoder, OPUS_SET_PACKET_LOSS_PERC(5)); + opus_encoder_ctl(opusEncoder, OPUS_SET_COMPLEXITY(opusComplexity)); // Reduce complexity to maybe lower CPU? + qInfo(logAudioConverter()) << "Creating opus encoder: " << opus_strerror(opus_err); + } + + if (inFormat.sampleRate() != outFormat.sampleRate()) + { + int resampleError = 0; + unsigned int ratioNum; + unsigned int ratioDen; + // Sample rate conversion required. + resampler = wf_resampler_init(outFormat.channelCount(), inFormat.sampleRate(), outFormat.sampleRate(), resampleQuality, &resampleError); + wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen); + resampleRatio = static_cast(ratioDen) / ratioNum; + qInfo(logAudioConverter()) << "wf_resampler_init() returned: " << resampleError << " resampleRatio: " << resampleRatio; + } + return true; +} + +audioConverter::~audioConverter() +{ + + qInfo(logAudioConverter) << "Closing audioConverter() Input:" << inFormat.channelCount() << "Channels of" << inFormat.codec() << inFormat.sampleRate() << inFormat.sampleType() << inFormat.sampleSize() << + "Output:" << outFormat.channelCount() << "Channels of" << outFormat.codec() << outFormat.sampleRate() << outFormat.sampleType() << outFormat.sampleSize(); + + if (opusEncoder != Q_NULLPTR) { + qInfo(logAudioConverter()) << "Destroying opus encoder"; + opus_encoder_destroy(opusEncoder); + } + + if (opusDecoder != Q_NULLPTR) { + qInfo(logAudioConverter()) << "Destroying opus decoder"; + opus_decoder_destroy(opusDecoder); + } + + if (resampler != Q_NULLPTR) { + speex_resampler_destroy(resampler); + qDebug(logAudioConverter()) << "Resampler closed"; + } + +} + +bool audioConverter::convert(audioPacket audio) +{ + + if (inFormat.codec() == "audio/opus") + { + unsigned char* in = (unsigned char*)audio.data.data(); + + //Decode the frame. + int nSamples = opus_packet_get_nb_samples(in, audio.data.size(), inFormat.sampleRate()); + if (nSamples == -1) { + // No opus data yet? + return false; + } + QByteArray outPacket(nSamples * sizeof(float) * inFormat.channelCount(), (char)0xff); // Preset the output buffer size. + float* out = (float*)outPacket.data(); + + if (audio.seq > lastAudioSequence + 1) { + nSamples = opus_decode_float(opusDecoder, Q_NULLPTR, 0, out, nSamples, 1); + } + else { + nSamples = opus_decode_float(opusDecoder, in, audio.data.size(), out, nSamples, 0); + } + audio.data.clear(); + audio.data = outPacket; // Replace incoming data with converted. + } + else if (inFormat.codec() == "audio/PCMU") + { + // Current packet is "technically" 8bit so need to create a new buffer that is 16bit + QByteArray outPacket((int)audio.data.length() * 2, (char)0xff); + qint16* out = (qint16*)outPacket.data(); + for (int f = 0; f < audio.data.length(); f++) + { + *out++ = ulaw_decode[(quint8)audio.data[f]]; + } + audio.data.clear(); + audio.data = outPacket; // Replace incoming data with converted. + // Make sure that sample size/type is set correctly + } + + Eigen::VectorXf samplesF; + + if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 32) + { + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(qint32))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); + } + else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 16) + { + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(qint16))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); + } + else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 8) + { + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(qint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } + else if (inFormat.sampleType() == QAudioFormat::UnSignedInt && inFormat.sampleSize() == 8) + { + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(quint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } + else if (inFormat.sampleType() == QAudioFormat::Float) { + samplesF = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(float))); + } + else + { + qInfo(logAudio()) << "Unsupported Sample Type:" << inFormat.sampleType() << "Size:" << inFormat.sampleSize(); + } + + + audio.amplitude = samplesF.array().abs().maxCoeff(); + // Set the volume + samplesF *= audio.volume; + + + + + /* + samplesF is now an Eigen Vector of the current samples in float format + The next step is to convert to the correct number of channels in outFormat.channelCount() + */ + + + if (inFormat.channelCount() == 2 && outFormat.channelCount() == 1) { + // If we need to drop one of the audio channels, do it now + Eigen::VectorXf samplesTemp(samplesF.size() / 2); + samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); + samplesF = samplesTemp; + } + else if (inFormat.channelCount() == 1 && outFormat.channelCount() == 2) { + // Convert mono to stereo if required + Eigen::VectorXf samplesTemp(samplesF.size() * 2); + Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; + Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; + samplesF = samplesTemp; + } + + /* + Next step is to resample (if needed) + */ + + if (resampler != Q_NULLPTR && resampleRatio != 1.0) + { + quint32 outFrames = ((samplesF.size() / outFormat.channelCount()) * resampleRatio); + quint32 inFrames = (samplesF.size() / outFormat.channelCount()); + QByteArray outPacket(outFrames * outFormat.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. + const float* in = (float*)samplesF.data(); + float* out = (float*)outPacket.data(); + + int err = 0; + if (outFormat.channelCount() == 1) { + err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); + } + else { + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + } + + if (err) { + qInfo(logAudioConverter()) << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + } + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } + + /* + Output is Opus so encode it now + */ + + if (outFormat.codec() == "audio/opus") + { + float* in = (float*)samplesF.data(); + QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size + unsigned char* out = (unsigned char*)outPacket.data(); + + int nbBytes = opus_encode_float(opusEncoder, in, (samplesF.size() / outFormat.channelCount()), out, outPacket.length()); + if (nbBytes < 0) + { + qInfo(logAudioConverter()) << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); + return false; + } + else { + outPacket.resize(nbBytes); + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } + + } + + + /* + Now convert back into the output format required + */ + audio.data.clear(); + + if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXuint8 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + } + if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint8 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); + } + if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint16 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + } + else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 32) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint32 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); + } + else if (outFormat.sampleType() == QAudioFormat::Float) + { + audio.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + } + else { + qInfo(logAudio()) << "Unsupported Sample Type:" << outFormat.sampleType(); + } + + /* + As we currently don't have a float based uLaw encoder, this must be done + after all other conversion has taken place. + */ + + if (outFormat.codec() == "audio/PCMU") + { + QByteArray outPacket((int)audio.data.length() / 2, (char)0xff); + qint16* in = (qint16*)audio.data.data(); + for (int f = 0; f < outPacket.length(); f++) + { + qint16 sample = *in++; + int sign = (sample >> 8) & 0x80; + if (sign) + sample = (short)-sample; + if (sample > cClip) + sample = cClip; + sample = (short)(sample + cBias); + int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; + int mantissa = (sample >> (exponent + 3)) & 0x0F; + int compressedByte = ~(sign | (exponent << 4) | mantissa); + outPacket[f] = (quint8)compressedByte; + } + audio.data.clear(); + audio.data = outPacket; // Copy output packet back to input buffer. + + } + + emit converted(audio); + return true; +} + diff --git a/audioconverter.h b/audioconverter.h new file mode 100644 index 0000000..c3cc941 --- /dev/null +++ b/audioconverter.h @@ -0,0 +1,68 @@ +#ifndef AUDIOCONVERTER_H +#define AUDIOCONVERTER_H +#include +#include +#include +#include +#include +#include + +/* Opus and Eigen */ +#ifdef Q_OS_WIN +#include "opus.h" +#include +#else +#include "opus/opus.h" +#include +#endif + +#include "resampler/speex_resampler.h" + +#include "packettypes.h" + +struct audioPacket { + quint32 seq; + QTime time; + quint16 sent; + QByteArray data; + quint8 guid[GUIDLEN]; + float amplitude; + qreal volume = 1.0; +}; + +class audioConverter : public QObject +{ + Q_OBJECT + +public: + audioConverter(); + ~audioConverter(); + +public slots: + bool init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 opusComplexity, quint8 resampleQuality); + bool convert(audioPacket audio); + +signals: + void converted(audioPacket audio); + +protected: + QAudioFormat inFormat; + QAudioFormat outFormat; + OpusEncoder* opusEncoder = Q_NULLPTR; + OpusDecoder* opusDecoder = Q_NULLPTR; + SpeexResamplerState* resampler = Q_NULLPTR; + quint8 opusComplexity; + quint8 resampleQuality = 4; + double resampleRatio=1.0; // Default resample ratio is 1:1 + quint32 lastAudioSequence; +}; + + +// Various audio handling functions declared inline + +typedef Eigen::Matrix VectorXuint8; +typedef Eigen::Matrix VectorXint8; +typedef Eigen::Matrix VectorXint16; +typedef Eigen::Matrix VectorXint32; + +#endif \ No newline at end of file diff --git a/audiohandler.cpp b/audiohandler.cpp index 4a51aab..975a5fd 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -31,18 +31,9 @@ audioHandler::~audioHandler() audioOutput = Q_NULLPTR; } - - if (resampler != Q_NULLPTR) { - speex_resampler_destroy(resampler); - qDebug(logAudio()) << "Resampler closed"; - } - if (encoder != Q_NULLPTR) { - qInfo(logAudio()) << "Destroying opus encoder"; - opus_encoder_destroy(encoder); - } - if (decoder != Q_NULLPTR) { - qInfo(logAudio()) << "Destroying opus decoder"; - opus_decoder_destroy(decoder); + if (converterThread != Q_NULLPTR) { + converterThread->quit(); + converterThread->wait(); } } @@ -58,12 +49,15 @@ bool audioHandler::init(audioSetup setupIn) 0x08 PCM 2ch 8bit 0x10 PCM 2ch 16bit 0x20 uLaw 2ch 8bit + 0x40 Opus 1ch + 0x80 Opus 2ch */ setup = setupIn; setup.format.setChannelCount(1); setup.format.setSampleSize(8); setup.format.setSampleType(QAudioFormat::UnSignedInt); + setup.format.setCodec("audio/pcm"); qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; if (setup.port.isNull()) @@ -76,6 +70,7 @@ bool audioHandler::init(audioSetup setupIn) setup.ulaw = true; setup.format.setSampleSize(16); setup.format.setSampleType(QAudioFormat::SignedInt); + setup.format.setCodec("audio/PCMU"); } if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20 || setup.codec == 0x80) { @@ -88,7 +83,9 @@ bool audioHandler::init(audioSetup setupIn) } if (setup.codec == 0x40 || setup.codec == 0x80) { + setup.format.setSampleSize(32); setup.format.setSampleType(QAudioFormat::Float); + setup.format.setCodec("audio/opus"); } qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name << @@ -151,51 +148,32 @@ bool audioHandler::init(audioSetup setupIn) // We "hopefully" now have a valid format that is supported so try connecting + converter = new audioConverter(); + converterThread = new QThread(this); + converterThread->setObjectName("audioConverter()"); + converter->moveToThread(converterThread); + + connect(this, SIGNAL(setupConverter(QAudioFormat,QAudioFormat,quint8,quint8)), converter, SLOT(init(QAudioFormat,QAudioFormat,quint8,quint8))); + connect(converterThread, SIGNAL(finished()), converter, SLOT(deleteLater())); + connect(this, SIGNAL(sendToConverter(audioPacket)), converter, SLOT(convert(audioPacket))); + converterThread->start(QThread::TimeCriticalPriority); + if (setup.isinput) { audioInput = new QAudioInput(setup.port, format, this); - connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); + emit setupConverter(format, setup.format, 7, setup.resampleQuality); + connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket))); } else { audioOutput = new QAudioOutput(setup.port, format, this); connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); + emit setupConverter(setup.format, format, 7, setup.resampleQuality); + connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket))); } - // Setup resampler and opus if they are needed. - int resample_error = 0; - int opus_err = 0; - if (setup.isinput) { - resampler = wf_resampler_init(format.channelCount(), format.sampleRate(), setup.format.sampleRate(), setup.resampleQuality, &resample_error); - if (setup.codec == 0x40 || setup.codec == 0x80) { - // Opus codec - encoder = opus_encoder_create(setup.format.sampleRate(), setup.format.channelCount(), OPUS_APPLICATION_AUDIO, &opus_err); - opus_encoder_ctl(encoder, OPUS_SET_LSB_DEPTH(16)); - opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1)); - opus_encoder_ctl(encoder, OPUS_SET_DTX(1)); - opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(5)); - opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(7)); // Reduce complexity to maybe lower CPU? - qInfo(logAudio()) << "Creating opus encoder: " << opus_strerror(opus_err); - } - } - else { - //resampBufs = new r8b::CFixedBuffer[format.channelCount()]; - //resamps = new r8b::CPtrKeeper[format.channelCount()]; - resampler = wf_resampler_init(format.channelCount(), setup.format.sampleRate(), format.sampleRate(), setup.resampleQuality, &resample_error); - if (setup.codec == 0x40 || setup.codec == 0x80) { - // Opus codec - decoder = opus_decoder_create(setup.format.sampleRate(), setup.format.channelCount(), &opus_err); - qInfo(logAudio()) << "Creating opus decoder: " << opus_strerror(opus_err); - } - } - unsigned int ratioNum; - unsigned int ratioDen; - wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen); - resampleRatio = static_cast(ratioDen) / ratioNum; - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "wf_resampler_init() returned: " << resample_error << " resampleRatio: " << resampleRatio; - - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "thread id" << QThread::currentThreadId(); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "thread id" << QThread::currentThreadId(); underTimer = new QTimer(); underTimer->setSingleShot(true); @@ -211,9 +189,10 @@ void audioHandler::start() qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "start() running"; if (setup.isinput) { - audioDevice = audioInput->start(); + this->open(QIODevice::WriteOnly); + audioInput->start(this); connect(audioInput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); - connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); + //connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); } else { // Buffer size must be set before audio is started. @@ -249,6 +228,31 @@ void audioHandler::stop() audioDevice = Q_NULLPTR; } +qint64 audioHandler::readData(char* data, qint64 nBytes) { + return nBytes; +} +qint64 audioHandler::writeData(const char* data, qint64 nBytes) { + + tempBuf.data.append(data,nBytes); + + while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { + audioPacket packet; + packet.time = QTime::currentTime(); + packet.sent = 0; + packet.volume = volume; + memcpy(&packet.guid, setup.guid, GUIDLEN); + QTime startProcessing = QTime::currentTime(); + packet.data.clear(); + packet.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); + tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); + + emit sendToConverter(packet); + } + + return nBytes; +} + + void audioHandler::setVolume(unsigned char volume) { this->volume = audiopot[volume]; @@ -256,386 +260,77 @@ void audioHandler::setVolume(unsigned char volume) } -void audioHandler::incomingAudio(audioPacket inPacket) +void audioHandler::incomingAudio(audioPacket packet) { QTime startProcessing = QTime::currentTime(); - audioPacket livePacket = inPacket; - // Process uLaw. - if (setup.ulaw) - { - // Current packet is 8bit so need to create a new buffer that is 16bit - QByteArray outPacket((int)livePacket.data.length() * 2, (char)0xff); - qint16* out = (qint16*)outPacket.data(); - for (int f = 0; f < livePacket.data.length(); f++) - { - *out++ = ulaw_decode[(quint8)livePacket.data[f]]; - } - livePacket.data.clear(); - livePacket.data = outPacket; // Replace incoming data with converted. - } + packet.volume = volume; - - /* Opus data */ - if (setup.codec == 0x40 || setup.codec == 0x80) { - unsigned char* in = (unsigned char*)inPacket.data.data(); - - //Decode the frame. - int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.format.sampleRate()); - if (nSamples == -1) { - // No opus data yet? - return; - } - - QByteArray outPacket(nSamples*sizeof(float)*setup.format.channelCount(), (char)0xff); // Preset the output buffer size. - float* out = (float*)outPacket.data(); - - if (livePacket.seq > lastSentSeq + 1) { - nSamples = opus_decode_float(decoder, Q_NULLPTR,0, out, nSamples, 1); - } - else { - nSamples = opus_decode_float(decoder, in, livePacket.data.size(), out, nSamples, 0); - } - - if (nSamples < 0) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decode failed:" << opus_strerror(nSamples) << "packet size" << livePacket.data.length(); - return; - } - else { - if (int(nSamples * sizeof(float) * setup.format.channelCount()) != outPacket.size()) - { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(float) * setup.format.channelCount() << "outPacket:" << outPacket.size(); - outPacket.resize(nSamples * sizeof(float) * setup.format.channelCount()); - } - //qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoded" << livePacket.data.size() << "bytes, into" << outPacket.length() << "bytes"; - livePacket.data.clear(); - livePacket.data = outPacket; // Replace incoming data with converted. - } - } - - - if (!livePacket.data.isEmpty()) { - - Eigen::VectorXf samplesF; - if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (setup.format.sampleType() == QAudioFormat::Float) { - samplesF = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(float))); - } - else - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); - } - - - /* samplesF is now an Eigen Vector of the current samples in float format */ - - - // Set the max amplitude found in the vector - // Should it be before or after volume is applied? - - amplitude = samplesF.array().abs().maxCoeff(); - // Set the volume - samplesF *= volume; - - - if (setup.format.channelCount() == 2 && format.channelCount() == 1) { - // If we need to drop one of the audio channels, do it now - Eigen::VectorXf samplesTemp(samplesF.size() / 2); - samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); - samplesF = samplesTemp; - } - else if (setup.format.channelCount() == 1 && format.channelCount() == 2) { - // Convert mono to stereo if required - Eigen::VectorXf samplesTemp(samplesF.size() * 2); - Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; - Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; - samplesF = samplesTemp; - } - - // We now have format.channelCount() (native) channels of audio. - - if (resampleRatio != 1.0) { - - // We need to resample - // We have a stereo 16bit stream. - quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); - quint32 inFrames = (samplesF.size() / format.channelCount()); - QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. - const float* in = (float*)samplesF.data(); - float* out = (float*)outPacket.data(); - - int err = 0; - if (format.channelCount() == 1) { - err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); - } - else { - err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); - } - - if (err) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } - - if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXuint8 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); - } - if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint8 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); - } - if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint16 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); - } - else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint32 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); - } - else if (format.sampleType() == QAudioFormat::Float) - { - livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); - } - else { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); - } - - currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize()-audioOutput->bytesFree())/1000); - if (audioDevice != Q_NULLPTR) { - audioDevice->write(livePacket.data); - if (lastReceived.msecsTo(QTime::currentTime()) > 100) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); - } - lastReceived = QTime::currentTime(); - } - if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq; - lastSentSeq = inPacket.seq; - incomingAudio(inPacket); // Call myself again to run the packet a second time (FEC) - } - - lastSentSeq = inPacket.seq; - } - - emit haveLevels(getAmplitude(), setup.latency, currentLatency,isUnderrun); + emit sendToConverter(packet); return; } +void audioHandler::convertedOutput(audioPacket packet) { + + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); + if (audioDevice != Q_NULLPTR) { + audioDevice->write(packet.data); + if (lastReceived.msecsTo(QTime::currentTime()) > 100) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; + } + lastReceived = QTime::currentTime(); + } + + /* + if ((packet.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << packet.seq << "as last is" << lastSentSeq; + lastSentSeq = packet.seq; + incomingAudio(packet); // Call myself again to run the packet a second time (FEC) + } + */ + + lastSentSeq = packet.seq; + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + + amplitude = packet.amplitude; + +} void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { - audioPacket livePacket; - livePacket.time = QTime::currentTime(); - livePacket.sent = 0; - memcpy(&livePacket.guid, setup.guid, GUIDLEN); + audioPacket packet; + packet.time = QTime::currentTime(); + packet.sent = 0; + packet.volume = volume; + memcpy(&packet.guid, setup.guid, GUIDLEN); QTime startProcessing = QTime::currentTime(); - livePacket.data.clear(); - livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); + packet.data.clear(); + packet.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); - if (livePacket.data.length() > 0) - { - Eigen::VectorXf samplesF; - if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (format.sampleType() == QAudioFormat::Float) - { - samplesF = Eigen::Map(reinterpret_cast(livePacket.data.data()), livePacket.data.size() / int(sizeof(float))); - } - else { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType() << format.sampleSize(); - } - /* samplesF is now an Eigen Vector of the current samples in float format */ - - // Set the max amplitude found in the vector - if (samplesF.size() > 0) { - amplitude = samplesF.array().abs().maxCoeff(); - - if (resampleRatio != 1.0) { - - // We need to resample - // We have a stereo 16bit stream. - quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio); - quint32 inFrames = (samplesF.size() / format.channelCount()); - - QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. - const float* in = (float*)samplesF.data(); - float* out = (float*)outPacket.data(); - - int err = 0; - if (format.channelCount() == 1) { - err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); - } - else { - err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); - } - if (err) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } - - // If we need to drop one of the audio channels, do it now - if (format.channelCount() == 2 && setup.format.channelCount() == 1) { - Eigen::VectorXf samplesTemp(samplesF.size() / 2); - samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); - samplesF = samplesTemp; - } - else if (format.channelCount() == 1 && setup.format.channelCount() == 2) { - // Convert mono to stereo if required - Eigen::VectorXf samplesTemp(samplesF.size() * 2); - Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; - Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; - samplesF = samplesTemp; - } - - //qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length() << "resampled" << samplesF.size(); - - if (setup.codec == 0x40 || setup.codec == 0x80) - { - //Are we using the opus codec? - float* in = (float*)samplesF.data(); - - /* Encode the frame. */ - QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size - unsigned char* out = (unsigned char*)outPacket.data(); - - int nbBytes = opus_encode_float(encoder, in, (samplesF.size() / setup.format.channelCount()), out, outPacket.length()); - if (nbBytes < 0) - { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); - return; - } - else { - outPacket.resize(nbBytes); - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } - } - - - if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint8 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); - } - else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXuint8 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); - } - else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint16 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); - } - else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint32 samplesI = samplesITemp.cast(); - livePacket.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); - } - else if (setup.format.sampleType() == QAudioFormat::Float) - { - livePacket.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); - } - else { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType(); - } - - /* Need to find a floating point uLaw encoder!*/ - if (setup.ulaw) - { - QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff); - qint16* in = (qint16*)livePacket.data.data(); - for (int f = 0; f < outPacket.length(); f++) - { - qint16 sample = *in++; - if (setup.ulaw) { - int sign = (sample >> 8) & 0x80; - if (sign) - sample = (short)-sample; - if (sample > cClip) - sample = cClip; - sample = (short)(sample + cBias); - int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - int compressedByte = ~(sign | (exponent << 4) | mantissa); - outPacket[f] = (quint8)compressedByte; - } - } - livePacket.data.clear(); - livePacket.data = outPacket; // Copy output packet back to input buffer. - } - emit haveAudioData(livePacket); - if (lastReceived.msecsTo(QTime::currentTime()) > 100) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize << "Processing time" << startProcessing.msecsTo(QTime::currentTime()); - } - lastReceived = QTime::currentTime(); - //ret = livePacket.data; - } - } - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + emit sendToConverter(packet); } return; } +void audioHandler::convertedInput(audioPacket audio) +{ + emit haveAudioData(audio); + if (lastReceived.msecsTo(QTime::currentTime()) > 100) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize ; + } + lastReceived = QTime::currentTime(); + //ret = livePacket.data; + amplitude = audio.amplitude; + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); +} + void audioHandler::changeLatency(const quint16 newSize) { diff --git a/audiohandler.h b/audiohandler.h index 20929d8..ea2e140 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -48,16 +48,14 @@ /* Logarithmic taper for volume control */ #include "audiotaper.h" + +/* Audio converter class*/ +#include "audioconverter.h" + + #define MULAW_BIAS 33 #define MULAW_MAX 0x1fff -struct audioPacket { - quint32 seq; - QTime time; - quint16 sent; - QByteArray data; - quint8 guid[GUIDLEN]; -}; struct audioSetup { QString name; @@ -74,7 +72,7 @@ struct audioSetup { }; // For QtMultimedia, use a native QIODevice -class audioHandler : public QObject +class audioHandler : public QIODevice { Q_OBJECT @@ -94,6 +92,8 @@ public slots: void changeLatency(const quint16 newSize); void setVolume(unsigned char volume); void incomingAudio(const audioPacket data); + void convertedInput(audioPacket audio); + void convertedOutput(audioPacket audio); private slots: void stateChanged(QAudio::State state); @@ -105,8 +105,16 @@ signals: void sendLatency(quint16 newSize); void haveAudioData(const audioPacket& data); void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under); + void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp); + void sendToConverter(audioPacket audio); + + private: + qint64 readData(char* data, qint64 nBytes); + qint64 writeData(const char* data, qint64 nBytes); + + bool isUnderrun = false; bool isInitialized=false; bool isReady = false; @@ -117,7 +125,9 @@ private: QIODevice* audioDevice=Q_NULLPTR; QAudioFormat format; QAudioDeviceInfo deviceInfo; - SpeexResamplerState* resampler = Q_NULLPTR; + + audioConverter* converter=Q_NULLPTR; + QThread* converterThread = Q_NULLPTR; QTime lastReceived; //r8b::CFixedBuffer* resampBufs; //r8b::CPtrKeeper* resamps; @@ -136,21 +146,14 @@ private: audioPacket tempBuf; quint16 currentLatency; float amplitude; - qreal volume=1.0; + qreal volume = 1.0; audioSetup setup; - OpusEncoder* encoder=Q_NULLPTR; - OpusDecoder* decoder=Q_NULLPTR; + OpusEncoder* encoder = Q_NULLPTR; + OpusDecoder* decoder = Q_NULLPTR; QTimer* underTimer; }; -// Various audio handling functions declared inline - -typedef Eigen::Matrix VectorXuint8; -typedef Eigen::Matrix VectorXint8; -typedef Eigen::Matrix VectorXint16; -typedef Eigen::Matrix VectorXint32; - #endif // AUDIOHANDLER_H diff --git a/logcategories.cpp b/logcategories.cpp index 5719ef1..da4c6d7 100644 --- a/logcategories.cpp +++ b/logcategories.cpp @@ -9,3 +9,4 @@ Q_LOGGING_CATEGORY(logUdp, "udp") Q_LOGGING_CATEGORY(logUdpServer, "udp.server") Q_LOGGING_CATEGORY(logRigCtlD, "rigctld") Q_LOGGING_CATEGORY(logTcpServer, "tcpserver") +Q_LOGGING_CATEGORY(logAudioConverter, "audioconverter") diff --git a/logcategories.h b/logcategories.h index 5f7362a..987278d 100644 --- a/logcategories.h +++ b/logcategories.h @@ -12,6 +12,7 @@ Q_DECLARE_LOGGING_CATEGORY(logUdp) Q_DECLARE_LOGGING_CATEGORY(logUdpServer) Q_DECLARE_LOGGING_CATEGORY(logRigCtlD) Q_DECLARE_LOGGING_CATEGORY(logTcpServer) +Q_DECLARE_LOGGING_CATEGORY(logAudioConverter) #if defined(Q_OS_WIN) && !defined(__PRETTY_FUNCTION__) diff --git a/wfview.vcxproj b/wfview.vcxproj index 5a843a9..7a48226 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,7 +16,8 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild
+ $(MSBuildProjectDirectory)\QtMsBuild + v142 @@ -36,7 +37,10 @@ debug\ wfview - + + + + @@ -44,8 +48,34 @@ - debug\debug\wfviewtruerelease\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport - + + + + + + debug\ + debug\ + wfview + true + + + release\ + release\ + wfview + true + false + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + msvc2019 + core;network;gui;multimedia;widgets;serialport;printsupport + + + + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) @@ -59,12 +89,14 @@ MaxSpeed _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="b510b70";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - + + MultiThreadedDLL true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -87,7 +119,26 @@ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"b510b70\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) @@ -105,7 +156,8 @@ true true Level3 - true + true + ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) @@ -126,9 +178,29 @@ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"b510b70\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(ProjectDir) + ui_%(Filename).h + + + @@ -154,207 +226,54 @@ - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Document true @@ -371,121 +290,21 @@ release\moc_predefs.h;%(Outputs) true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -519,30 +338,16 @@ - - - - - - - - - - resourcesresources + resources + resources + - - - - - - - - - - stylestyle + style + style + @@ -556,6 +361,9 @@ - + + + + \ No newline at end of file diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index b7c023b..a12a594 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -116,6 +116,9 @@ Source Files + + Source Files + @@ -202,61 +205,17 @@ Header Files + + Header Files + - - - - - - - - - - Generated Files Generated Files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -413,6 +372,6 @@ - + \ No newline at end of file From 9118d8f4dc87b0f33ce4bd48aefa4cd976659232 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 6 May 2022 23:12:29 +0100 Subject: [PATCH 225/323] Update audiohandler.cpp --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 975a5fd..a3a850d 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -274,7 +274,7 @@ void audioHandler::incomingAudio(audioPacket packet) void audioHandler::convertedOutput(audioPacket packet) { currentLatency = packet.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); - if (audioDevice != Q_NULLPTR) { + if (audioDevice != Q_NULLPTR && audioOutput != Q_NULLPTR) { audioDevice->write(packet.data); if (lastReceived.msecsTo(QTime::currentTime()) > 100) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; From 550407e0a9850d0af8f5d78bb077ad88eb15a4a8 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 6 May 2022 23:15:32 +0100 Subject: [PATCH 226/323] Update wfserver.pro --- wfserver.pro | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wfserver.pro b/wfserver.pro index a6b2341..1154884 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -142,6 +142,7 @@ SOURCES += main.cpp\ udphandler.cpp \ logcategories.cpp \ audiohandler.cpp \ + audioconverter.cpp \ udpserver.cpp \ pttyhandler.cpp \ resampler/resample.c \ @@ -157,6 +158,7 @@ HEADERS += servermain.h \ udphandler.h \ logcategories.h \ audiohandler.h \ + audioconverter.h \ udpserver.h \ packettypes.h \ pttyhandler.h \ From de6db645def2104a800b0646cfd2bc700f3e462b Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 6 May 2022 23:19:31 +0100 Subject: [PATCH 227/323] Update audioconverter.cpp --- audioconverter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/audioconverter.cpp b/audioconverter.cpp index 3545752..9b4b6b6 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -78,6 +78,11 @@ audioConverter::~audioConverter() bool audioConverter::convert(audioPacket audio) { + if (audio.data.size() == 0) + { + return false; + } + if (inFormat.codec() == "audio/opus") { unsigned char* in = (unsigned char*)audio.data.data(); From 0d529b4a1b5830dd21c8ebcb1da81151105d3cb8 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 6 May 2022 23:27:25 +0100 Subject: [PATCH 228/323] Update audiohandler.cpp --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index a3a850d..1c5927f 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -189,7 +189,7 @@ void audioHandler::start() qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "start() running"; if (setup.isinput) { - this->open(QIODevice::WriteOnly); + this->open(QIODevice::WriteOnly | QIODevice::Unbuffered); audioInput->start(this); connect(audioInput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); //connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); From 1e51e36c9de3320848de6dd63ebe660a3a05c74c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 7 May 2022 10:43:28 +0100 Subject: [PATCH 229/323] Backup out of last change --- audiohandler.cpp | 22 ++++++++++------------ audiohandler.h | 7 ++++--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 1c5927f..2cfafe0 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -189,10 +189,11 @@ void audioHandler::start() qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "start() running"; if (setup.isinput) { - this->open(QIODevice::WriteOnly | QIODevice::Unbuffered); - audioInput->start(this); + //this->open(QIODevice::WriteOnly); + //audioInput->start(this); + audioDevice = audioInput->start(); connect(audioInput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); - //connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); + connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); } else { // Buffer size must be set before audio is started. @@ -228,6 +229,7 @@ void audioHandler::stop() audioDevice = Q_NULLPTR; } +/* qint64 audioHandler::readData(char* data, qint64 nBytes) { return nBytes; } @@ -251,7 +253,7 @@ qint64 audioHandler::writeData(const char* data, qint64 nBytes) { return nBytes; } - +*/ void audioHandler::setVolume(unsigned char volume) { @@ -262,7 +264,7 @@ void audioHandler::setVolume(unsigned char volume) void audioHandler::incomingAudio(audioPacket packet) { - QTime startProcessing = QTime::currentTime(); + //QTime startProcessing = QTime::currentTime(); packet.volume = volume; @@ -274,22 +276,19 @@ void audioHandler::incomingAudio(audioPacket packet) void audioHandler::convertedOutput(audioPacket packet) { currentLatency = packet.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); - if (audioDevice != Q_NULLPTR && audioOutput != Q_NULLPTR) { + if (audioDevice != Q_NULLPTR) { audioDevice->write(packet.data); if (lastReceived.msecsTo(QTime::currentTime()) > 100) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; } lastReceived = QTime::currentTime(); } - - /* - if ((packet.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { + /*if ((packet.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << packet.seq << "as last is" << lastSentSeq; lastSentSeq = packet.seq; incomingAudio(packet); // Call myself again to run the packet a second time (FEC) } */ - lastSentSeq = packet.seq; emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); @@ -307,7 +306,7 @@ void audioHandler::getNextAudioChunk() packet.sent = 0; packet.volume = volume; memcpy(&packet.guid, setup.guid, GUIDLEN); - QTime startProcessing = QTime::currentTime(); + //QTime startProcessing = QTime::currentTime(); packet.data.clear(); packet.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); @@ -326,7 +325,6 @@ void audioHandler::convertedInput(audioPacket audio) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize ; } lastReceived = QTime::currentTime(); - //ret = livePacket.data; amplitude = audio.amplitude; emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); } diff --git a/audiohandler.h b/audiohandler.h index ea2e140..01a512e 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -72,7 +72,8 @@ struct audioSetup { }; // For QtMultimedia, use a native QIODevice -class audioHandler : public QIODevice +//class audioHandler : public QIODevice +class audioHandler : public QObject { Q_OBJECT @@ -111,8 +112,8 @@ signals: private: - qint64 readData(char* data, qint64 nBytes); - qint64 writeData(const char* data, qint64 nBytes); + //qint64 readData(char* data, qint64 nBytes); + //qint64 writeData(const char* data, qint64 nBytes); bool isUnderrun = false; From 05c3433bcd9394d8fad73ac84d2d98f0534ac0d7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 7 May 2022 10:51:02 +0100 Subject: [PATCH 230/323] More testing --- audiohandler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 2cfafe0..ff1e088 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -193,7 +193,9 @@ void audioHandler::start() //audioInput->start(this); audioDevice = audioInput->start(); connect(audioInput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); - connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); + connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::DirectConnection); + //audioInput->setNotifyInterval(setup.blockSize/2); + //connect(audioInput, SIGNAL(notify()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); } else { // Buffer size must be set before audio is started. From 3068b0f5ccb21f927ed589025c74f81dc4156149 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 7 May 2022 10:54:20 +0100 Subject: [PATCH 231/323] Update audiohandler.cpp --- audiohandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index ff1e088..acd351b 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -193,7 +193,7 @@ void audioHandler::start() //audioInput->start(this); audioDevice = audioInput->start(); connect(audioInput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); - connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::DirectConnection); + connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); //audioInput->setNotifyInterval(setup.blockSize/2); //connect(audioInput, SIGNAL(notify()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); } From bbbb36ebaecadbebcd91daa7d06dcc306c315ead Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 7 May 2022 11:14:01 +0100 Subject: [PATCH 232/323] Remove Opus FEC for now --- audioconverter.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index 9b4b6b6..6905167 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -96,12 +96,13 @@ bool audioConverter::convert(audioPacket audio) QByteArray outPacket(nSamples * sizeof(float) * inFormat.channelCount(), (char)0xff); // Preset the output buffer size. float* out = (float*)outPacket.data(); - if (audio.seq > lastAudioSequence + 1) { - nSamples = opus_decode_float(opusDecoder, Q_NULLPTR, 0, out, nSamples, 1); - } - else { - nSamples = opus_decode_float(opusDecoder, in, audio.data.size(), out, nSamples, 0); - } + //if (audio.seq > lastAudioSequence + 1) { + // nSamples = opus_decode_float(opusDecoder, Q_NULLPTR, 0, out, nSamples, 1); + //} + //else { + nSamples = opus_decode_float(opusDecoder, in, audio.data.size(), out, nSamples, 0); + //} + //lastAudioSequence = audio.seq; audio.data.clear(); audio.data = outPacket; // Replace incoming data with converted. } From 71f88cdfedc024ea16dab28bf3050592b46db3b4 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 10:04:36 +0100 Subject: [PATCH 233/323] Skip unneeded float conversion for Opus --- audioconverter.cpp | 135 ++++++++++++++++++++++++--------------------- audiohandler.cpp | 8 ++- 2 files changed, 76 insertions(+), 67 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index 6905167..8a0558c 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -16,6 +16,10 @@ bool audioConverter::init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 qInfo(logAudioConverter) << "Starting audioConverter() Input:" << inFormat.channelCount() << "Channels of" << inFormat.codec() << inFormat.sampleRate() << inFormat.sampleType() << inFormat.sampleSize() << "Output:" << outFormat.channelCount() << "Channels of" << outFormat.codec() << outFormat.sampleRate() << outFormat.sampleType() << outFormat.sampleSize(); + if (inFormat.byteOrder() != outFormat.byteOrder()) { + qInfo(logAudioConverter) << "Byteorder mismatch in:" << inFormat.byteOrder() << "out:" << outFormat.byteOrder(); + } + if (inFormat.codec() == "audio/opus") { @@ -31,7 +35,7 @@ bool audioConverter::init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 int opus_err = 0; opusEncoder = opus_encoder_create(outFormat.sampleRate(), outFormat.channelCount(), OPUS_APPLICATION_AUDIO, &opus_err); opus_encoder_ctl(opusEncoder, OPUS_SET_LSB_DEPTH(16)); - opus_encoder_ctl(opusEncoder, OPUS_SET_INBAND_FEC(1)); + //opus_encoder_ctl(opusEncoder, OPUS_SET_INBAND_FEC(1)); opus_encoder_ctl(opusEncoder, OPUS_SET_DTX(1)); opus_encoder_ctl(opusEncoder, OPUS_SET_PACKET_LOSS_PERC(5)); opus_encoder_ctl(opusEncoder, OPUS_SET_COMPLEXITY(opusComplexity)); // Reduce complexity to maybe lower CPU? @@ -222,77 +226,80 @@ bool audioConverter::convert(audioPacket audio) } else { outPacket.resize(nbBytes); - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + audio.data.clear(); + audio.data = outPacket; // Copy output packet back to input buffer. + //samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); } } - - - /* - Now convert back into the output format required - */ - audio.data.clear(); - - if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXuint8 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); - } - if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint8 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); - } - if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint16 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); - } - else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 32) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint32 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); - } - else if (outFormat.sampleType() == QAudioFormat::Float) - { - audio.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); - } else { - qInfo(logAudio()) << "Unsupported Sample Type:" << outFormat.sampleType(); - } - /* - As we currently don't have a float based uLaw encoder, this must be done - after all other conversion has taken place. - */ - - if (outFormat.codec() == "audio/PCMU") - { - QByteArray outPacket((int)audio.data.length() / 2, (char)0xff); - qint16* in = (qint16*)audio.data.data(); - for (int f = 0; f < outPacket.length(); f++) - { - qint16 sample = *in++; - int sign = (sample >> 8) & 0x80; - if (sign) - sample = (short)-sample; - if (sample > cClip) - sample = cClip; - sample = (short)(sample + cBias); - int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - int compressedByte = ~(sign | (exponent << 4) | mantissa); - outPacket[f] = (quint8)compressedByte; - } + + /* + Now convert back into the output format required + */ audio.data.clear(); - audio.data = outPacket; // Copy output packet back to input buffer. + if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXuint8 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + } + if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint8 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); + } + if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint16 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + } + else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 32) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint32 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); + } + else if (outFormat.sampleType() == QAudioFormat::Float) + { + audio.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + } + else { + qInfo(logAudio()) << "Unsupported Sample Type:" << outFormat.sampleType(); + } + + /* + As we currently don't have a float based uLaw encoder, this must be done + after all other conversion has taken place. + */ + + if (outFormat.codec() == "audio/PCMU") + { + QByteArray outPacket((int)audio.data.length() / 2, (char)0xff); + qint16* in = (qint16*)audio.data.data(); + for (int f = 0; f < outPacket.length(); f++) + { + qint16 sample = *in++; + int sign = (sample >> 8) & 0x80; + if (sign) + sample = (short)-sample; + if (sample > cClip) + sample = cClip; + sample = (short)(sample + cBias); + int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; + int mantissa = (sample >> (exponent + 3)) & 0x0F; + int compressedByte = ~(sign | (exponent << 4) | mantissa); + outPacket[f] = (quint8)compressedByte; + } + audio.data.clear(); + audio.data = outPacket; // Copy output packet back to input buffer. + + } } - emit converted(audio); return true; } diff --git a/audiohandler.cpp b/audiohandler.cpp index acd351b..81a88f4 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -57,6 +57,7 @@ bool audioHandler::init(audioSetup setupIn) setup.format.setChannelCount(1); setup.format.setSampleSize(8); setup.format.setSampleType(QAudioFormat::UnSignedInt); + setup.format.setByteOrder(QAudioFormat::LittleEndian); setup.format.setCodec("audio/pcm"); qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; @@ -267,10 +268,11 @@ void audioHandler::setVolume(unsigned char volume) void audioHandler::incomingAudio(audioPacket packet) { //QTime startProcessing = QTime::currentTime(); + if (audioDevice != Q_NULLPTR) { + packet.volume = volume; - packet.volume = volume; - - emit sendToConverter(packet); + emit sendToConverter(packet); + } return; } From 72bef7f783397e9a92602efbafe33ff491e26076 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 10:19:21 +0100 Subject: [PATCH 234/323] Fix build for new audioconverter --- wfview.pro | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wfview.pro b/wfview.pro index 61d2218..203dc88 100644 --- a/wfview.pro +++ b/wfview.pro @@ -165,6 +165,7 @@ SOURCES += main.cpp\ udphandler.cpp \ logcategories.cpp \ audiohandler.cpp \ + audioconverter.cpp \ calibrationwindow.cpp \ satellitesetup.cpp \ udpserver.cpp \ @@ -187,6 +188,7 @@ HEADERS += wfmain.h \ udphandler.h \ logcategories.h \ audiohandler.h \ + audioconverter.h \ calibrationwindow.h \ satellitesetup.h \ udpserver.h \ From 8e38a8efb254a01cb4468c678e38b7cbd7987b26 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 11:46:33 +0100 Subject: [PATCH 235/323] Create function to convert Icom format to QAudioFormat --- audioconverter.cpp | 2 +- audioconverter.h | 47 ++++++++++++++ audiohandler.cpp | 148 ++++++++++++++------------------------------- audiohandler.h | 5 +- udphandler.cpp | 15 ++--- udpserver.cpp | 4 +- wfmain.cpp | 14 ++--- 7 files changed, 111 insertions(+), 124 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index 8a0558c..44bd25f 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -209,7 +209,7 @@ bool audioConverter::convert(audioPacket audio) } /* - Output is Opus so encode it now + If output is Opus so encode it now, don't do any more conversion on the output of Opus. */ if (outFormat.codec() == "audio/opus") diff --git a/audioconverter.h b/audioconverter.h index c3cc941..2b4f309 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -65,4 +65,51 @@ typedef Eigen::Matrix VectorXint8; typedef Eigen::Matrix VectorXint16; typedef Eigen::Matrix VectorXint32; +static QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) +{ + QAudioFormat format; + + /* + 0x01 uLaw 1ch 8bit + 0x02 PCM 1ch 8bit + 0x04 PCM 1ch 16bit + 0x08 PCM 2ch 8bit + 0x10 PCM 2ch 16bit + 0x20 uLaw 2ch 8bit + 0x40 Opus 1ch + 0x80 Opus 2ch + */ + + format.setChannelCount(1); + format.setSampleSize(8); + format.setSampleType(QAudioFormat::UnSignedInt); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setCodec("audio/pcm"); + + if (codec == 0x01 || codec == 0x20) { + /* Set sample to be what is expected by the encoder and the output of the decoder */ + format.setSampleSize(16); + format.setSampleType(QAudioFormat::SignedInt); + format.setCodec("audio/PCMU"); + } + + if (codec == 0x08 || codec == 0x10 || codec == 0x20 || codec == 0x80) { + format.setChannelCount(2); + } + + if (codec == 0x04 || codec == 0x10) { + format.setSampleSize(16); + format.setSampleType(QAudioFormat::SignedInt); + } + + if (codec == 0x40 || codec == 0x80) { + format.setSampleSize(32); + format.setSampleType(QAudioFormat::Float); + format.setCodec("audio/opus"); + } + + format.setSampleRate(sampleRate); + return format; +} + #endif \ No newline at end of file diff --git a/audiohandler.cpp b/audiohandler.cpp index 81a88f4..aaf6b34 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -37,121 +37,89 @@ audioHandler::~audioHandler() } } -bool audioHandler::init(audioSetup setupIn) +bool audioHandler::init(audioSetup setup) { if (isInitialized) { return false; } - /* - 0x01 uLaw 1ch 8bit - 0x02 PCM 1ch 8bit - 0x04 PCM 1ch 16bit - 0x08 PCM 2ch 8bit - 0x10 PCM 2ch 16bit - 0x20 uLaw 2ch 8bit - 0x40 Opus 1ch - 0x80 Opus 2ch - */ - setup = setupIn; - setup.format.setChannelCount(1); - setup.format.setSampleSize(8); - setup.format.setSampleType(QAudioFormat::UnSignedInt); - setup.format.setByteOrder(QAudioFormat::LittleEndian); - setup.format.setCodec("audio/pcm"); + this->setup = setup; qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; - if (setup.port.isNull()) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins."; return false; } - if (setup.codec == 0x01 || setup.codec == 0x20) { - setup.ulaw = true; - setup.format.setSampleSize(16); - setup.format.setSampleType(QAudioFormat::SignedInt); - setup.format.setCodec("audio/PCMU"); - } - - if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20 || setup.codec == 0x80) { - setup.format.setChannelCount(2); - } - - if (setup.codec == 0x04 || setup.codec == 0x10) { - setup.format.setSampleSize(16); - setup.format.setSampleType(QAudioFormat::SignedInt); - } - - if (setup.codec == 0x40 || setup.codec == 0x80) { - setup.format.setSampleSize(32); - setup.format.setSampleType(QAudioFormat::Float); - setup.format.setCodec("audio/opus"); - } - qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name << - ", bits" << setup.format.sampleSize() << + ", bits" << inFormat.sampleSize() << ", codec" << setup.codec << ", latency" << setup.latency << ", localAFGain" << setup.localAFgain << - ", radioChan" << setup.format.channelCount() << + ", radioChan" << inFormat.channelCount() << ", resampleQuality" << setup.resampleQuality << - ", samplerate" << setup.format.sampleRate() << + ", samplerate" << inFormat.sampleRate() << ", uLaw" << setup.ulaw; + inFormat = toQAudioFormat(setup.codec, setup.sampleRate); if(!setup.isinput) { this->setVolume(setup.localAFgain); } - format = setup.port.preferredFormat(); - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Preferred Format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() << - "Sample Rate" << format.sampleRate() << "Codec" << format.codec() << "Sample Type" << format.sampleType(); - if (format.channelCount() > 2) { - format.setChannelCount(2); + outFormat = setup.port.preferredFormat(); + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Preferred Format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() << + "Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType(); + if (outFormat.channelCount() > 2) { + outFormat.setChannelCount(2); } - else if (format.channelCount() < 1) + else if (outFormat.channelCount() < 1) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup."; return false; } - if (format.channelCount() == 1 && setup.format.channelCount() == 2) { - format.setChannelCount(2); - if (!setup.port.isFormatSupported(format)) { + if (outFormat.channelCount() == 1 && inFormat.channelCount() == 2) { + outFormat.setChannelCount(2); + if (!setup.port.isFormatSupported(outFormat)) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request stereo input!"; - format.setChannelCount(1); + outFormat.setChannelCount(1); } } - if (format.sampleType()==QAudioFormat::SignedInt) { - format.setSampleType(QAudioFormat::Float); - format.setSampleSize(32); - if (!setup.port.isFormatSupported(format)) { + if (outFormat.sampleType()==QAudioFormat::SignedInt) { + outFormat.setSampleType(QAudioFormat::Float); + outFormat.setSampleSize(32); + if (!setup.port.isFormatSupported(outFormat)) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempt to select 32bit Float failed, reverting to SignedInt"; - format.setSampleType(QAudioFormat::SignedInt); - format.setSampleSize(16); + outFormat.setSampleType(QAudioFormat::SignedInt); + outFormat.setSampleSize(16); } } - if (format.sampleSize() == 24) { + if (outFormat.sampleSize() == 24) { // We can't convert this easily so use 32 bit instead. - format.setSampleSize(32); - if (!setup.port.isFormatSupported(format)) { + outFormat.setSampleSize(32); + if (!setup.port.isFormatSupported(outFormat)) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "24 bit requested and 32 bit audio not supported, try 16 bit instead"; - format.setSampleSize(16); + outFormat.setSampleSize(16); } } - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() << - "Sample Rate" << format.sampleRate() << "Codec" << format.codec() << "Sample Type" << format.sampleType(); + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() << + "Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType(); // We "hopefully" now have a valid format that is supported so try connecting converter = new audioConverter(); converterThread = new QThread(this); - converterThread->setObjectName("audioConverter()"); + if (setup.isinput) { + converterThread->setObjectName("audioConvIn()"); + } + else { + converterThread->setObjectName("audioConvOut()"); + } converter->moveToThread(converterThread); connect(this, SIGNAL(setupConverter(QAudioFormat,QAudioFormat,quint8,quint8)), converter, SLOT(init(QAudioFormat,QAudioFormat,quint8,quint8))); @@ -160,15 +128,15 @@ bool audioHandler::init(audioSetup setupIn) converterThread->start(QThread::TimeCriticalPriority); if (setup.isinput) { - audioInput = new QAudioInput(setup.port, format, this); + audioInput = new QAudioInput(setup.port, outFormat, this); connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); - emit setupConverter(format, setup.format, 7, setup.resampleQuality); + emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality); connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket))); } else { - audioOutput = new QAudioOutput(setup.port, format, this); + audioOutput = new QAudioOutput(setup.port, outFormat, this); connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); - emit setupConverter(setup.format, format, 7, setup.resampleQuality); + emit setupConverter(inFormat, outFormat, 7, setup.resampleQuality); connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket))); } @@ -201,9 +169,9 @@ void audioHandler::start() else { // Buffer size must be set before audio is started. #ifdef Q_OS_WIN - audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 100)); + audioOutput->setBufferSize(outFormat.bytesForDuration(setup.latency * 100)); #else - audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 1000)); + audioOutput->setBufferSize(outFormat.bytesForDuration(setup.latency * 1000)); #endif audioDevice = audioOutput->start(); connect(audioOutput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); @@ -232,32 +200,6 @@ void audioHandler::stop() audioDevice = Q_NULLPTR; } -/* -qint64 audioHandler::readData(char* data, qint64 nBytes) { - return nBytes; -} -qint64 audioHandler::writeData(const char* data, qint64 nBytes) { - - tempBuf.data.append(data,nBytes); - - while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { - audioPacket packet; - packet.time = QTime::currentTime(); - packet.sent = 0; - packet.volume = volume; - memcpy(&packet.guid, setup.guid, GUIDLEN); - QTime startProcessing = QTime::currentTime(); - packet.data.clear(); - packet.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); - tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); - - emit sendToConverter(packet); - } - - return nBytes; -} -*/ - void audioHandler::setVolume(unsigned char volume) { this->volume = audiopot[volume]; @@ -279,7 +221,7 @@ void audioHandler::incomingAudio(audioPacket packet) void audioHandler::convertedOutput(audioPacket packet) { - currentLatency = packet.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); if (audioDevice != Q_NULLPTR) { audioDevice->write(packet.data); if (lastReceived.msecsTo(QTime::currentTime()) > 100) { @@ -304,7 +246,7 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { + while (tempBuf.data.length() >= outFormat.bytesForDuration(setup.blockSize * 1000)) { audioPacket packet; packet.time = QTime::currentTime(); packet.sent = 0; @@ -312,8 +254,8 @@ void audioHandler::getNextAudioChunk() memcpy(&packet.guid, setup.guid, GUIDLEN); //QTime startProcessing = QTime::currentTime(); packet.data.clear(); - packet.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); - tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); + packet.data = tempBuf.data.mid(0, outFormat.bytesForDuration(setup.blockSize * 1000)); + tempBuf.data.remove(0, outFormat.bytesForDuration(setup.blockSize * 1000)); emit sendToConverter(packet); } @@ -342,7 +284,7 @@ void audioHandler::changeLatency(const quint16 newSize) stop(); start(); } - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << format.durationForBytes(audioOutput->bufferSize())/1000 << "ms"; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << outFormat.durationForBytes(audioOutput->bufferSize())/1000 << "ms"; } diff --git a/audiohandler.h b/audiohandler.h index 01a512e..c5ac1e7 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -63,7 +63,7 @@ struct audioSetup { quint8 codec; bool ulaw = false; bool isinput; - QAudioFormat format; // Use this for all audio APIs + quint32 sampleRate; QAudioDeviceInfo port; quint8 resampleQuality; unsigned char localAFgain; @@ -124,7 +124,8 @@ private: QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; QIODevice* audioDevice=Q_NULLPTR; - QAudioFormat format; + QAudioFormat inFormat; + QAudioFormat outFormat; QAudioDeviceInfo deviceInfo; audioConverter* converter=Q_NULLPTR; diff --git a/udphandler.cpp b/udphandler.cpp index 0a615d7..5084ee9 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -26,8 +26,8 @@ udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) : { splitWf = false; } - qInfo(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxSetup.latency << " tx latency:" << txSetup.latency << " rx sample rate: " << rxSetup.format.sampleRate() << - " rx codec: " << rxSetup.codec << " tx sample rate: " << txSetup.format.sampleRate() << " tx codec: " << txSetup.codec; + qInfo(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxSetup.latency << " tx latency:" << txSetup.latency << " rx sample rate: " << rxSetup.sampleRate << + " rx codec: " << rxSetup.codec << " tx sample rate: " << txSetup.sampleRate << " tx codec: " << txSetup.codec; // Try to set the IP address, if it is a hostname then perform a DNS lookup. if (!radioIP.setAddress(prefs.ipAddress)) @@ -295,7 +295,7 @@ void udpHandler::dataReceived() // TX is not supported if (txSampleRates < 2) { - txSetup.format.setSampleRate(0); + txSetup.sampleRate=0; txSetup.codec = 0; } audio = new udpAudio(localIP, radioIP, audioPort, audioLocalPort, rxSetup, txSetup); @@ -545,8 +545,8 @@ void udpHandler::sendRequestStream() } p.rxcodec = rxSetup.codec; memcpy(&p.username, usernameEncoded.constData(), usernameEncoded.length()); - p.rxsample = qToBigEndian((quint32)rxSetup.format.sampleRate()); - p.txsample = qToBigEndian((quint32)txSetup.format.sampleRate()); + p.rxsample = qToBigEndian((quint32)rxSetup.sampleRate); + p.txsample = qToBigEndian((quint32)txSetup.sampleRate); p.civport = qToBigEndian((quint32)civLocalPort); p.audioport = qToBigEndian((quint32)audioLocalPort); p.txbuffer = qToBigEndian((quint32)txSetup.latency); @@ -897,7 +897,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint this->port = audioPort; this->radioIP = ip; - if (txSetup.format.sampleRate() == 0) { + if (txSetup.sampleRate == 0) { enableTx = false; } @@ -922,9 +922,6 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16,bool))); connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); - txSetup.format.setChannelCount(1); // TX Audio is always single channel. - - sendControl(false, 0x03, 0x00); // First connect packet diff --git a/udpserver.cpp b/udpserver.cpp index b0e2078..74ed8e4 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -360,7 +360,7 @@ void udpServer::controlReceived() if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size()==1) && radio->txaudio == Q_NULLPTR && !config->lan) { radio->txAudioSetup.codec = current->txCodec; - radio->txAudioSetup.format.setSampleRate(current->txSampleRate); + radio->txAudioSetup.sampleRate=current->txSampleRate; radio->txAudioSetup.isinput = false; radio->txAudioSetup.latency = current->txBufferLen; @@ -403,7 +403,7 @@ void udpServer::controlReceived() #endif radio->rxAudioSetup.codec = current->rxCodec; - radio->rxAudioSetup.format.setSampleRate(current->rxSampleRate); + radio->rxAudioSetup.sampleRate=current->rxSampleRate; radio->rxAudioSetup.latency = current->txBufferLen; radio->rxAudioSetup.isinput = true; memcpy(radio->rxAudioSetup.guid, radio->guid, GUIDLEN); diff --git a/wfmain.cpp b/wfmain.cpp index d2f4468..26b86a0 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1490,11 +1490,11 @@ void wfmain::loadSettings() ui->txLatencySlider->setTracking(false); // Stop it sending value on every change. ui->audioSampleRateCombo->blockSignals(true); - rxSetup.format.setSampleRate(settings->value("AudioRXSampleRate", "48000").toInt()); - txSetup.format.setSampleRate(rxSetup.format.sampleRate()); + rxSetup.sampleRate=settings->value("AudioRXSampleRate", "48000").toInt(); + txSetup.sampleRate=rxSetup.sampleRate; ui->audioSampleRateCombo->setEnabled(ui->lanEnableBtn->isChecked()); - int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(rxSetup.format.sampleRate())); + int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(rxSetup.sampleRate)); if (audioSampleRateIndex != -1) { ui->audioSampleRateCombo->setCurrentIndex(audioSampleRateIndex); } @@ -1893,9 +1893,9 @@ void wfmain::saveSettings() settings->setValue("Password", udpPrefs.password); settings->setValue("AudioRXLatency", rxSetup.latency); settings->setValue("AudioTXLatency", txSetup.latency); - settings->setValue("AudioRXSampleRate", rxSetup.format.sampleRate()); + settings->setValue("AudioRXSampleRate", rxSetup.sampleRate); settings->setValue("AudioRXCodec", rxSetup.codec); - settings->setValue("AudioTXSampleRate", txSetup.format.sampleRate()); + settings->setValue("AudioTXSampleRate", txSetup.sampleRate); settings->setValue("AudioTXCodec", txSetup.codec); settings->setValue("AudioOutput", rxSetup.name); settings->setValue("AudioInput", txSetup.name); @@ -4642,8 +4642,8 @@ void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text) { //udpPrefs.audioRXSampleRate = text.toInt(); //udpPrefs.audioTXSampleRate = text.toInt(); - rxSetup.format.setSampleRate(text.toInt()); - txSetup.format.setSampleRate(text.toInt()); + rxSetup.sampleRate=text.toInt(); + txSetup.sampleRate=text.toInt(); } void wfmain::on_audioRXCodecCombo_currentIndexChanged(int value) From 376fe72b497c3e14590f67a0f19c2f6181c8eb82 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 11:52:24 +0100 Subject: [PATCH 236/323] Update audioconverter.h --- audioconverter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audioconverter.h b/audioconverter.h index 2b4f309..faa4d62 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -65,7 +65,7 @@ typedef Eigen::Matrix VectorXint8; typedef Eigen::Matrix VectorXint16; typedef Eigen::Matrix VectorXint32; -static QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) +QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) { QAudioFormat format; From 3deb7ab6ecb762d3a2f9ffcf3c6f070eef5db331 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 11:54:42 +0100 Subject: [PATCH 237/323] Update audioconverter.h --- audioconverter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audioconverter.h b/audioconverter.h index faa4d62..5a279b0 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -65,7 +65,7 @@ typedef Eigen::Matrix VectorXint8; typedef Eigen::Matrix VectorXint16; typedef Eigen::Matrix VectorXint32; -QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) +static inline QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) { QAudioFormat format; From 181c2eeb0712aab1384be1ece086d8ef29d48c0a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 12:19:35 +0100 Subject: [PATCH 238/323] Log when port is being closed --- commhandler.cpp | 1 + selectradio.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/commhandler.cpp b/commhandler.cpp index f234509..fd0f381 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -65,6 +65,7 @@ commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat) commHandler::~commHandler() { + qInfo(logSerial()) << "Closing serial port: " << port->portName(); if (isConnected) { this->closePort(); } diff --git a/selectradio.cpp b/selectradio.cpp index 43cf093..0862ace 100644 --- a/selectradio.cpp +++ b/selectradio.cpp @@ -35,10 +35,10 @@ void selectRadio::populate(QList radios) void selectRadio::setInUse(quint8 radio, quint8 busy, QString user, QString ip) { - if ((radio > 0)&& !this->isVisible()) { - qInfo() << "setInUse: radio:" << radio <<"busy" << busy << "user" << user << "ip"<setVisible(true); - } + //if ((radio > 0)&& !this->isVisible()) { + // qInfo() << "setInUse: radio:" << radio <<"busy" << busy << "user" << user << "ip"<setVisible(true); + //} ui->table->setItem(radio, 3, new QTableWidgetItem(user)); ui->table->setItem(radio, 4, new QTableWidgetItem(ip)); for (int f = 0; f < 5; f++) { From b8838e24682269a35b148bd696f9c840f5424a7a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 18:44:20 +0100 Subject: [PATCH 239/323] some improvements to audio --- audioconverter.cpp | 383 +++++++++++++++++++++++---------------------- audioconverter.h | 4 +- audiohandler.cpp | 70 +++++---- 3 files changed, 234 insertions(+), 223 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index 44bd25f..edc6ec7 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -82,224 +82,225 @@ audioConverter::~audioConverter() bool audioConverter::convert(audioPacket audio) { - if (audio.data.size() == 0) + // If inFormat and outFormat are identical, just emit the data back. + if (audio.data.size() != 0 && inFormat != outFormat) { - return false; - } - if (inFormat.codec() == "audio/opus") - { - unsigned char* in = (unsigned char*)audio.data.data(); + if (inFormat.codec() == "audio/opus") + { + unsigned char* in = (unsigned char*)audio.data.data(); - //Decode the frame. - int nSamples = opus_packet_get_nb_samples(in, audio.data.size(), inFormat.sampleRate()); - if (nSamples == -1) { - // No opus data yet? - return false; - } - QByteArray outPacket(nSamples * sizeof(float) * inFormat.channelCount(), (char)0xff); // Preset the output buffer size. - float* out = (float*)outPacket.data(); + //Decode the frame. + int nSamples = opus_packet_get_nb_samples(in, audio.data.size(), inFormat.sampleRate()); + if (nSamples == -1) { + // No opus data yet? + return false; + } + QByteArray outPacket(nSamples * sizeof(float) * inFormat.channelCount(), (char)0xff); // Preset the output buffer size. + float* out = (float*)outPacket.data(); - //if (audio.seq > lastAudioSequence + 1) { - // nSamples = opus_decode_float(opusDecoder, Q_NULLPTR, 0, out, nSamples, 1); - //} - //else { - nSamples = opus_decode_float(opusDecoder, in, audio.data.size(), out, nSamples, 0); - //} - //lastAudioSequence = audio.seq; - audio.data.clear(); - audio.data = outPacket; // Replace incoming data with converted. - } - else if (inFormat.codec() == "audio/PCMU") - { - // Current packet is "technically" 8bit so need to create a new buffer that is 16bit - QByteArray outPacket((int)audio.data.length() * 2, (char)0xff); - qint16* out = (qint16*)outPacket.data(); - for (int f = 0; f < audio.data.length(); f++) - { - *out++ = ulaw_decode[(quint8)audio.data[f]]; - } - audio.data.clear(); - audio.data = outPacket; // Replace incoming data with converted. - // Make sure that sample size/type is set correctly - } - - Eigen::VectorXf samplesF; + //if (audio.seq > lastAudioSequence + 1) { + // nSamples = opus_decode_float(opusDecoder, Q_NULLPTR, 0, out, nSamples, 1); + //} + //else { + nSamples = opus_decode_float(opusDecoder, in, audio.data.size(), out, nSamples, 0); + //} + //lastAudioSequence = audio.seq; + audio.data.clear(); + audio.data = outPacket; // Replace incoming data with converted. + } + else if (inFormat.codec() == "audio/PCMU") + { + // Current packet is "technically" 8bit so need to create a new buffer that is 16bit + QByteArray outPacket((int)audio.data.length() * 2, (char)0xff); + qint16* out = (qint16*)outPacket.data(); + for (int f = 0; f < audio.data.length(); f++) + { + *out++ = ulaw_decode[(quint8)audio.data[f]]; + } + audio.data.clear(); + audio.data = outPacket; // Replace incoming data with converted. + // Make sure that sample size/type is set correctly + } - if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 32) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(qint32))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 16) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(qint16))); - samplesF = samplesI.cast() / float(std::numeric_limits::max()); - } - else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 8) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(qint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (inFormat.sampleType() == QAudioFormat::UnSignedInt && inFormat.sampleSize() == 8) - { - Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(quint8))); - samplesF = samplesI.cast() / float(std::numeric_limits::max());; - } - else if (inFormat.sampleType() == QAudioFormat::Float) { - samplesF = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(float))); - } - else - { - qInfo(logAudio()) << "Unsupported Sample Type:" << inFormat.sampleType() << "Size:" << inFormat.sampleSize(); - } + Eigen::VectorXf samplesF; + + if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 32) + { + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(qint32))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); + } + else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 16) + { + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(qint16))); + samplesF = samplesI.cast() / float(std::numeric_limits::max()); + } + else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 8) + { + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(qint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } + else if (inFormat.sampleType() == QAudioFormat::UnSignedInt && inFormat.sampleSize() == 8) + { + Eigen::Ref samplesI = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(quint8))); + samplesF = samplesI.cast() / float(std::numeric_limits::max());; + } + else if (inFormat.sampleType() == QAudioFormat::Float) { + samplesF = Eigen::Map(reinterpret_cast(audio.data.data()), audio.data.size() / int(sizeof(float))); + } + else + { + qInfo(logAudio()) << "Unsupported Sample Type:" << inFormat.sampleType() << "Size:" << inFormat.sampleSize(); + } - audio.amplitude = samplesF.array().abs().maxCoeff(); - // Set the volume - samplesF *= audio.volume; + audio.amplitude = samplesF.array().abs().maxCoeff(); + // Set the volume + samplesF *= audio.volume; - /* - samplesF is now an Eigen Vector of the current samples in float format - The next step is to convert to the correct number of channels in outFormat.channelCount() - */ + /* + samplesF is now an Eigen Vector of the current samples in float format + The next step is to convert to the correct number of channels in outFormat.channelCount() + */ - if (inFormat.channelCount() == 2 && outFormat.channelCount() == 1) { - // If we need to drop one of the audio channels, do it now - Eigen::VectorXf samplesTemp(samplesF.size() / 2); - samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); - samplesF = samplesTemp; - } - else if (inFormat.channelCount() == 1 && outFormat.channelCount() == 2) { - // Convert mono to stereo if required - Eigen::VectorXf samplesTemp(samplesF.size() * 2); - Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; - Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; - samplesF = samplesTemp; - } + if (inFormat.channelCount() == 2 && outFormat.channelCount() == 1) { + // If we need to drop one of the audio channels, do it now + Eigen::VectorXf samplesTemp(samplesF.size() / 2); + samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); + samplesF = samplesTemp; + } + else if (inFormat.channelCount() == 1 && outFormat.channelCount() == 2) { + // Convert mono to stereo if required + Eigen::VectorXf samplesTemp(samplesF.size() * 2); + Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; + Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; + samplesF = samplesTemp; + } - /* - Next step is to resample (if needed) - */ + /* + Next step is to resample (if needed) + */ - if (resampler != Q_NULLPTR && resampleRatio != 1.0) - { - quint32 outFrames = ((samplesF.size() / outFormat.channelCount()) * resampleRatio); - quint32 inFrames = (samplesF.size() / outFormat.channelCount()); - QByteArray outPacket(outFrames * outFormat.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. - const float* in = (float*)samplesF.data(); - float* out = (float*)outPacket.data(); + if (resampler != Q_NULLPTR && resampleRatio != 1.0) + { + quint32 outFrames = ((samplesF.size() / outFormat.channelCount()) * resampleRatio); + quint32 inFrames = (samplesF.size() / outFormat.channelCount()); + QByteArray outPacket(outFrames * outFormat.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. + const float* in = (float*)samplesF.data(); + float* out = (float*)outPacket.data(); - int err = 0; - if (outFormat.channelCount() == 1) { - err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); - } - else { - err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); - } + int err = 0; + if (outFormat.channelCount() == 1) { + err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); + } + else { + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); + } - if (err) { - qInfo(logAudioConverter()) << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } + if (err) { + qInfo(logAudioConverter()) << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + } + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } - /* - If output is Opus so encode it now, don't do any more conversion on the output of Opus. - */ + /* + If output is Opus so encode it now, don't do any more conversion on the output of Opus. + */ - if (outFormat.codec() == "audio/opus") - { - float* in = (float*)samplesF.data(); - QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size - unsigned char* out = (unsigned char*)outPacket.data(); + if (outFormat.codec() == "audio/opus") + { + float* in = (float*)samplesF.data(); + QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size + unsigned char* out = (unsigned char*)outPacket.data(); - int nbBytes = opus_encode_float(opusEncoder, in, (samplesF.size() / outFormat.channelCount()), out, outPacket.length()); - if (nbBytes < 0) - { - qInfo(logAudioConverter()) << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); - return false; - } - else { - outPacket.resize(nbBytes); - audio.data.clear(); - audio.data = outPacket; // Copy output packet back to input buffer. - //samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } + int nbBytes = opus_encode_float(opusEncoder, in, (samplesF.size() / outFormat.channelCount()), out, outPacket.length()); + if (nbBytes < 0) + { + qInfo(logAudioConverter()) << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); + return false; + } + else { + outPacket.resize(nbBytes); + audio.data.clear(); + audio.data = outPacket; // Copy output packet back to input buffer. + //samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } - } - else { + } + else { - /* - Now convert back into the output format required - */ - audio.data.clear(); + /* + Now convert back into the output format required + */ + audio.data.clear(); - if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXuint8 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); - } - if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint8 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); - } - if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint16 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); - } - else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 32) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint32 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); - } - else if (outFormat.sampleType() == QAudioFormat::Float) - { - audio.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); - } - else { - qInfo(logAudio()) << "Unsupported Sample Type:" << outFormat.sampleType(); - } + if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXuint8 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + } + if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint8 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); + } + if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint16 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + } + else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 32) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint32 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); + } + else if (outFormat.sampleType() == QAudioFormat::Float) + { + audio.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + } + else { + qInfo(logAudio()) << "Unsupported Sample Type:" << outFormat.sampleType(); + } - /* - As we currently don't have a float based uLaw encoder, this must be done - after all other conversion has taken place. - */ + /* + As we currently don't have a float based uLaw encoder, this must be done + after all other conversion has taken place. + */ - if (outFormat.codec() == "audio/PCMU") - { - QByteArray outPacket((int)audio.data.length() / 2, (char)0xff); - qint16* in = (qint16*)audio.data.data(); - for (int f = 0; f < outPacket.length(); f++) - { - qint16 sample = *in++; - int sign = (sample >> 8) & 0x80; - if (sign) - sample = (short)-sample; - if (sample > cClip) - sample = cClip; - sample = (short)(sample + cBias); - int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - int compressedByte = ~(sign | (exponent << 4) | mantissa); - outPacket[f] = (quint8)compressedByte; - } - audio.data.clear(); - audio.data = outPacket; // Copy output packet back to input buffer. + if (outFormat.codec() == "audio/PCMU") + { + QByteArray outPacket((int)audio.data.length() / 2, (char)0xff); + qint16* in = (qint16*)audio.data.data(); + for (int f = 0; f < outPacket.length(); f++) + { + qint16 sample = *in++; + int sign = (sample >> 8) & 0x80; + if (sign) + sample = (short)-sample; + if (sample > cClip) + sample = cClip; + sample = (short)(sample + cBias); + int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; + int mantissa = (sample >> (exponent + 3)) & 0x0F; + int compressedByte = ~(sign | (exponent << 4) | mantissa); + outPacket[f] = (quint8)compressedByte; + } + audio.data.clear(); + audio.data = outPacket; // Copy output packet back to input buffer. + + } + } + } - } - } emit converted(audio); return true; } diff --git a/audioconverter.h b/audioconverter.h index 5a279b0..cd598b4 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -85,6 +85,7 @@ static inline QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) format.setSampleType(QAudioFormat::UnSignedInt); format.setByteOrder(QAudioFormat::LittleEndian); format.setCodec("audio/pcm"); + format.setSampleRate(sampleRate); if (codec == 0x01 || codec == 0x20) { /* Set sample to be what is expected by the encoder and the output of the decoder */ @@ -108,8 +109,7 @@ static inline QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) format.setCodec("audio/opus"); } - format.setSampleRate(sampleRate); return format; } -#endif \ No newline at end of file +#endif diff --git a/audiohandler.cpp b/audiohandler.cpp index aaf6b34..b4678e3 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -209,8 +209,8 @@ void audioHandler::setVolume(unsigned char volume) void audioHandler::incomingAudio(audioPacket packet) { - //QTime startProcessing = QTime::currentTime(); - if (audioDevice != Q_NULLPTR) { + + if (audioDevice != Q_NULLPTR && packet.data.size() > 0) { packet.volume = volume; emit sendToConverter(packet); @@ -221,32 +221,35 @@ void audioHandler::incomingAudio(audioPacket packet) void audioHandler::convertedOutput(audioPacket packet) { - currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); - if (audioDevice != Q_NULLPTR) { - audioDevice->write(packet.data); - if (lastReceived.msecsTo(QTime::currentTime()) > 100) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; - } - lastReceived = QTime::currentTime(); - } - /*if ((packet.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << packet.seq << "as last is" << lastSentSeq; - lastSentSeq = packet.seq; - incomingAudio(packet); // Call myself again to run the packet a second time (FEC) - } - */ - lastSentSeq = packet.seq; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + if (packet.data.size() > 0 ) { - amplitude = packet.amplitude; + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); + if (audioDevice != Q_NULLPTR) { + audioDevice->write(packet.data); + if (lastReceived.msecsTo(QTime::currentTime()) > 100) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; + } + lastReceived = QTime::currentTime(); + } + /*if ((packet.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << packet.seq << "as last is" << lastSentSeq; + lastSentSeq = packet.seq; + incomingAudio(packet); // Call myself again to run the packet a second time (FEC) + } + */ + lastSentSeq = packet.seq; + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + amplitude = packet.amplitude; + } } void audioHandler::getNextAudioChunk() { - tempBuf.data.append(audioDevice->readAll()); - - while (tempBuf.data.length() >= outFormat.bytesForDuration(setup.blockSize * 1000)) { + if (audioDevice) { + tempBuf.data.append(audioDevice->readAll()); + } + if (tempBuf.data.length() >= outFormat.bytesForDuration(setup.blockSize * 1000)) { audioPacket packet; packet.time = QTime::currentTime(); packet.sent = 0; @@ -259,20 +262,27 @@ void audioHandler::getNextAudioChunk() emit sendToConverter(packet); } - return; + /* If there is still enough data in the buffer, call myself again in 20ms */ + if (tempBuf.data.length() >= outFormat.bytesForDuration(setup.blockSize * 1000)) { + QTimer::singleShot(setup.blockSize, this, &audioHandler::getNextAudioChunk); + } + + return; } void audioHandler::convertedInput(audioPacket audio) { - emit haveAudioData(audio); - if (lastReceived.msecsTo(QTime::currentTime()) > 100) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize ; - } - lastReceived = QTime::currentTime(); - amplitude = audio.amplitude; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + if (audio.data.size() > 0) { + emit haveAudioData(audio); + if (lastReceived.msecsTo(QTime::currentTime()) > 100) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize ; + } + lastReceived = QTime::currentTime(); + amplitude = audio.amplitude; + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + } } void audioHandler::changeLatency(const quint16 newSize) From 7cb31cc8afc5006e650f30476fb986c408e9630c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 18:45:22 +0100 Subject: [PATCH 240/323] Update udphandler.cpp --- udphandler.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/udphandler.cpp b/udphandler.cpp index 5084ee9..2413bc0 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -90,10 +90,12 @@ udpHandler::~udpHandler() if (streamOpened) { if (audio != Q_NULLPTR) { delete audio; + audio = Q_NULLPTR; } if (civ != Q_NULLPTR) { delete civ; + civ = Q_NULLPTR; } qInfo(logUdp()) << "Sending token removal packet"; sendToken(0x01); @@ -282,7 +284,7 @@ void udpHandler::dataReceived() delete civ; civ = Q_NULLPTR; } - + streamOpened = false; } } @@ -474,6 +476,20 @@ void udpHandler::dataReceived() void udpHandler::setCurrentRadio(quint8 radio) { + + // If we are currently connected to a different radio, disconnect first + if (audio != Q_NULLPTR) { + delete audio; + audio = Q_NULLPTR; + } + + if (civ != Q_NULLPTR) { + delete civ; + civ = Q_NULLPTR; + } + + streamOpened = false; + qInfo(logUdp()) << "Got Radio" << radio; qInfo(logUdp()) << "Find available local ports"; From ac677db7ac20ade73ecbf8aa0ff03ff5b63f28c5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 19:31:05 +0100 Subject: [PATCH 241/323] Change class constructors to include parent --- audioconverter.cpp | 3 ++- audioconverter.h | 2 +- audiohandler.cpp | 4 ++-- audiohandler.h | 2 +- commhandler.cpp | 4 ++-- commhandler.h | 4 ++-- pttyhandler.cpp | 2 +- pttyhandler.h | 2 +- rigcommander.cpp | 10 +++++----- rigcommander.h | 4 ++-- rigctld.h | 2 +- servermain.cpp | 4 ++-- udphandler.cpp | 4 ++-- udpserver.cpp | 9 +++++---- udpserver.h | 2 +- wfmain.cpp | 4 ++-- 16 files changed, 32 insertions(+), 30 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index edc6ec7..a835050 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -2,7 +2,8 @@ #include "logcategories.h" #include "ulaw.h" -audioConverter::audioConverter(){ +audioConverter::audioConverter(QObject* parent) : QObject(parent) +{ } bool audioConverter::init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 opusComplexity, quint8 resampleQuality) diff --git a/audioconverter.h b/audioconverter.h index cd598b4..8c784f0 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -35,7 +35,7 @@ class audioConverter : public QObject Q_OBJECT public: - audioConverter(); + explicit audioConverter(QObject* parent = nullptr);; ~audioConverter(); public slots: diff --git a/audiohandler.cpp b/audiohandler.cpp index b4678e3..95b2de5 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -10,7 +10,7 @@ -audioHandler::audioHandler(QObject* parent) +audioHandler::audioHandler(QObject* parent) : QObject(parent) { Q_UNUSED(parent) } @@ -112,7 +112,7 @@ bool audioHandler::init(audioSetup setup) // We "hopefully" now have a valid format that is supported so try connecting - converter = new audioConverter(); + converter = new audioConverter(this); converterThread = new QThread(this); if (setup.isinput) { converterThread->setObjectName("audioConvIn()"); diff --git a/audiohandler.h b/audiohandler.h index c5ac1e7..f9ec2a1 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -78,7 +78,7 @@ class audioHandler : public QObject Q_OBJECT public: - audioHandler(QObject* parent = 0); + explicit audioHandler(QObject* parent = nullptr); ~audioHandler(); int getLatency(); diff --git a/commhandler.cpp b/commhandler.cpp index fd0f381..be6fb5b 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -5,7 +5,7 @@ // Copyright 2017-2020 Elliott H. Liggett -commHandler::commHandler() +commHandler::commHandler(QObject* parent) : QObject(parent) { //constructor // grab baud rate and other comm port details @@ -31,7 +31,7 @@ commHandler::commHandler() connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn())); } -commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat) +commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QObject* parent) : QObject(parent) { //constructor // grab baud rate and other comm port details diff --git a/commhandler.h b/commhandler.h index 4baeacd..5546175 100644 --- a/commhandler.h +++ b/commhandler.h @@ -15,8 +15,8 @@ class commHandler : public QObject Q_OBJECT public: - commHandler(); - commHandler(QString portName, quint32 baudRate, quint8 wfFormat); + commHandler(QObject* parent = nullptr); + commHandler(QString portName, quint32 baudRate, quint8 wfFormat,QObject* parent = nullptr); bool serialError; bool rtsStatus(); diff --git a/pttyhandler.cpp b/pttyhandler.cpp index 0639bc0..60ea0ae 100644 --- a/pttyhandler.cpp +++ b/pttyhandler.cpp @@ -13,7 +13,7 @@ // Copyright 2017-2021 Elliott H. Liggett & Phil Taylor -pttyHandler::pttyHandler(QString pty) +pttyHandler::pttyHandler(QString pty, QObject* parent) : QObject(parent) { //constructor if (pty == "" || pty.toLower() == "none") diff --git a/pttyhandler.h b/pttyhandler.h index 3c18206..d056e77 100644 --- a/pttyhandler.h +++ b/pttyhandler.h @@ -19,7 +19,7 @@ class pttyHandler : public QObject Q_OBJECT public: - pttyHandler(QString portName); + explicit pttyHandler(QString portName, QObject* parent = nullptr); pttyHandler(QString portName, quint32 baudRate); bool serialError; diff --git a/rigcommander.cpp b/rigcommander.cpp index d4768f8..60efd9f 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -20,13 +20,13 @@ // Note: When sending \x00, must use QByteArray.setRawData() -rigCommander::rigCommander() +rigCommander::rigCommander(QObject* parent) : QObject(parent) { qInfo(logRig()) << "creating instance of rigCommander()"; state.set(SCOPEFUNC, true, false); } -rigCommander::rigCommander(quint8 guid[GUIDLEN]) +rigCommander::rigCommander(quint8 guid[GUIDLEN], QObject* parent) : QObject(parent) { qInfo(logRig()) << "creating instance of rigCommander()"; state.set(SCOPEFUNC, true, false); @@ -59,8 +59,8 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu this->rigBaudRate = rigBaudRate; rigCaps.baudRate = rigBaudRate; - comm = new commHandler(rigSerialPort, rigBaudRate,wf); - ptty = new pttyHandler(vsp); + comm = new commHandler(rigSerialPort, rigBaudRate,wf,this); + ptty = new pttyHandler(vsp,this); if (tcpPort > 0) { tcp = new tcpServer(this); @@ -132,7 +132,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud //this->rigSerialPort = rigSerialPort; //this->rigBaudRate = rigBaudRate; - ptty = new pttyHandler(vsp); + ptty = new pttyHandler(vsp,this); if (tcpPort > 0) { tcp = new tcpServer(this); diff --git a/rigcommander.h b/rigcommander.h index dc1b153..7921b24 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -69,8 +69,8 @@ class rigCommander : public QObject Q_OBJECT public: - rigCommander(); - rigCommander(quint8 guid[GUIDLEN]); + explicit rigCommander(QObject* parent=nullptr); + explicit rigCommander(quint8 guid[GUIDLEN], QObject* parent = nullptr); ~rigCommander(); bool usingLAN(); diff --git a/rigctld.h b/rigctld.h index d86818d..2018576 100644 --- a/rigctld.h +++ b/rigctld.h @@ -326,7 +326,7 @@ class rigCtlD : public QTcpServer Q_OBJECT public: - explicit rigCtlD(QObject *parent=Q_NULLPTR); + explicit rigCtlD(QObject *parent=nullptr); virtual ~rigCtlD(); int startServer(qint16 port); diff --git a/servermain.cpp b/servermain.cpp index 2db93d3..8802cb7 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -102,7 +102,7 @@ void servermain::makeRig() if (radio->rigThread == Q_NULLPTR) { qInfo(logSystem()) << "Creating new rigThread()"; - radio->rig = new rigCommander(radio->guid); + radio->rig = new rigCommander(radio->guid,this); radio->rigThread = new QThread(this); radio->rigThread->setObjectName("rigCommander()"); @@ -376,7 +376,7 @@ void servermain::setServerToPrefs() udp = Q_NULLPTR; } - udp = new udpServer(&serverConfig); + udp = new udpServer(&serverConfig,this); serverThread = new QThread(this); serverThread->setObjectName("udpServer()"); diff --git a/udphandler.cpp b/udphandler.cpp index 2413bc0..a235e16 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -921,7 +921,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); - rxaudio = new audioHandler(); + rxaudio = new audioHandler(this); rxAudioThread = new QThread(this); rxAudioThread->setObjectName("rxAudio()"); @@ -946,7 +946,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint pingTimer->start(PING_PERIOD); // send ping packets every 100ms if (enableTx) { - txaudio = new audioHandler(); + txaudio = new audioHandler(this); txAudioThread = new QThread(this); rxAudioThread->setObjectName("txAudio()"); diff --git a/udpserver.cpp b/udpserver.cpp index 74ed8e4..0387fc0 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -4,8 +4,9 @@ #define STALE_CONNECTION 15 #define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms #define AUDIO_SEND_PERIOD 20 // -udpServer::udpServer(SERVERCONFIG* config) : - config(config) +udpServer::udpServer(SERVERCONFIG* config, QObject* parent) : + config(config), + QObject(parent) { qInfo(logUdpServer()) << "Starting udp server"; } @@ -366,7 +367,7 @@ void udpServer::controlReceived() outAudio.isinput = false; - radio->txaudio = new audioHandler(); + radio->txaudio = new audioHandler(this); radio->txAudioThread = new QThread(this); radio->txAudioThread->setObjectName("txAudio()"); @@ -408,7 +409,7 @@ void udpServer::controlReceived() radio->rxAudioSetup.isinput = true; memcpy(radio->rxAudioSetup.guid, radio->guid, GUIDLEN); - radio->rxaudio = new audioHandler(); + radio->rxaudio = new audioHandler(this); radio->rxAudioThread = new QThread(this); radio->rxAudioThread->setObjectName("rxAudio()"); diff --git a/udpserver.h b/udpserver.h index 9fd4f4a..fe1682b 100644 --- a/udpserver.h +++ b/udpserver.h @@ -99,7 +99,7 @@ class udpServer : public QObject Q_OBJECT public: - udpServer(SERVERCONFIG* config); + explicit udpServer(SERVERCONFIG* config, QObject* parent = nullptr); ~udpServer(); public slots: diff --git a/wfmain.cpp b/wfmain.cpp index 26b86a0..3dac678 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -405,7 +405,7 @@ void wfmain::makeRig() { if (rigThread == Q_NULLPTR) { - rig = new rigCommander(); + rig = new rigCommander(this); rigThread = new QThread(this); rigThread->setObjectName("rigCommander()"); @@ -993,7 +993,7 @@ void wfmain::setServerToPrefs() if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; - udp = new udpServer(&serverConfig); + udp = new udpServer(&serverConfig,this); serverThread = new QThread(this); From ed3ba16a8d34c17b7510ee0c47ab27ce71729e2a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 19:33:22 +0100 Subject: [PATCH 242/323] Update udpserver.cpp --- udpserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udpserver.cpp b/udpserver.cpp index 0387fc0..6f8b9c2 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -5,8 +5,8 @@ #define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms #define AUDIO_SEND_PERIOD 20 // udpServer::udpServer(SERVERCONFIG* config, QObject* parent) : - config(config), - QObject(parent) + QObject(parent), + config(config) { qInfo(logUdpServer()) << "Starting udp server"; } From 135640df448fee40ff7c8627228538d27aef9080 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 19:35:47 +0100 Subject: [PATCH 243/323] Remove parent from classes that are moved to a thread --- audiohandler.cpp | 2 +- servermain.cpp | 2 +- udphandler.cpp | 4 ++-- udpserver.cpp | 4 ++-- wfmain.cpp | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 95b2de5..311f1d0 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -112,7 +112,7 @@ bool audioHandler::init(audioSetup setup) // We "hopefully" now have a valid format that is supported so try connecting - converter = new audioConverter(this); + converter = new audioConverter(); converterThread = new QThread(this); if (setup.isinput) { converterThread->setObjectName("audioConvIn()"); diff --git a/servermain.cpp b/servermain.cpp index 8802cb7..1800523 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -102,7 +102,7 @@ void servermain::makeRig() if (radio->rigThread == Q_NULLPTR) { qInfo(logSystem()) << "Creating new rigThread()"; - radio->rig = new rigCommander(radio->guid,this); + radio->rig = new rigCommander(radio->guid); radio->rigThread = new QThread(this); radio->rigThread->setObjectName("rigCommander()"); diff --git a/udphandler.cpp b/udphandler.cpp index a235e16..2413bc0 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -921,7 +921,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); - rxaudio = new audioHandler(this); + rxaudio = new audioHandler(); rxAudioThread = new QThread(this); rxAudioThread->setObjectName("rxAudio()"); @@ -946,7 +946,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint pingTimer->start(PING_PERIOD); // send ping packets every 100ms if (enableTx) { - txaudio = new audioHandler(this); + txaudio = new audioHandler(); txAudioThread = new QThread(this); rxAudioThread->setObjectName("txAudio()"); diff --git a/udpserver.cpp b/udpserver.cpp index 6f8b9c2..3b5e505 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -367,7 +367,7 @@ void udpServer::controlReceived() outAudio.isinput = false; - radio->txaudio = new audioHandler(this); + radio->txaudio = new audioHandler(); radio->txAudioThread = new QThread(this); radio->txAudioThread->setObjectName("txAudio()"); @@ -409,7 +409,7 @@ void udpServer::controlReceived() radio->rxAudioSetup.isinput = true; memcpy(radio->rxAudioSetup.guid, radio->guid, GUIDLEN); - radio->rxaudio = new audioHandler(this); + radio->rxaudio = new audioHandler(); radio->rxAudioThread = new QThread(this); radio->rxAudioThread->setObjectName("rxAudio()"); diff --git a/wfmain.cpp b/wfmain.cpp index 3dac678..26b86a0 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -405,7 +405,7 @@ void wfmain::makeRig() { if (rigThread == Q_NULLPTR) { - rig = new rigCommander(this); + rig = new rigCommander(); rigThread = new QThread(this); rigThread->setObjectName("rigCommander()"); @@ -993,7 +993,7 @@ void wfmain::setServerToPrefs() if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; - udp = new udpServer(&serverConfig,this); + udp = new udpServer(&serverConfig); serverThread = new QThread(this); From 75f1f2d6ea35119838ee31f1c776f14b2bb66d1a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 19:37:29 +0100 Subject: [PATCH 244/323] Update servermain.cpp --- servermain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servermain.cpp b/servermain.cpp index 1800523..2db93d3 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -376,7 +376,7 @@ void servermain::setServerToPrefs() udp = Q_NULLPTR; } - udp = new udpServer(&serverConfig,this); + udp = new udpServer(&serverConfig); serverThread = new QThread(this); serverThread->setObjectName("udpServer()"); From 70985641216d753f2df96a4008675d8e34bc9a47 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 19:44:31 +0100 Subject: [PATCH 245/323] use quit() instead of exit() to cleanly shutdown --- keyboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keyboard.cpp b/keyboard.cpp index ccddeab..f6f4b15 100644 --- a/keyboard.cpp +++ b/keyboard.cpp @@ -19,7 +19,7 @@ void keyboard::run() { char key = getchar(); if (key == 'q') { - QCoreApplication::exit(0); + QCoreApplication::quit(); } } return; From 3e0c0081447c254ded55e254b3000fd2dd1c7596 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 20:57:35 +0100 Subject: [PATCH 246/323] Fix if 0 samples detected --- audioconverter.cpp | 260 ++++++++++++++++++++++----------------------- 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index a835050..e95c761 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -84,7 +84,7 @@ bool audioConverter::convert(audioPacket audio) { // If inFormat and outFormat are identical, just emit the data back. - if (audio.data.size() != 0 && inFormat != outFormat) + if (audio.data.size() > 0 && inFormat != outFormat) { if (inFormat.codec() == "audio/opus") @@ -154,150 +154,150 @@ bool audioConverter::convert(audioPacket audio) qInfo(logAudio()) << "Unsupported Sample Type:" << inFormat.sampleType() << "Size:" << inFormat.sampleSize(); } + if (samplesF.size() > 0) - audio.amplitude = samplesF.array().abs().maxCoeff(); - // Set the volume - samplesF *= audio.volume; - - - - - /* - samplesF is now an Eigen Vector of the current samples in float format - The next step is to convert to the correct number of channels in outFormat.channelCount() - */ - - - if (inFormat.channelCount() == 2 && outFormat.channelCount() == 1) { - // If we need to drop one of the audio channels, do it now - Eigen::VectorXf samplesTemp(samplesF.size() / 2); - samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); - samplesF = samplesTemp; - } - else if (inFormat.channelCount() == 1 && outFormat.channelCount() == 2) { - // Convert mono to stereo if required - Eigen::VectorXf samplesTemp(samplesF.size() * 2); - Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; - Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; - samplesF = samplesTemp; - } - - /* - Next step is to resample (if needed) - */ - - if (resampler != Q_NULLPTR && resampleRatio != 1.0) { - quint32 outFrames = ((samplesF.size() / outFormat.channelCount()) * resampleRatio); - quint32 inFrames = (samplesF.size() / outFormat.channelCount()); - QByteArray outPacket(outFrames * outFormat.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. - const float* in = (float*)samplesF.data(); - float* out = (float*)outPacket.data(); - - int err = 0; - if (outFormat.channelCount() == 1) { - err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); - } - else { - err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); - } - - if (err) { - qInfo(logAudioConverter()) << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } - - /* - If output is Opus so encode it now, don't do any more conversion on the output of Opus. - */ - - if (outFormat.codec() == "audio/opus") - { - float* in = (float*)samplesF.data(); - QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size - unsigned char* out = (unsigned char*)outPacket.data(); - - int nbBytes = opus_encode_float(opusEncoder, in, (samplesF.size() / outFormat.channelCount()), out, outPacket.length()); - if (nbBytes < 0) - { - qInfo(logAudioConverter()) << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); - return false; - } - else { - outPacket.resize(nbBytes); - audio.data.clear(); - audio.data = outPacket; // Copy output packet back to input buffer. - //samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); - } - - } - else { - + audio.amplitude = samplesF.array().abs().maxCoeff(); + // Set the volume + samplesF *= audio.volume; /* - Now convert back into the output format required + samplesF is now an Eigen Vector of the current samples in float format + The next step is to convert to the correct number of channels in outFormat.channelCount() */ - audio.data.clear(); - if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXuint8 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + + if (inFormat.channelCount() == 2 && outFormat.channelCount() == 1) { + // If we need to drop one of the audio channels, do it now + Eigen::VectorXf samplesTemp(samplesF.size() / 2); + samplesTemp = Eigen::Map >(samplesF.data(), samplesF.size() / 2); + samplesF = samplesTemp; } - if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint8 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); - } - if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint16 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); - } - else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 32) - { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); - VectorXint32 samplesI = samplesITemp.cast(); - audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); - } - else if (outFormat.sampleType() == QAudioFormat::Float) - { - audio.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); - } - else { - qInfo(logAudio()) << "Unsupported Sample Type:" << outFormat.sampleType(); + else if (inFormat.channelCount() == 1 && outFormat.channelCount() == 2) { + // Convert mono to stereo if required + Eigen::VectorXf samplesTemp(samplesF.size() * 2); + Eigen::Map >(samplesTemp.data(), samplesF.size()) = samplesF; + Eigen::Map >(samplesTemp.data() + 1, samplesF.size()) = samplesF; + samplesF = samplesTemp; } /* - As we currently don't have a float based uLaw encoder, this must be done - after all other conversion has taken place. + Next step is to resample (if needed) */ - if (outFormat.codec() == "audio/PCMU") + if (resampler != Q_NULLPTR && resampleRatio != 1.0) { - QByteArray outPacket((int)audio.data.length() / 2, (char)0xff); - qint16* in = (qint16*)audio.data.data(); - for (int f = 0; f < outPacket.length(); f++) - { - qint16 sample = *in++; - int sign = (sample >> 8) & 0x80; - if (sign) - sample = (short)-sample; - if (sample > cClip) - sample = cClip; - sample = (short)(sample + cBias); - int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - int compressedByte = ~(sign | (exponent << 4) | mantissa); - outPacket[f] = (quint8)compressedByte; + quint32 outFrames = ((samplesF.size() / outFormat.channelCount()) * resampleRatio); + quint32 inFrames = (samplesF.size() / outFormat.channelCount()); + QByteArray outPacket(outFrames * outFormat.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size. + const float* in = (float*)samplesF.data(); + float* out = (float*)outPacket.data(); + + int err = 0; + if (outFormat.channelCount() == 1) { + err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames); + } + else { + err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames); } - audio.data.clear(); - audio.data = outPacket; // Copy output packet back to input buffer. + if (err) { + qInfo(logAudioConverter()) << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + } + samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } + + /* + If output is Opus so encode it now, don't do any more conversion on the output of Opus. + */ + + if (outFormat.codec() == "audio/opus") + { + float* in = (float*)samplesF.data(); + QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size + unsigned char* out = (unsigned char*)outPacket.data(); + + int nbBytes = opus_encode_float(opusEncoder, in, (samplesF.size() / outFormat.channelCount()), out, outPacket.length()); + if (nbBytes < 0) + { + qInfo(logAudioConverter()) << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size(); + return false; + } + else { + outPacket.resize(nbBytes); + audio.data.clear(); + audio.data = outPacket; // Copy output packet back to input buffer. + //samplesF = Eigen::Map(reinterpret_cast(outPacket.data()), outPacket.size() / int(sizeof(float))); + } + + } + else { + + + /* + Now convert back into the output format required + */ + audio.data.clear(); + + if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXuint8 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); + } + if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint8 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); + } + if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint16 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16))); + } + else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 32) + { + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + VectorXint32 samplesI = samplesITemp.cast(); + audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32))); + } + else if (outFormat.sampleType() == QAudioFormat::Float) + { + audio.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); + } + else { + qInfo(logAudio()) << "Unsupported Sample Type:" << outFormat.sampleType(); + } + + /* + As we currently don't have a float based uLaw encoder, this must be done + after all other conversion has taken place. + */ + + if (outFormat.codec() == "audio/PCMU") + { + QByteArray outPacket((int)audio.data.length() / 2, (char)0xff); + qint16* in = (qint16*)audio.data.data(); + for (int f = 0; f < outPacket.length(); f++) + { + qint16 sample = *in++; + int sign = (sample >> 8) & 0x80; + if (sign) + sample = (short)-sample; + if (sample > cClip) + sample = cClip; + sample = (short)(sample + cBias); + int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF]; + int mantissa = (sample >> (exponent + 3)) & 0x0F; + int compressedByte = ~(sign | (exponent << 4) | mantissa); + outPacket[f] = (quint8)compressedByte; + } + audio.data.clear(); + audio.data = outPacket; // Copy output packet back to input buffer. + + } } } } From fda7d2123941972a20d1f0473d5daa9074b502c1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 21:48:47 +0100 Subject: [PATCH 247/323] Update audioconverter.cpp --- audioconverter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/audioconverter.cpp b/audioconverter.cpp index e95c761..a8549a3 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -300,6 +300,10 @@ bool audioConverter::convert(audioPacket audio) } } } + else + { + qDebug(logAudioConverter) << "Detected empty packet"; + } } emit converted(audio); From ec169ca3d4962eb52726d1695348d3435b9beb92 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 22:48:39 +0100 Subject: [PATCH 248/323] Fixes to audioconverter --- audioconverter.cpp | 8 ++++---- audioconverter.h | 11 +++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index a8549a3..48b62b2 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -151,7 +151,7 @@ bool audioConverter::convert(audioPacket audio) } else { - qInfo(logAudio()) << "Unsupported Sample Type:" << inFormat.sampleType() << "Size:" << inFormat.sampleSize(); + qInfo(logAudio()) << "Unsupported Input Sample Type:" << inFormat.sampleType() << "Size:" << inFormat.sampleSize(); } if (samplesF.size() > 0) @@ -245,13 +245,13 @@ bool audioConverter::convert(audioPacket audio) VectorXuint8 samplesI = samplesITemp.cast(); audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); } - if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8) + else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8) { Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); VectorXint8 samplesI = samplesITemp.cast(); audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8))); } - if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16) + else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16) { Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); VectorXint16 samplesI = samplesITemp.cast(); @@ -268,7 +268,7 @@ bool audioConverter::convert(audioPacket audio) audio.data = QByteArray(reinterpret_cast(samplesF.data()), int(samplesF.size()) * int(sizeof(float))); } else { - qInfo(logAudio()) << "Unsupported Sample Type:" << outFormat.sampleType(); + qInfo(logAudio()) << "Unsupported Output Sample Type:" << outFormat.sampleType() << "Size:" << outFormat.sampleSize(); } /* diff --git a/audioconverter.h b/audioconverter.h index 8c784f0..00d75a1 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -80,9 +80,6 @@ static inline QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) 0x80 Opus 2ch */ - format.setChannelCount(1); - format.setSampleSize(8); - format.setSampleType(QAudioFormat::UnSignedInt); format.setByteOrder(QAudioFormat::LittleEndian); format.setCodec("audio/pcm"); format.setSampleRate(sampleRate); @@ -94,9 +91,15 @@ static inline QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) format.setCodec("audio/PCMU"); } + if (codec == 0x02 || codec == 0x08) { + format.setSampleSize(8); + format.setSampleType(QAudioFormat::UnSignedInt); + } if (codec == 0x08 || codec == 0x10 || codec == 0x20 || codec == 0x80) { format.setChannelCount(2); - } + } else { + format.setChannelCount(1); + } if (codec == 0x04 || codec == 0x10) { format.setSampleSize(16); From bddf283e7a356a9e80c6bc45c214aa3851655f9e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 9 May 2022 00:29:07 +0100 Subject: [PATCH 249/323] Always use "preferred" format where possible --- audiohandler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 311f1d0..53d29d9 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -79,7 +79,8 @@ bool audioHandler::init(audioSetup setup) qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup."; return false; } - if (outFormat.channelCount() == 1 && inFormat.channelCount() == 2) { + + /* if (outFormat.channelCount() == 1 && inFormat.channelCount() == 2) { outFormat.setChannelCount(2); if (!setup.port.isFormatSupported(outFormat)) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request stereo input!"; @@ -97,6 +98,7 @@ bool audioHandler::init(audioSetup setup) } } + */ if (outFormat.sampleSize() == 24) { // We can't convert this easily so use 32 bit instead. From 8d924b6fdadda957de0edcab9b58d7680847b2d3 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 9 May 2022 10:33:30 +0100 Subject: [PATCH 250/323] Configure buffer for input audio --- audiohandler.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/audiohandler.cpp b/audiohandler.cpp index 53d29d9..52326dd 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -162,6 +162,11 @@ void audioHandler::start() if (setup.isinput) { //this->open(QIODevice::WriteOnly); //audioInput->start(this); +#ifdef Q_OS_WIN + audioInput->setBufferSize(inFormat.bytesForDuration(setup.latency * 100)); +#else + audioInput->setBufferSize(inFormat.bytesForDuration(setup.latency * 1000)); +#endif audioDevice = audioInput->start(); connect(audioInput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection); From 82faf2c463263cd504059686da3d182485f4e173 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 9 May 2022 10:38:33 +0100 Subject: [PATCH 251/323] Remove extra Opus configuration --- audioconverter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index 48b62b2..4180741 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -35,10 +35,10 @@ bool audioConverter::init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 // Create instance of opus encoder int opus_err = 0; opusEncoder = opus_encoder_create(outFormat.sampleRate(), outFormat.channelCount(), OPUS_APPLICATION_AUDIO, &opus_err); - opus_encoder_ctl(opusEncoder, OPUS_SET_LSB_DEPTH(16)); + //opus_encoder_ctl(opusEncoder, OPUS_SET_LSB_DEPTH(16)); //opus_encoder_ctl(opusEncoder, OPUS_SET_INBAND_FEC(1)); - opus_encoder_ctl(opusEncoder, OPUS_SET_DTX(1)); - opus_encoder_ctl(opusEncoder, OPUS_SET_PACKET_LOSS_PERC(5)); + //opus_encoder_ctl(opusEncoder, OPUS_SET_DTX(1)); + //opus_encoder_ctl(opusEncoder, OPUS_SET_PACKET_LOSS_PERC(5)); opus_encoder_ctl(opusEncoder, OPUS_SET_COMPLEXITY(opusComplexity)); // Reduce complexity to maybe lower CPU? qInfo(logAudioConverter()) << "Creating opus encoder: " << opus_strerror(opus_err); } From fba9c6f20750a8d8a126d74709e0ce7422b455e1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 9 May 2022 18:47:12 +0100 Subject: [PATCH 252/323] Try to force minimum of 48K sample rate for native audio --- audiohandler.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 52326dd..e79a9ea 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -80,14 +80,25 @@ bool audioHandler::init(audioSetup setup) return false; } - /* if (outFormat.channelCount() == 1 && inFormat.channelCount() == 2) { + if (outFormat.channelCount() == 1 && inFormat.channelCount() == 2) { outFormat.setChannelCount(2); if (!setup.port.isFormatSupported(outFormat)) { - qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request stereo input!"; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request stereo reverting to mono"; outFormat.setChannelCount(1); } } + if (outFormat.sampleRate() < 48000) { + int tempRate=outFormat.sampleRate(); + outFormat.setSampleRate(48000); + if (!setup.port.isFormatSupported(outFormat)) { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request 48K, reverting to "<< tempRate; + outFormat.setSampleRate(tempRate); + } + } + + /* + if (outFormat.sampleType()==QAudioFormat::SignedInt) { outFormat.setSampleType(QAudioFormat::Float); outFormat.setSampleSize(32); From 30ca8aa44a842d0bbf676fc0ca1fc0b34a053109 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 9 May 2022 18:51:26 +0100 Subject: [PATCH 253/323] Try to force 16bit int instead of 8bit uint --- audiohandler.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/audiohandler.cpp b/audiohandler.cpp index e79a9ea..f2e4c3c 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -97,6 +97,17 @@ bool audioHandler::init(audioSetup setup) } } + if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize()==8) { + outFormat.setSampleType(QAudioFormat::SignedInt); + outFormat.setSampleSize(16); + + if (!setup.port.isFormatSupported(outFormat)) { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request 16bit Signed samples, reverting to 8bit Unsigned"; + outFormat.setSampleType(QAudioFormat::UnSignedInt); + outFormat.setSampleSize(8); + } + } + /* if (outFormat.sampleType()==QAudioFormat::SignedInt) { From 00f15a059ef841cc7ef2df06161fdb4d8dc0af34 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 10 May 2022 23:55:18 +0100 Subject: [PATCH 254/323] Add some more audio status --- audioconverter.cpp | 4 ++-- audiohandler.cpp | 16 ++++++++++------ audiohandler.h | 3 ++- udphandler.cpp | 32 ++++++++++++++++++++------------ udphandler.h | 14 ++++++++------ 5 files changed, 42 insertions(+), 27 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index 4180741..50723db 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -83,8 +83,8 @@ audioConverter::~audioConverter() bool audioConverter::convert(audioPacket audio) { - // If inFormat and outFormat are identical, just emit the data back. - if (audio.data.size() > 0 && inFormat != outFormat) + // If inFormat and outFormat are identical, just emit the data back (removed as it doesn't then process amplitude) + if (audio.data.size() > 0) { if (inFormat.codec() == "audio/opus") diff --git a/audiohandler.cpp b/audiohandler.cpp index f2e4c3c..51d015d 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -35,9 +35,7 @@ audioHandler::~audioHandler() converterThread->quit(); converterThread->wait(); } -} - -bool audioHandler::init(audioSetup setup) +}bool audioHandler::init(audioSetup setup) { if (isInitialized) { return false; @@ -108,6 +106,7 @@ bool audioHandler::init(audioSetup setup) } } + /* if (outFormat.sampleType()==QAudioFormat::SignedInt) { @@ -254,7 +253,12 @@ void audioHandler::convertedOutput(audioPacket packet) { currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); if (audioDevice != Q_NULLPTR) { - audioDevice->write(packet.data); + if (audioDevice->write(packet.data) < packet.data.size()) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full!"; + isOverrun=true; + } else { + isOverrun = false; + } if (lastReceived.msecsTo(QTime::currentTime()) > 100) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; } @@ -267,7 +271,7 @@ void audioHandler::convertedOutput(audioPacket packet) { } */ lastSentSeq = packet.seq; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); amplitude = packet.amplitude; } @@ -310,7 +314,7 @@ void audioHandler::convertedInput(audioPacket audio) } lastReceived = QTime::currentTime(); amplitude = audio.amplitude; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun); + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); } } diff --git a/audiohandler.h b/audiohandler.h index f9ec2a1..8bc8103 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -105,7 +105,7 @@ signals: void audioMessage(QString message); void sendLatency(quint16 newSize); void haveAudioData(const audioPacket& data); - void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under); + void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under,bool over); void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp); void sendToConverter(audioPacket audio); @@ -117,6 +117,7 @@ private: bool isUnderrun = false; + bool isOverrun = true; bool isInitialized=false; bool isReady = false; bool audioBuffered = false; diff --git a/udphandler.cpp b/udphandler.cpp index 2413bc0..29b7dc2 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -143,18 +143,20 @@ void udpHandler::receiveDataFromUserToRig(QByteArray data) } } -void udpHandler::getRxLevels(quint16 amplitude,quint16 latency,quint16 current, bool under) { +void udpHandler::getRxLevels(quint16 amplitude,quint16 latency,quint16 current, bool under, bool over) { status.rxAudioLevel = amplitude; status.rxLatency = latency; status.rxCurrentLatency = current; status.rxUnderrun = under; + status.rxOverrun = over; } -void udpHandler::getTxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under) { +void udpHandler::getTxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under, bool over) { status.txAudioLevel = amplitude; status.txLatency = latency; status.txCurrentLatency = current; status.txUnderrun = under; + status.txOverrun = over; } void udpHandler::dataReceived() @@ -208,13 +210,19 @@ void udpHandler::dataReceived() } QString tempLatency; - if (status.rxCurrentLatency < status.rxLatency*1.2 && !status.rxUnderrun) + if (status.rxCurrentLatency < status.rxLatency*1.2 && !status.rxUnderrun && !status.rxOverrun) { tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); } - else { + else if (status.rxUnderrun){ tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); } + else if (status.rxOverrun){ + tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); + } else + { + tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); + } QString txString=""; if (txSetup.codec == 0) { txString = "(no tx)"; @@ -306,8 +314,8 @@ void udpHandler::dataReceived() QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16))); QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char))); - QObject::connect(audio, SIGNAL(haveRxLevels(quint16, quint16, quint16,bool)), this, SLOT(getRxLevels(quint16, quint16,quint16,bool))); - QObject::connect(audio, SIGNAL(haveTxLevels(quint16, quint16,quint16,bool)), this, SLOT(getTxLevels(quint16, quint16,quint16,bool))); + QObject::connect(audio, SIGNAL(haveRxLevels(quint16, quint16, quint16,bool,bool)), this, SLOT(getRxLevels(quint16, quint16,quint16,bool,bool))); + QObject::connect(audio, SIGNAL(haveTxLevels(quint16, quint16,quint16,bool,bool)), this, SLOT(getTxLevels(quint16, quint16,quint16,bool,bool))); streamOpened = true; } @@ -935,7 +943,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); - connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16,bool))); + connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16,bool,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16,bool,bool))); connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); @@ -956,7 +964,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); - connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool))); + connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool,bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool,bool))); connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); emit setupTxAudio(txSetup); @@ -1086,13 +1094,13 @@ void udpAudio::setVolume(unsigned char value) emit haveSetVolume(value); } -void udpAudio::getRxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under) { +void udpAudio::getRxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under, bool over) { - emit haveRxLevels(amplitude,latency, current, under); + emit haveRxLevels(amplitude,latency, current, under, over); } -void udpAudio::getTxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under) { - emit haveTxLevels(amplitude,latency, current, under); +void udpAudio::getTxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under, bool over) { + emit haveTxLevels(amplitude,latency, current, under, over); } void udpAudio::dataReceived() diff --git a/udphandler.h b/udphandler.h index 3e51ed9..f9f9485 100644 --- a/udphandler.h +++ b/udphandler.h @@ -46,6 +46,8 @@ struct networkStatus { quint16 txLatency; bool rxUnderrun; bool txUnderrun; + bool txOverrun; + bool rxOverrun; quint16 rxCurrentLatency; quint16 txCurrentLatency; quint32 packetsSent=0; @@ -193,14 +195,14 @@ signals: void haveChangeLatency(quint16 value); void haveSetVolume(unsigned char value); - void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); - void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); public slots: void changeLatency(quint16 value); void setVolume(unsigned char value); - void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); - void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); void receiveAudioData(audioPacket audio); private: @@ -251,8 +253,8 @@ public slots: void setVolume(unsigned char value); void init(); void setCurrentRadio(quint8 radio); - void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); - void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); signals: From 498f5020a57dec9996ba427b6c6f71e81a3db33e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 11 May 2022 00:01:04 +0100 Subject: [PATCH 255/323] make latency too high trigger at lower value --- udphandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udphandler.cpp b/udphandler.cpp index 29b7dc2..38646e2 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -210,7 +210,7 @@ void udpHandler::dataReceived() } QString tempLatency; - if (status.rxCurrentLatency < status.rxLatency*1.2 && !status.rxUnderrun && !status.rxOverrun) + if (status.rxCurrentLatency <= status.rxLatency && !status.rxUnderrun && !status.rxOverrun) { tempLatency = QString("%1 ms").arg(status.rxCurrentLatency,3); } From 772375fb9dbf7074d43a9122a36a36fba4f0707f Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 11 May 2022 00:11:29 +0100 Subject: [PATCH 256/323] Split udpHandler into multiple class files --- udpaudio.cpp | 248 +++++++++ udpaudio.h | 83 +++ udpbase.cpp | 545 ++++++++++++++++++++ udpbase.h | 205 ++++++++ udpcivdata.cpp | 268 ++++++++++ udpcivdata.h | 56 ++ udphandler.cpp | 1109 ---------------------------------------- udphandler.h | 205 +------- wfserver.pro | 6 + wfview.pro | 6 + wfview.vcxproj | 6 + wfview.vcxproj.filters | 20 + 12 files changed, 1448 insertions(+), 1309 deletions(-) create mode 100644 udpaudio.cpp create mode 100644 udpaudio.h create mode 100644 udpbase.cpp create mode 100644 udpbase.h create mode 100644 udpcivdata.cpp create mode 100644 udpcivdata.h diff --git a/udpaudio.cpp b/udpaudio.cpp new file mode 100644 index 0000000..e8d0f3a --- /dev/null +++ b/udpaudio.cpp @@ -0,0 +1,248 @@ +#include "udpaudio.h" +#include "logcategories.h" + +// Audio stream +udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 lport, audioSetup rxSetup, audioSetup txSetup) +{ + qInfo(logUdp()) << "Starting udpAudio"; + this->localIP = local; + this->port = audioPort; + this->radioIP = ip; + + if (txSetup.sampleRate == 0) { + enableTx = false; + } + + init(lport); // Perform connection + + QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); + + rxaudio = new audioHandler(); + rxAudioThread = new QThread(this); + rxAudioThread->setObjectName("rxAudio()"); + + rxaudio->moveToThread(rxAudioThread); + + rxAudioThread->start(QThread::TimeCriticalPriority); + + connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); + + // signal/slot not currently used. + connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); + connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); + connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); + connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool))); + connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); + + + sendControl(false, 0x03, 0x00); // First connect packet + + pingTimer = new QTimer(); + connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); + pingTimer->start(PING_PERIOD); // send ping packets every 100ms + + if (enableTx) { + txaudio = new audioHandler(); + txAudioThread = new QThread(this); + rxAudioThread->setObjectName("txAudio()"); + + txaudio->moveToThread(txAudioThread); + + txAudioThread->start(QThread::TimeCriticalPriority); + + connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); + connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); + connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool))); + + connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); + emit setupTxAudio(txSetup); + } + + emit setupRxAudio(rxSetup); + + watchdogTimer = new QTimer(); + connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog); + watchdogTimer->start(WATCHDOG_PERIOD); + + areYouThereTimer = new QTimer(); + connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0)); + areYouThereTimer->start(AREYOUTHERE_PERIOD); +} + +udpAudio::~udpAudio() +{ + if (pingTimer != Q_NULLPTR) + { + qDebug(logUdp()) << "Stopping pingTimer"; + pingTimer->stop(); + delete pingTimer; + pingTimer = Q_NULLPTR; + } + + if (idleTimer != Q_NULLPTR) + { + qDebug(logUdp()) << "Stopping idleTimer"; + idleTimer->stop(); + delete idleTimer; + idleTimer = Q_NULLPTR; + } + + if (watchdogTimer != Q_NULLPTR) + { + qDebug(logUdp()) << "Stopping watchdogTimer"; + watchdogTimer->stop(); + delete watchdogTimer; + watchdogTimer = Q_NULLPTR; + } + + if (rxAudioThread != Q_NULLPTR) { + qDebug(logUdp()) << "Stopping rxaudio thread"; + rxAudioThread->quit(); + rxAudioThread->wait(); + } + + if (txAudioThread != Q_NULLPTR) { + qDebug(logUdp()) << "Stopping txaudio thread"; + txAudioThread->quit(); + txAudioThread->wait(); + } + qDebug(logUdp()) << "udpHandler successfully closed"; +} + +void udpAudio::watchdog() +{ + static bool alerted = false; + if (lastReceived.msecsTo(QTime::currentTime()) > 2000) + { + if (!alerted) { + /* Just log it at the moment, maybe try signalling the control channel that it needs to + try requesting civ/audio again? */ + + qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required?"; + alerted = true; + } + } + else + { + alerted = false; + } +} + + +void udpAudio::sendTxAudio() +{ + if (txaudio == Q_NULLPTR) { + return; + } + +} + +void udpAudio::receiveAudioData(audioPacket audio) { + // I really can't see how this could be possible but a quick sanity check! + if (txaudio == Q_NULLPTR) { + return; + } + if (audio.data.length() > 0) { + int counter = 1; + int len = 0; + + while (len < audio.data.length()) { + QByteArray partial = audio.data.mid(len, 1364); + audio_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p) + partial.length(); + p.sentid = myId; + p.rcvdid = remoteId; + if (partial.length() == 0xa0) { + p.ident = 0x9781; + } + else { + p.ident = 0x0080; // TX audio is always this? + } + p.datalen = (quint16)qToBigEndian((quint16)partial.length()); + p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN! + QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + tx.append(partial); + len = len + partial.length(); + //qInfo(logUdp()) << "Sending audio packet length: " << tx.length(); + sendTrackedPacket(tx); + sendAudioSeq++; + counter++; + } + } +} + +void udpAudio::changeLatency(quint16 value) +{ + emit haveChangeLatency(value); +} + +void udpAudio::setVolume(unsigned char value) +{ + emit haveSetVolume(value); +} + +void udpAudio::getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under) { + + emit haveRxLevels(amplitude, latency, current, under); +} + +void udpAudio::getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under) { + emit haveTxLevels(amplitude, latency, current, under); +} + +void udpAudio::dataReceived() +{ + while (udp->hasPendingDatagrams()) { + QNetworkDatagram datagram = udp->receiveDatagram(); + //qInfo(logUdp()) << "Received: " << datagram.data().mid(0,10); + QByteArray r = datagram.data(); + + switch (r.length()) + { + case (16): // Response to control packet handled in udpBase + { + //control_packet_t in = (control_packet_t)r.constData(); + break; + } + default: + { + /* Audio packets start as follows: + PCM 16bit and PCM8/uLAW stereo: 0x44,0x02 for first packet and 0x6c,0x05 for second. + uLAW 8bit/PCM 8bit 0xd8,0x03 for all packets + PCM 16bit stereo 0x6c,0x05 first & second 0x70,0x04 third + + + */ + control_packet_t in = (control_packet_t)r.constData(); + + if (in->type != 0x01 && in->len >= 0x20) { + if (in->seq == 0) + { + // Seq number has rolled over. + seqPrefix++; + } + + // 0xac is the smallest possible audio packet. + lastReceived = QTime::currentTime(); + audioPacket tempAudio; + tempAudio.seq = (quint32)seqPrefix << 16 | in->seq; + tempAudio.time = lastReceived; + tempAudio.sent = 0; + tempAudio.data = r.mid(0x18); + // Prefer signal/slot to forward audio as it is thread/safe + // Need to do more testing but latency appears fine. + //rxaudio->incomingAudio(tempAudio); + emit haveAudioData(tempAudio); + } + break; + } + } + + udpBase::dataReceived(r); // Call parent function to process the rest. + r.clear(); + datagram.clear(); + } +} + + diff --git a/udpaudio.h b/udpaudio.h new file mode 100644 index 0000000..ec03b8d --- /dev/null +++ b/udpaudio.h @@ -0,0 +1,83 @@ +#ifndef UDPAUDIO_H +#define UDPAUDIO_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Allow easy endian-ness conversions +#include + +// Needed for audio +#include +#include + +#include + +#include "packettypes.h" + +#include "udpbase.h" + +#include "audiohandler.h" + + +// Class for all audio communications. +class udpAudio : public udpBase +{ + Q_OBJECT + +public: + udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 lport, audioSetup rxSetup, audioSetup txSetup); + ~udpAudio(); + + int audioLatency = 0; + +signals: + void haveAudioData(audioPacket data); + + void setupTxAudio(audioSetup setup); + void setupRxAudio(audioSetup setup); + + void haveChangeLatency(quint16 value); + void haveSetVolume(unsigned char value); + void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + +public slots: + void changeLatency(quint16 value); + void setVolume(unsigned char value); + void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void receiveAudioData(audioPacket audio); + +private: + + void sendTxAudio(); + void dataReceived(); + void watchdog(); + + uint16_t sendAudioSeq = 0; + + audioHandler* rxaudio = Q_NULLPTR; + QThread* rxAudioThread = Q_NULLPTR; + + audioHandler* txaudio = Q_NULLPTR; + QThread* txAudioThread = Q_NULLPTR; + + QTimer* txAudioTimer = Q_NULLPTR; + bool enableTx = true; + + QMutex audioMutex; + +}; + +#endif \ No newline at end of file diff --git a/udpbase.cpp b/udpbase.cpp new file mode 100644 index 0000000..9381fd8 --- /dev/null +++ b/udpbase.cpp @@ -0,0 +1,545 @@ +#include "udpbase.h" +#include "logcategories.h" + +void udpBase::init(quint16 lport) +{ + //timeStarted.start(); + udp = new QUdpSocket(this); + udp->bind(lport); // Bind to random port. + localPort = udp->localPort(); + qInfo(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port; + uint32_t addr = localIP.toIPv4Address(); + myId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (localPort & 0xffff); + + retransmitTimer = new QTimer(); + connect(retransmitTimer, &QTimer::timeout, this, &udpBase::sendRetransmitRequest); + retransmitTimer->start(RETRANSMIT_PERIOD); +} + +udpBase::~udpBase() +{ + qInfo(logUdp()) << "Closing UDP stream :" << radioIP.toString() << ":" << port; + if (udp != Q_NULLPTR) { + sendControl(false, 0x05, 0x00); // Send disconnect + udp->close(); + delete udp; + } + if (areYouThereTimer != Q_NULLPTR) + { + areYouThereTimer->stop(); + delete areYouThereTimer; + } + + if (pingTimer != Q_NULLPTR) + { + pingTimer->stop(); + delete pingTimer; + } + if (idleTimer != Q_NULLPTR) + { + idleTimer->stop(); + delete idleTimer; + } + if (retransmitTimer != Q_NULLPTR) + { + retransmitTimer->stop(); + delete retransmitTimer; + } + + pingTimer = Q_NULLPTR; + idleTimer = Q_NULLPTR; + areYouThereTimer = Q_NULLPTR; + retransmitTimer = Q_NULLPTR; + +} + +// Base class! + +void udpBase::dataReceived(QByteArray r) +{ + if (r.length() < 0x10) + { + return; // Packet too small do to anything with? + } + + switch (r.length()) + { + case (CONTROL_SIZE): // Empty response used for simple comms and retransmit requests. + { + control_packet_t in = (control_packet_t)r.constData(); + if (in->type == 0x01 && in->len == 0x10) + { + // Single packet request + packetsLost++; + congestion = static_cast(packetsSent) / packetsLost * 100; + txBufferMutex.lock(); + auto match = txSeqBuf.find(in->seq); + if (match != txSeqBuf.end()) { + // Found matching entry? + // Send "untracked" as it has already been sent once. + // Don't constantly retransmit the same packet, give-up eventually + qDebug(logUdp()) << this->metaObject()->className() << ": Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16); + match->retransmitCount++; + udpMutex.lock(); + udp->writeDatagram(match->data, radioIP, port); + udpMutex.unlock(); + } + else { + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet" + << QString("0x%1").arg(in->seq, 0, 16) << + "not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) << + "to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16); + } + txBufferMutex.unlock(); + } + if (in->type == 0x04) { + qInfo(logUdp()) << this->metaObject()->className() << ": Received I am here "; + areYouThereCounter = 0; + // I don't think that we will ever receive an "I am here" other than in response to "Are you there?" + remoteId = in->sentid; + if (areYouThereTimer != Q_NULLPTR && areYouThereTimer->isActive()) { + // send ping packets every second + areYouThereTimer->stop(); + } + sendControl(false, 0x06, 0x01); // Send Are you ready - untracked. + } + else if (in->type == 0x06) + { + // Just get the seqnum and ignore the rest. + } + break; + } + case (PING_SIZE): // ping packet + { + ping_packet_t in = (ping_packet_t)r.constData(); + if (in->type == 0x07) + { + // It is a ping request/response + if (in->reply == 0x00) + { + ping_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p); + p.type = 0x07; + p.sentid = myId; + p.rcvdid = remoteId; + p.reply = 0x01; + p.seq = in->seq; + p.time = in->time; + udpMutex.lock(); + udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); + udpMutex.unlock(); + } + else if (in->reply == 0x01) { + if (in->seq == pingSendSeq) + { + // This is response to OUR request so increment counter + pingSendSeq++; + } + } + else { + qInfo(logUdp()) << this->metaObject()->className() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16]; + } + + } + break; + } + default: + { + + // All packets "should" be added to the incoming buffer. + // First check that we haven't already received it. + + + } + break; + + } + + + // All packets except ping and retransmit requests should trigger this. + control_packet_t in = (control_packet_t)r.constData(); + + // This is a variable length retransmit request! + if (in->type == 0x01 && in->len != 0x10) + { + + for (quint16 i = 0x10; i < r.length(); i = i + 2) + { + quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; + auto match = txSeqBuf.find(seq); + if (match == txSeqBuf.end()) { + qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet" + << QString("0x%1").arg(seq, 0, 16) << + "not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) << + "to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16); + // Just send idle packet. + sendControl(false, 0, seq); + } + else { + // Found matching entry? + // Send "untracked" as it has already been sent once. + qDebug(logUdp()) << this->metaObject()->className() << ": Sending (multiple packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16); + match->retransmitCount++; + udpMutex.lock(); + udp->writeDatagram(match->data, radioIP, port); + udpMutex.unlock(); + packetsLost++; + congestion = static_cast(packetsSent) / packetsLost * 100; + } + } + } + else if (in->len != PING_SIZE && in->type == 0x00 && in->seq != 0x00) + { + rxBufferMutex.lock(); + if (rxSeqBuf.isEmpty()) { + rxSeqBuf.insert(in->seq, QTime::currentTime()); + } + else + { + if (in->seq < rxSeqBuf.firstKey() || in->seq - rxSeqBuf.lastKey() > MAX_MISSING) + { + qInfo(logUdp()) << this->metaObject()->className() << "Large seq number gap detected, previous highest: " << + QString("0x%1").arg(rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16); + //seqPrefix++; + // Looks like it has rolled over so clear buffer and start again. + rxSeqBuf.clear(); + // Add this packet to the incoming buffer + rxSeqBuf.insert(in->seq, QTime::currentTime()); + rxBufferMutex.unlock(); + missingMutex.lock(); + rxMissing.clear(); + missingMutex.unlock(); + return; + } + + if (!rxSeqBuf.contains(in->seq)) + { + // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. + + if (in->seq > rxSeqBuf.lastKey() + 1) { + qInfo(logUdp()) << this->metaObject()->className() << "1 or more missing packets detected, previous: " << + QString("0x%1").arg(rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16); + // We are likely missing packets then! + missingMutex.lock(); + //int missCounter = 0; + // Sanity check! + for (quint16 f = rxSeqBuf.lastKey() + 1; f <= in->seq; f++) + { + if (rxSeqBuf.size() > BUFSIZE) + { + rxSeqBuf.erase(rxSeqBuf.begin()); + } + rxSeqBuf.insert(f, QTime::currentTime()); + if (f != in->seq && !rxMissing.contains(f)) + { + rxMissing.insert(f, 0); + } + } + missingMutex.unlock(); + } + else { + if (rxSeqBuf.size() > BUFSIZE) + { + rxSeqBuf.erase(rxSeqBuf.begin()); + } + rxSeqBuf.insert(in->seq, QTime::currentTime()); + + } + } + else { + // This is probably one of our missing packets! + missingMutex.lock(); + auto s = rxMissing.find(in->seq); + if (s != rxMissing.end()) + { + qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << QString("0x%1").arg(in->seq, 0, 16); + + s = rxMissing.erase(s); + } + missingMutex.unlock(); + + } + + } + rxBufferMutex.unlock(); + + } +} + + +void udpBase::sendRetransmitRequest() +{ + // Find all gaps in received packets and then send requests for them. + // This will run every 100ms so out-of-sequence packets will not trigger a retransmit request. + if (rxMissing.isEmpty()) { + return; + } + else if (rxMissing.size() > MAX_MISSING) { + qInfo(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers"; + missingMutex.lock(); + rxMissing.clear(); + missingMutex.unlock(); + + rxBufferMutex.lock(); + rxSeqBuf.clear(); + rxBufferMutex.unlock(); + return; + } + + QByteArray missingSeqs; + + missingMutex.lock(); + auto it = rxMissing.begin(); + while (it != rxMissing.end()) + { + if (it.key() != 0x0) + { + if (it.value() < 4) + { + missingSeqs.append(it.key() & 0xff); + missingSeqs.append(it.key() >> 8 & 0xff); + missingSeqs.append(it.key() & 0xff); + missingSeqs.append(it.key() >> 8 & 0xff); + it.value()++; + it++; + } + else { + qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << QString("0x%1").arg(it.key(), 0, 16) << "deleting"; + it = rxMissing.erase(it); + } + } + else { + qInfo(logUdp()) << this->metaObject()->className() << ": found empty key in missing buffer"; + it++; + } + } + missingMutex.unlock(); + + if (missingSeqs.length() != 0) + { + control_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p); + p.type = 0x01; + p.seq = 0x0000; + p.sentid = myId; + p.rcvdid = remoteId; + if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. + { + p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); + qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq, 0, 16); + udpMutex.lock(); + udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); + udpMutex.unlock(); + } + else + { + qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':'); + missingMutex.lock(); + p.len = sizeof(p) + missingSeqs.size(); + missingSeqs.insert(0, p.packet, sizeof(p)); + missingMutex.unlock(); + + udpMutex.lock(); + udp->writeDatagram(missingSeqs, radioIP, port); + udpMutex.unlock(); + } + } + +} + + + +// Used to send idle and other "control" style messages +void udpBase::sendControl(bool tracked = true, quint8 type = 0, quint16 seq = 0) +{ + control_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p); + p.type = type; + p.sentid = myId; + p.rcvdid = remoteId; + + if (!tracked) { + p.seq = seq; + udpMutex.lock(); + udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); + udpMutex.unlock(); + } + else { + sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p))); + } + return; +} + +// Send periodic ping packets +void udpBase::sendPing() +{ + ping_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p); + p.type = 0x07; + p.sentid = myId; + p.rcvdid = remoteId; + p.seq = pingSendSeq; + QTime now = QTime::currentTime(); + p.time = (quint32)now.msecsSinceStartOfDay(); + lastPingSentTime = QDateTime::currentDateTime(); + udpMutex.lock(); + udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); + udpMutex.unlock(); + return; +} + + +void udpBase::sendTrackedPacket(QByteArray d) +{ + // As the radio can request retransmission of these packets, store them in a buffer + d[6] = sendSeq & 0xff; + d[7] = (sendSeq >> 8) & 0xff; + SEQBUFENTRY s; + s.seqNum = sendSeq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = d; + if (txBufferMutex.tryLock(100)) + { + + if (sendSeq == 0) { + // We are either the first ever sent packet or have rolled-over so clear the buffer. + txSeqBuf.clear(); + congestion = 0; + } + if (txSeqBuf.size() > BUFSIZE) + { + txSeqBuf.erase(txSeqBuf.begin()); + } + txSeqBuf.insert(sendSeq, s); + + txBufferMutex.unlock(); + } + else { + qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked"; + } + // Stop using purgeOldEntries() as it is likely slower than just removing the earliest packet. + //qInfo(logUdp()) << this->metaObject()->className() << "RX:" << rxSeqBuf.size() << "TX:" <writeDatagram(d, radioIP, port); + if (congestion > 10) { // Poor quality connection? + udp->writeDatagram(d, radioIP, port); + if (congestion > 20) // Even worse so send again. + udp->writeDatagram(d, radioIP, port); + } + if (idleTimer != Q_NULLPTR && idleTimer->isActive()) { + idleTimer->start(IDLE_PERIOD); // Reset idle counter if it's running + } + udpMutex.unlock(); + packetsSent++; + return; +} + + +/// +/// Once a packet has reached PURGE_SECONDS old (currently 10) then it is not likely to be any use. +/// +void udpBase::purgeOldEntries() +{ + // Erase old entries from the tx packet buffer + if (txBufferMutex.tryLock(100)) + { + if (!txSeqBuf.isEmpty()) + { + // Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS + for (auto it = txSeqBuf.begin(); it != txSeqBuf.end();) { + if (it.value().timeSent.secsTo(QTime::currentTime()) > PURGE_SECONDS) { + txSeqBuf.erase(it++); + } + else { + break; + } + } + } + txBufferMutex.unlock(); + + } + else { + qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked"; + } + + + + if (rxBufferMutex.tryLock(100)) + { + if (!rxSeqBuf.isEmpty()) { + // Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS + for (auto it = rxSeqBuf.begin(); it != rxSeqBuf.end();) { + if (it.value().secsTo(QTime::currentTime()) > PURGE_SECONDS) { + rxSeqBuf.erase(it++); + } + else { + break; + } + } + } + rxBufferMutex.unlock(); + } + else { + qInfo(logUdp()) << this->metaObject()->className() << ": rxBuffer mutex is locked"; + } + + if (missingMutex.tryLock(100)) + { + // Erase old entries from the missing packets buffer + if (!rxMissing.isEmpty() && rxMissing.size() > 50) { + for (size_t i = 0; i < 25; ++i) { + rxMissing.erase(rxMissing.begin()); + } + } + missingMutex.unlock(); + } + else { + qInfo(logUdp()) << this->metaObject()->className() << ": missingBuffer mutex is locked"; + } +} + +void udpBase::printHex(const QByteArray& pdata) +{ + printHex(pdata, false, true); +} + +void udpBase::printHex(const QByteArray& pdata, bool printVert, bool printHoriz) +{ + qDebug(logUdp()) << "---- Begin hex dump -----:"; + QString sdata("DATA: "); + QString index("INDEX: "); + QStringList strings; + + for (int i = 0; i < pdata.length(); i++) + { + strings << QString("[%1]: %2").arg(i, 8, 10, QChar('0')).arg((unsigned char)pdata[i], 2, 16, QChar('0')); + sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0'))); + index.append(QString("%1 ").arg(i, 2, 10, QChar('0'))); + } + + if (printVert) + { + for (int i = 0; i < strings.length(); i++) + { + //sdata = QString(strings.at(i)); + qDebug(logUdp()) << strings.at(i); + } + } + + if (printHoriz) + { + qDebug(logUdp()) << index; + qDebug(logUdp()) << sdata; + } + qDebug(logUdp()) << "----- End hex dump -----"; +} + + diff --git a/udpbase.h b/udpbase.h new file mode 100644 index 0000000..43f4c77 --- /dev/null +++ b/udpbase.h @@ -0,0 +1,205 @@ +#ifndef UDPBASE_H +#define UDPBASE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Allow easy endian-ness conversions +#include + +// Needed for audio +#include +#include + +#include + +#include "packettypes.h" + + + +struct udpPreferences { + QString ipAddress; + quint16 controlLANPort; + quint16 serialLANPort; + quint16 audioLANPort; + QString username; + QString password; + QString clientName; + quint8 waterfallFormat; +}; + +struct networkStatus { + quint8 rxAudioBufferPercent; + quint8 txAudioBufferPercent; + quint8 rxAudioLevel; + quint8 txAudioLevel; + quint16 rxLatency; + quint16 txLatency; + bool rxUnderrun; + bool txUnderrun; + quint16 rxCurrentLatency; + quint16 txCurrentLatency; + quint32 packetsSent = 0; + quint32 packetsLost = 0; + quint16 rtt = 0; + quint32 networkLatency = 0; + QString message; +}; + + +// Parent class that contains all common items. +class udpBase : public QObject +{ + + +public: + ~udpBase(); + + void init(quint16 local); + + void reconnect(); + + void dataReceived(QByteArray r); + void sendPing(); + void sendRetransmitRange(quint16 first, quint16 second, quint16 third, quint16 fourth); + + void sendControl(bool tracked, quint8 id, quint16 seq); + + void printHex(const QByteArray& pdata); + void printHex(const QByteArray& pdata, bool printVert, bool printHoriz); + + + //QTime timeStarted; + + QUdpSocket* udp = Q_NULLPTR; + uint32_t myId = 0; + uint32_t remoteId = 0; + uint16_t authSeq = 0x30; + uint16_t sendSeqB = 0; + uint16_t sendSeq = 1; + uint16_t lastReceivedSeq = 1; + uint16_t pkt0SendSeq = 0; + uint16_t periodicSeq = 0; + quint64 latency = 0; + + QString username = ""; + QString password = ""; + QHostAddress radioIP; + QHostAddress localIP; + bool isAuthenticated = false; + quint16 localPort = 0; + quint16 port = 0; + bool periodicRunning = false; + bool sentPacketConnect2 = false; + QTime lastReceived = QTime::currentTime(); + QMutex udpMutex; + QMutex txBufferMutex; + QMutex rxBufferMutex; + QMutex missingMutex; + + struct SEQBUFENTRY { + QTime timeSent; + uint16_t seqNum; + QByteArray data; + quint8 retransmitCount; + }; + + QMap rxSeqBuf; + QMap txSeqBuf; + QMap rxMissing; + + void sendTrackedPacket(QByteArray d); + void purgeOldEntries(); + + QTimer* areYouThereTimer = Q_NULLPTR; // Send are-you-there packets every second until a response is received. + QTimer* pingTimer = Q_NULLPTR; // Start sending pings immediately. + QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected. + + QTimer* watchdogTimer = Q_NULLPTR; + QTimer* retransmitTimer = Q_NULLPTR; + + QDateTime lastPingSentTime; + uint16_t pingSendSeq = 0; + + quint16 areYouThereCounter = 0; + + quint32 packetsSent = 0; + quint32 packetsLost = 0; + + quint16 seqPrefix = 0; + QString connectionType = ""; + int congestion = 0; + + +private: + void sendRetransmitRequest(); + +}; + + +/// +/// passcode function used to generate secure (ish) code +/// +/// +/// pointer to encoded username or password +static inline void passcode(QString in, QByteArray& out) +{ + const quint8 sequence[] = + { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x47,0x5d,0x4c,0x42,0x66,0x20,0x23,0x46,0x4e,0x57,0x45,0x3d,0x67,0x76,0x60,0x41,0x62,0x39,0x59,0x2d,0x68,0x7e, + 0x7c,0x65,0x7d,0x49,0x29,0x72,0x73,0x78,0x21,0x6e,0x5a,0x5e,0x4a,0x3e,0x71,0x2c,0x2a,0x54,0x3c,0x3a,0x63,0x4f, + 0x43,0x75,0x27,0x79,0x5b,0x35,0x70,0x48,0x6b,0x56,0x6f,0x34,0x32,0x6c,0x30,0x61,0x6d,0x7b,0x2f,0x4b,0x64,0x38, + 0x2b,0x2e,0x50,0x40,0x3f,0x55,0x33,0x37,0x25,0x77,0x24,0x26,0x74,0x6a,0x28,0x53,0x4d,0x69,0x22,0x5c,0x44,0x31, + 0x36,0x58,0x3b,0x7a,0x51,0x5f,0x52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + }; + + QByteArray ba = in.toLocal8Bit(); + uchar* ascii = (uchar*)ba.constData(); + for (int i = 0; i < in.length() && i < 16; i++) + { + int p = ascii[i] + i; + if (p > 126) + { + p = 32 + p % 127; + } + out.append(sequence[p]); + } + return; +} + +/// +/// returns a QByteArray of a null terminated string +/// +/// +/// +/// +static inline QByteArray parseNullTerminatedString(QByteArray c, int s) +{ + //QString res = ""; + QByteArray res; + for (int i = s; i < c.length(); i++) + { + if (c[i] != '\0') + { + res.append(c[i]); + } + else + { + break; + } + } + return res; +} + +#endif diff --git a/udpcivdata.cpp b/udpcivdata.cpp new file mode 100644 index 0000000..f6cd51d --- /dev/null +++ b/udpcivdata.cpp @@ -0,0 +1,268 @@ +#include "udpcivdata.h" +#include "logcategories.h" + +// Class that manages all Civ Data to/from the rig +udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 localPort = 0) +{ + qInfo(logUdp()) << "Starting udpCivData"; + localIP = local; + port = civPort; + radioIP = ip; + splitWaterfall = splitWf; + + udpBase::init(localPort); // Perform connection + + QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpCivData::dataReceived); + + sendControl(false, 0x03, 0x00); // First connect packet + + /* + Connect various timers + */ + pingTimer = new QTimer(); + idleTimer = new QTimer(); + areYouThereTimer = new QTimer(); + startCivDataTimer = new QTimer(); + watchdogTimer = new QTimer(); + + connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); + connect(watchdogTimer, &QTimer::timeout, this, &udpCivData::watchdog); + connect(idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0)); + connect(startCivDataTimer, &QTimer::timeout, this, std::bind(&udpCivData::sendOpenClose, this, false)); + connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0)); + watchdogTimer->start(WATCHDOG_PERIOD); + // Start sending are you there packets - will be stopped once "I am here" received + // send ping packets every 100 ms (maybe change to less frequent?) + pingTimer->start(PING_PERIOD); + // Send idle packets every 100ms, this timer will be reset every time a non-idle packet is sent. + idleTimer->start(IDLE_PERIOD); + areYouThereTimer->start(AREYOUTHERE_PERIOD); +} + +udpCivData::~udpCivData() +{ + sendOpenClose(true); + if (startCivDataTimer != Q_NULLPTR) + { + startCivDataTimer->stop(); + delete startCivDataTimer; + startCivDataTimer = Q_NULLPTR; + } + if (pingTimer != Q_NULLPTR) + { + pingTimer->stop(); + delete pingTimer; + pingTimer = Q_NULLPTR; + } + if (idleTimer != Q_NULLPTR) + { + idleTimer->stop(); + delete idleTimer; + idleTimer = Q_NULLPTR; + } + if (watchdogTimer != Q_NULLPTR) + { + watchdogTimer->stop(); + delete watchdogTimer; + watchdogTimer = Q_NULLPTR; + } +} + +void udpCivData::watchdog() +{ + static bool alerted = false; + if (lastReceived.msecsTo(QTime::currentTime()) > 2000) + { + if (!alerted) { + qInfo(logUdp()) << " CIV Watchdog: no CIV data received for 2s, requesting data start."; + if (startCivDataTimer != Q_NULLPTR) + { + startCivDataTimer->start(100); + } + alerted = true; + } + } + else + { + alerted = false; + } +} + +void udpCivData::send(QByteArray d) +{ + //qInfo(logUdp()) << "Sending: (" << d.length() << ") " << d; + data_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p) + d.length(); + p.sentid = myId; + p.rcvdid = remoteId; + p.reply = (char)0xc1; + p.datalen = d.length(); + p.sendseq = qToBigEndian(sendSeqB); // THIS IS BIG ENDIAN! + + QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + t.append(d); + sendTrackedPacket(t); + sendSeqB++; + return; +} + + +void udpCivData::sendOpenClose(bool close) +{ + uint8_t magic = 0x04; + + if (close) + { + magic = 0x00; + } + + openclose_packet p; + memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! + p.len = sizeof(p); + p.sentid = myId; + p.rcvdid = remoteId; + p.data = 0x01c0; // Not sure what other values are available: + p.sendseq = qToBigEndian(sendSeqB); + p.magic = magic; + + sendSeqB++; + + sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p))); + return; +} + + + +void udpCivData::dataReceived() +{ + while (udp->hasPendingDatagrams()) + { + QNetworkDatagram datagram = udp->receiveDatagram(); + //qInfo(logUdp()) << "Received: " << datagram.data(); + QByteArray r = datagram.data(); + + + switch (r.length()) + { + case (CONTROL_SIZE): // Control packet + { + control_packet_t in = (control_packet_t)r.constData(); + if (in->type == 0x04) + { + areYouThereTimer->stop(); + } + else if (in->type == 0x06) + { + // Update remoteId + remoteId = in->sentid; + // Manually send a CIV start request and start the timer if it isn't received. + // The timer will be stopped as soon as valid CIV data is received. + sendOpenClose(false); + if (startCivDataTimer != Q_NULLPTR) { + startCivDataTimer->start(100); + } + } + break; + } + default: + { + if (r.length() > 21) { + data_packet_t in = (data_packet_t)r.constData(); + if (in->type != 0x01) { + // Process this packet, any re-transmit requests will happen later. + //uint16_t gotSeq = qFromLittleEndian(r.mid(6, 2)); + // We have received some Civ data so stop sending Start packets! + if (startCivDataTimer != Q_NULLPTR) { + startCivDataTimer->stop(); + } + lastReceived = QTime::currentTime(); + if (quint16(in->datalen + 0x15) == (quint16)in->len) + { + //if (r.mid(0x15).length() != 157) + // Find data length + int pos = r.indexOf(QByteArrayLiteral("\x27\x00\x00")) + 2; + int len = r.mid(pos).indexOf(QByteArrayLiteral("\xfd")); + //splitWaterfall = false; + if (splitWaterfall && pos > 1 && len > 100) { + // We need to split waterfall data into its component parts + // There are only 2 types that we are currently aware of + int numDivisions = 0; + if (len == 490) // IC705, IC9700, IC7300(LAN) + { + numDivisions = 11; + } + else if (len == 704) // IC7610, IC7851, ICR8600 + { + numDivisions = 15; + } + else { + qInfo(logUdp()) << "Unknown spectrum size" << len; + break; + } + // (sequence #1) includes center/fixed mode at [05]. No pixels. + // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 " + // "DATA: 27 00 00 01 11 01 00 00 00 14 00 00 00 35 14 00 00 fd " + // (sequences 2-10, 50 pixels) + // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 " + // "DATA: 27 00 00 07 11 27 13 15 01 00 22 21 09 08 06 19 0e 20 23 25 2c 2d 17 27 29 16 14 1b 1b 21 27 1a 18 17 1e 21 1b 24 21 22 23 13 19 23 2f 2d 25 25 0a 0e 1e 20 1f 1a 0c fd " + // ^--^--(seq 7/11) + // ^-- start waveform data 0x00 to 0xA0, index 05 to 54 + // (sequence #11) + // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 " + // "DATA: 27 00 00 11 11 0b 13 21 23 1a 1b 22 1e 1a 1d 13 21 1d 26 28 1f 19 1a 18 09 2c 2c 2c 1a 1b fd " + + int divSize = (len / numDivisions) + 6; + QByteArray wfPacket; + for (int i = 0; i < numDivisions; i++) { + + wfPacket = r.mid(pos - 6, 9); // First part of packet + + wfPacket = r.mid(pos - 6, 9); // First part of packet + char tens = ((i + 1) / 10); + char units = ((i + 1) - (10 * tens)); + wfPacket[7] = units | (tens << 4); + + tens = (numDivisions / 10); + units = (numDivisions - (10 * tens)); + wfPacket[8] = units | (tens << 4); + + if (i == 0) { + //Just send initial data, first BCD encode the max number: + wfPacket.append(r.mid(pos + 3, 12)); + } + else + { + wfPacket.append(r.mid((pos + 15) + ((i - 1) * divSize), divSize)); + } + if (i < numDivisions - 1) { + wfPacket.append('\xfd'); + } + //printHex(wfPacket, false, true); + + emit receive(wfPacket); + wfPacket.clear(); + + } + //qDebug(logUdp()) << "Waterfall packet len" << len << "Num Divisions" << numDivisions << "Division Size" << divSize; + } + else { + // Not waterfall data or split not enabled. + emit receive(r.mid(0x15)); + } + //qDebug(logUdp()) << "Got incoming CIV datagram" << r.mid(0x15).length(); + + } + + } + } + break; + } + } + udpBase::dataReceived(r); // Call parent function to process the rest. + + r.clear(); + datagram.clear(); + + } +} diff --git a/udpcivdata.h b/udpcivdata.h new file mode 100644 index 0000000..410f969 --- /dev/null +++ b/udpcivdata.h @@ -0,0 +1,56 @@ +// Class for all (pseudo) serial communications +#ifndef UDPCIVDATA_H +#define UDPCIVDATA_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Allow easy endian-ness conversions +#include + +// Needed for audio +#include +#include + +#include + +#include "packettypes.h" + +#include "udpbase.h" + +class udpCivData : public udpBase +{ + Q_OBJECT + +public: + udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 lport); + ~udpCivData(); + QMutex serialmutex; + +signals: + int receive(QByteArray); + +public slots: + void send(QByteArray d); + + +private: + void watchdog(); + void dataReceived(); + void sendOpenClose(bool close); + + QTimer* startCivDataTimer = Q_NULLPTR; + bool splitWaterfall = false; +}; + + +#endif \ No newline at end of file diff --git a/udphandler.cpp b/udphandler.cpp index 2413bc0..384c517 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -637,1112 +637,3 @@ void udpHandler::sendToken(uint8_t magic) return; } - -// Class that manages all Civ Data to/from the rig -udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 localPort=0 ) -{ - qInfo(logUdp()) << "Starting udpCivData"; - localIP = local; - port = civPort; - radioIP = ip; - splitWaterfall = splitWf; - - udpBase::init(localPort); // Perform connection - - QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpCivData::dataReceived); - - sendControl(false, 0x03, 0x00); // First connect packet - - /* - Connect various timers - */ - pingTimer = new QTimer(); - idleTimer = new QTimer(); - areYouThereTimer = new QTimer(); - startCivDataTimer = new QTimer(); - watchdogTimer = new QTimer(); - - connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); - connect(watchdogTimer, &QTimer::timeout, this, &udpCivData::watchdog); - connect(idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0)); - connect(startCivDataTimer, &QTimer::timeout, this, std::bind(&udpCivData::sendOpenClose, this, false)); - connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0)); - watchdogTimer->start(WATCHDOG_PERIOD); - // Start sending are you there packets - will be stopped once "I am here" received - // send ping packets every 100 ms (maybe change to less frequent?) - pingTimer->start(PING_PERIOD); - // Send idle packets every 100ms, this timer will be reset every time a non-idle packet is sent. - idleTimer->start(IDLE_PERIOD); - areYouThereTimer->start(AREYOUTHERE_PERIOD); -} - -udpCivData::~udpCivData() -{ - sendOpenClose(true); - if (startCivDataTimer != Q_NULLPTR) - { - startCivDataTimer->stop(); - delete startCivDataTimer; - startCivDataTimer = Q_NULLPTR; - } - if (pingTimer != Q_NULLPTR) - { - pingTimer->stop(); - delete pingTimer; - pingTimer = Q_NULLPTR; - } - if (idleTimer != Q_NULLPTR) - { - idleTimer->stop(); - delete idleTimer; - idleTimer = Q_NULLPTR; - } - if (watchdogTimer != Q_NULLPTR) - { - watchdogTimer->stop(); - delete watchdogTimer; - watchdogTimer = Q_NULLPTR; - } -} - -void udpCivData::watchdog() -{ - static bool alerted = false; - if (lastReceived.msecsTo(QTime::currentTime()) > 2000) - { - if (!alerted) { - qInfo(logUdp()) << " CIV Watchdog: no CIV data received for 2s, requesting data start."; - if (startCivDataTimer != Q_NULLPTR) - { - startCivDataTimer->start(100); - } - alerted = true; - } - } - else - { - alerted = false; - } -} - -void udpCivData::send(QByteArray d) -{ - //qInfo(logUdp()) << "Sending: (" << d.length() << ") " << d; - data_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p)+d.length(); - p.sentid = myId; - p.rcvdid = remoteId; - p.reply = (char)0xc1; - p.datalen = d.length(); - p.sendseq = qToBigEndian(sendSeqB); // THIS IS BIG ENDIAN! - - QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - t.append(d); - sendTrackedPacket(t); - sendSeqB++; - return; -} - - -void udpCivData::sendOpenClose(bool close) -{ - uint8_t magic = 0x04; - - if (close) - { - magic = 0x00; - } - - openclose_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.sentid = myId; - p.rcvdid = remoteId; - p.data = 0x01c0; // Not sure what other values are available: - p.sendseq = qToBigEndian(sendSeqB); - p.magic = magic; - - sendSeqB++; - - sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p))); - return; -} - - - -void udpCivData::dataReceived() -{ - while (udp->hasPendingDatagrams()) - { - QNetworkDatagram datagram = udp->receiveDatagram(); - //qInfo(logUdp()) << "Received: " << datagram.data(); - QByteArray r = datagram.data(); - - - switch (r.length()) - { - case (CONTROL_SIZE): // Control packet - { - control_packet_t in = (control_packet_t)r.constData(); - if (in->type == 0x04) - { - areYouThereTimer->stop(); - } - else if (in->type == 0x06) - { - // Update remoteId - remoteId = in->sentid; - // Manually send a CIV start request and start the timer if it isn't received. - // The timer will be stopped as soon as valid CIV data is received. - sendOpenClose(false); - if (startCivDataTimer != Q_NULLPTR) { - startCivDataTimer->start(100); - } - } - break; - } - default: - { - if (r.length() > 21) { - data_packet_t in = (data_packet_t)r.constData(); - if (in->type != 0x01) { - // Process this packet, any re-transmit requests will happen later. - //uint16_t gotSeq = qFromLittleEndian(r.mid(6, 2)); - // We have received some Civ data so stop sending Start packets! - if (startCivDataTimer != Q_NULLPTR) { - startCivDataTimer->stop(); - } - lastReceived = QTime::currentTime(); - if (quint16(in->datalen + 0x15) == (quint16)in->len) - { - //if (r.mid(0x15).length() != 157) - // Find data length - int pos = r.indexOf(QByteArrayLiteral("\x27\x00\x00"))+2; - int len = r.mid(pos).indexOf(QByteArrayLiteral("\xfd")); - //splitWaterfall = false; - if (splitWaterfall && pos > 1 && len > 100) { - // We need to split waterfall data into its component parts - // There are only 2 types that we are currently aware of - int numDivisions = 0; - if (len == 490) // IC705, IC9700, IC7300(LAN) - { - numDivisions = 11; - } - else if (len == 704) // IC7610, IC7851, ICR8600 - { - numDivisions = 15; - } - else { - qInfo(logUdp()) << "Unknown spectrum size" << len; - break; - } - // (sequence #1) includes center/fixed mode at [05]. No pixels. - // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 " - // "DATA: 27 00 00 01 11 01 00 00 00 14 00 00 00 35 14 00 00 fd " - // (sequences 2-10, 50 pixels) - // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 " - // "DATA: 27 00 00 07 11 27 13 15 01 00 22 21 09 08 06 19 0e 20 23 25 2c 2d 17 27 29 16 14 1b 1b 21 27 1a 18 17 1e 21 1b 24 21 22 23 13 19 23 2f 2d 25 25 0a 0e 1e 20 1f 1a 0c fd " - // ^--^--(seq 7/11) - // ^-- start waveform data 0x00 to 0xA0, index 05 to 54 - // (sequence #11) - // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 " - // "DATA: 27 00 00 11 11 0b 13 21 23 1a 1b 22 1e 1a 1d 13 21 1d 26 28 1f 19 1a 18 09 2c 2c 2c 1a 1b fd " - - int divSize = (len / numDivisions)+6; - QByteArray wfPacket; - for (int i = 0; i < numDivisions; i++) { - - wfPacket = r.mid(pos - 6, 9); // First part of packet - - wfPacket = r.mid(pos - 6, 9); // First part of packet - char tens = ((i + 1) / 10); - char units = ((i + 1) - (10 * tens)); - wfPacket[7] = units | (tens << 4); - - tens = (numDivisions / 10); - units = (numDivisions - (10 * tens)); - wfPacket[8] = units | (tens << 4); - - if (i == 0) { - //Just send initial data, first BCD encode the max number: - wfPacket.append(r.mid(pos + 3, 12)); - } - else - { - wfPacket.append(r.mid((pos + 15) + ((i-1) * divSize),divSize)); - } - if (i < numDivisions-1) { - wfPacket.append('\xfd'); - } - //printHex(wfPacket, false, true); - - emit receive(wfPacket); - wfPacket.clear(); - - } - //qDebug(logUdp()) << "Waterfall packet len" << len << "Num Divisions" << numDivisions << "Division Size" << divSize; - } - else { - // Not waterfall data or split not enabled. - emit receive(r.mid(0x15)); - } - //qDebug(logUdp()) << "Got incoming CIV datagram" << r.mid(0x15).length(); - - } - - } - } - break; - } - } - udpBase::dataReceived(r); // Call parent function to process the rest. - - r.clear(); - datagram.clear(); - - } -} - - -// Audio stream -udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 lport, audioSetup rxSetup, audioSetup txSetup) -{ - qInfo(logUdp()) << "Starting udpAudio"; - this->localIP = local; - this->port = audioPort; - this->radioIP = ip; - - if (txSetup.sampleRate == 0) { - enableTx = false; - } - - init(lport); // Perform connection - - QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); - - rxaudio = new audioHandler(); - rxAudioThread = new QThread(this); - rxAudioThread->setObjectName("rxAudio()"); - - rxaudio->moveToThread(rxAudioThread); - - rxAudioThread->start(QThread::TimeCriticalPriority); - - connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); - - // signal/slot not currently used. - connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); - connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); - connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); - connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16,bool))); - connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); - - - sendControl(false, 0x03, 0x00); // First connect packet - - pingTimer = new QTimer(); - connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); - pingTimer->start(PING_PERIOD); // send ping packets every 100ms - - if (enableTx) { - txaudio = new audioHandler(); - txAudioThread = new QThread(this); - rxAudioThread->setObjectName("txAudio()"); - - txaudio->moveToThread(txAudioThread); - - txAudioThread->start(QThread::TimeCriticalPriority); - - connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); - connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); - connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool))); - - connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); - emit setupTxAudio(txSetup); - } - - emit setupRxAudio(rxSetup); - - watchdogTimer = new QTimer(); - connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog); - watchdogTimer->start(WATCHDOG_PERIOD); - - areYouThereTimer = new QTimer(); - connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0)); - areYouThereTimer->start(AREYOUTHERE_PERIOD); -} - -udpAudio::~udpAudio() -{ - if (pingTimer != Q_NULLPTR) - { - qDebug(logUdp()) << "Stopping pingTimer"; - pingTimer->stop(); - delete pingTimer; - pingTimer = Q_NULLPTR; - } - - if (idleTimer != Q_NULLPTR) - { - qDebug(logUdp()) << "Stopping idleTimer"; - idleTimer->stop(); - delete idleTimer; - idleTimer = Q_NULLPTR; - } - - if (watchdogTimer != Q_NULLPTR) - { - qDebug(logUdp()) << "Stopping watchdogTimer"; - watchdogTimer->stop(); - delete watchdogTimer; - watchdogTimer = Q_NULLPTR; - } - - if (rxAudioThread != Q_NULLPTR) { - qDebug(logUdp()) << "Stopping rxaudio thread"; - rxAudioThread->quit(); - rxAudioThread->wait(); - } - - if (txAudioThread != Q_NULLPTR) { - qDebug(logUdp()) << "Stopping txaudio thread"; - txAudioThread->quit(); - txAudioThread->wait(); - } - qDebug(logUdp()) << "udpHandler successfully closed"; -} - -void udpAudio::watchdog() -{ - static bool alerted = false; - if (lastReceived.msecsTo(QTime::currentTime()) > 2000) - { - if (!alerted) { - /* Just log it at the moment, maybe try signalling the control channel that it needs to - try requesting civ/audio again? */ - - qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required?"; - alerted = true; - } - } - else - { - alerted = false; - } -} - - -void udpAudio::sendTxAudio() -{ - if (txaudio == Q_NULLPTR) { - return; - } - -} - -void udpAudio::receiveAudioData(audioPacket audio) { - // I really can't see how this could be possible but a quick sanity check! - if (txaudio == Q_NULLPTR) { - return; - } - if (audio.data.length() > 0) { - int counter = 1; - int len = 0; - - while (len < audio.data.length()) { - QByteArray partial = audio.data.mid(len, 1364); - audio_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p) + partial.length(); - p.sentid = myId; - p.rcvdid = remoteId; - if (partial.length() == 0xa0) { - p.ident = 0x9781; - } - else { - p.ident = 0x0080; // TX audio is always this? - } - p.datalen = (quint16)qToBigEndian((quint16)partial.length()); - p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN! - QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - tx.append(partial); - len = len + partial.length(); - //qInfo(logUdp()) << "Sending audio packet length: " << tx.length(); - sendTrackedPacket(tx); - sendAudioSeq++; - counter++; - } - } -} - -void udpAudio::changeLatency(quint16 value) -{ - emit haveChangeLatency(value); -} - -void udpAudio::setVolume(unsigned char value) -{ - emit haveSetVolume(value); -} - -void udpAudio::getRxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under) { - - emit haveRxLevels(amplitude,latency, current, under); -} - -void udpAudio::getTxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under) { - emit haveTxLevels(amplitude,latency, current, under); -} - -void udpAudio::dataReceived() -{ - while (udp->hasPendingDatagrams()) { - QNetworkDatagram datagram = udp->receiveDatagram(); - //qInfo(logUdp()) << "Received: " << datagram.data().mid(0,10); - QByteArray r = datagram.data(); - - switch (r.length()) - { - case (16): // Response to control packet handled in udpBase - { - //control_packet_t in = (control_packet_t)r.constData(); - break; - } - default: - { - /* Audio packets start as follows: - PCM 16bit and PCM8/uLAW stereo: 0x44,0x02 for first packet and 0x6c,0x05 for second. - uLAW 8bit/PCM 8bit 0xd8,0x03 for all packets - PCM 16bit stereo 0x6c,0x05 first & second 0x70,0x04 third - - - */ - control_packet_t in = (control_packet_t)r.constData(); - - if (in->type != 0x01 && in->len >= 0x20) { - if (in->seq == 0) - { - // Seq number has rolled over. - seqPrefix++; - } - - // 0xac is the smallest possible audio packet. - lastReceived = QTime::currentTime(); - audioPacket tempAudio; - tempAudio.seq = (quint32)seqPrefix << 16 | in->seq; - tempAudio.time = lastReceived; - tempAudio.sent = 0; - tempAudio.data = r.mid(0x18); - // Prefer signal/slot to forward audio as it is thread/safe - // Need to do more testing but latency appears fine. - //rxaudio->incomingAudio(tempAudio); - emit haveAudioData(tempAudio); - } - break; - } - } - - udpBase::dataReceived(r); // Call parent function to process the rest. - r.clear(); - datagram.clear(); - } -} - - - -void udpBase::init(quint16 lport) -{ - //timeStarted.start(); - udp = new QUdpSocket(this); - udp->bind(lport); // Bind to random port. - localPort = udp->localPort(); - qInfo(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port; - uint32_t addr = localIP.toIPv4Address(); - myId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (localPort & 0xffff); - - retransmitTimer = new QTimer(); - connect(retransmitTimer, &QTimer::timeout, this, &udpBase::sendRetransmitRequest); - retransmitTimer->start(RETRANSMIT_PERIOD); -} - -udpBase::~udpBase() -{ - qInfo(logUdp()) << "Closing UDP stream :" << radioIP.toString() << ":" << port; - if (udp != Q_NULLPTR) { - sendControl(false, 0x05, 0x00); // Send disconnect - udp->close(); - delete udp; - } - if (areYouThereTimer != Q_NULLPTR) - { - areYouThereTimer->stop(); - delete areYouThereTimer; - } - - if (pingTimer != Q_NULLPTR) - { - pingTimer->stop(); - delete pingTimer; - } - if (idleTimer != Q_NULLPTR) - { - idleTimer->stop(); - delete idleTimer; - } - if (retransmitTimer != Q_NULLPTR) - { - retransmitTimer->stop(); - delete retransmitTimer; - } - - pingTimer = Q_NULLPTR; - idleTimer = Q_NULLPTR; - areYouThereTimer = Q_NULLPTR; - retransmitTimer = Q_NULLPTR; - -} - -// Base class! - -void udpBase::dataReceived(QByteArray r) -{ - if (r.length() < 0x10) - { - return; // Packet too small do to anything with? - } - - switch (r.length()) - { - case (CONTROL_SIZE): // Empty response used for simple comms and retransmit requests. - { - control_packet_t in = (control_packet_t)r.constData(); - if (in->type == 0x01 && in->len == 0x10) - { - // Single packet request - packetsLost++; - congestion = static_cast(packetsSent) / packetsLost * 100; - txBufferMutex.lock(); - auto match = txSeqBuf.find(in->seq); - if (match != txSeqBuf.end()) { - // Found matching entry? - // Send "untracked" as it has already been sent once. - // Don't constantly retransmit the same packet, give-up eventually - qDebug(logUdp()) << this->metaObject()->className() << ": Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16); - match->retransmitCount++; - udpMutex.lock(); - udp->writeDatagram(match->data, radioIP, port); - udpMutex.unlock(); - } - else { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet" - << QString("0x%1").arg(in->seq, 0, 16) << - "not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) << - "to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16); - } - txBufferMutex.unlock(); - } - if (in->type == 0x04) { - qInfo(logUdp()) << this->metaObject()->className() << ": Received I am here "; - areYouThereCounter = 0; - // I don't think that we will ever receive an "I am here" other than in response to "Are you there?" - remoteId = in->sentid; - if (areYouThereTimer != Q_NULLPTR && areYouThereTimer->isActive()) { - // send ping packets every second - areYouThereTimer->stop(); - } - sendControl(false, 0x06, 0x01); // Send Are you ready - untracked. - } - else if (in->type == 0x06) - { - // Just get the seqnum and ignore the rest. - } - break; - } - case (PING_SIZE): // ping packet - { - ping_packet_t in = (ping_packet_t)r.constData(); - if (in->type == 0x07) - { - // It is a ping request/response - if (in->reply == 0x00) - { - ping_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.type = 0x07; - p.sentid = myId; - p.rcvdid = remoteId; - p.reply = 0x01; - p.seq = in->seq; - p.time = in->time; - udpMutex.lock(); - udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); - udpMutex.unlock(); - } - else if (in->reply == 0x01) { - if (in->seq == pingSendSeq) - { - // This is response to OUR request so increment counter - pingSendSeq++; - } - } - else { - qInfo(logUdp()) << this->metaObject()->className() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16]; - } - - } - break; - } - default: - { - - // All packets "should" be added to the incoming buffer. - // First check that we haven't already received it. - - - } - break; - - } - - - // All packets except ping and retransmit requests should trigger this. - control_packet_t in = (control_packet_t)r.constData(); - - // This is a variable length retransmit request! - if (in->type == 0x01 && in->len != 0x10) - { - - for (quint16 i = 0x10; i < r.length(); i = i + 2) - { - quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; - auto match = txSeqBuf.find(seq); - if (match == txSeqBuf.end()) { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet" - << QString("0x%1").arg(seq, 0, 16) << - "not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) << - "to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16); - // Just send idle packet. - sendControl(false, 0, seq); - } - else { - // Found matching entry? - // Send "untracked" as it has already been sent once. - qDebug(logUdp()) << this->metaObject()->className() << ": Sending (multiple packet) retransmit of " << QString("0x%1").arg(match->seqNum,0,16); - match->retransmitCount++; - udpMutex.lock(); - udp->writeDatagram(match->data, radioIP, port); - udpMutex.unlock(); - packetsLost++; - congestion = static_cast(packetsSent) / packetsLost * 100; - } - } - } - else if (in->len != PING_SIZE && in->type == 0x00 && in->seq != 0x00) - { - rxBufferMutex.lock(); - if (rxSeqBuf.isEmpty()) { - rxSeqBuf.insert(in->seq, QTime::currentTime()); - } - else - { - if (in->seq < rxSeqBuf.firstKey() || in->seq - rxSeqBuf.lastKey() > MAX_MISSING) - { - qInfo(logUdp()) << this->metaObject()->className() << "Large seq number gap detected, previous highest: " << - QString("0x%1").arg(rxSeqBuf.lastKey(),0,16) << " current: " << QString("0x%1").arg(in->seq,0,16); - //seqPrefix++; - // Looks like it has rolled over so clear buffer and start again. - rxSeqBuf.clear(); - // Add this packet to the incoming buffer - rxSeqBuf.insert(in->seq, QTime::currentTime()); - rxBufferMutex.unlock(); - missingMutex.lock(); - rxMissing.clear(); - missingMutex.unlock(); - return; - } - - if (!rxSeqBuf.contains(in->seq)) - { - // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. - - if (in->seq > rxSeqBuf.lastKey() + 1) { - qInfo(logUdp()) << this->metaObject()->className() << "1 or more missing packets detected, previous: " << - QString("0x%1").arg(rxSeqBuf.lastKey(),0,16) << " current: " << QString("0x%1").arg(in->seq,0,16); - // We are likely missing packets then! - missingMutex.lock(); - //int missCounter = 0; - // Sanity check! - for (quint16 f = rxSeqBuf.lastKey() + 1; f <= in->seq; f++) - { - if (rxSeqBuf.size() > BUFSIZE) - { - rxSeqBuf.erase(rxSeqBuf.begin()); - } - rxSeqBuf.insert(f, QTime::currentTime()); - if (f != in->seq && !rxMissing.contains(f)) - { - rxMissing.insert(f, 0); - } - } - missingMutex.unlock(); - } - else { - if (rxSeqBuf.size() > BUFSIZE) - { - rxSeqBuf.erase(rxSeqBuf.begin()); - } - rxSeqBuf.insert(in->seq, QTime::currentTime()); - - } - } - else { - // This is probably one of our missing packets! - missingMutex.lock(); - auto s = rxMissing.find(in->seq); - if (s != rxMissing.end()) - { - qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << QString("0x%1").arg(in->seq,0,16); - - s = rxMissing.erase(s); - } - missingMutex.unlock(); - - } - - } - rxBufferMutex.unlock(); - - } -} - -bool missing(quint16 i, quint16 j) { - return (i + 1 != j); -} - -void udpBase::sendRetransmitRequest() -{ - // Find all gaps in received packets and then send requests for them. - // This will run every 100ms so out-of-sequence packets will not trigger a retransmit request. - if (rxMissing.isEmpty()) { - return; - } - else if (rxMissing.size() > MAX_MISSING) { - qInfo(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers"; - missingMutex.lock(); - rxMissing.clear(); - missingMutex.unlock(); - - rxBufferMutex.lock(); - rxSeqBuf.clear(); - rxBufferMutex.unlock(); - return; - } - - QByteArray missingSeqs; - - missingMutex.lock(); - auto it = rxMissing.begin(); - while (it != rxMissing.end()) - { - if (it.key() != 0x0) - { - if (it.value() < 4) - { - missingSeqs.append(it.key() & 0xff); - missingSeqs.append(it.key() >> 8 & 0xff); - missingSeqs.append(it.key() & 0xff); - missingSeqs.append(it.key() >> 8 & 0xff); - it.value()++; - it++; - } - else { - qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << QString("0x%1").arg(it.key(), 0, 16) << "deleting"; - it = rxMissing.erase(it); - } - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": found empty key in missing buffer"; - it++; - } - } - missingMutex.unlock(); - - if (missingSeqs.length() != 0) - { - control_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.type = 0x01; - p.seq = 0x0000; - p.sentid = myId; - p.rcvdid = remoteId; - if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. - { - p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); - qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq,0,16); - udpMutex.lock(); - udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); - udpMutex.unlock(); - } - else - { - qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':'); - missingMutex.lock(); - p.len = sizeof(p)+missingSeqs.size(); - missingSeqs.insert(0, p.packet, sizeof(p)); - missingMutex.unlock(); - - udpMutex.lock(); - udp->writeDatagram(missingSeqs, radioIP, port); - udpMutex.unlock(); - } - } - -} - - - -// Used to send idle and other "control" style messages -void udpBase::sendControl(bool tracked = true, quint8 type = 0, quint16 seq = 0) -{ - control_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.type = type; - p.sentid = myId; - p.rcvdid = remoteId; - - if (!tracked) { - p.seq = seq; - udpMutex.lock(); - udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); - udpMutex.unlock(); - } - else { - sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p))); - } - return; -} - -// Send periodic ping packets -void udpBase::sendPing() -{ - ping_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.type = 0x07; - p.sentid = myId; - p.rcvdid = remoteId; - p.seq = pingSendSeq; - QTime now=QTime::currentTime(); - p.time = (quint32)now.msecsSinceStartOfDay(); - lastPingSentTime = QDateTime::currentDateTime(); - udpMutex.lock(); - udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); - udpMutex.unlock(); - return; -} - - -void udpBase::sendTrackedPacket(QByteArray d) -{ - // As the radio can request retransmission of these packets, store them in a buffer - d[6] = sendSeq & 0xff; - d[7] = (sendSeq >> 8) & 0xff; - SEQBUFENTRY s; - s.seqNum = sendSeq; - s.timeSent = QTime::currentTime(); - s.retransmitCount = 0; - s.data = d; - if (txBufferMutex.tryLock(100)) - { - - if (sendSeq == 0) { - // We are either the first ever sent packet or have rolled-over so clear the buffer. - txSeqBuf.clear(); - congestion = 0; - } - if (txSeqBuf.size() > BUFSIZE) - { - txSeqBuf.erase(txSeqBuf.begin()); - } - txSeqBuf.insert(sendSeq, s); - - txBufferMutex.unlock(); - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked"; - } - // Stop using purgeOldEntries() as it is likely slower than just removing the earliest packet. - //qInfo(logUdp()) << this->metaObject()->className() << "RX:" << rxSeqBuf.size() << "TX:" <writeDatagram(d, radioIP, port); - if (congestion>10) { // Poor quality connection? - udp->writeDatagram(d, radioIP, port); - if (congestion>20) // Even worse so send again. - udp->writeDatagram(d, radioIP, port); - } - if (idleTimer != Q_NULLPTR && idleTimer->isActive()) { - idleTimer->start(IDLE_PERIOD); // Reset idle counter if it's running - } - udpMutex.unlock(); - packetsSent++; - return; -} - - -/// -/// Once a packet has reached PURGE_SECONDS old (currently 10) then it is not likely to be any use. -/// -void udpBase::purgeOldEntries() -{ - // Erase old entries from the tx packet buffer - if (txBufferMutex.tryLock(100)) - { - if (!txSeqBuf.isEmpty()) - { - // Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS - for (auto it = txSeqBuf.begin(); it != txSeqBuf.end();) { - if (it.value().timeSent.secsTo(QTime::currentTime()) > PURGE_SECONDS) { - txSeqBuf.erase(it++); - } - else { - break; - } - } - } - txBufferMutex.unlock(); - - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked"; - } - - - - if (rxBufferMutex.tryLock(100)) - { - if (!rxSeqBuf.isEmpty()) { - // Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS - for (auto it = rxSeqBuf.begin(); it != rxSeqBuf.end();) { - if (it.value().secsTo(QTime::currentTime()) > PURGE_SECONDS) { - rxSeqBuf.erase(it++); - } - else { - break; - } - } - } - rxBufferMutex.unlock(); - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": rxBuffer mutex is locked"; - } - - if (missingMutex.tryLock(100)) - { - // Erase old entries from the missing packets buffer - if (!rxMissing.isEmpty() && rxMissing.size() > 50) { - for (size_t i = 0; i < 25; ++i) { - rxMissing.erase(rxMissing.begin()); - } - } - missingMutex.unlock(); - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": missingBuffer mutex is locked"; - } -} - -void udpBase::printHex(const QByteArray& pdata) -{ - printHex(pdata, false, true); -} - -void udpBase::printHex(const QByteArray& pdata, bool printVert, bool printHoriz) -{ - qDebug(logUdp()) << "---- Begin hex dump -----:"; - QString sdata("DATA: "); - QString index("INDEX: "); - QStringList strings; - - for (int i = 0; i < pdata.length(); i++) - { - strings << QString("[%1]: %2").arg(i, 8, 10, QChar('0')).arg((unsigned char)pdata[i], 2, 16, QChar('0')); - sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0'))); - index.append(QString("%1 ").arg(i, 2, 10, QChar('0'))); - } - - if (printVert) - { - for (int i = 0; i < strings.length(); i++) - { - //sdata = QString(strings.at(i)); - qDebug(logUdp()) << strings.at(i); - } - } - - if (printHoriz) - { - qDebug(logUdp()) << index; - qDebug(logUdp()) << sdata; - } - qDebug(logUdp()) << "----- End hex dump -----"; -} - -/// -/// passcode function used to generate secure (ish) code -/// -/// -/// pointer to encoded username or password -void passcode(QString in, QByteArray& out) -{ - const quint8 sequence[] = - { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0x47,0x5d,0x4c,0x42,0x66,0x20,0x23,0x46,0x4e,0x57,0x45,0x3d,0x67,0x76,0x60,0x41,0x62,0x39,0x59,0x2d,0x68,0x7e, - 0x7c,0x65,0x7d,0x49,0x29,0x72,0x73,0x78,0x21,0x6e,0x5a,0x5e,0x4a,0x3e,0x71,0x2c,0x2a,0x54,0x3c,0x3a,0x63,0x4f, - 0x43,0x75,0x27,0x79,0x5b,0x35,0x70,0x48,0x6b,0x56,0x6f,0x34,0x32,0x6c,0x30,0x61,0x6d,0x7b,0x2f,0x4b,0x64,0x38, - 0x2b,0x2e,0x50,0x40,0x3f,0x55,0x33,0x37,0x25,0x77,0x24,0x26,0x74,0x6a,0x28,0x53,0x4d,0x69,0x22,0x5c,0x44,0x31, - 0x36,0x58,0x3b,0x7a,0x51,0x5f,0x52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - - }; - - QByteArray ba = in.toLocal8Bit(); - uchar* ascii = (uchar*)ba.constData(); - for (int i = 0; i < in.length() && i < 16; i++) - { - int p = ascii[i] + i; - if (p > 126) - { - p = 32 + p % 127; - } - out.append(sequence[p]); - } - return; -} - -/// -/// returns a QByteArray of a null terminated string -/// -/// -/// -/// -QByteArray parseNullTerminatedString(QByteArray c, int s) -{ - //QString res = ""; - QByteArray res; - for (int i = s; i < c.length(); i++) - { - if (c[i] != '\0') - { - res.append(c[i]); - } - else - { - break; - } - } - return res; -} - diff --git a/udphandler.h b/udphandler.h index 3e51ed9..0faf2eb 100644 --- a/udphandler.h +++ b/udphandler.h @@ -1,6 +1,7 @@ #ifndef UDPHANDLER_H #define UDPHANDLER_H + #include #include #include @@ -22,207 +23,11 @@ #include -#include "audiohandler.h" #include "packettypes.h" - - -struct udpPreferences { - QString ipAddress; - quint16 controlLANPort; - quint16 serialLANPort; - quint16 audioLANPort; - QString username; - QString password; - QString clientName; - quint8 waterfallFormat; -}; - -struct networkStatus { - quint8 rxAudioBufferPercent; - quint8 txAudioBufferPercent; - quint8 rxAudioLevel; - quint8 txAudioLevel; - quint16 rxLatency; - quint16 txLatency; - bool rxUnderrun; - bool txUnderrun; - quint16 rxCurrentLatency; - quint16 txCurrentLatency; - quint32 packetsSent=0; - quint32 packetsLost=0; - quint16 rtt=0; - quint32 networkLatency=0; - QString message; -}; - -void passcode(QString in, QByteArray& out); -QByteArray parseNullTerminatedString(QByteArray c, int s); - -// Parent class that contains all common items. -class udpBase : public QObject -{ - - -public: - ~udpBase(); - - void init(quint16 local); - - void reconnect(); - - void dataReceived(QByteArray r); - void sendPing(); - void sendRetransmitRange(quint16 first, quint16 second, quint16 third,quint16 fourth); - - void sendControl(bool tracked,quint8 id, quint16 seq); - - void printHex(const QByteArray& pdata); - void printHex(const QByteArray& pdata, bool printVert, bool printHoriz); - - - //QTime timeStarted; - - QUdpSocket* udp=Q_NULLPTR; - uint32_t myId = 0; - uint32_t remoteId = 0; - uint16_t authSeq = 0x30; - uint16_t sendSeqB = 0; - uint16_t sendSeq = 1; - uint16_t lastReceivedSeq = 1; - uint16_t pkt0SendSeq = 0; - uint16_t periodicSeq = 0; - quint64 latency = 0; - - QString username = ""; - QString password = ""; - QHostAddress radioIP; - QHostAddress localIP; - bool isAuthenticated = false; - quint16 localPort=0; - quint16 port=0; - bool periodicRunning = false; - bool sentPacketConnect2 = false; - QTime lastReceived =QTime::currentTime(); - QMutex udpMutex; - QMutex txBufferMutex; - QMutex rxBufferMutex; - QMutex missingMutex; - - struct SEQBUFENTRY { - QTime timeSent; - uint16_t seqNum; - QByteArray data; - quint8 retransmitCount; - }; - - QMap rxSeqBuf; - QMap txSeqBuf; - QMap rxMissing; - - void sendTrackedPacket(QByteArray d); - void purgeOldEntries(); - - QTimer* areYouThereTimer = Q_NULLPTR; // Send are-you-there packets every second until a response is received. - QTimer* pingTimer = Q_NULLPTR; // Start sending pings immediately. - QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected. - - QTimer* watchdogTimer = Q_NULLPTR; - QTimer* retransmitTimer = Q_NULLPTR; - - QDateTime lastPingSentTime; - uint16_t pingSendSeq = 0; - - quint16 areYouThereCounter=0; - - quint32 packetsSent=0; - quint32 packetsLost=0; - - quint16 seqPrefix = 0; - QString connectionType=""; - int congestion = 0; - - -private: - void sendRetransmitRequest(); - -}; - -// Class for all (pseudo) serial communications -class udpCivData : public udpBase -{ - Q_OBJECT - -public: - udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 lport); - ~udpCivData(); - QMutex serialmutex; - -signals: - int receive(QByteArray); - -public slots: - void send(QByteArray d); - - -private: - void watchdog(); - void dataReceived(); - void sendOpenClose(bool close); - - QTimer* startCivDataTimer = Q_NULLPTR; - bool splitWaterfall = false; -}; - - -// Class for all audio communications. -class udpAudio : public udpBase -{ - Q_OBJECT - -public: - udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 lport, audioSetup rxSetup, audioSetup txSetup); - ~udpAudio(); - - int audioLatency = 0; - -signals: - void haveAudioData(audioPacket data); - - void setupTxAudio(audioSetup setup); - void setupRxAudio(audioSetup setup); - - void haveChangeLatency(quint16 value); - void haveSetVolume(unsigned char value); - void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); - void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); - -public slots: - void changeLatency(quint16 value); - void setVolume(unsigned char value); - void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); - void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); - void receiveAudioData(audioPacket audio); - -private: - - void sendTxAudio(); - void dataReceived(); - void watchdog(); - - uint16_t sendAudioSeq = 0; - - audioHandler* rxaudio = Q_NULLPTR; - QThread* rxAudioThread = Q_NULLPTR; - - audioHandler* txaudio = Q_NULLPTR; - QThread* txAudioThread = Q_NULLPTR; - - QTimer* txAudioTimer = Q_NULLPTR; - bool enableTx = true; - - QMutex audioMutex; - -}; +#include "audiohandler.h" +#include "udpbase.h" +#include "udpcivdata.h" +#include "udpaudio.h" diff --git a/wfserver.pro b/wfserver.pro index 1154884..37dad25 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -139,7 +139,10 @@ SOURCES += main.cpp\ rigcommander.cpp \ freqmemory.cpp \ rigidentities.cpp \ + udpbase.cpp \ udphandler.cpp \ + udpcivdata.cpp \ + udpaudio.cpp \ logcategories.cpp \ audiohandler.cpp \ audioconverter.cpp \ @@ -155,7 +158,10 @@ HEADERS += servermain.h \ rigcommander.h \ freqmemory.h \ rigidentities.h \ + udpbase.h \ udphandler.h \ + udpcivdata.h \ + udpaudio.h \ logcategories.h \ audiohandler.h \ audioconverter.h \ diff --git a/wfview.pro b/wfview.pro index 203dc88..667ffff 100644 --- a/wfview.pro +++ b/wfview.pro @@ -162,7 +162,10 @@ SOURCES += main.cpp\ rigcommander.cpp \ freqmemory.cpp \ rigidentities.cpp \ + udpbase.cpp \ udphandler.cpp \ + udpcivdata.cpp \ + udpaudio.cpp \ logcategories.cpp \ audiohandler.cpp \ audioconverter.cpp \ @@ -185,7 +188,10 @@ HEADERS += wfmain.h \ rigcommander.h \ freqmemory.h \ rigidentities.h \ + udpbase.h \ udphandler.h \ + udpcivdata.h \ + udpaudio.h \ logcategories.h \ audiohandler.h \ audioconverter.h \ diff --git a/wfview.vcxproj b/wfview.vcxproj index 7a48226..5c147a6 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -220,6 +220,9 @@ + + + @@ -269,6 +272,9 @@ + + + diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index a12a594..40ce604 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -119,6 +119,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files +
@@ -208,6 +217,12 @@ Header Files + + Header Files + + + Header Files + @@ -374,4 +389,9 @@ + + + Header Files + + \ No newline at end of file From 04ad04757d91dea304f69490fc60755bbe5095ac Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 11 May 2022 00:18:28 +0100 Subject: [PATCH 257/323] More post merge tidying --- udpbase.h | 2 + udphandler.cpp | 1109 ------------------------------------------------ 2 files changed, 2 insertions(+), 1109 deletions(-) diff --git a/udpbase.h b/udpbase.h index 43f4c77..f042b4d 100644 --- a/udpbase.h +++ b/udpbase.h @@ -46,6 +46,8 @@ struct networkStatus { quint16 txLatency; bool rxUnderrun; bool txUnderrun; + bool rxOverrun; + bool txOverrun; quint16 rxCurrentLatency; quint16 txCurrentLatency; quint32 packetsSent = 0; diff --git a/udphandler.cpp b/udphandler.cpp index 38646e2..93fd3f9 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -645,1112 +645,3 @@ void udpHandler::sendToken(uint8_t magic) return; } - -// Class that manages all Civ Data to/from the rig -udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 localPort=0 ) -{ - qInfo(logUdp()) << "Starting udpCivData"; - localIP = local; - port = civPort; - radioIP = ip; - splitWaterfall = splitWf; - - udpBase::init(localPort); // Perform connection - - QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpCivData::dataReceived); - - sendControl(false, 0x03, 0x00); // First connect packet - - /* - Connect various timers - */ - pingTimer = new QTimer(); - idleTimer = new QTimer(); - areYouThereTimer = new QTimer(); - startCivDataTimer = new QTimer(); - watchdogTimer = new QTimer(); - - connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); - connect(watchdogTimer, &QTimer::timeout, this, &udpCivData::watchdog); - connect(idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0)); - connect(startCivDataTimer, &QTimer::timeout, this, std::bind(&udpCivData::sendOpenClose, this, false)); - connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0)); - watchdogTimer->start(WATCHDOG_PERIOD); - // Start sending are you there packets - will be stopped once "I am here" received - // send ping packets every 100 ms (maybe change to less frequent?) - pingTimer->start(PING_PERIOD); - // Send idle packets every 100ms, this timer will be reset every time a non-idle packet is sent. - idleTimer->start(IDLE_PERIOD); - areYouThereTimer->start(AREYOUTHERE_PERIOD); -} - -udpCivData::~udpCivData() -{ - sendOpenClose(true); - if (startCivDataTimer != Q_NULLPTR) - { - startCivDataTimer->stop(); - delete startCivDataTimer; - startCivDataTimer = Q_NULLPTR; - } - if (pingTimer != Q_NULLPTR) - { - pingTimer->stop(); - delete pingTimer; - pingTimer = Q_NULLPTR; - } - if (idleTimer != Q_NULLPTR) - { - idleTimer->stop(); - delete idleTimer; - idleTimer = Q_NULLPTR; - } - if (watchdogTimer != Q_NULLPTR) - { - watchdogTimer->stop(); - delete watchdogTimer; - watchdogTimer = Q_NULLPTR; - } -} - -void udpCivData::watchdog() -{ - static bool alerted = false; - if (lastReceived.msecsTo(QTime::currentTime()) > 2000) - { - if (!alerted) { - qInfo(logUdp()) << " CIV Watchdog: no CIV data received for 2s, requesting data start."; - if (startCivDataTimer != Q_NULLPTR) - { - startCivDataTimer->start(100); - } - alerted = true; - } - } - else - { - alerted = false; - } -} - -void udpCivData::send(QByteArray d) -{ - //qInfo(logUdp()) << "Sending: (" << d.length() << ") " << d; - data_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p)+d.length(); - p.sentid = myId; - p.rcvdid = remoteId; - p.reply = (char)0xc1; - p.datalen = d.length(); - p.sendseq = qToBigEndian(sendSeqB); // THIS IS BIG ENDIAN! - - QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - t.append(d); - sendTrackedPacket(t); - sendSeqB++; - return; -} - - -void udpCivData::sendOpenClose(bool close) -{ - uint8_t magic = 0x04; - - if (close) - { - magic = 0x00; - } - - openclose_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.sentid = myId; - p.rcvdid = remoteId; - p.data = 0x01c0; // Not sure what other values are available: - p.sendseq = qToBigEndian(sendSeqB); - p.magic = magic; - - sendSeqB++; - - sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p))); - return; -} - - - -void udpCivData::dataReceived() -{ - while (udp->hasPendingDatagrams()) - { - QNetworkDatagram datagram = udp->receiveDatagram(); - //qInfo(logUdp()) << "Received: " << datagram.data(); - QByteArray r = datagram.data(); - - - switch (r.length()) - { - case (CONTROL_SIZE): // Control packet - { - control_packet_t in = (control_packet_t)r.constData(); - if (in->type == 0x04) - { - areYouThereTimer->stop(); - } - else if (in->type == 0x06) - { - // Update remoteId - remoteId = in->sentid; - // Manually send a CIV start request and start the timer if it isn't received. - // The timer will be stopped as soon as valid CIV data is received. - sendOpenClose(false); - if (startCivDataTimer != Q_NULLPTR) { - startCivDataTimer->start(100); - } - } - break; - } - default: - { - if (r.length() > 21) { - data_packet_t in = (data_packet_t)r.constData(); - if (in->type != 0x01) { - // Process this packet, any re-transmit requests will happen later. - //uint16_t gotSeq = qFromLittleEndian(r.mid(6, 2)); - // We have received some Civ data so stop sending Start packets! - if (startCivDataTimer != Q_NULLPTR) { - startCivDataTimer->stop(); - } - lastReceived = QTime::currentTime(); - if (quint16(in->datalen + 0x15) == (quint16)in->len) - { - //if (r.mid(0x15).length() != 157) - // Find data length - int pos = r.indexOf(QByteArrayLiteral("\x27\x00\x00"))+2; - int len = r.mid(pos).indexOf(QByteArrayLiteral("\xfd")); - //splitWaterfall = false; - if (splitWaterfall && pos > 1 && len > 100) { - // We need to split waterfall data into its component parts - // There are only 2 types that we are currently aware of - int numDivisions = 0; - if (len == 490) // IC705, IC9700, IC7300(LAN) - { - numDivisions = 11; - } - else if (len == 704) // IC7610, IC7851, ICR8600 - { - numDivisions = 15; - } - else { - qInfo(logUdp()) << "Unknown spectrum size" << len; - break; - } - // (sequence #1) includes center/fixed mode at [05]. No pixels. - // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 " - // "DATA: 27 00 00 01 11 01 00 00 00 14 00 00 00 35 14 00 00 fd " - // (sequences 2-10, 50 pixels) - // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 " - // "DATA: 27 00 00 07 11 27 13 15 01 00 22 21 09 08 06 19 0e 20 23 25 2c 2d 17 27 29 16 14 1b 1b 21 27 1a 18 17 1e 21 1b 24 21 22 23 13 19 23 2f 2d 25 25 0a 0e 1e 20 1f 1a 0c fd " - // ^--^--(seq 7/11) - // ^-- start waveform data 0x00 to 0xA0, index 05 to 54 - // (sequence #11) - // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 " - // "DATA: 27 00 00 11 11 0b 13 21 23 1a 1b 22 1e 1a 1d 13 21 1d 26 28 1f 19 1a 18 09 2c 2c 2c 1a 1b fd " - - int divSize = (len / numDivisions)+6; - QByteArray wfPacket; - for (int i = 0; i < numDivisions; i++) { - - wfPacket = r.mid(pos - 6, 9); // First part of packet - - wfPacket = r.mid(pos - 6, 9); // First part of packet - char tens = ((i + 1) / 10); - char units = ((i + 1) - (10 * tens)); - wfPacket[7] = units | (tens << 4); - - tens = (numDivisions / 10); - units = (numDivisions - (10 * tens)); - wfPacket[8] = units | (tens << 4); - - if (i == 0) { - //Just send initial data, first BCD encode the max number: - wfPacket.append(r.mid(pos + 3, 12)); - } - else - { - wfPacket.append(r.mid((pos + 15) + ((i-1) * divSize),divSize)); - } - if (i < numDivisions-1) { - wfPacket.append('\xfd'); - } - //printHex(wfPacket, false, true); - - emit receive(wfPacket); - wfPacket.clear(); - - } - //qDebug(logUdp()) << "Waterfall packet len" << len << "Num Divisions" << numDivisions << "Division Size" << divSize; - } - else { - // Not waterfall data or split not enabled. - emit receive(r.mid(0x15)); - } - //qDebug(logUdp()) << "Got incoming CIV datagram" << r.mid(0x15).length(); - - } - - } - } - break; - } - } - udpBase::dataReceived(r); // Call parent function to process the rest. - - r.clear(); - datagram.clear(); - - } -} - - -// Audio stream -udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 lport, audioSetup rxSetup, audioSetup txSetup) -{ - qInfo(logUdp()) << "Starting udpAudio"; - this->localIP = local; - this->port = audioPort; - this->radioIP = ip; - - if (txSetup.sampleRate == 0) { - enableTx = false; - } - - init(lport); // Perform connection - - QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); - - rxaudio = new audioHandler(); - rxAudioThread = new QThread(this); - rxAudioThread->setObjectName("rxAudio()"); - - rxaudio->moveToThread(rxAudioThread); - - rxAudioThread->start(QThread::TimeCriticalPriority); - - connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); - - // signal/slot not currently used. - connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); - connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); - connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); - connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16,bool,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16,bool,bool))); - connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); - - - sendControl(false, 0x03, 0x00); // First connect packet - - pingTimer = new QTimer(); - connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); - pingTimer->start(PING_PERIOD); // send ping packets every 100ms - - if (enableTx) { - txaudio = new audioHandler(); - txAudioThread = new QThread(this); - rxAudioThread->setObjectName("txAudio()"); - - txaudio->moveToThread(txAudioThread); - - txAudioThread->start(QThread::TimeCriticalPriority); - - connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); - connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); - connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool,bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool,bool))); - - connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); - emit setupTxAudio(txSetup); - } - - emit setupRxAudio(rxSetup); - - watchdogTimer = new QTimer(); - connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog); - watchdogTimer->start(WATCHDOG_PERIOD); - - areYouThereTimer = new QTimer(); - connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0)); - areYouThereTimer->start(AREYOUTHERE_PERIOD); -} - -udpAudio::~udpAudio() -{ - if (pingTimer != Q_NULLPTR) - { - qDebug(logUdp()) << "Stopping pingTimer"; - pingTimer->stop(); - delete pingTimer; - pingTimer = Q_NULLPTR; - } - - if (idleTimer != Q_NULLPTR) - { - qDebug(logUdp()) << "Stopping idleTimer"; - idleTimer->stop(); - delete idleTimer; - idleTimer = Q_NULLPTR; - } - - if (watchdogTimer != Q_NULLPTR) - { - qDebug(logUdp()) << "Stopping watchdogTimer"; - watchdogTimer->stop(); - delete watchdogTimer; - watchdogTimer = Q_NULLPTR; - } - - if (rxAudioThread != Q_NULLPTR) { - qDebug(logUdp()) << "Stopping rxaudio thread"; - rxAudioThread->quit(); - rxAudioThread->wait(); - } - - if (txAudioThread != Q_NULLPTR) { - qDebug(logUdp()) << "Stopping txaudio thread"; - txAudioThread->quit(); - txAudioThread->wait(); - } - qDebug(logUdp()) << "udpHandler successfully closed"; -} - -void udpAudio::watchdog() -{ - static bool alerted = false; - if (lastReceived.msecsTo(QTime::currentTime()) > 2000) - { - if (!alerted) { - /* Just log it at the moment, maybe try signalling the control channel that it needs to - try requesting civ/audio again? */ - - qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required?"; - alerted = true; - } - } - else - { - alerted = false; - } -} - - -void udpAudio::sendTxAudio() -{ - if (txaudio == Q_NULLPTR) { - return; - } - -} - -void udpAudio::receiveAudioData(audioPacket audio) { - // I really can't see how this could be possible but a quick sanity check! - if (txaudio == Q_NULLPTR) { - return; - } - if (audio.data.length() > 0) { - int counter = 1; - int len = 0; - - while (len < audio.data.length()) { - QByteArray partial = audio.data.mid(len, 1364); - audio_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p) + partial.length(); - p.sentid = myId; - p.rcvdid = remoteId; - if (partial.length() == 0xa0) { - p.ident = 0x9781; - } - else { - p.ident = 0x0080; // TX audio is always this? - } - p.datalen = (quint16)qToBigEndian((quint16)partial.length()); - p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN! - QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - tx.append(partial); - len = len + partial.length(); - //qInfo(logUdp()) << "Sending audio packet length: " << tx.length(); - sendTrackedPacket(tx); - sendAudioSeq++; - counter++; - } - } -} - -void udpAudio::changeLatency(quint16 value) -{ - emit haveChangeLatency(value); -} - -void udpAudio::setVolume(unsigned char value) -{ - emit haveSetVolume(value); -} - -void udpAudio::getRxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under, bool over) { - - emit haveRxLevels(amplitude,latency, current, under, over); -} - -void udpAudio::getTxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under, bool over) { - emit haveTxLevels(amplitude,latency, current, under, over); -} - -void udpAudio::dataReceived() -{ - while (udp->hasPendingDatagrams()) { - QNetworkDatagram datagram = udp->receiveDatagram(); - //qInfo(logUdp()) << "Received: " << datagram.data().mid(0,10); - QByteArray r = datagram.data(); - - switch (r.length()) - { - case (16): // Response to control packet handled in udpBase - { - //control_packet_t in = (control_packet_t)r.constData(); - break; - } - default: - { - /* Audio packets start as follows: - PCM 16bit and PCM8/uLAW stereo: 0x44,0x02 for first packet and 0x6c,0x05 for second. - uLAW 8bit/PCM 8bit 0xd8,0x03 for all packets - PCM 16bit stereo 0x6c,0x05 first & second 0x70,0x04 third - - - */ - control_packet_t in = (control_packet_t)r.constData(); - - if (in->type != 0x01 && in->len >= 0x20) { - if (in->seq == 0) - { - // Seq number has rolled over. - seqPrefix++; - } - - // 0xac is the smallest possible audio packet. - lastReceived = QTime::currentTime(); - audioPacket tempAudio; - tempAudio.seq = (quint32)seqPrefix << 16 | in->seq; - tempAudio.time = lastReceived; - tempAudio.sent = 0; - tempAudio.data = r.mid(0x18); - // Prefer signal/slot to forward audio as it is thread/safe - // Need to do more testing but latency appears fine. - //rxaudio->incomingAudio(tempAudio); - emit haveAudioData(tempAudio); - } - break; - } - } - - udpBase::dataReceived(r); // Call parent function to process the rest. - r.clear(); - datagram.clear(); - } -} - - - -void udpBase::init(quint16 lport) -{ - //timeStarted.start(); - udp = new QUdpSocket(this); - udp->bind(lport); // Bind to random port. - localPort = udp->localPort(); - qInfo(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port; - uint32_t addr = localIP.toIPv4Address(); - myId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (localPort & 0xffff); - - retransmitTimer = new QTimer(); - connect(retransmitTimer, &QTimer::timeout, this, &udpBase::sendRetransmitRequest); - retransmitTimer->start(RETRANSMIT_PERIOD); -} - -udpBase::~udpBase() -{ - qInfo(logUdp()) << "Closing UDP stream :" << radioIP.toString() << ":" << port; - if (udp != Q_NULLPTR) { - sendControl(false, 0x05, 0x00); // Send disconnect - udp->close(); - delete udp; - } - if (areYouThereTimer != Q_NULLPTR) - { - areYouThereTimer->stop(); - delete areYouThereTimer; - } - - if (pingTimer != Q_NULLPTR) - { - pingTimer->stop(); - delete pingTimer; - } - if (idleTimer != Q_NULLPTR) - { - idleTimer->stop(); - delete idleTimer; - } - if (retransmitTimer != Q_NULLPTR) - { - retransmitTimer->stop(); - delete retransmitTimer; - } - - pingTimer = Q_NULLPTR; - idleTimer = Q_NULLPTR; - areYouThereTimer = Q_NULLPTR; - retransmitTimer = Q_NULLPTR; - -} - -// Base class! - -void udpBase::dataReceived(QByteArray r) -{ - if (r.length() < 0x10) - { - return; // Packet too small do to anything with? - } - - switch (r.length()) - { - case (CONTROL_SIZE): // Empty response used for simple comms and retransmit requests. - { - control_packet_t in = (control_packet_t)r.constData(); - if (in->type == 0x01 && in->len == 0x10) - { - // Single packet request - packetsLost++; - congestion = static_cast(packetsSent) / packetsLost * 100; - txBufferMutex.lock(); - auto match = txSeqBuf.find(in->seq); - if (match != txSeqBuf.end()) { - // Found matching entry? - // Send "untracked" as it has already been sent once. - // Don't constantly retransmit the same packet, give-up eventually - qDebug(logUdp()) << this->metaObject()->className() << ": Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16); - match->retransmitCount++; - udpMutex.lock(); - udp->writeDatagram(match->data, radioIP, port); - udpMutex.unlock(); - } - else { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet" - << QString("0x%1").arg(in->seq, 0, 16) << - "not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) << - "to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16); - } - txBufferMutex.unlock(); - } - if (in->type == 0x04) { - qInfo(logUdp()) << this->metaObject()->className() << ": Received I am here "; - areYouThereCounter = 0; - // I don't think that we will ever receive an "I am here" other than in response to "Are you there?" - remoteId = in->sentid; - if (areYouThereTimer != Q_NULLPTR && areYouThereTimer->isActive()) { - // send ping packets every second - areYouThereTimer->stop(); - } - sendControl(false, 0x06, 0x01); // Send Are you ready - untracked. - } - else if (in->type == 0x06) - { - // Just get the seqnum and ignore the rest. - } - break; - } - case (PING_SIZE): // ping packet - { - ping_packet_t in = (ping_packet_t)r.constData(); - if (in->type == 0x07) - { - // It is a ping request/response - if (in->reply == 0x00) - { - ping_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.type = 0x07; - p.sentid = myId; - p.rcvdid = remoteId; - p.reply = 0x01; - p.seq = in->seq; - p.time = in->time; - udpMutex.lock(); - udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); - udpMutex.unlock(); - } - else if (in->reply == 0x01) { - if (in->seq == pingSendSeq) - { - // This is response to OUR request so increment counter - pingSendSeq++; - } - } - else { - qInfo(logUdp()) << this->metaObject()->className() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16]; - } - - } - break; - } - default: - { - - // All packets "should" be added to the incoming buffer. - // First check that we haven't already received it. - - - } - break; - - } - - - // All packets except ping and retransmit requests should trigger this. - control_packet_t in = (control_packet_t)r.constData(); - - // This is a variable length retransmit request! - if (in->type == 0x01 && in->len != 0x10) - { - - for (quint16 i = 0x10; i < r.length(); i = i + 2) - { - quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8; - auto match = txSeqBuf.find(seq); - if (match == txSeqBuf.end()) { - qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet" - << QString("0x%1").arg(seq, 0, 16) << - "not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) << - "to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16); - // Just send idle packet. - sendControl(false, 0, seq); - } - else { - // Found matching entry? - // Send "untracked" as it has already been sent once. - qDebug(logUdp()) << this->metaObject()->className() << ": Sending (multiple packet) retransmit of " << QString("0x%1").arg(match->seqNum,0,16); - match->retransmitCount++; - udpMutex.lock(); - udp->writeDatagram(match->data, radioIP, port); - udpMutex.unlock(); - packetsLost++; - congestion = static_cast(packetsSent) / packetsLost * 100; - } - } - } - else if (in->len != PING_SIZE && in->type == 0x00 && in->seq != 0x00) - { - rxBufferMutex.lock(); - if (rxSeqBuf.isEmpty()) { - rxSeqBuf.insert(in->seq, QTime::currentTime()); - } - else - { - if (in->seq < rxSeqBuf.firstKey() || in->seq - rxSeqBuf.lastKey() > MAX_MISSING) - { - qInfo(logUdp()) << this->metaObject()->className() << "Large seq number gap detected, previous highest: " << - QString("0x%1").arg(rxSeqBuf.lastKey(),0,16) << " current: " << QString("0x%1").arg(in->seq,0,16); - //seqPrefix++; - // Looks like it has rolled over so clear buffer and start again. - rxSeqBuf.clear(); - // Add this packet to the incoming buffer - rxSeqBuf.insert(in->seq, QTime::currentTime()); - rxBufferMutex.unlock(); - missingMutex.lock(); - rxMissing.clear(); - missingMutex.unlock(); - return; - } - - if (!rxSeqBuf.contains(in->seq)) - { - // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. - - if (in->seq > rxSeqBuf.lastKey() + 1) { - qInfo(logUdp()) << this->metaObject()->className() << "1 or more missing packets detected, previous: " << - QString("0x%1").arg(rxSeqBuf.lastKey(),0,16) << " current: " << QString("0x%1").arg(in->seq,0,16); - // We are likely missing packets then! - missingMutex.lock(); - //int missCounter = 0; - // Sanity check! - for (quint16 f = rxSeqBuf.lastKey() + 1; f <= in->seq; f++) - { - if (rxSeqBuf.size() > BUFSIZE) - { - rxSeqBuf.erase(rxSeqBuf.begin()); - } - rxSeqBuf.insert(f, QTime::currentTime()); - if (f != in->seq && !rxMissing.contains(f)) - { - rxMissing.insert(f, 0); - } - } - missingMutex.unlock(); - } - else { - if (rxSeqBuf.size() > BUFSIZE) - { - rxSeqBuf.erase(rxSeqBuf.begin()); - } - rxSeqBuf.insert(in->seq, QTime::currentTime()); - - } - } - else { - // This is probably one of our missing packets! - missingMutex.lock(); - auto s = rxMissing.find(in->seq); - if (s != rxMissing.end()) - { - qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << QString("0x%1").arg(in->seq,0,16); - - s = rxMissing.erase(s); - } - missingMutex.unlock(); - - } - - } - rxBufferMutex.unlock(); - - } -} - -bool missing(quint16 i, quint16 j) { - return (i + 1 != j); -} - -void udpBase::sendRetransmitRequest() -{ - // Find all gaps in received packets and then send requests for them. - // This will run every 100ms so out-of-sequence packets will not trigger a retransmit request. - if (rxMissing.isEmpty()) { - return; - } - else if (rxMissing.size() > MAX_MISSING) { - qInfo(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers"; - missingMutex.lock(); - rxMissing.clear(); - missingMutex.unlock(); - - rxBufferMutex.lock(); - rxSeqBuf.clear(); - rxBufferMutex.unlock(); - return; - } - - QByteArray missingSeqs; - - missingMutex.lock(); - auto it = rxMissing.begin(); - while (it != rxMissing.end()) - { - if (it.key() != 0x0) - { - if (it.value() < 4) - { - missingSeqs.append(it.key() & 0xff); - missingSeqs.append(it.key() >> 8 & 0xff); - missingSeqs.append(it.key() & 0xff); - missingSeqs.append(it.key() >> 8 & 0xff); - it.value()++; - it++; - } - else { - qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << QString("0x%1").arg(it.key(), 0, 16) << "deleting"; - it = rxMissing.erase(it); - } - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": found empty key in missing buffer"; - it++; - } - } - missingMutex.unlock(); - - if (missingSeqs.length() != 0) - { - control_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.type = 0x01; - p.seq = 0x0000; - p.sentid = myId; - p.rcvdid = remoteId; - if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control. - { - p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8); - qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq,0,16); - udpMutex.lock(); - udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); - udpMutex.unlock(); - } - else - { - qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':'); - missingMutex.lock(); - p.len = sizeof(p)+missingSeqs.size(); - missingSeqs.insert(0, p.packet, sizeof(p)); - missingMutex.unlock(); - - udpMutex.lock(); - udp->writeDatagram(missingSeqs, radioIP, port); - udpMutex.unlock(); - } - } - -} - - - -// Used to send idle and other "control" style messages -void udpBase::sendControl(bool tracked = true, quint8 type = 0, quint16 seq = 0) -{ - control_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.type = type; - p.sentid = myId; - p.rcvdid = remoteId; - - if (!tracked) { - p.seq = seq; - udpMutex.lock(); - udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); - udpMutex.unlock(); - } - else { - sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p))); - } - return; -} - -// Send periodic ping packets -void udpBase::sendPing() -{ - ping_packet p; - memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); - p.type = 0x07; - p.sentid = myId; - p.rcvdid = remoteId; - p.seq = pingSendSeq; - QTime now=QTime::currentTime(); - p.time = (quint32)now.msecsSinceStartOfDay(); - lastPingSentTime = QDateTime::currentDateTime(); - udpMutex.lock(); - udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port); - udpMutex.unlock(); - return; -} - - -void udpBase::sendTrackedPacket(QByteArray d) -{ - // As the radio can request retransmission of these packets, store them in a buffer - d[6] = sendSeq & 0xff; - d[7] = (sendSeq >> 8) & 0xff; - SEQBUFENTRY s; - s.seqNum = sendSeq; - s.timeSent = QTime::currentTime(); - s.retransmitCount = 0; - s.data = d; - if (txBufferMutex.tryLock(100)) - { - - if (sendSeq == 0) { - // We are either the first ever sent packet or have rolled-over so clear the buffer. - txSeqBuf.clear(); - congestion = 0; - } - if (txSeqBuf.size() > BUFSIZE) - { - txSeqBuf.erase(txSeqBuf.begin()); - } - txSeqBuf.insert(sendSeq, s); - - txBufferMutex.unlock(); - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked"; - } - // Stop using purgeOldEntries() as it is likely slower than just removing the earliest packet. - //qInfo(logUdp()) << this->metaObject()->className() << "RX:" << rxSeqBuf.size() << "TX:" <writeDatagram(d, radioIP, port); - if (congestion>10) { // Poor quality connection? - udp->writeDatagram(d, radioIP, port); - if (congestion>20) // Even worse so send again. - udp->writeDatagram(d, radioIP, port); - } - if (idleTimer != Q_NULLPTR && idleTimer->isActive()) { - idleTimer->start(IDLE_PERIOD); // Reset idle counter if it's running - } - udpMutex.unlock(); - packetsSent++; - return; -} - - -/// -/// Once a packet has reached PURGE_SECONDS old (currently 10) then it is not likely to be any use. -/// -void udpBase::purgeOldEntries() -{ - // Erase old entries from the tx packet buffer - if (txBufferMutex.tryLock(100)) - { - if (!txSeqBuf.isEmpty()) - { - // Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS - for (auto it = txSeqBuf.begin(); it != txSeqBuf.end();) { - if (it.value().timeSent.secsTo(QTime::currentTime()) > PURGE_SECONDS) { - txSeqBuf.erase(it++); - } - else { - break; - } - } - } - txBufferMutex.unlock(); - - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked"; - } - - - - if (rxBufferMutex.tryLock(100)) - { - if (!rxSeqBuf.isEmpty()) { - // Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS - for (auto it = rxSeqBuf.begin(); it != rxSeqBuf.end();) { - if (it.value().secsTo(QTime::currentTime()) > PURGE_SECONDS) { - rxSeqBuf.erase(it++); - } - else { - break; - } - } - } - rxBufferMutex.unlock(); - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": rxBuffer mutex is locked"; - } - - if (missingMutex.tryLock(100)) - { - // Erase old entries from the missing packets buffer - if (!rxMissing.isEmpty() && rxMissing.size() > 50) { - for (size_t i = 0; i < 25; ++i) { - rxMissing.erase(rxMissing.begin()); - } - } - missingMutex.unlock(); - } else { - qInfo(logUdp()) << this->metaObject()->className() << ": missingBuffer mutex is locked"; - } -} - -void udpBase::printHex(const QByteArray& pdata) -{ - printHex(pdata, false, true); -} - -void udpBase::printHex(const QByteArray& pdata, bool printVert, bool printHoriz) -{ - qDebug(logUdp()) << "---- Begin hex dump -----:"; - QString sdata("DATA: "); - QString index("INDEX: "); - QStringList strings; - - for (int i = 0; i < pdata.length(); i++) - { - strings << QString("[%1]: %2").arg(i, 8, 10, QChar('0')).arg((unsigned char)pdata[i], 2, 16, QChar('0')); - sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0'))); - index.append(QString("%1 ").arg(i, 2, 10, QChar('0'))); - } - - if (printVert) - { - for (int i = 0; i < strings.length(); i++) - { - //sdata = QString(strings.at(i)); - qDebug(logUdp()) << strings.at(i); - } - } - - if (printHoriz) - { - qDebug(logUdp()) << index; - qDebug(logUdp()) << sdata; - } - qDebug(logUdp()) << "----- End hex dump -----"; -} - -/// -/// passcode function used to generate secure (ish) code -/// -/// -/// pointer to encoded username or password -void passcode(QString in, QByteArray& out) -{ - const quint8 sequence[] = - { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0x47,0x5d,0x4c,0x42,0x66,0x20,0x23,0x46,0x4e,0x57,0x45,0x3d,0x67,0x76,0x60,0x41,0x62,0x39,0x59,0x2d,0x68,0x7e, - 0x7c,0x65,0x7d,0x49,0x29,0x72,0x73,0x78,0x21,0x6e,0x5a,0x5e,0x4a,0x3e,0x71,0x2c,0x2a,0x54,0x3c,0x3a,0x63,0x4f, - 0x43,0x75,0x27,0x79,0x5b,0x35,0x70,0x48,0x6b,0x56,0x6f,0x34,0x32,0x6c,0x30,0x61,0x6d,0x7b,0x2f,0x4b,0x64,0x38, - 0x2b,0x2e,0x50,0x40,0x3f,0x55,0x33,0x37,0x25,0x77,0x24,0x26,0x74,0x6a,0x28,0x53,0x4d,0x69,0x22,0x5c,0x44,0x31, - 0x36,0x58,0x3b,0x7a,0x51,0x5f,0x52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - - }; - - QByteArray ba = in.toLocal8Bit(); - uchar* ascii = (uchar*)ba.constData(); - for (int i = 0; i < in.length() && i < 16; i++) - { - int p = ascii[i] + i; - if (p > 126) - { - p = 32 + p % 127; - } - out.append(sequence[p]); - } - return; -} - -/// -/// returns a QByteArray of a null terminated string -/// -/// -/// -/// -QByteArray parseNullTerminatedString(QByteArray c, int s) -{ - //QString res = ""; - QByteArray res; - for (int i = s; i < c.length(); i++) - { - if (c[i] != '\0') - { - res.append(c[i]); - } - else - { - break; - } - } - return res; -} - From 63c5e0257d3ed82a18802374577037e97331630f Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 11 May 2022 00:22:08 +0100 Subject: [PATCH 258/323] Some more tidying --- udpaudio.cpp | 10 +++++----- udpaudio.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/udpaudio.cpp b/udpaudio.cpp index e8d0f3a..39ef3bf 100644 --- a/udpaudio.cpp +++ b/udpaudio.cpp @@ -31,7 +31,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); - connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool))); + connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool,bool))); connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); @@ -182,13 +182,13 @@ void udpAudio::setVolume(unsigned char value) emit haveSetVolume(value); } -void udpAudio::getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under) { +void udpAudio::getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over) { - emit haveRxLevels(amplitude, latency, current, under); + emit haveRxLevels(amplitude, latency, current, under, over); } -void udpAudio::getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under) { - emit haveTxLevels(amplitude, latency, current, under); +void udpAudio::getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over) { + emit haveTxLevels(amplitude, latency, current, under, over); } void udpAudio::dataReceived() diff --git a/udpaudio.h b/udpaudio.h index ec03b8d..dc726ea 100644 --- a/udpaudio.h +++ b/udpaudio.h @@ -49,14 +49,14 @@ signals: void haveChangeLatency(quint16 value); void haveSetVolume(unsigned char value); - void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); - void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); public slots: void changeLatency(quint16 value); void setVolume(unsigned char value); - void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); - void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under); + void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); void receiveAudioData(audioPacket audio); private: From 44f6ec27402555c2c3cde7c6a59bf9d50b725286 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 01:46:20 +0100 Subject: [PATCH 259/323] Add back support for portaudo and rtaudio APIs --- audioconverter.h | 23 +- audiohandler.cpp | 1 - audiohandler.h | 35 --- pahandler.cpp | 275 ++++++++++++++++++++++++ pahandler.h | 93 ++++++++ rthandler.cpp | 334 +++++++++++++++++++++++++++++ rthandler.h | 111 ++++++++++ udpaudio.cpp | 31 ++- udpaudio.h | 6 +- wfmain.cpp | 370 +++++++++++++++++++++++--------- wfmain.h | 11 + wfmain.ui | 28 ++- wfserver.pro | 43 +++- wfview.pro | 45 ++-- wfview.vcxproj | 467 +++++++++++++++++++++++++++++++---------- wfview.vcxproj.filters | 127 ++++++++--- 16 files changed, 1698 insertions(+), 302 deletions(-) create mode 100644 pahandler.cpp create mode 100644 pahandler.h create mode 100644 rthandler.cpp create mode 100644 rthandler.h diff --git a/audioconverter.h b/audioconverter.h index 00d75a1..a0d2953 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -6,6 +6,7 @@ #include #include #include +#include /* Opus and Eigen */ #ifdef Q_OS_WIN @@ -16,6 +17,8 @@ #include #endif +enum audioType {qtAudio,portAudio,rtAudio}; + #include "resampler/speex_resampler.h" #include "packettypes.h" @@ -30,6 +33,22 @@ struct audioPacket { qreal volume = 1.0; }; +struct audioSetup { + audioType type; + QString name; + quint16 latency; + quint8 codec; + bool ulaw = false; + bool isinput; + quint32 sampleRate; + QAudioDeviceInfo port; + int portInt; + quint8 resampleQuality; + unsigned char localAFgain; + quint16 blockSize = 20; // Each 'block' of audio is 20ms long by default. + quint8 guid[GUIDLEN]; +}; + class audioConverter : public QObject { Q_OBJECT @@ -80,8 +99,8 @@ static inline QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) 0x80 Opus 2ch */ - format.setByteOrder(QAudioFormat::LittleEndian); - format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setCodec("audio/pcm"); format.setSampleRate(sampleRate); if (codec == 0x01 || codec == 0x20) { diff --git a/audiohandler.cpp b/audiohandler.cpp index 51d015d..d05b102 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -330,7 +330,6 @@ void audioHandler::changeLatency(const quint16 newSize) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << outFormat.durationForBytes(audioOutput->bufferSize())/1000 << "ms"; } - int audioHandler::getLatency() { return currentLatency; diff --git a/audiohandler.h b/audiohandler.h index 8bc8103..47e6412 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -20,27 +20,6 @@ #include #include -/* Current resampler code */ -#include "resampler/speex_resampler.h" - -/* Potential new resampler */ -//#include -//#include - - -/* Opus */ -#ifdef Q_OS_WIN -#include "opus.h" -#else -#include "opus/opus.h" -#endif - -/* Eigen */ -#ifndef Q_OS_WIN -#include -#else -#include -#endif /* wfview Packet types */ #include "packettypes.h" @@ -57,20 +36,6 @@ #define MULAW_MAX 0x1fff -struct audioSetup { - QString name; - quint16 latency; - quint8 codec; - bool ulaw = false; - bool isinput; - quint32 sampleRate; - QAudioDeviceInfo port; - quint8 resampleQuality; - unsigned char localAFgain; - quint16 blockSize=20; // Each 'block' of audio is 20ms long by default. - quint8 guid[GUIDLEN]; -}; - // For QtMultimedia, use a native QIODevice //class audioHandler : public QIODevice class audioHandler : public QObject diff --git a/pahandler.cpp b/pahandler.cpp new file mode 100644 index 0000000..7014ce2 --- /dev/null +++ b/pahandler.cpp @@ -0,0 +1,275 @@ +#include "pahandler.h" + +#include "logcategories.h" + +#if defined(Q_OS_WIN) +#include +#endif + + +paHandler::paHandler(QObject* parent) +{ + Q_UNUSED(parent) +} + +paHandler::~paHandler() +{ + + if (isInitialized) { + Pa_StopStream(audio); + Pa_CloseStream(audio); + } + + if (converterThread != Q_NULLPTR) { + converterThread->quit(); + converterThread->wait(); + } + + //Pa_Terminate(); + +} + +bool paHandler::init(audioSetup setup) +{ + if (isInitialized) { + return false; + } + + this->setup = setup; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "PortAudio handler starting:" << setup.name; + + if (setup.portInt==-1) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found."; + return false; + } + + inFormat = toQAudioFormat(setup.codec, setup.sampleRate); + + qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name << + ", bits" << inFormat.sampleSize() << + ", codec" << setup.codec << + ", latency" << setup.latency << + ", localAFGain" << setup.localAFgain << + ", radioChan" << inFormat.channelCount() << + ", resampleQuality" << setup.resampleQuality << + ", samplerate" << inFormat.sampleRate() << + ", uLaw" << setup.ulaw; + + PaError err; +#ifdef Q_OS_WIN + CoInitialize(0); +#endif + + //err = Pa_Initialize(); + //if (err != paNoError) + //{ + // qDebug(logAudio()) << "Portaudio initialized"; + //} + + memset(&aParams, 0, sizeof(PaStreamParameters)); + + aParams.device = setup.portInt; + info = Pa_GetDeviceInfo(aParams.device); + + qDebug(logAudio()) << "PortAudio" << (setup.isinput ? "Input" : "Output") << setup.portInt << "Input Channels" << info->maxInputChannels << "Output Channels" << info->maxOutputChannels; + + + if (setup.isinput) { + outFormat.setChannelCount(info->maxInputChannels); + } + else { + outFormat.setChannelCount(info->maxOutputChannels); + } + + aParams.suggestedLatency = (float)setup.latency/1000.0f; + outFormat.setSampleRate(info->defaultSampleRate); + aParams.sampleFormat = paFloat32; + outFormat.setSampleSize(32); + outFormat.setSampleType(QAudioFormat::Float); + outFormat.setByteOrder(QAudioFormat::LittleEndian); + outFormat.setCodec("audio/pcm"); + + + if (!setup.isinput) + { + this->setVolume(setup.localAFgain); + } + + if (outFormat.channelCount() > 2) { + outFormat.setChannelCount(2); + } + else if (outFormat.channelCount() < 1) + { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup."; + return false; + } + + if (inFormat.channelCount() < outFormat.channelCount()) { + outFormat.setChannelCount(inFormat.channelCount()); + } + + aParams.channelCount = outFormat.channelCount(); + + if (outFormat.sampleRate() < 44100) { + outFormat.setSampleRate(48000); + } + + + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() << + "Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType(); + + // We "hopefully" now have a valid format that is supported so try connecting + + converter = new audioConverter(); + converterThread = new QThread(this); + if (setup.isinput) { + converterThread->setObjectName("audioConvIn()"); + } + else { + converterThread->setObjectName("audioConvOut()"); + } + converter->moveToThread(converterThread); + + connect(this, SIGNAL(setupConverter(QAudioFormat, QAudioFormat, quint8, quint8)), converter, SLOT(init(QAudioFormat, QAudioFormat, quint8, quint8))); + connect(converterThread, SIGNAL(finished()), converter, SLOT(deleteLater())); + connect(this, SIGNAL(sendToConverter(audioPacket)), converter, SLOT(convert(audioPacket))); + converterThread->start(QThread::TimeCriticalPriority); + + aParams.hostApiSpecificStreamInfo = NULL; + + // Per channel chunk size. + this->chunkSize = (outFormat.bytesForDuration(setup.blockSize*1000)/sizeof(float))*outFormat.channelCount(); + + + if (setup.isinput) { + err = Pa_OpenStream(&audio, &aParams, 0, outFormat.sampleRate(), this->chunkSize, paNoFlag, &paHandler::staticWrite, (void*)this); + emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality); + connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket))); + } + else { + err = Pa_OpenStream(&audio, 0, &aParams, outFormat.sampleRate(), this->chunkSize, paNoFlag, NULL, NULL); + emit setupConverter(inFormat, outFormat, 7, setup.resampleQuality); + connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket))); + } + + if (err == paNoError) { + err = Pa_StartStream(audio); + } + if (err == paNoError) { + isInitialized = true; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened"; + } + else { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "failed to open device" << Pa_GetErrorText(err); + } + + return isInitialized; +} + + +void paHandler::setVolume(unsigned char volume) +{ + + this->volume = audiopot[volume]; + + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; +} + +void paHandler::incomingAudio(audioPacket packet) +{ + packet.volume = volume; + emit sendToConverter(packet); + return; +} + + + +int paHandler::writeData(const void* inputBuffer, void* outputBuffer, + unsigned long nFrames, const PaStreamCallbackTimeInfo * streamTime, + PaStreamCallbackFlags status) +{ + Q_UNUSED(outputBuffer); + Q_UNUSED(streamTime); + Q_UNUSED(status); + audioPacket packet; + packet.time = QTime::currentTime(); + packet.sent = 0; + packet.volume = volume; + memcpy(&packet.guid, setup.guid, GUIDLEN); + packet.data.append((char*)inputBuffer, nFrames*inFormat.channelCount()*sizeof(float)); + emit sendToConverter(packet); + + return paContinue; +} + + +void paHandler::convertedOutput(audioPacket packet) { + + if (packet.data.size() > 0) { + + if (Pa_IsStreamActive(audio) == 1) { + PaError err = Pa_WriteStream(audio, (char*)packet.data.data(), packet.data.size() / sizeof(float) / outFormat.channelCount()); + + if (err != paNoError) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Error writing audio!"; + } + const PaStreamInfo* info = Pa_GetStreamInfo(audio); + + //currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->outputLatency * 1000); + currentLatency = (info->outputLatency * 1000); + + } + /* + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); + + if (audioDevice != Q_NULLPTR) { + if (audioDevice->write(packet.data) < packet.data.size()) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full!"; + isOverrun = true; + } + else { + isOverrun = false; + } + if (lastReceived.msecsTo(QTime::currentTime()) > 100) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; + } + lastReceived = QTime::currentTime(); + } + + lastSentSeq = packet.seq; + + */ + amplitude = packet.amplitude; + emit haveLevels(getAmplitude(), setup.latency, currentLatency, false, false); + } +} + + + +void paHandler::convertedInput(audioPacket audio) +{ + if (audio.data.size() > 0) { + emit haveAudioData(audio); + amplitude = audio.amplitude; + emit haveLevels(getAmplitude(), setup.latency, currentLatency, false,false); + } +} + + + +void paHandler::changeLatency(const quint16 newSize) +{ + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency; +} + +int paHandler::getLatency() +{ + return currentLatency; +} + + +quint16 paHandler::getAmplitude() +{ + return amplitude; +} diff --git a/pahandler.h b/pahandler.h new file mode 100644 index 0000000..5e311aa --- /dev/null +++ b/pahandler.h @@ -0,0 +1,93 @@ +#ifndef PAHANDLER_H +#define PAHANDLER_H + +#include +#include +#include + +#include "portaudio.h" + +#include +#include +#include + + +/* wfview Packet types */ +#include "packettypes.h" + +/* Logarithmic taper for volume control */ +#include "audiotaper.h" + + +/* Audio converter class*/ +#include "audioconverter.h" + +#include + + +class paHandler : public QObject +{ + Q_OBJECT + +public: + paHandler(QObject* parent = 0); + ~paHandler(); + + int getLatency(); + + + void getNextAudioChunk(QByteArray& data); + quint16 getAmplitude(); + +public slots: + bool init(audioSetup setup); + void changeLatency(const quint16 newSize); + void setVolume(unsigned char volume); + void convertedInput(audioPacket audio); + void convertedOutput(audioPacket audio); + void incomingAudio(const audioPacket data); + + +private slots: + +signals: + void audioMessage(QString message); + void sendLatency(quint16 newSize); + void haveAudioData(const audioPacket& data); + void haveLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp); + void sendToConverter(audioPacket audio); + +private: + + int writeData(const void* inputBuffer, void* outputBuffer, + unsigned long nFrames, + const PaStreamCallbackTimeInfo* streamTime, + PaStreamCallbackFlags status); + static int staticWrite(const void* inputBuffer, void* outputBuffer, unsigned long nFrames, const PaStreamCallbackTimeInfo* streamTime, PaStreamCallbackFlags status, void* userData) { + return ((paHandler*)userData)->writeData(inputBuffer, outputBuffer, nFrames, streamTime, status); + } + + bool isInitialized = false; + PaStream* audio = Q_NULLPTR; + PaStreamParameters aParams; + const PaDeviceInfo* info; + + quint16 audioLatency; + unsigned int chunkSize; + + quint32 lastSeq; + quint32 lastSentSeq = 0; + + quint16 currentLatency; + quint16 amplitude = 0; + qreal volume = 1.0; + + audioSetup setup; + QAudioFormat inFormat; + QAudioFormat outFormat; + audioConverter* converter = Q_NULLPTR; + QThread* converterThread = Q_NULLPTR; +}; + +#endif // PAHANDLER_H diff --git a/rthandler.cpp b/rthandler.cpp new file mode 100644 index 0000000..44674c6 --- /dev/null +++ b/rthandler.cpp @@ -0,0 +1,334 @@ +#include "rthandler.h" + +#include "logcategories.h" + +#if defined(Q_OS_WIN) +#include +#endif + + +rtHandler::rtHandler(QObject* parent) +{ + Q_UNUSED(parent) +} + +rtHandler::~rtHandler() +{ + + if (isInitialized) { + try { + audio->abortStream(); + audio->closeStream(); + } + catch (RtAudioError& e) { + qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); + } + delete audio; + + } + + if (converterThread != Q_NULLPTR) { + converterThread->quit(); + converterThread->wait(); + } + +} + +bool rtHandler::init(audioSetup setup) +{ + if (isInitialized) { + return false; + } + + this->setup = setup; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "PortAudio handler starting:" << setup.name; + + if (setup.portInt==-1) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found."; + return false; + } + + inFormat = toQAudioFormat(setup.codec, setup.sampleRate); + + qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name << + ", bits" << inFormat.sampleSize() << + ", codec" << setup.codec << + ", latency" << setup.latency << + ", localAFGain" << setup.localAFgain << + ", radioChan" << inFormat.channelCount() << + ", resampleQuality" << setup.resampleQuality << + ", samplerate" << inFormat.sampleRate() << + ", uLaw" << setup.ulaw; + +#if !defined(Q_OS_MACX) + //options.flags = !RTAUDIO_HOG_DEVICE | RTAUDIO_MINIMIZE_LATENCY; + //options.flags = RTAUDIO_MINIMIZE_LATENCY; +#endif + +#if defined(Q_OS_LINUX) + audio = new RtAudio(RtAudio::Api::LINUX_ALSA); +#elif defined(Q_OS_WIN) + audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); +#elif defined(Q_OS_MACX) + audio = new RtAudio(RtAudio::Api::MACOSX_CORE); +#endif + + options.numberOfBuffers = setup.latency/setup.blockSize; + + if (setup.portInt > 0) { + aParams.deviceId = setup.portInt; + } + else if (setup.isinput) { + aParams.deviceId = audio->getDefaultInputDevice(); + } + else { + aParams.deviceId = audio->getDefaultOutputDevice(); + } + aParams.firstChannel = 0; + + try { + info = audio->getDeviceInfo(aParams.deviceId); + } + catch (RtAudioError& e) { + qInfo(logAudio()) << "Device error:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); + return isInitialized; + } + + if (info.probed) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed"; + + RtAudioFormat sampleFormat; + outFormat.setByteOrder(QAudioFormat::LittleEndian); + outFormat.setCodec("audio/pcm"); + + if (info.nativeFormats == 0) + { + qCritical(logAudio()) << " No natively supported data formats!"; + return false; + } + else { + qDebug(logAudio()) << " Supported formats:" << + (info.nativeFormats & RTAUDIO_SINT8 ? "8-bit int," : "") << + (info.nativeFormats & RTAUDIO_SINT16 ? "16-bit int," : "") << + (info.nativeFormats & RTAUDIO_SINT24 ? "24-bit int," : "") << + (info.nativeFormats & RTAUDIO_SINT32 ? "32-bit int," : "") << + (info.nativeFormats & RTAUDIO_FLOAT32 ? "32-bit float," : "") << + (info.nativeFormats & RTAUDIO_FLOAT64 ? "64-bit float," : ""); + + qInfo(logAudio()) << " Preferred sample rate:" << info.preferredSampleRate; + if (setup.isinput) { + outFormat.setChannelCount(info.inputChannels); + } + else { + outFormat.setChannelCount(info.outputChannels); + } + + qInfo(logAudio()) << " Channels:" << outFormat.channelCount(); + + if (outFormat.channelCount() > 2) { + outFormat.setChannelCount(2); + } + else if (outFormat.channelCount() < 1) + { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup."; + return false; + } + + aParams.nChannels = outFormat.channelCount(); + + + outFormat.setSampleRate(info.preferredSampleRate); + + if (outFormat.sampleRate() < 44100) { + outFormat.setSampleRate(48000); + } + + if (info.nativeFormats & RTAUDIO_FLOAT32) { + outFormat.setSampleType(QAudioFormat::Float); + outFormat.setSampleSize(32); + sampleFormat = RTAUDIO_FLOAT32; + } + else if (info.nativeFormats & RTAUDIO_SINT32) { + outFormat.setSampleType(QAudioFormat::SignedInt); + outFormat.setSampleSize(32); + sampleFormat = RTAUDIO_SINT32; + } + else if (info.nativeFormats & RTAUDIO_SINT16) { + outFormat.setSampleType(QAudioFormat::SignedInt); + outFormat.setSampleSize(16); + sampleFormat = RTAUDIO_SINT16; + } + else { + qCritical(logAudio()) << "Cannot find supported sample format!"; + return false; + } + } + + + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() << + "Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType(); + + // We "hopefully" now have a valid format that is supported so try connecting + converter = new audioConverter(); + converterThread = new QThread(this); + if (setup.isinput) { + converterThread->setObjectName("audioConvIn()"); + } + else { + converterThread->setObjectName("audioConvOut()"); + } + converter->moveToThread(converterThread); + + connect(this, SIGNAL(setupConverter(QAudioFormat, QAudioFormat, quint8, quint8)), converter, SLOT(init(QAudioFormat, QAudioFormat, quint8, quint8))); + connect(converterThread, SIGNAL(finished()), converter, SLOT(deleteLater())); + connect(this, SIGNAL(sendToConverter(audioPacket)), converter, SLOT(convert(audioPacket))); + converterThread->start(QThread::TimeCriticalPriority); + + + // Per channel chunk size. + this->chunkSize = (outFormat.bytesForDuration(setup.blockSize * 1000) / (outFormat.sampleSize()/8) / outFormat.channelCount()); + + try { + if (setup.isinput) { + audio->openStream(NULL, &aParams, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticWrite, this, &options); + emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality); + connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket))); + } + else { + audio->openStream(&aParams, NULL, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticRead, this , &options); + emit setupConverter(inFormat, outFormat, 7, setup.resampleQuality); + connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket))); + } + audio->startStream(); + isInitialized = true; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened"; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "detected latency:" << audio->getStreamLatency(); + } + catch (RtAudioError& e) { + qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); + } + } + else + { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!"; + } + + if (!setup.isinput) + { + this->setVolume(setup.localAFgain); + } + + + return isInitialized; +} + + +void rtHandler::setVolume(unsigned char volume) +{ + + this->volume = audiopot[volume]; + + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; +} + +void rtHandler::incomingAudio(audioPacket packet) +{ + packet.volume = volume; + emit sendToConverter(packet); + return; +} + +int rtHandler::readData(void* outputBuffer, void* inputBuffer, + unsigned int nFrames, double streamTime, RtAudioStreamStatus status) +{ + Q_UNUSED(inputBuffer); + Q_UNUSED(streamTime); + int nBytes = nFrames * outFormat.channelCount() * (outFormat.sampleSize()/8); + + //lastSentSeq = packet.seq; + if (arrayBuffer.length() >= nBytes) { + std::memcpy(outputBuffer, arrayBuffer.constData(), nBytes); + arrayBuffer.remove(0, nBytes); + } + + if (status == RTAUDIO_INPUT_OVERFLOW) { + isUnderrun = true; + } + else if (status == RTAUDIO_OUTPUT_UNDERFLOW) { + isOverrun = true; + } + else + { + isUnderrun = false; + isOverrun = false; + } + return 0; +} + + +int rtHandler::writeData(void* outputBuffer, void* inputBuffer, + unsigned int nFrames, double streamTime, RtAudioStreamStatus status) +{ + Q_UNUSED(outputBuffer); + Q_UNUSED(streamTime); + Q_UNUSED(status); + audioPacket packet; + packet.time = QTime::currentTime(); + packet.sent = 0; + packet.volume = volume; + memcpy(&packet.guid, setup.guid, GUIDLEN); + packet.data.append((char*)inputBuffer, nFrames *outFormat.channelCount() * (outFormat.sampleSize()/8)); + emit sendToConverter(packet); + if (status == RTAUDIO_INPUT_OVERFLOW) { + isUnderrun = true; + } + else if (status == RTAUDIO_OUTPUT_UNDERFLOW) { + isOverrun = true; + } + else + { + isUnderrun = false; + isOverrun = false; + } + + return 0; +} + + +void rtHandler::convertedOutput(audioPacket packet) +{ + arrayBuffer.append(packet.data); + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount()) * 1000); + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); +} + + + +void rtHandler::convertedInput(audioPacket audio) +{ + if (audio.data.size() > 0) { + emit haveAudioData(audio); + amplitude = audio.amplitude; + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); + } +} + + + +void rtHandler::changeLatency(const quint16 newSize) +{ + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency; +} + +int rtHandler::getLatency() +{ + return currentLatency; +} + + +quint16 rtHandler::getAmplitude() +{ + return amplitude; +} diff --git a/rthandler.h b/rthandler.h new file mode 100644 index 0000000..cb8ec58 --- /dev/null +++ b/rthandler.h @@ -0,0 +1,111 @@ +#ifndef rtHandler_H +#define rtHandler_H + +#include +#include +#include + +#ifdef Q_OS_WIN +#include "RtAudio.h" +#else +#include "rtaudio/RtAudio.h" +#endif + + +#include +#include +#include + + +/* wfview Packet types */ +#include "packettypes.h" + +/* Logarithmic taper for volume control */ +#include "audiotaper.h" + + +/* Audio converter class*/ +#include "audioconverter.h" + +#include + + +class rtHandler : public QObject +{ + Q_OBJECT + +public: + rtHandler(QObject* parent = 0); + ~rtHandler(); + + int getLatency(); + + + void getNextAudioChunk(QByteArray& data); + quint16 getAmplitude(); + +public slots: + bool init(audioSetup setup); + void changeLatency(const quint16 newSize); + void setVolume(unsigned char volume); + void convertedInput(audioPacket audio); + void convertedOutput(audioPacket audio); + void incomingAudio(const audioPacket data); + + +private slots: + +signals: + void audioMessage(QString message); + void sendLatency(quint16 newSize); + void haveAudioData(const audioPacket& data); + void haveLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp); + void sendToConverter(audioPacket audio); + +private: + + + int readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status); + + static int staticRead(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) { + return static_cast(userData)->readData(outputBuffer, inputBuffer, nFrames, streamTime, status); + } + + + int writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status); + + static int staticWrite(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) { + return static_cast(userData)->writeData(outputBuffer, inputBuffer, nFrames, streamTime, status); + } + + + bool isInitialized = false; + + RtAudio* audio = Q_NULLPTR; + int audioDevice = 0; + RtAudio::StreamParameters aParams; + RtAudio::StreamOptions options; + RtAudio::DeviceInfo info; + + quint16 audioLatency; + unsigned int chunkSize; + + quint32 lastSeq; + quint32 lastSentSeq = 0; + + quint16 currentLatency; + quint16 amplitude = 0; + qreal volume = 1.0; + + audioSetup setup; + QAudioFormat inFormat; + QAudioFormat outFormat; + audioConverter* converter = Q_NULLPTR; + QThread* converterThread = Q_NULLPTR; + QByteArray arrayBuffer; + bool isUnderrun = false; + bool isOverrun = true; +}; + +#endif // rtHandler_H diff --git a/udpaudio.cpp b/udpaudio.cpp index 39ef3bf..b49359d 100644 --- a/udpaudio.cpp +++ b/udpaudio.cpp @@ -16,8 +16,20 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint init(lport); // Perform connection QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); + if (rxSetup.type == qtAudio) { + rxaudio = new audioHandler(); + } + else if (rxSetup.type == portAudio) { + rxaudio = new paHandler(); + } + else if (rxSetup.type == rtAudio) { + rxaudio = new rtHandler(); + } + else + { + qCritical(logAudio()) << "Unsupported Receive Audio Handler selected!"; + } - rxaudio = new audioHandler(); rxAudioThread = new QThread(this); rxAudioThread->setObjectName("rxAudio()"); @@ -42,7 +54,20 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint pingTimer->start(PING_PERIOD); // send ping packets every 100ms if (enableTx) { - txaudio = new audioHandler(); + if (txSetup.type == qtAudio) { + txaudio = new audioHandler(); + } + else if (txSetup.type == portAudio) { + txaudio = new paHandler(); + } + else if (txSetup.type == rtAudio) { + txaudio = new rtHandler(); + } + else + { + qCritical(logAudio()) << "Unsupported Transmit Audio Handler selected!"; + } + txAudioThread = new QThread(this); rxAudioThread->setObjectName("txAudio()"); @@ -52,7 +77,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); - connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool))); + connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool, bool))); connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); emit setupTxAudio(txSetup); diff --git a/udpaudio.h b/udpaudio.h index dc726ea..478c342 100644 --- a/udpaudio.h +++ b/udpaudio.h @@ -28,6 +28,8 @@ #include "udpbase.h" #include "audiohandler.h" +#include "pahandler.h" +#include "rthandler.h" // Class for all audio communications. @@ -67,10 +69,10 @@ private: uint16_t sendAudioSeq = 0; - audioHandler* rxaudio = Q_NULLPTR; + QObject* rxaudio = Q_NULLPTR; QThread* rxAudioThread = Q_NULLPTR; - audioHandler* txaudio = Q_NULLPTR; + QObject* txaudio = Q_NULLPTR; QThread* txAudioThread = Q_NULLPTR; QTimer* txAudioTimer = Q_NULLPTR; diff --git a/wfmain.cpp b/wfmain.cpp index 26b86a0..3789bf1 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -56,8 +56,6 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s setSerialDevicesUI(); - setAudioDevicesUI(); - setDefaultColors(); setDefPrefs(); @@ -65,6 +63,9 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s setupPlots(); loadSettings(); // Look for saved preferences + + setAudioDevicesUI(); + setTuningSteps(); // TODO: Combine into preferences qDebug(logSystem()) << "Running setUIToPrefs()"; @@ -102,6 +103,10 @@ wfmain::~wfmain() if (rigCtl != Q_NULLPTR) { delete rigCtl; } + + if (prefs.audioSystem == portAudio) { + Pa_Terminate(); + } delete rpt; delete ui; delete settings; @@ -1061,43 +1066,6 @@ void wfmain::setUIToPrefs() ui->useCIVasRigIDChk->blockSignals(false); } -void wfmain::setAudioDevicesUI() -{ - - // Enumerate audio devices, need to do before settings are loaded. - qDebug(logSystem()) << "Finding audio output devices"; - const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); - for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { -#ifdef Q_OS_WIN - if (deviceInfo.realm() == "wasapi") { -#endif - ui->audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); -#ifdef Q_OS_WIN - } - -#endif - } - - qDebug(logSystem()) << "Finding audio input devices"; - const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); - for (const QAudioDeviceInfo& deviceInfo : audioInputs) { -#ifdef Q_OS_WIN - if (deviceInfo.realm() == "wasapi") { -#endif - ui->audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - ui->serverRXAudioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); -#ifdef Q_OS_WIN - } -#endif - } - // Set these to default audio devices initially. - qDebug(logSystem()) << "Audio devices done."; - rxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); - txSetup.port = QAudioDeviceInfo::defaultInputDevice(); - qDebug(logSystem()) << "Audio set to default device initially"; -} - void wfmain::setSerialDevicesUI() { ui->serialDeviceListCombo->blockSignals(true); @@ -1302,6 +1270,7 @@ void wfmain::setDefPrefs() defPrefs.meter2Type = meterNone; defPrefs.tcpPort = 0; defPrefs.waterfallFormat = 0; + defPrefs.audioSystem = qtAudio; udpDefPrefs.ipAddress = QString(""); udpDefPrefs.controlLANPort = 50001; @@ -1422,6 +1391,12 @@ void wfmain::loadSettings() rxSetup.localAFgain = prefs.localAFgain; txSetup.localAFgain = 255; + prefs.audioSystem = static_cast(settings->value("AudioSystem", defPrefs.audioSystem).toInt()); + ui->audioSystemCombo->blockSignals(true); + ui->audioSystemCombo->setCurrentIndex(prefs.audioSystem); + ui->audioSystemCombo->blockSignals(false); + + settings->endGroup(); // Misc. user settings (enable PTT, draw peaks, etc) @@ -1429,6 +1404,7 @@ void wfmain::loadSettings() prefs.enablePTT = settings->value("EnablePTT", defPrefs.enablePTT).toBool(); ui->pttEnableChk->setChecked(prefs.enablePTT); prefs.niceTS = settings->value("NiceTS", defPrefs.niceTS).toBool(); + settings->endGroup(); settings->beginGroup("LAN"); @@ -1531,31 +1507,12 @@ void wfmain::loadSettings() ui->audioTXCodecCombo->setCurrentIndex(f); ui->audioRXCodecCombo->blockSignals(false); - ui->audioOutputCombo->blockSignals(true); rxSetup.name = settings->value("AudioOutput", "").toString(); - qInfo(logGui()) << "Got Audio Output: " << rxSetup.name; - int audioOutputIndex = ui->audioOutputCombo->findText(rxSetup.name); - if (audioOutputIndex != -1) { - ui->audioOutputCombo->setCurrentIndex(audioOutputIndex); + qInfo(logGui()) << "Got Audio Output from Settings: " << rxSetup.name; - QVariant v = ui->audioOutputCombo->currentData(); - rxSetup.port = v.value(); - - } - ui->audioOutputCombo->blockSignals(false); - - ui->audioInputCombo->blockSignals(true); txSetup.name = settings->value("AudioInput", "").toString(); - qInfo(logGui()) << "Got Audio Input: " << txSetup.name; - int audioInputIndex = ui->audioInputCombo->findText(txSetup.name); - if (audioInputIndex != -1) { - ui->audioInputCombo->setCurrentIndex(audioInputIndex); + qInfo(logGui()) << "Got Audio Input from Settings: " << txSetup.name; - QVariant v = ui->audioInputCombo->currentData(); - txSetup.port = v.value(); - - } - ui->audioInputCombo->blockSignals(false); rxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); txSetup.resampleQuality = rxSetup.resampleQuality; @@ -1614,6 +1571,8 @@ void wfmain::loadSettings() rigTemp->txAudioSetup.localAFgain = 255; rigTemp->rxAudioSetup.resampleQuality = 4; rigTemp->txAudioSetup.resampleQuality = 4; + rigTemp->rxAudioSetup.type = prefs.audioSystem; + rigTemp->txAudioSetup.type = prefs.audioSystem; rigTemp->baudRate = prefs.serialPortBaud; rigTemp->civAddr = prefs.radioCIVAddr; @@ -1628,31 +1587,8 @@ void wfmain::loadSettings() memcpy(rigTemp->guid, QUuid::fromString(guid).toRfc4122().constData(), GUIDLEN); #endif - ui->serverRXAudioInputCombo->blockSignals(true); rigTemp->rxAudioSetup.name = settings->value("ServerAudioInput", "").toString(); - qInfo(logGui()) << "Got Server Audio Input: " << rigTemp->rxAudioSetup.name; - int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(rigTemp->rxAudioSetup.name); - if (serverAudioInputIndex != -1) { - ui->serverRXAudioInputCombo->setCurrentIndex(serverAudioInputIndex); - - QVariant v = ui->serverRXAudioInputCombo->currentData(); - rigTemp->rxAudioSetup.port = v.value(); - - } - ui->serverRXAudioInputCombo->blockSignals(false); - - ui->serverTXAudioOutputCombo->blockSignals(true); rigTemp->txAudioSetup.name = settings->value("ServerAudioOutput", "").toString(); - qInfo(logGui()) << "Got Server Audio Output: " << rigTemp->txAudioSetup.name; - int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(rigTemp->txAudioSetup.name); - if (serverAudioOutputIndex != -1) { - ui->serverTXAudioOutputCombo->setCurrentIndex(serverAudioOutputIndex); - - QVariant v = ui->serverTXAudioOutputCombo->currentData(); - rigTemp->txAudioSetup.port = v.value(); - - } - ui->serverTXAudioOutputCombo->blockSignals(false); serverConfig.rigs.append(rigTemp); int row = 0; @@ -1793,25 +1729,40 @@ void wfmain::on_serverAudioPortText_textChanged(QString text) void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) { - if (serverConfig.rigs.size() > 0) - { - QVariant v = ui->serverRXAudioInputCombo->itemData(value); - serverConfig.rigs.first()->rxAudioSetup.port = v.value(); - serverConfig.rigs.first()->rxAudioSetup.name = ui->serverRXAudioInputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio input to:" << serverConfig.rigs.first()->rxAudioSetup.name; + if (!serverConfig.rigs.isEmpty()) + { + if (prefs.audioSystem == qtAudio) { + QVariant v = ui->serverRXAudioInputCombo->itemData(value); + serverConfig.rigs.first()->rxAudioSetup.port = v.value(); + } + else { + serverConfig.rigs.first()->rxAudioSetup.portInt = ui->serverRXAudioInputCombo->itemData(value).toInt(); + } + } + + serverConfig.rigs.first()->rxAudioSetup.name = ui->audioInputCombo->itemText(value); + } void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) { - if (serverConfig.rigs.size() > 0) { - QVariant v = ui->serverTXAudioOutputCombo->itemData(value); - serverConfig.rigs.first()->txAudioSetup.port = v.value(); - serverConfig.rigs.first()->txAudioSetup.name = ui->serverTXAudioOutputCombo->itemText(value); - qDebug(logGui()) << "Changed default server audio output to:" << serverConfig.rigs.first()->txAudioSetup.name; + if (!serverConfig.rigs.isEmpty()) + { + if (prefs.audioSystem == qtAudio) { + QVariant v = ui->serverTXAudioOutputCombo->itemData(value); + serverConfig.rigs.first()->txAudioSetup.port = v.value(); + } + else { + serverConfig.rigs.first()->txAudioSetup.portInt = ui->serverTXAudioOutputCombo->itemData(value).toInt(); + } + } + + serverConfig.rigs.first()->txAudioSetup.name = ui->audioInputCombo->itemText(value); + } void wfmain::on_serverUsersTable_cellChanged(int row, int column) @@ -1871,6 +1822,8 @@ void wfmain::saveSettings() settings->setValue("SerialPortBaud", prefs.serialPortBaud); settings->setValue("VirtualSerialPort", prefs.virtualSerialPort); settings->setValue("localAFgain", prefs.localAFgain); + settings->setValue("AudioSystem", prefs.audioSystem); + settings->endGroup(); // Misc. user settings (enable PTT, draw peaks, etc) @@ -3364,6 +3317,8 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) ui->useRTSforPTTchk->blockSignals(false); ui->connectBtn->setText("Disconnect"); // We must be connected now. + ui->audioSystemCombo->setEnabled(false); + prepareWf(ui->wfLengthSlider->value()); if(usingLAN) { @@ -4622,20 +4577,33 @@ void wfmain::on_passwordTxt_textChanged(QString text) void wfmain::on_audioOutputCombo_currentIndexChanged(int value) { - QVariant v = ui->audioOutputCombo->itemData(value); - rxSetup.port = v.value(); + + if (prefs.audioSystem == qtAudio) { + QVariant v = ui->audioOutputCombo->itemData(value); + rxSetup.port = v.value(); + } + else { + rxSetup.portInt = ui->audioOutputCombo->itemData(value).toInt(); + } rxSetup.name = ui->audioOutputCombo->itemText(value); - qDebug(logGui()) << "Changed default audio output to:" << rxSetup.name; + qDebug(logGui()) << "Changed audio output to:" << rxSetup.name; } void wfmain::on_audioInputCombo_currentIndexChanged(int value) { - QVariant v = ui->audioInputCombo->itemData(value); - txSetup.port = v.value(); + + if (prefs.audioSystem == qtAudio) { + QVariant v = ui->audioInputCombo->itemData(value); + txSetup.port = v.value(); + } + else { + txSetup.portInt = ui->audioInputCombo->itemData(value).toInt(); + } txSetup.name = ui->audioInputCombo->itemText(value); - qDebug(logGui()) << "Changed default audio input to:" << txSetup.name; + + qDebug(logGui()) << "Changed audio input to:" << txSetup.name; } void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text) @@ -4690,6 +4658,7 @@ void wfmain::on_connectBtn_clicked() if (haveRigCaps) { emit sendCloseComm(); ui->connectBtn->setText("Connect"); + ui->audioSystemCombo->setEnabled(true); haveRigCaps = false; rigName->setText("NONE"); } @@ -5799,3 +5768,204 @@ void wfmain::on_debugBtn_clicked() adjustSize(); } + + +void wfmain::setAudioDevicesUI() +{ + + // Enumerate audio devices, need to do before settings are loaded, + // First clear all existing entries + ui->audioInputCombo->blockSignals(true); + ui->audioOutputCombo->blockSignals(true); + ui->serverTXAudioOutputCombo->blockSignals(true); + ui->serverRXAudioInputCombo->blockSignals(true); + + ui->audioInputCombo->clear(); + ui->audioOutputCombo->clear(); + ui->serverTXAudioOutputCombo->clear(); + ui->serverRXAudioInputCombo->clear(); + + qDebug(logSystem()) << "Finding audio devices, output=" << rxSetup.name << "input="<audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); + ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); +#ifdef Q_OS_WIN + } + #endif + } + + const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + for (const QAudioDeviceInfo& deviceInfo : audioInputs) { +#ifdef Q_OS_WIN + if (deviceInfo.realm() == "wasapi") { +#endif + ui->audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); + ui->serverRXAudioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); +#ifdef Q_OS_WIN + } +#endif + } + + } + break; + case portAudio: + { + PaError err; + + err = Pa_Initialize(); + + if (err != paNoError) + { + qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio"; + return; + } + + qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText; + + int numDevices; + numDevices = Pa_GetDeviceCount(); + qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; + + const PaDeviceInfo* info; + for (int i = 0; i < numDevices; i++) + { + info = Pa_GetDeviceInfo(i); + if (info->maxInputChannels > 0) { + qInfo(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name; + + ui->audioInputCombo->addItem(info->name, i); + ui->serverRXAudioInputCombo->addItem(info->name, i); + } + if (info->maxOutputChannels > 0) { + qInfo(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name; + ui->audioOutputCombo->addItem(info->name, i); + ui->serverTXAudioOutputCombo->addItem(info->name, i); + } + } + } + break; + case rtAudio: + { + Pa_Terminate(); + +#if defined(Q_OS_LINUX) + RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); +#elif defined(Q_OS_WIN) + RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); +#elif defined(Q_OS_MACX) + RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE); +#endif + + + // Enumerate audio devices, need to do before settings are loaded. + std::map apiMap; + apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; + apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; + apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound"; + apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI"; + apiMap[RtAudio::UNIX_JACK] = "Jack Client"; + apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; + apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; + apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; + apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; + + std::vector< RtAudio::Api > apis; + RtAudio::getCompiledApi(apis); + + qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion()); + + qInfo(logAudio()) << "Compiled APIs:"; + for (unsigned int i = 0; i < apis.size(); i++) { + qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]); + } + + RtAudio::DeviceInfo info; + + qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]); + + unsigned int devices = audio->getDeviceCount(); + qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; + + for (unsigned int i = 1; i < devices; i++) { + info = audio->getDeviceInfo(i); + if (info.outputChannels > 0) { + qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); + ui->audioOutputCombo->addItem(QString::fromStdString(info.name), i); + ui->serverTXAudioOutputCombo->addItem(QString::fromStdString(info.name), i); + } + if (info.inputChannels > 0) { + qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name); + ui->audioInputCombo->addItem(QString::fromStdString(info.name), i); + ui->serverRXAudioInputCombo->addItem(QString::fromStdString(info.name), i); + } + } + + delete audio; + + } + break; + + } + + + // Stop blocking signals so we can set the current values + ui->audioInputCombo->blockSignals(false); + ui->audioOutputCombo->blockSignals(false); + ui->serverTXAudioOutputCombo->blockSignals(false); + ui->serverRXAudioInputCombo->blockSignals(false); + + + rxSetup.type = prefs.audioSystem; + txSetup.type = prefs.audioSystem; + + int audioInputIndex = ui->audioInputCombo->findText(txSetup.name); + if (audioInputIndex != -1) { + ui->audioInputCombo->setCurrentIndex(audioInputIndex); + } + else { + qDebug(logSystem()) << "Audio input not found"; + } + + int audioOutputIndex = ui->audioOutputCombo->findText(rxSetup.name); + if (audioOutputIndex != -1) { + ui->audioOutputCombo->setCurrentIndex(audioOutputIndex); + } + else { + qDebug(logSystem()) << "Audio output not found"; + } + + if (!serverConfig.rigs.isEmpty()) + + { + qInfo(logGui()) << "Got Server Audio Input: " << serverConfig.rigs.first()->rxAudioSetup.name; + int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(serverConfig.rigs.first()->rxAudioSetup.name); + if (serverAudioInputIndex != -1) { + ui->serverRXAudioInputCombo->setCurrentIndex(serverAudioInputIndex); + } + + qInfo(logGui()) << "Got Server Audio Output: " << serverConfig.rigs.first()->txAudioSetup.name; + int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(serverConfig.rigs.first()->txAudioSetup.name); + if (serverAudioOutputIndex != -1) { + ui->serverTXAudioOutputCombo->setCurrentIndex(serverAudioOutputIndex); + } + } + // Set these to default audio devices initially. + qDebug(logSystem()) << "Audio devices done."; +} + +void wfmain::on_audioSystemCombo_currentIndexChanged(int value) +{ + prefs.audioSystem = static_cast(value); + setAudioDevicesUI(); // Force all audio devices to update +} \ No newline at end of file diff --git a/wfmain.h b/wfmain.h index 474c165..29bded4 100644 --- a/wfmain.h +++ b/wfmain.h @@ -40,6 +40,14 @@ #include #include +#include +#ifdef Q_OS_WIN +#include "RtAudio.h" +#else +#include "rtaudio/RtAudio.h" +#endif + + namespace Ui { class wfmain; } @@ -529,6 +537,8 @@ private slots: void on_radioStatusBtn_clicked(); + void on_audioSystemCombo_currentIndexChanged(int value); + private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); @@ -769,6 +779,7 @@ private: meterKind meter2Type; quint16 tcpPort; quint8 waterfallFormat; + audioType audioSystem; // plot scheme } prefs; diff --git a/wfmain.ui b/wfmain.ui index e1b24a7..124a54b 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -2066,7 +2066,7 @@ - 4 + 0 @@ -2633,6 +2633,32 @@ + + + + Audio System + + + + + + + + QT Audio + + + + + PortAudio + + + + + RT Audio + + + + diff --git a/wfserver.pro b/wfserver.pro index 37dad25..d17f9eb 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -17,21 +17,38 @@ DEFINES += WFVIEW_VERSION=\\\"1.2e\\\" DEFINES += BUILD_WFSERVER -CONFIG(debug, release|debug) { -# For Debug builds only: -QMAKE_CXXFLAGS += -faligned-new -WIN32:DESTDIR = wfview-debug +CONFIG(debug, release|debug) { + # For Debug builds only: + QMAKE_CXXFLAGS += -faligned-new + win32:DESTDIR = wfview-release + win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 } else { -# For Release builds only: -linux:QMAKE_CXXFLAGS += -s -QMAKE_CXXFLAGS += -fvisibility=hidden -QMAKE_CXXFLAGS += -fvisibility-inlines-hidden -QMAKE_CXXFLAGS += -faligned-new -linux:QMAKE_LFLAGS += -O2 -s -WIN32:DESTDIR = wfview-release + # For Release builds only: + linux:QMAKE_CXXFLAGS += -s + QMAKE_CXXFLAGS += -fvisibility=hidden + QMAKE_CXXFLAGS += -fvisibility-inlines-hidden + QMAKE_CXXFLAGS += -faligned-new + linux:QMAKE_LFLAGS += -O2 -s + win32:DESTDIR = wfview-debug + win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 } +# RTAudio defines +win32:DEFINES += __WINDOWS_WASAPI__ +#win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries +#linux:DEFINES += __LINUX_ALSA__ +#linux:DEFINES += __LINUX_OSS__ +linux:DEFINES += __LINUX_PULSE__ +macx:DEFINES += __MACOSX_CORE__ +win32:SOURCES += ../rtaudio/RTAudio.cpp +win32:HEADERS += ../rtaudio/RTAUdio.h +!linux:INCLUDEPATH += ../rtaudio +linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread + +win32:INCLUDEPATH += ../portaudio/include +!win32:LIBS += -lportaudio + # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the @@ -144,6 +161,8 @@ SOURCES += main.cpp\ udpcivdata.cpp \ udpaudio.cpp \ logcategories.cpp \ + pahandler.cpp \ + rthandler.cpp \ audiohandler.cpp \ audioconverter.cpp \ udpserver.cpp \ @@ -163,6 +182,8 @@ HEADERS += servermain.h \ udpcivdata.h \ udpaudio.h \ logcategories.h \ + pahandler.h \ + rthandler.h \ audiohandler.h \ audioconverter.h \ udpserver.h \ diff --git a/wfview.pro b/wfview.pro index 667ffff..ef0e2be 100644 --- a/wfview.pro +++ b/wfview.pro @@ -16,19 +16,36 @@ DEFINES += WFVIEW_VERSION=\\\"1.2e\\\" DEFINES += BUILD_WFVIEW CONFIG(debug, release|debug) { -# For Debug builds only: -QMAKE_CXXFLAGS += -faligned-new -WIN32:DESTDIR = wfview-release + # For Debug builds only: + QMAKE_CXXFLAGS += -faligned-new + win32:DESTDIR = wfview-release + win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 } else { -# For Release builds only: -linux:QMAKE_CXXFLAGS += -s -QMAKE_CXXFLAGS += -fvisibility=hidden -QMAKE_CXXFLAGS += -fvisibility-inlines-hidden -QMAKE_CXXFLAGS += -faligned-new -linux:QMAKE_LFLAGS += -O2 -s -WIN32:DESTDIR = wfview-debug + # For Release builds only: + linux:QMAKE_CXXFLAGS += -s + QMAKE_CXXFLAGS += -fvisibility=hidden + QMAKE_CXXFLAGS += -fvisibility-inlines-hidden + QMAKE_CXXFLAGS += -faligned-new + linux:QMAKE_LFLAGS += -O2 -s + win32:DESTDIR = wfview-debug + win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 } +# RTAudio defines +win32:DEFINES += __WINDOWS_WASAPI__ +#win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries +#linux:DEFINES += __LINUX_ALSA__ +#linux:DEFINES += __LINUX_OSS__ +linux:DEFINES += __LINUX_PULSE__ +macx:DEFINES += __MACOSX_CORE__ +win32:SOURCES += ../rtaudio/RTAudio.cpp +win32:HEADERS += ../rtaudio/RTAUdio.h +!linux:INCLUDEPATH += ../rtaudio +linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread + +win32:INCLUDEPATH += ../portaudio/include +!win32:LIBS += -lportaudio + # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the @@ -59,10 +76,6 @@ isEmpty(PREFIX) { DEFINES += PREFIX=\\\"$$PREFIX\\\" -# Choose audio system, uses QTMultimedia if both are commented out. -# DEFINES += RTAUDIO -# DEFINES += PORTAUDIO - contains(DEFINES, RTAUDIO) { # RTAudio defines win32:DEFINES += __WINDOWS_WASAPI__ @@ -167,6 +180,8 @@ SOURCES += main.cpp\ udpcivdata.cpp \ udpaudio.cpp \ logcategories.cpp \ + pahandler.cpp \ + rthandler.cpp \ audiohandler.cpp \ audioconverter.cpp \ calibrationwindow.cpp \ @@ -193,6 +208,8 @@ HEADERS += wfmain.h \ udpcivdata.h \ udpaudio.h \ logcategories.h \ + pahandler.h \ + rthandler.h \ audiohandler.h \ audioconverter.h \ calibrationwindow.h \ diff --git a/wfview.vcxproj b/wfview.vcxproj index 5c147a6..355ab27 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -16,12 +16,11 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild - + $(MSBuildProjectDirectory)\QtMsBuild v142 - release\ + wfview-release\ false NotSet Application @@ -30,17 +29,14 @@ v142 - debug\ + wfview-debug\ false NotSet Application debug\ wfview - - - - + @@ -48,37 +44,11 @@ - - - - - - debug\ - debug\ - wfview - true - - - release\ - release\ - wfview - true - false - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - - + wfview-debug\debug\wfviewtruewfview-release\release\wfviewtruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport + - .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) + .;..\rtaudio;..\portaudio\include;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -87,19 +57,17 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="b510b70";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="63c5e02";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - - + MultiThreadedDLL true true Level3 - true - + true - ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) - ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + ..\portaudio\msvc\Win32\Release\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) + ..\portaudio\msvc\Win32\Release;..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true false @@ -117,31 +85,12 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"b510b70\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"63c5e02\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h - .;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) + .;..\rtaudio;..\portaudio\include;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -150,17 +99,16 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="b510b70";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="63c5e02";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true - + true - ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) - ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + ..\portaudio\msvc\Win32\Debug\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) + ..\portaudio\msvc\Win32\Debug;..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true true @@ -176,29 +124,11 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"b510b70\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"63c5e02\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - - Uic'ing %(Identity)... - $(ProjectDir) - ui_%(Filename).h - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + @@ -208,6 +138,7 @@ + @@ -216,6 +147,7 @@ + @@ -228,58 +160,264 @@ + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + Document true @@ -296,21 +434,127 @@ release\moc_predefs.h;%(Outputs) true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -344,16 +588,30 @@ - resources - resources - + + + + + + + + + + resourcesresources - style - style - + + + + + + + + + + stylestyle @@ -367,9 +625,6 @@ - - - - + \ No newline at end of file diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index 40ce604..a8fca3d 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -47,9 +47,15 @@ + + Source Files + Source Files + + Source Files + Source Files @@ -71,6 +77,9 @@ Source Files + + Source Files + Source Files @@ -95,6 +104,9 @@ Source Files + + Source Files + Source Files @@ -107,6 +119,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + Source Files @@ -116,26 +137,20 @@ Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - + + Header Files + Header Files Header Files + + Header Files + Header Files @@ -160,6 +175,9 @@ Header Files + + Header Files + Header Files @@ -187,6 +205,9 @@ Header Files + + Header Files + Header Files @@ -202,6 +223,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + Header Files @@ -214,23 +244,71 @@ Header Files - - Header Files - - - Header Files - - - Header Files - + + + + + + + + + + + + + + Generated Files Generated Files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -387,11 +465,6 @@ - - - - - Header Files - + \ No newline at end of file From 578b993f70a8311b972aae27007ac697ed79b7cb Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 12:24:27 +0100 Subject: [PATCH 260/323] Various fixes --- rthandler.cpp | 2 +- servermain.cpp | 377 ++++++++++++++++++++++----------------- servermain.h | 1 + wfmain.cpp | 4 +- wfserver.vcxproj | 310 ++++++++++++++++++++++---------- wfserver.vcxproj.filters | 92 +++++++++- 6 files changed, 513 insertions(+), 273 deletions(-) diff --git a/rthandler.cpp b/rthandler.cpp index 44674c6..41ad6c1 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -74,7 +74,7 @@ bool rtHandler::init(audioSetup setup) audio = new RtAudio(RtAudio::Api::MACOSX_CORE); #endif - options.numberOfBuffers = setup.latency/setup.blockSize; + options.numberOfBuffers = int(setup.latency/setup.blockSize); if (setup.portInt > 0) { aParams.deviceId = setup.portInt; diff --git a/servermain.cpp b/servermain.cpp index 2db93d3..adaef67 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -265,7 +265,7 @@ void servermain::receiveCommReady() void servermain::connectToRig(RIGCONFIG* rig) { if (!rig->rigAvailable) { - //qDebug(logSystem()) << "Searching for rig on" << rig->serialPort; + qDebug(logSystem()) << "Searching for rig on" << rig->serialPort; QMetaObject::invokeMethod(rig->rig, [=]() { rig->rig->findRigs(); }, Qt::QueuedConnection); @@ -421,6 +421,9 @@ void servermain::setDefPrefs() defPrefs.serialPortBaud = 115200; defPrefs.localAFgain = 255; defPrefs.tcpPort = 0; + defPrefs.audioSystem = qtAudio; + defPrefs.rxAudio.name = QString("default"); + defPrefs.txAudio.name = QString("default"); udpDefPrefs.ipAddress = QString(""); udpDefPrefs.controlLANPort = 50001; @@ -434,86 +437,58 @@ void servermain::setDefPrefs() void servermain::loadSettings() { qInfo(logSystem()) << "Loading settings from " << settings->fileName(); + prefs.audioSystem = static_cast(settings->value("AudioSystem", defPrefs.audioSystem).toInt()); - int numRadios=settings->beginReadArray("Radios"); - int tempNum = numRadios; + int numRadios = settings->beginReadArray("Radios"); if (numRadios == 0) { settings->endArray(); + + // We assume that QSettings is empty as there are no radios configured, create new: + qInfo(logSystem()) << "Creating new settings file " << settings->fileName(); + settings->setValue("AudioSystem", defPrefs.audioSystem); numRadios = 1; - } - -#if defined(RTAUDIO) - -#if defined(Q_OS_LINUX) - RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); -#elif defined(Q_OS_WIN) - RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); -#elif defined(Q_OS_MACX) - RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE); -#endif - - // Enumerate audio devices, need to do before settings are loaded. - std::map apiMap; - apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; - apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; - apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound"; - apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI"; - apiMap[RtAudio::UNIX_JACK] = "Jack Client"; - apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; - apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; - apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; - apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; - - std::vector< RtAudio::Api > apis; - RtAudio::getCompiledApi(apis); - - qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion()); - - qInfo(logAudio()) << "Compiled APIs:"; - for (unsigned int i = 0; i < apis.size(); i++) { - qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]); - } - - RtAudio::DeviceInfo info; - - qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]); - - unsigned int devices = audio->getDeviceCount(); - qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; - -#elif defined(PORTAUDIO) - // Use PortAudio device enumeration - - PaError err; - - err = Pa_Initialize(); - - if (err != paNoError) - { - qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio"; - } - - qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText; - - int numDevices; - numDevices = Pa_GetDeviceCount(); - qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; - - const PaDeviceInfo* info; - -#else - - const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); - const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); - -#endif - for (int i = 0; i < numRadios; i++) { - if (tempNum == 0) { - settings->beginGroup("Radio"); - } - else { + settings->beginWriteArray("Radios"); + for (int i = 0; i < numRadios; i++) + { settings->setArrayIndex(i); + settings->setValue("RigCIVuInt", defPrefs.radioCIVAddr); + settings->setValue("ForceRTSasPTT", defPrefs.forceRTSasPTT); + settings->setValue("SerialPortRadio", defPrefs.serialPortRadio); + settings->setValue("RigName", ""); + settings->setValue("SerialPortBaud", defPrefs.serialPortBaud); + settings->setValue("AudioInput", defPrefs.rxAudio.name); + settings->setValue("AudioOutput", defPrefs.txAudio.name); } + settings->endArray(); + + settings->beginGroup("Server"); + settings->setValue("ServerEnabled", true); + settings->setValue("ServerControlPort", serverConfig.controlPort); + settings->setValue("ServerCivPort", serverConfig.civPort); + settings->setValue("ServerAudioPort", serverConfig.audioPort); + + settings->beginWriteArray("Users"); + settings->setArrayIndex(0); + settings->setValue("Username", "user"); + QByteArray pass; + passcode("password", pass); + settings->setValue("Password", QString(pass)); + settings->setValue("UserType", 0); + + settings->endArray(); + + settings->endGroup(); + settings->sync(); + + } else { + settings->endArray(); + } + + numRadios = settings->beginReadArray("Radios"); + int tempNum = numRadios; + + for (int i = 0; i < numRadios; i++) { + settings->setArrayIndex(i); RIGCONFIG* tempPrefs = new RIGCONFIG(); tempPrefs->civAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); tempPrefs->forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool(); @@ -521,17 +496,23 @@ void servermain::loadSettings() tempPrefs->rigName = settings->value("RigName", "").toString(); tempPrefs->baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); + QString tempPort = "auto"; if (tempPrefs->rigName=="") { foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) { qDebug(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); - if (serialPortInfo.portName() == tempPrefs->serialPort && !serialPortInfo.serialNumber().isEmpty()) + if ((serialPortInfo.portName() == tempPrefs->serialPort || tempPrefs->serialPort == "auto") && !serialPortInfo.serialNumber().isEmpty()) { - tempPrefs->rigName = serialPortInfo.serialNumber(); + if (serialPortInfo.serialNumber().startsWith("IC-")) { + tempPrefs->rigName = serialPortInfo.serialNumber(); + tempPort = serialPortInfo.portName(); + } } } } + tempPrefs->serialPort = tempPort; + QString guid = settings->value("GUID", "").toString(); if (guid.isEmpty()) { guid = QUuid::createUuid().toString(); @@ -549,83 +530,6 @@ void servermain::loadSettings() tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "").toString(); tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "").toString(); - bool rxDeviceFound = false; - bool txDeviceFound = false; - // Find the actual audio devices -#if defined(RTAUDIO) - for (unsigned int i = 1; i < devices; i++) { - info = audio->getDeviceInfo(i); - if (info.outputChannels > 0) { - if (tempPrefs->txAudioSetup.name == info->name) { - tempPrefs->txAudioSetup.port = i; - txDeviceFound = true; - } - } - if (info.inputChannels > 0) { - if (tempPrefs->rxAudioSetup.name == info->name) { - tempPrefs->rxAudioSetup.port = i; - rxDeviceFound = true; - } - } - } -#elif defined(PORTAUDIO) - for (int i = 0; i < numDevices; i++) - { - info = Pa_GetDeviceInfo(i); - if (info->maxInputChannels > 0) { - if (tempPrefs->txAudioSetup.name == info->name) { - tempPrefs->txAudioSetup.port = i; - txDeviceFound = true; - } - } - if (info->maxOutputChannels > 0) { - if (tempPrefs->rxAudioSetup.name == info->name) { - tempPrefs->rxAudioSetup.port = i; - rxDeviceFound = true; - } - } - } -#else - - /* If no external library is configured, use QTMultimedia - // Set these to default audio devices initially. - */ - - //qInfo(logAudio()) << "Looking for audio output devices"; - for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { - qDebug(logSystem()) << "Found Audio output: " << deviceInfo.deviceName(); - if (deviceInfo.deviceName() == tempPrefs->txAudioSetup.name -#ifdef Q_OS_WIN - && deviceInfo.realm() == "wasapi" -#endif - ) { - qDebug(logSystem()) << "Audio output: " << deviceInfo.deviceName(); - tempPrefs->txAudioSetup.port = deviceInfo; - txDeviceFound = true; - } - } - - //qInfo(logAudio()) << "Looking for audio input devices"; - for (const QAudioDeviceInfo& deviceInfo : audioInputs) { - qDebug(logSystem()) << "Found Audio input: " << deviceInfo.deviceName(); - if (deviceInfo.deviceName() == tempPrefs->rxAudioSetup.name -#ifdef Q_OS_WIN - && deviceInfo.realm() == "wasapi" -#endif - ) { - qDebug(logSystem()) << "Audio input: " << deviceInfo.deviceName(); - tempPrefs->rxAudioSetup.port = deviceInfo; - rxDeviceFound = true; - } - } -#endif - - if (!txDeviceFound) { - qInfo() << "Cannot find txAudioDevice" << tempPrefs->txAudioSetup.name; - } - if (!rxDeviceFound) { - qInfo() << "Cannot find rxAudioDevice" << tempPrefs->rxAudioSetup.name; - } tempPrefs->rig = Q_NULLPTR; tempPrefs->rigThread = Q_NULLPTR; serverConfig.rigs.append(tempPrefs); @@ -638,6 +542,157 @@ void servermain::loadSettings() } + /* + Now we have an array of rig objects, we need to match the configured audio devices with physical devices + */ + switch (prefs.audioSystem) + { + case rtAudio: + { +#if defined(Q_OS_LINUX) + RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); +#elif defined(Q_OS_WIN) + RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); +#elif defined(Q_OS_MACX) + RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE); +#endif + + // Enumerate audio devices, need to do before settings are loaded. + std::map apiMap; + apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; + apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; + apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound"; + apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI"; + apiMap[RtAudio::UNIX_JACK] = "Jack Client"; + apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; + apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; + apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; + apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; + + std::vector< RtAudio::Api > apis; + RtAudio::getCompiledApi(apis); + + qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion()); + + qInfo(logAudio()) << "Compiled APIs:"; + for (unsigned int i = 0; i < apis.size(); i++) { + qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]); + } + + RtAudio::DeviceInfo info; + + qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]); + + unsigned int devices = audio->getDeviceCount(); + qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; + + for (unsigned int i = 1; i < devices; i++) { + info = audio->getDeviceInfo(i); + for (RIGCONFIG* rig : serverConfig.rigs) + { + if (info.outputChannels > 0) + { + qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); + if (rig->txAudioSetup.name.toStdString() == info.name) { + rig->txAudioSetup.portInt = i; + } + } + if (info.inputChannels > 0) + { + qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name); + if (rig->rxAudioSetup.name.toStdString() == info.name) { + rig->rxAudioSetup.portInt = i; + } + } + } + } + break; + } + case portAudio: + { + // Use PortAudio device enumeration + + PaError err; + + err = Pa_Initialize(); + + if (err != paNoError) + { + qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio"; + } + + qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText; + + int numDevices; + numDevices = Pa_GetDeviceCount(); + qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; + + const PaDeviceInfo* info; + for (int i = 0; i < numDevices; i++) + { + info = Pa_GetDeviceInfo(i); + for (RIGCONFIG* rig : serverConfig.rigs) + { + if (info->maxInputChannels > 0) { + qDebug(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name; + + if (rig->txAudioSetup.name == info->name) { + rig->txAudioSetup.portInt = i; + } + } + if (info->maxOutputChannels > 0) { + qDebug(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name; + if (rig->rxAudioSetup.name == info->name) { + rig->rxAudioSetup.portInt = i; + } + } + } + } + break; + } + case qtAudio: + { + const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); + const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + //qInfo(logAudio()) << "Looking for audio input devices"; + for (const QAudioDeviceInfo& deviceInfo : audioInputs) { + qDebug(logSystem()) << "Found Audio input: " << deviceInfo.deviceName(); + for (RIGCONFIG* rig : serverConfig.rigs) + { + if (deviceInfo.deviceName() == rig->rxAudioSetup.name +#ifdef Q_OS_WIN + && deviceInfo.realm() == "wasapi" +#endif + ) + { + qDebug(logSystem()) << "Audio input: " << deviceInfo.deviceName(); + rig->rxAudioSetup.port = deviceInfo; + } + } + } + + //qInfo(logAudio()) << "Looking for audio output devices"; + for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { + qDebug(logSystem()) << "Found Audio output: " << deviceInfo.deviceName(); + for (RIGCONFIG* rig : serverConfig.rigs) + { + if (deviceInfo.deviceName() == rig->txAudioSetup.name +#ifdef Q_OS_WIN + && deviceInfo.realm() == "wasapi" +#endif + ) + { + qDebug(logSystem()) << "Audio output: " << deviceInfo.deviceName(); + rig->txAudioSetup.port = deviceInfo; + } + } + } + break; + } + } + + + settings->beginGroup("Server"); serverConfig.enabled = settings->value("ServerEnabled", false).toBool(); serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt(); @@ -657,24 +712,10 @@ void servermain::loadSettings() user.password = settings->value("Password", "").toString(); user.userType = settings->value("UserType", 0).toInt(); serverConfig.users.append(user); - } } settings->endArray(); } - else { - /* Support old way of storing users just to get them loaded*/ - settings->endArray(); - numUsers = settings->value("ServerNumUsers", 2).toInt(); - for (int f = 0; f < numUsers; f++) - { - SERVERUSER user; - user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); - user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); - user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); - serverConfig.users.append(user); - } - } settings->endGroup(); settings->sync(); diff --git a/servermain.h b/servermain.h index af9150c..a3af388 100644 --- a/servermain.h +++ b/servermain.h @@ -243,6 +243,7 @@ private: rigCapabilities rigCaps; bool haveRigCaps = false; quint16 tcpPort; + audioType audioSystem; } prefs; preferences defPrefs; diff --git a/wfmain.cpp b/wfmain.cpp index 3789bf1..aea3a07 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -5842,13 +5842,13 @@ void wfmain::setAudioDevicesUI() { info = Pa_GetDeviceInfo(i); if (info->maxInputChannels > 0) { - qInfo(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name; + qDebug(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name; ui->audioInputCombo->addItem(info->name, i); ui->serverRXAudioInputCombo->addItem(info->name, i); } if (info->maxOutputChannels > 0) { - qInfo(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name; + qDebug(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name; ui->audioOutputCombo->addItem(info->name, i); ui->serverTXAudioOutputCombo->addItem(info->name, i); } diff --git a/wfserver.vcxproj b/wfserver.vcxproj index e619017..471461b 100644 --- a/wfserver.vcxproj +++ b/wfserver.vcxproj @@ -16,12 +16,11 @@ QtVS_v304 10.0.19041.0 10.0.19041.0 - $(MSBuildProjectDirectory)\QtMsBuild - + $(MSBuildProjectDirectory)\QtMsBuild v142 - release\ + wfview-release\ false NotSet Application @@ -30,17 +29,14 @@ v142 - debug\ + wfview-debug\ false NotSet Application debug\ wfserver - - - - + @@ -48,37 +44,11 @@ - - - - - - debug\ - debug\ - wfserver - true - - - release\ - release\ - wfserver - true - false - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - msvc2019 - core;network;gui;multimedia;widgets;serialport;printsupport - - - - + wfview-debug\debug\wfservertruewfview-release\release\wfservertruefalsemsvc2019core;network;gui;multimedia;widgets;serialport;printsupportmsvc2019core;network;gui;multimedia;widgets;serialport;printsupport + - .;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) + .;..\rtaudio;..\portaudio\include;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -87,19 +57,17 @@ Sync release\ MaxSpeed - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="44f6ec2";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false - - + MultiThreadedDLL true true Level3 - true - + true - ..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies) - ..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories) + ..\portaudio\msvc\Win32\Release\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies) + ..\portaudio\msvc\Win32\Release;..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true false @@ -117,26 +85,12 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"44f6ec2\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp - .;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) + .;..\rtaudio;..\portaudio\include;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -145,17 +99,16 @@ Sync debug\ Disabled - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="44f6ec2";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 - true - + true - ..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies) - ..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories) + ..\portaudio\msvc\Win32\Debug\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies) + ..\portaudio\msvc\Win32\Debug;..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true true @@ -171,72 +124,208 @@ 0 - _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"44f6ec2\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) - - msvc - ./$(Configuration)/moc_predefs.h - Moc'ing %(Identity)... - output - $(Configuration) - moc_%(Filename).cpp - - - default - Rcc'ing %(Identity)... - $(Configuration) - qrc_%(Filename).cpp - - + msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cpp + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document true @@ -253,6 +342,30 @@ release\moc_predefs.h;%(Outputs) true + + + + + + + + + + + + + + + + + + + + + + + + @@ -285,16 +398,30 @@ - resources - resources - + + + + + + + + + + resourcesresources - style - style - + + + + + + + + + + stylestyle @@ -308,9 +435,6 @@ - - - - + \ No newline at end of file diff --git a/wfserver.vcxproj.filters b/wfserver.vcxproj.filters index 1a18943..b378c08 100644 --- a/wfserver.vcxproj.filters +++ b/wfserver.vcxproj.filters @@ -37,6 +37,12 @@ + + Source Files + + + Source Files + Source Files @@ -46,12 +52,18 @@ Source Files + + Source Files + Source Files Source Files + + Source Files + Source Files @@ -67,26 +79,41 @@ Source Files + + Source Files + Source Files Source Files + + Source Files + + + Source Files + + + Source Files + Source Files Source Files - - Source Files - + + Header Files + Header Files + + Header Files + Header Files @@ -99,12 +126,18 @@ Header Files + + Header Files + Header Files Header Files + + Header Files + Header Files @@ -123,6 +156,9 @@ Header Files + + Header Files + Header Files @@ -132,6 +168,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + Header Files @@ -143,12 +188,46 @@ + + + + + + + + + + Generated Files Generated Files + + + + + + + + + + + + + + + + + + + + + + + + @@ -282,11 +361,6 @@ - - - - - Header Files - + \ No newline at end of file From 92b43e8fa27bdccadbc102ac7c1132fcc27cc429 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 12:29:29 +0100 Subject: [PATCH 261/323] Update servermain.h --- servermain.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/servermain.h b/servermain.h index a3af388..1b7e3a8 100644 --- a/servermain.h +++ b/servermain.h @@ -30,6 +30,13 @@ #include #include +#include +#ifdef Q_OS_WIN +#include "RtAudio.h" +#else +#include "rtaudio/RtAudio.h" +#endif + namespace Ui { class wfmain; } From 6b7387cba28a0fe5b80620c749106e8dc7145231 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 12:35:53 +0100 Subject: [PATCH 262/323] Update servermain.cpp --- servermain.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index adaef67..734c5e9 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -463,9 +463,9 @@ void servermain::loadSettings() settings->beginGroup("Server"); settings->setValue("ServerEnabled", true); - settings->setValue("ServerControlPort", serverConfig.controlPort); - settings->setValue("ServerCivPort", serverConfig.civPort); - settings->setValue("ServerAudioPort", serverConfig.audioPort); + settings->setValue("ServerControlPort", udpDefPrefs.controlLANPort); + settings->setValue("ServerCivPort", udpDefPrefs.serialLANPort); + settings->setValue("ServerAudioPort", udpDefPrefs.audioLANPort); settings->beginWriteArray("Users"); settings->setArrayIndex(0); From 95ec59d46def14cea893ae26d5ae8d804e489e48 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 13:13:59 +0100 Subject: [PATCH 263/323] make PulseAudio the default --- servermain.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/servermain.cpp b/servermain.cpp index 734c5e9..9d93e14 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -550,7 +550,8 @@ void servermain::loadSettings() case rtAudio: { #if defined(Q_OS_LINUX) - RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); +// RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); + RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_PULSE); #elif defined(Q_OS_WIN) RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); #elif defined(Q_OS_MACX) From 33211908bcc859d72f42fc334ec385bde5df9489 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 13:27:35 +0100 Subject: [PATCH 264/323] Update servermain.cpp --- servermain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index 9d93e14..d18a61c 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -550,8 +550,8 @@ void servermain::loadSettings() case rtAudio: { #if defined(Q_OS_LINUX) -// RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); - RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_PULSE); + RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); +// RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_PULSE); #elif defined(Q_OS_WIN) RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); #elif defined(Q_OS_MACX) From cd1f0f0ba9f9e103ddfdb5e6efc74777a95da2ae Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 13:33:34 +0100 Subject: [PATCH 265/323] Update servermain.cpp --- servermain.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/servermain.cpp b/servermain.cpp index d18a61c..3209bd5 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -596,6 +596,7 @@ void servermain::loadSettings() qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); if (rig->txAudioSetup.name.toStdString() == info.name) { rig->txAudioSetup.portInt = i; + qDebug(logAudio()) << "Rig" << rig->rigName << "Selected txAudio device:" << QString(info.name.c_str()); } } if (info.inputChannels > 0) @@ -603,6 +604,7 @@ void servermain::loadSettings() qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name); if (rig->rxAudioSetup.name.toStdString() == info.name) { rig->rxAudioSetup.portInt = i; + qDebug(logAudio()) << "Rig" << rig->rigName << "Selected rxAudio device:" << QString(info.name.c_str()); } } } From 728f01abf484e61f5a168c616db4804de6bbb44c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 13:39:00 +0100 Subject: [PATCH 266/323] Update servermain.cpp --- servermain.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index 3209bd5..3124b81 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -639,14 +639,15 @@ void servermain::loadSettings() if (info->maxInputChannels > 0) { qDebug(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name; - if (rig->txAudioSetup.name == info->name) { - rig->txAudioSetup.portInt = i; + if (rig->rxAudioSetup.name == info->name) { + qDebug(logAudio()) << "Rig" << rig->rigName << "Selected rxAudio device:" << QString(info->name); + rig->rxAudioSetup.portInt = i; } } if (info->maxOutputChannels > 0) { - qDebug(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name; - if (rig->rxAudioSetup.name == info->name) { - rig->rxAudioSetup.portInt = i; + if (rig->txAudioSetup.name == info->name) { + qDebug(logAudio()) << "Rig" << rig->rigName << "Selected txAudio device:" << QString(info->name); + rig->txAudioSetup.portInt = i; } } } @@ -668,7 +669,7 @@ void servermain::loadSettings() #endif ) { - qDebug(logSystem()) << "Audio input: " << deviceInfo.deviceName(); + qDebug(logAudio()) << "Rig" << rig->rigName << "Selected rxAudio device:" << deviceInfo.deviceName(); rig->rxAudioSetup.port = deviceInfo; } } @@ -685,7 +686,7 @@ void servermain::loadSettings() #endif ) { - qDebug(logSystem()) << "Audio output: " << deviceInfo.deviceName(); + qDebug(logAudio()) << "Rig" << rig->rigName << "Selected txAudio device:" << deviceInfo.deviceName(); rig->txAudioSetup.port = deviceInfo; } } From 5eb294dc6da4a632dfcb9ea41535724bdd354647 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 13:42:19 +0100 Subject: [PATCH 267/323] Update servermain.cpp --- servermain.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/servermain.cpp b/servermain.cpp index 3124b81..6b871c7 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -591,6 +591,8 @@ void servermain::loadSettings() info = audio->getDeviceInfo(i); for (RIGCONFIG* rig : serverConfig.rigs) { + qDebug(logAudio()) << "Rig" << rig->rigName << "rxAudio device:" << rig->rxAudioSetup.name; + qDebug(logAudio()) << "Rig" << rig->rigName << "txAudio device:" << rig->txAudioSetup.name; if (info.outputChannels > 0) { qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); @@ -636,6 +638,8 @@ void servermain::loadSettings() info = Pa_GetDeviceInfo(i); for (RIGCONFIG* rig : serverConfig.rigs) { + qDebug(logAudio()) << "Rig" << rig->rigName << "rxAudio device:" << rig->rxAudioSetup.name; + qDebug(logAudio()) << "Rig" << rig->rigName << "txAudio device:" << rig->txAudioSetup.name; if (info->maxInputChannels > 0) { qDebug(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name; @@ -663,6 +667,8 @@ void servermain::loadSettings() qDebug(logSystem()) << "Found Audio input: " << deviceInfo.deviceName(); for (RIGCONFIG* rig : serverConfig.rigs) { + qDebug(logAudio()) << "Rig" << rig->rigName << "rxAudio device:" << rig->rxAudioSetup.name; + qDebug(logAudio()) << "Rig" << rig->rigName << "txAudio device:" << rig->txAudioSetup.name; if (deviceInfo.deviceName() == rig->rxAudioSetup.name #ifdef Q_OS_WIN && deviceInfo.realm() == "wasapi" From e6ff2ae15228b4da1b051e3ffe7c1111872e3ce1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 14:29:08 +0100 Subject: [PATCH 268/323] Update servermain.cpp --- servermain.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index 6b871c7..6baed86 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -456,8 +456,8 @@ void servermain::loadSettings() settings->setValue("SerialPortRadio", defPrefs.serialPortRadio); settings->setValue("RigName", ""); settings->setValue("SerialPortBaud", defPrefs.serialPortBaud); - settings->setValue("AudioInput", defPrefs.rxAudio.name); - settings->setValue("AudioOutput", defPrefs.txAudio.name); + settings->setValue("AudioInput", "default"); + settings->setValue("AudioOutput", "default"); } settings->endArray(); @@ -495,6 +495,8 @@ void servermain::loadSettings() tempPrefs->serialPort = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString(); tempPrefs->rigName = settings->value("RigName", "").toString(); tempPrefs->baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); + tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "default").toString(); + tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "default").toString(); QString tempPort = "auto"; if (tempPrefs->rigName=="") @@ -528,8 +530,6 @@ void servermain::loadSettings() tempPrefs->rxAudioSetup.resampleQuality = 4; tempPrefs->txAudioSetup.resampleQuality = 4; - tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "").toString(); - tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "").toString(); tempPrefs->rig = Q_NULLPTR; tempPrefs->rigThread = Q_NULLPTR; serverConfig.rigs.append(tempPrefs); From 2e433ed71f63bfbd9b9507cfdc5092dadaf978bf Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 15:53:48 +0100 Subject: [PATCH 269/323] Add pa/rt to server --- servermain.cpp | 1 + udpserver.cpp | 51 +++++++++++++++++++++++++++++++++++--------------- udpserver.h | 4 ++-- wfmain.cpp | 2 +- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/servermain.cpp b/servermain.cpp index 6baed86..ce5a88f 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -498,6 +498,7 @@ void servermain::loadSettings() tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "default").toString(); tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "default").toString(); + QString tempPort = "auto"; if (tempPrefs->rigName=="") { diff --git a/udpserver.cpp b/udpserver.cpp index 3b5e505..255ba61 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -367,7 +367,22 @@ void udpServer::controlReceived() outAudio.isinput = false; - radio->txaudio = new audioHandler(); + + if (radio->txAudioSetup.type == qtAudio) { + radio->txaudio = new audioHandler(); + } + else if (radio->txAudioSetup.type == portAudio) { + radio->txaudio = new paHandler(); + } + else if (radio->txAudioSetup.type == rtAudio) { + radio->txaudio = new rtHandler(); + } + else + { + qCritical(logAudio()) << "Unsupported Transmit Audio Handler selected!"; + } + + //radio->txaudio = new audioHandler(); radio->txAudioThread = new QThread(this); radio->txAudioThread->setObjectName("txAudio()"); @@ -380,13 +395,13 @@ void udpServer::controlReceived() connect(radio->txAudioThread, SIGNAL(finished()), radio->txaudio, SLOT(deleteLater())); // Not sure how we make this work in QT5.9? -#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) - QMetaObject::invokeMethod(radio->txaudio, [=]() { +#if (QT_VERSION >= QT_VERSION_CHECK(7,10,0)) + QMetaObject::invokeMethod((audioHandler*)radio->txaudio, [=]() { radio->txaudio->init(radio->txAudioSetup); }, Qt::QueuedConnection); #else emit setupTxAudio(radio->txAudioSetup); - #warning "QT 5.9 is not fully supported multiple rigs will NOT work!" + //#warning "QT 5.9 is not fully supported multiple rigs will NOT work!" #endif hasTxAudio = datagram.senderAddress(); @@ -395,21 +410,27 @@ void udpServer::controlReceived() } if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size() == 1) && radio->rxaudio == Q_NULLPTR && !config->lan) { - #if !defined(PORTAUDIO) && !defined(RTAUDIO) - qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio input(RX) :" << radio->rxAudioSetup.port.deviceName(); - qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio output(TX) :" << radio->txAudioSetup.port.deviceName(); - #else - qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.name; - qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.name; - #endif - radio->rxAudioSetup.codec = current->rxCodec; radio->rxAudioSetup.sampleRate=current->rxSampleRate; radio->rxAudioSetup.latency = current->txBufferLen; radio->rxAudioSetup.isinput = true; memcpy(radio->rxAudioSetup.guid, radio->guid, GUIDLEN); - radio->rxaudio = new audioHandler(); + //radio->rxaudio = new audioHandler(); + if (radio->rxAudioSetup.type == qtAudio) { + radio->rxaudio = new audioHandler(); + } + else if (radio->rxAudioSetup.type == portAudio) { + radio->rxaudio = new paHandler(); + } + else if (radio->rxAudioSetup.type == rtAudio) { + radio->rxaudio = new rtHandler(); + } + else + { + qCritical(logAudio()) << "Unsupported Receive Audio Handler selected!"; + } + radio->rxAudioThread = new QThread(this); radio->rxAudioThread->setObjectName("rxAudio()"); @@ -421,12 +442,12 @@ void udpServer::controlReceived() connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater())); connect(radio->rxaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); -#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) +#if (QT_VERSION >= QT_VERSION_CHECK(7,10,0)) QMetaObject::invokeMethod(radio->rxaudio, [=]() { radio->rxaudio->init(radio->rxAudioSetup); }, Qt::QueuedConnection); #else - #warning "QT 5.9 is not fully supported multiple rigs will NOT work!" + //#warning "QT 5.9 is not fully supported multiple rigs will NOT work!" connect(this, SIGNAL(setupRxAudio(audioSetup)), radio->rxaudio, SLOT(init(audioSetup))); setupRxAudio(radio->rxAudioSetup); #endif diff --git a/udpserver.h b/udpserver.h index fe1682b..7b98e7c 100644 --- a/udpserver.h +++ b/udpserver.h @@ -70,9 +70,9 @@ struct RIGCONFIG { rigCapabilities rigCaps; rigCommander* rig = Q_NULLPTR; QThread* rigThread = Q_NULLPTR; - audioHandler* rxaudio = Q_NULLPTR; + QObject* rxaudio = Q_NULLPTR; QThread* rxAudioThread = Q_NULLPTR; - audioHandler* txaudio = Q_NULLPTR; + QObject* txaudio = Q_NULLPTR; QThread* txAudioThread = Q_NULLPTR; QTimer* rxAudioTimer = Q_NULLPTR; QTimer* connectTimer = Q_NULLPTR; diff --git a/wfmain.cpp b/wfmain.cpp index aea3a07..2b9ba68 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -466,7 +466,7 @@ void wfmain::removeRig() rigThread->disconnect(); rig->disconnect(); - + delete rigThread; delete rig; rig = Q_NULLPTR; From 717b71ad4c1fe6e49be9543454de0dc0ec42e6ce Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 15:57:45 +0100 Subject: [PATCH 270/323] Update servermain.cpp --- servermain.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/servermain.cpp b/servermain.cpp index ce5a88f..dd4673c 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -497,8 +497,9 @@ void servermain::loadSettings() tempPrefs->baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "default").toString(); tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "default").toString(); + tempPrefs->rxAudioSetup.type = prefs.audioSystem; + tempPrefs->txAudioSetup.type = prefs.audioSystem; - QString tempPort = "auto"; if (tempPrefs->rigName=="") { From c90611e444638f56af19eb52353f44fb10939df6 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 20:04:15 +0100 Subject: [PATCH 271/323] Fix 8 bit audio encoding. --- audioconverter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index 50723db..2316229 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -241,7 +241,8 @@ bool audioConverter::convert(audioPacket audio) if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8) { - Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits::max()); + samplesITemp.array() += 127; VectorXuint8 samplesI = samplesITemp.cast(); audio.data = QByteArray(reinterpret_cast(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8))); } From a855c931b6d8a8e3ab6b125402a87b02833d0312 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 20:04:30 +0100 Subject: [PATCH 272/323] Add amplitude to output audio --- rthandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rthandler.cpp b/rthandler.cpp index 41ad6c1..c7d4b64 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -300,6 +300,7 @@ int rtHandler::writeData(void* outputBuffer, void* inputBuffer, void rtHandler::convertedOutput(audioPacket packet) { arrayBuffer.append(packet.data); + amplitude = packet.amplitude; currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount()) * 1000); emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); } From b28dab4dc268d28757f4ee5279e4c8adef38d8fb Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 12 May 2022 20:30:51 +0100 Subject: [PATCH 273/323] Use a mutex to protect RT buffer. --- rthandler.cpp | 9 +++++++-- rthandler.h | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/rthandler.cpp b/rthandler.cpp index c7d4b64..ce4ff83 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -249,8 +249,11 @@ int rtHandler::readData(void* outputBuffer, void* inputBuffer, //lastSentSeq = packet.seq; if (arrayBuffer.length() >= nBytes) { - std::memcpy(outputBuffer, arrayBuffer.constData(), nBytes); - arrayBuffer.remove(0, nBytes); + if (audioMutex.tryLock(0)) { + std::memcpy(outputBuffer, arrayBuffer.constData(), nBytes); + arrayBuffer.remove(0, nBytes); + audioMutex.unlock(); + } } if (status == RTAUDIO_INPUT_OVERFLOW) { @@ -299,7 +302,9 @@ int rtHandler::writeData(void* outputBuffer, void* inputBuffer, void rtHandler::convertedOutput(audioPacket packet) { + audioMutex.lock(); arrayBuffer.append(packet.data); + audioMutex.unlock(); amplitude = packet.amplitude; currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount()) * 1000); emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); diff --git a/rthandler.h b/rthandler.h index cb8ec58..c78cf6c 100644 --- a/rthandler.h +++ b/rthandler.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef Q_OS_WIN #include "RtAudio.h" @@ -106,6 +107,7 @@ private: QByteArray arrayBuffer; bool isUnderrun = false; bool isOverrun = true; + QMutex audioMutex; }; #endif // rtHandler_H From 0b74407448c38be313cdbebc8884e3d20c4f4b1b Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 13 May 2022 09:55:16 +0100 Subject: [PATCH 274/323] Set default audio device if not found --- audiohandler.cpp | 6 ++-- pahandler.cpp | 2 ++ rthandler.cpp | 5 +-- wfmain.cpp | 84 +++++++++++++++++++++++++++++++++--------------- 4 files changed, 63 insertions(+), 34 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index d05b102..b274545 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -61,10 +61,6 @@ audioHandler::~audioHandler() inFormat = toQAudioFormat(setup.codec, setup.sampleRate); - if(!setup.isinput) - { - this->setVolume(setup.localAFgain); - } outFormat = setup.port.preferredFormat(); qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Preferred Format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() << @@ -171,6 +167,8 @@ audioHandler::~audioHandler() underTimer->setSingleShot(true); connect(underTimer, SIGNAL(timeout()), this, SLOT(clearUnderrun())); + this->setVolume(setup.localAFgain); + this->start(); return true; diff --git a/pahandler.cpp b/pahandler.cpp index 7014ce2..ee2736f 100644 --- a/pahandler.cpp +++ b/pahandler.cpp @@ -164,6 +164,8 @@ bool paHandler::init(audioSetup setup) qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "failed to open device" << Pa_GetErrorText(err); } + this->setVolume(setup.localAFgain); + return isInitialized; } diff --git a/rthandler.cpp b/rthandler.cpp index ce4ff83..9d298d1 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -215,10 +215,7 @@ bool rtHandler::init(audioSetup setup) qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!"; } - if (!setup.isinput) - { - this->setVolume(setup.localAFgain); - } + this->setVolume(setup.localAFgain); return isInitialized; diff --git a/wfmain.cpp b/wfmain.cpp index 2b9ba68..6d71ad8 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -5787,24 +5787,16 @@ void wfmain::setAudioDevicesUI() qDebug(logSystem()) << "Finding audio devices, output=" << rxSetup.name << "input="<audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); -#ifdef Q_OS_WIN - } - #endif - } - const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (const QAudioDeviceInfo& deviceInfo : audioInputs) { #ifdef Q_OS_WIN @@ -5812,13 +5804,26 @@ void wfmain::setAudioDevicesUI() #endif ui->audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); ui->serverRXAudioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); + inCount++; #ifdef Q_OS_WIN } #endif } + const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); + for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { +#ifdef Q_OS_WIN + if (deviceInfo.realm() == "wasapi") { +#endif + ui->audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); + ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); + outCount++; +#ifdef Q_OS_WIN + } +#endif + } + break; } - break; case portAudio: { PaError err; @@ -5833,11 +5838,10 @@ void wfmain::setAudioDevicesUI() qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText; - int numDevices; - numDevices = Pa_GetDeviceCount(); + int numDevices = Pa_GetDeviceCount(); qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; - const PaDeviceInfo* info; + const PaDeviceInfo* info; for (int i = 0; i < numDevices; i++) { info = Pa_GetDeviceInfo(i); @@ -5846,15 +5850,23 @@ void wfmain::setAudioDevicesUI() ui->audioInputCombo->addItem(info->name, i); ui->serverRXAudioInputCombo->addItem(info->name, i); + if (i == Pa_GetDefaultInputDevice()) { + defaultAudioInputIndex = inCount; + } + inCount++; } if (info->maxOutputChannels > 0) { qDebug(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name; ui->audioOutputCombo->addItem(info->name, i); ui->serverTXAudioOutputCombo->addItem(info->name, i); + if (i == Pa_GetDefaultOutputDevice()) { + defaultAudioOutputIndex = outCount; + } + outCount++; } } + break; } - break; case rtAudio: { Pa_Terminate(); @@ -5899,22 +5911,29 @@ void wfmain::setAudioDevicesUI() for (unsigned int i = 1; i < devices; i++) { info = audio->getDeviceInfo(i); - if (info.outputChannels > 0) { - qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); - ui->audioOutputCombo->addItem(QString::fromStdString(info.name), i); - ui->serverTXAudioOutputCombo->addItem(QString::fromStdString(info.name), i); - } if (info.inputChannels > 0) { qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name); ui->audioInputCombo->addItem(QString::fromStdString(info.name), i); ui->serverRXAudioInputCombo->addItem(QString::fromStdString(info.name), i); + if (info.isDefaultInput) { + defaultAudioInputIndex = inCount; + } + inCount++; + } + if (info.outputChannels > 0) { + qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); + ui->audioOutputCombo->addItem(QString::fromStdString(info.name), i); + ui->serverTXAudioOutputCombo->addItem(QString::fromStdString(info.name), i); + if (info.isDefaultOutput) { + defaultAudioOutputIndex = outCount; + } + outCount++; } } delete audio; - + break; } - break; } @@ -5935,6 +5954,7 @@ void wfmain::setAudioDevicesUI() } else { qDebug(logSystem()) << "Audio input not found"; + ui->audioInputCombo->setCurrentIndex(defaultAudioInputIndex); } int audioOutputIndex = ui->audioOutputCombo->findText(rxSetup.name); @@ -5943,24 +5963,36 @@ void wfmain::setAudioDevicesUI() } else { qDebug(logSystem()) << "Audio output not found"; + ui->audioOutputCombo->setCurrentIndex(defaultAudioOutputIndex); } if (!serverConfig.rigs.isEmpty()) { qInfo(logGui()) << "Got Server Audio Input: " << serverConfig.rigs.first()->rxAudioSetup.name; + + serverConfig.rigs.first()->rxAudioSetup.type = prefs.audioSystem; + serverConfig.rigs.first()->txAudioSetup.type = prefs.audioSystem; + int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(serverConfig.rigs.first()->rxAudioSetup.name); if (serverAudioInputIndex != -1) { ui->serverRXAudioInputCombo->setCurrentIndex(serverAudioInputIndex); } + else { + // Set to default + ui->serverRXAudioInputCombo->setCurrentIndex(defaultAudioInputIndex); + } qInfo(logGui()) << "Got Server Audio Output: " << serverConfig.rigs.first()->txAudioSetup.name; int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(serverConfig.rigs.first()->txAudioSetup.name); if (serverAudioOutputIndex != -1) { ui->serverTXAudioOutputCombo->setCurrentIndex(serverAudioOutputIndex); } + else { + ui->serverTXAudioOutputCombo->setCurrentIndex(defaultAudioOutputIndex); + } } - // Set these to default audio devices initially. + qDebug(logSystem()) << "Audio devices done."; } From 1aa45dc849e3f4e6baf9f9e1948a269554b53dc8 Mon Sep 17 00:00:00 2001 From: M0VSE Date: Fri, 13 May 2022 10:20:17 +0100 Subject: [PATCH 275/323] Fix latency display on rtaudio --- rthandler.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rthandler.cpp b/rthandler.cpp index 9d298d1..c6afb31 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -62,7 +62,7 @@ bool rtHandler::init(audioSetup setup) ", uLaw" << setup.ulaw; #if !defined(Q_OS_MACX) - //options.flags = !RTAUDIO_HOG_DEVICE | RTAUDIO_MINIMIZE_LATENCY; + options.flags = ((!RTAUDIO_HOG_DEVICE) | (RTAUDIO_MINIMIZE_LATENCY)); //options.flags = RTAUDIO_MINIMIZE_LATENCY; #endif @@ -303,17 +303,18 @@ void rtHandler::convertedOutput(audioPacket packet) arrayBuffer.append(packet.data); audioMutex.unlock(); amplitude = packet.amplitude; - currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount()) * 1000); + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount())/1000); emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); } -void rtHandler::convertedInput(audioPacket audio) +void rtHandler::convertedInput(audioPacket packet) { - if (audio.data.size() > 0) { - emit haveAudioData(audio); - amplitude = audio.amplitude; + if (packet.data.size() > 0) { + emit haveAudioData(packet); + amplitude = packet.amplitude; + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount())/1000); emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); } } From 6a5f9ce988689b4f9c8b5c6ebe94309fff1d9733 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 13 May 2022 12:35:28 +0100 Subject: [PATCH 276/323] Portaudio try to find compatible format --- pahandler.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++-------- wfview.vcxproj | 8 +++---- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/pahandler.cpp b/pahandler.cpp index ee2736f..6bd3875 100644 --- a/pahandler.cpp +++ b/pahandler.cpp @@ -38,7 +38,7 @@ bool paHandler::init(audioSetup setup) this->setup = setup; qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "PortAudio handler starting:" << setup.name; - if (setup.portInt==-1) + if (setup.portInt == -1) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found."; return false; @@ -82,7 +82,7 @@ bool paHandler::init(audioSetup setup) outFormat.setChannelCount(info->maxOutputChannels); } - aParams.suggestedLatency = (float)setup.latency/1000.0f; + aParams.suggestedLatency = (float)setup.latency / 1000.0f; outFormat.setSampleRate(info->defaultSampleRate); aParams.sampleFormat = paFloat32; outFormat.setSampleSize(32); @@ -91,11 +91,6 @@ bool paHandler::init(audioSetup setup) outFormat.setCodec("audio/pcm"); - if (!setup.isinput) - { - this->setVolume(setup.localAFgain); - } - if (outFormat.channelCount() > 2) { outFormat.setChannelCount(2); } @@ -114,7 +109,7 @@ bool paHandler::init(audioSetup setup) if (outFormat.sampleRate() < 44100) { outFormat.setSampleRate(48000); } - + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() << "Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType(); @@ -139,10 +134,59 @@ bool paHandler::init(audioSetup setup) aParams.hostApiSpecificStreamInfo = NULL; // Per channel chunk size. - this->chunkSize = (outFormat.bytesForDuration(setup.blockSize*1000)/sizeof(float))*outFormat.channelCount(); + this->chunkSize = (outFormat.bytesForDuration(setup.blockSize * 1000) / sizeof(float)) * outFormat.channelCount(); + + // Check the format is supported if (setup.isinput) { + err = Pa_IsFormatSupported(&aParams, NULL, outFormat.sampleRate()); + } + else + { + err = Pa_IsFormatSupported(NULL,&aParams, outFormat.sampleRate()); + } + + if (err != paNoError) { + if (err == paInvalidChannelCount) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported channel count" << aParams.channelCount; + if (aParams.channelCount == 2) { + aParams.channelCount = 1; + outFormat.setChannelCount(1); + } + else { + aParams.channelCount = 2; + outFormat.setChannelCount(2); + } + } + else if (err == paInvalidSampleRate) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported sample rate" << outFormat.sampleRate(); + outFormat.setSampleRate(44100); + } + else if (err == paSampleFormatNotSupported) + { + aParams.sampleFormat = paInt16; + outFormat.setSampleType(QAudioFormat::SignedInt); + outFormat.setSampleSize(16); + } + + if (setup.isinput) { + err = Pa_IsFormatSupported(&aParams, NULL, outFormat.sampleRate()); + } + else + { + err = Pa_IsFormatSupported(NULL, &aParams, outFormat.sampleRate()); + } + if (err != paNoError) { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot find suitable format, aborting:" << Pa_GetErrorText(err); + return false; + } + } + + if (setup.isinput) { + err = Pa_OpenStream(&audio, &aParams, 0, outFormat.sampleRate(), this->chunkSize, paNoFlag, &paHandler::staticWrite, (void*)this); emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality); connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket))); diff --git a/wfview.vcxproj b/wfview.vcxproj index 355ab27..3ca575a 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="63c5e02";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="1aa45dc";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -85,7 +85,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"63c5e02\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"1aa45dc\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -99,7 +99,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="63c5e02";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="1aa45dc";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -124,7 +124,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"63c5e02\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"1aa45dc\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h From b78f613ef2fd9ca75cdf1496621ccb4554d4c3c7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 13 May 2022 16:09:26 +0100 Subject: [PATCH 277/323] Bit of code-tidying and minor fixes --- audiohandler.h | 2 +- pahandler.cpp | 54 +++++++++++++++++++++++--------------------------- pahandler.h | 2 ++ rthandler.h | 2 +- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/audiohandler.h b/audiohandler.h index 47e6412..1f56b2b 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -82,7 +82,7 @@ private: bool isUnderrun = false; - bool isOverrun = true; + bool isOverrun = false; bool isInitialized=false; bool isReady = false; bool audioBuffered = false; diff --git a/pahandler.cpp b/pahandler.cpp index 6bd3875..646142f 100644 --- a/pahandler.cpp +++ b/pahandler.cpp @@ -217,7 +217,11 @@ bool paHandler::init(audioSetup setup) void paHandler::setVolume(unsigned char volume) { +#ifdef Q_OS_WIN + this->volume = audiopot[volume] * 5; +#else this->volume = audiopot[volume]; +#endif qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } @@ -246,6 +250,18 @@ int paHandler::writeData(const void* inputBuffer, void* outputBuffer, packet.data.append((char*)inputBuffer, nFrames*inFormat.channelCount()*sizeof(float)); emit sendToConverter(packet); + if (status == paInputUnderflow) { + isUnderrun = true; + } + else if (status == paInputOverflow) { + isOverrun = true; + } + else + { + isUnderrun = false; + isOverrun = false; + } + return paContinue; } @@ -261,44 +277,24 @@ void paHandler::convertedOutput(audioPacket packet) { qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Error writing audio!"; } const PaStreamInfo* info = Pa_GetStreamInfo(audio); - - //currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->outputLatency * 1000); - currentLatency = (info->outputLatency * 1000); - - } - /* - currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); - - if (audioDevice != Q_NULLPTR) { - if (audioDevice->write(packet.data) < packet.data.size()) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full!"; - isOverrun = true; - } - else { - isOverrun = false; - } - if (lastReceived.msecsTo(QTime::currentTime()) > 100) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize; - } - lastReceived = QTime::currentTime(); + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->outputLatency * 1000); } - lastSentSeq = packet.seq; - - */ amplitude = packet.amplitude; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, false, false); + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); } } -void paHandler::convertedInput(audioPacket audio) +void paHandler::convertedInput(audioPacket packet) { - if (audio.data.size() > 0) { - emit haveAudioData(audio); - amplitude = audio.amplitude; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, false,false); + if (packet.data.size() > 0) { + emit haveAudioData(packet); + amplitude = packet.amplitude; + const PaStreamInfo* info = Pa_GetStreamInfo(audio); + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->inputLatency * 1000); + emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); } } diff --git a/pahandler.h b/pahandler.h index 5e311aa..f55a79f 100644 --- a/pahandler.h +++ b/pahandler.h @@ -88,6 +88,8 @@ private: QAudioFormat outFormat; audioConverter* converter = Q_NULLPTR; QThread* converterThread = Q_NULLPTR; + bool isUnderrun = false; + bool isOverrun = false; }; #endif // PAHANDLER_H diff --git a/rthandler.h b/rthandler.h index c78cf6c..e3461cc 100644 --- a/rthandler.h +++ b/rthandler.h @@ -106,7 +106,7 @@ private: QThread* converterThread = Q_NULLPTR; QByteArray arrayBuffer; bool isUnderrun = false; - bool isOverrun = true; + bool isOverrun = false; QMutex audioMutex; }; From c84cc4330d33e2fb7422a7a0310ba987d7cb138a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 13 May 2022 18:45:13 +0100 Subject: [PATCH 278/323] Add waterfallFormat to wfserver --- servermain.cpp | 4 +++- udpserver.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/servermain.cpp b/servermain.cpp index dd4673c..c2942c5 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -89,7 +89,7 @@ void servermain::openRig() { //qInfo(logSystem()) << "Got rig"; QMetaObject::invokeMethod(radio->rig, [=]() { - radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none"),prefs.tcpPort,0); + radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none"),prefs.tcpPort,radio->waterfallFormat); }, Qt::QueuedConnection); } } @@ -458,6 +458,7 @@ void servermain::loadSettings() settings->setValue("SerialPortBaud", defPrefs.serialPortBaud); settings->setValue("AudioInput", "default"); settings->setValue("AudioOutput", "default"); + settings->setValue("WaterfallFormat", 0); } settings->endArray(); @@ -497,6 +498,7 @@ void servermain::loadSettings() tempPrefs->baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "default").toString(); tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "default").toString(); + tempPrefs->waterfallFormat = settings->value("WaterfallFormat", 0).toInt(); tempPrefs->rxAudioSetup.type = prefs.audioSystem; tempPrefs->txAudioSetup.type = prefs.audioSystem; diff --git a/udpserver.h b/udpserver.h index 7b98e7c..ff2e78d 100644 --- a/udpserver.h +++ b/udpserver.h @@ -76,6 +76,7 @@ struct RIGCONFIG { QThread* txAudioThread = Q_NULLPTR; QTimer* rxAudioTimer = Q_NULLPTR; QTimer* connectTimer = Q_NULLPTR; + quint8 waterfallFormat; }; From 8fd8fa390ef0b56746571c178af8acc60656f328 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 01:11:00 +0100 Subject: [PATCH 279/323] Fix mod meters in PA/RT --- audiohandler.h | 2 +- pahandler.cpp | 2 +- pahandler.h | 2 +- rthandler.cpp | 2 +- rthandler.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/audiohandler.h b/audiohandler.h index 1f56b2b..9c503ec 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -113,7 +113,7 @@ private: volatile bool ready = false; audioPacket tempBuf; quint16 currentLatency; - float amplitude; + float amplitude=0.0; qreal volume = 1.0; audioSetup setup; diff --git a/pahandler.cpp b/pahandler.cpp index 646142f..e0de0eb 100644 --- a/pahandler.cpp +++ b/pahandler.cpp @@ -313,5 +313,5 @@ int paHandler::getLatency() quint16 paHandler::getAmplitude() { - return amplitude; + return static_cast(amplitude * 255.0); } diff --git a/pahandler.h b/pahandler.h index f55a79f..cd55e95 100644 --- a/pahandler.h +++ b/pahandler.h @@ -80,7 +80,7 @@ private: quint32 lastSentSeq = 0; quint16 currentLatency; - quint16 amplitude = 0; + float amplitude=0.0; qreal volume = 1.0; audioSetup setup; diff --git a/rthandler.cpp b/rthandler.cpp index c6afb31..ea00958 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -334,5 +334,5 @@ int rtHandler::getLatency() quint16 rtHandler::getAmplitude() { - return amplitude; + return static_cast(amplitude * 255.0); } diff --git a/rthandler.h b/rthandler.h index e3461cc..5f34997 100644 --- a/rthandler.h +++ b/rthandler.h @@ -96,7 +96,7 @@ private: quint32 lastSentSeq = 0; quint16 currentLatency; - quint16 amplitude = 0; + float amplitude = 0.0; qreal volume = 1.0; audioSetup setup; From 48562aa23b01f7e6b516448854f2adc7360ec0ce Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 01:11:15 +0100 Subject: [PATCH 280/323] Don't try to connect to radio we are already connected to --- selectradio.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/selectradio.cpp b/selectradio.cpp index 0862ace..61e0192 100644 --- a/selectradio.cpp +++ b/selectradio.cpp @@ -42,21 +42,29 @@ void selectRadio::setInUse(quint8 radio, quint8 busy, QString user, QString ip) ui->table->setItem(radio, 3, new QTableWidgetItem(user)); ui->table->setItem(radio, 4, new QTableWidgetItem(ip)); for (int f = 0; f < 5; f++) { - if (busy == 1) + if (busy == 1) + { ui->table->item(radio, f)->setBackground(Qt::darkGreen); - else if (busy == 2) + } + else if (busy == 2) + { ui->table->item(radio, f)->setBackground(Qt::red); + } else + { ui->table->item(radio, f)->setBackground(Qt::black); + } } } void selectRadio::on_table_cellClicked(int row, int col) { qInfo() << "Clicked on " << row << "," << col; - ui->table->selectRow(row); - emit selectedRadio(row); - this->setVisible(false); + if (ui->table->item(row, col)->backgroundColor() != Qt::darkGreen) { + ui->table->selectRow(row); + emit selectedRadio(row); + this->setVisible(false); + } } From c1461947eb45afdd6b8d777404ff73152f6184fe Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 01:27:44 +0100 Subject: [PATCH 281/323] If audio device fails to open, retry 10 times before giving up. --- rthandler.cpp | 6 ++++++ rthandler.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/rthandler.cpp b/rthandler.cpp index ea00958..c710f8f 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -208,6 +208,12 @@ bool rtHandler::init(audioSetup setup) } catch (RtAudioError& e) { qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); + // Try again? + if (retryConnectCount < 10) { + QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); + retryConnectCount++; + return false; + } } } else diff --git a/rthandler.h b/rthandler.h index 5f34997..10acd78 100644 --- a/rthandler.h +++ b/rthandler.h @@ -16,6 +16,7 @@ #include #include #include +#include /* wfview Packet types */ @@ -108,6 +109,7 @@ private: bool isUnderrun = false; bool isOverrun = false; QMutex audioMutex; + int retryConnectCount = 0; }; #endif // rtHandler_H From 35d54468cd43760bb70d7f5ea0587eb73cf56acd Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 01:31:04 +0100 Subject: [PATCH 282/323] Update rthandler.cpp --- rthandler.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rthandler.cpp b/rthandler.cpp index c710f8f..1c10493 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -219,6 +219,11 @@ bool rtHandler::init(audioSetup setup) else { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!"; + if (retryConnectCount < 10) { + QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); + retryConnectCount++; + return false; + } } this->setVolume(setup.localAFgain); From 28a3209c2148072bfc20f26c85d6d742077a999a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 01:37:03 +0100 Subject: [PATCH 283/323] Update rthandler.cpp --- rthandler.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rthandler.cpp b/rthandler.cpp index 1c10493..a392c46 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -133,7 +133,11 @@ bool rtHandler::init(audioSetup setup) else if (outFormat.channelCount() < 1) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup."; - return false; + if (retryConnectCount < 10) { + QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); + retryConnectCount++; + return false; + } } aParams.nChannels = outFormat.channelCount(); From 50eb498026763907f2e3294b86badf576925e08a Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 10:30:26 +0100 Subject: [PATCH 284/323] Another error check --- rthandler.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rthandler.cpp b/rthandler.cpp index a392c46..5b542c5 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -92,6 +92,10 @@ bool rtHandler::init(audioSetup setup) } catch (RtAudioError& e) { qInfo(logAudio()) << "Device error:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); + if (retryConnectCount < 10) { + QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); + retryConnectCount++; + } return isInitialized; } From b95cf793186f988d6e19b71900a6fe5e4beab98e Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 10:33:31 +0100 Subject: [PATCH 285/323] Update rthandler.cpp --- rthandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rthandler.cpp b/rthandler.cpp index 5b542c5..44f343c 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -91,7 +91,7 @@ bool rtHandler::init(audioSetup setup) info = audio->getDeviceInfo(aParams.deviceId); } catch (RtAudioError& e) { - qInfo(logAudio()) << "Device error:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Device exception:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); if (retryConnectCount < 10) { QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); retryConnectCount++; From 466571a7584705872bbd4decc7ea5b5c4e7b247c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 10:41:44 +0100 Subject: [PATCH 286/323] Use goto statement for error handling. --- rthandler.cpp | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/rthandler.cpp b/rthandler.cpp index 44f343c..c3d0353 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -92,11 +92,7 @@ bool rtHandler::init(audioSetup setup) } catch (RtAudioError& e) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Device exception:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); - if (retryConnectCount < 10) { - QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); - retryConnectCount++; - } - return isInitialized; + goto errorHandler; } if (info.probed) @@ -110,7 +106,7 @@ bool rtHandler::init(audioSetup setup) if (info.nativeFormats == 0) { qCritical(logAudio()) << " No natively supported data formats!"; - return false; + goto errorHandler; } else { qDebug(logAudio()) << " Supported formats:" << @@ -137,11 +133,7 @@ bool rtHandler::init(audioSetup setup) else if (outFormat.channelCount() < 1) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup."; - if (retryConnectCount < 10) { - QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); - retryConnectCount++; - return false; - } + goto errorHandler; } aParams.nChannels = outFormat.channelCount(); @@ -170,7 +162,7 @@ bool rtHandler::init(audioSetup setup) } else { qCritical(logAudio()) << "Cannot find supported sample format!"; - return false; + goto errorHandler; } } @@ -217,27 +209,32 @@ bool rtHandler::init(audioSetup setup) catch (RtAudioError& e) { qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); // Try again? - if (retryConnectCount < 10) { - QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); - retryConnectCount++; - return false; - } + goto errorHandler; } } else { - qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!"; - if (retryConnectCount < 10) { - QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); - retryConnectCount++; - return false; - } + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!"; + goto errorHandler; } this->setVolume(setup.localAFgain); return isInitialized; + +errorHandler: + if (retryConnectCount < 10) { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "*** Attempting to reconnect to audio device in 500ms"; + QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup)); + retryConnectCount++; + } + else + { + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "*** Retry count exceeded, giving up!"; + } + return false; + } From e248882e31c627240e6925da1f2ad6a86aa57282 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 11:07:05 +0100 Subject: [PATCH 287/323] Add reconnect on failure to commHandler() --- commhandler.cpp | 15 ++++++++++++++- commhandler.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/commhandler.cpp b/commhandler.cpp index be6fb5b..a8ac59e 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -29,6 +29,7 @@ commHandler::commHandler(QObject* parent) : QObject(parent) //qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize(); connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn())); + connect(port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError))); } commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QObject* parent) : QObject(parent) @@ -143,7 +144,7 @@ void commHandler::sendDataOut(const QByteArray &writeData) } bytesWritten = port->write(writeData); - + if(bytesWritten != (qint64)writeData.size()) { qDebug(logSerial()) << "bytesWritten: " << bytesWritten << " length of byte array: " << writeData.length()\ @@ -371,3 +372,15 @@ void commHandler::printHex(const QByteArray &pdata, bool printVert, bool printHo } +void commHandler::handleError(QSerialPort::SerialPortError err) +{ + switch (err) { + case QSerialPort::NoError: + break; + default: + qDebug(logSerial()) << "Serial port" << port->portName() << "Error, attempting disconnect/reconnect"; + closePort(); + openPort(); + break; + } +} diff --git a/commhandler.h b/commhandler.h index 5546175..d23673b 100644 --- a/commhandler.h +++ b/commhandler.h @@ -25,6 +25,7 @@ public: public slots: void setUseRTSforPTT(bool useRTS); void setRTS(bool rtsOn); + void handleError(QSerialPort::SerialPortError error); private slots: void receiveDataIn(); // from physical port From 630d2eaba3ba7a6d3e629dca0878ec2ed3727a5f Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 11:10:27 +0100 Subject: [PATCH 288/323] Update commhandler.cpp --- commhandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commhandler.cpp b/commhandler.cpp index a8ac59e..a771fad 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -90,7 +90,7 @@ void commHandler::sendDataOut(const QByteArray &writeData) { mutex.lock(); // Recycle port to attempt reconnection. - if (!this->isConnected) { + if (!this->isConnected || !port->isOpen()) { closePort(); openPort(); } From f003a8a1b877e80eaf4f63ce1dcdb74c8ae89927 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 11:13:20 +0100 Subject: [PATCH 289/323] Update commhandler.cpp --- commhandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/commhandler.cpp b/commhandler.cpp index a771fad..0c8eeeb 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -96,6 +96,7 @@ void commHandler::sendDataOut(const QByteArray &writeData) } if (!this->isConnected) { + mutex.unlock(); return; } qint64 bytesWritten; From 6078a31a3c4b78262806f44546d6c8f4499f2f7c Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 11:24:09 +0100 Subject: [PATCH 290/323] Add timeout if no data received on serial port for 2s --- commhandler.cpp | 5 ++++- commhandler.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/commhandler.cpp b/commhandler.cpp index 0c8eeeb..800fe4e 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -30,6 +30,7 @@ commHandler::commHandler(QObject* parent) : QObject(parent) connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn())); connect(port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError))); + lastDataReceived = QTime::currentTime(); } commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QObject* parent) : QObject(parent) @@ -90,7 +91,8 @@ void commHandler::sendDataOut(const QByteArray &writeData) { mutex.lock(); // Recycle port to attempt reconnection. - if (!this->isConnected || !port->isOpen()) { + if (!this->isConnected || !port->isOpen() || lastDataReceived.msecsTo(QTime::currentTime()) > 2000) { + qDebug(logSerial()) << "Serial port error? Attempting reconnect..."; closePort(); openPort(); } @@ -164,6 +166,7 @@ void commHandler::receiveDataIn() // because we know what constitutes a valid "frame" of data. // new code: + lastDataReceived = QTime::currentTime(); port->startTransaction(); inPortData = port->readAll(); diff --git a/commhandler.h b/commhandler.h index d23673b..38c2d5c 100644 --- a/commhandler.h +++ b/commhandler.h @@ -6,6 +6,7 @@ #include #include #include +#include // This class abstracts the comm port in a useful way and connects to // the command creator and command parser. @@ -83,6 +84,7 @@ private: quint8 spectrumInformation; quint8 spectrumOutOfRange; quint8 lastSpectrum = 0; + QTime lastDataReceived; }; #endif // COMMHANDLER_H From b049704dd2cdcbaf78ec3ed897fd8635aee804a5 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 11:35:41 +0100 Subject: [PATCH 291/323] Use qtimer to signal a reconnect --- commhandler.cpp | 30 +++++++++++++++++++----------- commhandler.h | 4 +++- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/commhandler.cpp b/commhandler.cpp index 800fe4e..5e592f0 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -40,7 +40,6 @@ commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QO // if they need to be changed later, please // destroy this and create a new one. - port = new QSerialPort(); if (wfFormat == 1) { // Single waterfall packet combineWf = true; @@ -55,6 +54,17 @@ commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QO this->portName = portName; this->PTTviaRTS = false; +} + +void commHandler::init() +{ + if (port != Q_NULLPTR) { + delete port; + port = Q_NULLPTR; + isConnected = false; + } + + port = new QSerialPort(); setupComm(); // basic parameters openPort(); // qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize(); @@ -62,9 +72,10 @@ commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QO //qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize(); connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn())); + connect(port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError))); + lastDataReceived = QTime::currentTime(); } - commHandler::~commHandler() { qInfo(logSerial()) << "Closing serial port: " << port->portName(); @@ -89,18 +100,16 @@ void commHandler::receiveDataFromUserToRig(const QByteArray &data) void commHandler::sendDataOut(const QByteArray &writeData) { - mutex.lock(); // Recycle port to attempt reconnection. if (!this->isConnected || !port->isOpen() || lastDataReceived.msecsTo(QTime::currentTime()) > 2000) { qDebug(logSerial()) << "Serial port error? Attempting reconnect..."; - closePort(); - openPort(); - } - - if (!this->isConnected) { - mutex.unlock(); + lastDataReceived = QTime::currentTime(); + QTimer::singleShot(500, this, SLOT(init())); return; } + + mutex.lock(); + qint64 bytesWritten; if(PTTviaRTS) @@ -383,8 +392,7 @@ void commHandler::handleError(QSerialPort::SerialPortError err) break; default: qDebug(logSerial()) << "Serial port" << port->portName() << "Error, attempting disconnect/reconnect"; - closePort(); - openPort(); + QTimer::singleShot(500, this, SLOT(init())); break; } } diff --git a/commhandler.h b/commhandler.h index 38c2d5c..acfc8be 100644 --- a/commhandler.h +++ b/commhandler.h @@ -7,6 +7,7 @@ #include #include #include +#include // This class abstracts the comm port in a useful way and connects to // the command creator and command parser. @@ -27,6 +28,7 @@ public slots: void setUseRTSforPTT(bool useRTS); void setRTS(bool rtsOn); void handleError(QSerialPort::SerialPortError error); + void init(); private slots: void receiveDataIn(); // from physical port @@ -60,7 +62,7 @@ private: unsigned char buffer[256]; QString portName; - QSerialPort *port; + QSerialPort *port=Q_NULLPTR; qint32 baudrate; unsigned char stopbits; bool rolledBack; From 1305f06eabe8886c37cf319edc7eb87435a7c7ad Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 11:38:35 +0100 Subject: [PATCH 292/323] Update commhandler.cpp --- commhandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/commhandler.cpp b/commhandler.cpp index 5e592f0..437d88d 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -53,6 +53,7 @@ commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QO stopbits = 1; this->portName = portName; this->PTTviaRTS = false; + init(); } From d468e01d20beaa830f7a3bc289da7cf41e739675 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 11:39:52 +0100 Subject: [PATCH 293/323] Update commhandler.cpp --- commhandler.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/commhandler.cpp b/commhandler.cpp index 437d88d..2716335 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -8,12 +8,6 @@ commHandler::commHandler(QObject* parent) : QObject(parent) { //constructor - // grab baud rate and other comm port details - // if they need to be changed later, please - // destroy this and create a new one. - port = new QSerialPort(); - - // TODO: The following should become arguments and/or functions // Add signal/slot everywhere for comm port setup. // Consider how to "re-setup" and how to save the state for next time. @@ -22,15 +16,7 @@ commHandler::commHandler(QObject* parent) : QObject(parent) portName = "/dev/ttyUSB0"; this->PTTviaRTS = false; - setupComm(); // basic parameters - openPort(); - //qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize(); - //port->setReadBufferSize(1024); // manually. 256 never saw any return from the radio. why... - //qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize(); - - connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn())); - connect(port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError))); - lastDataReceived = QTime::currentTime(); + init(); } commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QObject* parent) : QObject(parent) From 2e1d88c19490195aa1273da4ed381901d589e7b3 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 11:40:58 +0100 Subject: [PATCH 294/323] Update commhandler.cpp --- commhandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commhandler.cpp b/commhandler.cpp index 2716335..bee481a 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -88,7 +88,7 @@ void commHandler::receiveDataFromUserToRig(const QByteArray &data) void commHandler::sendDataOut(const QByteArray &writeData) { // Recycle port to attempt reconnection. - if (!this->isConnected || !port->isOpen() || lastDataReceived.msecsTo(QTime::currentTime()) > 2000) { + if (!this->isConnected || lastDataReceived.msecsTo(QTime::currentTime()) > 2000) { qDebug(logSerial()) << "Serial port error? Attempting reconnect..."; lastDataReceived = QTime::currentTime(); QTimer::singleShot(500, this, SLOT(init())); From bcde69c92b3f1b7f7ce21a11e802837694f5b308 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 11:48:58 +0100 Subject: [PATCH 295/323] Update commhandler.cpp --- commhandler.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/commhandler.cpp b/commhandler.cpp index bee481a..3b500ef 100644 --- a/commhandler.cpp +++ b/commhandler.cpp @@ -88,7 +88,7 @@ void commHandler::receiveDataFromUserToRig(const QByteArray &data) void commHandler::sendDataOut(const QByteArray &writeData) { // Recycle port to attempt reconnection. - if (!this->isConnected || lastDataReceived.msecsTo(QTime::currentTime()) > 2000) { + if (lastDataReceived.msecsTo(QTime::currentTime()) > 2000) { qDebug(logSerial()) << "Serial port error? Attempting reconnect..."; lastDataReceived = QTime::currentTime(); QTimer::singleShot(500, this, SLOT(init())); @@ -378,8 +378,11 @@ void commHandler::handleError(QSerialPort::SerialPortError err) case QSerialPort::NoError: break; default: - qDebug(logSerial()) << "Serial port" << port->portName() << "Error, attempting disconnect/reconnect"; - QTimer::singleShot(500, this, SLOT(init())); + if (lastDataReceived.msecsTo(QTime::currentTime()) > 2000) { + qDebug(logSerial()) << "Serial port" << port->portName() << "Error, attempting disconnect/reconnect"; + lastDataReceived = QTime::currentTime(); + QTimer::singleShot(500, this, SLOT(init())); + } break; } } From b3f611543c23679a593e9bffb5bf5e51c6205cd1 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 13:45:56 +0100 Subject: [PATCH 296/323] Make RT/PA a subclass of audioHandler --- pahandler.h | 3 ++- rthandler.h | 4 ++-- udpaudio.h | 4 ++-- udpserver.cpp | 8 ++++---- udpserver.h | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pahandler.h b/pahandler.h index cd55e95..a8fac05 100644 --- a/pahandler.h +++ b/pahandler.h @@ -18,6 +18,7 @@ /* Logarithmic taper for volume control */ #include "audiotaper.h" +#include "audiohandler.h" /* Audio converter class*/ #include "audioconverter.h" @@ -25,7 +26,7 @@ #include -class paHandler : public QObject +class paHandler : public audioHandler { Q_OBJECT diff --git a/rthandler.h b/rthandler.h index 10acd78..cbbd4b4 100644 --- a/rthandler.h +++ b/rthandler.h @@ -25,14 +25,14 @@ /* Logarithmic taper for volume control */ #include "audiotaper.h" - +#include "audiohandler.h" /* Audio converter class*/ #include "audioconverter.h" #include -class rtHandler : public QObject +class rtHandler : public audioHandler { Q_OBJECT diff --git a/udpaudio.h b/udpaudio.h index 478c342..87ae9cf 100644 --- a/udpaudio.h +++ b/udpaudio.h @@ -69,10 +69,10 @@ private: uint16_t sendAudioSeq = 0; - QObject* rxaudio = Q_NULLPTR; + audioHandler* rxaudio = Q_NULLPTR; QThread* rxAudioThread = Q_NULLPTR; - QObject* txaudio = Q_NULLPTR; + audioHandler* txaudio = Q_NULLPTR; QThread* txAudioThread = Q_NULLPTR; QTimer* txAudioTimer = Q_NULLPTR; diff --git a/udpserver.cpp b/udpserver.cpp index 255ba61..b99f2eb 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -395,13 +395,13 @@ void udpServer::controlReceived() connect(radio->txAudioThread, SIGNAL(finished()), radio->txaudio, SLOT(deleteLater())); // Not sure how we make this work in QT5.9? -#if (QT_VERSION >= QT_VERSION_CHECK(7,10,0)) - QMetaObject::invokeMethod((audioHandler*)radio->txaudio, [=]() { +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) + QMetaObject::invokeMethod(radio->txaudio, [=]() { radio->txaudio->init(radio->txAudioSetup); }, Qt::QueuedConnection); #else emit setupTxAudio(radio->txAudioSetup); - //#warning "QT 5.9 is not fully supported multiple rigs will NOT work!" + #warning "QT 5.9 is not fully supported multiple rigs will NOT work!" #endif hasTxAudio = datagram.senderAddress(); @@ -442,7 +442,7 @@ void udpServer::controlReceived() connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater())); connect(radio->rxaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); -#if (QT_VERSION >= QT_VERSION_CHECK(7,10,0)) +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) QMetaObject::invokeMethod(radio->rxaudio, [=]() { radio->rxaudio->init(radio->rxAudioSetup); }, Qt::QueuedConnection); diff --git a/udpserver.h b/udpserver.h index ff2e78d..3cbed99 100644 --- a/udpserver.h +++ b/udpserver.h @@ -70,9 +70,9 @@ struct RIGCONFIG { rigCapabilities rigCaps; rigCommander* rig = Q_NULLPTR; QThread* rigThread = Q_NULLPTR; - QObject* rxaudio = Q_NULLPTR; + audioHandler* rxaudio = Q_NULLPTR; QThread* rxAudioThread = Q_NULLPTR; - QObject* txaudio = Q_NULLPTR; + audioHandler* txaudio = Q_NULLPTR; QThread* txAudioThread = Q_NULLPTR; QTimer* rxAudioTimer = Q_NULLPTR; QTimer* connectTimer = Q_NULLPTR; From 8f5ba2efb197f2f6edfca3834559b8cf5980b966 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 13:54:09 +0100 Subject: [PATCH 297/323] Convert audioHandler functions to virtual --- audiohandler.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/audiohandler.h b/audiohandler.h index 9c503ec..a793973 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -43,28 +43,28 @@ class audioHandler : public QObject Q_OBJECT public: - explicit audioHandler(QObject* parent = nullptr); - ~audioHandler(); + audioHandler(QObject* parent = nullptr); + virtual ~audioHandler(); - int getLatency(); + virtual int getLatency(); - void start(); - void stop(); + virtual void start(); + virtual void stop(); - quint16 getAmplitude(); + virtual quint16 getAmplitude(); public slots: - bool init(audioSetup setup); - void changeLatency(const quint16 newSize); - void setVolume(unsigned char volume); - void incomingAudio(const audioPacket data); - void convertedInput(audioPacket audio); - void convertedOutput(audioPacket audio); + virtual bool init(audioSetup setup); + virtual void changeLatency(const quint16 newSize); + virtual void setVolume(unsigned char volume); + virtual void incomingAudio(const audioPacket data); + virtual void convertedInput(audioPacket audio); + virtual void convertedOutput(audioPacket audio); private slots: - void stateChanged(QAudio::State state); - void clearUnderrun(); - void getNextAudioChunk(); + virtual void stateChanged(QAudio::State state); + virtual void clearUnderrun(); + virtual void getNextAudioChunk(); signals: void audioMessage(QString message); From 5ef50c97069aa71f5a40fe2e3891b81126c1e394 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 14:02:31 +0100 Subject: [PATCH 298/323] Update rthandler.cpp --- rthandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rthandler.cpp b/rthandler.cpp index c3d0353..96229f2 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -41,7 +41,7 @@ bool rtHandler::init(audioSetup setup) } this->setup = setup; - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "PortAudio handler starting:" << setup.name; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "RTAudio handler starting:" << setup.name; if (setup.portInt==-1) { From 079a50980d91b1792699885ed44387e8aca65d08 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sat, 14 May 2022 15:05:48 +0100 Subject: [PATCH 299/323] Fix RT/PA builds on MacOs --- rthandler.cpp | 4 ++-- rthandler.h | 2 +- wfmain.h | 4 ++-- wfserver.pro | 4 ++-- wfview.pro | 5 +++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/rthandler.cpp b/rthandler.cpp index 96229f2..b7438f5 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -20,7 +20,7 @@ rtHandler::~rtHandler() audio->abortStream(); audio->closeStream(); } - catch (RtAudioError& e) { + catch (RtAudioError& e) { qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); } delete audio; @@ -90,7 +90,7 @@ bool rtHandler::init(audioSetup setup) try { info = audio->getDeviceInfo(aParams.deviceId); } - catch (RtAudioError& e) { + catch (RtAudioError e) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Device exception:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); goto errorHandler; } diff --git a/rthandler.h b/rthandler.h index cbbd4b4..c48a2b4 100644 --- a/rthandler.h +++ b/rthandler.h @@ -6,7 +6,7 @@ #include #include -#ifdef Q_OS_WIN +#ifndef Q_OS_LINUX #include "RtAudio.h" #else #include "rtaudio/RtAudio.h" diff --git a/wfmain.h b/wfmain.h index 29bded4..e691258 100644 --- a/wfmain.h +++ b/wfmain.h @@ -41,7 +41,7 @@ #include #include -#ifdef Q_OS_WIN +#ifndef Q_OS_LINUX #include "RtAudio.h" #else #include "rtaudio/RtAudio.h" @@ -914,4 +914,4 @@ Q_DECLARE_METATYPE(rigstate*) #endif // WFMAIN_H -#endif \ No newline at end of file +#endif diff --git a/wfserver.pro b/wfserver.pro index d17f9eb..bbd4fee 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -41,8 +41,8 @@ win32:DEFINES += __WINDOWS_WASAPI__ #linux:DEFINES += __LINUX_OSS__ linux:DEFINES += __LINUX_PULSE__ macx:DEFINES += __MACOSX_CORE__ -win32:SOURCES += ../rtaudio/RTAudio.cpp -win32:HEADERS += ../rtaudio/RTAUdio.h +!linux:SOURCES += ../rtaudio/RTAudio.cpp +!linux:HEADERS += ../rtaudio/RTAUdio.h !linux:INCLUDEPATH += ../rtaudio linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread diff --git a/wfview.pro b/wfview.pro index ef0e2be..63d0391 100644 --- a/wfview.pro +++ b/wfview.pro @@ -38,9 +38,10 @@ win32:DEFINES += __WINDOWS_WASAPI__ #linux:DEFINES += __LINUX_OSS__ linux:DEFINES += __LINUX_PULSE__ macx:DEFINES += __MACOSX_CORE__ -win32:SOURCES += ../rtaudio/RTAudio.cpp -win32:HEADERS += ../rtaudio/RTAUdio.h +!linux:SOURCES += ../rtaudio/RTAudio.cpp +!linux:HEADERS += ../rtaudio/RTAUdio.h !linux:INCLUDEPATH += ../rtaudio + linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread win32:INCLUDEPATH += ../portaudio/include From cd03ac40d552d35d3e6b8fb7992ad60db2e53240 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 19 May 2022 16:03:06 +0000 Subject: [PATCH 300/323] Update INSTALL.md --- INSTALL.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/INSTALL.md b/INSTALL.md index 589eab7..70612cc 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -14,6 +14,9 @@ sudo apt-get install libqt5multimedia5-plugins sudo apt-get install qtmultimedia5-dev sudo apt-get install git sudo apt-get install libopus-dev +sudo apt-get install libeigen3-dev +sudo apt-get install portaudio19-dev +sudo apt-get install librtaudio-dev ~~~ Now you need to install qcustomplot. There are two versions that are commonly found in linux distros: 1.3 and 2.0. Either will work fine. If you are not sure which version your linux install comes with, simply run both commands. One will work and the other will fail, and that's fine! From f2e9a9000021e58fb87c09ea7c7b77b964d9df4f Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 20 May 2022 15:50:47 +0100 Subject: [PATCH 301/323] Support newer versions of rtaudio that don't generate exceptions --- rthandler.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/rthandler.cpp b/rthandler.cpp index b7438f5..af5e7cb 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -6,6 +6,7 @@ #include #endif +#define RT_EXCEPTION rtHandler::rtHandler(QObject* parent) { @@ -16,13 +17,17 @@ rtHandler::~rtHandler() { if (isInitialized) { +#ifdef RT_EXCEPTION try { +#endif audio->abortStream(); audio->closeStream(); +#ifdef RT_EXCEPTION } catch (RtAudioError& e) { qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); } +#endif delete audio; } @@ -87,14 +92,17 @@ bool rtHandler::init(audioSetup setup) } aParams.firstChannel = 0; +#ifdef RT_EXCEPTION try { +#endif info = audio->getDeviceInfo(aParams.deviceId); +#ifdef RT_EXCEPTION } catch (RtAudioError e) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Device exception:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); goto errorHandler; } - +#endif if (info.probed) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed"; @@ -190,7 +198,9 @@ bool rtHandler::init(audioSetup setup) // Per channel chunk size. this->chunkSize = (outFormat.bytesForDuration(setup.blockSize * 1000) / (outFormat.sampleSize()/8) / outFormat.channelCount()); +#ifdef RT_EXCEPTION try { +#endif if (setup.isinput) { audio->openStream(NULL, &aParams, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticWrite, this, &options); emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality); @@ -205,12 +215,14 @@ bool rtHandler::init(audioSetup setup) isInitialized = true; qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened"; qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "detected latency:" << audio->getStreamLatency(); +#ifdef RT_EXCEPTION } catch (RtAudioError& e) { qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); // Try again? goto errorHandler; } +#endif } else { From c807c40bd25a7acc6d12ef6175e523606c09e220 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 20 May 2022 18:24:28 +0100 Subject: [PATCH 302/323] Small .pro file update and cleanup --- wfserver.pro | 32 ++------------------------------ wfview.pro | 28 ++-------------------------- 2 files changed, 4 insertions(+), 56 deletions(-) diff --git a/wfserver.pro b/wfserver.pro index bbd4fee..5a854ec 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -22,7 +22,7 @@ CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new win32:DESTDIR = wfview-release - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 -ole32 } else { # For Release builds only: linux:QMAKE_CXXFLAGS += -s @@ -31,7 +31,7 @@ CONFIG(debug, release|debug) { QMAKE_CXXFLAGS += -faligned-new linux:QMAKE_LFLAGS += -O2 -s win32:DESTDIR = wfview-debug - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 -lole32 } # RTAudio defines @@ -76,34 +76,6 @@ equals(QT_ARCH, x86_64): DEFINES += EIGEN_VECTORIZE_SSE3 DEFINES += PREFIX=\\\"$$PREFIX\\\" -# Choose audio system, uses QTMultimedia if both are commented out. -# DEFINES += RTAUDIO -# DEFINES += PORTAUDIO - -contains(DEFINES, RTAUDIO) { - # RTAudio defines - win32:DEFINES += __WINDOWS_WASAPI__ - #win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries - linux:DEFINES += __LINUX_ALSA__ - #linux:DEFINES += __LINUX_OSS__ - #linux:DEFINES += __LINUX_PULSE__ - macx:DEFINES += __MACOSX_CORE__ - win32:SOURCES += ../rtaudio/RTAudio.cpp - win32:HEADERS += ../rtaudio/RTAUdio.h - !linux:INCLUDEPATH += ../rtaudio - linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread -} - -contains(DEFINES, PORTAUDIO) { - CONFIG(debug, release|debug) { - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 - } else { - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 - } - win32:INCLUDEPATH += ../portaudio/include - !win32:LIBS += -lportaudio -} - macx:INCLUDEPATH += /usr/local/include /opt/local/include macx:LIBS += -L/usr/local/lib -L/opt/local/lib diff --git a/wfview.pro b/wfview.pro index 63d0391..c9cd8aa 100644 --- a/wfview.pro +++ b/wfview.pro @@ -19,7 +19,7 @@ CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new win32:DESTDIR = wfview-release - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 -lole32 } else { # For Release builds only: linux:QMAKE_CXXFLAGS += -s @@ -28,7 +28,7 @@ CONFIG(debug, release|debug) { QMAKE_CXXFLAGS += -faligned-new linux:QMAKE_LFLAGS += -O2 -s win32:DESTDIR = wfview-debug - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 -lole32 } # RTAudio defines @@ -77,30 +77,6 @@ isEmpty(PREFIX) { DEFINES += PREFIX=\\\"$$PREFIX\\\" -contains(DEFINES, RTAUDIO) { - # RTAudio defines - win32:DEFINES += __WINDOWS_WASAPI__ - #win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries - linux:DEFINES += __LINUX_ALSA__ - #linux:DEFINES += __LINUX_OSS__ - #linux:DEFINES += __LINUX_PULSE__ - macx:DEFINES += __MACOSX_CORE__ - win32:SOURCES += ../rtaudio/RTAudio.cpp - win32:HEADERS += ../rtaudio/RTAUdio.h - !linux:INCLUDEPATH += ../rtaudio - linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread -} - -contains(DEFINES, PORTAUDIO) { - CONFIG(debug, release|debug) { - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 - } else { - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 - } - win32:INCLUDEPATH += ../portaudio/include - !win32:LIBS += -lportaudio -} - macx:INCLUDEPATH += /usr/local/include /opt/local/include macx:LIBS += -L/usr/local/lib -L/opt/local/lib From 227ea8fc348db38cf426ce3b459ba31b4911f9d2 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 20 May 2022 18:24:28 +0100 Subject: [PATCH 303/323] Small .pro file update and cleanup --- wfserver.pro | 32 ++------------------------------ wfview.pro | 28 ++-------------------------- 2 files changed, 4 insertions(+), 56 deletions(-) diff --git a/wfserver.pro b/wfserver.pro index bbd4fee..5a854ec 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -22,7 +22,7 @@ CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new win32:DESTDIR = wfview-release - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 -ole32 } else { # For Release builds only: linux:QMAKE_CXXFLAGS += -s @@ -31,7 +31,7 @@ CONFIG(debug, release|debug) { QMAKE_CXXFLAGS += -faligned-new linux:QMAKE_LFLAGS += -O2 -s win32:DESTDIR = wfview-debug - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 -lole32 } # RTAudio defines @@ -76,34 +76,6 @@ equals(QT_ARCH, x86_64): DEFINES += EIGEN_VECTORIZE_SSE3 DEFINES += PREFIX=\\\"$$PREFIX\\\" -# Choose audio system, uses QTMultimedia if both are commented out. -# DEFINES += RTAUDIO -# DEFINES += PORTAUDIO - -contains(DEFINES, RTAUDIO) { - # RTAudio defines - win32:DEFINES += __WINDOWS_WASAPI__ - #win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries - linux:DEFINES += __LINUX_ALSA__ - #linux:DEFINES += __LINUX_OSS__ - #linux:DEFINES += __LINUX_PULSE__ - macx:DEFINES += __MACOSX_CORE__ - win32:SOURCES += ../rtaudio/RTAudio.cpp - win32:HEADERS += ../rtaudio/RTAUdio.h - !linux:INCLUDEPATH += ../rtaudio - linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread -} - -contains(DEFINES, PORTAUDIO) { - CONFIG(debug, release|debug) { - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 - } else { - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 - } - win32:INCLUDEPATH += ../portaudio/include - !win32:LIBS += -lportaudio -} - macx:INCLUDEPATH += /usr/local/include /opt/local/include macx:LIBS += -L/usr/local/lib -L/opt/local/lib diff --git a/wfview.pro b/wfview.pro index 63d0391..c9cd8aa 100644 --- a/wfview.pro +++ b/wfview.pro @@ -19,7 +19,7 @@ CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new win32:DESTDIR = wfview-release - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 -lole32 } else { # For Release builds only: linux:QMAKE_CXXFLAGS += -s @@ -28,7 +28,7 @@ CONFIG(debug, release|debug) { QMAKE_CXXFLAGS += -faligned-new linux:QMAKE_LFLAGS += -O2 -s win32:DESTDIR = wfview-debug - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 -lole32 } # RTAudio defines @@ -77,30 +77,6 @@ isEmpty(PREFIX) { DEFINES += PREFIX=\\\"$$PREFIX\\\" -contains(DEFINES, RTAUDIO) { - # RTAudio defines - win32:DEFINES += __WINDOWS_WASAPI__ - #win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries - linux:DEFINES += __LINUX_ALSA__ - #linux:DEFINES += __LINUX_OSS__ - #linux:DEFINES += __LINUX_PULSE__ - macx:DEFINES += __MACOSX_CORE__ - win32:SOURCES += ../rtaudio/RTAudio.cpp - win32:HEADERS += ../rtaudio/RTAUdio.h - !linux:INCLUDEPATH += ../rtaudio - linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread -} - -contains(DEFINES, PORTAUDIO) { - CONFIG(debug, release|debug) { - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 - } else { - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 - } - win32:INCLUDEPATH += ../portaudio/include - !win32:LIBS += -lportaudio -} - macx:INCLUDEPATH += /usr/local/include /opt/local/include macx:LIBS += -L/usr/local/lib -L/opt/local/lib From 5cb81b412f328f0315ef158db72e67cf229f8eb7 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 24 May 2022 10:08:18 +0100 Subject: [PATCH 304/323] Try to quit wfserver on ctrl-c --- main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 2dc33e0..3583a0a 100644 --- a/main.cpp +++ b/main.cpp @@ -33,7 +33,7 @@ bool debugMode=false; Q_UNUSED(sig) qDebug() << "Exiting via SIGNAL"; if (w!=Q_NULLPTR) w->deleteLater(); - qApp->quit(); + QCoreApplication::quit(); #ifdef Q_OS_WIN return true; @@ -177,6 +177,8 @@ int main(int argc, char *argv[]) SetConsoleCtrlHandler((PHANDLER_ROUTINE)cleanup, TRUE); #else signal(SIGINT, cleanup); + signal(SIG_TERM, cleanup); + signal(SIG_CLOSE, cleanup); #endif w = new servermain(serialPortCL, hostCL, settingsFile); #else From 06017148dd7d5ed3bc548b9af2fbdc3d27d0bb50 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 24 May 2022 10:13:44 +0100 Subject: [PATCH 305/323] Update main.cpp --- main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 3583a0a..8e945ae 100644 --- a/main.cpp +++ b/main.cpp @@ -177,8 +177,8 @@ int main(int argc, char *argv[]) SetConsoleCtrlHandler((PHANDLER_ROUTINE)cleanup, TRUE); #else signal(SIGINT, cleanup); - signal(SIG_TERM, cleanup); - signal(SIG_CLOSE, cleanup); + signal(SIGTERM, cleanup); + signal(SIGKILL, cleanup); #endif w = new servermain(serialPortCL, hostCL, settingsFile); #else From 485d012b4294ccbdf29476e487bbc5b20c60cf15 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Tue, 24 May 2022 17:37:19 +0200 Subject: [PATCH 306/323] WHATSNEW --- WHATSNEW | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/WHATSNEW b/WHATSNEW index 9d9eca8..75a34a8 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -32,13 +32,12 @@ The following highlights are in this 1.x-release: + More multi-radio support (mostly working!) + Split LAN waterfall data for N1MM+ Spectrum Scope support: There is a combobox that allows you to select split/combine (or default). Split only makes sense for LAN and Combine for USB. -+ portaudio removed + added radio status display with meters for audio (speaker/mic) - ++ selector for audio: QT, PortAudio, RealTime audio ( Notes: - We know about high CPU usage on RPi. - the audio stack has been rewritten multiple times to get the best out of it and have the lowest possible latencies. - (At least three times) + (At least five times - you can choose now) From d6263223af112ace31e66f921317660ff6e8e4ed Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Tue, 24 May 2022 17:55:03 +0200 Subject: [PATCH 307/323] dropped openSUSE 15.2, out of support --- INSTALL_PREBUILT_BINARY.md | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/INSTALL_PREBUILT_BINARY.md b/INSTALL_PREBUILT_BINARY.md index 6c8fc7e..6f0dd57 100644 --- a/INSTALL_PREBUILT_BINARY.md +++ b/INSTALL_PREBUILT_BINARY.md @@ -22,8 +22,8 @@ Debian 11 (Debian 10 is outdated) Fedora 33 Fedora 34 mint 20.1 (and up?) -openSUSE 15.2 -openSUSE 15.3 (see notes at the end) +openSUSE 15.2 (outdated/deprecated) +openSUSE 15.3/15.4) openSUSE Tumbleweed(s) SLES 15.x Ubuntu 20.04.2 @@ -86,16 +86,8 @@ sudo ln -s /lib/x86_64-linux-gnu/libqcustomplot.so.2.0.1 /lib/x86_64-linux-gnu/l ~~~ -### openSUSE/Tumbleweed/SLES based on 15.2: +### openSUSE/Tumbleweed/SLES based on 15.3/15.4: ~~~ -sudo zypper in libqcustomplot2 libQt5SerialPort5 -wfview -~~~ - -### openSUSE/Tumbleweed/SLES based on 15.3: -~~~ - -SEE THE NOTES AT THE END. You need wfview153 here sudo zypper in libqcustomplot2 libQt5SerialPort5 wfview @@ -119,14 +111,8 @@ sudo ln -s /lib/x86_64-linux-gnu/libqcustomplot.so.2.0.1 /lib/x86_64-linux-gnu/l ### notes: ~~~ -Some newer versions of mint. ubuntu, openSUSE have different kernels and such which cause wfview to segfault. +openSUSE 15.2 is deprecated; old binary has been removed -For these cases we created two binaries: one for current systems ("wfview") and one for the new systems ("wfview153") - -So if you encounter a SEGFAULT at start: - -go in to the dist directory, rename wfview to wfvie152; rename wfview153 to wfview and re-execute the install.sh -script ~~~ From a9e9c807088c75ff76b10bd02dc3b7306f62371b Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 14 Jun 2022 10:31:29 +0100 Subject: [PATCH 308/323] Fix silly bug introduced in server audio TX combo --- wfmain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 6d71ad8..d2f5b4c 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1740,9 +1740,9 @@ void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) serverConfig.rigs.first()->rxAudioSetup.portInt = ui->serverRXAudioInputCombo->itemData(value).toInt(); } + serverConfig.rigs.first()->rxAudioSetup.name = ui->serverRXAudioInputCombo->itemText(value); } - serverConfig.rigs.first()->rxAudioSetup.name = ui->audioInputCombo->itemText(value); } @@ -1759,9 +1759,9 @@ void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) serverConfig.rigs.first()->txAudioSetup.portInt = ui->serverTXAudioOutputCombo->itemData(value).toInt(); } + serverConfig.rigs.first()->txAudioSetup.name = ui->serverTXAudioOutputCombo->itemText(value); } - serverConfig.rigs.first()->txAudioSetup.name = ui->audioInputCombo->itemText(value); } From bcc16b8d2945da0dc15366a4f1c1fda1da8b6fba Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 5 Jul 2022 10:31:45 +0100 Subject: [PATCH 309/323] Send blank audio packet on disconnct --- udpaudio.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/udpaudio.cpp b/udpaudio.cpp index b49359d..38cd48d 100644 --- a/udpaudio.cpp +++ b/udpaudio.cpp @@ -96,6 +96,16 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint udpAudio::~udpAudio() { + + // Send empty packet before deleting audio + audioPacket tempAudio; + QByteArray tempArray((const char *)'\0', 1920); + tempAudio.seq = 0; + tempAudio.time = lastReceived; + tempAudio.sent = 0; + tempAudio.data = tempArray; + emit haveAudioData(tempAudio); + if (pingTimer != Q_NULLPTR) { qDebug(logUdp()) << "Stopping pingTimer"; From ac2c104209700a5236ab8375ed89e057b591dd72 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 5 Jul 2022 10:37:10 +0100 Subject: [PATCH 310/323] Re-order audio handler destructors --- audiohandler.cpp | 11 ++++++----- pahandler.cpp | 10 ++++------ rthandler.cpp | 11 ++++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index b274545..cf49045 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -18,23 +18,24 @@ audioHandler::audioHandler(QObject* parent) : QObject(parent) audioHandler::~audioHandler() { + if (converterThread != Q_NULLPTR) { + converterThread->quit(); + converterThread->wait(); + } + if (isInitialized) { stop(); } if (audioInput != Q_NULLPTR) { - audioInput = Q_NULLPTR; delete audioInput; + audioInput = Q_NULLPTR; } if (audioOutput != Q_NULLPTR) { delete audioOutput; audioOutput = Q_NULLPTR; } - if (converterThread != Q_NULLPTR) { - converterThread->quit(); - converterThread->wait(); - } }bool audioHandler::init(audioSetup setup) { if (isInitialized) { diff --git a/pahandler.cpp b/pahandler.cpp index e0de0eb..9db91d1 100644 --- a/pahandler.cpp +++ b/pahandler.cpp @@ -15,17 +15,15 @@ paHandler::paHandler(QObject* parent) paHandler::~paHandler() { - if (isInitialized) { - Pa_StopStream(audio); - Pa_CloseStream(audio); - } - if (converterThread != Q_NULLPTR) { converterThread->quit(); converterThread->wait(); } - //Pa_Terminate(); + if (isInitialized) { + Pa_StopStream(audio); + Pa_CloseStream(audio); + } } diff --git a/rthandler.cpp b/rthandler.cpp index af5e7cb..f848c38 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -16,7 +16,13 @@ rtHandler::rtHandler(QObject* parent) rtHandler::~rtHandler() { + if (converterThread != Q_NULLPTR) { + converterThread->quit(); + converterThread->wait(); + } + if (isInitialized) { + #ifdef RT_EXCEPTION try { #endif @@ -31,11 +37,6 @@ rtHandler::~rtHandler() delete audio; } - - if (converterThread != Q_NULLPTR) { - converterThread->quit(); - converterThread->wait(); - } } From 9a77baaead6b1687b80447b28cf08b511bd141b4 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Thu, 4 Aug 2022 20:46:15 +0100 Subject: [PATCH 311/323] Shutdown audio when radio is turned off. --- udpaudio.cpp | 172 +++++++++++++++++++++++++++---------------------- udpaudio.h | 3 + udphandler.cpp | 12 ++-- 3 files changed, 105 insertions(+), 82 deletions(-) diff --git a/udpaudio.cpp b/udpaudio.cpp index 38cd48d..dc1e5c5 100644 --- a/udpaudio.cpp +++ b/udpaudio.cpp @@ -8,6 +8,8 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint this->localIP = local; this->port = audioPort; this->radioIP = ip; + this->rxSetup = rxSetup; + this->txSetup = txSetup; if (txSetup.sampleRate == 0) { enableTx = false; @@ -16,74 +18,8 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint init(lport); // Perform connection QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); - if (rxSetup.type == qtAudio) { - rxaudio = new audioHandler(); - } - else if (rxSetup.type == portAudio) { - rxaudio = new paHandler(); - } - else if (rxSetup.type == rtAudio) { - rxaudio = new rtHandler(); - } - else - { - qCritical(logAudio()) << "Unsupported Receive Audio Handler selected!"; - } - - rxAudioThread = new QThread(this); - rxAudioThread->setObjectName("rxAudio()"); - - rxaudio->moveToThread(rxAudioThread); - - rxAudioThread->start(QThread::TimeCriticalPriority); - - connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); - - // signal/slot not currently used. - connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); - connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); - connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); - connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool,bool))); - connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); - - - sendControl(false, 0x03, 0x00); // First connect packet - - pingTimer = new QTimer(); - connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); - pingTimer->start(PING_PERIOD); // send ping packets every 100ms - - if (enableTx) { - if (txSetup.type == qtAudio) { - txaudio = new audioHandler(); - } - else if (txSetup.type == portAudio) { - txaudio = new paHandler(); - } - else if (txSetup.type == rtAudio) { - txaudio = new rtHandler(); - } - else - { - qCritical(logAudio()) << "Unsupported Transmit Audio Handler selected!"; - } - - txAudioThread = new QThread(this); - rxAudioThread->setObjectName("txAudio()"); - - txaudio->moveToThread(txAudioThread); - - txAudioThread->start(QThread::TimeCriticalPriority); - - connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); - connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); - connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool, bool))); - - connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); - emit setupTxAudio(txSetup); - } - - emit setupRxAudio(rxSetup); + + startAudio(); watchdogTimer = new QTimer(); connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog); @@ -97,15 +33,6 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint udpAudio::~udpAudio() { - // Send empty packet before deleting audio - audioPacket tempAudio; - QByteArray tempArray((const char *)'\0', 1920); - tempAudio.seq = 0; - tempAudio.time = lastReceived; - tempAudio.sent = 0; - tempAudio.data = tempArray; - emit haveAudioData(tempAudio); - if (pingTimer != Q_NULLPTR) { qDebug(logUdp()) << "Stopping pingTimer"; @@ -155,6 +82,21 @@ void udpAudio::watchdog() qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required?"; alerted = true; + if (rxAudioThread != Q_NULLPTR) { + qDebug(logUdp()) << "Stopping rxaudio thread"; + rxAudioThread->quit(); + rxAudioThread->wait(); + rxAudioThread = Q_NULLPTR; + rxaudio = Q_NULLPTR; + } + + if (txAudioThread != Q_NULLPTR) { + qDebug(logUdp()) << "Stopping txaudio thread"; + txAudioThread->quit(); + txAudioThread->wait(); + txAudioThread = Q_NULLPTR; + txaudio = Q_NULLPTR; + } } } else @@ -228,6 +170,7 @@ void udpAudio::getTxLevels(quint16 amplitude, quint16 latency, quint16 current, void udpAudio::dataReceived() { + while (udp->hasPendingDatagrams()) { QNetworkDatagram datagram = udp->receiveDatagram(); //qInfo(logUdp()) << "Received: " << datagram.data().mid(0,10); @@ -268,6 +211,10 @@ void udpAudio::dataReceived() // Prefer signal/slot to forward audio as it is thread/safe // Need to do more testing but latency appears fine. //rxaudio->incomingAudio(tempAudio); + if (rxAudioThread == Q_NULLPTR) + { + startAudio(); + } emit haveAudioData(tempAudio); } break; @@ -280,4 +227,75 @@ void udpAudio::dataReceived() } } +void udpAudio::startAudio() { + if (rxSetup.type == qtAudio) { + rxaudio = new audioHandler(); + } + else if (rxSetup.type == portAudio) { + rxaudio = new paHandler(); + } + else if (rxSetup.type == rtAudio) { + rxaudio = new rtHandler(); + } + else + { + qCritical(logAudio()) << "Unsupported Receive Audio Handler selected!"; + } + + rxAudioThread = new QThread(this); + rxAudioThread->setObjectName("rxAudio()"); + + rxaudio->moveToThread(rxAudioThread); + + rxAudioThread->start(QThread::TimeCriticalPriority); + + connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); + + // signal/slot not currently used. + connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); + connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); + connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); + connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool, bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool, bool))); + connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); + + + sendControl(false, 0x03, 0x00); // First connect packet + + pingTimer = new QTimer(); + connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); + pingTimer->start(PING_PERIOD); // send ping packets every 100ms + + if (enableTx) { + if (txSetup.type == qtAudio) { + txaudio = new audioHandler(); + } + else if (txSetup.type == portAudio) { + txaudio = new paHandler(); + } + else if (txSetup.type == rtAudio) { + txaudio = new rtHandler(); + } + else + { + qCritical(logAudio()) << "Unsupported Transmit Audio Handler selected!"; + } + + txAudioThread = new QThread(this); + rxAudioThread->setObjectName("txAudio()"); + + txaudio->moveToThread(txAudioThread); + + txAudioThread->start(QThread::TimeCriticalPriority); + + connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); + connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); + connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool, bool))); + + connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); + emit setupTxAudio(txSetup); + } + + emit setupRxAudio(rxSetup); + +} diff --git a/udpaudio.h b/udpaudio.h index 87ae9cf..884edef 100644 --- a/udpaudio.h +++ b/udpaudio.h @@ -66,6 +66,9 @@ private: void sendTxAudio(); void dataReceived(); void watchdog(); + void startAudio(); + audioSetup rxSetup; + audioSetup txSetup; uint16_t sendAudioSeq = 0; diff --git a/udphandler.cpp b/udphandler.cpp index 93fd3f9..f0ffd60 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -302,23 +302,25 @@ void udpHandler::dataReceived() if (!streamOpened) { civ = new udpCivData(localIP, radioIP, civPort, splitWf, civLocalPort); + QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); // TX is not supported if (txSampleRates < 2) { txSetup.sampleRate=0; txSetup.codec = 0; } + streamOpened = true; + } + if (audio == Q_NULLPTR) { audio = new udpAudio(localIP, radioIP, audioPort, audioLocalPort, rxSetup, txSetup); - QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16))); QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char))); - QObject::connect(audio, SIGNAL(haveRxLevels(quint16, quint16, quint16,bool,bool)), this, SLOT(getRxLevels(quint16, quint16,quint16,bool,bool))); - QObject::connect(audio, SIGNAL(haveTxLevels(quint16, quint16,quint16,bool,bool)), this, SLOT(getTxLevels(quint16, quint16,quint16,bool,bool))); - - streamOpened = true; + QObject::connect(audio, SIGNAL(haveRxLevels(quint16, quint16, quint16, bool, bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool, bool))); + QObject::connect(audio, SIGNAL(haveTxLevels(quint16, quint16, quint16, bool, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool, bool))); } + qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName; From c753089ac7b56f5af67f23e758555e213d636aad Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 15:10:07 -0700 Subject: [PATCH 312/323] Added spectrum plasma metering. --- wfmain.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- wfmain.h | 5 +++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/wfmain.cpp b/wfmain.cpp index d2f5b4c..c0ec779 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -2035,6 +2035,15 @@ void wfmain::prepareWf(unsigned int wfLength) QByteArray empty((int)spectWidth, '\x01'); spectrumPeaks = QByteArray( (int)spectWidth, '\x01' ); + if(spectrumPlasmaSize == 0) + spectrumPlasmaSize = 128; + + //spectrumPlasma.resize(spectrumPlasmaSize); + for(unsigned int p=0; p < spectrumPlasmaSize; p++) + { + spectrumPlasma.append(empty); + } + //wfimage.resize(wfLengthMax); if((unsigned int)wfimage.size() < wfLengthMax) @@ -3522,6 +3531,12 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e } } + spectrumPlasma.push_front(spectrum); + spectrumPlasma.resize(spectrumPlasmaSize); + + // HACK DO NOT CHECK IN: + drawPeaks = false; + drawPlasma = true; if(!spectrumDrawLock) { @@ -3535,6 +3550,9 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e if(drawPeaks) { plot->graph(1)->setData(x,y2); // peaks + } else if (drawPlasma) { + computePlasma(); + plot->graph(1)->setData(x,spectrumPlasmaLine); // peaks } plot->yAxis->setRange(0, rigCaps.spectAmpMax+1); plot->xAxis->setRange(startFreq, endFreq); @@ -3562,6 +3580,35 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e } } +void wfmain::computePlasma() +{ + spectrumPlasmaLine.clear(); + spectrumPlasmaLine.resize(spectWidth); + bool averageMode = true; + if(averageMode) + { + for(int col=0; col < spectWidth; col++) + { + for(int pos=0; pos < spectrumPlasma.size(); pos++) + { + spectrumPlasmaLine[col] += spectrumPlasma[pos][col]; + + } + spectrumPlasmaLine[col] = spectrumPlasmaLine[col] / spectrumPlasma.size(); + } + } else { + // peak mode, running peak display + for(int col=0; col < spectWidth; col++) + { + for(int pos=0; pos < spectrumPlasma.size(); pos++) + { + if((double)(spectrumPlasma[pos][col]) > spectrumPlasmaLine[col]) + spectrumPlasmaLine[col] = spectrumPlasma[pos][col]; + } + } + } +} + void wfmain::receiveSpectrumMode(spectrumMode spectMode) { for (int i = 0; i < ui->spectrumModeCombo->count(); i++) @@ -6000,4 +6047,4 @@ void wfmain::on_audioSystemCombo_currentIndexChanged(int value) { prefs.audioSystem = static_cast(value); setAudioDevicesUI(); // Force all audio devices to update -} \ No newline at end of file +} diff --git a/wfmain.h b/wfmain.h index e691258..c283a94 100644 --- a/wfmain.h +++ b/wfmain.h @@ -557,6 +557,7 @@ private: void setPlotTheme(QCustomPlot *plot, bool isDark); void prepareWf(); void prepareWf(unsigned int wfLength); + void computePlasma(); void showHideSpectrum(bool show); void getInitialRigState(); void setBandButtons(); @@ -648,6 +649,10 @@ private: bool spectrumDrawLock; QByteArray spectrumPeaks; + QVector spectrumPlasmaLine; + QVector spectrumPlasma; + unsigned int spectrumPlasmaSize = 64; + bool drawPlasma = true; QVector wfimage; unsigned int wfLengthMax; From 02e6733cddebcde2d478e7179053a902db68f1dc Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 15:51:26 -0700 Subject: [PATCH 313/323] Fixed ancient issue with layering of the plot data --- wfmain.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index c0ec779..1236813 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -617,20 +617,21 @@ void wfmain::receiveStatusUpdate(networkStatus status) void wfmain::setupPlots() { - -// Line 290-- spectrumDrawLock = true; - plot = ui->plot; // rename it waterfall. + plot = ui->plot; wf = ui->waterfall; freqIndicatorLine = new QCPItemLine(plot); freqIndicatorLine->setAntialiased(true); freqIndicatorLine->setPen(QPen(Qt::blue)); -// ui->plot->addGraph(); // primary - ui->plot->addGraph(0, 0); // secondary, peaks, same axis as first? + ui->plot->addGraph(0, 0); // secondary, peaks, same axis as first. + ui->plot->addLayer( "Top Layer", ui->plot->layer("main")); + ui->plot->graph(0)->setLayer("Top Layer"); + + ui->waterfall->addGraph(); colorMap = new QCPColorMap(wf->xAxis, wf->yAxis); From 53bed16b45782c978bea99b40a8b5f6f0ba1c446 Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 16:21:33 -0700 Subject: [PATCH 314/323] Added floor and ceiling adjustments for the plots. --- wfmain.cpp | 75 ++++++++++++++++++++++------------- wfmain.h | 9 +++++ wfmain.ui | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 168 insertions(+), 30 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 1236813..157f843 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -3192,6 +3192,11 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) } setWindowTitle(rigCaps.modelName); this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true. + wfCeiling = rigCaps.spectAmpMax; + plotCeiling = rigCaps.spectAmpMax; + ui->topLevelSlider->setMaximum(rigCaps.spectAmpMax); + wfFloor = 0; + plotFloor = 0; haveRigCaps = true; // Added so that server receives rig capabilities. emit sendRigCaps(rigCaps); @@ -3555,7 +3560,7 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e computePlasma(); plot->graph(1)->setData(x,spectrumPlasmaLine); // peaks } - plot->yAxis->setRange(0, rigCaps.spectAmpMax+1); + plot->yAxis->setRange(plotFloor, plotCeiling); plot->xAxis->setRange(startFreq, endFreq); plot->replot(); @@ -5792,32 +5797,6 @@ void wfmain::on_radioStatusBtn_clicked() } } -// --- DEBUG FUNCTION --- -void wfmain::on_debugBtn_clicked() -{ - qInfo(logSystem()) << "Debug button pressed."; - // issueDelayedCommand(cmdGetRigID); - //emit getRigCIV(); - //trxadj->show(); - //setRadioTimeDatePrep(); - //wf->setInteraction(QCP::iRangeZoom, true); - //wf->setInteraction(QCP::iRangeDrag, true); - bool ok; - int height = QInputDialog::getInt(this, "wfview Radio Polling Setup", "Poll Timing Interval (ms)", 350, 1, 500, 1, &ok ); - - this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - this->setMaximumSize(QSize(1025,height)); - this->setMinimumSize(QSize(1025,height)); - //this->setMaximumSize(QSize(929, 270)); - //this->setMinimumSize(QSize(929, 270)); - - resize(minimumSize()); - adjustSize(); // main window - adjustSize(); - -} - - void wfmain::setAudioDevicesUI() { @@ -6049,3 +6028,45 @@ void wfmain::on_audioSystemCombo_currentIndexChanged(int value) prefs.audioSystem = static_cast(value); setAudioDevicesUI(); // Force all audio devices to update } + +void wfmain::on_topLevelSlider_valueChanged(int value) +{ + wfCeiling = value; + plotCeiling = value; + plot->yAxis->setRange(QCPRange(plotFloor, plotCeiling)); + colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); +} + +void wfmain::on_botLevelSlider_valueChanged(int value) +{ + wfFloor = value; + plotFloor = value; + plot->yAxis->setRange(QCPRange(plotFloor, plotCeiling)); + colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); +} + + +// --- DEBUG FUNCTION --- +void wfmain::on_debugBtn_clicked() +{ + qInfo(logSystem()) << "Debug button pressed."; + // issueDelayedCommand(cmdGetRigID); + //emit getRigCIV(); + //trxadj->show(); + //setRadioTimeDatePrep(); + //wf->setInteraction(QCP::iRangeZoom, true); + //wf->setInteraction(QCP::iRangeDrag, true); + bool ok; + int height = QInputDialog::getInt(this, "wfview window fixed height", "number: ", 350, 1, 500, 1, &ok ); + + this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + this->setMaximumSize(QSize(1025,height)); + this->setMinimumSize(QSize(1025,height)); + //this->setMaximumSize(QSize(929, 270)); + //this->setMinimumSize(QSize(929, 270)); + + resize(minimumSize()); + adjustSize(); // main window + adjustSize(); + +} diff --git a/wfmain.h b/wfmain.h index c283a94..5f202b5 100644 --- a/wfmain.h +++ b/wfmain.h @@ -539,6 +539,10 @@ private slots: void on_audioSystemCombo_currentIndexChanged(int value); + void on_topLevelSlider_valueChanged(int value); + + void on_botLevelSlider_valueChanged(int value); + private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); @@ -654,6 +658,11 @@ private: unsigned int spectrumPlasmaSize = 64; bool drawPlasma = true; + double plotFloor = 0; + double plotCeiling = 160; + double wfFloor = 0; + double wfCeiling = 160; + QVector wfimage; unsigned int wfLengthMax; diff --git a/wfmain.ui b/wfmain.ui index 124a54b..67732e6 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -6,7 +6,7 @@ 0 0 - 940 + 1002 569 @@ -18,7 +18,7 @@ - 3 + 0 @@ -846,6 +846,114 @@ + + + + 0 + + + 0 + + + + + + 0 + 70 + + + + + 16777215 + 80 + + + + Sets the ceiling for the waterfall and spectrum displays + + + Ceiling for waterfall and spectrum display + + + 1 + + + 160 + + + 160 + + + Qt::Vertical + + + + + + + + 16777215 + 15 + + + + Top + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 70 + + + + + 16777215 + 80 + + + + 160 + + + Qt::Vertical + + + + + + + + 16777215 + 15 + + + + Bot + + + + + @@ -3790,7 +3898,7 @@ 0 0 - 940 + 1002 21 From 32d55ef490ed99296b21137ebc4d48f55dbdf2bb Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 16:34:26 -0700 Subject: [PATCH 315/323] Fixed spectrum line length --- wfmain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wfmain.cpp b/wfmain.cpp index 157f843..58891aa 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -3551,7 +3551,7 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e if((freq.MHzDouble < endFreq) && (freq.MHzDouble > startFreq)) { freqIndicatorLine->start->setCoords(freq.MHzDouble,0); - freqIndicatorLine->end->setCoords(freq.MHzDouble,160); + freqIndicatorLine->end->setCoords(freq.MHzDouble,rigCaps.spectAmpMax); } if(drawPeaks) { From 6ebb3b7680161275f8bd7e02acf686e566e0ad04 Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 18:41:35 -0700 Subject: [PATCH 316/323] Added some preferences for the plasma. --- wfmain.cpp | 132 +++++++++++++++++++++++++++++++++++++++++++++-------- wfmain.h | 18 +++++++- wfmain.ui | 113 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 241 insertions(+), 22 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 58891aa..5a2f477 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1050,6 +1050,25 @@ void wfmain::setUIToPrefs() on_drawPeakChk_clicked(prefs.drawPeaks); drawPeaks = prefs.drawPeaks; + underlayMode = prefs.underlayMode; + switch(underlayMode) + { + case underlayNone: + ui->underlayNone->setChecked(true); + break; + case underlayPeakHold: + ui->underlayPeakHold->setChecked(true); + break; + case underlayPeakBuffer: + ui->underlayPeakBuffer->setChecked(true); + break; + case underlayAverageBuffer: + ui->underlayAverageBuffer->setChecked(true); + break; + default: + break; + } + ui->wfAntiAliasChk->setChecked(prefs.wfAntiAlias); on_wfAntiAliasChk_clicked(prefs.wfAntiAlias); @@ -1059,6 +1078,12 @@ void wfmain::setUIToPrefs() ui->wfLengthSlider->setValue(prefs.wflength); prepareWf(prefs.wflength); + ui->topLevelSlider->setValue(prefs.plotCeiling); + ui->botLevelSlider->setValue(prefs.plotFloor); + + plot->yAxis->setRange(QCPRange(prefs.plotFloor, prefs.plotCeiling)); + colorMap->setDataRange(QCPRange(prefs.plotFloor, prefs.plotCeiling)); + ui->wfthemeCombo->setCurrentIndex(ui->wfthemeCombo->findData(prefs.wftheme)); colorMap->setGradient(static_cast(prefs.wftheme)); @@ -1250,6 +1275,7 @@ void wfmain::setDefPrefs() defPrefs.useDarkMode = true; defPrefs.useSystemTheme = false; defPrefs.drawPeaks = true; + defPrefs.underlayMode = underlayNone; defPrefs.wfAntiAlias = false; defPrefs.wfInterpolate = true; defPrefs.stylesheetPath = QString("qdarkstyle/style.qss"); @@ -1266,6 +1292,8 @@ void wfmain::setDefPrefs() defPrefs.localAFgain = 255; defPrefs.wflength = 160; defPrefs.wftheme = static_cast(QCPColorGradient::gpJet); + defPrefs.plotFloor = 0; + defPrefs.plotCeiling = 160; defPrefs.confirmExit = true; defPrefs.confirmPowerOff = true; defPrefs.meter2Type = meterNone; @@ -1293,7 +1321,14 @@ void wfmain::loadSettings() prefs.useDarkMode = settings->value("UseDarkMode", defPrefs.useDarkMode).toBool(); prefs.useSystemTheme = settings->value("UseSystemTheme", defPrefs.useSystemTheme).toBool(); prefs.wftheme = settings->value("WFTheme", defPrefs.wftheme).toInt(); + prefs.plotFloor = settings->value("plotFloor", defPrefs.plotFloor).toInt(); + prefs.plotCeiling = settings->value("plotCeiling", defPrefs.plotCeiling).toInt(); + plotFloor = prefs.plotFloor; + plotCeiling = prefs.plotCeiling; + wfFloor = prefs.plotFloor; + wfCeiling = prefs.plotCeiling; prefs.drawPeaks = settings->value("DrawPeaks", defPrefs.drawPeaks).toBool(); + prefs.underlayMode = static_cast(settings->value("underlayMode", defPrefs.underlayMode).toInt()); prefs.wfAntiAlias = settings->value("WFAntiAlias", defPrefs.wfAntiAlias).toBool(); prefs.wfInterpolate = settings->value("WFInterpolate", defPrefs.wfInterpolate).toBool(); prefs.wflength = (unsigned int)settings->value("WFLength", defPrefs.wflength).toInt(); @@ -1801,9 +1836,12 @@ void wfmain::saveSettings() settings->setValue("UseSystemTheme", prefs.useSystemTheme); settings->setValue("UseDarkMode", prefs.useDarkMode); settings->setValue("DrawPeaks", prefs.drawPeaks); + settings->setValue("underlayMode", prefs.underlayMode); settings->setValue("WFAntiAlias", prefs.wfAntiAlias); settings->setValue("WFInterpolate", prefs.wfInterpolate); settings->setValue("WFTheme", prefs.wftheme); + settings->setValue("plotFloor", prefs.plotFloor); + settings->setValue("plotCeiling", prefs.plotCeiling); settings->setValue("StylesheetPath", prefs.stylesheetPath); settings->setValue("splitter", ui->splitter->saveState()); settings->setValue("windowGeometry", saveGeometry()); @@ -3195,8 +3233,7 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) wfCeiling = rigCaps.spectAmpMax; plotCeiling = rigCaps.spectAmpMax; ui->topLevelSlider->setMaximum(rigCaps.spectAmpMax); - wfFloor = 0; - plotFloor = 0; + haveRigCaps = true; // Added so that server receives rig capabilities. emit sendRigCaps(rigCaps); @@ -3316,6 +3353,8 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) { ui->scopeBWCombo->addItem(rigCaps.scopeCenterSpans.at(i).name, (int)rigCaps.scopeCenterSpans.at(i).cstype); } + plot->yAxis->setRange(QCPRange(prefs.plotFloor, prefs.plotCeiling)); + colorMap->setDataRange(QCPRange(prefs.plotFloor, prefs.plotCeiling)); } else { ui->scopeBWCombo->setHidden(true); } @@ -3527,7 +3566,7 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e { //x[i] = (i * (endFreq-startFreq)/specLen) + startFreq; y[i] = (unsigned char)spectrum.at(i); - if(drawPeaks) + if(underlayMode == underlayPeakHold) { if((unsigned char)spectrum.at(i) > (unsigned char)spectrumPeaks.at(i)) { @@ -3553,13 +3592,16 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e freqIndicatorLine->start->setCoords(freq.MHzDouble,0); freqIndicatorLine->end->setCoords(freq.MHzDouble,rigCaps.spectAmpMax); } - if(drawPeaks) + if(underlayMode == underlayPeakHold) { plot->graph(1)->setData(x,y2); // peaks - } else if (drawPlasma) { + } else if (underlayMode != underlayNone) { computePlasma(); - plot->graph(1)->setData(x,spectrumPlasmaLine); // peaks + plot->graph(1)->setData(x,spectrumPlasmaLine); + } else { + plot->graph(1)->setData(x,y2); // peaks, but probably cleared out } + plot->yAxis->setRange(plotFloor, plotCeiling); plot->xAxis->setRange(startFreq, endFreq); plot->replot(); @@ -3590,8 +3632,7 @@ void wfmain::computePlasma() { spectrumPlasmaLine.clear(); spectrumPlasmaLine.resize(spectWidth); - bool averageMode = true; - if(averageMode) + if(underlayMode == underlayAverageBuffer) { for(int col=0; col < spectWidth; col++) { @@ -3602,7 +3643,7 @@ void wfmain::computePlasma() } spectrumPlasmaLine[col] = spectrumPlasmaLine[col] / spectrumPlasma.size(); } - } else { + } else if (underlayMode == underlayPeakBuffer){ // peak mode, running peak display for(int col=0; col < spectWidth; col++) { @@ -6033,6 +6074,7 @@ void wfmain::on_topLevelSlider_valueChanged(int value) { wfCeiling = value; plotCeiling = value; + prefs.plotCeiling = value; plot->yAxis->setRange(QCPRange(plotFloor, plotCeiling)); colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); } @@ -6041,10 +6083,59 @@ void wfmain::on_botLevelSlider_valueChanged(int value) { wfFloor = value; plotFloor = value; + prefs.plotFloor = value; plot->yAxis->setRange(QCPRange(plotFloor, plotCeiling)); colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); } +void wfmain::on_underlayBufferSlider_valueChanged(int value) +{ + // TODO: lock first... + spectrumPlasma.resize(value); +} + +void wfmain::on_underlayNone_toggled(bool checked) +{ + ui->underlayBufferSlider->setDisabled(checked); + if(checked) + { + underlayMode = underlayNone; + prefs.underlayMode = underlayMode; + on_clearPeakBtn_clicked(); + } +} + +void wfmain::on_underlayPeakHold_toggled(bool checked) +{ + ui->underlayBufferSlider->setDisabled(checked); + if(checked) + { + underlayMode = underlayPeakHold; + prefs.underlayMode = underlayMode; + on_clearPeakBtn_clicked(); + } + +} + +void wfmain::on_underlayPeakBuffer_toggled(bool checked) +{ + ui->underlayBufferSlider->setDisabled(!checked); + if(checked) + { + underlayMode = underlayPeakBuffer; + prefs.underlayMode = underlayMode; + } +} + +void wfmain::on_underlayAverageBuffer_toggled(bool checked) +{ + ui->underlayBufferSlider->setDisabled(!checked); + if(checked) + { + underlayMode = underlayAverageBuffer; + prefs.underlayMode = underlayMode; + } +} // --- DEBUG FUNCTION --- void wfmain::on_debugBtn_clicked() @@ -6056,17 +6147,20 @@ void wfmain::on_debugBtn_clicked() //setRadioTimeDatePrep(); //wf->setInteraction(QCP::iRangeZoom, true); //wf->setInteraction(QCP::iRangeDrag, true); - bool ok; - int height = QInputDialog::getInt(this, "wfview window fixed height", "number: ", 350, 1, 500, 1, &ok ); + plot->yAxis->setRange(QCPRange(plotFloor, plotCeiling)); + colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); - this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - this->setMaximumSize(QSize(1025,height)); - this->setMinimumSize(QSize(1025,height)); - //this->setMaximumSize(QSize(929, 270)); - //this->setMinimumSize(QSize(929, 270)); +// bool ok; +// int height = QInputDialog::getInt(this, "wfview window fixed height", "number: ", 350, 1, 500, 1, &ok ); - resize(minimumSize()); - adjustSize(); // main window - adjustSize(); +// this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); +// this->setMaximumSize(QSize(1025,height)); +// this->setMinimumSize(QSize(1025,height)); +// //this->setMaximumSize(QSize(929, 270)); +// //this->setMinimumSize(QSize(929, 270)); + +// resize(minimumSize()); +// adjustSize(); // main window +// adjustSize(); } diff --git a/wfmain.h b/wfmain.h index 5f202b5..5d3329c 100644 --- a/wfmain.h +++ b/wfmain.h @@ -543,6 +543,16 @@ private slots: void on_botLevelSlider_valueChanged(int value); + void on_underlayBufferSlider_valueChanged(int value); + + void on_underlayNone_toggled(bool checked); + + void on_underlayPeakHold_toggled(bool checked); + + void on_underlayPeakBuffer_toggled(bool checked); + + void on_underlayAverageBuffer_toggled(bool checked); + private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); @@ -652,10 +662,14 @@ private: quint16 wfLength; bool spectrumDrawLock; + enum underlay_t { underlayNone, underlayPeakHold, underlayPeakBuffer, underlayAverageBuffer }; + + QByteArray spectrumPeaks; QVector spectrumPlasmaLine; QVector spectrumPlasma; unsigned int spectrumPlasmaSize = 64; + underlay_t underlayMode = underlayNone; bool drawPlasma = true; double plotFloor = 0; @@ -770,6 +784,7 @@ private: bool useDarkMode; bool useSystemTheme; bool drawPeaks; + underlay_t underlayMode = underlayNone; bool wfAntiAlias; bool wfInterpolate; QString stylesheetPath; @@ -788,13 +803,14 @@ private: unsigned char localAFgain; unsigned int wflength; int wftheme; + int plotFloor; + int plotCeiling; bool confirmExit; bool confirmPowerOff; meterKind meter2Type; quint16 tcpPort; quint8 waterfallFormat; audioType audioSystem; - // plot scheme } prefs; preferences defPrefs; diff --git a/wfmain.ui b/wfmain.ui index 67732e6..2e0d107 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -18,7 +18,7 @@ - 0 + 3 @@ -2174,7 +2174,7 @@ - 0 + 1 @@ -2907,6 +2907,114 @@ + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Underlay Mode + + + + + + + None + + + true + + + underlayButtonGroup + + + + + + + Peak Hold + + + underlayButtonGroup + + + + + + + Peak + + + underlayButtonGroup + + + + + + + Average + + + underlayButtonGroup + + + + + + + Uneerlay Buffer Size: + + + + + + + + 100 + 16777215 + + + + 8 + + + 128 + + + 64 + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -3921,5 +4029,6 @@ + From 03a279087e2ea6c18f213f5668254c02bcac846e Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 18:46:00 -0700 Subject: [PATCH 317/323] Added underlayBufferSize to the preferences. --- wfmain.cpp | 7 +++++++ wfmain.h | 1 + 2 files changed, 8 insertions(+) diff --git a/wfmain.cpp b/wfmain.cpp index 5a2f477..3f6adb8 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1069,6 +1069,9 @@ void wfmain::setUIToPrefs() break; } + ui->underlayBufferSlider->setValue(prefs.underlayBufferSize); + on_underlayBufferSlider_valueChanged(prefs.underlayBufferSize); + ui->wfAntiAliasChk->setChecked(prefs.wfAntiAlias); on_wfAntiAliasChk_clicked(prefs.wfAntiAlias); @@ -1276,6 +1279,7 @@ void wfmain::setDefPrefs() defPrefs.useSystemTheme = false; defPrefs.drawPeaks = true; defPrefs.underlayMode = underlayNone; + defPrefs.underlayBufferSize = 64; defPrefs.wfAntiAlias = false; defPrefs.wfInterpolate = true; defPrefs.stylesheetPath = QString("qdarkstyle/style.qss"); @@ -1328,6 +1332,7 @@ void wfmain::loadSettings() wfFloor = prefs.plotFloor; wfCeiling = prefs.plotCeiling; prefs.drawPeaks = settings->value("DrawPeaks", defPrefs.drawPeaks).toBool(); + prefs.underlayBufferSize = settings->value("underlayBufferSize", defPrefs.underlayBufferSize).toInt(); prefs.underlayMode = static_cast(settings->value("underlayMode", defPrefs.underlayMode).toInt()); prefs.wfAntiAlias = settings->value("WFAntiAlias", defPrefs.wfAntiAlias).toBool(); prefs.wfInterpolate = settings->value("WFInterpolate", defPrefs.wfInterpolate).toBool(); @@ -1837,6 +1842,7 @@ void wfmain::saveSettings() settings->setValue("UseDarkMode", prefs.useDarkMode); settings->setValue("DrawPeaks", prefs.drawPeaks); settings->setValue("underlayMode", prefs.underlayMode); + settings->setValue("underlayBufferSize", prefs.underlayBufferSize); settings->setValue("WFAntiAlias", prefs.wfAntiAlias); settings->setValue("WFInterpolate", prefs.wfInterpolate); settings->setValue("WFTheme", prefs.wftheme); @@ -6092,6 +6098,7 @@ void wfmain::on_underlayBufferSlider_valueChanged(int value) { // TODO: lock first... spectrumPlasma.resize(value); + prefs.underlayBufferSize = value; } void wfmain::on_underlayNone_toggled(bool checked) diff --git a/wfmain.h b/wfmain.h index 5d3329c..d514950 100644 --- a/wfmain.h +++ b/wfmain.h @@ -785,6 +785,7 @@ private: bool useSystemTheme; bool drawPeaks; underlay_t underlayMode = underlayNone; + int underlayBufferSize = 64; bool wfAntiAlias; bool wfInterpolate; QString stylesheetPath; From 123084c7794000049413b07858c279ded7bbefb9 Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 19:58:04 -0700 Subject: [PATCH 318/323] Slightly faster plotting due to data being already sorted. --- wfmain.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 3f6adb8..6020a39 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -3592,7 +3592,7 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e if(!spectrumDrawLock) { //ui->qcp->addGraph(); - plot->graph(0)->setData(x,y); + plot->graph(0)->setData(x,y, true); if((freq.MHzDouble < endFreq) && (freq.MHzDouble > startFreq)) { freqIndicatorLine->start->setCoords(freq.MHzDouble,0); @@ -3600,12 +3600,12 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e } if(underlayMode == underlayPeakHold) { - plot->graph(1)->setData(x,y2); // peaks + plot->graph(1)->setData(x,y2, true); // peaks } else if (underlayMode != underlayNone) { computePlasma(); plot->graph(1)->setData(x,spectrumPlasmaLine); } else { - plot->graph(1)->setData(x,y2); // peaks, but probably cleared out + plot->graph(1)->setData(x,y2, true); // peaks, but probably cleared out } plot->yAxis->setRange(plotFloor, plotCeiling); From 5215984de82e912d7b6f6997752c63498f5a7596 Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 21:46:47 -0700 Subject: [PATCH 319/323] Minor change for slightly faster averages. --- wfmain.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 6020a39..94652c7 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -3536,12 +3536,13 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e if((startFreq != oldLowerFreq) || (endFreq != oldUpperFreq)) { // If the frequency changed and we were drawing peaks, now is the time to clearn them - if(drawPeaks) + if(underlayMode == underlayPeakHold) { // TODO: create non-button function to do this // This will break if the button is ever moved or renamed. on_clearPeakBtn_clicked(); } + // TODO: Add clear-out for the buffer } oldLowerFreq = startFreq; @@ -3638,22 +3639,22 @@ void wfmain::computePlasma() { spectrumPlasmaLine.clear(); spectrumPlasmaLine.resize(spectWidth); + int specPlasmaSize = spectrumPlasma.size(); if(underlayMode == underlayAverageBuffer) { for(int col=0; col < spectWidth; col++) { - for(int pos=0; pos < spectrumPlasma.size(); pos++) + for(int pos=0; pos < specPlasmaSize; pos++) { spectrumPlasmaLine[col] += spectrumPlasma[pos][col]; - } - spectrumPlasmaLine[col] = spectrumPlasmaLine[col] / spectrumPlasma.size(); + spectrumPlasmaLine[col] = spectrumPlasmaLine[col] / specPlasmaSize; } } else if (underlayMode == underlayPeakBuffer){ // peak mode, running peak display for(int col=0; col < spectWidth; col++) { - for(int pos=0; pos < spectrumPlasma.size(); pos++) + for(int pos=0; pos < specPlasmaSize; pos++) { if((double)(spectrumPlasma[pos][col]) > spectrumPlasmaLine[col]) spectrumPlasmaLine[col] = spectrumPlasma[pos][col]; From b2204b35193f45685491a70d44ddfe7479f935f1 Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 22:38:44 -0700 Subject: [PATCH 320/323] Fixed resize; added mutex. --- wfmain.cpp | 38 ++++++++++++++++++++++++++++++++++---- wfmain.h | 4 ++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 94652c7..45c6454 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -3581,10 +3581,12 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e } y2[i] = (unsigned char)spectrumPeaks.at(i); } - } + plasmaMutex.lock(); spectrumPlasma.push_front(spectrum); - spectrumPlasma.resize(spectrumPlasmaSize); + spectrumPlasma.pop_back(); + //spectrumPlasma.resize(spectrumPlasmaSize); + plasmaMutex.unlock(); // HACK DO NOT CHECK IN: drawPeaks = false; @@ -3637,6 +3639,7 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e void wfmain::computePlasma() { + plasmaMutex.lock(); spectrumPlasmaLine.clear(); spectrumPlasmaLine.resize(spectWidth); int specPlasmaSize = spectrumPlasma.size(); @@ -3661,6 +3664,8 @@ void wfmain::computePlasma() } } } + plasmaMutex.unlock(); + } void wfmain::receiveSpectrumMode(spectrumMode spectMode) @@ -6097,11 +6102,36 @@ void wfmain::on_botLevelSlider_valueChanged(int value) void wfmain::on_underlayBufferSlider_valueChanged(int value) { - // TODO: lock first... - spectrumPlasma.resize(value); + resizePlasmaBuffer(value); prefs.underlayBufferSize = value; } +void wfmain::resizePlasmaBuffer(int newSize) +{ + + QByteArray empty((int)spectWidth, '\x01'); + plasmaMutex.lock(); + + int oldSize = spectrumPlasma.size(); + + if(oldSize < newSize) + { + spectrumPlasma.resize(newSize); + for(int p=oldSize; p < newSize; p++) + { + spectrumPlasma.append(empty); + } + } else if (oldSize > newSize){ + for(int p = oldSize; p > newSize; p--) + { + spectrumPlasma.pop_back(); + } + } + + spectrumPlasma.squeeze(); + plasmaMutex.unlock(); +} + void wfmain::on_underlayNone_toggled(bool checked) { ui->underlayBufferSlider->setDisabled(checked); diff --git a/wfmain.h b/wfmain.h index d514950..6bafd06 100644 --- a/wfmain.h +++ b/wfmain.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "logcategories.h" #include "commhandler.h" @@ -671,6 +673,8 @@ private: unsigned int spectrumPlasmaSize = 64; underlay_t underlayMode = underlayNone; bool drawPlasma = true; + QMutex plasmaMutex; + void resizePlasmaBuffer(int newSize); double plotFloor = 0; double plotCeiling = 160; From 76398808612b7e77822d277b8c20c2fae8c749f4 Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 17 Aug 2022 23:30:02 -0700 Subject: [PATCH 321/323] Fixed issue with the floor and ceiling not updating initially. The waterfall is now ranged prior to replot each time. --- wfmain.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index 45c6454..bbdfe7d 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -3236,8 +3236,8 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) } setWindowTitle(rigCaps.modelName); this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true. - wfCeiling = rigCaps.spectAmpMax; - plotCeiling = rigCaps.spectAmpMax; + //wfCeiling = rigCaps.spectAmpMax; + //plotCeiling = rigCaps.spectAmpMax; ui->topLevelSlider->setMaximum(rigCaps.spectAmpMax); haveRigCaps = true; @@ -3611,7 +3611,7 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e plot->graph(1)->setData(x,y2, true); // peaks, but probably cleared out } - plot->yAxis->setRange(plotFloor, plotCeiling); + plot->yAxis->setRange(prefs.plotFloor, prefs.plotCeiling); plot->xAxis->setRange(startFreq, endFreq); plot->replot(); @@ -3629,10 +3629,10 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e colorMap->data()->setCell( col, row, wfimage.at(row).at(col)); } } - - wf->yAxis->setRange(0,wfLength - 1); - wf->xAxis->setRange(0, spectWidth-1); - wf->replot(); + colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); + wf->yAxis->setRange(0,wfLength - 1); + wf->xAxis->setRange(0, spectWidth-1); + wf->replot(); } } } From f1f58a10cf602f87e94dce703f1b1a38f6c883e6 Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Thu, 18 Aug 2022 09:56:06 -0700 Subject: [PATCH 322/323] Slight reduction in CPU usage with regards to wf, more to come. --- wfmain.cpp | 25 +++++++++++++++++++------ wfmain.h | 2 ++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/wfmain.cpp b/wfmain.cpp index bbdfe7d..c55d6cc 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -3533,6 +3533,9 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e return; } + QElapsedTimer performanceTimer; + bool updateRange = false; + if((startFreq != oldLowerFreq) || (endFreq != oldUpperFreq)) { // If the frequency changed and we were drawing peaks, now is the time to clearn them @@ -3594,6 +3597,9 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e if(!spectrumDrawLock) { + if((plotFloor != oldPlotFloor) || (plotCeiling != oldPlotCeiling)) + updateRange = true; + //ui->qcp->addGraph(); plot->graph(0)->setData(x,y, true); if((freq.MHzDouble < endFreq) && (freq.MHzDouble > startFreq)) @@ -3611,29 +3617,36 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e plot->graph(1)->setData(x,y2, true); // peaks, but probably cleared out } - plot->yAxis->setRange(prefs.plotFloor, prefs.plotCeiling); + if(updateRange) + plot->yAxis->setRange(prefs.plotFloor, prefs.plotCeiling); + plot->xAxis->setRange(startFreq, endFreq); plot->replot(); if(specLen == spectWidth) { wfimage.prepend(spectrum); - wfimage.resize(wfLengthMax); - wfimage.squeeze(); - + wfimage.pop_back(); + QByteArray wfRow; // Waterfall: for(int row = 0; row < wfLength; row++) { + wfRow = wfimage.at(row); for(int col = 0; col < spectWidth; col++) { - colorMap->data()->setCell( col, row, wfimage.at(row).at(col)); + colorMap->data()->setCell( col, row, wfRow.at(col)); } } - colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); + if(updateRange) + { + colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); + } wf->yAxis->setRange(0,wfLength - 1); wf->xAxis->setRange(0, spectWidth-1); wf->replot(); } + oldPlotFloor = plotFloor; + oldPlotCeiling = plotCeiling; } } diff --git a/wfmain.h b/wfmain.h index 6bafd06..55fc8d2 100644 --- a/wfmain.h +++ b/wfmain.h @@ -680,6 +680,8 @@ private: double plotCeiling = 160; double wfFloor = 0; double wfCeiling = 160; + double oldPlotFloor = -1; + double oldPlotCeiling = 999; QVector wfimage; unsigned int wfLengthMax; From 105675a128aaccb778e45953e75fb8fc6b938dc3 Mon Sep 17 00:00:00 2001 From: Roeland Jansen Date: Fri, 19 Aug 2022 08:12:22 +0200 Subject: [PATCH 323/323] modified version numbers, WHATSNEW and CHANGELOG --- CHANGELOG | 10 ++++++++++ WHATSNEW | 56 +++++++++++++++++++++------------------------------- wfserver.pro | 2 +- wfview.pro | 2 +- 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 25ba84e..a10f418 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,15 @@ # CHANGELOG +- 20220819 + + version bumped to 1.4 + temporary squashed logs; may redo later + audio fixes at exit + introduction of peak decays at the scope + resizing of top and bottom scope/waterfall + various fixes on the spectrum display + + - 20220414 this is a cherrypicked CHANGELOG add as there are loads of changes due to different diff --git a/WHATSNEW b/WHATSNEW index 75a34a8..270f5ae 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -1,43 +1,31 @@ The following highlights are in this 1.x-release: - many changes/mods/updates/enhancements to rigctld - rigctld box added in the UI - build process changed: you can add the install prefix (derSuessmann) - added "do not ask again" for switching off rig and exiting wfview - added opus as audio transport - dual meter support - rigctl basic split support - rigctl prevents switching off civ transceive - added 25 kHz step - as a temporary measure sending multiple TX/FREQ change commands to the rig - when we use rigctld. - people should use "fake it" in wsjtx as the split code is not reliable. - tidied up udp server function for better reliability - added some IC736 stuff - and that lowers the latency to maybe less than 50 ms - added PBT and IF-shift - several bugs fixed - RTS as PTT for several radios like the 706/718/736… -+ New major change is the audio transport mechanism. Lower latencies. -+ About box updated to include Patreon site -+ added 500 Hz step for VFO -+ Added clock and UTC toggle. -+ Added forced manual RTS setting -+ Add RIT function and other rigctl fixes -+ Adjusted window size for radios without spectrum. Thanks K5TUX. -+ Allow dynamic restarting of server -+ New Settings tab -+ Enable High DPI Scaling -+ More multi-radio support (mostly working!) -+ Split LAN waterfall data for N1MM+ Spectrum Scope support: There is a combobox that allows you to select + New major change is the audio transport mechanism. Lower latencies. + About box updated to include Patreon site + added 500 Hz step for VFO + Added clock and UTC toggle. + Added forced manual RTS setting + Add RIT function and other rigctl fixes + Adjusted window size for radios without spectrum. Thanks K5TUX. + Allow dynamic restarting of server + New Settings tab + Enable High DPI Scaling + More multi-radio support (mostly working!) + Split LAN waterfall data for N1MM+ Spectrum Scope support: There is a combobox that allows you to select split/combine (or default). Split only makes sense for LAN and Combine for USB. -+ added radio status display with meters for audio (speaker/mic) -+ selector for audio: QT, PortAudio, RealTime audio ( + added radio status display with meters for audio (speaker/mic) + selector for audio: QT, PortAudio, RealTime audio ++ version bumped to 1.4 -- rethinking of a new version schema that makes more sense ++ temporary squashed logs; may redo later ++ audio fixes at exit ++ introduction of peak decays at the scope ++ resizing of top and bottom scope/waterfall ++ various fixes on the spectrum display + Notes: - We know about high CPU usage on RPi. -- the audio stack has been rewritten multiple times to get the best out of it and have the lowest possible latencies. - (At least five times - you can choose now) +- try the audiostack you like the most/what works for you diff --git a/wfserver.pro b/wfserver.pro index 5a854ec..779a7ed 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -13,7 +13,7 @@ TEMPLATE = app CONFIG += console -DEFINES += WFVIEW_VERSION=\\\"1.2e\\\" +DEFINES += WFVIEW_VERSION=\\\"1.4\\\" DEFINES += BUILD_WFSERVER diff --git a/wfview.pro b/wfview.pro index c9cd8aa..4ba46d7 100644 --- a/wfview.pro +++ b/wfview.pro @@ -11,7 +11,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport TARGET = wfview TEMPLATE = app -DEFINES += WFVIEW_VERSION=\\\"1.2e\\\" +DEFINES += WFVIEW_VERSION=\\\"1.4\\\" DEFINES += BUILD_WFVIEW