diff --git a/dsp/framed_model.cpp b/dsp/framed_model.cpp index 77daa93..428df20 100644 --- a/dsp/framed_model.cpp +++ b/dsp/framed_model.cpp @@ -57,6 +57,32 @@ static bool is_internal_grid(size_t nfft, float sample_rate) { return nfft == 4096 && std::abs(sample_rate - 48000.0f) < 1.0f; } +static double k_mapping_factor(float fc, float q, float sens) { + // k(sens,q,fc) = k_sens * k_q * k_fc (NOTES 24x/24dd/24ee) + // Fitted: k_sens 6→0.44 12→1.0 18→5.37 24→22.0 (exp after 12, linear before) + // k_q 0.5→1.0 2.0→0.403 (log interp, q no effect above 2 per 24kk) + // k_fc 1.0 for now (fc via W_eq weak, keep 1.0 gated RT_KMAP_FC) + double k_sens; + if (sens < 12) k_sens = 0.44 + (sens - 6.0) * (0.56 / 6.0); + else if (sens == 12) k_sens = 1.0; + else if (sens < 24) k_sens = std::exp((sens - 12.0) * std::log(22.0) / 12.0); + else k_sens = 22.0; + double k_q; + if (q >= 2.0) k_q = 0.403; + else if (q <= 0.5) k_q = 1.0; + else { + double t = (std::log(q) - std::log(0.5)) / (std::log(2.0) - std::log(0.5)); + k_q = 1.0 + t * (0.403 - 1.0); + } + double k_fc = 1.0; + if (getenv("RT_KMAP_FC")) { + // opt-in fc factor via W_eq magnitude at fc (simple H=1 at fc) + double w_fc = std::pow(10.0, (sens * 0.3 / 12.0) / 20.0); + k_fc = 1.0 / w_fc; // naive, gated + } + return k_sens * k_q * k_fc; +} + static void process_band_structural( const float* am, const float* res, @@ -117,34 +143,10 @@ static void process_band_structural( lvl_in[k] = static_cast(static_cast(am[k]) / res_k * scale_factor_x); } // k-mapping per NOTES 24x/24dd/24ee: lvl_impl = lvl_ours / k(sens,q,fc) - // k = k_sens(sens) * k_q(q) * k_fc(fc) ; default OFF (canon), opt-in RT_KMAP=1 - // Fitted from table 24x: k_sens 6→0.44, 12→1.0, 18→5.37, 24→22.0 ; k_q 0.5→1.0, 2.0→0.403 - static const int kmap_on = []{ const char* e=getenv("RT_KMAP"); return e ? atoi(e) : 0; }(); + // default OFF (canon), opt-in RT_KMAP=1 (helper k_mapping_factor) + static const int kmap_on = getenv("RT_KMAP") ? atoi(getenv("RT_KMAP")) : 0; if (kmap_on) { - double k_sens; - if (band.sens < 12) { - // 6→0.44, 12→1.0 linear - k_sens = 0.44 + (band.sens - 6.0) * (0.56 / 6.0); - } else if (band.sens == 12) { - k_sens = 1.0; - } else if (band.sens < 24) { - // 12→1.0, 24→22.0 exponential - k_sens = std::exp((band.sens - 12.0) * std::log(22.0) / 12.0); - } else { - k_sens = 22.0; - } - double k_q; - if (band.q >= 2.0) k_q = 0.403; - else if (band.q <= 0.5) k_q = 1.0; - else { - // interpolate log q 0.5→2.0 : 1.0→0.403 - double t = (std::log(band.q) - std::log(0.5)) / (std::log(2.0) - std::log(0.5)); - k_q = 1.0 + t * (0.403 - 1.0); - } - double k_fc = 1.0; - // fc 500→1.0, 1000→~1.4 per 24w-2 (1.15@500 vs 1.62@1000) -> k_fc 1.0→0.85? - // Keep 1.0 for now; fc effect is weak vs sens/q. - double k_tot = k_sens * k_q * k_fc; + double k_tot = k_mapping_factor(band.fc, band.q, band.sens); if (k_tot > 1e-9) { for (size_t k = 0; k < nbin; k++) lvl_in[k] = static_cast(lvl_in[k] / k_tot); } @@ -202,19 +204,7 @@ static void process_band_structural( raw_level[k] = static_cast(static_cast(am[k]) / res_k * scale_factor_x); } if (kmap_on) { - double k_sens; - if (band.sens < 12) k_sens = 0.44 + (band.sens - 6.0) * (0.56 / 6.0); - else if (band.sens == 12) k_sens = 1.0; - else if (band.sens < 24) k_sens = std::exp((band.sens - 12.0) * std::log(22.0) / 12.0); - else k_sens = 22.0; - double k_q; - if (band.q >= 2.0) k_q = 0.403; - else if (band.q <= 0.5) k_q = 1.0; - else { - double t = (std::log(band.q) - std::log(0.5)) / (std::log(2.0) - std::log(0.5)); - k_q = 1.0 + t * (0.403 - 1.0); - } - double k_tot2 = k_sens * k_q; + double k_tot2 = k_mapping_factor(band.fc, band.q, band.sens); if (k_tot2 > 1e-9) for (size_t k = 0; k < nbin; k++) raw_level[k] = static_cast(raw_level[k] / k_tot2); } static const int eq_on2 = getenv("RT_EQ") ? atoi(getenv("RT_EQ")) : 1;