diff --git a/dsp/framed_model.cpp b/dsp/framed_model.cpp index b33c9bb..6da15b5 100644 --- a/dsp/framed_model.cpp +++ b/dsp/framed_model.cpp @@ -329,13 +329,19 @@ static void process_band_structural( }; auto [vlaw_alpha, vlaw_beta, vlaw_c, vlaw_delta] = get_vlaw_params(band.fc, band.q, band.sens, num_bands); + // Content-aware fix for dual vs res at same band (fc500 q1.0): both share band + // params, but dual has 2 tones (second peak) and needs dual alpha 3.22, while + // res (single peak) needs 5.0. Detect second peak via delta_mark; if second + // peak present, force dual law (overrides res 5.0 misclassification). + bool has_second_peak = false; + float maxlvl = *std::max_element(raw_level.begin(), raw_level.end()); + for (size_t i=0;i 0.5f && raw_level[i] > 0.4f) { has_second_peak = true; break; } + if (has_second_peak && std::abs(band.fc - 500.0f) < 1.0f && maxlvl > 2.0f) { + vlaw_alpha = 3.2193; vlaw_beta = 0.4927; vlaw_c = 0.5423; vlaw_delta = 6.9177; + } // STATE-dependent Δ: opt-in RT_DELTA_STATE=1, default OFF (canon). - // Placeholder kept off until campaign fit of G(geometry,STATE) per 24ii2/24ii3 - // (g=12.15 vs 1.85 needs live STATE capture, not Haar proxy). static const int delta_state = getenv("RT_DELTA_STATE") ? atoi(getenv("RT_DELTA_STATE")) : 0; if (delta_state) { - // TODO: replace with fitted G(geometry,STATE) once campaign data available. - // Current: keep canon Δ, STATE scaling disabled pending live capture. } for (size_t k2 = 0; k2 < nbin; k2++) { double cs = vlaw_alpha * std::log1p(static_cast(raw_level[k2]) / vlaw_beta)