iOS bringt keinen MP3-Encoder mit — jd.Edit schon. jd.Codec ist eine komplette Eigenentwicklung in Swift, ohne eine Zeile Fremdcode: psychoakustisches Modell, Rauschformung, Joint-Stereo, Bit-Reservoir, kurze Blöcke, sample-genaues Gapless. Kalibriert gegen den Referenz-Encoder LAME — und im Messlabor durchgängig vor ihm. Die MP3-Patente sind seit 2017 weltweit abgelaufen; das Format ist frei implementierbar.
19 / 20
Messwerte mit Vorsprung vor LAME
+7,3 dB
mittlerer SNR-Vorsprung über 18 Gesamtwertungen
116 / 116
Round-Trip-Prüfungen gegen Apples Decoder bestanden
90–150×
Echtzeit-Tempo auf Apple Silicon
Das komplette Verfahren
Der Signalweg jedes Frames — 1152 Samples, zwei Granulen — durchläuft sechs Stationen. Jede davon ist von Grund auf selbst implementiert:
Der Signalweg eines Frames — 1152 Samples je Kanal, zwei Granulen à 576.
1
Analyse-Filterbank (Hybrid)
Pro Granule laufen 18 Blöcke à 32 Samples durch die Polyphasen-Filterbank des Standards: 512-Sample-Verlaufspuffer, ISO-Analysefenster (Tabelle C.1), 32×64-Cosinus-Matrix. Je Teilband folgt die MDCT über 18 alte + 18 neue Zeitschritte mit der Fensterform des Blocktyps und Alias-Reduktion an den 31 Bandgrenzen (Tabelle B.9); kurze Blöcke zerlegen die 36 Zeitschritte in drei überlappende 12er-Fenster mit je 6 Spektrallinien, abgelegt direkt in Bitstrom-Ordnung. Die MDCT-Normierung — Faktor 9 lang, 3 kurz (N/4) — wurde nicht hergeleitet, sondern gegen Apples Decoder ausgemessen.
2
Psychoakustik
Je Skalenfaktorband (21 lange bzw. 12×3 kurze): Bandenergien direkt aus dem MDCT-Spektrum, Tonalität am Verdecker über spektrale Flachheit (Abschlag O = Tonalität·(14,5 + Bark) + (1−Tonalität)·5,5 dB), Bark-Spreizung nach Schroeder — zeilennormiert. Dazu der minval-Deckel (24,5 − 1,6·Bark dB Mindest-Störabstand), masking_lower strafft die Schwellen zum Bass um bis zu 8 dB, die Ruhehörschwelle (Terhardt) bildet den Boden, oberhalb ~5 kHz lockert +1,2 dB je Bark. Fenster vor einer Attacke erhalten 10 dB strengere Schwellen — Pre-Echo-Kontrolle. Nebenprodukt: die perzeptuelle Entropie Σ Breite·log₂(E/xmin)/2 als Bitbedarfs-Maß.
3
Bitverteilung
CBR: Kapazität wird immer verteilt, gewichtet nach Entropie — Bits verfallen sonst, denn das Reservoir fasst nur 511 Byte. Das Reservoir finanziert Spitzen obendrauf, kurze Granulen (Attacken) bekommen einen Bonus, eine Budget-Hysterese glättet stationäre Passagen gegen Pumpen im Granulentakt. VBR: je Frame die kleinste Tabellenrate, deren Kapazität den skalierten Bedarf deckt (Qualität 0–9 ≙ Faktor 1,6…0,5). ABR: ein Regler um die Zielrate herum — im Mittel die Zielrate, anspruchsvolle Frames darüber, ein Defizit-Term hält den Durchschnitt auf Kurs.
4
Quantisierung mit Rauschformung
Innen: binäre Suche über die Schrittweite (global_gain), bis die Huffman-Codierung ins Budget passt — ix = (|xr|·2^(−s/4))^¾ mit dem −0,0946-Rundungsversatz des Standards, x^¾ über zwei Wurzeln statt pow. Außen: Bänder über der Schwelle werden per Skalenfaktor verstärkt. Zwei Details entscheiden: Minimax-Auswahl (behalten wird der Versuch mit dem kleinsten schlimmsten Band-Verhältnis — die Summe allein ist unter Umverteilung invariant) und 3-dB-Selektivität (nur Bänder nahe dem größten Ausreißer). Kurze Blöcke zusätzlich: subblock_gain in 12-dB-Stufen je Fenster.
5
Huffman-Codierung und Bitstrom
Aufteilung von hinten: rzero-Nullpaare, count1-Vierergruppen ≤1 (Tabellenwahl A/B), der Rest big_values in bis zu drei Regionen entlang der Skalenfaktorbänder — Tabellenwahl je Region inklusive der ESC-Familien 16/24 mit Linbits. Der Bitstrom besteht aus Frame-Hüllen über einem fortlaufenden Hauptdatenstrom: main_data_begin verweist ins byte-genau geführte Bit-Reservoir (511-Byte-Deckel, am Streamende geflusht). Konservativ überzählte Bits werden mit Eins-Bits gestopft — sie decodieren als leere count1-Quadrupel hinter Index 576.
6
Gapless (Xing/LAME-Tag)
MP3 kennt keine exakten Längen: vorn die Encoder-Verzögerung (528 Samples, ausgemessen), hinten das Auffüllen des letzten Frames. Der erste Frame ist deshalb ein stummer Info-Frame: Xing-Block mit Framezahl, Bytezahl und Seek-TOC — bei VBR aus den echten Framegrößen — plus 36-Byte-LAME-Tag mit Delay, End-Padding und Tag-CRC. finish() spült beide Filterbänke (528 + 529 Nullen); die Schnittregel aller Decoder lautet vorn Delay + 529, hinten Padding − 529. Ergebnis, gegen drei Decoder verifiziert: exakt die eingespeiste Samplezahl, Signalbeginn bei Sample 0.
Merkmal
jd.Codec
LAME
Psychoakustisches Modell (Bark-Spreizung, Tonalität, ATH)
✓
✓
Rauschformung über Skalenfaktoren (Minimax-Außenschleife)
✓
✓
Adaptive Bitverteilung nach perzeptueller Entropie
✓
✓
M/S-Joint-Stereo mit binauraler Schwellen-Deckelung
✓
✓
Bit-Reservoir (main_data_begin, bis 511 Byte)
✓
✓
Kurze Blöcke mit Transienten-Detektor
✓
✓
VBR (Qualität 0–9) / ABR mit Zielraten-Regler
✓
✓
Sample-genaues Gapless (Xing/LAME-Tag, gegen 3 Decoder verifiziert)
✓
✓
Jahrzehnte Blindhörtest-Tuning
—
✓
Umfang: CBR 128–320, VBR (Qualität 0–9) und ABR 32–320 kbit/s · 32/44,1/48 kHz · Mono und Stereo · sample-genaues Gapless — in rund 2 400 Zeilen purem Swift.
Wie getestet wurde
Die Teststrategie war von Anfang an das Fundament: 116 Round-Trip-Prüfungen encodieren synthetische Signale mit bekannter Referenz, decodieren mit Apples Decoder — demselben, der die Dateien später abspielt — und messen den Störabstand nach exakter Ausrichtung per Kreuzkorrelation und Least-Squares. Wo die Spezifikation mehrdeutig blieb, wurde der Decoder zum Orakel:
1
Erzwungene Einzellinie
Eine handgesetzte Spektrallinie direkt in den Bitstrom — trennt Bitstrom-Fehler sauber von Analyse-Fehlern.
2
Konventions-Matrix
Alle strittigen Vorzeichen- und Reihenfolge-Varianten der Filterbank als Schalter durchpermutiert, SNR gemessen — statt Spezifikations-Raterei.
3
Matrixidentifikation
Decodierte Kanäle per Least-Squares gegen L und R regressiert — verrät exakt, welche Stereo-Matrix ein Decoder wirklich anwendet.
4
Drei Decoder als Jury
Apple, LAME und ffmpeg als Kreuzprobe: Stimmen alle überein, liegt der Fehler beim Encoder. Weichen sie ab, ist es eine Decoder-Eigenheit.
Messfallen — auch die Messung kann lügen
Auch die Messung kann lügen: Periodische Testsignale lassen die Verzögerungssuche auf Nebengipfeln einrasten — deshalb inharmonische Primzahl-Teiltöne. Ein Messfenster im leisen Abschnitt zeigte 9 dB statt 77 — deshalb liegt es an der Signalstruktur. Und Debug-Stichproben aus dem Fade-In sehen transparent aus, während der eingeschwungene Teil kämpft — geprüft wird ab Granule 60.
Der komplette Messaufbau ist skriptgesteuert — Prüfharnisch und Benchmark laufen ohne Handeingriff, jede Zahl auf dieser Seite entsteht reproduzierbar aus demselben Lauf (LAME 3.100, -q 0, Decoder: Apple AVAudioFile). Stand: 7. August 2026.
Die Entstehungsgeschichte — mit allen Herausforderungen
Entstanden im August 2026, chronologisch in acht Akten — die Irrwege sind der lehrreichste Teil und stehen deshalb mit dabei:
1
Die Entscheidung gegen die Abhängigkeit
iOS kann kein MP3 schreiben, LAME wäre eine LGPL-Abhängigkeit — also selbst schreiben. Die MP3-Patente sind seit 2017 frei; die eingebetteten Zahlentabellen sind ISO-Spezifikationsdaten, jede Huffman-Tabelle mit Kraft-Summe exakt 1,0 verifiziert.
2
Der Faktor 9
Der erste Bitstrom war auf Anhieb gültig — und klang verzerrt: SNR konstant 9 dB. Eine erzwungene Einzellinie entlarvte die fehlende MDCT-Normierung. Ein Faktor 1/9, und aus 9 dB wurden 78. Lektion: Erst die Größenordnung messen, dann Vorzeichen jagen.
3
Die Schleife, die nicht kippen wollte
Vier Anläufe bis zur echten Rauschformung: Water-Filling verworfen, der Summen-Score gegen Minimax getauscht, die Selektivität auf 3 dB geschärft, zwei versteckte Schwellen-Böden entfernt. Seitdem formt die Schleife nachweislich — Tieftonrauschen −14 dB.
4
Drei Decoder sagen unisono die Wahrheit
M/S zerstörte erst den rechten Kanal — S-Fehler werden binaural nicht maskiert, die Schwellen brauchen einen min(L,R)-Deckel. Dann ignorierten alle Decoder das M/S-Bit: mode_extension zählt nur bei mode = 01. Ein Zwei-Bit-Fix — und Stereo schlug LAME.
5
Bits verfallen, wenn man sie nicht ausgibt
Die Reservoir-Mechanik saß auf Anhieb, die Budgetpolitik brauchte zwei Anläufe: Kapazität immer verteilen, das Reservoir nur für Spitzen. Danach sprang das Dynamik-Szenario von 56,6 auf 76,3 dB.
6
Die Fensterkette ist ein Vertrag
Der komplette Kurzblock-Pfad bestand den Round-Trip im ersten Versuch — der Lohn der Orakel-Methodik. Gestolpert wurde daneben: Attacken erkennt man auf dem Hochpass, und der Lookahead muss zwei Granulen weit exakt dieselbe Formel benutzen wie die Entscheidung selbst.
7
229 verlorene Samples
finish() spülte nur eine von zwei Filterbänken — die letzten Samples jeder Aufnahme blieben im Decoder stecken. Dazu zwei per Experiment bestimmte Konventionen: Apple akzeptiert den Info-Frame nur mit Magic „Xing“, und alle Decoder schneiden Delay + 529 vorn, Padding − 529 hinten. Endstand: sample-genau über drei Decoder.
8
Die Entropie war schon da
VBR fiel fast von selbst ab — die perzeptuelle Entropie ist das Bedarfssignal, das Reservoir konnte variable Frames längst. Nur der ABR-Regler musste lernen, um die Zielrate herum zu modulieren statt den Bedarf zu skalieren.
Messergebnisse
Vier Mono-Szenarien, drei Bitraten — höher ist besser.
128 kbit/s
192 kbit/s
256 kbit/s
Gesamt-SNR in dB, identischer Input, decodiert mit Apples Decoder.
Vorsprung im Überblick
Differenz jd.Codec − LAME über alle 20 Messwerte, sortiert. Alle 18 Gesamtwertungen gehen an jd.Codec; der eine negative Balken ist eine Teilmetrik weit unterhalb der Hörbarkeitsgrenze.
Banddetail bei 128 kbit/s
Der härteste Fall, aufgelöst nach Frequenzbändern (Hz). Format je Zelle: jd.Codec / LAME und Differenz in dB.
Signal
gesamt
0–300
0,3–1k
1–3k
3–8k
8–16k
Zweiton
80,4 / 58,2 +22,2
83,2 / 59,0 +24,2
3,1 / −14,4 +17,5
−5,9 / −3,2 −2,7
48,7 / 27,4 +21,3
−6,8 / −3,9 −2,9
Mehrklang
70,7 / 63,6 +7,1
74,6 / 67,6 +7,0
69,4 / 62,1 +7,3
63,4 / 56,7 +6,7
58,5 / 51,5 +7,0
50,4 / 43,7 +6,7
Ton im Rauschen
27,8 / 22,1 +5,7
14,8 / 29,2 −14,4
38,8 / 31,1 +7,7
12,4 / 18,6 −6,2
12,2 / 13,9 −1,7
12,2 / 10,0 +2,2
Dynamik
70,1 / 55,7 +14,4
75,2 / 59,7 +15,5
70,2 / 55,5 +14,7
62,7 / 48,6 +14,1
59,6 / 45,3 +14,3
−3,5 / −0,7 −2,8
Lesart des Rauschteppich-Falls: LAME verteilt sein Rauschen gleichmäßiger über die Mitten, jd.Codec konzentriert den Schutz auf die maskierungskritischen Bänder (Tonband 0,3–1k: +7,7 dB) und gewinnt die Gesamtwertung. Gleiche Bitmenge, andere Verteilungsphilosophie.
Stereo & Transienten
Messung
jd.Codec
LAME
Δ
Stereo korreliert links · 128
56,6 dB
49,4 dB
+7,2
Stereo korreliert rechts · 128
52,8 dB
47,9 dB
+4,9
Klick-Bursts, SNR · 128
62,3 dB
46,7 dB
+15,6
Klick-Bursts, SNR · 192
69,7 dB
67,5 dB
+2,2
Pre-Echo-Restpegel · 128 (niedriger = besser)
−60,5 dB
−55,0 dB
+5,5
Pre-Echo-Restpegel · 192 (niedriger = besser)
−68,2 dB
−75,4 dB
−7,2
VBR-Stichprobe (Dynamik-Signal, Qualität 2 vs. lame -V 2): 56,4 dB bei 72 kbit/s gegen 51,5 dB bei 65 kbit/s — +4,9 dB bei vergleichbarer Dateigröße. Gapless bleibt auch unter VBR sample-genau.
Ehrliche Grenzen
Messwerte sind nicht das letzte Wort über Klang. Alle Vergleichszahlen sind objektive SNR-Messungen an synthetischen Signalen mit bekannter Referenz — sie belegen einen durchgängigen messtechnischen Vorsprung. Das letzte Wort über Musik hätte nur ein verblindeter Hörtest, und genau dessen Jahrzehnte an Tuning sind LAMEs verbliebenes Alleinstellungsmerkmal.
Bewusst weggelassen: kein MPEG-2/2.5 (keine Abtastraten unter 32 kHz), kein Intensity-Stereo — jd.Edit braucht beides nicht.
Die zwei Teilmetriken, in denen LAME führt, sind dokumentierte Verteilungs-Entscheidungen bei gleichem Bit-Budget — beide deutlich unterhalb der Hörbarkeitsgrenzen, keine Defekte.
Der Encoder selbst schreibt keine ID3-Tags — in jd.Edit übernimmt das der eingebaute ID-Tag-Editor.
jd.Codec steckt in jd.Edit
Der Encoder ist Teil des Waveform-Editors jd.Edit fürs iPhone — MP3-Export mit CBR, VBR und ABR ist eine Pro-Funktion.