Merge remote-tracking branch 'origin/lan-alpha' into ui-enhance

merge-requests/2/head
Elliott Liggett 2021-02-13 09:21:32 -08:00
commit 27815a7994
4 zmienionych plików z 150 dodań i 103 usunięć

Wyświetl plik

@ -4,14 +4,15 @@
*/
#include "audiohandler.h"
static int8_t uLawEncode(int16_t number)
#define MULAW_BIAS 33
#define MULAW_MAX 0x1fff
qint8 uLawEncode(qint16 number)
{
const uint16_t MULAW_MAX = 0x1FFF;
const uint16_t MULAW_BIAS = 33;
uint16_t mask = 0x1000;
uint8_t sign = 0;
uint8_t position = 12;
uint8_t lsb = 0;
quint16 mask = 0x1000;
quint8 sign = 0;
quint8 position = 12;
quint8 lsb = 0;
if (number < 0)
{
number = -number;
@ -28,8 +29,25 @@ static int8_t uLawEncode(int16_t number)
return (~(sign | ((position - 5) << 4) | lsb));
}
/*
qint16 uLawDecode(qint8 number)
{
quint8 sign = 0, position = 0;
qint16 decoded = 0;
number = ~number;
if (number & 0x80)
{
number &= ~(1 << 7);
sign = -1;
}
position = ((number & 0xF0) >> 4) + 5;
decoded = ((1 << position) | ((number & 0x0F) << (position - 4))
| (1 << (position - 5))) - MULAW_BIAS;
return (sign == 0) ? (decoded) : (-(decoded));
}
*/
static qint16 uLawDecode(quint8 in)
qint16 uLawDecode(const quint8 in)
{
static const qint16 ulaw_decode[256] = {
-32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
@ -64,12 +82,9 @@ static qint16 uLawDecode(quint8 in)
244, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64,
56, 48, 40, 32, 24, 16, 8, 0 };
if (in == 0x02) // MUZERO
in = 0;
return ulaw_decode[in];
}
audioHandler::audioHandler(QObject* parent) :
QIODevice(parent),
isInitialized(false),
@ -80,7 +95,6 @@ audioHandler::audioHandler(QObject* parent) :
isInput(0),
volume(1.0f)
{
}
audioHandler::~audioHandler()
@ -167,25 +181,29 @@ void audioHandler::reinit()
// Calculate the minimum required audio buffer
// This may need work depending on how it performs on other platforms.
int audioBuffer = format.sampleRate() / 10;
audioBuffer = audioBuffer * (format.sampleSize() / 8);
//int audioBuffer = format.sampleRate() / 10;
//audioBuffer = audioBuffer * (format.sampleSize() / 8);
if (this->isInput)
{
// (Re)initialize audio input
delete audioInput;
audioInput = Q_NULLPTR;
if (audioInput != Q_NULLPTR)
delete audioInput;
audioInput = new QAudioInput(deviceInfo, format, this);
audioInput->setBufferSize(audioBuffer);
//audioInput->setBufferSize(audioBuffer);
//audioInput->setNotifyInterval(20);
connect(audioInput, SIGNAL(notify()), SLOT(notified()));
connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
}
else {
// (Re)initialize audio output
delete audioOutput;
if (audioOutput != Q_NULLPTR)
delete audioOutput;
audioOutput = Q_NULLPTR;
audioOutput = new QAudioOutput(deviceInfo, format, this);
audioOutput->setBufferSize(audioBuffer);
//audioOutput->setBufferSize(audioBuffer);
connect(audioOutput, SIGNAL(notify()), SLOT(notified()));
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
}
@ -193,8 +211,6 @@ void audioHandler::reinit()
if (running) {
this->start();
}
flush();
}
void audioHandler::start()
@ -212,7 +228,6 @@ void audioHandler::start()
else {
this->open(QIODevice::ReadOnly);
}
buffer.clear(); // No buffer used on audioinput.
if (isInput) {
@ -231,8 +246,6 @@ void audioHandler::setVolume(float volume)
void audioHandler::flush()
{
// Flushing buffers is a bit tricky...
// Don't modify this unless you're sure
qDebug() << this->metaObject()->className() << ": flush() running";
this->stop();
if (isInput) {
@ -246,9 +259,12 @@ void audioHandler::flush()
void audioHandler::stop()
{
QMutexLocker locker(&mutex);
if (audioOutput && audioOutput->state() != QAudio::StoppedState) {
// Stop audio output
audioOutput->stop();
QByteArray ret;
buffer.clear();
this->close();
}
@ -265,49 +281,51 @@ qint64 audioHandler::readData(char* data, qint64 maxlen)
{
// Calculate output length, always full samples
int outlen = 0;
if (isUlaw)
if (radioSampleBits == 8)
{
// Need to process uLaw.
// Input buffer is 8bit and output buffer is 16bit
outlen = qMin(buffer.length(), (int)maxlen / 2);
for (int f = 0; f < outlen; f++)
for (quint16 f = 0; f < outlen; f++)
{
qToLittleEndian<qint16>(uLawDecode(buffer.at(f)), data + f * 2);
if (isUlaw)
qToLittleEndian<qint16>(uLawDecode(buffer.at(f)), data + (f * 2));
else
qToLittleEndian<qint16>((qint16)(buffer[f] << 8) - 32640, data + (f * 2));
}
QMutexLocker locker(&mutex);
buffer.remove(0, outlen);
outlen = outlen * 2;
}
else {
if (radioSampleBits == 8)
{
outlen = qMin(buffer.length(), (int)maxlen/2);
for (int f = 0; f < outlen; f++)
{
qToLittleEndian<qint16>((qint16)(buffer[f]<<8) - 32640, data + f * 2);
}
buffer.remove(0, outlen);
outlen = outlen * 2;
} else {
// Just copy it.
outlen = qMin(buffer.length(), (int)maxlen);
if (outlen % 2 != 0) {
outlen += 1;
}
memcpy(data, buffer.data(), outlen);
else if (radioSampleBits == 16) {
// Just copy it
outlen = qMin(buffer.length(), (int)maxlen);
if (outlen % 2 != 0) {
outlen += 1; // Not sure if this is necessary as we should always have an even number!
}
memcpy(data, buffer.data(), outlen);
QMutexLocker locker(&mutex);
buffer.remove(0, outlen);
}
else {
qDebug() << "Sample bits MUST be 8 or 16 - got: " << radioSampleBits; // Should never happen?
}
return outlen;
}
qint64 audioHandler::writeData(const char* data, qint64 len)
{
int chunkSize = 960; // Assume 8bit or uLaw.
QMutexLocker locker(&mutex);
if (buffer.length() > bufferSize * 4)
{
qWarning() << "writeData() Buffer overflow";
buffer.clear(); // Will cause a click!
}
if (isUlaw) {
for (int f = 0; f < len / 2; f++)
{
buffer.append(uLawEncode(qFromLittleEndian<qint16>(data + f * 2)));
buffer.append((quint8)uLawEncode(qFromLittleEndian<qint16>(data + f * 2)));
}
}
else if (radioSampleBits == 8) {
@ -318,16 +336,12 @@ qint64 audioHandler::writeData(const char* data, qint64 len)
}
else if (radioSampleBits == 16) {
buffer.append(QByteArray::fromRawData(data, len));
chunkSize = 1920;
}
else {
qWarning() << "Unsupported number of bits! :" << radioSampleBits;
}
while (buffer.length() >= chunkSize)
{
emit haveAudioData(buffer.mid(0, chunkSize));
buffer.remove(0, chunkSize);
if (buffer.size() >= radioSampleBits * 120) {
chunkAvailable = true;
}
return (len); // Always return the same number as we received
}
@ -346,15 +360,18 @@ void audioHandler::notified()
{
}
void audioHandler::stateChanged(QAudio::State state)
{
if (state == QAudio::IdleState && audioOutput->error() == QAudio::UnderrunError) {
qDebug() << this->metaObject()->className() << ":Buffer underrun";
if (buffer.length() < bufferSize) {
audioOutput->suspend();
}
qDebug() << this->metaObject()->className() << "RX:Buffer underrun";
//if (buffer.length() < bufferSize) {
// audioOutput->suspend();
//}
}
qDebug() << this->metaObject()->className() << ": state = " << state;
//qDebug() << this->metaObject()->className() << ": state = " << state;
}
@ -362,21 +379,13 @@ void audioHandler::stateChanged(QAudio::State state)
void audioHandler::incomingAudio(const QByteArray& data)
{
//qDebug() << "Got " << data.length() << " samples";
QMutexLocker locker(&mutex);
if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) {
// Append input data to the end of buffer
QMutexLocker locker(&mutex);
buffer.append(data);
//if (buffer.length() > bufferSize*2) {
// this->flush();
//}
// If audio is suspended and buffer is full, resume
if (audioOutput->state() == QAudio::SuspendedState) {
if (buffer.length() >= bufferSize) {
qDebug() << this->metaObject()->className() << ": Resuming...";
audioOutput->resume();
}
qDebug() << "RX Audio Suspended, Resuming...";
audioOutput->resume();
}
}
else {
@ -389,7 +398,6 @@ void audioHandler::changeBufferSize(const quint16 newSize)
QMutexLocker locker(&mutex);
qDebug() << this->metaObject()->className() << ": Changing buffer size to: " << newSize << " from " << bufferSize;
bufferSize = newSize;
flush();
}
void audioHandler::getBufferSize()
@ -397,5 +405,20 @@ void audioHandler::getBufferSize()
emit sendBufferSize(audioOutput->bufferSize());
}
void audioHandler::getNextAudioChunk(QByteArray& ret)
{
quint16 numSamples = radioSampleBits * 120;
if (buffer.size() >= numSamples) {
QMutexLocker locker(&mutex);
ret.append(buffer.mid(0, numSamples));
buffer.remove(0, numSamples);
}
if (buffer.size() < numSamples)
{
chunkAvailable = false;
}
return;
}

Wyświetl plik

@ -13,6 +13,7 @@
#include <QAudioInput>
#include <QIODevice>
#include <QThread>
#include <QTimer>
#include <QDebug>
@ -39,10 +40,12 @@ public:
qint64 writeData(const char* data, qint64 len);
qint64 bytesAvailable() const;
bool isSequential() const;
volatile bool chunkAvailable;
void incomingAudio(const QByteArray& data);
void getNextAudioChunk(QByteArray &data);
public slots:
bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 bufferSize, const bool isulaw, const bool isinput);
void incomingAudio(const QByteArray& data);
void changeBufferSize(const quint16 newSize);
private slots:
@ -54,6 +57,7 @@ signals:
void sendBufferSize(quint16 newSize);
void haveAudioData(const QByteArray& data);
private:
void reinit();
@ -73,6 +77,7 @@ private:
quint16 radioSampleRate;
quint8 radioSampleBits;
};
#endif // AUDIOHANDLER_H

Wyświetl plik

@ -498,7 +498,7 @@ void udpSerial::DataReceived()
pkt7Timer->start(3000); // send pkt7 idle packets every 3 seconds
pkt0Timer = new QTimer(this);
connect(pkt0Timer, &QTimer::timeout, this, std::bind(&udpBase::SendPkt0Idle,this,true,0));
connect(pkt0Timer, &QTimer::timeout, this, std::bind(&udpBase::SendPkt0Idle, this, true, 0));
pkt0Timer->start(100);
periodicRunning = true;
@ -584,7 +584,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 b
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(haveAudioData(QByteArray)), rxaudio, SLOT(incomingAudio(QByteArray)));
//connect(this, SIGNAL(haveAudioData(QByteArray)), rxaudio, SLOT(incomingAudio(QByteArray)));
connect(this, SIGNAL(haveChangeBufferSize(quint16)), rxaudio, SLOT(changeBufferSize(quint16)));
connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater()));
@ -601,7 +601,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 b
txaudio->moveToThread(txAudioThread);
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(txaudio, SIGNAL(haveAudioData(QByteArray)), this, SLOT(sendTxAudio(QByteArray)));
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
rxAudioThread->start();
@ -624,36 +624,46 @@ udpAudio::~udpAudio()
txAudioThread->quit();
txAudioThread->wait();
}
if (txAudioTimer != Q_NULLPTR)
{
txAudioTimer->stop();
delete txAudioTimer;
}
}
void udpAudio::sendTxAudio(QByteArray audio)
void udpAudio::sendTxAudio()
{
quint8 p[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff),
0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
int counter = 0;
if (((txCodec == 0x01 || txCodec == 0x02) && audio.length() != 960) || (txCodec == 0x04 && audio.length() != 1920)) {
qDebug() << "Unsupported TX audio length :" << audio.length() << " With codec: " << txCodec;
//if (((txCodec == 0x01 || txCodec == 0x02) && audio.length() != 960) || (txCodec == 0x04 && audio.length() != 1920)) {
// qDebug() << "Unsupported TX audio length :" << audio.length() << " With codec: " << txCodec;
//}
if (txaudio->chunkAvailable) {
QByteArray audio;
txaudio->getNextAudioChunk(audio);
int counter = 0;
while (counter < audio.length()) {
QByteArray tx = QByteArray::fromRawData((const char*)p, sizeof(p));
QByteArray partial = audio.mid(counter, 1364);
tx.append(partial);
tx[0] = static_cast<quint8>(tx.length() & 0xff);
tx[1] = static_cast<quint8>(tx.length() >> 8 & 0xff);
tx[18] = static_cast<quint8>(sendAudioSeq >> 8 & 0xff);
tx[19] = static_cast<quint8>(sendAudioSeq & 0xff);
tx[22] = static_cast<quint8>(partial.length() >> 8 & 0xff);
tx[23] = static_cast<quint8>(partial.length() & 0xff);
counter = counter + partial.length();
//qDebug() << "Sending audio packet length: " << tx.length();
SendTrackedPacket(tx);
sendAudioSeq++;
}
}
while (counter < audio.length())
{
QByteArray tx = QByteArray::fromRawData((const char*)p, sizeof(p));
QByteArray partial = audio.mid(counter, 1364);
tx.append(partial);
tx[0] = static_cast<quint8>(tx.length() & 0xff);
tx[1] = static_cast<quint8>(tx.length() >> 8 & 0xff);
tx[18] = static_cast<quint8>(sendAudioSeq >> 8 & 0xff);
tx[19] = static_cast<quint8>(sendAudioSeq & 0xff);
tx[22] = static_cast<quint8>(partial.length() >> 8 & 0xff);
tx[23] = static_cast<quint8>(partial.length() & 0xff);
counter = counter + partial.length();
//qDebug() << "Sending audio packet length: " << tx.length();
SendTrackedPacket(tx);
sendAudioSeq++;
}
}
void udpAudio::changeBufferSize(quint16 value)
@ -661,6 +671,8 @@ void udpAudio::changeBufferSize(quint16 value)
emit haveChangeBufferSize(value);
}
void udpAudio::DataReceived()
{
while (udp->hasPendingDatagrams()) {
@ -677,9 +689,14 @@ void udpAudio::DataReceived()
remoteSID = qFromBigEndian<quint32>(r.mid(8, 4));
if (!periodicRunning) {
periodicRunning = true;
pkt7Timer = new QTimer(this);
connect(pkt7Timer, &QTimer::timeout, this, &udpBase::SendPkt7Idle);
pkt7Timer->start(3000); // send pkt7 idle packets every 3 seconds
txAudioTimer = new QTimer(this);
connect(txAudioTimer, &QTimer::timeout, this, &udpAudio::sendTxAudio);
txAudioTimer->start(10); // send pkt7 idle packets every 3 seconds
}
}
break;
@ -711,7 +728,7 @@ void udpAudio::DataReceived()
lastReceivedSeq = gotSeq;
emit haveAudioData(r.mid(24));
rxaudio->incomingAudio(r.mid(24));
}
break;
}

Wyświetl plik

@ -69,7 +69,7 @@ public:
quint16 port=0;
QTimer *pkt7Timer=Q_NULLPTR; // Send pkt7 packets every 3 seconds
QTimer *pkt0Timer=Q_NULLPTR; // Send pkt0 packets every 1000ms.
QTimer *periodic=Q_NULLPTR; // Send pkt0 packets every 1000ms.
QTimer* periodic = Q_NULLPTR; // Send pkt0 packets every 1000ms.
bool periodicRunning = false;
bool sentPacketConnect2 = false;
time_t lastReceived = time(0);
@ -132,10 +132,10 @@ signals:
public slots:
void changeBufferSize(quint16 value);
void sendTxAudio(QByteArray d);
private:
void sendTxAudio();
void DataReceived();
QAudioFormat format;
quint16 bufferSize;
@ -153,11 +153,13 @@ private:
bool sentPacketConnect2 = false;
uint16_t sendAudioSeq = 0;
audioHandler* rxaudio;
QThread* rxAudioThread;
audioHandler* rxaudio=Q_NULLPTR;
QThread* rxAudioThread=Q_NULLPTR;
audioHandler* txaudio;
QThread* txAudioThread;
audioHandler* txaudio=Q_NULLPTR;
QThread* txAudioThread=Q_NULLPTR;
QTimer* txAudioTimer=Q_NULLPTR;
};