Skip to content

Commit ce76e7f

Browse files
authored
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.
1 parent 4c9c2c1 commit ce76e7f

8 files changed

+128
-16
lines changed

cmake/Buildportaudio-2.0.cmake

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ if(NOT portaudio_POPULATED)
1717
list(APPEND FREEDV_PACKAGE_SEARCH_PATHS ${portaudio_BINARY_DIR})
1818
endif()
1919

20-
list(APPEND FREEDV_LINK_LIBS PortAudio)
21-
list(APPEND FREEDV_STATIC_DEPS PortAudio)
20+
list(APPEND FREEDV_LINK_LIBS portaudio)
21+
list(APPEND FREEDV_STATIC_DEPS portaudio)
2222

2323
include_directories(${portaudio_SOURCE_DIR}/include)

src/main.cpp

+76-12
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ wxConfigBase *pConfig = NULL;
191191
// Unit test management
192192
wxString testName;
193193
wxString utFreeDVMode;
194+
wxString utTxFile;
195+
wxString utRxFile;
196+
wxString utTxFeatureFile;
197+
wxString utRxFeatureFile;
194198

195199
// WxWidgets - initialize the application
196200

@@ -306,8 +310,26 @@ void MainApp::UnitTest_()
306310
delete txEvent;
307311
});
308312

309-
// Transmit for 60 seconds
310-
std::this_thread::sleep_for(60s);
313+
if (utTxFile != "")
314+
{
315+
// Transmit until file has finished playing
316+
SF_INFO sfInfo;
317+
sfInfo.format = 0;
318+
g_sfPlayFile = sf_open((const char*)utTxFile.ToUTF8(), SFM_READ, &sfInfo);
319+
g_sfTxFs = sfInfo.samplerate;
320+
g_loopPlayFileToMicIn = false;
321+
g_playFileToMicIn = true;
322+
323+
while (g_playFileToMicIn)
324+
{
325+
std::this_thread::sleep_for(20ms);
326+
}
327+
}
328+
else
329+
{
330+
// Transmit for 60 seconds
331+
std::this_thread::sleep_for(60s);
332+
}
311333

312334
// Stop transmitting
313335
log_info("Firing PTT");
@@ -323,22 +345,40 @@ void MainApp::UnitTest_()
323345
sim.MouseClick();*/
324346

325347
// Wait 5 seconds for FreeDV to stop
326-
std::this_thread::sleep_for(5s);
348+
//std::this_thread::sleep_for(5s);
327349
}
328350
else
329351
{
330-
// Receive for 60 seconds
331-
auto sync = 0;
332-
for (int i = 0; i < 60*10; i++)
352+
if (utRxFile != "")
333353
{
334-
std::this_thread::sleep_for(100ms);
335-
auto newSync = freedvInterface.getSync();
336-
if (newSync != sync)
354+
// Receive until file has finished playing
355+
SF_INFO sfInfo;
356+
sfInfo.format = 0;
357+
g_sfPlayFileFromRadio = sf_open((const char*)utRxFile.ToUTF8(), SFM_READ, &sfInfo);
358+
g_sfFs = sfInfo.samplerate;
359+
g_loopPlayFileFromRadio = false;
360+
g_playFileFromRadio = true;
361+
362+
while (g_playFileFromRadio)
337363
{
338-
log_info("Sync changed from %d to %d", sync, newSync);
339-
sync = newSync;
364+
std::this_thread::sleep_for(20ms);
340365
}
341-
}
366+
}
367+
else
368+
{
369+
// Receive for 60 seconds
370+
auto sync = 0;
371+
for (int i = 0; i < 60*10; i++)
372+
{
373+
std::this_thread::sleep_for(100ms);
374+
auto newSync = freedvInterface.getSync();
375+
if (newSync != sync)
376+
{
377+
log_info("Sync changed from %d to %d", sync, newSync);
378+
sync = newSync;
379+
}
380+
}
381+
}
342382
}
343383

344384
// Fire event to stop FreeDV
@@ -369,6 +409,10 @@ void MainApp::OnInitCmdLine(wxCmdLineParser& parser)
369409
parser.AddOption("f", "config", "Use different configuration file instead of the default.");
370410
parser.AddOption("ut", "unit_test", "Execute FreeDV in unit test mode.");
371411
parser.AddOption("utmode", wxEmptyString, "Switch FreeDV to the given mode before UT execution.");
412+
parser.AddOption("rxfile", wxEmptyString, "In UT mode, pipes given WAV file through receive pipeline.");
413+
parser.AddOption("txfile", wxEmptyString, "In UT mode, pipes given WAV file through transmit pipeline.");
414+
parser.AddOption("rxfeaturefile", wxEmptyString, "Capture RX features from RADE decoder into the provided file.");
415+
parser.AddOption("txfeaturefile", wxEmptyString, "Capture TX features from FARGAN encoder into the provided file.");
372416
}
373417

374418
bool MainApp::OnCmdLineParsed(wxCmdLineParser& parser)
@@ -403,6 +447,26 @@ bool MainApp::OnCmdLineParsed(wxCmdLineParser& parser)
403447
{
404448
log_info("Using mode %s for tests", (const char*)utFreeDVMode.ToUTF8());
405449
}
450+
451+
if (parser.Found("rxfile", &utRxFile))
452+
{
453+
log_info("Piping %s through RX pipeline", (const char*)utRxFile.ToUTF8());
454+
}
455+
456+
if (parser.Found("txfile", &utTxFile))
457+
{
458+
log_info("Piping %s through TX pipeline", (const char*)utTxFile.ToUTF8());
459+
}
460+
}
461+
462+
if (parser.Found("rxfeaturefile", &utRxFeatureFile))
463+
{
464+
log_info("Capturing RADE RX features into file %s", (const char*)utRxFeatureFile.ToUTF8());
465+
}
466+
467+
if (parser.Found("txfeaturefile", &utTxFeatureFile))
468+
{
469+
log_info("Capturing RADE TX features into file %s", (const char*)utTxFeatureFile.ToUTF8());
406470
}
407471

408472
return true;

src/pipeline/RADEReceiveStep.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@
2525
#include "../defines.h"
2626
#include "lpcnet.h" // from Opus source tree
2727

28+
extern wxString utRxFeatureFile;
29+
2830
RADEReceiveStep::RADEReceiveStep(struct rade* dv, FARGANState* fargan)
2931
: dv_(dv)
3032
, fargan_(fargan)
3133
, inputSampleFifo_(nullptr)
3234
, outputSampleFifo_(nullptr)
35+
, featuresFile_(nullptr)
3336
{
3437
// Set FIFO to be 2x the number of samples per run so we don't lose anything.
3538
inputSampleFifo_ = codec2_fifo_create(rade_nin_max(dv_) * 2);
@@ -38,10 +41,21 @@ RADEReceiveStep::RADEReceiveStep(struct rade* dv, FARGANState* fargan)
3841
// Enough for one second of audio. Probably way overkill.
3942
outputSampleFifo_ = codec2_fifo_create(16000);
4043
assert(outputSampleFifo_ != nullptr);
44+
45+
if (utRxFeatureFile != "")
46+
{
47+
featuresFile_ = fopen((const char*)utRxFeatureFile.ToUTF8(), "wb");
48+
assert(featuresFile_ != nullptr);
49+
}
4150
}
4251

4352
RADEReceiveStep::~RADEReceiveStep()
4453
{
54+
if (featuresFile_ != nullptr)
55+
{
56+
fclose(featuresFile_);
57+
}
58+
4559
if (inputSampleFifo_ != nullptr)
4660
{
4761
codec2_fifo_free(inputSampleFifo_);
@@ -93,6 +107,11 @@ std::shared_ptr<short> RADEReceiveStep::execute(std::shared_ptr<short> inputSamp
93107

94108
// RADE processing (input signal->features).
95109
nout = rade_rx(dv_, features_out, input_buf_cplx);
110+
if (featuresFile_)
111+
{
112+
fwrite(features_out, sizeof(float), nout, featuresFile_);
113+
}
114+
96115
for (int i = 0; i < nout; i++)
97116
{
98117
pendingFeatures_.push_back(features_out[i]);

src/pipeline/RADEReceiveStep.h

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#ifndef AUDIO_PIPELINE__RADE_RECEIVE_STEP_H
2424
#define AUDIO_PIPELINE__RADE_RECEIVE_STEP_H
2525

26+
#include <cstdio>
2627
#include <vector>
2728
#include "IPipelineStep.h"
2829
#include "../freedv_interface.h"
@@ -53,6 +54,8 @@ class RADEReceiveStep : public IPipelineStep
5354
struct FIFO* inputSampleFifo_;
5455
struct FIFO* outputSampleFifo_;
5556
std::vector<float> pendingFeatures_;
57+
58+
FILE* featuresFile_;
5659
};
5760

5861
#endif // AUDIO_PIPELINE__RADE_RECEIVE_STEP_H

src/pipeline/RADETransmitStep.cpp

+22-1
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,38 @@
2323
#include <cstring>
2424
#include <cassert>
2525
#include <cmath>
26+
#include "../defines.h"
2627
#include "codec2_fifo.h"
2728
#include "RADETransmitStep.h"
2829

30+
extern wxString utTxFeatureFile;
31+
2932
RADETransmitStep::RADETransmitStep(struct rade* dv, LPCNetEncState* encState)
3033
: dv_(dv)
3134
, encState_(encState)
3235
, inputSampleFifo_(nullptr)
3336
, outputSampleFifo_(nullptr)
37+
, featuresFile_(nullptr)
3438
{
3539
inputSampleFifo_ = codec2_fifo_create(RADE_SPEECH_SAMPLE_RATE);
3640
assert(inputSampleFifo_ != nullptr);
3741
outputSampleFifo_ = codec2_fifo_create(RADE_MODEM_SAMPLE_RATE);
3842
assert(outputSampleFifo_ != nullptr);
43+
44+
if (utTxFeatureFile != "")
45+
{
46+
featuresFile_ = fopen((const char*)utTxFeatureFile.ToUTF8(), "wb");
47+
assert(featuresFile_ != nullptr);
48+
}
3949
}
4050

4151
RADETransmitStep::~RADETransmitStep()
4252
{
53+
if (featuresFile_ != nullptr)
54+
{
55+
fclose(featuresFile_);
56+
}
57+
4358
if (inputSampleFifo_ != nullptr)
4459
{
4560
codec2_fifo_free(inputSampleFifo_);
@@ -86,6 +101,12 @@ std::shared_ptr<short> RADETransmitStep::execute(std::shared_ptr<short> inputSam
86101
// Feature extraction
87102
codec2_fifo_read(inputSampleFifo_, pcm, LPCNET_FRAME_SIZE);
88103
lpcnet_compute_single_frame_features(encState_, pcm, features, arch);
104+
105+
if (featuresFile_)
106+
{
107+
fwrite(features, sizeof(float), NB_TOTAL_FEATURES, featuresFile_);
108+
}
109+
89110
for (int index = 0; index < NB_TOTAL_FEATURES; index++)
90111
{
91112
featureList_.push_back(features[index]);
@@ -102,7 +123,7 @@ std::shared_ptr<short> RADETransmitStep::execute(std::shared_ptr<short> inputSam
102123
for (int index = 0; index < numOutputSamples; index++)
103124
{
104125
// We only need the real component for TX.
105-
radeOutShort[index] = radeOut[index].real * 32767;
126+
radeOutShort[index] = radeOut[index].real * 16383;
106127
}
107128
codec2_fifo_write(outputSampleFifo_, radeOutShort, numOutputSamples);
108129
}

src/pipeline/RADETransmitStep.h

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#ifndef AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H
2424
#define AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H
2525

26+
#include <cstdio>
2627
#include <vector>
2728
#include "IPipelineStep.h"
2829
#include "../freedv_interface.h"
@@ -48,6 +49,8 @@ class RADETransmitStep : public IPipelineStep
4849
struct FIFO* inputSampleFifo_;
4950
struct FIFO* outputSampleFifo_;
5051
std::vector<float> featureList_;
52+
53+
FILE* featuresFile_;
5154
};
5255

5356
#endif // AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H

src/pipeline/ResampleStep.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ ResampleStep::ResampleStep(int inputSampleRate, int outputSampleRate)
7373
, outputSampleRate_(outputSampleRate)
7474
{
7575
int src_error;
76-
resampleState_ = src_new(SRC_SINC_FASTEST, 1, &src_error);
76+
resampleState_ = src_new(SRC_SINC_MEDIUM_QUALITY, 1, &src_error);
7777
assert(resampleState_ != nullptr);
7878
}
7979

src/pipeline/TxRxThread.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,13 @@ void TxRxThread::initializePipeline_()
268268
auto txAttenuationStep = new LevelAdjustStep(outputSampleRate_, []() {
269269
double dbLoss = g_txLevel / 10.0;
270270

271+
#if 0
271272
if (freedvInterface.getTxMode() == FREEDV_MODE_RADE)
272273
{
273274
// Attenuate by 4 dB as there's no BPF; anything louder distorts the signal
274275
dbLoss -= 4.0;
275276
}
277+
#endif // 0
276278

277279
double scaleFactor = exp(dbLoss/20.0 * log(10.0));
278280
return scaleFactor;

0 commit comments

Comments
 (0)