kopia lustrzana https://gitlab.com/eliggett/wfview
Add initial TX audio support
rodzic
b8fe90ebc0
commit
b7f8ad1dee
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
This class handles both RX and TX audio, each is created as a seperate instance of the class
|
||||
but as the setup/handling if output (RX) and input (TX) devices is so similar I have combined them.
|
||||
*/
|
||||
#include "audiohandler.h"
|
||||
|
||||
int8_t uLawEncode(int16_t 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;
|
||||
if (number < 0)
|
||||
{
|
||||
number = -number;
|
||||
sign = 0x80;
|
||||
}
|
||||
number += MULAW_BIAS;
|
||||
if (number > MULAW_MAX)
|
||||
{
|
||||
number = MULAW_MAX;
|
||||
}
|
||||
for (; ((number & mask) != mask && position >= 5); mask >>= 1, position--)
|
||||
;
|
||||
lsb = (number >> (position - 4)) & 0x0f;
|
||||
return (~(sign | ((position - 5) << 4) | lsb));
|
||||
}
|
||||
|
||||
|
||||
static qint16 uLawDecode(quint8 in)
|
||||
{
|
||||
static const qint16 ulaw_decode[256] = {
|
||||
-32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
|
||||
-23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
|
||||
-15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
|
||||
-11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
|
||||
-7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
|
||||
-5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
|
||||
-3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
|
||||
-2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
|
||||
-1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
|
||||
-1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
|
||||
-876, -844, -812, -780, -748, -716, -684, -652,
|
||||
-620, -588, -556, -524, -492, -460, -428, -396,
|
||||
-372, -356, -340, -324, -308, -292, -276, -260,
|
||||
-244, -228, -212, -196, -180, -164, -148, -132,
|
||||
-120, -112, -104, -96, -88, -80, -72, -64,
|
||||
-56, -48, -40, -32, -24, -16, -8, 0,
|
||||
32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
|
||||
23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
|
||||
15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
|
||||
11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
|
||||
7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
|
||||
5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
|
||||
3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
|
||||
2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
|
||||
1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
|
||||
1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
|
||||
876, 844, 812, 780, 748, 716, 684, 652,
|
||||
620, 588, 556, 524, 492, 460, 428, 396,
|
||||
372, 356, 340, 324, 308, 292, 276, 260,
|
||||
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),
|
||||
audioOutput(0),
|
||||
volume(1.0f),
|
||||
isUlaw(false),
|
||||
bufferSize(0),
|
||||
isInput(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
audioHandler::~audioHandler()
|
||||
{
|
||||
stop();
|
||||
if (audioOutput != Q_NULLPTR) {
|
||||
delete audioOutput;
|
||||
}
|
||||
if (audioInput != Q_NULLPTR) {
|
||||
delete audioInput;
|
||||
}
|
||||
}
|
||||
|
||||
bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 buffer, const bool ulaw, const bool isinput)
|
||||
{
|
||||
if (isInitialized) {
|
||||
return false;
|
||||
}
|
||||
format.setSampleSize(bits);
|
||||
format.setChannelCount(channels);
|
||||
format.setSampleRate(samplerate);
|
||||
format.setCodec("audio/pcm");
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
|
||||
this->bufferSize = buffer;
|
||||
this->isUlaw = ulaw;
|
||||
this->isInput = isinput;
|
||||
|
||||
if (isInput)
|
||||
isInitialized = setDevice(QAudioDeviceInfo::defaultInputDevice());
|
||||
else
|
||||
isInitialized = setDevice(QAudioDeviceInfo::defaultOutputDevice());
|
||||
|
||||
this->start();
|
||||
return isInitialized;
|
||||
}
|
||||
|
||||
|
||||
bool audioHandler::setDevice(QAudioDeviceInfo deviceInfo)
|
||||
{
|
||||
qDebug() << this->metaObject()->className() << ": setDevice() running :" << deviceInfo.deviceName();
|
||||
if (!deviceInfo.isFormatSupported(format)) {
|
||||
if (deviceInfo.isNull())
|
||||
{
|
||||
qDebug() << "No audio device was found. You probably need to install libqt5multimedia-plugins.";
|
||||
}
|
||||
else {
|
||||
qDebug() << "Audio Devices found: ";
|
||||
const auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
|
||||
for (const QAudioDeviceInfo& deviceInfo : deviceInfos)
|
||||
{
|
||||
qDebug() << "Device name: " << deviceInfo.deviceName();
|
||||
qDebug() << "is null (probably not good):" << deviceInfo.isNull();
|
||||
qDebug() << "channel count:" << deviceInfo.supportedChannelCounts();
|
||||
qDebug() << "byte order:" << deviceInfo.supportedByteOrders();
|
||||
qDebug() << "supported codecs:" << deviceInfo.supportedCodecs();
|
||||
qDebug() << "sample rates:" << deviceInfo.supportedSampleRates();
|
||||
qDebug() << "sample sizes:" << deviceInfo.supportedSampleSizes();
|
||||
qDebug() << "sample types:" << deviceInfo.supportedSampleTypes();
|
||||
}
|
||||
qDebug() << "----- done with audio info -----";
|
||||
}
|
||||
|
||||
qDebug() << "Format not supported, choosing nearest supported format - which may not work!";
|
||||
deviceInfo.nearestFormat(format);
|
||||
}
|
||||
this->deviceInfo = deviceInfo;
|
||||
this->reinit();
|
||||
return true;
|
||||
}
|
||||
|
||||
void audioHandler::reinit()
|
||||
{
|
||||
qDebug() << this->metaObject()->className() << ": reinit() running";
|
||||
bool running = false;
|
||||
if (audioOutput && audioOutput->state() != QAudio::StoppedState) {
|
||||
running = true;
|
||||
}
|
||||
this->stop();
|
||||
|
||||
// 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);
|
||||
|
||||
if (this->isInput)
|
||||
{
|
||||
// (Re)initialize audio input
|
||||
delete audioInput;
|
||||
audioInput = Q_NULLPTR;
|
||||
audioInput = new QAudioInput(deviceInfo, format, this);
|
||||
audioInput->setBufferSize(audioBuffer);
|
||||
connect(audioInput, SIGNAL(notify()), SLOT(notified()));
|
||||
connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
|
||||
}
|
||||
else {
|
||||
// (Re)initialize audio output
|
||||
delete audioOutput;
|
||||
audioOutput = Q_NULLPTR;
|
||||
audioOutput = new QAudioOutput(deviceInfo, format, this);
|
||||
audioOutput->setBufferSize(audioBuffer);
|
||||
connect(audioOutput, SIGNAL(notify()), SLOT(notified()));
|
||||
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
|
||||
}
|
||||
|
||||
if (running) {
|
||||
this->start();
|
||||
}
|
||||
}
|
||||
|
||||
void audioHandler::start()
|
||||
{
|
||||
qDebug() << this->metaObject()->className() << ": start() running";
|
||||
|
||||
if ((audioOutput == Q_NULLPTR || audioOutput->state() != QAudio::StoppedState) &&
|
||||
(audioInput == Q_NULLPTR || audioInput->state() != QAudio::StoppedState) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInput) {
|
||||
this->open(QIODevice::WriteOnly);
|
||||
}
|
||||
else {
|
||||
this->open(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
buffer.clear(); // No buffer used on audioinput.
|
||||
|
||||
if (isInput) {
|
||||
audioInput->start(this);
|
||||
}
|
||||
else {
|
||||
audioOutput->start(this);
|
||||
}
|
||||
}
|
||||
|
||||
void audioHandler::setVolume(float volume)
|
||||
{
|
||||
volume = 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) {
|
||||
audioInput->reset();
|
||||
}
|
||||
else {
|
||||
audioOutput->reset();
|
||||
}
|
||||
this->start();
|
||||
}
|
||||
|
||||
void audioHandler::stop()
|
||||
{
|
||||
if (audioOutput && audioOutput->state() != QAudio::StoppedState) {
|
||||
// Stop audio output
|
||||
audioOutput->stop();
|
||||
buffer.clear();
|
||||
this->close();
|
||||
}
|
||||
|
||||
if (audioInput && audioInput->state() != QAudio::StoppedState) {
|
||||
// Stop audio output
|
||||
audioInput->stop();
|
||||
buffer.clear();
|
||||
this->close();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 audioHandler::readData(char* data, qint64 maxlen)
|
||||
{
|
||||
// Calculate output length, always full samples
|
||||
int outlen = 0;
|
||||
if (isUlaw)
|
||||
{
|
||||
// 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++)
|
||||
{
|
||||
qToLittleEndian<qint16>(uLawDecode(buffer.at(f)), 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);
|
||||
buffer.remove(0, outlen);
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
qint64 audioHandler::writeData(const char* data, qint64 len)
|
||||
{
|
||||
if (isUlaw) {
|
||||
QByteArray out;
|
||||
for (int f = 0; f < len / 2; f++)
|
||||
{
|
||||
out.append(uLawEncode(qFromLittleEndian<qint16>(data+f*2)));
|
||||
}
|
||||
emit haveAudioData(out);
|
||||
} else {
|
||||
emit haveAudioData(QByteArray::fromRawData(data, (len)));
|
||||
}
|
||||
return (len);
|
||||
}
|
||||
|
||||
qint64 audioHandler::bytesAvailable() const
|
||||
{
|
||||
return buffer.length() + QIODevice::bytesAvailable();
|
||||
}
|
||||
|
||||
bool audioHandler::isSequential() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
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() << ": 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
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
qDebug() << this->metaObject()->className() << ": Audio received from radio but audio output is stopped!";
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
emit sendBufferSize(audioOutput->bufferSize());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef AUDIOHANDLER_H
|
||||
#define AUDIOHANDLER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QtMultimedia/QAudioOutput>
|
||||
#include <QMutexLocker>
|
||||
#include <QByteArray>
|
||||
#include <QtEndian>
|
||||
#include <QAudioFormat>
|
||||
#include <QAudioDeviceInfo>
|
||||
#include <QAudioOutput>
|
||||
#include <QAudioInput>
|
||||
#include <QIODevice>
|
||||
#include <QThread>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
//#define BUFFER_SIZE (32*1024)
|
||||
|
||||
class audioHandler : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
audioHandler(QObject* parent = 0);
|
||||
~audioHandler();
|
||||
|
||||
void getBufferSize();
|
||||
|
||||
bool setDevice(QAudioDeviceInfo deviceInfo);
|
||||
|
||||
void start();
|
||||
void setVolume(float volume);
|
||||
void flush();
|
||||
void stop();
|
||||
|
||||
qint64 readData(char* data, qint64 maxlen);
|
||||
qint64 writeData(const char* data, qint64 len);
|
||||
qint64 bytesAvailable() const;
|
||||
bool isSequential() const;
|
||||
|
||||
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:
|
||||
void notified();
|
||||
void stateChanged(QAudio::State state);
|
||||
|
||||
signals:
|
||||
void audioMessage(QString message);
|
||||
void sendBufferSize(quint16 newSize);
|
||||
void haveAudioData(const QByteArray& data);
|
||||
|
||||
private:
|
||||
void reinit();
|
||||
|
||||
int bufferSize;
|
||||
QMutex mutex;
|
||||
bool isUlaw;
|
||||
bool isInput; // Used to determine whether input or output audio
|
||||
|
||||
bool isInitialized;
|
||||
QByteArray buffer;
|
||||
QAudioFormat format;
|
||||
QAudioDeviceInfo deviceInfo;
|
||||
QAudioOutput* audioOutput = Q_NULLPTR;
|
||||
QAudioInput* audioInput = Q_NULLPTR;
|
||||
float volume;
|
||||
|
||||
};
|
||||
|
||||
#endif // AUDIOHANDLER_H
|
|
@ -1,104 +0,0 @@
|
|||
#include "rxaudiohandler.h"
|
||||
|
||||
rxAudioHandler::rxAudioHandler()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
rxAudioHandler::~rxAudioHandler()
|
||||
{
|
||||
audio->stop();
|
||||
delete audio;
|
||||
}
|
||||
|
||||
void rxAudioHandler::process()
|
||||
{
|
||||
qDebug() << "rxAudio Handler created.";
|
||||
}
|
||||
|
||||
void rxAudioHandler::setup(const QAudioFormat format, const quint16 bufferSize, const bool isUlaw)
|
||||
{
|
||||
this->format = format;
|
||||
this->bufferSize = bufferSize;
|
||||
this->isUlaw = isUlaw;
|
||||
audio = new QAudioOutput(format);
|
||||
audio->setBufferSize(bufferSize);
|
||||
device = audio->start();
|
||||
}
|
||||
|
||||
|
||||
void rxAudioHandler::incomingAudio(const QByteArray data)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
if (isUlaw) {
|
||||
device->write(uLawDecode(data));
|
||||
}
|
||||
else {
|
||||
device->write(data, data.length());
|
||||
}
|
||||
}
|
||||
|
||||
void rxAudioHandler::changeBufferSize(const quint16 newSize)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
qDebug() << "Changing buffer size to: " << newSize << " from " << audio->bufferSize();
|
||||
audio->stop();
|
||||
audio->setBufferSize(newSize);
|
||||
device = audio->start();
|
||||
}
|
||||
|
||||
void rxAudioHandler::getBufferSize()
|
||||
{
|
||||
emit sendBufferSize(audio->bufferSize());
|
||||
}
|
||||
|
||||
|
||||
|
||||
QByteArray rxAudioHandler::uLawDecode(const QByteArray in)
|
||||
{
|
||||
static const qint16 ulaw_decode[256] = {
|
||||
-32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
|
||||
-23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
|
||||
-15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
|
||||
-11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
|
||||
-7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
|
||||
-5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
|
||||
-3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
|
||||
-2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
|
||||
-1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
|
||||
-1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
|
||||
-876, -844, -812, -780, -748, -716, -684, -652,
|
||||
-620, -588, -556, -524, -492, -460, -428, -396,
|
||||
-372, -356, -340, -324, -308, -292, -276, -260,
|
||||
-244, -228, -212, -196, -180, -164, -148, -132,
|
||||
-120, -112, -104, -96, -88, -80, -72, -64,
|
||||
-56, -48, -40, -32, -24, -16, -8, 0,
|
||||
32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
|
||||
23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
|
||||
15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
|
||||
11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
|
||||
7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
|
||||
5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
|
||||
3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
|
||||
2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
|
||||
1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
|
||||
1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
|
||||
876, 844, 812, 780, 748, 716, 684, 652,
|
||||
620, 588, 556, 524, 492, 460, 428, 396,
|
||||
372, 356, 340, 324, 308, 292, 276, 260,
|
||||
244, 228, 212, 196, 180, 164, 148, 132,
|
||||
120, 112, 104, 96, 88, 80, 72, 64,
|
||||
56, 48, 40, 32, 24, 16, 8, 0 };
|
||||
|
||||
QByteArray out;
|
||||
foreach(qint8 const temp, in)
|
||||
{
|
||||
qint16 decoded = ulaw_decode[0];
|
||||
if (temp != 0) {
|
||||
decoded = ulaw_decode[static_cast<quint8>(temp)];
|
||||
}
|
||||
out.append(static_cast<qint8>(decoded & 0xff));
|
||||
out.append(static_cast<qint8>(decoded >> 8 & 0xff));
|
||||
}
|
||||
return out;
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef RXAUDIOHANDLER_H
|
||||
#define RXAUDIOHANDLER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QtMultimedia/QAudioOutput>
|
||||
#include <QMutexLocker>
|
||||
#include <QIODevice>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
class rxAudioHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
rxAudioHandler();
|
||||
~rxAudioHandler();
|
||||
|
||||
|
||||
public slots:
|
||||
void process();
|
||||
void setup(const QAudioFormat format, const quint16 bufferSize, const bool isulaw);
|
||||
|
||||
void incomingAudio(const QByteArray data);
|
||||
void changeBufferSize(const quint16 newSize);
|
||||
void getBufferSize();
|
||||
|
||||
signals:
|
||||
void audioMessage(QString message);
|
||||
void sendBufferSize(quint16 newSize);
|
||||
|
||||
|
||||
private:
|
||||
QByteArray uLawDecode(const QByteArray in);
|
||||
|
||||
QAudioOutput* audio;
|
||||
QAudioFormat format;
|
||||
QIODevice* device;
|
||||
int bufferSize;
|
||||
QMutex mutex;
|
||||
bool isUlaw;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // RXAUDIOHANDLER_H
|
105
udphandler.cpp
105
udphandler.cpp
|
@ -311,7 +311,7 @@ qint64 udpHandler::SendRequestSerialAndAudio()
|
|||
*/
|
||||
|
||||
quint8* usernameEncoded = Passcode(username);
|
||||
int txSeqBufLengthMs = 200;
|
||||
int txSeqBufLengthMs = 50;
|
||||
const quint8 p[] = {
|
||||
0x90, 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),
|
||||
|
@ -574,59 +574,46 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 b
|
|||
rxChannelCount = 2;
|
||||
if (rxCodec == 0x02 || rxCodec == 0x8)
|
||||
rxNumSamples = 8; // uLaw is actually 16bit.
|
||||
|
||||
// Init audio
|
||||
format.setSampleRate(rxSampleRate);
|
||||
format.setChannelCount(rxChannelCount);
|
||||
format.setSampleSize(rxNumSamples);
|
||||
format.setCodec("audio/pcm");
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
|
||||
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
|
||||
if (txCodec == 0x01)
|
||||
txIsUlawCodec = true;
|
||||
else if (txCodec == 0x02)
|
||||
txNumSamples = 8; // uLaw is actually 16bit.
|
||||
|
||||
if (info.isFormatSupported(format))
|
||||
{
|
||||
qDebug() << "Audio format supported";
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Audio format not supported!";
|
||||
if (info.isNull())
|
||||
{
|
||||
qDebug() << "No device was found. You probably need to install libqt5multimedia-plugins.";
|
||||
}
|
||||
else {
|
||||
qDebug() << "Audio Devices found: ";
|
||||
const auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
|
||||
for (const QAudioDeviceInfo& deviceInfo : deviceInfos)
|
||||
{
|
||||
qDebug() << "Device name: " << deviceInfo.deviceName();
|
||||
qDebug() << "is null (probably not good):" << deviceInfo.isNull();
|
||||
qDebug() << "channel count:" << deviceInfo.supportedChannelCounts();
|
||||
qDebug() << "byte order:" << deviceInfo.supportedByteOrders();
|
||||
qDebug() << "supported codecs:" << deviceInfo.supportedCodecs();
|
||||
qDebug() << "sample rates:" << deviceInfo.supportedSampleRates();
|
||||
qDebug() << "sample sizes:" << deviceInfo.supportedSampleSizes();
|
||||
qDebug() << "sample types:" << deviceInfo.supportedSampleTypes();
|
||||
}
|
||||
qDebug() << "----- done with audio info -----";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rxaudio = new rxAudioHandler();
|
||||
|
||||
rxaudio = new audioHandler();
|
||||
rxAudioThread = new QThread(this);
|
||||
|
||||
rxaudio->moveToThread(rxAudioThread);
|
||||
|
||||
connect(this,SIGNAL(setupAudio(QAudioFormat,quint16,bool)), rxaudio, SLOT(setup(QAudioFormat,quint16,bool)));
|
||||
connect(this, SIGNAL(setupAudio(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)));
|
||||
connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater()));
|
||||
|
||||
if (txCodec == 0x01)
|
||||
txIsUlawCodec = true;
|
||||
else if (txCodec == 0x02)
|
||||
txNumSamples = 8; // uLaw is actually 16bit.
|
||||
|
||||
txChannelCount = 1; // Only 1 channel is supported.
|
||||
|
||||
txaudio = new audioHandler();
|
||||
txAudioThread = new QThread(this);
|
||||
|
||||
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(setupRxAudio(quint8, quint8, quint16, quint16, bool, bool)), rxaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool)));
|
||||
connect(txaudio, SIGNAL(haveAudioData(QByteArray)), this, SLOT(sendTxAudio(QByteArray)));
|
||||
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
|
||||
|
||||
rxAudioThread->start();
|
||||
emit setupAudio(format, bufferSize,rxIsUlawCodec);
|
||||
emit setupRxAudio(rxNumSamples, rxChannelCount, rxSampleRate, bufferSize, rxIsUlawCodec,false);
|
||||
|
||||
txAudioThread->start();
|
||||
emit setupTxAudio(txNumSamples, txChannelCount, txSampleRate, bufferSize, txIsUlawCodec,true);
|
||||
|
||||
SendPacketConnect(); // First connect packet, audio should start very soon after.
|
||||
}
|
||||
|
||||
|
@ -636,6 +623,35 @@ udpAudio::~udpAudio()
|
|||
rxAudioThread->quit();
|
||||
rxAudioThread->wait();
|
||||
}
|
||||
if (txAudioThread) {
|
||||
txAudioThread->quit();
|
||||
txAudioThread->wait();
|
||||
}
|
||||
}
|
||||
|
||||
void udpAudio::sendTxAudio(QByteArray audio)
|
||||
{
|
||||
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;
|
||||
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();
|
||||
SendTrackedPacket(tx);
|
||||
sendAudioSeq++;
|
||||
}
|
||||
}
|
||||
|
||||
void udpAudio::changeBufferSize(quint16 value)
|
||||
|
@ -897,8 +913,7 @@ void udpBase::PurgeOldEntries()
|
|||
{
|
||||
for (int f = txSeqBuf.length() - 1; f >= 0; f--)
|
||||
{
|
||||
// Delete any entries older than 1 second.
|
||||
if (difftime(time(NULL), txSeqBuf[f].timeSent) > 60) // Delete anything more than 60 seconds old.
|
||||
if (difftime(time(NULL), txSeqBuf[f].timeSent) > 5) // Delete anything more than 5 seconds old.
|
||||
{
|
||||
txSeqBuf.removeAt(f);
|
||||
}
|
||||
|
|
15
udphandler.h
15
udphandler.h
|
@ -8,6 +8,7 @@
|
|||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QDateTime>
|
||||
#include <QByteArray>
|
||||
|
||||
// Allow easy endian-ness conversions
|
||||
#include <QtEndian>
|
||||
|
@ -20,7 +21,7 @@
|
|||
|
||||
#include <QDebug>
|
||||
|
||||
#include "rxaudiohandler.h"
|
||||
#include "audiohandler.h"
|
||||
|
||||
// Parent class that contains all common items.
|
||||
class udpBase : public QObject
|
||||
|
@ -124,11 +125,14 @@ public:
|
|||
|
||||
signals:
|
||||
void haveAudioData(QByteArray data);
|
||||
void setupAudio(const QAudioFormat format, const quint16 bufferSize, const bool isulaw);
|
||||
void setupTxAudio(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 bufferSize, const bool isUlaw, const bool isInput);
|
||||
void haveChangeBufferSize(quint16 value);
|
||||
|
||||
public slots:
|
||||
void changeBufferSize(quint16 value);
|
||||
void sendTxAudio(QByteArray d);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
@ -149,10 +153,11 @@ private:
|
|||
bool sentPacketConnect2 = false;
|
||||
uint16_t sendAudioSeq = 0;
|
||||
|
||||
rxAudioHandler* rxaudio;
|
||||
QThread* rxAudioThread;
|
||||
|
||||
audioHandler* rxaudio;
|
||||
QThread* rxAudioThread;
|
||||
|
||||
audioHandler* txaudio;
|
||||
QThread* txAudioThread;
|
||||
};
|
||||
|
||||
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -664,8 +664,8 @@ void wfmain::loadSettings()
|
|||
|
||||
// Add codec combobox items here so that we can add userdata!
|
||||
ui->audioRXCodecCombo->addItem("LPCM 1ch 16bit", 4);
|
||||
ui->audioRXCodecCombo->addItem("LPCM 1ch 8bit", 1);
|
||||
ui->audioRXCodecCombo->addItem("uLaw 1ch 8bit", 2);
|
||||
ui->audioRXCodecCombo->addItem("LPCM 1ch 8bit", 2);
|
||||
ui->audioRXCodecCombo->addItem("uLaw 1ch 8bit", 1);
|
||||
ui->audioRXCodecCombo->addItem("LPCM 2ch 16bit", 16);
|
||||
ui->audioRXCodecCombo->addItem("uLaw 2ch 8bit", 32);
|
||||
ui->audioRXCodecCombo->addItem("PCM 2ch 8bit", 8);
|
||||
|
@ -677,8 +677,8 @@ void wfmain::loadSettings()
|
|||
ui->audioRXCodecCombo->setCurrentIndex(f);
|
||||
|
||||
ui->audioTXCodecCombo->addItem("LPCM 1ch 16bit", 4);
|
||||
ui->audioTXCodecCombo->addItem("LPCM 1ch 8bit", 1);
|
||||
ui->audioTXCodecCombo->addItem("uLaw 1ch 8bit", 2);
|
||||
ui->audioTXCodecCombo->addItem("LPCM 1ch 8bit", 2);
|
||||
ui->audioTXCodecCombo->addItem("uLaw 1ch 8bit", 1);
|
||||
|
||||
prefs.audioTXCodec = settings.value("AudioTXCodec", defPrefs.audioTXCodec).toInt();
|
||||
ui->audioTXCodecCombo->setEnabled(ui->lanEnableChk->isChecked());
|
||||
|
|
Ładowanie…
Reference in New Issue