mirror of
https://git.vladimir.cc/vladimir/wdsp.git
synced 2026-08-25 17:27:33 +00:00
fd2ba84e7d5248fe04d66fb2055f29c8a7fc8902
Three separate costs, found with a sampling profile of the RX chain with
emnr forced on:
aepf() averaged mask[] over a window of N = 2*psi + 1 = 41 bins by walking
the window for every one of the 2049 bins, i.e. O(msize*N). Its three spans
are all symmetric windows clipped at the array ends, so take each from a
prefix sum instead: one subtraction per output, O(msize).
xemnr() advanced four ring indices with a '% size' per step. iasize is 4096
and oasize 1024 here, and neither is known to the compiler, so each step was
a real integer division -- ~8700 of them per frame. The indices step by one
and, since iasize >= fsize and oasize >= incr always hold, wrap at most once
per loop, so walk contiguous runs and wrap between them.
calc_gain() called getKey() twice per bin with the same gamma, so the gamma
row index and its log10 were computed twice. Split getKey into keyIndex() +
keyLerp() and locate gamma once. The remaining logs go through wdsp_log10()
(new fastmath.h), accurate to 2e-13 against libm and ~2.4x its throughput;
gamma and xi are bracketed against the table limits first, so the argument
is always positive and normal. Also clamp the row index so the second
bilinear corner cannot address the next row of the 241x241 table.
Measured in situ on an Apple M1 Pro, 512-sample buffers, cost of turning
emnr on, best of 5:
baseline 73661 ns
+ aepf, ring walks 48927 ns 1.51x
+ getKey 37852 ns 1.95x
Output is not bit-identical, as the prefix sum and the reassociated logs
round differently. Over 300 buffers with emnr alone the worst deviation is
4.0e-09, an SNR of 196 dB; perturbing a single input sample of the unmodified
code by one ulp diverges it from itself by 1.4e-08 (186 dB), so this change
disturbs the chain less than the last bit of the input does.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WDSP
DSP library for SDR (Software Defined Radio) applications.
Originally written for Windows by Warren Pratt, NR0V. Ported to Linux and Android by John Melton G0ORX/N6LYT.
Project structure
wdsp/
├── *.c / *.h — WDSP library sources
├── org_openhpsdr_dsp_Wdsp.c/.h — JNI bridge
├── Makefile — host build (Linux / macOS)
├── Makefile.android — Android cross-build
├── java/
│ └── org/openhpsdr/dsp/Wdsp.java
├── third_party/
│ ├── fftw/ — FFTW source (download manually, see below)
│ ├── rnnoise/ — RNNoise noise suppression
│ └── libspecbleach/ — spectral noise reduction
├── obj/ — object files (generated)
└── lib/ — built libraries (generated)
Host build (Linux / macOS)
Dependencies
Linux:
sudo apt install build-essential libfftw3-dev pkg-config default-jdk
macOS:
brew install fftw pkg-config
Build
# Full build: static + shared + JNI library + Java classes
make
# Individual targets
make static # lib/libwdsp.a
make shared # lib/libwdsp.so (or .dylib on macOS)
make java # lib/libwdspj.so + java/build/
Install
sudo make install # installs to /usr/local/lib and /usr/local/include
sudo make PREFIX=/opt/wdsp install # custom prefix
sudo make install_java # install libwdspj alongside libwdsp
Options
| Variable | Default | Description |
|---|---|---|
PREFIX |
/usr/local |
Install prefix |
NR34LIB |
off | Set to ON to skip bundled rnnoise/libspecbleach |
CC |
gcc |
C compiler |
CFLAGS |
-pthread -O3 ... |
Compiler flags |
make NR34LIB=ON # build without NR3/NR4 noise reduction
make CC=clang
Clean
make clean
Android build
Prerequisites
- Android NDK r23 or newer
- FFTW source — download and extract into
third_party/fftw/:
wget https://www.fftw.org/fftw-3.3.10.tar.gz
tar xf fftw-3.3.10.tar.gz
mv fftw-3.3.10 third_party/fftw
- Java compiler — for building the
.classfile (Android Studio's JBR or any JDK)
Build
make android
# or directly
make -f Makefile.android
Output is placed into:
lib/android/
├── arm64-v8a/
│ ├── libfftw3.so
│ ├── libfftw3f.so
│ ├── libwdsp.so
│ └── libwdspj.so
├── armeabi-v7a/
│ └── ...
├── x86_64/
│ └── ...
└── java/
└── org/openhpsdr/dsp/Wdsp.class
libwdsp.so has FFTW linked statically — no separate FFTW dependency at runtime on the device.
Options
| Variable | Default | Description |
|---|---|---|
ANDROID_NDK |
/home/vladimir/Android/Sdk/ndk/29.0.14206865 |
Path to Android NDK |
ANDROID_API |
24 |
Minimum API level |
ANDROID_ABIS |
arm64-v8a armeabi-v7a x86_64 |
Target ABIs |
ANDROID_HOST_TAG |
auto-detected | Host platform (linux-x86_64, darwin-x86_64, etc.) |
FFTW_SRC |
third_party/fftw |
Path to FFTW source |
JAVAC |
/opt/android-studio/jbr/bin/javac |
Java compiler |
make -f Makefile.android \
ANDROID_NDK=~/Android/Sdk/ndk/26.3.11579264 \
ANDROID_API=26 \
ANDROID_ABIS=arm64-v8a
Clean
make -f Makefile.android clean
# removes obj/android/ and lib/android/
Using in an Android project
Copy lib/android/{abi}/ into your Android project's jniLibs:
app/src/main/jniLibs/
├── arm64-v8a/
│ ├── libwdsp.so
│ └── libwdspj.so
├── armeabi-v7a/
│ └── ...
└── x86_64/
└── ...
Add Wdsp.java to your source tree and load the libraries at startup:
System.loadLibrary("wdsp");
System.loadLibrary("wdspj");
Or use the singleton:
Wdsp wdsp = Wdsp.getInstance();
Languages
C
99.9%
Java
0.1%