Change audio output to use single/slot

merge-requests/9/merge
Phil Taylor 2022-04-10 23:13:51 +01:00
rodzic a108f1ca99
commit 50c8b4e545
6 zmienionych plików z 127 dodań i 150 usunięć

Wyświetl plik

@ -31,6 +31,12 @@ audioHandler::~audioHandler()
audioOutput = Q_NULLPTR;
}
if (audioTimer != Q_NULLPTR) {
audioTimer->stop();
delete audioTimer;
audioTimer = Q_NULLPTR;
}
if (resampler != Q_NULLPTR) {
speex_resampler_destroy(resampler);
@ -128,6 +134,11 @@ bool audioHandler::init(audioSetup setupIn)
if (setup.isinput) {
audioInput = new QAudioInput(setup.port, format, this);
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Starting audio timer";
audioTimer = new QTimer();
audioTimer->setTimerType(Qt::PreciseTimer);
connect(audioTimer, &QTimer::timeout, this, &audioHandler::getNextAudioChunk);
connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
}
else {
@ -186,6 +197,7 @@ void audioHandler::start()
if (setup.isinput) {
audioDevice = audioInput->start();
connect(audioInput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection);
audioTimer->start(setup.blockSize);
}
else {
// Buffer size must be set before audio is started.
@ -394,19 +406,17 @@ void audioHandler::incomingAudio(audioPacket inPacket)
}
void audioHandler::getNextAudioChunk(QByteArray& ret)
void audioHandler::getNextAudioChunk()
{
audioPacket livePacket;
livePacket.time= QTime::currentTime();
livePacket.sent = 0;
// Don't start sending until we have at least 1/2 of setup.latency of audio buffered
// For some reason the audioDevice->bytesAvailable() function always returns 0?
if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR && audioInput->bytesReady() > format.bytesForDuration(setup.latency)) {
if (setup.codec == 0x40 || setup.codec == 0x80) {
livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format.
}
else {
livePacket.data = audioDevice->readAll(); // 20000uS is 20ms in NATIVE format.
}
memcpy(&livePacket.guid, setup.guid, GUIDLEN);
if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR) {
livePacket.data = audioDevice->readAll();
if (livePacket.data.length() > 0)
{
Eigen::VectorXf samplesF;
@ -555,12 +565,13 @@ void audioHandler::getNextAudioChunk(QByteArray& ret)
livePacket.data.clear();
livePacket.data = outPacket; // Copy output packet back to input buffer.
}
ret = livePacket.data;
emit haveAudioData(livePacket);
//ret = livePacket.data;
}
}
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun);
}
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun);
return;

Wyświetl plik

@ -69,6 +69,8 @@ struct audioSetup {
QAudioDeviceInfo port;
quint8 resampleQuality;
unsigned char localAFgain;
quint16 blockSize=20; // Each 'block' of audio is 20ms long by default.
quint8 guid[GUIDLEN];
};
// For QtMultimedia, use a native QIODevice
@ -85,7 +87,6 @@ public:
void start();
void stop();
void getNextAudioChunk(QByteArray &data);
quint16 getAmplitude();
public slots:
@ -96,14 +97,14 @@ public slots:
private slots:
void stateChanged(QAudio::State state);
void clearUnderrun();
void clearUnderrun();
void getNextAudioChunk();
signals:
void audioMessage(QString message);
void sendLatency(quint16 newSize);
void haveAudioData(const QByteArray& data);
void haveAudioData(const audioPacket& data);
void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under);
private:
bool isUnderrun = false;
@ -114,9 +115,9 @@ private:
QAudioOutput* audioOutput=Q_NULLPTR;
QAudioInput* audioInput=Q_NULLPTR;
QIODevice* audioDevice=Q_NULLPTR;
QTimer* audioTimer = Q_NULLPTR;
QAudioFormat format;
QAudioDeviceInfo deviceInfo;
SpeexResamplerState* resampler = Q_NULLPTR;
//r8b::CFixedBuffer<double>* resampBufs;

Wyświetl plik

@ -826,17 +826,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
txSetup.format.setChannelCount(1); // TX Audio is always single channel.
txaudio = new audioHandler();
txAudioThread = new QThread(this);
txaudio->moveToThread(txAudioThread);
txAudioThread->start(QThread::TimeCriticalPriority);
connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup)));
connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool)));
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
sendControl(false, 0x03, 0x00); // First connect packet
@ -845,6 +835,18 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
pingTimer->start(PING_PERIOD); // send ping packets every 100ms
if (enableTx) {
txaudio = new audioHandler();
txAudioThread = new QThread(this);
txaudio->moveToThread(txAudioThread);
txAudioThread->start(QThread::TimeCriticalPriority);
connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup)));
connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool)));
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
emit setupTxAudio(txSetup);
}
@ -854,10 +856,6 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog);
watchdogTimer->start(WATCHDOG_PERIOD);
txAudioTimer = new QTimer();
txAudioTimer->setTimerType(Qt::PreciseTimer);
connect(txAudioTimer, &QTimer::timeout, this, &udpAudio::sendTxAudio);
areYouThereTimer = new QTimer();
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
areYouThereTimer->start(AREYOUTHERE_PERIOD);
@ -889,13 +887,6 @@ udpAudio::~udpAudio()
watchdogTimer = Q_NULLPTR;
}
if (txAudioTimer != Q_NULLPTR)
{
qDebug(logUdp()) << "Stopping txaudio timer";
txAudioTimer->stop();
delete txAudioTimer;
}
if (rxAudioThread != Q_NULLPTR) {
qDebug(logUdp()) << "Stopping rxaudio thread";
rxAudioThread->quit();
@ -935,44 +926,41 @@ void udpAudio::sendTxAudio()
if (txaudio == Q_NULLPTR) {
return;
}
QByteArray audio;
if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
txaudio->getNextAudioChunk(audio);
// Now we have the next audio chunk, we can release the mutex.
audioMutex.unlock();
if (audio.length() > 0) {
int counter = 1;
int len = 0;
}
while (len < audio.length()) {
QByteArray partial = audio.mid(len, 1364);
audio_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p) + partial.length();
p.sentid = myId;
p.rcvdid = remoteId;
if (partial.length() == 0xa0) {
p.ident = 0x9781;
}
else {
p.ident = 0x0080; // TX audio is always this?
}
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN!
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
tx.append(partial);
len = len + partial.length();
//qInfo(logUdp()) << "Sending audio packet length: " << tx.length();
sendTrackedPacket(tx);
sendAudioSeq++;
counter++;
}
}
void udpAudio::receiveAudioData(audioPacket audio) {
// I really can't see how this could be possible but a quick sanity check!
if (txaudio == Q_NULLPTR) {
return;
}
else {
qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio";
if (audio.data.length() > 0) {
int counter = 1;
int len = 0;
while (len < audio.data.length()) {
QByteArray partial = audio.data.mid(len, 1364);
audio_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p) + partial.length();
p.sentid = myId;
p.rcvdid = remoteId;
if (partial.length() == 0xa0) {
p.ident = 0x9781;
}
else {
p.ident = 0x0080; // TX audio is always this?
}
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN!
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
tx.append(partial);
len = len + partial.length();
//qInfo(logUdp()) << "Sending audio packet length: " << tx.length();
sendTrackedPacket(tx);
sendAudioSeq++;
counter++;
}
}
}
@ -1006,12 +994,7 @@ void udpAudio::dataReceived()
{
case (16): // Response to control packet handled in udpBase
{
control_packet_t in = (control_packet_t)r.constData();
if (in->type == 0x04 && enableTx)
{
txAudioTimer->start(AUDIO_PERIOD);
}
//control_packet_t in = (control_packet_t)r.constData();
break;
}
default:
@ -1024,7 +1007,6 @@ void udpAudio::dataReceived()
*/
control_packet_t in = (control_packet_t)r.constData();
if (in->type != 0x01 && in->len >= 0x20) {
if (in->seq == 0)

Wyświetl plik

@ -196,6 +196,7 @@ public slots:
void setVolume(unsigned char value);
void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
void receiveAudioData(audioPacket audio);
private:
@ -211,7 +212,7 @@ private:
audioHandler* txaudio = Q_NULLPTR;
QThread* txAudioThread = Q_NULLPTR;
QTimer* txAudioTimer=Q_NULLPTR;
QTimer* txAudioTimer = Q_NULLPTR;
bool enableTx = true;
QMutex audioMutex;

Wyświetl plik

@ -353,6 +353,7 @@ void udpServer::controlReceived()
radio->txAudioSetup.format.setSampleRate(current->txSampleRate);
radio->txAudioSetup.isinput = false;
radio->txAudioSetup.latency = current->txBufferLen;
outAudio.isinput = false;
radio->txaudio = new audioHandler();
@ -393,6 +394,7 @@ void udpServer::controlReceived()
radio->rxAudioSetup.format.setSampleRate(current->rxSampleRate);
radio->rxAudioSetup.latency = current->txBufferLen;
radio->rxAudioSetup.isinput = true;
memcpy(radio->rxAudioSetup.guid, radio->guid, GUIDLEN);
radio->rxaudio = new audioHandler();
@ -404,6 +406,7 @@ void udpServer::controlReceived()
connect(this, SIGNAL(setupRxAudio(audioSetup)), radio->rxaudio, SLOT(init(audioSetup)));
connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater()));
connect(radio->rxaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0))
QMetaObject::invokeMethod(radio->rxaudio, [=]() {
@ -414,10 +417,6 @@ void udpServer::controlReceived()
setupRxAudio(radio->rxAudioSetup);
#endif
radio->rxAudioTimer = new QTimer();
radio->rxAudioTimer->setTimerType(Qt::PreciseTimer);
connect(radio->rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this));
radio->rxAudioTimer->start(AUDIO_PERIOD);
}
}
@ -1596,30 +1595,12 @@ void udpServer::dataForServer(QByteArray d)
return;
}
void udpServer::sendRxAudio()
void udpServer::sendRxAudio(const audioPacket& audio)
{
QByteArray audio;
audioHandler* sender = qobject_cast<audioHandler*>(QObject::sender());
for (RIGCONFIG* rig : config.rigs) {
if (rig->rxaudio != Q_NULLPTR) {
if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
audio.clear();
rig->rxaudio->getNextAudioChunk(audio);
int len = 0;
while (len < audio.length()) {
audioPacket partial;
memcpy(partial.guid, rig->guid, GUIDLEN);
partial.data = audio.mid(len, 1364);
receiveAudioData(partial);
len = len + partial.data.length();
}
// Now we have the next audio chunk, we can release the mutex.
audioMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio";
}
if (sender != Q_NULLPTR && rig->rxaudio == sender) {
}
}
}
@ -1637,52 +1618,58 @@ void udpServer::receiveAudioData(const audioPacket& d)
else {
memcpy(guid, d.guid, GUIDLEN);
}
//qInfo(logUdpServer()) << "Server got:" << d.data.length();
//qInfo(logUdpServer()) << "Server got:" << d.data.length();
foreach(CLIENT * client, audioClients)
{
if (client != Q_NULLPTR && client->connected && !memcmp(client->guid,guid, GUIDLEN)) {
audio_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p) + d.data.length();
p.sentid = client->myId;
p.rcvdid = client->remoteId;
p.ident = 0x0080; // audio is always this?
p.datalen = (quint16)qToBigEndian((quint16)d.data.length());
p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN!
p.seq = client->txSeq;
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
t.append(d.data);
int len = 0;
while (len < d.data.length()) {
QByteArray partial;
partial = d.data.mid(len, 1364);
len = len + partial.length();
if (client != Q_NULLPTR && client->connected && !memcmp(client->guid, guid, GUIDLEN)) {
audio_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p) + partial.length();
p.sentid = client->myId;
p.rcvdid = client->remoteId;
p.ident = 0x0080; // audio is always this?
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN!
p.seq = client->txSeq;
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
t.append(d.data);
SEQBUFENTRY s;
s.seqNum = p.seq;
s.timeSent = QTime::currentTime();
s.retransmitCount = 0;
s.data = t;
if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
if (client->txSeqBuf.size() > BUFSIZE)
SEQBUFENTRY s;
s.seqNum = p.seq;
s.timeSent = QTime::currentTime();
s.retransmitCount = 0;
s.data = t;
if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
client->txSeqBuf.remove(client->txSeqBuf.firstKey());
if (client->txSeqBuf.size() > BUFSIZE)
{
client->txSeqBuf.remove(client->txSeqBuf.firstKey());
}
client->txSeqBuf.insert(p.seq, s);
client->txSeq++;
client->sendAudioSeq++;
client->txMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock txMutex()";
}
client->txSeqBuf.insert(p.seq, s);
client->txSeq++;
client->sendAudioSeq++;
client->txMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock txMutex()";
}
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
client->socket->writeDatagram(t, client->ipAddress, client->port);
udpMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock udpMutex()";
}
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
client->socket->writeDatagram(t, client->ipAddress, client->port);
udpMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock udpMutex()";
}
}
}
}
@ -1871,11 +1858,6 @@ void udpServer::deleteConnection(QList<CLIENT*>* l, CLIENT* c)
for (RIGCONFIG* radio : config.rigs) {
if (!memcmp(radio->guid, guid, GUIDLEN))
{
if (radio->rxAudioTimer != Q_NULLPTR) {
radio->rxAudioTimer->stop();
delete radio->rxAudioTimer;
radio->rxAudioTimer = Q_NULLPTR;
}
if (radio->rxAudioThread != Q_NULLPTR) {
radio->rxAudioThread->quit();

Wyświetl plik

@ -96,6 +96,7 @@ public slots:
void dataForServer(QByteArray);
void receiveAudioData(const audioPacket &data);
void receiveRigCaps(rigCapabilities caps);
void sendRxAudio(const audioPacket &audio);
signals:
void haveDataFromServer(QByteArray);
@ -184,7 +185,6 @@ private:
void sendStatus(CLIENT* c);
void sendRetransmitRequest(CLIENT* c);
void watchdog();
void sendRxAudio();
void deleteConnection(QList<CLIENT*> *l, CLIENT* c);
SERVERCONFIG config;