mirror of
https://codeberg.org/armin/tcdweb.git
synced 2026-09-01 05:30:45 +02:00
155 lines
12 KiB
HTML
155 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>tcd - Audio Transcode Detector</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
</head>
|
||
<body>
|
||
|
||
<div class="container">
|
||
|
||
<input type="file" id="fileInput" accept=".mp3,.flac,.wav,.aiff,.aif,.ogg,.opus,.m4a,.wma,.ac3,.eac3,.aac,.alac,.wv,.mp2,.mp1,.ape,.dsf,.dff,audio/*" hidden>
|
||
|
||
<div id="errorMsg"></div>
|
||
|
||
<div id="results">
|
||
|
||
<section id="playerSection">
|
||
<div class="section-header">
|
||
<h2>Player & Realtime Analysis</h2>
|
||
<div class="section-brand"><span class="logo">tcd</span> <span class="subtitle">Audio Authenticity Analyzer</span><br><span class="tagline">Psychoacoustic transcode detection via spectral analysis</span></div>
|
||
</div>
|
||
<div class="player-controls">
|
||
<button id="loadBtn" class="player-btn load-btn" title="Load Audio File">📂 Load File</button>
|
||
<button id="playBtn" class="player-btn" title="Play/Pause">▶</button>
|
||
<button id="stopBtn" class="player-btn" title="Stop">⏹</button>
|
||
<button id="holdBtn" class="player-btn" title="Hold Max Cutoff">⊡</button>
|
||
<button id="resetBtn" class="player-btn" title="Reset Peaks">↺</button>
|
||
<span id="playTime" class="player-time">0:00.0 / 0:00.0</span>
|
||
<select id="windowSelect" class="window-select" title="Spectrum window function"><option value="none">None</option><option value="hanning" selected>Hanning</option><option value="blackman">Blackman-Harris</option><option value="flatTop">Flat Top</option></select>
|
||
</div>
|
||
<div class="chart-container" style="position:relative">
|
||
<canvas id="waveformChart"></canvas>
|
||
<div id="loadingOverlay" class="loading-overlay">
|
||
<div class="spinner"></div>
|
||
<div id="loadingMsg">Processing...</div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-container" style="margin-top:10px;position:relative">
|
||
<canvas id="liveSpectrum"></canvas>
|
||
<canvas id="liveSpectrumOverlay" style="position:absolute;top:0;left:0;pointer-events:none"></canvas>
|
||
</div>
|
||
<div class="metrics-grid" style="margin-top:10px">
|
||
<div class="metric-card">
|
||
<div class="metric-label">Live Peak</div>
|
||
<div class="metric-value" id="livePeak">-</div>
|
||
</div>
|
||
<div class="metric-card">
|
||
<div class="metric-label">Live Cutoff</div>
|
||
<div class="metric-value" id="liveCutoff">-</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="fileInfo">
|
||
<h2>File Information</h2>
|
||
<div class="info-grid">
|
||
<div class="info-item fullwidth"><span class="info-label">Filename</span><span id="fileName" class="info-value">-</span></div>
|
||
<div class="info-item"><span class="info-label">Format</span><span id="fileFormat" class="info-value">-</span></div>
|
||
<div class="info-item"><span class="info-label">Sample Rate</span><span id="fileSampleRate" class="info-value">-</span></div>
|
||
<div class="info-item"><span class="info-label">Channels</span><span class="info-value"><span id="fileChannels">-</span> <span id="fileStereoMode" class="info-value-tag"></span></span></div>
|
||
<div class="info-item"><span class="info-label">Duration</span><span id="fileDuration" class="info-value">-</span></div>
|
||
<div class="info-item"><span class="info-label">Bitrate</span><span class="info-value"><span id="fileBitrate">-</span> <span id="fileBitrateMode" class="info-value-tag"></span></span></div>
|
||
<div class="info-item"><span class="info-label">File Size</span><span id="fileSize" class="info-value">-</span></div>
|
||
<div class="info-item"><span class="info-label">BPM</span><span class="info-value bpm-value"><span id="fileBPM">-</span><span class="bpm-buttons"><button class="bpm-btn" id="bpmHalf" title="Halve">÷2</button><button class="bpm-btn" id="bpmDouble" title="Double">×2</button></span></span></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="metadataSection">
|
||
<h2>Metadata Tags</h2>
|
||
<div id="metadataGrid" class="metadata-grid"></div>
|
||
<div id="metadataEmpty" class="metadata-empty">No metadata tags found</div>
|
||
</section>
|
||
|
||
<section id="spectrumSection">
|
||
<h2>Power Spectrum</h2>
|
||
<div class="chart-container">
|
||
<canvas id="spectrumChart"></canvas>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="metricsSection">
|
||
<h2>Key Metrics</h2>
|
||
<div class="metrics-grid">
|
||
<div class="metric-card" title="Peak Magnitude - highest spectral amplitude in dBFS (0 dBFS = full scale). How to read: • > -1 dBFS: Heavily limited/mastered, extremely loud • -6 to -1 dBFS: Typical modern mastering • -12 to -6 dBFS: Conservative, good headroom • < -12 dBFS: Quiet or attenuated Significance: Combined with noise floor, indicates the effective dynamic range of the audio.">
|
||
<div class="metric-label">Peak</div>
|
||
<div class="metric-value" id="mPeak">-</div>
|
||
</div>
|
||
<div class="metric-card" title="Spectral Cutoff - highest frequency bin with significant energy above floor, as Hz and % of Nyquist (sample rate / 2). How to read: • ≥ 20 kHz (≥ 90%): Full bandwidth - lossless or high-bitrate lossy (320k MP3, 256k AAC) • 16-19 kHz (70-85%): ~192k MP3 / ~128k AAC • 14-16 kHz (60-70%): ~128k MP3 • 10-14 kHz (45-60%): ~64-96k lossy • < 10 kHz (< 45%): Very low bitrate (< 64k) Significance: The single strongest indicator of lossy compression. Lower cutoff = more aggressive compression.">
|
||
<div class="metric-label">Cutoff</div>
|
||
<div class="metric-value" id="mCutoff">-</div>
|
||
</div>
|
||
<div class="metric-card" title="Transition Bandwidth (Steepness) - frequency gap between the -20 dBFS and -60 dBFS points. Measures how abruptly high-frequencies drop off. How to read: • < 500 Hz: Brick-wall cutoff - transcode or low-bitrate lossy • 500-1500 Hz: Moderately sharp - suspicious, may be higher-bitrate lossy • 1500-3000 Hz: Gradual rolloff - typical of genuine lossless • > 3000 Hz: Very gradual - natural rolloff from analog sources Significance: The strongest differentiator between genuine lossless and transcodes. Lossy encoders produce artificially steep lowpass filters.">
|
||
<div class="metric-label">Steepness</div>
|
||
<div class="metric-value" id="mSteepness">-</div>
|
||
</div>
|
||
<div class="metric-card" title="Noise Floor - average magnitude in the top quarter of the spectrum (> 3/4 Nyquist), in dB relative to peak. How to read: • < -90 dB: Excellent - extremely clean, high dynamic range • -80 to -90 dB: Very good - typical of quality lossless • -70 to -80 dB: Moderate - some quantization noise present • -60 to -70 dB: Elevated - suggests lossy compression or analog hiss • > -60 dB: High - almost certainly lossy compressed Significance: Lossy codecs raise the noise floor through quantization. High noise floor + sharp cutoff = strong transcode indicator.">
|
||
<div class="metric-label">Noise Floor</div>
|
||
<div class="metric-value" id="mNoise">-</div>
|
||
</div>
|
||
<div class="metric-card" title="Spectral Roughness (CoV) - coefficient of variation (σ/μ) of spectral magnitudes in the 60-95% region below cutoff. Measures how uneven/scalloped the spectrum is. How to read: • > 0.40: Strong spectral scalloping - clear lossy encoding signature • 0.30-0.40: Moderate scalloping - suspicious, may be high-bitrate lossy • 0.15-0.30: Mild unevenness - needs band-ratio correlation • < 0.15: Smooth spectrum - typical of genuine lossless Significance: Lossy codecs create a comb-filter pattern via frequency-bin quantization. High roughness = high probability of lossy encoding.">
|
||
<div class="metric-label">Roughness</div>
|
||
<div class="metric-value" id="mRoughness">-</div>
|
||
</div>
|
||
<div class="metric-card" title="Band Ratio - ratio of average spectral energy in 16-20 kHz band to 12-16 kHz band. Detects unnatural high-frequency tilt. How to read: • < 0.5: Normal natural rolloff in top octave • 0.5-0.9: Typical for most recordings • 0.9-1.0: Flat HF - possible spectral reshaping from lossy encoding • > 1.0: Inverted tilt (more energy at 16-20 kHz than below) - strong transcode signature Significance: Used as secondary criterion when cutoff is high but roughness is ambiguous. Transcodes often show unnatural energy distribution in the top octaves.">
|
||
<div class="metric-label">Band Ratio</div>
|
||
<div class="metric-value" id="mBandRatio">-</div>
|
||
</div>
|
||
</div>
|
||
<div class="analysis-info">
|
||
<span><strong>Windows:</strong> <span id="windowsCount">-</span></span>
|
||
<span><strong>FFT:</strong> <span id="fftSize">-</span></span>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="decisionSection" title="Verdict - the overall classification of the audio file based on spectral analysis. NATIVE: File matches its declared lossy format at a consistent bitrate (single encode). UPSCALED: Lossy file re-encoded to a higher bitrate than the original source. GENUINE: File appears to be native lossless (no transcode artifacts detected). TRANSCODE: Lossy source re-encoded to a lossless format (e.g. 128k MP3 → FLAC). The verdict is decided by: cutoff ratio, transition bandwidth (steepness), roughness (CoV), and band-ratio analysis against adaptive thresholds.">
|
||
<h2>Decision</h2>
|
||
<div id="decision" class="decision-text">-</div>
|
||
<div class="confidence-container" title="Validity Confidence - how strongly the evidence supports the verdict, from 0% (random chance) to 100% (certain). 85-100%: Very strong evidence - multiple criteria agree decisively. 70-84%: Strong evidence - most criteria support the verdict. 50-69%: Moderate evidence - some criteria triggered but not all. 30-49%: Weak evidence - marginal, borderline detection. 0-29%: Inconclusive - possible false positive, or audio is ambiguous. For lossy files: confidence is based on how far below the expected cutoff ratio the file sits. For lossless candidates: confidence blends cutoff ratio margin, steepness margin, and (above 80% Nyquist) secondary roughness + band-ratio criteria.">
|
||
<div class="confidence-label">Validity Confidence</div>
|
||
<div class="confidence-bar-bg">
|
||
<div id="confidenceBar" class="confidence-bar-fill" style="width:0%">0%</div>
|
||
</div>
|
||
<div id="confidenceLabel" class="confidence-label-small">-</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="factors" title="Factor Analysis - step-by-step breakdown of the detection criteria used to reach the verdict. ① PRIMARY criteria (applied to ALL files): Compares transition bandwidth (steepness) to an adaptive threshold based on cutoff ratio. If steepness is BELOW the threshold, the spectrum has an artificially sharp cutoff → transcode flagged. Files with cutoff_ratio ≥ 0.99 (full spectrum) bypass this check. ② SECONDARY criteria (applied when cutoff_ratio > 0.80): Checks spectral roughness (CoV) and band ratio for additional transcode signatures. High roughness (> 0.40) alone is sufficient to confirm. Moderate roughness needs band-ratio corroboration. Lossy files: checked only for cutoff ratio vs expected range for the bitrate. ✓ = criterion met (supports transcode/upscaled), ✗ = criterion not met (supports genuine/native).">
|
||
|
||
<section id="dataSection">
|
||
<h2>Analysis Data</h2>
|
||
<div class="table-container">
|
||
<table id="dataTable">
|
||
<thead>
|
||
<tr><th>Parameter</th><th>Value</th></tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
|
||
</div>
|
||
|
||
<footer>
|
||
<p>Based on <a href="https://codeberg.org/armin/tcd" target="_blank">tcd</a> - Psychoacoustic audio authenticity analysis</p>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<script src="js/metadata.js"></script>
|
||
<script src="js/analyzer.js"></script>
|
||
<script src="js/app.js"></script>
|
||
</body>
|
||
</html>
|