{"id":18604,"date":"2026-09-17T08:15:00","date_gmt":"2026-09-17T06:15:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/werkzeuge\/lufs-true-peak-loudness-meter\/"},"modified":"2026-09-17T08:19:47","modified_gmt":"2026-09-17T06:19:47","slug":"lufs-true-peak-loudness-meter","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/","title":{"rendered":"LUFS- und True-Peak-Messger\u00e4t: Lautheit, Lautheitsumfang und Streaming-Normalisierung"},"content":{"rendered":"<div class=\"lw-lufs\" id=\"lufs-wurzel\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<style>\n.lw-lufs { --lufs-warn: #e8a33d; }\n:root[data-theme=\"light\"] .lw-lufs { --lufs-warn: #8f5300; }\n.lw-lufs .lufs-zone { border: 2px dashed var(--border, #2a2a35); border-radius: 8px; padding: 24px 16px; text-align: center; transition: border-color 0.15s, background 0.15s; }\n.lw-lufs .lufs-zone.lufs-ueber { border-color: var(--accent, #2ba6df); background: var(--bg-body, #14141a); }\n.lw-lufs .lufs-kacheln { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin: 14px 0 4px; }\n.lw-lufs .lufs-kachel { background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 8px; padding: 11px 14px; min-width: 0; }\n.lw-lufs .lufs-kachel-name { font-size: 0.74rem; color: var(--text-secondary, #a0a0b0); text-transform: uppercase; letter-spacing: 0.4px; }\n.lw-lufs .lufs-kachel-wert { font-family: monospace; font-size: 1.4rem; font-weight: 700; color: var(--text-primary, #e8e8ee); line-height: 1.25; white-space: nowrap; }\n.lw-lufs .lufs-einheit { font-size: 0.8rem; font-weight: 400; color: var(--text-secondary, #a0a0b0); margin-left: 4px; }\n.lw-lufs .lufs-tabelle { overflow-x: auto; }\n.lw-lufs table { border-collapse: collapse; width: 100%; font-size: 0.86rem; }\n.lw-lufs th, .lw-lufs td { text-align: left; padding: 6px 12px 6px 0; border-bottom: 1px solid var(--border, #2a2a35); vertical-align: top; }\n.lw-lufs th { color: var(--text-primary, #e8e8ee); white-space: nowrap; }\n.lw-lufs td { color: var(--text-secondary, #a0a0b0); }\n.lw-lufs td.lufs-zahl { font-family: monospace; color: var(--text-primary, #e8e8ee); white-space: nowrap; }\n.lw-lufs .lufs-ok { color: var(--ok, #7ee787) !important; }\n.lw-lufs .lufs-warn { color: var(--lufs-warn) !important; }\n.lw-lufs .lufs-fail { color: var(--fail, #ff7b72) !important; }\n.lw-lufs .lufs-balken { height: 8px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 4px; overflow: hidden; }\n.lw-lufs .lufs-balken > div { height: 100%; width: 0; background: var(--accent, #2ba6df); }\n.lw-lufs .lufs-befund { background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-left-width: 3px; border-radius: 8px; padding: 11px 14px; margin: 0 0 8px; }\n.lw-lufs .lufs-h { font-weight: 700; color: var(--text-primary, #e8e8ee); font-size: 0.95rem; margin: 24px 0 8px; }\n.lw-lufs .lufs-klein { font-size: 0.84rem; color: var(--text-secondary, #a0a0b0); line-height: 1.55; }\n.lw-lufs canvas { display: block; width: 100%; max-width: 100%; }\n.lw-lufs a { color: var(--accent, #2ba6df); }\n@media (max-width: 480px) {\n    .lw-lufs .lufs-kacheln { grid-template-columns: 1fr 1fr; gap: 8px; }\n    .lw-lufs .lufs-kachel { padding: 10px 11px; }\n    .lw-lufs .lufs-kachel-wert { font-size: 1.15rem; }\n}\n<\/style>\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 18px;\">Streamingdienste spielen einen Titel nicht so laut ab, wie er gemastert wurde, sondern gleichen ihn an einen Zielwert an. Ein laut gemasterter Titel wird leiser gestellt, ein leiser nur so weit angehoben, wie der Spitzenpegel Platz l\u00e4sst. Das Messger\u00e4t ermittelt die Werte, auf denen diese Entscheidung beruht, und rechnet die Normalisierung f\u00fcr Spotify und nach den Empfehlungen von AES und EBU nach.<\/p>\n<div id=\"lufs-zone\" class=\"lufs-zone\">\n<p style=\"margin: 0 0 12px; color: var(--text-primary, #e8e8ee); font-weight: 700;\">Audiodatei hier ablegen<\/p>\n<p>        <button id=\"lufs-knopf\" type=\"button\" class=\"button\" style=\"background: var(--accent, #7ee787); color: var(--on-accent, #0b1114); border: none; padding: 11px 22px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Audiodatei w\u00e4hlen<\/button><br \/>\n        <input id=\"lufs-datei\" type=\"file\" accept=\"audio\/*,.wav,.wave,.aif,.aiff,.aifc,.flac,.mp3,.m4a,.mp4,.aac,.ogg,.oga,.opus\" hidden><\/p>\n<p class=\"lufs-klein\" style=\"margin: 12px 0 0;\">WAV und AIFF werden Abtastwert f\u00fcr Abtastwert gelesen, FLAC, MP3, AAC, Ogg und Opus \u00fcber den Decoder des Browsers.<\/p>\n<\/p><\/div>\n<div class=\"lufs-klein\" style=\"display: flex; flex-wrap: wrap; gap: 10px 24px; align-items: center; margin: 14px 0 0;\">\n        <label style=\"display: inline-flex; gap: 8px; align-items: center; cursor: pointer;\"><input id=\"lufs-dualmono\" type=\"checkbox\" checked> Mono als Dual-Mono messen (wie \u00fcber zwei Lautsprecher)<\/label><br \/>\n        <label style=\"display: inline-flex; gap: 8px; align-items: center;\">Eigener Zielwert <input id=\"lufs-ziel\" type=\"number\" step=\"0.5\" min=\"-40\" max=\"0\" value=\"-14\" style=\"width: 88px; padding: 6px 8px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px;\"> LUFS<\/label>\n    <\/div>\n<div id=\"lufs-status\" style=\"margin-top: 16px;\" hidden>\n<div id=\"lufs-status-text\" class=\"lufs-klein\" style=\"margin-bottom: 6px;\"><\/div>\n<div class=\"lufs-balken\">\n<div id=\"lufs-fortschritt\"><\/div>\n<\/div><\/div>\n<div id=\"lufs-ausgabe\" style=\"margin-top: 18px;\" aria-live=\"polite\"><\/div>\n<div class=\"lufs-h\">Was die Werte bedeuten<\/div>\n<p class=\"lufs-klein\" style=\"margin: 0 0 6px;\">Integrierte Lautheit: Mittelwert \u00fcber die ganze Datei, K-gewichtet und an zwei Schwellen gegatet (absolut \u221270 LUFS, relativ 10 LU unter dem Mittel). Auf diesem Wert beruht die Normalisierung.<\/p>\n<p class=\"lufs-klein\" style=\"margin: 0 0 6px;\">True Peak: h\u00f6chster Pegel des Signals zwischen den Abtastwerten, ermittelt durch vierfache \u00dcberabtastung. Er liegt oft \u00fcber dem Sample Peak.<\/p>\n<p class=\"lufs-klein\" style=\"margin: 0 0 6px;\">Lautheitsumfang (LRA): Abstand zwischen dem 10. und dem 95. Perzentil der Short-term-Werte nach EBU Tech 3342, ein Ma\u00df f\u00fcr die Dynamik \u00fcber die Zeit.<\/p>\n<p class=\"lufs-klein\" style=\"margin: 0 0 6px;\">Momentary und Short-term: Lautheit in gleitenden Fenstern von 0,4 und 3 Sekunden, hier im 10-ms-Raster gesucht.<\/p>\n<p class=\"lufs-klein\" style=\"margin: 0 0 6px;\">PLR: Abstand zwischen True Peak und integrierter Lautheit. Ein kleiner Wert zeigt stark verdichtetes Material.<\/p>\n<p class=\"lufs-klein\" style=\"margin: 20px 0 0;\">Quellen:<br \/>\n        <a href=\"https:\/\/www.itu.int\/rec\/R-REC-BS.1770\" rel=\"noopener\">ITU-R BS.1770-5<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/tech.ebu.ch\/publications\/r128\" rel=\"noopener\">EBU R 128<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/tech.ebu.ch\/publications\/tech3341\" rel=\"noopener\">EBU Tech 3341<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/tech.ebu.ch\/publications\/tech3342\" rel=\"noopener\">EBU Tech 3342<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/support.spotify.com\/us\/artists\/article\/loudness-normalization\/\" rel=\"noopener\">Spotify for Artists<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/aes.org\/wp-content\/uploads\/2024\/01\/20210924_TD1008_v3.13.pdf\" rel=\"noopener\">AES TD1008<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/www.apple.com\/apple-music\/apple-digital-masters\/docs\/apple-digital-masters.pdf\" rel=\"noopener\">Apple Digital Masters<\/a>\n    <\/p>\n<p class=\"lufs-klein\" style=\"font-size: 0.8rem; margin: 14px 0 0;\">Die Datei wird im Browser gelesen und gemessen und verl\u00e4sst das Ger\u00e4t nicht. Gemessen wird nach ITU-R BS.1770-5 (K-Gewichtung, Gating, vierfache \u00dcberabtastung f\u00fcr True Peak) sowie EBU Tech 3341 und 3342.<\/p>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    const T = {\"einleitung\":\"Streamingdienste spielen einen Titel nicht so laut ab, wie er gemastert wurde, sondern gleichen ihn an einen Zielwert an. Ein laut gemasterter Titel wird leiser gestellt, ein leiser nur so weit angehoben, wie der Spitzenpegel Platz l\u00e4sst. Das Messger\u00e4t ermittelt die Werte, auf denen diese Entscheidung beruht, und rechnet die Normalisierung f\u00fcr Spotify und nach den Empfehlungen von AES und EBU nach.\",\"zone_titel\":\"Audiodatei hier ablegen\",\"knopf_datei\":\"Audiodatei w\u00e4hlen\",\"zone_hinweis\":\"WAV und AIFF werden Abtastwert f\u00fcr Abtastwert gelesen, FLAC, MP3, AAC, Ogg und Opus \u00fcber den Decoder des Browsers.\",\"dualmono\":\"Mono als Dual-Mono messen (wie \u00fcber zwei Lautsprecher)\",\"eigenes_ziel\":\"Eigener Zielwert\",\"grenze\":\"Die Datei wird im Browser gelesen und gemessen und verl\u00e4sst das Ger\u00e4t nicht. Gemessen wird nach ITU-R BS.1770-5 (K-Gewichtung, Gating, vierfache \u00dcberabtastung f\u00fcr True Peak) sowie EBU Tech 3341 und 3342.\",\"dezimal\":\",\",\"status_lesen\":\"Datei wird gelesen \u2026\",\"status_dekodieren\":\"Audio wird dekodiert \u2026\",\"status_messen\":\"Messung l\u00e4uft: {prozent} %\",\"fehler_dekodieren\":\"Die Datei lie\u00df sich nicht dekodieren. Entweder ist sie besch\u00e4digt, oder der Browser unterst\u00fctzt das Format nicht; WAV funktioniert in jedem Browser.\",\"fehler_webaudio\":\"Dieser Browser bietet kein Web Audio. WAV- und AIFF-Dateien lassen sich trotzdem messen.\",\"fehler_speicher\":\"F\u00fcr diese Datei reichte der Arbeitsspeicher des Browsers nicht. Mit einer k\u00fcrzeren Datei oder einem Desktop-Browser klappt es.\",\"fehler_leer\":\"Die Datei enth\u00e4lt keine Audiodaten.\",\"fehler_format\":\"Diese WAV- oder AIFF-Variante wird nicht unterst\u00fctzt ({detail}).\",\"kanal_mono\":\"Mono\",\"kanal_stereo\":\"Stereo\",\"kanal_mehr\":\"{anzahl} Kan\u00e4le\",\"bit\":\"{bits} Bit\",\"bit_float\":\"{bits} Bit Gleitkomma\",\"k_integriert\":\"Integrierte Lautheit\",\"k_truepeak\":\"True Peak (max.)\",\"k_lra\":\"Lautheitsumfang (LRA)\",\"k_mmax\":\"Momentary max.\",\"k_smax\":\"Short-term max.\",\"k_samplepeak\":\"Sample Peak (max.)\",\"k_plr\":\"Peak to Loudness (PLR)\",\"h_befunde\":\"Befunde\",\"h_normalisierung\":\"Normalisierung beim Abspielen\",\"h_verlauf\":\"Lautheitsverlauf\",\"h_erklaerung\":\"Was die Werte bedeuten\",\"befund_tp_ueber_null\":\"True Peak \u00fcber 0 dBTP ({tp} dBTP) || Zwischen den Abtastwerten liegt das Signal \u00fcber Vollaussteuerung. Beim Wandeln in analog und beim Kodieren nach AAC oder Ogg Vorbis entstehen daraus Verzerrungen.\",\"befund_tp_ueber_eins\":\"True Peak \u00fcber \u22121 dBTP ({tp} dBTP) || Spotify, die AES-Empfehlung TD1008 und EBU R 128 nennen \u22121 dBTP als Obergrenze, weil verlustbehaftete Kodierung Spitzen weiter anhebt.\",\"befund_spotify_zwei\":\"Lauter als \u221214 LUFS bei True Peak \u00fcber \u22122 dBTP || F\u00fcr Master, die lauter als \u221214 LUFS sind, empfiehlt Spotify einen True Peak unter \u22122 dBTP.\",\"befund_voll\":\"{anzahl} Abtastwerte bei 0 dBFS oder dar\u00fcber || Werte auf Vollaussteuerung sind ein Hinweis auf Clipping im Master oder in der Kodierung.\",\"befund_kurz_lra\":\"K\u00fcrzer als 60 Sekunden || Nach EBU Tech 3341 gilt der Lautheitsumfang in den ersten 60 Sekunden als noch nicht stabil.\",\"befund_zu_kurz_s\":\"K\u00fcrzer als 3 Sekunden || F\u00fcr Short-term-Lautheit und Lautheitsumfang ist die Datei zu kurz.\",\"befund_zu_kurz_i\":\"K\u00fcrzer als 400 Millisekunden || F\u00fcr eine integrierte Lautheit ist die Datei zu kurz.\",\"befund_still\":\"Keine Lautheit messbar || Alle Messbl\u00f6cke liegen unter der absoluten Schwelle von \u221270 LUFS.\",\"befund_dualmono\":\"Mono als Dual-Mono gemessen || Der Kanal z\u00e4hlt doppelt, wie bei der Wiedergabe \u00fcber zwei Lautsprecher. Als einzelner Kanal gemessen l\u00e4ge die Lautheit 3,01 LU tiefer.\",\"befund_rate_unbekannt\":\"Abtastrate nicht im Dateikopf gefunden || Der Browser hat die Datei mit {rate} Hz dekodiert. Die Lautheit \u00e4ndert das kaum; der Sample Peak bezieht sich dann auf das umgerechnete Signal.\",\"befund_gut\":\"Spitzenpegel mit Abstand || True Peak bei {tp} dBTP: innerhalb der Empfehlungen von Spotify, AES und EBU.\",\"spotify_satz_leiser\":\"In der Standardeinstellung spielt Spotify diesen Titel um {db} dB leiser ab.\",\"spotify_satz_lauter\":\"In der Standardeinstellung hebt Spotify diesen Titel um {db} dB an.\",\"spotify_satz_gleich\":\"In der Standardeinstellung spielt Spotify diesen Titel praktisch unver\u00e4ndert ab.\",\"sp_vorgabe\":\"Vorgabe\",\"sp_ziel\":\"Ziel\",\"sp_aenderung\":\"Pegel\u00e4nderung\",\"sp_ergebnis\":\"Ergebnis\",\"sp_tp\":\"True Peak danach\",\"sp_hinweis\":\"Hinweis\",\"ziel_spotify_normal\":\"Spotify, Normal\",\"ziel_spotify_laut\":\"Spotify, Laut\",\"ziel_spotify_leise\":\"Spotify, Leise\",\"ziel_aes_titel\":\"AES TD1008, einzelner Titel\",\"ziel_aes_album\":\"AES TD1008, lautester Albumtitel\",\"ziel_ebu\":\"EBU R 128 (Rundfunk)\",\"ziel_eigen\":\"Eigener Zielwert\",\"norm_begrenzt\":\"Anhebung begrenzt: 1 dB Headroom unter dem True Peak\",\"norm_kein_platz\":\"keine Anhebung, der True Peak l\u00e4sst keinen Platz\",\"norm_limiter\":\"Limiter bei \u22121 dB greift\",\"norm_ueber\":\"True Peak danach \u00fcber \u22121 dBTP\",\"apple_hinweis\":\"Apple Music nennt keinen Zielwert: Laut Apples Dokument \u201eApple Digital Masters\u201c stellt Sound Check Titel lauter oder leiser, und Apple empfiehlt mindestens 1 dB Headroom. Spotify normalisiert Alben als Ganzes, und die AES empfiehlt dasselbe; ein ruhiger Albumtitel bleibt dann ruhig.\",\"verlauf_hinweis\":\"Short-term-Lautheit (3 s) im 100-ms-Raster. Gestrichelt: integrierte Lautheit, gepunktet: eigener Zielwert.\",\"erkl_integriert\":\"Integrierte Lautheit: Mittelwert \u00fcber die ganze Datei, K-gewichtet und an zwei Schwellen gegatet (absolut \u221270 LUFS, relativ 10 LU unter dem Mittel). Auf diesem Wert beruht die Normalisierung.\",\"erkl_truepeak\":\"True Peak: h\u00f6chster Pegel des Signals zwischen den Abtastwerten, ermittelt durch vierfache \u00dcberabtastung. Er liegt oft \u00fcber dem Sample Peak.\",\"erkl_lra\":\"Lautheitsumfang (LRA): Abstand zwischen dem 10. und dem 95. Perzentil der Short-term-Werte nach EBU Tech 3342, ein Ma\u00df f\u00fcr die Dynamik \u00fcber die Zeit.\",\"erkl_momentan\":\"Momentary und Short-term: Lautheit in gleitenden Fenstern von 0,4 und 3 Sekunden, hier im 10-ms-Raster gesucht.\",\"erkl_plr\":\"PLR: Abstand zwischen True Peak und integrierter Lautheit. Ein kleiner Wert zeigt stark verdichtetes Material.\",\"knopf_kopieren\":\"Messwerte kopieren\",\"kopiert\":\"In die Zwischenablage kopiert\",\"quellen_titel\":\"Quellen\"};<\/p>\n<p>    \/\/ KERN-ANFANG ------------------------------------------------------------\n    \/\/ Messkern nach ITU-R BS.1770-5 (K-Gewichtung, Gating, True Peak) und\n    \/\/ EBU Tech 3341\/3342 (Momentary, Short-term, Loudness Range). Rein\n    \/\/ rechnend, ohne Dokument - derselbe Text laeuft im Browser und in Node.<\/p>\n<p>    \/\/ Tabelle 1 und 2 der Norm, gueltig fuer 48 kHz.\n    var ITU_48K = {\n        stufe1: { b: [1.53512485958697, -2.69169618940638, 1.19839281085285], a: [-1.69065929318241, 0.73248077421585] },\n        stufe2: { b: [1.0, -2.0, 1.0], a: [-1.99004745483398, 0.99007225036621] }\n    };<\/p>\n<p>    \/\/ Anhang 2: 4-fach ueberabtastendes FIR mit 48 Koeffizienten, vier Phasen.\n    var TP_PHASEN = [\n        [0.0017089843750, 0.0109863281250, -0.0196533203125, 0.0332031250000, -0.0594482421875, 0.1373291015625,\n         0.9721679687500, -0.1022949218750, 0.0476074218750, -0.0266113281250, 0.0148925781250, -0.0083007812500],\n        [-0.0291748046875, 0.0292968750000, -0.0517578125000, 0.0891113281250, -0.1665039062500, 0.4650878906250,\n         0.7797851562500, -0.2003173828125, 0.1015625000000, -0.0582275390625, 0.0330810546875, -0.0189208984375],\n        [-0.0189208984375, 0.0330810546875, -0.0582275390625, 0.1015625000000, -0.2003173828125, 0.7797851562500,\n         0.4650878906250, -0.1665039062500, 0.0891113281250, -0.0517578125000, 0.0292968750000, -0.0291748046875],\n        [-0.0083007812500, 0.0148925781250, -0.0266113281250, 0.0476074218750, -0.1022949218750, 0.9721679687500,\n         0.1373291015625, -0.0594482421875, 0.0332031250000, -0.0196533203125, 0.0109863281250, 0.0017089843750]\n    ];<\/p>\n<p>    var ABS_GATE = -70;      \/\/ LUFS, BS.1770 und Tech 3342\n    var REL_GATE_I = -10;    \/\/ LU, integrierte Lautheit\n    var REL_GATE_LRA = -20;  \/\/ LU, Loudness Range\n    var UNTERBLOECKE_JE_S = 100;  \/\/ 10-ms-Raster fuer M und S<\/p>\n<p>    \/\/ Die Norm nennt Koeffizienten nur fuer 48 kHz und verlangt fuer andere\n    \/\/ Raten \"denselben Frequenzgang\". Dafuer wird die bilineare Transformation\n    \/\/ umgekehrt: aus a1\/a2 folgen Vorverzerrung K und Guete Q, aus b0\/b1 die\n    \/\/ beiden Verstaerkungen des Kuhschwanzfilters. Mit K = tan(pi*f0\/fs) entsteht\n    \/\/ daraus fuer jede Rate dasselbe Filter.\n    function vorbild(stufe) {\n        var a1 = stufe.a[0], a2 = stufe.a[1];\n        var K2 = (1 + a1 + a2) \/ (1 - a1 + a2);\n        var K = Math.sqrt(K2);\n        var d = 4 \/ (1 - a1 + a2);\n        var kq = (1 - a2) * d \/ 2;\n        var vh = K2 - stufe.b[1] * d \/ 2;\n        return { f0: Math.atan(K) * 48000 \/ Math.PI, Q: K \/ kq, Vh: vh, Vb: (stufe.b[0] * d - vh - K2) \/ kq };\n    }\n    var VORBILD1 = vorbild(ITU_48K.stufe1);\n    var VORBILD2 = vorbild(ITU_48K.stufe2);<\/p>\n<p>    function kKoeffizienten(fs) {\n        var s = VORBILD1, K = Math.tan(Math.PI * s.f0 \/ fs), K2 = K * K, d = 1 + K \/ s.Q + K2;\n        var st1 = {\n            b: [(s.Vh + s.Vb * K \/ s.Q + K2) \/ d, 2 * (K2 - s.Vh) \/ d, (s.Vh - s.Vb * K \/ s.Q + K2) \/ d],\n            a: [2 * (K2 - 1) \/ d, (1 - K \/ s.Q + K2) \/ d]\n        };\n        s = VORBILD2; K = Math.tan(Math.PI * s.f0 \/ fs); K2 = K * K; d = 1 + K \/ s.Q + K2;\n        \/\/ Zaehler wie in Tabelle 2 unnormiert: die Konstante -0,691 ist auf\n        \/\/ genau diese Verstaerkung bezogen.\n        var st2 = { b: [1, -2, 1], a: [2 * (K2 - 1) \/ d, (1 - K \/ s.Q + K2) \/ d] };\n        return [st1, st2];\n    }<\/p>\n<p>    \/\/ Kanalgewichte nach Tabelle 3. Reihenfolge wie bei WAV und Web Audio:\n    \/\/ L R C LFE Ls Rs. LFE zaehlt nicht mit.\n    function kanalGewichte(anzahl, dualMono) {\n        if (anzahl === 1) { return [dualMono ? 2.0 : 1.0]; }\n        if (anzahl === 2) { return [1, 1]; }\n        if (anzahl === 3) { return [1, 1, 1]; }\n        if (anzahl === 4) { return [1, 1, 1.41, 1.41]; }\n        if (anzahl === 5) { return [1, 1, 1, 1.41, 1.41]; }\n        var g = [1, 1, 1, 0, 1.41, 1.41];\n        for (var i = 6; i < anzahl; i++) { g.push(1.41); }\n        return g;\n    }\n\n    function grenze(k, fs) { return Math.round(k * fs \/ UNTERBLOECKE_JE_S); }\n\n    function messungAnlegen(kanaele, fs, optionen) {\n        optionen = optionen || {};\n        var n = kanaele[0].length;\n        var nUnter = Math.floor(n * UNTERBLOECKE_JE_S \/ fs);\n        while (nUnter > 0 && grenze(nUnter, fs) > n) { nUnter--; }\n        while (grenze(nUnter + 1, fs) <= n) { nUnter++; }\n        var koeff = kKoeffizienten(fs);\n        return {\n            fs: fs, n: n, kanaele: kanaele, anzahl: kanaele.length,\n            gewichte: kanalGewichte(kanaele.length, optionen.dualMono !== false),\n            koeff: koeff, nUnter: nUnter, energieEnde: grenze(nUnter, fs),\n            unterE: new Float64Array(Math.max(nUnter, 1)),\n            kanal: 0, pos: 0, k: 0, akku: 0, z: [0, 0, 0, 0],\n            ring: new Float64Array(24), ringPos: 0,\n            tp: new Float64Array(kanaele.length), sp: new Float64Array(kanaele.length),\n            voll: new Float64Array(kanaele.length)\n        };\n    }\n\n    \/\/ Verarbeitet hoechstens \"budget\" Abtastwerte. Gibt true zurueck, wenn alle\n    \/\/ Kanaele durch sind. So kann die Oberflaeche zwischendurch Luft holen.\n    function messungSchritt(m, budget) {\n        var P0 = TP_PHASEN[0], P1 = TP_PHASEN[1], P2 = TP_PHASEN[2], P3 = TP_PHASEN[3];\n        while (budget > 0 && m.kanal < m.anzahl) {\n            var c = m.kanal, d = m.kanaele[c], g = m.gewichte[c];\n            var st1 = m.koeff[0], st2 = m.koeff[1];\n            var b10 = st1.b[0], b11 = st1.b[1], b12 = st1.b[2], a10 = st1.a[0], a11 = st1.a[1];\n            var b20 = st2.b[0], b21 = st2.b[1], b22 = st2.b[2], a20 = st2.a[0], a21 = st2.a[1];\n            var z1 = m.z[0], z2 = m.z[1], w1 = m.z[2], w2 = m.z[3];\n            var ring = m.ring, rp = m.ringPos;\n            var tp = m.tp[c], sp = m.sp[c], voll = m.voll[c];\n            var k = m.k, akku = m.akku, fs = m.fs, nUnter = m.nUnter;\n            var naechste = grenze(k + 1, fs);\n            var ende = Math.min(m.n, m.pos + budget);\n            var mitEnergie = g > 0;\n            for (var i = m.pos; i < ende; i++) {\n                var x = d[i];\n                var ax = x < 0 ? -x : x;\n                if (ax > sp) { sp = ax; }\n                if (ax >= 1) { voll++; }<\/p>\n<p>                var y1 = b10 * x + z1;\n                z1 = b11 * x - a10 * y1 + z2;\n                z2 = b12 * x - a11 * y1;\n                var y2 = b20 * y1 + w1;\n                w1 = b21 * y1 - a20 * y2 + w2;\n                w2 = b22 * y1 - a21 * y2;<\/p>\n<p>                if (k < nUnter) {\n                    akku += y2 * y2;\n                    if (i + 1 === naechste) {\n                        if (mitEnergie) { m.unterE[k] += g * akku; }\n                        akku = 0; k++; naechste = grenze(k + 1, fs);\n                        \/\/ Abklingende Filterzustaende nicht in den\n                        \/\/ Subnormalbereich laufen lassen - der ist langsam.\n                        if (z1 < 1e-30 &#038;&#038; z1 > -1e-30) { z1 = 0; }\n                        if (z2 < 1e-30 &#038;&#038; z2 > -1e-30) { z2 = 0; }\n                        if (w1 < 1e-30 &#038;&#038; w1 > -1e-30) { w1 = 0; }\n                        if (w2 < 1e-30 &#038;&#038; w2 > -1e-30) { w2 = 0; }\n                    }\n                }<\/p>\n<p>                \/\/ True Peak: Ringpuffer doppelt beschrieben, damit die zwoelf\n                \/\/ letzten Werte zusammenhaengend lesbar sind.\n                ring[rp] = x; ring[rp + 12] = x;\n                var o = rp + 12;\n                var s0 = 0, s1 = 0, s2 = 0, s3 = 0;\n                for (var t = 0; t < 12; t++) {\n                    var v = ring[o - t];\n                    s0 += P0[t] * v; s1 += P1[t] * v; s2 += P2[t] * v; s3 += P3[t] * v;\n                }\n                rp = rp === 11 ? 0 : rp + 1;\n                if (s0 < 0) { s0 = -s0; } if (s1 < 0) { s1 = -s1; } if (s2 < 0) { s2 = -s2; } if (s3 < 0) { s3 = -s3; }\n                if (s0 > tp) { tp = s0; } if (s1 > tp) { tp = s1; } if (s2 > tp) { tp = s2; } if (s3 > tp) { tp = s3; }\n            }\n            budget -= (ende - m.pos);\n            m.pos = ende; m.k = k; m.akku = akku;\n            m.z[0] = z1; m.z[1] = z2; m.z[2] = w1; m.z[3] = w2; m.ringPos = rp;\n            m.tp[c] = tp; m.sp[c] = sp; m.voll[c] = voll;\n            if (m.pos >= m.n) {\n                \/\/ Nachlauf: elf Nullen, damit auch die letzten Zwischenwerte zaehlen.\n                for (var nach = 0; nach < 12; nach++) {\n                    m.ring[m.ringPos] = 0; m.ring[m.ringPos + 12] = 0;\n                    var oo = m.ringPos + 12, q0 = 0, q1 = 0, q2 = 0, q3 = 0;\n                    for (var tt = 0; tt < 12; tt++) {\n                        var vv = m.ring[oo - tt];\n                        q0 += P0[tt] * vv; q1 += P1[tt] * vv; q2 += P2[tt] * vv; q3 += P3[tt] * vv;\n                    }\n                    m.ringPos = m.ringPos === 11 ? 0 : m.ringPos + 1;\n                    m.tp[c] = Math.max(m.tp[c], Math.abs(q0), Math.abs(q1), Math.abs(q2), Math.abs(q3));\n                }\n                m.tp[c] = Math.max(m.tp[c], m.sp[c]);\n                m.kanal++; m.pos = 0; m.k = 0; m.akku = 0;\n                m.z = [0, 0, 0, 0]; m.ring = new Float64Array(24); m.ringPos = 0;\n            }\n        }\n        return m.kanal >= m.anzahl;\n    }<\/p>\n<p>    function db(wert) { return wert > 0 ? 20 * Math.log10(wert) : -Infinity; }\n    function lautheit(energie, abtastwerte) {\n        return energie > 0 && abtastwerte > 0 ? -0.691 + 10 * Math.log10(energie \/ abtastwerte) : -Infinity;\n    }<\/p>\n<p>    \/\/ Fenster aus Unterbloecken: Laenge und Schritt in 10-ms-Einheiten.\n    function fensterWerte(m, laenge, schritt) {\n        var raus = [];\n        for (var k = 0; k + laenge <= m.nUnter; k += schritt) {\n            var e = 0;\n            for (var j = k; j < k + laenge; j++) { e += m.unterE[j]; }\n            raus.push(lautheit(e, grenze(k + laenge, m.fs) - grenze(k, m.fs)));\n        }\n        return raus;\n    }\n\n    function integriert(m) {\n        var bloecke = [];\n        for (var k = 0; k + 40 <= m.nUnter; k += 10) {\n            var e = 0;\n            for (var j = k; j < k + 40; j++) { e += m.unterE[j]; }\n            var anz = grenze(k + 40, m.fs) - grenze(k, m.fs);\n            bloecke.push({ l: lautheit(e, anz), z: e \/ anz });\n        }\n        if (!bloecke.length) { return { wert: null, bloecke: 0, gezaehlt: 0 }; }\n        var summe = 0, anzahl = 0, i;\n        for (i = 0; i < bloecke.length; i++) {\n            if (bloecke[i].l > ABS_GATE) { summe += bloecke[i].z; anzahl++; }\n        }\n        if (!anzahl) { return { wert: -Infinity, bloecke: bloecke.length, gezaehlt: 0 }; }\n        var relativ = -0.691 + 10 * Math.log10(summe \/ anzahl) + REL_GATE_I;\n        summe = 0; anzahl = 0;\n        for (i = 0; i < bloecke.length; i++) {\n            if (bloecke[i].l > ABS_GATE && bloecke[i].l > relativ) { summe += bloecke[i].z; anzahl++; }\n        }\n        return { wert: -0.691 + 10 * Math.log10(summe \/ anzahl), bloecke: bloecke.length, gezaehlt: anzahl, schwelle: relativ };\n    }<\/p>\n<p>    \/\/ Tech 3342, Abschnitt 5, fast zeilengleich.\n    function loudnessRange(kurzzeit) {\n        var abs = kurzzeit.filter(function (s) { return s >= ABS_GATE; });\n        if (!abs.length) { return null; }\n        var leistung = 0;\n        abs.forEach(function (s) { leistung += Math.pow(10, s \/ 10); });\n        var schwelle = 10 * Math.log10(leistung \/ abs.length) + REL_GATE_LRA;\n        var rel = abs.filter(function (s) { return s >= schwelle; }).sort(function (a, b) { return a - b; });\n        var n = rel.length;\n        if (!n) { return null; }\n        var tief = rel[Math.round((n - 1) * 10 \/ 100)];\n        var hoch = rel[Math.round((n - 1) * 95 \/ 100)];\n        return { wert: hoch - tief, p10: tief, p95: hoch, schwelle: schwelle, werte: n };\n    }<\/p>\n<p>    function maxWert(liste) {\n        var m = -Infinity;\n        for (var i = 0; i < liste.length; i++) { if (liste[i] > m) { m = liste[i]; } }\n        return liste.length ? m : null;\n    }<\/p>\n<p>    function messungAuswerten(m) {\n        var momentan = fensterWerte(m, 40, 1);\n        var kurz = fensterWerte(m, 300, 1);\n        var kurzVerlauf = fensterWerte(m, 300, 10);\n        var I = integriert(m);\n        var tpMax = 0, spMax = 0, voll = 0;\n        for (var c = 0; c < m.anzahl; c++) {\n            tpMax = Math.max(tpMax, m.tp[c]); spMax = Math.max(spMax, m.sp[c]); voll += m.voll[c];\n        }\n        return {\n            fs: m.fs, kanaele: m.anzahl, dauer: m.n \/ m.fs,\n            integriert: I.wert, bloecke: I.bloecke, bloeckeGezaehlt: I.gezaehlt,\n            momentanMax: maxWert(momentan), kurzMax: maxWert(kurz),\n            lra: kurzVerlauf.length ? loudnessRange(kurzVerlauf) : null,\n            kurzVerlauf: kurzVerlauf,\n            truePeak: db(tpMax), samplePeak: db(spMax),\n            truePeakKanal: Array.prototype.map.call(m.tp, db),\n            samplePeakKanal: Array.prototype.map.call(m.sp, db),\n            vollAusgesteuert: voll\n        };\n    }\n\n    function analysiere(kanaele, fs, optionen) {\n        var m = messungAnlegen(kanaele, fs, optionen);\n        while (!messungSchritt(m, 1 << 20)) { \/* weiter *\/ }\n        return messungAuswerten(m);\n    }\n\n    \/\/ Normalisierung eines Dienstes nachrechnen. Nur belegte Regeln:\n    \/\/  - Spotify Normal\/Leise: Absenken immer; Anheben nur so weit, dass 1 dB\n    \/\/    Headroom bleibt (Beispiel der Hilfeseite: -20 LUFS, TP -5 -> -16 LUFS).\n    \/\/  - Spotify Laut: Zielwert \"regardless of maximum True Peak\", Limiter\n    \/\/    setzt bei -1 dB ein.\n    \/\/  - alles andere: reine Pegelverschiebung auf den Zielwert.\n    function normalisierung(I, tp, ziel, regel) {\n        if (I === null || !isFinite(I)) { return null; }\n        var wunsch = ziel - I, gain = wunsch, begrenzt = false, limiter = false;\n        if (regel === 'headroom' && wunsch > 0) {\n            var platz = -1 - tp;\n            gain = Math.max(0, Math.min(wunsch, platz));\n            begrenzt = gain < wunsch - 1e-9;\n        }\n        if (regel === 'limiter' &#038;&#038; tp + gain > -1) { limiter = true; }\n        return {\n            gain: gain, ergebnis: I + gain, tpDanach: tp + gain, begrenzt: begrenzt,\n            limiter: limiter, ueberEins: !limiter && tp + gain > -1\n        };\n    }\n    \/\/ KERN-ENDE --------------------------------------------------------------<\/p>\n<p>    function text(schluessel, daten) {\n        var t = T[schluessel];\n        if (typeof t !== 'string') { return null; }\n        return t.replace(\/\\{([a-z_0-9]+)\\}\/g, function (m, k) {\n            return (daten && daten[k] !== undefined && daten[k] !== null) ? String(daten[k]) : m;\n        });\n    }<\/p>\n<p>    \/\/ Zahl mit Komma oder Punkt je Sprache und echtem Minuszeichen.\n    function zahl(wert, stellen, plus) {\n        if (wert === null || wert === undefined || (typeof wert === 'number' && isNaN(wert))) { return '\u2013'; }\n        if (wert === -Infinity) { return '\u2212\u221e'; }\n        if (wert === Infinity) { return '+\u221e'; }\n        var betrag = Math.abs(wert).toFixed(stellen);\n        var null_ = parseFloat(betrag) === 0;\n        var s = betrag.replace('.', T.dezimal);\n        if (wert < 0 &#038;&#038; !null_) { return '\u2212' + s; }\n        if (plus &#038;&#038; wert > 0 && !null_) { return '+' + s; }\n        return s;\n    }\n    function zahlAscii(wert, stellen) {\n        if (wert === null || wert === undefined || !isFinite(wert)) { return wert === -Infinity ? '-inf' : '-'; }\n        return wert.toFixed(stellen);\n    }<\/p>\n<p>    \/\/ --- Dateien lesen -------------------------------------------------------\n    \/\/ WAV und AIFF werden selbst gelesen: so bleibt jeder Abtastwert, wie er in\n    \/\/ der Datei steht, und der Browser rechnet nicht auf seine eigene Rate um.\n    function ascii(dv, o, l) {\n        var s = '';\n        for (var i = 0; i < l &#038;&#038; o + i < dv.byteLength; i++) { s += String.fromCharCode(dv.getUint8(o + i)); }\n        return s;\n    }\n\n    function pcmZuKanaele(puffer, start, laenge, kanaele, bytes, art, kleinEndian) {\n        var rahmen = Math.floor(laenge \/ (bytes * kanaele));\n        var raus = [], c, i;\n        for (c = 0; c < kanaele; c++) { raus.push(new Float32Array(rahmen)); }\n        var ausgerichtet = kleinEndian &#038;&#038; (art === 'int' &#038;&#038; (bytes === 2 || bytes === 4) || art === 'float');\n        if (ausgerichtet) {\n            var stueck = puffer.slice(start, start + rahmen * bytes * kanaele);\n            var ansicht = art === 'float' ? (bytes === 4 ? new Float32Array(stueck) : new Float64Array(stueck))\n                : (bytes === 2 ? new Int16Array(stueck) : new Int32Array(stueck));\n            var teiler = art === 'float' ? 1 : (bytes === 2 ? 32768 : 2147483648);\n            for (c = 0; c < kanaele; c++) {\n                var ziel = raus[c];\n                for (i = 0; i < rahmen; i++) { ziel[i] = ansicht[i * kanaele + c] \/ teiler; }\n            }\n            return raus;\n        }\n        var dv = new DataView(puffer, start, rahmen * bytes * kanaele);\n        var u8 = new Uint8Array(puffer, start, rahmen * bytes * kanaele);\n        var o = 0;\n        for (i = 0; i < rahmen; i++) {\n            for (c = 0; c < kanaele; c++) {\n                var v;\n                if (art === 'float') { v = bytes === 4 ? dv.getFloat32(o, kleinEndian) : dv.getFloat64(o, kleinEndian); }\n                else if (bytes === 1) { v = kleinEndian ? (u8[o] - 128) \/ 128 : dv.getInt8(o) \/ 128; }\n                else if (bytes === 2) { v = dv.getInt16(o, kleinEndian) \/ 32768; }\n                else if (bytes === 3) {\n                    var w = kleinEndian ? (u8[o] | (u8[o + 1] << 8) | (u8[o + 2] << 16)) : ((u8[o] << 16) | (u8[o + 1] << 8) | u8[o + 2]);\n                    if (w &#038; 0x800000) { w -= 0x1000000; }\n                    v = w \/ 8388608;\n                } else { v = dv.getInt32(o, kleinEndian) \/ 2147483648; }\n                raus[c][i] = v;\n                o += bytes;\n            }\n        }\n        return raus;\n    }\n\n    function wavLesen(puffer) {\n        var dv = new DataView(puffer), n = puffer.byteLength, o = 12, fmt = null, ds64 = null;\n        while (o + 8 <= n) {\n            var id = ascii(dv, o, 4), groesse = dv.getUint32(o + 4, true);\n            if (id === 'ds64' &#038;&#038; groesse >= 16) {\n                ds64 = dv.getUint32(o + 16, true) + dv.getUint32(o + 20, true) * 4294967296;\n            } else if (id === 'fmt ') {\n                var tag = dv.getUint16(o + 8, true);\n                if (tag === 0xFFFE && groesse >= 26) { tag = dv.getUint16(o + 32, true); }\n                fmt = { tag: tag, kanaele: dv.getUint16(o + 10, true), rate: dv.getUint32(o + 12, true), bits: dv.getUint16(o + 22, true) };\n            } else if (id === 'data') {\n                if (!fmt) { return { fehler: 'fmt' }; }\n                var laenge = (groesse === 0xFFFFFFFF && ds64 !== null) ? ds64 : groesse;\n                laenge = Math.min(laenge, n - o - 8);\n                var bytes = fmt.bits \/ 8;\n                var art = fmt.tag === 1 ? 'int' : (fmt.tag === 3 ? 'float' : null);\n                if (!art || (art === 'int' && [1, 2, 3, 4].indexOf(bytes) === -1) || (art === 'float' && bytes !== 4 && bytes !== 8) || !fmt.kanaele) {\n                    return { fehler: 'tag ' + fmt.tag + ', ' + fmt.bits + ' bit' };\n                }\n                return { format: 'WAV', rate: fmt.rate, bits: fmt.bits, gleitkomma: art === 'float',\n                    kanaele: pcmZuKanaele(puffer, o + 8, laenge, fmt.kanaele, bytes, art, true) };\n            }\n            if (groesse === 0xFFFFFFFF) { break; }\n            o += 8 + groesse + (groesse % 2);\n        }\n        return { fehler: 'data' };\n    }<\/p>\n<p>    function erweitert80(dv, o) {\n        var exp = dv.getUint16(o, false) & 0x7FFF, hi = dv.getUint32(o + 2, false), lo = dv.getUint32(o + 6, false);\n        if (!exp && !hi && !lo) { return 0; }\n        return (hi * 4294967296 + lo) * Math.pow(2, exp - 16383 - 63);\n    }<\/p>\n<p>    function aiffLesen(puffer, aifc) {\n        var dv = new DataView(puffer), n = puffer.byteLength, o = 12, comm = null;\n        while (o + 8 <= n) {\n            var id = ascii(dv, o, 4), groesse = dv.getUint32(o + 4, false);\n            if (id === 'COMM') {\n                comm = { kanaele: dv.getUint16(o + 8, false), bits: dv.getUint16(o + 14, false), rate: Math.round(erweitert80(dv, o + 16)), art: 'NONE' };\n                if (aifc &#038;&#038; groesse >= 22) { comm.art = ascii(dv, o + 26, 4); }\n            } else if (id === 'SSND') {\n                if (!comm) { return { fehler: 'COMM' }; }\n                var versatz = dv.getUint32(o + 8, false), start = o + 16 + versatz;\n                var laenge = Math.min(groesse - 8 - versatz, n - start);\n                var art = comm.art, bytes = Math.ceil(comm.bits \/ 8);\n                if (art === 'NONE' || art === 'twos') {\n                    return { format: 'AIFF', rate: comm.rate, bits: comm.bits, kanaele: pcmZuKanaele(puffer, start, laenge, comm.kanaele, bytes, 'int', false) };\n                }\n                if (art === 'sowt') {\n                    return { format: 'AIFF', rate: comm.rate, bits: comm.bits, kanaele: pcmZuKanaele(puffer, start, laenge, comm.kanaele, bytes, 'int', true) };\n                }\n                if (art === 'fl32' || art === 'FL32' || art === 'fl64' || art === 'FL64') {\n                    return { format: 'AIFF', rate: comm.rate, bits: art.slice(2) === '32' ? 32 : 64, gleitkomma: true,\n                        kanaele: pcmZuKanaele(puffer, start, laenge, comm.kanaele, art.slice(2) === '32' ? 4 : 8, 'float', false) };\n                }\n                return { fehler: art };\n            }\n            o += 8 + groesse + (groesse % 2);\n        }\n        return { fehler: 'SSND' };\n    }<\/p>\n<p>    \/\/ Abtastrate komprimierter Formate aus dem Kopf, damit der Decoder des\n    \/\/ Browsers nicht auf eine fremde Rate umrechnet.\n    function kopfLesen(puffer) {\n        var n = puffer.byteLength, dv = new DataView(puffer);\n        var b = new Uint8Array(puffer, 0, Math.min(n, 4 << 20));\n        var k4 = ascii(dv, 0, 4);\n        if (k4 === 'fLaC' &#038;&#038; n > 22) {\n            return { format: 'FLAC', rate: (b[18] << 12) | (b[19] << 4) | (b[20] >> 4), kanaeleKopf: ((b[20] >> 1) & 7) + 1, bits: (((b[20] & 1) << 4) | (b[21] >> 4)) + 1 };\n        }\n        if (k4 === 'OggS' && n > 64) {\n            var d = 27 + b[26];\n            if (ascii(dv, d + 1, 6) === 'vorbis' && b[d] === 1) { return { format: 'Ogg Vorbis', rate: dv.getUint32(d + 12, true), kanaeleKopf: b[d + 11] }; }\n            if (ascii(dv, d, 8) === 'OpusHead') { return { format: 'Opus', rate: 48000, kanaeleKopf: b[d + 9] }; }\n            if (ascii(dv, d + 1, 4) === 'FLAC' && ascii(dv, d + 9, 4) === 'fLaC') {\n                var s = d + 17;\n                return { format: 'Ogg FLAC', rate: (b[s + 10] << 12) | (b[s + 11] << 4) | (b[s + 12] >> 4) };\n            }\n            return { format: 'Ogg', rate: null };\n        }\n        if (ascii(dv, 4, 4) === 'ftyp') {\n            var suchen = function (a, bb, c, dd) {\n                for (var i = 4; i + 36 < b.length; i++) {\n                    if (b[i] === a &#038;&#038; b[i + 1] === bb &#038;&#038; b[i + 2] === c &#038;&#038; b[i + 3] === dd) { return i - 4; }\n                }\n                return -1;\n            };\n            var box = suchen(0x6D, 0x70, 0x34, 0x61), name = 'AAC';\n            if (box < 0) { box = suchen(0x61, 0x6C, 0x61, 0x63); name = 'ALAC'; }\n            var rate = box >= 0 ? dv.getUint16(box + 32, false) : 0;\n            return { format: name, rate: rate >= 8000 ? rate : null };\n        }\n        var start = 0;\n        if (ascii(dv, 0, 3) === 'ID3' && n > 10) {\n            start = 10 + ((b[6] & 0x7f) << 21 | (b[7] &#038; 0x7f) << 14 | (b[8] &#038; 0x7f) << 7 | (b[9] &#038; 0x7f)) + ((b[5] &#038; 0x10) ? 10 : 0);\n        }\n        var bis = Math.min(b.length - 4, start + 65536);\n        for (var j = start; j < bis; j++) {\n            if (b[j] !== 0xFF || (b[j + 1] &#038; 0xE0) !== 0xE0) { continue; }\n            var version = (b[j + 1] >> 3) & 3, schicht = (b[j + 1] >> 1) & 3;\n            if (schicht === 0 && (b[j + 1] & 0xF6) === 0xF0) {\n                var idx = (b[j + 2] >> 2) & 15;\n                var raten = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];\n                if (idx < raten.length) { return { format: 'AAC', rate: raten[idx] }; }\n                continue;\n            }\n            var ri = (b[j + 2] >> 2) & 3, bri = b[j + 2] >> 4;\n            if (version === 1 || schicht === 0 || ri === 3 || bri === 15 || bri === 0) { continue; }\n            var basis = [44100, 48000, 32000][ri];\n            return { format: schicht === 1 ? 'MP3' : 'MPEG', rate: version === 3 ? basis : (version === 2 ? basis \/ 2 : basis \/ 4), kanaeleKopf: (b[j + 3] >> 6) === 3 ? 1 : 2 };\n        }\n        return { format: null, rate: null };\n    }<\/p>\n<p>    function dekodieren(puffer, rate) {\n        return new Promise(function (ok, fehler) {\n            var Ctx = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n            if (!Ctx) { fehler(new Error('webaudio')); return; }\n            var ctx;\n            try { ctx = new Ctx(1, 1, rate); } catch (e) { ctx = new Ctx(1, 1, 48000); }\n            var erledigt = false;\n            function gut(buf) { if (!erledigt) { erledigt = true; ok(buf); } }\n            function schlecht(e) { if (!erledigt) { erledigt = true; fehler(e || new Error('dekodieren')); } }\n            try {\n                var p = ctx.decodeAudioData(puffer, gut, schlecht);\n                if (p && typeof p.then === 'function') { p.then(gut, schlecht); }\n            } catch (e) { schlecht(e); }\n        });\n    }<\/p>\n<p>    \/\/ Einlesen: gibt {format, rate, bits, kanaele[], geschaetzt} zurueck.\n    function einlesen(puffer) {\n        var dv = new DataView(puffer), k4 = ascii(dv, 0, 4), art = ascii(dv, 8, 4);\n        if ((k4 === 'RIFF' || k4 === 'RF64' || k4 === 'BW64') && art === 'WAVE') {\n            var w = wavLesen(puffer);\n            if (!w.fehler) { return Promise.resolve(w); }\n            return Promise.reject({ format: w.fehler });\n        }\n        if (k4 === 'FORM' && (art === 'AIFF' || art === 'AIFC')) {\n            var a = aiffLesen(puffer, art === 'AIFC');\n            if (!a.fehler) { return Promise.resolve(a); }\n            \/\/ komprimiertes AIFC: vielleicht kann es der Browser\n        }\n        var kopf = kopfLesen(puffer);\n        var rate = kopf.rate && kopf.rate >= 3000 && kopf.rate <= 768000 ? kopf.rate : 48000;\n        return dekodieren(puffer, rate).then(function (buf) {\n            var kanaele = [];\n            for (var c = 0; c < buf.numberOfChannels; c++) { kanaele.push(buf.getChannelData(c)); }\n            return { format: kopf.format, rate: buf.sampleRate, bits: kopf.bits || null, kanaele: kanaele, geschaetzt: !kopf.rate };\n        });\n    }\n\n    \/\/ --- Messen in Scheiben ------------------------------------------------\n    var tick = typeof MessageChannel !== 'undefined' ? new MessageChannel() : null, schlange = [];\n    if (tick) { tick.port1.onmessage = function () { var f = schlange.shift(); if (f) { f(); } }; }\n    function spaeter(f) { if (tick) { schlange.push(f); tick.port2.postMessage(0); } else { setTimeout(f, 0); } }\n\n    function messenAsync(kanaele, fs, optionen, fortschritt) {\n        return new Promise(function (ok, fehler) {\n            var m;\n            try { m = messungAnlegen(kanaele, fs, optionen); } catch (e) { fehler(e); return; }\n            var gesamt = m.n * m.anzahl || 1;\n            function schritt() {\n                try {\n                    var fertig = messungSchritt(m, 1 << 18);\n                    if (fortschritt) { fortschritt(Math.min(1, (m.kanal * m.n + m.pos) \/ gesamt)); }\n                    if (fertig) { ok(messungAuswerten(m)); } else { spaeter(schritt); }\n                } catch (e) { fehler(e); }\n            }\n            spaeter(schritt);\n        });\n    }\n\n    \/\/ --- Bewertung -----------------------------------------------------------\n    var ZIELE = [\n        { id: 'spotify_normal', ziel: -14, regel: 'headroom' },\n        { id: 'spotify_laut', ziel: -11, regel: 'limiter' },\n        { id: 'spotify_leise', ziel: -19, regel: 'headroom' },\n        { id: 'aes_titel', ziel: -16, regel: 'frei' },\n        { id: 'aes_album', ziel: -14, regel: 'frei' },\n        { id: 'ebu', ziel: -23, regel: 'frei' },\n        { id: 'eigen', ziel: null, regel: 'frei' }\n    ];\n\n    \/\/ Geurteilt wird ueber den Wert, der auch angezeigt wird (eine Nachkommastelle):\n    \/\/ sonst steht bei -0,98 dBTP \"-1,0 dBTP\" neben dem Hinweis \"ueber -1 dBTP\".\n    function r1(x) { return (typeof x === 'number' &#038;&#038; isFinite(x)) ? Math.round(x * 10) \/ 10 : x; }\n\n    function befunde(e, info) {\n        var b = [];\n        function dazu(key, stufe, daten) { b.push({ key: key, stufe: stufe, daten: daten || {} }); }\n        var tp = r1(e.truePeak), I = r1(e.integriert);\n        if (e.dauer < 0.4) { dazu('zu_kurz_i', 'info'); }\n        else if (e.dauer < 3) { dazu('zu_kurz_s', 'info'); }\n        if (e.integriert === -Infinity) { dazu('still', 'info'); }\n        if (isFinite(tp) &#038;&#038; tp > 0) { dazu('tp_ueber_null', 'kritisch', { tp: zahl(tp, 1) }); }\n        else if (isFinite(tp) && tp > -1) { dazu('tp_ueber_eins', 'warnung', { tp: zahl(tp, 1) }); }\n        if (I !== null && isFinite(I) && I > -14 && tp > -2) { dazu('spotify_zwei', 'warnung'); }\n        if (e.vollAusgesteuert > 0) { dazu('voll', 'warnung', { anzahl: e.vollAusgesteuert }); }\n        if (e.dauer >= 3 && e.dauer < 60) { dazu('kurz_lra', 'info'); }\n        if (info &#038;&#038; info.dualMono) { dazu('dualmono', 'info'); }\n        if (info &#038;&#038; info.geschaetzt) { dazu('rate_unbekannt', 'info', { rate: e.fs }); }\n        if (isFinite(tp) &#038;&#038; tp <= -1 &#038;&#038; !(I !== null &#038;&#038; I > -14 && tp > -2) && e.vollAusgesteuert === 0) {\n            dazu('gut', 'gut', { tp: zahl(tp, 1) });\n        }\n        return b;\n    }<\/p>\n<p>    function eigenesZiel() {\n        var feld = document.getElementById('lufs-ziel');\n        var v = feld ? parseFloat(String(feld.value).replace(',', '.')) : NaN;\n        return isFinite(v) ? Math.max(-60, Math.min(0, v)) : -14;\n    }<\/p>\n<p>    function tabelle(e) {\n        return ZIELE.map(function (z) {\n            var ziel = z.ziel === null ? eigenesZiel() : z.ziel;\n            return { id: z.id, ziel: ziel, regel: z.regel, n: normalisierung(e.integriert, e.truePeak, ziel, z.regel) };\n        });\n    }<\/p>\n<p>    \/\/ --- Darstellung --------------------------------------------------------\n    var wurzel = document.getElementById('lufs-wurzel');\n    var ausgabe = document.getElementById('lufs-ausgabe');\n    var letztes = null;<\/p>\n<p>    function el(tag, klasse, txt) {\n        var e = document.createElement(tag);\n        if (klasse) { e.className = klasse; }\n        if (txt !== undefined && txt !== null) { e.textContent = txt; }\n        return e;\n    }<\/p>\n<p>    function kachel(ziel, name, wert, einheit, klasse) {\n        var k = el('div', 'lufs-kachel');\n        k.appendChild(el('div', 'lufs-kachel-name', name));\n        var w = el('div', 'lufs-kachel-wert' + (klasse ? ' ' + klasse : ''), wert);\n        if (einheit) { w.appendChild(el('span', 'lufs-einheit', einheit)); }\n        k.appendChild(w);\n        ziel.appendChild(k);\n        return k;\n    }<\/p>\n<p>    function dauerText(s) {\n        var min = Math.floor(s \/ 60), sek = s - min * 60;\n        return min + ':' + (sek < 10 ? '0' : '') + sek.toFixed(1).replace('.', T.dezimal);\n    }\n\n    function kanalText(n) { return n === 1 ? T.kanal_mono : (n === 2 ? T.kanal_stereo : text('kanal_mehr', { anzahl: n })); }\n\n    var STUFEN_KLASSE = { kritisch: 'lufs-fail', warnung: 'lufs-warn', gut: 'lufs-ok', info: '' };\n\n    function darstellen(e, info) {\n        letztes = { e: e, info: info || {} };\n        info = letztes.info;\n        ausgabe.innerHTML = '';\n\n        var kopf = [];\n        if (info.name) { kopf.push(info.name); }\n        if (info.format) { kopf.push(info.format); }\n        kopf.push(e.fs + ' Hz');\n        kopf.push(kanalText(e.kanaele));\n        if (info.bits) { kopf.push(text(info.gleitkomma ? 'bit_float' : 'bit', { bits: info.bits })); }\n        kopf.push(dauerText(e.dauer));\n        ausgabe.appendChild(el('p', 'lufs-klein', kopf.join(' \u00b7 ')));\n\n        var kacheln = el('div', 'lufs-kacheln');\n        var tp = e.truePeak;\n        kachel(kacheln, T.k_integriert, zahl(e.integriert, 1), 'LUFS');\n        kachel(kacheln, T.k_truepeak, zahl(tp, 1), 'dBTP', !isFinite(tp) ? '' : (r1(tp) > 0 ? 'lufs-fail' : (r1(tp) > -1 ? 'lufs-warn' : 'lufs-ok')));\n        kachel(kacheln, T.k_lra, e.lra ? zahl(e.lra.wert, 1) : '\u2013', 'LU');\n        kachel(kacheln, T.k_mmax, zahl(e.momentanMax, 1), 'LUFS');\n        kachel(kacheln, T.k_smax, zahl(e.kurzMax, 1), 'LUFS');\n        kachel(kacheln, T.k_samplepeak, zahl(e.samplePeak, 1), 'dBFS');\n        var plr = (e.integriert !== null && isFinite(e.integriert) && isFinite(tp)) ? tp - e.integriert : null;\n        kachel(kacheln, T.k_plr, zahl(plr, 1), 'dB');\n        ausgabe.appendChild(kacheln);<\/p>\n<p>        \/\/ Spotify in einem Satz\n        var zeilen = tabelle(e), normal = zeilen[0].n;\n        if (normal) {\n            var satz = normal.gain < -0.05 ? text('spotify_satz_leiser', { db: zahl(-normal.gain, 1) })\n                : (normal.gain > 0.05 ? text('spotify_satz_lauter', { db: zahl(normal.gain, 1) }) : T.spotify_satz_gleich);\n            ausgabe.appendChild(el('p', '', satz)).setAttribute('style', 'margin: 12px 0 0; color: var(--text-primary, #e8e8ee); font-weight: 700;');\n        }<\/p>\n<p>        ausgabe.appendChild(el('div', 'lufs-h', T.h_befunde));\n        befunde(e, info).forEach(function (f) {\n            var k = el('div', 'lufs-befund');\n            var farbe = { kritisch: 'var(--fail, #ff7b72)', warnung: 'var(--lufs-warn)', gut: 'var(--ok, #7ee787)', info: 'var(--border, #2a2a35)' }[f.stufe];\n            k.style.borderLeftColor = farbe;\n            var t = (text('befund_' + f.key, f.daten) || f.key).split(' || ');\n            k.appendChild(el('div', STUFEN_KLASSE[f.stufe], t[0])).setAttribute('style', 'font-weight: 700; font-size: 0.9rem; color: var(--text-primary, #e8e8ee);');\n            if (t[1]) { k.appendChild(el('div', 'lufs-klein', t[1])).style.paddingTop = '4px'; }\n            ausgabe.appendChild(k);\n        });<\/p>\n<p>        ausgabe.appendChild(el('div', 'lufs-h', T.h_normalisierung));\n        var huelle = el('div', 'lufs-tabelle'), tab = el('table'), kz = el('tr');\n        [T.sp_vorgabe, T.sp_ziel, T.sp_aenderung, T.sp_ergebnis, T.sp_tp, T.sp_hinweis].forEach(function (s) { kz.appendChild(el('th', '', s)); });\n        var thead = el('thead'); thead.appendChild(kz); tab.appendChild(thead);\n        var tbody = el('tbody');\n        zeilen.forEach(function (z) {\n            var tr = el('tr');\n            tr.setAttribute('data-ziel', z.id);\n            tr.appendChild(el('td', '', T['ziel_' + z.id]));\n            tr.appendChild(el('td', 'lufs-zahl', zahl(z.ziel, 1) + ' LUFS'));\n            if (!z.n) {\n                ['\u2013', '\u2013', '\u2013', ''].forEach(function (s) { tr.appendChild(el('td', 'lufs-zahl', s)); });\n            } else {\n                tr.appendChild(el('td', 'lufs-zahl', zahl(z.n.gain, 1, true) + ' dB'));\n                tr.appendChild(el('td', 'lufs-zahl', zahl(z.n.ergebnis, 1) + ' LUFS'));\n                var tpD = r1(z.n.tpDanach);\n                tr.appendChild(el('td', 'lufs-zahl' + (tpD > -1 ? ' lufs-warn' : ''), zahl(z.n.tpDanach, 1) + ' dBTP'));\n                var hinweis = [];\n                if (z.n.begrenzt && z.n.gain > 0) { hinweis.push(T.norm_begrenzt); }\n                if (z.n.begrenzt && z.n.gain === 0) { hinweis.push(T.norm_kein_platz); }\n                if (z.regel === 'limiter' && tpD > -1) { hinweis.push(T.norm_limiter); }\n                if (z.regel !== 'limiter' && tpD > -1) { hinweis.push(T.norm_ueber); }\n                tr.appendChild(el('td', '', hinweis.join('; ')));\n            }\n            tbody.appendChild(tr);\n        });\n        tab.appendChild(tbody);\n        huelle.appendChild(tab);\n        ausgabe.appendChild(huelle);\n        ausgabe.appendChild(el('p', 'lufs-klein', T.apple_hinweis)).style.marginTop = '10px';<\/p>\n<p>        ausgabe.appendChild(el('div', 'lufs-h', T.h_verlauf));\n        var leinwand = el('canvas');\n        leinwand.id = 'lufs-verlauf';\n        leinwand.setAttribute('role', 'img');\n        leinwand.setAttribute('aria-label', T.h_verlauf);\n        ausgabe.appendChild(leinwand);\n        ausgabe.appendChild(el('p', 'lufs-klein', T.verlauf_hinweis)).style.marginTop = '6px';\n        verlaufZeichnen();<\/p>\n<p>        var kopieren = el('button', 'button', T.knopf_kopieren);\n        kopieren.type = 'button';\n        kopieren.setAttribute('style', 'margin-top: 14px; background: transparent; color: var(--accent, #2ba6df); border: 1px solid var(--accent, #2ba6df); padding: 8px 16px; border-radius: 6px; font-weight: 700; cursor: pointer;');\n        var meldung = el('span', 'lufs-klein');\n        meldung.style.marginLeft = '12px';\n        kopieren.addEventListener('click', function () {\n            var bericht = berichtText(e, info);\n            var fertig = function () { meldung.textContent = T.kopiert; };\n            if (navigator.clipboard && navigator.clipboard.writeText) {\n                navigator.clipboard.writeText(bericht).then(fertig, function () { ersatzKopieren(bericht); fertig(); });\n            } else { ersatzKopieren(bericht); fertig(); }\n        });\n        ausgabe.appendChild(kopieren);\n        ausgabe.appendChild(meldung);\n    }<\/p>\n<p>    function ersatzKopieren(s) {\n        var ta = document.createElement('textarea');\n        ta.value = s; ta.setAttribute('readonly', ''); ta.style.position = 'fixed'; ta.style.opacity = '0';\n        document.body.appendChild(ta); ta.select();\n        try { document.execCommand('copy'); } catch (x) { \/* nichts *\/ }\n        document.body.removeChild(ta);\n    }<\/p>\n<p>    function berichtText(e, info) {\n        var z = [];\n        z.push((info.name || '') + ' (' + e.fs + ' Hz, ' + kanalText(e.kanaele) + ', ' + dauerText(e.dauer) + ')');\n        z.push(T.k_integriert + ': ' + zahlAscii(e.integriert, 1) + ' LUFS');\n        z.push(T.k_truepeak + ': ' + zahlAscii(e.truePeak, 1) + ' dBTP');\n        z.push(T.k_lra + ': ' + (e.lra ? zahlAscii(e.lra.wert, 1) : '-') + ' LU');\n        z.push(T.k_mmax + ': ' + zahlAscii(e.momentanMax, 1) + ' LUFS');\n        z.push(T.k_smax + ': ' + zahlAscii(e.kurzMax, 1) + ' LUFS');\n        z.push(T.k_samplepeak + ': ' + zahlAscii(e.samplePeak, 1) + ' dBFS');\n        tabelle(e).forEach(function (r) {\n            if (r.n) { z.push(T['ziel_' + r.id] + ' (' + zahlAscii(r.ziel, 1) + ' LUFS): ' + (r.n.gain > 0 ? '+' : '') + zahlAscii(r.n.gain, 1) + ' dB -> ' + zahlAscii(r.n.ergebnis, 1) + ' LUFS'); }\n        });\n        return z.join('\\n');\n    }<\/p>\n<p>    function farbe(name, rueckfall) {\n        var v = wurzel ? getComputedStyle(wurzel).getPropertyValue(name) : '';\n        return (v && v.trim()) || rueckfall;\n    }<\/p>\n<p>    function verlaufZeichnen() {\n        var leinwand = document.getElementById('lufs-verlauf');\n        if (!leinwand || !letztes) { return; }\n        var e = letztes.e, werte = e.kurzVerlauf;\n        var breite = Math.max(260, Math.round(leinwand.parentNode.clientWidth || 600)), hoehe = 210;\n        var dpr = window.devicePixelRatio || 1;\n        leinwand.width = Math.round(breite * dpr); leinwand.height = Math.round(hoehe * dpr);\n        leinwand.style.height = hoehe + 'px';\n        var g = leinwand.getContext && leinwand.getContext('2d');\n        if (!g) { return; }\n        g.setTransform(dpr, 0, 0, dpr, 0, 0);\n        g.clearRect(0, 0, breite, hoehe);\n        var cText = farbe('--text-secondary', '#94a3b8'), cLinie = farbe('--border', '#2d3748'),\n            cAkzent = farbe('--accent', '#2ba6df'), cStark = farbe('--text-primary', '#e0e6ed'), cWarn = farbe('--lufs-warn', '#e8a33d');\n        var L = 46, R = 10, O = 8, U = 24, w = breite - L - R, h = hoehe - O - U;\n        var ziel = eigenesZiel(), I = e.integriert !== null && isFinite(e.integriert) ? e.integriert : null;\n        var tief = Infinity, hoch = -Infinity;\n        for (var i = 0; i < werte.length; i++) { if (isFinite(werte[i])) { tief = Math.min(tief, werte[i]); hoch = Math.max(hoch, werte[i]); } }\n        var yMax = Math.max(0, Math.ceil(((isFinite(hoch) ? hoch : -20) + 1) \/ 6) * 6);\n        var unten = Math.min(isFinite(tief) ? tief : -40, I !== null ? I - 12 : -40, ziel - 6);\n        var yMin = Math.max(-72, Math.floor(unten \/ 6) * 6);\n        if (yMax - yMin < 18) { yMin = yMax - 18; }\n        var y = function (v) { v = Math.max(yMin, Math.min(yMax, v)); return O + (yMax - v) \/ (yMax - yMin) * h; };\n        g.font = '11px monospace'; g.lineWidth = 1; g.textBaseline = 'middle';\n        var stufe = (yMax - yMin) > 48 ? 12 : 6;\n        for (var t = yMin; t <= yMax; t += stufe) {\n            g.strokeStyle = cLinie; g.beginPath(); g.moveTo(L, Math.round(y(t)) + 0.5); g.lineTo(L + w, Math.round(y(t)) + 0.5); g.stroke();\n            g.fillStyle = cText; g.fillText(zahl(t, 0), 4, y(t));\n        }\n        var dauer = Math.max(e.dauer, 0.001), schritte = [1, 2, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600];\n        var sek = schritte[schritte.length - 1];\n        for (var s = 0; s < schritte.length; s++) { if (dauer \/ schritte[s] <= Math.max(3, Math.floor(w \/ 70))) { sek = schritte[s]; break; } }\n        g.textBaseline = 'top';\n        for (var z = 0; z <= dauer + 1e-9; z += sek) {\n            var xz = L + z \/ dauer * w;\n            g.fillStyle = cText; g.fillText(sek >= 60 || dauer >= 60 ? Math.floor(z \/ 60) + ':' + ((z % 60) < 10 ? '0' : '') + Math.floor(z % 60) : z + ' s', Math.min(xz, L + w - 30), O + h + 6);\n        }\n        if (werte.length) {\n            g.strokeStyle = cAkzent; g.lineWidth = 1.6; g.beginPath();\n            var offen = false;\n            for (var k = 0; k < werte.length; k++) {\n                var x = L + (3 + k * 0.1) \/ dauer * w;\n                if (!isFinite(werte[k]) || werte[k] < yMin) { offen = false; continue; }\n                if (!offen) { g.moveTo(x, y(werte[k])); offen = true; } else { g.lineTo(x, y(werte[k])); }\n            }\n            g.stroke();\n        }\n        g.lineWidth = 1.2;\n        if (I !== null &#038;&#038; g.setLineDash) {\n            g.setLineDash([6, 4]); g.strokeStyle = cStark; g.beginPath(); g.moveTo(L, y(I)); g.lineTo(L + w, y(I)); g.stroke();\n        }\n        if (g.setLineDash) {\n            g.setLineDash([2, 3]); g.strokeStyle = cWarn; g.beginPath(); g.moveTo(L, y(ziel)); g.lineTo(L + w, y(ziel)); g.stroke();\n            g.setLineDash([]);\n        }\n    }\n\n    \/\/ --- Ablauf ---------------------------------------------------------------\n    var status = document.getElementById('lufs-status');\n    var statusText = document.getElementById('lufs-status-text');\n    var balken = document.getElementById('lufs-fortschritt');\n    var dualmono = document.getElementById('lufs-dualmono');\n    var gelesen = null;\n\n    function zeigeStatus(t, anteil) {\n        if (!status) { return; }\n        status.hidden = false;\n        statusText.textContent = t;\n        balken.style.width = Math.round((anteil || 0) * 100) + '%';\n    }\n    function fehlerZeigen(t) {\n        if (status) { status.hidden = true; }\n        ausgabe.innerHTML = '';\n        var k = el('div', 'lufs-befund lufs-klein', t);\n        k.style.borderLeftColor = 'var(--fail, #ff7b72)';\n        ausgabe.appendChild(k);\n    }\n\n    function messenUndZeigen() {\n        if (!gelesen) { return Promise.resolve(null); }\n        var mono = gelesen.kanaele.length === 1 &#038;&#038; dualmono &#038;&#038; dualmono.checked;\n        zeigeStatus(text('status_messen', { prozent: 0 }), 0);\n        return messenAsync(gelesen.kanaele, gelesen.rate, { dualMono: mono }, function (a) {\n            zeigeStatus(text('status_messen', { prozent: Math.round(a * 100) }), a);\n        }).then(function (e) {\n            status.hidden = true;\n            darstellen(e, { name: gelesen.name, format: gelesen.format, bits: gelesen.bits, gleitkomma: gelesen.gleitkomma, dualMono: mono, geschaetzt: gelesen.geschaetzt });\n            return e;\n        });\n    }\n\n    function verarbeitenPuffer(puffer, name) {\n        zeigeStatus(T.status_dekodieren, 0);\n        return einlesen(puffer).then(function (d) {\n            if (!d.kanaele.length || !d.kanaele[0].length) { throw { leer: true }; }\n            gelesen = { name: name, format: d.format || String(name || '').split('.').pop().toUpperCase(), rate: d.rate, bits: d.bits, gleitkomma: !!d.gleitkomma, kanaele: d.kanaele, geschaetzt: d.geschaetzt };\n            return messenUndZeigen();\n        }).catch(function (f) {\n            gelesen = null;\n            if (window.LW_TEST &#038;&#038; window.LW_TEST.LUFS) { window.LW_TEST.LUFS.letzterFehler = f; }\n            if (window.console &#038;&#038; console.warn) { console.warn(f); }\n            if (f &#038;&#038; f.leer) { fehlerZeigen(T.fehler_leer); }\n            else if (f &#038;&#038; f.format) { fehlerZeigen(text('fehler_format', { detail: f.format })); }\n            else if (f &#038;&#038; f.message === 'webaudio') { fehlerZeigen(T.fehler_webaudio); }\n            else if (f &#038;&#038; (f.name === 'RangeError' || \/memory|allocation\/i.test(String(f.message || '')))) { fehlerZeigen(T.fehler_speicher); }\n            else { fehlerZeigen(T.fehler_dekodieren); }\n            return null;\n        });\n    }\n\n    function dateiNehmen(datei) {\n        if (!datei) { return; }\n        zeigeStatus(T.status_lesen, 0);\n        var leser = new FileReader();\n        leser.onload = function () { verarbeitenPuffer(leser.result, datei.name); };\n        leser.onerror = function () { fehlerZeigen(T.fehler_dekodieren); };\n        try { leser.readAsArrayBuffer(datei); } catch (x) { fehlerZeigen(T.fehler_speicher); }\n    }\n\n    window.LW_TEST = window.LW_TEST || {};\n    window.LW_TEST.LUFS = {\n        T: T, ZIELE: ZIELE, text: text, zahl: zahl, kKoeffizienten: kKoeffizienten, analysiere: analysiere,\n        messungAnlegen: messungAnlegen, messungSchritt: messungSchritt, messungAuswerten: messungAuswerten,\n        fensterWerte: fensterWerte, loudnessRange: loudnessRange, normalisierung: normalisierung,\n        kopfLesen: kopfLesen, wavLesen: wavLesen, aiffLesen: aiffLesen, befunde: befunde,\n        darstellen: darstellen, verarbeitenPuffer: verarbeitenPuffer, einlesen: einlesen\n    };\n\n    var zone = document.getElementById('lufs-zone'), eingabe = document.getElementById('lufs-datei'), knopf = document.getElementById('lufs-knopf');\n    if (!zone || !eingabe || !ausgabe) { return; }\n    knopf.addEventListener('click', function () { eingabe.click(); });\n    eingabe.addEventListener('change', function () { dateiNehmen(eingabe.files &#038;&#038; eingabe.files[0]); eingabe.value = ''; });\n    ['dragenter', 'dragover'].forEach(function (typ) {\n        zone.addEventListener(typ, function (ev) { ev.preventDefault(); zone.classList.add('lufs-ueber'); });\n    });\n    ['dragleave', 'drop'].forEach(function (typ) {\n        zone.addEventListener(typ, function (ev) { ev.preventDefault(); zone.classList.remove('lufs-ueber'); });\n    });\n    zone.addEventListener('drop', function (ev) {\n        var f = ev.dataTransfer &#038;&#038; ev.dataTransfer.files &#038;&#038; ev.dataTransfer.files[0];\n        dateiNehmen(f);\n    });\n    if (dualmono) {\n        dualmono.addEventListener('change', function () { if (gelesen &#038;&#038; gelesen.kanaele.length === 1) { messenUndZeigen(); } });\n    }\n    var zielFeld = document.getElementById('lufs-ziel');\n    if (zielFeld) {\n        zielFeld.addEventListener('input', function () { if (letztes) { darstellen(letztes.e, letztes.info); } });\n    }\n    var warten = null;\n    window.addEventListener('resize', function () { clearTimeout(warten); warten = setTimeout(verlaufZeichnen, 150); });\n    if (window.MutationObserver) {\n        new MutationObserver(function () { verlaufZeichnen(); }).observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });\n    }\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Misst integrierte Lautheit (LUFS), True Peak (dBTP) und Lautheitsumfang einer Audiodatei direkt im Browser nach ITU-R BS.1770-5 und EBU R 128 und rechnet nach, wie Spotify und die AES-Empfehlung sie normalisieren w\u00fcrden. Die Datei verl\u00e4sst das Ger\u00e4t nicht.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":9283,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[92903],"class_list":["post-18604","page","type-page","status-publish","hentry","tag-music-production-de"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>LUFS- und True-Peak-Messger\u00e4t: Lautheit, Lautheitsumfang und Streaming-Normalisierung - Lukas Wojcik - Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"LUFS- und True-Peak-Messger\u00e4t: Lautheit, Lautheitsumfang und Streaming-Normalisierung - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Misst integrierte Lautheit (LUFS), True Peak (dBTP) und Lautheitsumfang einer Audiodatei direkt im Browser nach ITU-R BS.1770-5 und EBU R 128 und rechnet nach, wie Spotify und die AES-Empfehlung sie normalisieren w\u00fcrden. Die Datei verl\u00e4sst das Ger\u00e4t nicht.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-17T06:19:47+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/lufs-true-peak-loudness-meter\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/lufs-true-peak-loudness-meter\\\/\",\"name\":\"LUFS- und True-Peak-Messger\u00e4t: Lautheit, Lautheitsumfang und Streaming-Normalisierung - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-09-17T06:15:00+00:00\",\"dateModified\":\"2026-09-17T06:19:47+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/lufs-true-peak-loudness-meter\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/lufs-true-peak-loudness-meter\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/lufs-true-peak-loudness-meter\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/toolbox-2\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"LUFS- und True-Peak-Messger\u00e4t: Lautheit, Lautheitsumfang und Streaming-Normalisierung\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"LUFS- und True-Peak-Messger\u00e4t: Lautheit, Lautheitsumfang und Streaming-Normalisierung - Lukas Wojcik - Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/","og_locale":"en_US","og_type":"article","og_title":"LUFS- und True-Peak-Messger\u00e4t: Lautheit, Lautheitsumfang und Streaming-Normalisierung - Lukas Wojcik - Blog","og_description":"Misst integrierte Lautheit (LUFS), True Peak (dBTP) und Lautheitsumfang einer Audiodatei direkt im Browser nach ITU-R BS.1770-5 und EBU R 128 und rechnet nach, wie Spotify und die AES-Empfehlung sie normalisieren w\u00fcrden. Die Datei verl\u00e4sst das Ger\u00e4t nicht.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/","og_site_name":"Lukas Wojcik - Blog","article_modified_time":"2026-09-17T06:19:47+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/","url":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/","name":"LUFS- und True-Peak-Messger\u00e4t: Lautheit, Lautheitsumfang und Streaming-Normalisierung - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-09-17T06:15:00+00:00","dateModified":"2026-09-17T06:19:47+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/lufs-true-peak-loudness-meter\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/toolbox-2\/"},{"@type":"ListItem","position":3,"name":"LUFS- und True-Peak-Messger\u00e4t: Lautheit, Lautheitsumfang und Streaming-Normalisierung"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/18604","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=18604"}],"version-history":[{"count":1,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/18604\/revisions"}],"predecessor-version":[{"id":18607,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/18604\/revisions\/18607"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9283"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=18604"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=18604"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}