mirror of
https://codeberg.org/armin/tcd.git
synced 2026-09-01 05:50:46 +02:00
re-format usage information output, update README accordingly
This commit is contained in:
parent
6c9fb5b7e9
commit
545ada8bc3
2 changed files with 146 additions and 42 deletions
154
README.md
154
README.md
|
|
@ -21,13 +21,32 @@ please read at least the --help information and *understand* what -a does.
|
|||
|
||||
---
|
||||
|
||||
How it works (the short version)
|
||||
--------------------------------
|
||||
How it works (precise description)
|
||||
-----------------------------------
|
||||
|
||||
tcd decodes your audio file, converts it to the frequency domain (like a
|
||||
graph showing how much energy exists at each frequency), then measures several
|
||||
properties of that frequency graph. Each property is a clue about whether the
|
||||
audio was produced by a lossy encoder. Combined, these clues give a verdict.
|
||||
tcd decodes the audio file to PCM via FFmpeg, applies a Hann window, and
|
||||
computes the FFT (default 4096-point) with 50% overlap for each windowed
|
||||
frame. Magnitude spectra from all frames are accumulated and averaged into a
|
||||
single power spectrum. From this averaged spectrum, five metrics are computed:
|
||||
|
||||
- **Cutoff**: highest frequency bin whose magnitude ≥ peak × 10^(threshold_db/20),
|
||||
controlled by the sensitivity setting (see [Threshold sensitivity](#threshold-sensitivity)).
|
||||
- **Steepness**: transition bandwidth between the −20 dB and −60 dB points
|
||||
(relative to peak), measuring how sharply the spectrum rolls off.
|
||||
- **Noise floor**: average magnitude in the top quartile of the spectrum
|
||||
(≈16500–22050 Hz at 44.1 kHz sample rate), expressed in dB relative to peak.
|
||||
- **Roughness**: coefficient of variation (standard deviation / mean) of
|
||||
magnitude values in the 60%–95% band below cutoff. This quantifies spectral
|
||||
irregularity introduced by quantization noise.
|
||||
- **Band ratio**: ratio of mean magnitude in 16–20 kHz to mean magnitude in
|
||||
12–16 kHz. Lossy codecs under-allocate bits above 16 kHz, producing a
|
||||
characteristic dip in this region.
|
||||
|
||||
The verdict is determined by evaluating these metrics against empirically
|
||||
derived thresholds (which are scaled by the [sensitivity setting](#threshold-sensitivity)),
|
||||
first using cutoff + steepness, then secondarily using roughness + band ratio
|
||||
for high-cutoff cases. For lossy input formats, cutoff is compared against
|
||||
bitrate-specific expectations to detect upscaling.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -268,6 +287,77 @@ If neither layer triggers, the file is **GENUINE** (native lossless).
|
|||
|
||||
---
|
||||
|
||||
Threshold sensitivity (`-t`)
|
||||
-----------------------------
|
||||
|
||||
The `-t` parameter (1–99, default 50) controls how aggressively tcd detects
|
||||
transcodes and upscaled files. It works at two levels.
|
||||
|
||||
### 1. Cutoff detection threshold
|
||||
|
||||
The cutoff frequency is defined as the highest bin whose magnitude is at
|
||||
least `peak × 10^(threshold_db / 20)`. The percentage is mapped linearly
|
||||
to dB:
|
||||
|
||||
```
|
||||
threshold_db = −(40 + (pct − 1) × 40 / 98)
|
||||
```
|
||||
|
||||
| `-t` value | threshold_db | Magnitude threshold | Behaviour |
|
||||
|-----------|-------------|--------------------|-----------|
|
||||
| 1 | −40.0 dB | 1.0 % of peak | Least sensitive – only the strongest signal counts |
|
||||
| 50 | −60.0 dB | 0.1 % of peak | Default – balances sensitivity and specificity |
|
||||
| 99 | −80.0 dB | 0.01 % of peak | Most sensitive – detects cutoff deep in the noise floor |
|
||||
|
||||
A higher `-t` (more negative dB) detects the cutoff at a *higher* frequency
|
||||
because it can follow the spectrum deeper into the noise floor. This makes
|
||||
the tool stricter: small spectral remnants above a true lossy cutoff are
|
||||
still recognised as "signal."
|
||||
|
||||
### 2. Verdict-threshold scaling
|
||||
|
||||
The same percentage also produces a continuous `sensitivity` factor:
|
||||
|
||||
```
|
||||
sensitivity = (pct − 1) / 98.0
|
||||
```
|
||||
|
||||
| `-t` value | sensitivity | Effect on verdict thresholds |
|
||||
|-----------|-------------|------------------------------|
|
||||
| 1 | 0.00 | Doubles the allowed steepness – very permissive, few false positives |
|
||||
| 50 | 0.50 | Base thresholds used as published above |
|
||||
| 99 | 1.00 | Steepness limits multiplied by 0.25; roughness thresholds lowered by up to 4× – very strict, catches borderline cases |
|
||||
|
||||
The table of steepness thresholds used in the transcode verdict (see
|
||||
[How tcd combines these clues](#how-tcd-combines-these-clues-into-a-verdict))
|
||||
is scaled by:
|
||||
|
||||
```
|
||||
bw_factor = max(2.0 × (1.0 − sensitivity), 0.25)
|
||||
allowed_steepness = base_max_bw × bw_factor
|
||||
```
|
||||
|
||||
The roughness thresholds (r1 = 0.40, r2 = 0.30, r3 = 0.20) are likewise
|
||||
scaled:
|
||||
|
||||
```
|
||||
r1 = 0.40 × (1.0 + (0.5 − sensitivity) × 1.5)
|
||||
r2 = 0.30 × (1.0 + (0.5 − sensitivity) × 1.5)
|
||||
r3 = 0.20 × (1.0 + (0.5 − sensitivity) × 1.5)
|
||||
```
|
||||
|
||||
### 3. Practical guidance
|
||||
|
||||
- **Default (50):** well-tuned for most material. Use this unless you have a
|
||||
specific reason to change it.
|
||||
- **Lower values (1–40):** reduce false positives on already-filtered material
|
||||
(e.g. deliberate lowpass during mastering). Rarely needed.
|
||||
- **Higher values (60–99):** catch transcodes that leave very little spectral
|
||||
evidence. Useful for batch cleaning of a library, but may increase false
|
||||
positives on quiet or synthetic content.
|
||||
|
||||
---
|
||||
|
||||
Confidence score
|
||||
----------------
|
||||
|
||||
|
|
@ -341,30 +431,46 @@ positives on legitimate encodes that simply use a conservative lowpass.
|
|||
Usage
|
||||
-----
|
||||
|
||||
```
|
||||
tcd [options] <audio-file>
|
||||
tcd [options] <audio-file>
|
||||
|
||||
When run, tcd prints an analysis summary. Example:
|
||||
|
||||
-t, --threshold PCT Overall detection sensitivity (1-99). Controls all
|
||||
decision thresholds: transition bandwidth, roughness,
|
||||
and band ratio. Maps to -40 dB cutoff level (1, least
|
||||
sensitive) through -80 dB (99, most sensitive).
|
||||
[default: 50]. 50 is neutral; lower = fewer detections,
|
||||
higher = more detections. Adjust in small steps.
|
||||
-f, --fft-size N FFT size (power of 2) [default: 4096]
|
||||
-d, --duration SEC Max seconds to analyze [default: 60]
|
||||
-r, --recursive Recurse into subdirectories
|
||||
-F, --full Analyze entire file (overrides --duration)
|
||||
-v, --verbose Verbose output
|
||||
-s, --visual Graphical spectrum visualization (TUI)
|
||||
-V Alias for -s
|
||||
-a, --auto-remove Automatically remove non-native files
|
||||
-h, --help Show this help
|
||||
```
|
||||
File: ./08 You Got Me.mp3
|
||||
Format: mp3
|
||||
Bitrate: 320 kbps
|
||||
Sample rate: 44100 Hz
|
||||
Channels: 2
|
||||
Windows: 13550
|
||||
Peak: 47.5 dBFS
|
||||
Cutoff: 20790 / 22050 Hz = 94.3%
|
||||
Steepness: 20209 Hz
|
||||
Roughness: 0.323
|
||||
Band ratio: 0.555
|
||||
Noise floor: -56.4 dB
|
||||
Verdict: NATIVE
|
||||
Verdict Info: bandwidth used=94.3% (20790/22050 Hz), expected≥90% for 320 kbps
|
||||
```
|
||||
|
||||
Each field is explained in the [What each number means](#what-tcd-displays-and-what-each-number-means) section.
|
||||
|
||||
Options:
|
||||
|
||||
-t, --threshold PCT Detection sensitivity 1-99 [50]
|
||||
-f, --fft-size N FFT size, power of 2 [4096]
|
||||
-d, --duration SEC Seconds to analyze [120]
|
||||
-r, --recursive Recurse into subdirectories
|
||||
-F, --full Analyze entire file (no duration limit)
|
||||
-v, --verbose Verbose output with decision log
|
||||
-s, --visual Graphical spectrum TUI visualization
|
||||
-V Alias for -s
|
||||
-a, --auto-remove Automatically delete detected transcodes
|
||||
-h, --help Show this help screen
|
||||
|
||||
Exit codes:
|
||||
|
||||
| Code | Meaning |
|
||||
|------|-----------------------------------|
|
||||
|------|-------------------------------------|
|
||||
| 0 | NATIVE or GENUINE (file is clean) |
|
||||
| 1 | UPSCALED or TRANSCODE detected |
|
||||
| 2 | SILENT (no detectable content) |
|
||||
|
|
|
|||
16
tcd.c
16
tcd.c
|
|
@ -939,8 +939,7 @@ static void print_help(const char *prog)
|
|||
{
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), ANSI_BOLD ANSI_CYAN "tcd" ANSI_RESET
|
||||
" \xe2\x80\x94 Transcode Detector "
|
||||
"Psychoacoustic audio authenticity analysis");
|
||||
" (Transcode Detector / Psychoacoustic audio authenticity analysis tool)");
|
||||
int v = vis_len(buf);
|
||||
printf(" %s", buf);
|
||||
int pad = tw - 2 - v;
|
||||
|
|
@ -967,8 +966,13 @@ static void print_help(const char *prog)
|
|||
printf(" " ANSI_BOLD "Options:" ANSI_RESET);
|
||||
printf("\n");
|
||||
|
||||
#define OPT_COL 28
|
||||
|
||||
#define OPT(fmt, desc) do { \
|
||||
printf(" " ANSI_GREEN fmt ANSI_RESET " %s", desc); \
|
||||
int flen = vis_len(fmt); \
|
||||
int pad = OPT_COL - flen; \
|
||||
if (pad < 1) pad = 1; \
|
||||
printf(" " ANSI_GREEN "%s" ANSI_RESET "%*s%s", fmt, pad, "", desc); \
|
||||
printf("\n"); \
|
||||
} while (0)
|
||||
|
||||
|
|
@ -977,12 +981,6 @@ static void print_help(const char *prog)
|
|||
snprintf(buf, sizeof(buf), "Detection sensitivity 1-99 [" ANSI_CYAN "%d" ANSI_RESET "]", THRESHOLD_DEFAULT);
|
||||
OPT("-t, --threshold PCT", buf);
|
||||
}
|
||||
{
|
||||
char buf[80];
|
||||
snprintf(buf, sizeof(buf), ANSI_DIM " Lower = fewer, higher = more" ANSI_RESET);
|
||||
printf(" %s", buf);
|
||||
printf("\n");
|
||||
}
|
||||
{
|
||||
char buf[80];
|
||||
snprintf(buf, sizeof(buf), "FFT size, power of 2 [" ANSI_CYAN "%d" ANSI_RESET "]", FFT_SIZE_DEFAULT);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue