From 869659ad54c0cbddfa888bf18e09a3d137c88040 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 16 Jun 2021 09:49:38 +0100 Subject: [PATCH] Add opus encoding/decoding --- audiohandler.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- audiohandler.h | 3 +++ wfmain.cpp | 3 +++ wfview.pro | 8 ++++-- wfview.vcxproj | 20 +++++++-------- 5 files changed, 87 insertions(+), 14 deletions(-) diff --git a/audiohandler.cpp b/audiohandler.cpp index f051830..ccee899 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -40,7 +40,12 @@ audioHandler::~audioHandler() speex_resampler_destroy(resampler); qDebug(logAudio()) << "Resampler closed"; } - + if (encoder != Q_NULLPTR) { + opus_encoder_destroy(encoder); + } + if (decoder != Q_NULLPTR) { + opus_decoder_destroy(decoder); + } } bool audioHandler::init(audioSetup setupIn) @@ -71,6 +76,14 @@ bool audioHandler::init(audioSetup setupIn) if (setup.codec == 0x04 || setup.codec == 0x10) { setup.bits = 16; } + if (setup.codec == 0x40 || setup.codec == 0x80) + { + setup.bits = 16; + } + if (setup.codec == 0x80) + { + setup.radioChan = 2; + } ringBuf = new wilt::Ring(100); // Should be customizable. @@ -264,6 +277,8 @@ void audioHandler::start() return; } + int err = 0; + if (setup.isinput) { #ifdef Q_OS_MACX this->open(QIODevice::WriteOnly); @@ -271,6 +286,10 @@ void audioHandler::start() this->open(QIODevice::WriteOnly | QIODevice::Unbuffered); #endif audioInput->start(this); + if (setup.codec == 0x40 || setup.codec == 0x80) { + // Opus codec + encoder = opus_encoder_create(setup.samplerate, setup.radioChan, OPUS_APPLICATION_AUDIO, &err); + } } else { #ifdef Q_OS_MACX @@ -279,6 +298,14 @@ void audioHandler::start() this->open(QIODevice::ReadOnly | QIODevice::Unbuffered); #endif audioOutput->start(this); + if (setup.codec == 0x40 || setup.codec == 0x80) { + // Opus codec + decoder = opus_decoder_create(setup.samplerate, setup.radioChan, &err); + } + } + if (err < 0) + { + fprintf(stderr, "failed to create opus encoder or decoder: %s\n", opus_strerror(err)); } } #endif @@ -449,12 +476,30 @@ void audioHandler::incomingAudio(audioPacket inPacket) qDebug(logAudio()) << "Packet received when stream was not ready"; return; } + + if (setup.codec == 0x40 || setup.codec == 0x80) { + /* Encode the frame. */ + QByteArray outPacket(chunkSize * setup.radioChan * 2, (char)0xff); // Preset the output buffer size. + qint16* in = (qint16*)inPacket.data.data(); + unsigned char* out = (unsigned char*)outPacket.data(); + + int nbBytes = opus_decode(decoder, out, outPacket.length() / 2, in, inPacket.data.length(),0); + if (nbBytes < 0) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); + return; + } + outPacket.resize(nbBytes); + inPacket.data.clear(); + inPacket.data = outPacket; // Replace incoming data with converted. + } + //qDebug(logAudio()) << "Got" << radioSampleBits << "bits, length" << inPacket.data.length(); // Incoming data is 8bits? 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/setup.radioChan), (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++) { @@ -625,6 +670,24 @@ void audioHandler::getNextAudioChunk(QByteArray& ret) packet.data.clear(); packet.data = outPacket; // Copy output packet back to input buffer. } + + if (setup.codec == 0x40 || setup.codec == 0x80) { + /* Encode the frame. */ + QByteArray outPacket(packet.data.length() * 2, (char)0xff); // Preset the output buffer size. + qint16* in = (qint16*)packet.data.data(); + unsigned char* out = (unsigned char*)outPacket.data(); + + int nbBytes = opus_encode(encoder, in, packet.data.length() / 2, out, outPacket.length()); + if (nbBytes < 0) + { + qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes); + return; + } + outPacket.resize(nbBytes); + packet.data.clear(); + packet.data = outPacket; // Replace incoming data with converted. + } + ret = packet.data; //qDebug(logAudio()) << "Now radio format, length" << packet.data.length(); } diff --git a/audiohandler.h b/audiohandler.h index 36993a5..740ce38 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -33,6 +33,7 @@ typedef signed short MY_TYPE; #include #include "resampler/speex_resampler.h" #include "ring/ring.h" +#include "opus.h" #include @@ -179,6 +180,8 @@ private: qreal volume=1.0; int devChannels; audioSetup setup; + OpusEncoder* encoder=Q_NULLPTR; + OpusDecoder* decoder=Q_NULLPTR; }; #endif // AUDIOHANDLER_H diff --git a/wfmain.cpp b/wfmain.cpp index ab42e4c..d8a8c4d 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -1234,6 +1234,8 @@ void wfmain::loadSettings() ui->audioRXCodecCombo->addItem("LPCM 2ch 16bit", 16); ui->audioRXCodecCombo->addItem("uLaw 2ch 8bit", 32); ui->audioRXCodecCombo->addItem("PCM 2ch 8bit", 8); + ui->audioRXCodecCombo->addItem("Opus 1ch", 64); + ui->audioRXCodecCombo->addItem("Opus 2ch", 128); ui->audioRXCodecCombo->blockSignals(true); rxSetup.codec = settings->value("AudioRXCodec", "4").toInt(); @@ -1246,6 +1248,7 @@ void wfmain::loadSettings() ui->audioTXCodecCombo->addItem("LPCM 1ch 16bit", 4); ui->audioTXCodecCombo->addItem("LPCM 1ch 8bit", 2); ui->audioTXCodecCombo->addItem("uLaw 1ch 8bit", 1); + ui->audioTXCodecCombo->addItem("Opus 1ch", 64); ui->audioRXCodecCombo->blockSignals(true); txSetup.codec = settings->value("AudioTXCodec", "4").toInt(); diff --git a/wfview.pro b/wfview.pro index 1e0aed9..a487ae7 100644 --- a/wfview.pro +++ b/wfview.pro @@ -92,13 +92,15 @@ linux:QMAKE_POST_LINK += echo; echo; echo "Run install.sh as root from the build CONFIG(debug, release|debug) { linux: QCPLIB = qcustomplotd + win32:LIBS += -L../opus/win32/VS2015/Win32/Debug/ -lopus } else { linux: QCPLIB = qcustomplot + win32:LIBS += -L../opus/win32/VS2015/Win32/Release/ -lopus } #linux:LIBS += -L./ -l$$QCPLIB -lpulse -lpulse-simple -lpthread -linux:LIBS += -L./ -l$$QCPLIB -macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread +linux:LIBS += -L./ -l$$QCPLIB -lopus +macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread -lopus #win32:SOURCES += rtaudio/RTAudio.cpp #win32:HEADERS += rtaudio/RTAUdio.h @@ -106,6 +108,8 @@ macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread !linux:HEADERS += ../qcustomplot/qcustomplot.h !linux:INCLUDEPATH += ../qcustomplot +!linux:INCLUDEPATH += ../opus/include + INCLUDEPATH += resampler !linux:INCLUDEPATH += rtaudio diff --git a/wfview.vcxproj b/wfview.vcxproj index 2502415..dbf5fb4 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -48,7 +48,7 @@ - .;..\qcustomplot;resampler;rtaudio;release;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;resampler;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;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="ea09e1f";HOST="wfview.org";UNAME="build";QT_NO_DEBUG;NDEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="8cd64c2";HOST="wfview.org";UNAME="build";QT_NO_DEBUG;NDEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -66,8 +66,8 @@ Level3 true - shell32.lib;%(AdditionalDependencies) - C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.6.11-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + ..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies) + ..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.6.11-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 @@ -84,12 +84,12 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"ea09e1f\";HOST=\"wfview.org\";UNAME=\"build\";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) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"8cd64c2\";HOST=\"wfview.org\";UNAME=\"build\";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 - .;..\qcustomplot;resampler;rtaudio;debug;/include;%(AdditionalIncludeDirectories) + .;..\qcustomplot;..\opus\include;resampler;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 @@ -98,7 +98,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="ea09e1f";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="8cd64c2";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -106,8 +106,8 @@ Level3 true - shell32.lib;%(AdditionalDependencies) - C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.6.11-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + ..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies) + ..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.6.11-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 @@ -123,7 +123,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"ea09e1f\";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) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"8cd64c2\";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