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
Mooneer Salem 2025-10-05 21:21:46 -07:00 committed by GitHub
parent 7d19ea8f3d
commit 2ef5d6c671
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
115 changed files with 1155 additions and 717 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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[],

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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__

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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];

View File

@ -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
}

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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}

View 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);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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;

View File

@ -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_;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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__

View File

@ -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;

View File

@ -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;

View File

@ -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_;

View File

@ -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_)
{

View File

@ -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);

View File

@ -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.

View File

@ -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_;
};

View File

@ -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();

View File

@ -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_;
};

View File

@ -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();

View File

@ -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_;
};

View File

@ -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)
{

View File

@ -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_; }

View File

@ -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)
{

View File

@ -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_;

View File

@ -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

View File

@ -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();

View File

@ -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_;
};

View File

@ -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();

View File

@ -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_;

View File

@ -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;

View File

@ -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_;

View File

@ -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++)
{

View File

@ -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__)

View File

@ -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();
}

View File

@ -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_;

View File

@ -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();

View File

@ -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_();
};

View File

@ -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();

View File

@ -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_;

View File

@ -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)

View File

@ -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_();

View File

@ -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);

View File

@ -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_;

View File

@ -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)
{

View File

@ -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_;

View File

@ -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();
}

View File

@ -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_;

View File

@ -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_);

View File

@ -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_;
};

View File

@ -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;

View File

@ -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_;
};

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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));

View File

@ -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:

View File

@ -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);

View File

@ -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