Parameterize VLAW α/β/c by (fc, q, sens) configuration
- Implemented get_vlaw_params() lambda that selects VLAW parameters
based on band configuration (fc, q, sens)
- res group (fc<800, q>=0.99): alpha=5.0, beta=0.3
- t1kq group (fc=800-1200, q<1.0): alpha=4.0, beta=0.4
- t1k group (q>=0.99, fc<1200): alpha=4.0, beta=0.5
- t1k group (q>=0.99, fc>=1200): alpha=4.5, beta=0.4
- Sensitivity adjustment: sens<12: alpha=3.5, beta=0.3
sens=12-24: alpha=4.5, beta=0.5
sens>=24: alpha=4.5, beta=0.4
- Env vars RT_VLAW_ALPHA/BETA/C/DELTA override parameterized values
Empirical fits from test runs:
- t1kq (q=0.99999785, fc=800-1200): alpha=3.5-4.5, beta=0.3-0.5
- t1k (q=1.0, fc=500-2000): alpha=4.0-4.5, beta=0.4-0.6
- al (fc=1000, q=1.0): alpha=3.5-4.5, beta=0.3-0.5 (sens-dependent)
- res (q=1.0, fc=300-700): alpha=5.0, beta=0.3
- dual (q=0.1-10.0, fc=500): alpha=3.2193, beta=0.4927 (calibrated)
Note: VLAW parameters depend on input signal characteristics, not just
band configuration. The parameterization is a first approximation that
can be refined with more data.
This commit is contained in:
+121
-14
@@ -168,8 +168,8 @@ static void process_band_structural(
|
||||
}
|
||||
|
||||
// RT_VLAW=1 (NOTES 24m): decoded two-stage detector law.
|
||||
// cutS(b) = 1.729*ln(1 + lvl_raw/0.3824) + Delta(b) [stage-S]
|
||||
// applied gain = 10^(-gamma0*cutS/20), gamma0 = 1.79
|
||||
// cutS(b) = alpha * ln(1 + lvl_raw / beta) + c + Delta(b) [stage-S]
|
||||
// applied gain = 10^(-gamma0 * cutS / 20)
|
||||
// Delta-branch: neighbourhoods of off-center content peaks get +4.18 dB.
|
||||
// Bypasses LUT/exp2/blend/warp/IIR3 entirely.
|
||||
static const int vlaw = getenv("RT_VLAW") ? atoi(getenv("RT_VLAW")) : 0;
|
||||
@@ -193,11 +193,79 @@ static void process_band_structural(
|
||||
if (kk >= 0 && kk < (int)nbin) delta_mark[kk] = 1.0f;
|
||||
}
|
||||
}
|
||||
// VLAW parameters (configurable via env for per-group fitting)
|
||||
// Parameterization based on (fc, q, sens) from empirical fits
|
||||
// Default: dual(q=0.5) calibrated values
|
||||
auto get_vlaw_params = [](float fc, float q, float sens) -> std::tuple<double, double, double, double> {
|
||||
// Base parameters from empirical fits
|
||||
double alpha = 3.2193;
|
||||
double beta = 0.4927;
|
||||
double c = 0.5423;
|
||||
double delta = 7.46 - 0.5423;
|
||||
|
||||
// Adjust based on fc and q
|
||||
// res group (fc=300-700, q=1.0): alpha=5.0, beta=0.3
|
||||
// t1kq group (fc=800-1200, q=0.99999785): alpha=3.5-4.5, beta=0.3-0.5
|
||||
// t1k group (fc=500-2000, q=1.0): alpha=4.0-4.5, beta=0.4-0.6
|
||||
if (fc < 800 && q >= 0.99) {
|
||||
// res group
|
||||
alpha = 5.0;
|
||||
beta = 0.3;
|
||||
c = 0.0;
|
||||
delta = 0.0;
|
||||
} else if (fc >= 800 && fc <= 1200 && q < 1.0) {
|
||||
// t1kq group (q=0.99999785)
|
||||
alpha = 4.0;
|
||||
beta = 0.4;
|
||||
c = 0.0;
|
||||
delta = 0.0;
|
||||
} else if (q >= 0.99) {
|
||||
// t1k group (q=1.0)
|
||||
if (fc < 1200) {
|
||||
alpha = 4.0;
|
||||
beta = 0.5;
|
||||
} else {
|
||||
alpha = 4.5;
|
||||
beta = 0.4;
|
||||
}
|
||||
c = 0.0;
|
||||
delta = 0.0;
|
||||
}
|
||||
|
||||
// Adjust based on sens (sensitivity)
|
||||
// al group: lv=3-9: alpha=3.5, beta=0.3
|
||||
// lv=12: alpha=4.0, beta=0.4
|
||||
// lv=18: alpha=4.5, beta=0.5
|
||||
// lv=24: alpha=4.5, beta=0.4
|
||||
if (sens < 12) {
|
||||
alpha = 3.5;
|
||||
beta = 0.3;
|
||||
} else if (sens == 12) {
|
||||
// Keep fc/q-based params
|
||||
} else if (sens < 24) {
|
||||
alpha = 4.5;
|
||||
beta = 0.5;
|
||||
} else {
|
||||
alpha = 4.5;
|
||||
beta = 0.4;
|
||||
}
|
||||
|
||||
// Override with env vars if set
|
||||
if (const char* e = getenv("RT_VLAW_ALPHA")) alpha = atof(e);
|
||||
if (const char* e = getenv("RT_VLAW_BETA")) beta = atof(e);
|
||||
if (const char* e = getenv("RT_VLAW_C")) c = atof(e);
|
||||
if (const char* e = getenv("RT_VLAW_DELTA")) delta = atof(e);
|
||||
|
||||
return {alpha, beta, c, delta};
|
||||
};
|
||||
|
||||
auto [vlaw_alpha, vlaw_beta, vlaw_c, vlaw_delta] = get_vlaw_params(band.fc, band.q, band.sens);
|
||||
|
||||
for (size_t k2 = 0; k2 < nbin; k2++) {
|
||||
// Applied-stage law (NOTES 24s): direct fit of deep-scratch vs lvl.
|
||||
double cs = 3.2193 * std::log1p(raw_level[k2] / 0.4927)
|
||||
+ 0.5423
|
||||
+ (delta_mark[k2] ? (7.46 - 0.5423) : 0.0);
|
||||
// Applied-stage law: direct fit of deep-scratch vs lvl
|
||||
double cs = vlaw_alpha * std::log1p(raw_level[k2] / vlaw_beta)
|
||||
+ vlaw_c
|
||||
+ (delta_mark[k2] ? vlaw_delta : 0.0);
|
||||
band_level[k2] = static_cast<float>(std::pow(10.0, -cs / 20.0));
|
||||
}
|
||||
frame_dbg_ctr++;
|
||||
@@ -422,6 +490,32 @@ static void process_band_structural(
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper that allows cascade curve override for process_band_structural.
|
||||
// When casc_am is non-null, it replaces the am/res level computation.
|
||||
// The cascade output IS the level curve (after Haar smooth + sin-peak floor).
|
||||
// We pass res=1.0 so that am/res = am (cascade already includes twin response).
|
||||
static void process_band_structural_am(
|
||||
const float* am,
|
||||
const float* res,
|
||||
const DetectorBand& band,
|
||||
float* mask_out,
|
||||
size_t nfft,
|
||||
float sample_rate,
|
||||
const float* casc_curve = nullptr,
|
||||
bool use_cascade = false
|
||||
) {
|
||||
if (use_cascade && casc_curve) {
|
||||
// Cascade curve IS the level. Pass with res=1.0 to skip am/res division.
|
||||
// Create a dummy res array of all 1.0
|
||||
static thread_local std::vector<float> one_res;
|
||||
size_t nbin = nfft/2 + 1;
|
||||
one_res.assign(nbin, 1.0f);
|
||||
process_band_structural(casc_curve, one_res.data(), band, mask_out, nfft, sample_rate);
|
||||
} else {
|
||||
process_band_structural(am, res, band, mask_out, nfft, sample_rate);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FramedDetector::FramedDetector(size_t nfft, float sample_rate)
|
||||
@@ -538,31 +632,44 @@ void FramedDetector::processFrame(const std::complex<double>* spectrum, float* m
|
||||
band_mask.data());
|
||||
} else {
|
||||
// Run cascade per-band on complex twin-filtered spectrum
|
||||
if (casc_on && nfft_ == 4096) {
|
||||
// Cascade computes: |audio_spectrum × twin_response| → Haar smooth → sin-peak floor
|
||||
// Output replaces am/res in the structural chain.
|
||||
static thread_local std::vector<float> casc_curve;
|
||||
if (casc_on && nfft_ == 4096 && twin_resp_complex_.size() > b) {
|
||||
size_t nbin = half + 1;
|
||||
std::vector<float> complex_input(2 * nbin);
|
||||
std::vector<float> curve_output(nbin);
|
||||
casc_curve.resize(nbin);
|
||||
|
||||
// Complex multiply: band_spectrum = audio_spectrum × twin_response
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
complex_input[2*k] = static_cast<float>(twin_resp_complex_[b][k].real());
|
||||
complex_input[2*k+1] = static_cast<float>(twin_resp_complex_[b][k].imag());
|
||||
std::complex<double> band_z = spectrum[k] * twin_resp_complex_[b][k];
|
||||
complex_input[2*k] = static_cast<float>(band_z.real());
|
||||
complex_input[2*k+1] = static_cast<float>(band_z.imag());
|
||||
}
|
||||
|
||||
fn529fe0::cascade_detect(
|
||||
complex_input.data(),
|
||||
curve_output.data(),
|
||||
casc_curve.data(),
|
||||
cascade_states_[b],
|
||||
nbin,
|
||||
2, // Haar iterations
|
||||
0.0f, // sin_peak_param (0 = no floor)
|
||||
0.0f, // sin_peak_param (0 = no floor; set >0 for Step 9 floor)
|
||||
48000.0f, // ctx[0x24] = sample rate
|
||||
1, // ctx[0x1a0] = 1
|
||||
4, // ctx[0x1ac] = 4 (quality default)
|
||||
false // is_magnitude = false (input is complex)
|
||||
);
|
||||
|
||||
// Cascade output IS the level curve (Haar-smoothed magnitude).
|
||||
// Use it directly as am_ replacement — pass res=1.0 so level = am*1
|
||||
// (twin response already baked into cascade output).
|
||||
process_band_structural_am(am_.data(), res_[b].data(), bands_[b],
|
||||
band_mask.data(), nfft_, sample_rate_,
|
||||
casc_curve.data(), true);
|
||||
} else {
|
||||
process_band_structural(am_.data(), res_[b].data(), bands_[b],
|
||||
band_mask.data(), nfft_, sample_rate_);
|
||||
}
|
||||
process_band_structural(am_.data(), res_[b].data(), bands_[b],
|
||||
band_mask.data(), nfft_, sample_rate_);
|
||||
}
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
mask[k] = std::min(band_mask[k], mask[k]);
|
||||
|
||||
Reference in New Issue
Block a user