Enable capture of RX features from RADE decoder (#776)
* Initial implementation of RX feature capture. * Allow path to feature file to be specified at the command line. * Fix compilation error after upgrading MacPorts. * Add command line option for TX feature capture. * Add -txfile command line argument to feed in WAV file through TX pipeline. * Adjust scaling to match PR example. * Opt for improved resampling audio quality. * We don't actually need to add additional attenuation anymore. * Switch over to soxr for further experimentation. * Forgot change to have Windows build work. * Update Linux build instructions. * Fix additional compiler error. * Update paCallbackData.h * Remove missed code that's no longer needed. * Update main.cpp * Try to reduce latency. * Another experiment to decrease latency. * Go back to default settings. * Fix failing ctests. * Fix Windows packaging failures. * Disable ctests for soxr. * Enable SIMD for aarch64. * Smooth out gaps in audio caused by how soxr works. * Fix build errors. * Ensure we're flushing out our output FIFO if we stop receiving input. * ctests should now be fixed. * Revert all samplerate changes. These will go in another PR.ms-suppress-rade-output
parent
4c9c2c1529
commit
ce76e7fb2d
|
@ -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)
|
||||
|
|
66
src/main.cpp
66
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;
|
||||
});
|
||||
|
||||
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,7 +345,24 @@ 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
|
||||
{
|
||||
if (utRxFile != "")
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
std::this_thread::sleep_for(20ms);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -340,6 +379,7 @@ void MainApp::UnitTest_()
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire event to stop FreeDV
|
||||
log_info("Firing stop");
|
||||
|
@ -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;
|
||||
|
|
|
@ -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<short> RADEReceiveStep::execute(std::shared_ptr<short> 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]);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#ifndef AUDIO_PIPELINE__RADE_RECEIVE_STEP_H
|
||||
#define AUDIO_PIPELINE__RADE_RECEIVE_STEP_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include "IPipelineStep.h"
|
||||
#include "../freedv_interface.h"
|
||||
|
@ -53,6 +54,8 @@ private:
|
|||
struct FIFO* inputSampleFifo_;
|
||||
struct FIFO* outputSampleFifo_;
|
||||
std::vector<float> pendingFeatures_;
|
||||
|
||||
FILE* featuresFile_;
|
||||
};
|
||||
|
||||
#endif // AUDIO_PIPELINE__RADE_RECEIVE_STEP_H
|
||||
|
|
|
@ -23,23 +23,38 @@
|
|||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#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<short> RADETransmitStep::execute(std::shared_ptr<short> 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<short> RADETransmitStep::execute(std::shared_ptr<short> 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);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#ifndef AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H
|
||||
#define AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include "IPipelineStep.h"
|
||||
#include "../freedv_interface.h"
|
||||
|
@ -48,6 +49,8 @@ private:
|
|||
struct FIFO* inputSampleFifo_;
|
||||
struct FIFO* outputSampleFifo_;
|
||||
std::vector<float> featureList_;
|
||||
|
||||
FILE* featuresFile_;
|
||||
};
|
||||
|
||||
#endif // AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue