223 lines
9.4 KiB
C++
223 lines
9.4 KiB
C++
// levelpath.cpp — transcription of FUN_180563440 (LUT curve + band combine)
|
||
// and FUN_18056e3e0 (twin-mask factory)
|
||
//
|
||
// Extracted from: handoff/nls_dasm/f_563440.dis (222 lines)
|
||
// Constants from: soothe_mem.bin at ImageBase 0x180000000
|
||
//
|
||
// Key addresses:
|
||
// 0x540000+0x2198 = r13+0x2198 = output accumulator (1024 doubles, stride 0x2000)
|
||
// 0x540000+0x188 = band config struct (A, B, C, flag, callback)
|
||
// 0x5408b0 = LUT coefficient table (PRNG state)
|
||
// 0x540868 = band count (max 6)
|
||
// 0x540870 = level weight (float)
|
||
// 0x540874 = level-dependent weight (float)
|
||
// 0x54087c = band weight (float)
|
||
// 0x54088c = sharpness weight (float)
|
||
// 0x540658 = window table (2048 floats, live-captured)
|
||
// 0x540698 = freq-axis (2049 floats, live-captured)
|
||
// 0x5406a8 = warp table (2049 floats)
|
||
// 0x5406b8 = warp exponent (float, = A_FIT)
|
||
|
||
#include <cmath>
|
||
#include <algorithm>
|
||
#include <cstring>
|
||
#include <cstdint>
|
||
|
||
// Constants extracted from binary
|
||
static constexpr float SCALE = 0.0009775171056389809f; // 1/1023 (DAT_1824c3c54, verified 2026-08-19)
|
||
static constexpr float ONE = 1.0f; // DAT_1824c3ea4
|
||
static constexpr float TWO = 2.0f; // DAT_1824c41e0
|
||
static constexpr float NEG1 = -1.0f; // DAT_1824c4680
|
||
static constexpr float HALF = 0.5f; // DAT_1824c3d8c
|
||
static constexpr float ZERO = 0.0f; // DAT_1824c4140
|
||
static constexpr float DEPTH_SCALE = 4.0f; // DAT_1824c4334
|
||
static constexpr float DB_CONV = 8.68588924407959f; // 20/ln(10) (DAT_1824c43e0)
|
||
static constexpr float FLOOR_DB = -6.907755374908447f; // ln(0.001) (DAT_1824c4704)
|
||
static constexpr float FLOOR_LIN = 0.001f; // exp(FLOOR_DB)
|
||
static constexpr double TWO_PI = 6.283185307179586; // DAT_1824c4248 (2π, twin-mask factory)
|
||
static constexpr float SCALE_1024 = 0.0009765625f; // 1/1024 (DAT_1824c3c50, band LUT apply)
|
||
static constexpr float CONST_5 = 5.0f; // DAT_1824c4230 (AudioProcessingModule ctor)
|
||
|
||
// PRNG state offsets from param_1
|
||
static constexpr int PRNG_STATE = 0x2404e0;
|
||
static constexpr int PRNG_LUT = 0x5408b0;
|
||
|
||
// Band config struct layout (offsets from band_base = param_1 + 0x188)
|
||
struct BandConfig {
|
||
float A; // +0x00: start value
|
||
float B; // +0x04: end value
|
||
float _pad[2];
|
||
float threshold; // +0x0c: threshold (compared to 1.0)
|
||
uint8_t flag; // +0x10: 0=linear, 1=power-law
|
||
uint8_t _pad2[3];
|
||
float _pad3[15];
|
||
void* callback; // +0x50: vtable callback (if non-null, use callback)
|
||
};
|
||
|
||
// Structural LUT curve (f_563440.dis, exact transcription 2026-08-19)
|
||
// x in [0,1], gamma == band->threshold (offset +0x0c), A=+0x00, B=+0x04
|
||
static float eval_lut_bin(float x, const BandConfig* band) {
|
||
float gamma = band->threshold;
|
||
float result;
|
||
if (band->flag == 0) {
|
||
// Linear path (0x563595): t = x^(1/γ) if γ!=1 && x>0; val = A + (B-A)*t
|
||
float t = x;
|
||
if (gamma != ONE && x > ZERO) {
|
||
double d = static_cast<double>(fabsf(x));
|
||
d = std::log(d) / static_cast<double>(gamma);
|
||
t = static_cast<float>(std::exp(d));
|
||
}
|
||
result = band->A + (band->B - band->A) * t;
|
||
} else {
|
||
// Power-law path (0x5635cd): t = 2x-1; if γ!=1 && t!=0: t = sign(t)·|t|^(1/γ)
|
||
// val = A + (B-A)·0.5·(1+t)
|
||
float t = TWO * x - ONE;
|
||
if (gamma != ONE && t != ZERO) {
|
||
float sign = (t < ZERO) ? NEG1 : ONE;
|
||
double d = static_cast<double>(fabsf(t));
|
||
d = std::log(d) / static_cast<double>(gamma);
|
||
t = static_cast<float>(std::exp(d)) * sign;
|
||
}
|
||
result = band->A + (band->B - band->A) * HALF * (ONE + t);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// FUN_180563440: LUT curve evaluation for 0x400 bins
|
||
// r13 = context pointer (param_1). Loop counter edi, x = i*SCALE clamp[0,1],
|
||
// band config read from r13+0x188 each iteration (rbx), output double at r13+0x198[i*8].
|
||
void lut_curve_eval(void* ctx) {
|
||
auto* base = static_cast<uint8_t*>(ctx);
|
||
double* output = reinterpret_cast<double*>(base + 0x198);
|
||
BandConfig* band = reinterpret_cast<BandConfig*>(base + 0x188);
|
||
for (int bin = 0; bin < 0x400; bin++) { // cmp $0x400 jl
|
||
float x = static_cast<float>(bin) * SCALE;
|
||
x = fminf(x, ONE);
|
||
if (x < ZERO) x = ZERO;
|
||
output[bin] = static_cast<double>(eval_lut_bin(x, band));
|
||
}
|
||
}
|
||
|
||
// FUN_18056e3e0: twin-mask factory
|
||
// DECODED (decomp_funs2.txt:7988 + f_56e3e0.dis): fills the 0x400-bin mask
|
||
// with a SINGLE scalar s = 2π / (count·SR), where
|
||
// count = [ctx+0x240080] (int), SR = [ctx+0x24] (float, internal SR).
|
||
// NOT a per-bin twin resonance — a constant fill (the "twin" shape enters
|
||
// elsewhere via the LUT curve FUN_180563440). Output mask stride 0x2000/band.
|
||
void twin_mask_factory(void* ctx, int band_idx, int n_bins) {
|
||
auto* base = static_cast<uint8_t*>(ctx);
|
||
int count = *reinterpret_cast<int*>(base + 0x240080);
|
||
float sr = *reinterpret_cast<float*>(base + 0x24);
|
||
double s = TWO_PI / (static_cast<double>(count) * static_cast<double>(sr));
|
||
float* mask = reinterpret_cast<float*>(base + 0x4198 + band_idx * 0x2000);
|
||
for (int i = 0; i < 0x400; i++) {
|
||
mask[i] = static_cast<float>(s);
|
||
}
|
||
}
|
||
|
||
// FUN_180563a60: band LUT apply (level -> gain). DECODED (decomp_funs2.txt:8975 + f_563a60.dis).
|
||
// For each of 6 bands and 0x400 bins:
|
||
// level_dB = 20·log10(mask[band][bin]) (logf · 8.6859)
|
||
// level_axis[bin] = bin·(1/1024) (SCALE_1024)
|
||
// t = clamp((dB − A)/(B − A), 0, 1) (BandConfig ctx+0x180: A,B,gamma,flag)
|
||
// if gamma == 1.0: val = t
|
||
// elif flag == 0 (linear): val = t^gamma (powf, NOT 1/gamma)
|
||
// else (power-law): val = 0.5·(1 + sign(2t−1)·|2t−1|^gamma)
|
||
// level_axis[bin+1] = val (pairs level, gain)
|
||
// NOTE: this is the INVERSE curve of FUN_180563440 (which uses x^(1/γ)).
|
||
void band_lut_apply(void* ctx) {
|
||
auto* base = static_cast<uint8_t*>(ctx);
|
||
float* bandcfg = *reinterpret_cast<float**>(base + 0x180);
|
||
float A = bandcfg[0];
|
||
float B = bandcfg[1];
|
||
float gamma = bandcfg[3];
|
||
float flag = bandcfg[4];
|
||
double* mask = reinterpret_cast<double*>(base + 0x4198);
|
||
for (int band = 0; band < 6; band++) {
|
||
double* m = mask + band * 0x400;
|
||
float* level_gain = *reinterpret_cast<float**>(base + 0xe0 + band * 0x18);
|
||
for (int bin = 0; bin < 0x400; bin++) {
|
||
float db = logf(static_cast<float>(m[bin])) * DB_CONV;
|
||
level_gain[bin * 2] = static_cast<float>(bin) * SCALE_1024;
|
||
float t = (db - A) / (B - A);
|
||
t = std::max(ZERO, std::min(ONE, t));
|
||
float val = t;
|
||
if (gamma != ONE) {
|
||
if (flag == ZERO) {
|
||
val = powf(t, gamma);
|
||
} else {
|
||
float u = TWO * t - ONE;
|
||
float sgn = (u < ZERO) ? NEG1 : ONE;
|
||
val = HALF * (ONE + sgn * powf(fabsf(u), gamma));
|
||
}
|
||
}
|
||
level_gain[bin * 2 + 1] = val;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---- mask-accumulator combine kernels (FUN_180529fe0, CRT thunks) ----
|
||
// Signatures recovered from raw bytes in the rt snap (objdump of 0x180008d60/5a20/3c40).
|
||
//
|
||
// 0x8d60 combine3: out[i] = a[i] - b[i] (vsubpd, 3 pointers; dst is the 3rd arg)
|
||
// In the per-band loop: 0x5406f8[i] = 0x540678[i] - 0x5407c8[i]
|
||
void combine_sub(double* out, const double* a, const double* b, int n) {
|
||
for (int i = 0; i < n; i++) out[i] = a[i] - b[i];
|
||
}
|
||
|
||
// 0x5a20: dst[i] += src[i] (double; kernel 0x18001a5a0)
|
||
void acc_add(double* dst, const double* src, int n) {
|
||
for (int i = 0; i < n; i++) dst[i] += src[i];
|
||
}
|
||
|
||
// 0x3c40: dst[i] += a[i] * b[i] (double; vfmadd213pd)
|
||
void acc_fma(double* dst, const double* a, const double* b, int n) {
|
||
for (int i = 0; i < n; i++) dst[i] += a[i] * b[i];
|
||
}
|
||
|
||
// FUN_180529fe0: coefficient setup (from decomp_funs.txt)
|
||
void coefficient_setup(void* ctx, int band_idx, int param3, int param4) {
|
||
auto* base = static_cast<uint8_t*>(ctx);
|
||
|
||
// Lock (atomic flag at 0x2404dc)
|
||
uint32_t* lock = reinterpret_cast<uint32_t*>(base + 0x2404dc);
|
||
// LOCK(); *lock |= 1; UNLOCK(); // simplified
|
||
|
||
// PRNG state update (LCG)
|
||
int32_t state = *reinterpret_cast<int32_t*>(base + PRNG_STATE);
|
||
state = (state + 0x3cdca) & 0x7fffffff;
|
||
*reinterpret_cast<int32_t*>(base + PRNG_STATE) = state;
|
||
|
||
// Load LUT coefficients
|
||
float* lut_table = reinterpret_cast<float*>(base + PRNG_LUT);
|
||
float coeff0 = lut_table[state];
|
||
float coeff1 = lut_table[state + 1];
|
||
|
||
// Generate 6 coefficient pairs
|
||
// Each pair: (coeff_i * scale + offset) * global_scale
|
||
float acc = ZERO;
|
||
for (int i = 0; i < 3; i++) {
|
||
state = (state + 0x140236 + i * 0x10d56) & 0x7fffffff;
|
||
float a = lut_table[state];
|
||
float b = lut_table[state + 1];
|
||
acc += a * b;
|
||
}
|
||
|
||
// Normalize
|
||
float normalized = acc / static_cast<float>(param3);
|
||
|
||
// Apply depth scaling: powf(normalized, depth)
|
||
float depth = *reinterpret_cast<float*>(base + 0x2c);
|
||
float depthScaled = powf(normalized, depth);
|
||
|
||
// Store result
|
||
*reinterpret_cast<float*>(base + 0x54088c) = depthScaled;
|
||
|
||
// Apply sharpness weight
|
||
float sharpness = *reinterpret_cast<float*>(base + 0x540870);
|
||
depthScaled *= sharpness;
|
||
|
||
// Invert: gain = 1 - mask
|
||
*reinterpret_cast<float*>(base + 0x54088c) = ONE - depthScaled;
|
||
}
|