From 24ce16b0b9bbc0e63e4fbc82636a1179296138ca Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Fri, 4 Jun 2021 08:24:26 +0100 Subject: [PATCH] Add QtMultimedia as default audio --- audiohandler.cpp | 351 +++++++++++++++++++++++++++++++++++------------ audiohandler.h | 81 +++++++++-- rigcommander.cpp | 4 +- rigcommander.h | 2 +- udphandler.cpp | 82 +++-------- udphandler.h | 21 +-- udpserver.cpp | 30 +++- udpserver.h | 4 +- wfmain.cpp | 111 ++++++++++----- wfmain.h | 7 +- wfview.pro | 4 +- 11 files changed, 482 insertions(+), 215 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index 1a364c4..c644bf5 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -7,25 +7,16 @@ #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 (resampler != Q_NULLPTR) { - speex_resampler_destroy(resampler); - } if (isInitialized) { +#if defined(RTAUDIO) try { audio->abortStream(); @@ -34,34 +25,74 @@ audioHandler::~audioHandler() catch (RtAudioError& e) { qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); } - } - - if (audio != Q_NULLPTR) { 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"; + } + } -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) @@ -70,12 +101,10 @@ bool audioHandler::init(const quint8 bits, const quint8 radioChan, const quint16 audio = new RtAudio(RtAudio::Api::MACOSX_CORE); #endif - tempBuf.sent = 0; - if (port > 0) { aParams.deviceId = port; } - else if (isInput) { + else if (setup.isinput) { aParams.deviceId = audio->getDefaultInputDevice(); } else { @@ -105,7 +134,7 @@ bool audioHandler::init(const quint8 bits, const quint8 radioChan, const quint16 // Per channel chunk size. this->chunkSize = (this->nativeSampleRate / 50); - qInfo(logAudio()) << (isInput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed"; + 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!"; @@ -121,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; } @@ -134,60 +163,119 @@ 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 !defined(Q_OS_MACX) - options.flags = ((!RTAUDIO_HOG_DEVICE) | (RTAUDIO_MINIMIZE_LATENCY)); -#endif - - if (isInput) { - resampler = wf_resampler_init(devChannels, nativeSampleRate, samplerate, resampleQuality, &resample_error); - try { - audio->openStream(NULL, &aParams, RTAUDIO_SINT16, nativeSampleRate, &this->chunkSize, &staticWrite, this, &options); - audio->startStream(); - isInitialized = true; - } - 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, this->nativeSampleRate, resampleQuality, &resample_error); - try { - audio->openStream(&aParams, NULL, RTAUDIO_SINT16, this->nativeSampleRate, &this->chunkSize, &staticRead, this, &options); - audio->startStream(); - isInitialized = true; - } - 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:" <getStreamLatency(); + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Internal: sample rate" << format.sampleRate() << "channel count" << format.channelCount(); + // We "hopefully" now have a valid format that is supported so try connecting + if (setup.isinput) { + audioInput = new QAudioInput(setup.port, format, this); + connect(audioInput, SIGNAL(notify()), SLOT(notified())); + connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); + isInitialized = true; + } + else { + audioOutput = new QAudioOutput(setup.port, format, this); + connect(audioOutput, SIGNAL(notify()), SLOT(notified())); + connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); + isInitialized = true; + } + +#endif + // Setup resampler if it is needed. + int resample_error = 0; + if (setup.isinput) { + resampler = wf_resampler_init(devChannels, nativeSampleRate, setup.samplerate, setup.resampleQuality, &resample_error); + } + else { + resampler = wf_resampler_init(devChannels, setup.samplerate, this->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 << ")"; } @@ -198,17 +286,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) { @@ -241,7 +334,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; @@ -263,10 +356,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; } @@ -277,18 +370,28 @@ 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; while (sentlen < nBytes) { if (tempBuf.sent != nBytes) @@ -314,8 +417,12 @@ int audioHandler::writeData(void* outputBuffer, void* inputBuffer, unsigned int } //qDebug(logAudio()) << "sentlen" << sentlen; - - return 0; +#if defined(RTAUDIO) + return 0; +#elif defined(PORTAUDIO) +#else + return nBytes; +#endif } @@ -325,21 +432,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 (!isInitialized || !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; @@ -353,7 +460,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(); @@ -398,7 +505,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. @@ -415,7 +522,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; } @@ -446,7 +553,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(); @@ -457,7 +564,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); @@ -475,7 +582,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++) @@ -506,4 +613,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 ff40eb0..3407a79 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -7,7 +7,18 @@ #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 @@ -39,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 @@ -49,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); @@ -64,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) { @@ -75,16 +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; + +#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,11 +154,11 @@ private: unsigned int chunkSize; bool chunkAvailable; - quint32 lastSeq; + quint32 lastSeq; - quint16 radioSampleRate; - quint16 nativeSampleRate=0; - quint8 radioSampleBits; + quint16 radioSampleRate; + quint16 nativeSampleRate=0; + quint8 radioSampleBits; quint8 radioChannels; QMapaudioBuffer; @@ -106,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 abedddd..4272166 100644 --- a/udphandler.h +++ b/udphandler.h @@ -174,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; @@ -182,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); @@ -233,7 +233,7 @@ class udpHandler: public udpBase Q_OBJECT public: - udpHandler(udpPreferences prefs); + udpHandler(udpPreferences prefs, audioSetup rxAudio, audioSetup txAudio); ~udpHandler(); bool streamOpened = false; @@ -282,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 031e569..dd84af8 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -367,6 +367,10 @@ void udpServer::controlReceived() 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) { bool uLaw = false; @@ -385,6 +389,13 @@ void udpServer::controlReceived() samples = 16; } + //setup.port = config.audioOutput; + setup.bits = samples; + setup.radioChan = channels; + setup.ulaw = uLaw; + setup.samplerate = current->txSampleRate; + setup.latency = current->txBufferLen; + setup.isinput = false; txaudio = new audioHandler(); txAudioThread = new QThread(this); @@ -392,10 +403,11 @@ void udpServer::controlReceived() 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())); - emit setupTxAudio(samples, channels, current->txSampleRate, current->txBufferLen, uLaw, false, config.audioOutput, config.resampleQuality); + + emit setupTxAudio(setup); hasTxAudio = datagram.senderAddress(); connect(this, SIGNAL(haveAudioData(audioPacket)), txaudio, SLOT(incomingAudio(audioPacket))); @@ -419,16 +431,24 @@ void udpServer::controlReceived() 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(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); - emit setupRxAudio(samples, channels, current->rxSampleRate, 250, uLaw, true, config.audioInput, config.resampleQuality); + setup.bits = samples; + setup.radioChan = channels; + setup.ulaw = uLaw; + setup.samplerate = current->rxSampleRate; + setup.latency = 150; + setup.isinput = true; + //setup.port = config.audioInput; + setup.resampleQuality = config.resampleQuality; + + emit setupRxAudio(setup); rxAudioTimer = new QTimer(); rxAudioTimer->setTimerType(Qt::PreciseTimer); diff --git a/udpserver.h b/udpserver.h index 78916c1..2d6127f 100644 --- a/udpserver.h +++ b/udpserver.h @@ -52,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); diff --git a/wfmain.cpp b/wfmain.cpp index 33129be..f0d41fd 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -40,6 +40,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType (); haveRigCaps = false; @@ -154,7 +155,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())) @@ -352,7 +354,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))); @@ -763,6 +765,7 @@ void wfmain::setServerToPrefs() if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; if (!serverConfig.lan) { + // How do we setup the audio for server???? serverConfig.resampleQuality = udpPrefs.resampleQuality; serverConfig.audioInput = udpPrefs.audioInput; serverConfig.audioOutput = udpPrefs.audioOutput; @@ -809,6 +812,8 @@ 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) @@ -860,6 +865,22 @@ void wfmain::setAudioDevicesUI() } 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() @@ -1204,24 +1225,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); @@ -1231,39 +1256,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); +#if defined(RTAUDIO) udpPrefs.audioOutput = 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); +#if defined(RTAUDIO) udpPrefs.audioInput = ui->audioInputCombo->itemData(audioInputIndex).toInt(); +#elif defined(PORTAUDIO) +#else + QVariant v = ui->audioInputCombo->currentData(); + txSetup.port = v.value(); +#endif } + ui->audioOutputCombo->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(); @@ -1368,15 +1415,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("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", udpPrefs.audioOutputName); settings->setValue("AudioInput", udpPrefs.audioInputName); - settings->setValue("ResampleQuality", udpPrefs.resampleQuality); + settings->setValue("ResampleQuality", rxSetup.resampleQuality); settings->setValue("ClientName", udpPrefs.clientName); settings->endGroup(); @@ -3695,28 +3742,30 @@ 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 181a8d5..cbe899b 100644 --- a/wfmain.h +++ b/wfmain.h @@ -136,7 +136,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(); @@ -637,6 +637,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,6 +735,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 65d9395..67ee5e5 100644 --- a/wfview.pro +++ b/wfview.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui serialport network +QT += core gui serialport network multimedia greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport @@ -90,7 +90,7 @@ CONFIG(debug, release|debug) { } #linux:LIBS += -L./ -l$$QCPLIB -lpulse -lpulse-simple -lpthread -linux:LIBS += -L./ -l$$QCPLIB -lrtaudio -lpthread +linux:LIBS += -L./ -l$$QCPLIB macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread !linux:SOURCES += ../qcustomplot/qcustomplot.cpp rtaudio/RTAudio.cpp