diff --git a/audiohandler.cpp b/audiohandler.cpp index 1b3e001..b69b814 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -7,76 +7,134 @@ #include "logcategories.h" #include "ulaw.h" -audioHandler::audioHandler(QObject* parent) : - isInitialized(false), - isUlaw(false), - audioLatency(0), - isInput(0), - chunkAvailable(false) +audioHandler::audioHandler(QObject* parent) { Q_UNUSED(parent) } audioHandler::~audioHandler() { - //stop(); + + if (isInitialized) { +#if defined(RTAUDIO) + + try { + audio->abortStream(); + audio->closeStream(); + } + catch (RtAudioError& e) { + qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); + } + delete audio; +#elif defined(PORTAUDIO) +#else + stop(); + if (audioOutput != Q_NULLPTR) { + //audioOutput->stop(); + delete audioOutput; + qDebug(logAudio()) << "Audio output stopped"; + } + if (audioInput != Q_NULLPTR) { + //audioInput->stop(); + delete audioInput; + qDebug(logAudio()) << "Audio input stopped"; + } +#endif + } + + if (ringBuf != Q_NULLPTR) { + delete ringBuf; + } if (resampler != Q_NULLPTR) { speex_resampler_destroy(resampler); + qDebug(logAudio()) << "Resampler closed"; } - if (audio.isStreamRunning()) - { - audio.stopStream(); - audio.closeStream(); - } - if (ringBuf != Q_NULLPTR) - delete ringBuf; + } -bool audioHandler::init(const quint8 bits, const quint8 radioChan, const quint16 samplerate, const quint16 latency, const bool ulaw, const bool isinput, int port, quint8 resampleQuality) +bool audioHandler::init(audioSetup setupIn) { if (isInitialized) { return false; } - this->audioLatency = latency; - this->isUlaw = ulaw; - this->isInput = isinput; - this->radioSampleBits = bits; - this->radioSampleRate = samplerate; - this->radioChannels = radioChan; + /* + 0x01 uLaw 1ch 8bit + 0x02 PCM 1ch 8bit + 0x04 PCM 1ch 16bit + 0x08 PCM 2ch 8bit + 0x10 PCM 2ch 16bit + 0x20 uLaw 2ch 8bit + */ - // chunk size is always relative to Internal Sample Rate. + setup = setupIn; + setup.radioChan = 1; + setup.bits = 8; + + if (setup.codec == 0x01 || setup.codec == 0x20) { + setup.ulaw = true; + } + if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20) { + setup.radioChan = 2; + } + if (setup.codec == 0x04 || setup.codec == 0x10) { + setup.bits = 16; + } ringBuf = new wilt::Ring(100); // Should be customizable. + tempBuf.sent = 0; + + +#if defined(RTAUDIO) +#if !defined(Q_OS_MACX) + options.flags = ((!RTAUDIO_HOG_DEVICE) | (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 + if (port > 0) { aParams.deviceId = port; } - else if (isInput) { - aParams.deviceId = audio.getDefaultInputDevice(); + else if (setup.isinput) { + aParams.deviceId = audio->getDefaultInputDevice(); } else { - aParams.deviceId = audio.getDefaultOutputDevice(); + aParams.deviceId = audio->getDefaultOutputDevice(); } aParams.firstChannel = 0; try { - info = audio.getDeviceInfo(aParams.deviceId); + info = audio->getDeviceInfo(aParams.deviceId); } catch (RtAudioError& e) { qInfo(logAudio()) << "Device error:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); - return false; + return isInitialized; } if (info.probed) { - // Per channel chunk size. - aParams.nChannels = 2; // Internally this is always 2 channels for TX and RX. - this->chunkSize = (info.preferredSampleRate / 50); + // if "preferred" sample rate is 44100, try 48K instead + if (info.preferredSampleRate == (unsigned int)44100) { + qDebug(logAudio()) << "Preferred sample rate 44100, trying 48000"; + this->nativeSampleRate = 48000; + } + else { + this->nativeSampleRate = info.preferredSampleRate; + } - qInfo(logAudio()) << (isInput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed"; + // Per channel chunk size. + this->chunkSize = (this->nativeSampleRate / 50); + + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed"; if (info.nativeFormats == 0) { qInfo(logAudio()) << " No natively supported data formats!"; @@ -92,9 +150,9 @@ bool audioHandler::init(const quint8 bits, const quint8 radioChan, const quint16 (info.nativeFormats & RTAUDIO_FLOAT64 ? "64-bit float," : ""); qInfo(logAudio()) << " Preferred sample rate:" << info.preferredSampleRate; - if (isInput) { + if (setup.isinput) { devChannels = info.inputChannels; - } + } else { devChannels = info.outputChannels; } @@ -105,54 +163,120 @@ bool audioHandler::init(const quint8 bits, const quint8 radioChan, const quint16 aParams.nChannels = devChannels; } - qInfo(logAudio()) << " chunkSize: " << chunkSize; + qInfo(logAudio()) << " chunkSize: " << chunkSize; + try { + if (setup.isinput) { + audio->openStream(NULL, &aParams, RTAUDIO_SINT16, nativeSampleRate, &this->chunkSize, &staticWrite, this, &options); + } + else { + audio->openStream(&aParams, NULL, RTAUDIO_SINT16, this->nativeSampleRate, &this->chunkSize, &staticRead, this, &options); + } + 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()) << (isInput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!"; + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!"; + } + + +#elif defined(PORTAUDIO) +#else + + format.setSampleSize(16); + format.setChannelCount(2); + format.setSampleRate(INTERNAL_SAMPLE_RATE); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + if (setup.port.isNull()) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins."; return false; } - - int resample_error = 0; - - if (isInput) { - resampler = wf_resampler_init(devChannels, info.preferredSampleRate, samplerate, resampleQuality, &resample_error); - try { - audio.openStream(NULL, &aParams, RTAUDIO_SINT16, info.preferredSampleRate, &this->chunkSize, &staticWrite, this); - audio.startStream(); - } - catch (RtAudioError& e) { - qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); - return false; - } - } - else + else if (!setup.port.isFormatSupported(format)) { - resampler = wf_resampler_init(devChannels, samplerate, info.preferredSampleRate, resampleQuality, &resample_error); - try { - audio.openStream(&aParams, NULL, RTAUDIO_SINT16, info.preferredSampleRate, &this->chunkSize, &staticRead, this); - audio.startStream(); - } - catch (RtAudioError& e) { - qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); - return false; - } + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Format not supported, choosing nearest supported format - which may not work!"; + format=setup.port.nearestFormat(format); } - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "device successfully opened"; + if (format.channelCount() > 2) { + format.setChannelCount(2); + } + devChannels = format.channelCount(); + nativeSampleRate = format.sampleRate(); + // chunk size is always relative to Internal Sample Rate. + this->chunkSize = (nativeSampleRate / 50); - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "detected latency:" <nativeSampleRate, setup.resampleQuality, &resample_error); + } wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen); - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "wf_resampler_init() returned: " << resample_error << " ratioNum" << ratioNum << " ratioDen" << ratioDen; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "wf_resampler_init() returned: " << resample_error << " ratioNum" << ratioNum << " ratioDen" << ratioDen; + + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "thread id" << QThread::currentThreadId(); + + + if (isInitialized) { + this->start(); + } - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "thread id" << QThread::currentThreadId(); return isInitialized; } +void audioHandler::start() +{ + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "start() running"; + + if ((audioOutput == Q_NULLPTR || audioOutput->state() != QAudio::StoppedState) && + (audioInput == Q_NULLPTR || audioInput->state() != QAudio::StoppedState)) { + return; + } + + if (setup.isinput) { + this->open(QIODevice::WriteOnly | QIODevice::Unbuffered); + //this->open(QIODevice::WriteOnly); + audioInput->start(this); + } + else { + this->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + //this->open(QIODevice::ReadOnly); + audioOutput->start(this); + } +} + + void audioHandler::setVolume(unsigned char volume) { this->volume = (qreal)volume/255.0; - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } @@ -163,17 +287,22 @@ void audioHandler::setVolume(unsigned char volume) /// /// /// +#if defined(RTAUDIO) int audioHandler::readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status) { Q_UNUSED(inputBuffer); Q_UNUSED(streamTime); - // Calculate output length, always full samples - int sentlen = 0; - quint8* buffer = (quint8*)outputBuffer; if (status == RTAUDIO_OUTPUT_UNDERFLOW) qDebug(logAudio()) << "Underflow detected"; - int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels + quint8* buffer = (quint8*)outputBuffer; +#elif defined(PORTAUDIO) +#else +qint64 audioHandler::readData(char* buffer, qint64 nBytes) +{ +#endif + // Calculate output length, always full samples + int sentlen = 0; if (ringBuf->size()>0) { @@ -206,7 +335,7 @@ int audioHandler::readData(void* outputBuffer, void* inputBuffer, unsigned int n } if (currentLatency > (int)audioLatency) { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Packet " << hex << packet.seq << + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Packet " << hex << packet.seq << " arrived too late (increase output latency!) " << dec << packet.time.msecsTo(QTime::currentTime()) << "ms"; lastSeq = packet.seq; @@ -228,10 +357,10 @@ int audioHandler::readData(void* outputBuffer, void* inputBuffer, unsigned int n } if (packet.seq <= lastSeq) { - qDebug(logAudio()) << (isInput ? "Input" : "Output") << "Duplicate/early audio packet: " << hex << lastSeq << " got " << hex << packet.seq; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Duplicate/early audio packet: " << hex << lastSeq << " got " << hex << packet.seq; } else if (packet.seq != lastSeq + 1) { - qDebug(logAudio()) << (isInput ? "Input" : "Output") << "Missing audio packet(s) from: " << hex << lastSeq + 1 << " to " << hex << packet.seq - 1; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Missing audio packet(s) from: " << hex << lastSeq + 1 << " to " << hex << packet.seq - 1; } lastSeq = packet.seq; } @@ -242,23 +371,34 @@ int audioHandler::readData(void* outputBuffer, void* inputBuffer, unsigned int n if (nBytes > sentlen) { memset(buffer+sentlen,0,nBytes-sentlen); } +#if defined(RTAUDIO) return 0; +#elif defined(PORTAUDIO) +#else + return nBytes; +#endif } - +#if defined(RTAUDIO) int audioHandler::writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status) { Q_UNUSED(outputBuffer); Q_UNUSED(streamTime); Q_UNUSED(status); - int sentlen = 0; - int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample + int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels const char* data = (const char*)inputBuffer; +#elif defined(PORTAUDIO) +#else +qint64 audioHandler::writeData(const char* data, qint64 nBytes) +{ +#endif + int sentlen = 0; //qDebug(logAudio()) << "nFrames" << nFrames << "nBytes" << nBytes; + int chunkBytes = chunkSize * devChannels * 2; while (sentlen < nBytes) { - if (tempBuf.sent != nBytes) + if (tempBuf.sent != chunkBytes) { - int send = qMin((int)(nBytes - sentlen), (int)nBytes - tempBuf.sent); + int send = qMin((int)(nBytes - sentlen), chunkBytes - tempBuf.sent); tempBuf.data.append(QByteArray::fromRawData(data + sentlen, send)); sentlen = sentlen + send; tempBuf.seq = 0; // Not used in TX @@ -266,19 +406,25 @@ int audioHandler::writeData(void* outputBuffer, void* inputBuffer, unsigned int tempBuf.sent = tempBuf.sent + send; } else { + ringBuf->write(tempBuf); + /* if (!ringBuf->try_write(tempBuf)) { qDebug(logAudio()) << "outgoing audio buffer full!"; break; - } + } */ tempBuf.data.clear(); tempBuf.sent = 0; } } //qDebug(logAudio()) << "sentlen" << sentlen; - - return 0; +#if defined(RTAUDIO) + return 0; +#elif defined(PORTAUDIO) +#else + return nBytes; +#endif } @@ -288,21 +434,21 @@ void audioHandler::incomingAudio(audioPacket inPacket) // Regardless of the radio stream format, the buffered audio will ALWAYS be // 16bit sample interleaved stereo 48K (or whatever the native sample rate is) - if (!audio.isStreamRunning()) + if (!isInitialized) { qDebug(logAudio()) << "Packet received before stream was started"; return; } //qDebug(logAudio()) << "Got" << radioSampleBits << "bits, length" << inPacket.data.length(); // Incoming data is 8bits? - if (radioSampleBits == 8) + if (setup.bits == 8) { // Current packet is 8bit so need to create a new buffer that is 16bit - QByteArray outPacket((int)inPacket.data.length() * 2 *(devChannels/radioChannels), (char)0xff); + QByteArray outPacket((int)inPacket.data.length() * 2 *(devChannels/setup.radioChan), (char)0xff); qint16* out = (qint16*)outPacket.data(); for (int f = 0; f < inPacket.data.length(); f++) { - for (int g = radioChannels; g <= devChannels; g++) + for (int g = setup.radioChan; g <= devChannels; g++) { if (isUlaw) *out++ = ulaw_decode[(quint8)inPacket.data[f]] * this->volume; @@ -316,7 +462,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) else { // This is already a 16bit stream, do we need to convert to stereo? - if (radioChannels == 1 && devChannels > 1) { + if (setup.radioChan == 1 && devChannels > 1) { // Yes QByteArray outPacket(inPacket.data.length() * 2, (char)0xff); // Preset the output buffer size. qint16* in = (qint16*)inPacket.data.data(); @@ -361,7 +507,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) int err = 0; err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames); if (err) { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } inPacket.data.clear(); inPacket.data = outPacket; // Replace incoming data with converted. @@ -378,7 +524,7 @@ void audioHandler::incomingAudio(audioPacket inPacket) void audioHandler::changeLatency(const quint16 newSize) { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << audioLatency; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << audioLatency; audioLatency = newSize; } @@ -392,7 +538,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) audioPacket packet; packet.sent = 0; - if (ringBuf != Q_NULLPTR && ringBuf->try_read(packet)) + if (isInitialized && ringBuf != Q_NULLPTR && ringBuf->try_read(packet)) { //qDebug(logAudio) << "Chunksize" << this->chunkSize << "Packet size" << packet.data.length(); @@ -409,7 +555,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) int err = 0; err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames); if (err) { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } //qInfo(logAudio()) << "Resampler run " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; //qInfo(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length(); @@ -420,7 +566,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) //qDebug(logAudio()) << "Now resampled, length" << packet.data.length(); // Do we need to convert mono to stereo? - if (radioChannels == 1 && devChannels > 1) + if (setup.radioChan == 1 && devChannels > 1) { // Strip out right channel? QByteArray outPacket(packet.data.length()/2, (char)0xff); @@ -438,7 +584,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) //qDebug(logAudio()) << "Now mono, length" << packet.data.length(); // Do we need to convert 16-bit to 8-bit? - if (radioSampleBits == 8) { + if (setup.bits == 8) { QByteArray outPacket((int)packet.data.length() / 2, (char)0xff); qint16* in = (qint16*)packet.data.data(); for (int f = 0; f < outPacket.length(); f++) @@ -469,4 +615,74 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) } +qint64 audioHandler::bytesAvailable() const +{ + return 0; +} + +bool audioHandler::isSequential() const +{ + return true; +} + +void audioHandler::notified() +{ +} + + +void audioHandler::stateChanged(QAudio::State state) +{ + // Process the state + switch (state) + { + case QAudio::IdleState: + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in idle state: " << audioBuffer.size() << " packets in buffer"; + if (audioOutput != Q_NULLPTR && audioOutput->error() == QAudio::UnderrunError) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "buffer underrun"; + //audioOutput->suspend(); + } + break; + } + case QAudio::ActiveState: + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in active state: " << audioBuffer.size() << " packets in buffer"; + break; + } + case QAudio::SuspendedState: + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in suspended state: " << audioBuffer.size() << " packets in buffer"; + break; + } + case QAudio::StoppedState: + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in stopped state: " << audioBuffer.size() << " packets in buffer"; + break; + } + default: { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unhandled audio state: " << audioBuffer.size() << " packets in buffer"; + } + } +} + +void audioHandler::stop() +{ + if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) { + // Stop audio output + audioOutput->stop(); + this->stop(); + this->close(); + } + + if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) { + // Stop audio output + audioInput->stop(); + this->stop(); + this->close(); + } +} + + + diff --git a/audiohandler.h b/audiohandler.h index d978644..3407a79 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -3,12 +3,22 @@ #include -#include #include #include #include #include -#include "rtaudio/RtAudio.h" + +#if defined(RTAUDIO) +#include "RtAudio.h" +#elif defined (PORTAUDIO) +#include "portaudio.h" +#else +#include +#include +#include +#include +#include +#endif typedef signed short MY_TYPE; #define FORMAT RTAUDIO_SINT16 @@ -40,7 +50,30 @@ struct audioPacket { QByteArray data; }; +struct audioSetup { + QString name; + quint8 bits; + quint8 radioChan; + quint16 samplerate; + quint16 latency; + quint8 codec; + bool ulaw; + bool isinput; +#if defined(RTAUDIO) + int port; +#elif defined(PORTAUDIO) +#else + QAudioDeviceInfo port; +#endif + quint8 resampleQuality; +}; + +// For QtMultimedia, use a native QIODevice +#if !defined(PORTAUDIO) && !defined(RTAUDIO) +class audioHandler : public QIODevice +#else class audioHandler : public QObject +#endif { Q_OBJECT @@ -50,14 +83,26 @@ public: int getLatency(); + bool setDevice(QAudioDeviceInfo deviceInfo); + + void start(); + void flush(); + void stop(); + qint64 bytesAvailable() const; + bool isSequential() const; + void getNextAudioChunk(QByteArray &data); -private slots: - bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isulaw, const bool isinput, int port, quint8 resampleQuality); +public slots: + bool init(audioSetup setup); void changeLatency(const quint16 newSize); void setVolume(unsigned char volume); void incomingAudio(const audioPacket data); +private slots: + void notified(); + void stateChanged(QAudio::State state); + signals: void audioMessage(QString message); void sendLatency(quint16 newSize); @@ -65,6 +110,8 @@ signals: private: + +#if defined(RTAUDIO) 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) { @@ -76,15 +123,29 @@ private: static int staticWrite(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) { return static_cast(userData)->writeData(outputBuffer, inputBuffer, nFrames, streamTime, status); } +#elif defined(PORTAUDIO) + +#else + qint64 readData(char* data, qint64 nBytes); + qint64 writeData(const char* data, qint64 nBytes); +#endif void reinit(); + bool isInitialized=false; - bool isInitialized; - RtAudio audio; +#if defined(RTAUDIO) + RtAudio* audio = Q_NULLPTR; int audioDevice = 0; RtAudio::StreamParameters aParams; + RtAudio::StreamOptions options; RtAudio::DeviceInfo info; - +#elif defined(PORTAUDIO) +#else + QAudioOutput* audioOutput=Q_NULLPTR; + QAudioInput* audioInput=Q_NULLPTR; + QAudioFormat format; + QAudioDeviceInfo deviceInfo; +#endif SpeexResamplerState* resampler = Q_NULLPTR; bool isUlaw; @@ -93,10 +154,11 @@ private: unsigned int chunkSize; bool chunkAvailable; - quint32 lastSeq; + quint32 lastSeq; - quint16 radioSampleRate; - quint8 radioSampleBits; + quint16 radioSampleRate; + quint16 nativeSampleRate=0; + quint8 radioSampleBits; quint8 radioChannels; QMapaudioBuffer; @@ -105,11 +167,13 @@ private: unsigned int ratioDen; wilt::Ring *ringBuf=Q_NULLPTR; + volatile bool ready = false; audioPacket tempBuf; quint16 currentLatency; qreal volume=1.0; int devChannels; + audioSetup setup; }; #endif // AUDIOHANDLER_H diff --git a/rigcommander.cpp b/rigcommander.cpp index 2539f39..6e30b31 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -74,7 +74,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu } -void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, QString vsp) +void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp) { // construct // TODO: Bring this parameter and the comm port from the UI. @@ -90,7 +90,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, QSt if (udp == Q_NULLPTR) { - udp = new udpHandler(prefs); + udp = new udpHandler(prefs,rxSetup,txSetup); udpHandlerThread = new QThread(this); diff --git a/rigcommander.h b/rigcommander.h index 23f714e..c3c292c 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -88,7 +88,7 @@ public: public slots: void process(); void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp); - void commSetup(unsigned char rigCivAddr, udpPreferences prefs, QString vsp); + void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); void closeComm(); // Power: diff --git a/udphandler.cpp b/udphandler.cpp index d2fde85..b419f72 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -4,28 +4,22 @@ #include "udphandler.h" #include "logcategories.h" -udpHandler::udpHandler(udpPreferences prefs) : +udpHandler::udpHandler(udpPreferences prefs,audioSetup rx, audioSetup tx) : controlPort(prefs.controlLANPort), civPort(0), audioPort(0), - rxSampleRate(prefs.audioRXSampleRate), - txSampleRate(prefs.audioRXSampleRate), - rxLatency(prefs.audioRXLatency), - txLatency(prefs.audioTXLatency), - rxCodec(prefs.audioRXCodec), - txCodec(prefs.audioTXCodec), - audioInputPort(prefs.audioInput), - audioOutputPort(prefs.audioOutput), - resampleQuality(prefs.resampleQuality) + rxSetup(rx), + txSetup(tx) { + this->port = this->controlPort; this->username = prefs.username; this->password = prefs.password; this->compName = prefs.clientName.mid(0,8) + "-wfview"; - qInfo(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxLatency << " tx latency:" << txLatency << " rx sample rate: " << rxSampleRate << - " rx codec: " << rxCodec << " tx sample rate: " << txSampleRate << " tx codec: " << txCodec; + qInfo(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxSetup.latency << " tx latency:" << txSetup.latency << " rx sample rate: " << rxSetup.samplerate << + " rx codec: " << rxSetup.codec << " tx sample rate: " << txSetup.samplerate << " tx codec: " << txSetup.codec; // Try to set the IP address, if it is a hostname then perform a DNS lookup. if (!radioIP.setAddress(prefs.ipAddress)) @@ -190,7 +184,7 @@ void udpHandler::dataReceived() } QString tempLatency; - if (rxLatency > audio->audioLatency) + if (rxSetup.latency > audio->audioLatency) { tempLatency = QString("%1 ms").arg(audio->audioLatency,3); } @@ -324,10 +318,11 @@ void udpHandler::dataReceived() // TX is not supported if (txSampleRates <2 ) { - txSampleRate = 0; - txCodec = 0; + txSetup.samplerate = 0; + txSetup.codec = 0; } - audio = new udpAudio(localIP, radioIP, audioPort, rxLatency, txLatency, rxSampleRate, rxCodec, txSampleRate, txCodec, audioOutputPort, audioInputPort,resampleQuality); + + audio = new udpAudio(localIP, radioIP, audioPort, rxSetup, txSetup); QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); @@ -425,15 +420,15 @@ void udpHandler::sendRequestStream() p.rxenable = 1; if (this->txSampleRates > 1) { p.txenable = 1; - p.txcodec = txCodec; + p.txcodec = txSetup.codec; } - p.rxcodec = rxCodec; + p.rxcodec = rxSetup.codec; memcpy(&p.username, usernameEncoded.constData(), usernameEncoded.length()); - p.rxsample = qToBigEndian((quint32)rxSampleRate); - p.txsample = qToBigEndian((quint32)txSampleRate); + p.rxsample = qToBigEndian((quint32)rxSetup.samplerate); + p.txsample = qToBigEndian((quint32)txSetup.samplerate); p.civport = qToBigEndian((quint32)civPort); p.audioport = qToBigEndian((quint32)audioPort); - p.txbuffer = qToBigEndian((quint32)txLatency); + p.txbuffer = qToBigEndian((quint32)txSetup.latency); p.convert = 1; sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p))); return; @@ -699,18 +694,12 @@ void udpCivData::dataReceived() // Audio stream -udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, int outputPort, int inputPort, quint8 resampleQuality) +udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, audioSetup rxSetup, audioSetup txSetup) { qInfo(logUdp()) << "Starting udpAudio"; this->localIP = local; this->port = audioPort; this->radioIP = ip; - this->rxLatency = rxlatency; - this->txLatency = txlatency; - this->rxSampleRate = rxsample; - this->txSampleRate = txsample; - this->rxCodec = rxcodec; - this->txCodec = txcodec; if (txSampleRate == 0) { enableTx = false; @@ -720,27 +709,6 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); - /* - 0x72 is RX audio codec - 0x73 is TX audio codec (only single channel options) - 0x01 uLaw 1ch 8bit - 0x02 PCM 1ch 8bit - 0x04 PCM 1ch 16bit - 0x08 PCM 2ch 8bit - 0x10 PCM 2ch 16bit - 0x20 uLaw 2ch 8bit - */ - - if (rxCodec == 0x01 || rxCodec == 0x20) { - rxIsUlawCodec = true; - } - if (rxCodec == 0x08 || rxCodec == 0x10 || rxCodec == 0x20) { - rxChannelCount = 2; - } - if (rxCodec == 0x04 || rxCodec == 0x10) { - rxNumSamples = 16; - } - rxaudio = new audioHandler(); rxAudioThread = new QThread(this); @@ -748,7 +716,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint rxAudioThread->start(); - connect(this, SIGNAL(setupRxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), rxaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); + connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); // signal/slot not currently used. connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); @@ -756,13 +724,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); - - if (txCodec == 0x01) - txIsUlawCodec = true; - else if (txCodec == 0x04) - txNumSamples = 16; - - txChannelCount = 1; // Only 1 channel is supported. + txSetup.radioChan = 1; txaudio = new audioHandler(); txAudioThread = new QThread(this); @@ -771,7 +733,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint txAudioThread->start(); - connect(this, SIGNAL(setupTxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), txaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); + connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); @@ -782,10 +744,10 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint pingTimer->start(PING_PERIOD); // send ping packets every 100ms if (enableTx) { - emit setupTxAudio(txNumSamples, txChannelCount, txSampleRate, txLatency, txIsUlawCodec, true, inputPort, resampleQuality); + emit setupTxAudio(txSetup); } - emit setupRxAudio(rxNumSamples, rxChannelCount, rxSampleRate, txLatency, rxIsUlawCodec, false, outputPort,resampleQuality); + emit setupRxAudio(rxSetup); watchdogTimer = new QTimer(); connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog); diff --git a/udphandler.h b/udphandler.h index a835d95..4272166 100644 --- a/udphandler.h +++ b/udphandler.h @@ -16,7 +16,6 @@ #include // Needed for audio -#include #include #include @@ -175,7 +174,7 @@ class udpAudio : public udpBase Q_OBJECT public: - udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, int outputPort, int inputPort, quint8 resampleQuality); + udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, audioSetup rxSetup, audioSetup txSetup); ~udpAudio(); int audioLatency = 0; @@ -183,8 +182,8 @@ public: signals: void haveAudioData(audioPacket data); - void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, int port, quint8 resampleQuality); - void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, int port, quint8 resampleQuality); + void setupTxAudio(audioSetup setup); + void setupRxAudio(audioSetup setup); void haveChangeLatency(quint16 value); void haveSetVolume(unsigned char value); @@ -199,7 +198,6 @@ private: void dataReceived(); void watchdog(); - QAudioFormat format; quint16 rxLatency; quint16 txLatency; quint16 rxSampleRate; @@ -235,7 +233,7 @@ class udpHandler: public udpBase Q_OBJECT public: - udpHandler(udpPreferences prefs); + udpHandler(udpPreferences prefs, audioSetup rxAudio, audioSetup txAudio); ~udpHandler(); bool streamOpened = false; @@ -284,17 +282,8 @@ private: quint16 civPort; quint16 audioPort; - quint16 rxSampleRate; - quint16 txSampleRate; - quint16 rxLatency; - quint16 txLatency; - quint8 rxCodec; - quint8 txCodec; - - int audioInputPort; - int audioOutputPort; - - quint8 resampleQuality; + audioSetup rxSetup; + audioSetup txSetup; quint16 reauthInterval = 60000; QString devName; diff --git a/udpserver.cpp b/udpserver.cpp index 46bd4c5..c8ffbc8 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -2,9 +2,10 @@ #include "logcategories.h" #define STALE_CONNECTION 15 - -udpServer::udpServer(SERVERCONFIG config) : - config(config) +udpServer::udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudio) : + config(config), + outAudio(outAudio), + inAudio(inAudio) { qInfo(logUdpServer()) << "Starting udp server"; } @@ -44,7 +45,7 @@ void udpServer::init() qInfo(logUdpServer()) << "Server Binding Control to: " << config.controlPort; udpControl = new QUdpSocket(this); - udpControl->bind(config.controlPort); + udpControl->bind(config.controlPort); QUdpSocket::connect(udpControl, &QUdpSocket::readyRead, this, &udpServer::controlReceived); qInfo(logUdpServer()) << "Server Binding CIV to: " << config.civPort; @@ -156,7 +157,7 @@ udpServer::~udpServer() connMutex.unlock(); - + } @@ -213,7 +214,7 @@ void udpServer::controlReceived() current->wdTimer = new QTimer(); connect(current->wdTimer, &QTimer::timeout, this, std::bind(&udpServer::watchdog, this, current)); - current->wdTimer->start(1000); + //current->wdTimer->start(1000); current->retransmitTimer = new QTimer(); connect(current->retransmitTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRetransmitRequest, this, current)); @@ -232,218 +233,227 @@ void udpServer::controlReceived() switch (r.length()) { - - case (CONTROL_SIZE): - { - control_packet_t in = (control_packet_t)r.constData(); - if (in->type == 0x05) - { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received 'disconnect' request"; - sendControl(current, 0x00, in->seq); - //current->wdTimer->stop(); // Keep watchdog running to delete stale connection. - deleteConnection(&controlClients, current); - } - break; - } - case (PING_SIZE): - { - ping_packet_t in = (ping_packet_t)r.constData(); - if (in->type == 0x07) - { - // It is a ping request/response - if (in->reply == 0x00) + case (CONTROL_SIZE): + { + control_packet_t in = (control_packet_t)r.constData(); + if (in->type == 0x05) + { + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received 'disconnect' request"; + sendControl(current, 0x00, in->seq); + //current->wdTimer->stop(); // Keep watchdog running to delete stale connection. + deleteConnection(&controlClients, current); + } + break; + } + case (PING_SIZE): + { + ping_packet_t in = (ping_packet_t)r.constData(); + if (in->type == 0x07) + { + // It is a ping request/response + + if (in->reply == 0x00) + { + current->rxPingTime = in->time; + sendPing(&controlClients, current, in->seq, true); + } + else if (in->reply == 0x01) { + if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) { - current->rxPingTime = in->time; - sendPing(&controlClients, current, in->seq, true); - } - else if (in->reply == 0x01) { - if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) - { - // A Reply to our ping! - if (in->seq == current->pingSeq) { - current->pingSeq++; - } - else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; - } + // A Reply to our ping! + if (in->seq == current->pingSeq) { + current->pingSeq++; + } + else { + qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; } } } - break; } - case (TOKEN_SIZE): - { - // Token request - token_packet_t in = (token_packet_t)r.constData(); - current->rxSeq = in->seq; - current->authInnerSeq = in->innerseq; - current->identa = in->identa; - current->identb = in->identb; - if (in->res == 0x02) { - // Request for new token - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received create token request"; - sendCapabilities(current); - sendConnectionInfo(current); - } - else if (in->res == 0x01) { - // Token disconnect - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received token disconnect request"; - sendTokenResponse(current, in->res); - } - else if (in->res == 0x04) { - // Disconnect audio/civ - sendTokenResponse(current, in->res); - current->isStreaming = false; - sendConnectionInfo(current); - } - else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received token request"; - sendTokenResponse(current, in->res); - } - break; - } - case (LOGIN_SIZE): - { - login_packet_t in = (login_packet_t)r.constData(); - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received 'login'"; - bool userOk = false; - foreach(SERVERUSER user, config.users) - { - QByteArray usercomp; - passcode(user.username, usercomp); - QByteArray passcomp; - passcode(user.password, passcomp); - if (!strcmp(in->username, usercomp.constData()) && !strcmp(in->password, passcomp.constData())) - { - userOk = true; - current->user = user; - break; - } - - - } - // Generate login response - current->rxSeq = in->seq; - current->clientName = in->name; - current->authInnerSeq = in->innerseq; - current->tokenRx = in->tokrequest; - current->tokenTx =(quint8)rand() | (quint8)rand() << 8 | (quint8)rand() << 16 | (quint8)rand() << 24; - - if (userOk) { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": User " << current->user.username << " login OK"; - sendLoginResponse(current, true); - } - else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Incorrect username/password"; - - sendLoginResponse(current, false); - } - break; - } - case (CONNINFO_SIZE): - { - conninfo_packet_t in = (conninfo_packet_t)r.constData(); - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received request for radio connection"; - // Request to start audio and civ! - current->isStreaming = true; - current->rxSeq = in->seq; - current->rxCodec = in->rxcodec; - current->txCodec = in->txcodec; - current->rxSampleRate = qFromBigEndian(in->rxsample); - current->txSampleRate = qFromBigEndian(in->txsample); - current->txBufferLen = qFromBigEndian(in->txbuffer); - current->authInnerSeq = in->innerseq; - current->identa = in->identa; - current->identb = in->identb; - sendStatus(current); - current->authInnerSeq = 0x00; + break; + } + case (TOKEN_SIZE): + { + // Token request + token_packet_t in = (token_packet_t)r.constData(); + current->rxSeq = in->seq; + current->authInnerSeq = in->innerseq; + current->identa = in->identa; + current->identb = in->identb; + if (in->res == 0x02) { + // Request for new token + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received create token request"; + sendCapabilities(current); sendConnectionInfo(current); - qInfo(logUdpServer()) << current->ipAddress.toString() << ": rxCodec:" << current->rxCodec << " txCodec:" << current->txCodec << - " rxSampleRate" << current->rxSampleRate << - " txSampleRate" << current->rxSampleRate << - " txBufferLen" << current->txBufferLen; - - if (!config.lan) { - // Radio is connected by USB/Serial and we assume that audio is connected as well. - // Create audio TX/RX threads if they don't already exist (first client chooses samplerate/codec) - if (txaudio == Q_NULLPTR) - { - bool uLaw = false; - quint8 channels = 1; - quint8 samples = 8; - txSampleRate = current->txSampleRate; - txCodec = current->txCodec; - - if (current->txCodec == 0x01 || current->txCodec == 0x20) { - uLaw = true; - } - if (current->txCodec == 0x08 || current->txCodec == 0x10 || current->txCodec == 0x20) { - channels = 2; - } - if (current->txCodec == 0x04 || current->txCodec == 0x10) { - samples = 16; - } - - - txaudio = new audioHandler(); - txAudioThread = new QThread(this); - txaudio->moveToThread(txAudioThread); - - txAudioThread->start(); - - connect(this, SIGNAL(setupTxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), txaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); - connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); - - emit setupTxAudio(samples, channels, current->txSampleRate, current->txBufferLen, uLaw, false, config.audioOutput, config.resampleQuality); - hasTxAudio=datagram.senderAddress(); - - connect(this, SIGNAL(haveAudioData(audioPacket)), txaudio, SLOT(incomingAudio(audioPacket))); - - } - if (rxaudio == Q_NULLPTR) - { - bool uLaw = false; - quint8 channels = 1; - quint8 samples = 8; - rxSampleRate = current->rxSampleRate; - rxCodec = current->rxCodec; - - if (current->rxCodec == 0x01 || current->rxCodec == 0x20) { - uLaw = true; - } - if (current->rxCodec == 0x08 || current->rxCodec == 0x10 || current->rxCodec == 0x20) { - channels = 2; - } - if (current->rxCodec == 0x04 || current->rxCodec == 0x10) { - samples = 16; - } - - - rxaudio = new audioHandler(); - rxAudioThread = new QThread(this); - rxaudio->moveToThread(rxAudioThread); - rxAudioThread->start(); - - connect(this, SIGNAL(setupRxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), rxaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); - connect(rxAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); - - emit setupRxAudio(samples, channels, current->rxSampleRate,150, uLaw, true, config.audioInput, config.resampleQuality); - - rxAudioTimer = new QTimer(); - rxAudioTimer->setTimerType(Qt::PreciseTimer); - connect(rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); - rxAudioTimer->start(20); - } - + } + else if (in->res == 0x01) { + // Token disconnect + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received token disconnect request"; + sendTokenResponse(current, in->res); + } + else if (in->res == 0x04) { + // Disconnect audio/civ + sendTokenResponse(current, in->res); + current->isStreaming = false; + sendConnectionInfo(current); + } + else { + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received token request"; + sendTokenResponse(current, in->res); + } + break; + } + case (LOGIN_SIZE): + { + login_packet_t in = (login_packet_t)r.constData(); + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received 'login'"; + bool userOk = false; + foreach(SERVERUSER user, config.users) + { + QByteArray usercomp; + passcode(user.username, usercomp); + QByteArray passcomp; + passcode(user.password, passcomp); + if (!strcmp(in->username, usercomp.constData()) && !strcmp(in->password, passcomp.constData())) + { + userOk = true; + current->user = user; + break; } - break; + } - default: - { - break; + // Generate login response + current->rxSeq = in->seq; + current->clientName = in->name; + current->authInnerSeq = in->innerseq; + current->tokenRx = in->tokrequest; + current->tokenTx = (quint8)rand() | (quint8)rand() << 8 | (quint8)rand() << 16 | (quint8)rand() << 24; + + if (userOk) { + qInfo(logUdpServer()) << current->ipAddress.toString() << ": User " << current->user.username << " login OK"; + sendLoginResponse(current, true); } + else { + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Incorrect username/password"; + + sendLoginResponse(current, false); + } + break; + } + case (CONNINFO_SIZE): + { + conninfo_packet_t in = (conninfo_packet_t)r.constData(); + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received request for radio connection"; + // Request to start audio and civ! + current->isStreaming = true; + current->rxSeq = in->seq; + current->rxCodec = in->rxcodec; + current->txCodec = in->txcodec; + current->rxSampleRate = qFromBigEndian(in->rxsample); + current->txSampleRate = qFromBigEndian(in->txsample); + current->txBufferLen = qFromBigEndian(in->txbuffer); + current->authInnerSeq = in->innerseq; + current->identa = in->identa; + current->identb = in->identb; + sendStatus(current); + current->authInnerSeq = 0x00; + sendConnectionInfo(current); + qInfo(logUdpServer()) << current->ipAddress.toString() << ": rxCodec:" << current->rxCodec << " txCodec:" << current->txCodec << + " rxSampleRate" << current->rxSampleRate << + " txSampleRate" << current->rxSampleRate << + " txBufferLen" << current->txBufferLen; + + if (!config.lan) { + // Radio is connected by USB/Serial and we assume that audio is connected as well. + // Create audio TX/RX threads if they don't already exist (first client chooses samplerate/codec) + + audioSetup setup; + setup.resampleQuality = config.resampleQuality; + + if (txaudio == Q_NULLPTR) + { + outAudio.ulaw = false; + outAudio.radioChan = 1; + outAudio.bits = 8; + outAudio.codec = current->txCodec; + + if (current->txCodec == 0x01 || current->txCodec == 0x20) { + outAudio.ulaw = true; + } + if (current->txCodec == 0x08 || current->txCodec == 0x10 || current->txCodec == 0x20) { + outAudio.radioChan = 2; + } + if (current->txCodec == 0x04 || current->txCodec == 0x10) { + outAudio.bits = 16; + } + + outAudio.samplerate = current->txSampleRate; + outAudio.latency = current->txBufferLen; + + txaudio = new audioHandler(); + txAudioThread = new QThread(this); + txaudio->moveToThread(txAudioThread); + + txAudioThread->start(); + + connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); + connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); + + + emit setupTxAudio(outAudio); + hasTxAudio = datagram.senderAddress(); + + connect(this, SIGNAL(haveAudioData(audioPacket)), txaudio, SLOT(incomingAudio(audioPacket))); + + } + if (rxaudio == Q_NULLPTR) + { + inAudio.ulaw = false; + inAudio.radioChan = 1; + inAudio.bits = 8; + inAudio.codec = current->txCodec; + + rxSampleRate = current->rxSampleRate; + rxCodec = current->rxCodec; + + if (current->rxCodec == 0x01 || current->rxCodec == 0x20) { + inAudio.ulaw = true; + } + if (current->rxCodec == 0x08 || current->rxCodec == 0x10 || current->rxCodec == 0x20) { + inAudio.radioChan = 2; + } + if (current->rxCodec == 0x04 || current->rxCodec == 0x10) { + inAudio.bits = 16; + } + + inAudio.samplerate = current->rxSampleRate; + + rxaudio = new audioHandler(); + rxAudioThread = new QThread(this); + rxaudio->moveToThread(rxAudioThread); + rxAudioThread->start(); + + connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); + connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); + + emit setupRxAudio(inAudio); + + rxAudioTimer = new QTimer(); + rxAudioTimer->setTimerType(Qt::PreciseTimer); + connect(rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); + rxAudioTimer->start(20); + } + + } + + break; + } + default: + { + break; + } } commonReceived(&controlClients, current, r); @@ -499,7 +509,7 @@ void udpServer::civReceived() current->wdTimer = new QTimer(); connect(current->wdTimer, &QTimer::timeout, this, std::bind(&udpServer::watchdog, this, current)); - current->wdTimer->start(1000); + //current->wdTimer->start(1000); current->retransmitTimer = new QTimer(); connect(current->retransmitTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRetransmitRequest, this, current)); @@ -518,66 +528,66 @@ void udpServer::civReceived() { } */ - case (PING_SIZE): + case (PING_SIZE): + { + ping_packet_t in = (ping_packet_t)r.constData(); + if (in->type == 0x07) { - ping_packet_t in = (ping_packet_t)r.constData(); - if (in->type == 0x07) + // It is a ping request/response + + if (in->reply == 0x00) { - // It is a ping request/response - - if (in->reply == 0x00) - { - current->rxPingTime = in->time; - sendPing(&civClients, current, in->seq, true); - } - else if (in->reply == 0x01) { - if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) - { - // A Reply to our ping! - if (in->seq == current->pingSeq) { - current->pingSeq++; - } - else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; - } - } - } + current->rxPingTime = in->time; + sendPing(&civClients, current, in->seq, true); } - break; - } - default: - { - - if (r.length() > 0x18) { - data_packet_t in = (data_packet_t)r.constData(); - if (in->type != 0x01) + else if (in->reply == 0x01) { + if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) { - if (quint16(in->datalen + 0x15) == (quint16)in->len) - { - // Strip all '0xFE' command preambles first: - int lastFE = r.lastIndexOf((char)0xfe); - //qInfo(logUdpServer()) << "Got:" << r.mid(lastFE); - if (current->civId == 0 && r.length() > lastFE + 2 && (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; - } - else if (current->civId != 0 && r.length() > lastFE + 2 && (quint8)r[lastFE + 2] != current->civId) - { - current->civId = (quint8)r[lastFE + 2]; - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId; - } - - emit haveDataFromServer(r.mid(0x15)); + // A Reply to our ping! + if (in->seq == current->pingSeq) { + current->pingSeq++; } else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": Datalen mismatch " << quint16(in->datalen + 0x15) << ":" << (quint16)in->len; - + qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; } } } - //break; } + break; + } + default: + { + + if (r.length() > 0x18) { + data_packet_t in = (data_packet_t)r.constData(); + if (in->type != 0x01) + { + if (quint16(in->datalen + 0x15) == (quint16)in->len) + { + // Strip all '0xFE' command preambles first: + int lastFE = r.lastIndexOf((char)0xfe); + //qInfo(logUdpServer()) << "Got:" << r.mid(lastFE); + if (current->civId == 0 && r.length() > lastFE + 2 && (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; + } + else if (current->civId != 0 && r.length() > lastFE + 2 && (quint8)r[lastFE + 2] != current->civId) + { + current->civId = (quint8)r[lastFE + 2]; + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId; + } + + emit haveDataFromServer(r.mid(0x15)); + } + else { + qInfo(logUdpServer()) << current->ipAddress.toString() << ": Datalen mismatch " << quint16(in->datalen + 0x15) << ":" << (quint16)in->len; + + } + } + } + //break; + } } if (current != Q_NULLPTR) { udpServer::commonReceived(&civClients, current, r); @@ -620,14 +630,14 @@ void udpServer::audioReceived() current->remoteId = qFromLittleEndian(r.mid(8, 4)); current->socket = udpAudio; current->pingSeq = (quint8)rand() << 8 | (quint8)rand(); - + current->pingTimer = new QTimer(); connect(current->pingTimer, &QTimer::timeout, this, std::bind(&udpServer::sendPing, this, &audioClients, current, (quint16)0x00, false)); current->pingTimer->start(100); current->wdTimer = new QTimer(); connect(current->wdTimer, &QTimer::timeout, this, std::bind(&udpServer::watchdog, this, current)); - current->wdTimer->start(1000); + //current->wdTimer->start(1000); current->retransmitTimer = new QTimer(); connect(current->retransmitTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRetransmitRequest, this, current)); @@ -643,65 +653,65 @@ void udpServer::audioReceived() switch (r.length()) { - case (PING_SIZE): + case (PING_SIZE): + { + ping_packet_t in = (ping_packet_t)r.constData(); + if (in->type == 0x07) { - ping_packet_t in = (ping_packet_t)r.constData(); - if (in->type == 0x07) - { - // It is a ping request/response + // It is a ping request/response - if (in->reply == 0x00) + if (in->reply == 0x00) + { + current->rxPingTime = in->time; + sendPing(&audioClients, current, in->seq, true); + } + else if (in->reply == 0x01) { + if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) { - current->rxPingTime = in->time; - sendPing(&audioClients, current, in->seq, true); - } - else if (in->reply == 0x01) { - if (in->seq == current->pingSeq || in->seq == current->pingSeq - 1) - { - // A Reply to our ping! - if (in->seq == current->pingSeq) { - current->pingSeq++; - } - else { - qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; - } + // A Reply to our ping! + if (in->seq == current->pingSeq) { + current->pingSeq++; + } + else { + qInfo(logUdpServer()) << current->ipAddress.toString() << ": got out of sequence ping reply. Got: " << in->seq << " expecting: " << current->pingSeq; } } } - 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 + 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(); + */ + control_packet_t in = (control_packet_t)r.constData(); - if (in->type != 0x01 && in->len >= 0xAC) { - if (in->seq == 0) - { - // Seq number has rolled over. - current->seqPrefix++; - } - - if (hasTxAudio == current->ipAddress) - { - // 0xac is the smallest possible audio packet. - audioPacket tempAudio; - tempAudio.seq = (quint32)current->seqPrefix << 16 | in->seq; - tempAudio.time = QTime::currentTime();; - tempAudio.sent = 0; - tempAudio.data = r.mid(0x18); - //qInfo(logUdpServer()) << "sending tx audio " << in->seq; - emit haveAudioData(tempAudio); - } + if (in->type != 0x01 && in->len >= 0xAC) { + if (in->seq == 0) + { + // Seq number has rolled over. + current->seqPrefix++; + } + + if (hasTxAudio == current->ipAddress) + { + // 0xac is the smallest possible audio packet. + audioPacket tempAudio; + tempAudio.seq = (quint32)current->seqPrefix << 16 | in->seq; + tempAudio.time = QTime::currentTime();; + tempAudio.sent = 0; + tempAudio.data = r.mid(0x18); + //qInfo(logUdpServer()) << "sending tx audio " << in->seq; + emit haveAudioData(tempAudio); } - break; } + break; + } } if (current != Q_NULLPTR) { @@ -711,7 +721,7 @@ void udpServer::audioReceived() } -void udpServer::commonReceived(QList* l,CLIENT* current, QByteArray r) +void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) { Q_UNUSED(l); // We might need it later! if (current == Q_NULLPTR || r.isNull()) { @@ -727,51 +737,49 @@ void udpServer::commonReceived(QList* l,CLIENT* current, QByteArray r) switch (r.length()) { - case (CONTROL_SIZE): + case (CONTROL_SIZE): + { + control_packet_t in = (control_packet_t)r.constData(); + if (in->type == 0x03) { + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'are you there'"; + current->remoteId = in->sentid; + sendControl(current, 0x04, in->seq); + } // This is This is "Are you ready" in response to "I am here". + else if (in->type == 0x06) { - control_packet_t in = (control_packet_t)r.constData(); - if (in->type == 0x03) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'are you there'"; - current->remoteId = in->sentid; - sendControl(current, 0x04, in->seq); - } // This is This is "Are you ready" in response to "I am here". - else if (in->type == 0x06) - { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'Are you ready'"; - current->remoteId = in->sentid; - sendControl(current, 0x06, in->seq); - if (current->idleTimer != Q_NULLPTR && !current->idleTimer->isActive()) { - current->idleTimer->start(100); - } - } // This is a retransmit request - else if (in->type == 0x01) - { - // Single packet request - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'retransmit' request for " << in->seq; - - auto match = std::find_if(current->txSeqBuf.begin(), current->txSeqBuf.end(), [&cs = in->seq](SEQBUFENTRY& s) { - return s.seqNum == cs; - }); - - 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; - match->retransmitCount++; - udpMutex.lock(); - current->socket->writeDatagram(match->data, current->ipAddress, current->port); - udpMutex.unlock(); - } else { - // Just send an idle! - sendControl(current, 0x00, in->seq); - } + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'Are you ready'"; + current->remoteId = in->sentid; + sendControl(current, 0x06, in->seq); + if (current->idleTimer != Q_NULLPTR && !current->idleTimer->isActive()) { + current->idleTimer->start(100); } - break; - } - default: + } // This is a retransmit request + else if (in->type == 0x01) { - //break; + // Single packet request + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): Received 'retransmit' request for " << in->seq; + QMap::iterator 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; + match->retransmitCount++; + udpMutex.lock(); + current->socket->writeDatagram(match->data, current->ipAddress, current->port); + udpMutex.unlock(); + } + else { + // Just send an idle! + sendControl(current, 0x00, in->seq); + } } + break; + } + default: + { + //break; + } } // The packet is at least 0x10 in length so safe to cast it to control_packet for processing @@ -807,31 +815,42 @@ void udpServer::commonReceived(QList* l,CLIENT* current, QByteArray r) //if (current->type == "CIV") { // qInfo(logUdpServer()) << "Got:" << in->seq; //} - current->rxMutex.lock(); if (current->rxSeqBuf.isEmpty()) { - current->rxSeqBuf.append(in->seq); + current->rxMutex.lock(); + current->rxSeqBuf.insert(in->seq, QTime::currentTime()); + current->rxMutex.unlock(); } else { - std::sort(current->rxSeqBuf.begin(), current->rxSeqBuf.end()); - if (in->seq < current->rxSeqBuf.front()) + + if (in->seq < current->rxSeqBuf.firstKey()) { - qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): ******* seq number may have rolled over ****** previous highest: " << hex << current->rxSeqBuf.back() << " current: " << hex << in->seq; + qInfo(logUdpServer()) << current->ipAddress.toString() << "(" << current->type << "): ******* seq number may have rolled over ****** previous highest: " << hex << current->rxSeqBuf.lastKey() << " current: " << hex << in->seq; // Looks like it has rolled over so clear buffer and start again. + current->rxMutex.lock(); current->rxSeqBuf.clear(); - current->rxMutex.unlock(); // Must unlock the Mutex! + current->rxMutex.unlock(); + current->missMutex.lock(); + current->rxMissing.clear(); + current->missMutex.unlock(); return; } if (!current->rxSeqBuf.contains(in->seq)) { // Add incoming packet to the received buffer and if it is in the missing buffer, remove it. - - current->rxSeqBuf.append(in->seq); + current->rxMutex.lock(); + if (current->rxSeqBuf.size() > 400) + { + current->rxSeqBuf.remove(0); + } + current->rxSeqBuf.insert(in->seq, QTime::currentTime()); + current->rxMutex.unlock(); + } else{ // Check whether this is one of our missing ones! current->missMutex.lock(); - auto s = std::find_if(current->rxMissing.begin(), current->rxMissing.end(), [&cs = in->seq](SEQBUFENTRY& s) { return s.seqNum == cs; }); + QMap::iterator 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; @@ -840,7 +859,6 @@ void udpServer::commonReceived(QList* l,CLIENT* current, QByteArray r) current->missMutex.unlock(); } } - current->rxMutex.unlock(); } } @@ -861,20 +879,19 @@ void udpServer::sendControl(CLIENT* c, quint8 type, quint16 seq) if (seq == 0x00) { p.seq = c->txSeq; + SEQBUFENTRY s; + s.seqNum = seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); c->txMutex.lock(); - c->txSeqBuf.append(SEQBUFENTRY()); - c->txSeqBuf.last().seqNum = seq; - c->txSeqBuf.last().timeSent = QTime::currentTime(); - c->txSeqBuf.last().retransmitCount = 0; - c->txSeqBuf.last().data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + c->txSeqBuf.insert(seq, s); + c->txSeq++; + c->txMutex.unlock(); + udpMutex.lock(); c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); udpMutex.unlock(); - c->txSeq++; - //if (c->idleTimer != Q_NULLPTR) { - // c->idleTimer->start(100); - //} - c->txMutex.unlock(); } else { p.seq = seq; @@ -888,7 +905,7 @@ void udpServer::sendControl(CLIENT* c, quint8 type, quint16 seq) -void udpServer::sendPing(QList *l,CLIENT* c, quint16 seq, bool reply) +void udpServer::sendPing(QList* l, CLIENT* c, quint16 seq, bool reply) { // Also use to detect "stale" connections QDateTime now = QDateTime::currentDateTime(); @@ -962,24 +979,26 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed) c->wdTimer->stop(); } else { - strcpy(p.connection,"WFVIEW"); + strcpy(p.connection, "WFVIEW"); } + SEQBUFENTRY s; + s.seqNum = c->txSeq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); c->txMutex.lock(); - c->txSeqBuf.append(SEQBUFENTRY()); - c->txSeqBuf.last().seqNum = c->txSeq; - c->txSeqBuf.last().timeSent = QTime::currentTime(); - c->txSeqBuf.last().retransmitCount = 0; - c->txSeqBuf.last().data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + c->txSeqBuf.insert(c->txSeq, s); + c->txSeq++; + c->txMutex.unlock(); udpMutex.lock(); - c->socket->writeDatagram(c->txSeqBuf.last().data, c->ipAddress, c->port); + c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); udpMutex.unlock(); - c->txSeq++; + if (c->idleTimer != Q_NULLPTR) c->idleTimer->start(100); - c->txMutex.unlock(); return; } @@ -1063,26 +1082,32 @@ void udpServer::sendCapabilities(CLIENT* c) // I still don't know what these are? p.enableb = 0x01; // 0x01 doesn't seem to do anything? p.enablec = 0x01; // 0x01 doesn't seem to do anything? - p.capf = 0x5001; + p.capf = 0x5001; p.capg = 0x0190; - c->txMutex.lock(); - c->txSeqBuf.append(SEQBUFENTRY()); - c->txSeqBuf.last().seqNum = p.seq; - c->txSeqBuf.last().timeSent = QTime::currentTime(); - c->txSeqBuf.last().retransmitCount = 0; - c->txSeqBuf.last().data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + c->txMutex.lock(); + if (c->txSeqBuf.size() > 400) + { + c->txSeqBuf.remove(0); + } + c->txSeqBuf.insert(p.seq, s); + c->txSeq++; + c->txMutex.unlock(); + udpMutex.lock(); - c->socket->writeDatagram(c->txSeqBuf.last().data, c->ipAddress, c->port); + c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); udpMutex.unlock(); if (c->idleTimer != Q_NULLPTR) c->idleTimer->start(100); - c->txSeq++; - c->txMutex.unlock(); return; } @@ -1120,12 +1145,21 @@ void udpServer::sendConnectionInfo(CLIENT* c) p.identb = c->identb; } + + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + c->txMutex.lock(); - c->txSeqBuf.append(SEQBUFENTRY()); - c->txSeqBuf.last().seqNum = p.seq; - c->txSeqBuf.last().timeSent = QTime::currentTime(); - c->txSeqBuf.last().retransmitCount = 0; - c->txSeqBuf.last().data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + if (c->txSeqBuf.size() > 400) + { + c->txSeqBuf.remove(0); + } + c->txSeqBuf.insert(p.seq, s); + c->txSeq++; + c->txMutex.unlock(); udpMutex.lock(); c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); @@ -1134,8 +1168,6 @@ void udpServer::sendConnectionInfo(CLIENT* c) if (c->idleTimer != Q_NULLPTR) c->idleTimer->start(100); - c->txSeq++; - c->txMutex.unlock(); return; } @@ -1159,22 +1191,30 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) p.commoncap = c->commonCap; p.res = type; + + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + c->txMutex.lock(); - c->txSeqBuf.append(SEQBUFENTRY()); - c->txSeqBuf.last().seqNum = p.seq; - c->txSeqBuf.last().timeSent = QTime::currentTime(); - c->txSeqBuf.last().retransmitCount = 0; - c->txSeqBuf.last().data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + if (c->txSeqBuf.size() > 400) + { + c->txSeqBuf.remove(0); + } + c->txSeqBuf.insert(p.seq, s); + c->txSeq++; + c->txMutex.unlock(); udpMutex.lock(); c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); udpMutex.unlock(); + if (c->idleTimer != Q_NULLPTR) c->idleTimer->start(100); - c->txSeq++; - c->txMutex.unlock(); return; } @@ -1182,35 +1222,8 @@ void udpServer::sendTokenResponse(CLIENT* c, quint8 type) void udpServer::watchdog(CLIENT* c) { - if (c->txMutex.tryLock()) { - //qInfo(logUdpServer()) << c->ipAddress.toString() << ":" << c->port << ":Buffers tx:"<< c->txSeqBuf.length() << " rx:" << c->rxSeqBuf.length(); - // Erase old entries from the tx packet buffer. Keep the first 100 sent packets as we seem to get asked for these? - if (!c->txSeqBuf.isEmpty()) - { - c->txSeqBuf.erase(std::remove_if(c->txSeqBuf.begin(), c->txSeqBuf.end(), [](const SEQBUFENTRY& v) - { return v.timeSent.secsTo(QTime::currentTime()) > PURGE_SECONDS; }), c->txSeqBuf.end()); - } - c->txMutex.unlock(); - } - // Erase old entries from the missing packets buffer - if (c->missMutex.tryLock()) { - if (!c->rxMissing.isEmpty()) { - c->rxMissing.erase(std::remove_if(c->rxMissing.begin(), c->rxMissing.end(), [](const SEQBUFENTRY& v) - { return v.timeSent.secsTo(QTime::currentTime()) > PURGE_SECONDS; }), c->rxMissing.end()); - } - c->missMutex.unlock(); - } - if (c->rxMutex.tryLock()) { - if (!c->rxSeqBuf.isEmpty()) { - std::sort(c->rxSeqBuf.begin(), c->rxSeqBuf.end()); - - if (c->rxSeqBuf.length() > 400) - { - c->rxSeqBuf.remove(0, 200); - } - } - c->rxMutex.unlock(); - } + Q_UNUSED(c); + // Do something! } void udpServer::sendStatus(CLIENT* c) @@ -1235,20 +1248,24 @@ void udpServer::sendStatus(CLIENT* c) p.identa = c->identa; p.identb = c->identb; - p.civport=qToBigEndian(c->civPort); - p.audioport=qToBigEndian(c->audioPort); + p.civport = qToBigEndian(c->civPort); + p.audioport = qToBigEndian(c->audioPort); // Send this to reject the request to tx/rx audio/civ //memcpy(p + 0x30, QByteArrayLiteral("\xff\xff\xff\xfe").constData(), 4); - - c->txSeq++; + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); c->txMutex.lock(); - c->txSeqBuf.append(SEQBUFENTRY()); - c->txSeqBuf.last().seqNum = p.seq; - c->txSeqBuf.last().timeSent = QTime::currentTime(); - c->txSeqBuf.last().retransmitCount = 0; - c->txSeqBuf.last().data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); + if (c->txSeqBuf.size() > 400) + { + c->txSeqBuf.remove(0); + } + c->txSeq++; + c->txSeqBuf.insert(p.seq, s); c->txMutex.unlock(); udpMutex.lock(); @@ -1265,7 +1282,7 @@ void udpServer::dataForServer(QByteArray d) foreach(CLIENT * client, civClients) { int lastFE = d.lastIndexOf((quint8)0xfe); - if (client != Q_NULLPTR && client->connected && d.length() > lastFE+2 && ((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId)) { + if (client != Q_NULLPTR && client->connected && d.length() > lastFE + 2 && ((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId)) { data_packet p; memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! p.len = (quint16)d.length() + sizeof(p); @@ -1277,20 +1294,26 @@ void udpServer::dataForServer(QByteArray d) p.sendseq = client->innerSeq; QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); t.append(d); + + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = t; + client->txMutex.lock(); - client->txSeqBuf.append(SEQBUFENTRY()); - client->txSeqBuf.last().seqNum = p.seq; - client->txSeqBuf.last().timeSent = QTime::currentTime(); - client->txSeqBuf.last().retransmitCount = 0; - client->txSeqBuf.last().data = t; + if (client->txSeqBuf.size() > 400) + { + client->txSeqBuf.remove(0); + } + client->txSeqBuf.insert(p.seq, s); + client->txSeq++; + client->innerSeq++; client->txMutex.unlock(); - //qInfo(logUdpServer()) << "Sending:" << d; udpMutex.lock(); client->socket->writeDatagram(t, client->ipAddress, client->port); udpMutex.unlock(); - client->txSeq++; - client->innerSeq++; } } @@ -1315,7 +1338,7 @@ void udpServer::sendRxAudio() -void udpServer::receiveAudioData(const audioPacket &d) +void udpServer::receiveAudioData(const audioPacket& d) { //qInfo(logUdpServer()) << "Server got:" << d.data.length(); foreach(CLIENT * client, audioClients) @@ -1332,23 +1355,25 @@ void udpServer::receiveAudioData(const audioPacket &d) p.seq = client->txSeq; QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); t.append(d.data); - client->txMutex.lock(); - client->txSeqBuf.append(SEQBUFENTRY()); - client->txSeqBuf.last().seqNum = p.seq; - client->txSeqBuf.last().timeSent = QTime::currentTime(); - client->txSeqBuf.last().retransmitCount = 0; - client->txSeqBuf.last().data = t; - client->txMutex.unlock(); - if (udpMutex.tryLock()) { - client->socket->writeDatagram(t, client->ipAddress, client->port); - udpMutex.unlock(); - } - else { - qDebug(logUdpServer()) << "Failed to lock udpMutex()"; + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + s.data = t; + client->txMutex.lock(); + if (client->txSeqBuf.size() > 400) + { + client->txSeqBuf.remove(0); } + client->txSeqBuf.insert(p.seq, s); client->txSeq++; client->sendAudioSeq++; + client->txMutex.unlock(); + + udpMutex.lock(); + client->socket->writeDatagram(t, client->ipAddress, client->port); + udpMutex.unlock(); } } @@ -1360,71 +1385,81 @@ void udpServer::receiveAudioData(const audioPacket &d) /// This will run every 100ms so out-of-sequence packets will not trigger a retransmit request. /// /// -void udpServer::sendRetransmitRequest(CLIENT *c) +void udpServer::sendRetransmitRequest(CLIENT* c) { - - c->missMutex.lock(); + // 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. QByteArray missingSeqs; - auto i = std::adjacent_find(c->rxSeqBuf.begin(), c->rxSeqBuf.end(), [](quint16 l, quint16 r) {return l + 1 < r; }); - while (i != c->rxSeqBuf.end()) + + if (!c->rxSeqBuf.empty() && c->rxSeqBuf.size() <= c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey()) { - if (i + 1 != c->rxSeqBuf.end()) + if ((c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size()) > 20) { - if (*(i + 1) - *i < 30) - { - for (quint16 j = *i + 1; j < *(i + 1); j++) - { - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Found missing seq between " << *i << " : " << *(i + 1) << " (" << j << ")"; - auto s = std::find_if(c->rxMissing.begin(), c->rxMissing.end(), [&cs = j](SEQBUFENTRY& s) { return s.seqNum == cs; }); + // Too many packets to process, flush buffers and start again! + qDebug(logUdp()) << "Too many missing packets, flushing buffer: " << c->rxSeqBuf.lastKey() << "missing=" << c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() + 1; + c->missMutex.lock(); + c->rxMissing.clear(); + c->missMutex.unlock(); + + c->rxMutex.lock(); + c->rxSeqBuf.clear(); + c->rxMutex.unlock(); + } + else { + // We have at least 1 missing packet! + qDebug(logUdp()) << "Missing Seq: size=" << c->rxSeqBuf.size() << "firstKey=" << c->rxSeqBuf.firstKey() << "lastKey=" << c->rxSeqBuf.lastKey() << "missing=" << c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() + 1; + // We are missing packets so iterate through the buffer and add the missing ones to missing packet list + for (int i = 0; i < c->rxSeqBuf.keys().length() - 1; i++) { + for (quint16 j = c->rxSeqBuf.keys()[i] + 1; j < c->rxSeqBuf.keys()[i + 1]; j++) { + auto s = c->rxMissing.find(j); if (s == c->rxMissing.end()) { // We haven't seen this missing packet before - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Adding to missing buffer (len="<< c->rxMissing.length() << "): " << j; - c->rxMissing.append(SEQBUFENTRY()); - c->rxMissing.last().seqNum = j; - c->rxMissing.last().retransmitCount = 0; - c->rxMissing.last().timeSent = QTime::currentTime(); - //packetsLost++; + qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j; + + c->missMutex.lock(); + c->rxMissing.insert(j, 0); + c->missMutex.unlock(); + + c->rxMutex.lock(); + if (c->rxSeqBuf.size() > 400) + { + c->rxSeqBuf.remove(0); + } + c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it. + c->rxMutex.unlock(); } else { - if (s->retransmitCount == 4 && j != 0) + if (s.value() == 4) { // We have tried 4 times to request this packet, time to give up! + c->missMutex.lock(); s = c->rxMissing.erase(s); - c->rxMutex.lock(); - c->rxSeqBuf.append(j); // Final thing is to add to received buffer! - c->rxMutex.unlock(); + c->missMutex.unlock(); } } } } - else { - qInfo(logUdpServer()) << c->ipAddress.toString() << ": Too many missing, flushing buffers"; - c->rxMutex.lock(); - c->rxSeqBuf.clear(); - c->rxMutex.unlock(); - missingSeqs.clear(); - break; - } } - i++; } - + c->missMutex.lock(); for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it) { - if (it->retransmitCount < 10) + if (it.value() < 10) { - missingSeqs.append(it->seqNum & 0xff); - missingSeqs.append(it->seqNum >> 8 & 0xff); - missingSeqs.append(it->seqNum & 0xff); - missingSeqs.append(it->seqNum >> 8 & 0xff); - it->retransmitCount++; + missingSeqs.append(it.key() & 0xff); + missingSeqs.append(it.key() >> 8 & 0xff); + missingSeqs.append(it.key() & 0xff); + missingSeqs.append(it.key() >> 8 & 0xff); + it.value()++; } } + c->missMutex.unlock(); + if (missingSeqs.length() != 0) { control_packet p; @@ -1436,21 +1471,23 @@ void udpServer::sendRetransmitRequest(CLIENT *c) 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(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): sending request for missing packet : " << hex << p.seq; + qDebug(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << hex << p.seq; + udpMutex.lock(); c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); udpMutex.unlock(); } else { - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): sending request for multiple missing packets : " << missingSeqs.toHex(); - missingSeqs.insert(0, p.packet, sizeof(p.packet)); + qDebug(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(); + udpMutex.lock(); + missingSeqs.insert(0, p.packet, sizeof(p.packet)); c->socket->writeDatagram(missingSeqs, c->ipAddress, c->port); udpMutex.unlock(); } } - c->missMutex.unlock(); + } @@ -1461,9 +1498,8 @@ void udpServer::sendRetransmitRequest(CLIENT *c) /// /// /// -void udpServer::deleteConnection(QList *l, CLIENT* c) +void udpServer::deleteConnection(QList* l, CLIENT* c) { - connMutex.lock(); qInfo(logUdpServer()) << "Deleting connection to: " << c->ipAddress.toString() << ":" << QString::number(c->port); if (c->idleTimer != Q_NULLPTR) { @@ -1484,6 +1520,20 @@ void udpServer::deleteConnection(QList *l, CLIENT* c) delete c->retransmitTimer; } + c->rxMutex.lock(); + c->rxSeqBuf.clear(); + c->rxMutex.unlock(); + + c->txMutex.lock(); + c->txSeqBuf.clear(); + c->txMutex.unlock(); + + c->missMutex.lock(); + c->rxMissing.clear(); + c->missMutex.unlock(); + + + connMutex.lock(); QList::iterator it = l->begin(); while (it != l->end()) { CLIENT* client = *it; @@ -1497,6 +1547,7 @@ void udpServer::deleteConnection(QList *l, CLIENT* c) delete c; // Is this needed or will the erase have done it? c = Q_NULLPTR; qInfo(logUdpServer()) << "Current Number of clients connected: " << l->length(); + connMutex.unlock(); if (l->length() == 0) { @@ -1519,8 +1570,6 @@ void udpServer::deleteConnection(QList *l, CLIENT* c) txaudio = Q_NULLPTR; txAudioThread = Q_NULLPTR; } - - } - connMutex.unlock(); + } } diff --git a/udpserver.h b/udpserver.h index 25083d8..c1dc122 100644 --- a/udpserver.h +++ b/udpserver.h @@ -12,6 +12,7 @@ #include #include #include +#include // Allow easy endian-ness conversions #include @@ -38,7 +39,7 @@ class udpServer : public QObject Q_OBJECT public: - udpServer(SERVERCONFIG config); + udpServer(SERVERCONFIG config,audioSetup outAudio, audioSetup inAudio); ~udpServer(); public slots: @@ -51,8 +52,8 @@ signals: void haveDataFromServer(QByteArray); void haveAudioData(audioPacket data); - void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, int device, quint8 resampleQuality); - void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, int device, quint8 resampleQuality); + void setupTxAudio(audioSetup); + void setupRxAudio(audioSetup); @@ -102,9 +103,11 @@ private: quint16 txSampleRate; SERVERUSER user; - QVector txSeqBuf; - QVector rxSeqBuf; - QVector rxMissing; + + QMap rxSeqBuf; + QMap txSeqBuf; + QMap rxMissing; + QMutex txMutex; QMutex rxMutex; QMutex missMutex; @@ -160,6 +163,9 @@ private: audioHandler* txaudio = Q_NULLPTR; QThread* txAudioThread = Q_NULLPTR; + audioSetup outAudio; + audioSetup inAudio; + QTimer* rxAudioTimer=Q_NULLPTR; quint16 rxSampleRate = 0; quint16 txSampleRate = 0; diff --git a/udpserversetup.h b/udpserversetup.h index 92966df..a96ec99 100644 --- a/udpserversetup.h +++ b/udpserversetup.h @@ -4,7 +4,6 @@ #include #include #include -#include #include diff --git a/wfmain.cpp b/wfmain.cpp index 5af16c8..ab5f92b 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -39,6 +39,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType (); haveRigCaps = false; @@ -159,7 +160,8 @@ void wfmain::openRig() if (prefs.enableLAN) { ui->lanEnableBtn->setChecked(true); - emit sendCommSetup(prefs.radioCIVAddr, udpPrefs,prefs.virtualSerialPort); + // We need to setup the tx/rx audio: + emit sendCommSetup(prefs.radioCIVAddr, udpPrefs, rxSetup, txSetup, prefs.virtualSerialPort); } else { ui->serialEnableBtn->setChecked(true); if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) @@ -357,7 +359,7 @@ void wfmain::makeRig() connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString))); // Rig comm setup: - connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, QString))); + connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString))); @@ -767,16 +769,8 @@ void wfmain::setServerToPrefs() // Start server if enabled in config if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; - if (!serverConfig.lan) { - serverConfig.resampleQuality = udpPrefs.resampleQuality; - serverConfig.audioInput = udpPrefs.audioInput; - serverConfig.audioOutput = udpPrefs.audioOutput; - serverConfig.baudRate = prefs.serialPortBaud; - serverConfig.audioInput = udpPrefs.audioInput; - serverConfig.audioOutput = udpPrefs.audioOutput; - } - udp = new udpServer(serverConfig); + udp = new udpServer(serverConfig,rxSetup,txSetup); serverThread = new QThread(this); @@ -813,6 +807,18 @@ void wfmain::setUIToPrefs() void wfmain::setAudioDevicesUI() { + +#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 apiMap; apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; @@ -836,13 +842,14 @@ void wfmain::setAudioDevicesUI() } RtAudio::DeviceInfo info; - qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio.getCurrentApi()]); - unsigned int devices = audio.getDeviceCount(); + 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); + 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); @@ -852,6 +859,24 @@ void wfmain::setAudioDevicesUI() ui->audioInputCombo->addItem(QString::fromStdString(info.name), i); } } + + delete audio; + +#elif defined(PORTAUDIO) + // Use PortAudio device enumeration +#else + +// If no external library is configured, use QTMultimedia + // Enumerate audio devices, need to do before settings are loaded. + const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); + for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { + ui->audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); + } + const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + for (const QAudioDeviceInfo& deviceInfo : audioInputs) { + ui->audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); + } +#endif } void wfmain::setSerialDevicesUI() @@ -1212,24 +1237,28 @@ void wfmain::loadSettings() ui->passwordTxt->setEnabled(ui->lanEnableBtn->isChecked()); ui->passwordTxt->setText(QString("%1").arg(udpPrefs.password)); + rxSetup.isinput = false; + txSetup.isinput = true; - udpPrefs.audioRXLatency = settings->value("AudioRXLatency", udpDefPrefs.audioRXLatency).toInt(); + rxSetup.latency = settings->value("AudioRXLatency", "150").toInt(); ui->rxLatencySlider->setEnabled(ui->lanEnableBtn->isChecked()); - ui->rxLatencySlider->setValue(udpPrefs.audioRXLatency); + ui->rxLatencySlider->setValue(rxSetup.latency); ui->rxLatencySlider->setTracking(false); // Stop it sending value on every change. - udpPrefs.audioTXLatency = settings->value("AudioTXLatency", udpDefPrefs.audioTXLatency).toInt(); + txSetup.latency = settings->value("AudioTXLatency", "150").toInt(); ui->txLatencySlider->setEnabled(ui->lanEnableBtn->isChecked()); - ui->txLatencySlider->setValue(udpPrefs.audioTXLatency); + ui->txLatencySlider->setValue(rxSetup.latency); ui->txLatencySlider->setTracking(false); // Stop it sending value on every change. - udpPrefs.audioRXSampleRate = settings->value("AudioRXSampleRate", udpDefPrefs.audioRXSampleRate).toInt(); - udpPrefs.audioTXSampleRate = settings->value("AudioTXSampleRate",udpDefPrefs.audioTXSampleRate).toInt(); + ui->audioSampleRateCombo->blockSignals(true); + rxSetup.samplerate = settings->value("AudioRXSampleRate", "48000").toInt(); + txSetup.samplerate = rxSetup.samplerate; ui->audioSampleRateCombo->setEnabled(ui->lanEnableBtn->isChecked()); - int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(udpPrefs.audioRXSampleRate)); + int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(rxSetup.samplerate)); if (audioSampleRateIndex != -1) { ui->audioSampleRateCombo->setCurrentIndex(audioSampleRateIndex); } + ui->audioSampleRateCombo->blockSignals(false); // Add codec combobox items here so that we can add userdata! ui->audioRXCodecCombo->addItem("LPCM 1ch 16bit", 4); @@ -1239,39 +1268,61 @@ void wfmain::loadSettings() ui->audioRXCodecCombo->addItem("uLaw 2ch 8bit", 32); ui->audioRXCodecCombo->addItem("PCM 2ch 8bit", 8); - udpPrefs.audioRXCodec = settings->value("AudioRXCodec", udpDefPrefs.audioRXCodec).toInt(); + ui->audioRXCodecCombo->blockSignals(true); + rxSetup.codec = settings->value("AudioRXCodec", "4").toInt(); ui->audioRXCodecCombo->setEnabled(ui->lanEnableBtn->isChecked()); for (int f = 0; f < ui->audioRXCodecCombo->count(); f++) - if (ui->audioRXCodecCombo->itemData(f).toInt() == udpPrefs.audioRXCodec) + if (ui->audioRXCodecCombo->itemData(f).toInt() == rxSetup.codec) ui->audioRXCodecCombo->setCurrentIndex(f); + ui->audioRXCodecCombo->blockSignals(false); ui->audioTXCodecCombo->addItem("LPCM 1ch 16bit", 4); ui->audioTXCodecCombo->addItem("LPCM 1ch 8bit", 2); ui->audioTXCodecCombo->addItem("uLaw 1ch 8bit", 1); - udpPrefs.audioTXCodec = settings->value("AudioTXCodec", udpDefPrefs.audioTXCodec).toInt(); + ui->audioRXCodecCombo->blockSignals(true); + txSetup.codec = settings->value("AudioTXCodec", "4").toInt(); ui->audioTXCodecCombo->setEnabled(ui->lanEnableBtn->isChecked()); for (int f = 0; f < ui->audioTXCodecCombo->count(); f++) - if (ui->audioTXCodecCombo->itemData(f).toInt() == udpPrefs.audioTXCodec) + if (ui->audioTXCodecCombo->itemData(f).toInt() == txSetup.codec) ui->audioTXCodecCombo->setCurrentIndex(f); + ui->audioRXCodecCombo->blockSignals(false); - udpPrefs.audioOutputName = settings->value("AudioOutput", udpDefPrefs.audioOutputName).toString(); - qInfo(logGui()) << "Got Audio Output: " << udpPrefs.audioOutputName; - int audioOutputIndex = ui->audioOutputCombo->findText(udpPrefs.audioOutputName); + ui->audioOutputCombo->blockSignals(true); + rxSetup.name = settings->value("AudioOutput", udpDefPrefs.audioOutputName).toString(); + qInfo(logGui()) << "Got Audio Output: " << rxSetup.name; + int audioOutputIndex = ui->audioOutputCombo->findText(rxSetup.name); if (audioOutputIndex != -1) { ui->audioOutputCombo->setCurrentIndex(audioOutputIndex); - udpPrefs.audioOutput = ui->audioOutputCombo->itemData(audioOutputIndex).toInt(); +#if defined(RTAUDIO) + rxSetup.port = ui->audioOutputCombo->itemData(audioOutputIndex).toInt(); +#elif defined(PORTAUDIO) +#else + QVariant v = ui->audioOutputCombo->currentData(); + rxSetup.port = v.value(); +#endif } + ui->audioOutputCombo->blockSignals(false); - udpPrefs.audioInputName = settings->value("AudioInput", udpDefPrefs.audioInputName).toString(); - qInfo(logGui()) << "Got Audio Input: " << udpPrefs.audioInputName; - int audioInputIndex = ui->audioInputCombo->findText(udpPrefs.audioInputName); + ui->audioInputCombo->blockSignals(true); + txSetup.name = settings->value("AudioInput", udpDefPrefs.audioInputName).toString(); + qInfo(logGui()) << "Got Audio Input: " << txSetup.name; + int audioInputIndex = ui->audioInputCombo->findText(txSetup.name); if (audioInputIndex != -1) { ui->audioInputCombo->setCurrentIndex(audioInputIndex); - udpPrefs.audioInput = ui->audioInputCombo->itemData(audioInputIndex).toInt(); +#if defined(RTAUDIO) + txSetup.port = ui->audioInputCombo->itemData(audioInputIndex).toInt(); +#elif defined(PORTAUDIO) +#else + QVariant v = ui->audioInputCombo->currentData(); + txSetup.port = v.value(); +#endif } + ui->audioInputCombo->blockSignals(false); + + rxSetup.resampleQuality = settings->value("ResampleQuality", udpDefPrefs.resampleQuality).toInt(); + txSetup.resampleQuality = rxSetup.resampleQuality; - udpPrefs.resampleQuality = settings->value("ResampleQuality", udpDefPrefs.resampleQuality).toInt(); udpPrefs.clientName = settings->value("ClientName", udpDefPrefs.clientName).toString(); settings->endGroup(); @@ -1376,15 +1427,15 @@ void wfmain::saveSettings() settings->setValue("AudioLANPort", udpPrefs.audioLANPort); settings->setValue("Username", udpPrefs.username); settings->setValue("Password", udpPrefs.password); - settings->setValue("AudioRXLatency", udpPrefs.audioRXLatency); - settings->setValue("AudioTXLatency", udpPrefs.audioTXLatency); - settings->setValue("AudioRXSampleRate", udpPrefs.audioRXSampleRate); - settings->setValue("AudioRXCodec", udpPrefs.audioRXCodec); - settings->setValue("AudioTXSampleRate", udpPrefs.audioTXSampleRate); - settings->setValue("AudioTXCodec", udpPrefs.audioTXCodec); - settings->setValue("AudioOutput", udpPrefs.audioOutputName); - settings->setValue("AudioInput", udpPrefs.audioInputName); - settings->setValue("ResampleQuality", udpPrefs.resampleQuality); + settings->setValue("AudioRXLatency", rxSetup.latency); + settings->setValue("AudioTXLatency", txSetup.latency); + settings->setValue("AudioRXSampleRate", rxSetup.samplerate); + settings->setValue("AudioRXCodec", rxSetup.codec); + settings->setValue("AudioTXSampleRate", txSetup.samplerate); + settings->setValue("AudioTXCodec", txSetup.codec); + settings->setValue("AudioOutput", rxSetup.name); + settings->setValue("AudioInput", txSetup.name); + settings->setValue("ResampleQuality", rxSetup.resampleQuality); settings->setValue("ClientName", udpPrefs.clientName); settings->endGroup(); @@ -3689,42 +3740,58 @@ void wfmain::on_passwordTxt_textChanged(QString text) void wfmain::on_audioOutputCombo_currentIndexChanged(int value) { - udpPrefs.audioOutput = ui->audioOutputCombo->itemData(value).toInt(); - udpPrefs.audioOutputName = ui->audioOutputCombo->itemText(value); +#if defined(RTAUDIO) + rxSetup.port = ui->audioOutputCombo->itemData(value).toInt(); +#elif defined(PORTAUDIO) +#else + QVariant v = ui->audioOutputCombo->itemData(value); + rxSetup.port = v.value(); +#endif + rxSetup.name = ui->audioOutputCombo->itemText(value); + qDebug(logGui()) << "Changed default audio output to:" << rxSetup.name; } void wfmain::on_audioInputCombo_currentIndexChanged(int value) { - udpPrefs.audioInput = ui->audioInputCombo->itemData(value).toInt(); - udpPrefs.audioInputName = ui->audioInputCombo->itemText(value); +#if defined(RTAUDIO) + txSetup.port = ui->audioInputCombo->itemData(value).toInt(); +#elif defined(PORTAUDIO) +#else + QVariant v = ui->audioInputCombo->itemData(value); + txSetup.port = v.value(); +#endif + txSetup.name = ui->audioInputCombo->itemText(value); + qDebug(logGui()) << "Changed default audio input to:" << txSetup.name; } void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text) { udpPrefs.audioRXSampleRate = text.toInt(); udpPrefs.audioTXSampleRate = text.toInt(); + rxSetup.samplerate = text.toInt(); + txSetup.samplerate = text.toInt(); } void wfmain::on_audioRXCodecCombo_currentIndexChanged(int value) { - udpPrefs.audioRXCodec = ui->audioRXCodecCombo->itemData(value).toInt(); + rxSetup.codec = ui->audioRXCodecCombo->itemData(value).toInt(); } void wfmain::on_audioTXCodecCombo_currentIndexChanged(int value) { - udpPrefs.audioTXCodec = ui->audioTXCodecCombo->itemData(value).toInt(); + txSetup.codec = ui->audioTXCodecCombo->itemData(value).toInt(); } void wfmain::on_rxLatencySlider_valueChanged(int value) { - udpPrefs.audioRXLatency = value; + rxSetup.latency = value; ui->rxLatencyValue->setText(QString::number(value)); emit sendChangeLatency(value); } void wfmain::on_txLatencySlider_valueChanged(int value) { - udpPrefs.audioTXLatency = value; + txSetup.latency = value; ui->txLatencyValue->setText(QString::number(value)); } diff --git a/wfmain.h b/wfmain.h index e44d687..36e193d 100644 --- a/wfmain.h +++ b/wfmain.h @@ -138,7 +138,7 @@ signals: void sayMode(); void sayAll(); void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp); - void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, QString vsp); + void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); void sendCloseComm(); void sendChangeLatency(quint16 latency); void initServer(); @@ -642,6 +642,10 @@ private: udpPreferences udpPrefs; udpPreferences udpDefPrefs; + // Configuration for audio output and input. + audioSetup rxSetup; + audioSetup txSetup; + colors defaultColors; void setDefaultColors(); // populate with default values @@ -731,7 +735,10 @@ private: SERVERCONFIG serverConfig; shuttle *shuttleDev = Q_NULLPTR; QThread *shuttleThread = Q_NULLPTR; +#ifdef RTAUDIO RtAudio audio; +#endif + }; Q_DECLARE_METATYPE(struct rigCapabilities) @@ -739,6 +746,7 @@ Q_DECLARE_METATYPE(struct freqt) Q_DECLARE_METATYPE(struct udpPreferences) Q_DECLARE_METATYPE(struct rigStateStruct) Q_DECLARE_METATYPE(struct audioPacket) +Q_DECLARE_METATYPE(struct audioSetup) Q_DECLARE_METATYPE(enum rigInput) Q_DECLARE_METATYPE(enum meterKind) Q_DECLARE_METATYPE(enum spectrumMode) diff --git a/wfview.pro b/wfview.pro index e8f0a67..ad9e1fd 100644 --- a/wfview.pro +++ b/wfview.pro @@ -15,12 +15,14 @@ TEMPLATE = app CONFIG(debug, release|debug) { # For Debug builds only: +QMAKE_CXXFLAGS += -faligned-new } 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 } @@ -43,15 +45,6 @@ linux:DEFINES += __LINUX_ALSA__ #linux:DEFINES += __LINUX_PULSE__ macx:DEFINES += __MACOSX_CORE__ -#option(RTAUDIO_API_DS "Build DirectSound API" OFF) -#option(RTAUDIO_API_ASIO "Build ASIO API" OFF) -#option(RTAUDIO_API_WASAPI "Build WASAPI API" ${WIN32}) -#option(RTAUDIO_API_OSS "Build OSS4 API" ${xBSD}) -#option(RTAUDIO_API_ALSA "Build ALSA API" ${LINUX}) -#option(RTAUDIO_API_PULSE "Build PulseAudio API" ${pulse_FOUND}) -#option(RTAUDIO_API_JACK "Build JACK audio server API" ${HAVE_JACK}) -#option(RTAUDIO_API_CORE "Build CoreAudio API" ${APPLE}) - macx:INCLUDEPATH += /usr/local/include /opt/local/include macx:LIBS += -L/usr/local/lib -L/opt/local/lib @@ -98,18 +91,23 @@ CONFIG(debug, release|debug) { linux: QCPLIB = qcustomplot } -linux:LIBS += -L./ -l$$QCPLIB -lasound +#linux:LIBS += -L./ -l$$QCPLIB -lpulse -lpulse-simple -lpthread +linux:LIBS += -L./ -l$$QCPLIB macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread win32:LIBS += -L../hidapi/windows/release -lhidapi win32:INCLUDEPATH += ../hidapi/hidapi +#!linux:SOURCES += ../qcustomplot/qcustomplot.cpp rtaudio/RTAudio.cpp +#!linux:HEADERS += ../qcustomplot/qcustomplot.h rtaudio/RTAUdio.h + !linux:SOURCES += ../qcustomplot/qcustomplot.cpp !linux:HEADERS += ../qcustomplot/qcustomplot.h + !linux:INCLUDEPATH += ../qcustomplot INCLUDEPATH += opus-tools/src -INCLUDEPATH += rtaudio +!linux:INCLUDEPATH += rtaudio SOURCES += main.cpp\ wfmain.cpp \ @@ -130,7 +128,6 @@ SOURCES += main.cpp\ opus-tools/src/resample.c \ repeatersetup.cpp \ rigctld.cpp \ - rtaudio/RtAudio.cpp \ ring/ring.cpp \ shuttle.cpp @@ -156,7 +153,6 @@ HEADERS += wfmain.h \ repeatersetup.h \ repeaterattributes.h \ rigctld.h \ - rtaudio/RtAudio.h \ ulaw.h \ ring/ring.h \ shuttle.h diff --git a/wfview.vcxproj b/wfview.vcxproj index 1e6e96b..33c7455 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -48,7 +48,7 @@ - .;..\hidapi\hidapi;..\qcustomplot;opus-tools\src;rtaudio;release;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;opus-tools\src;rtaudio;release;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="84a4e7c";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;QT_USB_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="24ce16b";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -66,8 +66,8 @@ Level3 true - ..\hidapi\windows\release\hidapi.lib;$(QTDIR)\lib\Qt5Usb.lib;shell32.lib;%(AdditionalDependencies) - ..\hidapi\windows\release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + shell32.lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true false @@ -85,12 +85,12 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"84a4e7c\";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_USB_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"24ce16b\";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) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h - .;..\hidapi\hidapi;..\qcustomplot;opus-tools\src;rtaudio;debug;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;opus-tools\src;rtaudio;debug;/include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -99,7 +99,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="84a4e7c";HOST="wfview.org";UNAME="build";QT_USB_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="24ce16b";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -107,8 +107,8 @@ Level3 true - ..\hidapi\windows\release\hidapi.lib;$(QTDIR)\lib\Qt5Usbd.lib;shell32.lib;%(AdditionalDependencies) - ..\hidapi\windows\release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + shell32.lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true true @@ -124,11 +124,11 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"84a4e7c\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_USB_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"24ce16b\";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) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h - + @@ -146,14 +146,13 @@ - - + @@ -271,16 +270,6 @@ - - - - - - - - - - @@ -338,14 +327,14 @@ Document true $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) - cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -faligned-new -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h Generate moc_predefs.h debug\moc_predefs.h;%(Outputs) Document $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) - cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -fvisibility=hidden -fvisibility-inlines-hidden -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -fvisibility=hidden -fvisibility-inlines-hidden -faligned-new -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h Generate moc_predefs.h release\moc_predefs.h;%(Outputs) true @@ -378,8 +367,6 @@ - - diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index 36e51bb..1c9e103 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -57,7 +57,7 @@ - + Source Files @@ -111,9 +111,6 @@ Source Files - - Source Files - Source Files @@ -128,7 +125,7 @@ - + Header Files @@ -188,9 +185,6 @@ Header Files - - Header Files - Header Files @@ -253,8 +247,6 @@ - -