kopia lustrzana https://gitlab.com/eliggett/wfview
Create rx audio packets with timestamp
Lots of other changes but if this works OK, I will update tx audio to use the same system.merge-requests/2/head
rodzic
6d8d1df45e
commit
27eb855adb
148
audiohandler.cpp
148
audiohandler.cpp
|
@ -735,7 +735,7 @@ audioHandler::audioHandler(QObject* parent) :
|
||||||
audioOutput(Q_NULLPTR),
|
audioOutput(Q_NULLPTR),
|
||||||
audioInput(Q_NULLPTR),
|
audioInput(Q_NULLPTR),
|
||||||
isUlaw(false),
|
isUlaw(false),
|
||||||
bufferSize(0),
|
latency(0),
|
||||||
isInput(0),
|
isInput(0),
|
||||||
volume(1.0f)
|
volume(1.0f)
|
||||||
{
|
{
|
||||||
|
@ -752,7 +752,7 @@ audioHandler::~audioHandler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 buffer, const bool ulaw, const bool isinput)
|
bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool ulaw, const bool isinput)
|
||||||
{
|
{
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -765,8 +765,8 @@ bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16
|
||||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||||
format.setSampleType(QAudioFormat::SignedInt);
|
format.setSampleType(QAudioFormat::SignedInt);
|
||||||
|
|
||||||
this->bufferSize = buffer;
|
this->latency = latency;
|
||||||
this->isUlaw = ulaw;
|
this->isUlaw = ulaw;
|
||||||
this->isInput = isinput;
|
this->isInput = isinput;
|
||||||
this->radioSampleBits = bits;
|
this->radioSampleBits = bits;
|
||||||
this->radioSampleRate = samplerate;
|
this->radioSampleRate = samplerate;
|
||||||
|
@ -831,7 +831,7 @@ void audioHandler::reinit()
|
||||||
if (audioInput != Q_NULLPTR)
|
if (audioInput != Q_NULLPTR)
|
||||||
delete audioInput;
|
delete audioInput;
|
||||||
audioInput = new QAudioInput(deviceInfo, format, this);
|
audioInput = new QAudioInput(deviceInfo, format, this);
|
||||||
//audioInput->setBufferSize(audioBuffer);
|
//audioInput->setLatency(audioBuffer);
|
||||||
//audioInput->setNotifyInterval(20);
|
//audioInput->setNotifyInterval(20);
|
||||||
|
|
||||||
connect(audioInput, SIGNAL(notify()), SLOT(notified()));
|
connect(audioInput, SIGNAL(notify()), SLOT(notified()));
|
||||||
|
@ -844,7 +844,7 @@ void audioHandler::reinit()
|
||||||
delete audioOutput;
|
delete audioOutput;
|
||||||
audioOutput = Q_NULLPTR;
|
audioOutput = Q_NULLPTR;
|
||||||
audioOutput = new QAudioOutput(deviceInfo, format, this);
|
audioOutput = new QAudioOutput(deviceInfo, format, this);
|
||||||
//audioOutput->setBufferSize(audioBuffer);
|
//audioOutput->setLatency(audioBuffer);
|
||||||
connect(audioOutput, SIGNAL(notify()), SLOT(notified()));
|
connect(audioOutput, SIGNAL(notify()), SLOT(notified()));
|
||||||
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
|
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
|
||||||
}
|
}
|
||||||
|
@ -899,8 +899,8 @@ void audioHandler::stop()
|
||||||
// Stop audio output
|
// Stop audio output
|
||||||
audioOutput->stop();
|
audioOutput->stop();
|
||||||
QByteArray ret;
|
QByteArray ret;
|
||||||
|
audioBuffer.clear();
|
||||||
buffer.clear();
|
//buffer.clear();
|
||||||
this->close();
|
this->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,47 +915,92 @@ void audioHandler::stop()
|
||||||
qint64 audioHandler::readData(char* data, qint64 maxlen)
|
qint64 audioHandler::readData(char* data, qint64 maxlen)
|
||||||
{
|
{
|
||||||
// Calculate output length, always full samples
|
// Calculate output length, always full samples
|
||||||
int outlen = 0;
|
int sentlen = 0;
|
||||||
if (radioSampleBits == 8)
|
|
||||||
{
|
// Get next packet from buffer.
|
||||||
// Input buffer is 8bit and output buffer is 16bit
|
if (!audioBuffer.isEmpty())
|
||||||
outlen = qMin(buffer.length(), (int)maxlen / 2);
|
{
|
||||||
for (int f = 0; f < outlen; f++)
|
|
||||||
{
|
// Sort the buffer by seq number.
|
||||||
if (isUlaw)
|
std::sort(audioBuffer.begin(), audioBuffer.end(),
|
||||||
qToLittleEndian<qint16>(ulaw_decode[(quint8)buffer.at(f)], data + (f * 2));
|
[](const AUDIOPACKET& a, const AUDIOPACKET& b) -> bool
|
||||||
else
|
{
|
||||||
qToLittleEndian<qint16>((qint16)(buffer[f] << 8) - 32640, data + (f * 2));
|
return a.seq < b.seq;
|
||||||
}
|
});
|
||||||
QMutexLocker locker(&mutex);
|
|
||||||
buffer.remove(0, outlen);
|
// Output buffer is ALWAYS 16 bit.
|
||||||
outlen = outlen * 2;
|
int divisor = 16 / radioSampleBits;
|
||||||
}
|
|
||||||
else if (radioSampleBits == 16) {
|
auto packet = audioBuffer.begin();
|
||||||
// Just copy it
|
while (packet != audioBuffer.end() && sentlen<maxlen)
|
||||||
outlen = qMin(buffer.length(), (int)maxlen);
|
{
|
||||||
if (outlen % 2 != 0) {
|
if (packet->time.msecsTo(QTime::currentTime()) > latency) {
|
||||||
outlen += 1; // Not sure if this is necessary as we should always have an even number!
|
qDebug(logAudio()) << "Packet " << hex << packet->seq << "is too late, deleting" << dec << packet->time.msecsTo(QTime::currentTime()) << "ms";
|
||||||
}
|
QMutexLocker locker(&mutex);
|
||||||
memcpy(data, buffer.data(), outlen);
|
packet=audioBuffer.erase(packet); // returns next packet
|
||||||
QMutexLocker locker(&mutex);
|
}
|
||||||
buffer.remove(0, outlen);
|
else
|
||||||
}
|
{
|
||||||
else {
|
// Will this packet fit in the current buffer?
|
||||||
qDebug(logAudio()) << "Sample bits MUST be 8 or 16 - got: " << radioSampleBits; // Should never happen?
|
int send = qMin((int)((maxlen/divisor) - (sentlen/divisor)), packet->data.length() - packet->sent);
|
||||||
}
|
|
||||||
return outlen;
|
if (divisor == 2)
|
||||||
|
{
|
||||||
|
// Input buffer is 8bit and output buffer is 16bit
|
||||||
|
for (int f = 0; f < send; f++)
|
||||||
|
{
|
||||||
|
if (isUlaw)
|
||||||
|
qToLittleEndian<qint16>(ulaw_decode[(quint8)packet->data[f+packet->sent]], data + (f * 2 + sentlen));
|
||||||
|
else
|
||||||
|
qToLittleEndian<qint16>((qint16)(packet->data[f+packet->sent] << 8) - 32640, data + (f * 2 + sentlen));
|
||||||
|
}
|
||||||
|
sentlen = sentlen + (send*divisor);
|
||||||
|
}
|
||||||
|
else if (divisor == 1)
|
||||||
|
{
|
||||||
|
// 16 bit audio so just copy it in place.
|
||||||
|
//qDebug(logAudio()) << "Adding packet to buffer:" << (*packet).seq << ": " << (*packet).data.length()-(*packet).sent;
|
||||||
|
memcpy(data+sentlen, packet->data.constData()+packet->sent, send);
|
||||||
|
sentlen = sentlen + (send*divisor);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//qDebug(logAudio()) << "Invalid number of bits in audio " << radioSampleBits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send == packet->data.length())
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex);
|
||||||
|
packet = audioBuffer.erase(packet); // returns next packet
|
||||||
|
if (maxlen - sentlen == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (send == 0)
|
||||||
|
{
|
||||||
|
// We have no more space or no packets so just break.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We ended-up with a partial packet left so add it to the buffer and store where we left off.
|
||||||
|
packet->sent = send;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//qDebug(logAudio()) << "Returning: " << sentlen << " max: " << maxlen;
|
||||||
|
|
||||||
|
return sentlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 audioHandler::writeData(const char* data, qint64 len)
|
qint64 audioHandler::writeData(const char* data, qint64 len)
|
||||||
{
|
{
|
||||||
|
|
||||||
QMutexLocker locker(&mutex);
|
QMutexLocker locker(&mutex);
|
||||||
if (buffer.length() > bufferSize * 4)
|
|
||||||
{
|
|
||||||
qWarning() << "writeData() Buffer overflow";
|
|
||||||
buffer.clear(); // Will cause a click!
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isUlaw) {
|
if (isUlaw) {
|
||||||
for (int f = 0; f < len / 2; f++)
|
for (int f = 0; f < len / 2; f++)
|
||||||
|
@ -1000,8 +1045,6 @@ void audioHandler::notified()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void audioHandler::stateChanged(QAudio::State state)
|
void audioHandler::stateChanged(QAudio::State state)
|
||||||
{
|
{
|
||||||
if (state == QAudio::IdleState && audioOutput->error() == QAudio::UnderrunError) {
|
if (state == QAudio::IdleState && audioOutput->error() == QAudio::UnderrunError) {
|
||||||
|
@ -1016,12 +1059,11 @@ void audioHandler::stateChanged(QAudio::State state)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void audioHandler::incomingAudio(const QByteArray& data)
|
void audioHandler::incomingAudio(const AUDIOPACKET data)
|
||||||
{
|
{
|
||||||
//qDebug(logAudio()) << "Got " << data.length() << " samples";
|
|
||||||
if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) {
|
if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) {
|
||||||
QMutexLocker locker(&mutex);
|
QMutexLocker locker(&mutex);
|
||||||
buffer.append(data);
|
audioBuffer.push_back(data);
|
||||||
// Restart playback
|
// Restart playback
|
||||||
if (audioOutput->state() == QAudio::SuspendedState)
|
if (audioOutput->state() == QAudio::SuspendedState)
|
||||||
{
|
{
|
||||||
|
@ -1031,15 +1073,15 @@ void audioHandler::incomingAudio(const QByteArray& data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void audioHandler::changeBufferSize(const quint16 newSize)
|
void audioHandler::changeLatency(const quint16 newSize)
|
||||||
{
|
{
|
||||||
qDebug(logAudio()) << this->metaObject()->className() << ": Changing buffer size to: " << newSize << " from " << bufferSize;
|
qDebug(logAudio()) << this->metaObject()->className() << ": Changing latency to: " << newSize << " from " << latency;
|
||||||
bufferSize = newSize;
|
latency = newSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audioHandler::getBufferSize()
|
void audioHandler::getLatency()
|
||||||
{
|
{
|
||||||
emit sendBufferSize(audioOutput->bufferSize());
|
emit sendLatency(latency);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audioHandler::isChunkAvailable()
|
bool audioHandler::isChunkAvailable()
|
||||||
|
|
|
@ -14,20 +14,31 @@
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QTime>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
//#define BUFFER_SIZE (32*1024)
|
//#define BUFFER_SIZE (32*1024)
|
||||||
|
|
||||||
|
|
||||||
|
struct AUDIOPACKET {
|
||||||
|
quint16 seq;
|
||||||
|
QTime time;
|
||||||
|
quint16 sent;
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class audioHandler : public QIODevice
|
class audioHandler : public QIODevice
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
audioHandler(QObject* parent = 0);
|
audioHandler(QObject* parent = 0);
|
||||||
~audioHandler();
|
~audioHandler();
|
||||||
|
|
||||||
void getBufferSize();
|
void getLatency();
|
||||||
|
|
||||||
bool setDevice(QAudioDeviceInfo deviceInfo);
|
bool setDevice(QAudioDeviceInfo deviceInfo);
|
||||||
|
|
||||||
|
@ -43,9 +54,9 @@ public:
|
||||||
void getNextAudioChunk(QByteArray &data);
|
void getNextAudioChunk(QByteArray &data);
|
||||||
bool isChunkAvailable();
|
bool isChunkAvailable();
|
||||||
public slots:
|
public slots:
|
||||||
bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 bufferSize, const bool isulaw, const bool isinput);
|
bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isulaw, const bool isinput);
|
||||||
void incomingAudio(const QByteArray& data);
|
void incomingAudio(const AUDIOPACKET data);
|
||||||
void changeBufferSize(const quint16 newSize);
|
void changeLatency(const quint16 newSize);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void notified();
|
void notified();
|
||||||
|
@ -53,7 +64,7 @@ private slots:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void audioMessage(QString message);
|
void audioMessage(QString message);
|
||||||
void sendBufferSize(quint16 newSize);
|
void sendLatency(quint16 newSize);
|
||||||
void haveAudioData(const QByteArray& data);
|
void haveAudioData(const QByteArray& data);
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,7 +78,7 @@ private:
|
||||||
QAudioOutput* audioOutput;
|
QAudioOutput* audioOutput;
|
||||||
QAudioInput* audioInput;
|
QAudioInput* audioInput;
|
||||||
bool isUlaw;
|
bool isUlaw;
|
||||||
int bufferSize;
|
quint16 latency;
|
||||||
bool isInput; // Used to determine whether input or output audio
|
bool isInput; // Used to determine whether input or output audio
|
||||||
float volume;
|
float volume;
|
||||||
|
|
||||||
|
@ -76,8 +87,7 @@ private:
|
||||||
QAudioDeviceInfo deviceInfo;
|
QAudioDeviceInfo deviceInfo;
|
||||||
quint16 radioSampleRate;
|
quint16 radioSampleRate;
|
||||||
quint8 radioSampleBits;
|
quint8 radioSampleBits;
|
||||||
|
QVector<AUDIOPACKET> audioBuffer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AUDIOHANDLER_H
|
#endif // AUDIOHANDLER_H
|
||||||
|
|
|
@ -64,7 +64,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu
|
||||||
}
|
}
|
||||||
|
|
||||||
void rigCommander::commSetup(unsigned char rigCivAddr, QString ip, quint16 cport, quint16 sport, quint16 aport,
|
void rigCommander::commSetup(unsigned char rigCivAddr, QString ip, quint16 cport, quint16 sport, quint16 aport,
|
||||||
QString username, QString password, quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec)
|
QString username, QString password, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec)
|
||||||
{
|
{
|
||||||
// construct
|
// construct
|
||||||
// TODO: Bring this parameter and the comm port from the UI.
|
// TODO: Bring this parameter and the comm port from the UI.
|
||||||
|
@ -88,7 +88,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString ip, quint16 cport
|
||||||
*/
|
*/
|
||||||
if (udp == Q_NULLPTR) {
|
if (udp == Q_NULLPTR) {
|
||||||
|
|
||||||
udp = new udpHandler(ip, cport, sport, aport, username, password, buffer, rxsample, rxcodec, txsample, txcodec);
|
udp = new udpHandler(ip, cport, sport, aport, username, password, rxlatency, txlatency, rxsample, rxcodec, txsample, txcodec);
|
||||||
|
|
||||||
udpHandlerThread = new QThread(this);
|
udpHandlerThread = new QThread(this);
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString ip, quint16 cport
|
||||||
|
|
||||||
// data from the program to the comm port:
|
// data from the program to the comm port:
|
||||||
connect(this, SIGNAL(dataForComm(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray)));
|
connect(this, SIGNAL(dataForComm(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray)));
|
||||||
connect(this, SIGNAL(haveChangeBufferSize(quint16)), udp, SLOT(changeBufferSize(quint16)));
|
connect(this, SIGNAL(haveChangeLatency(quint16)), udp, SLOT(changeLatency(quint16)));
|
||||||
|
|
||||||
// Connect for errors/alerts
|
// Connect for errors/alerts
|
||||||
connect(udp, SIGNAL(haveNetworkError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString)));
|
connect(udp, SIGNAL(haveNetworkError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString)));
|
||||||
|
@ -2480,9 +2480,9 @@ void rigCommander::getRigID()
|
||||||
prepDataAndSend(payload);
|
prepDataAndSend(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rigCommander::changeBufferSize(const quint16 value)
|
void rigCommander::changeLatency(const quint16 value)
|
||||||
{
|
{
|
||||||
emit haveChangeBufferSize(value);
|
emit haveChangeLatency(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rigCommander::sayAll()
|
void rigCommander::sayAll()
|
||||||
|
|
|
@ -48,7 +48,7 @@ public slots:
|
||||||
void process();
|
void process();
|
||||||
void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate);
|
void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate);
|
||||||
void commSetup(unsigned char rigCivAddr, QString ip, quint16 cport, quint16 sport, quint16 aport,
|
void commSetup(unsigned char rigCivAddr, QString ip, quint16 cport, quint16 sport, quint16 aport,
|
||||||
QString username, QString password, quint16 buffer, quint16 rxsample, quint8 rxcodec,quint16 txsample, quint8 txcodec);
|
QString username, QString password, quint16 rxlatency,quint16 txlatency, quint16 rxsample, quint8 rxcodec,quint16 txsample, quint8 txcodec);
|
||||||
void closeComm();
|
void closeComm();
|
||||||
|
|
||||||
void enableSpectOutput();
|
void enableSpectOutput();
|
||||||
|
@ -139,7 +139,7 @@ public slots:
|
||||||
void handleNewData(const QByteArray &data);
|
void handleNewData(const QByteArray &data);
|
||||||
void handleSerialPortError(const QString port, const QString errorText);
|
void handleSerialPortError(const QString port, const QString errorText);
|
||||||
void handleStatusUpdate(const QString text);
|
void handleStatusUpdate(const QString text);
|
||||||
void changeBufferSize(const quint16 value);
|
void changeLatency(const quint16 value);
|
||||||
void sayFrequency();
|
void sayFrequency();
|
||||||
void sayMode();
|
void sayMode();
|
||||||
void sayAll();
|
void sayAll();
|
||||||
|
@ -197,7 +197,7 @@ signals:
|
||||||
void finished();
|
void finished();
|
||||||
void havePTTStatus(bool pttOn);
|
void havePTTStatus(bool pttOn);
|
||||||
void haveATUStatus(unsigned char status);
|
void haveATUStatus(unsigned char status);
|
||||||
void haveChangeBufferSize(quint16 value);
|
void haveChangeLatency(quint16 value);
|
||||||
void haveDataForServer(QByteArray outData);
|
void haveDataForServer(QByteArray outData);
|
||||||
void initUdpHandler();
|
void initUdpHandler();
|
||||||
|
|
||||||
|
|
247
udphandler.cpp
247
udphandler.cpp
|
@ -4,7 +4,7 @@
|
||||||
#include "udphandler.h"
|
#include "udphandler.h"
|
||||||
#include "logcategories.h"
|
#include "logcategories.h"
|
||||||
udpHandler::udpHandler(QString ip, quint16 controlPort, quint16 civPort, quint16 audioPort, QString username, QString password,
|
udpHandler::udpHandler(QString ip, quint16 controlPort, quint16 civPort, quint16 audioPort, QString username, QString password,
|
||||||
quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec) :
|
quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec) :
|
||||||
controlPort(controlPort),
|
controlPort(controlPort),
|
||||||
civPort(civPort),
|
civPort(civPort),
|
||||||
audioPort(audioPort)
|
audioPort(audioPort)
|
||||||
|
@ -13,13 +13,14 @@ udpHandler::udpHandler(QString ip, quint16 controlPort, quint16 civPort, quint16
|
||||||
this->port = this->controlPort;
|
this->port = this->controlPort;
|
||||||
this->username = username;
|
this->username = username;
|
||||||
this->password = password;
|
this->password = password;
|
||||||
this->rxBufferSize = buffer;
|
this->rxLatency = rxlatency;
|
||||||
|
this->txLatency = txlatency;
|
||||||
this->rxSampleRate = rxsample;
|
this->rxSampleRate = rxsample;
|
||||||
this->txSampleRate = txsample;
|
this->txSampleRate = txsample;
|
||||||
this->rxCodec = rxcodec;
|
this->rxCodec = rxcodec;
|
||||||
this->txCodec = txcodec;
|
this->txCodec = txcodec;
|
||||||
|
|
||||||
qDebug(logUdp()) << "Starting udpHandler user:" << username << " buffer:" << buffer << " rx sample rate: " << rxsample <<
|
qDebug(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxLatency << " tx latency:" << txLatency << " rx sample rate: " << rxsample <<
|
||||||
" rx codec: " << rxcodec << " tx sample rate: " << txsample << " tx codec: " << txcodec;
|
" rx codec: " << rxcodec << " tx sample rate: " << txsample << " tx codec: " << txcodec;
|
||||||
|
|
||||||
// Try to set the IP address, if it is a hostname then perform a DNS lookup.
|
// Try to set the IP address, if it is a hostname then perform a DNS lookup.
|
||||||
|
@ -110,9 +111,9 @@ udpHandler::~udpHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void udpHandler::changeBufferSize(quint16 value)
|
void udpHandler::changeLatency(quint16 value)
|
||||||
{
|
{
|
||||||
emit haveChangeBufferSize(value);
|
emit haveChangeLatency(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void udpHandler::receiveFromCivStream(QByteArray data)
|
void udpHandler::receiveFromCivStream(QByteArray data)
|
||||||
|
@ -291,10 +292,10 @@ void udpHandler::dataReceived()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
civ = new udpCivData(localIP, radioIP, civPort);
|
civ = new udpCivData(localIP, radioIP, civPort);
|
||||||
audio = new udpAudio(localIP, radioIP, audioPort, rxBufferSize, rxSampleRate, rxCodec, txSampleRate, txCodec);
|
audio = new udpAudio(localIP, radioIP, audioPort, rxLatency, txLatency, rxSampleRate, rxCodec, txSampleRate, txCodec);
|
||||||
|
|
||||||
QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray)));
|
QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray)));
|
||||||
QObject::connect(this, SIGNAL(haveChangeBufferSize(quint16)), audio, SLOT(changeBufferSize(quint16)));
|
QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16)));
|
||||||
|
|
||||||
|
|
||||||
streamOpened = true;
|
streamOpened = true;
|
||||||
|
@ -360,7 +361,6 @@ void udpHandler::sendRequestStream()
|
||||||
|
|
||||||
QByteArray usernameEncoded;
|
QByteArray usernameEncoded;
|
||||||
passcode(username, usernameEncoded);
|
passcode(username, usernameEncoded);
|
||||||
int txSeqBufLengthMs = 300;
|
|
||||||
|
|
||||||
conninfo_packet p;
|
conninfo_packet p;
|
||||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
|
@ -385,7 +385,7 @@ void udpHandler::sendRequestStream()
|
||||||
p.txsample = qToBigEndian((quint32)txSampleRate);
|
p.txsample = qToBigEndian((quint32)txSampleRate);
|
||||||
p.civport = qToBigEndian((quint32)civPort);
|
p.civport = qToBigEndian((quint32)civPort);
|
||||||
p.audioport = qToBigEndian((quint32)audioPort);
|
p.audioport = qToBigEndian((quint32)audioPort);
|
||||||
p.txbuffer = qToBigEndian((quint32)txSeqBufLengthMs);
|
p.txbuffer = qToBigEndian((quint32)txLatency);
|
||||||
|
|
||||||
authInnerSendSeq++;
|
authInnerSendSeq++;
|
||||||
|
|
||||||
|
@ -631,13 +631,14 @@ void udpCivData::dataReceived()
|
||||||
|
|
||||||
|
|
||||||
// Audio stream
|
// Audio stream
|
||||||
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec)
|
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec)
|
||||||
{
|
{
|
||||||
qDebug(logUdp()) << "Starting udpAudio";
|
qDebug(logUdp()) << "Starting udpAudio";
|
||||||
this->localIP = local;
|
this->localIP = local;
|
||||||
this->port = audioPort;
|
this->port = audioPort;
|
||||||
this->radioIP = ip;
|
this->radioIP = ip;
|
||||||
this->bufferSize = buffer;
|
this->rxLatency = rxlatency;
|
||||||
|
this->txLatency = txlatency;
|
||||||
this->rxSampleRate = rxsample;
|
this->rxSampleRate = rxsample;
|
||||||
this->txSampleRate = txsample;
|
this->txSampleRate = txsample;
|
||||||
this->rxCodec = rxcodec;
|
this->rxCodec = rxcodec;
|
||||||
|
@ -674,9 +675,10 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
||||||
rxaudio->moveToThread(rxAudioThread);
|
rxaudio->moveToThread(rxAudioThread);
|
||||||
|
|
||||||
connect(this, SIGNAL(setupRxAudio(quint8, quint8, quint16, quint16, bool, bool)), rxaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool)));
|
connect(this, SIGNAL(setupRxAudio(quint8, quint8, quint16, quint16, bool, bool)), rxaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool)));
|
||||||
connect(this, SIGNAL(haveAudioData(QByteArray)), rxaudio, SLOT(incomingAudio(QByteArray)));
|
|
||||||
connect(this, SIGNAL(haveChangeBufferSize(quint16)), rxaudio, SLOT(changeBufferSize(quint16)));
|
qRegisterMetaType<AUDIOPACKET>();
|
||||||
connect(this, SIGNAL(haveChangeBufferSize(quint16)), rxaudio, SLOT(changeBufferSize(quint16)));
|
connect(this, SIGNAL(haveAudioData(AUDIOPACKET)), rxaudio, SLOT(incomingAudio(AUDIOPACKET)));
|
||||||
|
connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16)));
|
||||||
connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater()));
|
connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater()));
|
||||||
|
|
||||||
if (txCodec == 0x01)
|
if (txCodec == 0x01)
|
||||||
|
@ -692,7 +694,6 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
||||||
txaudio->moveToThread(txAudioThread);
|
txaudio->moveToThread(txAudioThread);
|
||||||
|
|
||||||
connect(this, SIGNAL(setupTxAudio(quint8, quint8, quint16, quint16, bool, bool)), txaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool)));
|
connect(this, SIGNAL(setupTxAudio(quint8, quint8, quint16, quint16, bool, bool)), txaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool)));
|
||||||
//connect(txaudio, SIGNAL(haveAudioData(QByteArray)), this, SLOT(sendTxAudio(QByteArray)));
|
|
||||||
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
|
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
|
||||||
|
|
||||||
rxAudioThread->start();
|
rxAudioThread->start();
|
||||||
|
@ -705,8 +706,8 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
||||||
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
|
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
|
||||||
pingTimer->start(PING_PERIOD); // send ping packets every 100ms
|
pingTimer->start(PING_PERIOD); // send ping packets every 100ms
|
||||||
|
|
||||||
emit setupTxAudio(txNumSamples, txChannelCount, txSampleRate, bufferSize, txIsUlawCodec, true);
|
emit setupTxAudio(txNumSamples, txChannelCount, txSampleRate, txLatency, txIsUlawCodec, true);
|
||||||
emit setupRxAudio(rxNumSamples, rxChannelCount, rxSampleRate, bufferSize, rxIsUlawCodec, false);
|
emit setupRxAudio(rxNumSamples, rxChannelCount, rxSampleRate, txLatency, rxIsUlawCodec, false);
|
||||||
|
|
||||||
watchdogTimer = new QTimer();
|
watchdogTimer = new QTimer();
|
||||||
connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog);
|
connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog);
|
||||||
|
@ -719,9 +720,6 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
||||||
areYouThereTimer = new QTimer();
|
areYouThereTimer = new QTimer();
|
||||||
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
|
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
|
||||||
areYouThereTimer->start(AREYOUTHERE_PERIOD);
|
areYouThereTimer->start(AREYOUTHERE_PERIOD);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
udpAudio::~udpAudio()
|
udpAudio::~udpAudio()
|
||||||
|
@ -793,9 +791,9 @@ void udpAudio::sendTxAudio()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void udpAudio::changeBufferSize(quint16 value)
|
void udpAudio::changeLatency(quint16 value)
|
||||||
{
|
{
|
||||||
emit haveChangeBufferSize(value);
|
emit haveChangeLatency(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -836,14 +834,20 @@ void udpAudio::dataReceived()
|
||||||
r.mid(0, 2) == QByteArrayLiteral("\x70\x04"))
|
r.mid(0, 2) == QByteArrayLiteral("\x70\x04"))
|
||||||
{
|
{
|
||||||
lastReceived = QTime::currentTime();
|
lastReceived = QTime::currentTime();
|
||||||
emit haveAudioData(r.mid(24));
|
AUDIOPACKET tempAudio;
|
||||||
|
tempAudio.seq = in->seq;
|
||||||
|
tempAudio.time = lastReceived;
|
||||||
|
tempAudio.sent = 0;
|
||||||
|
tempAudio.data = r.mid(24);
|
||||||
|
emit haveAudioData(tempAudio);
|
||||||
|
//rxaudio->incomingAudio(tempAudio);
|
||||||
//rxaudio->incomingAudio(r.mid(24));
|
//rxaudio->incomingAudio(r.mid(24));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
udpBase::dataReceived(r); // Call parent function to process the rest.
|
udpBase::dataReceived(r); // Call parent function to process the rest.
|
||||||
r.clear();
|
r.clear();
|
||||||
datagram.clear();
|
datagram.clear();
|
||||||
|
@ -861,6 +865,11 @@ void udpBase::init()
|
||||||
qDebug(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port;
|
qDebug(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port;
|
||||||
uint32_t addr = localIP.toIPv4Address();
|
uint32_t addr = localIP.toIPv4Address();
|
||||||
myId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (localPort & 0xffff);
|
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()
|
udpBase::~udpBase()
|
||||||
|
@ -887,10 +896,16 @@ udpBase::~udpBase()
|
||||||
idleTimer->stop();
|
idleTimer->stop();
|
||||||
delete idleTimer;
|
delete idleTimer;
|
||||||
}
|
}
|
||||||
|
if (retransmitTimer != Q_NULLPTR)
|
||||||
|
{
|
||||||
|
retransmitTimer->stop();
|
||||||
|
delete retransmitTimer;
|
||||||
|
}
|
||||||
|
|
||||||
pingTimer = Q_NULLPTR;
|
pingTimer = Q_NULLPTR;
|
||||||
idleTimer = Q_NULLPTR;
|
idleTimer = Q_NULLPTR;
|
||||||
areYouThereTimer = Q_NULLPTR;
|
areYouThereTimer = Q_NULLPTR;
|
||||||
|
retransmitTimer = Q_NULLPTR;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1023,13 +1038,13 @@ void udpBase::dataReceived(QByteArray r)
|
||||||
packetsLost++;
|
packetsLost++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
if (in->len != PING_SIZE && in->type == 0x00 && in->seq != 0x00)
|
else if (in->len != PING_SIZE && in->type == 0x00 && in->seq != 0x00)
|
||||||
{
|
{
|
||||||
if (rxSeqBuf.isEmpty()) {
|
if (rxSeqBuf.isEmpty()) {
|
||||||
rxSeqBuf.append(in->seq);
|
rxSeqBuf.append(in->seq);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::sort(rxSeqBuf.begin(), rxSeqBuf.end());
|
std::sort(rxSeqBuf.begin(), rxSeqBuf.end());
|
||||||
if (in->seq < rxSeqBuf.front())
|
if (in->seq < rxSeqBuf.front())
|
||||||
|
@ -1038,11 +1053,12 @@ void udpBase::dataReceived(QByteArray r)
|
||||||
|
|
||||||
// Looks like it has rolled over so clear buffer and start again.
|
// Looks like it has rolled over so clear buffer and start again.
|
||||||
rxSeqBuf.clear();
|
rxSeqBuf.clear();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rxSeqBuf.contains(in->seq))
|
if (!rxSeqBuf.contains(in->seq))
|
||||||
{
|
{
|
||||||
// Add incoming packet to the received buffer and if it is in the mising buffer, remove it.
|
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
|
||||||
rxSeqBuf.append(in->seq);
|
rxSeqBuf.append(in->seq);
|
||||||
// Check whether this is one of our missing ones!
|
// Check whether this is one of our missing ones!
|
||||||
auto s = std::find_if(rxMissing.begin(), rxMissing.end(), [&cs = in->seq](SEQBUFENTRY& s) { return s.seqNum == cs; });
|
auto s = std::find_if(rxMissing.begin(), rxMissing.end(), [&cs = in->seq](SEQBUFENTRY& s) { return s.seqNum == cs; });
|
||||||
|
@ -1051,95 +1067,104 @@ void udpBase::dataReceived(QByteArray r)
|
||||||
qDebug(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << hex << in->seq;
|
qDebug(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << hex << in->seq;
|
||||||
s = rxMissing.erase(s);
|
s = rxMissing.erase(s);
|
||||||
}
|
}
|
||||||
std::sort(rxSeqBuf.begin(), rxSeqBuf.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find all gaps in received packets
|
|
||||||
|
|
||||||
QByteArray missingSeqs;
|
|
||||||
|
|
||||||
auto i = std::adjacent_find(rxSeqBuf.begin(), rxSeqBuf.end(), [](quint16 l, quint16 r) {return l + 1 < r; });
|
|
||||||
while (i != rxSeqBuf.end())
|
|
||||||
{
|
|
||||||
if (i + 1 != rxSeqBuf.end())
|
|
||||||
{
|
|
||||||
if (*(i + 1) - *i < 30)
|
|
||||||
{
|
|
||||||
for (quint16 j = *i + 1; j < *(i + 1); j++)
|
|
||||||
{
|
|
||||||
//qDebug(logUdp()) << this->metaObject()->className() << ": Found missing seq between " << *i << " : " << *(i + 1) << " (" << j << ")";
|
|
||||||
auto s = std::find_if(rxMissing.begin(), rxMissing.end(), [&cs = j](SEQBUFENTRY& s) { return s.seqNum == cs; });
|
|
||||||
if (s == rxMissing.end())
|
|
||||||
{
|
|
||||||
// We haven't seen this missing packet before
|
|
||||||
//qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len="<< rxMissing.length() << "): " << j;
|
|
||||||
SEQBUFENTRY b;
|
|
||||||
b.seqNum = j;
|
|
||||||
b.retransmitCount = 0;
|
|
||||||
b.timeSent = QTime::currentTime();
|
|
||||||
rxMissing.append(b);
|
|
||||||
packetsLost++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (s->retransmitCount == 10)
|
|
||||||
{
|
|
||||||
// We have tried 10 times to request this packet, time to give up!
|
|
||||||
s = rxMissing.erase(s);
|
|
||||||
rxSeqBuf.append(j); // Final thing is to add to received buffer!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qDebug(logUdp()) << this->metaObject()->className() << ": Too many missing, flushing buffers";
|
|
||||||
rxSeqBuf.clear();
|
|
||||||
missingSeqs.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (auto it = rxMissing.begin(); it != rxMissing.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->retransmitCount < 10)
|
|
||||||
{
|
|
||||||
missingSeqs.append(it->seqNum & 0xff);
|
|
||||||
missingSeqs.append(it->seqNum >> 8 & 0xff);
|
|
||||||
missingSeqs.append(it->seqNum & 0xff);
|
|
||||||
missingSeqs.append(it->seqNum >> 8 & 0xff);
|
|
||||||
it->retransmitCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (missingSeqs.length() != 0)
|
|
||||||
{
|
|
||||||
control_packet p;
|
|
||||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
|
||||||
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) ;
|
|
||||||
qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq;
|
|
||||||
QMutexLocker locker(&mutex);
|
|
||||||
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qDebug(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " <<missingSeqs.toHex();
|
|
||||||
missingSeqs.insert(0, p.packet, sizeof(p.packet));
|
|
||||||
QMutexLocker locker(&mutex);
|
|
||||||
udp->writeDatagram(missingSeqs, radioIP, port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
QByteArray missingSeqs;
|
||||||
|
|
||||||
|
auto i = std::adjacent_find(rxSeqBuf.begin(), rxSeqBuf.end(), [](quint16 l, quint16 r) {return l + 1 < r; });
|
||||||
|
while (i != rxSeqBuf.end())
|
||||||
|
{
|
||||||
|
if (i + 1 != rxSeqBuf.end())
|
||||||
|
{
|
||||||
|
if (*(i + 1) - *i < 30)
|
||||||
|
{
|
||||||
|
for (quint16 j = *i + 1; j < *(i + 1); j++)
|
||||||
|
{
|
||||||
|
//qDebug(logUdp()) << this->metaObject()->className() << ": Found missing seq between " << *i << " : " << *(i + 1) << " (" << j << ")";
|
||||||
|
auto s = std::find_if(rxMissing.begin(), rxMissing.end(), [&cs = j](SEQBUFENTRY& s) { return s.seqNum == cs; });
|
||||||
|
if (s == rxMissing.end())
|
||||||
|
{
|
||||||
|
// We haven't seen this missing packet before
|
||||||
|
//qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len="<< rxMissing.length() << "): " << j;
|
||||||
|
SEQBUFENTRY b;
|
||||||
|
b.seqNum = j;
|
||||||
|
b.retransmitCount = 0;
|
||||||
|
b.timeSent = QTime::currentTime();
|
||||||
|
rxMissing.append(b);
|
||||||
|
packetsLost++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (s->retransmitCount == 4)
|
||||||
|
{
|
||||||
|
// We have tried 4 times to request this packet, time to give up!
|
||||||
|
s = rxMissing.erase(s);
|
||||||
|
rxSeqBuf.append(j); // Final thing is to add to received buffer!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug(logUdp()) << this->metaObject()->className() << ": Too many missing, flushing buffers";
|
||||||
|
rxSeqBuf.clear();
|
||||||
|
missingSeqs.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (auto it = rxMissing.begin(); it != rxMissing.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->retransmitCount < 10)
|
||||||
|
{
|
||||||
|
missingSeqs.append(it->seqNum & 0xff);
|
||||||
|
missingSeqs.append(it->seqNum >> 8 & 0xff);
|
||||||
|
missingSeqs.append(it->seqNum & 0xff);
|
||||||
|
missingSeqs.append(it->seqNum >> 8 & 0xff);
|
||||||
|
it->retransmitCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (missingSeqs.length() != 0)
|
||||||
|
{
|
||||||
|
control_packet p;
|
||||||
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
|
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);
|
||||||
|
qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq;
|
||||||
|
QMutexLocker locker(&mutex);
|
||||||
|
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex();
|
||||||
|
missingSeqs.insert(0, p.packet, sizeof(p.packet));
|
||||||
|
QMutexLocker locker(&mutex);
|
||||||
|
udp->writeDatagram(missingSeqs, radioIP, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Used to send idle and other "control" style messages
|
// Used to send idle and other "control" style messages
|
||||||
void udpBase::sendControl(bool tracked=true, quint8 type=0, quint16 seq=0)
|
void udpBase::sendControl(bool tracked=true, quint8 type=0, quint16 seq=0)
|
||||||
{
|
{
|
||||||
|
|
31
udphandler.h
31
udphandler.h
|
@ -31,7 +31,9 @@
|
||||||
#define TXAUDIO_PERIOD 10
|
#define TXAUDIO_PERIOD 10
|
||||||
#define AREYOUTHERE_PERIOD 500
|
#define AREYOUTHERE_PERIOD 500
|
||||||
#define WATCHDOG_PERIOD 500
|
#define WATCHDOG_PERIOD 500
|
||||||
|
#define RETRANSMIT_PERIOD 100
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(AUDIOPACKET)
|
||||||
|
|
||||||
void passcode(QString in, QByteArray& out);
|
void passcode(QString in, QByteArray& out);
|
||||||
QByteArray parseNullTerminatedString(QByteArray c, int s);
|
QByteArray parseNullTerminatedString(QByteArray c, int s);
|
||||||
|
@ -40,6 +42,7 @@ QByteArray parseNullTerminatedString(QByteArray c, int s);
|
||||||
class udpBase : public QObject
|
class udpBase : public QObject
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~udpBase();
|
~udpBase();
|
||||||
|
|
||||||
|
@ -98,6 +101,7 @@ public:
|
||||||
QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected.
|
QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected.
|
||||||
|
|
||||||
QTimer* watchdogTimer = Q_NULLPTR;
|
QTimer* watchdogTimer = Q_NULLPTR;
|
||||||
|
QTimer* retransmitTimer = Q_NULLPTR;
|
||||||
|
|
||||||
QDateTime lastPingSentTime;
|
QDateTime lastPingSentTime;
|
||||||
uint16_t pingSendSeq = 0;
|
uint16_t pingSendSeq = 0;
|
||||||
|
@ -107,6 +111,9 @@ public:
|
||||||
quint32 packetsSent=0;
|
quint32 packetsSent=0;
|
||||||
quint32 packetsLost=0;
|
quint32 packetsLost=0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sendRetransmitRequest();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,19 +149,19 @@ class udpAudio : public udpBase
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec);
|
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec);
|
||||||
~udpAudio();
|
~udpAudio();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void haveAudioData(QByteArray data);
|
void haveAudioData(AUDIOPACKET data);
|
||||||
|
|
||||||
void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 bufferSize, const bool isUlaw, const bool isInput);
|
void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput);
|
||||||
void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 bufferSize, const bool isUlaw, const bool isInput);
|
void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput);
|
||||||
|
|
||||||
void haveChangeBufferSize(quint16 value);
|
void haveChangeLatency(quint16 value);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void changeBufferSize(quint16 value);
|
void changeLatency(quint16 value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -163,7 +170,8 @@ private:
|
||||||
void watchdog();
|
void watchdog();
|
||||||
|
|
||||||
QAudioFormat format;
|
QAudioFormat format;
|
||||||
quint16 bufferSize;
|
quint16 rxLatency;
|
||||||
|
quint16 txLatency;
|
||||||
quint16 rxSampleRate;
|
quint16 rxSampleRate;
|
||||||
quint16 txSampleRate;
|
quint16 txSampleRate;
|
||||||
quint8 rxCodec;
|
quint8 rxCodec;
|
||||||
|
@ -197,7 +205,7 @@ class udpHandler: public udpBase
|
||||||
|
|
||||||
public:
|
public:
|
||||||
udpHandler(QString ip, quint16 cport, quint16 sport, quint16 aport, QString username, QString password,
|
udpHandler(QString ip, quint16 cport, quint16 sport, quint16 aport, QString username, QString password,
|
||||||
quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec);
|
quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec);
|
||||||
~udpHandler();
|
~udpHandler();
|
||||||
|
|
||||||
bool streamOpened = false;
|
bool streamOpened = false;
|
||||||
|
@ -209,14 +217,14 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void receiveDataFromUserToRig(QByteArray); // This slot will send data on to
|
void receiveDataFromUserToRig(QByteArray); // This slot will send data on to
|
||||||
void receiveFromCivStream(QByteArray);
|
void receiveFromCivStream(QByteArray);
|
||||||
void changeBufferSize(quint16 value);
|
void changeLatency(quint16 value);
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void haveDataFromPort(QByteArray data); // emit this when we have data, connect to rigcommander
|
void haveDataFromPort(QByteArray data); // emit this when we have data, connect to rigcommander
|
||||||
void haveNetworkError(QString, QString);
|
void haveNetworkError(QString, QString);
|
||||||
void haveNetworkStatus(QString);
|
void haveNetworkStatus(QString);
|
||||||
void haveChangeBufferSize(quint16 value);
|
void haveChangeLatency(quint16 value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -243,7 +251,8 @@ private:
|
||||||
|
|
||||||
quint16 rxSampleRate;
|
quint16 rxSampleRate;
|
||||||
quint16 txSampleRate;
|
quint16 txSampleRate;
|
||||||
quint16 rxBufferSize;
|
quint16 rxLatency;
|
||||||
|
quint16 txLatency;
|
||||||
quint8 rxCodec;
|
quint8 rxCodec;
|
||||||
quint8 txCodec;
|
quint8 txCodec;
|
||||||
|
|
||||||
|
|
40
wfmain.cpp
40
wfmain.cpp
|
@ -533,11 +533,11 @@ void wfmain::openRig()
|
||||||
connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString)));
|
connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString)));
|
||||||
connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString)));
|
connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString)));
|
||||||
|
|
||||||
connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint16, quint16, quint16, QString, QString,quint16,quint16,quint8,quint16,quint8)), rig, SLOT(commSetup(unsigned char, QString, quint16, quint16, quint16, QString, QString,quint16,quint16,quint8,quint16,quint8)));
|
connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint16, quint16, quint16, QString, QString,quint16,quint16,quint16,quint8,quint16,quint8)), rig, SLOT(commSetup(unsigned char, QString, quint16, quint16, quint16, QString, QString,quint16,quint16,quint16,quint8,quint16,quint8)));
|
||||||
connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32)), rig, SLOT(commSetup(unsigned char, QString, quint32)));
|
connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32)), rig, SLOT(commSetup(unsigned char, QString, quint32)));
|
||||||
|
|
||||||
connect(this, SIGNAL(sendCloseComm()), rig, SLOT(closeComm()));
|
connect(this, SIGNAL(sendCloseComm()), rig, SLOT(closeComm()));
|
||||||
connect(this, SIGNAL(sendChangeBufferSize(quint16)), rig, SLOT(changeBufferSize(quint16)));
|
connect(this, SIGNAL(sendChangeLatency(quint16)), rig, SLOT(changeLatency(quint16)));
|
||||||
connect(this, SIGNAL(getRigCIV()), rig, SLOT(findRigs()));
|
connect(this, SIGNAL(getRigCIV()), rig, SLOT(findRigs()));
|
||||||
connect(rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities)));
|
connect(rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities)));
|
||||||
connect(rig, SIGNAL(commReady()), this, SLOT(receiveCommReady()));
|
connect(rig, SIGNAL(commReady()), this, SLOT(receiveCommReady()));
|
||||||
|
@ -546,7 +546,7 @@ void wfmain::openRig()
|
||||||
if (prefs.enableLAN)
|
if (prefs.enableLAN)
|
||||||
{
|
{
|
||||||
emit sendCommSetup(prefs.radioCIVAddr, prefs.ipAddress, prefs.controlLANPort,
|
emit sendCommSetup(prefs.radioCIVAddr, prefs.ipAddress, prefs.controlLANPort,
|
||||||
prefs.serialLANPort, prefs.audioLANPort, prefs.username, prefs.password,prefs.audioRXBufferSize,prefs.audioRXSampleRate,prefs.audioRXCodec,prefs.audioTXSampleRate,prefs.audioTXCodec);
|
prefs.serialLANPort, prefs.audioLANPort, prefs.username, prefs.password,prefs.audioRXLatency,prefs.audioTXLatency,prefs.audioRXSampleRate,prefs.audioRXCodec,prefs.audioTXSampleRate,prefs.audioTXCodec);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if( (prefs.serialPortRadio == QString("auto")) && (serialPortCL.isEmpty()))
|
if( (prefs.serialPortRadio == QString("auto")) && (serialPortCL.isEmpty()))
|
||||||
|
@ -699,7 +699,8 @@ void wfmain::setDefPrefs()
|
||||||
defPrefs.password = QString("");
|
defPrefs.password = QString("");
|
||||||
defPrefs.audioOutput = QAudioDeviceInfo::defaultOutputDevice().deviceName();
|
defPrefs.audioOutput = QAudioDeviceInfo::defaultOutputDevice().deviceName();
|
||||||
defPrefs.audioInput = QAudioDeviceInfo::defaultInputDevice().deviceName();
|
defPrefs.audioInput = QAudioDeviceInfo::defaultInputDevice().deviceName();
|
||||||
defPrefs.audioRXBufferSize = 12000;
|
defPrefs.audioRXLatency = 150;
|
||||||
|
defPrefs.audioTXLatency = 150;
|
||||||
defPrefs.audioRXSampleRate = 48000;
|
defPrefs.audioRXSampleRate = 48000;
|
||||||
defPrefs.audioRXCodec = 4;
|
defPrefs.audioRXCodec = 4;
|
||||||
defPrefs.audioTXSampleRate = 48000;
|
defPrefs.audioTXSampleRate = 48000;
|
||||||
|
@ -769,10 +770,15 @@ void wfmain::loadSettings()
|
||||||
ui->passwordTxt->setEnabled(ui->lanEnableChk->isChecked());
|
ui->passwordTxt->setEnabled(ui->lanEnableChk->isChecked());
|
||||||
ui->passwordTxt->setText(QString("%1").arg(prefs.password));
|
ui->passwordTxt->setText(QString("%1").arg(prefs.password));
|
||||||
|
|
||||||
prefs.audioRXBufferSize = settings.value("AudioRXBufferSize", defPrefs.audioRXBufferSize).toInt();
|
prefs.audioRXLatency = settings.value("AudioRXLatency", defPrefs.audioRXLatency).toInt();
|
||||||
ui->audioBufferSizeSlider->setEnabled(ui->lanEnableChk->isChecked());
|
ui->rxLatencySlider->setEnabled(ui->lanEnableChk->isChecked());
|
||||||
ui->audioBufferSizeSlider->setValue(prefs.audioRXBufferSize);
|
ui->txLatencySlider->setValue(prefs.audioRXLatency);
|
||||||
ui->audioBufferSizeSlider->setTracking(false); // Stop it sending value on every change.
|
ui->rxLatencySlider->setTracking(false); // Stop it sending value on every change.
|
||||||
|
|
||||||
|
prefs.audioTXLatency = settings.value("AudioTXLatency", defPrefs.audioTXLatency).toInt();
|
||||||
|
ui->rxLatencySlider->setEnabled(ui->lanEnableChk->isChecked());
|
||||||
|
ui->rxLatencySlider->setValue(prefs.audioTXLatency);
|
||||||
|
ui->rxLatencySlider->setTracking(false); // Stop it sending value on every change.
|
||||||
|
|
||||||
prefs.audioRXSampleRate = settings.value("AudioRXSampleRate", defPrefs.audioRXSampleRate).toInt();
|
prefs.audioRXSampleRate = settings.value("AudioRXSampleRate", defPrefs.audioRXSampleRate).toInt();
|
||||||
prefs.audioTXSampleRate = settings.value("AudioTXSampleRate", defPrefs.audioTXSampleRate).toInt();
|
prefs.audioTXSampleRate = settings.value("AudioTXSampleRate", defPrefs.audioTXSampleRate).toInt();
|
||||||
|
@ -917,10 +923,10 @@ void wfmain::saveSettings()
|
||||||
settings.setValue("AudioLANPort", prefs.audioLANPort);
|
settings.setValue("AudioLANPort", prefs.audioLANPort);
|
||||||
settings.setValue("Username", prefs.username);
|
settings.setValue("Username", prefs.username);
|
||||||
settings.setValue("Password", prefs.password);
|
settings.setValue("Password", prefs.password);
|
||||||
settings.setValue("AudioRXBufferSize", prefs.audioRXBufferSize);
|
settings.setValue("AudioRXLatency", prefs.audioRXLatency);
|
||||||
|
settings.setValue("AudioTXLatency", prefs.audioTXLatency);
|
||||||
settings.setValue("AudioRXSampleRate", prefs.audioRXSampleRate);
|
settings.setValue("AudioRXSampleRate", prefs.audioRXSampleRate);
|
||||||
settings.setValue("AudioRXCodec", prefs.audioRXCodec);
|
settings.setValue("AudioRXCodec", prefs.audioRXCodec);
|
||||||
settings.setValue("AudioTXBufferSize", prefs.audioRXBufferSize);
|
|
||||||
settings.setValue("AudioTXSampleRate", prefs.audioRXSampleRate);
|
settings.setValue("AudioTXSampleRate", prefs.audioRXSampleRate);
|
||||||
settings.setValue("AudioTXCodec", prefs.audioTXCodec);
|
settings.setValue("AudioTXCodec", prefs.audioTXCodec);
|
||||||
settings.setValue("AudioOutput", prefs.audioOutput);
|
settings.setValue("AudioOutput", prefs.audioOutput);
|
||||||
|
@ -2866,11 +2872,17 @@ void wfmain::on_audioTXCodecCombo_currentIndexChanged(int value)
|
||||||
prefs.audioTXCodec = ui->audioTXCodecCombo->itemData(value).toInt();
|
prefs.audioTXCodec = ui->audioTXCodecCombo->itemData(value).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wfmain::on_audioBufferSizeSlider_valueChanged(int value)
|
void wfmain::on_rxLatencySlider_valueChanged(int value)
|
||||||
{
|
{
|
||||||
prefs.audioRXBufferSize = value;
|
prefs.audioRXLatency = value;
|
||||||
ui->bufferValue->setText(QString::number(value));
|
ui->rxLatencyValue->setText(QString::number(value));
|
||||||
emit sendChangeBufferSize(value);
|
emit sendChangeLatency(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wfmain::on_txLatencySlider_valueChanged(int value)
|
||||||
|
{
|
||||||
|
prefs.audioTXLatency = value;
|
||||||
|
ui->txLatencyValue->setText(QString::number(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void wfmain::on_toFixedBtn_clicked()
|
void wfmain::on_toFixedBtn_clicked()
|
||||||
|
|
11
wfmain.h
11
wfmain.h
|
@ -108,9 +108,9 @@ signals:
|
||||||
void sayAll();
|
void sayAll();
|
||||||
void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate);
|
void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate);
|
||||||
void sendCommSetup(unsigned char rigCivAddr, QString ip, quint16 cport, quint16 sport, quint16 aport,
|
void sendCommSetup(unsigned char rigCivAddr, QString ip, quint16 cport, quint16 sport, quint16 aport,
|
||||||
QString username, QString password, quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec);
|
QString username, QString password, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec);
|
||||||
void sendCloseComm();
|
void sendCloseComm();
|
||||||
void sendChangeBufferSize(quint16 value);
|
void sendChangeLatency(quint16 latency);
|
||||||
void initServer();
|
void initServer();
|
||||||
void sendServerConfig(SERVERCONFIG conf);
|
void sendServerConfig(SERVERCONFIG conf);
|
||||||
|
|
||||||
|
@ -329,7 +329,9 @@ private slots:
|
||||||
|
|
||||||
void on_connectBtn_clicked();
|
void on_connectBtn_clicked();
|
||||||
|
|
||||||
void on_audioBufferSizeSlider_valueChanged(int value);
|
void on_rxLatencySlider_valueChanged(int value);
|
||||||
|
|
||||||
|
void on_txLatencySlider_valueChanged(int value);
|
||||||
|
|
||||||
void on_audioRXCodecCombo_currentIndexChanged(int value);
|
void on_audioRXCodecCombo_currentIndexChanged(int value);
|
||||||
|
|
||||||
|
@ -527,7 +529,8 @@ private:
|
||||||
QString password;
|
QString password;
|
||||||
QString audioOutput;
|
QString audioOutput;
|
||||||
QString audioInput;
|
QString audioInput;
|
||||||
quint16 audioRXBufferSize;
|
quint16 audioRXLatency;
|
||||||
|
quint16 audioTXLatency;
|
||||||
quint16 audioRXSampleRate;
|
quint16 audioRXSampleRate;
|
||||||
quint8 audioRXCodec;
|
quint8 audioRXCodec;
|
||||||
quint16 audioTXSampleRate;
|
quint16 audioTXSampleRate;
|
||||||
|
|
42
wfmain.ui
42
wfmain.ui
|
@ -18,7 +18,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="mainTab">
|
<widget class="QWidget" name="mainTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -1765,20 +1765,23 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_16">
|
<widget class="QLabel" name="label_16">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>RX Audio Buffer Size</string>
|
<string>RX Latency (ms)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSlider" name="audioBufferSizeSlider">
|
<widget class="QSlider" name="rxLatencySlider">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
<height>0</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>30</number>
|
||||||
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>65536</number>
|
<number>500</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
|
@ -1786,7 +1789,34 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="bufferValue">
|
<widget class="QLabel" name="rxLatencyValue">
|
||||||
|
<property name="text">
|
||||||
|
<string>0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_24">
|
||||||
|
<property name="text">
|
||||||
|
<string>TX Latency (ms)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="txLatencySlider">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>30</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>500</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="txLatencyValue">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>0</string>
|
<string>0</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -1948,7 +1978,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>810</width>
|
<width>810</width>
|
||||||
<height>22</height>
|
<height>21</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
Ładowanie…
Reference in New Issue