wdsp/Makefile.android
Uladzimir Karpenka 4313006fa6 Auto-detect Android NDK and javac paths in Makefile.android
Remove hardcoded /home/vladimir and /opt paths. NDK is now resolved via
ANDROID_NDK_HOME, ANDROID_SDK_ROOT/ANDROID_HOME, or the default SDK
location for Linux/macOS (latest version picked automatically). javac is
resolved via JAVA_HOME, system PATH, or Android Studio JBR on Linux/macOS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 21:31:15 +03:00

350 lines
12 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"
#
# FFTW source is downloaded and extracted automatically.
# Override FFTW_SRC to use an existing directory.
# Auto-detect NDK path (can always be overridden by setting ANDROID_NDK explicitly):
# 1. ANDROID_NDK_HOME environment variable
# 2. Latest NDK under ANDROID_SDK_ROOT or ANDROID_HOME
# 3. Default SDK locations: ~/Android/Sdk (Linux) or ~/Library/Android/sdk (macOS)
ifeq ($(origin ANDROID_NDK),undefined)
ifdef ANDROID_NDK_HOME
ANDROID_NDK := $(ANDROID_NDK_HOME)
else
_SDK_ROOT := $(or $(ANDROID_SDK_ROOT),$(ANDROID_HOME),\
$(wildcard $(HOME)/Android/Sdk),\
$(wildcard $(HOME)/Library/Android/sdk))
ifdef _SDK_ROOT
ANDROID_NDK := $(lastword $(sort $(wildcard $(_SDK_ROOT)/ndk/*)))
endif
endif
endif
ifndef ANDROID_NDK
$(error Cannot find Android NDK. Set ANDROID_NDK, ANDROID_NDK_HOME, ANDROID_SDK_ROOT, or ANDROID_HOME)
endif
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/')
# Auto-detect javac: JAVA_HOME > system PATH > Android Studio JBR (Linux/macOS)
_JAVAC_CANDIDATES := \
$(if $(JAVA_HOME),$(wildcard $(JAVA_HOME)/bin/javac)) \
$(shell command -v javac 2>/dev/null) \
$(wildcard /opt/android-studio/jbr/bin/javac) \
$(wildcard /Applications/Android\ Studio.app/Contents/jbr/Contents/Home/bin/javac)
JAVAC ?= $(firstword $(_JAVAC_CANDIDATES))
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_VERSION := 3.3.11
FFTW_TAR := fftw-$(FFTW_VERSION).tar.gz
FFTW_URL := https://fftw.org/pub/fftw/$(FFTW_TAR)
FFTW_SRC ?= third_party/fftw-$(FFTW_VERSION)
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 \
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 distclean java-classes
all: $(FFTW_SRC)/configure \
$(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
# ── Автоматическое получение FFTW ────────────────────────────────────────────
third_party/$(FFTW_TAR):
mkdir -p third_party
@echo ">>> Скачиваем FFTW $(FFTW_VERSION)..."
@if command -v wget >/dev/null 2>&1; then \
wget -q --show-progress -O $@ "$(FFTW_URL)"; \
else \
curl -L --progress-bar -o $@ "$(FFTW_URL)"; \
fi
$(FFTW_SRC)/configure: third_party/$(FFTW_TAR)
@echo ">>> Распаковываем FFTW..."
tar xf $< -C third_party/
@touch $@
# 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): $(FFTW_SRC)/configure
@mkdir -p $$(ABI_$(1)_OBJDIR)/fftw-build $$(ABI_$(1)_FFTW_PREFIX)
cd $$(ABI_$(1)_OBJDIR)/fftw-build && \
$(abspath $(FFTW_SRC))/configure \
--build=$(shell uname -m)-linux-gnu \
--host=$(strip $(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): $(FFTW_SRC)/configure
@mkdir -p $$(ABI_$(1)_OBJDIR)/fftwf-build $$(ABI_$(1)_FFTWF_PREFIX)
cd $$(ABI_$(1)_OBJDIR)/fftwf-build && \
$(abspath $(FFTW_SRC))/configure \
--build=$(shell uname -m)-linux-gnu \
--host=$(strip $(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)
distclean: clean
-rm -rf $(FFTW_SRC) third_party/$(FFTW_TAR)