improve transcode detection at 128/160

This commit is contained in:
Armin 2026-07-04 09:44:24 +02:00
commit ddb31b429d

125
tcd.c
View file

@ -237,14 +237,43 @@ static int detect_cutoff(const Analyzer *a, double threshold_db,
} }
*out_extended_cutoff = ext_cutoff; *out_extended_cutoff = ext_cutoff;
/* When the noise floor is above the detection threshold, the cutoff is
determined by noise rather than signal. Compute a noise-aware cutoff
at (noise floor + 10 dB) for the roughness computation so it operates
on signal content rather than noise-dominated bins. */
double rough_cutoff = cutoff_hz;
if (*out_noise_db > threshold_db) {
double adj_db = *out_noise_db + 10.0;
double adj_thresh = peak * pow(10.0, adj_db / 20.0);
double adj_cutoff = 0;
for (int i = n - 1; i >= 0; i--) {
if (mag[i] >= adj_thresh) { adj_cutoff = (double)i * sr / a->fft_size; break; }
}
if (adj_cutoff > 0) rough_cutoff = adj_cutoff;
}
double roughness = 0.0; double roughness = 0.0;
double cutoff_idx = cutoff_hz * a->fft_size / sr; double cutoff_idx = rough_cutoff * a->fft_size / sr;
int lo = (int)(cutoff_idx * 0.60); int lo = (int)(cutoff_idx * 0.60);
int hi = (int)(cutoff_idx * 0.95); int hi = (int)(cutoff_idx * 0.95);
if (hi >= n) hi = n - 1; if (hi >= n) hi = n - 1;
if (lo < 1) lo = 1; if (lo < 1) lo = 1;
if (hi > lo) { /* Check whether the roughness region is noise-dominated. When the noise
floor is close to or above the detection threshold the cutoff is
determined by noise, and the roughness region may contain mostly noise
bins, producing spuriously high roughness values. If fewer than 30 %
of bins in the region are more than 6 dB above the noise floor, the
roughness is unreliable and we set it to a low value. */
double noise_mag = pow(10.0, *out_noise_db / 20.0) * peak;
int region_total = hi - lo + 1;
int above_noise = 0;
for (int i = lo; i <= hi; i++) {
if (mag[i] > noise_mag * 2.0) above_noise++;
}
if ((double)above_noise / region_total < 0.30) {
roughness = 0.01;
} else if (hi > lo) {
double sum = 0; double sum = 0;
for (int i = lo; i <= hi; i++) sum += mag[i]; for (int i = lo; i <= hi; i++) sum += mag[i];
double mean = sum / (hi - lo + 1); double mean = sum / (hi - lo + 1);
@ -273,10 +302,25 @@ static int detect_cutoff(const Analyzer *a, double threshold_db,
: 0.5; : 0.5;
*out_band_ratio = band_ratio; *out_band_ratio = band_ratio;
free(mag);
double nyquist = sr / 2.0; double nyquist = sr / 2.0;
double cutoff_ratio = cutoff_hz / nyquist;
/* When the noise floor is above the detection threshold, the cutoff
is determined by noise, not signal. Use the noise-aware cutoff
(at noise floor + 10 dB) for decision-making to avoid false positives
from an inflated cutoff ratio where noise dominates the spectrum. */
double decision_cutoff = cutoff_hz;
if (*out_noise_db > threshold_db) {
double dc_db = *out_noise_db + 10.0;
double dc_thresh = peak * pow(10.0, dc_db / 20.0);
for (int i = n - 1; i >= 0; i--) {
if (mag[i] >= dc_thresh) {
decision_cutoff = (double)i * sr / a->fft_size;
break;
}
}
if (decision_cutoff <= 0) decision_cutoff = cutoff_hz;
}
double cutoff_ratio = decision_cutoff / nyquist;
/* Scale detection thresholds by sensitivity (0.0 = least, 0.5 = default, 1.0 = most) */ /* Scale detection thresholds by sensitivity (0.0 = least, 0.5 = default, 1.0 = most) */
double bw_factor = 2.0 * (1.0 - sensitivity); double bw_factor = 2.0 * (1.0 - sensitivity);
@ -290,10 +334,11 @@ static int detect_cutoff(const Analyzer *a, double threshold_db,
int score = 0; int score = 0;
if (cutoff_hz <= 0 || cutoff_ratio >= bypass) { if (decision_cutoff <= 0 || cutoff_ratio >= bypass) {
if (roughness > r1) score = 1; if (roughness > r1) score = 1;
else if (roughness > r2 && band_ratio < b1) score = 1; else if (roughness > r2 && band_ratio < b1) score = 1;
else if (roughness > r3 && band_ratio < b2) score = 1; else if (roughness > r3 && band_ratio < b2) score = 1;
free(mag);
return score; return score;
} }
@ -318,6 +363,7 @@ static int detect_cutoff(const Analyzer *a, double threshold_db,
else if (roughness > r3 && band_ratio < b2) score = 1; else if (roughness > r3 && band_ratio < b2) score = 1;
} }
free(mag);
return score; return score;
} }
@ -693,6 +739,22 @@ static void render_visual_spectrum(
is_native_lossy ? eff_cutoff_ratio : cutoff_ratio, is_native_lossy ? eff_cutoff_ratio : cutoff_ratio,
steepness, roughness, band_ratio, is_native_lossy); steepness, roughness, band_ratio, is_native_lossy);
/* Compute noise-aware factor ratio for consistent display with detect_cutoff().
When the noise floor is above the user's threshold the cutoff is noise-
dominated; use (noise floor + 10 dB) for the factor display instead. */
double factor_ratio = eff_cutoff_ratio;
if (!is_native_lossy && noise_db > threshold_db) {
double fc_db = noise_db + 10.0;
double fc_thresh = peak * pow(10.0, fc_db / 20.0);
int n_bins = fft_size / 2;
double fc_cut = cutoff_hz;
for (int i = n_bins - 1; i >= 0; i--) {
double m = sqrt(a->power[i] / a->count);
if (m >= fc_thresh) { fc_cut = (double)i * sample_rate / fft_size; break; }
}
if (fc_cut > 0) factor_ratio = fc_cut / nyquist;
}
printf(" " ANSI_BOLD "Decision:" ANSI_RESET " "); printf(" " ANSI_BOLD "Decision:" ANSI_RESET " ");
if (peak_db < -90.0) { if (peak_db < -90.0) {
printf(ANSI_RED "SILENT" ANSI_RESET " (no detectable audio content)"); printf(ANSI_RED "SILENT" ANSI_RESET " (no detectable audio content)");
@ -757,24 +819,25 @@ static void render_visual_spectrum(
printf("\n"); printf("\n");
} }
} else { } else {
double fr = factor_ratio;
double max_bw_display; double max_bw_display;
if (cutoff_ratio < 0.50) { if (fr < 0.50) {
max_bw_display = 4000.0; max_bw_display = 4000.0;
} else if (cutoff_ratio < 0.70) { } else if (fr < 0.70) {
max_bw_display = 3000.0; max_bw_display = 3000.0;
} else if (cutoff_ratio < 0.80) { } else if (fr < 0.80) {
max_bw_display = 2000.0; max_bw_display = 2000.0;
} else if (cutoff_ratio < 0.90) { } else if (fr < 0.90) {
max_bw_display = 1200.0; max_bw_display = 1200.0;
} else { } else {
max_bw_display = 500.0; max_bw_display = 500.0;
} }
int primary_hit = (cutoff_ratio >= 0.99) ? 0 : (steepness < max_bw_display); int primary_hit = (fr >= 0.99) ? 0 : (steepness < max_bw_display);
int secondary_applies = (cutoff_ratio > 0.80); int secondary_applies = (fr > 0.80);
if (cutoff_ratio >= 0.99) { if (fr >= 0.99) {
printf(" ① cutoff_ratio=%.3f ≥ 0.99 " ANSI_RED "" ANSI_RESET " (full spectrum, no cutoff)", cutoff_ratio); printf(" ① cutoff_ratio=%.3f ≥ 0.99 " ANSI_RED "" ANSI_RESET " (full spectrum, no cutoff)", fr);
printf("\n"); printf("\n");
} else { } else {
char bw_label[64]; char bw_label[64];
@ -783,11 +846,11 @@ static void render_visual_spectrum(
if (primary_hit) { if (primary_hit) {
printf(" ① cutoff_ratio=%.3f, %s " ANSI_GREEN "" ANSI_RESET, printf(" ① cutoff_ratio=%.3f, %s " ANSI_GREEN "" ANSI_RESET,
cutoff_ratio, bw_label); fr, bw_label);
printf("\n"); printf("\n");
} else { } else {
printf(" ① cutoff_ratio=%.3f, transition_bw=%.0fHz ≥ %.0fHz threshold " ANSI_RED "" ANSI_RESET, printf(" ① cutoff_ratio=%.3f, transition_bw=%.0fHz ≥ %.0fHz threshold " ANSI_RED "" ANSI_RESET,
cutoff_ratio, steepness, max_bw_display); fr, steepness, max_bw_display);
printf("\n"); printf("\n");
} }
} }
@ -1182,13 +1245,15 @@ static int process_file(const char *filename, const Options *opts)
} }
double effective_cutoff = cutoff_hz; double effective_cutoff = cutoff_hz;
if (extended_cutoff > effective_cutoff) effective_cutoff = extended_cutoff; if (extended_cutoff > effective_cutoff && noise_db >= -100.0)
effective_cutoff = extended_cutoff;
double cutoff_ratio = effective_cutoff / nyquist; double cutoff_ratio = effective_cutoff / nyquist;
int upscaled = 0; int upscaled = 0;
if (is_native_lossy) { if (is_native_lossy) {
double expected_min = 0.90; double expected_min = 0.90;
if (bitrate > 0 && bitrate < 192000) expected_min = 0.75; if (bitrate > 0 && bitrate < 192000) expected_min = 0.75;
else if (bitrate > 0 && bitrate < 256000) expected_min = 0.85; else if (bitrate > 0 && bitrate < 256000) expected_min = 0.85;
if (noise_db < -80.0) expected_min -= 0.05;
if (cutoff_ratio > 0 && cutoff_ratio < expected_min - 0.08) { if (cutoff_ratio > 0 && cutoff_ratio < expected_min - 0.08) {
upscaled = 1; upscaled = 1;
} }
@ -1223,6 +1288,8 @@ static int process_file(const char *filename, const Options *opts)
printf(ANSI_RESET "\n"); printf(ANSI_RESET "\n");
} }
printf(ANSI_BOLD ANSI_CYAN "Format:" ANSI_RESET " %s\n", fmt_name); printf(ANSI_BOLD ANSI_CYAN "Format:" ANSI_RESET " %s\n", fmt_name);
if (bitrate > 0)
printf(ANSI_BOLD ANSI_CYAN "Bitrate:" ANSI_RESET " %lld kbps\n", (long long)(bitrate / 1000));
printf(ANSI_BOLD ANSI_CYAN "Sample rate:" ANSI_RESET " %d Hz\n", sample_rate); printf(ANSI_BOLD ANSI_CYAN "Sample rate:" ANSI_RESET " %d Hz\n", sample_rate);
printf(ANSI_BOLD ANSI_CYAN "Channels:" ANSI_RESET " %d\n", channels); printf(ANSI_BOLD ANSI_CYAN "Channels:" ANSI_RESET " %d\n", channels);
printf(ANSI_BOLD ANSI_CYAN "Windows:" ANSI_RESET " %d\n", analyzer.count); printf(ANSI_BOLD ANSI_CYAN "Windows:" ANSI_RESET " %d\n", analyzer.count);
@ -1242,7 +1309,8 @@ static int process_file(const char *filename, const Options *opts)
} }
double effective_cutoff = cutoff_hz; double effective_cutoff = cutoff_hz;
if (extended_cutoff > effective_cutoff) effective_cutoff = extended_cutoff; if (extended_cutoff > effective_cutoff && noise_db >= -100.0)
effective_cutoff = extended_cutoff;
double cutoff_ratio = effective_cutoff / nyquist; double cutoff_ratio = effective_cutoff / nyquist;
int upscaled = 0; int upscaled = 0;
@ -1250,6 +1318,7 @@ static int process_file(const char *filename, const Options *opts)
double expected_min = 0.90; double expected_min = 0.90;
if (bitrate > 0 && bitrate < 192000) expected_min = 0.75; if (bitrate > 0 && bitrate < 192000) expected_min = 0.75;
else if (bitrate > 0 && bitrate < 256000) expected_min = 0.85; else if (bitrate > 0 && bitrate < 256000) expected_min = 0.85;
if (noise_db < -80.0) expected_min -= 0.05;
if (cutoff_ratio > 0 && cutoff_ratio < expected_min - 0.08) { if (cutoff_ratio > 0 && cutoff_ratio < expected_min - 0.08) {
upscaled = 1; upscaled = 1;
@ -1257,16 +1326,22 @@ static int process_file(const char *filename, const Options *opts)
if (upscaled) { if (upscaled) {
printf(ANSI_YELLOW "UPSCALED" ANSI_RESET " (re-encoded from a lower-bitrate source)\n"); printf(ANSI_YELLOW "UPSCALED" ANSI_RESET " (re-encoded from a lower-bitrate source)\n");
printf(ANSI_BOLD ANSI_CYAN "Verdict Info:" ANSI_RESET " cut-off suggests ");
if (cutoff_ratio < 0.70)
printf(ANSI_BOLD "<= 64 kbps" ANSI_RESET " source\n");
else if (cutoff_ratio < 0.80)
printf(ANSI_BOLD "96-128 kbps" ANSI_RESET " source\n");
else if (cutoff_ratio < 0.88)
printf(ANSI_BOLD "128-192 kbps" ANSI_RESET " source\n");
} else { } else {
printf(ANSI_GREEN "NATIVE" ANSI_RESET " (single encode at this bitrate)\n"); printf(ANSI_GREEN "NATIVE" ANSI_RESET " (single encode at this bitrate)\n");
} }
printf(ANSI_BOLD ANSI_CYAN "Verdict Info:" ANSI_RESET " cutoff=%.1f%% Nyquist, expected≥%.0f%% for %lld kbps",
100.0 * cutoff_ratio, 100.0 * expected_min,
bitrate > 0 ? (long long)(bitrate / 1000) : 0);
if (upscaled) {
printf(" → suggests ");
if (cutoff_ratio < 0.70)
printf(ANSI_BOLD "≤ 64 kbps" ANSI_RESET " source");
else if (cutoff_ratio < 0.80)
printf(ANSI_BOLD "96128 kbps" ANSI_RESET " source");
else if (cutoff_ratio < 0.88)
printf(ANSI_BOLD "128192 kbps" ANSI_RESET " source");
}
printf("\n");
} else { } else {
if (is_transcode) if (is_transcode)
printf(ANSI_YELLOW "TRANSCODE" ANSI_RESET " (lossy -> lossless re-encode detected)\n"); printf(ANSI_YELLOW "TRANSCODE" ANSI_RESET " (lossy -> lossless re-encode detected)\n");