mirror of
https://git.vladimir.cc/vladimir/wdsp.git
synced 2026-08-25 17:27:33 +00:00
645cbbb2d18846a4cc75a2c417a9fda3843f8b4a
hshift() rebuilds the whole interpolated tap set on every output sample,
reading h[hidx + m*R] for m = 0..rsize-1. With R = 1024 (what rmatch asks
for) that strides 8 KB at a time through a 1.1 MB table, so every one of
the 2*rsize reads is its own cache line. It cost more than the filter it
was feeding: 258 ns per output sample against 146 ns for the tap loop.
Store the coefficients transposed instead, hp[p*rsize + m] = h[p + m*R],
so the two phases hshift() interpolates between are each contiguous. R+1
phases are needed since it reads hidx and hidx+1, and h_offset is kept in
[0,1) by the caller so hidx <= R-1. The untransposed h is freed; the
impulse cache hands back a copy, so varsamp owns it. Net memory is
unchanged.
The tap loop had the same wrap test per tap as resample.c did, so split it
at the wrap and carry four independent accumulator pairs; the ring is split
into I/Q so the taps load unit-stride.
Note a->hs is rewritten by hshift() inside the sample loop, so it must not
be hoisted behind a restrict pointer in xvarsamp().
Measured on an Apple M1 Pro, 512-sample buffers, best of 5:
48k -> 48k varmode=0 138580 ns -> 37370 ns 3.71x
48k -> 48k varmode=1 138390 ns -> 32333 ns 4.28x
48k -> 44.1k varmode=1 141473 ns -> 38220 ns 3.70x
44.1k -> 48k varmode=1 152307 ns -> 45263 ns 3.36x
hshift() is numerically identical -- same coefficients, different layout.
Only the reassociated tap sum rounds differently: worst deviation 7.2e-16
over four rate configurations, an SNR of 344 dB. Driven end to end through
rmatch's public API, output SNR is 306 dB and total energy matches.
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%