Fix txaudio and move udphandler into its own thread

merge-requests/2/head
Phil Taylor 2021-02-21 14:53:42 +00:00
rodzic 4cb3289602
commit ba0509ba61
8 zmienionych plików z 123 dodań i 85 usunięć

Wyświetl plik

@ -131,7 +131,7 @@ bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16
isInitialized = setDevice(QAudioDeviceInfo::defaultInputDevice()); isInitialized = setDevice(QAudioDeviceInfo::defaultInputDevice());
else else
isInitialized = setDevice(QAudioDeviceInfo::defaultOutputDevice()); isInitialized = setDevice(QAudioDeviceInfo::defaultOutputDevice());
return isInitialized; return isInitialized;
} }
@ -172,11 +172,9 @@ bool audioHandler::setDevice(QAudioDeviceInfo deviceInfo)
void audioHandler::reinit() void audioHandler::reinit()
{ {
qDebug() << this->metaObject()->className() << ": reinit() running"; qDebug() << this->metaObject()->className() << ": reinit() running";
bool running = false;
if (audioOutput && audioOutput->state() != QAudio::StoppedState) { if (audioOutput && audioOutput->state() != QAudio::StoppedState) {
running = true; this->stop();
} }
this->stop();
// Calculate the minimum required audio buffer // Calculate the minimum required audio buffer
// This may need work depending on how it performs on other platforms. // This may need work depending on how it performs on other platforms.
@ -207,9 +205,7 @@ void audioHandler::reinit()
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
} }
if (running) { this->start();
this->start();
}
this->flush(); this->flush();
} }
@ -224,15 +220,10 @@ void audioHandler::start()
if (isInput) { if (isInput) {
this->open(QIODevice::WriteOnly | QIODevice::Unbuffered); this->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
}
else {
this->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
}
if (isInput) {
audioInput->start(this); audioInput->start(this);
} }
else { else {
this->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
audioOutput->start(this); audioOutput->start(this);
} }
} }
@ -319,7 +310,7 @@ qint64 audioHandler::writeData(const char* data, qint64 len)
if (buffer.length() > bufferSize * 4) if (buffer.length() > bufferSize * 4)
{ {
qWarning() << "writeData() Buffer overflow"; qWarning() << "writeData() Buffer overflow";
buffer.clear(); // Will cause a click! // buffer.clear(); // Will cause a click!
} }
if (isUlaw) { if (isUlaw) {
@ -402,6 +393,11 @@ void audioHandler::getBufferSize()
emit sendBufferSize(audioOutput->bufferSize()); emit sendBufferSize(audioOutput->bufferSize());
} }
bool audioHandler::isChunkAvailable()
{
return chunkAvailable;
}
void audioHandler::getNextAudioChunk(QByteArray& ret) void audioHandler::getNextAudioChunk(QByteArray& ret)
{ {
quint16 numSamples = radioSampleBits * 120; quint16 numSamples = radioSampleBits * 120;

Wyświetl plik

@ -40,10 +40,9 @@ public:
qint64 writeData(const char* data, qint64 len); qint64 writeData(const char* data, qint64 len);
qint64 bytesAvailable() const; qint64 bytesAvailable() const;
bool isSequential() const; bool isSequential() const;
volatile bool chunkAvailable;
void incomingAudio(const QByteArray& data); void incomingAudio(const QByteArray& data);
void getNextAudioChunk(QByteArray &data); void getNextAudioChunk(QByteArray &data);
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 bufferSize, const bool isulaw, const bool isinput);
void changeBufferSize(const quint16 newSize); void changeBufferSize(const quint16 newSize);
@ -63,13 +62,14 @@ private:
QMutex mutex; QMutex mutex;
bool isInitialized; bool chunkAvailable;
QAudioOutput* audioOutput; bool isInitialized;
QAudioInput* audioInput; QAudioOutput* audioOutput;
QAudioInput* audioInput;
bool isUlaw; bool isUlaw;
int bufferSize; int bufferSize;
bool isInput; // Used to determine whether input or output audio bool isInput; // Used to determine whether input or output audio
float volume; float volume;
QByteArray buffer; QByteArray buffer;
QAudioFormat format; QAudioFormat format;

Wyświetl plik

@ -253,8 +253,8 @@ typedef union conninfo_packet {
quint32 txsample; // 0x78 quint32 txsample; // 0x78
quint32 civport; // 0x7c quint32 civport; // 0x7c
quint32 audioport; // 0x80 quint32 audioport; // 0x80
char unusedk; // 0x84
quint32 txbuffer; // 0x85 quint32 txbuffer; // 0x85
char unusedk; // 0x84
char unusedl[7]; // 0x89 char unusedl[7]; // 0x89
}; };
}; };

Wyświetl plik

@ -86,7 +86,22 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString ip, quint16 cport
this->password = password; this->password = password;
*/ */
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, buffer, rxsample, rxcodec, txsample, txcodec);
udpHandlerThread = new QThread(this);
udp->moveToThread(udpHandlerThread);
connect(this, SIGNAL(initUdpHandler()), udp, SLOT(init()));
connect(udpHandlerThread, SIGNAL(finished()), udp, SLOT(deleteLater()));
udpHandlerThread->start();
emit initUdpHandler();
connect(udp, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray))); connect(udp, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray)));
// data from the program to the comm port: // data from the program to the comm port:
@ -113,8 +128,9 @@ void rigCommander::closeComm()
} }
comm = Q_NULLPTR; comm = Q_NULLPTR;
if (udp != Q_NULLPTR) { if (udpHandlerThread != Q_NULLPTR) {
delete udp; udpHandlerThread->quit();
udpHandlerThread->wait();
} }
udp = Q_NULLPTR; udp = Q_NULLPTR;
} }
@ -1438,7 +1454,7 @@ void rigCommander::getMeters(bool transmitting)
{ {
// Nice function to just grab every meter // Nice function to just grab every meter
qDebug() << __func__ << ": grabbing all metering for mode " << (transmitting==true? "transmitting":"receiving") ; qDebug() << __func__ << ": grabbing all metering for mode " << (transmitting==true? "transmitting":"receiving") ;
if(transmitting) if(transmitting)
{ {
getRFPowerMeter(); getRFPowerMeter();
@ -1452,6 +1468,7 @@ void rigCommander::getMeters(bool transmitting)
getVdMeter(); getVdMeter();
getIDMeter(); getIDMeter();
} }
void rigCommander::getSMeter() void rigCommander::getSMeter()

Wyświetl plik

@ -199,7 +199,7 @@ signals:
void haveATUStatus(unsigned char status); void haveATUStatus(unsigned char status);
void haveChangeBufferSize(quint16 value); void haveChangeBufferSize(quint16 value);
void haveDataForServer(QByteArray outData); void haveDataForServer(QByteArray outData);
void initUdpHandler();
private: private:
void setup(); void setup();
@ -237,6 +237,8 @@ private:
commHandler * comm=Q_NULLPTR; commHandler * comm=Q_NULLPTR;
udpHandler* udp=Q_NULLPTR; udpHandler* udp=Q_NULLPTR;
QThread* udpHandlerThread = Q_NULLPTR;
void determineRigCaps(); void determineRigCaps();
QByteArray payloadIn; QByteArray payloadIn;
QByteArray echoPerfix; QByteArray echoPerfix;

Wyświetl plik

@ -52,6 +52,14 @@ udpHandler::udpHandler(QString ip, quint16 controlPort, quint16 civPort, quint16
} }
} }
// Set my computer name. Should this be configurable?
compName = "wfview";
}
void udpHandler::init()
{
udpBase::init(); // Perform UDP socket initialization. udpBase::init(); // Perform UDP socket initialization.
// Connect socket to my dataReceived function. // Connect socket to my dataReceived function.
@ -60,17 +68,18 @@ udpHandler::udpHandler(QString ip, quint16 controlPort, quint16 civPort, quint16
/* /*
Connect various timers Connect various timers
*/ */
connect(&tokenTimer, &QTimer::timeout, this, std::bind(&udpHandler::sendToken, this, 0x05)); tokenTimer = new QTimer();
connect(&areYouThereTimer, &QTimer::timeout, this, QOverload<>::of(&udpHandler::sendAreYouThere)); areYouThereTimer = new QTimer();
connect(&pingTimer, &QTimer::timeout, this, &udpBase::sendPing); pingTimer = new QTimer();
connect(&idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0)); idleTimer = new QTimer();
connect(tokenTimer, &QTimer::timeout, this, std::bind(&udpHandler::sendToken, this, 0x05));
connect(areYouThereTimer, &QTimer::timeout, this, QOverload<>::of(&udpHandler::sendAreYouThere));
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
connect(idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0));
// Start sending are you there packets - will be stopped once "I am here" received // Start sending are you there packets - will be stopped once "I am here" received
areYouThereTimer.start(AREYOUTHERE_PERIOD); areYouThereTimer->start(AREYOUTHERE_PERIOD);
// Set my computer name. Should this be configurable?
compName = "wfview";
} }
udpHandler::~udpHandler() udpHandler::~udpHandler()
@ -120,11 +129,11 @@ void udpHandler::dataReceived()
control_packet_t in = (control_packet_t)r.constData(); control_packet_t in = (control_packet_t)r.constData();
if (in->type == 0x04) { if (in->type == 0x04) {
// If timer is active, stop it as they are obviously there! // If timer is active, stop it as they are obviously there!
if (areYouThereTimer.isActive()) { if (areYouThereTimer->isActive()) {
areYouThereTimer.stop(); areYouThereTimer->stop();
// send ping packets every second // send ping packets every second
pingTimer.start(PING_PERIOD); pingTimer->start(PING_PERIOD);
idleTimer.start(IDLE_PERIOD); idleTimer->start(IDLE_PERIOD);
} }
} }
// This is "I am ready" in response to "Are you ready" so send login. // This is "I am ready" in response to "Are you ready" so send login.
@ -166,7 +175,7 @@ void udpHandler::dataReceived()
if (in->response == 0x0000) if (in->response == 0x0000)
{ {
qDebug() << this->metaObject()->className() << ": Token renewal successful"; qDebug() << this->metaObject()->className() << ": Token renewal successful";
tokenTimer.start(TOKEN_RENEWAL); tokenTimer->start(TOKEN_RENEWAL);
gotAuthOK = true; gotAuthOK = true;
if (!streamOpened) if (!streamOpened)
{ {
@ -178,8 +187,10 @@ void udpHandler::dataReceived()
{ {
qWarning() << this->metaObject()->className() << ": Radio rejected token renewal, performing login"; qWarning() << this->metaObject()->className() << ": Radio rejected token renewal, performing login";
remoteId = in->sentid; remoteId = in->sentid;
isAuthenticated = false; tokRequest = in->tokrequest;
sendLogin(); // Try sending login packet (didn't seem to work?) token = in->token;
// Got new token response
sendToken(0x02); // Update it.
} }
else else
{ {
@ -200,6 +211,17 @@ void udpHandler::dataReceived()
{ {
emit haveNetworkError(radioIP.toString(), "Got radio disconnected."); emit haveNetworkError(radioIP.toString(), "Got radio disconnected.");
qDebug() << this->metaObject()->className() << ": Got radio disconnected."; qDebug() << this->metaObject()->className() << ": Got radio disconnected.";
if (streamOpened) {
// Close stream connections but keep connection open to the radio.
if (audio != Q_NULLPTR) {
delete audio;
}
if (civ != Q_NULLPTR) {
delete civ;
}
streamOpened = false;
}
} }
break; break;
} }
@ -220,7 +242,7 @@ void udpHandler::dataReceived()
qDebug() << this->metaObject()->className() << ": Received matching token response to our request"; qDebug() << this->metaObject()->className() << ": Received matching token response to our request";
token = in->token; token = in->token;
sendToken(0x02); sendToken(0x02);
tokenTimer.start(TOKEN_RENEWAL); // Start token request timer tokenTimer->start(TOKEN_RENEWAL); // Start token request timer
isAuthenticated = true; isAuthenticated = true;
} }
else else
@ -402,7 +424,7 @@ void udpHandler::sendToken(uint8_t magic)
authInnerSendSeq++; authInnerSendSeq++;
sendTrackedPacket(QByteArray::fromRawData((const char *)p.packet, sizeof(p))); sendTrackedPacket(QByteArray::fromRawData((const char *)p.packet, sizeof(p)));
tokenTimer.start(100); // Set 100ms timer for retry (this will be cancelled if a response is received) tokenTimer->start(100); // Set 100ms timer for retry (this will be cancelled if a response is received)
return; return;
} }
@ -424,13 +446,16 @@ udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort)
/* /*
Connect various timers Connect various timers
*/ */
connect(&pingTimer, &QTimer::timeout, this, &udpBase::sendPing); pingTimer = new QTimer();
connect(&idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0)); idleTimer = new QTimer();
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
connect(idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0));
// send ping packets every 100 ms (maybe change to less frequent?) // send ping packets every 100 ms (maybe change to less frequent?)
pingTimer.start(PING_PERIOD); 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 everytime a non-idle packet is sent.
idleTimer.start(IDLE_PERIOD); idleTimer->start(IDLE_PERIOD);
} }
udpCivData::~udpCivData() { udpCivData::~udpCivData() {
@ -608,23 +633,25 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
sendControl(false, 0x03, 0x00); // First connect packet sendControl(false, 0x03, 0x00); // First connect packet
connect(&pingTimer, &QTimer::timeout, this, &udpBase::sendPing); pingTimer = new QTimer();
pingTimer.start(PING_PERIOD); // send ping packets every 100ms connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
txAudioTimer.setTimerType(Qt::PreciseTimer); pingTimer->start(PING_PERIOD); // send ping packets every 100ms
connect(&txAudioTimer, &QTimer::timeout, this, &udpAudio::sendTxAudio);
txAudioTimer.start(TXAUDIO_PERIOD);
emit setupTxAudio(txNumSamples, txChannelCount, txSampleRate, bufferSize, txIsUlawCodec, true); emit setupTxAudio(txNumSamples, txChannelCount, txSampleRate, bufferSize, txIsUlawCodec, true);
emit setupRxAudio(rxNumSamples, rxChannelCount, rxSampleRate, bufferSize, rxIsUlawCodec, false); emit setupRxAudio(rxNumSamples, rxChannelCount, rxSampleRate, bufferSize, rxIsUlawCodec, false);
txAudioTimer = new QTimer();
txAudioTimer->setTimerType(Qt::PreciseTimer);
connect(txAudioTimer, &QTimer::timeout, this, &udpAudio::sendTxAudio);
txAudioTimer->start(TXAUDIO_PERIOD);
} }
udpAudio::~udpAudio() udpAudio::~udpAudio()
{ {
if (txAudioTimer.isActive()) if (txAudioTimer != Q_NULLPTR)
{ {
txAudioTimer.stop(); txAudioTimer->stop();
delete txAudioTimer;
} }
if (rxAudioThread) { if (rxAudioThread) {
@ -643,11 +670,12 @@ udpAudio::~udpAudio()
void udpAudio::sendTxAudio() void udpAudio::sendTxAudio()
{ {
if (txaudio->chunkAvailable) { if (txaudio->isChunkAvailable()) {
QByteArray audio; QByteArray audio;
txaudio->getNextAudioChunk(audio); txaudio->getNextAudioChunk(audio);
int counter = 1; int counter = 1;
int len = 0; int len = 0;
while (len < audio.length()) { while (len < audio.length()) {
QByteArray partial = audio.mid(len, 1364); QByteArray partial = audio.mid(len, 1364);
txaudio_packet p; txaudio_packet p;
@ -655,15 +683,9 @@ void udpAudio::sendTxAudio()
p.len = sizeof(p)+partial.length(); p.len = sizeof(p)+partial.length();
p.sentid = myId; p.sentid = myId;
p.rcvdid = remoteId; p.rcvdid = remoteId;
if (counter % 2 == 0) p.ident = 0x0080; // TX audio is always this?
{
p.ident = 0x0680;
}
else {
p.ident = 0x0681;
}
p.datalen = (quint16)qToBigEndian((quint16)partial.length()); p.datalen = (quint16)qToBigEndian((quint16)partial.length());
p.sendseq = (quint16)qToBigEndian(sendAudioSeq); // THIS IS BIG ENDIAN! p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN!
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
tx.append(partial); tx.append(partial);
len = len + partial.length(); len = len + partial.length();
@ -754,14 +776,19 @@ udpBase::~udpBase()
udp->close(); udp->close();
delete udp; delete udp;
} }
if (pingTimer.isActive()) if (pingTimer != Q_NULLPTR)
{ {
pingTimer.stop(); pingTimer->stop();
delete pingTimer;
} }
if (idleTimer.isActive()) if (idleTimer != Q_NULLPTR)
{ {
idleTimer.stop(); idleTimer->stop();
delete idleTimer;
} }
pingTimer = Q_NULLPTR;
idleTimer = Q_NULLPTR;
} }
// Base class! // Base class!
@ -908,9 +935,6 @@ void udpBase::sendControl(bool tracked=true, quint8 type=0, quint16 seq=0)
else { else {
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p))); sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
} }
if (idleTimer.isActive()) {
idleTimer.start(IDLE_PERIOD); // Reset idle counter if it's running
}
return; return;
} }
@ -948,8 +972,8 @@ void udpBase::sendTrackedPacket(QByteArray d)
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);
udp->writeDatagram(d, radioIP, port); udp->writeDatagram(d, radioIP, port);
if (idleTimer.isActive()) { if (idleTimer != Q_NULLPTR && idleTimer->isActive()) {
idleTimer.start(IDLE_PERIOD); // Reset idle counter if it's running idleTimer->start(IDLE_PERIOD); // Reset idle counter if it's running
} }
packetsSent++; packetsSent++;
return; return;

Wyświetl plik

@ -27,7 +27,7 @@
#define TOKEN_RENEWAL 60000 #define TOKEN_RENEWAL 60000
#define PING_PERIOD 100 #define PING_PERIOD 100
#define IDLE_PERIOD 100 #define IDLE_PERIOD 100
#define TXAUDIO_PERIOD 10 #define TXAUDIO_PERIOD 5
#define AREYOUTHERE_PERIOD 500 #define AREYOUTHERE_PERIOD 500
@ -85,9 +85,9 @@ public:
void sendTrackedPacket(QByteArray d); void sendTrackedPacket(QByteArray d);
void purgeOldEntries(); void purgeOldEntries();
QTimer areYouThereTimer; // Send are-you-there packets every second until a response is received. QTimer* areYouThereTimer = Q_NULLPTR; // Send are-you-there packets every second until a response is received.
QTimer pingTimer; // Start sending pings immediately. QTimer* pingTimer = Q_NULLPTR; // Start sending pings immediately.
QTimer idleTimer; // Start watchdog once we are connected. QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected.
QDateTime lastPingSentTime; QDateTime lastPingSentTime;
uint16_t pingSendSeq = 0; uint16_t pingSendSeq = 0;
@ -169,7 +169,7 @@ private:
audioHandler* txaudio=Q_NULLPTR; audioHandler* txaudio=Q_NULLPTR;
QThread* txAudioThread=Q_NULLPTR; QThread* txAudioThread=Q_NULLPTR;
QTimer txAudioTimer; QTimer* txAudioTimer=Q_NULLPTR;
}; };
@ -195,7 +195,7 @@ 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 changeBufferSize(quint16 value);
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
@ -205,7 +205,6 @@ signals:
private: private:
void sendAreYouThere(); void sendAreYouThere();
void dataReceived(); void dataReceived();
@ -244,8 +243,8 @@ private:
char identa; char identa;
quint32 identb; quint32 identb;
QTimer tokenTimer; QTimer* tokenTimer = Q_NULLPTR;
QTimer areYouThereTimer; QTimer* areYouThereTimer = Q_NULLPTR;
bool highBandwidthConnection = false; bool highBandwidthConnection = false;
}; };

Wyświetl plik

@ -580,8 +580,8 @@ private:
satelliteSetup *sat; satelliteSetup *sat;
udpServerSetup *srv; udpServerSetup *srv;
udpServer *udp = Q_NULLPTR; udpServer* udp = Q_NULLPTR;
QThread *serverThread = Q_NULLPTR; QThread* serverThread = Q_NULLPTR;
void bandStackBtnClick(); void bandStackBtnClick();
bool waitingForBandStackRtn; bool waitingForBandStackRtn;