Merge branch 'wfserver' into shuttle

half-duplex
Phil Taylor 2022-05-12 23:54:48 +01:00
commit 11c0973897
39 zmienionych plików z 4153 dodań i 2605 usunięć

313
audioconverter.cpp 100644
Wyświetl plik

@ -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;
}

137
audioconverter.h 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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();
}

Wyświetl plik

@ -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();

Wyświetl plik

@ -19,7 +19,7 @@ void keyboard::run()
{
char key = getchar();
if (key == 'q') {
QCoreApplication::exit(0);
QCoreApplication::quit();
}
}
return;

Wyświetl plik

@ -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")

Wyświetl plik

@ -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__)

275
pahandler.cpp 100644
Wyświetl plik

@ -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;
}

93
pahandler.h 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -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))

Wyświetl plik

@ -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;

Wyświetl plik

@ -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)

Wyświetl plik

@ -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();

Wyświetl plik

@ -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);

340
rthandler.cpp 100644
Wyświetl plik

@ -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;
}

113
rthandler.h 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -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++) {

Wyświetl plik

@ -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();

Wyświetl plik

@ -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;

273
udpaudio.cpp 100644
Wyświetl plik

@ -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();
}
}

85
udpaudio.h 100644
Wyświetl plik

@ -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

545
udpbase.cpp 100644
Wyświetl plik

@ -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 -----";
}

207
udpbase.h 100644
Wyświetl plik

@ -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

268
udpcivdata.cpp 100644
Wyświetl plik

@ -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();
}
}

56
udpcivdata.h 100644
Wyświetl plik

@ -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

Plik diff jest za duży Load Diff

Wyświetl plik

@ -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:

Wyświetl plik

@ -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)))
{

Wyświetl plik

@ -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

Wyświetl plik

@ -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
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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>

Wyświetl plik

@ -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 \

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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 \

Wyświetl plik

@ -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">