From 8e38a8efb254a01cb4468c678e38b7cbd7987b26 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Sun, 8 May 2022 11:46:33 +0100 Subject: [PATCH] Create function to convert Icom format to QAudioFormat --- audioconverter.cpp | 2 +- audioconverter.h | 47 ++++++++++++++ audiohandler.cpp | 148 ++++++++++++++------------------------------- audiohandler.h | 5 +- udphandler.cpp | 15 ++--- udpserver.cpp | 4 +- wfmain.cpp | 14 ++--- 7 files changed, 111 insertions(+), 124 deletions(-) diff --git a/audioconverter.cpp b/audioconverter.cpp index 8a0558c..44bd25f 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -209,7 +209,7 @@ bool audioConverter::convert(audioPacket audio) } /* - Output is Opus so encode it now + If output is Opus so encode it now, don't do any more conversion on the output of Opus. */ if (outFormat.codec() == "audio/opus") diff --git a/audioconverter.h b/audioconverter.h index c3cc941..2b4f309 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -65,4 +65,51 @@ typedef Eigen::Matrix VectorXint8; typedef Eigen::Matrix VectorXint16; typedef Eigen::Matrix VectorXint32; +static QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate) +{ + QAudioFormat format; + + /* + 0x01 uLaw 1ch 8bit + 0x02 PCM 1ch 8bit + 0x04 PCM 1ch 16bit + 0x08 PCM 2ch 8bit + 0x10 PCM 2ch 16bit + 0x20 uLaw 2ch 8bit + 0x40 Opus 1ch + 0x80 Opus 2ch + */ + + format.setChannelCount(1); + format.setSampleSize(8); + format.setSampleType(QAudioFormat::UnSignedInt); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setCodec("audio/pcm"); + + if (codec == 0x01 || codec == 0x20) { + /* Set sample to be what is expected by the encoder and the output of the decoder */ + format.setSampleSize(16); + format.setSampleType(QAudioFormat::SignedInt); + format.setCodec("audio/PCMU"); + } + + if (codec == 0x08 || codec == 0x10 || codec == 0x20 || codec == 0x80) { + format.setChannelCount(2); + } + + if (codec == 0x04 || codec == 0x10) { + format.setSampleSize(16); + format.setSampleType(QAudioFormat::SignedInt); + } + + if (codec == 0x40 || codec == 0x80) { + format.setSampleSize(32); + format.setSampleType(QAudioFormat::Float); + format.setCodec("audio/opus"); + } + + format.setSampleRate(sampleRate); + return format; +} + #endif \ No newline at end of file diff --git a/audiohandler.cpp b/audiohandler.cpp index 81a88f4..aaf6b34 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -37,121 +37,89 @@ audioHandler::~audioHandler() } } -bool audioHandler::init(audioSetup setupIn) +bool audioHandler::init(audioSetup setup) { if (isInitialized) { return false; } - /* - 0x01 uLaw 1ch 8bit - 0x02 PCM 1ch 8bit - 0x04 PCM 1ch 16bit - 0x08 PCM 2ch 8bit - 0x10 PCM 2ch 16bit - 0x20 uLaw 2ch 8bit - 0x40 Opus 1ch - 0x80 Opus 2ch - */ - setup = setupIn; - setup.format.setChannelCount(1); - setup.format.setSampleSize(8); - setup.format.setSampleType(QAudioFormat::UnSignedInt); - setup.format.setByteOrder(QAudioFormat::LittleEndian); - setup.format.setCodec("audio/pcm"); + this->setup = setup; qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; - if (setup.port.isNull()) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins."; return false; } - if (setup.codec == 0x01 || setup.codec == 0x20) { - setup.ulaw = true; - setup.format.setSampleSize(16); - setup.format.setSampleType(QAudioFormat::SignedInt); - setup.format.setCodec("audio/PCMU"); - } - - if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20 || setup.codec == 0x80) { - setup.format.setChannelCount(2); - } - - if (setup.codec == 0x04 || setup.codec == 0x10) { - setup.format.setSampleSize(16); - setup.format.setSampleType(QAudioFormat::SignedInt); - } - - if (setup.codec == 0x40 || setup.codec == 0x80) { - setup.format.setSampleSize(32); - setup.format.setSampleType(QAudioFormat::Float); - setup.format.setCodec("audio/opus"); - } - qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name << - ", bits" << setup.format.sampleSize() << + ", bits" << inFormat.sampleSize() << ", codec" << setup.codec << ", latency" << setup.latency << ", localAFGain" << setup.localAFgain << - ", radioChan" << setup.format.channelCount() << + ", radioChan" << inFormat.channelCount() << ", resampleQuality" << setup.resampleQuality << - ", samplerate" << setup.format.sampleRate() << + ", samplerate" << inFormat.sampleRate() << ", uLaw" << setup.ulaw; + inFormat = toQAudioFormat(setup.codec, setup.sampleRate); if(!setup.isinput) { this->setVolume(setup.localAFgain); } - format = setup.port.preferredFormat(); - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Preferred Format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() << - "Sample Rate" << format.sampleRate() << "Codec" << format.codec() << "Sample Type" << format.sampleType(); - if (format.channelCount() > 2) { - format.setChannelCount(2); + outFormat = setup.port.preferredFormat(); + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Preferred Format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() << + "Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType(); + if (outFormat.channelCount() > 2) { + outFormat.setChannelCount(2); } - else if (format.channelCount() < 1) + else if (outFormat.channelCount() < 1) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup."; return false; } - if (format.channelCount() == 1 && setup.format.channelCount() == 2) { - format.setChannelCount(2); - if (!setup.port.isFormatSupported(format)) { + if (outFormat.channelCount() == 1 && inFormat.channelCount() == 2) { + outFormat.setChannelCount(2); + if (!setup.port.isFormatSupported(outFormat)) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot request stereo input!"; - format.setChannelCount(1); + outFormat.setChannelCount(1); } } - if (format.sampleType()==QAudioFormat::SignedInt) { - format.setSampleType(QAudioFormat::Float); - format.setSampleSize(32); - if (!setup.port.isFormatSupported(format)) { + if (outFormat.sampleType()==QAudioFormat::SignedInt) { + outFormat.setSampleType(QAudioFormat::Float); + outFormat.setSampleSize(32); + if (!setup.port.isFormatSupported(outFormat)) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Attempt to select 32bit Float failed, reverting to SignedInt"; - format.setSampleType(QAudioFormat::SignedInt); - format.setSampleSize(16); + outFormat.setSampleType(QAudioFormat::SignedInt); + outFormat.setSampleSize(16); } } - if (format.sampleSize() == 24) { + if (outFormat.sampleSize() == 24) { // We can't convert this easily so use 32 bit instead. - format.setSampleSize(32); - if (!setup.port.isFormatSupported(format)) { + outFormat.setSampleSize(32); + if (!setup.port.isFormatSupported(outFormat)) { qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "24 bit requested and 32 bit audio not supported, try 16 bit instead"; - format.setSampleSize(16); + outFormat.setSampleSize(16); } } - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << format.sampleSize() << "Channel Count" << format.channelCount() << - "Sample Rate" << format.sampleRate() << "Codec" << format.codec() << "Sample Type" << format.sampleType(); + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() << + "Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType(); // We "hopefully" now have a valid format that is supported so try connecting converter = new audioConverter(); converterThread = new QThread(this); - converterThread->setObjectName("audioConverter()"); + if (setup.isinput) { + converterThread->setObjectName("audioConvIn()"); + } + else { + converterThread->setObjectName("audioConvOut()"); + } converter->moveToThread(converterThread); connect(this, SIGNAL(setupConverter(QAudioFormat,QAudioFormat,quint8,quint8)), converter, SLOT(init(QAudioFormat,QAudioFormat,quint8,quint8))); @@ -160,15 +128,15 @@ bool audioHandler::init(audioSetup setupIn) converterThread->start(QThread::TimeCriticalPriority); if (setup.isinput) { - audioInput = new QAudioInput(setup.port, format, this); + audioInput = new QAudioInput(setup.port, outFormat, this); connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); - emit setupConverter(format, setup.format, 7, setup.resampleQuality); + emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality); connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket))); } else { - audioOutput = new QAudioOutput(setup.port, format, this); + audioOutput = new QAudioOutput(setup.port, outFormat, this); connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); - emit setupConverter(setup.format, format, 7, setup.resampleQuality); + emit setupConverter(inFormat, outFormat, 7, setup.resampleQuality); connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket))); } @@ -201,9 +169,9 @@ void audioHandler::start() else { // Buffer size must be set before audio is started. #ifdef Q_OS_WIN - audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 100)); + audioOutput->setBufferSize(outFormat.bytesForDuration(setup.latency * 100)); #else - audioOutput->setBufferSize(format.bytesForDuration(setup.latency * 1000)); + audioOutput->setBufferSize(outFormat.bytesForDuration(setup.latency * 1000)); #endif audioDevice = audioOutput->start(); connect(audioOutput, SIGNAL(destroyed()), audioDevice, SLOT(deleteLater()), Qt::UniqueConnection); @@ -232,32 +200,6 @@ void audioHandler::stop() audioDevice = Q_NULLPTR; } -/* -qint64 audioHandler::readData(char* data, qint64 nBytes) { - return nBytes; -} -qint64 audioHandler::writeData(const char* data, qint64 nBytes) { - - tempBuf.data.append(data,nBytes); - - while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { - audioPacket packet; - packet.time = QTime::currentTime(); - packet.sent = 0; - packet.volume = volume; - memcpy(&packet.guid, setup.guid, GUIDLEN); - QTime startProcessing = QTime::currentTime(); - packet.data.clear(); - packet.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); - tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); - - emit sendToConverter(packet); - } - - return nBytes; -} -*/ - void audioHandler::setVolume(unsigned char volume) { this->volume = audiopot[volume]; @@ -279,7 +221,7 @@ void audioHandler::incomingAudio(audioPacket packet) void audioHandler::convertedOutput(audioPacket packet) { - currentLatency = packet.time.msecsTo(QTime::currentTime()) + (format.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); + currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audioOutput->bufferSize() - audioOutput->bytesFree()) / 1000); if (audioDevice != Q_NULLPTR) { audioDevice->write(packet.data); if (lastReceived.msecsTo(QTime::currentTime()) > 100) { @@ -304,7 +246,7 @@ void audioHandler::getNextAudioChunk() { tempBuf.data.append(audioDevice->readAll()); - while (tempBuf.data.length() >= format.bytesForDuration(setup.blockSize * 1000)) { + while (tempBuf.data.length() >= outFormat.bytesForDuration(setup.blockSize * 1000)) { audioPacket packet; packet.time = QTime::currentTime(); packet.sent = 0; @@ -312,8 +254,8 @@ void audioHandler::getNextAudioChunk() memcpy(&packet.guid, setup.guid, GUIDLEN); //QTime startProcessing = QTime::currentTime(); packet.data.clear(); - packet.data = tempBuf.data.mid(0, format.bytesForDuration(setup.blockSize * 1000)); - tempBuf.data.remove(0, format.bytesForDuration(setup.blockSize * 1000)); + packet.data = tempBuf.data.mid(0, outFormat.bytesForDuration(setup.blockSize * 1000)); + tempBuf.data.remove(0, outFormat.bytesForDuration(setup.blockSize * 1000)); emit sendToConverter(packet); } @@ -342,7 +284,7 @@ void audioHandler::changeLatency(const quint16 newSize) stop(); start(); } - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << format.durationForBytes(audioOutput->bufferSize())/1000 << "ms"; + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Configured latency: " << setup.latency << "Buffer Duration:" << outFormat.durationForBytes(audioOutput->bufferSize())/1000 << "ms"; } diff --git a/audiohandler.h b/audiohandler.h index 01a512e..c5ac1e7 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -63,7 +63,7 @@ struct audioSetup { quint8 codec; bool ulaw = false; bool isinput; - QAudioFormat format; // Use this for all audio APIs + quint32 sampleRate; QAudioDeviceInfo port; quint8 resampleQuality; unsigned char localAFgain; @@ -124,7 +124,8 @@ private: QAudioOutput* audioOutput=Q_NULLPTR; QAudioInput* audioInput=Q_NULLPTR; QIODevice* audioDevice=Q_NULLPTR; - QAudioFormat format; + QAudioFormat inFormat; + QAudioFormat outFormat; QAudioDeviceInfo deviceInfo; audioConverter* converter=Q_NULLPTR; diff --git a/udphandler.cpp b/udphandler.cpp index 0a615d7..5084ee9 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -26,8 +26,8 @@ udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) : { splitWf = false; } - qInfo(logUdp()) << "Starting udpHandler user:" << username << " rx latency:" << rxSetup.latency << " tx latency:" << txSetup.latency << " rx sample rate: " << rxSetup.format.sampleRate() << - " rx codec: " << rxSetup.codec << " tx sample rate: " << txSetup.format.sampleRate() << " tx codec: " << txSetup.codec; + 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)) @@ -295,7 +295,7 @@ void udpHandler::dataReceived() // TX is not supported if (txSampleRates < 2) { - txSetup.format.setSampleRate(0); + txSetup.sampleRate=0; txSetup.codec = 0; } audio = new udpAudio(localIP, radioIP, audioPort, audioLocalPort, rxSetup, txSetup); @@ -545,8 +545,8 @@ void udpHandler::sendRequestStream() } p.rxcodec = rxSetup.codec; memcpy(&p.username, usernameEncoded.constData(), usernameEncoded.length()); - p.rxsample = qToBigEndian((quint32)rxSetup.format.sampleRate()); - p.txsample = qToBigEndian((quint32)txSetup.format.sampleRate()); + p.rxsample = qToBigEndian((quint32)rxSetup.sampleRate); + p.txsample = qToBigEndian((quint32)txSetup.sampleRate); p.civport = qToBigEndian((quint32)civLocalPort); p.audioport = qToBigEndian((quint32)audioLocalPort); p.txbuffer = qToBigEndian((quint32)txSetup.latency); @@ -897,7 +897,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint this->port = audioPort; this->radioIP = ip; - if (txSetup.format.sampleRate() == 0) { + if (txSetup.sampleRate == 0) { enableTx = false; } @@ -922,9 +922,6 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16,bool))); connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); - txSetup.format.setChannelCount(1); // TX Audio is always single channel. - - sendControl(false, 0x03, 0x00); // First connect packet diff --git a/udpserver.cpp b/udpserver.cpp index b0e2078..74ed8e4 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -360,7 +360,7 @@ void udpServer::controlReceived() if ((!memcmp(radio->guid, current->guid, GUIDLEN) || config->rigs.size()==1) && radio->txaudio == Q_NULLPTR && !config->lan) { radio->txAudioSetup.codec = current->txCodec; - radio->txAudioSetup.format.setSampleRate(current->txSampleRate); + radio->txAudioSetup.sampleRate=current->txSampleRate; radio->txAudioSetup.isinput = false; radio->txAudioSetup.latency = current->txBufferLen; @@ -403,7 +403,7 @@ void udpServer::controlReceived() #endif radio->rxAudioSetup.codec = current->rxCodec; - radio->rxAudioSetup.format.setSampleRate(current->rxSampleRate); + radio->rxAudioSetup.sampleRate=current->rxSampleRate; radio->rxAudioSetup.latency = current->txBufferLen; radio->rxAudioSetup.isinput = true; memcpy(radio->rxAudioSetup.guid, radio->guid, GUIDLEN); diff --git a/wfmain.cpp b/wfmain.cpp index d2f4468..26b86a0 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1490,11 +1490,11 @@ void wfmain::loadSettings() ui->txLatencySlider->setTracking(false); // Stop it sending value on every change. ui->audioSampleRateCombo->blockSignals(true); - rxSetup.format.setSampleRate(settings->value("AudioRXSampleRate", "48000").toInt()); - txSetup.format.setSampleRate(rxSetup.format.sampleRate()); + rxSetup.sampleRate=settings->value("AudioRXSampleRate", "48000").toInt(); + txSetup.sampleRate=rxSetup.sampleRate; ui->audioSampleRateCombo->setEnabled(ui->lanEnableBtn->isChecked()); - int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(rxSetup.format.sampleRate())); + int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(rxSetup.sampleRate)); if (audioSampleRateIndex != -1) { ui->audioSampleRateCombo->setCurrentIndex(audioSampleRateIndex); } @@ -1893,9 +1893,9 @@ void wfmain::saveSettings() settings->setValue("Password", udpPrefs.password); settings->setValue("AudioRXLatency", rxSetup.latency); settings->setValue("AudioTXLatency", txSetup.latency); - settings->setValue("AudioRXSampleRate", rxSetup.format.sampleRate()); + settings->setValue("AudioRXSampleRate", rxSetup.sampleRate); settings->setValue("AudioRXCodec", rxSetup.codec); - settings->setValue("AudioTXSampleRate", txSetup.format.sampleRate()); + settings->setValue("AudioTXSampleRate", txSetup.sampleRate); settings->setValue("AudioTXCodec", txSetup.codec); settings->setValue("AudioOutput", rxSetup.name); settings->setValue("AudioInput", txSetup.name); @@ -4642,8 +4642,8 @@ void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text) { //udpPrefs.audioRXSampleRate = text.toInt(); //udpPrefs.audioTXSampleRate = text.toInt(); - rxSetup.format.setSampleRate(text.toInt()); - txSetup.format.setSampleRate(text.toInt()); + rxSetup.sampleRate=text.toInt(); + txSetup.sampleRate=text.toInt(); } void wfmain::on_audioRXCodecCombo_currentIndexChanged(int value)