Further real-time code cleanup (#1039)
* Extend rtsan declarations to rest of pipeline and audio classes. * Add construct for marking RT-unsafe code. * Use realtime_fp instead of std::function for stuff that needs to happen real-time. * Add realtime_fp wrapper to allow checking of lambdas in pipeline. * Add annotations to lambdas. * Add additional nonblocking assertions. * Create wrapper around wxGetApp() to suppress warnings. * mustStopWork needs to be non-blocking. * Disable thread-local variables in PulseAudioDevice. * Misc warning cleanup. * Add missed file. * Refactor EqualizerStep to not need shared_ptr. * Annotate MacAudioDevice. * Fix compiler errors. * Add additional suppressions in src/pipeline and some dependent code. * Increment version to 2.0.3 and reenable dev tag. * Remove std::function from RADEReceiveStep. * Additional codec2 annotation, missed reference to codec2_malloc in kiss_fft. * Add additional codec2 annotations. * Resolve remaining freedv_interface.cpp realtime compiler warnings. * Minor TSan based cleanup. * Use relaxed atomic semantics for GenericFIFO. * Enable ability to instrument with TSan. * Fix localtime-related data races in ulog. * Fix g_State related data races. * Fix memcpy related data races in GenericFIFO. * Remove macos-13 from CI as it's about to go away. * Add PR #1039 to changelog. * test-app-folder isn't actually failing the CI run. * Enable TSan support via build scripts. * TSan warning cleanup on macOS. * Add default virtual destructors for audio devices. * Work around IAudioDevice destruction data race. * Fix PlotWaterfall data race.ms-detect-stereo-vk
parent
7d19ea8f3d
commit
2ef5d6c671
|
|
@ -18,7 +18,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-13, macos-14, macos-15, macos-15-intel, macos-26] # x86_64, ARM64, ARM64, x86_64, ARM64
|
||||
os: [macos-14, macos-15, macos-15-intel, macos-26] # ARM64, ARM64, x86_64, ARM64
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
|
@ -51,13 +51,6 @@ jobs:
|
|||
run: BUILD_TYPE=RelWithDebInfo UT_ENABLE=1 UNIV_BUILD=0 BUILD_DEPS=0 ./build_osx.sh
|
||||
|
||||
- name: Workaround macOS permission issues
|
||||
if: ${{ matrix.os == 'macos-13' }}
|
||||
run: |
|
||||
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159);"
|
||||
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/opt/off/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159);"
|
||||
|
||||
- name: Workaround macOS permission issues
|
||||
if: ${{ matrix.os != 'macos-13' }}
|
||||
run: |
|
||||
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159,NULL,NULL,'UNUSED',1687786159);"
|
||||
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/opt/off/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159,NULL,NULL,'UNUSED',1687786159);"
|
||||
|
|
@ -122,7 +115,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-13, macos-14, macos-15, macos-15-intel, macos-26] # x86_64, ARM64, ARM64, x86_64, ARM64
|
||||
os: [macos-14, macos-15, macos-15-intel, macos-26] # ARM64, ARM64, x86_64, ARM64
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: dist
|
||||
|
|
@ -158,13 +151,6 @@ jobs:
|
|||
run: ./build_macos_sound_drivers.sh
|
||||
|
||||
- name: Workaround macOS permission issues
|
||||
if: ${{ matrix.os == 'macos-13' }}
|
||||
run: |
|
||||
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159);"
|
||||
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/opt/off/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159);"
|
||||
|
||||
- name: Workaround macOS permission issues
|
||||
if: ${{ matrix.os != 'macos-13' }}
|
||||
run: |
|
||||
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159,NULL,NULL,'UNUSED',1687786159);"
|
||||
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/opt/off/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159,NULL,NULL,'UNUSED',1687786159);"
|
||||
|
|
@ -179,4 +165,5 @@ jobs:
|
|||
- name: Sanity check RADE
|
||||
shell: bash
|
||||
run: |
|
||||
FREEDV_COMPUTER_TO_RADIO_DEVICE="VB-Cable" FREEDV_RADIO_TO_COMPUTER_DEVICE="VB-Cable" FREEDV_COMPUTER_TO_SPEAKER_DEVICE="BlackHole1 2ch" FREEDV_MICROPHONE_TO_COMPUTER_DEVICE="BlackHole2 2ch" FREEDV_BINARY=./FreeDV-dmg/FreeDV.app/Contents/MacOS/FreeDV ./test/test_zeros.sh txrx RADEV1
|
||||
FREEDV_COMPUTER_TO_RADIO_DEVICE="VB-Cable" FREEDV_RADIO_TO_COMPUTER_DEVICE="VB-Cable" FREEDV_COMPUTER_TO_SPEAKER_DEVICE="BlackHole1 2ch" FREEDV_MICROPHONE_TO_COMPUTER_DEVICE="BlackHole2 2ch" FREEDV_BINARY=./FreeDV-dmg/FreeDV.app/Contents/MacOS/FreeDV ./test/test_zeros.sh txrx RADEV1 | tee temp-test.txt
|
||||
grep "Got 1 sync changes" temp-test.txt
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ if(APPLE)
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
endif(APPLE)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
|
|
@ -144,6 +144,8 @@ set(ENABLE_ASAN FALSE CACHE BOOL
|
|||
"Instruments the build with AddressSanitizer for memory checks.")
|
||||
set(ENABLE_RTSAN FALSE CACHE BOOL
|
||||
"Instruments the build with RealtimeSanitizer for RT safety checks.")
|
||||
set(ENABLE_TSAN FALSE CACHE BOOL
|
||||
"Instruments the build with ThreadSanitizer for thread checks.")
|
||||
|
||||
if(ENABLE_ASAN)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||
|
|
@ -157,6 +159,12 @@ if(ENABLE_RTSAN)
|
|||
set(CMAKE_OBJCXX_FLAGS "${CMAKE_OBJCXX_FLAGS} -fsanitize=realtime")
|
||||
endif(ENABLE_RTSAN)
|
||||
|
||||
if(ENABLE_TSAN)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
|
||||
set(CMAKE_OBJCXX_FLAGS "${CMAKE_OBJCXX_FLAGS} -fsanitize=thread")
|
||||
endif(ENABLE_TSAN)
|
||||
|
||||
if(SIGN_WINDOWS_BINARIES)
|
||||
if(NOT WIN32 AND NOT MINGW)
|
||||
message(FATAL_ERROR "Signing only makes sense for Windows builds.")
|
||||
|
|
@ -559,9 +567,9 @@ else()
|
|||
endif()
|
||||
|
||||
# On Linux/macOS, build mimalloc for a bit more performance
|
||||
if ((LINUX OR APPLE) AND (NOT ENABLE_ASAN AND NOT ENABLE_RTSAN))
|
||||
if ((LINUX OR APPLE) AND (NOT ENABLE_TSAN AND NOT ENABLE_ASAN AND NOT ENABLE_RTSAN))
|
||||
include(cmake/BuildMimalloc.cmake)
|
||||
endif ((LINUX OR APPLE) AND (NOT ENABLE_ASAN AND NOT ENABLE_RTSAN))
|
||||
endif ((LINUX OR APPLE) AND (NOT ENABLE_TSAN AND NOT ENABLE_ASAN AND NOT ENABLE_RTSAN))
|
||||
|
||||
#
|
||||
# Find libdl for dlopen/dlclose
|
||||
|
|
|
|||
|
|
@ -848,6 +848,8 @@ LDPC | Low Density Parity Check Codes - a family of powerful FEC codes
|
|||
|
||||
1. Bugfixes:
|
||||
* Clean up wxWidgets warnings in Audio Config window. (PR #1044)
|
||||
2. Enhancements:
|
||||
* Additional cleanup of code that runs in real-time. (PR #1039)
|
||||
|
||||
## V2.0.2 October 2025
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ USE_NATIVE_AUDIO=${USE_NATIVE_AUDIO:-1}
|
|||
BUILD_TYPE=${BUILD_TYPE:-Debug}
|
||||
WITH_ASAN=${WITH_ASAN:-0}
|
||||
WITH_RTSAN=${WITH_RTSAN:-0}
|
||||
WITH_TSAN=${WITH_RTSAN:-0}
|
||||
|
||||
export FREEDVGUIDIR=${PWD}
|
||||
|
||||
|
|
@ -21,5 +22,5 @@ if [ -d .git ]; then
|
|||
git pull
|
||||
fi
|
||||
mkdir -p build_linux && cd build_linux && rm -Rf *
|
||||
cmake -DENABLE_RTSAN=${WITH_RTSAN} -DENABLE_ASAN=${WITH_ASAN} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DUSE_NATIVE_AUDIO=$USE_NATIVE_AUDIO -DUNITTEST=$UT_ENABLE ..
|
||||
cmake -DENABLE_TSAN=${ENABLE_TSAN} -DENABLE_RTSAN=${WITH_RTSAN} -DENABLE_ASAN=${WITH_ASAN} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DUSE_NATIVE_AUDIO=$USE_NATIVE_AUDIO -DUNITTEST=$UT_ENABLE ..
|
||||
make VERBOSE=1 -j$(nproc)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export USE_NATIVE_AUDIO=${USE_NATIVE_AUDIO:-1}
|
|||
export BUILD_TYPE=${BUILD_TYPE:-Debug}
|
||||
export WITH_ASAN=${WITH_ASAN:-0}
|
||||
export WITH_RTSAN=${WITH_RTSAN:-0}
|
||||
export WITH_TSAN=${WITH_TSAN:-0}
|
||||
|
||||
# Prerequisite: build dylibbundler
|
||||
if [ ! -d macdylibbundler ]; then
|
||||
|
|
@ -68,9 +69,9 @@ if [ "$CODESIGN_KEYCHAIN_PROFILE" != "" ]; then
|
|||
fi
|
||||
|
||||
if [ $BUILD_DEPS == 1 ]; then
|
||||
cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_RTSAN=${WITH_RTSAN} -DENABLE_ASAN=${WITH_ASAN} -DUSE_NATIVE_AUDIO=$USE_NATIVE_AUDIO -DPython3_ROOT_DIR=$PWD/../Python.framework/Versions/3.12 -DBUILD_OSX_UNIVERSAL=${UNIV_BUILD} -DUNITTEST=$UT_ENABLE -DBOOTSTRAP_WXWIDGETS=1 -DUSE_STATIC_SPEEXDSP=1 -DUSE_STATIC_PORTAUDIO=1 -DUSE_STATIC_SAMPLERATE=1 -DUSE_STATIC_SNDFILE=1 -DHAMLIB_INCLUDE_DIR=${HAMLIBDIR}/include -DHAMLIB_LIBRARY=${HAMLIBDIR}/lib/libhamlib.dylib -DMACOS_CODESIGN_IDENTITY=${CODESIGN_IDENTITY} ${CODESIGN_KEYCHAIN_PROFILE_ARG} ..
|
||||
cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_TSAN=${WITH_TSAN} -DENABLE_RTSAN=${WITH_RTSAN} -DENABLE_ASAN=${WITH_ASAN} -DUSE_NATIVE_AUDIO=$USE_NATIVE_AUDIO -DPython3_ROOT_DIR=$PWD/../Python.framework/Versions/3.12 -DBUILD_OSX_UNIVERSAL=${UNIV_BUILD} -DUNITTEST=$UT_ENABLE -DBOOTSTRAP_WXWIDGETS=1 -DUSE_STATIC_SPEEXDSP=1 -DUSE_STATIC_PORTAUDIO=1 -DUSE_STATIC_SAMPLERATE=1 -DUSE_STATIC_SNDFILE=1 -DHAMLIB_INCLUDE_DIR=${HAMLIBDIR}/include -DHAMLIB_LIBRARY=${HAMLIBDIR}/lib/libhamlib.dylib -DMACOS_CODESIGN_IDENTITY=${CODESIGN_IDENTITY} ${CODESIGN_KEYCHAIN_PROFILE_ARG} ..
|
||||
else
|
||||
cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_RTSAN=${WITH_RTSAN} -DENABLE_ASAN=${WITH_ASAN} -DUSE_NATIVE_AUDIO=$USE_NATIVE_AUDIO -DPython3_ROOT_DIR=$PWD/../Python.framework/Versions/3.12 -DBUILD_OSX_UNIVERSAL=${UNIV_BUILD} -DUNITTEST=$UT_ENABLE -DMACOS_CODESIGN_IDENTITY=${CODESIGN_IDENTITY} ${CODESIGN_KEYCHAIN_PROFILE_ARG} ..
|
||||
cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_TSAN=${WITH_TSAN} -DENABLE_RTSAN=${WITH_RTSAN} -DENABLE_ASAN=${WITH_ASAN} -DUSE_NATIVE_AUDIO=$USE_NATIVE_AUDIO -DPython3_ROOT_DIR=$PWD/../Python.framework/Versions/3.12 -DBUILD_OSX_UNIVERSAL=${UNIV_BUILD} -DUNITTEST=$UT_ENABLE -DMACOS_CODESIGN_IDENTITY=${CODESIGN_IDENTITY} ${CODESIGN_KEYCHAIN_PROFILE_ARG} ..
|
||||
fi
|
||||
|
||||
make VERBOSE=1 -j$(sysctl -n hw.logicalcpu)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include "comp.h"
|
||||
#include "modem_stats.h"
|
||||
#include "sanitizers.h"
|
||||
|
||||
struct COHPSK;
|
||||
|
||||
|
|
@ -52,7 +53,7 @@ void cohpsk_mod(struct COHPSK *cohpsk, COMP tx_fdm[], int tx_bits[], int nbits);
|
|||
void cohpsk_clip(COMP tx_fdm[], float clip_thresh, int n);
|
||||
void cohpsk_demod(struct COHPSK *cohpsk, float rx_bits[], int *sync,
|
||||
COMP rx_fdm[], int *nin_frame);
|
||||
void cohpsk_get_demod_stats(struct COHPSK *cohpsk, struct MODEM_STATS *stats);
|
||||
void cohpsk_get_demod_stats(struct COHPSK *cohpsk, struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT;
|
||||
void cohpsk_set_verbose(struct COHPSK *coh, int verbose);
|
||||
void cohpsk_get_test_bits(struct COHPSK *coh, int rx_bits[]);
|
||||
void cohpsk_put_test_bits(struct COHPSK *coh, int *state, short error_pattern[],
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@
|
|||
#include "comp.h"
|
||||
#include "modem_stats.h"
|
||||
|
||||
#include "sanitizers.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -111,7 +113,7 @@ void fdmdv_put_test_bits(struct FDMDV *f, int *sync, short error_pattern[],
|
|||
int *bit_errors, int *ntest_bits, int rx_bits[]);
|
||||
|
||||
void fdmdv_get_demod_stats(struct FDMDV *fdmdv_state,
|
||||
struct MODEM_STATS *stats);
|
||||
struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
void fdmdv_8_to_16(float out16k[], float in8k[], int n);
|
||||
void fdmdv_8_to_16_short(short out16k[], short in8k[], int n);
|
||||
|
|
@ -129,7 +131,7 @@ void fdmdv_freq_shift(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff,
|
|||
|
||||
void fdmdv_dump_osc_mags(struct FDMDV *f);
|
||||
void fdmdv_simulate_channel(float *sig_pwr_av, COMP samples[], int nin,
|
||||
float target_snr);
|
||||
float target_snr) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ void codec2_fifo_destroy(struct FIFO *fifo) {
|
|||
free(fifo);
|
||||
}
|
||||
|
||||
int codec2_fifo_write(struct FIFO *fifo, short data[], int n) {
|
||||
int codec2_fifo_write(struct FIFO *fifo, short data[], int n) FREEDV_NONBLOCKING_EXCEPT {
|
||||
int i;
|
||||
short *pdata;
|
||||
short *pin = fifo->pin;
|
||||
|
|
@ -95,7 +95,7 @@ int codec2_fifo_write(struct FIFO *fifo, short data[], int n) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int codec2_fifo_read(struct FIFO *fifo, short data[], int n) {
|
||||
int codec2_fifo_read(struct FIFO *fifo, short data[], int n) FREEDV_NONBLOCKING_EXCEPT {
|
||||
int i;
|
||||
short *pdata;
|
||||
short *pout = fifo->pout;
|
||||
|
|
@ -120,7 +120,7 @@ int codec2_fifo_read(struct FIFO *fifo, short data[], int n) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int codec2_fifo_used(const struct FIFO *const fifo) {
|
||||
int codec2_fifo_used(const struct FIFO *const fifo) FREEDV_NONBLOCKING_EXCEPT {
|
||||
short *pin = fifo->pin;
|
||||
short *pout = fifo->pout;
|
||||
unsigned int used;
|
||||
|
|
@ -134,7 +134,7 @@ int codec2_fifo_used(const struct FIFO *const fifo) {
|
|||
return used;
|
||||
}
|
||||
|
||||
int codec2_fifo_free(const struct FIFO *const fifo) {
|
||||
int codec2_fifo_free(const struct FIFO *const fifo) FREEDV_NONBLOCKING_EXCEPT {
|
||||
// available storage is one less than nshort as prd == pwr
|
||||
// is reserved for empty rather than full
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#ifndef __FIFO__
|
||||
#define __FIFO__
|
||||
|
||||
#include "sanitizers.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -41,14 +43,14 @@ struct FIFO;
|
|||
struct FIFO *codec2_fifo_create(int nshort);
|
||||
struct FIFO *codec2_fifo_create_buf(int nshort, short *buf);
|
||||
void codec2_fifo_destroy(struct FIFO *fifo);
|
||||
int codec2_fifo_write(struct FIFO *fifo, short data[], int n);
|
||||
int codec2_fifo_read(struct FIFO *fifo, short data[], int n);
|
||||
int codec2_fifo_write(struct FIFO *fifo, short data[], int n) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int codec2_fifo_read(struct FIFO *fifo, short data[], int n) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
/* Return the number of bytes stored in the FIFO */
|
||||
int codec2_fifo_used(const struct FIFO *const fifo);
|
||||
int codec2_fifo_used(const struct FIFO *const fifo) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
/* Return the space available in the FIFO */
|
||||
int codec2_fifo_free(const struct FIFO *const fifo);
|
||||
int codec2_fifo_free(const struct FIFO *const fifo) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1281,7 +1281,7 @@ int cohpsk_fs_offset(COMP out[], COMP in[], int n, float sample_rate_ppm) {
|
|||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
void cohpsk_get_demod_stats(struct COHPSK *coh, struct MODEM_STATS *stats) {
|
||||
void cohpsk_get_demod_stats(struct COHPSK *coh, struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT {
|
||||
float new_snr_est;
|
||||
|
||||
#ifndef __EMBEDDED__
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include "rn.h"
|
||||
#include "rxdec_coeff.h"
|
||||
#include "test_bits.h"
|
||||
#include "sanitizers.h"
|
||||
|
||||
static int sync_uw[] = {1, -1, 1, -1, 1, -1};
|
||||
|
||||
|
|
@ -1709,7 +1710,7 @@ float calc_snr(int Nc, float sig_est[], float noise_est[]) {
|
|||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
void fdmdv_get_demod_stats(struct FDMDV *fdmdv, struct MODEM_STATS *stats) {
|
||||
void fdmdv_get_demod_stats(struct FDMDV *fdmdv, struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT {
|
||||
assert(fdmdv->Nc <= MODEM_STATS_NC_MAX);
|
||||
|
||||
stats->Nc = fdmdv->Nc;
|
||||
|
|
@ -2004,7 +2005,7 @@ static float randn() {
|
|||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
void fdmdv_simulate_channel(float *sig_pwr_av, COMP samples[], int nin,
|
||||
float target_snr) {
|
||||
float target_snr) FREEDV_NONBLOCKING_EXCEPT {
|
||||
float sig_pwr, target_snr_linear, noise_pwr, noise_pwr_1Hz, noise_pwr_4000Hz,
|
||||
noise_gain;
|
||||
int i;
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ void fmfsk_destroy(struct FMFSK *fmfsk) {
|
|||
*/
|
||||
uint32_t fmfsk_nin(struct FMFSK *fmfsk) { return (uint32_t)fmfsk->nin; }
|
||||
|
||||
void fmfsk_get_demod_stats(struct FMFSK *fmfsk, struct MODEM_STATS *stats) {
|
||||
void fmfsk_get_demod_stats(struct FMFSK *fmfsk, struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT {
|
||||
/* copy from internal stats, note we can't overwrite stats completely
|
||||
as it has other states rqd by caller, also we want a consistent
|
||||
interface across modem types for the freedv_api.
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "comp.h"
|
||||
#include "modem_stats.h"
|
||||
#include "sanitizers.h"
|
||||
|
||||
#define FMFSK_SCALE 16383
|
||||
|
||||
|
|
@ -82,7 +83,7 @@ void fmfsk_destroy(struct FMFSK *fmfsk);
|
|||
/*
|
||||
* Deposit demod statistics into a MODEM_STATS struct
|
||||
*/
|
||||
void fmfsk_get_demod_stats(struct FMFSK *fmfsk, struct MODEM_STATS *stats);
|
||||
void fmfsk_get_demod_stats(struct FMFSK *fmfsk, struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
/*
|
||||
* Returns the number of samples that must be fed to fmfsk_demod the next
|
||||
|
|
|
|||
|
|
@ -595,7 +595,7 @@ int freedv_data_ntxframes(struct freedv *f) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int freedv_nin(struct freedv *f) {
|
||||
int freedv_nin(struct freedv *f) FREEDV_NONBLOCKING_EXCEPT {
|
||||
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode))
|
||||
// For mode 700C, the input rate is 8000 sps, but the modem rate is 7500 sps
|
||||
// For mode 700C, we request a larger number of Rx samples that will be
|
||||
|
|
@ -1288,7 +1288,7 @@ void freedv_set_test_frames(struct freedv *f, int val) { f->test_frames = val; }
|
|||
void freedv_set_test_frames_diversity(struct freedv *f, int val) {
|
||||
f->test_frames_diversity = val;
|
||||
}
|
||||
void freedv_set_squelch_en(struct freedv *f, bool val) { f->squelch_en = val; }
|
||||
void freedv_set_squelch_en(struct freedv *f, bool val) FREEDV_NONBLOCKING_EXCEPT { f->squelch_en = val; }
|
||||
void freedv_set_total_bit_errors(struct freedv *f, int val) {
|
||||
f->total_bit_errors = val;
|
||||
}
|
||||
|
|
@ -1309,7 +1309,7 @@ void freedv_set_varicode_code_num(struct freedv *f, int val) {
|
|||
varicode_set_code_num(&f->varicode_dec_states, val);
|
||||
}
|
||||
void freedv_set_ext_vco(struct freedv *f, int val) { f->ext_vco = val; }
|
||||
void freedv_set_snr_squelch_thresh(struct freedv *f, float val) {
|
||||
void freedv_set_snr_squelch_thresh(struct freedv *f, float val) FREEDV_NONBLOCKING_EXCEPT {
|
||||
f->snr_squelch_thresh = val;
|
||||
}
|
||||
void freedv_set_tx_amp(struct freedv *f, float amp) { f->tx_amp = amp; }
|
||||
|
|
@ -1385,7 +1385,7 @@ void freedv_set_carrier_ampl(struct freedv *f, int c, float ampl) {
|
|||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
void freedv_set_sync(struct freedv *freedv, int sync_cmd) {
|
||||
void freedv_set_sync(struct freedv *freedv, int sync_cmd) FREEDV_NONBLOCKING_EXCEPT {
|
||||
assert(freedv != NULL);
|
||||
|
||||
if (freedv->ofdm != NULL) {
|
||||
|
|
@ -1418,24 +1418,24 @@ struct FSK *freedv_get_fsk(struct freedv *f) {
|
|||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
int freedv_get_protocol_bits(struct freedv *f) { return f->n_protocol_bits; }
|
||||
int freedv_get_mode(struct freedv *f) { return f->mode; }
|
||||
int freedv_get_mode(struct freedv *f) FREEDV_NONBLOCKING_EXCEPT { return f->mode; }
|
||||
int freedv_get_test_frames(struct freedv *f) { return f->test_frames; }
|
||||
int freedv_get_speech_sample_rate(struct freedv *f) {
|
||||
int freedv_get_speech_sample_rate(struct freedv *f) FREEDV_NONBLOCKING_EXCEPT {
|
||||
return f->speech_sample_rate;
|
||||
}
|
||||
int freedv_get_n_speech_samples(struct freedv *f) {
|
||||
int freedv_get_n_speech_samples(struct freedv *f) FREEDV_NONBLOCKING_EXCEPT {
|
||||
return f->n_speech_samples;
|
||||
}
|
||||
int freedv_get_modem_sample_rate(struct freedv *f) {
|
||||
int freedv_get_modem_sample_rate(struct freedv *f) FREEDV_NONBLOCKING_EXCEPT {
|
||||
return f->modem_sample_rate;
|
||||
}
|
||||
int freedv_get_modem_symbol_rate(struct freedv *f) {
|
||||
return f->modem_symbol_rate;
|
||||
}
|
||||
int freedv_get_n_max_modem_samples(struct freedv *f) {
|
||||
int freedv_get_n_max_modem_samples(struct freedv *f) FREEDV_NONBLOCKING_EXCEPT {
|
||||
return f->n_max_modem_samples;
|
||||
}
|
||||
int freedv_get_n_nom_modem_samples(struct freedv *f) {
|
||||
int freedv_get_n_nom_modem_samples(struct freedv *f) FREEDV_NONBLOCKING_EXCEPT {
|
||||
return f->n_nom_modem_samples;
|
||||
}
|
||||
int freedv_get_n_tx_modem_samples(struct freedv *f) {
|
||||
|
|
@ -1455,7 +1455,7 @@ int freedv_get_total_packets(struct freedv *f) { return f->total_packets; }
|
|||
int freedv_get_total_packet_errors(struct freedv *f) {
|
||||
return f->total_packet_errors;
|
||||
}
|
||||
int freedv_get_sync(struct freedv *f) { return f->sync; }
|
||||
int freedv_get_sync(struct freedv *f) FREEDV_NONBLOCKING_EXCEPT { return f->sync; }
|
||||
struct CODEC2 *freedv_get_codec2(struct freedv *f) {
|
||||
return f->codec2;
|
||||
}
|
||||
|
|
@ -1497,7 +1497,7 @@ int freedv_set_tuning_range(struct freedv *freedv, float val_fmin,
|
|||
}
|
||||
}
|
||||
|
||||
int freedv_get_n_max_speech_samples(struct freedv *f) {
|
||||
int freedv_get_n_max_speech_samples(struct freedv *f) FREEDV_NONBLOCKING_EXCEPT {
|
||||
/* When "passing through" demod samples to the speech output
|
||||
(e.g. no sync and squelch off) f->nin bounces around with
|
||||
timing variations. So we may return
|
||||
|
|
@ -1532,7 +1532,7 @@ int freedv_get_sz_error_pattern(struct freedv *f) {
|
|||
|
||||
// Get modem status, scatter/eye diagram for plotting, other goodies
|
||||
void freedv_get_modem_extended_stats(struct freedv *f,
|
||||
struct MODEM_STATS *stats) {
|
||||
struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT {
|
||||
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, f->mode))
|
||||
fdmdv_get_demod_stats(f->fdmdv, stats);
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
// This declares a single-precision (float) complex number
|
||||
#include "comp.h"
|
||||
|
||||
#include "sanitizers.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -212,7 +214,7 @@ int freedv_rawdatapostamblecomptx(struct freedv *f, COMP mod_out[]);
|
|||
|
||||
// Receive -------------------------------------------------------------------
|
||||
|
||||
int freedv_nin(struct freedv *freedv);
|
||||
int freedv_nin(struct freedv *freedv) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int freedv_rx(struct freedv *freedv, short speech_out[], short demod_in[]);
|
||||
int freedv_shortrx(struct freedv *freedv, short speech_out[], short demod_in[],
|
||||
float gain);
|
||||
|
|
@ -254,9 +256,9 @@ void freedv_set_test_frames(struct freedv *freedv, int test_frames);
|
|||
void freedv_set_test_frames_diversity(struct freedv *freedv,
|
||||
int test_frames_diversity);
|
||||
void freedv_set_smooth_symbols(struct freedv *freedv, int smooth_symbols);
|
||||
void freedv_set_squelch_en(struct freedv *freedv, bool squelch_en);
|
||||
void freedv_set_squelch_en(struct freedv *freedv, bool squelch_en) FREEDV_NONBLOCKING_EXCEPT;
|
||||
void freedv_set_snr_squelch_thresh(struct freedv *freedv,
|
||||
float snr_squelch_thresh);
|
||||
float snr_squelch_thresh) FREEDV_NONBLOCKING_EXCEPT;
|
||||
void freedv_set_clip(struct freedv *freedv, bool val);
|
||||
void freedv_set_total_bit_errors(struct freedv *freedv, int val);
|
||||
void freedv_set_total_bits(struct freedv *freedv, int val);
|
||||
|
|
@ -270,7 +272,7 @@ void freedv_set_callback_error_pattern(struct freedv *freedv,
|
|||
void freedv_set_varicode_code_num(struct freedv *freedv, int val);
|
||||
void freedv_set_data_header(struct freedv *freedv, unsigned char *header);
|
||||
void freedv_set_carrier_ampl(struct freedv *freedv, int c, float ampl);
|
||||
void freedv_set_sync(struct freedv *freedv, int sync_cmd);
|
||||
void freedv_set_sync(struct freedv *freedv, int sync_cmd) FREEDV_NONBLOCKING_EXCEPT;
|
||||
void freedv_set_verbose(struct freedv *freedv, int verbosity);
|
||||
void freedv_set_tx_bpf(struct freedv *freedv, int val);
|
||||
void freedv_set_tx_amp(struct freedv *freedv, float amp);
|
||||
|
|
@ -289,20 +291,20 @@ struct MODEM_STATS;
|
|||
|
||||
int freedv_get_version(void);
|
||||
char *freedv_get_hash(void);
|
||||
int freedv_get_mode(struct freedv *freedv);
|
||||
int freedv_get_mode(struct freedv *freedv) FREEDV_NONBLOCKING_EXCEPT;
|
||||
void freedv_get_modem_stats(struct freedv *freedv, int *sync, float *snr_est);
|
||||
void freedv_get_modem_extended_stats(struct freedv *freedv,
|
||||
struct MODEM_STATS *stats);
|
||||
struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int freedv_get_test_frames(struct freedv *freedv);
|
||||
|
||||
int freedv_get_speech_sample_rate(struct freedv *freedv);
|
||||
int freedv_get_n_speech_samples(struct freedv *freedv);
|
||||
int freedv_get_n_max_speech_samples(struct freedv *freedv);
|
||||
int freedv_get_speech_sample_rate(struct freedv *freedv) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int freedv_get_n_speech_samples(struct freedv *freedv) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int freedv_get_n_max_speech_samples(struct freedv *freedv) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
int freedv_get_modem_sample_rate(struct freedv *freedv);
|
||||
int freedv_get_modem_sample_rate(struct freedv *freedv) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int freedv_get_modem_symbol_rate(struct freedv *freedv);
|
||||
int freedv_get_n_max_modem_samples(struct freedv *freedv);
|
||||
int freedv_get_n_nom_modem_samples(struct freedv *freedv);
|
||||
int freedv_get_n_max_modem_samples(struct freedv *freedv) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int freedv_get_n_nom_modem_samples(struct freedv *freedv) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int freedv_get_n_tx_modem_samples(struct freedv *freedv);
|
||||
int freedv_get_n_tx_preamble_modem_samples(struct freedv *freedv);
|
||||
int freedv_get_n_tx_postamble_modem_samples(struct freedv *freedv);
|
||||
|
|
@ -318,7 +320,7 @@ int freedv_get_total_packet_errors(struct freedv *freedv);
|
|||
int freedv_get_rx_status(struct freedv *freedv);
|
||||
void freedv_get_fsk_S_and_N(struct freedv *freedv, float *S, float *N);
|
||||
|
||||
int freedv_get_sync(struct freedv *freedv);
|
||||
int freedv_get_sync(struct freedv *freedv) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int freedv_get_sync_interleaver(struct freedv *freedv);
|
||||
|
||||
// access to speech codec states
|
||||
|
|
|
|||
|
|
@ -1012,7 +1012,7 @@ void fsk_clear_estimators(struct FSK *fsk) {
|
|||
fsk->nin = fsk->N;
|
||||
}
|
||||
|
||||
void fsk_get_demod_stats(struct FSK *fsk, struct MODEM_STATS *stats) {
|
||||
void fsk_get_demod_stats(struct FSK *fsk, struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT {
|
||||
/* copy from internal stats, note we can't overwrite stats completely
|
||||
as it has other states rqd by caller, also we want a consistent
|
||||
interface across modem types for the freedv_api.
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ octave/fsk_horus.m
|
|||
#include "kiss_fftr.h"
|
||||
#include "modem_stats.h"
|
||||
|
||||
#include "sanitizers.h"
|
||||
|
||||
#define MODE_2FSK 2
|
||||
#define MODE_4FSK 4
|
||||
|
||||
|
|
@ -145,7 +147,7 @@ void fsk_clear_estimators(struct FSK *fsk);
|
|||
/*
|
||||
* Fills MODEM_STATS struct with demod statistics
|
||||
*/
|
||||
void fsk_get_demod_stats(struct FSK *fsk, struct MODEM_STATS *stats);
|
||||
void fsk_get_demod_stats(struct FSK *fsk, struct MODEM_STATS *stats) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
/*
|
||||
* Destroy an FSK state struct and free it's memory
|
||||
|
|
|
|||
|
|
@ -404,7 +404,7 @@ void kiss_fft_stride(kiss_fft_cfg st, const kiss_fft_cpx *fin,
|
|||
}
|
||||
}
|
||||
|
||||
void kiss_fft(kiss_fft_cfg cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout) {
|
||||
void kiss_fft(kiss_fft_cfg cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout) FREEDV_NONBLOCKING_EXCEPT {
|
||||
kiss_fft_stride(cfg, fin, fout, 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "debug_alloc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -29,8 +31,8 @@ extern "C" {
|
|||
#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes, 16)
|
||||
#define KISS_FFT_FREE _mm_free
|
||||
#else
|
||||
#define KISS_FFT_MALLOC malloc
|
||||
#define KISS_FFT_FREE free
|
||||
#define KISS_FFT_MALLOC codec2_malloc
|
||||
#define KISS_FFT_FREE codec2_free
|
||||
#endif
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
|
|
@ -47,6 +49,8 @@ extern "C" {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#include "sanitizers.h"
|
||||
|
||||
typedef struct {
|
||||
kiss_fft_scalar r;
|
||||
kiss_fft_scalar i;
|
||||
|
|
@ -90,7 +94,7 @@ kiss_fft_cfg kiss_fft_alloc(int nfft, int inverse_fft, void *mem,
|
|||
* Note that each element is complex and can be accessed like
|
||||
f[k].r and f[k].i
|
||||
* */
|
||||
void kiss_fft(kiss_fft_cfg cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout);
|
||||
void kiss_fft(kiss_fft_cfg cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
/*
|
||||
A more generic version of the above function. It reads its input from every Nth
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ void modem_stats_close(struct MODEM_STATS *f) {
|
|||
|
||||
#ifndef __EMBEDDED__
|
||||
void modem_stats_get_rx_spectrum(struct MODEM_STATS *f, float mag_spec_dB[],
|
||||
COMP rx_fdm[], int nin) {
|
||||
COMP rx_fdm[], int nin) FREEDV_NONBLOCKING_EXCEPT {
|
||||
int i, j;
|
||||
COMP fft_in[2 * MODEM_STATS_NSPEC];
|
||||
COMP fft_out[2 * MODEM_STATS_NSPEC];
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#define __MODEM_STATS__
|
||||
|
||||
#include "comp.h"
|
||||
#include "sanitizers.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -81,7 +82,7 @@ struct MODEM_STATS {
|
|||
void modem_stats_open(struct MODEM_STATS *f);
|
||||
void modem_stats_close(struct MODEM_STATS *f);
|
||||
void modem_stats_get_rx_spectrum(struct MODEM_STATS *f, float mag_spec_dB[],
|
||||
COMP rx_fdm[], int nin);
|
||||
COMP rx_fdm[], int nin) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
//=========================================================================
|
||||
// Name: sanitizers.h
|
||||
// Purpose: Defines items needed to enable LLVM sanitizer execution.
|
||||
//
|
||||
// Authors: Mooneer Salem
|
||||
// License:
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.1,
|
||||
// as published by the Free Software Foundation. This program is
|
||||
// distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//=========================================================================
|
||||
|
||||
#ifndef SANITIZERS_H
|
||||
#define SANITIZERS_H
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#define RTSAN_IS_ENABLED
|
||||
#else
|
||||
#define FREEDV_NONBLOCKING noexcept
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#else
|
||||
#define FREEDV_NONBLOCKING noexcept
|
||||
#endif // defined(__clang__)
|
||||
|
||||
#if defined(RTSAN_IS_ENABLED)
|
||||
#include <sanitizer/rtsan_interface.h>
|
||||
#define FREEDV_NONBLOCKING_EXCEPT [[clang::nonblocking]]
|
||||
#define FREEDV_NONBLOCKING noexcept [[clang::nonblocking]]
|
||||
|
||||
// *_VERIFIED_SAFE are intended for use where the code was manually verified
|
||||
// to be RT-safe (i.e. for third party libraries). rtsan run-time checks are
|
||||
// still enabled in case this ever changes in a future release of rtsan and/or
|
||||
// the library in question.
|
||||
#define FREEDV_BEGIN_VERIFIED_SAFE \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wunknown-warning-option\"") \
|
||||
_Pragma("clang diagnostic ignored \"-Wfunction-effects\"")
|
||||
#define FREEDV_END_VERIFIED_SAFE \
|
||||
_Pragma("clang diagnostic pop")
|
||||
|
||||
#define FREEDV_BEGIN_REALTIME_UNSAFE \
|
||||
{ \
|
||||
FREEDV_BEGIN_VERIFIED_SAFE \
|
||||
__rtsan_disable();
|
||||
#define FREEDV_END_REALTIME_UNSAFE \
|
||||
__rtsan_enable(); \
|
||||
FREEDV_END_VERIFIED_SAFE \
|
||||
}
|
||||
|
||||
#else
|
||||
#define FREEDV_NONBLOCKING_EXCEPT
|
||||
#define FREEDV_NONBLOCKING noexcept
|
||||
#define FREEDV_BEGIN_VERIFIED_SAFE
|
||||
#define FREEDV_END_VERIFIED_SAFE
|
||||
#define FREEDV_BEGIN_REALTIME_UNSAFE {
|
||||
#define FREEDV_END_REALTIME_UNSAFE }
|
||||
#endif // defined(RTSAN_IS_ENABLED)
|
||||
|
||||
#endif // SANITIZERS_H
|
||||
|
|
@ -1936,7 +1936,7 @@ int WebRtcAgc_Process(void *agcInst,
|
|||
int32_t inMicLevel,
|
||||
int32_t *outMicLevel,
|
||||
int16_t echo,
|
||||
uint8_t *saturationWarning) {
|
||||
uint8_t *saturationWarning) FREEDV_NONBLOCKING_EXCEPT {
|
||||
LegacyAgc *stt;
|
||||
|
||||
stt = (LegacyAgc *) agcInst;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include <stdint.h> // NOLINT(build/include)
|
||||
#include <assert.h>
|
||||
|
||||
#include "../../util/sanitizers.h"
|
||||
|
||||
// allpass filter coefficients.
|
||||
static const uint16_t kResampleAllpass1[3] = {3284, 24441, 49528};
|
||||
static const uint16_t kResampleAllpass2[3] = {12199, 37471, 60255};
|
||||
|
|
@ -244,7 +246,7 @@ int WebRtcAgc_Process(void *agcInst,
|
|||
int32_t inMicLevel,
|
||||
int32_t *outMicLevel,
|
||||
int16_t echo,
|
||||
uint8_t *saturationWarning);
|
||||
uint8_t *saturationWarning) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
/*
|
||||
* This function sets the config parameters (targetLevelDbfs,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
if((LINUX OR APPLE) AND (NOT ENABLE_ASAN AND NOT ENABLE_RTSAN))
|
||||
if((LINUX OR APPLE) AND (NOT ENABLE_TSAN AND NOT ENABLE_ASAN AND NOT ENABLE_RTSAN))
|
||||
set(MIMALLOC_SOURCE_FILE $<TARGET_OBJECTS:mimalloc-obj>)
|
||||
endif((LINUX OR APPLE) AND (NOT ENABLE_ASAN AND NOT ENABLE_RTSAN))
|
||||
endif((LINUX OR APPLE) AND (NOT ENABLE_TSAN AND NOT ENABLE_ASAN AND NOT ENABLE_RTSAN))
|
||||
|
||||
set(FREEDV_SOURCES
|
||||
${MIMALLOC_SOURCE_FILE}
|
||||
|
|
|
|||
|
|
@ -31,20 +31,23 @@
|
|||
|
||||
#include "AudioDeviceSpecification.h"
|
||||
#include "../util/IRealtimeHelper.h"
|
||||
#include "../util/sanitizers.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class IAudioDevice : public IRealtimeHelper
|
||||
{
|
||||
public:
|
||||
typedef void (*AudioDataCallbackFn)(IAudioDevice&, void*, size_t, void*);
|
||||
virtual ~IAudioDevice() = default;
|
||||
|
||||
typedef void (*AudioDataCallbackFn)(IAudioDevice&, void*, size_t, void*) FREEDV_NONBLOCKING;
|
||||
typedef void (*AudioUnderflowCallbackFn)(IAudioDevice&, void*);
|
||||
typedef void (*AudioOverflowCallbackFn)(IAudioDevice&, void*);
|
||||
typedef void (*AudioErrorCallbackFn)(IAudioDevice&, std::string, void*);
|
||||
typedef void (*AudioDeviceChangedCallbackFn)(IAudioDevice&, std::string, void*);
|
||||
|
||||
virtual int getNumChannels() = 0;
|
||||
virtual int getSampleRate() const = 0;
|
||||
virtual int getNumChannels() FREEDV_NONBLOCKING = 0;
|
||||
virtual int getSampleRate() const FREEDV_NONBLOCKING = 0;
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
|
@ -70,7 +73,7 @@ public:
|
|||
|
||||
// Returns true if real-time thread MUST sleep ASAP. Failure to do so
|
||||
// may result in SIGKILL being sent to the process by the kernel.
|
||||
virtual bool mustStopWork() override { return false; }
|
||||
virtual bool mustStopWork() FREEDV_NONBLOCKING override { return false; }
|
||||
|
||||
// Sets user friendly description of device. Not used by all engines.
|
||||
void setDescription(std::string desc);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ public:
|
|||
|
||||
enum AudioDirection { AUDIO_ENGINE_IN, AUDIO_ENGINE_OUT };
|
||||
|
||||
virtual ~IAudioEngine() = default;
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual std::vector<AudioDeviceSpecification> getAudioDeviceList(AudioDirection direction) = 0;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#include <future>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
|
|
@ -35,6 +37,7 @@
|
|||
#include <pthread.h>
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
thread_local void* MacAudioDevice::Workgroup_ = nullptr;
|
||||
thread_local void* MacAudioDevice::JoinToken_ = nullptr;
|
||||
|
|
@ -136,12 +139,12 @@ MacAudioDevice::~MacAudioDevice()
|
|||
dispatch_release(sem_);
|
||||
}
|
||||
|
||||
int MacAudioDevice::getNumChannels()
|
||||
int MacAudioDevice::getNumChannels() FREEDV_NONBLOCKING
|
||||
{
|
||||
return numChannels_;
|
||||
}
|
||||
|
||||
int MacAudioDevice::getSampleRate() const
|
||||
int MacAudioDevice::getSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
|
@ -509,6 +512,11 @@ void MacAudioDevice::stop()
|
|||
log_info("Device %d: stopping audio unit", coreAudioId_);
|
||||
running_ = false;
|
||||
AudioOutputUnitStop(auHAL_);
|
||||
|
||||
// XXX - should really use an AudioOutputUnitStopProc to determine when we're actually
|
||||
// stopped. For now, we just sleep for a couple of calls to the callback.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(3 * (10000 * chosenFrameSize_) / sampleRate_));
|
||||
|
||||
AudioUnitUninitialize(auHAL_);
|
||||
if (bufferList_ != nullptr)
|
||||
{
|
||||
|
|
@ -726,7 +734,7 @@ OSStatus MacAudioDevice::InputProc_(
|
|||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList * ioData)
|
||||
AudioBufferList * ioData) FREEDV_NONBLOCKING
|
||||
{
|
||||
MacAudioDevice* thisObj = (MacAudioDevice*)inRefCon;
|
||||
OSStatus err = noErr;
|
||||
|
|
@ -752,16 +760,23 @@ OSStatus MacAudioDevice::InputProc_(
|
|||
|
||||
thisObj->onAudioDataFunction(*thisObj, thisObj->inputFrames_, inNumberFrames, thisObj->onAudioDataState);
|
||||
}
|
||||
|
||||
|
||||
auto numWorkers = thisObj->numRealTimeWorkers_.load(std::memory_order_acquire);
|
||||
for (; numWorkers > 0; numWorkers--)
|
||||
{
|
||||
// Note: assuming that semaphore signalling is safe. This mechanism will need to revisited
|
||||
// if that turns out not to be the case.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
dispatch_semaphore_signal(thisObj->sem_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: this is definitely unsafe. However, if we get to this point, the audio will likely glitch anyway.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
log_warn("Device %d: got error in render func (%d)", thisObj->coreAudioId_, err);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
@ -773,7 +788,7 @@ OSStatus MacAudioDevice::OutputProc_(
|
|||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList * ioData)
|
||||
AudioBufferList * ioData) FREEDV_NONBLOCKING
|
||||
{
|
||||
MacAudioDevice* thisObj = (MacAudioDevice*)inRefCon;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <AudioUnit/AudioUnit.h>
|
||||
|
||||
#include "../util/ThreadedObject.h"
|
||||
#include "../util/sanitizers.h"
|
||||
#include "IAudioEngine.h"
|
||||
#include "IAudioDevice.h"
|
||||
|
||||
|
|
@ -39,8 +40,8 @@ class MacAudioDevice : public ThreadedObject, public IAudioDevice
|
|||
public:
|
||||
virtual ~MacAudioDevice();
|
||||
|
||||
virtual int getNumChannels() override;
|
||||
virtual int getSampleRate() const override;
|
||||
virtual int getNumChannels() FREEDV_NONBLOCKING override;
|
||||
virtual int getSampleRate() const FREEDV_NONBLOCKING override;
|
||||
|
||||
virtual void start() override;
|
||||
virtual void stop() override;
|
||||
|
|
@ -113,7 +114,7 @@ private:
|
|||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList * ioData);
|
||||
AudioBufferList * ioData) FREEDV_NONBLOCKING;
|
||||
|
||||
static OSStatus OutputProc_(
|
||||
void *inRefCon,
|
||||
|
|
@ -121,7 +122,7 @@ private:
|
|||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList * ioData);
|
||||
AudioBufferList * ioData) FREEDV_NONBLOCKING;
|
||||
};
|
||||
|
||||
#endif // MAC_AUDIO_DEVICE_H
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ class PortAudioDevice : public IAudioDevice
|
|||
public:
|
||||
virtual ~PortAudioDevice();
|
||||
|
||||
virtual int getNumChannels() override { return numChannels_; }
|
||||
virtual int getSampleRate() const override { return sampleRate_; }
|
||||
virtual int getNumChannels() FREEDV_NONBLOCKING override { return numChannels_; }
|
||||
virtual int getSampleRate() const FREEDV_NONBLOCKING override { return sampleRate_; }
|
||||
|
||||
virtual void start() override;
|
||||
virtual void stop() override;
|
||||
|
|
@ -60,4 +60,4 @@ private:
|
|||
static int OnPortAudioStreamCallback_(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData);
|
||||
};
|
||||
|
||||
#endif // PORT_AUDIO_DEVICE_H
|
||||
#endif // PORT_AUDIO_DEVICE_H
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <cstdio>
|
||||
#include <chrono>
|
||||
#include <sched.h>
|
||||
#include <alloca.h>
|
||||
#include <sys/resource.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
|
@ -42,8 +43,10 @@ using namespace std::chrono_literals;
|
|||
// TX audio to reach the radio.
|
||||
#define PULSE_TARGET_LATENCY_US 20000
|
||||
|
||||
#if 0
|
||||
thread_local bool PulseAudioDevice::MustStopWork_ = false;
|
||||
|
||||
#endif // 0
|
||||
|
||||
PulseAudioDevice::PulseAudioDevice(pa_threaded_mainloop *mainloop, pa_context* context, wxString devName, IAudioEngine::AudioDirection direction, int sampleRate, int numChannels)
|
||||
: context_(context)
|
||||
, mainloop_(mainloop)
|
||||
|
|
@ -317,7 +320,9 @@ void PulseAudioDevice::stopRealTimeWork(bool fastMode)
|
|||
IAudioDevice::stopRealTimeWork();
|
||||
}
|
||||
|
||||
#if 0
|
||||
MustStopWork_ = false;
|
||||
#endif // 0
|
||||
}
|
||||
|
||||
void PulseAudioDevice::clearHelperRealTime()
|
||||
|
|
@ -325,11 +330,14 @@ void PulseAudioDevice::clearHelperRealTime()
|
|||
IAudioDevice::clearHelperRealTime();
|
||||
}
|
||||
|
||||
bool PulseAudioDevice::mustStopWork()
|
||||
// Disabled for now as thread-local variables are apparently not RT-safe.
|
||||
#if 0
|
||||
bool PulseAudioDevice::mustStopWork() FREEDV_NONBLOCKING
|
||||
{
|
||||
return MustStopWork_;
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
||||
void PulseAudioDevice::StreamReadCallback_(pa_stream *s, size_t length, void *userdata)
|
||||
{
|
||||
const void* data = nullptr;
|
||||
|
|
@ -358,8 +366,9 @@ void PulseAudioDevice::StreamWriteCallback_(pa_stream *s, size_t length, void *u
|
|||
{
|
||||
// Note that PulseAudio gives us lengths in terms of number of bytes, not samples.
|
||||
int numSamples = length / sizeof(short);
|
||||
short data[numSamples];
|
||||
memset(data, 0, sizeof(data));
|
||||
short *data = (short*)alloca(length); // auto-freed on exit
|
||||
assert(data != nullptr);
|
||||
memset(data, 0, length);
|
||||
|
||||
PulseAudioDevice* thisObj = static_cast<PulseAudioDevice*>(userdata);
|
||||
|
||||
|
|
@ -435,5 +444,7 @@ void PulseAudioDevice::HandleXCPU_(int signum, siginfo_t *info, void *extra)
|
|||
{
|
||||
// Notify thread that it has to stop work immediately and sleep.
|
||||
log_warn("Taking too much CPU handling real-time tasks, pausing for a bit");
|
||||
#if 0
|
||||
MustStopWork_ = true;
|
||||
#endif // 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ class PulseAudioDevice : public IAudioDevice
|
|||
public:
|
||||
virtual ~PulseAudioDevice();
|
||||
|
||||
virtual int getNumChannels() override { return numChannels_; }
|
||||
virtual int getSampleRate() const override { return sampleRate_; }
|
||||
virtual int getNumChannels() FREEDV_NONBLOCKING override { return numChannels_; }
|
||||
virtual int getSampleRate() const FREEDV_NONBLOCKING override { return sampleRate_; }
|
||||
|
||||
virtual void start() override;
|
||||
virtual void stop() override;
|
||||
|
|
@ -62,10 +62,13 @@ public:
|
|||
// Reverts real-time priority for current thread.
|
||||
virtual void clearHelperRealTime() override;
|
||||
|
||||
#if 0
|
||||
// Returns true if real-time thread MUST sleep ASAP. Failure to do so
|
||||
// may result in SIGKILL being sent to the process by the kernel.
|
||||
virtual bool mustStopWork() override;
|
||||
|
||||
// XXX - disabled as current implementation uses thread_local (not RT safe).
|
||||
virtual bool mustStopWork() FREEDV_NONBLOCKING override;
|
||||
#endif // 0
|
||||
|
||||
protected:
|
||||
// PulseAudioDevice cannot be created directly, only via PulseAudioEngine.
|
||||
friend class PulseAudioEngine;
|
||||
|
|
|
|||
|
|
@ -73,12 +73,12 @@ WASAPIAudioDevice::~WASAPIAudioDevice()
|
|||
fut.wait();
|
||||
}
|
||||
|
||||
int WASAPIAudioDevice::getNumChannels()
|
||||
int WASAPIAudioDevice::getNumChannels() FREEDV_NONBLOCKING
|
||||
{
|
||||
return numChannels_;
|
||||
}
|
||||
|
||||
int WASAPIAudioDevice::getSampleRate() const
|
||||
int WASAPIAudioDevice::getSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ class WASAPIAudioDevice : public Win32COMObject, public IAudioDevice
|
|||
public:
|
||||
virtual ~WASAPIAudioDevice();
|
||||
|
||||
virtual int getNumChannels() override;
|
||||
virtual int getSampleRate() const override;
|
||||
virtual int getNumChannels() FREEDV_NONBLOCKING override;
|
||||
virtual int getSampleRate() const FREEDV_NONBLOCKING override;
|
||||
|
||||
virtual void start() override;
|
||||
virtual void stop() override;
|
||||
|
|
|
|||
27
src/eq.cpp
27
src/eq.cpp
|
|
@ -13,7 +13,7 @@ extern int g_nSoundCards;
|
|||
|
||||
#define SBQ_MAX_ARGS 5
|
||||
|
||||
std::shared_ptr<void> MainFrame::designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q, int sampleRate)
|
||||
void* MainFrame::designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q, int sampleRate)
|
||||
{
|
||||
const int STR_LENGTH = 80;
|
||||
|
||||
|
|
@ -58,11 +58,13 @@ std::shared_ptr<void> MainFrame::designAnEQFilter(const char filterType[], float
|
|||
|
||||
assert(argc <= SBQ_MAX_ARGS);
|
||||
// Note - the argc count doesn't include the command!
|
||||
return std::shared_ptr<void>(sox_biquad_create(argc-1, (const char **)arg), [](void* p) { if (p != nullptr) sox_biquad_destroy(p); });
|
||||
return sox_biquad_create(argc-1, (const char **)arg);
|
||||
}
|
||||
|
||||
void MainFrame::designEQFilters(paCallBackData *cb, int rxSampleRate, int txSampleRate)
|
||||
{
|
||||
cb->eqLock.lock();
|
||||
|
||||
// Volume can be adjusted via main window without enabling filters
|
||||
if (wxGetApp().appConfiguration.filterConfiguration.micInChannel.volInDB != 0 && g_nSoundCards > 1)
|
||||
{
|
||||
|
|
@ -100,18 +102,23 @@ void MainFrame::designEQFilters(paCallBackData *cb, int rxSampleRate, int txSam
|
|||
// Note: vol can be a no-op!
|
||||
assert(cb->sbqSpkOutBass != nullptr && cb->sbqSpkOutTreble != nullptr && cb->sbqSpkOutMid != nullptr);
|
||||
}
|
||||
cb->eqLock.unlock();
|
||||
}
|
||||
|
||||
#define VERIFY_AND_DESTROY(x) if (x != nullptr) { sox_biquad_destroy(x); x = nullptr; }
|
||||
|
||||
void MainFrame::deleteEQFilters(paCallBackData *cb)
|
||||
{
|
||||
cb->sbqMicInBass = nullptr;
|
||||
cb->sbqMicInTreble = nullptr;
|
||||
cb->sbqMicInMid = nullptr;
|
||||
cb->sbqMicInVol = nullptr;
|
||||
cb->sbqSpkOutBass = nullptr;
|
||||
cb->sbqSpkOutTreble = nullptr;
|
||||
cb->sbqSpkOutMid = nullptr;
|
||||
cb->sbqSpkOutVol = nullptr;
|
||||
cb->eqLock.lock();
|
||||
VERIFY_AND_DESTROY(cb->sbqMicInBass);
|
||||
VERIFY_AND_DESTROY(cb->sbqMicInTreble);
|
||||
VERIFY_AND_DESTROY(cb->sbqMicInMid);
|
||||
VERIFY_AND_DESTROY(cb->sbqMicInVol);
|
||||
VERIFY_AND_DESTROY(cb->sbqSpkOutBass);
|
||||
VERIFY_AND_DESTROY(cb->sbqSpkOutTreble);
|
||||
VERIFY_AND_DESTROY(cb->sbqSpkOutMid);
|
||||
VERIFY_AND_DESTROY(cb->sbqSpkOutVol);
|
||||
cb->eqLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ void FreeDVInterface::changeTxMode(int txMode)
|
|||
assert(false);
|
||||
}
|
||||
|
||||
void FreeDVInterface::setSync(int val)
|
||||
void FreeDVInterface::setSync(int val) FREEDV_NONBLOCKING
|
||||
{
|
||||
// Special case for RADE.
|
||||
if (currentRxMode_ == nullptr) return;
|
||||
|
|
@ -505,7 +505,7 @@ void FreeDVInterface::setTextCallbackFn(void (*rxFunc)(void *, char), char (*txF
|
|||
}
|
||||
}
|
||||
|
||||
int FreeDVInterface::getTxModemSampleRate() const
|
||||
int FreeDVInterface::getTxModemSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
if (txMode_ >= FREEDV_MODE_RADE)
|
||||
{
|
||||
|
|
@ -538,12 +538,17 @@ int FreeDVInterface::getTxNumSpeechSamples() const
|
|||
return freedv_get_n_speech_samples(currentTxMode_);
|
||||
}
|
||||
|
||||
int FreeDVInterface::getTxNNomModemSamples() const
|
||||
int FreeDVInterface::getTxNNomModemSamples() const FREEDV_NONBLOCKING
|
||||
{
|
||||
if (txMode_ >= FREEDV_MODE_RADE)
|
||||
{
|
||||
const int NUM_SAMPLES_SILENCE = 60 * RADE_MODEM_SAMPLE_RATE / 1000;
|
||||
|
||||
// Verified that rade_api.c from librade has no unbounded operations
|
||||
// as of 2025-10-03.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
return std::max(rade_n_tx_out(rade_), rade_n_tx_eoo_out(rade_) + NUM_SAMPLES_SILENCE);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
|
||||
assert(currentTxMode_ != nullptr);
|
||||
|
|
@ -602,7 +607,7 @@ int FreeDVInterface::getRxNumModemSamples() const
|
|||
return result;
|
||||
}
|
||||
|
||||
int FreeDVInterface::getRxNumSpeechSamples() const
|
||||
int FreeDVInterface::getRxNumSpeechSamples() const FREEDV_NONBLOCKING
|
||||
{
|
||||
if (rxMode_ >= FREEDV_MODE_RADE)
|
||||
{
|
||||
|
|
@ -618,7 +623,7 @@ int FreeDVInterface::getRxNumSpeechSamples() const
|
|||
return result;
|
||||
}
|
||||
|
||||
int FreeDVInterface::getRxSpeechSampleRate() const
|
||||
int FreeDVInterface::getRxSpeechSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
if (rxMode_ >= FREEDV_MODE_RADE)
|
||||
{
|
||||
|
|
@ -634,7 +639,7 @@ int FreeDVInterface::getRxSpeechSampleRate() const
|
|||
return result;
|
||||
}
|
||||
|
||||
void FreeDVInterface::setSquelch(bool enable, float level)
|
||||
void FreeDVInterface::setSquelch(bool enable, float level) FREEDV_NONBLOCKING
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
|
|
@ -713,7 +718,7 @@ float FreeDVInterface::getSNREstimate()
|
|||
IPipelineStep* FreeDVInterface::createTransmitPipeline(
|
||||
int inputSampleRate,
|
||||
int outputSampleRate,
|
||||
std::function<float()> getFreqOffsetFn,
|
||||
realtime_fp<float()> getFreqOffsetFn,
|
||||
std::shared_ptr<IRealtimeHelper> realtimeHelper)
|
||||
{
|
||||
std::vector<IPipelineStep*> parallelSteps;
|
||||
|
|
@ -735,17 +740,18 @@ IPipelineStep* FreeDVInterface::createTransmitPipeline(
|
|||
parallelSteps.push_back(new FreeDVTransmitStep(dv, getFreqOffsetFn));
|
||||
}
|
||||
|
||||
std::function<int(ParallelStep*)> modeFn =
|
||||
[&](ParallelStep*) {
|
||||
realtime_fp<int(ParallelStep*)> modeFn =
|
||||
+[](ParallelStep* s) FREEDV_NONBLOCKING {
|
||||
FreeDVInterface* thisObj = (FreeDVInterface*)s->getCallbackState();
|
||||
int index = 0;
|
||||
|
||||
auto currentTxMode = currentTxMode_;
|
||||
auto txModeInt = txMode_;
|
||||
auto currentTxMode = thisObj->currentTxMode_;
|
||||
auto txModeInt = thisObj->txMode_;
|
||||
|
||||
// Special handling for RADE.
|
||||
if (txModeInt >= FREEDV_MODE_RADE) return 0;
|
||||
|
||||
for (auto& dv : dvObjects_)
|
||||
for (auto& dv : thisObj->dvObjects_)
|
||||
{
|
||||
if (dv == currentTxMode) return index;
|
||||
index++;
|
||||
|
|
@ -761,6 +767,7 @@ IPipelineStep* FreeDVInterface::createTransmitPipeline(
|
|||
modeFn,
|
||||
parallelSteps,
|
||||
nullptr,
|
||||
this,
|
||||
realtimeHelper
|
||||
);
|
||||
|
||||
|
|
@ -769,11 +776,11 @@ IPipelineStep* FreeDVInterface::createTransmitPipeline(
|
|||
|
||||
IPipelineStep* FreeDVInterface::createReceivePipeline(
|
||||
int inputSampleRate, int outputSampleRate,
|
||||
std::function<int*()> getRxStateFn,
|
||||
std::function<int()> getChannelNoiseFn,
|
||||
std::function<int()> getChannelNoiseSnrFn,
|
||||
std::function<float()> getFreqOffsetFn,
|
||||
std::function<float*()> getSigPwrAvgFn,
|
||||
realtime_fp<std::atomic<int>*()> getRxStateFn,
|
||||
realtime_fp<int()> getChannelNoiseFn,
|
||||
realtime_fp<int()> getChannelNoiseSnrFn,
|
||||
realtime_fp<float()> getFreqOffsetFn,
|
||||
realtime_fp<float*()> getSigPwrAvgFn,
|
||||
std::shared_ptr<IRealtimeHelper> realtimeHelper)
|
||||
{
|
||||
std::vector<IPipelineStep*> parallelSteps;
|
||||
|
|
@ -790,13 +797,16 @@ IPipelineStep* FreeDVInterface::createReceivePipeline(
|
|||
if (txMode_ >= FREEDV_MODE_RADE)
|
||||
{
|
||||
// special handling for RADE
|
||||
auto rxStep = new RADEReceiveStep(rade_, &fargan_, radeTextPtr_, [&, getRxStateFn](RADEReceiveStep* s) {
|
||||
auto finalSync = s->getSync();
|
||||
*getRxStateFn() = finalSync;
|
||||
sync_.store(finalSync, std::memory_order_release);
|
||||
radeSnr_.store(s->getSnr(), std::memory_order_release);
|
||||
});
|
||||
|
||||
auto rxStep = new RADEReceiveStep(rade_, &fargan_, radeTextPtr_, +[](RADEReceiveStep* step) FREEDV_NONBLOCKING {
|
||||
FreeDVInterface* state = (FreeDVInterface*)step->getStateObj();
|
||||
auto finalSync = step->getSync();
|
||||
*step->getRxStateFn()() = finalSync;
|
||||
state->sync_.store(finalSync, std::memory_order_release);
|
||||
state->radeSnr_.store(step->getSnr(), std::memory_order_release);
|
||||
});
|
||||
rxStep->setStateObj(this);
|
||||
rxStep->setRxStateFn(getRxStateFn);
|
||||
|
||||
auto pipeline = new AudioPipeline(inputSampleRate, outputSampleRate);
|
||||
pipeline->appendPipelineStep(rxStep);
|
||||
return pipeline;
|
||||
|
|
@ -811,8 +821,14 @@ IPipelineStep* FreeDVInterface::createReceivePipeline(
|
|||
parallelSteps.push_back(recvStep);
|
||||
}
|
||||
|
||||
state->preProcessFn = std::bind(&FreeDVInterface::preProcessRxFn_, this, _1);
|
||||
state->postProcessFn = std::bind(&FreeDVInterface::postProcessRxFn_, this, _1);
|
||||
state->preProcessFn = +[](ParallelStep* s) FREEDV_NONBLOCKING {
|
||||
FreeDVInterface* thisObj = (FreeDVInterface*)s->getCallbackState();
|
||||
return thisObj->preProcessRxFn_(s);
|
||||
};
|
||||
state->postProcessFn = +[](ParallelStep* s) FREEDV_NONBLOCKING {
|
||||
FreeDVInterface* thisObj = (FreeDVInterface*)s->getCallbackState();
|
||||
return thisObj->postProcessRxFn_(s);
|
||||
};
|
||||
}
|
||||
|
||||
auto parallelStep = new ParallelStep(
|
||||
|
|
@ -823,21 +839,22 @@ IPipelineStep* FreeDVInterface::createReceivePipeline(
|
|||
state->postProcessFn,
|
||||
parallelSteps,
|
||||
state,
|
||||
this,
|
||||
realtimeHelper
|
||||
);
|
||||
|
||||
return parallelStep;
|
||||
}
|
||||
|
||||
void FreeDVInterface::restartTxVocoder()
|
||||
void FreeDVInterface::restartTxVocoder() FREEDV_NONBLOCKING
|
||||
{
|
||||
radeTxStep_->restartVocoder();
|
||||
}
|
||||
|
||||
int FreeDVInterface::preProcessRxFn_(ParallelStep* stepObj)
|
||||
int FreeDVInterface::preProcessRxFn_(ParallelStep* stepObj) FREEDV_NONBLOCKING
|
||||
{
|
||||
int rxIndex = 0;
|
||||
std::shared_ptr<ReceivePipelineState> state = std::static_pointer_cast<ReceivePipelineState>(stepObj->getState());
|
||||
ReceivePipelineState* state = static_cast<ReceivePipelineState*>(stepObj->getState());
|
||||
|
||||
if (txMode_ >= FREEDV_MODE_RADE)
|
||||
{
|
||||
|
|
@ -871,9 +888,9 @@ int FreeDVInterface::preProcessRxFn_(ParallelStep* stepObj)
|
|||
return -1;
|
||||
};
|
||||
|
||||
int FreeDVInterface::postProcessRxFn_(ParallelStep* stepObj)
|
||||
int FreeDVInterface::postProcessRxFn_(ParallelStep* stepObj) FREEDV_NONBLOCKING
|
||||
{
|
||||
std::shared_ptr<ReceivePipelineState> state = std::static_pointer_cast<ReceivePipelineState>(stepObj->getState());
|
||||
ReceivePipelineState* state = static_cast<ReceivePipelineState*>(stepObj->getState());
|
||||
auto& parallelSteps = stepObj->getParallelSteps();
|
||||
|
||||
// If the current RX mode is still sync'd, only let that one out.
|
||||
|
|
|
|||
|
|
@ -53,12 +53,15 @@ extern "C"
|
|||
}
|
||||
|
||||
#include "util/IRealtimeHelper.h"
|
||||
#include "util/sanitizers.h"
|
||||
#include "util/realtime_fp.h"
|
||||
|
||||
#include <samplerate.h>
|
||||
|
||||
class IPipelineStep;
|
||||
class ParallelStep;
|
||||
class RADETransmitStep;
|
||||
class RADEReceiveStep;
|
||||
|
||||
// Anything above 255 is a RADE mode. There's only one right now,
|
||||
// this is just for future expansion.
|
||||
|
|
@ -94,7 +97,7 @@ public:
|
|||
int getErrorPattern(short** outputPattern);
|
||||
|
||||
int getSync() const;
|
||||
void setSync(int val);
|
||||
void setSync(int val) FREEDV_NONBLOCKING;
|
||||
void setEq(int val);
|
||||
void setVerbose(bool val);
|
||||
|
||||
|
|
@ -102,21 +105,21 @@ public:
|
|||
|
||||
void addRxMode(int mode) { enabledModes_.push_back(mode); }
|
||||
|
||||
int getTxModemSampleRate() const;
|
||||
int getTxModemSampleRate() const FREEDV_NONBLOCKING;
|
||||
int getTxSpeechSampleRate() const;
|
||||
int getTxNumSpeechSamples() const;
|
||||
int getTxNNomModemSamples() const;
|
||||
int getTxNNomModemSamples() const FREEDV_NONBLOCKING;
|
||||
|
||||
int getRxModemSampleRate() const;
|
||||
int getRxNumModemSamples() const;
|
||||
int getRxNumSpeechSamples() const;
|
||||
int getRxSpeechSampleRate() const;
|
||||
int getRxNumSpeechSamples() const FREEDV_NONBLOCKING;
|
||||
int getRxSpeechSampleRate() const FREEDV_NONBLOCKING;
|
||||
|
||||
void setLpcPostFilter(int enable, int bassBoost, float beta, float gamma);
|
||||
|
||||
void setTextVaricodeNum(int num);
|
||||
|
||||
void setSquelch(bool enable, float level);
|
||||
void setSquelch(bool enable, float level) FREEDV_NONBLOCKING;
|
||||
|
||||
void setCarrierAmplitude(int c, float amp);
|
||||
|
||||
|
|
@ -129,32 +132,32 @@ public:
|
|||
IPipelineStep* createTransmitPipeline(
|
||||
int inputSampleRate,
|
||||
int outputSampleRate,
|
||||
std::function<float()> getFreqOffsetFn,
|
||||
realtime_fp<float()> getFreqOffsetFn,
|
||||
std::shared_ptr<IRealtimeHelper> realtimeHelper);
|
||||
IPipelineStep* createReceivePipeline(
|
||||
int inputSampleRate, int outputSampleRate,
|
||||
std::function<int*()> getRxStateFn,
|
||||
std::function<int()> getChannelNoiseFn,
|
||||
std::function<int()> getChannelNoiseSnrFn,
|
||||
std::function<float()> getFreqOffsetFn,
|
||||
std::function<float*()> getSigPwrAvgFn,
|
||||
realtime_fp<std::atomic<int>*()> getRxStateFn,
|
||||
realtime_fp<int()> getChannelNoiseFn,
|
||||
realtime_fp<int()> getChannelNoiseSnrFn,
|
||||
realtime_fp<float()> getFreqOffsetFn,
|
||||
realtime_fp<float*()> getSigPwrAvgFn,
|
||||
std::shared_ptr<IRealtimeHelper> realtimeHelper
|
||||
);
|
||||
|
||||
void restartTxVocoder();
|
||||
void restartTxVocoder() FREEDV_NONBLOCKING;
|
||||
|
||||
float getSNREstimate();
|
||||
|
||||
private:
|
||||
struct ReceivePipelineState
|
||||
{
|
||||
std::function<int*()> getRxStateFn;
|
||||
std::function<int()> getChannelNoiseFn;
|
||||
std::function<int()> getChannelNoiseSnrFn;
|
||||
std::function<float()> getFreqOffsetFn;
|
||||
std::function<float*()> getSigPwrAvgFn;
|
||||
std::function<int(ParallelStep*)> preProcessFn;
|
||||
std::function<int(ParallelStep*)> postProcessFn;
|
||||
realtime_fp<std::atomic<int>*()> getRxStateFn;
|
||||
realtime_fp<int()> getChannelNoiseFn;
|
||||
realtime_fp<int()> getChannelNoiseSnrFn;
|
||||
realtime_fp<float()> getFreqOffsetFn;
|
||||
realtime_fp<float*()> getSigPwrAvgFn;
|
||||
realtime_fp<int(ParallelStep*)> preProcessFn;
|
||||
realtime_fp<int(ParallelStep*)> postProcessFn;
|
||||
};
|
||||
|
||||
struct FreeDVTextFnState
|
||||
|
|
@ -207,8 +210,10 @@ private:
|
|||
std::atomic<int> radeSnr_;
|
||||
rade_text_t radeTextPtr_;
|
||||
|
||||
int preProcessRxFn_(ParallelStep* ps);
|
||||
int postProcessRxFn_(ParallelStep* ps);
|
||||
int preProcessRxFn_(ParallelStep* ps) FREEDV_NONBLOCKING;
|
||||
int postProcessRxFn_(ParallelStep* ps) FREEDV_NONBLOCKING;
|
||||
|
||||
void radeSyncFn_(RADEReceiveStep* step) FREEDV_NONBLOCKING;
|
||||
};
|
||||
|
||||
#endif // FREEDV_INTERFACE_H
|
||||
|
|
|
|||
|
|
@ -27,10 +27,13 @@
|
|||
#include "plot_waterfall.h"
|
||||
#include "codec2_fdmdv.h" // for FDMDV_FCENTRE
|
||||
|
||||
#include "../../util/audio_spin_mutex.h"
|
||||
|
||||
// Tweak accordingly
|
||||
#define Y_PER_SECOND (30)
|
||||
|
||||
extern float g_avmag[]; // av mag spec passed in to draw()
|
||||
extern audio_spin_mutex g_avmag_mtx;
|
||||
extern float g_RxFreqOffsetHz;
|
||||
void clickTune(float frequency); // callback to pass new click freq
|
||||
|
||||
|
|
@ -398,6 +401,7 @@ void PlotWaterfall::plotPixelData()
|
|||
int min_fft_bin=((float)200/m_modem_stats_max_f_hz)*MODEM_STATS_NSPEC;
|
||||
int max_fft_bin=((float)2800/m_modem_stats_max_f_hz)*MODEM_STATS_NSPEC;
|
||||
|
||||
g_avmag_mtx.lock();
|
||||
for(int i=min_fft_bin; i<max_fft_bin; i++)
|
||||
{
|
||||
if (g_avmag[i] > max_mag)
|
||||
|
|
@ -415,7 +419,7 @@ void PlotWaterfall::plotPixelData()
|
|||
if (dy_ != dy && dyImageData_ != nullptr)
|
||||
{
|
||||
delete[] dyImageData_;
|
||||
dyImageData_ = nullptr;
|
||||
dyImageData_ = nullptr;
|
||||
|
||||
delete tmpImage_;
|
||||
}
|
||||
|
|
@ -462,6 +466,8 @@ void PlotWaterfall::plotPixelData()
|
|||
}
|
||||
}
|
||||
|
||||
g_avmag_mtx.unlock();
|
||||
|
||||
for (int row = 1; row < dy; row++)
|
||||
{
|
||||
memcpy(&dyImageData_[row * 3 * baseRowWidthPixels], &dyImageData_[0], 3 * baseRowWidthPixels);
|
||||
|
|
|
|||
|
|
@ -885,7 +885,7 @@ void AudioOptsDialog::plotDeviceInputForAFewSecs(wxString devName, PlotScalar *p
|
|||
assert(callbackFifo != nullptr);
|
||||
|
||||
AudioDeviceCapture capture { .fifo = callbackFifo, .cv = &callbackFifoCV, .running = &running };
|
||||
device->setOnAudioData([](IAudioDevice&, void* data, size_t numSamples, void* state) {
|
||||
device->setOnAudioData([](IAudioDevice&, void* data, size_t numSamples, void* state) FREEDV_NONBLOCKING {
|
||||
AudioDeviceCapture* castedState = (AudioDeviceCapture*)state;
|
||||
|
||||
if (*castedState->running && data != nullptr)
|
||||
|
|
@ -1021,7 +1021,7 @@ void AudioOptsDialog::plotDeviceOutputForAFewSecs(wxString devName, PlotScalar *
|
|||
assert(callbackFifo != nullptr);
|
||||
|
||||
AudioDeviceCapture capture { .fifo = callbackFifo, .cv = &callbackFifoCV, .running = &running, .n = &n };
|
||||
device->setOnAudioData([](IAudioDevice& dev, void* data, size_t numSamples, void* state) {
|
||||
device->setOnAudioData([](IAudioDevice& dev, void* data, size_t numSamples, void* state) FREEDV_NONBLOCKING {
|
||||
AudioDeviceCapture* castedState = (AudioDeviceCapture*)state;
|
||||
|
||||
if (*castedState->running && data != nullptr)
|
||||
|
|
|
|||
|
|
@ -916,7 +916,7 @@ void EasySetupDialog::OnTest(wxCommandEvent& event)
|
|||
|
||||
sineWaveSampleNumber_ = 0;
|
||||
|
||||
txTestAudioDevice_->setOnAudioData([](IAudioDevice& dev, void* data, size_t size, void* state) {
|
||||
txTestAudioDevice_->setOnAudioData([](IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING {
|
||||
auto sr = dev.getSampleRate();
|
||||
EasySetupDialog* castedThis = (EasySetupDialog*)state;
|
||||
short* audioData = static_cast<short*>(data);
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ extern FreeDVInterface freedvInterface;
|
|||
|
||||
// PortAudio over/underflow counters
|
||||
|
||||
extern int g_infifo1_full;
|
||||
extern int g_outfifo1_empty;
|
||||
extern int g_infifo2_full;
|
||||
extern int g_outfifo2_empty;
|
||||
extern std::atomic<int> g_infifo1_full;
|
||||
extern std::atomic<int> g_outfifo1_empty;
|
||||
extern std::atomic<int> g_infifo2_full;
|
||||
extern std::atomic<int> g_outfifo2_empty;
|
||||
extern int g_AEstatus1[4];
|
||||
extern int g_AEstatus2[4];
|
||||
extern wxDatagramSocket *g_sock;
|
||||
|
|
@ -1280,7 +1280,10 @@ void OptionsDlg::OnDebugConsole(wxScrollEvent& event) {
|
|||
|
||||
void OptionsDlg::OnFifoReset(wxCommandEvent& event)
|
||||
{
|
||||
g_infifo1_full = g_outfifo1_empty = g_infifo2_full = g_outfifo2_empty = 0;
|
||||
g_infifo1_full.store(0, std::memory_order_release);
|
||||
g_outfifo1_empty.store(0, std::memory_order_release);
|
||||
g_infifo2_full.store(0, std::memory_order_release);
|
||||
g_outfifo2_empty.store(0, std::memory_order_release);
|
||||
for (int i=0; i<4; i++) {
|
||||
g_AEstatus1[i] = g_AEstatus2[i] = 0;
|
||||
}
|
||||
|
|
@ -1414,7 +1417,7 @@ void OptionsDlg::OnFreqModeChangeEnable(wxCommandEvent& event)
|
|||
void OptionsDlg::DisplayFifoPACounters() {
|
||||
if (IsShownOnScreen())
|
||||
{
|
||||
wxString fifo_counters = wxString::Format(wxT("Fifos: infull1: %d outempty1: %d infull2: %d outempty2: %d"), g_infifo1_full, g_outfifo1_empty, g_infifo2_full, g_outfifo2_empty);
|
||||
wxString fifo_counters = wxString::Format(wxT("Fifos: infull1: %d outempty1: %d infull2: %d outempty2: %d"), g_infifo1_full.load(std::memory_order_acquire), g_outfifo1_empty.load(std::memory_order_acquire), g_infifo2_full.load(std::memory_order_acquire), g_outfifo2_empty.load(std::memory_order_acquire));
|
||||
m_textFifos->SetLabel(fifo_counters);
|
||||
|
||||
// input: underflow overflow output: underflow overflow
|
||||
|
|
|
|||
|
|
@ -806,8 +806,8 @@ void ComPortsDlg::OnTest(wxCommandEvent& event) {
|
|||
std::make_shared<OmniRigController>(
|
||||
m_cbOmniRigRigId->GetCurrentSelection());
|
||||
|
||||
rig->onRigError += [=, this](IRigController*, std::string error) {
|
||||
CallAfter([=, this]() {
|
||||
rig->onRigError += [=](IRigController*, std::string error) {
|
||||
CallAfter([=]() {
|
||||
wxMessageBox("Couldn't connect to Radio with OmniRig. Make sure the rig ID and OmniRig configuration is correct.",
|
||||
wxT("Error"), wxOK | wxICON_ERROR, this);
|
||||
|
||||
|
|
@ -815,11 +815,11 @@ void ComPortsDlg::OnTest(wxCommandEvent& event) {
|
|||
});
|
||||
};
|
||||
|
||||
rig->onRigConnected += [=, this](IRigController*) {
|
||||
rig->onRigConnected += [=](IRigController*) {
|
||||
rig->ptt(true);
|
||||
};
|
||||
|
||||
rig->onPttChange += [=, this](IRigController*, bool state) {
|
||||
rig->onPttChange += [=](IRigController*, bool state) {
|
||||
if (state)
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
|
|
@ -831,7 +831,7 @@ void ComPortsDlg::OnTest(wxCommandEvent& event) {
|
|||
}
|
||||
};
|
||||
|
||||
std::thread omniRigThread([=, this]() {
|
||||
std::thread omniRigThread([=]() {
|
||||
rig->connect();
|
||||
|
||||
std::unique_lock<std::mutex> lk(*mtx);
|
||||
|
|
|
|||
61
src/main.cpp
61
src/main.cpp
|
|
@ -57,6 +57,7 @@
|
|||
#include "gui/dialogs/freedv_reporter.h"
|
||||
|
||||
#include "util/logging/ulog.h"
|
||||
#include "util/audio_spin_mutex.h"
|
||||
|
||||
#include "rade_api.h"
|
||||
|
||||
|
|
@ -103,6 +104,7 @@ float g_tone_phase;
|
|||
|
||||
// time averaged magnitude spectrum used for waterfall and spectrum display
|
||||
float g_avmag[MODEM_STATS_NSPEC];
|
||||
audio_spin_mutex g_avmag_mtx;
|
||||
|
||||
// TX level for attenuation
|
||||
int g_txLevel = 0;
|
||||
|
|
@ -124,7 +126,7 @@ struct FIFO *g_txDataInFifo;
|
|||
struct FIFO *g_rxDataOutFifo;
|
||||
|
||||
// tx/rx processing states
|
||||
int g_State, g_prev_State;
|
||||
std::atomic<int> g_State, g_prev_State;
|
||||
paCallBackData *g_rxUserdata;
|
||||
int g_dump_timing;
|
||||
int g_dump_fifo_state;
|
||||
|
|
@ -140,10 +142,10 @@ int g_nSoundCards;
|
|||
|
||||
// PortAudio over/underflow counters
|
||||
|
||||
int g_infifo1_full;
|
||||
int g_outfifo1_empty;
|
||||
int g_infifo2_full;
|
||||
int g_outfifo2_empty;
|
||||
std::atomic<int> g_infifo1_full;
|
||||
std::atomic<int> g_outfifo1_empty;
|
||||
std::atomic<int> g_infifo2_full;
|
||||
std::atomic<int> g_outfifo2_empty;
|
||||
int g_AEstatus1[4];
|
||||
int g_AEstatus2[4];
|
||||
|
||||
|
|
@ -1705,7 +1707,7 @@ void MainFrame::OnTimer(wxTimerEvent &evt)
|
|||
m_textSync->Refresh();
|
||||
}
|
||||
}
|
||||
g_prev_State = g_State;
|
||||
g_prev_State.store(g_State.load());
|
||||
|
||||
// send Callsign ----------------------------------------------------
|
||||
|
||||
|
|
@ -2838,8 +2840,10 @@ void MainFrame::startRxStream()
|
|||
log_debug("fifoSize_ms: %d infifo2: %d/outfilo2: %d",
|
||||
wxGetApp().appConfiguration.fifoSizeMs.get(), soundCard2InFifoSizeSamples, soundCard2OutFifoSizeSamples);
|
||||
|
||||
g_rxUserdata->tmpReadBuffer_ = std::make_unique<short[]>(std::max(soundCard1InFifoSizeSamples, soundCard2InFifoSizeSamples));
|
||||
g_rxUserdata->tmpWriteBuffer_ = std::make_unique<short[]>(std::max(soundCard1OutFifoSizeSamples, soundCard2OutFifoSizeSamples));
|
||||
g_rxUserdata->tmpReadRxBuffer_ = std::make_unique<short[]>(std::max(soundCard1InFifoSizeSamples, soundCard2InFifoSizeSamples));
|
||||
g_rxUserdata->tmpReadTxBuffer_ = std::make_unique<short[]>(std::max(soundCard1InFifoSizeSamples, soundCard2InFifoSizeSamples));
|
||||
g_rxUserdata->tmpWriteRxBuffer_ = std::make_unique<short[]>(std::max(soundCard1OutFifoSizeSamples, soundCard2OutFifoSizeSamples));
|
||||
g_rxUserdata->tmpWriteTxBuffer_ = std::make_unique<short[]>(std::max(soundCard1OutFifoSizeSamples, soundCard2OutFifoSizeSamples));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2848,8 +2852,10 @@ void MainFrame::startRxStream()
|
|||
g_rxUserdata->infifo2 = nullptr;
|
||||
g_rxUserdata->outfifo2 = nullptr;
|
||||
|
||||
g_rxUserdata->tmpReadBuffer_ = std::make_unique<short[]>(soundCard1InFifoSizeSamples);
|
||||
g_rxUserdata->tmpWriteBuffer_ = std::make_unique<short[]>(soundCard1OutFifoSizeSamples);
|
||||
g_rxUserdata->tmpReadRxBuffer_ = std::make_unique<short[]>(soundCard1InFifoSizeSamples);
|
||||
g_rxUserdata->tmpReadTxBuffer_ = std::make_unique<short[]>(soundCard1InFifoSizeSamples);
|
||||
g_rxUserdata->tmpWriteRxBuffer_ = std::make_unique<short[]>(soundCard1OutFifoSizeSamples);
|
||||
g_rxUserdata->tmpWriteTxBuffer_ = std::make_unique<short[]>(soundCard1OutFifoSizeSamples);
|
||||
}
|
||||
|
||||
log_debug("fifoSize_ms: %d infifo1: %d/outfilo1 %d",
|
||||
|
|
@ -3087,8 +3093,10 @@ void MainFrame::startRxStream()
|
|||
|
||||
// reset debug stats for FIFOs
|
||||
|
||||
g_infifo1_full = g_outfifo1_empty = g_infifo2_full = g_outfifo2_empty = 0;
|
||||
g_infifo1_full = g_outfifo1_empty = g_infifo2_full = g_outfifo2_empty = 0;
|
||||
g_infifo1_full.store(0, std::memory_order_release);
|
||||
g_outfifo1_empty.store(0, std::memory_order_release);
|
||||
g_infifo2_full.store(0, std::memory_order_release);
|
||||
g_outfifo2_empty.store(0, std::memory_order_release);
|
||||
for (int i=0; i<4; i++) {
|
||||
g_AEstatus1[i] = g_AEstatus2[i] = 0;
|
||||
}
|
||||
|
|
@ -3183,10 +3191,10 @@ void MainFrame::startRxStream()
|
|||
}
|
||||
else
|
||||
{
|
||||
rxOutSoundDevice->setOnAudioData([](IAudioDevice& dev, void* data, size_t size, void* state) {
|
||||
rxOutSoundDevice->setOnAudioData([](IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING {
|
||||
paCallBackData* cbData = static_cast<paCallBackData*>(state);
|
||||
short* audioData = static_cast<short*>(data);
|
||||
short* tmpOutput = cbData->tmpWriteBuffer_.get();
|
||||
short* tmpOutput = cbData->tmpWriteRxBuffer_.get();
|
||||
|
||||
auto toRead = std::min((size_t)cbData->outfifo1->numUsed(), size);
|
||||
if (toRead < size)
|
||||
|
|
@ -3589,11 +3597,11 @@ void MainFrame::OnAudioDeviceError_(IAudioDevice&, std::string error, void* stat
|
|||
castedState->CallAfter(&MainFrame::onAudioDeviceError_, error);
|
||||
}
|
||||
|
||||
void MainFrame::OnTxInAudioData_(IAudioDevice& dev, void* data, size_t size, void* state)
|
||||
void MainFrame::OnTxInAudioData_(IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING
|
||||
{
|
||||
paCallBackData* cbData = static_cast<paCallBackData*>(state);
|
||||
short* audioData = static_cast<short*>(data);
|
||||
short* tmpInput = cbData->tmpReadBuffer_.get();
|
||||
short* tmpInput = cbData->tmpReadTxBuffer_.get();
|
||||
|
||||
if (!endingTx)
|
||||
{
|
||||
|
|
@ -3604,21 +3612,21 @@ void MainFrame::OnTxInAudioData_(IAudioDevice& dev, void* data, size_t size, voi
|
|||
}
|
||||
if (cbData->infifo2->write(tmpInput, size))
|
||||
{
|
||||
g_infifo2_full++;
|
||||
g_infifo2_full.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::OnTxOutAudioData_(IAudioDevice& dev, void* data, size_t size, void* state)
|
||||
void MainFrame::OnTxOutAudioData_(IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING
|
||||
{
|
||||
paCallBackData* cbData = static_cast<paCallBackData*>(state);
|
||||
short* audioData = static_cast<short*>(data);
|
||||
short* tmpOutput = cbData->tmpWriteBuffer_.get();
|
||||
short* tmpOutput = cbData->tmpWriteTxBuffer_.get();
|
||||
|
||||
auto toRead = std::min((size_t)cbData->outfifo1->numUsed(), size);
|
||||
if (toRead < size)
|
||||
{
|
||||
g_outfifo1_empty++;
|
||||
g_outfifo1_empty.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
cbData->outfifo1->read(tmpOutput, toRead);
|
||||
|
|
@ -3645,11 +3653,11 @@ void MainFrame::OnTxOutAudioData_(IAudioDevice& dev, void* data, size_t size, vo
|
|||
}
|
||||
}
|
||||
|
||||
void MainFrame::OnRxInAudioData_(IAudioDevice& dev, void* data, size_t size, void* state)
|
||||
void MainFrame::OnRxInAudioData_(IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING
|
||||
{
|
||||
paCallBackData* cbData = static_cast<paCallBackData*>(state);
|
||||
short* audioData = static_cast<short*>(data);
|
||||
short* tmpInput = cbData->tmpReadBuffer_.get();
|
||||
short* tmpInput = cbData->tmpReadRxBuffer_.get();
|
||||
|
||||
auto numChannels = dev.getNumChannels();
|
||||
for (size_t i = 0; i < size; i++, audioData += numChannels)
|
||||
|
|
@ -3658,21 +3666,20 @@ void MainFrame::OnRxInAudioData_(IAudioDevice& dev, void* data, size_t size, voi
|
|||
}
|
||||
if (cbData->infifo1->write(tmpInput, size))
|
||||
{
|
||||
log_warn("RX FIFO full");
|
||||
g_infifo1_full++;
|
||||
g_infifo1_full.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::OnRxOutAudioData_(IAudioDevice& dev, void* data, size_t size, void* state)
|
||||
void MainFrame::OnRxOutAudioData_(IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING
|
||||
{
|
||||
paCallBackData* cbData = static_cast<paCallBackData*>(state);
|
||||
short* audioData = static_cast<short*>(data);
|
||||
short* tmpOutput = cbData->tmpWriteBuffer_.get();
|
||||
short* tmpOutput = cbData->tmpWriteRxBuffer_.get();
|
||||
|
||||
auto toRead = std::min((size_t)cbData->outfifo2->numUsed(), size);
|
||||
if (toRead < size)
|
||||
{
|
||||
g_outfifo2_empty++;
|
||||
g_outfifo2_empty.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
cbData->outfifo2->read(tmpOutput, toRead);
|
||||
|
|
|
|||
15
src/main.h
15
src/main.h
|
|
@ -91,6 +91,7 @@
|
|||
#include "config/FreeDVConfiguration.h"
|
||||
#include "pipeline/paCallbackData.h"
|
||||
#include "pipeline/LinkStep.h"
|
||||
#include "util/sanitizers.h"
|
||||
|
||||
#define _USE_TIMER 1
|
||||
#define _USE_ONIDLE 1
|
||||
|
|
@ -517,7 +518,7 @@ class MainFrame : public TopFrame
|
|||
bool m_newMicInFilter;
|
||||
bool m_newSpkOutFilter;
|
||||
|
||||
std::shared_ptr<void> designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q = 0.0, int sampleRate = 8000);
|
||||
void* designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q = 0.0, int sampleRate = 8000);
|
||||
void designEQFilters(paCallBackData *cb, int rxSampleRate, int txSampleRate);
|
||||
void deleteEQFilters(paCallBackData *cb);
|
||||
|
||||
|
|
@ -575,10 +576,10 @@ class MainFrame : public TopFrame
|
|||
void handleAudioDeviceChange_(std::string newDeviceName);
|
||||
|
||||
// Audio device data handlers
|
||||
static void OnTxInAudioData_(IAudioDevice& dev, void* data, size_t size, void* state);
|
||||
static void OnTxOutAudioData_(IAudioDevice& dev, void* data, size_t size, void* state);
|
||||
static void OnRxInAudioData_(IAudioDevice& dev, void* data, size_t size, void* state);
|
||||
static void OnRxOutAudioData_(IAudioDevice& dev, void* data, size_t size, void* state);
|
||||
static void OnTxInAudioData_(IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING;
|
||||
static void OnTxOutAudioData_(IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING;
|
||||
static void OnRxInAudioData_(IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING;
|
||||
static void OnRxOutAudioData_(IAudioDevice& dev, void* data, size_t size, void* state) FREEDV_NONBLOCKING;
|
||||
|
||||
// QSY request handling
|
||||
struct QsyRequestArgs {
|
||||
|
|
@ -591,7 +592,7 @@ class MainFrame : public TopFrame
|
|||
void onQsyRequestUIThread_(QsyRequestArgs* args);
|
||||
};
|
||||
|
||||
void resample_for_plot(struct FIFO *plotFifo, short buf[], short* dec_samples, int length, int fs);
|
||||
void resample_for_plot(struct FIFO *plotFifo, short buf[], short* dec_samples, int length, int fs) FREEDV_NONBLOCKING;
|
||||
|
||||
int resample(SRC_STATE *src,
|
||||
short output_short[],
|
||||
|
|
@ -614,6 +615,6 @@ void my_put_next_rx_char(void *callback_state, char c);
|
|||
|
||||
// helper complex freq shift function
|
||||
|
||||
void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin);
|
||||
void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin) FREEDV_NONBLOCKING;
|
||||
|
||||
#endif //__FDMDV2_MAIN__
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ extern int g_SquelchActive;
|
|||
extern float g_SquelchLevel;
|
||||
extern int g_analog;
|
||||
extern std::atomic<int> g_tx;
|
||||
extern int g_State, g_prev_State;
|
||||
extern std::atomic<int> g_State, g_prev_State;
|
||||
extern FreeDVInterface freedvInterface;
|
||||
extern bool g_queueResync;
|
||||
extern short *g_error_hist, *g_error_histn;
|
||||
|
|
|
|||
|
|
@ -92,17 +92,17 @@ AgcStep::~AgcStep()
|
|||
ebur128_destroy((ebur128_state**)&ebur128State_);
|
||||
}
|
||||
|
||||
int AgcStep::getInputSampleRate() const
|
||||
int AgcStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
int AgcStep::getOutputSampleRate() const
|
||||
int AgcStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
short* AgcStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* AgcStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
ebur128_state* state = static_cast<ebur128_state*>(ebur128State_);
|
||||
|
||||
|
|
@ -124,9 +124,15 @@ short* AgcStep::execute(short* inputSamples, int numInputSamples, int* numOutput
|
|||
|
||||
// Step 1: feed samples into ebur128 and return current
|
||||
// loudness in LUFS.
|
||||
ebur128_add_frames_short(state, tmpInput, numSamplesPerRun_);
|
||||
double lufs = 0.0;
|
||||
|
||||
// Note: libebur128 is unlikely to use RT-unsafe constructs in normal operation
|
||||
// (per existing RTSan-enabled tests). Verified on 2025-09-30.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
ebur128_add_frames_short(state, tmpInput, numSamplesPerRun_);
|
||||
auto result = ebur128_loudness_momentary(state, &lufs);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
if (result == EBUR128_SUCCESS && lufs != -HUGE_VAL && lufs > SILENCE_THRESHOLD_LUFS)
|
||||
{
|
||||
// Returned loudness is valid.
|
||||
|
|
@ -146,7 +152,6 @@ short* AgcStep::execute(short* inputSamples, int numInputSamples, int* numOutput
|
|||
agcInterval = AGC_RELEASE_TIME_SEC;
|
||||
}
|
||||
currentGainDb_ += ((targetGainDb_ - currentGainDb_) / agcInterval) * ((float)numSamplesPerRun_ / sampleRate_);
|
||||
//log_info("LUFS: %f, targetGain: %f, currentGain: %f", lufs, targetGainDb_, currentGainDb_);
|
||||
}
|
||||
|
||||
// Scale samples based on current gain.
|
||||
|
|
@ -161,14 +166,9 @@ short* AgcStep::execute(short* inputSamples, int numInputSamples, int* numOutput
|
|||
int inMicLevel = 0;
|
||||
short echo = 0;
|
||||
unsigned char saturationWarning = 1;
|
||||
auto status = WebRtcAgc_Process(
|
||||
WebRtcAgc_Process(
|
||||
agcState_, const_cast<const int16_t *const *>(&tmpInput), 1, numSamplesPerRun_,
|
||||
const_cast<int16_t *const *>(&tmpOutput), inMicLevel, &outMicLevel, echo, &saturationWarning);
|
||||
if (status != 0)
|
||||
{
|
||||
// XXX - not RT-safe
|
||||
log_error("Failed processing AGC (err = %d)", status);
|
||||
}
|
||||
tmpOutput += numSamplesPerRun_;
|
||||
}
|
||||
}
|
||||
|
|
@ -180,7 +180,7 @@ short* AgcStep::execute(short* inputSamples, int numInputSamples, int* numOutput
|
|||
return outputSamples;
|
||||
}
|
||||
|
||||
void AgcStep::reset()
|
||||
void AgcStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
inputSampleFifo_.reset();
|
||||
currentGainDb_ = 0;
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ public:
|
|||
AgcStep(int sampleRate);
|
||||
virtual ~AgcStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
int sampleRate_;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
//
|
||||
//=========================================================================
|
||||
|
||||
#include <cassert>
|
||||
#include "AudioPipeline.h"
|
||||
#include "../util/logging/ulog.h"
|
||||
|
||||
|
|
@ -35,12 +36,12 @@ AudioPipeline::~AudioPipeline()
|
|||
// empty, unique_ptr will automatically deallocate.
|
||||
}
|
||||
|
||||
int AudioPipeline::getInputSampleRate() const
|
||||
int AudioPipeline::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return inputSampleRate_;
|
||||
}
|
||||
|
||||
int AudioPipeline::getOutputSampleRate() const
|
||||
int AudioPipeline::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return outputSampleRate_;
|
||||
}
|
||||
|
|
@ -69,7 +70,7 @@ void AudioPipeline::dumpSetup() const
|
|||
log_debug("End at SR %d", getOutputSampleRate());
|
||||
}
|
||||
|
||||
short* AudioPipeline::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* AudioPipeline::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
short* tempInput = inputSamples;
|
||||
short* tempResult = inputSamples;
|
||||
|
|
@ -83,12 +84,7 @@ short* AudioPipeline::execute(short* inputSamples, int numInputSamples, int* num
|
|||
auto& step = pipelineSteps_[index];
|
||||
if (resampler != nullptr)
|
||||
{
|
||||
if (resampler->getOutputSampleRate() != step->getInputSampleRate())
|
||||
{
|
||||
resampler = nullptr;
|
||||
reloadResampler_(index);
|
||||
}
|
||||
|
||||
assert (resampler->getOutputSampleRate() == step->getInputSampleRate());
|
||||
tempResult = resampler->execute(tempInput, tempInputSamples, &tempOutputSamples);
|
||||
tempInput = tempResult;
|
||||
tempInputSamples = tempOutputSamples;
|
||||
|
|
@ -183,7 +179,7 @@ void AudioPipeline::reloadResultResampler_()
|
|||
}
|
||||
}
|
||||
|
||||
void AudioPipeline::reset()
|
||||
void AudioPipeline::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
for (auto& step : pipelineSteps_)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ public:
|
|||
AudioPipeline(int inputSampleRate, int outputSampleRate);
|
||||
virtual ~AudioPipeline();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
void appendPipelineStep(IPipelineStep* pipelineStep);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,10 +22,14 @@
|
|||
|
||||
#include "ComputeRfSpectrumStep.h"
|
||||
#include "../defines.h"
|
||||
#include "../util/audio_spin_mutex.h"
|
||||
|
||||
// TBD - find a way to not need this mutex
|
||||
extern audio_spin_mutex g_avmag_mtx;
|
||||
|
||||
ComputeRfSpectrumStep::ComputeRfSpectrumStep(
|
||||
std::function<struct MODEM_STATS*()> modemStatsFn,
|
||||
std::function<float*()> getAvMagFn)
|
||||
realtime_fp<struct MODEM_STATS*()> modemStatsFn,
|
||||
realtime_fp<float*()> getAvMagFn)
|
||||
: modemStatsFn_(modemStatsFn)
|
||||
, getAvMagFn_(getAvMagFn)
|
||||
{
|
||||
|
|
@ -42,17 +46,17 @@ ComputeRfSpectrumStep::~ComputeRfSpectrumStep()
|
|||
delete[] rxFdm_;
|
||||
}
|
||||
|
||||
int ComputeRfSpectrumStep::getInputSampleRate() const
|
||||
int ComputeRfSpectrumStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return FS;
|
||||
}
|
||||
|
||||
int ComputeRfSpectrumStep::getOutputSampleRate() const
|
||||
int ComputeRfSpectrumStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return FS;
|
||||
}
|
||||
|
||||
short* ComputeRfSpectrumStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* ComputeRfSpectrumStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
auto inputSamplesPtr = inputSamples;
|
||||
for (int i = 0; i < numInputSamples; i++)
|
||||
|
|
@ -63,10 +67,15 @@ short* ComputeRfSpectrumStep::execute(short* inputSamples, int numInputSamples,
|
|||
modem_stats_get_rx_spectrum(modemStatsFn_(), rxSpectrum_, rxFdm_, numInputSamples);
|
||||
|
||||
// Average rx spectrum data using a simple IIR low pass filter
|
||||
auto avMagPtr = getAvMagFn_();
|
||||
for(int i = 0; i < MODEM_STATS_NSPEC; i++)
|
||||
if (g_avmag_mtx.try_lock())
|
||||
{
|
||||
avMagPtr[i] = BETA * avMagPtr[i] + (1.0 - BETA) * rxSpectrum_[i];
|
||||
auto avMagPtr = getAvMagFn_();
|
||||
for(int i = 0; i < MODEM_STATS_NSPEC; i++)
|
||||
{
|
||||
avMagPtr[i] = BETA * avMagPtr[i] + (1.0 - BETA) * rxSpectrum_[i];
|
||||
}
|
||||
|
||||
g_avmag_mtx.unlock();
|
||||
}
|
||||
|
||||
// Tap only, no output.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "modem_stats.h"
|
||||
#include "IPipelineStep.h"
|
||||
#include "../util/realtime_fp.h"
|
||||
|
||||
class ComputeRfSpectrumStep : public IPipelineStep
|
||||
{
|
||||
|
|
@ -35,17 +36,17 @@ public:
|
|||
// Note: only supports 8 kHz, so needs to be inserted into an AudioPipeline
|
||||
// in order to downconvert properly.
|
||||
ComputeRfSpectrumStep(
|
||||
std::function<struct MODEM_STATS*()> modemStatsFn,
|
||||
std::function<float*()> getAvMagFn);
|
||||
realtime_fp<struct MODEM_STATS*()> modemStatsFn,
|
||||
realtime_fp<float*()> getAvMagFn);
|
||||
virtual ~ComputeRfSpectrumStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
std::function<struct MODEM_STATS*()> modemStatsFn_;
|
||||
std::function<float*()> getAvMagFn_;
|
||||
realtime_fp<struct MODEM_STATS*()> modemStatsFn_;
|
||||
realtime_fp<float*()> getAvMagFn_;
|
||||
float* rxSpectrum_;
|
||||
COMP* rxFdm_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include <assert.h>
|
||||
#include "EitherOrStep.h"
|
||||
|
||||
EitherOrStep::EitherOrStep(std::function<bool()> conditionalFn, IPipelineStep* trueStep, IPipelineStep* falseStep)
|
||||
EitherOrStep::EitherOrStep(realtime_fp<bool()> conditionalFn, IPipelineStep* trueStep, IPipelineStep* falseStep)
|
||||
: conditionalFn_(conditionalFn)
|
||||
, falseStep_(std::unique_ptr<IPipelineStep>(falseStep))
|
||||
, trueStep_(std::unique_ptr<IPipelineStep>(trueStep))
|
||||
|
|
@ -36,19 +36,19 @@ EitherOrStep::~EitherOrStep()
|
|||
// empty, shared_ptr will automatically clean up members
|
||||
}
|
||||
|
||||
int EitherOrStep::getInputSampleRate() const
|
||||
int EitherOrStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
assert(falseStep_->getInputSampleRate() == trueStep_->getInputSampleRate());
|
||||
return trueStep_->getInputSampleRate();
|
||||
}
|
||||
|
||||
int EitherOrStep::getOutputSampleRate() const
|
||||
int EitherOrStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
assert(falseStep_->getOutputSampleRate() == trueStep_->getOutputSampleRate());
|
||||
return trueStep_->getOutputSampleRate();
|
||||
}
|
||||
|
||||
short* EitherOrStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* EitherOrStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
bool condResult = conditionalFn_();
|
||||
if (condResult)
|
||||
|
|
@ -61,7 +61,7 @@ short* EitherOrStep::execute(short* inputSamples, int numInputSamples, int* numO
|
|||
}
|
||||
}
|
||||
|
||||
void EitherOrStep::reset()
|
||||
void EitherOrStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
trueStep_->reset();
|
||||
falseStep_->reset();
|
||||
|
|
|
|||
|
|
@ -23,23 +23,23 @@
|
|||
#ifndef AUDIO_PIPELINE__EITHER_OR_STEP_H
|
||||
#define AUDIO_PIPELINE__EITHER_OR_STEP_H
|
||||
|
||||
#include <functional>
|
||||
#include "IPipelineStep.h"
|
||||
#include "ResampleStep.h"
|
||||
#include "../util/realtime_fp.h"
|
||||
|
||||
class EitherOrStep : public IPipelineStep
|
||||
{
|
||||
public:
|
||||
EitherOrStep(std::function<bool()> conditionalFn, IPipelineStep* trueStep, IPipelineStep* falseStep);
|
||||
EitherOrStep(realtime_fp<bool()> conditionalFn, IPipelineStep* trueStep, IPipelineStep* falseStep);
|
||||
virtual ~EitherOrStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
std::function<bool()> conditionalFn_;
|
||||
realtime_fp<bool()> conditionalFn_;
|
||||
std::unique_ptr<IPipelineStep> falseStep_;
|
||||
std::unique_ptr<IPipelineStep> trueStep_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,13 +27,14 @@
|
|||
#include "../sox_biquad.h"
|
||||
#include <assert.h>
|
||||
|
||||
EqualizerStep::EqualizerStep(int sampleRate, bool* enableFilter, std::shared_ptr<void>* bassFilter, std::shared_ptr<void>* midFilter, std::shared_ptr<void>* trebleFilter, std::shared_ptr<void>* volFilter)
|
||||
EqualizerStep::EqualizerStep(int sampleRate, bool* enableFilter, void** bassFilter, void** midFilter, void** trebleFilter, void** volFilter, audio_spin_mutex& filterLock)
|
||||
: sampleRate_(sampleRate)
|
||||
, enableFilter_(enableFilter)
|
||||
, bassFilter_(bassFilter)
|
||||
, midFilter_(midFilter)
|
||||
, trebleFilter_(trebleFilter)
|
||||
, volFilter_(volFilter)
|
||||
, filterLock_(filterLock)
|
||||
{
|
||||
// Pre-allocate buffers so we don't have to do so during real-time operation.
|
||||
auto maxSamples = std::max(getInputSampleRate(), getOutputSampleRate());
|
||||
|
|
@ -46,55 +47,58 @@ EqualizerStep::~EqualizerStep()
|
|||
// empty
|
||||
}
|
||||
|
||||
int EqualizerStep::getInputSampleRate() const
|
||||
int EqualizerStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
int EqualizerStep::getOutputSampleRate() const
|
||||
int EqualizerStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
short* EqualizerStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* EqualizerStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
bool copiedToOutput = false;
|
||||
std::shared_ptr<void> tmpVolFilter = *volFilter_;
|
||||
|
||||
if (tmpVolFilter != nullptr)
|
||||
// Note: if we can't lock, an update is in progress. Just assume no filters enabled
|
||||
// until update completes.
|
||||
if (filterLock_.try_lock())
|
||||
{
|
||||
memcpy(outputSamples_.get(), inputSamples, sizeof(short)*numInputSamples);
|
||||
copiedToOutput = true;
|
||||
*numOutputSamples = numInputSamples;
|
||||
sox_biquad_filter(tmpVolFilter.get(), outputSamples_.get(), outputSamples_.get(), numInputSamples);
|
||||
}
|
||||
|
||||
if (*enableFilter_)
|
||||
{
|
||||
if (!copiedToOutput)
|
||||
if (*volFilter_ != nullptr)
|
||||
{
|
||||
memcpy(outputSamples_.get(), inputSamples, sizeof(short)*numInputSamples);
|
||||
copiedToOutput = true;
|
||||
*numOutputSamples = numInputSamples;
|
||||
sox_biquad_filter(*volFilter_, outputSamples_.get(), outputSamples_.get(), numInputSamples);
|
||||
}
|
||||
|
||||
if (*enableFilter_)
|
||||
{
|
||||
if (!copiedToOutput)
|
||||
{
|
||||
memcpy(outputSamples_.get(), inputSamples, sizeof(short)*numInputSamples);
|
||||
}
|
||||
|
||||
std::shared_ptr<void> tmpBassFilter = *bassFilter_;
|
||||
std::shared_ptr<void> tmpTrebleFilter = *trebleFilter_;
|
||||
std::shared_ptr<void> tmpMidFilter = *midFilter_;
|
||||
if (tmpBassFilter != nullptr)
|
||||
if (*bassFilter_ != nullptr)
|
||||
{
|
||||
sox_biquad_filter(*bassFilter_, outputSamples_.get(), outputSamples_.get(), numInputSamples);
|
||||
}
|
||||
if (*trebleFilter_ != nullptr)
|
||||
{
|
||||
sox_biquad_filter(*trebleFilter_, outputSamples_.get(), outputSamples_.get(), numInputSamples);
|
||||
}
|
||||
if (*midFilter_ != nullptr)
|
||||
{
|
||||
sox_biquad_filter(*midFilter_, outputSamples_.get(), outputSamples_.get(), numInputSamples);
|
||||
}
|
||||
}
|
||||
else if (!copiedToOutput)
|
||||
{
|
||||
sox_biquad_filter(tmpBassFilter.get(), outputSamples_.get(), outputSamples_.get(), numInputSamples);
|
||||
filterLock_.unlock();
|
||||
return inputSamples;
|
||||
}
|
||||
if (tmpTrebleFilter != nullptr)
|
||||
{
|
||||
sox_biquad_filter(tmpTrebleFilter.get(), outputSamples_.get(), outputSamples_.get(), numInputSamples);
|
||||
}
|
||||
if (tmpMidFilter != nullptr)
|
||||
{
|
||||
sox_biquad_filter(tmpMidFilter.get(), outputSamples_.get(), outputSamples_.get(), numInputSamples);
|
||||
}
|
||||
}
|
||||
else if (!copiedToOutput)
|
||||
{
|
||||
return inputSamples;
|
||||
filterLock_.unlock();
|
||||
}
|
||||
|
||||
return outputSamples_.get();
|
||||
|
|
|
|||
|
|
@ -25,24 +25,26 @@
|
|||
|
||||
#include "IPipelineStep.h"
|
||||
#include <memory>
|
||||
#include "../util/audio_spin_mutex.h"
|
||||
|
||||
class EqualizerStep : public IPipelineStep
|
||||
{
|
||||
public:
|
||||
EqualizerStep(int sampleRate, bool* enableFilter, std::shared_ptr<void>* bassFilter, std::shared_ptr<void>* midFilter, std::shared_ptr<void>* trebleFilter, std::shared_ptr<void>* volFilter);
|
||||
EqualizerStep(int sampleRate, bool* enableFilter, void** bassFilter, void** midFilter, void** trebleFilter, void** volFilter, audio_spin_mutex& filterLock);
|
||||
virtual ~EqualizerStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
int sampleRate_;
|
||||
bool* enableFilter_;
|
||||
std::shared_ptr<void>* bassFilter_;
|
||||
std::shared_ptr<void>* midFilter_;
|
||||
std::shared_ptr<void>* trebleFilter_;
|
||||
std::shared_ptr<void>* volFilter_;
|
||||
void** bassFilter_;
|
||||
void** midFilter_;
|
||||
void** trebleFilter_;
|
||||
void** volFilter_;
|
||||
audio_spin_mutex& filterLock_;
|
||||
std::unique_ptr<short[]> outputSamples_;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include "codec2_fdmdv.h"
|
||||
#include "../defines.h"
|
||||
|
||||
extern void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin);
|
||||
extern void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin) FREEDV_NONBLOCKING;
|
||||
|
||||
FreeDVReceiveStep::FreeDVReceiveStep(struct freedv* dv)
|
||||
: dv_(dv)
|
||||
|
|
@ -73,17 +73,17 @@ FreeDVReceiveStep::~FreeDVReceiveStep()
|
|||
}
|
||||
}
|
||||
|
||||
int FreeDVReceiveStep::getInputSampleRate() const
|
||||
int FreeDVReceiveStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return freedv_get_modem_sample_rate(dv_);
|
||||
}
|
||||
|
||||
int FreeDVReceiveStep::getOutputSampleRate() const
|
||||
int FreeDVReceiveStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return freedv_get_speech_sample_rate(dv_);
|
||||
}
|
||||
|
||||
short* FreeDVReceiveStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* FreeDVReceiveStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
auto maxSamples = std::max(getInputSampleRate(), getOutputSampleRate());
|
||||
auto maxSpeechSamples = freedv_get_n_max_speech_samples(dv_);
|
||||
|
|
@ -114,7 +114,12 @@ short* FreeDVReceiveStep::execute(short* inputSamples, int numInputSamples, int*
|
|||
|
||||
// Optional frequency shifting
|
||||
freq_shift_coh(rxFdmOffset_, rxFdm_, freqOffsetHz_, freedv_get_modem_sample_rate(dv_), &rxFreqOffsetPhaseRectObjs_, nin);
|
||||
|
||||
// Legacy modes, marked safe due to use of o1alloc().
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
nout = freedv_comprx(dv_, outputSamples_.get() + *numOutputSamples, rxFdmOffset_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
*numOutputSamples += nout;
|
||||
|
||||
nin = freedv_nin(dv_);
|
||||
|
|
@ -125,7 +130,7 @@ short* FreeDVReceiveStep::execute(short* inputSamples, int numInputSamples, int*
|
|||
return outputSamples_.get();
|
||||
}
|
||||
|
||||
void FreeDVReceiveStep::reset()
|
||||
void FreeDVReceiveStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
while (codec2_fifo_used(inputSampleFifo_) > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@ public:
|
|||
FreeDVReceiveStep(struct freedv* dv);
|
||||
virtual ~FreeDVReceiveStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
void setSigPwrAvg(float newVal) { sigPwrAvg_ = newVal; }
|
||||
float getSigPwrAvg() const { return sigPwrAvg_; }
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@
|
|||
#include "FreeDVTransmitStep.h"
|
||||
#include "freedv_api.h"
|
||||
|
||||
extern void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin);
|
||||
extern void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin) FREEDV_NONBLOCKING;
|
||||
|
||||
FreeDVTransmitStep::FreeDVTransmitStep(struct freedv* dv, std::function<float()> getFreqOffsetFn)
|
||||
FreeDVTransmitStep::FreeDVTransmitStep(struct freedv* dv, realtime_fp<float()> getFreqOffsetFn)
|
||||
: dv_(dv)
|
||||
, getFreqOffsetFn_(getFreqOffsetFn)
|
||||
, inputSampleFifo_(nullptr)
|
||||
|
|
@ -74,17 +74,17 @@ FreeDVTransmitStep::~FreeDVTransmitStep()
|
|||
}
|
||||
}
|
||||
|
||||
int FreeDVTransmitStep::getInputSampleRate() const
|
||||
int FreeDVTransmitStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return freedv_get_speech_sample_rate(dv_);
|
||||
}
|
||||
|
||||
int FreeDVTransmitStep::getOutputSampleRate() const
|
||||
int FreeDVTransmitStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return freedv_get_modem_sample_rate(dv_);
|
||||
}
|
||||
|
||||
short* FreeDVTransmitStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* FreeDVTransmitStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
auto maxSamples = std::max(getInputSampleRate(), getOutputSampleRate());
|
||||
int mode = freedv_get_mode(dv_);
|
||||
|
|
@ -106,11 +106,17 @@ short* FreeDVTransmitStep::execute(short* inputSamples, int numInputSamples, int
|
|||
if (mode == FREEDV_MODE_800XA)
|
||||
{
|
||||
/* 800XA doesn't support complex output just yet */
|
||||
// Legacy modes, marked safe due to use of o1alloc.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
freedv_tx(dv_, tmpOutput_, codecInput_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
// Legacy modes, marked safe due to use of o1alloc.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
freedv_comptx(dv_, txFdm_, codecInput_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
freq_shift_coh(txFdmOffset_, txFdm_, getFreqOffsetFn_(), getOutputSampleRate(), &txFreqOffsetPhaseRectObj_, nfreedv);
|
||||
for(int i = 0; i<nfreedv; i++)
|
||||
|
|
@ -125,7 +131,7 @@ short* FreeDVTransmitStep::execute(short* inputSamples, int numInputSamples, int
|
|||
return outputSamples_.get();
|
||||
}
|
||||
|
||||
void FreeDVTransmitStep::reset()
|
||||
void FreeDVTransmitStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
while (codec2_fifo_used(inputSampleFifo_) > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,17 +38,17 @@ extern "C"
|
|||
class FreeDVTransmitStep : public IPipelineStep
|
||||
{
|
||||
public:
|
||||
FreeDVTransmitStep(struct freedv* dv, std::function<float()> getFreqOffsetFn);
|
||||
FreeDVTransmitStep(struct freedv* dv, realtime_fp<float()> getFreqOffsetFn);
|
||||
virtual ~FreeDVTransmitStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
struct freedv* dv_;
|
||||
std::function<float()> getFreqOffsetFn_;
|
||||
realtime_fp<float()> getFreqOffsetFn_;
|
||||
struct FIFO* inputSampleFifo_;
|
||||
COMP txFreqOffsetPhaseRectObj_;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,16 +23,18 @@
|
|||
#ifndef AUDIO_PIPELINE__I_PIPELINE_STEP_H
|
||||
#define AUDIO_PIPELINE__I_PIPELINE_STEP_H
|
||||
|
||||
#include "../util/sanitizers.h"
|
||||
|
||||
class IPipelineStep
|
||||
{
|
||||
public:
|
||||
virtual ~IPipelineStep();
|
||||
|
||||
// Returns required input sample rate.
|
||||
virtual int getInputSampleRate() const = 0;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING = 0;
|
||||
|
||||
// Returns output sample rate after performing the pipeline step.
|
||||
virtual int getOutputSampleRate() const = 0;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING = 0;
|
||||
|
||||
// Executes pipeline step.
|
||||
// Required parameters:
|
||||
|
|
@ -40,11 +42,10 @@ public:
|
|||
// numInputSamples: Number of samples in the input array.
|
||||
// numOutputSamples: Location to store number of output samples.
|
||||
// Returns: Array of int16 values corresponding to result audio.
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) = 0;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING = 0;
|
||||
|
||||
// Resets internal state of the pipeline step.
|
||||
virtual void reset() { /* empty */ }
|
||||
virtual void reset() FREEDV_NONBLOCKING { /* empty */ }
|
||||
};
|
||||
|
||||
|
||||
#endif // AUDIO_PIPELINE__I_PIPELINE_STEP_H
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@
|
|||
|
||||
#include "LevelAdjustStep.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
|
||||
LevelAdjustStep::LevelAdjustStep(int sampleRate, std::function<float()> scaleFactorFn)
|
||||
LevelAdjustStep::LevelAdjustStep(int sampleRate, realtime_fp<float()> scaleFactorFn)
|
||||
: scaleFactorFn_(scaleFactorFn)
|
||||
, sampleRate_(sampleRate)
|
||||
{
|
||||
|
|
@ -39,17 +40,17 @@ LevelAdjustStep::~LevelAdjustStep()
|
|||
// empty
|
||||
}
|
||||
|
||||
int LevelAdjustStep::getInputSampleRate() const
|
||||
int LevelAdjustStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
int LevelAdjustStep::getOutputSampleRate() const
|
||||
int LevelAdjustStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
short* LevelAdjustStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* LevelAdjustStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
float scaleFactor = scaleFactorFn_();
|
||||
short* outPtr = outputSamples_.get();
|
||||
|
|
|
|||
|
|
@ -23,23 +23,23 @@
|
|||
#ifndef AUDIO_PIPELINE__LEVEL_ADJUST_STEP_H
|
||||
#define AUDIO_PIPELINE__LEVEL_ADJUST_STEP_H
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "IPipelineStep.h"
|
||||
#include "../util/realtime_fp.h"
|
||||
|
||||
class LevelAdjustStep : public IPipelineStep
|
||||
{
|
||||
public:
|
||||
LevelAdjustStep(int sampleRate, std::function<float()> scaleFactorFn);
|
||||
LevelAdjustStep(int sampleRate, realtime_fp<float()> scaleFactorFn);
|
||||
virtual ~LevelAdjustStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
std::function<float()> scaleFactorFn_;
|
||||
realtime_fp<float()> scaleFactorFn_;
|
||||
int sampleRate_;
|
||||
std::unique_ptr<short[]> outputSamples_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ LinkStep::~LinkStep()
|
|||
delete[] tmpBuffer_;
|
||||
}
|
||||
|
||||
void LinkStep::clearFifo()
|
||||
void LinkStep::clearFifo() FREEDV_NONBLOCKING
|
||||
{
|
||||
fifo_.reset();
|
||||
}
|
||||
|
||||
short* LinkStep::InputStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* LinkStep::InputStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
auto& fifo = parent_->getFifo();
|
||||
auto samplePtr = inputSamples;
|
||||
|
|
@ -57,7 +57,7 @@ short* LinkStep::InputStep::execute(short* inputSamples, int numInputSamples, in
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
short* LinkStep::OutputStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* LinkStep::OutputStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
auto& fifo = parent_->getFifo();
|
||||
*numOutputSamples = numInputSamples > 0 ? std::min(fifo.numUsed(), numInputSamples) : fifo.numUsed();
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public:
|
|||
int getSampleRate() const { return sampleRate_; }
|
||||
GenericFIFO<short>& getFifo() { return fifo_; }
|
||||
|
||||
void clearFifo();
|
||||
void clearFifo() FREEDV_NONBLOCKING;
|
||||
|
||||
private:
|
||||
class InputStep : public IPipelineStep
|
||||
|
|
@ -63,10 +63,10 @@ private:
|
|||
virtual ~InputStep() = default;
|
||||
|
||||
// Returns required input sample rate.
|
||||
virtual int getInputSampleRate() const override { return parent_->getSampleRate(); }
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override { return parent_->getSampleRate(); }
|
||||
|
||||
// Returns output sample rate after performing the pipeline step.
|
||||
virtual int getOutputSampleRate() const override { return parent_->getSampleRate(); }
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override { return parent_->getSampleRate(); }
|
||||
|
||||
// Executes pipeline step.
|
||||
// Required parameters:
|
||||
|
|
@ -74,7 +74,7 @@ private:
|
|||
// numInputSamples: Number of samples in the input array.
|
||||
// numOutputSamples: Location to store number of output samples.
|
||||
// Returns: Array of int16 values corresponding to result audio.
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
LinkStep* parent_;
|
||||
|
|
@ -95,10 +95,10 @@ private:
|
|||
virtual ~OutputStep() = default;
|
||||
|
||||
// Returns required input sample rate.
|
||||
virtual int getInputSampleRate() const override { return parent_->getSampleRate(); }
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override { return parent_->getSampleRate(); }
|
||||
|
||||
// Returns output sample rate after performing the pipeline step.
|
||||
virtual int getOutputSampleRate() const override { return parent_->getSampleRate(); }
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override { return parent_->getSampleRate(); }
|
||||
|
||||
// Executes pipeline step.
|
||||
// Required parameters:
|
||||
|
|
@ -106,7 +106,7 @@ private:
|
|||
// numInputSamples: Number of samples in the input array.
|
||||
// numOutputSamples: Location to store number of output samples.
|
||||
// Returns: Array of int16 values corresponding to result audio.
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
LinkStep* parent_;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ MuteStep::MuteStep(int outputSampleRate)
|
|||
// numInputSamples: Number of samples in the input array.
|
||||
// numOutputSamples: Location to store number of output samples.
|
||||
// Returns: Array of int16 values corresponding to result audio.
|
||||
short* MuteStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* MuteStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
*numOutputSamples = numInputSamples;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ public:
|
|||
virtual ~MuteStep() = default;
|
||||
|
||||
// Returns required input sample rate.
|
||||
virtual int getInputSampleRate() const override { return sampleRate_; }
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override { return sampleRate_; }
|
||||
|
||||
// Returns output sample rate after performing the pipeline step.
|
||||
virtual int getOutputSampleRate() const override { return sampleRate_; }
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override { return sampleRate_; }
|
||||
|
||||
// Executes pipeline step.
|
||||
// Required parameters:
|
||||
|
|
@ -44,7 +44,7 @@ public:
|
|||
// numInputSamples: Number of samples in the input array.
|
||||
// numOutputSamples: Location to store number of output samples.
|
||||
// Returns: Array of int16 values corresponding to result audio.
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
int sampleRate_;
|
||||
|
|
|
|||
|
|
@ -35,10 +35,11 @@ using namespace std::chrono_literals;
|
|||
ParallelStep::ParallelStep(
|
||||
int inputSampleRate, int outputSampleRate,
|
||||
bool runMultiThreaded,
|
||||
std::function<int(ParallelStep*)> inputRouteFn,
|
||||
std::function<int(ParallelStep*)> outputRouteFn,
|
||||
realtime_fp<int(ParallelStep*)> inputRouteFn,
|
||||
realtime_fp<int(ParallelStep*)> outputRouteFn,
|
||||
std::vector<IPipelineStep*> parallelSteps,
|
||||
std::shared_ptr<void> state,
|
||||
void* callbackState,
|
||||
std::shared_ptr<IRealtimeHelper> realtimeHelper)
|
||||
: inputSampleRate_(inputSampleRate)
|
||||
, outputSampleRate_(outputSampleRate)
|
||||
|
|
@ -47,6 +48,7 @@ ParallelStep::ParallelStep(
|
|||
, outputRouteFn_(outputRouteFn)
|
||||
, realtimeHelper_(realtimeHelper)
|
||||
, state_(state)
|
||||
, callbackState_(callbackState)
|
||||
{
|
||||
for (auto& step : parallelSteps)
|
||||
{
|
||||
|
|
@ -195,17 +197,17 @@ ParallelStep::~ParallelStep()
|
|||
parallelSteps_.clear();
|
||||
}
|
||||
|
||||
int ParallelStep::getInputSampleRate() const
|
||||
int ParallelStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return inputSampleRate_;
|
||||
}
|
||||
|
||||
int ParallelStep::getOutputSampleRate() const
|
||||
int ParallelStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return outputSampleRate_;
|
||||
}
|
||||
|
||||
short* ParallelStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* ParallelStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
// Step 1: determine what steps to execute.
|
||||
auto stepToExecute = inputRouteFn_(this);
|
||||
|
|
@ -226,6 +228,7 @@ short* ParallelStep::execute(short* inputSamples, int numInputSamples, int* numO
|
|||
else
|
||||
{
|
||||
// Wake up thread
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
#if defined(_WIN32)
|
||||
if (threadInfo->sem != nullptr)
|
||||
{
|
||||
|
|
@ -239,6 +242,7 @@ short* ParallelStep::execute(short* inputSamples, int numInputSamples, int* numO
|
|||
#else
|
||||
sem_post(&threadInfo->sem);
|
||||
#endif // defined(_WIN32) || defined(__APPLE__)
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
|
||||
if (stepToExecute != -1) break;
|
||||
|
|
@ -288,7 +292,7 @@ void ParallelStep::executeRunnerThread_(ThreadInfo* threadState) noexcept
|
|||
} while (samplesIn > 0 && !threadState->exitingThread);
|
||||
}
|
||||
|
||||
void ParallelStep::reset()
|
||||
void ParallelStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
for (size_t index = 0; index < threads_.size(); index++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#endif // defined(_WIN32) || defined(__APPLE__)
|
||||
|
||||
#include "../util/IRealtimeHelper.h"
|
||||
#include "../util/realtime_fp.h"
|
||||
#include "codec2_fifo.h"
|
||||
|
||||
class ParallelStep : public IPipelineStep
|
||||
|
|
@ -49,22 +50,24 @@ public:
|
|||
ParallelStep(
|
||||
int inputSampleRate, int outputSampleRate,
|
||||
bool runMultiThreaded,
|
||||
std::function<int(ParallelStep*)> inputRouteFn,
|
||||
std::function<int(ParallelStep*)> outputRouteFn,
|
||||
realtime_fp<int(ParallelStep*)> inputRouteFn,
|
||||
realtime_fp<int(ParallelStep*)> outputRouteFn,
|
||||
std::vector<IPipelineStep*> parallelSteps,
|
||||
std::shared_ptr<void> state,
|
||||
void* callbackState,
|
||||
std::shared_ptr<IRealtimeHelper> realtimeHelper);
|
||||
virtual ~ParallelStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
const std::vector<IPipelineStep*>& getParallelSteps() const { return parallelSteps_; }
|
||||
|
||||
std::shared_ptr<void> getState() { return state_; }
|
||||
|
||||
void* getState() { return state_.get(); }
|
||||
void* getCallbackState() const { return callbackState_; }
|
||||
|
||||
private:
|
||||
struct ThreadInfo
|
||||
{
|
||||
|
|
@ -88,12 +91,13 @@ private:
|
|||
int inputSampleRate_;
|
||||
int outputSampleRate_;
|
||||
bool runMultiThreaded_;
|
||||
std::function<int(ParallelStep*)> inputRouteFn_;
|
||||
std::function<int(ParallelStep*)> outputRouteFn_;
|
||||
realtime_fp<int(ParallelStep*)> inputRouteFn_;
|
||||
realtime_fp<int(ParallelStep*)> outputRouteFn_;
|
||||
std::vector<ThreadInfo*> threads_;
|
||||
std::shared_ptr<IRealtimeHelper> realtimeHelper_;
|
||||
std::shared_ptr<void> state_;
|
||||
std::vector<IPipelineStep*> parallelSteps_;
|
||||
void* callbackState_;
|
||||
|
||||
void executeRunnerThread_(ThreadInfo* threadState) noexcept
|
||||
#if defined(__clang__)
|
||||
|
|
|
|||
|
|
@ -68,17 +68,17 @@ PlaybackStep::~PlaybackStep()
|
|||
}
|
||||
}
|
||||
|
||||
int PlaybackStep::getInputSampleRate() const
|
||||
int PlaybackStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return inputSampleRate_;
|
||||
}
|
||||
|
||||
int PlaybackStep::getOutputSampleRate() const
|
||||
int PlaybackStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return inputSampleRate_;
|
||||
}
|
||||
|
||||
short* PlaybackStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* PlaybackStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
unsigned int nsf = numInputSamples * getOutputSampleRate()/getInputSampleRate();
|
||||
*numOutputSamples = std::min((unsigned int)outputFifo_.numUsed(), nsf);
|
||||
|
|
@ -183,7 +183,7 @@ void PlaybackStep::nonRtThreadEntry_()
|
|||
}
|
||||
}
|
||||
|
||||
void PlaybackStep::reset()
|
||||
void PlaybackStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
outputFifo_.reset();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <sndfile.h>
|
||||
#include "../util/GenericFIFO.h"
|
||||
#include "../util/Semaphore.h"
|
||||
|
|
@ -40,10 +41,10 @@ public:
|
|||
std::function<SNDFILE*()> getSndFileFn, std::function<void()> fileCompleteFn);
|
||||
virtual ~PlaybackStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
int inputSampleRate_;
|
||||
|
|
@ -52,7 +53,7 @@ private:
|
|||
std::function<void()> fileCompleteFn_;
|
||||
std::unique_ptr<short[]> outputSamples_;
|
||||
std::thread nonRtThread_;
|
||||
bool nonRtThreadEnding_;
|
||||
std::atomic<bool> nonRtThreadEnding_;
|
||||
Semaphore fileIoThreadSem_;
|
||||
ResampleStep* playbackResampler_;
|
||||
GenericFIFO<short> outputFifo_;
|
||||
|
|
|
|||
|
|
@ -20,17 +20,12 @@
|
|||
//
|
||||
//=========================================================================
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#include <sanitizer/rtsan_interface.h>
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include "RADEReceiveStep.h"
|
||||
#include "../defines.h"
|
||||
#include "lpcnet.h" // from Opus source tree
|
||||
#include "../util/realtime_fp.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <pthread.h>
|
||||
|
|
@ -43,7 +38,7 @@ extern wxString utRxFeatureFile;
|
|||
|
||||
#define FEATURE_FIFO_SIZE ((RADE_SPEECH_SAMPLE_RATE / LPCNET_FRAME_SIZE) * rade_n_features_in_out(dv_))
|
||||
|
||||
RADEReceiveStep::RADEReceiveStep(struct rade* dv, FARGANState* fargan, rade_text_t textPtr, std::function<void(RADEReceiveStep*)> syncFn)
|
||||
RADEReceiveStep::RADEReceiveStep(struct rade* dv, FARGANState* fargan, rade_text_t textPtr, realtime_fp<void(RADEReceiveStep*)> syncFn)
|
||||
: dv_(dv)
|
||||
, fargan_(fargan)
|
||||
, pendingFeatures_(nullptr)
|
||||
|
|
@ -107,28 +102,33 @@ RADEReceiveStep::~RADEReceiveStep()
|
|||
}
|
||||
}
|
||||
|
||||
int RADEReceiveStep::getInputSampleRate() const
|
||||
int RADEReceiveStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return RADE_MODEM_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
int RADEReceiveStep::getOutputSampleRate() const
|
||||
int RADEReceiveStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return RADE_SPEECH_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
short* RADEReceiveStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* RADEReceiveStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
auto maxSamples = std::max(getInputSampleRate(), getOutputSampleRate());
|
||||
*numOutputSamples = 0;
|
||||
|
||||
inputSampleFifo_.write(inputSamples, numInputSamples);
|
||||
|
||||
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
int nin = rade_nin(dv_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
int nout = 0;
|
||||
while ((*numOutputSamples + LPCNET_FRAME_SIZE) < maxSamples && inputSampleFifo_.read(inputBuf_, nin) == 0)
|
||||
{
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
assert(nin <= rade_nin_max(dv_));
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
// demod per frame processing
|
||||
for(int i=0; i<nin; i++)
|
||||
|
|
@ -140,37 +140,18 @@ short* RADEReceiveStep::execute(short* inputSamples, int numInputSamples, int* n
|
|||
// RADE processing (input signal->features).
|
||||
int hasEooOut = 0;
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_disable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
|
||||
nout = rade_rx(dv_, featuresOut_, &hasEooOut, eooOut_, inputBufCplx_);
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_enable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
FREEDV_BEGIN_REALTIME_UNSAFE
|
||||
nout = rade_rx(dv_, featuresOut_, &hasEooOut, eooOut_, inputBufCplx_);
|
||||
FREEDV_END_REALTIME_UNSAFE
|
||||
|
||||
if (hasEooOut && textPtr_ != nullptr)
|
||||
{
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_disable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
FREEDV_BEGIN_REALTIME_UNSAFE
|
||||
|
||||
// Handle RX of bits from EOO.
|
||||
rade_text_rx(textPtr_, eooOut_, rade_n_eoo_bits(dv_) / 2);
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_enable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
|
||||
FREEDV_END_REALTIME_UNSAFE
|
||||
}
|
||||
else if (!hasEooOut)
|
||||
{
|
||||
|
|
@ -193,7 +174,9 @@ short* RADEReceiveStep::execute(short* inputSamples, int numInputSamples, int* n
|
|||
// FARGAN processing (features->analog audio)
|
||||
float fpcm[LPCNET_FRAME_SIZE];
|
||||
short pcm[LPCNET_FRAME_SIZE];
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
fargan_synthesize(fargan_, fpcm, pendingFeatures_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
for (int i = 0; i < LPCNET_FRAME_SIZE; i++)
|
||||
{
|
||||
pcm[i] = (int)floor(.5 + MIN32(32767, MAX32(-32767, 32768.f*fpcm[i])));
|
||||
|
|
@ -205,7 +188,9 @@ short* RADEReceiveStep::execute(short* inputSamples, int numInputSamples, int* n
|
|||
}
|
||||
}
|
||||
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
nin = rade_nin(dv_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
|
||||
if (*numOutputSamples > 0)
|
||||
|
|
@ -213,27 +198,23 @@ short* RADEReceiveStep::execute(short* inputSamples, int numInputSamples, int* n
|
|||
outputSampleFifo_.read(outputSamples_.get(), *numOutputSamples);
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_disable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
int sync = 0;
|
||||
int snr = 0;
|
||||
|
||||
syncState_.store(rade_sync(dv_), std::memory_order_release);
|
||||
snr_.store(rade_snrdB_3k_est(dv_), std::memory_order_release);
|
||||
FREEDV_BEGIN_REALTIME_UNSAFE
|
||||
sync = rade_sync(dv_);
|
||||
snr = rade_snrdB_3k_est(dv_);
|
||||
FREEDV_END_REALTIME_UNSAFE
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_enable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
syncState_.store(sync, std::memory_order_release);
|
||||
snr_.store(snr, std::memory_order_release);
|
||||
|
||||
syncFn_(this);
|
||||
|
||||
return outputSamples_.get();
|
||||
}
|
||||
|
||||
void RADEReceiveStep::reset()
|
||||
void RADEReceiveStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
inputSampleFifo_.reset();
|
||||
outputSampleFifo_.reset();
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "IPipelineStep.h"
|
||||
#include "../freedv_interface.h"
|
||||
|
|
@ -33,6 +34,7 @@
|
|||
#include "rade_text.h"
|
||||
#include "../util/GenericFIFO.h"
|
||||
#include "../util/Semaphore.h"
|
||||
#include "../util/realtime_fp.h"
|
||||
|
||||
// Number of features to store. This is set to be close to the
|
||||
// typical size for RX/TX features for the rade_loss ctest to
|
||||
|
|
@ -48,17 +50,21 @@ extern "C"
|
|||
class RADEReceiveStep : public IPipelineStep
|
||||
{
|
||||
public:
|
||||
RADEReceiveStep(struct rade* dv, FARGANState* fargan, rade_text_t textPtr, std::function<void(RADEReceiveStep*)> syncFn);
|
||||
RADEReceiveStep(struct rade* dv, FARGANState* fargan, rade_text_t textPtr, realtime_fp<void(RADEReceiveStep*)> syncFn);
|
||||
virtual ~RADEReceiveStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
int getSync() const { return syncState_.load(std::memory_order_acquire); }
|
||||
int getSnr() const { return snr_.load(std::memory_order_acquire); }
|
||||
|
||||
realtime_fp<std::atomic<int>*()> getRxStateFn() { return rxStateFn_; }
|
||||
void setRxStateFn(realtime_fp<std::atomic<int>*()> rxFn) { rxStateFn_ = rxFn; }
|
||||
void* getStateObj() const { return stateObj_; }
|
||||
void setStateObj(void* state) { stateObj_ = state; }
|
||||
|
||||
private:
|
||||
std::atomic<int> syncState_;
|
||||
std::atomic<int> snr_;
|
||||
|
|
@ -70,8 +76,7 @@ private:
|
|||
int pendingFeaturesIdx_;
|
||||
FILE* featuresFile_;
|
||||
rade_text_t textPtr_;
|
||||
std::function<void(RADEReceiveStep*)> syncFn_;
|
||||
|
||||
realtime_fp<void(RADEReceiveStep*)> syncFn_;
|
||||
RADE_COMP* inputBufCplx_;
|
||||
short* inputBuf_;
|
||||
float* featuresOut_;
|
||||
|
|
@ -82,7 +87,10 @@ private:
|
|||
std::thread utFeatureThread_;
|
||||
bool exitingFeatureThread_;
|
||||
Semaphore featuresAvailableSem_;
|
||||
|
||||
|
||||
realtime_fp<std::atomic<int>*()> rxStateFn_;
|
||||
void* stateObj_;
|
||||
|
||||
void utFeatureThreadEntry_();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,6 @@
|
|||
//
|
||||
//=========================================================================
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#include <sanitizer/rtsan_interface.h>
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
|
@ -114,20 +108,22 @@ RADETransmitStep::~RADETransmitStep()
|
|||
}
|
||||
}
|
||||
|
||||
int RADETransmitStep::getInputSampleRate() const
|
||||
int RADETransmitStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return RADE_SPEECH_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
int RADETransmitStep::getOutputSampleRate() const
|
||||
int RADETransmitStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return RADE_MODEM_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
short* RADETransmitStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* RADETransmitStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
auto maxSamples = std::max(getInputSampleRate(), getOutputSampleRate());
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
int numSamplesPerTx = rade_n_tx_out(dv_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
*numOutputSamples = 0;
|
||||
|
||||
|
|
@ -146,13 +142,18 @@ short* RADETransmitStep::execute(short* inputSamples, int numInputSamples, int*
|
|||
inputSampleFifo_.write(inputSamples, numInputSamples);
|
||||
while ((*numOutputSamples + numSamplesPerTx) < maxSamples && inputSampleFifo_.numUsed() >= LPCNET_FRAME_SIZE)
|
||||
{
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
int numRequiredFeaturesForRADE = rade_n_features_in_out(dv_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
short pcm[LPCNET_FRAME_SIZE];
|
||||
float features[NB_TOTAL_FEATURES];
|
||||
|
||||
// Feature extraction
|
||||
inputSampleFifo_.read(pcm, LPCNET_FRAME_SIZE);
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
lpcnet_compute_single_frame_features(encState_, pcm, features, arch_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
if (featuresFile_)
|
||||
{
|
||||
|
|
@ -171,19 +172,9 @@ short* RADETransmitStep::execute(short* inputSamples, int numInputSamples, int*
|
|||
featureListIdx_ = 0;
|
||||
|
||||
// RADE TX handling
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_disable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
|
||||
rade_tx(dv_, radeOut_, &featureList_[0]);
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_enable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
FREEDV_BEGIN_REALTIME_UNSAFE
|
||||
rade_tx(dv_, radeOut_, &featureList_[0]);
|
||||
FREEDV_END_REALTIME_UNSAFE
|
||||
|
||||
for (int index = 0; index < numSamplesPerTx; index++)
|
||||
{
|
||||
|
|
@ -205,25 +196,17 @@ short* RADETransmitStep::execute(short* inputSamples, int numInputSamples, int*
|
|||
return outputSamples_.get();
|
||||
}
|
||||
|
||||
void RADETransmitStep::restartVocoder()
|
||||
void RADETransmitStep::restartVocoder() FREEDV_NONBLOCKING
|
||||
{
|
||||
// Queues up EOO for return on the next call to this pipeline step.
|
||||
const int NUM_SAMPLES_SILENCE = 60 * getOutputSampleRate() / 1000;
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
int numEOOSamples = rade_n_tx_eoo_out(dv_);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_disable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
|
||||
rade_tx_eoo(dv_, eooOut_);
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_enable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
FREEDV_BEGIN_REALTIME_UNSAFE
|
||||
rade_tx_eoo(dv_, eooOut_);
|
||||
FREEDV_END_REALTIME_UNSAFE
|
||||
|
||||
memset(eooOutShort_, 0, sizeof(short) * (numEOOSamples + NUM_SAMPLES_SILENCE));
|
||||
for (int index = 0; index < numEOOSamples; index++)
|
||||
|
|
@ -233,11 +216,13 @@ void RADETransmitStep::restartVocoder()
|
|||
|
||||
if (outputSampleFifo_.write(eooOutShort_, numEOOSamples + NUM_SAMPLES_SILENCE) != 0)
|
||||
{
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
log_warn("Could not queue EOO samples (remaining space in FIFO = %d)", outputSampleFifo_.numFree());
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
}
|
||||
|
||||
void RADETransmitStep::reset()
|
||||
void RADETransmitStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
inputSampleFifo_.reset();
|
||||
outputSampleFifo_.reset();
|
||||
|
|
|
|||
|
|
@ -44,13 +44,13 @@ public:
|
|||
RADETransmitStep(struct rade* dv, LPCNetEncState* encState);
|
||||
virtual ~RADETransmitStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
// For triggering EOO
|
||||
void restartVocoder();
|
||||
void restartVocoder() FREEDV_NONBLOCKING;
|
||||
|
||||
private:
|
||||
struct rade* dv_;
|
||||
|
|
|
|||
|
|
@ -59,17 +59,17 @@ RecordStep::~RecordStep()
|
|||
codec2_fifo_destroy(inputFifo_);
|
||||
}
|
||||
|
||||
int RecordStep::getInputSampleRate() const
|
||||
int RecordStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return inputSampleRate_;
|
||||
}
|
||||
|
||||
int RecordStep::getOutputSampleRate() const
|
||||
int RecordStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return inputSampleRate_;
|
||||
}
|
||||
|
||||
short* RecordStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* RecordStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
codec2_fifo_write(inputFifo_, inputSamples, numInputSamples);
|
||||
fileIoThreadSem_.signal();
|
||||
|
|
@ -78,7 +78,7 @@ short* RecordStep::execute(short* inputSamples, int numInputSamples, int* numOut
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void RecordStep::reset()
|
||||
void RecordStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
short buf;
|
||||
while (codec2_fifo_used(inputFifo_) > 0)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <functional>
|
||||
#include <sndfile.h>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include "codec2_fifo.h"
|
||||
#include "../util/Semaphore.h"
|
||||
|
||||
|
|
@ -38,10 +39,10 @@ public:
|
|||
int inputSampleRate, std::function<SNDFILE*()> getSndFileFn, std::function<void(int)> isFileCompleteFn);
|
||||
virtual ~RecordStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
int inputSampleRate_;
|
||||
|
|
@ -50,7 +51,7 @@ private:
|
|||
std::function<void(int)> isFileCompleteFn_;
|
||||
std::thread fileIoThread_;
|
||||
FIFO* inputFifo_;
|
||||
bool fileIoThreadEnding_;
|
||||
std::atomic<bool> fileIoThreadEnding_;
|
||||
Semaphore fileIoThreadSem_;
|
||||
|
||||
void fileIoThreadEntry_();
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include "ResamplePlotStep.h"
|
||||
|
||||
// TBD - maybe include code for function here?
|
||||
extern void resample_for_plot(struct FIFO *plotFifo, short buf[], short* dec_samples, int length, int fs);
|
||||
extern void resample_for_plot(struct FIFO *plotFifo, short buf[], short* dec_samples, int length, int fs) FREEDV_NONBLOCKING;
|
||||
|
||||
ResampleForPlotStep::ResampleForPlotStep(struct FIFO* fifo)
|
||||
: fifo_(fifo)
|
||||
|
|
@ -38,17 +38,17 @@ ResampleForPlotStep::~ResampleForPlotStep()
|
|||
delete[] decSamples_;
|
||||
}
|
||||
|
||||
int ResampleForPlotStep::getInputSampleRate() const
|
||||
int ResampleForPlotStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return FS;
|
||||
}
|
||||
|
||||
int ResampleForPlotStep::getOutputSampleRate() const
|
||||
int ResampleForPlotStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return FS;
|
||||
}
|
||||
|
||||
short* ResampleForPlotStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* ResampleForPlotStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
resample_for_plot(fifo_, inputSamples, decSamples_, numInputSamples, FS);
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ public:
|
|||
ResampleForPlotStep(struct FIFO* fifo);
|
||||
virtual ~ResampleForPlotStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
struct FIFO* fifo_;
|
||||
|
|
|
|||
|
|
@ -39,14 +39,18 @@ static int resample_step(SRC_STATE *src,
|
|||
int length_input_short,
|
||||
float *tmpInput,
|
||||
float *tmpOutput
|
||||
)
|
||||
) FREEDV_NONBLOCKING
|
||||
{
|
||||
SRC_DATA src_data;
|
||||
int ret;
|
||||
|
||||
assert(src != NULL);
|
||||
|
||||
// libsamplerate is unlikely to use RT-unsafe constructs in normal use
|
||||
// (verified with RTsan-enabled automated testing). Verified on 2025-09-30.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
src_short_to_float_array(input_short, tmpInput, length_input_short);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
src_data.data_in = tmpInput;
|
||||
src_data.data_out = tmpOutput;
|
||||
|
|
@ -55,15 +59,19 @@ static int resample_step(SRC_STATE *src,
|
|||
src_data.end_of_input = 0;
|
||||
src_data.src_ratio = (float)output_sample_rate/input_sample_rate;
|
||||
|
||||
// libsamplerate is unlikely to use RT-unsafe constructs in normal use
|
||||
// (verified with RTsan-enabled automated testing). Verified on 2025-09-30.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
ret = src_process(src, &src_data);
|
||||
if (ret != 0)
|
||||
{
|
||||
log_warn("Resampling failed: %s", src_strerror(ret));
|
||||
}
|
||||
assert(ret == 0);
|
||||
|
||||
assert(src_data.output_frames_gen <= length_output_short);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
// libsamplerate is unlikely to use RT-unsafe constructs in normal use
|
||||
// (verified with RTsan-enabled automated testing). Verified on 2025-09-30.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
src_float_to_short_array(tmpOutput, output_short, src_data.output_frames_gen);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
return src_data.output_frames_gen;
|
||||
}
|
||||
|
|
@ -96,17 +104,17 @@ ResampleStep::~ResampleStep()
|
|||
delete[] tempOutput_;
|
||||
}
|
||||
|
||||
int ResampleStep::getInputSampleRate() const
|
||||
int ResampleStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return inputSampleRate_;
|
||||
}
|
||||
|
||||
int ResampleStep::getOutputSampleRate() const
|
||||
int ResampleStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return outputSampleRate_;
|
||||
}
|
||||
|
||||
short* ResampleStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* ResampleStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
if (numInputSamples == 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ public:
|
|||
ResampleStep(int inputSampleRate, int outputSampleRate, bool forPlotsOnly = false);
|
||||
virtual ~ResampleStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
int inputSampleRate_;
|
||||
|
|
|
|||
|
|
@ -50,17 +50,17 @@ SpeexStep::~SpeexStep()
|
|||
speex_preprocess_state_destroy(speexStateObj_);
|
||||
}
|
||||
|
||||
int SpeexStep::getInputSampleRate() const
|
||||
int SpeexStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
int SpeexStep::getOutputSampleRate() const
|
||||
int SpeexStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
short* SpeexStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* SpeexStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
*numOutputSamples = 0;
|
||||
|
||||
|
|
@ -77,7 +77,13 @@ short* SpeexStep::execute(short* inputSamples, int numInputSamples, int* numOutp
|
|||
while (inputSampleFifo_.numUsed() >= numSamplesPerSpeexRun_)
|
||||
{
|
||||
inputSampleFifo_.read(tmpOutput, numSamplesPerSpeexRun_);
|
||||
|
||||
// Note: Speex is unlikely to use RT-unsafe constructs in normal operation
|
||||
// (per existing RTSan-enabled tests). Verified on 2025-09-30.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
speex_preprocess_run(speexStateObj_, tmpOutput);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
|
||||
tmpOutput += numSamplesPerSpeexRun_;
|
||||
}
|
||||
}
|
||||
|
|
@ -89,7 +95,7 @@ short* SpeexStep::execute(short* inputSamples, int numInputSamples, int* numOutp
|
|||
return outputSamples;
|
||||
}
|
||||
|
||||
void SpeexStep::reset()
|
||||
void SpeexStep::reset() FREEDV_NONBLOCKING
|
||||
{
|
||||
inputSampleFifo_.reset();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ public:
|
|||
SpeexStep(int sampleRate);
|
||||
virtual ~SpeexStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual void reset() override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
virtual void reset() FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
int sampleRate_;
|
||||
|
|
|
|||
|
|
@ -70,17 +70,17 @@ TapStep::~TapStep()
|
|||
tapThread_.join();
|
||||
}
|
||||
|
||||
int TapStep::getInputSampleRate() const
|
||||
int TapStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
int TapStep::getOutputSampleRate() const
|
||||
int TapStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
short* TapStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* TapStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
assert(tapStep_->getInputSampleRate() == sampleRate_);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include "../util/GenericFIFO.h"
|
||||
#include "../util/Semaphore.h"
|
||||
|
||||
|
|
@ -36,15 +37,15 @@ public:
|
|||
TapStep(int inputSampleRate, IPipelineStep* tapStep);
|
||||
virtual ~TapStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<IPipelineStep> tapStep_;
|
||||
int sampleRate_;
|
||||
std::thread tapThread_;
|
||||
bool endingTapThread_;
|
||||
std::atomic<bool> endingTapThread_;
|
||||
GenericFIFO<short> tapThreadInput_;
|
||||
Semaphore sem_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include "ToneInterfererStep.h"
|
||||
|
||||
// M_PI is not available on some compilers, so define it here just in case.
|
||||
|
|
@ -31,8 +32,8 @@
|
|||
#endif
|
||||
|
||||
ToneInterfererStep::ToneInterfererStep(
|
||||
int sampleRate, std::function<float()> toneFrequencyFn,
|
||||
std::function<float()> toneAmplitudeFn, std::function<float*()> tonePhaseFn)
|
||||
int sampleRate, realtime_fp<float()> toneFrequencyFn,
|
||||
realtime_fp<float()> toneAmplitudeFn, realtime_fp<float*()> tonePhaseFn)
|
||||
: sampleRate_(sampleRate)
|
||||
, toneFrequencyFn_(toneFrequencyFn)
|
||||
, toneAmplitudeFn_(toneAmplitudeFn)
|
||||
|
|
@ -49,17 +50,17 @@ ToneInterfererStep::~ToneInterfererStep()
|
|||
// empty
|
||||
}
|
||||
|
||||
int ToneInterfererStep::getInputSampleRate() const
|
||||
int ToneInterfererStep::getInputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
int ToneInterfererStep::getOutputSampleRate() const
|
||||
int ToneInterfererStep::getOutputSampleRate() const FREEDV_NONBLOCKING
|
||||
{
|
||||
return sampleRate_;
|
||||
}
|
||||
|
||||
short* ToneInterfererStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
short* ToneInterfererStep::execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
*numOutputSamples = numInputSamples;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,27 +24,26 @@
|
|||
#define AUDIO_PIPELINE__TONE_INTERFERER_STEP_H
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#include "../util/realtime_fp.h"
|
||||
#include "IPipelineStep.h"
|
||||
|
||||
class ToneInterfererStep : public IPipelineStep
|
||||
{
|
||||
public:
|
||||
ToneInterfererStep(
|
||||
int sampleRate, std::function<float()> toneFrequencyFn,
|
||||
std::function<float()> toneAmplitudeFn, std::function<float*()> tonePhaseFn);
|
||||
int sampleRate, realtime_fp<float()> toneFrequencyFn,
|
||||
realtime_fp<float()> toneAmplitudeFn, realtime_fp<float*()> tonePhaseFn);
|
||||
virtual ~ToneInterfererStep();
|
||||
|
||||
virtual int getInputSampleRate() const override;
|
||||
virtual int getOutputSampleRate() const override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) override;
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING override;
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING override;
|
||||
|
||||
private:
|
||||
int sampleRate_;
|
||||
std::function<float()> toneFrequencyFn_;
|
||||
std::function<float()> toneAmplitudeFn_;
|
||||
std::function<float*()> tonePhaseFn_;
|
||||
realtime_fp<float()> toneFrequencyFn_;
|
||||
realtime_fp<float()> toneAmplitudeFn_;
|
||||
realtime_fp<float*()> tonePhaseFn_;
|
||||
std::unique_ptr<short[]> outputSamples_;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
#include <chrono>
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#include "../util/sanitizers.h"
|
||||
|
||||
// WebRTC uses FS, which is defined in defines.h. Thus, it needs to be included
|
||||
// first.
|
||||
#include "AgcStep.h"
|
||||
|
|
@ -55,8 +57,6 @@ using namespace std::chrono_literals;
|
|||
|
||||
#include "codec2_alloc.h"
|
||||
|
||||
#include <wx/stopwatch.h>
|
||||
|
||||
// Experimental options for potential future release:
|
||||
//
|
||||
// * ENABLE_FASTER_PLOTS: This uses a faster resampling algorithm to reduce the CPU
|
||||
|
|
@ -99,7 +99,7 @@ extern int g_SquelchActive;
|
|||
extern float g_SquelchLevel;
|
||||
extern float g_tone_phase;
|
||||
extern float g_avmag[MODEM_STATS_NSPEC];
|
||||
extern int g_State;
|
||||
extern std::atomic<int> g_State;
|
||||
extern int g_channel_noise;
|
||||
extern float g_RxFreqOffsetHz;
|
||||
extern float g_sig_pwr_av;
|
||||
|
|
@ -116,6 +116,15 @@ extern FreeDVInterface freedvInterface;
|
|||
#include "../main.h"
|
||||
extern wxWindow* g_parent;
|
||||
|
||||
static auto& NonblockingWxGetApp() FREEDV_NONBLOCKING
|
||||
{
|
||||
// Note: wxWidgets implementation of wxGetApp() only returns the App object
|
||||
// and performs no other tasks. Verified RT safe as of wxWidgets version 3.3.1.
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
return wxGetApp();
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
|
||||
#include <sndfile.h>
|
||||
extern SNDFILE* g_sfPlayFile;
|
||||
extern SNDFILE* g_sfRecFileFromModulator;
|
||||
|
|
@ -159,7 +168,7 @@ void TxRxThread::initializePipeline_()
|
|||
auto bypassRecordMic = new AudioPipeline(inputSampleRate_, inputSampleRate_);
|
||||
|
||||
auto eitherOrRecordMic = new EitherOrStep(
|
||||
[]() { return (g_recVoiceKeyerFile || g_recFileFromMic) && (g_sfRecMicFile != NULL); },
|
||||
+[]() FREEDV_NONBLOCKING { return (g_recVoiceKeyerFile || g_recFileFromMic) && (g_sfRecMicFile != NULL); },
|
||||
recordMicTap,
|
||||
bypassRecordMic
|
||||
);
|
||||
|
|
@ -184,7 +193,7 @@ void TxRxThread::initializePipeline_()
|
|||
eitherOrPlayMicIn->appendPipelineStep(playMicIn);
|
||||
|
||||
auto eitherOrPlayStep = new EitherOrStep(
|
||||
[]() { return g_playFileToMicIn.load(std::memory_order_acquire) && (g_sfPlayFile != NULL); },
|
||||
+[]() FREEDV_NONBLOCKING { return g_playFileToMicIn.load(std::memory_order_acquire) && (g_sfPlayFile != NULL); },
|
||||
eitherOrPlayMicIn,
|
||||
eitherOrBypassPlay);
|
||||
pipeline_->appendPipelineStep(eitherOrPlayStep);
|
||||
|
|
@ -197,7 +206,7 @@ void TxRxThread::initializePipeline_()
|
|||
eitherOrProcessSpeex->appendPipelineStep(speexStep);
|
||||
|
||||
auto eitherOrSpeexStep = new EitherOrStep(
|
||||
[]() { return wxGetApp().appConfiguration.filterConfiguration.speexppEnable; },
|
||||
+[]() FREEDV_NONBLOCKING { return (bool)NonblockingWxGetApp().appConfiguration.filterConfiguration.speexppEnable.getWithoutProcessing(); },
|
||||
eitherOrProcessSpeex,
|
||||
eitherOrBypassSpeex);
|
||||
pipeline_->appendPipelineStep(eitherOrSpeexStep);
|
||||
|
|
@ -210,7 +219,7 @@ void TxRxThread::initializePipeline_()
|
|||
eitherOrProcessAgc->appendPipelineStep(agcStep);
|
||||
|
||||
auto eitherOrAgcStep = new EitherOrStep(
|
||||
[]() { return g_agcEnabled.load(std::memory_order_acquire); },
|
||||
+[]() FREEDV_NONBLOCKING { return g_agcEnabled.load(std::memory_order_acquire); },
|
||||
eitherOrProcessAgc,
|
||||
eitherOrBypassAgc);
|
||||
pipeline_->appendPipelineStep(eitherOrAgcStep);
|
||||
|
|
@ -222,7 +231,8 @@ void TxRxThread::initializePipeline_()
|
|||
&g_rxUserdata->sbqMicInBass,
|
||||
&g_rxUserdata->sbqMicInMid,
|
||||
&g_rxUserdata->sbqMicInTreble,
|
||||
&g_rxUserdata->sbqMicInVol);
|
||||
&g_rxUserdata->sbqMicInVol,
|
||||
g_rxUserdata->eqLock);
|
||||
pipeline_->appendPipelineStep(equalizerStep);
|
||||
|
||||
// Take TX audio post-equalizer and send it to RX for possible monitoring use.
|
||||
|
|
@ -248,20 +258,20 @@ void TxRxThread::initializePipeline_()
|
|||
pipeline_->appendPipelineStep(resampleForPlotTap);
|
||||
|
||||
// FreeDV TX step (analog leg)
|
||||
auto doubleLevelStep = new LevelAdjustStep(inputSampleRate_, []() { return 2.0; });
|
||||
auto doubleLevelStep = new LevelAdjustStep(inputSampleRate_, +[]() FREEDV_NONBLOCKING { return (float)2.0; });
|
||||
auto analogTxPipeline = new AudioPipeline(inputSampleRate_, outputSampleRate_);
|
||||
analogTxPipeline->appendPipelineStep(doubleLevelStep);
|
||||
|
||||
auto digitalTxStep = freedvInterface.createTransmitPipeline(
|
||||
inputSampleRate_,
|
||||
outputSampleRate_,
|
||||
[]() { return g_TxFreqOffsetHz; },
|
||||
+[]() FREEDV_NONBLOCKING { return g_TxFreqOffsetHz; },
|
||||
helper_);
|
||||
auto digitalTxPipeline = new AudioPipeline(inputSampleRate_, outputSampleRate_);
|
||||
digitalTxPipeline->appendPipelineStep(digitalTxStep);
|
||||
|
||||
auto eitherOrDigitalAnalog = new EitherOrStep(
|
||||
[]() { return g_analog; },
|
||||
+[]() FREEDV_NONBLOCKING { return g_analog != 0; },
|
||||
analogTxPipeline,
|
||||
digitalTxPipeline);
|
||||
pipeline_->appendPipelineStep(eitherOrDigitalAnalog);
|
||||
|
|
@ -283,13 +293,13 @@ void TxRxThread::initializePipeline_()
|
|||
auto bypassRecordModulated = new AudioPipeline(outputSampleRate_, outputSampleRate_);
|
||||
|
||||
auto eitherOrRecordModulated = new EitherOrStep(
|
||||
[]() { return g_recFileFromModulator && (g_sfRecFileFromModulator != NULL); },
|
||||
+[]() FREEDV_NONBLOCKING { return g_recFileFromModulator && (g_sfRecFileFromModulator != NULL); },
|
||||
recordModulatedTapPipeline,
|
||||
bypassRecordModulated);
|
||||
pipeline_->appendPipelineStep(eitherOrRecordModulated);
|
||||
|
||||
// TX attenuation step
|
||||
auto txAttenuationStep = new LevelAdjustStep(outputSampleRate_, []() {
|
||||
auto txAttenuationStep = new LevelAdjustStep(outputSampleRate_, +[]() FREEDV_NONBLOCKING {
|
||||
return g_txLevelScale.load(std::memory_order_acquire);
|
||||
});
|
||||
pipeline_->appendPipelineStep(txAttenuationStep);
|
||||
|
|
@ -317,7 +327,7 @@ void TxRxThread::initializePipeline_()
|
|||
auto bypassRecordRadio = new AudioPipeline(inputSampleRate_, inputSampleRate_);
|
||||
|
||||
auto eitherOrRecordRadio = new EitherOrStep(
|
||||
[]() { return g_recFileFromRadio && (g_sfRecFile != NULL); },
|
||||
+[]() FREEDV_NONBLOCKING { return g_recFileFromRadio && (g_sfRecFile != NULL); },
|
||||
recordRadioTap,
|
||||
bypassRecordRadio
|
||||
);
|
||||
|
|
@ -342,7 +352,7 @@ void TxRxThread::initializePipeline_()
|
|||
eitherOrPlayRadio->appendPipelineStep(playRadio);
|
||||
|
||||
auto eitherOrPlayRadioStep = new EitherOrStep(
|
||||
[]() {
|
||||
+[]() FREEDV_NONBLOCKING {
|
||||
auto result = g_playFileFromRadio.load(std::memory_order_acquire) && (g_sfPlayFileFromRadio != NULL);
|
||||
return result;
|
||||
},
|
||||
|
|
@ -366,12 +376,12 @@ void TxRxThread::initializePipeline_()
|
|||
auto bypassToneInterferer = new AudioPipeline(inputSampleRate_, inputSampleRate_);
|
||||
auto toneInterfererStep = new ToneInterfererStep(
|
||||
inputSampleRate_,
|
||||
[]() { return wxGetApp().m_tone_freq_hz; },
|
||||
[]() { return wxGetApp().m_tone_amplitude; },
|
||||
[]() { return &g_tone_phase; }
|
||||
+[]() FREEDV_NONBLOCKING { return (float)NonblockingWxGetApp().m_tone_freq_hz; },
|
||||
+[]() FREEDV_NONBLOCKING { return (float)NonblockingWxGetApp().m_tone_amplitude; },
|
||||
+[]() FREEDV_NONBLOCKING { return (float*)&g_tone_phase; }
|
||||
);
|
||||
auto eitherOrToneInterferer = new EitherOrStep(
|
||||
[]() { return wxGetApp().m_tone; },
|
||||
+[]() FREEDV_NONBLOCKING { return NonblockingWxGetApp().m_tone; },
|
||||
toneInterfererStep,
|
||||
bypassToneInterferer
|
||||
);
|
||||
|
|
@ -379,8 +389,8 @@ void TxRxThread::initializePipeline_()
|
|||
|
||||
// RF spectrum computation step
|
||||
auto computeRfSpectrumStep = new ComputeRfSpectrumStep(
|
||||
[]() { return freedvInterface.getCurrentRxModemStats(); },
|
||||
[]() { return &g_avmag[0]; }
|
||||
+[]() FREEDV_NONBLOCKING { return freedvInterface.getCurrentRxModemStats(); },
|
||||
+[]() FREEDV_NONBLOCKING { return &g_avmag[0]; }
|
||||
);
|
||||
auto computeRfSpectrumPipeline = new AudioPipeline(
|
||||
inputSampleRate_, computeRfSpectrumStep->getOutputSampleRate());
|
||||
|
|
@ -398,11 +408,11 @@ void TxRxThread::initializePipeline_()
|
|||
auto rfDemodulationPipeline = new AudioPipeline(inputSampleRate_, outputSampleRate_);
|
||||
auto rfDemodulationStep = freedvInterface.createReceivePipeline(
|
||||
inputSampleRate_, outputSampleRate_,
|
||||
[]() { return &g_State; },
|
||||
[]() { return g_channel_noise; },
|
||||
[]() { return wxGetApp().appConfiguration.noiseSNR; },
|
||||
[]() { return g_RxFreqOffsetHz; },
|
||||
[]() { return &g_sig_pwr_av; },
|
||||
+[]() FREEDV_NONBLOCKING { return &g_State; },
|
||||
+[]() FREEDV_NONBLOCKING { return g_channel_noise; },
|
||||
+[]() FREEDV_NONBLOCKING { return NonblockingWxGetApp().appConfiguration.noiseSNR.getWithoutProcessing(); },
|
||||
+[]() FREEDV_NONBLOCKING { return g_RxFreqOffsetHz; },
|
||||
+[]() FREEDV_NONBLOCKING { return &g_sig_pwr_av; },
|
||||
helper_
|
||||
);
|
||||
rfDemodulationPipeline->appendPipelineStep(rfDemodulationStep);
|
||||
|
|
@ -428,15 +438,15 @@ void TxRxThread::initializePipeline_()
|
|||
auto monitorPipeline = new AudioPipeline(inputSampleRate_, outputSampleRate_);
|
||||
monitorPipeline->appendPipelineStep(equalizedMicAudioLink_->getOutputPipelineStep());
|
||||
|
||||
auto monitorLevelStep = new LevelAdjustStep(outputSampleRate_, [&]() {
|
||||
auto monitorLevelStep = new LevelAdjustStep(outputSampleRate_, +[]() FREEDV_NONBLOCKING {
|
||||
float volInDb = 0;
|
||||
if (g_voice_keyer_tx.load(std::memory_order_acquire) && wxGetApp().appConfiguration.monitorVoiceKeyerAudio)
|
||||
if (g_voice_keyer_tx.load(std::memory_order_acquire) && NonblockingWxGetApp().appConfiguration.monitorVoiceKeyerAudio.getWithoutProcessing())
|
||||
{
|
||||
volInDb = wxGetApp().appConfiguration.monitorVoiceKeyerAudioVol;
|
||||
volInDb = NonblockingWxGetApp().appConfiguration.monitorVoiceKeyerAudioVol.getWithoutProcessing();
|
||||
}
|
||||
else
|
||||
{
|
||||
volInDb = wxGetApp().appConfiguration.monitorTxAudioVol;
|
||||
volInDb = NonblockingWxGetApp().appConfiguration.monitorTxAudioVol.getWithoutProcessing();
|
||||
}
|
||||
|
||||
return std::exp(volInDb/20.0f * std::log(10.0f));
|
||||
|
|
@ -448,31 +458,41 @@ void TxRxThread::initializePipeline_()
|
|||
mutePipeline->appendPipelineStep(muteStep);
|
||||
|
||||
auto eitherOrMuteStep = new EitherOrStep(
|
||||
[]() { return g_recVoiceKeyerFile; },
|
||||
+[]() FREEDV_NONBLOCKING { return g_recVoiceKeyerFile; },
|
||||
mutePipeline,
|
||||
bypassMonitorAudio
|
||||
);
|
||||
|
||||
auto eitherOrMicMonitorStep = new EitherOrStep(
|
||||
[]() { return
|
||||
(g_voice_keyer_tx.load(std::memory_order_acquire) && wxGetApp().appConfiguration.monitorVoiceKeyerAudio) ||
|
||||
(g_tx.load(std::memory_order_acquire) && wxGetApp().appConfiguration.monitorTxAudio); },
|
||||
+[]() FREEDV_NONBLOCKING { return
|
||||
(g_voice_keyer_tx.load(std::memory_order_acquire) && NonblockingWxGetApp().appConfiguration.monitorVoiceKeyerAudio.getWithoutProcessing()) ||
|
||||
(g_tx.load(std::memory_order_acquire) && NonblockingWxGetApp().appConfiguration.monitorTxAudio.getWithoutProcessing()); },
|
||||
monitorPipeline,
|
||||
eitherOrMuteStep
|
||||
);
|
||||
bypassRfDemodulationPipeline->appendPipelineStep(eitherOrMicMonitorStep);
|
||||
}
|
||||
|
||||
auto eitherOrRfDemodulationStep = new EitherOrStep(
|
||||
[this]() { return g_analog ||
|
||||
(equalizedMicAudioLink_ != nullptr && (
|
||||
(g_recVoiceKeyerFile) ||
|
||||
(g_voice_keyer_tx.load(std::memory_order_acquire) && wxGetApp().appConfiguration.monitorVoiceKeyerAudio) ||
|
||||
(g_tx.load(std::memory_order_acquire) && wxGetApp().appConfiguration.monitorTxAudio)
|
||||
)); },
|
||||
bypassRfDemodulationPipeline,
|
||||
rfDemodulationPipeline
|
||||
);
|
||||
EitherOrStep* eitherOrRfDemodulationStep = nullptr;
|
||||
if (equalizedMicAudioLink_ != nullptr)
|
||||
{
|
||||
eitherOrRfDemodulationStep = new EitherOrStep(
|
||||
+[]() FREEDV_NONBLOCKING { return g_analog ||
|
||||
(
|
||||
(g_recVoiceKeyerFile) ||
|
||||
(g_voice_keyer_tx.load(std::memory_order_acquire) && NonblockingWxGetApp().appConfiguration.monitorVoiceKeyerAudio.getWithoutProcessing()) ||
|
||||
(g_tx.load(std::memory_order_acquire) && NonblockingWxGetApp().appConfiguration.monitorTxAudio.getWithoutProcessing())
|
||||
); },
|
||||
bypassRfDemodulationPipeline,
|
||||
rfDemodulationPipeline);
|
||||
}
|
||||
else
|
||||
{
|
||||
eitherOrRfDemodulationStep = new EitherOrStep(
|
||||
+[]() FREEDV_NONBLOCKING { return g_analog != 0; },
|
||||
bypassRfDemodulationPipeline,
|
||||
rfDemodulationPipeline);
|
||||
}
|
||||
|
||||
pipeline_->appendPipelineStep(eitherOrRfDemodulationStep);
|
||||
|
||||
|
|
@ -483,7 +503,8 @@ void TxRxThread::initializePipeline_()
|
|||
&g_rxUserdata->sbqSpkOutBass,
|
||||
&g_rxUserdata->sbqSpkOutMid,
|
||||
&g_rxUserdata->sbqSpkOutTreble,
|
||||
&g_rxUserdata->sbqSpkOutVol);
|
||||
&g_rxUserdata->sbqSpkOutVol,
|
||||
g_rxUserdata->eqLock);
|
||||
pipeline_->appendPipelineStep(equalizerStep);
|
||||
|
||||
// Clear anything in the FIFO before resuming decode.
|
||||
|
|
@ -491,7 +512,7 @@ void TxRxThread::initializePipeline_()
|
|||
}
|
||||
}
|
||||
|
||||
void* TxRxThread::Entry()
|
||||
void* TxRxThread::Entry() noexcept
|
||||
{
|
||||
// Get raw pointer so we don't need to constantly access the shared_ptr
|
||||
// and thus constantly increment/decrement refcounts.
|
||||
|
|
@ -610,7 +631,7 @@ void TxRxThread::reportStats_()
|
|||
}
|
||||
#endif // defined(ENABLE_PROCESSING_STATS)
|
||||
|
||||
void TxRxThread::clearFifos_()
|
||||
void TxRxThread::clearFifos_() FREEDV_NONBLOCKING
|
||||
{
|
||||
paCallBackData *cbData = g_rxUserdata;
|
||||
|
||||
|
|
@ -642,12 +663,7 @@ void TxRxThread::clearFifos_()
|
|||
// Main real time processing for tx and rx of FreeDV signals, run in its own threads
|
||||
//---------------------------------------------------------------------------------------------
|
||||
|
||||
void TxRxThread::txProcessing_(IRealtimeHelper* helper) noexcept
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
[[clang::nonblocking]]
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
void TxRxThread::txProcessing_(IRealtimeHelper* helper) FREEDV_NONBLOCKING
|
||||
{
|
||||
paCallBackData *cbData = g_rxUserdata;
|
||||
|
||||
|
|
@ -686,8 +702,10 @@ void TxRxThread::txProcessing_(IRealtimeHelper* helper) noexcept
|
|||
if (g_dump_fifo_state) {
|
||||
// If this drops to zero we have a problem as we will run out of output samples
|
||||
// to send to the sound driver
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
log_debug("outfifo1 used: %6d free: %6d nsam_one_modem_frame: %d",
|
||||
cbData->outfifo1->numUsed(), cbData->outfifo1->numFree(), nsam_one_modem_frame);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
|
||||
int nsam_in_48 = (inputSampleRate_ * FRAME_DURATION_MS) / MS_TO_SEC;
|
||||
|
|
@ -733,7 +751,9 @@ void TxRxThread::txProcessing_(IRealtimeHelper* helper) noexcept
|
|||
{
|
||||
if (cbData->outfifo1->write(outputSamples, nout) != 0)
|
||||
{
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
log_warn("Could not inject resampled EOO samples (space remaining in FIFO = %d)", cbData->outfifo1->numFree());
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -752,7 +772,9 @@ void TxRxThread::txProcessing_(IRealtimeHelper* helper) noexcept
|
|||
auto outputSamples = pipeline_->execute(inputPtr, nsam_in_48, &nout);
|
||||
|
||||
if (g_dump_fifo_state) {
|
||||
FREEDV_BEGIN_VERIFIED_SAFE
|
||||
log_info(" nout: %d", nout);
|
||||
FREEDV_END_VERIFIED_SAFE
|
||||
}
|
||||
|
||||
if (outputSamples != nullptr)
|
||||
|
|
@ -772,12 +794,7 @@ void TxRxThread::txProcessing_(IRealtimeHelper* helper) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
void TxRxThread::rxProcessing_(IRealtimeHelper* helper) noexcept
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
[[clang::nonblocking]]
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
void TxRxThread::rxProcessing_(IRealtimeHelper* helper) FREEDV_NONBLOCKING
|
||||
{
|
||||
paCallBackData *cbData = g_rxUserdata;
|
||||
|
||||
|
|
@ -792,7 +809,6 @@ void TxRxThread::rxProcessing_(IRealtimeHelper* helper) noexcept
|
|||
|
||||
if (g_queueResync)
|
||||
{
|
||||
log_debug("Unsyncing per user request.");
|
||||
g_queueResync = false;
|
||||
freedvInterface.setSync(FREEDV_SYNC_UNSYNC);
|
||||
g_resyncs++;
|
||||
|
|
@ -810,8 +826,8 @@ void TxRxThread::rxProcessing_(IRealtimeHelper* helper) noexcept
|
|||
bool tmpVkTx = g_voice_keyer_tx.load(std::memory_order_acquire);
|
||||
bool tmpHalfDuplex = g_half_duplex.load(std::memory_order_acquire);
|
||||
bool processInputFifo =
|
||||
(tmpVkTx && wxGetApp().appConfiguration.monitorVoiceKeyerAudio) ||
|
||||
(tmpTx && wxGetApp().appConfiguration.monitorTxAudio) ||
|
||||
(tmpVkTx && NonblockingWxGetApp().appConfiguration.monitorVoiceKeyerAudio.getWithoutProcessing()) ||
|
||||
(tmpTx && NonblockingWxGetApp().appConfiguration.monitorTxAudio.getWithoutProcessing()) ||
|
||||
(!tmpVkTx && ((tmpHalfDuplex && !tmpTx) || !tmpHalfDuplex));
|
||||
if (!processInputFifo)
|
||||
{
|
||||
|
|
@ -842,8 +858,8 @@ void TxRxThread::rxProcessing_(IRealtimeHelper* helper) noexcept
|
|||
tmpVkTx = g_voice_keyer_tx.load(std::memory_order_acquire);
|
||||
tmpHalfDuplex = g_half_duplex.load(std::memory_order_acquire);
|
||||
processInputFifo =
|
||||
(tmpVkTx && wxGetApp().appConfiguration.monitorVoiceKeyerAudio) ||
|
||||
(tmpTx && wxGetApp().appConfiguration.monitorTxAudio) ||
|
||||
(tmpVkTx && NonblockingWxGetApp().appConfiguration.monitorVoiceKeyerAudio.getWithoutProcessing()) ||
|
||||
(tmpTx && NonblockingWxGetApp().appConfiguration.monitorTxAudio.getWithoutProcessing()) ||
|
||||
(!tmpVkTx && ((tmpHalfDuplex && !tmpTx) || !tmpHalfDuplex));
|
||||
|
||||
#if defined(ENABLE_PROCESSING_STATS)
|
||||
|
|
|
|||
|
|
@ -28,10 +28,12 @@
|
|||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
|
||||
#include "AudioPipeline.h"
|
||||
#include "util/IRealtimeHelper.h"
|
||||
#include "util/Semaphore.h"
|
||||
#include "util/sanitizers.h"
|
||||
|
||||
// Forward declarations
|
||||
class LinkStep;
|
||||
|
|
@ -87,14 +89,14 @@ public:
|
|||
}
|
||||
|
||||
// thread execution starts here
|
||||
void *Entry();
|
||||
void *Entry() noexcept;
|
||||
|
||||
void waitForReady() { readySem_.wait(); }
|
||||
void signalToStart() { startSem_.signal(); }
|
||||
|
||||
private:
|
||||
bool m_tx;
|
||||
bool m_run;
|
||||
std::atomic<bool> m_run;
|
||||
std::unique_ptr<AudioPipeline> pipeline_;
|
||||
int inputSampleRate_;
|
||||
int outputSampleRate_;
|
||||
|
|
@ -126,23 +128,9 @@ private:
|
|||
#endif // defined(ENABLE_PROCESSING_STATS)
|
||||
|
||||
void initializePipeline_();
|
||||
void txProcessing_(IRealtimeHelper* helper) noexcept
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
[[clang::nonblocking]]
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
;
|
||||
|
||||
void rxProcessing_(IRealtimeHelper* helper) noexcept
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
[[clang::nonblocking]]
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
;
|
||||
|
||||
void clearFifos_();
|
||||
void txProcessing_(IRealtimeHelper* helper) FREEDV_NONBLOCKING;
|
||||
void rxProcessing_(IRealtimeHelper* helper) FREEDV_NONBLOCKING;
|
||||
void clearFifos_() FREEDV_NONBLOCKING;
|
||||
};
|
||||
|
||||
#endif // AUDIO_PIPELINE__TX_RX_THREAD_H
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <samplerate.h>
|
||||
#include "../util/GenericFIFO.h"
|
||||
#include "../util/audio_spin_mutex.h"
|
||||
|
||||
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
|
||||
// paCallBackData
|
||||
|
|
@ -39,25 +40,28 @@ typedef struct paCallBackData
|
|||
GenericFIFO<short> *outfifo2;
|
||||
|
||||
// EQ filter states
|
||||
std::shared_ptr<void> sbqMicInBass;
|
||||
std::shared_ptr<void> sbqMicInTreble;
|
||||
std::shared_ptr<void> sbqMicInMid;
|
||||
std::shared_ptr<void> sbqMicInVol;
|
||||
std::shared_ptr<void> sbqSpkOutBass;
|
||||
std::shared_ptr<void> sbqSpkOutTreble;
|
||||
std::shared_ptr<void> sbqSpkOutMid;
|
||||
std::shared_ptr<void> sbqSpkOutVol;
|
||||
void* sbqMicInBass;
|
||||
void* sbqMicInTreble;
|
||||
void* sbqMicInMid;
|
||||
void* sbqMicInVol;
|
||||
void* sbqSpkOutBass;
|
||||
void* sbqSpkOutTreble;
|
||||
void* sbqSpkOutMid;
|
||||
void* sbqSpkOutVol;
|
||||
audio_spin_mutex eqLock;
|
||||
|
||||
bool micInEQEnable;
|
||||
bool spkOutEQEnable;
|
||||
|
||||
// optional loud tone on left channel to reliably trigger vox
|
||||
bool leftChannelVoxTone;
|
||||
float voxTonePhase;
|
||||
std::atomic<bool> leftChannelVoxTone;
|
||||
float voxTonePhase;
|
||||
|
||||
// Temporary buffers for reading and writing
|
||||
std::unique_ptr<short[]> tmpReadBuffer_;
|
||||
std::unique_ptr<short[]> tmpWriteBuffer_;
|
||||
std::unique_ptr<short[]> tmpReadRxBuffer_;
|
||||
std::unique_ptr<short[]> tmpReadTxBuffer_;
|
||||
std::unique_ptr<short[]> tmpWriteRxBuffer_;
|
||||
std::unique_ptr<short[]> tmpWriteTxBuffer_;
|
||||
} paCallBackData;
|
||||
|
||||
#endif // AUDIO_PIPELINE_PA_CALLBACK_DATA_H
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ bool passthroughDownsample()
|
|||
bool resampleBeforeStepCommon(int inputSampleRate, int stepSampleRate, int outputSampleRate)
|
||||
{
|
||||
AudioPipeline pipeline(inputSampleRate, outputSampleRate);
|
||||
auto levelAdjustStep = new LevelAdjustStep(stepSampleRate, []() { return 1.0; });
|
||||
auto levelAdjustStep = new LevelAdjustStep(stepSampleRate, +[]() FREEDV_NONBLOCKING { return (float)1.0; });
|
||||
assert(levelAdjustStep != nullptr);
|
||||
|
||||
pipeline.appendPipelineStep(levelAdjustStep);
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ public:
|
|||
result_[0] = 0;
|
||||
}
|
||||
|
||||
virtual int getInputSampleRate() const { return 8000; }
|
||||
virtual int getOutputSampleRate() const { return 8000; }
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING { return 8000; }
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING { return 8000; }
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
*numOutputSamples = 1;
|
||||
return result_.get();
|
||||
|
|
@ -31,9 +31,9 @@ public:
|
|||
result_[0] = 1;
|
||||
}
|
||||
|
||||
virtual int getInputSampleRate() const { return 8000; }
|
||||
virtual int getOutputSampleRate() const { return 8000; }
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING { return 8000; }
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING { return 8000; }
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
*numOutputSamples = 1;
|
||||
return result_.get();
|
||||
|
|
@ -43,10 +43,13 @@ private:
|
|||
std::unique_ptr<short[]> result_;
|
||||
};
|
||||
|
||||
static bool EitherOrCommonVal_;
|
||||
|
||||
bool eitherOrCommon(bool val)
|
||||
{
|
||||
EitherOrStep eitherOrStep([&]() {
|
||||
return val;
|
||||
EitherOrCommonVal_ = val;
|
||||
EitherOrStep eitherOrStep(+[]() FREEDV_NONBLOCKING {
|
||||
return EitherOrCommonVal_;
|
||||
}, new TrueStep(), new FalseStep());
|
||||
|
||||
int outputSamples = 0;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
#include "LevelAdjustStep.h"
|
||||
#include "PipelineTestCommon.h"
|
||||
|
||||
static float LevelAdjustCommonVal;
|
||||
|
||||
bool levelAdjustCommon(float val)
|
||||
{
|
||||
LevelAdjustStep levelAdjustStep(8000, [val]() { return val; });
|
||||
LevelAdjustCommonVal = val;
|
||||
LevelAdjustStep levelAdjustStep(8000, +[]() FREEDV_NONBLOCKING { return LevelAdjustCommonVal; });
|
||||
|
||||
int outputSamples = 0;
|
||||
std::unique_ptr<short[]> pData = std::make_unique<short[]>(1);
|
||||
|
|
|
|||
|
|
@ -53,13 +53,13 @@ int main()
|
|||
rade_text_t txt = rade_text_create();
|
||||
assert(txt != nullptr);
|
||||
int nsyms = rade_n_eoo_bits(rade);
|
||||
float txSyms[nsyms];
|
||||
float txSyms[1024]; // randomly selected maximum to suppress warning
|
||||
rade_text_generate_tx_string(txt, "K6AQ", 4, txSyms, nsyms);
|
||||
rade_text_set_rx_callback(txt, OnRadeTextRx, nullptr);
|
||||
rade_tx_set_eoo_bits(rade, txSyms);
|
||||
|
||||
// Initialize RADE steps
|
||||
RADEReceiveStep* recvStep = new RADEReceiveStep(rade, &fargan, txt, [](RADEReceiveStep*) { });
|
||||
RADEReceiveStep* recvStep = new RADEReceiveStep(rade, &fargan, txt, +[](RADEReceiveStep*) FREEDV_NONBLOCKING { });
|
||||
assert(recvStep != nullptr);
|
||||
RADETransmitStep* txStep = new RADETransmitStep(rade, encState);
|
||||
assert(txStep != nullptr);
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
class PassThroughStep : public IPipelineStep
|
||||
{
|
||||
public:
|
||||
virtual int getInputSampleRate() const { return 8000; }
|
||||
virtual int getOutputSampleRate() const { return 8000; }
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples)
|
||||
virtual int getInputSampleRate() const FREEDV_NONBLOCKING { return 8000; }
|
||||
virtual int getOutputSampleRate() const FREEDV_NONBLOCKING { return 8000; }
|
||||
virtual short* execute(short* inputSamples, int numInputSamples, int* numOutputSamples) FREEDV_NONBLOCKING
|
||||
{
|
||||
lastInputSamples = inputSamples;
|
||||
*numOutputSamples = numInputSamples;
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ int lsx_biquad_start(sox_effect_t * effp)
|
|||
|
||||
|
||||
int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf,
|
||||
sox_sample_t *obuf, size_t *isamp, size_t *osamp)
|
||||
sox_sample_t *obuf, size_t *isamp, size_t *osamp) FREEDV_NONBLOCKING_EXCEPT
|
||||
{
|
||||
priv_t * p = (priv_t *)effp->priv;
|
||||
size_t len = *isamp = *osamp = min(*isamp, *osamp);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,6 @@ int lsx_biquad_getopts(sox_effect_t * effp, int n, char **argv,
|
|||
char const * allowed_width_types, filter_t filter_type);
|
||||
int lsx_biquad_start(sox_effect_t * effp);
|
||||
int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
|
||||
size_t *isamp, size_t *osamp);
|
||||
size_t *isamp, size_t *osamp) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ static int default_function(sox_effect_t * effp UNUSED)
|
|||
|
||||
/* Pass through samples verbatim */
|
||||
int lsx_flow_copy(sox_effect_t * effp UNUSED, const sox_sample_t * ibuf,
|
||||
sox_sample_t * obuf, size_t * isamp, size_t * osamp)
|
||||
sox_sample_t * obuf, size_t * isamp, size_t * osamp) FREEDV_NONBLOCKING_EXCEPT
|
||||
{
|
||||
*isamp = *osamp = min(*isamp, *osamp);
|
||||
memcpy(obuf, ibuf, *isamp * sizeof(*obuf));
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ LSX_ and lsx_ symbols should not be used by libSoX-based applications.
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../util/sanitizers.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -1225,7 +1227,7 @@ typedef int (LSX_API * sox_effect_handler_flow)(
|
|||
LSX_PARAM_OUT_CAP_POST_COUNT(*osamp,*osamp) sox_sample_t * obuf, /**< Buffer to which samples are written. */
|
||||
LSX_PARAM_INOUT size_t *isamp, /**< On entry, contains capacity of ibuf; on exit, contains number of samples consumed. */
|
||||
LSX_PARAM_INOUT size_t *osamp /**< On entry, contains capacity of obuf; on exit, contains number of samples written. */
|
||||
);
|
||||
) FREEDV_NONBLOCKING_EXCEPT;
|
||||
|
||||
/**
|
||||
Client API:
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ char * lsx_cat_comments(sox_comments_t comments);
|
|||
/*--------------------------------- Effects ----------------------------------*/
|
||||
|
||||
int lsx_flow_copy(sox_effect_t * effp, const sox_sample_t * ibuf,
|
||||
sox_sample_t * obuf, size_t * isamp, size_t * osamp);
|
||||
sox_sample_t * obuf, size_t * isamp, size_t * osamp) FREEDV_NONBLOCKING_EXCEPT;
|
||||
int lsx_usage(sox_effect_t * effp);
|
||||
char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n);
|
||||
#define EFFECT(f) extern sox_effect_handler_t const * lsx_##f##_effect_fn(void);
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ static int start(sox_effect_t * effp)
|
|||
* Process data.
|
||||
*/
|
||||
static int flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
|
||||
size_t *isamp, size_t *osamp)
|
||||
size_t *isamp, size_t *osamp) FREEDV_NONBLOCKING_EXCEPT
|
||||
{
|
||||
priv_t * vol = (priv_t *) effp->priv;
|
||||
register double gain = vol->gain;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue