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