Files
wdsp/Makefile.android
T
Uladzimir Karpenka 65cb3c386e wfm: add a wideband FM modulator and demodulator
Model the pair on fmd.c / fmmod.c, but with the parts that a 75 kHz
deviation forces:

  - the demodulator discriminates with arg(x[n] * conj(x[n-1])) rather
    than a PLL; an omegaN in the tens of kHz cannot track 75 kHz.
  - emphasis is a one-pole RC (tau 75 us) on both ends, not fmd's 1/f
    fc_impulse FIR.  A 1/f FIR from f_low = 20 Hz would sit ~+57 dB at
    20 Hz, where broadcast FM specifies flat below the corner.
  - TXA_WFM leaves the shared preemph block off; wfmmod carries its own.
  - wfmmod clamps bp_fc = deviation + f_high to 0.45 * samplerate, since
    +/-90 kHz exceeds Nyquist at the rates the narrowband modes use.

Scope is mono: no 19 kHz pilot, no 38 kHz stereo subcarrier, no RDS.

Both mode enums are appended to so the ABI stays stable for clients.
The JNI bindings are deliberately left alone.

Verified against a wfmmod -> wfmd loopback: the discriminator is exact,
the dc-removal one-pole tracks |H_lp(f)| * ain * sdelta to ratio 1.000,
and a 700 + 1900 Hz two-tone comes back with THD+N ~ 3e-5 % at +/-37.5
kHz deviation.  Note the demodulator aliases unless samplerate exceeds
2 * deviation * |aud|max, so 192 kHz is the practical floor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 07:43:07 +03:00

316 lines
10 KiB
Makefile

# Android cross-build for WDSP and JNI bridge
# Usage:
# make android
# make -f Makefile.android all
# Optional overrides:
# ANDROID_NDK=/path/to/ndk ANDROID_API=24 ANDROID_ABIS="arm64-v8a x86_64"
#
# Requires FFTW source in third_party/fftw/
# Download from https://www.fftw.org/download.html and extract so that
# third_party/fftw/configure exists.
ANDROID_NDK ?= /home/vladimir/Android/Sdk/ndk/29.0.14206865
ANDROID_API ?= 24
ANDROID_ABIS ?= arm64-v8a armeabi-v7a x86_64
ANDROID_HOST_TAG ?= $(shell uname -s | tr '[:upper:]' '[:lower:]' | sed 's/darwin/darwin/;s/linux/linux/')-$(shell uname -m | sed 's/aarch64/arm64/;s/x86_64/x86_64/')
JBR_BIN ?= /opt/android-studio/jbr/bin
JAVAC ?= $(JBR_BIN)/javac
TOOLCHAIN := $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(ANDROID_HOST_TAG)
COMMON_CFLAGS ?= -O3 -D_GNU_SOURCE -Wno-parentheses -fPIC
COMMON_CPPFLAGS ?= -I. -I third_party/rnnoise/include -I third_party/libspecbleach/include
ANDROID_JNI_CFLAGS ?= -std=gnu89 -Wno-implicit-function-declaration -Wno-int-conversion \
-Wno-incompatible-pointer-types -Wno-incompatible-pointer-types-discards-qualifiers
FFTW_SRC ?= third_party/fftw
FFTW_MAKEJOBS ?= $(shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
WDSP_SOURCES = amd.c \
ammod.c \
amsq.c \
analyzer.c \
anf.c \
anr.c \
apfshadow.c \
bandpass.c \
calcc.c \
calculus.c \
cblock.c \
cfcomp.c \
cfir.c \
channel.c \
cmath.c \
compress.c \
delay.c \
dexp.c \
div.c \
doublepole.c \
eer.c \
emnr.c \
emph.c \
eq.c \
fcurve.c \
FDnoiseIQ.c \
fir.c \
firmin.c \
fmd.c \
fmmod.c \
fmsq.c \
gain.c \
gaussian.c \
gen.c \
icfir.c \
iir.c \
impulse_cache.c \
iobuffs.c \
iqc.c \
linux_port.c \
lmath.c \
main.c \
matchedCW.c \
meter.c \
meterlog10.c \
nbp.c \
nob.c \
nobII.c \
osctrl.c \
patchpanel.c \
resample.c \
rmatch.c \
rnnr.c \
RXA.c \
sbnr.c \
sender.c \
shift.c \
siphon.c \
slew.c \
snb.c \
ssql.c \
syncbuffs.c \
TXA.c \
utilities.c \
varsamp.c \
version.c \
wcpAGC.c \
wfmd.c \
wfmmod.c \
wisdom.c \
zetaHat.c
RNNOISE_SOURCES = \
third_party/rnnoise/src/denoise.c \
third_party/rnnoise/src/celt_lpc.c \
third_party/rnnoise/src/kiss_fft.c \
third_party/rnnoise/src/nnet.c \
third_party/rnnoise/src/nnet_default.c \
third_party/rnnoise/src/parse_lpcnet_weights.c \
third_party/rnnoise/src/pitch.c \
third_party/rnnoise/src/rnn.c \
third_party/rnnoise/src/rnnoise_data.c \
third_party/rnnoise/src/rnnoise_data_1.c \
third_party/rnnoise/src/rnnoise_data_2.c \
third_party/rnnoise/src/rnnoise_data_3.c \
third_party/rnnoise/src/rnnoise_data_4.c \
third_party/rnnoise/src/rnnoise_data_5.c \
third_party/rnnoise/src/rnnoise_data_6.c \
third_party/rnnoise/src/rnnoise_tables.c
SPECBLEACH_SOURCES = \
third_party/libspecbleach/src/processors/specbleach_adenoiser.c \
third_party/libspecbleach/src/processors/specbleach_denoiser.c \
third_party/libspecbleach/src/processors/adaptivedenoiser/adaptive_denoiser.c \
third_party/libspecbleach/src/shared/gain_estimation/gain_estimators.c \
third_party/libspecbleach/src/shared/noise_estimation/adaptive_noise_estimator.c \
third_party/libspecbleach/src/shared/pre_estimation/absolute_hearing_thresholds.c \
third_party/libspecbleach/src/shared/pre_estimation/critical_bands.c \
third_party/libspecbleach/src/shared/pre_estimation/masking_estimator.c \
third_party/libspecbleach/src/shared/pre_estimation/noise_scaling_criterias.c \
third_party/libspecbleach/src/shared/pre_estimation/spectral_smoother.c \
third_party/libspecbleach/src/shared/pre_estimation/transient_detector.c \
third_party/libspecbleach/src/shared/post_estimation/noise_floor_manager.c \
third_party/libspecbleach/src/shared/post_estimation/postfilter.c \
third_party/libspecbleach/src/shared/post_estimation/spectral_whitening.c \
third_party/libspecbleach/src/shared/utils/denoise_mixer.c \
third_party/libspecbleach/src/shared/utils/general_utils.c \
third_party/libspecbleach/src/shared/utils/spectral_features.c \
third_party/libspecbleach/src/shared/utils/spectral_utils.c \
third_party/libspecbleach/src/shared/stft/stft_processor.c \
third_party/libspecbleach/src/shared/stft/fft_transform.c \
third_party/libspecbleach/src/shared/stft/stft_buffer.c \
third_party/libspecbleach/src/shared/stft/stft_windows.c
ALL_C_SOURCES = $(WDSP_SOURCES) $(RNNOISE_SOURCES) $(SPECBLEACH_SOURCES)
JNI_SOURCE = org_openhpsdr_dsp_Wdsp.c
JAVA_SOURCE = java/org/openhpsdr/dsp/Wdsp.java
ANDROID_OBJ_ROOT := obj/android
ANDROID_LIB_ROOT := lib/android
ANDROID_JAVA_OUT := lib/android/java
.PHONY: all clean java-classes check-fftw-src
all: check-fftw-src \
$(foreach abi,$(ANDROID_ABIS),\
$(ANDROID_LIB_ROOT)/$(abi)/libfftw3.so \
$(ANDROID_LIB_ROOT)/$(abi)/libfftw3f.so \
$(ANDROID_LIB_ROOT)/$(abi)/libwdsp.so \
$(ANDROID_LIB_ROOT)/$(abi)/libwdspj.so) \
java-classes
check-fftw-src:
@test -f $(FFTW_SRC)/configure || { \
echo ""; \
echo "ERROR: FFTW source not found at $(FFTW_SRC)/configure"; \
echo "Download and extract FFTW from https://www.fftw.org/download.html"; \
echo "so that $(FFTW_SRC)/configure exists."; \
echo ""; \
exit 1; \
}
# abi_target: compiler triple for NDK clang wrapper
define abi_target
$(if $(filter $(1),arm64-v8a),aarch64-linux-android,\
$(if $(filter $(1),armeabi-v7a),armv7a-linux-androideabi,\
$(if $(filter $(1),x86_64),x86_64-linux-android,unsupported)))
endef
# fftw_host: --host value for FFTW configure (autoconf canonical names)
define fftw_host
$(if $(filter $(1),arm64-v8a),aarch64-linux-android,\
$(if $(filter $(1),armeabi-v7a),arm-linux-androideabi,\
$(if $(filter $(1),x86_64),x86_64-linux-android,$(1))))
endef
# abi_cflags: extra CFLAGS per ABI
define abi_cflags
$(if $(filter $(1),armeabi-v7a),-march=armv7-a -mfloat-abi=softfp -mfpu=neon,)
endef
# fftw3 (double) SIMD flags per ABI
define fftw_simd
$(if $(filter $(1),x86_64),--enable-sse2,)
endef
# fftw3f (float) SIMD flags per ABI
define fftwf_simd
$(if $(filter $(1),arm64-v8a),--enable-neon,\
$(if $(filter $(1),armeabi-v7a),--enable-neon,\
$(if $(filter $(1),x86_64),--enable-sse --enable-sse2,)))
endef
# -----------------------------------------------------------------------
# Per-ABI rules
# -----------------------------------------------------------------------
define make_abi_rules
ABI_$(1)_TARGET := $(call abi_target,$(1))
ABI_$(1)_CC := $(TOOLCHAIN)/bin/$$(ABI_$(1)_TARGET)$(ANDROID_API)-clang
ABI_$(1)_OBJDIR := $(ANDROID_OBJ_ROOT)/$(1)
ABI_$(1)_LIBDIR := $(ANDROID_LIB_ROOT)/$(1)
ABI_$(1)_CFLAGS := $(COMMON_CFLAGS) $(call abi_cflags,$(1))
# FFTW install prefixes (inside obj tree)
ABI_$(1)_FFTW_PREFIX := $$(ABI_$(1)_OBJDIR)/fftw-install
ABI_$(1)_FFTWF_PREFIX := $$(ABI_$(1)_OBJDIR)/fftwf-install
# Stamp files (avoid re-running configure+make on every build)
ABI_$(1)_FFTW_STAMP := $$(ABI_$(1)_FFTW_PREFIX)/.built
ABI_$(1)_FFTWF_STAMP := $$(ABI_$(1)_FFTWF_PREFIX)/.built
ABI_$(1)_CPPFLAGS := $(COMMON_CPPFLAGS) \
-I third_party/rnnoise/src -I third_party/libspecbleach/include -I third_party/libspecbleach/src -I third_party/libspecbleach/src/shared \
-I$$(ABI_$(1)_FFTW_PREFIX)/include
ABI_$(1)_WDSP_OBJS := $$(patsubst %.c,$$(ABI_$(1)_OBJDIR)/%.o,$$(ALL_C_SOURCES))
ABI_$(1)_JNI_OBJ := $$(ABI_$(1)_OBJDIR)/$(JNI_SOURCE:.c=.o)
# --- Build fftw3 (double) ---
$$(ABI_$(1)_FFTW_STAMP):
@mkdir -p $$(ABI_$(1)_OBJDIR)/fftw-build $$(ABI_$(1)_FFTW_PREFIX)
cd $$(ABI_$(1)_OBJDIR)/fftw-build && \
$(abspath $(FFTW_SRC))/configure \
--host=$(call fftw_host,$(1)) \
CC="$$(ABI_$(1)_CC)" \
CFLAGS="-fPIC" \
--prefix=$(abspath $$(ABI_$(1)_FFTW_PREFIX)) \
--enable-static --disable-shared --disable-fortran \
$(call fftw_simd,$(1)) && \
$(MAKE) -j$(FFTW_MAKEJOBS) install
@touch $$@
# --- Build fftw3f (float/single) ---
$$(ABI_$(1)_FFTWF_STAMP):
@mkdir -p $$(ABI_$(1)_OBJDIR)/fftwf-build $$(ABI_$(1)_FFTWF_PREFIX)
cd $$(ABI_$(1)_OBJDIR)/fftwf-build && \
$(abspath $(FFTW_SRC))/configure \
--host=$(call fftw_host,$(1)) \
CC="$$(ABI_$(1)_CC)" \
CFLAGS="-fPIC" \
--prefix=$(abspath $$(ABI_$(1)_FFTWF_PREFIX)) \
--enable-static --disable-shared --disable-fortran \
--enable-float \
$(call fftwf_simd,$(1)) && \
$(MAKE) -j$(FFTW_MAKEJOBS) install
@touch $$@
# --- Shared fftw3.so / fftw3f.so for packaging alongside libwdsp.so ---
$$(ABI_$(1)_LIBDIR)/libfftw3.so: $$(ABI_$(1)_FFTW_STAMP)
@mkdir -p $$(@D)
$$(ABI_$(1)_CC) -shared -Wl,-soname,libfftw3.so \
-Wl,--whole-archive $$(ABI_$(1)_FFTW_PREFIX)/lib/libfftw3.a -Wl,--no-whole-archive \
-lm -o $$@
$$(ABI_$(1)_LIBDIR)/libfftw3f.so: $$(ABI_$(1)_FFTWF_STAMP)
@mkdir -p $$(@D)
$$(ABI_$(1)_CC) -shared -Wl,-soname,libfftw3f.so \
-Wl,--whole-archive $$(ABI_$(1)_FFTWF_PREFIX)/lib/libfftw3f.a -Wl,--no-whole-archive \
-lm -o $$@
# --- Compile WDSP + rnnoise + specbleach sources ---
$$(ABI_$(1)_OBJDIR)/%.o: %.c | $$(ABI_$(1)_FFTW_STAMP) $$(ABI_$(1)_FFTWF_STAMP)
@mkdir -p $$(@D)
$$(ABI_$(1)_CC) $$(ABI_$(1)_CPPFLAGS) $$(ABI_$(1)_CFLAGS) -c -o $$@ $$<
# --- Build libwdsp.so (statically links fftw) ---
$$(ABI_$(1)_LIBDIR)/libwdsp.so: $$(ABI_$(1)_WDSP_OBJS) $$(ABI_$(1)_FFTW_STAMP) $$(ABI_$(1)_FFTWF_STAMP)
@mkdir -p $$(@D)
$$(ABI_$(1)_CC) -shared -Wl,-soname,libwdsp.so -o $$@ \
$$(ABI_$(1)_WDSP_OBJS) \
$$(ABI_$(1)_FFTW_PREFIX)/lib/libfftw3.a \
$$(ABI_$(1)_FFTWF_PREFIX)/lib/libfftw3f.a \
-lm -llog
# --- Compile JNI bridge ---
$$(ABI_$(1)_JNI_OBJ): $(JNI_SOURCE) org_openhpsdr_dsp_Wdsp.h | $$(ABI_$(1)_FFTW_STAMP)
@mkdir -p $$(@D)
$$(ABI_$(1)_CC) $$(ABI_$(1)_CPPFLAGS) $$(ABI_$(1)_CFLAGS) $(ANDROID_JNI_CFLAGS) \
-I$(TOOLCHAIN)/sysroot/usr/include -c -o $$@ $$<
# --- Build libwdspj.so ---
$$(ABI_$(1)_LIBDIR)/libwdspj.so: $$(ABI_$(1)_JNI_OBJ) $$(ABI_$(1)_LIBDIR)/libwdsp.so
@mkdir -p $$(@D)
$$(ABI_$(1)_CC) -shared -Wl,-soname,libwdspj.so -o $$@ \
$$(ABI_$(1)_JNI_OBJ) \
-L$$(ABI_$(1)_LIBDIR) -lwdsp -llog
endef
$(foreach abi,$(ANDROID_ABIS),$(eval $(call make_abi_rules,$(abi))))
# -----------------------------------------------------------------------
java-classes: $(JAVA_SOURCE)
@if [ -x "$(JAVAC)" ]; then \
mkdir -p "$(ANDROID_JAVA_OUT)"; \
"$(JAVAC)" -d "$(ANDROID_JAVA_OUT)" "$(JAVA_SOURCE)"; \
echo "Java classes built in $(ANDROID_JAVA_OUT)"; \
else \
echo "Skipping Java class build: javac not found at $(JAVAC)"; \
fi
clean:
rm -rf $(ANDROID_OBJ_ROOT) $(ANDROID_LIB_ROOT)