P1.1: embed runtime-captured DSP tables (WIN_WINDOW, FREQAXIS@48k, WA/WB/WC/WD), dsp_ctx registry mirror, tables_check ALL OK

This commit is contained in:
2026-08-19 22:48:32 +03:00
parent 83af30d7c0
commit fd9b1fd001
5 changed files with 5595 additions and 0 deletions
+2
View File
@@ -22,7 +22,9 @@ add_library(soothe2_dsp SHARED
add_executable(soothe2_harness harness.cpp)
add_executable(twin_check twin_check.cpp)
add_executable(tables_check tables_check.cpp)
target_link_libraries(twin_check soothe2_dsp)
target_link_libraries(tables_check soothe2_dsp)
target_link_libraries(soothe2_harness soothe2_dsp)
target_include_directories(soothe2_dsp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+53
View File
@@ -0,0 +1,53 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include "tables_data.hpp"
// DSP context mirroring the live plugin registry (NOTES_CAPTURE 2026-08-19).
// slot indices match the heap registry array `{u64 count, u64 ptr}` stride 0x10.
namespace dsp_ctx {
enum RegistrySlot : uint32_t {
R_IDENTITY = 0x00, // 8193, ~1.0
R_WINDOW = 0x01, // 8193 f32 0.5->1.0 — WIN_freq (0x540658 FFT-conv window)
R_LEVELS = 0x02, // 8193, 0 -> ~0.01 (levels/curve)
R_WA = 0x03, // 8193, 0.596->0.126 = rwin_C0
R_WB = 0x04, // 8193, 0.404->0.874 = 1-[03]
R_WC = 0x05, // 8193, 0.0435->0
R_WD = 0x06, // 8193, 0.9565->~1 = 1-[05]
R_AUX = 0x07, // 8193, zeros + small negatives
R_FREQ_AXIS = 0x0d, // 2049, 0..23988Hz @48000 internal
R_LUT_KNEE = 0x0e, // 8193, 2.017->0
R_LUT_KNEE2 = 0x10, // 16384, 1.2914->0
R_FIRSTFIRE = 0x14, // 8193, first-fire IR?
R_RAMP_32768a = 0x17, // 32768, 0.9999->1
R_RAMP_32768b = 0x19, // 32768, 1.2915->1.0
};
// Internal frequency axis (registry[0d]): spacing = 48000/4096 ≈ 11.713 Hz.
constexpr float INTERNAL_SR = 48000.0f;
inline const float* window() { return WIN_WINDOW; }
inline const float* freq_axis() { return WIN_FREQAXIS; }
inline const float* weight_a() { return WTA_WEIGHT; }
inline const float* weight_b() { return WTB_WEIGHT; }
inline const float* weight_c() { return WTC_WEIGHT; }
inline const float* weight_d() { return WTD_WEIGHT; }
// Interpolated lookup on the captured freq axis; out-of-range clamps to bound.
inline float freq_at(float idx) {
const size_t n = WIN_FREQAXIS_COUNT;
if (idx <= 0.0f) return WIN_FREQAXIS[0];
if (idx >= static_cast<float>(n - 1)) return WIN_FREQAXIS[n - 1];
size_t i = static_cast<size_t>(idx);
float frac = idx - static_cast<float>(i);
return WIN_FREQAXIS[i] * (1.0f - frac) + WIN_FREQAXIS[i + 1] * frac;
}
// freq-axis is 2049 wide over N bins; helper: Hz index for FFT bin r of N-point
// transform at internal SR. Matches spacing 48000/N for N=4096.
inline float hz_of_bin(float r, float nfft) {
return r * (INTERNAL_SR / nfft);
}
} // namespace dsp_ctx
+59
View File
@@ -0,0 +1,59 @@
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include "dsp_ctx.hpp"
#include "tables_data.hpp"
int main() {
const size_t N = 8193;
bool ok = true;
auto report = [&](const char* name, double v, double expect, double tol) {
bool p = std::fabs(v - expect) < tol;
ok = ok && p;
std::printf(" %-28s %12.6f (%s)\n", name, v, p ? "ok" : "MISMATCH");
};
std::printf("dsp_ctx tables synthetic check (from runtime capture npy)\n");
std::printf("window WIN_WINDOW[%zu]\n", WIN_WINDOW_COUNT);
report("win[0]", WIN_WINDOW[0], 0.5, 1e-6);
report("win[N-1]", WIN_WINDOW[N - 1], 1.0, 1e-6);
double mono = 0.0;
for (size_t i = 0; i < N; i++) mono += WIN_WINDOW[i];
report("win sum", mono, 0.0, 1e9);
std::printf("weights: WA[%zu] WB[%zu] WC[%zu] WD[%zu]\n",
WTA_WEIGHT_COUNT, WTB_WEIGHT_COUNT, WTC_WEIGHT_COUNT, WTD_WEIGHT_COUNT);
// Active half is bins 0..2048 (4096-point FFT half); mirror half is zeroed.
const size_t half = 2049;
report("WA active tail[2048]", WTA_WEIGHT[2048], 0.125666, 1e-4);
report("WA mirror zero", WTA_WEIGHT[4096], 0.0, 1e-9);
report("WB mirror zero", WTB_WEIGHT[8192], 0.0, 1e-9);
double m = 0.0;
for (size_t i = 0; i < half; i++) {
double err = std::fabs(WTA_WEIGHT[i] + WTB_WEIGHT[i] - 1.0);
if (err > m) m = err;
}
report("max|WA+WB-1| (0..2048)", m, 0.0, 1e-5);
m = 0.0;
for (size_t i = 0; i < half; i++) {
double err = std::fabs(WTC_WEIGHT[i] + WTD_WEIGHT[i] - 1.0);
if (err > m) m = err;
}
report("max|WC+WD-1| (0..2048)", m, 0.0, 1e-5);
report("WA[0]", WTA_WEIGHT[0], 0.596076, 1e-4);
report("WA[1024]", WTA_WEIGHT[1024], 0.168067, 1e-5);
std::printf("freq axis FREQAXIS[%zu] @48000 internal (step=48000/4098)\n", WIN_FREQAXIS_COUNT);
report("fa[0]", WIN_FREQAXIS[0], 0.0, 1e-6);
report("fa[1]-fa[0]", WIN_FREQAXIS[1] - WIN_FREQAXIS[0], 11.713, 0.01);
report("fa[N-1]", WIN_FREQAXIS[WIN_FREQAXIS_COUNT - 1], 23976.574, 0.1);
report("freq_at(50)", dsp_ctx::freq_at(50.0f), 50.0f * 11.713f, 1.0);
std::printf("dsp_ctx::hz_of_bin(1000,4096) = %.2f\n",
static_cast<double>(dsp_ctx::hz_of_bin(1000, 4096)));
std::printf(ok ? "ALL OK\n" : "FAILURES\n");
return ok ? 0 : 1;
}
+5411
View File
File diff suppressed because it is too large Load Diff
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""Emit dsp/tables_data.hpp: embed captured live .npy tables as C arrays.
Sourced from handoff/rt*.npy (runtime capture of the DSP registry, NOTES_CAPTURE).
Output is a single header so the port zero-copies the real tables the plugin used.
"""
import numpy as np
import os
HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(HERE, "..", "dsp", "tables_data.hpp")
SPECS = [
("WIN_WINDOW", "rtwin_freq_44100.npy"), # 0x540658 FFT-conv window, 8193 f32
("WIN_FREQAXIS", "rtfreqaxis_48000_internal.npy"), # freq-axis 2048 f32 (internal SR 48000)
("WTA_WEIGHT", "rtwa_596.npy"), # [03] 0.596->0.126
("WTB_WEIGHT", "rtwb_404.npy"), # [04] 0.404->0.874
("WTC_WEIGHT", "rtwc_043.npy"), # [05]
("WTD_WEIGHT", "rtwd_956.npy"), # [06]
]
WINDOW_N = 8193
def fmt_floats(a):
s = []
for v in a:
r = ("%.9g" % float(v)).encode().decode("ascii")
s.append(r)
return s
def emit_header(out_path, blocks):
with open(out_path, "w") as f:
f.write("// AUTOGENERATED from runtime capture handoff/rt*.npy (NOTES_CAPTURE 2026-08-19).\n")
f.write("// Do not edit by hand; regenerate with handoff/emit_tables.py.\n")
f.write("#pragma once\n#include <cstddef>\n#include <cstdint>\n\n")
for name, npy_file, arr in blocks:
f.write(f"constexpr size_t {name}_COUNT = {arr.size};\n")
f.write(f"const float {name}[{arr.size}] = {{\n")
line = []
for s in fmt_floats(arr):
line.append(s)
if len(line) == 8:
f.write(" " + ", ".join(line) + ",\n")
line = []
if line:
f.write(" " + ", ".join(line) + ",\n")
f.write("};\n\n")
def main():
blocks = []
for name, filename in SPECS:
path = os.path.join(HERE, filename)
if not os.path.exists(path):
print(f"missing table {filename} — skipping")
continue
arr = np.load(path)
arr = arr.ravel().astype(np.float32)
if arr.shape[0] not in (WINDOW_N, 8193, 2048, 2049):
print(f"unexpected size for {filename}: {arr.shape}")
blocks.append((name, filename, arr))
print(f"loaded {filename}: {arr.shape} {arr.dtype}")
emit_header(OUT, blocks)
print(f"wrote {OUT} ({sum(b[2].size for b in blocks)} floats)")
if __name__ == "__main__":
main()