Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable capture of RX features from RADE decoder #776

Merged
merged 27 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1fa572e
Initial implementation of RX feature capture.
tmiw Nov 25, 2024
169a673
Allow path to feature file to be specified at the command line.
tmiw Nov 25, 2024
747ce63
Fix compilation error after upgrading MacPorts.
tmiw Nov 27, 2024
d3cd11a
Add command line option for TX feature capture.
tmiw Nov 27, 2024
60bb5e1
Add -txfile command line argument to feed in WAV file through TX pipe…
tmiw Nov 28, 2024
2c486ee
Adjust scaling to match PR example.
tmiw Nov 28, 2024
6040683
Opt for improved resampling audio quality.
tmiw Nov 28, 2024
ce8c1ef
We don't actually need to add additional attenuation anymore.
tmiw Nov 28, 2024
4aba48f
Switch over to soxr for further experimentation.
tmiw Nov 29, 2024
560ec7c
Forgot change to have Windows build work.
tmiw Nov 30, 2024
8d5648a
Update Linux build instructions.
tmiw Nov 30, 2024
b5f436e
Fix additional compiler error.
tmiw Nov 30, 2024
16bfed4
Update paCallbackData.h
tmiw Nov 30, 2024
22c2586
Remove missed code that's no longer needed.
tmiw Nov 30, 2024
c245394
Update main.cpp
tmiw Nov 30, 2024
4970530
Try to reduce latency.
tmiw Nov 30, 2024
dea68ad
Another experiment to decrease latency.
tmiw Dec 1, 2024
0d9fce6
Go back to default settings.
tmiw Dec 1, 2024
5921369
Fix failing ctests.
tmiw Dec 1, 2024
a13103a
Fix Windows packaging failures.
tmiw Dec 1, 2024
22a49fe
Disable ctests for soxr.
tmiw Dec 1, 2024
dc37633
Enable SIMD for aarch64.
tmiw Dec 2, 2024
4c20eba
Smooth out gaps in audio caused by how soxr works.
tmiw Dec 3, 2024
04504b7
Fix build errors.
tmiw Dec 3, 2024
a390463
Ensure we're flushing out our output FIFO if we stop receiving input.
tmiw Dec 3, 2024
7b2e79d
ctests should now be fixed.
tmiw Dec 4, 2024
f1a66f0
Revert all samplerate changes. These will go in another PR.
tmiw Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmake/Buildportaudio-2.0.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
88 changes: 76 additions & 12 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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");
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
19 changes: 19 additions & 0 deletions src/pipeline/RADEReceiveStep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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_);
Expand Down Expand Up @@ -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]);
Expand Down
3 changes: 3 additions & 0 deletions src/pipeline/RADEReceiveStep.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -53,6 +54,8 @@ class RADEReceiveStep : public IPipelineStep
struct FIFO* inputSampleFifo_;
struct FIFO* outputSampleFifo_;
std::vector<float> pendingFeatures_;

FILE* featuresFile_;
};

#endif // AUDIO_PIPELINE__RADE_RECEIVE_STEP_H
23 changes: 22 additions & 1 deletion src/pipeline/RADETransmitStep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_);
Expand Down Expand Up @@ -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]);
Expand All @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions src/pipeline/RADETransmitStep.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -48,6 +49,8 @@ class RADETransmitStep : public IPipelineStep
struct FIFO* inputSampleFifo_;
struct FIFO* outputSampleFifo_;
std::vector<float> featureList_;

FILE* featuresFile_;
};

#endif // AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H
2 changes: 1 addition & 1 deletion src/pipeline/ResampleStep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
2 changes: 2 additions & 0 deletions src/pipeline/TxRxThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The level of the RADE signal should be set at the float to int conversion stage (correctly done above ☝️), not tweaked later in the Tx signal processing. This code should be rm-ed, not just if-deffed out.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in 2c3f6a2.

double scaleFactor = exp(dbLoss/20.0 * log(10.0));
return scaleFactor;
Expand Down
Loading