2021-02-11 19:18:35 +00:00
|
|
|
#ifndef AUDIOHANDLER_H
|
|
|
|
#define AUDIOHANDLER_H
|
|
|
|
|
2022-04-05 15:47:43 +00:00
|
|
|
/* QT Headers */
|
2021-02-11 19:18:35 +00:00
|
|
|
#include <QObject>
|
|
|
|
#include <QByteArray>
|
2021-05-24 08:27:18 +00:00
|
|
|
#include <QMutex>
|
2021-05-27 12:54:52 +00:00
|
|
|
#include <QtEndian>
|
2021-05-29 17:59:45 +00:00
|
|
|
#include <QtMath>
|
2022-04-05 15:47:43 +00:00
|
|
|
#include <QThread>
|
|
|
|
#include <QTimer>
|
|
|
|
#include <QTime>
|
|
|
|
#include <QMap>
|
|
|
|
#include <QDebug>
|
2021-06-04 07:24:26 +00:00
|
|
|
|
2022-04-05 15:47:43 +00:00
|
|
|
/* QT Audio Headers */
|
2021-06-04 07:24:26 +00:00
|
|
|
#include <QAudioOutput>
|
|
|
|
#include <QAudioFormat>
|
|
|
|
#include <QAudioDeviceInfo>
|
|
|
|
#include <QAudioInput>
|
|
|
|
#include <QIODevice>
|
2022-01-29 22:50:58 +00:00
|
|
|
|
2022-04-05 15:47:43 +00:00
|
|
|
/* Current resampler code */
|
|
|
|
#include "resampler/speex_resampler.h"
|
2022-03-23 17:27:47 +00:00
|
|
|
|
2022-04-05 15:47:43 +00:00
|
|
|
/* Potential new resampler */
|
|
|
|
//#include <r8bbase.h>
|
|
|
|
//#include <CDSPResampler.h>
|
2021-11-07 14:11:22 +00:00
|
|
|
|
2021-07-29 15:18:59 +00:00
|
|
|
|
2022-04-05 15:47:43 +00:00
|
|
|
/* Opus */
|
2021-06-16 08:52:47 +00:00
|
|
|
#ifdef Q_OS_WIN
|
2021-06-16 08:49:38 +00:00
|
|
|
#include "opus.h"
|
2021-06-16 08:52:47 +00:00
|
|
|
#else
|
|
|
|
#include "opus/opus.h"
|
|
|
|
#endif
|
2021-05-16 12:34:04 +00:00
|
|
|
|
2022-04-05 15:47:43 +00:00
|
|
|
/* Eigen */
|
2022-04-03 23:23:23 +00:00
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
#include <eigen3/Eigen/Eigen>
|
|
|
|
#else
|
2022-04-03 19:16:52 +00:00
|
|
|
#include <Eigen/Eigen>
|
2022-04-03 23:23:23 +00:00
|
|
|
#endif
|
2022-04-03 19:16:52 +00:00
|
|
|
|
2022-04-05 15:47:43 +00:00
|
|
|
/* wfview Packet types */
|
|
|
|
#include "packettypes.h"
|
2021-02-11 19:18:35 +00:00
|
|
|
|
2022-04-05 15:47:43 +00:00
|
|
|
/* Logarithmic taper for volume control */
|
|
|
|
#include "audiotaper.h"
|
2021-02-27 00:37:00 +00:00
|
|
|
|
2021-05-16 20:16:59 +00:00
|
|
|
#define MULAW_BIAS 33
|
|
|
|
#define MULAW_MAX 0x1fff
|
|
|
|
|
2021-03-01 19:53:12 +00:00
|
|
|
struct audioPacket {
|
2021-03-14 08:44:30 +00:00
|
|
|
quint32 seq;
|
2021-02-27 00:37:00 +00:00
|
|
|
QTime time;
|
|
|
|
quint16 sent;
|
2021-05-27 10:41:08 +00:00
|
|
|
QByteArray data;
|
2022-01-29 22:50:58 +00:00
|
|
|
quint8 guid[GUIDLEN];
|
2021-02-27 00:37:00 +00:00
|
|
|
};
|
|
|
|
|
2021-06-04 07:24:26 +00:00
|
|
|
struct audioSetup {
|
|
|
|
QString name;
|
|
|
|
quint16 latency;
|
|
|
|
quint8 codec;
|
2022-04-03 19:16:52 +00:00
|
|
|
bool ulaw = false;
|
2021-06-04 07:24:26 +00:00
|
|
|
bool isinput;
|
2022-04-03 19:16:52 +00:00
|
|
|
QAudioFormat format; // Use this for all audio APIs
|
2021-06-04 07:24:26 +00:00
|
|
|
QAudioDeviceInfo port;
|
|
|
|
quint8 resampleQuality;
|
2021-07-05 23:45:19 +00:00
|
|
|
unsigned char localAFgain;
|
2021-06-04 07:24:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// For QtMultimedia, use a native QIODevice
|
2021-05-23 15:09:41 +00:00
|
|
|
class audioHandler : public QObject
|
2021-02-11 19:18:35 +00:00
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
audioHandler(QObject* parent = 0);
|
|
|
|
~audioHandler();
|
|
|
|
|
2021-05-27 17:34:44 +00:00
|
|
|
int getLatency();
|
2021-02-11 19:18:35 +00:00
|
|
|
|
2021-06-04 07:24:26 +00:00
|
|
|
void start();
|
|
|
|
void stop();
|
|
|
|
|
2021-02-13 11:04:26 +00:00
|
|
|
void getNextAudioChunk(QByteArray &data);
|
2022-01-22 15:12:36 +00:00
|
|
|
quint16 getAmplitude();
|
2021-05-23 15:09:41 +00:00
|
|
|
|
2021-06-04 07:24:26 +00:00
|
|
|
public slots:
|
|
|
|
bool init(audioSetup setup);
|
2021-02-27 00:37:00 +00:00
|
|
|
void changeLatency(const quint16 newSize);
|
2021-03-22 18:53:34 +00:00
|
|
|
void setVolume(unsigned char volume);
|
2021-05-27 17:34:44 +00:00
|
|
|
void incomingAudio(const audioPacket data);
|
2021-02-11 19:18:35 +00:00
|
|
|
|
2021-06-04 07:24:26 +00:00
|
|
|
private slots:
|
2022-04-06 20:52:22 +00:00
|
|
|
void stateChanged(QAudio::State state);
|
|
|
|
void clearUnderrun();
|
2021-06-04 07:24:26 +00:00
|
|
|
|
2021-02-11 19:18:35 +00:00
|
|
|
signals:
|
|
|
|
void audioMessage(QString message);
|
2021-02-27 00:37:00 +00:00
|
|
|
void sendLatency(quint16 newSize);
|
2021-02-11 19:18:35 +00:00
|
|
|
void haveAudioData(const QByteArray& data);
|
2022-04-06 20:52:22 +00:00
|
|
|
void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under);
|
2021-02-12 20:42:56 +00:00
|
|
|
|
2021-02-11 19:18:35 +00:00
|
|
|
private:
|
2021-06-04 07:24:26 +00:00
|
|
|
|
2022-04-06 20:52:22 +00:00
|
|
|
bool isUnderrun = false;
|
2021-06-03 11:05:28 +00:00
|
|
|
bool isInitialized=false;
|
2021-07-06 09:04:35 +00:00
|
|
|
bool isReady = false;
|
2022-01-14 20:23:59 +00:00
|
|
|
bool audioBuffered = false;
|
2022-04-03 23:01:08 +00:00
|
|
|
|
2021-06-04 07:24:26 +00:00
|
|
|
QAudioOutput* audioOutput=Q_NULLPTR;
|
|
|
|
QAudioInput* audioInput=Q_NULLPTR;
|
2022-04-03 19:16:52 +00:00
|
|
|
QIODevice* audioDevice=Q_NULLPTR;
|
2021-06-04 07:24:26 +00:00
|
|
|
QAudioFormat format;
|
|
|
|
QAudioDeviceInfo deviceInfo;
|
2022-04-03 23:01:08 +00:00
|
|
|
|
2021-05-23 15:09:41 +00:00
|
|
|
SpeexResamplerState* resampler = Q_NULLPTR;
|
2021-05-16 20:16:59 +00:00
|
|
|
|
2022-04-03 19:16:52 +00:00
|
|
|
//r8b::CFixedBuffer<double>* resampBufs;
|
|
|
|
//r8b::CPtrKeeper<r8b::CDSPResampler24*>* resamps;
|
|
|
|
|
2021-05-16 20:16:59 +00:00
|
|
|
quint16 audioLatency;
|
2021-03-03 09:49:49 +00:00
|
|
|
|
2021-06-04 07:24:26 +00:00
|
|
|
quint32 lastSeq;
|
2021-06-17 08:55:09 +00:00
|
|
|
quint32 lastSentSeq=0;
|
2022-04-03 19:16:52 +00:00
|
|
|
qint64 elapsedMs = 0;
|
2021-06-17 08:55:09 +00:00
|
|
|
|
2022-01-14 16:00:05 +00:00
|
|
|
int delayedPackets=0;
|
|
|
|
|
2021-11-07 13:27:52 +00:00
|
|
|
double resampleRatio;
|
2021-05-27 10:41:08 +00:00
|
|
|
|
|
|
|
volatile bool ready = false;
|
|
|
|
audioPacket tempBuf;
|
|
|
|
quint16 currentLatency;
|
2022-04-03 19:16:52 +00:00
|
|
|
float amplitude;
|
2021-05-27 12:54:52 +00:00
|
|
|
qreal volume=1.0;
|
2022-04-05 15:47:43 +00:00
|
|
|
|
2021-06-04 07:24:26 +00:00
|
|
|
audioSetup setup;
|
2022-04-05 15:47:43 +00:00
|
|
|
|
2021-06-16 08:49:38 +00:00
|
|
|
OpusEncoder* encoder=Q_NULLPTR;
|
|
|
|
OpusDecoder* decoder=Q_NULLPTR;
|
2022-04-06 21:02:43 +00:00
|
|
|
QTimer* underTimer;
|
2021-02-11 19:18:35 +00:00
|
|
|
};
|
|
|
|
|
2022-04-03 19:16:52 +00:00
|
|
|
|
|
|
|
// Various audio handling functions declared inline
|
|
|
|
|
|
|
|
static inline qint64 getAudioSize(qint64 timeInMs, const QAudioFormat& format)
|
|
|
|
{
|
2022-04-03 23:23:23 +00:00
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000) * timeInMs));
|
|
|
|
#else
|
2022-04-03 19:16:52 +00:00
|
|
|
qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(10000) * timeInMs));
|
2022-04-03 23:23:23 +00:00
|
|
|
#endif
|
|
|
|
|
2022-04-03 19:16:52 +00:00
|
|
|
|
|
|
|
if (value % (format.channelCount() * (format.sampleSize() / 8)) != 0)
|
|
|
|
value += (format.channelCount() * (format.sampleSize() / 8) - value % (format.channelCount() * (format.sampleSize() / 8)));
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline qint64 getAudioDuration(qint64 bytes, const QAudioFormat& format)
|
|
|
|
{
|
|
|
|
return qint64(qFloor(bytes / (format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000))));
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef Eigen::Matrix<quint8, Eigen::Dynamic, 1> VectorXuint8;
|
|
|
|
typedef Eigen::Matrix<qint8, Eigen::Dynamic, 1> VectorXint8;
|
|
|
|
typedef Eigen::Matrix<qint16, Eigen::Dynamic, 1> VectorXint16;
|
|
|
|
typedef Eigen::Matrix<qint32, Eigen::Dynamic, 1> VectorXint32;
|
|
|
|
|
|
|
|
static inline QByteArray samplesToInt(const QByteArray& data, const QAudioFormat& supported_format)
|
|
|
|
{
|
|
|
|
QByteArray input = data;
|
|
|
|
|
|
|
|
switch (supported_format.sampleSize())
|
|
|
|
{
|
|
|
|
case 8:
|
|
|
|
{
|
|
|
|
switch (supported_format.sampleType())
|
|
|
|
{
|
|
|
|
case QAudioFormat::UnSignedInt:
|
|
|
|
{
|
|
|
|
Eigen::Ref<Eigen::VectorXf> samples_float = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(input.data()), input.size() / int(sizeof(float)));
|
|
|
|
|
|
|
|
Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits<quint8>::max());
|
|
|
|
|
|
|
|
VectorXuint8 samples_int = samples_int_tmp.cast<quint8>();
|
|
|
|
|
|
|
|
QByteArray raw = QByteArray(reinterpret_cast<char*>(samples_int.data()), int(samples_int.size()) * int(sizeof(quint8)));
|
|
|
|
|
|
|
|
return raw;
|
|
|
|
}
|
|
|
|
case QAudioFormat::SignedInt:
|
|
|
|
{
|
|
|
|
Eigen::Ref<Eigen::VectorXf> samples_float = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(input.data()), input.size() / int(sizeof(float)));
|
|
|
|
|
|
|
|
Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits<qint8>::max());
|
|
|
|
|
|
|
|
VectorXint8 samples_int = samples_int_tmp.cast<qint8>();
|
|
|
|
|
|
|
|
QByteArray raw = QByteArray(reinterpret_cast<char*>(samples_int.data()), int(samples_int.size()) * int(sizeof(qint8)));
|
|
|
|
|
|
|
|
return raw;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 16:
|
|
|
|
{
|
|
|
|
switch (supported_format.sampleType())
|
|
|
|
{
|
|
|
|
case QAudioFormat::SignedInt:
|
|
|
|
{
|
|
|
|
Eigen::Ref<Eigen::VectorXf> samples_float = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(input.data()), input.size() / int(sizeof(float)));
|
|
|
|
|
|
|
|
Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits<qint16>::max());
|
|
|
|
|
|
|
|
VectorXint16 samples_int = samples_int_tmp.cast<qint16>();
|
|
|
|
|
|
|
|
QByteArray raw = QByteArray(reinterpret_cast<char*>(samples_int.data()), int(samples_int.size()) * int(sizeof(qint16)));
|
|
|
|
|
|
|
|
return raw;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 32:
|
|
|
|
{
|
|
|
|
switch (supported_format.sampleType())
|
|
|
|
{
|
|
|
|
case QAudioFormat::SignedInt:
|
|
|
|
{
|
|
|
|
Eigen::Ref<Eigen::VectorXf> samples_float = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(input.data()), input.size() / int(sizeof(float)));
|
|
|
|
|
|
|
|
Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits<qint32>::max());
|
|
|
|
|
|
|
|
VectorXint32 samples_int = samples_int_tmp.cast<qint32>();
|
|
|
|
|
|
|
|
QByteArray raw = QByteArray(reinterpret_cast<char*>(samples_int.data()), int(samples_int.size()) * int(sizeof(qint32)));
|
|
|
|
|
|
|
|
return raw;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline QByteArray samplesToFloat(const QByteArray& data, const QAudioFormat& supported_format)
|
|
|
|
{
|
|
|
|
QByteArray input = data;
|
|
|
|
|
|
|
|
switch (supported_format.sampleSize())
|
|
|
|
{
|
|
|
|
case 8:
|
|
|
|
{
|
|
|
|
switch (supported_format.sampleType())
|
|
|
|
{
|
|
|
|
case QAudioFormat::UnSignedInt:
|
|
|
|
{
|
|
|
|
QByteArray raw = input;
|
|
|
|
|
|
|
|
Eigen::Ref<VectorXuint8> samples_int = Eigen::Map<VectorXuint8>(reinterpret_cast<quint8*>(raw.data()), raw.size() / int(sizeof(quint8)));
|
|
|
|
|
|
|
|
Eigen::VectorXf samples_float = samples_int.cast<float>() / float(std::numeric_limits<quint8>::max());
|
|
|
|
|
|
|
|
return QByteArray(reinterpret_cast<char*>(samples_float.data()), int(samples_float.size()) * int(sizeof(float)));
|
|
|
|
}
|
|
|
|
case QAudioFormat::SignedInt:
|
|
|
|
{
|
|
|
|
QByteArray raw = input;
|
|
|
|
|
|
|
|
Eigen::Ref<VectorXint8> samples_int = Eigen::Map<VectorXint8>(reinterpret_cast<qint8*>(raw.data()), raw.size() / int(sizeof(qint8)));
|
|
|
|
|
|
|
|
Eigen::VectorXf samples_float = samples_int.cast<float>() / float(std::numeric_limits<qint8>::max());
|
|
|
|
|
|
|
|
return QByteArray(reinterpret_cast<char*>(samples_float.data()), int(samples_float.size()) * int(sizeof(float)));
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 16:
|
|
|
|
{
|
|
|
|
switch (supported_format.sampleType())
|
|
|
|
{
|
|
|
|
case QAudioFormat::SignedInt:
|
|
|
|
{
|
|
|
|
QByteArray raw = input;
|
|
|
|
|
|
|
|
Eigen::Ref<VectorXint16> samples_int = Eigen::Map<VectorXint16>(reinterpret_cast<qint16*>(raw.data()), raw.size() / int(sizeof(qint16)));
|
|
|
|
|
|
|
|
Eigen::VectorXf samples_float = samples_int.cast<float>() / float(std::numeric_limits<qint16>::max());
|
|
|
|
|
|
|
|
return QByteArray(reinterpret_cast<char*>(samples_float.data()), int(samples_float.size()) * int(sizeof(float)));
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 32:
|
|
|
|
{
|
|
|
|
switch (supported_format.sampleType())
|
|
|
|
{
|
|
|
|
case QAudioFormat::SignedInt:
|
|
|
|
{
|
|
|
|
QByteArray raw = input;
|
|
|
|
|
|
|
|
Eigen::Ref<VectorXint32> samples_int = Eigen::Map<VectorXint32>(reinterpret_cast<qint32*>(raw.data()), raw.size() / int(sizeof(qint32)));
|
|
|
|
|
|
|
|
Eigen::VectorXf samples_float = samples_int.cast<float>() / float(std::numeric_limits<qint32>::max());
|
|
|
|
|
|
|
|
return QByteArray(reinterpret_cast<char*>(samples_float.data()), int(samples_float.size()) * int(sizeof(float)));
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QByteArray();
|
|
|
|
}
|
2021-02-11 19:18:35 +00:00
|
|
|
#endif // AUDIOHANDLER_H
|