kopia lustrzana https://gitlab.com/eliggett/wfview
Merge branch 'wfserver' into shuttle
commit
11c0973897
|
@ -0,0 +1,313 @@
|
|||
#include "audioconverter.h"
|
||||
#include "logcategories.h"
|
||||
#include "ulaw.h"
|
||||
|
||||
audioConverter::audioConverter(QObject* parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool audioConverter::init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 opusComplexity, quint8 resampleQuality)
|
||||
{
|
||||
|
||||
this->inFormat = inFormat;
|
||||
this->outFormat = outFormat;
|
||||
this->opusComplexity = opusComplexity;
|
||||
this->resampleQuality = resampleQuality;
|
||||
|
||||
qInfo(logAudioConverter) << "Starting audioConverter() Input:" << inFormat.channelCount() << "Channels of" << inFormat.codec() << inFormat.sampleRate() << inFormat.sampleType() << inFormat.sampleSize() <<
|
||||
"Output:" << outFormat.channelCount() << "Channels of" << outFormat.codec() << outFormat.sampleRate() << outFormat.sampleType() << outFormat.sampleSize();
|
||||
|
||||
if (inFormat.byteOrder() != outFormat.byteOrder()) {
|
||||
qInfo(logAudioConverter) << "Byteorder mismatch in:" << inFormat.byteOrder() << "out:" << outFormat.byteOrder();
|
||||
}
|
||||
|
||||
|
||||
if (inFormat.codec() == "audio/opus")
|
||||
{
|
||||
// Create instance of opus decoder
|
||||
int opus_err = 0;
|
||||
opusDecoder = opus_decoder_create(inFormat.sampleRate(), inFormat.channelCount(), &opus_err);
|
||||
qInfo(logAudioConverter()) << "Creating opus decoder: " << opus_strerror(opus_err);
|
||||
}
|
||||
|
||||
if (outFormat.codec() == "audio/opus")
|
||||
{
|
||||
// Create instance of opus encoder
|
||||
int opus_err = 0;
|
||||
opusEncoder = opus_encoder_create(outFormat.sampleRate(), outFormat.channelCount(), OPUS_APPLICATION_AUDIO, &opus_err);
|
||||
//opus_encoder_ctl(opusEncoder, OPUS_SET_LSB_DEPTH(16));
|
||||
//opus_encoder_ctl(opusEncoder, OPUS_SET_INBAND_FEC(1));
|
||||
//opus_encoder_ctl(opusEncoder, OPUS_SET_DTX(1));
|
||||
//opus_encoder_ctl(opusEncoder, OPUS_SET_PACKET_LOSS_PERC(5));
|
||||
opus_encoder_ctl(opusEncoder, OPUS_SET_COMPLEXITY(opusComplexity)); // Reduce complexity to maybe lower CPU?
|
||||
qInfo(logAudioConverter()) << "Creating opus encoder: " << opus_strerror(opus_err);
|
||||
}
|
||||
|
||||
if (inFormat.sampleRate() != outFormat.sampleRate())
|
||||
{
|
||||
int resampleError = 0;
|
||||
unsigned int ratioNum;
|
||||
unsigned int ratioDen;
|
||||
// Sample rate conversion required.
|
||||
resampler = wf_resampler_init(outFormat.channelCount(), inFormat.sampleRate(), outFormat.sampleRate(), resampleQuality, &resampleError);
|
||||
wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen);
|
||||
resampleRatio = static_cast<double>(ratioDen) / ratioNum;
|
||||
qInfo(logAudioConverter()) << "wf_resampler_init() returned: " << resampleError << " resampleRatio: " << resampleRatio;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
audioConverter::~audioConverter()
|
||||
{
|
||||
|
||||
qInfo(logAudioConverter) << "Closing audioConverter() Input:" << inFormat.channelCount() << "Channels of" << inFormat.codec() << inFormat.sampleRate() << inFormat.sampleType() << inFormat.sampleSize() <<
|
||||
"Output:" << outFormat.channelCount() << "Channels of" << outFormat.codec() << outFormat.sampleRate() << outFormat.sampleType() << outFormat.sampleSize();
|
||||
|
||||
if (opusEncoder != Q_NULLPTR) {
|
||||
qInfo(logAudioConverter()) << "Destroying opus encoder";
|
||||
opus_encoder_destroy(opusEncoder);
|
||||
}
|
||||
|
||||
if (opusDecoder != Q_NULLPTR) {
|
||||
qInfo(logAudioConverter()) << "Destroying opus decoder";
|
||||
opus_decoder_destroy(opusDecoder);
|
||||
}
|
||||
|
||||
if (resampler != Q_NULLPTR) {
|
||||
speex_resampler_destroy(resampler);
|
||||
qDebug(logAudioConverter()) << "Resampler closed";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool audioConverter::convert(audioPacket audio)
|
||||
{
|
||||
|
||||
// If inFormat and outFormat are identical, just emit the data back (removed as it doesn't then process amplitude)
|
||||
if (audio.data.size() > 0)
|
||||
{
|
||||
|
||||
if (inFormat.codec() == "audio/opus")
|
||||
{
|
||||
unsigned char* in = (unsigned char*)audio.data.data();
|
||||
|
||||
//Decode the frame.
|
||||
int nSamples = opus_packet_get_nb_samples(in, audio.data.size(), inFormat.sampleRate());
|
||||
if (nSamples == -1) {
|
||||
// No opus data yet?
|
||||
return false;
|
||||
}
|
||||
QByteArray outPacket(nSamples * sizeof(float) * inFormat.channelCount(), (char)0xff); // Preset the output buffer size.
|
||||
float* out = (float*)outPacket.data();
|
||||
|
||||
//if (audio.seq > lastAudioSequence + 1) {
|
||||
// nSamples = opus_decode_float(opusDecoder, Q_NULLPTR, 0, out, nSamples, 1);
|
||||
//}
|
||||
//else {
|
||||
nSamples = opus_decode_float(opusDecoder, in, audio.data.size(), out, nSamples, 0);
|
||||
//}
|
||||
//lastAudioSequence = audio.seq;
|
||||
audio.data.clear();
|
||||
audio.data = outPacket; // Replace incoming data with converted.
|
||||
}
|
||||
else if (inFormat.codec() == "audio/PCMU")
|
||||
{
|
||||
// Current packet is "technically" 8bit so need to create a new buffer that is 16bit
|
||||
QByteArray outPacket((int)audio.data.length() * 2, (char)0xff);
|
||||
qint16* out = (qint16*)outPacket.data();
|
||||
for (int f = 0; f < audio.data.length(); f++)
|
||||
{
|
||||
*out++ = ulaw_decode[(quint8)audio.data[f]];
|
||||
}
|
||||
audio.data.clear();
|
||||
audio.data = outPacket; // Replace incoming data with converted.
|
||||
// Make sure that sample size/type is set correctly
|
||||
}
|
||||
|
||||
Eigen::VectorXf samplesF;
|
||||
|
||||
if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 32)
|
||||
{
|
||||
Eigen::Ref<VectorXint32> samplesI = Eigen::Map<VectorXint32>(reinterpret_cast<qint32*>(audio.data.data()), audio.data.size() / int(sizeof(qint32)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint32>::max());
|
||||
}
|
||||
else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 16)
|
||||
{
|
||||
Eigen::Ref<VectorXint16> samplesI = Eigen::Map<VectorXint16>(reinterpret_cast<qint16*>(audio.data.data()), audio.data.size() / int(sizeof(qint16)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint16>::max());
|
||||
}
|
||||
else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 8)
|
||||
{
|
||||
Eigen::Ref<VectorXint8> samplesI = Eigen::Map<VectorXint8>(reinterpret_cast<qint8*>(audio.data.data()), audio.data.size() / int(sizeof(qint8)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint8>::max());;
|
||||
}
|
||||
else if (inFormat.sampleType() == QAudioFormat::UnSignedInt && inFormat.sampleSize() == 8)
|
||||
{
|
||||
Eigen::Ref<VectorXuint8> samplesI = Eigen::Map<VectorXuint8>(reinterpret_cast<quint8*>(audio.data.data()), audio.data.size() / int(sizeof(quint8)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<quint8>::max());;
|
||||
}
|
||||
else if (inFormat.sampleType() == QAudioFormat::Float) {
|
||||
samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(audio.data.data()), audio.data.size() / int(sizeof(float)));
|
||||
}
|
||||
else
|
||||
{
|
||||
qInfo(logAudio()) << "Unsupported Input Sample Type:" << inFormat.sampleType() << "Size:" << inFormat.sampleSize();
|
||||
}
|
||||
|
||||
if (samplesF.size() > 0)
|
||||
|
||||
{
|
||||
audio.amplitude = samplesF.array().abs().maxCoeff();
|
||||
// Set the volume
|
||||
samplesF *= audio.volume;
|
||||
|
||||
/*
|
||||
samplesF is now an Eigen Vector of the current samples in float format
|
||||
The next step is to convert to the correct number of channels in outFormat.channelCount()
|
||||
*/
|
||||
|
||||
|
||||
if (inFormat.channelCount() == 2 && outFormat.channelCount() == 1) {
|
||||
// If we need to drop one of the audio channels, do it now
|
||||
Eigen::VectorXf samplesTemp(samplesF.size() / 2);
|
||||
samplesTemp = Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesF.data(), samplesF.size() / 2);
|
||||
samplesF = samplesTemp;
|
||||
}
|
||||
else if (inFormat.channelCount() == 1 && outFormat.channelCount() == 2) {
|
||||
// Convert mono to stereo if required
|
||||
Eigen::VectorXf samplesTemp(samplesF.size() * 2);
|
||||
Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesTemp.data(), samplesF.size()) = samplesF;
|
||||
Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesTemp.data() + 1, samplesF.size()) = samplesF;
|
||||
samplesF = samplesTemp;
|
||||
}
|
||||
|
||||
/*
|
||||
Next step is to resample (if needed)
|
||||
*/
|
||||
|
||||
if (resampler != Q_NULLPTR && resampleRatio != 1.0)
|
||||
{
|
||||
quint32 outFrames = ((samplesF.size() / outFormat.channelCount()) * resampleRatio);
|
||||
quint32 inFrames = (samplesF.size() / outFormat.channelCount());
|
||||
QByteArray outPacket(outFrames * outFormat.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size.
|
||||
const float* in = (float*)samplesF.data();
|
||||
float* out = (float*)outPacket.data();
|
||||
|
||||
int err = 0;
|
||||
if (outFormat.channelCount() == 1) {
|
||||
err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames);
|
||||
}
|
||||
else {
|
||||
err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
qInfo(logAudioConverter()) << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
|
||||
}
|
||||
samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(outPacket.data()), outPacket.size() / int(sizeof(float)));
|
||||
}
|
||||
|
||||
/*
|
||||
If output is Opus so encode it now, don't do any more conversion on the output of Opus.
|
||||
*/
|
||||
|
||||
if (outFormat.codec() == "audio/opus")
|
||||
{
|
||||
float* in = (float*)samplesF.data();
|
||||
QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size
|
||||
unsigned char* out = (unsigned char*)outPacket.data();
|
||||
|
||||
int nbBytes = opus_encode_float(opusEncoder, in, (samplesF.size() / outFormat.channelCount()), out, outPacket.length());
|
||||
if (nbBytes < 0)
|
||||
{
|
||||
qInfo(logAudioConverter()) << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size();
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
outPacket.resize(nbBytes);
|
||||
audio.data.clear();
|
||||
audio.data = outPacket; // Copy output packet back to input buffer.
|
||||
//samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(outPacket.data()), outPacket.size() / int(sizeof(float)));
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
/*
|
||||
Now convert back into the output format required
|
||||
*/
|
||||
audio.data.clear();
|
||||
|
||||
if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint8>::max());
|
||||
samplesITemp.array() += 127;
|
||||
VectorXuint8 samplesI = samplesITemp.cast<quint8>();
|
||||
audio.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8)));
|
||||
}
|
||||
else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint8>::max());
|
||||
VectorXint8 samplesI = samplesITemp.cast<qint8>();
|
||||
audio.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8)));
|
||||
}
|
||||
else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint16>::max());
|
||||
VectorXint16 samplesI = samplesITemp.cast<qint16>();
|
||||
audio.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16)));
|
||||
}
|
||||
else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 32)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint32>::max());
|
||||
VectorXint32 samplesI = samplesITemp.cast<qint32>();
|
||||
audio.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32)));
|
||||
}
|
||||
else if (outFormat.sampleType() == QAudioFormat::Float)
|
||||
{
|
||||
audio.data = QByteArray(reinterpret_cast<char*>(samplesF.data()), int(samplesF.size()) * int(sizeof(float)));
|
||||
}
|
||||
else {
|
||||
qInfo(logAudio()) << "Unsupported Output Sample Type:" << outFormat.sampleType() << "Size:" << outFormat.sampleSize();
|
||||
}
|
||||
|
||||
/*
|
||||
As we currently don't have a float based uLaw encoder, this must be done
|
||||
after all other conversion has taken place.
|
||||
*/
|
||||
|
||||
if (outFormat.codec() == "audio/PCMU")
|
||||
{
|
||||
QByteArray outPacket((int)audio.data.length() / 2, (char)0xff);
|
||||
qint16* in = (qint16*)audio.data.data();
|
||||
for (int f = 0; f < outPacket.length(); f++)
|
||||
{
|
||||
qint16 sample = *in++;
|
||||
int sign = (sample >> 8) & 0x80;
|
||||
if (sign)
|
||||
sample = (short)-sample;
|
||||
if (sample > cClip)
|
||||
sample = cClip;
|
||||
sample = (short)(sample + cBias);
|
||||
int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF];
|
||||
int mantissa = (sample >> (exponent + 3)) & 0x0F;
|
||||
int compressedByte = ~(sign | (exponent << 4) | mantissa);
|
||||
outPacket[f] = (quint8)compressedByte;
|
||||
}
|
||||
audio.data.clear();
|
||||
audio.data = outPacket; // Copy output packet back to input buffer.
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug(logAudioConverter) << "Detected empty packet";
|
||||
}
|
||||
}
|
||||
|
||||
emit converted(audio);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
#ifndef AUDIOCONVERTER_H
|
||||
#define AUDIOCONVERTER_H
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QTime>
|
||||
#include <QMap>
|
||||
#include <QDebug>
|
||||
#include <QAudioFormat>
|
||||
#include <QAudioDeviceInfo>
|
||||
|
||||
/* Opus and Eigen */
|
||||
#ifdef Q_OS_WIN
|
||||
#include "opus.h"
|
||||
#include <Eigen/Eigen>
|
||||
#else
|
||||
#include "opus/opus.h"
|
||||
#include <eigen3/Eigen/Eigen>
|
||||
#endif
|
||||
|
||||
enum audioType {qtAudio,portAudio,rtAudio};
|
||||
|
||||
#include "resampler/speex_resampler.h"
|
||||
|
||||
#include "packettypes.h"
|
||||
|
||||
struct audioPacket {
|
||||
quint32 seq;
|
||||
QTime time;
|
||||
quint16 sent;
|
||||
QByteArray data;
|
||||
quint8 guid[GUIDLEN];
|
||||
float amplitude;
|
||||
qreal volume = 1.0;
|
||||
};
|
||||
|
||||
struct audioSetup {
|
||||
audioType type;
|
||||
QString name;
|
||||
quint16 latency;
|
||||
quint8 codec;
|
||||
bool ulaw = false;
|
||||
bool isinput;
|
||||
quint32 sampleRate;
|
||||
QAudioDeviceInfo port;
|
||||
int portInt;
|
||||
quint8 resampleQuality;
|
||||
unsigned char localAFgain;
|
||||
quint16 blockSize = 20; // Each 'block' of audio is 20ms long by default.
|
||||
quint8 guid[GUIDLEN];
|
||||
};
|
||||
|
||||
class audioConverter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit audioConverter(QObject* parent = nullptr);;
|
||||
~audioConverter();
|
||||
|
||||
public slots:
|
||||
bool init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 opusComplexity, quint8 resampleQuality);
|
||||
bool convert(audioPacket audio);
|
||||
|
||||
signals:
|
||||
void converted(audioPacket audio);
|
||||
|
||||
protected:
|
||||
QAudioFormat inFormat;
|
||||
QAudioFormat outFormat;
|
||||
OpusEncoder* opusEncoder = Q_NULLPTR;
|
||||
OpusDecoder* opusDecoder = Q_NULLPTR;
|
||||
SpeexResamplerState* resampler = Q_NULLPTR;
|
||||
quint8 opusComplexity;
|
||||
quint8 resampleQuality = 4;
|
||||
double resampleRatio=1.0; // Default resample ratio is 1:1
|
||||
quint32 lastAudioSequence;
|
||||
};
|
||||
|
||||
|
||||
// Various audio handling functions declared inline
|
||||
|
||||
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 QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate)
|
||||
{
|
||||
QAudioFormat format;
|
||||
|
||||
/*
|
||||
0x01 uLaw 1ch 8bit
|
||||
0x02 PCM 1ch 8bit
|
||||
0x04 PCM 1ch 16bit
|
||||
0x08 PCM 2ch 8bit
|
||||
0x10 PCM 2ch 16bit
|
||||
0x20 uLaw 2ch 8bit
|
||||
0x40 Opus 1ch
|
||||
0x80 Opus 2ch
|
||||
*/
|
||||
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setCodec("audio/pcm");
|
||||
format.setSampleRate(sampleRate);
|
||||
|
||||
if (codec == 0x01 || codec == 0x20) {
|
||||
/* Set sample to be what is expected by the encoder and the output of the decoder */
|
||||
format.setSampleSize(16);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
format.setCodec("audio/PCMU");
|
||||
}
|
||||
|
||||
if (codec == 0x02 || codec == 0x08) {
|
||||
format.setSampleSize(8);
|
||||
format.setSampleType(QAudioFormat::UnSignedInt);
|
||||
}
|
||||
if (codec == 0x08 || codec == 0x10 || codec == 0x20 || codec == 0x80) {
|
||||
format.setChannelCount(2);
|
||||
} else {
|
||||
format.setChannelCount(1);
|
||||
}
|
||||
|
||||
if (codec == 0x04 || codec == 0x10) {
|
||||
format.setSampleSize(16);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
}
|
||||
|
||||
if (codec == 0x40 || codec == 0x80) {
|
||||
format.setSampleSize(32);
|
||||
format.setSampleType(QAudioFormat::Float);
|
||||
format.setCodec("audio/opus");
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
#endif
|
630
audiohandler.cpp
630
audiohandler.cpp
|
@ -10,7 +10,7 @@
|
|||
|
||||
|
||||
|
||||
audioHandler::audioHandler(QObject* parent)
|
||||
audioHandler::audioHandler(QObject* parent) : QObject(parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
}
|
||||
|
@ -31,169 +31,145 @@ audioHandler::~audioHandler()
|
|||
audioOutput = Q_NULLPTR;
|
||||
}
|
||||
|
||||
if (audioTimer != Q_NULLPTR) {
|
||||
audioTimer->stop();
|
||||
delete audioTimer;
|
||||
audioTimer = Q_NULLPTR;
|
||||
if (converterThread != Q_NULLPTR) {
|
||||
converterThread->quit();
|
||||
converterThread->wait();
|
||||
}
|
||||
|
||||
|
||||
if (resampler != Q_NULLPTR) {
|
||||
speex_resampler_destroy(resampler);
|
||||
qDebug(logAudio()) << "Resampler closed";
|
||||
}
|
||||
if (encoder != Q_NULLPTR) {
|
||||
qInfo(logAudio()) << "Destroying opus encoder";
|
||||
opus_encoder_destroy(encoder);
|
||||
}
|
||||
if (decoder != Q_NULLPTR) {
|
||||
qInfo(logAudio()) << "Destroying opus decoder";
|
||||
opus_decoder_destroy(decoder);
|
||||
}
|
||||
}
|
||||
|
||||
bool audioHandler::init(audioSetup setupIn)
|
||||
}bool audioHandler::init(audioSetup setup)
|
||||
{
|
||||
if (isInitialized) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
0x01 uLaw 1ch 8bit
|
||||
0x02 PCM 1ch 8bit
|
||||
0x04 PCM 1ch 16bit
|
||||
0x08 PCM 2ch 8bit
|
||||
0x10 PCM 2ch 16bit
|
||||
0x20 uLaw 2ch 8bit
|
||||
*/
|
||||
|
||||
setup = setupIn;
|
||||
setup.format.setChannelCount(1);
|
||||
setup.format.setSampleSize(8);
|
||||
setup.format.setSampleType(QAudioFormat::UnSignedInt);
|
||||
this->setup = setup;
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name;
|
||||
|
||||
if (setup.port.isNull())
|
||||
{
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setup.codec == 0x01 || setup.codec == 0x20) {
|
||||
/* Althought uLaw is 8bit unsigned, it is 16bit signed once decoded*/
|
||||
setup.ulaw = true;
|
||||
setup.format.setSampleSize(16);
|
||||
setup.format.setSampleType(QAudioFormat::SignedInt);
|
||||
|
||||
}
|
||||
if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20 || setup.codec == 0x80) {
|
||||
setup.format.setChannelCount(2);
|
||||
}
|
||||
|
||||
if (setup.codec == 0x04 || setup.codec == 0x10) {
|
||||
setup.format.setSampleSize(16);
|
||||
setup.format.setSampleType(QAudioFormat::SignedInt);
|
||||
}
|
||||
|
||||
if (setup.codec == 0x40 || setup.codec == 0x80) {
|
||||
setup.format.setSampleType(QAudioFormat::Float);
|
||||
}
|
||||
|
||||
qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name <<
|
||||
", bits" << setup.format.sampleSize() <<
|
||||
", bits" << inFormat.sampleSize() <<
|
||||
", codec" << setup.codec <<
|
||||
", latency" << setup.latency <<
|
||||
", localAFGain" << setup.localAFgain <<
|
||||
", radioChan" << setup.format.channelCount() <<
|
||||
", radioChan" << inFormat.channelCount() <<
|
||||
", resampleQuality" << setup.resampleQuality <<
|
||||
", samplerate" << setup.format.sampleRate() <<
|
||||
", samplerate" << inFormat.sampleRate() <<
|
||||
", uLaw" << setup.ulaw;
|
||||
|
||||
inFormat = toQAudioFormat(setup.codec, setup.sampleRate);
|
||||
|
||||
if(!setup.isinput)
|
||||
{
|
||||
this->setVolume(setup.localAFgain);
|
||||
}
|
||||
|
||||
format = setup.port.preferredFormat();
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Preferred Format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() <<
|
||||
"Sample Rate" << format.sampleRate() << "Codec" << format.codec() << "Sample Type" << format.sampleType();
|
||||
if (format.channelCount() > 2) {
|
||||
format.setChannelCount(2);
|
||||
outFormat = setup.port.preferredFormat();
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Preferred Format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() <<
|
||||
"Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType();
|
||||
if (outFormat.channelCount() > 2) {
|
||||
outFormat.setChannelCount(2);
|
||||
}
|
||||
else if (format.channelCount() < 1)
|
||||
else if (outFormat.channelCount() < 1)
|
||||
{
|
||||
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup.";
|
||||
return false;
|
||||
}
|
||||
if (format.channelCount() == 1 && setup.format.channelCount() == 2) {
|
||||
format.setChannelCount(2);
|
||||
if (!setup.port.isFormatSupported(format)) {
|
||||
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request stereo input!";
|
||||
format.setChannelCount(1);
|
||||
|
||||
if (outFormat.channelCount() == 1 && inFormat.channelCount() == 2) {
|
||||
outFormat.setChannelCount(2);
|
||||
if (!setup.port.isFormatSupported(outFormat)) {
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request stereo reverting to mono";
|
||||
outFormat.setChannelCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (format.sampleSize() == 24) {
|
||||
// We can't convert this easily
|
||||
format.setSampleSize(16);
|
||||
if (outFormat.sampleRate() < 48000) {
|
||||
int tempRate=outFormat.sampleRate();
|
||||
outFormat.setSampleRate(48000);
|
||||
if (!setup.port.isFormatSupported(outFormat)) {
|
||||
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request 48K, reverting to "<< tempRate;
|
||||
outFormat.setSampleRate(tempRate);
|
||||
}
|
||||
}
|
||||
|
||||
if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize()==8) {
|
||||
outFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
outFormat.setSampleSize(16);
|
||||
|
||||
if (!setup.port.isFormatSupported(outFormat)) {
|
||||
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request 16bit Signed samples, reverting to 8bit Unsigned";
|
||||
outFormat.setSampleType(QAudioFormat::UnSignedInt);
|
||||
outFormat.setSampleSize(8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
if (outFormat.sampleType()==QAudioFormat::SignedInt) {
|
||||
outFormat.setSampleType(QAudioFormat::Float);
|
||||
outFormat.setSampleSize(32);
|
||||
if (!setup.port.isFormatSupported(outFormat)) {
|
||||
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempt to select 32bit Float failed, reverting to SignedInt";
|
||||
outFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
outFormat.setSampleSize(16);
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
if (outFormat.sampleSize() == 24) {
|
||||
// We can't convert this easily so use 32 bit instead.
|
||||
outFormat.setSampleSize(32);
|
||||
if (!setup.port.isFormatSupported(outFormat)) {
|
||||
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "24 bit requested and 32 bit audio not supported, try 16 bit instead";
|
||||
outFormat.setSampleSize(16);
|
||||
}
|
||||
}
|
||||
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Internal: sample rate" << format.sampleRate() << "channel count" << format.channelCount();
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() <<
|
||||
"Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType();
|
||||
|
||||
// We "hopefully" now have a valid format that is supported so try connecting
|
||||
|
||||
converter = new audioConverter();
|
||||
converterThread = new QThread(this);
|
||||
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);
|
||||
converterThread->setObjectName("audioConvIn()");
|
||||
}
|
||||
else {
|
||||
converterThread->setObjectName("audioConvOut()");
|
||||
}
|
||||
converter->moveToThread(converterThread);
|
||||
|
||||
connect(this, SIGNAL(setupConverter(QAudioFormat,QAudioFormat,quint8,quint8)), converter, SLOT(init(QAudioFormat,QAudioFormat,quint8,quint8)));
|
||||
connect(converterThread, SIGNAL(finished()), converter, SLOT(deleteLater()));
|
||||
connect(this, SIGNAL(sendToConverter(audioPacket)), converter, SLOT(convert(audioPacket)));
|
||||
converterThread->start(QThread::TimeCriticalPriority);
|
||||
|
||||
if (setup.isinput) {
|
||||
audioInput = new QAudioInput(setup.port, outFormat, this);
|
||||
connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
|
||||
emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality);
|
||||
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket)));
|
||||
}
|
||||
else {
|
||||
audioOutput = new QAudioOutput(setup.port, format, this);
|
||||
audioOutput = new QAudioOutput(setup.port, outFormat, this);
|
||||
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
|
||||
emit setupConverter(inFormat, outFormat, 7, setup.resampleQuality);
|
||||
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket)));
|
||||
}
|
||||
|
||||
// Setup resampler and opus if they are needed.
|
||||
int resample_error = 0;
|
||||
int opus_err = 0;
|
||||
if (setup.isinput) {
|
||||
resampler = wf_resampler_init(format.channelCount(), format.sampleRate(), setup.format.sampleRate(), setup.resampleQuality, &resample_error);
|
||||
if (setup.codec == 0x40 || setup.codec == 0x80) {
|
||||
// Opus codec
|
||||
encoder = opus_encoder_create(setup.format.sampleRate(), setup.format.channelCount(), OPUS_APPLICATION_AUDIO, &opus_err);
|
||||
opus_encoder_ctl(encoder, OPUS_SET_LSB_DEPTH(16));
|
||||
opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1));
|
||||
opus_encoder_ctl(encoder, OPUS_SET_DTX(1));
|
||||
opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(5));
|
||||
qInfo(logAudio()) << "Creating opus encoder: " << opus_strerror(opus_err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
//resampBufs = new r8b::CFixedBuffer<double>[format.channelCount()];
|
||||
//resamps = new r8b::CPtrKeeper<r8b::CDSPResampler24*>[format.channelCount()];
|
||||
resampler = wf_resampler_init(format.channelCount(), setup.format.sampleRate(), format.sampleRate(), setup.resampleQuality, &resample_error);
|
||||
if (setup.codec == 0x40 || setup.codec == 0x80) {
|
||||
// Opus codec
|
||||
decoder = opus_decoder_create(setup.format.sampleRate(), setup.format.channelCount(), &opus_err);
|
||||
qInfo(logAudio()) << "Creating opus decoder: " << opus_strerror(opus_err);
|
||||
}
|
||||
}
|
||||
unsigned int ratioNum;
|
||||
unsigned int ratioDen;
|
||||
|
||||
wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen);
|
||||
resampleRatio = static_cast<double>(ratioDen) / ratioNum;
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "wf_resampler_init() returned: " << resample_error << " resampleRatio: " << resampleRatio;
|
||||
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "thread id" << QThread::currentThreadId();
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "thread id" << QThread::currentThreadId();
|
||||
|
||||
underTimer = new QTimer();
|
||||
underTimer->setSingleShot(true);
|
||||
connect(underTimer, &QTimer::timeout, this, &audioHandler::clearUnderrun);
|
||||
connect(underTimer, SIGNAL(timeout()), this, SLOT(clearUnderrun()));
|
||||
|
||||
this->start();
|
||||
|
||||
|
@ -205,16 +181,28 @@ void audioHandler::start()
|
|||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "start() running";
|
||||
|
||||
if (setup.isinput) {
|
||||
//this->open(QIODevice::WriteOnly);
|
||||
//audioInput->start(this);
|
||||
#ifdef Q_OS_WIN
|
||||
audioInput->setBufferSize(inFormat.bytesForDuration(setup.latency * 100));
|
||||
#else
|
||||
audioInput->setBufferSize(inFormat.bytesForDuration(setup.latency * 1000));
|
||||
#endif
|
||||
audioDevice = audioInput->start();
|
||||
connect(audioInput, &QAudioInput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection);
|
||||
//connect(audioDevice, &QIODevice::readyRead, this, &audioHandler::getNextAudioChunk);
|
||||
audioTimer->start(setup.blockSize);
|
||||
connect(audioInput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection);
|
||||
connect(audioDevice, SIGNAL(readyRead()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection);
|
||||
//audioInput->setNotifyInterval(setup.blockSize/2);
|
||||
//connect(audioInput, SIGNAL(notify()), this, SLOT(getNextAudioChunk()), Qt::UniqueConnection);
|
||||
}
|
||||
else {
|
||||
// Buffer size must be set before audio is started.
|
||||
audioOutput->setBufferSize(getAudioSize(setup.latency, format));
|
||||
#ifdef Q_OS_WIN
|
||||
audioOutput->setBufferSize(outFormat.bytesForDuration(setup.latency * 100));
|
||||
#else
|
||||
audioOutput->setBufferSize(outFormat.bytesForDuration(setup.latency * 1000));
|
||||
#endif
|
||||
audioDevice = audioOutput->start();
|
||||
connect(audioOutput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection);
|
||||
connect(audioOutput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection);
|
||||
}
|
||||
if (!audioDevice) {
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio device failed to start()";
|
||||
|
@ -247,359 +235,89 @@ void audioHandler::setVolume(unsigned char volume)
|
|||
}
|
||||
|
||||
|
||||
void audioHandler::incomingAudio(audioPacket inPacket)
|
||||
void audioHandler::incomingAudio(audioPacket packet)
|
||||
{
|
||||
|
||||
audioPacket livePacket = inPacket;
|
||||
// Process uLaw.
|
||||
if (setup.ulaw)
|
||||
{
|
||||
// Current packet is 8bit so need to create a new buffer that is 16bit
|
||||
QByteArray outPacket((int)livePacket.data.length() * 2, (char)0xff);
|
||||
qint16* out = (qint16*)outPacket.data();
|
||||
for (int f = 0; f < livePacket.data.length(); f++)
|
||||
{
|
||||
*out++ = ulaw_decode[(quint8)livePacket.data[f]];
|
||||
}
|
||||
livePacket.data.clear();
|
||||
livePacket.data = outPacket; // Replace incoming data with converted.
|
||||
// Buffer now contains 16bit signed samples.
|
||||
|
||||
if (audioDevice != Q_NULLPTR && packet.data.size() > 0) {
|
||||
packet.volume = volume;
|
||||
|
||||
emit sendToConverter(packet);
|
||||
}
|
||||
|
||||
|
||||
/* Opus data */
|
||||
if (setup.codec == 0x40 || setup.codec == 0x80) {
|
||||
unsigned char* in = (unsigned char*)inPacket.data.data();
|
||||
|
||||
//Decode the frame.
|
||||
int nSamples = opus_packet_get_nb_samples(in, livePacket.data.size(),setup.format.sampleRate());
|
||||
if (nSamples == -1) {
|
||||
// No opus data yet?
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray outPacket(nSamples*sizeof(float)*setup.format.channelCount(), (char)0xff); // Preset the output buffer size.
|
||||
float* out = (float*)outPacket.data();
|
||||
|
||||
if (livePacket.seq > lastSentSeq + 1) {
|
||||
nSamples = opus_decode_float(decoder, Q_NULLPTR,0, out, nSamples, 1);
|
||||
}
|
||||
else {
|
||||
nSamples = opus_decode_float(decoder, in, livePacket.data.size(), out, nSamples, 0);
|
||||
}
|
||||
|
||||
if (nSamples < 0)
|
||||
{
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decode failed:" << opus_strerror(nSamples) << "packet size" << livePacket.data.length();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (int(nSamples * sizeof(float) * setup.format.channelCount()) != outPacket.size())
|
||||
{
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(float) * setup.format.channelCount() << "outPacket:" << outPacket.size();
|
||||
outPacket.resize(nSamples * sizeof(float) * setup.format.channelCount());
|
||||
}
|
||||
//qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoded" << livePacket.data.size() << "bytes, into" << outPacket.length() << "bytes";
|
||||
livePacket.data.clear();
|
||||
livePacket.data = outPacket; // Replace incoming data with converted.
|
||||
}
|
||||
setup.format.setSampleType(QAudioFormat::Float);
|
||||
}
|
||||
|
||||
|
||||
if (!livePacket.data.isEmpty()) {
|
||||
|
||||
Eigen::VectorXf samplesF;
|
||||
if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16)
|
||||
{
|
||||
VectorXint16 samplesI = Eigen::Map<VectorXint16>(reinterpret_cast<qint16*>(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint16>::max());
|
||||
}
|
||||
else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8)
|
||||
{
|
||||
VectorXuint8 samplesI = Eigen::Map<VectorXuint8>(reinterpret_cast<quint8*>(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<quint8>::max());;
|
||||
}
|
||||
else if (setup.format.sampleType() == QAudioFormat::Float) {
|
||||
samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(livePacket.data.data()), livePacket.data.size() / int(sizeof(float)));
|
||||
}
|
||||
else
|
||||
{
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType();
|
||||
}
|
||||
|
||||
|
||||
/* samplesF is now an Eigen Vector of the current samples in float format */
|
||||
|
||||
|
||||
// Set the max amplitude found in the vector
|
||||
// Should it be before or after volume is applied?
|
||||
|
||||
amplitude = samplesF.array().abs().maxCoeff();
|
||||
// Set the volume
|
||||
samplesF *= volume;
|
||||
|
||||
// Convert mono to stereo if required
|
||||
if (setup.format.channelCount() == 1) {
|
||||
Eigen::VectorXf samplesTemp(samplesF.size() * 2);
|
||||
Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesTemp.data(), samplesF.size()) = samplesF;
|
||||
Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesTemp.data() + 1, samplesF.size()) = samplesF;
|
||||
samplesF = samplesTemp;
|
||||
}
|
||||
|
||||
|
||||
if (resampleRatio != 1.0) {
|
||||
|
||||
// We need to resample
|
||||
// We have a stereo 16bit stream.
|
||||
quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio);
|
||||
quint32 inFrames = (samplesF.size() / format.channelCount());
|
||||
QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size.
|
||||
const float* in = (float*)samplesF.data();
|
||||
float* out = (float*)outPacket.data();
|
||||
|
||||
int err = 0;
|
||||
if (format.channelCount() == 1) {
|
||||
err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames);
|
||||
}
|
||||
else {
|
||||
err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
|
||||
}
|
||||
samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(outPacket.data()), outPacket.size() / int(sizeof(float)));
|
||||
}
|
||||
|
||||
if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<quint8>::max());
|
||||
VectorXuint8 samplesI = samplesITemp.cast<quint8>();
|
||||
livePacket.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8)));
|
||||
}
|
||||
if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint16>::max());
|
||||
VectorXint16 samplesI = samplesITemp.cast<qint16>();
|
||||
livePacket.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16)));
|
||||
}
|
||||
else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint32>::max());
|
||||
VectorXint32 samplesI = samplesITemp.cast<qint32>();
|
||||
livePacket.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32)));
|
||||
}
|
||||
else if (format.sampleType() == QAudioFormat::Float)
|
||||
{
|
||||
livePacket.data = QByteArray(reinterpret_cast<char*>(samplesF.data()), int(samplesF.size()) * int(sizeof(float)));
|
||||
}
|
||||
else {
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType();
|
||||
}
|
||||
|
||||
currentLatency = livePacket.time.msecsTo(QTime::currentTime()) + getAudioDuration(audioOutput->bufferSize()-audioOutput->bytesFree(),format);
|
||||
if (audioDevice != Q_NULLPTR) {
|
||||
audioDevice->write(livePacket.data);
|
||||
}
|
||||
if ((inPacket.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) {
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << inPacket.seq << "as last is" << lastSentSeq;
|
||||
lastSentSeq = inPacket.seq;
|
||||
incomingAudio(inPacket); // Call myself again to run the packet a second time (FEC)
|
||||
}
|
||||
|
||||
lastSentSeq = inPacket.seq;
|
||||
}
|
||||
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency,isUnderrun);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void audioHandler::convertedOutput(audioPacket packet) {
|
||||
|
||||
if (packet.data.size() > 0 ) {
|
||||
|
||||
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000);
|
||||
if (audioDevice != Q_NULLPTR) {
|
||||
if (audioDevice->write(packet.data) < packet.data.size()) {
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full!";
|
||||
isOverrun=true;
|
||||
} else {
|
||||
isOverrun = false;
|
||||
}
|
||||
if (lastReceived.msecsTo(QTime::currentTime()) > 100) {
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize;
|
||||
}
|
||||
lastReceived = QTime::currentTime();
|
||||
}
|
||||
/*if ((packet.seq > lastSentSeq + 1) && (setup.codec == 0x40 || setup.codec == 0x80)) {
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempting FEC on packet" << packet.seq << "as last is" << lastSentSeq;
|
||||
lastSentSeq = packet.seq;
|
||||
incomingAudio(packet); // Call myself again to run the packet a second time (FEC)
|
||||
}
|
||||
*/
|
||||
lastSentSeq = packet.seq;
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun);
|
||||
|
||||
amplitude = packet.amplitude;
|
||||
}
|
||||
}
|
||||
|
||||
void audioHandler::getNextAudioChunk()
|
||||
{
|
||||
tempBuf.data.append(audioDevice->readAll());
|
||||
if (audioDevice) {
|
||||
tempBuf.data.append(audioDevice->readAll());
|
||||
}
|
||||
if (tempBuf.data.length() >= outFormat.bytesForDuration(setup.blockSize * 1000)) {
|
||||
audioPacket packet;
|
||||
packet.time = QTime::currentTime();
|
||||
packet.sent = 0;
|
||||
packet.volume = volume;
|
||||
memcpy(&packet.guid, setup.guid, GUIDLEN);
|
||||
//QTime startProcessing = QTime::currentTime();
|
||||
packet.data.clear();
|
||||
packet.data = tempBuf.data.mid(0, outFormat.bytesForDuration(setup.blockSize * 1000));
|
||||
tempBuf.data.remove(0, outFormat.bytesForDuration(setup.blockSize * 1000));
|
||||
|
||||
if (tempBuf.data.length() < format.bytesForDuration(setup.blockSize * 1000)) {
|
||||
return;
|
||||
emit sendToConverter(packet);
|
||||
}
|
||||
|
||||
audioPacket livePacket;
|
||||
livePacket.time= QTime::currentTime();
|
||||
livePacket.sent = 0;
|
||||
memcpy(&livePacket.guid, setup.guid, GUIDLEN);
|
||||
while (tempBuf.data.length() > format.bytesForDuration(setup.blockSize * 1000)) {
|
||||
livePacket.data.clear();
|
||||
livePacket.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000));
|
||||
tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000));
|
||||
if (livePacket.data.length() > 0)
|
||||
{
|
||||
Eigen::VectorXf samplesF;
|
||||
if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 32)
|
||||
{
|
||||
VectorXint32 samplesI = Eigen::Map<VectorXint32>(reinterpret_cast<qint32*>(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint32)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint32>::max());
|
||||
}
|
||||
else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 16)
|
||||
{
|
||||
VectorXint16 samplesI = Eigen::Map<VectorXint16>(reinterpret_cast<qint16*>(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint16)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint16>::max());
|
||||
}
|
||||
else if (format.sampleType() == QAudioFormat::UnSignedInt && format.sampleSize() == 8)
|
||||
{
|
||||
VectorXuint8 samplesI = Eigen::Map<VectorXuint8>(reinterpret_cast<quint8*>(livePacket.data.data()), livePacket.data.size() / int(sizeof(quint8)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<quint8>::max());;
|
||||
}
|
||||
else if (format.sampleType() == QAudioFormat::SignedInt && format.sampleSize() == 8)
|
||||
{
|
||||
VectorXint8 samplesI = Eigen::Map<VectorXint8>(reinterpret_cast<qint8*>(livePacket.data.data()), livePacket.data.size() / int(sizeof(qint8)));
|
||||
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint8>::max());;
|
||||
}
|
||||
else if (format.sampleType() == QAudioFormat::Float)
|
||||
{
|
||||
samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(livePacket.data.data()), livePacket.data.size() / int(sizeof(float)));
|
||||
}
|
||||
else {
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType() << format.sampleSize();
|
||||
}
|
||||
|
||||
/* samplesF is now an Eigen Vector of the current samples in float format */
|
||||
/* If there is still enough data in the buffer, call myself again in 20ms */
|
||||
if (tempBuf.data.length() >= outFormat.bytesForDuration(setup.blockSize * 1000)) {
|
||||
QTimer::singleShot(setup.blockSize, this, &audioHandler::getNextAudioChunk);
|
||||
}
|
||||
|
||||
// Set the max amplitude found in the vector
|
||||
if (samplesF.size() > 0) {
|
||||
amplitude = samplesF.array().abs().maxCoeff();
|
||||
// Channel count should now match the device that audio is going to (rig)
|
||||
|
||||
if (resampleRatio != 1.0) {
|
||||
|
||||
// We need to resample
|
||||
// We have a stereo 16bit stream.
|
||||
quint32 outFrames = ((samplesF.size() / format.channelCount()) * resampleRatio);
|
||||
quint32 inFrames = (samplesF.size() / format.channelCount());
|
||||
|
||||
QByteArray outPacket(outFrames * format.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size.
|
||||
const float* in = (float*)samplesF.data();
|
||||
float* out = (float*)outPacket.data();
|
||||
|
||||
int err = 0;
|
||||
if (format.channelCount() == 1) {
|
||||
err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames);
|
||||
}
|
||||
else {
|
||||
err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames);
|
||||
}
|
||||
if (err) {
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
|
||||
}
|
||||
samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(outPacket.data()), outPacket.size() / int(sizeof(float)));
|
||||
}
|
||||
|
||||
// If we need to drop one of the audio channels, do it now
|
||||
if (format.channelCount() == 2 && setup.format.channelCount() == 1) {
|
||||
Eigen::VectorXf samplesTemp(samplesF.size() / 2);
|
||||
samplesTemp = Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesF.data(), samplesF.size() / 2);
|
||||
samplesF = samplesTemp;
|
||||
}
|
||||
else if (format.channelCount() == 1 && setup.format.channelCount() == 2) {
|
||||
// Convert mono to stereo if required
|
||||
Eigen::VectorXf samplesTemp(samplesF.size() * 2);
|
||||
Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesTemp.data(), samplesF.size()) = samplesF;
|
||||
Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesTemp.data() + 1, samplesF.size()) = samplesF;
|
||||
samplesF = samplesTemp;
|
||||
}
|
||||
|
||||
//qInfo(logAudio()) << "Sending audio len" << livePacket.data.length() << "remaining" << tempBuf.data.length() << "resampled" << samplesF.size();
|
||||
|
||||
if (setup.codec == 0x40 || setup.codec == 0x80)
|
||||
{
|
||||
//Are we using the opus codec?
|
||||
float* in = (float*)samplesF.data();
|
||||
|
||||
/* Encode the frame. */
|
||||
QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size
|
||||
unsigned char* out = (unsigned char*)outPacket.data();
|
||||
|
||||
int nbBytes = opus_encode_float(encoder, in, (samplesF.size() / setup.format.channelCount()), out, outPacket.length());
|
||||
if (nbBytes < 0)
|
||||
{
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
outPacket.resize(nbBytes);
|
||||
samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(outPacket.data()), outPacket.size() / int(sizeof(float)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 8)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint8>::max());
|
||||
VectorXint8 samplesI = samplesITemp.cast<qint8>();
|
||||
livePacket.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8)));
|
||||
}
|
||||
else if (setup.format.sampleType() == QAudioFormat::UnSignedInt && setup.format.sampleSize() == 8)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<quint8>::max());
|
||||
VectorXuint8 samplesI = samplesITemp.cast<quint8>();
|
||||
livePacket.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8)));
|
||||
}
|
||||
else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 16)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint16>::max());
|
||||
VectorXint16 samplesI = samplesITemp.cast<qint16>();
|
||||
livePacket.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16)));
|
||||
}
|
||||
else if (setup.format.sampleType() == QAudioFormat::SignedInt && setup.format.sampleSize() == 32)
|
||||
{
|
||||
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint32>::max());
|
||||
VectorXint32 samplesI = samplesITemp.cast<qint32>();
|
||||
livePacket.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32)));
|
||||
}
|
||||
else if (setup.format.sampleType() == QAudioFormat::Float)
|
||||
{
|
||||
livePacket.data = QByteArray(reinterpret_cast<char*>(samplesF.data()), int(samplesF.size()) * int(sizeof(float)));
|
||||
}
|
||||
else {
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported Sample Type:" << format.sampleType();
|
||||
}
|
||||
|
||||
/* Need to find a floating point uLaw encoder!*/
|
||||
if (setup.ulaw)
|
||||
{
|
||||
QByteArray outPacket((int)livePacket.data.length() / 2, (char)0xff);
|
||||
qint16* in = (qint16*)livePacket.data.data();
|
||||
for (int f = 0; f < outPacket.length(); f++)
|
||||
{
|
||||
qint16 sample = *in++;
|
||||
if (setup.ulaw) {
|
||||
int sign = (sample >> 8) & 0x80;
|
||||
if (sign)
|
||||
sample = (short)-sample;
|
||||
if (sample > cClip)
|
||||
sample = cClip;
|
||||
sample = (short)(sample + cBias);
|
||||
int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF];
|
||||
int mantissa = (sample >> (exponent + 3)) & 0x0F;
|
||||
int compressedByte = ~(sign | (exponent << 4) | mantissa);
|
||||
outPacket[f] = (quint8)compressedByte;
|
||||
}
|
||||
}
|
||||
livePacket.data.clear();
|
||||
livePacket.data = outPacket; // Copy output packet back to input buffer.
|
||||
}
|
||||
emit haveAudioData(livePacket);
|
||||
//ret = livePacket.data;
|
||||
}
|
||||
}
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun);
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void audioHandler::convertedInput(audioPacket audio)
|
||||
{
|
||||
if (audio.data.size() > 0) {
|
||||
emit haveAudioData(audio);
|
||||
if (lastReceived.msecsTo(QTime::currentTime()) > 100) {
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize ;
|
||||
}
|
||||
lastReceived = QTime::currentTime();
|
||||
amplitude = audio.amplitude;
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun);
|
||||
}
|
||||
}
|
||||
|
||||
void audioHandler::changeLatency(const quint16 newSize)
|
||||
{
|
||||
|
||||
|
@ -609,10 +327,9 @@ void audioHandler::changeLatency(const quint16 newSize)
|
|||
stop();
|
||||
start();
|
||||
}
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << getAudioDuration(audioOutput->bufferSize(), format) << "ms";
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << outFormat.durationForBytes(audioOutput->bufferSize())/1000 << "ms";
|
||||
|
||||
}
|
||||
|
||||
int audioHandler::getLatency()
|
||||
{
|
||||
return currentLatency;
|
||||
|
@ -665,5 +382,4 @@ void audioHandler::stateChanged(QAudio::State state)
|
|||
void audioHandler::clearUnderrun()
|
||||
{
|
||||
isUnderrun = false;
|
||||
underTimer->stop();
|
||||
}
|
||||
}
|
||||
|
|
279
audiohandler.h
279
audiohandler.h
|
@ -8,10 +8,10 @@
|
|||
#include <QtEndian>
|
||||
#include <QtMath>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QTime>
|
||||
#include <QMap>
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
|
||||
/* QT Audio Headers */
|
||||
#include <QAudioOutput>
|
||||
|
@ -20,27 +20,6 @@
|
|||
#include <QAudioInput>
|
||||
#include <QIODevice>
|
||||
|
||||
/* Current resampler code */
|
||||
#include "resampler/speex_resampler.h"
|
||||
|
||||
/* Potential new resampler */
|
||||
//#include <r8bbase.h>
|
||||
//#include <CDSPResampler.h>
|
||||
|
||||
|
||||
/* Opus */
|
||||
#ifdef Q_OS_WIN
|
||||
#include "opus.h"
|
||||
#else
|
||||
#include "opus/opus.h"
|
||||
#endif
|
||||
|
||||
/* Eigen */
|
||||
#ifndef Q_OS_WIN
|
||||
#include <eigen3/Eigen/Eigen>
|
||||
#else
|
||||
#include <Eigen/Eigen>
|
||||
#endif
|
||||
|
||||
/* wfview Packet types */
|
||||
#include "packettypes.h"
|
||||
|
@ -48,38 +27,23 @@
|
|||
/* Logarithmic taper for volume control */
|
||||
#include "audiotaper.h"
|
||||
|
||||
|
||||
/* Audio converter class*/
|
||||
#include "audioconverter.h"
|
||||
|
||||
|
||||
#define MULAW_BIAS 33
|
||||
#define MULAW_MAX 0x1fff
|
||||
|
||||
struct audioPacket {
|
||||
quint32 seq;
|
||||
QTime time;
|
||||
quint16 sent;
|
||||
QByteArray data;
|
||||
quint8 guid[GUIDLEN];
|
||||
};
|
||||
|
||||
struct audioSetup {
|
||||
QString name;
|
||||
quint16 latency;
|
||||
quint8 codec;
|
||||
bool ulaw = false;
|
||||
bool isinput;
|
||||
QAudioFormat format; // Use this for all audio APIs
|
||||
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
|
||||
//class audioHandler : public QIODevice
|
||||
class audioHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
audioHandler(QObject* parent = 0);
|
||||
explicit audioHandler(QObject* parent = nullptr);
|
||||
~audioHandler();
|
||||
|
||||
int getLatency();
|
||||
|
@ -94,6 +58,8 @@ public slots:
|
|||
void changeLatency(const quint16 newSize);
|
||||
void setVolume(unsigned char volume);
|
||||
void incomingAudio(const audioPacket data);
|
||||
void convertedInput(audioPacket audio);
|
||||
void convertedOutput(audioPacket audio);
|
||||
|
||||
private slots:
|
||||
void stateChanged(QAudio::State state);
|
||||
|
@ -104,10 +70,19 @@ signals:
|
|||
void audioMessage(QString message);
|
||||
void sendLatency(quint16 newSize);
|
||||
void haveAudioData(const audioPacket& data);
|
||||
void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under);
|
||||
void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under,bool over);
|
||||
void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp);
|
||||
void sendToConverter(audioPacket audio);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
//qint64 readData(char* data, qint64 nBytes);
|
||||
//qint64 writeData(const char* data, qint64 nBytes);
|
||||
|
||||
|
||||
bool isUnderrun = false;
|
||||
bool isOverrun = true;
|
||||
bool isInitialized=false;
|
||||
bool isReady = false;
|
||||
bool audioBuffered = false;
|
||||
|
@ -115,11 +90,13 @@ private:
|
|||
QAudioOutput* audioOutput=Q_NULLPTR;
|
||||
QAudioInput* audioInput=Q_NULLPTR;
|
||||
QIODevice* audioDevice=Q_NULLPTR;
|
||||
QTimer* audioTimer = Q_NULLPTR;
|
||||
QAudioFormat format;
|
||||
QAudioFormat inFormat;
|
||||
QAudioFormat outFormat;
|
||||
QAudioDeviceInfo deviceInfo;
|
||||
SpeexResamplerState* resampler = Q_NULLPTR;
|
||||
|
||||
audioConverter* converter=Q_NULLPTR;
|
||||
QThread* converterThread = Q_NULLPTR;
|
||||
QTime lastReceived;
|
||||
//r8b::CFixedBuffer<double>* resampBufs;
|
||||
//r8b::CPtrKeeper<r8b::CDSPResampler24*>* resamps;
|
||||
|
||||
|
@ -137,214 +114,14 @@ private:
|
|||
audioPacket tempBuf;
|
||||
quint16 currentLatency;
|
||||
float amplitude;
|
||||
qreal volume=1.0;
|
||||
qreal volume = 1.0;
|
||||
|
||||
audioSetup setup;
|
||||
|
||||
OpusEncoder* encoder=Q_NULLPTR;
|
||||
OpusDecoder* decoder=Q_NULLPTR;
|
||||
OpusEncoder* encoder = Q_NULLPTR;
|
||||
OpusDecoder* decoder = Q_NULLPTR;
|
||||
QTimer* underTimer;
|
||||
};
|
||||
|
||||
|
||||
// Various audio handling functions declared inline
|
||||
|
||||
static inline qint64 getAudioSize(qint64 timeInMs, const QAudioFormat& format)
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(1000) * timeInMs));
|
||||
#else
|
||||
qint64 value = qint64(qCeil(format.channelCount() * (format.sampleSize() / 8) * format.sampleRate() / qreal(10000) * timeInMs));
|
||||
#endif
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
#endif // AUDIOHANDLER_H
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
// Copyright 2017-2020 Elliott H. Liggett
|
||||
|
||||
commHandler::commHandler()
|
||||
commHandler::commHandler(QObject* parent) : QObject(parent)
|
||||
{
|
||||
//constructor
|
||||
// grab baud rate and other comm port details
|
||||
|
@ -31,7 +31,7 @@ commHandler::commHandler()
|
|||
connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn()));
|
||||
}
|
||||
|
||||
commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat)
|
||||
commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QObject* parent) : QObject(parent)
|
||||
{
|
||||
//constructor
|
||||
// grab baud rate and other comm port details
|
||||
|
@ -65,6 +65,7 @@ commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat)
|
|||
|
||||
commHandler::~commHandler()
|
||||
{
|
||||
qInfo(logSerial()) << "Closing serial port: " << port->portName();
|
||||
if (isConnected) {
|
||||
this->closePort();
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ class commHandler : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
commHandler();
|
||||
commHandler(QString portName, quint32 baudRate, quint8 wfFormat);
|
||||
commHandler(QObject* parent = nullptr);
|
||||
commHandler(QString portName, quint32 baudRate, quint8 wfFormat,QObject* parent = nullptr);
|
||||
bool serialError;
|
||||
bool rtsStatus();
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ void keyboard::run()
|
|||
{
|
||||
char key = getchar();
|
||||
if (key == 'q') {
|
||||
QCoreApplication::exit(0);
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -10,3 +10,4 @@ Q_LOGGING_CATEGORY(logUdpServer, "udp.server")
|
|||
Q_LOGGING_CATEGORY(logRigCtlD, "rigctld")
|
||||
Q_LOGGING_CATEGORY(logTcpServer, "tcpserver")
|
||||
Q_LOGGING_CATEGORY(logUsbControl, "usbcontrol")
|
||||
Q_LOGGING_CATEGORY(logAudioConverter, "audioconverter")
|
||||
|
|
|
@ -13,6 +13,7 @@ Q_DECLARE_LOGGING_CATEGORY(logUdpServer)
|
|||
Q_DECLARE_LOGGING_CATEGORY(logRigCtlD)
|
||||
Q_DECLARE_LOGGING_CATEGORY(logTcpServer)
|
||||
Q_DECLARE_LOGGING_CATEGORY(logUsbControl)
|
||||
Q_DECLARE_LOGGING_CATEGORY(logAudioConverter)
|
||||
|
||||
|
||||
#if defined(Q_OS_WIN) && !defined(__PRETTY_FUNCTION__)
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
#include "pahandler.h"
|
||||
|
||||
#include "logcategories.h"
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
|
||||
|
||||
paHandler::paHandler(QObject* parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
}
|
||||
|
||||
paHandler::~paHandler()
|
||||
{
|
||||
|
||||
if (isInitialized) {
|
||||
Pa_StopStream(audio);
|
||||
Pa_CloseStream(audio);
|
||||
}
|
||||
|
||||
if (converterThread != Q_NULLPTR) {
|
||||
converterThread->quit();
|
||||
converterThread->wait();
|
||||
}
|
||||
|
||||
//Pa_Terminate();
|
||||
|
||||
}
|
||||
|
||||
bool paHandler::init(audioSetup setup)
|
||||
{
|
||||
if (isInitialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->setup = setup;
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "PortAudio handler starting:" << setup.name;
|
||||
|
||||
if (setup.portInt==-1)
|
||||
{
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
inFormat = toQAudioFormat(setup.codec, setup.sampleRate);
|
||||
|
||||
qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name <<
|
||||
", bits" << inFormat.sampleSize() <<
|
||||
", codec" << setup.codec <<
|
||||
", latency" << setup.latency <<
|
||||
", localAFGain" << setup.localAFgain <<
|
||||
", radioChan" << inFormat.channelCount() <<
|
||||
", resampleQuality" << setup.resampleQuality <<
|
||||
", samplerate" << inFormat.sampleRate() <<
|
||||
", uLaw" << setup.ulaw;
|
||||
|
||||
PaError err;
|
||||
#ifdef Q_OS_WIN
|
||||
CoInitialize(0);
|
||||
#endif
|
||||
|
||||
//err = Pa_Initialize();
|
||||
//if (err != paNoError)
|
||||
//{
|
||||
// qDebug(logAudio()) << "Portaudio initialized";
|
||||
//}
|
||||
|
||||
memset(&aParams, 0, sizeof(PaStreamParameters));
|
||||
|
||||
aParams.device = setup.portInt;
|
||||
info = Pa_GetDeviceInfo(aParams.device);
|
||||
|
||||
qDebug(logAudio()) << "PortAudio" << (setup.isinput ? "Input" : "Output") << setup.portInt << "Input Channels" << info->maxInputChannels << "Output Channels" << info->maxOutputChannels;
|
||||
|
||||
|
||||
if (setup.isinput) {
|
||||
outFormat.setChannelCount(info->maxInputChannels);
|
||||
}
|
||||
else {
|
||||
outFormat.setChannelCount(info->maxOutputChannels);
|
||||
}
|
||||
|
||||
aParams.suggestedLatency = (float)setup.latency/1000.0f;
|
||||
outFormat.setSampleRate(info->defaultSampleRate);
|
||||
aParams.sampleFormat = paFloat32;
|
||||
outFormat.setSampleSize(32);
|
||||
outFormat.setSampleType(QAudioFormat::Float);
|
||||
outFormat.setByteOrder(QAudioFormat::LittleEndian);
|
||||
outFormat.setCodec("audio/pcm");
|
||||
|
||||
|
||||
if (!setup.isinput)
|
||||
{
|
||||
this->setVolume(setup.localAFgain);
|
||||
}
|
||||
|
||||
if (outFormat.channelCount() > 2) {
|
||||
outFormat.setChannelCount(2);
|
||||
}
|
||||
else if (outFormat.channelCount() < 1)
|
||||
{
|
||||
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inFormat.channelCount() < outFormat.channelCount()) {
|
||||
outFormat.setChannelCount(inFormat.channelCount());
|
||||
}
|
||||
|
||||
aParams.channelCount = outFormat.channelCount();
|
||||
|
||||
if (outFormat.sampleRate() < 44100) {
|
||||
outFormat.setSampleRate(48000);
|
||||
}
|
||||
|
||||
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() <<
|
||||
"Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType();
|
||||
|
||||
// We "hopefully" now have a valid format that is supported so try connecting
|
||||
|
||||
converter = new audioConverter();
|
||||
converterThread = new QThread(this);
|
||||
if (setup.isinput) {
|
||||
converterThread->setObjectName("audioConvIn()");
|
||||
}
|
||||
else {
|
||||
converterThread->setObjectName("audioConvOut()");
|
||||
}
|
||||
converter->moveToThread(converterThread);
|
||||
|
||||
connect(this, SIGNAL(setupConverter(QAudioFormat, QAudioFormat, quint8, quint8)), converter, SLOT(init(QAudioFormat, QAudioFormat, quint8, quint8)));
|
||||
connect(converterThread, SIGNAL(finished()), converter, SLOT(deleteLater()));
|
||||
connect(this, SIGNAL(sendToConverter(audioPacket)), converter, SLOT(convert(audioPacket)));
|
||||
converterThread->start(QThread::TimeCriticalPriority);
|
||||
|
||||
aParams.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
// Per channel chunk size.
|
||||
this->chunkSize = (outFormat.bytesForDuration(setup.blockSize*1000)/sizeof(float))*outFormat.channelCount();
|
||||
|
||||
|
||||
if (setup.isinput) {
|
||||
err = Pa_OpenStream(&audio, &aParams, 0, outFormat.sampleRate(), this->chunkSize, paNoFlag, &paHandler::staticWrite, (void*)this);
|
||||
emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality);
|
||||
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket)));
|
||||
}
|
||||
else {
|
||||
err = Pa_OpenStream(&audio, 0, &aParams, outFormat.sampleRate(), this->chunkSize, paNoFlag, NULL, NULL);
|
||||
emit setupConverter(inFormat, outFormat, 7, setup.resampleQuality);
|
||||
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket)));
|
||||
}
|
||||
|
||||
if (err == paNoError) {
|
||||
err = Pa_StartStream(audio);
|
||||
}
|
||||
if (err == paNoError) {
|
||||
isInitialized = true;
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened";
|
||||
}
|
||||
else {
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "failed to open device" << Pa_GetErrorText(err);
|
||||
}
|
||||
|
||||
return isInitialized;
|
||||
}
|
||||
|
||||
|
||||
void paHandler::setVolume(unsigned char volume)
|
||||
{
|
||||
|
||||
this->volume = audiopot[volume];
|
||||
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")";
|
||||
}
|
||||
|
||||
void paHandler::incomingAudio(audioPacket packet)
|
||||
{
|
||||
packet.volume = volume;
|
||||
emit sendToConverter(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int paHandler::writeData(const void* inputBuffer, void* outputBuffer,
|
||||
unsigned long nFrames, const PaStreamCallbackTimeInfo * streamTime,
|
||||
PaStreamCallbackFlags status)
|
||||
{
|
||||
Q_UNUSED(outputBuffer);
|
||||
Q_UNUSED(streamTime);
|
||||
Q_UNUSED(status);
|
||||
audioPacket packet;
|
||||
packet.time = QTime::currentTime();
|
||||
packet.sent = 0;
|
||||
packet.volume = volume;
|
||||
memcpy(&packet.guid, setup.guid, GUIDLEN);
|
||||
packet.data.append((char*)inputBuffer, nFrames*inFormat.channelCount()*sizeof(float));
|
||||
emit sendToConverter(packet);
|
||||
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
|
||||
void paHandler::convertedOutput(audioPacket packet) {
|
||||
|
||||
if (packet.data.size() > 0) {
|
||||
|
||||
if (Pa_IsStreamActive(audio) == 1) {
|
||||
PaError err = Pa_WriteStream(audio, (char*)packet.data.data(), packet.data.size() / sizeof(float) / outFormat.channelCount());
|
||||
|
||||
if (err != paNoError) {
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Error writing audio!";
|
||||
}
|
||||
const PaStreamInfo* info = Pa_GetStreamInfo(audio);
|
||||
|
||||
//currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->outputLatency * 1000);
|
||||
currentLatency = (info->outputLatency * 1000);
|
||||
|
||||
}
|
||||
/*
|
||||
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000);
|
||||
|
||||
if (audioDevice != Q_NULLPTR) {
|
||||
if (audioDevice->write(packet.data) < packet.data.size()) {
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full!";
|
||||
isOverrun = true;
|
||||
}
|
||||
else {
|
||||
isOverrun = false;
|
||||
}
|
||||
if (lastReceived.msecsTo(QTime::currentTime()) > 100) {
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize;
|
||||
}
|
||||
lastReceived = QTime::currentTime();
|
||||
}
|
||||
|
||||
lastSentSeq = packet.seq;
|
||||
|
||||
*/
|
||||
amplitude = packet.amplitude;
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void paHandler::convertedInput(audioPacket audio)
|
||||
{
|
||||
if (audio.data.size() > 0) {
|
||||
emit haveAudioData(audio);
|
||||
amplitude = audio.amplitude;
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency, false,false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void paHandler::changeLatency(const quint16 newSize)
|
||||
{
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency;
|
||||
}
|
||||
|
||||
int paHandler::getLatency()
|
||||
{
|
||||
return currentLatency;
|
||||
}
|
||||
|
||||
|
||||
quint16 paHandler::getAmplitude()
|
||||
{
|
||||
return amplitude;
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
#ifndef PAHANDLER_H
|
||||
#define PAHANDLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QThread>
|
||||
|
||||
#include "portaudio.h"
|
||||
|
||||
#include <QAudioFormat>
|
||||
#include <QTime>
|
||||
#include <QMap>
|
||||
|
||||
|
||||
/* wfview Packet types */
|
||||
#include "packettypes.h"
|
||||
|
||||
/* Logarithmic taper for volume control */
|
||||
#include "audiotaper.h"
|
||||
|
||||
|
||||
/* Audio converter class*/
|
||||
#include "audioconverter.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
class paHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
paHandler(QObject* parent = 0);
|
||||
~paHandler();
|
||||
|
||||
int getLatency();
|
||||
|
||||
|
||||
void getNextAudioChunk(QByteArray& data);
|
||||
quint16 getAmplitude();
|
||||
|
||||
public slots:
|
||||
bool init(audioSetup setup);
|
||||
void changeLatency(const quint16 newSize);
|
||||
void setVolume(unsigned char volume);
|
||||
void convertedInput(audioPacket audio);
|
||||
void convertedOutput(audioPacket audio);
|
||||
void incomingAudio(const audioPacket data);
|
||||
|
||||
|
||||
private slots:
|
||||
|
||||
signals:
|
||||
void audioMessage(QString message);
|
||||
void sendLatency(quint16 newSize);
|
||||
void haveAudioData(const audioPacket& data);
|
||||
void haveLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
|
||||
void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp);
|
||||
void sendToConverter(audioPacket audio);
|
||||
|
||||
private:
|
||||
|
||||
int writeData(const void* inputBuffer, void* outputBuffer,
|
||||
unsigned long nFrames,
|
||||
const PaStreamCallbackTimeInfo* streamTime,
|
||||
PaStreamCallbackFlags status);
|
||||
static int staticWrite(const void* inputBuffer, void* outputBuffer, unsigned long nFrames, const PaStreamCallbackTimeInfo* streamTime, PaStreamCallbackFlags status, void* userData) {
|
||||
return ((paHandler*)userData)->writeData(inputBuffer, outputBuffer, nFrames, streamTime, status);
|
||||
}
|
||||
|
||||
bool isInitialized = false;
|
||||
PaStream* audio = Q_NULLPTR;
|
||||
PaStreamParameters aParams;
|
||||
const PaDeviceInfo* info;
|
||||
|
||||
quint16 audioLatency;
|
||||
unsigned int chunkSize;
|
||||
|
||||
quint32 lastSeq;
|
||||
quint32 lastSentSeq = 0;
|
||||
|
||||
quint16 currentLatency;
|
||||
quint16 amplitude = 0;
|
||||
qreal volume = 1.0;
|
||||
|
||||
audioSetup setup;
|
||||
QAudioFormat inFormat;
|
||||
QAudioFormat outFormat;
|
||||
audioConverter* converter = Q_NULLPTR;
|
||||
QThread* converterThread = Q_NULLPTR;
|
||||
};
|
||||
|
||||
#endif // PAHANDLER_H
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
// Copyright 2017-2021 Elliott H. Liggett & Phil Taylor
|
||||
|
||||
pttyHandler::pttyHandler(QString pty)
|
||||
pttyHandler::pttyHandler(QString pty, QObject* parent) : QObject(parent)
|
||||
{
|
||||
//constructor
|
||||
if (pty == "" || pty.toLower() == "none")
|
||||
|
@ -214,12 +214,12 @@ void pttyHandler::receiveDataIn(int fd) {
|
|||
if (civId == 0 && inPortData.length() > lastFE + 2 && (quint8)inPortData[lastFE + 2] > (quint8)0xdf && (quint8)inPortData[lastFE + 2] < (quint8)0xef) {
|
||||
// This is (should be) the remotes CIV id.
|
||||
civId = (quint8)inPortData[lastFE + 2];
|
||||
qInfo(logSerial()) << "pty detected remote CI-V:" << hex << civId;
|
||||
qInfo(logSerial()) << "pty detected remote CI-V:" << QString("0x%1").arg(civId,0,16);
|
||||
}
|
||||
else if (civId != 0 && inPortData.length() > lastFE + 2 && (quint8)inPortData[lastFE + 2] != civId)
|
||||
{
|
||||
civId = (quint8)inPortData[lastFE + 2];
|
||||
qInfo(logSerial()) << "pty remote CI-V changed:" << hex << (quint8)civId;
|
||||
qInfo(logSerial()) << "pty remote CI-V changed:" << QString("0x%1").arg((quint8)civId,0,16);
|
||||
}
|
||||
// filter C-IV transceive command before forwarding on.
|
||||
if (inPortData.contains(rigCaps.transceiveCommand))
|
||||
|
|
|
@ -19,7 +19,7 @@ class pttyHandler : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
pttyHandler(QString portName);
|
||||
explicit pttyHandler(QString portName, QObject* parent = nullptr);
|
||||
pttyHandler(QString portName, quint32 baudRate);
|
||||
bool serialError;
|
||||
|
||||
|
|
|
@ -20,13 +20,13 @@
|
|||
|
||||
// Note: When sending \x00, must use QByteArray.setRawData()
|
||||
|
||||
rigCommander::rigCommander()
|
||||
rigCommander::rigCommander(QObject* parent) : QObject(parent)
|
||||
{
|
||||
qInfo(logRig()) << "creating instance of rigCommander()";
|
||||
state.set(SCOPEFUNC, true, false);
|
||||
}
|
||||
|
||||
rigCommander::rigCommander(quint8 guid[GUIDLEN])
|
||||
rigCommander::rigCommander(quint8 guid[GUIDLEN], QObject* parent) : QObject(parent)
|
||||
{
|
||||
qInfo(logRig()) << "creating instance of rigCommander()";
|
||||
state.set(SCOPEFUNC, true, false);
|
||||
|
@ -50,7 +50,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu
|
|||
civAddr = rigCivAddr; // address of the radio. Decimal is 148.
|
||||
usingNativeLAN = false;
|
||||
|
||||
//qInfo(logRig()) << "Opening connection to Rig:" << hex << (unsigned char)rigCivAddr << "on serial port" << rigSerialPort << "at baud rate" << rigBaudRate;
|
||||
//qInfo(logRig()) << "Opening connection to Rig:" << QString("0x%1").arg((unsigned char)rigCivAddr,0,16) << "on serial port" << rigSerialPort << "at baud rate" << rigBaudRate;
|
||||
// ---
|
||||
setup();
|
||||
// ---
|
||||
|
@ -59,8 +59,8 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu
|
|||
this->rigBaudRate = rigBaudRate;
|
||||
rigCaps.baudRate = rigBaudRate;
|
||||
|
||||
comm = new commHandler(rigSerialPort, rigBaudRate,wf);
|
||||
ptty = new pttyHandler(vsp);
|
||||
comm = new commHandler(rigSerialPort, rigBaudRate,wf,this);
|
||||
ptty = new pttyHandler(vsp,this);
|
||||
|
||||
if (tcpPort > 0) {
|
||||
tcp = new tcpServer(this);
|
||||
|
@ -108,7 +108,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud
|
|||
// civAddr = 0x94; // address of the radio. Decimal is 148.
|
||||
civAddr = rigCivAddr; // address of the radio. Decimal is 148.
|
||||
usingNativeLAN = true;
|
||||
// ---
|
||||
// --
|
||||
setup();
|
||||
// ---
|
||||
|
||||
|
@ -118,6 +118,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud
|
|||
udp = new udpHandler(prefs,rxSetup,txSetup);
|
||||
|
||||
udpHandlerThread = new QThread(this);
|
||||
udpHandlerThread->setObjectName("udpHandler()");
|
||||
|
||||
udp->moveToThread(udpHandlerThread);
|
||||
|
||||
|
@ -131,7 +132,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud
|
|||
//this->rigSerialPort = rigSerialPort;
|
||||
//this->rigBaudRate = rigBaudRate;
|
||||
|
||||
ptty = new pttyHandler(vsp);
|
||||
ptty = new pttyHandler(vsp,this);
|
||||
|
||||
if (tcpPort > 0) {
|
||||
tcp = new tcpServer(this);
|
||||
|
@ -3675,7 +3676,7 @@ void rigCommander::determineRigCaps()
|
|||
payloadPrefix.append(civAddr);
|
||||
payloadPrefix.append((char)compCivAddr);
|
||||
// if there is a compile-time error, remove the following line, the "hex" part is the issue:
|
||||
qInfo(logRig()) << "Using incomingCIVAddr: (int): " << this->civAddr << " hex: " << hex << this->civAddr;
|
||||
qInfo(logRig()) << "Using incomingCIVAddr: (int): " << this->civAddr << " hex: " << QString("0x%1").arg(this->civAddr,0,16);
|
||||
emit discoveredRigID(rigCaps);
|
||||
} else {
|
||||
if(!foundRig)
|
||||
|
|
|
@ -69,8 +69,8 @@ class rigCommander : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
rigCommander();
|
||||
rigCommander(quint8 guid[GUIDLEN]);
|
||||
explicit rigCommander(QObject* parent=nullptr);
|
||||
explicit rigCommander(quint8 guid[GUIDLEN], QObject* parent = nullptr);
|
||||
~rigCommander();
|
||||
|
||||
bool usingLAN();
|
||||
|
|
|
@ -326,7 +326,7 @@ class rigCtlD : public QTcpServer
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit rigCtlD(QObject *parent=Q_NULLPTR);
|
||||
explicit rigCtlD(QObject *parent=nullptr);
|
||||
virtual ~rigCtlD();
|
||||
|
||||
int startServer(qint16 port);
|
||||
|
|
|
@ -0,0 +1,340 @@
|
|||
#include "rthandler.h"
|
||||
|
||||
#include "logcategories.h"
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
|
||||
|
||||
rtHandler::rtHandler(QObject* parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
}
|
||||
|
||||
rtHandler::~rtHandler()
|
||||
{
|
||||
|
||||
if (isInitialized) {
|
||||
try {
|
||||
audio->abortStream();
|
||||
audio->closeStream();
|
||||
}
|
||||
catch (RtAudioError& e) {
|
||||
qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage());
|
||||
}
|
||||
delete audio;
|
||||
|
||||
}
|
||||
|
||||
if (converterThread != Q_NULLPTR) {
|
||||
converterThread->quit();
|
||||
converterThread->wait();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool rtHandler::init(audioSetup setup)
|
||||
{
|
||||
if (isInitialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->setup = setup;
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "PortAudio handler starting:" << setup.name;
|
||||
|
||||
if (setup.portInt==-1)
|
||||
{
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
inFormat = toQAudioFormat(setup.codec, setup.sampleRate);
|
||||
|
||||
qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name <<
|
||||
", bits" << inFormat.sampleSize() <<
|
||||
", codec" << setup.codec <<
|
||||
", latency" << setup.latency <<
|
||||
", localAFGain" << setup.localAFgain <<
|
||||
", radioChan" << inFormat.channelCount() <<
|
||||
", resampleQuality" << setup.resampleQuality <<
|
||||
", samplerate" << inFormat.sampleRate() <<
|
||||
", uLaw" << setup.ulaw;
|
||||
|
||||
#if !defined(Q_OS_MACX)
|
||||
//options.flags = !RTAUDIO_HOG_DEVICE | RTAUDIO_MINIMIZE_LATENCY;
|
||||
//options.flags = RTAUDIO_MINIMIZE_LATENCY;
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
audio = new RtAudio(RtAudio::Api::LINUX_ALSA);
|
||||
#elif defined(Q_OS_WIN)
|
||||
audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI);
|
||||
#elif defined(Q_OS_MACX)
|
||||
audio = new RtAudio(RtAudio::Api::MACOSX_CORE);
|
||||
#endif
|
||||
|
||||
options.numberOfBuffers = int(setup.latency/setup.blockSize);
|
||||
|
||||
if (setup.portInt > 0) {
|
||||
aParams.deviceId = setup.portInt;
|
||||
}
|
||||
else if (setup.isinput) {
|
||||
aParams.deviceId = audio->getDefaultInputDevice();
|
||||
}
|
||||
else {
|
||||
aParams.deviceId = audio->getDefaultOutputDevice();
|
||||
}
|
||||
aParams.firstChannel = 0;
|
||||
|
||||
try {
|
||||
info = audio->getDeviceInfo(aParams.deviceId);
|
||||
}
|
||||
catch (RtAudioError& e) {
|
||||
qInfo(logAudio()) << "Device error:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage());
|
||||
return isInitialized;
|
||||
}
|
||||
|
||||
if (info.probed)
|
||||
{
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed";
|
||||
|
||||
RtAudioFormat sampleFormat;
|
||||
outFormat.setByteOrder(QAudioFormat::LittleEndian);
|
||||
outFormat.setCodec("audio/pcm");
|
||||
|
||||
if (info.nativeFormats == 0)
|
||||
{
|
||||
qCritical(logAudio()) << " No natively supported data formats!";
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
qDebug(logAudio()) << " Supported formats:" <<
|
||||
(info.nativeFormats & RTAUDIO_SINT8 ? "8-bit int," : "") <<
|
||||
(info.nativeFormats & RTAUDIO_SINT16 ? "16-bit int," : "") <<
|
||||
(info.nativeFormats & RTAUDIO_SINT24 ? "24-bit int," : "") <<
|
||||
(info.nativeFormats & RTAUDIO_SINT32 ? "32-bit int," : "") <<
|
||||
(info.nativeFormats & RTAUDIO_FLOAT32 ? "32-bit float," : "") <<
|
||||
(info.nativeFormats & RTAUDIO_FLOAT64 ? "64-bit float," : "");
|
||||
|
||||
qInfo(logAudio()) << " Preferred sample rate:" << info.preferredSampleRate;
|
||||
if (setup.isinput) {
|
||||
outFormat.setChannelCount(info.inputChannels);
|
||||
}
|
||||
else {
|
||||
outFormat.setChannelCount(info.outputChannels);
|
||||
}
|
||||
|
||||
qInfo(logAudio()) << " Channels:" << outFormat.channelCount();
|
||||
|
||||
if (outFormat.channelCount() > 2) {
|
||||
outFormat.setChannelCount(2);
|
||||
}
|
||||
else if (outFormat.channelCount() < 1)
|
||||
{
|
||||
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup.";
|
||||
return false;
|
||||
}
|
||||
|
||||
aParams.nChannels = outFormat.channelCount();
|
||||
|
||||
|
||||
outFormat.setSampleRate(info.preferredSampleRate);
|
||||
|
||||
if (outFormat.sampleRate() < 44100) {
|
||||
outFormat.setSampleRate(48000);
|
||||
}
|
||||
|
||||
if (info.nativeFormats & RTAUDIO_FLOAT32) {
|
||||
outFormat.setSampleType(QAudioFormat::Float);
|
||||
outFormat.setSampleSize(32);
|
||||
sampleFormat = RTAUDIO_FLOAT32;
|
||||
}
|
||||
else if (info.nativeFormats & RTAUDIO_SINT32) {
|
||||
outFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
outFormat.setSampleSize(32);
|
||||
sampleFormat = RTAUDIO_SINT32;
|
||||
}
|
||||
else if (info.nativeFormats & RTAUDIO_SINT16) {
|
||||
outFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
outFormat.setSampleSize(16);
|
||||
sampleFormat = RTAUDIO_SINT16;
|
||||
}
|
||||
else {
|
||||
qCritical(logAudio()) << "Cannot find supported sample format!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() <<
|
||||
"Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType();
|
||||
|
||||
// We "hopefully" now have a valid format that is supported so try connecting
|
||||
converter = new audioConverter();
|
||||
converterThread = new QThread(this);
|
||||
if (setup.isinput) {
|
||||
converterThread->setObjectName("audioConvIn()");
|
||||
}
|
||||
else {
|
||||
converterThread->setObjectName("audioConvOut()");
|
||||
}
|
||||
converter->moveToThread(converterThread);
|
||||
|
||||
connect(this, SIGNAL(setupConverter(QAudioFormat, QAudioFormat, quint8, quint8)), converter, SLOT(init(QAudioFormat, QAudioFormat, quint8, quint8)));
|
||||
connect(converterThread, SIGNAL(finished()), converter, SLOT(deleteLater()));
|
||||
connect(this, SIGNAL(sendToConverter(audioPacket)), converter, SLOT(convert(audioPacket)));
|
||||
converterThread->start(QThread::TimeCriticalPriority);
|
||||
|
||||
|
||||
// Per channel chunk size.
|
||||
this->chunkSize = (outFormat.bytesForDuration(setup.blockSize * 1000) / (outFormat.sampleSize()/8) / outFormat.channelCount());
|
||||
|
||||
try {
|
||||
if (setup.isinput) {
|
||||
audio->openStream(NULL, &aParams, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticWrite, this, &options);
|
||||
emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality);
|
||||
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket)));
|
||||
}
|
||||
else {
|
||||
audio->openStream(&aParams, NULL, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticRead, this , &options);
|
||||
emit setupConverter(inFormat, outFormat, 7, setup.resampleQuality);
|
||||
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket)));
|
||||
}
|
||||
audio->startStream();
|
||||
isInitialized = true;
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened";
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "detected latency:" << audio->getStreamLatency();
|
||||
}
|
||||
catch (RtAudioError& e) {
|
||||
qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!";
|
||||
}
|
||||
|
||||
if (!setup.isinput)
|
||||
{
|
||||
this->setVolume(setup.localAFgain);
|
||||
}
|
||||
|
||||
|
||||
return isInitialized;
|
||||
}
|
||||
|
||||
|
||||
void rtHandler::setVolume(unsigned char volume)
|
||||
{
|
||||
|
||||
this->volume = audiopot[volume];
|
||||
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")";
|
||||
}
|
||||
|
||||
void rtHandler::incomingAudio(audioPacket packet)
|
||||
{
|
||||
packet.volume = volume;
|
||||
emit sendToConverter(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
int rtHandler::readData(void* outputBuffer, void* inputBuffer,
|
||||
unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
|
||||
{
|
||||
Q_UNUSED(inputBuffer);
|
||||
Q_UNUSED(streamTime);
|
||||
int nBytes = nFrames * outFormat.channelCount() * (outFormat.sampleSize()/8);
|
||||
|
||||
//lastSentSeq = packet.seq;
|
||||
if (arrayBuffer.length() >= nBytes) {
|
||||
if (audioMutex.tryLock(0)) {
|
||||
std::memcpy(outputBuffer, arrayBuffer.constData(), nBytes);
|
||||
arrayBuffer.remove(0, nBytes);
|
||||
audioMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
if (status == RTAUDIO_INPUT_OVERFLOW) {
|
||||
isUnderrun = true;
|
||||
}
|
||||
else if (status == RTAUDIO_OUTPUT_UNDERFLOW) {
|
||||
isOverrun = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isUnderrun = false;
|
||||
isOverrun = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int rtHandler::writeData(void* outputBuffer, void* inputBuffer,
|
||||
unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
|
||||
{
|
||||
Q_UNUSED(outputBuffer);
|
||||
Q_UNUSED(streamTime);
|
||||
Q_UNUSED(status);
|
||||
audioPacket packet;
|
||||
packet.time = QTime::currentTime();
|
||||
packet.sent = 0;
|
||||
packet.volume = volume;
|
||||
memcpy(&packet.guid, setup.guid, GUIDLEN);
|
||||
packet.data.append((char*)inputBuffer, nFrames *outFormat.channelCount() * (outFormat.sampleSize()/8));
|
||||
emit sendToConverter(packet);
|
||||
if (status == RTAUDIO_INPUT_OVERFLOW) {
|
||||
isUnderrun = true;
|
||||
}
|
||||
else if (status == RTAUDIO_OUTPUT_UNDERFLOW) {
|
||||
isOverrun = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isUnderrun = false;
|
||||
isOverrun = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void rtHandler::convertedOutput(audioPacket packet)
|
||||
{
|
||||
audioMutex.lock();
|
||||
arrayBuffer.append(packet.data);
|
||||
audioMutex.unlock();
|
||||
amplitude = packet.amplitude;
|
||||
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount()) * 1000);
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void rtHandler::convertedInput(audioPacket audio)
|
||||
{
|
||||
if (audio.data.size() > 0) {
|
||||
emit haveAudioData(audio);
|
||||
amplitude = audio.amplitude;
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void rtHandler::changeLatency(const quint16 newSize)
|
||||
{
|
||||
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency;
|
||||
}
|
||||
|
||||
int rtHandler::getLatency()
|
||||
{
|
||||
return currentLatency;
|
||||
}
|
||||
|
||||
|
||||
quint16 rtHandler::getAmplitude()
|
||||
{
|
||||
return amplitude;
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
#ifndef rtHandler_H
|
||||
#define rtHandler_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "RtAudio.h"
|
||||
#else
|
||||
#include "rtaudio/RtAudio.h"
|
||||
#endif
|
||||
|
||||
|
||||
#include <QAudioFormat>
|
||||
#include <QTime>
|
||||
#include <QMap>
|
||||
|
||||
|
||||
/* wfview Packet types */
|
||||
#include "packettypes.h"
|
||||
|
||||
/* Logarithmic taper for volume control */
|
||||
#include "audiotaper.h"
|
||||
|
||||
|
||||
/* Audio converter class*/
|
||||
#include "audioconverter.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
class rtHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
rtHandler(QObject* parent = 0);
|
||||
~rtHandler();
|
||||
|
||||
int getLatency();
|
||||
|
||||
|
||||
void getNextAudioChunk(QByteArray& data);
|
||||
quint16 getAmplitude();
|
||||
|
||||
public slots:
|
||||
bool init(audioSetup setup);
|
||||
void changeLatency(const quint16 newSize);
|
||||
void setVolume(unsigned char volume);
|
||||
void convertedInput(audioPacket audio);
|
||||
void convertedOutput(audioPacket audio);
|
||||
void incomingAudio(const audioPacket data);
|
||||
|
||||
|
||||
private slots:
|
||||
|
||||
signals:
|
||||
void audioMessage(QString message);
|
||||
void sendLatency(quint16 newSize);
|
||||
void haveAudioData(const audioPacket& data);
|
||||
void haveLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
|
||||
void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp);
|
||||
void sendToConverter(audioPacket audio);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
int readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status);
|
||||
|
||||
static int staticRead(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) {
|
||||
return static_cast<rtHandler*>(userData)->readData(outputBuffer, inputBuffer, nFrames, streamTime, status);
|
||||
}
|
||||
|
||||
|
||||
int writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status);
|
||||
|
||||
static int staticWrite(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) {
|
||||
return static_cast<rtHandler*>(userData)->writeData(outputBuffer, inputBuffer, nFrames, streamTime, status);
|
||||
}
|
||||
|
||||
|
||||
bool isInitialized = false;
|
||||
|
||||
RtAudio* audio = Q_NULLPTR;
|
||||
int audioDevice = 0;
|
||||
RtAudio::StreamParameters aParams;
|
||||
RtAudio::StreamOptions options;
|
||||
RtAudio::DeviceInfo info;
|
||||
|
||||
quint16 audioLatency;
|
||||
unsigned int chunkSize;
|
||||
|
||||
quint32 lastSeq;
|
||||
quint32 lastSentSeq = 0;
|
||||
|
||||
quint16 currentLatency;
|
||||
quint16 amplitude = 0;
|
||||
qreal volume = 1.0;
|
||||
|
||||
audioSetup setup;
|
||||
QAudioFormat inFormat;
|
||||
QAudioFormat outFormat;
|
||||
audioConverter* converter = Q_NULLPTR;
|
||||
QThread* converterThread = Q_NULLPTR;
|
||||
QByteArray arrayBuffer;
|
||||
bool isUnderrun = false;
|
||||
bool isOverrun = true;
|
||||
QMutex audioMutex;
|
||||
};
|
||||
|
||||
#endif // rtHandler_H
|
|
@ -35,10 +35,10 @@ void selectRadio::populate(QList<radio_cap_packet> radios)
|
|||
|
||||
void selectRadio::setInUse(quint8 radio, quint8 busy, QString user, QString ip)
|
||||
{
|
||||
if ((radio > 0)&& !this->isVisible()) {
|
||||
qInfo() << "setInUse: radio:" << radio <<"busy" << busy << "user" << user << "ip"<<ip;
|
||||
this->setVisible(true);
|
||||
}
|
||||
//if ((radio > 0)&& !this->isVisible()) {
|
||||
// qInfo() << "setInUse: radio:" << radio <<"busy" << busy << "user" << user << "ip"<<ip;
|
||||
// this->setVisible(true);
|
||||
//}
|
||||
ui->table->setItem(radio, 3, new QTableWidgetItem(user));
|
||||
ui->table->setItem(radio, 4, new QTableWidgetItem(ip));
|
||||
for (int f = 0; f < 5; f++) {
|
||||
|
|
395
servermain.cpp
395
servermain.cpp
|
@ -104,6 +104,7 @@ void servermain::makeRig()
|
|||
qInfo(logSystem()) << "Creating new rigThread()";
|
||||
radio->rig = new rigCommander(radio->guid);
|
||||
radio->rigThread = new QThread(this);
|
||||
radio->rigThread->setObjectName("rigCommander()");
|
||||
|
||||
// Thread:
|
||||
radio->rig->moveToThread(radio->rigThread);
|
||||
|
@ -264,7 +265,7 @@ void servermain::receiveCommReady()
|
|||
void servermain::connectToRig(RIGCONFIG* rig)
|
||||
{
|
||||
if (!rig->rigAvailable) {
|
||||
//qDebug(logSystem()) << "Searching for rig on" << rig->serialPort;
|
||||
qDebug(logSystem()) << "Searching for rig on" << rig->serialPort;
|
||||
QMetaObject::invokeMethod(rig->rig, [=]() {
|
||||
rig->rig->findRigs();
|
||||
}, Qt::QueuedConnection);
|
||||
|
@ -378,6 +379,7 @@ void servermain::setServerToPrefs()
|
|||
udp = new udpServer(&serverConfig);
|
||||
|
||||
serverThread = new QThread(this);
|
||||
serverThread->setObjectName("udpServer()");
|
||||
|
||||
udp->moveToThread(serverThread);
|
||||
|
||||
|
@ -419,6 +421,9 @@ void servermain::setDefPrefs()
|
|||
defPrefs.serialPortBaud = 115200;
|
||||
defPrefs.localAFgain = 255;
|
||||
defPrefs.tcpPort = 0;
|
||||
defPrefs.audioSystem = qtAudio;
|
||||
defPrefs.rxAudio.name = QString("default");
|
||||
defPrefs.txAudio.name = QString("default");
|
||||
|
||||
udpDefPrefs.ipAddress = QString("");
|
||||
udpDefPrefs.controlLANPort = 50001;
|
||||
|
@ -432,104 +437,86 @@ void servermain::setDefPrefs()
|
|||
void servermain::loadSettings()
|
||||
{
|
||||
qInfo(logSystem()) << "Loading settings from " << settings->fileName();
|
||||
prefs.audioSystem = static_cast<audioType>(settings->value("AudioSystem", defPrefs.audioSystem).toInt());
|
||||
|
||||
int numRadios=settings->beginReadArray("Radios");
|
||||
int tempNum = numRadios;
|
||||
int numRadios = settings->beginReadArray("Radios");
|
||||
if (numRadios == 0) {
|
||||
settings->endArray();
|
||||
|
||||
// We assume that QSettings is empty as there are no radios configured, create new:
|
||||
qInfo(logSystem()) << "Creating new settings file " << settings->fileName();
|
||||
settings->setValue("AudioSystem", defPrefs.audioSystem);
|
||||
numRadios = 1;
|
||||
}
|
||||
|
||||
#if defined(RTAUDIO)
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA);
|
||||
#elif defined(Q_OS_WIN)
|
||||
RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI);
|
||||
#elif defined(Q_OS_MACX)
|
||||
RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE);
|
||||
#endif
|
||||
|
||||
// Enumerate audio devices, need to do before settings are loaded.
|
||||
std::map<int, std::string> apiMap;
|
||||
apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio";
|
||||
apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO";
|
||||
apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound";
|
||||
apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI";
|
||||
apiMap[RtAudio::UNIX_JACK] = "Jack Client";
|
||||
apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA";
|
||||
apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio";
|
||||
apiMap[RtAudio::LINUX_OSS] = "Linux OSS";
|
||||
apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy";
|
||||
|
||||
std::vector< RtAudio::Api > apis;
|
||||
RtAudio::getCompiledApi(apis);
|
||||
|
||||
qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion());
|
||||
|
||||
qInfo(logAudio()) << "Compiled APIs:";
|
||||
for (unsigned int i = 0; i < apis.size(); i++) {
|
||||
qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]);
|
||||
}
|
||||
|
||||
RtAudio::DeviceInfo info;
|
||||
|
||||
qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]);
|
||||
|
||||
unsigned int devices = audio->getDeviceCount();
|
||||
qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default";
|
||||
|
||||
#elif defined(PORTAUDIO)
|
||||
// Use PortAudio device enumeration
|
||||
|
||||
PaError err;
|
||||
|
||||
err = Pa_Initialize();
|
||||
|
||||
if (err != paNoError)
|
||||
{
|
||||
qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio";
|
||||
}
|
||||
|
||||
qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText;
|
||||
|
||||
int numDevices;
|
||||
numDevices = Pa_GetDeviceCount();
|
||||
qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices;
|
||||
|
||||
const PaDeviceInfo* info;
|
||||
|
||||
#else
|
||||
|
||||
const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
|
||||
const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
|
||||
|
||||
#endif
|
||||
for (int i = 0; i < numRadios; i++) {
|
||||
if (tempNum == 0) {
|
||||
settings->beginGroup("Radio");
|
||||
}
|
||||
else {
|
||||
settings->beginWriteArray("Radios");
|
||||
for (int i = 0; i < numRadios; i++)
|
||||
{
|
||||
settings->setArrayIndex(i);
|
||||
settings->setValue("RigCIVuInt", defPrefs.radioCIVAddr);
|
||||
settings->setValue("ForceRTSasPTT", defPrefs.forceRTSasPTT);
|
||||
settings->setValue("SerialPortRadio", defPrefs.serialPortRadio);
|
||||
settings->setValue("RigName", "<NONE>");
|
||||
settings->setValue("SerialPortBaud", defPrefs.serialPortBaud);
|
||||
settings->setValue("AudioInput", "default");
|
||||
settings->setValue("AudioOutput", "default");
|
||||
}
|
||||
settings->endArray();
|
||||
|
||||
settings->beginGroup("Server");
|
||||
settings->setValue("ServerEnabled", true);
|
||||
settings->setValue("ServerControlPort", udpDefPrefs.controlLANPort);
|
||||
settings->setValue("ServerCivPort", udpDefPrefs.serialLANPort);
|
||||
settings->setValue("ServerAudioPort", udpDefPrefs.audioLANPort);
|
||||
|
||||
settings->beginWriteArray("Users");
|
||||
settings->setArrayIndex(0);
|
||||
settings->setValue("Username", "user");
|
||||
QByteArray pass;
|
||||
passcode("password", pass);
|
||||
settings->setValue("Password", QString(pass));
|
||||
settings->setValue("UserType", 0);
|
||||
|
||||
settings->endArray();
|
||||
|
||||
settings->endGroup();
|
||||
settings->sync();
|
||||
|
||||
} else {
|
||||
settings->endArray();
|
||||
}
|
||||
|
||||
numRadios = settings->beginReadArray("Radios");
|
||||
int tempNum = numRadios;
|
||||
|
||||
for (int i = 0; i < numRadios; i++) {
|
||||
settings->setArrayIndex(i);
|
||||
RIGCONFIG* tempPrefs = new RIGCONFIG();
|
||||
tempPrefs->civAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt();
|
||||
tempPrefs->forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool();
|
||||
tempPrefs->serialPort = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString();
|
||||
tempPrefs->rigName = settings->value("RigName", "<NONE>").toString();
|
||||
tempPrefs->baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt();
|
||||
tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "default").toString();
|
||||
tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "default").toString();
|
||||
tempPrefs->rxAudioSetup.type = prefs.audioSystem;
|
||||
tempPrefs->txAudioSetup.type = prefs.audioSystem;
|
||||
|
||||
QString tempPort = "auto";
|
||||
if (tempPrefs->rigName=="<NONE>")
|
||||
{
|
||||
foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts())
|
||||
{
|
||||
qDebug(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber();
|
||||
if (serialPortInfo.portName() == tempPrefs->serialPort && !serialPortInfo.serialNumber().isEmpty())
|
||||
if ((serialPortInfo.portName() == tempPrefs->serialPort || tempPrefs->serialPort == "auto") && !serialPortInfo.serialNumber().isEmpty())
|
||||
{
|
||||
tempPrefs->rigName = serialPortInfo.serialNumber();
|
||||
if (serialPortInfo.serialNumber().startsWith("IC-")) {
|
||||
tempPrefs->rigName = serialPortInfo.serialNumber();
|
||||
tempPort = serialPortInfo.portName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tempPrefs->serialPort = tempPort;
|
||||
|
||||
QString guid = settings->value("GUID", "").toString();
|
||||
if (guid.isEmpty()) {
|
||||
guid = QUuid::createUuid().toString();
|
||||
|
@ -545,85 +532,6 @@ void servermain::loadSettings()
|
|||
tempPrefs->rxAudioSetup.resampleQuality = 4;
|
||||
tempPrefs->txAudioSetup.resampleQuality = 4;
|
||||
|
||||
tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "").toString();
|
||||
tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "").toString();
|
||||
bool rxDeviceFound = false;
|
||||
bool txDeviceFound = false;
|
||||
// Find the actual audio devices
|
||||
#if defined(RTAUDIO)
|
||||
for (unsigned int i = 1; i < devices; i++) {
|
||||
info = audio->getDeviceInfo(i);
|
||||
if (info.outputChannels > 0) {
|
||||
if (tempPrefs->txAudioSetup.name == info->name) {
|
||||
tempPrefs->txAudioSetup.port = i;
|
||||
txDeviceFound = true;
|
||||
}
|
||||
}
|
||||
if (info.inputChannels > 0) {
|
||||
if (tempPrefs->rxAudioSetup.name == info->name) {
|
||||
tempPrefs->rxAudioSetup.port = i;
|
||||
rxDeviceFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(PORTAUDIO)
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
info = Pa_GetDeviceInfo(i);
|
||||
if (info->maxInputChannels > 0) {
|
||||
if (tempPrefs->txAudioSetup.name == info->name) {
|
||||
tempPrefs->txAudioSetup.port = i;
|
||||
txDeviceFound = true;
|
||||
}
|
||||
}
|
||||
if (info->maxOutputChannels > 0) {
|
||||
if (tempPrefs->rxAudioSetup.name == info->name) {
|
||||
tempPrefs->rxAudioSetup.port = i;
|
||||
rxDeviceFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
/* If no external library is configured, use QTMultimedia
|
||||
// Set these to default audio devices initially.
|
||||
*/
|
||||
|
||||
//qInfo(logAudio()) << "Looking for audio output devices";
|
||||
for (const QAudioDeviceInfo& deviceInfo : audioOutputs) {
|
||||
qDebug(logSystem()) << "Found Audio output: " << deviceInfo.deviceName();
|
||||
if (deviceInfo.deviceName() == tempPrefs->txAudioSetup.name
|
||||
#ifdef Q_OS_WIN
|
||||
&& deviceInfo.realm() == "wasapi"
|
||||
#endif
|
||||
) {
|
||||
qDebug(logSystem()) << "Audio output: " << deviceInfo.deviceName();
|
||||
tempPrefs->txAudioSetup.port = deviceInfo;
|
||||
txDeviceFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
//qInfo(logAudio()) << "Looking for audio input devices";
|
||||
for (const QAudioDeviceInfo& deviceInfo : audioInputs) {
|
||||
qDebug(logSystem()) << "Found Audio input: " << deviceInfo.deviceName();
|
||||
if (deviceInfo.deviceName() == tempPrefs->rxAudioSetup.name
|
||||
#ifdef Q_OS_WIN
|
||||
&& deviceInfo.realm() == "wasapi"
|
||||
#endif
|
||||
) {
|
||||
qDebug(logSystem()) << "Audio input: " << deviceInfo.deviceName();
|
||||
tempPrefs->rxAudioSetup.port = deviceInfo;
|
||||
rxDeviceFound = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!txDeviceFound) {
|
||||
qInfo() << "Cannot find txAudioDevice" << tempPrefs->txAudioSetup.name;
|
||||
}
|
||||
if (!rxDeviceFound) {
|
||||
qInfo() << "Cannot find rxAudioDevice" << tempPrefs->rxAudioSetup.name;
|
||||
}
|
||||
tempPrefs->rig = Q_NULLPTR;
|
||||
tempPrefs->rigThread = Q_NULLPTR;
|
||||
serverConfig.rigs.append(tempPrefs);
|
||||
|
@ -636,6 +544,167 @@ void servermain::loadSettings()
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Now we have an array of rig objects, we need to match the configured audio devices with physical devices
|
||||
*/
|
||||
switch (prefs.audioSystem)
|
||||
{
|
||||
case rtAudio:
|
||||
{
|
||||
#if defined(Q_OS_LINUX)
|
||||
RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA);
|
||||
// RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_PULSE);
|
||||
#elif defined(Q_OS_WIN)
|
||||
RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI);
|
||||
#elif defined(Q_OS_MACX)
|
||||
RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE);
|
||||
#endif
|
||||
|
||||
// Enumerate audio devices, need to do before settings are loaded.
|
||||
std::map<int, std::string> apiMap;
|
||||
apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio";
|
||||
apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO";
|
||||
apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound";
|
||||
apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI";
|
||||
apiMap[RtAudio::UNIX_JACK] = "Jack Client";
|
||||
apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA";
|
||||
apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio";
|
||||
apiMap[RtAudio::LINUX_OSS] = "Linux OSS";
|
||||
apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy";
|
||||
|
||||
std::vector< RtAudio::Api > apis;
|
||||
RtAudio::getCompiledApi(apis);
|
||||
|
||||
qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion());
|
||||
|
||||
qInfo(logAudio()) << "Compiled APIs:";
|
||||
for (unsigned int i = 0; i < apis.size(); i++) {
|
||||
qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]);
|
||||
}
|
||||
|
||||
RtAudio::DeviceInfo info;
|
||||
|
||||
qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]);
|
||||
|
||||
unsigned int devices = audio->getDeviceCount();
|
||||
qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default";
|
||||
|
||||
for (unsigned int i = 1; i < devices; i++) {
|
||||
info = audio->getDeviceInfo(i);
|
||||
for (RIGCONFIG* rig : serverConfig.rigs)
|
||||
{
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "rxAudio device:" << rig->rxAudioSetup.name;
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "txAudio device:" << rig->txAudioSetup.name;
|
||||
if (info.outputChannels > 0)
|
||||
{
|
||||
qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name);
|
||||
if (rig->txAudioSetup.name.toStdString() == info.name) {
|
||||
rig->txAudioSetup.portInt = i;
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected txAudio device:" << QString(info.name.c_str());
|
||||
}
|
||||
}
|
||||
if (info.inputChannels > 0)
|
||||
{
|
||||
qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name);
|
||||
if (rig->rxAudioSetup.name.toStdString() == info.name) {
|
||||
rig->rxAudioSetup.portInt = i;
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected rxAudio device:" << QString(info.name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case portAudio:
|
||||
{
|
||||
// Use PortAudio device enumeration
|
||||
|
||||
PaError err;
|
||||
|
||||
err = Pa_Initialize();
|
||||
|
||||
if (err != paNoError)
|
||||
{
|
||||
qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio";
|
||||
}
|
||||
|
||||
qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText;
|
||||
|
||||
int numDevices;
|
||||
numDevices = Pa_GetDeviceCount();
|
||||
qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices;
|
||||
|
||||
const PaDeviceInfo* info;
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
info = Pa_GetDeviceInfo(i);
|
||||
for (RIGCONFIG* rig : serverConfig.rigs)
|
||||
{
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "rxAudio device:" << rig->rxAudioSetup.name;
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "txAudio device:" << rig->txAudioSetup.name;
|
||||
if (info->maxInputChannels > 0) {
|
||||
qDebug(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name;
|
||||
|
||||
if (rig->rxAudioSetup.name == info->name) {
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected rxAudio device:" << QString(info->name);
|
||||
rig->rxAudioSetup.portInt = i;
|
||||
}
|
||||
}
|
||||
if (info->maxOutputChannels > 0) {
|
||||
if (rig->txAudioSetup.name == info->name) {
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected txAudio device:" << QString(info->name);
|
||||
rig->txAudioSetup.portInt = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case qtAudio:
|
||||
{
|
||||
const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
|
||||
const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
|
||||
//qInfo(logAudio()) << "Looking for audio input devices";
|
||||
for (const QAudioDeviceInfo& deviceInfo : audioInputs) {
|
||||
qDebug(logSystem()) << "Found Audio input: " << deviceInfo.deviceName();
|
||||
for (RIGCONFIG* rig : serverConfig.rigs)
|
||||
{
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "rxAudio device:" << rig->rxAudioSetup.name;
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "txAudio device:" << rig->txAudioSetup.name;
|
||||
if (deviceInfo.deviceName() == rig->rxAudioSetup.name
|
||||
#ifdef Q_OS_WIN
|
||||
&& deviceInfo.realm() == "wasapi"
|
||||
#endif
|
||||
)
|
||||
{
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected rxAudio device:" << deviceInfo.deviceName();
|
||||
rig->rxAudioSetup.port = deviceInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//qInfo(logAudio()) << "Looking for audio output devices";
|
||||
for (const QAudioDeviceInfo& deviceInfo : audioOutputs) {
|
||||
qDebug(logSystem()) << "Found Audio output: " << deviceInfo.deviceName();
|
||||
for (RIGCONFIG* rig : serverConfig.rigs)
|
||||
{
|
||||
if (deviceInfo.deviceName() == rig->txAudioSetup.name
|
||||
#ifdef Q_OS_WIN
|
||||
&& deviceInfo.realm() == "wasapi"
|
||||
#endif
|
||||
)
|
||||
{
|
||||
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected txAudio device:" << deviceInfo.deviceName();
|
||||
rig->txAudioSetup.port = deviceInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
settings->beginGroup("Server");
|
||||
serverConfig.enabled = settings->value("ServerEnabled", false).toBool();
|
||||
serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt();
|
||||
|
@ -655,24 +724,10 @@ void servermain::loadSettings()
|
|||
user.password = settings->value("Password", "").toString();
|
||||
user.userType = settings->value("UserType", 0).toInt();
|
||||
serverConfig.users.append(user);
|
||||
|
||||
}
|
||||
}
|
||||
settings->endArray();
|
||||
}
|
||||
else {
|
||||
/* Support old way of storing users just to get them loaded*/
|
||||
settings->endArray();
|
||||
numUsers = settings->value("ServerNumUsers", 2).toInt();
|
||||
for (int f = 0; f < numUsers; f++)
|
||||
{
|
||||
SERVERUSER user;
|
||||
user.username = settings->value("ServerUsername_" + QString::number(f), "").toString();
|
||||
user.password = settings->value("ServerPassword_" + QString::number(f), "").toString();
|
||||
user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt();
|
||||
serverConfig.users.append(user);
|
||||
}
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
settings->sync();
|
||||
|
|
|
@ -30,6 +30,13 @@
|
|||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
#include <portaudio.h>
|
||||
#ifdef Q_OS_WIN
|
||||
#include "RtAudio.h"
|
||||
#else
|
||||
#include "rtaudio/RtAudio.h"
|
||||
#endif
|
||||
|
||||
namespace Ui {
|
||||
class wfmain;
|
||||
}
|
||||
|
@ -243,6 +250,7 @@ private:
|
|||
rigCapabilities rigCaps;
|
||||
bool haveRigCaps = false;
|
||||
quint16 tcpPort;
|
||||
audioType audioSystem;
|
||||
} prefs;
|
||||
|
||||
preferences defPrefs;
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
#include "udpaudio.h"
|
||||
#include "logcategories.h"
|
||||
|
||||
// Audio stream
|
||||
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 lport, audioSetup rxSetup, audioSetup txSetup)
|
||||
{
|
||||
qInfo(logUdp()) << "Starting udpAudio";
|
||||
this->localIP = local;
|
||||
this->port = audioPort;
|
||||
this->radioIP = ip;
|
||||
|
||||
if (txSetup.sampleRate == 0) {
|
||||
enableTx = false;
|
||||
}
|
||||
|
||||
init(lport); // Perform connection
|
||||
|
||||
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived);
|
||||
if (rxSetup.type == qtAudio) {
|
||||
rxaudio = new audioHandler();
|
||||
}
|
||||
else if (rxSetup.type == portAudio) {
|
||||
rxaudio = new paHandler();
|
||||
}
|
||||
else if (rxSetup.type == rtAudio) {
|
||||
rxaudio = new rtHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical(logAudio()) << "Unsupported Receive Audio Handler selected!";
|
||||
}
|
||||
|
||||
rxAudioThread = new QThread(this);
|
||||
rxAudioThread->setObjectName("rxAudio()");
|
||||
|
||||
rxaudio->moveToThread(rxAudioThread);
|
||||
|
||||
rxAudioThread->start(QThread::TimeCriticalPriority);
|
||||
|
||||
connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup)));
|
||||
|
||||
// signal/slot not currently used.
|
||||
connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket)));
|
||||
connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16)));
|
||||
connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char)));
|
||||
connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool,bool)));
|
||||
connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater()));
|
||||
|
||||
|
||||
sendControl(false, 0x03, 0x00); // First connect packet
|
||||
|
||||
pingTimer = new QTimer();
|
||||
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
|
||||
pingTimer->start(PING_PERIOD); // send ping packets every 100ms
|
||||
|
||||
if (enableTx) {
|
||||
if (txSetup.type == qtAudio) {
|
||||
txaudio = new audioHandler();
|
||||
}
|
||||
else if (txSetup.type == portAudio) {
|
||||
txaudio = new paHandler();
|
||||
}
|
||||
else if (txSetup.type == rtAudio) {
|
||||
txaudio = new rtHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical(logAudio()) << "Unsupported Transmit Audio Handler selected!";
|
||||
}
|
||||
|
||||
txAudioThread = new QThread(this);
|
||||
rxAudioThread->setObjectName("txAudio()");
|
||||
|
||||
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, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool, bool)));
|
||||
|
||||
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
|
||||
emit setupTxAudio(txSetup);
|
||||
}
|
||||
|
||||
emit setupRxAudio(rxSetup);
|
||||
|
||||
watchdogTimer = new QTimer();
|
||||
connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog);
|
||||
watchdogTimer->start(WATCHDOG_PERIOD);
|
||||
|
||||
areYouThereTimer = new QTimer();
|
||||
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
|
||||
areYouThereTimer->start(AREYOUTHERE_PERIOD);
|
||||
}
|
||||
|
||||
udpAudio::~udpAudio()
|
||||
{
|
||||
if (pingTimer != Q_NULLPTR)
|
||||
{
|
||||
qDebug(logUdp()) << "Stopping pingTimer";
|
||||
pingTimer->stop();
|
||||
delete pingTimer;
|
||||
pingTimer = Q_NULLPTR;
|
||||
}
|
||||
|
||||
if (idleTimer != Q_NULLPTR)
|
||||
{
|
||||
qDebug(logUdp()) << "Stopping idleTimer";
|
||||
idleTimer->stop();
|
||||
delete idleTimer;
|
||||
idleTimer = Q_NULLPTR;
|
||||
}
|
||||
|
||||
if (watchdogTimer != Q_NULLPTR)
|
||||
{
|
||||
qDebug(logUdp()) << "Stopping watchdogTimer";
|
||||
watchdogTimer->stop();
|
||||
delete watchdogTimer;
|
||||
watchdogTimer = Q_NULLPTR;
|
||||
}
|
||||
|
||||
if (rxAudioThread != Q_NULLPTR) {
|
||||
qDebug(logUdp()) << "Stopping rxaudio thread";
|
||||
rxAudioThread->quit();
|
||||
rxAudioThread->wait();
|
||||
}
|
||||
|
||||
if (txAudioThread != Q_NULLPTR) {
|
||||
qDebug(logUdp()) << "Stopping txaudio thread";
|
||||
txAudioThread->quit();
|
||||
txAudioThread->wait();
|
||||
}
|
||||
qDebug(logUdp()) << "udpHandler successfully closed";
|
||||
}
|
||||
|
||||
void udpAudio::watchdog()
|
||||
{
|
||||
static bool alerted = false;
|
||||
if (lastReceived.msecsTo(QTime::currentTime()) > 2000)
|
||||
{
|
||||
if (!alerted) {
|
||||
/* Just log it at the moment, maybe try signalling the control channel that it needs to
|
||||
try requesting civ/audio again? */
|
||||
|
||||
qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required?";
|
||||
alerted = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
alerted = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void udpAudio::sendTxAudio()
|
||||
{
|
||||
if (txaudio == Q_NULLPTR) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void udpAudio::changeLatency(quint16 value)
|
||||
{
|
||||
emit haveChangeLatency(value);
|
||||
}
|
||||
|
||||
void udpAudio::setVolume(unsigned char value)
|
||||
{
|
||||
emit haveSetVolume(value);
|
||||
}
|
||||
|
||||
void udpAudio::getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over) {
|
||||
|
||||
emit haveRxLevels(amplitude, latency, current, under, over);
|
||||
}
|
||||
|
||||
void udpAudio::getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over) {
|
||||
emit haveTxLevels(amplitude, latency, current, under, over);
|
||||
}
|
||||
|
||||
void udpAudio::dataReceived()
|
||||
{
|
||||
while (udp->hasPendingDatagrams()) {
|
||||
QNetworkDatagram datagram = udp->receiveDatagram();
|
||||
//qInfo(logUdp()) << "Received: " << datagram.data().mid(0,10);
|
||||
QByteArray r = datagram.data();
|
||||
|
||||
switch (r.length())
|
||||
{
|
||||
case (16): // Response to control packet handled in udpBase
|
||||
{
|
||||
//control_packet_t in = (control_packet_t)r.constData();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
/* Audio packets start as follows:
|
||||
PCM 16bit and PCM8/uLAW stereo: 0x44,0x02 for first packet and 0x6c,0x05 for second.
|
||||
uLAW 8bit/PCM 8bit 0xd8,0x03 for all packets
|
||||
PCM 16bit stereo 0x6c,0x05 first & second 0x70,0x04 third
|
||||
|
||||
|
||||
*/
|
||||
control_packet_t in = (control_packet_t)r.constData();
|
||||
|
||||
if (in->type != 0x01 && in->len >= 0x20) {
|
||||
if (in->seq == 0)
|
||||
{
|
||||
// Seq number has rolled over.
|
||||
seqPrefix++;
|
||||
}
|
||||
|
||||
// 0xac is the smallest possible audio packet.
|
||||
lastReceived = QTime::currentTime();
|
||||
audioPacket tempAudio;
|
||||
tempAudio.seq = (quint32)seqPrefix << 16 | in->seq;
|
||||
tempAudio.time = lastReceived;
|
||||
tempAudio.sent = 0;
|
||||
tempAudio.data = r.mid(0x18);
|
||||
// Prefer signal/slot to forward audio as it is thread/safe
|
||||
// Need to do more testing but latency appears fine.
|
||||
//rxaudio->incomingAudio(tempAudio);
|
||||
emit haveAudioData(tempAudio);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
udpBase::dataReceived(r); // Call parent function to process the rest.
|
||||
r.clear();
|
||||
datagram.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
#ifndef UDPAUDIO_H
|
||||
#define UDPAUDIO_H
|
||||
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
#include <QNetworkDatagram>
|
||||
#include <QHostInfo>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QDateTime>
|
||||
#include <QByteArray>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
#include <QUuid>
|
||||
|
||||
// Allow easy endian-ness conversions
|
||||
#include <QtEndian>
|
||||
|
||||
// Needed for audio
|
||||
#include <QBuffer>
|
||||
#include <QThread>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "packettypes.h"
|
||||
|
||||
#include "udpbase.h"
|
||||
|
||||
#include "audiohandler.h"
|
||||
#include "pahandler.h"
|
||||
#include "rthandler.h"
|
||||
|
||||
|
||||
// Class for all audio communications.
|
||||
class udpAudio : public udpBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 lport, audioSetup rxSetup, audioSetup txSetup);
|
||||
~udpAudio();
|
||||
|
||||
int audioLatency = 0;
|
||||
|
||||
signals:
|
||||
void haveAudioData(audioPacket data);
|
||||
|
||||
void setupTxAudio(audioSetup setup);
|
||||
void setupRxAudio(audioSetup setup);
|
||||
|
||||
void haveChangeLatency(quint16 value);
|
||||
void haveSetVolume(unsigned char value);
|
||||
void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
|
||||
void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
|
||||
|
||||
public slots:
|
||||
void changeLatency(quint16 value);
|
||||
void setVolume(unsigned char value);
|
||||
void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
|
||||
void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
|
||||
void receiveAudioData(audioPacket audio);
|
||||
|
||||
private:
|
||||
|
||||
void sendTxAudio();
|
||||
void dataReceived();
|
||||
void watchdog();
|
||||
|
||||
uint16_t sendAudioSeq = 0;
|
||||
|
||||
QObject* rxaudio = Q_NULLPTR;
|
||||
QThread* rxAudioThread = Q_NULLPTR;
|
||||
|
||||
QObject* txaudio = Q_NULLPTR;
|
||||
QThread* txAudioThread = Q_NULLPTR;
|
||||
|
||||
QTimer* txAudioTimer = Q_NULLPTR;
|
||||
bool enableTx = true;
|
||||
|
||||
QMutex audioMutex;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,545 @@
|
|||
#include "udpbase.h"
|
||||
#include "logcategories.h"
|
||||
|
||||
void udpBase::init(quint16 lport)
|
||||
{
|
||||
//timeStarted.start();
|
||||
udp = new QUdpSocket(this);
|
||||
udp->bind(lport); // Bind to random port.
|
||||
localPort = udp->localPort();
|
||||
qInfo(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port;
|
||||
uint32_t addr = localIP.toIPv4Address();
|
||||
myId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (localPort & 0xffff);
|
||||
|
||||
retransmitTimer = new QTimer();
|
||||
connect(retransmitTimer, &QTimer::timeout, this, &udpBase::sendRetransmitRequest);
|
||||
retransmitTimer->start(RETRANSMIT_PERIOD);
|
||||
}
|
||||
|
||||
udpBase::~udpBase()
|
||||
{
|
||||
qInfo(logUdp()) << "Closing UDP stream :" << radioIP.toString() << ":" << port;
|
||||
if (udp != Q_NULLPTR) {
|
||||
sendControl(false, 0x05, 0x00); // Send disconnect
|
||||
udp->close();
|
||||
delete udp;
|
||||
}
|
||||
if (areYouThereTimer != Q_NULLPTR)
|
||||
{
|
||||
areYouThereTimer->stop();
|
||||
delete areYouThereTimer;
|
||||
}
|
||||
|
||||
if (pingTimer != Q_NULLPTR)
|
||||
{
|
||||
pingTimer->stop();
|
||||
delete pingTimer;
|
||||
}
|
||||
if (idleTimer != Q_NULLPTR)
|
||||
{
|
||||
idleTimer->stop();
|
||||
delete idleTimer;
|
||||
}
|
||||
if (retransmitTimer != Q_NULLPTR)
|
||||
{
|
||||
retransmitTimer->stop();
|
||||
delete retransmitTimer;
|
||||
}
|
||||
|
||||
pingTimer = Q_NULLPTR;
|
||||
idleTimer = Q_NULLPTR;
|
||||
areYouThereTimer = Q_NULLPTR;
|
||||
retransmitTimer = Q_NULLPTR;
|
||||
|
||||
}
|
||||
|
||||
// Base class!
|
||||
|
||||
void udpBase::dataReceived(QByteArray r)
|
||||
{
|
||||
if (r.length() < 0x10)
|
||||
{
|
||||
return; // Packet too small do to anything with?
|
||||
}
|
||||
|
||||
switch (r.length())
|
||||
{
|
||||
case (CONTROL_SIZE): // Empty response used for simple comms and retransmit requests.
|
||||
{
|
||||
control_packet_t in = (control_packet_t)r.constData();
|
||||
if (in->type == 0x01 && in->len == 0x10)
|
||||
{
|
||||
// Single packet request
|
||||
packetsLost++;
|
||||
congestion = static_cast<double>(packetsSent) / packetsLost * 100;
|
||||
txBufferMutex.lock();
|
||||
auto match = txSeqBuf.find(in->seq);
|
||||
if (match != txSeqBuf.end()) {
|
||||
// Found matching entry?
|
||||
// Send "untracked" as it has already been sent once.
|
||||
// Don't constantly retransmit the same packet, give-up eventually
|
||||
qDebug(logUdp()) << this->metaObject()->className() << ": Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16);
|
||||
match->retransmitCount++;
|
||||
udpMutex.lock();
|
||||
udp->writeDatagram(match->data, radioIP, port);
|
||||
udpMutex.unlock();
|
||||
}
|
||||
else {
|
||||
qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet"
|
||||
<< QString("0x%1").arg(in->seq, 0, 16) <<
|
||||
"not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) <<
|
||||
"to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16);
|
||||
}
|
||||
txBufferMutex.unlock();
|
||||
}
|
||||
if (in->type == 0x04) {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": Received I am here ";
|
||||
areYouThereCounter = 0;
|
||||
// I don't think that we will ever receive an "I am here" other than in response to "Are you there?"
|
||||
remoteId = in->sentid;
|
||||
if (areYouThereTimer != Q_NULLPTR && areYouThereTimer->isActive()) {
|
||||
// send ping packets every second
|
||||
areYouThereTimer->stop();
|
||||
}
|
||||
sendControl(false, 0x06, 0x01); // Send Are you ready - untracked.
|
||||
}
|
||||
else if (in->type == 0x06)
|
||||
{
|
||||
// Just get the seqnum and ignore the rest.
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (PING_SIZE): // ping packet
|
||||
{
|
||||
ping_packet_t in = (ping_packet_t)r.constData();
|
||||
if (in->type == 0x07)
|
||||
{
|
||||
// It is a ping request/response
|
||||
if (in->reply == 0x00)
|
||||
{
|
||||
ping_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p);
|
||||
p.type = 0x07;
|
||||
p.sentid = myId;
|
||||
p.rcvdid = remoteId;
|
||||
p.reply = 0x01;
|
||||
p.seq = in->seq;
|
||||
p.time = in->time;
|
||||
udpMutex.lock();
|
||||
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
|
||||
udpMutex.unlock();
|
||||
}
|
||||
else if (in->reply == 0x01) {
|
||||
if (in->seq == pingSendSeq)
|
||||
{
|
||||
// This is response to OUR request so increment counter
|
||||
pingSendSeq++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16];
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
||||
// All packets "should" be added to the incoming buffer.
|
||||
// First check that we haven't already received it.
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// All packets except ping and retransmit requests should trigger this.
|
||||
control_packet_t in = (control_packet_t)r.constData();
|
||||
|
||||
// This is a variable length retransmit request!
|
||||
if (in->type == 0x01 && in->len != 0x10)
|
||||
{
|
||||
|
||||
for (quint16 i = 0x10; i < r.length(); i = i + 2)
|
||||
{
|
||||
quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8;
|
||||
auto match = txSeqBuf.find(seq);
|
||||
if (match == txSeqBuf.end()) {
|
||||
qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet"
|
||||
<< QString("0x%1").arg(seq, 0, 16) <<
|
||||
"not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) <<
|
||||
"to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16);
|
||||
// Just send idle packet.
|
||||
sendControl(false, 0, seq);
|
||||
}
|
||||
else {
|
||||
// Found matching entry?
|
||||
// Send "untracked" as it has already been sent once.
|
||||
qDebug(logUdp()) << this->metaObject()->className() << ": Sending (multiple packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16);
|
||||
match->retransmitCount++;
|
||||
udpMutex.lock();
|
||||
udp->writeDatagram(match->data, radioIP, port);
|
||||
udpMutex.unlock();
|
||||
packetsLost++;
|
||||
congestion = static_cast<double>(packetsSent) / packetsLost * 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (in->len != PING_SIZE && in->type == 0x00 && in->seq != 0x00)
|
||||
{
|
||||
rxBufferMutex.lock();
|
||||
if (rxSeqBuf.isEmpty()) {
|
||||
rxSeqBuf.insert(in->seq, QTime::currentTime());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (in->seq < rxSeqBuf.firstKey() || in->seq - rxSeqBuf.lastKey() > MAX_MISSING)
|
||||
{
|
||||
qInfo(logUdp()) << this->metaObject()->className() << "Large seq number gap detected, previous highest: " <<
|
||||
QString("0x%1").arg(rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16);
|
||||
//seqPrefix++;
|
||||
// Looks like it has rolled over so clear buffer and start again.
|
||||
rxSeqBuf.clear();
|
||||
// Add this packet to the incoming buffer
|
||||
rxSeqBuf.insert(in->seq, QTime::currentTime());
|
||||
rxBufferMutex.unlock();
|
||||
missingMutex.lock();
|
||||
rxMissing.clear();
|
||||
missingMutex.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rxSeqBuf.contains(in->seq))
|
||||
{
|
||||
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
|
||||
|
||||
if (in->seq > rxSeqBuf.lastKey() + 1) {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << "1 or more missing packets detected, previous: " <<
|
||||
QString("0x%1").arg(rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16);
|
||||
// We are likely missing packets then!
|
||||
missingMutex.lock();
|
||||
//int missCounter = 0;
|
||||
// Sanity check!
|
||||
for (quint16 f = rxSeqBuf.lastKey() + 1; f <= in->seq; f++)
|
||||
{
|
||||
if (rxSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
rxSeqBuf.erase(rxSeqBuf.begin());
|
||||
}
|
||||
rxSeqBuf.insert(f, QTime::currentTime());
|
||||
if (f != in->seq && !rxMissing.contains(f))
|
||||
{
|
||||
rxMissing.insert(f, 0);
|
||||
}
|
||||
}
|
||||
missingMutex.unlock();
|
||||
}
|
||||
else {
|
||||
if (rxSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
rxSeqBuf.erase(rxSeqBuf.begin());
|
||||
}
|
||||
rxSeqBuf.insert(in->seq, QTime::currentTime());
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This is probably one of our missing packets!
|
||||
missingMutex.lock();
|
||||
auto s = rxMissing.find(in->seq);
|
||||
if (s != rxMissing.end())
|
||||
{
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << QString("0x%1").arg(in->seq, 0, 16);
|
||||
|
||||
s = rxMissing.erase(s);
|
||||
}
|
||||
missingMutex.unlock();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
rxBufferMutex.unlock();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void udpBase::sendRetransmitRequest()
|
||||
{
|
||||
// Find all gaps in received packets and then send requests for them.
|
||||
// This will run every 100ms so out-of-sequence packets will not trigger a retransmit request.
|
||||
if (rxMissing.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
else if (rxMissing.size() > MAX_MISSING) {
|
||||
qInfo(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers";
|
||||
missingMutex.lock();
|
||||
rxMissing.clear();
|
||||
missingMutex.unlock();
|
||||
|
||||
rxBufferMutex.lock();
|
||||
rxSeqBuf.clear();
|
||||
rxBufferMutex.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray missingSeqs;
|
||||
|
||||
missingMutex.lock();
|
||||
auto it = rxMissing.begin();
|
||||
while (it != rxMissing.end())
|
||||
{
|
||||
if (it.key() != 0x0)
|
||||
{
|
||||
if (it.value() < 4)
|
||||
{
|
||||
missingSeqs.append(it.key() & 0xff);
|
||||
missingSeqs.append(it.key() >> 8 & 0xff);
|
||||
missingSeqs.append(it.key() & 0xff);
|
||||
missingSeqs.append(it.key() >> 8 & 0xff);
|
||||
it.value()++;
|
||||
it++;
|
||||
}
|
||||
else {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << QString("0x%1").arg(it.key(), 0, 16) << "deleting";
|
||||
it = rxMissing.erase(it);
|
||||
}
|
||||
}
|
||||
else {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": found empty key in missing buffer";
|
||||
it++;
|
||||
}
|
||||
}
|
||||
missingMutex.unlock();
|
||||
|
||||
if (missingSeqs.length() != 0)
|
||||
{
|
||||
control_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p);
|
||||
p.type = 0x01;
|
||||
p.seq = 0x0000;
|
||||
p.sentid = myId;
|
||||
p.rcvdid = remoteId;
|
||||
if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control.
|
||||
{
|
||||
p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8);
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq, 0, 16);
|
||||
udpMutex.lock();
|
||||
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
|
||||
udpMutex.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':');
|
||||
missingMutex.lock();
|
||||
p.len = sizeof(p) + missingSeqs.size();
|
||||
missingSeqs.insert(0, p.packet, sizeof(p));
|
||||
missingMutex.unlock();
|
||||
|
||||
udpMutex.lock();
|
||||
udp->writeDatagram(missingSeqs, radioIP, port);
|
||||
udpMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Used to send idle and other "control" style messages
|
||||
void udpBase::sendControl(bool tracked = true, quint8 type = 0, quint16 seq = 0)
|
||||
{
|
||||
control_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p);
|
||||
p.type = type;
|
||||
p.sentid = myId;
|
||||
p.rcvdid = remoteId;
|
||||
|
||||
if (!tracked) {
|
||||
p.seq = seq;
|
||||
udpMutex.lock();
|
||||
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
|
||||
udpMutex.unlock();
|
||||
}
|
||||
else {
|
||||
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Send periodic ping packets
|
||||
void udpBase::sendPing()
|
||||
{
|
||||
ping_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p);
|
||||
p.type = 0x07;
|
||||
p.sentid = myId;
|
||||
p.rcvdid = remoteId;
|
||||
p.seq = pingSendSeq;
|
||||
QTime now = QTime::currentTime();
|
||||
p.time = (quint32)now.msecsSinceStartOfDay();
|
||||
lastPingSentTime = QDateTime::currentDateTime();
|
||||
udpMutex.lock();
|
||||
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
|
||||
udpMutex.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void udpBase::sendTrackedPacket(QByteArray d)
|
||||
{
|
||||
// As the radio can request retransmission of these packets, store them in a buffer
|
||||
d[6] = sendSeq & 0xff;
|
||||
d[7] = (sendSeq >> 8) & 0xff;
|
||||
SEQBUFENTRY s;
|
||||
s.seqNum = sendSeq;
|
||||
s.timeSent = QTime::currentTime();
|
||||
s.retransmitCount = 0;
|
||||
s.data = d;
|
||||
if (txBufferMutex.tryLock(100))
|
||||
{
|
||||
|
||||
if (sendSeq == 0) {
|
||||
// We are either the first ever sent packet or have rolled-over so clear the buffer.
|
||||
txSeqBuf.clear();
|
||||
congestion = 0;
|
||||
}
|
||||
if (txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
txSeqBuf.erase(txSeqBuf.begin());
|
||||
}
|
||||
txSeqBuf.insert(sendSeq, s);
|
||||
|
||||
txBufferMutex.unlock();
|
||||
}
|
||||
else {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked";
|
||||
}
|
||||
// Stop using purgeOldEntries() as it is likely slower than just removing the earliest packet.
|
||||
//qInfo(logUdp()) << this->metaObject()->className() << "RX:" << rxSeqBuf.size() << "TX:" <<txSeqBuf.size() << "MISS:" << rxMissing.size();
|
||||
//purgeOldEntries(); // Delete entries older than PURGE_SECONDS seconds (currently 5)
|
||||
sendSeq++;
|
||||
|
||||
udpMutex.lock();
|
||||
udp->writeDatagram(d, radioIP, port);
|
||||
if (congestion > 10) { // Poor quality connection?
|
||||
udp->writeDatagram(d, radioIP, port);
|
||||
if (congestion > 20) // Even worse so send again.
|
||||
udp->writeDatagram(d, radioIP, port);
|
||||
}
|
||||
if (idleTimer != Q_NULLPTR && idleTimer->isActive()) {
|
||||
idleTimer->start(IDLE_PERIOD); // Reset idle counter if it's running
|
||||
}
|
||||
udpMutex.unlock();
|
||||
packetsSent++;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Once a packet has reached PURGE_SECONDS old (currently 10) then it is not likely to be any use.
|
||||
/// </summary>
|
||||
void udpBase::purgeOldEntries()
|
||||
{
|
||||
// Erase old entries from the tx packet buffer
|
||||
if (txBufferMutex.tryLock(100))
|
||||
{
|
||||
if (!txSeqBuf.isEmpty())
|
||||
{
|
||||
// Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS
|
||||
for (auto it = txSeqBuf.begin(); it != txSeqBuf.end();) {
|
||||
if (it.value().timeSent.secsTo(QTime::currentTime()) > PURGE_SECONDS) {
|
||||
txSeqBuf.erase(it++);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
txBufferMutex.unlock();
|
||||
|
||||
}
|
||||
else {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked";
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (rxBufferMutex.tryLock(100))
|
||||
{
|
||||
if (!rxSeqBuf.isEmpty()) {
|
||||
// Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS
|
||||
for (auto it = rxSeqBuf.begin(); it != rxSeqBuf.end();) {
|
||||
if (it.value().secsTo(QTime::currentTime()) > PURGE_SECONDS) {
|
||||
rxSeqBuf.erase(it++);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
rxBufferMutex.unlock();
|
||||
}
|
||||
else {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": rxBuffer mutex is locked";
|
||||
}
|
||||
|
||||
if (missingMutex.tryLock(100))
|
||||
{
|
||||
// Erase old entries from the missing packets buffer
|
||||
if (!rxMissing.isEmpty() && rxMissing.size() > 50) {
|
||||
for (size_t i = 0; i < 25; ++i) {
|
||||
rxMissing.erase(rxMissing.begin());
|
||||
}
|
||||
}
|
||||
missingMutex.unlock();
|
||||
}
|
||||
else {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": missingBuffer mutex is locked";
|
||||
}
|
||||
}
|
||||
|
||||
void udpBase::printHex(const QByteArray& pdata)
|
||||
{
|
||||
printHex(pdata, false, true);
|
||||
}
|
||||
|
||||
void udpBase::printHex(const QByteArray& pdata, bool printVert, bool printHoriz)
|
||||
{
|
||||
qDebug(logUdp()) << "---- Begin hex dump -----:";
|
||||
QString sdata("DATA: ");
|
||||
QString index("INDEX: ");
|
||||
QStringList strings;
|
||||
|
||||
for (int i = 0; i < pdata.length(); i++)
|
||||
{
|
||||
strings << QString("[%1]: %2").arg(i, 8, 10, QChar('0')).arg((unsigned char)pdata[i], 2, 16, QChar('0'));
|
||||
sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0')));
|
||||
index.append(QString("%1 ").arg(i, 2, 10, QChar('0')));
|
||||
}
|
||||
|
||||
if (printVert)
|
||||
{
|
||||
for (int i = 0; i < strings.length(); i++)
|
||||
{
|
||||
//sdata = QString(strings.at(i));
|
||||
qDebug(logUdp()) << strings.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (printHoriz)
|
||||
{
|
||||
qDebug(logUdp()) << index;
|
||||
qDebug(logUdp()) << sdata;
|
||||
}
|
||||
qDebug(logUdp()) << "----- End hex dump -----";
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
#ifndef UDPBASE_H
|
||||
#define UDPBASE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
#include <QNetworkDatagram>
|
||||
#include <QHostInfo>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QDateTime>
|
||||
#include <QByteArray>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
#include <QUuid>
|
||||
|
||||
// Allow easy endian-ness conversions
|
||||
#include <QtEndian>
|
||||
|
||||
// Needed for audio
|
||||
#include <QBuffer>
|
||||
#include <QThread>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "packettypes.h"
|
||||
|
||||
|
||||
|
||||
struct udpPreferences {
|
||||
QString ipAddress;
|
||||
quint16 controlLANPort;
|
||||
quint16 serialLANPort;
|
||||
quint16 audioLANPort;
|
||||
QString username;
|
||||
QString password;
|
||||
QString clientName;
|
||||
quint8 waterfallFormat;
|
||||
};
|
||||
|
||||
struct networkStatus {
|
||||
quint8 rxAudioBufferPercent;
|
||||
quint8 txAudioBufferPercent;
|
||||
quint8 rxAudioLevel;
|
||||
quint8 txAudioLevel;
|
||||
quint16 rxLatency;
|
||||
quint16 txLatency;
|
||||
bool rxUnderrun;
|
||||
bool txUnderrun;
|
||||
bool rxOverrun;
|
||||
bool txOverrun;
|
||||
quint16 rxCurrentLatency;
|
||||
quint16 txCurrentLatency;
|
||||
quint32 packetsSent = 0;
|
||||
quint32 packetsLost = 0;
|
||||
quint16 rtt = 0;
|
||||
quint32 networkLatency = 0;
|
||||
QString message;
|
||||
};
|
||||
|
||||
|
||||
// Parent class that contains all common items.
|
||||
class udpBase : public QObject
|
||||
{
|
||||
|
||||
|
||||
public:
|
||||
~udpBase();
|
||||
|
||||
void init(quint16 local);
|
||||
|
||||
void reconnect();
|
||||
|
||||
void dataReceived(QByteArray r);
|
||||
void sendPing();
|
||||
void sendRetransmitRange(quint16 first, quint16 second, quint16 third, quint16 fourth);
|
||||
|
||||
void sendControl(bool tracked, quint8 id, quint16 seq);
|
||||
|
||||
void printHex(const QByteArray& pdata);
|
||||
void printHex(const QByteArray& pdata, bool printVert, bool printHoriz);
|
||||
|
||||
|
||||
//QTime timeStarted;
|
||||
|
||||
QUdpSocket* udp = Q_NULLPTR;
|
||||
uint32_t myId = 0;
|
||||
uint32_t remoteId = 0;
|
||||
uint16_t authSeq = 0x30;
|
||||
uint16_t sendSeqB = 0;
|
||||
uint16_t sendSeq = 1;
|
||||
uint16_t lastReceivedSeq = 1;
|
||||
uint16_t pkt0SendSeq = 0;
|
||||
uint16_t periodicSeq = 0;
|
||||
quint64 latency = 0;
|
||||
|
||||
QString username = "";
|
||||
QString password = "";
|
||||
QHostAddress radioIP;
|
||||
QHostAddress localIP;
|
||||
bool isAuthenticated = false;
|
||||
quint16 localPort = 0;
|
||||
quint16 port = 0;
|
||||
bool periodicRunning = false;
|
||||
bool sentPacketConnect2 = false;
|
||||
QTime lastReceived = QTime::currentTime();
|
||||
QMutex udpMutex;
|
||||
QMutex txBufferMutex;
|
||||
QMutex rxBufferMutex;
|
||||
QMutex missingMutex;
|
||||
|
||||
struct SEQBUFENTRY {
|
||||
QTime timeSent;
|
||||
uint16_t seqNum;
|
||||
QByteArray data;
|
||||
quint8 retransmitCount;
|
||||
};
|
||||
|
||||
QMap<quint16, QTime> rxSeqBuf;
|
||||
QMap<quint16, SEQBUFENTRY> txSeqBuf;
|
||||
QMap<quint16, int> rxMissing;
|
||||
|
||||
void sendTrackedPacket(QByteArray d);
|
||||
void purgeOldEntries();
|
||||
|
||||
QTimer* areYouThereTimer = Q_NULLPTR; // Send are-you-there packets every second until a response is received.
|
||||
QTimer* pingTimer = Q_NULLPTR; // Start sending pings immediately.
|
||||
QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected.
|
||||
|
||||
QTimer* watchdogTimer = Q_NULLPTR;
|
||||
QTimer* retransmitTimer = Q_NULLPTR;
|
||||
|
||||
QDateTime lastPingSentTime;
|
||||
uint16_t pingSendSeq = 0;
|
||||
|
||||
quint16 areYouThereCounter = 0;
|
||||
|
||||
quint32 packetsSent = 0;
|
||||
quint32 packetsLost = 0;
|
||||
|
||||
quint16 seqPrefix = 0;
|
||||
QString connectionType = "";
|
||||
int congestion = 0;
|
||||
|
||||
|
||||
private:
|
||||
void sendRetransmitRequest();
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// passcode function used to generate secure (ish) code
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns>pointer to encoded username or password</returns>
|
||||
static inline void passcode(QString in, QByteArray& out)
|
||||
{
|
||||
const quint8 sequence[] =
|
||||
{
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0x47,0x5d,0x4c,0x42,0x66,0x20,0x23,0x46,0x4e,0x57,0x45,0x3d,0x67,0x76,0x60,0x41,0x62,0x39,0x59,0x2d,0x68,0x7e,
|
||||
0x7c,0x65,0x7d,0x49,0x29,0x72,0x73,0x78,0x21,0x6e,0x5a,0x5e,0x4a,0x3e,0x71,0x2c,0x2a,0x54,0x3c,0x3a,0x63,0x4f,
|
||||
0x43,0x75,0x27,0x79,0x5b,0x35,0x70,0x48,0x6b,0x56,0x6f,0x34,0x32,0x6c,0x30,0x61,0x6d,0x7b,0x2f,0x4b,0x64,0x38,
|
||||
0x2b,0x2e,0x50,0x40,0x3f,0x55,0x33,0x37,0x25,0x77,0x24,0x26,0x74,0x6a,0x28,0x53,0x4d,0x69,0x22,0x5c,0x44,0x31,
|
||||
0x36,0x58,0x3b,0x7a,0x51,0x5f,0x52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
|
||||
};
|
||||
|
||||
QByteArray ba = in.toLocal8Bit();
|
||||
uchar* ascii = (uchar*)ba.constData();
|
||||
for (int i = 0; i < in.length() && i < 16; i++)
|
||||
{
|
||||
int p = ascii[i] + i;
|
||||
if (p > 126)
|
||||
{
|
||||
p = 32 + p % 127;
|
||||
}
|
||||
out.append(sequence[p]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns a QByteArray of a null terminated string
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
static inline QByteArray parseNullTerminatedString(QByteArray c, int s)
|
||||
{
|
||||
//QString res = "";
|
||||
QByteArray res;
|
||||
for (int i = s; i < c.length(); i++)
|
||||
{
|
||||
if (c[i] != '\0')
|
||||
{
|
||||
res.append(c[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,268 @@
|
|||
#include "udpcivdata.h"
|
||||
#include "logcategories.h"
|
||||
|
||||
// Class that manages all Civ Data to/from the rig
|
||||
udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 localPort = 0)
|
||||
{
|
||||
qInfo(logUdp()) << "Starting udpCivData";
|
||||
localIP = local;
|
||||
port = civPort;
|
||||
radioIP = ip;
|
||||
splitWaterfall = splitWf;
|
||||
|
||||
udpBase::init(localPort); // Perform connection
|
||||
|
||||
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpCivData::dataReceived);
|
||||
|
||||
sendControl(false, 0x03, 0x00); // First connect packet
|
||||
|
||||
/*
|
||||
Connect various timers
|
||||
*/
|
||||
pingTimer = new QTimer();
|
||||
idleTimer = new QTimer();
|
||||
areYouThereTimer = new QTimer();
|
||||
startCivDataTimer = new QTimer();
|
||||
watchdogTimer = new QTimer();
|
||||
|
||||
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
|
||||
connect(watchdogTimer, &QTimer::timeout, this, &udpCivData::watchdog);
|
||||
connect(idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0));
|
||||
connect(startCivDataTimer, &QTimer::timeout, this, std::bind(&udpCivData::sendOpenClose, this, false));
|
||||
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
|
||||
watchdogTimer->start(WATCHDOG_PERIOD);
|
||||
// Start sending are you there packets - will be stopped once "I am here" received
|
||||
// send ping packets every 100 ms (maybe change to less frequent?)
|
||||
pingTimer->start(PING_PERIOD);
|
||||
// Send idle packets every 100ms, this timer will be reset every time a non-idle packet is sent.
|
||||
idleTimer->start(IDLE_PERIOD);
|
||||
areYouThereTimer->start(AREYOUTHERE_PERIOD);
|
||||
}
|
||||
|
||||
udpCivData::~udpCivData()
|
||||
{
|
||||
sendOpenClose(true);
|
||||
if (startCivDataTimer != Q_NULLPTR)
|
||||
{
|
||||
startCivDataTimer->stop();
|
||||
delete startCivDataTimer;
|
||||
startCivDataTimer = Q_NULLPTR;
|
||||
}
|
||||
if (pingTimer != Q_NULLPTR)
|
||||
{
|
||||
pingTimer->stop();
|
||||
delete pingTimer;
|
||||
pingTimer = Q_NULLPTR;
|
||||
}
|
||||
if (idleTimer != Q_NULLPTR)
|
||||
{
|
||||
idleTimer->stop();
|
||||
delete idleTimer;
|
||||
idleTimer = Q_NULLPTR;
|
||||
}
|
||||
if (watchdogTimer != Q_NULLPTR)
|
||||
{
|
||||
watchdogTimer->stop();
|
||||
delete watchdogTimer;
|
||||
watchdogTimer = Q_NULLPTR;
|
||||
}
|
||||
}
|
||||
|
||||
void udpCivData::watchdog()
|
||||
{
|
||||
static bool alerted = false;
|
||||
if (lastReceived.msecsTo(QTime::currentTime()) > 2000)
|
||||
{
|
||||
if (!alerted) {
|
||||
qInfo(logUdp()) << " CIV Watchdog: no CIV data received for 2s, requesting data start.";
|
||||
if (startCivDataTimer != Q_NULLPTR)
|
||||
{
|
||||
startCivDataTimer->start(100);
|
||||
}
|
||||
alerted = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
alerted = false;
|
||||
}
|
||||
}
|
||||
|
||||
void udpCivData::send(QByteArray d)
|
||||
{
|
||||
//qInfo(logUdp()) << "Sending: (" << d.length() << ") " << d;
|
||||
data_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p) + d.length();
|
||||
p.sentid = myId;
|
||||
p.rcvdid = remoteId;
|
||||
p.reply = (char)0xc1;
|
||||
p.datalen = d.length();
|
||||
p.sendseq = qToBigEndian(sendSeqB); // THIS IS BIG ENDIAN!
|
||||
|
||||
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||
t.append(d);
|
||||
sendTrackedPacket(t);
|
||||
sendSeqB++;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void udpCivData::sendOpenClose(bool close)
|
||||
{
|
||||
uint8_t magic = 0x04;
|
||||
|
||||
if (close)
|
||||
{
|
||||
magic = 0x00;
|
||||
}
|
||||
|
||||
openclose_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p);
|
||||
p.sentid = myId;
|
||||
p.rcvdid = remoteId;
|
||||
p.data = 0x01c0; // Not sure what other values are available:
|
||||
p.sendseq = qToBigEndian(sendSeqB);
|
||||
p.magic = magic;
|
||||
|
||||
sendSeqB++;
|
||||
|
||||
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void udpCivData::dataReceived()
|
||||
{
|
||||
while (udp->hasPendingDatagrams())
|
||||
{
|
||||
QNetworkDatagram datagram = udp->receiveDatagram();
|
||||
//qInfo(logUdp()) << "Received: " << datagram.data();
|
||||
QByteArray r = datagram.data();
|
||||
|
||||
|
||||
switch (r.length())
|
||||
{
|
||||
case (CONTROL_SIZE): // Control packet
|
||||
{
|
||||
control_packet_t in = (control_packet_t)r.constData();
|
||||
if (in->type == 0x04)
|
||||
{
|
||||
areYouThereTimer->stop();
|
||||
}
|
||||
else if (in->type == 0x06)
|
||||
{
|
||||
// Update remoteId
|
||||
remoteId = in->sentid;
|
||||
// Manually send a CIV start request and start the timer if it isn't received.
|
||||
// The timer will be stopped as soon as valid CIV data is received.
|
||||
sendOpenClose(false);
|
||||
if (startCivDataTimer != Q_NULLPTR) {
|
||||
startCivDataTimer->start(100);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (r.length() > 21) {
|
||||
data_packet_t in = (data_packet_t)r.constData();
|
||||
if (in->type != 0x01) {
|
||||
// Process this packet, any re-transmit requests will happen later.
|
||||
//uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
||||
// We have received some Civ data so stop sending Start packets!
|
||||
if (startCivDataTimer != Q_NULLPTR) {
|
||||
startCivDataTimer->stop();
|
||||
}
|
||||
lastReceived = QTime::currentTime();
|
||||
if (quint16(in->datalen + 0x15) == (quint16)in->len)
|
||||
{
|
||||
//if (r.mid(0x15).length() != 157)
|
||||
// Find data length
|
||||
int pos = r.indexOf(QByteArrayLiteral("\x27\x00\x00")) + 2;
|
||||
int len = r.mid(pos).indexOf(QByteArrayLiteral("\xfd"));
|
||||
//splitWaterfall = false;
|
||||
if (splitWaterfall && pos > 1 && len > 100) {
|
||||
// We need to split waterfall data into its component parts
|
||||
// There are only 2 types that we are currently aware of
|
||||
int numDivisions = 0;
|
||||
if (len == 490) // IC705, IC9700, IC7300(LAN)
|
||||
{
|
||||
numDivisions = 11;
|
||||
}
|
||||
else if (len == 704) // IC7610, IC7851, ICR8600
|
||||
{
|
||||
numDivisions = 15;
|
||||
}
|
||||
else {
|
||||
qInfo(logUdp()) << "Unknown spectrum size" << len;
|
||||
break;
|
||||
}
|
||||
// (sequence #1) includes center/fixed mode at [05]. No pixels.
|
||||
// "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 "
|
||||
// "DATA: 27 00 00 01 11 01 00 00 00 14 00 00 00 35 14 00 00 fd "
|
||||
// (sequences 2-10, 50 pixels)
|
||||
// "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 "
|
||||
// "DATA: 27 00 00 07 11 27 13 15 01 00 22 21 09 08 06 19 0e 20 23 25 2c 2d 17 27 29 16 14 1b 1b 21 27 1a 18 17 1e 21 1b 24 21 22 23 13 19 23 2f 2d 25 25 0a 0e 1e 20 1f 1a 0c fd "
|
||||
// ^--^--(seq 7/11)
|
||||
// ^-- start waveform data 0x00 to 0xA0, index 05 to 54
|
||||
// (sequence #11)
|
||||
// "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 "
|
||||
// "DATA: 27 00 00 11 11 0b 13 21 23 1a 1b 22 1e 1a 1d 13 21 1d 26 28 1f 19 1a 18 09 2c 2c 2c 1a 1b fd "
|
||||
|
||||
int divSize = (len / numDivisions) + 6;
|
||||
QByteArray wfPacket;
|
||||
for (int i = 0; i < numDivisions; i++) {
|
||||
|
||||
wfPacket = r.mid(pos - 6, 9); // First part of packet
|
||||
|
||||
wfPacket = r.mid(pos - 6, 9); // First part of packet
|
||||
char tens = ((i + 1) / 10);
|
||||
char units = ((i + 1) - (10 * tens));
|
||||
wfPacket[7] = units | (tens << 4);
|
||||
|
||||
tens = (numDivisions / 10);
|
||||
units = (numDivisions - (10 * tens));
|
||||
wfPacket[8] = units | (tens << 4);
|
||||
|
||||
if (i == 0) {
|
||||
//Just send initial data, first BCD encode the max number:
|
||||
wfPacket.append(r.mid(pos + 3, 12));
|
||||
}
|
||||
else
|
||||
{
|
||||
wfPacket.append(r.mid((pos + 15) + ((i - 1) * divSize), divSize));
|
||||
}
|
||||
if (i < numDivisions - 1) {
|
||||
wfPacket.append('\xfd');
|
||||
}
|
||||
//printHex(wfPacket, false, true);
|
||||
|
||||
emit receive(wfPacket);
|
||||
wfPacket.clear();
|
||||
|
||||
}
|
||||
//qDebug(logUdp()) << "Waterfall packet len" << len << "Num Divisions" << numDivisions << "Division Size" << divSize;
|
||||
}
|
||||
else {
|
||||
// Not waterfall data or split not enabled.
|
||||
emit receive(r.mid(0x15));
|
||||
}
|
||||
//qDebug(logUdp()) << "Got incoming CIV datagram" << r.mid(0x15).length();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
udpBase::dataReceived(r); // Call parent function to process the rest.
|
||||
|
||||
r.clear();
|
||||
datagram.clear();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// Class for all (pseudo) serial communications
|
||||
#ifndef UDPCIVDATA_H
|
||||
#define UDPCIVDATA_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
#include <QNetworkDatagram>
|
||||
#include <QHostInfo>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QDateTime>
|
||||
#include <QByteArray>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
#include <QUuid>
|
||||
|
||||
// Allow easy endian-ness conversions
|
||||
#include <QtEndian>
|
||||
|
||||
// Needed for audio
|
||||
#include <QBuffer>
|
||||
#include <QThread>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "packettypes.h"
|
||||
|
||||
#include "udpbase.h"
|
||||
|
||||
class udpCivData : public udpBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 lport);
|
||||
~udpCivData();
|
||||
QMutex serialmutex;
|
||||
|
||||
signals:
|
||||
int receive(QByteArray);
|
||||
|
||||
public slots:
|
||||
void send(QByteArray d);
|
||||
|
||||
|
||||
private:
|
||||
void watchdog();
|
||||
void dataReceived();
|
||||
void sendOpenClose(bool close);
|
||||
|
||||
QTimer* startCivDataTimer = Q_NULLPTR;
|
||||
bool splitWaterfall = false;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
1160
udphandler.cpp
1160
udphandler.cpp
Plik diff jest za duży
Load Diff
209
udphandler.h
209
udphandler.h
|
@ -1,6 +1,7 @@
|
|||
#ifndef UDPHANDLER_H
|
||||
#define UDPHANDLER_H
|
||||
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
#include <QNetworkDatagram>
|
||||
|
@ -22,207 +23,11 @@
|
|||
|
||||
#include <QDebug>
|
||||
|
||||
#include "audiohandler.h"
|
||||
#include "packettypes.h"
|
||||
|
||||
|
||||
struct udpPreferences {
|
||||
QString ipAddress;
|
||||
quint16 controlLANPort;
|
||||
quint16 serialLANPort;
|
||||
quint16 audioLANPort;
|
||||
QString username;
|
||||
QString password;
|
||||
QString clientName;
|
||||
quint8 waterfallFormat;
|
||||
};
|
||||
|
||||
struct networkStatus {
|
||||
quint8 rxAudioBufferPercent;
|
||||
quint8 txAudioBufferPercent;
|
||||
quint8 rxAudioLevel;
|
||||
quint8 txAudioLevel;
|
||||
quint16 rxLatency;
|
||||
quint16 txLatency;
|
||||
bool rxUnderrun;
|
||||
bool txUnderrun;
|
||||
quint16 rxCurrentLatency;
|
||||
quint16 txCurrentLatency;
|
||||
quint32 packetsSent=0;
|
||||
quint32 packetsLost=0;
|
||||
quint16 rtt=0;
|
||||
quint32 networkLatency=0;
|
||||
QString message;
|
||||
};
|
||||
|
||||
void passcode(QString in, QByteArray& out);
|
||||
QByteArray parseNullTerminatedString(QByteArray c, int s);
|
||||
|
||||
// Parent class that contains all common items.
|
||||
class udpBase : public QObject
|
||||
{
|
||||
|
||||
|
||||
public:
|
||||
~udpBase();
|
||||
|
||||
void init(quint16 local);
|
||||
|
||||
void reconnect();
|
||||
|
||||
void dataReceived(QByteArray r);
|
||||
void sendPing();
|
||||
void sendRetransmitRange(quint16 first, quint16 second, quint16 third,quint16 fourth);
|
||||
|
||||
void sendControl(bool tracked,quint8 id, quint16 seq);
|
||||
|
||||
void printHex(const QByteArray& pdata);
|
||||
void printHex(const QByteArray& pdata, bool printVert, bool printHoriz);
|
||||
|
||||
|
||||
QTime timeStarted;
|
||||
|
||||
QUdpSocket* udp=Q_NULLPTR;
|
||||
uint32_t myId = 0;
|
||||
uint32_t remoteId = 0;
|
||||
uint16_t authSeq = 0x30;
|
||||
uint16_t sendSeqB = 0;
|
||||
uint16_t sendSeq = 1;
|
||||
uint16_t lastReceivedSeq = 1;
|
||||
uint16_t pkt0SendSeq = 0;
|
||||
uint16_t periodicSeq = 0;
|
||||
quint64 latency = 0;
|
||||
|
||||
QString username = "";
|
||||
QString password = "";
|
||||
QHostAddress radioIP;
|
||||
QHostAddress localIP;
|
||||
bool isAuthenticated = false;
|
||||
quint16 localPort=0;
|
||||
quint16 port=0;
|
||||
bool periodicRunning = false;
|
||||
bool sentPacketConnect2 = false;
|
||||
QTime lastReceived =QTime::currentTime();
|
||||
QMutex udpMutex;
|
||||
QMutex txBufferMutex;
|
||||
QMutex rxBufferMutex;
|
||||
QMutex missingMutex;
|
||||
|
||||
struct SEQBUFENTRY {
|
||||
QTime timeSent;
|
||||
uint16_t seqNum;
|
||||
QByteArray data;
|
||||
quint8 retransmitCount;
|
||||
};
|
||||
|
||||
QMap<quint16, QTime> rxSeqBuf;
|
||||
QMap<quint16, SEQBUFENTRY> txSeqBuf;
|
||||
QMap<quint16, int> rxMissing;
|
||||
|
||||
void sendTrackedPacket(QByteArray d);
|
||||
void purgeOldEntries();
|
||||
|
||||
QTimer* areYouThereTimer = Q_NULLPTR; // Send are-you-there packets every second until a response is received.
|
||||
QTimer* pingTimer = Q_NULLPTR; // Start sending pings immediately.
|
||||
QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected.
|
||||
|
||||
QTimer* watchdogTimer = Q_NULLPTR;
|
||||
QTimer* retransmitTimer = Q_NULLPTR;
|
||||
|
||||
QDateTime lastPingSentTime;
|
||||
uint16_t pingSendSeq = 0;
|
||||
|
||||
quint16 areYouThereCounter=0;
|
||||
|
||||
quint32 packetsSent=0;
|
||||
quint32 packetsLost=0;
|
||||
|
||||
quint16 seqPrefix = 0;
|
||||
QString connectionType="";
|
||||
int congestion = 0;
|
||||
|
||||
|
||||
private:
|
||||
void sendRetransmitRequest();
|
||||
|
||||
};
|
||||
|
||||
// Class for all (pseudo) serial communications
|
||||
class udpCivData : public udpBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 lport);
|
||||
~udpCivData();
|
||||
QMutex serialmutex;
|
||||
|
||||
signals:
|
||||
int receive(QByteArray);
|
||||
|
||||
public slots:
|
||||
void send(QByteArray d);
|
||||
|
||||
|
||||
private:
|
||||
void watchdog();
|
||||
void dataReceived();
|
||||
void sendOpenClose(bool close);
|
||||
|
||||
QTimer* startCivDataTimer = Q_NULLPTR;
|
||||
bool splitWaterfall = false;
|
||||
};
|
||||
|
||||
|
||||
// Class for all audio communications.
|
||||
class udpAudio : public udpBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 lport, audioSetup rxSetup, audioSetup txSetup);
|
||||
~udpAudio();
|
||||
|
||||
int audioLatency = 0;
|
||||
|
||||
signals:
|
||||
void haveAudioData(audioPacket data);
|
||||
|
||||
void setupTxAudio(audioSetup setup);
|
||||
void setupRxAudio(audioSetup setup);
|
||||
|
||||
void haveChangeLatency(quint16 value);
|
||||
void haveSetVolume(unsigned char value);
|
||||
void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
|
||||
void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
|
||||
|
||||
public slots:
|
||||
void changeLatency(quint16 value);
|
||||
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:
|
||||
|
||||
void sendTxAudio();
|
||||
void dataReceived();
|
||||
void watchdog();
|
||||
|
||||
uint16_t sendAudioSeq = 0;
|
||||
|
||||
audioHandler* rxaudio = Q_NULLPTR;
|
||||
QThread* rxAudioThread = Q_NULLPTR;
|
||||
|
||||
audioHandler* txaudio = Q_NULLPTR;
|
||||
QThread* txAudioThread = Q_NULLPTR;
|
||||
|
||||
QTimer* txAudioTimer = Q_NULLPTR;
|
||||
bool enableTx = true;
|
||||
|
||||
QMutex audioMutex;
|
||||
|
||||
};
|
||||
#include "audiohandler.h"
|
||||
#include "udpbase.h"
|
||||
#include "udpcivdata.h"
|
||||
#include "udpaudio.h"
|
||||
|
||||
|
||||
|
||||
|
@ -251,8 +56,8 @@ public slots:
|
|||
void setVolume(unsigned char value);
|
||||
void init();
|
||||
void setCurrentRadio(quint8 radio);
|
||||
void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
|
||||
void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
|
||||
void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
|
||||
void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
|
||||
|
||||
|
||||
signals:
|
||||
|
|
215
udpserver.cpp
215
udpserver.cpp
|
@ -4,7 +4,8 @@
|
|||
#define STALE_CONNECTION 15
|
||||
#define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms
|
||||
#define AUDIO_SEND_PERIOD 20 //
|
||||
udpServer::udpServer(SERVERCONFIG* config) :
|
||||
udpServer::udpServer(SERVERCONFIG* config, QObject* parent) :
|
||||
QObject(parent),
|
||||
config(config)
|
||||
{
|
||||
qInfo(logUdpServer()) << "Starting udp server";
|
||||
|
@ -39,7 +40,7 @@ void udpServer::init()
|
|||
;
|
||||
}
|
||||
srand(time(NULL)); // Generate random
|
||||
timeStarted.start();
|
||||
//timeStarted.start();
|
||||
// Convoluted way to find the external IP address, there must be a better way????
|
||||
QString localhostname = QHostInfo::localHostName();
|
||||
QList<QHostAddress> hostList = QHostInfo::fromName(localhostname).addresses();
|
||||
|
@ -357,17 +358,34 @@ void udpServer::controlReceived()
|
|||
audioSetup setup;
|
||||
setup.resampleQuality = config->resampleQuality;
|
||||
for (RIGCONFIG* radio : config->rigs) {
|
||||
if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size()==1) && radio->txaudio == Q_NULLPTR )
|
||||
if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size()==1) && radio->txaudio == Q_NULLPTR && !config->lan)
|
||||
{
|
||||
radio->txAudioSetup.codec = current->txCodec;
|
||||
radio->txAudioSetup.format.setSampleRate(current->txSampleRate);
|
||||
radio->txAudioSetup.sampleRate=current->txSampleRate;
|
||||
radio->txAudioSetup.isinput = false;
|
||||
radio->txAudioSetup.latency = current->txBufferLen;
|
||||
|
||||
outAudio.isinput = false;
|
||||
|
||||
radio->txaudio = new audioHandler();
|
||||
|
||||
if (radio->txAudioSetup.type == qtAudio) {
|
||||
radio->txaudio = new audioHandler();
|
||||
}
|
||||
else if (radio->txAudioSetup.type == portAudio) {
|
||||
radio->txaudio = new paHandler();
|
||||
}
|
||||
else if (radio->txAudioSetup.type == rtAudio) {
|
||||
radio->txaudio = new rtHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical(logAudio()) << "Unsupported Transmit Audio Handler selected!";
|
||||
}
|
||||
|
||||
//radio->txaudio = new audioHandler();
|
||||
radio->txAudioThread = new QThread(this);
|
||||
radio->txAudioThread->setObjectName("txAudio()");
|
||||
|
||||
|
||||
radio->txaudio->moveToThread(radio->txAudioThread);
|
||||
|
||||
|
@ -377,38 +395,45 @@ void udpServer::controlReceived()
|
|||
connect(radio->txAudioThread, SIGNAL(finished()), radio->txaudio, SLOT(deleteLater()));
|
||||
|
||||
// Not sure how we make this work in QT5.9?
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0))
|
||||
QMetaObject::invokeMethod(radio->txaudio, [=]() {
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(7,10,0))
|
||||
QMetaObject::invokeMethod((audioHandler*)radio->txaudio, [=]() {
|
||||
radio->txaudio->init(radio->txAudioSetup);
|
||||
}, Qt::QueuedConnection);
|
||||
#else
|
||||
emit setupTxAudio(radio->txAudioSetup);
|
||||
#warning "QT 5.9 is not fully supported multiple rigs will NOT work!"
|
||||
//#warning "QT 5.9 is not fully supported multiple rigs will NOT work!"
|
||||
#endif
|
||||
hasTxAudio = datagram.senderAddress();
|
||||
|
||||
connect(this, SIGNAL(haveAudioData(audioPacket)), radio->txaudio, SLOT(incomingAudio(audioPacket)));
|
||||
|
||||
}
|
||||
if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size() == 1) && radio->rxaudio == Q_NULLPTR)
|
||||
if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size() == 1) && radio->rxaudio == Q_NULLPTR && !config->lan)
|
||||
{
|
||||
#if !defined(PORTAUDIO) && !defined(RTAUDIO)
|
||||
qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio input(RX) :" << radio->rxAudioSetup.port.deviceName();
|
||||
qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio output(TX) :" << radio->txAudioSetup.port.deviceName();
|
||||
#else
|
||||
qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.name;
|
||||
qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.name;
|
||||
#endif
|
||||
|
||||
radio->rxAudioSetup.codec = current->rxCodec;
|
||||
radio->rxAudioSetup.format.setSampleRate(current->rxSampleRate);
|
||||
radio->rxAudioSetup.sampleRate=current->rxSampleRate;
|
||||
radio->rxAudioSetup.latency = current->txBufferLen;
|
||||
radio->rxAudioSetup.isinput = true;
|
||||
memcpy(radio->rxAudioSetup.guid, radio->guid, GUIDLEN);
|
||||
|
||||
radio->rxaudio = new audioHandler();
|
||||
//radio->rxaudio = new audioHandler();
|
||||
if (radio->rxAudioSetup.type == qtAudio) {
|
||||
radio->rxaudio = new audioHandler();
|
||||
}
|
||||
else if (radio->rxAudioSetup.type == portAudio) {
|
||||
radio->rxaudio = new paHandler();
|
||||
}
|
||||
else if (radio->rxAudioSetup.type == rtAudio) {
|
||||
radio->rxaudio = new rtHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical(logAudio()) << "Unsupported Receive Audio Handler selected!";
|
||||
}
|
||||
|
||||
|
||||
radio->rxAudioThread = new QThread(this);
|
||||
radio->rxAudioThread->setObjectName("rxAudio()");
|
||||
|
||||
radio->rxaudio->moveToThread(radio->rxAudioThread);
|
||||
|
||||
|
@ -417,12 +442,12 @@ void udpServer::controlReceived()
|
|||
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))
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(7,10,0))
|
||||
QMetaObject::invokeMethod(radio->rxaudio, [=]() {
|
||||
radio->rxaudio->init(radio->rxAudioSetup);
|
||||
}, Qt::QueuedConnection);
|
||||
#else
|
||||
#warning "QT 5.9 is not fully supported multiple rigs will NOT work!"
|
||||
//#warning "QT 5.9 is not fully supported multiple rigs will NOT work!"
|
||||
connect(this, SIGNAL(setupRxAudio(audioSetup)), radio->rxaudio, SLOT(init(audioSetup)));
|
||||
setupRxAudio(radio->rxAudioSetup);
|
||||
#endif
|
||||
|
@ -573,14 +598,15 @@ void udpServer::civReceived()
|
|||
if (current->civId == 0 && r.length() > lastFE + 2 && (quint8)r[lastFE+2] != 0xE1 && (quint8)r[lastFE + 2] > (quint8)0xdf && (quint8)r[lastFE + 2] < (quint8)0xef) {
|
||||
// This is (should be) the remotes CIV id.
|
||||
current->civId = (quint8)r[lastFE + 2];
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected remote CI-V:" << hex << current->civId;
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected remote CI-V:" << QString("0x%1").arg(current->civId,0,16);
|
||||
}
|
||||
else if (current->civId != 0 && r.length() > lastFE + 2 && (quint8)r[lastFE+2] != 0xE1 && (quint8)r[lastFE + 2] != current->civId)
|
||||
{
|
||||
current->civId = (quint8)r[lastFE + 2];
|
||||
qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId; qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId;
|
||||
qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << QString("0x%1").arg(current->civId,0,16);
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << QString("0x%1").arg(current->civId,0,16);
|
||||
} else if (r.length() > lastFE+2 && (quint8)r[lastFE+2] != 0xE1) {
|
||||
qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected invalid remote CI-V:" << hex << (quint8)r[lastFE+2];
|
||||
qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected invalid remote CI-V:" << QString("0x%1").arg((quint8)r[lastFE+2],0,16);
|
||||
}
|
||||
|
||||
for (RIGCONFIG* radio : config->rigs) {
|
||||
|
@ -792,17 +818,15 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
|
|||
if (current->idleTimer != Q_NULLPTR && !current->idleTimer->isActive()) {
|
||||
current->idleTimer->start(100);
|
||||
}
|
||||
} // This is a retransmit request
|
||||
else if (in->type == 0x01)
|
||||
} // This is a single packet retransmit request
|
||||
else if (in->type == 0x01 && in->len == 0x10)
|
||||
{
|
||||
// Single packet request
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'retransmit' request for " << in->seq;
|
||||
QMap<quint16, SEQBUFENTRY>::iterator match = current->txSeqBuf.find(in->seq);
|
||||
auto match = current->txSeqBuf.find(in->seq);
|
||||
|
||||
if (match != current->txSeqBuf.end() && match->retransmitCount < 5) {
|
||||
// Found matching entry?
|
||||
// Don't constantly retransmit the same packet, give-up eventually
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending retransmit of " << hex << match->seqNum;
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16);
|
||||
match->retransmitCount++;
|
||||
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
|
@ -816,6 +840,10 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
|
|||
}
|
||||
else {
|
||||
// Just send an idle!
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type <<
|
||||
"): Requested (single) packet " << QString("0x%1").arg(in->seq, 0, 16) <<
|
||||
"not found, have " << QString("0x%1").arg(current->txSeqBuf.firstKey(), 0, 16) <<
|
||||
"to" << QString("0x%1").arg(current->txSeqBuf.lastKey(), 0, 16);
|
||||
sendControl(current, 0x00, in->seq);
|
||||
}
|
||||
}
|
||||
|
@ -835,11 +863,13 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
|
|||
for (quint16 i = 0x10; i < r.length(); i = i + 2)
|
||||
{
|
||||
quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8;
|
||||
auto match = std::find_if(current->txSeqBuf.begin(), current->txSeqBuf.end(), [&cs = seq](SEQBUFENTRY& s) {
|
||||
return s.seqNum == cs;
|
||||
});
|
||||
auto match = current->txSeqBuf.find(seq);
|
||||
if (match == current->txSeqBuf.end()) {
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Requested packet " << hex << seq << " not found";
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type <<
|
||||
"): Requested (multiple) packet " << QString("0x%1").arg(seq,0,16) <<
|
||||
"not found, have " << QString("0x%1").arg(current->txSeqBuf.firstKey(), 0, 16) <<
|
||||
"to" << QString("0x%1").arg(current->txSeqBuf.lastKey(), 0, 16);
|
||||
|
||||
// Just send idle packet.
|
||||
sendControl(current, 0, in->seq);
|
||||
}
|
||||
|
@ -847,7 +877,7 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
|
|||
{
|
||||
// Found matching entry?
|
||||
// Send "untracked" as it has already been sent once.
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending retransmit of " << hex << match->seqNum;
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Sending (multiple packet) retransmit of " << QString("0x%1").arg(match->seqNum,0,16);
|
||||
match->retransmitCount++;
|
||||
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
|
@ -857,8 +887,6 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
|
|||
else {
|
||||
qInfo(logUdpServer()) << "Unable to lock udpMutex()";
|
||||
}
|
||||
|
||||
match++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -882,9 +910,10 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
|
|||
else
|
||||
{
|
||||
|
||||
if (in->seq < current->rxSeqBuf.firstKey())
|
||||
if (in->seq < current->rxSeqBuf.firstKey() || in->seq - current->rxSeqBuf.lastKey() > MAX_MISSING)
|
||||
{
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): ******* seq number may have rolled over ****** previous highest: " << hex << current->rxSeqBuf.lastKey() << " current: " << hex << in->seq;
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "Large seq number gap detected, previous highest: " <<
|
||||
QString("0x%1").arg(current->rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16);
|
||||
// Looks like it has rolled over so clear buffer and start again.
|
||||
if (current->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
|
@ -914,6 +943,8 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
|
|||
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
|
||||
//int missCounter = 0;
|
||||
if (in->seq > current->rxSeqBuf.lastKey() + 1) {
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "1 or more missing packets detected, previous: " <<
|
||||
QString("0x%1").arg(current->rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16);
|
||||
// We are likely missing packets then!
|
||||
if (current->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
|
@ -927,12 +958,9 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
|
|||
}
|
||||
current->rxSeqBuf.insert(f, QTime::currentTime());
|
||||
|
||||
if (f != in->seq) {
|
||||
qInfo(logUdpServer()) << "Detected missing packet" << f;
|
||||
if (!current->rxMissing.contains(f))
|
||||
{
|
||||
current->rxMissing.insert(f, 0);
|
||||
}
|
||||
if (f != in->seq && !current->rxMissing.contains(f))
|
||||
{
|
||||
current->rxMissing.insert(f, 0);
|
||||
}
|
||||
}
|
||||
current->missMutex.unlock();
|
||||
|
@ -959,10 +987,10 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
|
|||
// Check whether this is one of our missing ones!
|
||||
if (current->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
QMap<quint16, int>::iterator s = current->rxMissing.find(in->seq);
|
||||
auto s = current->rxMissing.find(in->seq);
|
||||
if (s != current->rxMissing.end())
|
||||
{
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Missing SEQ has been received! " << hex << in->seq;
|
||||
qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Missing SEQ has been received! " << QString("0x%1").arg(in->seq,0,16);
|
||||
s = current->rxMissing.erase(s);
|
||||
}
|
||||
current->missMutex.unlock();
|
||||
|
@ -993,18 +1021,22 @@ void udpServer::sendControl(CLIENT* c, quint8 type, quint16 seq)
|
|||
{
|
||||
p.seq = c->txSeq;
|
||||
SEQBUFENTRY s;
|
||||
s.seqNum = seq;
|
||||
s.seqNum = c->txSeq;
|
||||
s.timeSent = QTime::currentTime();
|
||||
s.retransmitCount = 0;
|
||||
s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||
if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
if (c->txSeqBuf.size() > BUFSIZE)
|
||||
if (c->txSeq == 0) {
|
||||
c->txSeqBuf.clear();
|
||||
}
|
||||
else
|
||||
if (c->txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
c->txSeqBuf.remove(c->txSeqBuf.firstKey());
|
||||
}
|
||||
|
||||
c->txSeqBuf.insert(seq, s);
|
||||
c->txSeqBuf.insert(c->txSeq, s);
|
||||
c->txSeq++;
|
||||
c->txMutex.unlock();
|
||||
}
|
||||
|
@ -1062,7 +1094,8 @@ void udpServer::sendPing(QList<CLIENT*>* l, CLIENT* c, quint16 seq, bool reply)
|
|||
pingTime = c->rxPingTime;
|
||||
}
|
||||
else {
|
||||
pingTime = (quint32)timeStarted.msecsSinceStartOfDay();
|
||||
QTime now=QTime::currentTime();
|
||||
pingTime = (quint32)now.msecsSinceStartOfDay();
|
||||
seq = c->pingSeq;
|
||||
// Don't increment pingseq until we receive a reply.
|
||||
}
|
||||
|
@ -1132,7 +1165,10 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed)
|
|||
s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||
if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
if (c->txSeqBuf.size() > BUFSIZE)
|
||||
if (c->txSeq == 0) {
|
||||
c->txSeqBuf.clear();
|
||||
}
|
||||
else if (c->txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
c->txSeqBuf.remove(c->txSeqBuf.firstKey());
|
||||
}
|
||||
|
@ -1254,11 +1290,14 @@ void udpServer::sendCapabilities(CLIENT* c)
|
|||
|
||||
if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
if (c->txSeqBuf.size() > BUFSIZE)
|
||||
if (c->txSeq == 0) {
|
||||
c->txSeqBuf.clear();
|
||||
}
|
||||
else if (c->txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
c->txSeqBuf.remove(c->txSeqBuf.firstKey());
|
||||
}
|
||||
c->txSeqBuf.insert(p.seq, s);
|
||||
c->txSeqBuf.insert(c->txSeq, s);
|
||||
c->txSeq++;
|
||||
c->txMutex.unlock();
|
||||
}
|
||||
|
@ -1336,11 +1375,14 @@ void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[GUIDLEN])
|
|||
|
||||
if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
if (c->txSeqBuf.size() > BUFSIZE)
|
||||
if (c->txSeq == 0) {
|
||||
c->txSeqBuf.clear();
|
||||
}
|
||||
else if (c->txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
c->txSeqBuf.remove(c->txSeqBuf.firstKey());
|
||||
}
|
||||
c->txSeqBuf.insert(p.seq, s);
|
||||
c->txSeqBuf.insert(c->txSeq, s);
|
||||
c->txSeq++;
|
||||
c->txMutex.unlock();
|
||||
}
|
||||
|
@ -1394,11 +1436,14 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type)
|
|||
|
||||
if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
if (c->txSeqBuf.size() > BUFSIZE)
|
||||
if (c->txSeq == 0) {
|
||||
c->txSeqBuf.clear();
|
||||
}
|
||||
else if (c->txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
c->txSeqBuf.remove(c->txSeqBuf.firstKey());
|
||||
}
|
||||
c->txSeqBuf.insert(p.seq, s);
|
||||
c->txSeqBuf.insert(c->txSeq, s);
|
||||
c->txSeq++;
|
||||
c->txMutex.unlock();
|
||||
}
|
||||
|
@ -1471,9 +1516,10 @@ void udpServer::watchdog()
|
|||
qInfo(logUdpServer()) << "Current client is NULL!";
|
||||
}
|
||||
}
|
||||
|
||||
status.message = QString("<pre>Server connections: Control:%1 CI-V:%2 Audio:%3</pre>").arg(controlClients.size()).arg(civClients.size()).arg(audioClients.size());
|
||||
emit haveNetworkStatus(status);
|
||||
if (!config->lan) {
|
||||
status.message = QString("<pre>Server connections: Control:%1 CI-V:%2 Audio:%3</pre>").arg(controlClients.size()).arg(civClients.size()).arg(audioClients.size());
|
||||
emit haveNetworkStatus(status);
|
||||
}
|
||||
}
|
||||
|
||||
void udpServer::sendStatus(CLIENT* c)
|
||||
|
@ -1510,12 +1556,15 @@ void udpServer::sendStatus(CLIENT* c)
|
|||
s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||
if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
if (c->txSeqBuf.size() > BUFSIZE)
|
||||
if (c->txSeq == 0) {
|
||||
c->txSeqBuf.clear();
|
||||
}
|
||||
else if (c->txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
c->txSeqBuf.remove(c->txSeqBuf.firstKey());
|
||||
}
|
||||
c->txSeq++;
|
||||
c->txSeqBuf.insert(p.seq, s);
|
||||
c->txSeqBuf.insert(c->txSeq, s);
|
||||
c->txMutex.unlock();
|
||||
}
|
||||
else {
|
||||
|
@ -1557,7 +1606,7 @@ void udpServer::dataForServer(QByteArray d)
|
|||
}
|
||||
|
||||
int lastFE = d.lastIndexOf((quint8)0xfe);
|
||||
//qInfo(logUdpServer()) << "Server got CIV data from" << radio->rigName << "length" << d.length();
|
||||
//qInfo(logUdpServer()) << "Server got CIV data from" << config->rigs.first()->rigName << "length" << d.length() << d.toHex();
|
||||
if (client->connected && d.length() > lastFE + 2 &&
|
||||
((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId ||
|
||||
(quint8)d[lastFE + 1] == 0x00 || (quint8)d[lastFE + 2] == 0x00 || (quint8)d[lastFE + 1] == 0xE1 || (quint8)d[lastFE + 2] == 0xE1))
|
||||
|
@ -1582,11 +1631,14 @@ void udpServer::dataForServer(QByteArray d)
|
|||
|
||||
if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
if (client->txSeqBuf.size() > BUFSIZE)
|
||||
if (client->txSeq == 0) {
|
||||
client->txSeqBuf.clear();
|
||||
}
|
||||
else if (client->txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
client->txSeqBuf.remove(client->txSeqBuf.firstKey());
|
||||
}
|
||||
client->txSeqBuf.insert(p.seq, s);
|
||||
client->txSeqBuf.insert(client->txSeq, s);
|
||||
client->txSeq++;
|
||||
//client->innerSeq = (qToBigEndian(qFromBigEndian(client->innerSeq) + 1));
|
||||
client->txMutex.unlock();
|
||||
|
@ -1606,7 +1658,8 @@ void udpServer::dataForServer(QByteArray d)
|
|||
}
|
||||
}
|
||||
else {
|
||||
qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE + 1] << ":" << hex << (quint8)d[lastFE + 2];
|
||||
qInfo(logUdpServer()) << "Got data for different ID" <<
|
||||
QString("0x%1").arg((quint8)d[lastFE + 1],0,16) << ":" << QString("0x%1").arg((quint8)d[lastFE + 2],0,16);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -1652,11 +1705,14 @@ void udpServer::receiveAudioData(const audioPacket& d)
|
|||
s.data = t;
|
||||
if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
if (client->txSeqBuf.size() > BUFSIZE)
|
||||
if (client->txSeq == 0) {
|
||||
client->txSeqBuf.clear();
|
||||
}
|
||||
else if (client->txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
client->txSeqBuf.remove(client->txSeqBuf.firstKey());
|
||||
}
|
||||
client->txSeqBuf.insert(p.seq, s);
|
||||
client->txSeqBuf.insert(client->txSeq, s);
|
||||
client->txSeq++;
|
||||
client->sendAudioSeq++;
|
||||
client->txMutex.unlock();
|
||||
|
@ -1696,7 +1752,7 @@ void udpServer::sendRetransmitRequest(CLIENT* c)
|
|||
return;
|
||||
}
|
||||
else if (c->rxMissing.size() > MAX_MISSING) {
|
||||
qDebug(logUdp()) << "Too many missing packets," << c->rxMissing.size() << "flushing all buffers";
|
||||
qInfo(logUdp()) << "Too many missing packets," << c->rxMissing.size() << "flushing all buffers";
|
||||
c->rxMutex.lock();
|
||||
c->rxSeqBuf.clear();
|
||||
c->rxMutex.unlock();
|
||||
|
@ -1713,9 +1769,10 @@ void udpServer::sendRetransmitRequest(CLIENT* c)
|
|||
|
||||
if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it)
|
||||
auto it = c->rxMissing.begin();
|
||||
while (it != c->rxMissing.end())
|
||||
{
|
||||
if (&it.key() != Q_NULLPTR)
|
||||
if (it.key() != 0x00)
|
||||
{
|
||||
if (it.value() < 4)
|
||||
{
|
||||
|
@ -1724,13 +1781,17 @@ void udpServer::sendRetransmitRequest(CLIENT* c)
|
|||
missingSeqs.append(it.key() & 0xff);
|
||||
missingSeqs.append(it.key() >> 8 & 0xff);
|
||||
it.value()++;
|
||||
it++;
|
||||
}
|
||||
|
||||
else {
|
||||
// We have tried 4 times to request this packet, time to give up!
|
||||
qDebug(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting";
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << QString("0x%1").arg(it.key(), 0, 16) << "deleting";
|
||||
it = c->rxMissing.erase(it);
|
||||
}
|
||||
} else {
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": found empty key in missing buffer";
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1744,8 +1805,9 @@ void udpServer::sendRetransmitRequest(CLIENT* c)
|
|||
p.rcvdid = c->remoteId;
|
||||
if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control.
|
||||
{
|
||||
p.len = sizeof(p);
|
||||
p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8);
|
||||
qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq;
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq,0,16);
|
||||
|
||||
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
|
@ -1759,9 +1821,10 @@ void udpServer::sendRetransmitRequest(CLIENT* c)
|
|||
}
|
||||
else
|
||||
{
|
||||
qDebug(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex();
|
||||
qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex();
|
||||
|
||||
missingSeqs.insert(0, p.packet, sizeof(p.packet));
|
||||
p.len = sizeof(p) + missingSeqs.size();
|
||||
missingSeqs.insert(0, p.packet, sizeof(p));
|
||||
|
||||
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
|
|
10
udpserver.h
10
udpserver.h
|
@ -70,9 +70,9 @@ struct RIGCONFIG {
|
|||
rigCapabilities rigCaps;
|
||||
rigCommander* rig = Q_NULLPTR;
|
||||
QThread* rigThread = Q_NULLPTR;
|
||||
audioHandler* rxaudio = Q_NULLPTR;
|
||||
QObject* rxaudio = Q_NULLPTR;
|
||||
QThread* rxAudioThread = Q_NULLPTR;
|
||||
audioHandler* txaudio = Q_NULLPTR;
|
||||
QObject* txaudio = Q_NULLPTR;
|
||||
QThread* txAudioThread = Q_NULLPTR;
|
||||
QTimer* rxAudioTimer = Q_NULLPTR;
|
||||
QTimer* connectTimer = Q_NULLPTR;
|
||||
|
@ -99,7 +99,7 @@ class udpServer : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
udpServer(SERVERCONFIG* config);
|
||||
explicit udpServer(SERVERCONFIG* config, QObject* parent = nullptr);
|
||||
~udpServer();
|
||||
|
||||
public slots:
|
||||
|
@ -217,7 +217,7 @@ private:
|
|||
QList <CLIENT*> civClients = QList<CLIENT*>();
|
||||
QList <CLIENT*> audioClients = QList<CLIENT*>();
|
||||
|
||||
QTime timeStarted;
|
||||
//QTime timeStarted;
|
||||
|
||||
|
||||
audioSetup outAudio;
|
||||
|
@ -235,4 +235,4 @@ private:
|
|||
};
|
||||
|
||||
|
||||
#endif // UDPSERVER_H
|
||||
#endif // UDPSERVER_H
|
||||
|
|
423
wfmain.cpp
423
wfmain.cpp
|
@ -70,6 +70,9 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s
|
|||
|
||||
setupPlots();
|
||||
loadSettings(); // Look for saved preferences
|
||||
|
||||
setAudioDevicesUI();
|
||||
|
||||
setTuningSteps(); // TODO: Combine into preferences
|
||||
|
||||
qDebug(logSystem()) << "Running setUIToPrefs()";
|
||||
|
@ -84,12 +87,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s
|
|||
qDebug(logSystem()) << "Running rigConnections()";
|
||||
rigConnections();
|
||||
|
||||
/* if (serverConfig.enabled && udp != Q_NULLPTR) {
|
||||
// Server
|
||||
connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket)));
|
||||
connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray)));
|
||||
connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray)));
|
||||
} */
|
||||
|
||||
|
||||
setServerToPrefs();
|
||||
|
||||
|
@ -112,6 +110,10 @@ wfmain::~wfmain()
|
|||
if (rigCtl != Q_NULLPTR) {
|
||||
delete rigCtl;
|
||||
}
|
||||
|
||||
if (prefs.audioSystem == portAudio) {
|
||||
Pa_Terminate();
|
||||
}
|
||||
delete rpt;
|
||||
delete ui;
|
||||
delete settings;
|
||||
|
@ -420,6 +422,8 @@ void wfmain::makeRig()
|
|||
{
|
||||
rig = new rigCommander();
|
||||
rigThread = new QThread(this);
|
||||
rigThread->setObjectName("rigCommander()");
|
||||
|
||||
|
||||
// Thread:
|
||||
rig->moveToThread(rigThread);
|
||||
|
@ -472,7 +476,7 @@ void wfmain::removeRig()
|
|||
rigThread->disconnect();
|
||||
|
||||
rig->disconnect();
|
||||
|
||||
|
||||
delete rigThread;
|
||||
delete rig;
|
||||
rig = Q_NULLPTR;
|
||||
|
@ -1003,8 +1007,7 @@ void wfmain::setServerToPrefs()
|
|||
|
||||
if (serverConfig.enabled) {
|
||||
serverConfig.lan = prefs.enableLAN;
|
||||
qInfo(logAudio()) << "Audio Input device " << serverConfig.rigs.first()->rxAudioSetup.name;
|
||||
qInfo(logAudio()) << "Audio Output device " << serverConfig.rigs.first()->txAudioSetup.name;
|
||||
|
||||
udp = new udpServer(&serverConfig);
|
||||
|
||||
serverThread = new QThread(this);
|
||||
|
@ -1017,12 +1020,17 @@ void wfmain::setServerToPrefs()
|
|||
|
||||
if (rig != Q_NULLPTR) {
|
||||
connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket)));
|
||||
// Need to add a signal/slot for audio from the client to rig.
|
||||
//connect(udp, SIGNAL(haveAudioData(audioPacket)), rig, SLOT(receiveAudioData(audioPacket)));
|
||||
connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray)));
|
||||
connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray)));
|
||||
}
|
||||
|
||||
if (!prefs.enableLAN) {
|
||||
if (serverConfig.lan) {
|
||||
connect(udp, SIGNAL(haveNetworkStatus(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus)));
|
||||
} else {
|
||||
qInfo(logAudio()) << "Audio Input device " << serverConfig.rigs.first()->rxAudioSetup.name;
|
||||
qInfo(logAudio()) << "Audio Output device " << serverConfig.rigs.first()->txAudioSetup.name;
|
||||
}
|
||||
|
||||
serverThread->start();
|
||||
|
@ -1068,43 +1076,6 @@ void wfmain::setUIToPrefs()
|
|||
ui->useCIVasRigIDChk->blockSignals(false);
|
||||
}
|
||||
|
||||
void wfmain::setAudioDevicesUI()
|
||||
{
|
||||
|
||||
// Enumerate audio devices, need to do before settings are loaded.
|
||||
qDebug(logSystem()) << "Finding audio output devices";
|
||||
const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
|
||||
for (const QAudioDeviceInfo& deviceInfo : audioOutputs) {
|
||||
#ifdef Q_OS_WIN
|
||||
if (deviceInfo.realm() == "wasapi") {
|
||||
#endif
|
||||
ui->audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo));
|
||||
ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo));
|
||||
#ifdef Q_OS_WIN
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
qDebug(logSystem()) << "Finding audio input devices";
|
||||
const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
|
||||
for (const QAudioDeviceInfo& deviceInfo : audioInputs) {
|
||||
#ifdef Q_OS_WIN
|
||||
if (deviceInfo.realm() == "wasapi") {
|
||||
#endif
|
||||
ui->audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo));
|
||||
ui->serverRXAudioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo));
|
||||
#ifdef Q_OS_WIN
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// Set these to default audio devices initially.
|
||||
qDebug(logSystem()) << "Audio devices done.";
|
||||
rxSetup.port = QAudioDeviceInfo::defaultOutputDevice();
|
||||
txSetup.port = QAudioDeviceInfo::defaultInputDevice();
|
||||
qDebug(logSystem()) << "Audio set to default device initially";
|
||||
}
|
||||
|
||||
void wfmain::setSerialDevicesUI()
|
||||
{
|
||||
ui->serialDeviceListCombo->blockSignals(true);
|
||||
|
@ -1401,6 +1372,7 @@ void wfmain::setDefPrefs()
|
|||
defPrefs.meter2Type = meterNone;
|
||||
defPrefs.tcpPort = 0;
|
||||
defPrefs.waterfallFormat = 0;
|
||||
defPrefs.audioSystem = qtAudio;
|
||||
|
||||
udpDefPrefs.ipAddress = QString("");
|
||||
udpDefPrefs.controlLANPort = 50001;
|
||||
|
@ -1521,6 +1493,12 @@ void wfmain::loadSettings()
|
|||
rxSetup.localAFgain = prefs.localAFgain;
|
||||
txSetup.localAFgain = 255;
|
||||
|
||||
prefs.audioSystem = static_cast<audioType>(settings->value("AudioSystem", defPrefs.audioSystem).toInt());
|
||||
ui->audioSystemCombo->blockSignals(true);
|
||||
ui->audioSystemCombo->setCurrentIndex(prefs.audioSystem);
|
||||
ui->audioSystemCombo->blockSignals(false);
|
||||
|
||||
|
||||
settings->endGroup();
|
||||
|
||||
// Misc. user settings (enable PTT, draw peaks, etc)
|
||||
|
@ -1528,20 +1506,19 @@ void wfmain::loadSettings()
|
|||
prefs.enablePTT = settings->value("EnablePTT", defPrefs.enablePTT).toBool();
|
||||
ui->pttEnableChk->setChecked(prefs.enablePTT);
|
||||
prefs.niceTS = settings->value("NiceTS", defPrefs.niceTS).toBool();
|
||||
|
||||
settings->endGroup();
|
||||
|
||||
settings->beginGroup("LAN");
|
||||
|
||||
prefs.enableLAN = settings->value("EnableLAN", defPrefs.enableLAN).toBool();
|
||||
if (prefs.enableLAN)
|
||||
{
|
||||
ui->baudRateCombo->setEnabled(false);
|
||||
ui->serialDeviceListCombo->setEnabled(false);
|
||||
}
|
||||
else {
|
||||
ui->baudRateCombo->setEnabled(true);
|
||||
ui->serialDeviceListCombo->setEnabled(true);
|
||||
}
|
||||
|
||||
// If LAN is enabled, server gets its audio straight from the LAN
|
||||
ui->serverRXAudioInputCombo->setEnabled(!prefs.enableLAN);
|
||||
ui->serverTXAudioOutputCombo->setEnabled(!prefs.enableLAN);
|
||||
|
||||
ui->baudRateCombo->setEnabled(!prefs.enableLAN);
|
||||
ui->serialDeviceListCombo->setEnabled(!prefs.enableLAN);
|
||||
|
||||
ui->lanEnableBtn->setChecked(prefs.enableLAN);
|
||||
ui->connectBtn->setEnabled(true);
|
||||
|
@ -1591,11 +1568,11 @@ void wfmain::loadSettings()
|
|||
ui->txLatencySlider->setTracking(false); // Stop it sending value on every change.
|
||||
|
||||
ui->audioSampleRateCombo->blockSignals(true);
|
||||
rxSetup.format.setSampleRate(settings->value("AudioRXSampleRate", "48000").toInt());
|
||||
txSetup.format.setSampleRate(rxSetup.format.sampleRate());
|
||||
rxSetup.sampleRate=settings->value("AudioRXSampleRate", "48000").toInt();
|
||||
txSetup.sampleRate=rxSetup.sampleRate;
|
||||
|
||||
ui->audioSampleRateCombo->setEnabled(ui->lanEnableBtn->isChecked());
|
||||
int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(rxSetup.format.sampleRate()));
|
||||
int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(rxSetup.sampleRate));
|
||||
if (audioSampleRateIndex != -1) {
|
||||
ui->audioSampleRateCombo->setCurrentIndex(audioSampleRateIndex);
|
||||
}
|
||||
|
@ -1632,31 +1609,12 @@ void wfmain::loadSettings()
|
|||
ui->audioTXCodecCombo->setCurrentIndex(f);
|
||||
ui->audioRXCodecCombo->blockSignals(false);
|
||||
|
||||
ui->audioOutputCombo->blockSignals(true);
|
||||
rxSetup.name = settings->value("AudioOutput", "").toString();
|
||||
qInfo(logGui()) << "Got Audio Output: " << rxSetup.name;
|
||||
int audioOutputIndex = ui->audioOutputCombo->findText(rxSetup.name);
|
||||
if (audioOutputIndex != -1) {
|
||||
ui->audioOutputCombo->setCurrentIndex(audioOutputIndex);
|
||||
qInfo(logGui()) << "Got Audio Output from Settings: " << rxSetup.name;
|
||||
|
||||
QVariant v = ui->audioOutputCombo->currentData();
|
||||
rxSetup.port = v.value<QAudioDeviceInfo>();
|
||||
|
||||
}
|
||||
ui->audioOutputCombo->blockSignals(false);
|
||||
|
||||
ui->audioInputCombo->blockSignals(true);
|
||||
txSetup.name = settings->value("AudioInput", "").toString();
|
||||
qInfo(logGui()) << "Got Audio Input: " << txSetup.name;
|
||||
int audioInputIndex = ui->audioInputCombo->findText(txSetup.name);
|
||||
if (audioInputIndex != -1) {
|
||||
ui->audioInputCombo->setCurrentIndex(audioInputIndex);
|
||||
qInfo(logGui()) << "Got Audio Input from Settings: " << txSetup.name;
|
||||
|
||||
QVariant v = ui->audioInputCombo->currentData();
|
||||
txSetup.port = v.value<QAudioDeviceInfo>();
|
||||
|
||||
}
|
||||
ui->audioInputCombo->blockSignals(false);
|
||||
|
||||
rxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt();
|
||||
txSetup.resampleQuality = rxSetup.resampleQuality;
|
||||
|
@ -1715,6 +1673,8 @@ void wfmain::loadSettings()
|
|||
rigTemp->txAudioSetup.localAFgain = 255;
|
||||
rigTemp->rxAudioSetup.resampleQuality = 4;
|
||||
rigTemp->txAudioSetup.resampleQuality = 4;
|
||||
rigTemp->rxAudioSetup.type = prefs.audioSystem;
|
||||
rigTemp->txAudioSetup.type = prefs.audioSystem;
|
||||
|
||||
rigTemp->baudRate = prefs.serialPortBaud;
|
||||
rigTemp->civAddr = prefs.radioCIVAddr;
|
||||
|
@ -1729,31 +1689,8 @@ void wfmain::loadSettings()
|
|||
memcpy(rigTemp->guid, QUuid::fromString(guid).toRfc4122().constData(), GUIDLEN);
|
||||
#endif
|
||||
|
||||
ui->serverRXAudioInputCombo->blockSignals(true);
|
||||
rigTemp->rxAudioSetup.name = settings->value("ServerAudioInput", "").toString();
|
||||
qInfo(logGui()) << "Got Server Audio Input: " << rigTemp->rxAudioSetup.name;
|
||||
int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(rigTemp->rxAudioSetup.name);
|
||||
if (serverAudioInputIndex != -1) {
|
||||
ui->serverRXAudioInputCombo->setCurrentIndex(serverAudioInputIndex);
|
||||
|
||||
QVariant v = ui->serverRXAudioInputCombo->currentData();
|
||||
rigTemp->rxAudioSetup.port = v.value<QAudioDeviceInfo>();
|
||||
|
||||
}
|
||||
ui->serverRXAudioInputCombo->blockSignals(false);
|
||||
|
||||
ui->serverTXAudioOutputCombo->blockSignals(true);
|
||||
rigTemp->txAudioSetup.name = settings->value("ServerAudioOutput", "").toString();
|
||||
qInfo(logGui()) << "Got Server Audio Output: " << rigTemp->txAudioSetup.name;
|
||||
int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(rigTemp->txAudioSetup.name);
|
||||
if (serverAudioOutputIndex != -1) {
|
||||
ui->serverTXAudioOutputCombo->setCurrentIndex(serverAudioOutputIndex);
|
||||
|
||||
QVariant v = ui->serverTXAudioOutputCombo->currentData();
|
||||
rigTemp->txAudioSetup.port = v.value<QAudioDeviceInfo>();
|
||||
|
||||
}
|
||||
ui->serverTXAudioOutputCombo->blockSignals(false);
|
||||
serverConfig.rigs.append(rigTemp);
|
||||
|
||||
int row = 0;
|
||||
|
@ -1894,25 +1831,40 @@ void wfmain::on_serverAudioPortText_textChanged(QString text)
|
|||
|
||||
void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value)
|
||||
{
|
||||
if (serverConfig.rigs.size() > 0)
|
||||
{
|
||||
QVariant v = ui->serverRXAudioInputCombo->itemData(value);
|
||||
serverConfig.rigs.first()->rxAudioSetup.port = v.value<QAudioDeviceInfo>();
|
||||
|
||||
serverConfig.rigs.first()->rxAudioSetup.name = ui->serverRXAudioInputCombo->itemText(value);
|
||||
qDebug(logGui()) << "Changed default server audio input to:" << serverConfig.rigs.first()->rxAudioSetup.name;
|
||||
if (!serverConfig.rigs.isEmpty())
|
||||
{
|
||||
if (prefs.audioSystem == qtAudio) {
|
||||
QVariant v = ui->serverRXAudioInputCombo->itemData(value);
|
||||
serverConfig.rigs.first()->rxAudioSetup.port = v.value<QAudioDeviceInfo>();
|
||||
}
|
||||
else {
|
||||
serverConfig.rigs.first()->rxAudioSetup.portInt = ui->serverRXAudioInputCombo->itemData(value).toInt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
serverConfig.rigs.first()->rxAudioSetup.name = ui->audioInputCombo->itemText(value);
|
||||
|
||||
}
|
||||
|
||||
void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value)
|
||||
{
|
||||
if (serverConfig.rigs.size() > 0) {
|
||||
QVariant v = ui->serverTXAudioOutputCombo->itemData(value);
|
||||
serverConfig.rigs.first()->txAudioSetup.port = v.value<QAudioDeviceInfo>();
|
||||
|
||||
serverConfig.rigs.first()->txAudioSetup.name = ui->serverTXAudioOutputCombo->itemText(value);
|
||||
qDebug(logGui()) << "Changed default server audio output to:" << serverConfig.rigs.first()->txAudioSetup.name;
|
||||
if (!serverConfig.rigs.isEmpty())
|
||||
{
|
||||
if (prefs.audioSystem == qtAudio) {
|
||||
QVariant v = ui->serverTXAudioOutputCombo->itemData(value);
|
||||
serverConfig.rigs.first()->txAudioSetup.port = v.value<QAudioDeviceInfo>();
|
||||
}
|
||||
else {
|
||||
serverConfig.rigs.first()->txAudioSetup.portInt = ui->serverTXAudioOutputCombo->itemData(value).toInt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
serverConfig.rigs.first()->txAudioSetup.name = ui->audioInputCombo->itemText(value);
|
||||
|
||||
}
|
||||
|
||||
void wfmain::on_serverUsersTable_cellChanged(int row, int column)
|
||||
|
@ -1972,6 +1924,8 @@ void wfmain::saveSettings()
|
|||
settings->setValue("SerialPortBaud", prefs.serialPortBaud);
|
||||
settings->setValue("VirtualSerialPort", prefs.virtualSerialPort);
|
||||
settings->setValue("localAFgain", prefs.localAFgain);
|
||||
settings->setValue("AudioSystem", prefs.audioSystem);
|
||||
|
||||
settings->endGroup();
|
||||
|
||||
// Misc. user settings (enable PTT, draw peaks, etc)
|
||||
|
@ -1994,9 +1948,9 @@ void wfmain::saveSettings()
|
|||
settings->setValue("Password", udpPrefs.password);
|
||||
settings->setValue("AudioRXLatency", rxSetup.latency);
|
||||
settings->setValue("AudioTXLatency", txSetup.latency);
|
||||
settings->setValue("AudioRXSampleRate", rxSetup.format.sampleRate());
|
||||
settings->setValue("AudioRXSampleRate", rxSetup.sampleRate);
|
||||
settings->setValue("AudioRXCodec", rxSetup.codec);
|
||||
settings->setValue("AudioTXSampleRate", txSetup.format.sampleRate());
|
||||
settings->setValue("AudioTXSampleRate", txSetup.sampleRate);
|
||||
settings->setValue("AudioTXCodec", txSetup.codec);
|
||||
settings->setValue("AudioOutput", rxSetup.name);
|
||||
settings->setValue("AudioInput", txSetup.name);
|
||||
|
@ -3499,6 +3453,8 @@ void wfmain::receiveRigID(rigCapabilities rigCaps)
|
|||
ui->useRTSforPTTchk->blockSignals(false);
|
||||
|
||||
ui->connectBtn->setText("Disconnect"); // We must be connected now.
|
||||
ui->audioSystemCombo->setEnabled(false);
|
||||
|
||||
prepareWf(ui->wfLengthSlider->value());
|
||||
if(usingLAN)
|
||||
{
|
||||
|
@ -4710,6 +4666,8 @@ void wfmain::on_serialEnableBtn_clicked(bool checked)
|
|||
ui->audioInputCombo->setEnabled(!checked);
|
||||
ui->baudRateCombo->setEnabled(checked);
|
||||
ui->serialDeviceListCombo->setEnabled(checked);
|
||||
ui->serverRXAudioInputCombo->setEnabled(checked);
|
||||
ui->serverTXAudioOutputCombo->setEnabled(checked);
|
||||
}
|
||||
|
||||
void wfmain::on_lanEnableBtn_clicked(bool checked)
|
||||
|
@ -4731,6 +4689,8 @@ void wfmain::on_lanEnableBtn_clicked(bool checked)
|
|||
ui->audioInputCombo->setEnabled(checked);
|
||||
ui->baudRateCombo->setEnabled(!checked);
|
||||
ui->serialDeviceListCombo->setEnabled(!checked);
|
||||
ui->serverRXAudioInputCombo->setEnabled(!checked);
|
||||
ui->serverTXAudioOutputCombo->setEnabled(!checked);
|
||||
if(checked)
|
||||
{
|
||||
showStatusBarText("After filling in values, press Save Settings.");
|
||||
|
@ -4759,28 +4719,41 @@ void wfmain::on_passwordTxt_textChanged(QString text)
|
|||
|
||||
void wfmain::on_audioOutputCombo_currentIndexChanged(int value)
|
||||
{
|
||||
QVariant v = ui->audioOutputCombo->itemData(value);
|
||||
rxSetup.port = v.value<QAudioDeviceInfo>();
|
||||
|
||||
if (prefs.audioSystem == qtAudio) {
|
||||
QVariant v = ui->audioOutputCombo->itemData(value);
|
||||
rxSetup.port = v.value<QAudioDeviceInfo>();
|
||||
}
|
||||
else {
|
||||
rxSetup.portInt = ui->audioOutputCombo->itemData(value).toInt();
|
||||
}
|
||||
|
||||
rxSetup.name = ui->audioOutputCombo->itemText(value);
|
||||
qDebug(logGui()) << "Changed default audio output to:" << rxSetup.name;
|
||||
qDebug(logGui()) << "Changed audio output to:" << rxSetup.name;
|
||||
}
|
||||
|
||||
void wfmain::on_audioInputCombo_currentIndexChanged(int value)
|
||||
{
|
||||
QVariant v = ui->audioInputCombo->itemData(value);
|
||||
txSetup.port = v.value<QAudioDeviceInfo>();
|
||||
|
||||
if (prefs.audioSystem == qtAudio) {
|
||||
QVariant v = ui->audioInputCombo->itemData(value);
|
||||
txSetup.port = v.value<QAudioDeviceInfo>();
|
||||
}
|
||||
else {
|
||||
txSetup.portInt = ui->audioInputCombo->itemData(value).toInt();
|
||||
}
|
||||
|
||||
txSetup.name = ui->audioInputCombo->itemText(value);
|
||||
qDebug(logGui()) << "Changed default audio input to:" << txSetup.name;
|
||||
|
||||
qDebug(logGui()) << "Changed audio input to:" << txSetup.name;
|
||||
}
|
||||
|
||||
void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text)
|
||||
{
|
||||
//udpPrefs.audioRXSampleRate = text.toInt();
|
||||
//udpPrefs.audioTXSampleRate = text.toInt();
|
||||
rxSetup.format.setSampleRate(text.toInt());
|
||||
txSetup.format.setSampleRate(text.toInt());
|
||||
rxSetup.sampleRate=text.toInt();
|
||||
txSetup.sampleRate=text.toInt();
|
||||
}
|
||||
|
||||
void wfmain::on_audioRXCodecCombo_currentIndexChanged(int value)
|
||||
|
@ -4827,6 +4800,7 @@ void wfmain::on_connectBtn_clicked()
|
|||
if (haveRigCaps) {
|
||||
emit sendCloseComm();
|
||||
ui->connectBtn->setText("Connect");
|
||||
ui->audioSystemCombo->setEnabled(true);
|
||||
haveRigCaps = false;
|
||||
rigName->setText("NONE");
|
||||
}
|
||||
|
@ -5935,3 +5909,204 @@ void wfmain::on_debugBtn_clicked()
|
|||
//adjustSize();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void wfmain::setAudioDevicesUI()
|
||||
{
|
||||
|
||||
// Enumerate audio devices, need to do before settings are loaded,
|
||||
// First clear all existing entries
|
||||
ui->audioInputCombo->blockSignals(true);
|
||||
ui->audioOutputCombo->blockSignals(true);
|
||||
ui->serverTXAudioOutputCombo->blockSignals(true);
|
||||
ui->serverRXAudioInputCombo->blockSignals(true);
|
||||
|
||||
ui->audioInputCombo->clear();
|
||||
ui->audioOutputCombo->clear();
|
||||
ui->serverTXAudioOutputCombo->clear();
|
||||
ui->serverRXAudioInputCombo->clear();
|
||||
|
||||
qDebug(logSystem()) << "Finding audio devices, output=" << rxSetup.name << "input="<<txSetup.name;
|
||||
|
||||
switch (prefs.audioSystem)
|
||||
{
|
||||
case qtAudio:
|
||||
{
|
||||
Pa_Terminate();
|
||||
|
||||
const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
|
||||
for (const QAudioDeviceInfo& deviceInfo : audioOutputs) {
|
||||
#ifdef Q_OS_WIN
|
||||
if (deviceInfo.realm() == "wasapi") {
|
||||
#endif
|
||||
ui->audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo));
|
||||
ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo));
|
||||
#ifdef Q_OS_WIN
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
|
||||
for (const QAudioDeviceInfo& deviceInfo : audioInputs) {
|
||||
#ifdef Q_OS_WIN
|
||||
if (deviceInfo.realm() == "wasapi") {
|
||||
#endif
|
||||
ui->audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo));
|
||||
ui->serverRXAudioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo));
|
||||
#ifdef Q_OS_WIN
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case portAudio:
|
||||
{
|
||||
PaError err;
|
||||
|
||||
err = Pa_Initialize();
|
||||
|
||||
if (err != paNoError)
|
||||
{
|
||||
qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio";
|
||||
return;
|
||||
}
|
||||
|
||||
qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText;
|
||||
|
||||
int numDevices;
|
||||
numDevices = Pa_GetDeviceCount();
|
||||
qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices;
|
||||
|
||||
const PaDeviceInfo* info;
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
info = Pa_GetDeviceInfo(i);
|
||||
if (info->maxInputChannels > 0) {
|
||||
qDebug(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name;
|
||||
|
||||
ui->audioInputCombo->addItem(info->name, i);
|
||||
ui->serverRXAudioInputCombo->addItem(info->name, i);
|
||||
}
|
||||
if (info->maxOutputChannels > 0) {
|
||||
qDebug(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name;
|
||||
ui->audioOutputCombo->addItem(info->name, i);
|
||||
ui->serverTXAudioOutputCombo->addItem(info->name, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case rtAudio:
|
||||
{
|
||||
Pa_Terminate();
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA);
|
||||
#elif defined(Q_OS_WIN)
|
||||
RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI);
|
||||
#elif defined(Q_OS_MACX)
|
||||
RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE);
|
||||
#endif
|
||||
|
||||
|
||||
// Enumerate audio devices, need to do before settings are loaded.
|
||||
std::map<int, std::string> apiMap;
|
||||
apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio";
|
||||
apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO";
|
||||
apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound";
|
||||
apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI";
|
||||
apiMap[RtAudio::UNIX_JACK] = "Jack Client";
|
||||
apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA";
|
||||
apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio";
|
||||
apiMap[RtAudio::LINUX_OSS] = "Linux OSS";
|
||||
apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy";
|
||||
|
||||
std::vector< RtAudio::Api > apis;
|
||||
RtAudio::getCompiledApi(apis);
|
||||
|
||||
qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion());
|
||||
|
||||
qInfo(logAudio()) << "Compiled APIs:";
|
||||
for (unsigned int i = 0; i < apis.size(); i++) {
|
||||
qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]);
|
||||
}
|
||||
|
||||
RtAudio::DeviceInfo info;
|
||||
|
||||
qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]);
|
||||
|
||||
unsigned int devices = audio->getDeviceCount();
|
||||
qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default";
|
||||
|
||||
for (unsigned int i = 1; i < devices; i++) {
|
||||
info = audio->getDeviceInfo(i);
|
||||
if (info.outputChannels > 0) {
|
||||
qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name);
|
||||
ui->audioOutputCombo->addItem(QString::fromStdString(info.name), i);
|
||||
ui->serverTXAudioOutputCombo->addItem(QString::fromStdString(info.name), i);
|
||||
}
|
||||
if (info.inputChannels > 0) {
|
||||
qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name);
|
||||
ui->audioInputCombo->addItem(QString::fromStdString(info.name), i);
|
||||
ui->serverRXAudioInputCombo->addItem(QString::fromStdString(info.name), i);
|
||||
}
|
||||
}
|
||||
|
||||
delete audio;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Stop blocking signals so we can set the current values
|
||||
ui->audioInputCombo->blockSignals(false);
|
||||
ui->audioOutputCombo->blockSignals(false);
|
||||
ui->serverTXAudioOutputCombo->blockSignals(false);
|
||||
ui->serverRXAudioInputCombo->blockSignals(false);
|
||||
|
||||
|
||||
rxSetup.type = prefs.audioSystem;
|
||||
txSetup.type = prefs.audioSystem;
|
||||
|
||||
int audioInputIndex = ui->audioInputCombo->findText(txSetup.name);
|
||||
if (audioInputIndex != -1) {
|
||||
ui->audioInputCombo->setCurrentIndex(audioInputIndex);
|
||||
}
|
||||
else {
|
||||
qDebug(logSystem()) << "Audio input not found";
|
||||
}
|
||||
|
||||
int audioOutputIndex = ui->audioOutputCombo->findText(rxSetup.name);
|
||||
if (audioOutputIndex != -1) {
|
||||
ui->audioOutputCombo->setCurrentIndex(audioOutputIndex);
|
||||
}
|
||||
else {
|
||||
qDebug(logSystem()) << "Audio output not found";
|
||||
}
|
||||
|
||||
if (!serverConfig.rigs.isEmpty())
|
||||
|
||||
{
|
||||
qInfo(logGui()) << "Got Server Audio Input: " << serverConfig.rigs.first()->rxAudioSetup.name;
|
||||
int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(serverConfig.rigs.first()->rxAudioSetup.name);
|
||||
if (serverAudioInputIndex != -1) {
|
||||
ui->serverRXAudioInputCombo->setCurrentIndex(serverAudioInputIndex);
|
||||
}
|
||||
|
||||
qInfo(logGui()) << "Got Server Audio Output: " << serverConfig.rigs.first()->txAudioSetup.name;
|
||||
int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(serverConfig.rigs.first()->txAudioSetup.name);
|
||||
if (serverAudioOutputIndex != -1) {
|
||||
ui->serverTXAudioOutputCombo->setCurrentIndex(serverAudioOutputIndex);
|
||||
}
|
||||
}
|
||||
// Set these to default audio devices initially.
|
||||
qDebug(logSystem()) << "Audio devices done.";
|
||||
}
|
||||
|
||||
void wfmain::on_audioSystemCombo_currentIndexChanged(int value)
|
||||
{
|
||||
prefs.audioSystem = static_cast<audioType>(value);
|
||||
setAudioDevicesUI(); // Force all audio devices to update
|
||||
}
|
11
wfmain.h
11
wfmain.h
|
@ -43,6 +43,14 @@
|
|||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
#include <portaudio.h>
|
||||
#ifdef Q_OS_WIN
|
||||
#include "RtAudio.h"
|
||||
#else
|
||||
#include "rtaudio/RtAudio.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace Ui {
|
||||
class wfmain;
|
||||
}
|
||||
|
@ -546,6 +554,8 @@ private slots:
|
|||
|
||||
void on_radioStatusBtn_clicked();
|
||||
|
||||
void on_audioSystemCombo_currentIndexChanged(int value);
|
||||
|
||||
private:
|
||||
Ui::wfmain *ui;
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
@ -789,6 +799,7 @@ private:
|
|||
meterKind meter2Type;
|
||||
quint16 tcpPort;
|
||||
quint8 waterfallFormat;
|
||||
audioType audioSystem;
|
||||
// plot scheme
|
||||
} prefs;
|
||||
|
||||
|
|
28
wfmain.ui
28
wfmain.ui
|
@ -2066,7 +2066,7 @@
|
|||
<item>
|
||||
<widget class="QStackedWidget" name="settingsStack">
|
||||
<property name="currentIndex">
|
||||
<number>4</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="radioAccess">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_21">
|
||||
|
@ -2633,6 +2633,32 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_40">
|
||||
<property name="text">
|
||||
<string>Audio System</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="audioSystemCombo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>QT Audio</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PortAudio</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>RT Audio</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
51
wfserver.pro
51
wfserver.pro
|
@ -17,21 +17,38 @@ DEFINES += WFVIEW_VERSION=\\\"1.2e\\\"
|
|||
|
||||
DEFINES += BUILD_WFSERVER
|
||||
|
||||
CONFIG(debug, release|debug) {
|
||||
# For Debug builds only:
|
||||
QMAKE_CXXFLAGS += -faligned-new
|
||||
WIN32:DESTDIR = wfview-debug
|
||||
|
||||
CONFIG(debug, release|debug) {
|
||||
# For Debug builds only:
|
||||
QMAKE_CXXFLAGS += -faligned-new
|
||||
win32:DESTDIR = wfview-release
|
||||
win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86
|
||||
} else {
|
||||
# For Release builds only:
|
||||
linux:QMAKE_CXXFLAGS += -s
|
||||
QMAKE_CXXFLAGS += -fvisibility=hidden
|
||||
QMAKE_CXXFLAGS += -fvisibility-inlines-hidden
|
||||
QMAKE_CXXFLAGS += -faligned-new
|
||||
linux:QMAKE_LFLAGS += -O2 -s
|
||||
WIN32:DESTDIR = wfview-release
|
||||
# For Release builds only:
|
||||
linux:QMAKE_CXXFLAGS += -s
|
||||
QMAKE_CXXFLAGS += -fvisibility=hidden
|
||||
QMAKE_CXXFLAGS += -fvisibility-inlines-hidden
|
||||
QMAKE_CXXFLAGS += -faligned-new
|
||||
linux:QMAKE_LFLAGS += -O2 -s
|
||||
win32:DESTDIR = wfview-debug
|
||||
win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86
|
||||
}
|
||||
|
||||
# RTAudio defines
|
||||
win32:DEFINES += __WINDOWS_WASAPI__
|
||||
#win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries
|
||||
#linux:DEFINES += __LINUX_ALSA__
|
||||
#linux:DEFINES += __LINUX_OSS__
|
||||
linux:DEFINES += __LINUX_PULSE__
|
||||
macx:DEFINES += __MACOSX_CORE__
|
||||
win32:SOURCES += ../rtaudio/RTAudio.cpp
|
||||
win32:HEADERS += ../rtaudio/RTAUdio.h
|
||||
!linux:INCLUDEPATH += ../rtaudio
|
||||
linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread
|
||||
|
||||
win32:INCLUDEPATH += ../portaudio/include
|
||||
!win32:LIBS += -lportaudio
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which as been marked as deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
|
@ -139,9 +156,15 @@ SOURCES += main.cpp\
|
|||
rigcommander.cpp \
|
||||
freqmemory.cpp \
|
||||
rigidentities.cpp \
|
||||
udpbase.cpp \
|
||||
udphandler.cpp \
|
||||
udpcivdata.cpp \
|
||||
udpaudio.cpp \
|
||||
logcategories.cpp \
|
||||
pahandler.cpp \
|
||||
rthandler.cpp \
|
||||
audiohandler.cpp \
|
||||
audioconverter.cpp \
|
||||
udpserver.cpp \
|
||||
pttyhandler.cpp \
|
||||
resampler/resample.c \
|
||||
|
@ -154,9 +177,15 @@ HEADERS += servermain.h \
|
|||
rigcommander.h \
|
||||
freqmemory.h \
|
||||
rigidentities.h \
|
||||
udpbase.h \
|
||||
udphandler.h \
|
||||
udpcivdata.h \
|
||||
udpaudio.h \
|
||||
logcategories.h \
|
||||
pahandler.h \
|
||||
rthandler.h \
|
||||
audiohandler.h \
|
||||
audioconverter.h \
|
||||
udpserver.h \
|
||||
packettypes.h \
|
||||
pttyhandler.h \
|
||||
|
|
310
wfserver.vcxproj
310
wfserver.vcxproj
|
@ -16,12 +16,11 @@
|
|||
<Keyword>QtVS_v304</Keyword>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.19041.0</WindowsTargetPlatformMinVersion>
|
||||
<QtMsBuild Condition="'$(QtMsBuild)'=='' or !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild>
|
||||
</PropertyGroup>
|
||||
<QtMsBuild Condition="'$(QtMsBuild)'=='' or !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild></PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<OutputDirectory>release\</OutputDirectory>
|
||||
<OutputDirectory>wfview-release\</OutputDirectory>
|
||||
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
|
@ -30,17 +29,14 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<OutputDirectory>debug\</OutputDirectory>
|
||||
<OutputDirectory>wfview-debug\</OutputDirectory>
|
||||
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<IntermediateDirectory>debug\</IntermediateDirectory>
|
||||
<PrimaryOutput>wfserver</PrimaryOutput>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')">
|
||||
<Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." />
|
||||
</Target>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /><Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"><Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /></Target>
|
||||
<ImportGroup Label="ExtensionSettings" />
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
|
@ -48,37 +44,11 @@
|
|||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')">
|
||||
<Import Project="$(QtMsBuild)\qt_defaults.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>debug\</OutDir>
|
||||
<IntDir>debug\</IntDir>
|
||||
<TargetName>wfserver</TargetName>
|
||||
<IgnoreImportLibrary>true</IgnoreImportLibrary>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>release\</OutDir>
|
||||
<IntDir>release\</IntDir>
|
||||
<TargetName>wfserver</TargetName>
|
||||
<IgnoreImportLibrary>true</IgnoreImportLibrary>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<QtInstall>msvc2019</QtInstall>
|
||||
<QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<QtInstall>msvc2019</QtInstall>
|
||||
<QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules>
|
||||
</PropertyGroup>
|
||||
<ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')">
|
||||
<Import Project="$(QtMsBuild)\qt.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" /><ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"><Import Project="$(QtMsBuild)\qt_defaults.props" /></ImportGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><OutDir>wfview-debug\</OutDir><IntDir>debug\</IntDir><TargetName>wfserver</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary></PropertyGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><OutDir>wfview-release\</OutDir><IntDir>release\</IntDir><TargetName>wfserver</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary><LinkIncremental>false</LinkIncremental></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"><Import Project="$(QtMsBuild)\qt.props" /></ImportGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>.;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>.;..\rtaudio;..\portaudio\include;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>release\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
|
@ -87,19 +57,17 @@
|
|||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<ObjectFileName>release\</ObjectFileName>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="44f6ec2";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessToFile>false</PreprocessToFile>
|
||||
<ProgramDataBaseFileName>
|
||||
</ProgramDataBaseFileName>
|
||||
<ProgramDataBaseFileName></ProgramDataBaseFileName>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation></ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>..\portaudio\msvc\Win32\Release\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\portaudio\msvc\Win32\Release;..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
|
@ -117,26 +85,12 @@
|
|||
<WarningLevel>0</WarningLevel>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"44f6ec2\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<QtMoc>
|
||||
<CompilerFlavor>msvc</CompilerFlavor>
|
||||
<Include>./$(Configuration)/moc_predefs.h</Include>
|
||||
<ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription>
|
||||
<DynamicSource>output</DynamicSource>
|
||||
<QtMocDir>$(Configuration)</QtMocDir>
|
||||
<QtMocFileName>moc_%(Filename).cpp</QtMocFileName>
|
||||
</QtMoc>
|
||||
<QtRcc>
|
||||
<Compression>default</Compression>
|
||||
<ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription>
|
||||
<QtRccDir>$(Configuration)</QtRccDir>
|
||||
<QtRccFileName>qrc_%(Filename).cpp</QtRccFileName>
|
||||
</QtRcc>
|
||||
</ItemDefinitionGroup>
|
||||
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc></ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>.;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>.;..\rtaudio;..\portaudio\include;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>debug\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
|
@ -145,17 +99,16 @@
|
|||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<ObjectFileName>debug\</ObjectFileName>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="47772a4";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="44f6ec2";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessToFile>false</PreprocessToFile>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation></ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>..\portaudio\msvc\Win32\Debug\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\portaudio\msvc\Win32\Debug;..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
@ -171,72 +124,208 @@
|
|||
<WarningLevel>0</WarningLevel>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"47772a4\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"44f6ec2\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<QtMoc>
|
||||
<CompilerFlavor>msvc</CompilerFlavor>
|
||||
<Include>./$(Configuration)/moc_predefs.h</Include>
|
||||
<ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription>
|
||||
<DynamicSource>output</DynamicSource>
|
||||
<QtMocDir>$(Configuration)</QtMocDir>
|
||||
<QtMocFileName>moc_%(Filename).cpp</QtMocFileName>
|
||||
</QtMoc>
|
||||
<QtRcc>
|
||||
<Compression>default</Compression>
|
||||
<ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription>
|
||||
<QtRccDir>$(Configuration)</QtRccDir>
|
||||
<QtRccFileName>qrc_%(Filename).cpp</QtRccFileName>
|
||||
</QtRcc>
|
||||
</ItemDefinitionGroup>
|
||||
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc></ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\rtaudio\RTAudio.cpp" />
|
||||
<ClCompile Include="audioconverter.cpp" />
|
||||
<ClCompile Include="audiohandler.cpp" />
|
||||
<ClCompile Include="commhandler.cpp" />
|
||||
<ClCompile Include="freqmemory.cpp" />
|
||||
<ClCompile Include="keyboard.cpp" />
|
||||
<ClCompile Include="logcategories.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="pahandler.cpp" />
|
||||
<ClCompile Include="pttyhandler.cpp" />
|
||||
<ClCompile Include="resampler\resample.c" />
|
||||
<ClCompile Include="rigcommander.cpp" />
|
||||
<ClCompile Include="rigctld.cpp" />
|
||||
<ClCompile Include="rigidentities.cpp" />
|
||||
<ClCompile Include="rthandler.cpp" />
|
||||
<ClCompile Include="servermain.cpp" />
|
||||
<ClCompile Include="tcpserver.cpp" />
|
||||
<ClCompile Include="udpaudio.cpp" />
|
||||
<ClCompile Include="udpbase.cpp" />
|
||||
<ClCompile Include="udpcivdata.cpp" />
|
||||
<ClCompile Include="udphandler.cpp" />
|
||||
<ClCompile Include="udpserver.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtMoc Include="keyboard.h" />
|
||||
<ClInclude Include="..\rtaudio\RTAUdio.h" />
|
||||
<ClInclude Include="resampler\arch.h" />
|
||||
<QtMoc Include="audioconverter.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="audiohandler.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="audiotaper.h" />
|
||||
<QtMoc Include="commhandler.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="freqmemory.h" />
|
||||
<QtMoc Include="keyboard.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="logcategories.h" />
|
||||
<ClInclude Include="packettypes.h" />
|
||||
<QtMoc Include="pahandler.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="pttyhandler.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="repeaterattributes.h" />
|
||||
<ClInclude Include="resampler\resample_sse.h" />
|
||||
<QtMoc Include="rigcommander.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="rigctld.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="rigidentities.h" />
|
||||
<QtMoc Include="rthandler.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="servermain.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="resampler\speex_resampler.h" />
|
||||
<QtMoc Include="tcpserver.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="udpaudio.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="udpbase.h" />
|
||||
<QtMoc Include="udpcivdata.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="udphandler.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="udpserver.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="ulaw.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
<FileType>Document</FileType>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
|
@ -253,6 +342,30 @@
|
|||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">release\moc_predefs.h;%(Outputs)</Outputs>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
</CustomBuild>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="qdarkstyle\rc\Hmovetoolbar.png" />
|
||||
|
@ -285,16 +398,30 @@
|
|||
<None Include="qdarkstyle\rc\radio_unchecked_disabled.png" />
|
||||
<None Include="qdarkstyle\rc\radio_unchecked_focus.png" />
|
||||
<QtRcc Include="resources\resources.qrc">
|
||||
<InitFuncName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">resources</InitFuncName>
|
||||
<InitFuncName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">resources</InitFuncName>
|
||||
</QtRcc>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<InitFuncName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">resources</InitFuncName><InitFuncName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">resources</InitFuncName></QtRcc>
|
||||
<None Include="qdarkstyle\rc\right_arrow.png" />
|
||||
<None Include="qdarkstyle\rc\right_arrow_disabled.png" />
|
||||
<None Include="qdarkstyle\rc\sizegrip.png" />
|
||||
<QtRcc Include="qdarkstyle\style.qrc">
|
||||
<InitFuncName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">style</InitFuncName>
|
||||
<InitFuncName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">style</InitFuncName>
|
||||
</QtRcc>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<InitFuncName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">style</InitFuncName><InitFuncName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">style</InitFuncName></QtRcc>
|
||||
<None Include="qdarkstyle\style.qss" />
|
||||
<None Include="qdarkstyle\rc\stylesheet-branch-end.png" />
|
||||
<None Include="qdarkstyle\rc\stylesheet-branch-more.png" />
|
||||
|
@ -308,9 +435,6 @@
|
|||
<ItemGroup>
|
||||
<ResourceCompile Include=".\wfserver_resource.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
|
||||
<Import Project="$(QtMsBuild)\qt.targets" />
|
||||
</ImportGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /><ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"><Import Project="$(QtMsBuild)\qt.targets" /></ImportGroup>
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
|
@ -37,6 +37,12 @@
|
|||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\rtaudio\RTAudio.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="audioconverter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="audiohandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -46,12 +52,18 @@
|
|||
<ClCompile Include="freqmemory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="keyboard.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="logcategories.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pahandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pttyhandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -67,26 +79,41 @@
|
|||
<ClCompile Include="rigidentities.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rthandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="servermain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tcpserver.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="udpaudio.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="udpbase.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="udpcivdata.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="udphandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="udpserver.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="keyboard.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\rtaudio\RTAUdio.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resampler\arch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<QtMoc Include="audioconverter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="audiohandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
|
@ -99,12 +126,18 @@
|
|||
<ClInclude Include="freqmemory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<QtMoc Include="keyboard.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
<ClInclude Include="logcategories.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="packettypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<QtMoc Include="pahandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="pttyhandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
|
@ -123,6 +156,9 @@
|
|||
<ClInclude Include="rigidentities.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<QtMoc Include="rthandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="servermain.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
|
@ -132,6 +168,15 @@
|
|||
<QtMoc Include="tcpserver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="udpaudio.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
<ClInclude Include="udpbase.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<QtMoc Include="udpcivdata.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="udphandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
|
@ -143,12 +188,46 @@
|
|||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
<Filter>Generated Files</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="release\moc_predefs.h.cbt">
|
||||
<Filter>Generated Files</Filter>
|
||||
</CustomBuild>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="qdarkstyle\rc\Hmovetoolbar.png">
|
||||
|
@ -282,11 +361,6 @@
|
|||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include=".\wfserver_resource.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtMoc Include="keyboard.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</QtMoc>
|
||||
<ResourceCompile Include="C:\Users\Phil\source\repos\wfview\wfserver_resource.rc" />
|
||||
</ItemGroup>
|
||||
</Project>
|
53
wfview.pro
53
wfview.pro
|
@ -16,19 +16,36 @@ DEFINES += WFVIEW_VERSION=\\\"1.2e\\\"
|
|||
DEFINES += BUILD_WFVIEW
|
||||
|
||||
CONFIG(debug, release|debug) {
|
||||
# For Debug builds only:
|
||||
QMAKE_CXXFLAGS += -faligned-new
|
||||
WIN32:DESTDIR = wfview-release
|
||||
# For Debug builds only:
|
||||
QMAKE_CXXFLAGS += -faligned-new
|
||||
win32:DESTDIR = wfview-release
|
||||
win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86
|
||||
} else {
|
||||
# For Release builds only:
|
||||
linux:QMAKE_CXXFLAGS += -s
|
||||
QMAKE_CXXFLAGS += -fvisibility=hidden
|
||||
QMAKE_CXXFLAGS += -fvisibility-inlines-hidden
|
||||
QMAKE_CXXFLAGS += -faligned-new
|
||||
linux:QMAKE_LFLAGS += -O2 -s
|
||||
WIN32:DESTDIR = wfview-debug
|
||||
# For Release builds only:
|
||||
linux:QMAKE_CXXFLAGS += -s
|
||||
QMAKE_CXXFLAGS += -fvisibility=hidden
|
||||
QMAKE_CXXFLAGS += -fvisibility-inlines-hidden
|
||||
QMAKE_CXXFLAGS += -faligned-new
|
||||
linux:QMAKE_LFLAGS += -O2 -s
|
||||
win32:DESTDIR = wfview-debug
|
||||
win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86
|
||||
}
|
||||
|
||||
# RTAudio defines
|
||||
win32:DEFINES += __WINDOWS_WASAPI__
|
||||
#win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries
|
||||
#linux:DEFINES += __LINUX_ALSA__
|
||||
#linux:DEFINES += __LINUX_OSS__
|
||||
linux:DEFINES += __LINUX_PULSE__
|
||||
macx:DEFINES += __MACOSX_CORE__
|
||||
win32:SOURCES += ../rtaudio/RTAudio.cpp
|
||||
win32:HEADERS += ../rtaudio/RTAUdio.h
|
||||
!linux:INCLUDEPATH += ../rtaudio
|
||||
linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread
|
||||
|
||||
win32:INCLUDEPATH += ../portaudio/include
|
||||
!win32:LIBS += -lportaudio
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which as been marked as deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
|
@ -59,10 +76,6 @@ isEmpty(PREFIX) {
|
|||
|
||||
DEFINES += PREFIX=\\\"$$PREFIX\\\"
|
||||
|
||||
# Choose audio system, uses QTMultimedia if both are commented out.
|
||||
# DEFINES += RTAUDIO
|
||||
# DEFINES += PORTAUDIO
|
||||
|
||||
contains(DEFINES, RTAUDIO) {
|
||||
# RTAudio defines
|
||||
win32:DEFINES += __WINDOWS_WASAPI__
|
||||
|
@ -168,9 +181,15 @@ SOURCES += main.cpp\
|
|||
rigcommander.cpp \
|
||||
freqmemory.cpp \
|
||||
rigidentities.cpp \
|
||||
udpbase.cpp \
|
||||
udphandler.cpp \
|
||||
udpcivdata.cpp \
|
||||
udpaudio.cpp \
|
||||
logcategories.cpp \
|
||||
pahandler.cpp \
|
||||
rthandler.cpp \
|
||||
audiohandler.cpp \
|
||||
audioconverter.cpp \
|
||||
calibrationwindow.cpp \
|
||||
satellitesetup.cpp \
|
||||
udpserver.cpp \
|
||||
|
@ -192,9 +211,15 @@ HEADERS += wfmain.h \
|
|||
rigcommander.h \
|
||||
freqmemory.h \
|
||||
rigidentities.h \
|
||||
udpbase.h \
|
||||
udphandler.h \
|
||||
udpcivdata.h \
|
||||
udpaudio.h \
|
||||
logcategories.h \
|
||||
pahandler.h \
|
||||
rthandler.h \
|
||||
audiohandler.h \
|
||||
audioconverter.h \
|
||||
calibrationwindow.h \
|
||||
satellitesetup.h \
|
||||
udpserver.h \
|
||||
|
|
127
wfview.vcxproj
127
wfview.vcxproj
|
@ -20,7 +20,7 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<OutputDirectory>release\</OutputDirectory>
|
||||
<OutputDirectory>wfview-release\</OutputDirectory>
|
||||
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
|
@ -29,7 +29,7 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<OutputDirectory>debug\</OutputDirectory>
|
||||
<OutputDirectory>wfview-debug\</OutputDirectory>
|
||||
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
|
@ -44,11 +44,11 @@
|
|||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" /><ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"><Import Project="$(QtMsBuild)\qt_defaults.props" /></ImportGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><OutDir>debug\</OutDir><IntDir>debug\</IntDir><TargetName>wfview</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary></PropertyGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><OutDir>release\</OutDir><IntDir>release\</IntDir><TargetName>wfview</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary><LinkIncremental>false</LinkIncremental></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"><Import Project="$(QtMsBuild)\qt.props" /></ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" /><ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"><Import Project="$(QtMsBuild)\qt_defaults.props" /></ImportGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><OutDir>wfview-debug\</OutDir><IntDir>debug\</IntDir><TargetName>wfview</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary></PropertyGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><OutDir>wfview-release\</OutDir><IntDir>release\</IntDir><TargetName>wfview</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary><LinkIncremental>false</LinkIncremental></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"><Import Project="$(QtMsBuild)\qt.props" /></ImportGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>.;..\hidapi\hidapi;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>.;..\rtaudio;..\portaudio\include;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>release\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
|
@ -57,7 +57,7 @@
|
|||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<ObjectFileName>release\</ObjectFileName>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="fad416c";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="63c5e02";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessToFile>false</PreprocessToFile>
|
||||
<ProgramDataBaseFileName></ProgramDataBaseFileName>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
|
@ -66,8 +66,8 @@
|
|||
<WarningLevel>Level3</WarningLevel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation></ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>..\portaudio\msvc\Win32\Release\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\portaudio\msvc\Win32\Release;..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
|
@ -85,12 +85,12 @@
|
|||
<WarningLevel>0</WarningLevel>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"fad416c\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"63c5e02\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc><QtUic><ExecutionDescription>Uic'ing %(Identity)...</ExecutionDescription><QtUicDir>$(ProjectDir)</QtUicDir><QtUicFileName>ui_%(Filename).h</QtUicFileName></QtUic></ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>.;..\hidapi\hidapi;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>.;..\rtaudio;..\portaudio\include;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>debug\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
|
@ -99,7 +99,7 @@
|
|||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<ObjectFileName>debug\</ObjectFileName>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="fad416c";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="63c5e02";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessToFile>false</PreprocessToFile>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
|
@ -107,8 +107,8 @@
|
|||
<WarningLevel>Level3</WarningLevel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation></ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>..\portaudio\msvc\Win32\Debug\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\portaudio\msvc\Win32\Debug;..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
@ -124,19 +124,21 @@
|
|||
<WarningLevel>0</WarningLevel>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"fad416c\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"63c5e02\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc><QtUic><ExecutionDescription>Uic'ing %(Identity)...</ExecutionDescription><QtUicDir>$(ProjectDir)</QtUicDir><QtUicFileName>ui_%(Filename).h</QtUicFileName></QtUic></ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\rtaudio\RTAudio.cpp" />
|
||||
<ClCompile Include="aboutbox.cpp" />
|
||||
<ClCompile Include="audioconverter.cpp" />
|
||||
<ClCompile Include="audiohandler.cpp" />
|
||||
<ClCompile Include="calibrationwindow.cpp" />
|
||||
<ClCompile Include="commhandler.cpp" />
|
||||
<ClCompile Include="freqmemory.cpp" />
|
||||
<ClCompile Include="..\hidapi\windows\hid.c" />
|
||||
<ClCompile Include="logcategories.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="meter.cpp" />
|
||||
<ClCompile Include="pahandler.cpp" />
|
||||
<ClCompile Include="pttyhandler.cpp" />
|
||||
<ClCompile Include="..\qcustomplot\qcustomplot.cpp" />
|
||||
<ClCompile Include="qledlabel.cpp" />
|
||||
|
@ -145,17 +147,20 @@
|
|||
<ClCompile Include="rigcommander.cpp" />
|
||||
<ClCompile Include="rigctld.cpp" />
|
||||
<ClCompile Include="rigidentities.cpp" />
|
||||
<ClCompile Include="rthandler.cpp" />
|
||||
<ClCompile Include="satellitesetup.cpp" />
|
||||
<ClCompile Include="selectradio.cpp" />
|
||||
<ClCompile Include="shuttlesetup.cpp" />
|
||||
<ClCompile Include="tcpserver.cpp" />
|
||||
<ClCompile Include="transceiveradjustments.cpp" />
|
||||
<ClCompile Include="udpaudio.cpp" />
|
||||
<ClCompile Include="udpbase.cpp" />
|
||||
<ClCompile Include="udpcivdata.cpp" />
|
||||
<ClCompile Include="udphandler.cpp" />
|
||||
<ClCompile Include="udpserver.cpp" />
|
||||
<ClCompile Include="usbcontroller.cpp" />
|
||||
<ClCompile Include="wfmain.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\rtaudio\RTAUdio.h" />
|
||||
<QtMoc Include="aboutbox.h">
|
||||
|
||||
|
||||
|
@ -167,6 +172,16 @@
|
|||
|
||||
</QtMoc>
|
||||
<ClInclude Include="resampler\arch.h" />
|
||||
<QtMoc Include="audioconverter.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="audiohandler.h">
|
||||
|
||||
|
||||
|
@ -211,6 +226,16 @@
|
|||
|
||||
</QtMoc>
|
||||
<ClInclude Include="packettypes.h" />
|
||||
<QtMoc Include="pahandler.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="pttyhandler.h">
|
||||
|
||||
|
||||
|
@ -274,6 +299,16 @@
|
|||
|
||||
</QtMoc>
|
||||
<ClInclude Include="rigidentities.h" />
|
||||
<QtMoc Include="rthandler.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="satellitesetup.h">
|
||||
|
||||
|
||||
|
@ -293,16 +328,6 @@
|
|||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="shuttlesetup.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="resampler\speex_resampler.h" />
|
||||
<QtMoc Include="tcpserver.h">
|
||||
|
@ -324,6 +349,27 @@
|
|||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="udpaudio.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<ClInclude Include="udpbase.h" />
|
||||
<QtMoc Include="udpcivdata.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="udphandler.h">
|
||||
|
||||
|
@ -346,16 +392,6 @@
|
|||
|
||||
</QtMoc>
|
||||
<ClInclude Include="ulaw.h" />
|
||||
<QtMoc Include="usbcontroller.h">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtMoc>
|
||||
<QtMoc Include="wfmain.h">
|
||||
|
||||
|
||||
|
@ -378,6 +414,10 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
<FileType>Document</FileType>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
|
@ -434,6 +474,7 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</ItemGroup>
|
||||
|
@ -492,17 +533,6 @@
|
|||
|
||||
|
||||
|
||||
</QtUic>
|
||||
<QtUic Include="shuttlesetup.ui">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</QtUic>
|
||||
<QtUic Include="transceiveradjustments.ui">
|
||||
|
||||
|
@ -557,7 +587,6 @@
|
|||
<None Include="qdarkstyle\rc\radio_unchecked.png" />
|
||||
<None Include="qdarkstyle\rc\radio_unchecked_disabled.png" />
|
||||
<None Include="qdarkstyle\rc\radio_unchecked_focus.png" />
|
||||
<None Include="resources\rc28.png" />
|
||||
<QtRcc Include="resources\resources.qrc">
|
||||
|
||||
|
||||
|
@ -571,8 +600,6 @@
|
|||
<InitFuncName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">resources</InitFuncName><InitFuncName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">resources</InitFuncName></QtRcc>
|
||||
<None Include="qdarkstyle\rc\right_arrow.png" />
|
||||
<None Include="qdarkstyle\rc\right_arrow_disabled.png" />
|
||||
<None Include="resources\shuttlepro.png" />
|
||||
<None Include="resources\shuttlexpress.png" />
|
||||
<None Include="qdarkstyle\rc\sizegrip.png" />
|
||||
<QtRcc Include="qdarkstyle\style.qrc">
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue