CodecDelta — Ses Kodlama Farkı AnaliziCodecDelta — Audio Codec Delta Analysis
Kayıplı bir kodlayıcının kayda ne yaptığını ölçen Windows uygulaması. Aynı kaydın iki kodlamasını örnek-altı hassasiyetle hizalayıp karşılaştırır, farkın duyulup duyulmadığını kör ABX testiyle sorar ve tek bir dosyadan "bu FLAC gerçekten kayıpsız mı" sorusuna cevap arar. Asıl değeri özellik listesi değil, ölçülerek alınan ve reddedilen kararların kaydı.A Windows application that measures what a lossy encoder did to a recording. It aligns two encodes of the same material to sub-sample accuracy, asks with a blind ABX test whether the difference is audible, and checks from a single file whether a FLAC is really lossless. Its real value is not the feature list but the record of decisions taken, and rejected, by measurement.
- TestTests
- 617
- SürümRelease
- v1.0.0
- DenetimAudit
- 12.600 denemetrials
- KodlayıcıEncoders
- 12
ProblemProblem
FLAC ile Opus, orijinal ile YouTube kopyası, MP3 ile AAC: ne kadar bilgi kayboldu, spektrumun neresinde, duyulur mu? Ve referans olmadan, tek dosyadan: bu FLAC gerçekten kayıpsız mı, yoksa kayıplı bir kaynaktan dönüştürülmüş mü?
Bu analizi ffprobe, ffmpeg filtreleri ve tek seferlik betiklerle elle yapıyordum. Tekrarlanabilir değildi ve video girdisini kaldıramıyordu. Hedef: örnek-altı hassasiyetle hizalayan, kararlarını ölçümle veren ve "fark yok" demeyi asla başarısız bir testten türetmeyen bir araç.
FLAC against Opus, an original against a YouTube rip, MP3 against AAC: how much information was lost, where in the spectrum, and is it audible? And with no reference, from a single file: is this FLAC really lossless, or a transcode from a lossy source?
I used to do this by hand with ffprobe, ffmpeg filters and one-off scripts. It was not repeatable and could not take video input. The goal: a tool that aligns to sub-sample accuracy, decides by measurement, and never derives "no difference" from a failed test.
YaklaşımApproach
- Dört sekmeli uygulama. Analiz iki dosyayı karşılaştırır ya da tek dosyayı doğrular; Kodlama ffmpeg yapısından bulunan 12 kodlayıcıyla kodlayıp hemen karşılaştırır ve 96–256 kbps arasında bit hızı taraması yapar; ABX hizalanmış, seviyesi eşitlenmiş, tıklamasız (8 ms çapraz geçiş) kör test yürütür; Kütüphane taraması bir klasörü kaldığı yerden devam edebilen önbellekle tarar ve CSV verir. Arayüz Türkçe ve İngilizce.
- Paylaşılabilir rapor. Tek dosyalık HTML, grafikler satır içi SVG, açık/koyu tema. Yazılmadan önce yol, kullanıcı ve bilgisayar adı için denetlenir; sızıntı bulunursa dosya hiç yazılmaz.
- Kurulum. Kullanıcı başına kurulum (yönetici yetkisi gerekmez), ffmpeg indirme betiği özetini ve soxr desteğini doğrular. CI temiz bir makinede paketi derler ve duman testinden geçmeyen yapıyı paketlemez.
- Aşağıdan yukarı motor.
core/(ffmpeg keşfi, PCM akışı, ffprobe, gizlilik temizliği, ABX istatistiği) →bitstream/(Ogg örneklenmiş CRC, Opus TOC, Vorbis, FLAC MD5, MP3 Xing/LAME) →dsp/vealign/(alt-örnek gecikme kestirimi ve geçerlilik ölçüsü). Motor katmanı Qt import etmez; bir test bunu kilitler. - Sözleşme koddan önce. Hizalama çekirdeğinin spesifikasyonu (
docs/SPEC-alignment.md) kod yazılmadan yazıldı. - Video girdisi bedava. Video akışı hiç decode edilmez; 20 GB'lık bir konser MKV'si yalnız ses parçası kadar maliyet çıkarır. 684 sn'lik gerçek bir YouTube m4a'da probe + decode 1242× gerçek zaman ölçüldü.
- Bilinçli eksikler. scipy ve matplotlib yok (FFT bütçenin %1'i; çizim numpy → QImage ve elle SVG). AAC bit akışı ayrıştırıcısı yazılmadı: ffprobe zaten
mp4a.40.2nesne tipini veriyor, ayrıştırıcı sıfıra yakın bilgi ekleyecekti. - Kalite kapısı. Her turda
ruff+ruff format+mypy --strict+pytestyeşil olmadan commit yok; Windows CI ilk commit'ten beri.
- A four-tab application. Analyze compares two files or verifies one; Encode encodes with the 12 codecs found in the ffmpeg build, compares immediately and sweeps bitrates from 96 to 256 kbps; ABX runs an aligned, level-matched, click-free (8 ms crossfade) blind test; Library scan checks a whole folder with a resumable cache and exports CSV. UI in English and Turkish.
- Shareable reports. A single self-contained HTML file, charts as inline SVG, light and dark. Before writing, it is audited for paths, user and machine names; if anything leaks, the file is not written.
- Packaging. Per-user install (no administrator rights); the ffmpeg download script checks the digest and soxr support. CI builds the package on a clean machine and refuses to zip a build that fails its smoke test.
- Bottom-up engine.
core/(ffmpeg discovery, PCM streaming, ffprobe, privacy scrubbing, ABX statistics) →bitstream/(Ogg with sampled CRC, Opus TOC, Vorbis, FLAC MD5, MP3 Xing/LAME) →dsp/andalign/(sub-sample delay estimation and a validity measure). The engine layer never imports Qt; a test locks that. - Contract before code. The alignment core's specification (
docs/SPEC-alignment.md) was written before the code it describes. - Video input for free. The video stream is never decoded; a 20 GB concert MKV costs the same as its audio track alone. Probe + decode measured at 1242× realtime on a real 684 s YouTube m4a.
- Deliberate omissions. No scipy, no matplotlib (FFT is 1% of the budget; plots go through numpy → QImage and hand-written SVG). No AAC bitstream parser: ffprobe already reports the
mp4a.40.2object type, so a parser would add almost nothing. - Quality gate. No commit without
ruff+ruff format+mypy --strict+pytestgreen; Windows CI since the first commit.
Kanıt iziEvidence trail
Makul görünen fikirler uygulandı, ölçüldü ve ölçüm aksini söyleyince kaldırıldı. Her biri docs/DECISIONS.md'de sayılarıyla kayıtlı; bir kısmı geri gelmesin diye testle kilitli.
| Karar | Ölçüm |
|---|---|
ffmpeg borusundan readinto, asla read(n) | 117,6 s → 2,1 s (57×, 211 MB akış) |
| Alt-örnek gecikme: faz eğimi yerine artık araması | gerçek müzik + lowpass: %79,17 → %0,00 hata |
| Gerçek FLAC vs Opus/MP3/AAC/Vorbis çiftleri | faz eğimi 6/6 başarısız → artık araması 0/6 |
| Ogg CRC örnekleme (ilk 16 sayfa + her 64.) | 1393 ms → 137 ms, aynı sonuç |
| Geçerlilik ölçüsü, ikinci kestirici değil | 2,4 ms (alternatifler 8,0 / 23,0 ms) |
| soxr yeniden örnekleme, cutoff açıkça 0,99 | varsayılan 21,5 kHz'de −30,4 dB bastırıyordu |
| ABX gücü n = 16 | plan 0,45 sanıyordu, gerçek 0,630; %80 güç n ≈ 26 |
| Kaba hizalama: örtüşme sayısı yerine Pearson | 40 tohumda 20/40 → 40/40 doğru |
ffmpeg -ss her formatta örnek-doğru değil | M4A/AAC'de 70–820 örnek sapma; yalnız ölçülenler hızlı yoldan |
| Yeniden örnekleme zinciri kayan noktada | 16-bit ara adım: 81 dB → 148 dB |
| Hüküm mutlak NMR'ye değil, kullanıcının referansından kurulan merdivene dayanır | tek bir tonalite seçimi NMR'yi 15 dB oynatıyor |
Faz eğiminin çöküşünün kök nedeni gürültü değil np.unwrap: banttaki tek bir düşük genlikli bin 2π dal değişimi yapıp sonraki tüm binlere taşıyor. Tonal müzik seyrek spektrumludur (en büyük %1 bin ağırlığın %78,6'sını taşıyor), bu yüzden sonsuz S/N'de bile çöküyor. Düşük binleri filtrelemek denendi ve reddedildi: spektral dropout'ta 150–400 örneklik hata veriyor.
Kaldırılan sezgiseller: FLAC blok boyutu kodlayıcıyı belirlemez (aynı ffmpeg 1024, 4096 ve 4608 üretti); ffmpeg gerçek LAME etiketi yazmaz (+9..+20 alanları sıfır, sıfır "0 Hz'de kesilmiş" diye okunmaz); tepe keskinliği geçerli/geçersizi ayırmaz (aralıklar tamamen örtüşüyor, korelasyon ayırıyor); plandaki dönüştürme sezgisellerinin bir kısmı tersti (9 CD parçası ve 117 dönüştürmede ayırt eden tek işaret çerçeve başına kesim frekansının medyanı çıktı).
Plausible ideas were implemented, measured and removed when the measurement disagreed. Each is recorded in docs/DECISIONS.md with its numbers; several are locked by tests so they cannot come back.
| Decision | Measurement |
|---|---|
readinto on the ffmpeg pipe, never read(n) | 117.6 s → 2.1 s (57×, 211 MB stream) |
| Sub-sample delay: residual search instead of phase slope | real music + lowpass: 79.17% → 0.00% failures |
| Real FLAC vs Opus/MP3/AAC/Vorbis pairs | phase slope failed 6/6 → residual search 0/6 |
| Ogg CRC sampling (first 16 pages + every 64th) | 1393 ms → 137 ms, identical results |
| A validity measure, not a second estimator | 2.4 ms (alternatives 8.0 / 23.0 ms) |
| soxr resampling with an explicit 0.99 cutoff | default attenuated −30.4 dB at 21.5 kHz |
| ABX power at n = 16 | plan assumed 0.45, actual 0.630; 80% power at n ≈ 26 |
| Coarse alignment: Pearson instead of overlap count | 40 seeds: 20/40 → 40/40 correct |
ffmpeg -ss is not sample-accurate on every format | 70–820 samples off on M4A/AAC; only measured formats take the fast path |
| Resampling chain forced to floating point | 16-bit midway step: 81 dB → 148 dB |
| Verdict rests on a ladder built from the user's reference, not absolute NMR | one tonality choice moves NMR by 15 dB |
The root cause of the phase-slope failure is not noise but np.unwrap: a single low-magnitude bin flips the branch by 2π and every subsequent bin inherits the offset. Tonal music is sparse (the top 1% of bins carry 78.6% of the fit weight), so it fails even at infinite SNR. Filtering low bins before unwrapping was tried and rejected: under spectral dropout it produces 150–400-sample errors.
Removed heuristics: FLAC blocksize does not identify an encoder (the same ffmpeg produced 1024, 4096 and 4608); ffmpeg does not write a real LAME tag (fields +9..+20 are zero, and zero must never be read as "cut at 0 Hz"); peak sharpness does not separate valid from invalid (the ranges overlap completely; correlation does the job); some of the plan's transcode heuristics were backwards (on 9 CD tracks and 117 transcodes, the only clean separator was the median per-frame cutoff).
Henüz doğrulanmayanlarNot verified yet
v1.0.0 çalışıyor, ama her şey kanıtlanmış değil; deponun README'si bunları açıkça listeliyor. ABX testi henüz kulakla denenmedi ve Bluetooth üzerinden çalma (tampon boşalmaları testi bozabilir) ölçülmedi. "Farklı master" sınıfı gerçek remaster çiftleri bulunamadığı için sentetik veriyle kalibre edildi; bu yüzden hüküm değil yalnız uyarı üretiyor. Kayıplı kaynak eşiklerini kullanıcının kendi dosyalarından kalibre etmek bilinçli olarak yapılmadı: 20,75 kHz kapısının iki yanında yaklaşık 120 Hz pay var ve otomatik ayar, denetlenemeyen yanlış pozitifler ekleyebilir.
v1.0.0 works, but not everything is proven; the repository README lists these openly. The ABX test has not yet been tried by ear, and playback over Bluetooth (where buffer underruns could spoil a test) has not been measured. The "different master" class was calibrated on synthetic data because no real remaster pairs were available, so it produces a warning rather than a verdict. Calibrating the lossy-source thresholds from the user's own files was deliberately not built: the margin is about 120 Hz either side of the 20.75 kHz gate, and an automatic adjustment could add false positives with no way to check them.
ÖğrendiklerimWhat I learned
Bir kestirici her zaman bir sayı döndürür; bu, sayının bir anlamı olduğu anlamına gelmez. İlgisiz iki sinyalde iki kestirici iki farklı "makul" gecikme verdi, ikisi de yanlıştı; "bulunacak bir gecikme var mı" sorusunu ancak bir geçerlilik ölçüsü cevaplayabildi.
Reddedilen fikirleri de belgelemek, aynı hatanın geri gelmesini önleyen tek şey. Bu projenin en değerli dosyası kod değil, DECISIONS.md.
An estimator always returns a number; that does not mean the number means anything. On two unrelated signals, two estimators returned two different "plausible" delays, both wrong; only a validity measure could answer whether there was a delay to find at all.
Documenting rejected ideas is the only thing that stops the same mistake from returning. The most valuable file in this project is not code, it is DECISIONS.md.