fix noise floor detection

This commit is contained in:
Armin 2026-07-03 23:00:42 +02:00
commit 1072dfba65

51
tcd.c
View file

@ -170,7 +170,8 @@ static int detect_cutoff(const Analyzer *a, double threshold_db,
double sensitivity,
double *out_cutoff, double *out_steepness,
double *out_noise_db, double *out_roughness,
double *out_band_ratio)
double *out_band_ratio,
double *out_extended_cutoff)
{
int n = a->fft_size / 2;
int sr = a->sample_rate;
@ -221,6 +222,21 @@ static int detect_cutoff(const Analyzer *a, double threshold_db,
double noise_floor = (noise_count > 0) ? (noise_sum / noise_count) : 1e-12;
*out_noise_db = 20.0 * log10(noise_floor / peak);
/* Compute extended cutoff at noise floor + 6 dB margin.
Catches high-frequency content below the user's threshold but
above the noise floor, proving the file isn't a low-bitrate
re-encode even when high frequencies are very quiet.
Limit to at most 30 dB more sensitive than user threshold. */
double ext_db = *out_noise_db + 6.0;
double min_ext_db = threshold_db - 30.0;
if (ext_db < min_ext_db) ext_db = min_ext_db;
double ext_peak = peak * pow(10.0, ext_db / 20.0);
double ext_cutoff = 0;
for (int i = n - 1; i >= 0; i--) {
if (mag[i] >= ext_peak) { ext_cutoff = (double)i * sr / a->fft_size; break; }
}
*out_extended_cutoff = ext_cutoff;
double roughness = 0.0;
double cutoff_idx = cutoff_hz * a->fft_size / sr;
int lo = (int)(cutoff_idx * 0.60);
@ -494,7 +510,8 @@ static void freq_label_row(int chart_w, double nyquist)
static void render_visual_spectrum(
const Analyzer *a,
double cutoff_hz, double steepness, double noise_db,
double cutoff_hz, double effective_cutoff_hz,
double steepness, double noise_db,
double roughness, double band_ratio,
int is_transcode, int is_native_lossy, int upscaled,
double peak_db, double threshold_db,
@ -671,8 +688,10 @@ static void render_visual_spectrum(
draw_hline(chart_w + label_w);
double cutoff_ratio = cutoff_hz / nyquist;
double eff_cutoff_ratio = effective_cutoff_hz / nyquist;
double confidence = compute_confidence(
cutoff_ratio, steepness, roughness, band_ratio, is_native_lossy);
is_native_lossy ? eff_cutoff_ratio : cutoff_ratio,
steepness, roughness, band_ratio, is_native_lossy);
printf(" " ANSI_BOLD "Decision:" ANSI_RESET " ");
if (peak_db < -90.0) {
@ -723,18 +742,18 @@ static void render_visual_spectrum(
char line[128];
snprintf(line, sizeof(line),
" cutoff_ratio=%.3f < expected for bitrate (%lld kbps)",
cutoff_ratio, (long long)(bitrate / 1000));
eff_cutoff_ratio, (long long)(bitrate / 1000));
printf(" %s " ANSI_GREEN "" ANSI_RESET, line);
printf("\n");
const char *src_hint = "";
if (cutoff_ratio < 0.70) src_hint = "≤ 64 kbps source";
else if (cutoff_ratio < 0.80) src_hint = "96128 kbps source";
else if (cutoff_ratio < 0.88) src_hint = "128192 kbps source";
if (eff_cutoff_ratio < 0.70) src_hint = "≤ 64 kbps source";
else if (eff_cutoff_ratio < 0.80) src_hint = "96128 kbps source";
else if (eff_cutoff_ratio < 0.88) src_hint = "128192 kbps source";
printf("" ANSI_BOLD "Suggest %s" ANSI_RESET, src_hint);
printf("\n");
} else {
printf(" cutoff_ratio=%.3f within expected range for this bitrate " ANSI_GREEN "" ANSI_RESET, cutoff_ratio);
printf(" cutoff_ratio=%.3f within expected range for this bitrate " ANSI_GREEN "" ANSI_RESET, eff_cutoff_ratio);
printf("\n");
}
} else {
@ -1129,11 +1148,12 @@ static int process_file(const char *filename, const Options *opts)
}
double cutoff_hz = 0, steepness = 0, noise_db = 0;
double roughness = 0, band_ratio = 0;
double roughness = 0, band_ratio = 0, extended_cutoff = 0;
int is_transcode = detect_cutoff(&analyzer, opts->threshold_db,
opts->sensitivity,
&cutoff_hz, &steepness, &noise_db,
&roughness, &band_ratio);
&roughness, &band_ratio,
&extended_cutoff);
double nyquist = sample_rate / 2.0;
double peak_mag = 0;
@ -1155,7 +1175,9 @@ static int process_file(const char *filename, const Options *opts)
return 2;
}
double cutoff_ratio = cutoff_hz / nyquist;
double effective_cutoff = cutoff_hz;
if (extended_cutoff > effective_cutoff) effective_cutoff = extended_cutoff;
double cutoff_ratio = effective_cutoff / nyquist;
int upscaled = 0;
if (is_native_lossy) {
double expected_min = 0.90;
@ -1167,7 +1189,8 @@ static int process_file(const char *filename, const Options *opts)
render_visual_spectrum(
&analyzer,
cutoff_hz, steepness, noise_db, roughness, band_ratio,
cutoff_hz, effective_cutoff, steepness, noise_db,
roughness, band_ratio,
is_transcode, is_native_lossy, upscaled,
peak_db, opts->threshold_db,
filename, fmt_name,
@ -1211,7 +1234,9 @@ static int process_file(const char *filename, const Options *opts)
return 2;
}
double cutoff_ratio = cutoff_hz / nyquist;
double effective_cutoff = cutoff_hz;
if (extended_cutoff > effective_cutoff) effective_cutoff = extended_cutoff;
double cutoff_ratio = effective_cutoff / nyquist;
int upscaled = 0;
if (is_native_lossy) {