19 Commits

Author SHA1 Message Date
vladimir 544e75798c Merge branch 'wfm' into cpu-optimization 2026-07-18 10:51:50 +03:00
vladimir 3ad40a00da calcc: port eu2av robustness patches for PureSignal calibration
Three fixes from Thetis-Enhanced (Yurij eu2av), measured on Orion MK2 /
Anvelina PRO3 hardware:

- drop overrange samples (env_TX*hw_scale > 1.0) before the cubic
  xbuilder fit - they distort the fit and produce a wrong rx_scale
- optional median-ratio + MAD outlier rejection before the fit,
  controlled by new SetPSOutlierSigma (0 = off)
- fallback rx_scale estimate from the top amplitude intervals when the
  xbuilder fit fails or is rejected by rxscheck

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 10:47:44 +03:00
Uladzimir Karpenka c1964817dd wfm: compile wfmd/wfmmod in the Windows build
Makefile.windows arrived from main, which predates the wfm module, so its
SOURCES list omitted wfmd.c/wfmmod.c even though RXA.c/TXA.c reference their
symbols -- the MinGW link failed on the missing objects. Add both to SOURCES,
matching Makefile and Makefile.android.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:03:03 +03:00
Uladzimir Karpenka 81f7750def Merge remote-tracking branch 'origin/main' into cpu-optimization 2026-07-15 10:50:34 +03:00
Uladzimir Karpenka d24b5ae316 Merge remote-tracking branch 'origin/main' into wfm 2026-07-15 10:50:15 +03:00
vladimir bb7e0b3df6 add gitignore third_party 2026-06-12 21:44:25 +03:00
vladimir 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
vladimir 4f44118299 Remove committed build artifacts from third_party and fix Android FFTW cross-compile
- Untrack all .o and .a files under third_party/ (rnnoise, libspecbleach)
- Add third_party/**/*.o and third_party/**/*.a to .gitignore
- Fix FFTW configure cross-compilation: add $(strip ...) around fftw_host
  macro call and explicit --build flag so configure detects cross-compile
  correctly (GNU make backslash-continuation in define blocks inserts a
  leading space, causing --host= arm-... to be parsed as empty --host)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 21:27:13 +03:00
vladimir edd8991a3c Enable AVX2 for FFTW Windows build to suppress SSE2-only warning
FFTW emits '#warning Only SSE and SSE2 are available' when cross-compiling
with a generic x86_64 target: the configure test for AVX intrinsics fails
without an explicit -mavx flag, so FFTW silently falls back to SSE2.

Fix: pass CFLAGS="-march=haswell" to configure, which enables SSE2/AVX/AVX2/FMA.
Add --enable-avx2 alongside existing --enable-avx.
FFTW_MARCH is overridable for older CPU targets:
  make -f Makefile.windows FFTW_MARCH=-march=sandybridge  # AVX only, 2011+
  make -f Makefile.windows FFTW_MARCH=-march=core2        # SSSE3, 2007+

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 13:39:24 +03:00
vladimir a3385bd1f9 Fix parallel build race: DLL link must wait for fftw-float install
With -jN, the DLL link step could start before fftw-float finished
installing libfftw3f.dll.a, since $(DLL) only had an implicit ordering
through .o compilation (which needs FFTW_LIB_D but not FFTW_LIB_F).

Add both FFTW_LIB_D and FFTW_LIB_F as explicit prerequisites of $(DLL).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 13:35:04 +03:00
vladimir b7d156ea40 Compile rnnoise and libspecbleach directly into obj_win/ for Windows
Sub-make of third_party/ reported 'up to date' when Linux ELF objects
were present from a prior host build, causing the linker to receive
ELF archives instead of PE/COFF and producing undefined reference errors.

Fix: compile rnnoise and libspecbleach sources directly into obj_win/
with explicit pattern rules, producing lib_win/librnnoise.a and
lib_win/libspecbleach.a. These are fully isolated from third_party/
and never conflict with the Linux build artifacts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 13:17:32 +03:00
vladimir d997990ceb Fix two Windows cross-compilation errors in analyzer.c
WDSP_FPE_GUARD: the macro is defined only in linux_port.h under
#if defined(linux)||defined(__APPLE__). On the _WIN32 path it was
completely undefined. Added a fallback no-op definition in comm.h
guarded by #ifndef so it applies to Windows (and any future platform
that doesn't include linux_port.h).

volatile int* vs volatile LONG*: Win32 Interlocked functions expect
volatile LONG* (= volatile long*). The dispatcher field is volatile int.
On Windows LLP64 both are 32-bit so the operation is correct, but GCC 14
promotes this mismatch from warning to error. Suppressed with
-Wno-incompatible-pointer-types in Makefile.windows, consistent with
how MSVC handles it silently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 12:54:56 +03:00
vladimir a77dbbb6e5 Fix Windows.h case on Linux: use lowercase windows.h
On Linux (case-sensitive filesystem) MinGW-w64 installs the header as
windows.h (lowercase) while comm.h included <Windows.h> (capital W),
causing a fatal compile error when cross-compiling for Windows.

Changed to <windows.h> which works on both Linux/MinGW-w64 and native
Windows/MSVC (Windows header includes are case-insensitive on NTFS).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 12:43:45 +03:00
vladimir 90c906eb3a Fix missing Windows.h: require full mingw-w64 package, not just compiler
gcc-mingw-w64-x86-64 on Debian/Ubuntu ships only the compiler binary,
without Windows API headers — causing fatal error: Windows.h not found.
The fix is to install the mingw-w64 meta-package which includes headers,
CRT and runtime libraries.

Added a check-tools probe that compiles #include <windows.h> and prints
a clear error with the correct package name if headers are missing.
Updated README with a warning about the incomplete package.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 12:33:13 +03:00
vladimir f29970542b Pass CC explicitly to FFTW configure to suppress cross-tools warning
Without CC=, autoconf issues "using cross tools not prefixed with host
triplet" because it cannot match the detected compiler against --host.
Passing CC=$(MINGW_PREFIX)-gcc directly resolves the ambiguity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 12:26:39 +03:00
vladimir 69ea631daa Fix parallel build race condition in Makefile.windows
With -jN, make started third-party and fftw targets in parallel.
libspecbleach depended on FFTW_HEADER which has no explicit rule
(it is a side effect of make install), causing 'No rule to make target'.
Object compilation also could start before FFTW headers were ready.

Fix: depend on FFTW_LIB_D (has an explicit rule) instead of FFTW_HEADER,
and add FFTW_LIB_D as order-only prerequisite for .o compilation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 12:24:39 +03:00
vladimir 02926b36ce Auto-download FFTW source for Android build
Makefile.android now downloads fftw-3.3.11.tar.gz automatically instead
of requiring the user to place sources in third_party/fftw/ manually.
The FFTW_SRC variable can still be overridden to use an existing copy.
Added distclean target. Updated README accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 12:15:59 +03:00
vladimir 5bbf9698a8 Build FFTW from source instead of downloading pre-built DLLs
FFTW 3.3.11 has no pre-built Windows binaries, so Makefile.windows now
downloads the source tarball and cross-compiles it twice with MinGW-w64:
once for double precision and once for float (--enable-float), installing
both into third_party/fftw-win64/. Links with -lfftw3/-lfftw3f via
libtool-generated import libs instead of dlltool-generated ones.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 12:13:35 +03:00
vladimir ec93fbfcbb Add Windows cross-compilation support via MinGW-w64
Makefile.windows builds libwdsp.dll and libwdsp.a for 64-bit Windows
on Linux. FFTW 3.3.11 Windows binaries are downloaded automatically
from fftw.org and import libraries are generated with dlltool.
README updated with Windows build instructions and FFTW version bump.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 10:46:51 +03:00
48 changed files with 626 additions and 37 deletions
+4
View File
@@ -7,6 +7,10 @@ java/build/
# third_party — only fftw is downloaded manually, rnnoise and libspecbleach are tracked
third_party/fftw/
third_party/**/*.o
third_party/**/*.a
third_party/fftw-3.3.11.tar.gz
third_party/fftw-3.3.11/
# macOS
.DS_Store
+58 -22
View File
@@ -5,17 +5,39 @@
# 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.
# FFTW source is downloaded and extracted automatically.
# Override FFTW_SRC to use an existing directory.
ANDROID_NDK ?= /home/vladimir/Android/Sdk/ndk/29.0.14206865
# 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/')
JBR_BIN ?= /opt/android-studio/jbr/bin
JAVAC ?= $(JBR_BIN)/javac
# 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)
@@ -24,7 +46,10 @@ COMMON_CPPFLAGS ?= -I. -I third_party/rnnoise/include -I third_party/libspecblea
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_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 \
@@ -150,9 +175,9 @@ ANDROID_OBJ_ROOT := obj/android
ANDROID_LIB_ROOT := lib/android
ANDROID_JAVA_OUT := lib/android/java
.PHONY: all clean java-classes check-fftw-src
.PHONY: all clean distclean java-classes
all: check-fftw-src \
all: $(FFTW_SRC)/configure \
$(foreach abi,$(ANDROID_ABIS),\
$(ANDROID_LIB_ROOT)/$(abi)/libfftw3.so \
$(ANDROID_LIB_ROOT)/$(abi)/libfftw3f.so \
@@ -160,15 +185,21 @@ all: check-fftw-src \
$(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; \
}
# ── Автоматическое получение 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
@@ -228,11 +259,12 @@ 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):
$$(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 \
--host=$(call fftw_host,$(1)) \
--build=$(shell uname -m)-linux-gnu \
--host=$(strip $(call fftw_host,$(1))) \
CC="$$(ABI_$(1)_CC)" \
CFLAGS="-fPIC" \
--prefix=$(abspath $$(ABI_$(1)_FFTW_PREFIX)) \
@@ -242,11 +274,12 @@ $$(ABI_$(1)_FFTW_STAMP):
@touch $$@
# --- Build fftw3f (float/single) ---
$$(ABI_$(1)_FFTWF_STAMP):
$$(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 \
--host=$(call fftw_host,$(1)) \
--build=$(shell uname -m)-linux-gnu \
--host=$(strip $(call fftw_host,$(1))) \
CC="$$(ABI_$(1)_CC)" \
CFLAGS="-fPIC" \
--prefix=$(abspath $$(ABI_$(1)_FFTWF_PREFIX)) \
@@ -313,3 +346,6 @@ java-classes: $(JAVA_SOURCE)
clean:
rm -rf $(ANDROID_OBJ_ROOT) $(ANDROID_LIB_ROOT)
distclean: clean
-rm -rf $(FFTW_SRC) third_party/$(FFTW_TAR)
+294
View File
@@ -0,0 +1,294 @@
#
# Makefile.windows — кросс-компиляция WDSP для 64-bit Windows на Linux (MinGW-w64)
#
# Использование:
# make -f Makefile.windows # DLL + статическая библиотека
# make -f Makefile.windows dll # только DLL
# make -f Makefile.windows static # только статическая библиотека
# make -f Makefile.windows clean # удалить артефакты сборки
# make -f Makefile.windows distclean # + удалить собранный и скачанный FFTW
#
# Зависимости (установить до запуска):
# Arch: sudo pacman -S mingw-w64-gcc (тянет mingw-w64-headers автоматически)
# Debian: sudo apt install mingw-w64 (мета-пакет: компилятор + заголовки + CRT)
# Fedora: sudo dnf install mingw64-gcc (тянет mingw64-headers автоматически)
# + wget или curl, tar
#
MINGW_PREFIX ?= x86_64-w64-mingw32
CC := $(MINGW_PREFIX)-gcc
AR := $(MINGW_PREFIX)-ar
RANLIB := $(MINGW_PREFIX)-ranlib
CFLAGS ?= -O3 -Wno-parentheses -Wno-incompatible-pointer-types
LDFLAGS ?=
# ── FFTW: сборка из исходников ────────────────────────────────────────────────
FFTW_VERSION := 3.3.11
FFTW_TAR := fftw-$(FFTW_VERSION).tar.gz
FFTW_URL := https://fftw.org/pub/fftw/$(FFTW_TAR)
# Абсолютные пути — нужны для configure --prefix и sub-make -C
FFTW_SRC := $(CURDIR)/third_party/fftw-$(FFTW_VERSION)
FFTW_INST := $(CURDIR)/third_party/fftw-win64
# Раздельные build-директории для double и float precision
FFTW_BUILD_D := $(FFTW_INST)/build-double
FFTW_BUILD_F := $(FFTW_INST)/build-float
# FFTW_MARCH задаёт минимальный набор инструкций для Windows-бинарника.
# haswell (2013+) гарантирует AVX2+FMA — оптимальный выбор для SDR на современном ПК.
# Для совместимости со старыми машинами замените на -march=sandybridge (AVX, 2011+)
# или -march=core2 (SSSE3, 2007+).
FFTW_MARCH ?= -march=haswell
FFTW_CONF_COMMON = \
--host=$(MINGW_PREFIX) \
CC=$(CC) \
CFLAGS="$(FFTW_MARCH)" \
--prefix=$(FFTW_INST) \
--enable-shared \
--disable-static \
--with-our-malloc \
--enable-sse2 \
--enable-avx \
--enable-avx2 \
--disable-fortran \
--quiet
# Sentinel-файлы для отслеживания готовности FFTW
FFTW_HEADER := $(FFTW_INST)/include/fftw3.h
FFTW_LIB_D := $(FFTW_INST)/lib/libfftw3.dll.a
FFTW_LIB_F := $(FFTW_INST)/lib/libfftw3f.dll.a
FFTW_DLL_D := $(FFTW_INST)/bin/libfftw3-3.dll
FFTW_DLL_F := $(FFTW_INST)/bin/libfftw3f-3.dll
# ── Выходные директории и файлы ───────────────────────────────────────────────
OBJDIR := obj_win
OUTDIR := lib_win
DLL := $(OUTDIR)/libwdsp.dll
IMPLIB := $(OUTDIR)/libwdsp.dll.a
STATIC_LIB := $(OUTDIR)/libwdsp.a
# ── Исходники (копия из основного Makefile) ───────────────────────────────────
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
OBJS := $(addprefix $(OBJDIR)/, $(SOURCES:.c=.o))
# ── NR3/NR4: исходники third-party ────────────────────────────────────────────
#
# Компилируем rnnoise и libspecbleach напрямую в obj_win/, минуя их sub-make.
# Это исключает конфликт с Linux-сборкой: third_party/*.a содержат ELF-объекты,
# и если они "свежее" исходников, sub-make считает их up-to-date и не пересобирает.
RNNOISE_DIR := third_party/rnnoise
SPECBLEACH_DIR := third_party/libspecbleach
RNNOISE_SRCS := \
src/denoise.c src/celt_lpc.c src/kiss_fft.c src/nnet.c \
src/nnet_default.c src/parse_lpcnet_weights.c src/pitch.c \
src/rnn.c src/rnnoise_data.c \
src/rnnoise_data_1.c src/rnnoise_data_2.c src/rnnoise_data_3.c \
src/rnnoise_data_4.c src/rnnoise_data_5.c src/rnnoise_data_6.c \
src/rnnoise_tables.c
SPECBLEACH_SRCS := \
src/processors/specbleach_adenoiser.c \
src/processors/specbleach_denoiser.c \
src/processors/adaptivedenoiser/adaptive_denoiser.c \
src/shared/gain_estimation/gain_estimators.c \
src/shared/noise_estimation/adaptive_noise_estimator.c \
src/shared/pre_estimation/absolute_hearing_thresholds.c \
src/shared/pre_estimation/critical_bands.c \
src/shared/pre_estimation/masking_estimator.c \
src/shared/pre_estimation/noise_scaling_criterias.c \
src/shared/pre_estimation/spectral_smoother.c \
src/shared/pre_estimation/transient_detector.c \
src/shared/post_estimation/noise_floor_manager.c \
src/shared/post_estimation/postfilter.c \
src/shared/post_estimation/spectral_whitening.c \
src/shared/utils/denoise_mixer.c \
src/shared/utils/general_utils.c \
src/shared/utils/spectral_features.c \
src/shared/utils/spectral_utils.c \
src/shared/stft/stft_processor.c \
src/shared/stft/fft_transform.c \
src/shared/stft/stft_buffer.c \
src/shared/stft/stft_windows.c
RNNOISE_OBJS := $(addprefix $(OBJDIR)/rnnoise/, $(RNNOISE_SRCS:.c=.o))
SPECBLEACH_OBJS := $(addprefix $(OBJDIR)/specbleach/, $(SPECBLEACH_SRCS:.c=.o))
RNNOISE_LIB := $(OUTDIR)/librnnoise.a
SPECBLEACH_LIB := $(OUTDIR)/libspecbleach.a
# ── Флаги компиляции ──────────────────────────────────────────────────────────
NR34_INC := -I$(RNNOISE_DIR)/include -I$(SPECBLEACH_DIR)/include
FFTW_INC := -I$(FFTW_INST)/include
COMPILE := $(CC) $(CFLAGS) $(FFTW_INC) $(NR34_INC)
NR34_DEPS := $(RNNOISE_LIB) $(SPECBLEACH_LIB)
# avrt нужен т.к. comm.h включает <avrt.h> под _WIN32
LINK_LIBS := $(NR34_DEPS) \
-L$(FFTW_INST)/lib -lfftw3 -lfftw3f \
-lavrt -lm
# ── Цели ──────────────────────────────────────────────────────────────────────
.PHONY: all dll static fftw fftw-double fftw-float third-party check-tools clean distclean
all: dll static
dll: check-tools fftw third-party $(DLL)
static: check-tools fftw third-party $(STATIC_LIB)
# ── Проверка инструментов ─────────────────────────────────────────────────────
check-tools:
@command -v $(CC) >/dev/null 2>&1 || { \
echo ""; \
echo "ОШИБКА: $(CC) не найден. Установите mingw-w64:"; \
echo " Arch: sudo pacman -S mingw-w64-gcc"; \
echo " Debian: sudo apt install mingw-w64"; \
echo " Fedora: sudo dnf install mingw64-gcc"; \
echo ""; exit 1; }
@echo '#include <windows.h>' | $(CC) -x c - -fsyntax-only -Wno-pragma-once-outside-header 2>/dev/null || { \
echo ""; \
echo "ОШИБКА: Windows API заголовки не найдены."; \
echo "Установите пакет с заголовками MinGW-w64:"; \
echo " Arch: sudo pacman -S mingw-w64-headers"; \
echo " Debian: sudo apt install mingw-w64 # не gcc-mingw-w64-x86-64!"; \
echo " Fedora: sudo dnf install mingw64-headers"; \
echo ""; exit 1; }
@(command -v wget >/dev/null 2>&1 || command -v curl >/dev/null 2>&1) || { \
echo "ОШИБКА: требуется wget или curl"; exit 1; }
# ── FFTW: скачать исходники → собрать double → собрать float ──────────────────
fftw: fftw-double fftw-float
# Шаг 1: скачать tarball
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
# Шаг 2: распаковать
$(FFTW_SRC)/configure: third_party/$(FFTW_TAR)
@echo ">>> Распаковываем FFTW..."
tar xf $< -C third_party/
@touch $@
# Шаг 3a: configure для double precision
$(FFTW_BUILD_D)/Makefile: $(FFTW_SRC)/configure
mkdir -p $(FFTW_BUILD_D)
@echo ">>> Конфигурируем FFTW (double)..."
cd $(FFTW_BUILD_D) && $(FFTW_SRC)/configure $(FFTW_CONF_COMMON)
# Шаг 4a: сборка и установка double precision
fftw-double: $(FFTW_LIB_D)
$(FFTW_LIB_D): $(FFTW_BUILD_D)/Makefile
@echo ">>> Собираем FFTW (double)..."
$(MAKE) -C $(FFTW_BUILD_D) install
# Шаг 3b: configure для float precision (--enable-float)
$(FFTW_BUILD_F)/Makefile: $(FFTW_SRC)/configure
mkdir -p $(FFTW_BUILD_F)
@echo ">>> Конфигурируем FFTW (float)..."
cd $(FFTW_BUILD_F) && $(FFTW_SRC)/configure $(FFTW_CONF_COMMON) --enable-float
# Шаг 4b: сборка и установка float precision
# Зависит от fftw-double чтобы install-шаги не конкурировали за prefix
fftw-float: $(FFTW_LIB_F)
$(FFTW_LIB_F): $(FFTW_BUILD_F)/Makefile $(FFTW_LIB_D)
@echo ">>> Собираем FFTW (float)..."
$(MAKE) -C $(FFTW_BUILD_F) install
# ── Third-party библиотеки (кросс-компиляция) ─────────────────────────────────
third-party: $(NR34_DEPS)
$(RNNOISE_LIB): $(RNNOISE_OBJS) | $(OUTDIR)
$(AR) rv $@ $^
$(RANLIB) $@
$(SPECBLEACH_LIB): $(SPECBLEACH_OBJS) | $(OUTDIR)
$(AR) rv $@ $^
$(RANLIB) $@
# % в GNU make совпадает через '/', поэтому паттерн покрывает вложенные пути
$(OBJDIR)/rnnoise/%.o: $(RNNOISE_DIR)/%.c
@mkdir -p $(@D)
$(CC) $(CFLAGS) -I$(RNNOISE_DIR)/include -I$(RNNOISE_DIR)/src -c -o $@ $<
$(OBJDIR)/specbleach/%.o: $(SPECBLEACH_DIR)/%.c | $(FFTW_LIB_D)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -I$(FFTW_INST)/include \
-I$(SPECBLEACH_DIR)/include -I$(SPECBLEACH_DIR)/src -I$(SPECBLEACH_DIR)/src/shared \
-c -o $@ $<
# ── Основная библиотека ───────────────────────────────────────────────────────
$(OUTDIR) $(OBJDIR):
mkdir -p $@
$(DLL): $(OBJS) $(NR34_DEPS) $(FFTW_LIB_D) $(FFTW_LIB_F) | $(OUTDIR)
@echo ">>> Линкуем $@..."
$(CC) -shared \
-Wl,--out-implib,$(IMPLIB) \
$(LDFLAGS) \
-o $@ \
$(OBJS) $(LINK_LIBS)
@cp $(FFTW_DLL_D) $(FFTW_DLL_F) $(OUTDIR)/
@echo ""
@echo "=== Готово ==="
@echo " DLL: $(DLL)"
@echo " Import lib: $(IMPLIB)"
@echo " Заголовок: wdsp.h"
@echo ""
@echo " FFTW DLL скопированы в $(OUTDIR)/ — включите их в дистрибутив"
@echo " рядом с libwdsp.dll."
$(STATIC_LIB): $(OBJS) | $(OUTDIR)
$(AR) rv $@ $(OBJS)
$(RANLIB) $@
@echo ""
@echo "=== Готово ==="
@echo " Статическая: $(STATIC_LIB)"
@echo " Линкуйте приложение с:"
@echo " $(STATIC_LIB) $(NR34_DEPS)"
@echo " -L$(FFTW_INST)/lib -lfftw3 -lfftw3f -lavrt -lm"
$(OBJDIR)/%.o: %.c | $(OBJDIR) $(FFTW_LIB_D)
$(COMPILE) -c -o $@ $<
# ── Очистка ───────────────────────────────────────────────────────────────────
clean:
-rm -rf $(OBJDIR) $(OUTDIR)
# distclean удаляет собранный и скачанный FFTW — при следующем запуске пересоберётся
distclean: clean
-rm -rf $(FFTW_INST) $(FFTW_SRC) third_party/$(FFTW_TAR)
+91 -12
View File
@@ -13,14 +13,18 @@ wdsp/
├── org_openhpsdr_dsp_Wdsp.c/.h — JNI bridge
├── Makefile — host build (Linux / macOS)
├── Makefile.android — Android cross-build
├── Makefile.windows — Windows cross-build (MinGW-w64, runs on Linux)
├── java/
│ └── org/openhpsdr/dsp/Wdsp.java
├── third_party/
│ ├── fftw/ — FFTW source (download manually, see below)
│ ├── fftw-3.3.11/ — FFTW source (downloaded automatically)
│ ├── fftw-win64/ — FFTW Windows binaries (downloaded automatically)
│ ├── rnnoise/ — RNNoise noise suppression
│ └── libspecbleach/ — spectral noise reduction
├── obj/ — object files (generated)
── lib/ — built libraries (generated)
── obj_win/ — Windows object files (generated)
├── lib/ — built libraries (generated)
└── lib_win/ — Windows libraries (generated)
```
---
@@ -81,20 +85,95 @@ make clean
---
## Windows build (кросс-компиляция с Linux)
Сборка выполняется на Linux с помощью MinGW-w64. FFTW скачивается и собирается из исходников автоматически.
### Зависимости
```bash
# Arch
sudo pacman -S mingw-w64-gcc # тянет mingw-w64-headers автоматически
# Debian / Ubuntu — нужен мета-пакет mingw-w64, а не только компилятор
sudo apt install mingw-w64
# Fedora
sudo dnf install mingw64-gcc # тянет mingw64-headers автоматически
```
Также нужен `wget` или `curl`.
> **Debian/Ubuntu:** пакет `gcc-mingw-w64-x86-64` содержит только компилятор без Windows API заголовков — `Windows.h` не будет найден. Используйте `mingw-w64`.
### Сборка
```bash
# DLL + статическая библиотека (рекомендуется)
make -f Makefile.windows
# Только DLL
make -f Makefile.windows dll
# Только статическая библиотека
make -f Makefile.windows static
```
Результат в `lib_win/`:
| Файл | Назначение |
|---|---|
| `libwdsp.dll` | DLL для Windows |
| `libwdsp.dll.a` | Import library для MinGW (`-lwdsp`) |
| `libwdsp.a` | Статическая библиотека |
| `libfftw3-3.dll` | Runtime dependency — распространять вместе с DLL |
| `libfftw3f-3.dll` | Runtime dependency — распространять вместе с DLL |
### Линковка приложения
**MinGW:**
```bash
gcc myapp.c -Llib_win -lwdsp -o myapp.exe
```
**MSVC** — переименовать `libwdsp.dll.a` в `libwdsp.lib` и подключить обычным образом.
**Статически** (MinGW):
```bash
gcc myapp.c libwdsp.a third_party/rnnoise/librnnoise.a \
third_party/libspecbleach/libspecbleach.a \
-Lthird_party/fftw-win64 -lfftw3-3 -lfftw3f-3 -lavrt -lm -o myapp.exe
```
### Параметры
| Переменная | По умолчанию | Описание |
|---|---|---|
| `MINGW_PREFIX` | `x86_64-w64-mingw32` | Префикс MinGW toolchain |
| `FFTW_VERSION` | `3.3.11` | Версия FFTW |
| `CFLAGS` | `-O3 -Wno-parentheses` | Флаги компилятора |
```bash
make -f Makefile.windows MINGW_PREFIX=i686-w64-mingw32 # 32-bit Windows
```
### Очистка
```bash
make -f Makefile.windows clean # артефакты сборки
make -f Makefile.windows distclean # + удалить скачанный FFTW
```
---
## Android build
### Prerequisites
1. **Android NDK** r23 or newer
2. **FFTW source** — download and extract into `third_party/fftw/`:
2. **Java compiler** — for building the `.class` file (Android Studio's JBR or any JDK)
```bash
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
```
3. **Java compiler** — for building the `.class` file (Android Studio's JBR or any JDK)
FFTW 3.3.11 скачивается и собирается автоматически. Чтобы использовать уже скачанный исходник, передайте `FFTW_SRC=/path/to/fftw-3.3.11`.
### Build
@@ -144,8 +223,8 @@ make -f Makefile.android \
### Clean
```bash
make -f Makefile.android clean
# removes obj/android/ and lib/android/
make -f Makefile.android clean # удаляет obj/android/ и lib/android/
make -f Makefile.android distclean # + удаляет скачанный FFTW
```
---
+171 -2
View File
@@ -138,6 +138,7 @@ CALCC create_calcc (int channel, int runcal, int size, int rate, int ints, int s
a->stbl = stbl;
a->npsamps = npsamps;
a->alpha = alpha;
a->outlier_sigma = 0.0;
a->info = (int *) malloc0 (16 * sizeof (int));
a->binfo = (int *) malloc0 (16 * sizeof (int));
@@ -321,6 +322,125 @@ void rxscheck (int rints, double* tvec, double* coef, int* info)
if (out < 0.00) *info |= 0x0020;
}
// Yurij_eu2av: fallback rx_scale estimator. It averages the top few
// amplitude intervals (ignoring overrange samples) and linearly extrapolates
// to full TX scale (env_TX = 1/hw_scale). Used only if the cubic xbuilder
// fit fails or is rejected by rxscheck.
static int estimate_rx_scale_from_top_intervals(CALCC a, double* rx_scale_out)
{
const int n_top = 4;
double sx[4], sy[4], sw[4];
int valid = 0;
int b, j;
for (b = a->ints - 1; b >= 0 && valid < n_top; b--)
{
int base = b * a->spi;
double sum_x = 0.0, sum_y = 0.0;
int n = 0;
for (j = 0; j < a->spi; j++)
{
int k = base + j;
double nx = a->env_TX[k] * a->hw_scale;
if (nx > 1.0 || nx < 0.0) continue;
if (a->env_TX[k] < 1.0e-30 || a->env_RX[k] < 1.0e-30) continue;
sum_x += a->env_TX[k];
sum_y += a->env_RX[k];
n++;
}
if (n == 0) continue;
sx[valid] = sum_x / (double)n;
sy[valid] = sum_y / (double)n;
sw[valid] = (double)n;
valid++;
}
if (valid < 2) return -1;
{
double s_w = 0.0, s_x = 0.0, s_y = 0.0, s_xx = 0.0, s_xy = 0.0;
double det, aa, bb, target_x, y_at_target;
int i;
for (i = 0; i < valid; i++)
{
double w = sw[i];
s_w += w;
s_x += w * sx[i];
s_y += w * sy[i];
s_xx += w * sx[i] * sx[i];
s_xy += w * sx[i] * sy[i];
}
det = s_w * s_xx - s_x * s_x;
if (fabs(det) < 1e-30) return -1;
bb = (s_w * s_xy - s_x * s_y) / det;
aa = (s_y - bb * s_x) / s_w;
target_x = 1.0 / a->hw_scale;
y_at_target = aa + bb * target_x;
if (y_at_target <= 1e-15) return -1;
*rx_scale_out = 1.0 / y_at_target;
}
return 0;
}
// Yurij_eu2av: robust outlier rejection for the cubic-spline xbuilder.
// Fits rx = k*tx through the origin via median ratio, then rejects points
// whose residual exceeds sigma * MAD.
static int cmp_double(const void* a, const void* b)
{
double da = *(const double*)a;
double db = *(const double*)b;
if (da < db) return -1;
if (da > db) return 1;
return 0;
}
static double median_double(double* v, int n)
{
if (n <= 0) return 0.0;
if (n % 2 == 1)
return v[n / 2];
else
return 0.5 * (v[n / 2 - 1] + v[n / 2]);
}
static int reject_outliers(double* tx, double* rx, int n, double sigma)
{
const int min_points = 32;
int i, keep = 0;
double* ratios;
double* absres;
double med_ratio, med_absres, thr;
if (n < min_points || sigma <= 0.0) return n;
ratios = (double*)malloc0(n * sizeof(double));
for (i = 0; i < n; i++)
ratios[i] = (tx[i] > 1.0e-30) ? rx[i] / tx[i] : 0.0;
qsort(ratios, n, sizeof(double), cmp_double);
med_ratio = median_double(ratios, n);
_aligned_free(ratios);
if (fabs(med_ratio) < 1.0e-30) return n;
absres = (double*)malloc0(n * sizeof(double));
for (i = 0; i < n; i++)
absres[i] = fabs(rx[i] - med_ratio * tx[i]);
qsort(absres, n, sizeof(double), cmp_double);
med_absres = median_double(absres, n);
_aligned_free(absres);
if (med_absres < 1.0e-30) return n;
thr = sigma * med_absres;
for (i = 0; i < n; i++)
{
if (fabs(rx[i] - med_ratio * tx[i]) <= thr)
{
tx[keep] = tx[i];
rx[keep] = rx[i];
keep++;
}
}
return (keep >= min_points) ? keep : n;
}
void calc (CALCC a)
{
int i;
@@ -336,21 +456,60 @@ void calc (CALCC a)
double tvec[3];
double txrxcoefs[4 * 2];
double rx_scale;
int xb_ok = 0;
double* tx_filt;
double* rx_filt;
int n_filt = 0;
if (a->ints < 16) rints = 1;
else rints = 2;
ix = rints - 1;
for (i = 0; i <= rints; i++)
tvec[i] = (double)i / (double)rints / a->hw_scale;
dx = tvec[rints] - tvec[rints - 1];
xbuilder(a->ccbld, a->nsamps, a->env_TX, a->env_RX, rints, tvec, &(a->binfo[0]), txrxcoefs, a->ptol);
// Yurij_eu2av: build a filtered dataset with overrange samples removed
// before running xbuilder. Overrange env_TX*hw_scale > 1.0 can distort
// the cubic fit and produce an incorrect rx_scale.
tx_filt = (double*)malloc0(a->nsamps * sizeof(double));
rx_filt = (double*)malloc0(a->nsamps * sizeof(double));
for (i = 0; i < a->nsamps; i++)
{
double nx = a->env_TX[i] * a->hw_scale;
if (nx > 1.0 || nx < 0.0) continue;
if (a->env_TX[i] < 1.0e-30 || a->env_RX[i] < 1.0e-30) continue;
tx_filt[n_filt] = a->env_TX[i];
rx_filt[n_filt] = a->env_RX[i];
n_filt++;
}
// Yurij_eu2av: optional outlier rejection before cubic-spline fit.
if (a->outlier_sigma > 0.0)
n_filt = reject_outliers(tx_filt, rx_filt, n_filt, a->outlier_sigma);
xbuilder(a->ccbld, n_filt, tx_filt, rx_filt, rints, tvec, &(a->binfo[0]), txrxcoefs, a->ptol);
rxscheck (rints, tvec, txrxcoefs, &a->binfo[7]);
if ((a->binfo[0] == 0) && (a->binfo[7] == 0))
{
rx_scale = 1.0 / (txrxcoefs[4 * ix + 0] + dx * (txrxcoefs[4 * ix + 1] + dx * (txrxcoefs[4 * ix + 2] + dx * txrxcoefs[4 * ix + 3])));
else
xb_ok = 1;
}
else if (estimate_rx_scale_from_top_intervals(a, &rx_scale) == 0)
{
// Yurij_eu2av: xbuilder failed, but the bucket-average fallback
// gave a usable rx_scale. Keep binfo[0] bit 0 set for diagnostics.
a->binfo[0] |= 0x0001;
xb_ok = 1;
}
_aligned_free(tx_filt);
_aligned_free(rx_filt);
if (!xb_ok)
{
a->scOK = 0;
goto cleanup;
}
if (a->stbl && _InterlockedAnd (&a->ctrl.running, 1))
a->rx_scale = a->alpha * a->rx_scale + (1.0 - a->alpha) * rx_scale;
else
@@ -1046,6 +1205,16 @@ void SetPSPtol (int channel, double ptol)
LeaveCriticalSection (&txa[channel].calcc.cs_update);
}
PORT
void SetPSOutlierSigma (int channel, double sigma)
{
// Yurij_eu2av: 0.0 disables the pre-xbuilder outlier filter.
if (sigma < 0.0) sigma = 0.0;
EnterCriticalSection (&txa[channel].calcc.cs_update);
txa[channel].calcc.p->outlier_sigma = sigma;
LeaveCriticalSection (&txa[channel].calcc.cs_update);
}
PORT
void GetPSDisp (int channel, double* x, double* ym, double* yc, double* ys, double* cm, double* cc, double* cs)
{
+1
View File
@@ -48,6 +48,7 @@ typedef struct _calcc
double hw_scale;
double rx_scale;
double alpha;
double outlier_sigma;
int tsamps;
double* env_TX;
+6 -1
View File
@@ -33,7 +33,7 @@ warren@wpratt.com
#endif
#ifdef _WIN32
#include <Windows.h>
#include <windows.h>
#include <process.h>
#include <intrin.h>
#endif
@@ -43,6 +43,11 @@ warren@wpratt.com
#ifdef _WIN32
#include <avrt.h>
#endif
#ifndef WDSP_FPE_GUARD
#define WDSP_FPE_GUARD ((void)0)
#define WDSP_FPE_RESTORE ((void)0)
#endif
#include "fftw3.h"
#include "amd.h"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -277,6 +277,7 @@ extern void SetPSHWPeak (int channel, double peak);
extern void GetPSHWPeak (int channel, double* peak);
extern void GetPSMaxTX (int channel, double* maxtx);
extern void SetPSPtol (int channel, double ptol);
extern void SetPSOutlierSigma (int channel, double sigma);
extern void GetPSDisp (int channel, double* x, double* ym, double* yc, double* ys, double* cm, double* cc, double* cs);
extern void SetPSFeedbackRate (int channel, int rate);
extern void SetPSPinMode (int channel, int pin);