diff --git a/cmake/Buildportaudio-2.0.cmake b/cmake/Buildportaudio-2.0.cmake index 788d7806..bc5e615e 100644 --- a/cmake/Buildportaudio-2.0.cmake +++ b/cmake/Buildportaudio-2.0.cmake @@ -17,7 +17,7 @@ if(NOT portaudio_POPULATED) list(APPEND FREEDV_PACKAGE_SEARCH_PATHS ${portaudio_BINARY_DIR}) endif() -list(APPEND FREEDV_LINK_LIBS PortAudio) -list(APPEND FREEDV_STATIC_DEPS PortAudio) +list(APPEND FREEDV_LINK_LIBS portaudio) +list(APPEND FREEDV_STATIC_DEPS portaudio) include_directories(${portaudio_SOURCE_DIR}/include) diff --git a/src/main.cpp b/src/main.cpp index bb21a5a9..274db86f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -191,6 +191,10 @@ wxConfigBase *pConfig = NULL; // Unit test management wxString testName; wxString utFreeDVMode; +wxString utTxFile; +wxString utRxFile; +wxString utTxFeatureFile; +wxString utRxFeatureFile; // WxWidgets - initialize the application @@ -306,8 +310,26 @@ void MainApp::UnitTest_() delete txEvent; }); - // Transmit for 60 seconds - std::this_thread::sleep_for(60s); + if (utTxFile != "") + { + // Transmit until file has finished playing + SF_INFO sfInfo; + sfInfo.format = 0; + g_sfPlayFile = sf_open((const char*)utTxFile.ToUTF8(), SFM_READ, &sfInfo); + g_sfTxFs = sfInfo.samplerate; + g_loopPlayFileToMicIn = false; + g_playFileToMicIn = true; + + while (g_playFileToMicIn) + { + std::this_thread::sleep_for(20ms); + } + } + else + { + // Transmit for 60 seconds + std::this_thread::sleep_for(60s); + } // Stop transmitting log_info("Firing PTT"); @@ -323,22 +345,40 @@ void MainApp::UnitTest_() sim.MouseClick();*/ // Wait 5 seconds for FreeDV to stop - std::this_thread::sleep_for(5s); + //std::this_thread::sleep_for(5s); } else { - // Receive for 60 seconds - auto sync = 0; - for (int i = 0; i < 60*10; i++) + if (utRxFile != "") { - std::this_thread::sleep_for(100ms); - auto newSync = freedvInterface.getSync(); - if (newSync != sync) + // Receive until file has finished playing + SF_INFO sfInfo; + sfInfo.format = 0; + g_sfPlayFileFromRadio = sf_open((const char*)utRxFile.ToUTF8(), SFM_READ, &sfInfo); + g_sfFs = sfInfo.samplerate; + g_loopPlayFileFromRadio = false; + g_playFileFromRadio = true; + + while (g_playFileFromRadio) { - log_info("Sync changed from %d to %d", sync, newSync); - sync = newSync; + std::this_thread::sleep_for(20ms); } - } + } + else + { + // Receive for 60 seconds + auto sync = 0; + for (int i = 0; i < 60*10; i++) + { + std::this_thread::sleep_for(100ms); + auto newSync = freedvInterface.getSync(); + if (newSync != sync) + { + log_info("Sync changed from %d to %d", sync, newSync); + sync = newSync; + } + } + } } // Fire event to stop FreeDV @@ -369,6 +409,10 @@ void MainApp::OnInitCmdLine(wxCmdLineParser& parser) parser.AddOption("f", "config", "Use different configuration file instead of the default."); parser.AddOption("ut", "unit_test", "Execute FreeDV in unit test mode."); parser.AddOption("utmode", wxEmptyString, "Switch FreeDV to the given mode before UT execution."); + parser.AddOption("rxfile", wxEmptyString, "In UT mode, pipes given WAV file through receive pipeline."); + parser.AddOption("txfile", wxEmptyString, "In UT mode, pipes given WAV file through transmit pipeline."); + parser.AddOption("rxfeaturefile", wxEmptyString, "Capture RX features from RADE decoder into the provided file."); + parser.AddOption("txfeaturefile", wxEmptyString, "Capture TX features from FARGAN encoder into the provided file."); } bool MainApp::OnCmdLineParsed(wxCmdLineParser& parser) @@ -403,6 +447,26 @@ bool MainApp::OnCmdLineParsed(wxCmdLineParser& parser) { log_info("Using mode %s for tests", (const char*)utFreeDVMode.ToUTF8()); } + + if (parser.Found("rxfile", &utRxFile)) + { + log_info("Piping %s through RX pipeline", (const char*)utRxFile.ToUTF8()); + } + + if (parser.Found("txfile", &utTxFile)) + { + log_info("Piping %s through TX pipeline", (const char*)utTxFile.ToUTF8()); + } + } + + if (parser.Found("rxfeaturefile", &utRxFeatureFile)) + { + log_info("Capturing RADE RX features into file %s", (const char*)utRxFeatureFile.ToUTF8()); + } + + if (parser.Found("txfeaturefile", &utTxFeatureFile)) + { + log_info("Capturing RADE TX features into file %s", (const char*)utTxFeatureFile.ToUTF8()); } return true; diff --git a/src/pipeline/RADEReceiveStep.cpp b/src/pipeline/RADEReceiveStep.cpp index c3a68cb7..0ce4ed1d 100644 --- a/src/pipeline/RADEReceiveStep.cpp +++ b/src/pipeline/RADEReceiveStep.cpp @@ -25,11 +25,14 @@ #include "../defines.h" #include "lpcnet.h" // from Opus source tree +extern wxString utRxFeatureFile; + RADEReceiveStep::RADEReceiveStep(struct rade* dv, FARGANState* fargan) : dv_(dv) , fargan_(fargan) , inputSampleFifo_(nullptr) , outputSampleFifo_(nullptr) + , featuresFile_(nullptr) { // Set FIFO to be 2x the number of samples per run so we don't lose anything. inputSampleFifo_ = codec2_fifo_create(rade_nin_max(dv_) * 2); @@ -38,10 +41,21 @@ RADEReceiveStep::RADEReceiveStep(struct rade* dv, FARGANState* fargan) // Enough for one second of audio. Probably way overkill. outputSampleFifo_ = codec2_fifo_create(16000); assert(outputSampleFifo_ != nullptr); + + if (utRxFeatureFile != "") + { + featuresFile_ = fopen((const char*)utRxFeatureFile.ToUTF8(), "wb"); + assert(featuresFile_ != nullptr); + } } RADEReceiveStep::~RADEReceiveStep() { + if (featuresFile_ != nullptr) + { + fclose(featuresFile_); + } + if (inputSampleFifo_ != nullptr) { codec2_fifo_free(inputSampleFifo_); @@ -93,6 +107,11 @@ std::shared_ptr RADEReceiveStep::execute(std::shared_ptr inputSamp // RADE processing (input signal->features). nout = rade_rx(dv_, features_out, input_buf_cplx); + if (featuresFile_) + { + fwrite(features_out, sizeof(float), nout, featuresFile_); + } + for (int i = 0; i < nout; i++) { pendingFeatures_.push_back(features_out[i]); diff --git a/src/pipeline/RADEReceiveStep.h b/src/pipeline/RADEReceiveStep.h index ae601d90..f63f06f2 100644 --- a/src/pipeline/RADEReceiveStep.h +++ b/src/pipeline/RADEReceiveStep.h @@ -23,6 +23,7 @@ #ifndef AUDIO_PIPELINE__RADE_RECEIVE_STEP_H #define AUDIO_PIPELINE__RADE_RECEIVE_STEP_H +#include #include #include "IPipelineStep.h" #include "../freedv_interface.h" @@ -53,6 +54,8 @@ private: struct FIFO* inputSampleFifo_; struct FIFO* outputSampleFifo_; std::vector pendingFeatures_; + + FILE* featuresFile_; }; #endif // AUDIO_PIPELINE__RADE_RECEIVE_STEP_H diff --git a/src/pipeline/RADETransmitStep.cpp b/src/pipeline/RADETransmitStep.cpp index 20ff2b45..8e53833a 100644 --- a/src/pipeline/RADETransmitStep.cpp +++ b/src/pipeline/RADETransmitStep.cpp @@ -23,23 +23,38 @@ #include #include #include +#include "../defines.h" #include "codec2_fifo.h" #include "RADETransmitStep.h" +extern wxString utTxFeatureFile; + RADETransmitStep::RADETransmitStep(struct rade* dv, LPCNetEncState* encState) : dv_(dv) , encState_(encState) , inputSampleFifo_(nullptr) , outputSampleFifo_(nullptr) + , featuresFile_(nullptr) { inputSampleFifo_ = codec2_fifo_create(RADE_SPEECH_SAMPLE_RATE); assert(inputSampleFifo_ != nullptr); outputSampleFifo_ = codec2_fifo_create(RADE_MODEM_SAMPLE_RATE); assert(outputSampleFifo_ != nullptr); + + if (utTxFeatureFile != "") + { + featuresFile_ = fopen((const char*)utTxFeatureFile.ToUTF8(), "wb"); + assert(featuresFile_ != nullptr); + } } RADETransmitStep::~RADETransmitStep() { + if (featuresFile_ != nullptr) + { + fclose(featuresFile_); + } + if (inputSampleFifo_ != nullptr) { codec2_fifo_free(inputSampleFifo_); @@ -86,6 +101,12 @@ std::shared_ptr RADETransmitStep::execute(std::shared_ptr inputSam // Feature extraction codec2_fifo_read(inputSampleFifo_, pcm, LPCNET_FRAME_SIZE); lpcnet_compute_single_frame_features(encState_, pcm, features, arch); + + if (featuresFile_) + { + fwrite(features, sizeof(float), NB_TOTAL_FEATURES, featuresFile_); + } + for (int index = 0; index < NB_TOTAL_FEATURES; index++) { featureList_.push_back(features[index]); @@ -102,7 +123,7 @@ std::shared_ptr RADETransmitStep::execute(std::shared_ptr inputSam for (int index = 0; index < numOutputSamples; index++) { // We only need the real component for TX. - radeOutShort[index] = radeOut[index].real * 32767; + radeOutShort[index] = radeOut[index].real * 16383; } codec2_fifo_write(outputSampleFifo_, radeOutShort, numOutputSamples); } diff --git a/src/pipeline/RADETransmitStep.h b/src/pipeline/RADETransmitStep.h index 49bf6e11..941f41a7 100644 --- a/src/pipeline/RADETransmitStep.h +++ b/src/pipeline/RADETransmitStep.h @@ -23,6 +23,7 @@ #ifndef AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H #define AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H +#include #include #include "IPipelineStep.h" #include "../freedv_interface.h" @@ -48,6 +49,8 @@ private: struct FIFO* inputSampleFifo_; struct FIFO* outputSampleFifo_; std::vector featureList_; + + FILE* featuresFile_; }; #endif // AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H diff --git a/src/pipeline/ResampleStep.cpp b/src/pipeline/ResampleStep.cpp index 315d8e75..9485dadc 100644 --- a/src/pipeline/ResampleStep.cpp +++ b/src/pipeline/ResampleStep.cpp @@ -73,7 +73,7 @@ ResampleStep::ResampleStep(int inputSampleRate, int outputSampleRate) , outputSampleRate_(outputSampleRate) { int src_error; - resampleState_ = src_new(SRC_SINC_FASTEST, 1, &src_error); + resampleState_ = src_new(SRC_SINC_MEDIUM_QUALITY, 1, &src_error); assert(resampleState_ != nullptr); } diff --git a/src/pipeline/TxRxThread.cpp b/src/pipeline/TxRxThread.cpp index ac29f29d..ef9f0320 100644 --- a/src/pipeline/TxRxThread.cpp +++ b/src/pipeline/TxRxThread.cpp @@ -268,11 +268,13 @@ void TxRxThread::initializePipeline_() auto txAttenuationStep = new LevelAdjustStep(outputSampleRate_, []() { double dbLoss = g_txLevel / 10.0; +#if 0 if (freedvInterface.getTxMode() == FREEDV_MODE_RADE) { // Attenuate by 4 dB as there's no BPF; anything louder distorts the signal dbLoss -= 4.0; } +#endif // 0 double scaleFactor = exp(dbLoss/20.0 * log(10.0)); return scaleFactor;