{"id":20128,"date":"2026-09-23T18:48:07","date_gmt":"2026-09-23T16:48:07","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/"},"modified":"2026-09-23T18:54:01","modified_gmt":"2026-09-23T16:54:01","slug":"release-file-checker-sample-rate-bit-depth-peaks-isrc-cover","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/","title":{"rendered":"Release File Checker: sample rate, bit depth, peaks, silence, ISRC and cover before the upload"},"content":{"rendered":"<div class=\"lw-rp\" id=\"rp-wurzel\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<style>\n.lw-rp { --rp-warn: #e8a33d; }\n:root[data-theme=\"light\"] .lw-rp { --rp-warn: #8f5300; }\n.lw-rp .rp-zone { border: 2px dashed var(--border, #2a2a35); border-radius: 8px; padding: 22px; text-align: center; }\n.lw-rp .rp-zone.rp-ueber { border-color: var(--accent, #2ba6df); }\n.lw-rp .rp-gesamt { border-radius: 8px; padding: 12px 15px; margin-top: 4px; border: 1px solid var(--border, #2a2a35); background: var(--bg-body, #14141a); }\n.lw-rp .rp-gesamt-kopf { font-weight: 700; color: var(--text-primary, #e8e8ee); margin-bottom: 4px; }\n.lw-rp .rp-g-ok .rp-gesamt-kopf { color: var(--ok, #7ee787); }\n.lw-rp .rp-g-hinweis .rp-gesamt-kopf { color: var(--rp-warn); }\n.lw-rp .rp-g-problem .rp-gesamt-kopf { color: var(--fehler, #f0857a); }\n.lw-rp .rp-tabelle { overflow-x: auto; margin-top: 6px; }\n.lw-rp table { border-collapse: collapse; width: 100%; font-size: 0.86rem; }\n.lw-rp th, .lw-rp td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--border, #2a2a35); vertical-align: top; }\n.lw-rp th { color: var(--text-primary, #e8e8ee); white-space: nowrap; }\n.lw-rp td { color: var(--text-primary, #e8e8ee); }\n.lw-rp td.rp-wert { font-family: monospace; color: var(--text-secondary, #a0a0b0); white-space: nowrap; }\n.lw-rp td.rp-text { color: var(--text-secondary, #a0a0b0); line-height: 1.5; }\n.lw-rp .rp-stufe { display: inline-block; font-size: 0.74rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px; padding: 2px 7px; border-radius: 4px; border: 1px solid currentColor; white-space: nowrap; }\n.lw-rp .rp-s-ok { color: var(--ok, #7ee787); }\n.lw-rp .rp-s-hinweis { color: var(--rp-warn); }\n.lw-rp .rp-s-problem { color: var(--fehler, #f0857a); }\n.lw-rp .rp-s-info, .lw-rp .rp-s-offen { color: var(--text-secondary, #a0a0b0); }\n.lw-rp .rp-h { font-weight: 700; color: var(--text-primary, #e8e8ee); font-size: 0.95rem; margin: 24px 0 8px; }\n.lw-rp .rp-klein { font-size: 0.84rem; color: var(--text-secondary, #a0a0b0); line-height: 1.55; }\n.lw-rp .rp-knopf { background: transparent; color: var(--text-secondary, #a0a0b0); border: 1px solid var(--border, #2a2a35); padding: 9px 14px; border-radius: 6px; cursor: pointer; font-size: 0.9rem; margin-top: 14px; }\n.lw-rp .rp-name { margin-top: 4px; }\n.lw-rp .rp-fehler { color: var(--fehler, #f0857a); font-size: 0.88rem; margin-top: 12px; }\n.lw-rp a { color: var(--accent, #2ba6df); }\n@media (max-width: 480px) {\n    .lw-rp th, .lw-rp td { padding: 6px 5px; }\n    .lw-rp td.rp-wert { white-space: normal; word-break: break-word; }\n}\n\/* Schmal: die Checkliste als gestapelte Karten, sonst wird der Befund abgeschnitten. *\/\n@media (max-width: 600px) {\n    .lw-rp table.rp-karten thead { display: none; }\n    .lw-rp table.rp-karten tr { display: block; padding: 9px 0; border-bottom: 1px solid var(--border, #2a2a35); }\n    .lw-rp table.rp-karten td { display: block; border: none; padding: 2px 2px; }\n    .lw-rp table.rp-karten td.rp-wert { white-space: normal; margin-top: 3px; }\n    .lw-rp table.rp-karten .rp-name { display: inline; margin: 0 0 0 7px; font-weight: 700; }\n}\n<\/style>\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 18px;\">Checks an audio file before it goes to a distributor: format, sample rate and bit depth against Apple&#039;s delivery requirements, bits that only pad the file, clipping, true peak, DC offset, silence at the start and the end, and title, artist, ISRC and cover from the tags. The file is read in the browser and not uploaded.<\/p>\n<div id=\"rp-zone\" class=\"rp-zone\">\n<p style=\"margin: 0 0 12px; color: var(--text-primary, #e8e8ee); font-weight: 700;\">Choose the release file<\/p>\n<p>        <button id=\"rp-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;\">Select file<\/button><br \/>\n        <input id=\"rp-datei\" type=\"file\" accept=\"audio\/*,.wav,.wave,.aif,.aiff,.aifc,.flac,.mp3,.m4a,.mp4,.aac,.ogg,.oga,.opus\" hidden><\/p>\n<p class=\"rp-klein\" style=\"margin: 12px 0 0;\">WAV, RF64, AIFF, FLAC, MP3, M4A, Ogg Vorbis and Opus. WAV and AIFF are read sample by sample; the other formats go through the browser&#039;s decoder.<\/p>\n<\/p><\/div>\n<div id=\"rp-status\" class=\"rp-klein\" data-mp-mask style=\"margin-top: 12px;\" aria-live=\"polite\"><\/div>\n<div id=\"rp-fehler\" class=\"rp-fehler\" data-mp-mask hidden><\/div>\n<div id=\"rp-ausgabe\" data-mp-mask style=\"margin-top: 18px;\" aria-live=\"polite\" hidden>\n<div id=\"rp-gesamt\" class=\"rp-gesamt\"><\/div>\n<div class=\"rp-h\">Checklist<\/div>\n<div class=\"rp-tabelle\">\n<table class=\"rp-karten\">\n<thead>\n<tr>\n<th>Check<\/th>\n<th>Value<\/th>\n<th>Finding<\/th>\n<\/tr>\n<\/thead>\n<tbody id=\"rp-liste\"><\/tbody>\n<\/table><\/div>\n<div class=\"rp-h\">Tags in the file<\/div>\n<div class=\"rp-tabelle\">\n<table>\n<thead>\n<tr>\n<th>Field<\/th>\n<th>Value<\/th>\n<th>Stored in<\/th>\n<\/tr>\n<\/thead>\n<tbody id=\"rp-tags\"><\/tbody>\n<\/table><\/div>\n<p>        <button id=\"rp-kopieren\" type=\"button\" class=\"rp-knopf\">Copy result<\/button><br \/>\n        <span id=\"rp-kopiert\" class=\"rp-klein\" style=\"margin-left: 10px;\"><\/span>\n    <\/div>\n<div class=\"rp-h\">What is checked and why<\/div>\n<p class=\"rp-klein\" style=\"margin: 0 0 6px;\">Apple Music takes audio at 44.1, 48, 88.2, 96, 176.4 or 192\u00a0kHz with 16 or 24\u00a0bits and asks for the master in the highest resolution available. A lossy file does not meet that: the encoder has already discarded parts of the signal, and the service encodes once more.<\/p>\n<p class=\"rp-klein\" style=\"margin: 0 0 6px;\">Bits in use: for every sample the lowest bits are checked. If they are zero throughout the file, the extra depth carries nothing, as with a 16-bit master saved as 24\u00a0bits. Apple Digital Masters explicitly rules out this padding. In floating-point files the check asks whether every value lies on the 16-bit or the 24-bit grid.<\/p>\n<p class=\"rp-klein\" style=\"margin: 0 0 6px;\">Full scale is the highest integer value, and three or more such samples in a row count as a clipped passage. The true peak follows ITU-R BS.1770 with the 48-coefficient filter at four times oversampling. It finds peaks between the samples that a conversion or an encoder turns into real ones.<\/p>\n<p class=\"rp-klein\" style=\"margin: 0 0 6px;\">Silence here means below \u221260\u00a0dBFS. The limits of one second at the start, five seconds at the end, \u221240\u00a0dBFS for the first and the last sample and \u221260\u00a0dBFS for the DC offset are rules of this check, not requirements of a service. They mark what is worth a listen before the upload.<\/p>\n<p class=\"rp-klein\" style=\"margin: 0 0 6px;\">Tags are read from ID3v2 and ID3v1, from Vorbis comments in FLAC, Ogg and Opus, from RIFF INFO and ID3 chunks in WAV and AIFF, and from the MP4 metadata. For the ISRC the field matters: TSRC in ID3, ISRC in Vorbis comments. ffmpeg writes other fields for -metadata ISRC=\u2026, as the article on release metadata measured.<\/p>\n<p class=\"rp-klein\" style=\"margin: 0 0 6px;\">Apple recommends 3000\u00a0\u00d7\u00a03000\u00a0pixels or more, requires at least 1400\u00a0\u00d7\u00a01400 and a square image, as JPEG or PNG. The check uses the embedded image; the cover uploaded to the distributor is a separate file.<\/p>\n<p class=\"rp-klein\" style=\"margin: 20px 0 0;\">Sources:<br \/>\n        <a href=\"https:\/\/itunespartner.apple.com\/music\/support\/5215-digital-packaging-music\" rel=\"noopener\">Apple Music Provider Support: Digital packaging for music<\/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> \u00b7<br \/>\n        <a href=\"https:\/\/www.itu.int\/rec\/R-REC-BS.1770\" rel=\"noopener\">ITU-R BS.1770<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/aes2.org\/wp-content\/uploads\/2024\/01\/20210924_TD1008_v3.13.pdf\" rel=\"noopener\">AES TD1008<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/isrc.ifpi.org\/images\/downloads\/ISRC_Handbook.pdf\" rel=\"noopener\">ISRC Handbook<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/mutagen-specs.readthedocs.io\/en\/latest\/id3\/id3v2.4.0-frames.html\" rel=\"noopener\">ID3v2.4 frames<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/xiph.org\/vorbis\/doc\/v-comment.html\" rel=\"noopener\">Vorbis comment<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/exiftool.org\/TagNames\/RIFF.html\" rel=\"noopener\">RIFF Tags (ExifTool)<\/a>\n    <\/p>\n<p class=\"rp-klein\" style=\"font-size: 0.8rem; margin: 14px 0 0;\">The requirements quoted are Apple&#039;s; other distributors publish their own. Formats other than WAV and AIFF depend on the browser&#039;s decoder, which in some browsers cannot read ALAC or compressed AIFC. Loudness is measured by the LUFS meter.<\/p>\n<\/div>\n<p><script>\n(function () {\n    'use strict';\n    const T = {\"dezimal\":\".\",\"tausender\":\"\",\"einleitung\":\"Checks an audio file before it goes to a distributor: format, sample rate and bit depth against Apple\\u0027s delivery requirements, bits that only pad the file, clipping, true peak, DC offset, silence at the start and the end, and title, artist, ISRC and cover from the tags. The file is read in the browser and not uploaded.\",\"zone_titel\":\"Choose the release file\",\"knopf_datei\":\"Select file\",\"zone_hinweis\":\"WAV, RF64, AIFF, FLAC, MP3, M4A, Ogg Vorbis and Opus. WAV and AIFF are read sample by sample; the other formats go through the browser\\u0027s decoder.\",\"h_liste\":\"Checklist\",\"sp_pruefpunkt\":\"Check\",\"sp_wert\":\"Value\",\"sp_befund\":\"Finding\",\"h_tags\":\"Tags in the file\",\"sp_feld\":\"Field\",\"sp_ort\":\"Stored in\",\"status_lesen\":\"Reading the file \u2026\",\"status_dekodieren\":\"Decoding \u2026\",\"status_messen\":\"Measuring \u2026 {p}\u00a0%\",\"gesamt_ok\":\"No problems, no notes\",\"gesamt_hinweis\":\"No problems \u00b7 Notes: {h}\",\"gesamt_problem\":\"Problems: {p} \u00b7 Notes: {h}\",\"gesamt_text_ok\":\"Everything measured here meets the requirements named in the sources. What a distributor checks beyond that, such as the details in its upload form, is not part of this file.\",\"gesamt_text_hinweis\":\"Nothing stands in the way of the delivery, but the notes deserve a look before the upload.\",\"gesamt_text_problem\":\"At least one point contradicts a delivery requirement or points to damage in the file. The findings below say which.\",\"stufe_ok\":\"OK\",\"stufe_hinweis\":\"Note\",\"stufe_problem\":\"Problem\",\"stufe_info\":\"Info\",\"stufe_offen\":\"Not measured\",\"p_format\":\"Format\",\"p_rate\":\"Sample rate\",\"p_bits\":\"Bit depth\",\"p_wirksam\":\"Bits in use\",\"p_kanaele\":\"Channels\",\"p_messung\":\"Audio\",\"p_dauer\":\"Duration\",\"p_spitze\":\"Sample peak\",\"p_clipping\":\"Clipped passages\",\"p_ueber\":\"Values above 0\u00a0dBFS\",\"p_truepeak\":\"True peak\",\"p_dc\":\"DC offset\",\"p_stille_anfang\":\"Silence at the start\",\"p_stille_ende\":\"Silence at the end\",\"p_sprung\":\"First and last sample\",\"p_kanalvergleich\":\"Left against right\",\"p_titel\":\"Title\",\"p_kuenstler\":\"Artist\",\"p_isrc\":\"ISRC\",\"p_cover\":\"Cover\",\"wert_float\":\"float\",\"wert_keine_tags\":\"No title, artist, ISRC or cover found\",\"bericht_kopf\":\"Release file check\",\"f_titel\":\"Title\",\"f_kuenstler\":\"Artist\",\"f_isrc\":\"ISRC\",\"f_cover\":\"Cover\",\"ort_id3\":\"ID3v2 tag\",\"ort_id3v1\":\"ID3v1 tag\",\"ort_tsrc\":\"ID3 frame TSRC\",\"ort_txxx\":\"ID3 frame TXXX:ISRC\",\"ort_vorbis\":\"Vorbis comment\",\"ort_vorbis_bild\":\"Vorbis comment METADATA_BLOCK_PICTURE\",\"ort_flac_bild\":\"FLAC PICTURE block\",\"ort_riff\":\"RIFF INFO\",\"ort_riff_isrc\":\"RIFF INFO field ISRC\",\"ort_aiff\":\"AIFF chunk\",\"ort_mp4\":\"MP4 tag\",\"ort_mdta\":\"QuickTime metadata key\",\"ort_mp4_frei\":\"MP4 freeform field ISRC\",\"b_format_verlustfrei\":\"{format} stores the audio without loss.\",\"b_format_verlustbehaftet\":\"{format} is lossy. Apple asks for the master in the highest resolution available, and a lossy file cannot be that.\",\"b_format_unbekannt\":\"The container {format} was recognised, the codec inside it was not.\",\"b_offen_kopf\":\"The file header does not state this value.\",\"b_rate_ok\":\"{rate} is one of the rates Apple accepts: 44.1, 48, 88.2, 96, 176.4 and 192\u00a0kHz.\",\"b_rate_andere\":\"{rate} is not one of the rates Apple accepts (44.1, 48, 88.2, 96, 176.4 and 192\u00a0kHz). A conversion is due before the delivery.\",\"b_bits_verlustbehaftet\":\"A lossy format has no fixed bit depth; the decoder returns floating-point values.\",\"b_bits_float\":\"Floating point with {bits}\u00a0bits. Apple names 16 and 24\u00a0bits, so a conversion to 24-bit integer is due before the delivery.\",\"b_bits_24\":\"24\u00a0bits, as Apple recommends for Apple Digital Masters.\",\"b_bits_16\":\"16\u00a0bits are accepted. For Apple Digital Masters, Apple asks for 24-bit sources.\",\"b_bits_wenig\":\"{bits}\u00a0bits are fewer than the 16 or 24\u00a0bits Apple accepts.\",\"b_bits_andere\":\"{bits}-bit integer is not one of the depths Apple names (16 and 24\u00a0bits).\",\"b_wirksam_stille\":\"The file contains only digital silence, so the bits in use cannot be determined.\",\"b_wirksam_float16\":\"Every value lies exactly on the 16-bit grid: a 16-bit signal in a floating-point file. The extra precision is empty.\",\"b_wirksam_float24\":\"Every value lies exactly on the 24-bit grid, so a conversion to 24-bit integer loses nothing.\",\"b_wirksam_float\":\"The values use the floating-point precision; a conversion to 24\u00a0bits needs dither.\",\"b_wirksam_offen\":\"Cannot be determined for this file.\",\"b_wirksam_voll\":\"All {bits}\u00a0bits carry signal.\",\"b_wirksam_aufgefuellt\":\"Only {wirksam} of {bits}\u00a0bits carry signal, the rest are always zero. The file is a {wirksam}-bit signal padded to {bits}\u00a0bits, which Apple Digital Masters explicitly rules out.\",\"b_wirksam_weniger\":\"Only {wirksam} of {bits}\u00a0bits carry signal; the lowest bits are always zero.\",\"b_kanaele_2\":\"Stereo.\",\"b_kanaele_1\":\"Mono. Whether a distributor takes a mono file or expects two identical channels is stated in its own specification.\",\"b_kanaele_mehr\":\"{n} channels. A multichannel mix such as Dolby Atmos is delivered separately and gets its own ISRC.\",\"b_offen_ton\":\"The browser could not decode the audio of this file, so peaks, silence and DC offset were not measured. Header and tags are shown all the same.\",\"b_dauer\":\"Length of the audio as stored in the file.\",\"b_spitze_ok\":\"The highest sample stays below full scale.\",\"b_spitze_voll\":\"Samples at full scale: {n}. Whether that is clipping is shown in the next line.\",\"b_clipping_keine\":\"No run of three or more consecutive samples at full scale.\",\"b_clipping\":\"Passages with three or more consecutive samples at full scale: {n}. There the waveform is cut flat.\",\"b_ueber_keine\":\"No value exceeds full scale.\",\"b_ueber\":\"Values above 0\u00a0dBFS: {n}. A floating-point file can hold them, but the conversion to integer PCM cuts them off.\",\"b_tp_ok\":\"At most \u22121\u00a0dBTP, as AES TD1008 recommends at the input of a codec.\",\"b_tp_hinweis\":\"Between \u22121 and 0\u00a0dBTP. AES TD1008 recommends at most \u22121\u00a0dBTP at the codec input, because encoding raises the peaks further.\",\"b_tp_problem\":\"Above 0\u00a0dBTP: between the samples the waveform already exceeds full scale, and every conversion or encoding can clip it.\",\"b_dc_ok\":\"No significant DC offset (limit of this check: \u221260\u00a0dBFS).\",\"b_dc_hoch\":\"The mean value lies at \u221260\u00a0dBFS or above. A DC offset takes headroom and turns the start and the end of the file into a step.\",\"b_stille_ganz\":\"The file contains no sample above \u221260\u00a0dBFS.\",\"b_stille_anfang_ok\":\"Time before the first sample above \u221260\u00a0dBFS.\",\"b_stille_anfang_lang\":\"At least one second passes before the first sample above \u221260\u00a0dBFS, and a player plays this silence before the music.\",\"b_stille_ende_ok\":\"Time after the last sample above \u221260\u00a0dBFS.\",\"b_stille_ende_lang\":\"At least five seconds remain after the last sample above \u221260\u00a0dBFS; on an album they lengthen the pause before the next track.\",\"b_sprung_ok\":\"The file starts and ends below \u221240\u00a0dBFS, so playback begins and stops without a step.\",\"b_sprung_anfang\":\"The first sample lies at \u221240\u00a0dBFS or above. Playback starts with a jump from silence to that value, audible as a click.\",\"b_sprung_ende\":\"The last sample lies at \u221240\u00a0dBFS or above. Playback ends with a jump into silence, audible as a click.\",\"b_sprung_beide\":\"The first and the last sample lie at \u221240\u00a0dBFS or above: jumps at both ends, audible as clicks.\",\"b_kanal_ok\":\"The channels differ, and both carry signal.\",\"b_kanal_gleich\":\"Left and right are identical sample for sample: a mono signal in a stereo file.\",\"b_kanal_stumm\":\"Channel {kanal} is silent while the other carries signal.\",\"b_titel_da\":\"Found in: {ort}.\",\"b_titel_fehlt\":\"No title in the file. The distributor takes the title from its upload form; a tag helps wherever the file travels on its own.\",\"b_kuenstler_da\":\"Found in: {ort}.\",\"b_kuenstler_fehlt\":\"No artist in the file. Here too the upload form is what counts for the distributor; the tag goes with the file everywhere else.\",\"b_isrc_ok\":\"Well-formed code, stored in the field intended for it ({ort}).\",\"b_isrc_txxx\":\"The code sits in a TXXX frame named ISRC, which is what ffmpeg writes for -metadata ISRC=\u2026. ID3v2.4 provides the frame TSRC for it.\",\"b_isrc_riff\":\"According to ExifTool\\u0027s tag reference, the RIFF INFO field ISRC stands for \u201cSource\u201d, not for the recording code. A program reading this file cannot rely on it.\",\"b_isrc_form\":\"The value ({ort}) is not a well-formed ISRC: two letters, three letters or digits, two digits for the year and five digits.\",\"b_isrc_mehrere\":\"The file carries {n} different ISRCs. According to the ISRC Handbook, a recording has exactly one.\",\"b_isrc_fehlt\":\"No ISRC in the file. According to the ISRC Handbook, the code should be encoded together with the recording wherever possible; the distributor delivers it separately via DDEX.\",\"b_cover_fehlt\":\"No embedded cover. Distributors take the cover as a separate upload.\",\"b_cover_format\":\"The embedded image is {typ}; Apple accepts JPEG or PNG.\",\"b_cover_masse\":\"The image size could not be read from the embedded file.\",\"b_cover_quadrat\":\"{breite}\u00a0\u00d7\u00a0{hoehe}\u00a0px is not square; Apple requires a 1:1 aspect ratio.\",\"b_cover_klein\":\"{breite}\u00a0\u00d7\u00a0{hoehe}\u00a0px is below Apple\\u0027s minimum of 1400\u00a0\u00d7\u00a01400\u00a0px.\",\"b_cover_mittel\":\"Above Apple\\u0027s minimum of 1400\u00a0\u00d7\u00a01400\u00a0px, below the recommended 3000\u00a0\u00d7\u00a03000\u00a0px.\",\"b_cover_ok\":\"Square and at least 3000\u00a0\u00d7\u00a03000\u00a0px, as Apple recommends.\",\"knopf_kopieren\":\"Copy result\",\"kopiert\":\"copied\",\"kopieren_fehler\":\"copying not possible\",\"fehler_format\":\"This file is not one of the formats read here: WAV, RF64, AIFF, FLAC, MP3, M4A, Ogg Vorbis or Opus.\",\"fehler_lesen\":\"The file could not be read.\",\"h_erklaerung\":\"What is checked and why\",\"erkl_format\":\"Apple Music takes audio at 44.1, 48, 88.2, 96, 176.4 or 192\u00a0kHz with 16 or 24\u00a0bits and asks for the master in the highest resolution available. A lossy file does not meet that: the encoder has already discarded parts of the signal, and the service encodes once more.\",\"erkl_bits\":\"Bits in use: for every sample the lowest bits are checked. If they are zero throughout the file, the extra depth carries nothing, as with a 16-bit master saved as 24\u00a0bits. Apple Digital Masters explicitly rules out this padding. In floating-point files the check asks whether every value lies on the 16-bit or the 24-bit grid.\",\"erkl_spitzen\":\"Full scale is the highest integer value, and three or more such samples in a row count as a clipped passage. The true peak follows ITU-R BS.1770 with the 48-coefficient filter at four times oversampling. It finds peaks between the samples that a conversion or an encoder turns into real ones.\",\"erkl_stille\":\"Silence here means below \u221260\u00a0dBFS. The limits of one second at the start, five seconds at the end, \u221240\u00a0dBFS for the first and the last sample and \u221260\u00a0dBFS for the DC offset are rules of this check, not requirements of a service. They mark what is worth a listen before the upload.\",\"erkl_tags\":\"Tags are read from ID3v2 and ID3v1, from Vorbis comments in FLAC, Ogg and Opus, from RIFF INFO and ID3 chunks in WAV and AIFF, and from the MP4 metadata. For the ISRC the field matters: TSRC in ID3, ISRC in Vorbis comments. ffmpeg writes other fields for -metadata ISRC=\u2026, as the article on release metadata measured.\",\"erkl_cover\":\"Apple recommends 3000\u00a0\u00d7\u00a03000\u00a0pixels or more, requires at least 1400\u00a0\u00d7\u00a01400 and a square image, as JPEG or PNG. The check uses the embedded image; the cover uploaded to the distributor is a separate file.\",\"quellen_titel\":\"Sources\",\"grenze\":\"The requirements quoted are Apple\\u0027s; other distributors publish their own. Formats other than WAV and AIFF depend on the browser\\u0027s decoder, which in some browsers cannot read ALAC or compressed AIFC. Loudness is measured by the LUFS meter.\"};<\/p>\n<p>    \/\/ KERN-ANFANG ------------------------------------------------------------\n    \/\/ Leser und Messung ohne Dokument: derselbe Text laeuft im Browser und in Node.<\/p>\n<p>    \/\/ Apple Music Provider Support, \"Digital packaging for music\": zulaessige Raten.\n    var RATEN_APPLE = [44100, 48000, 88200, 96000, 176400, 192000];\n    var STILLE = Math.pow(10, -60 \/ 20);          \/\/ Grenze fuer Stille: -60 dBFS\n    var SPRUNG = Math.pow(10, -40 \/ 20);          \/\/ erster\/letzter Wert ab -40 dBFS gilt als Sprung\n    var DC_GRENZE = Math.pow(10, -60 \/ 20);       \/\/ Gleichanteil ab -60 dBFS wird gemeldet\n    var STILLE_ANFANG_S = 1, STILLE_ENDE_S = 5;   \/\/ Hinweisgrenzen in Sekunden\n    var COVER_EMPFOHLEN = 3000, COVER_MINDEST = 1400;<\/p>\n<p>    \/\/ ITU-R BS.1770-5, Anhang 2: 4-fach ueberabtastendes FIR, vier Phasen zu je zwoelf Werten.\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>    \/\/ ---- Bytes --------------------------------------------------------------\n    function str(u8, o, l) {\n        var s = '';\n        for (var i = 0; i < l &#038;&#038; o + i < u8.length; i++) { s += String.fromCharCode(u8[o + i]); }\n        return s;\n    }\n    function be16(u8, o) { return (u8[o] << 8) | u8[o + 1]; }\n    function be32(u8, o) { return ((u8[o] << 24) >>> 0) + (u8[o + 1] << 16) + (u8[o + 2] << 8) + u8[o + 3]; }\n    function le16(u8, o) { return u8[o] | (u8[o + 1] << 8); }\n    function le32(u8, o) { return (u8[o] | (u8[o + 1] << 8) | (u8[o + 2] << 16)) + ((u8[o + 3] << 24) >>> 0); }\n    function syncsafe(u8, o) {\n        return ((u8[o] & 0x7F) << 21) | ((u8[o + 1] &#038; 0x7F) << 14) | ((u8[o + 2] &#038; 0x7F) << 7) | (u8[o + 3] &#038; 0x7F);\n    }\n\n    function utf8(u8) {\n        if (typeof TextDecoder !== 'undefined') { return new TextDecoder('utf-8').decode(u8); }\n        var s = str(u8, 0, u8.length);\n        try { return decodeURIComponent(escape(s)); } catch (e) { return s; }\n    }\n    \/\/ art wie im ID3-Kodierungsbyte: 0 Latin-1, 1 UTF-16 mit BOM, 2 UTF-16BE, 3 UTF-8\n    function text(u8, art) {\n        var s = '', i;\n        if (art === 3) { s = utf8(u8); }\n        else if (art === 1 || art === 2) {\n            var gross = art === 2;\n            i = 0;\n            if (u8.length >= 2 && u8[0] === 0xFF && u8[1] === 0xFE) { gross = false; i = 2; }\n            else if (u8.length >= 2 && u8[0] === 0xFE && u8[1] === 0xFF) { gross = true; i = 2; }\n            for (; i + 1 < u8.length; i += 2) {\n                s += String.fromCharCode(gross ? (u8[i] << 8) | u8[i + 1] : u8[i] | (u8[i + 1] << 8));\n            }\n        } else { s = str(u8, 0, u8.length); }\n        \/\/ ID3v2.4 trennt mehrere Werte mit Nullzeichen; der erste zaehlt.\n        var teile = s.split('\\u0000');\n        for (i = 0; i < teile.length; i++) { if (teile[i].trim() !== '') { return teile[i].trim(); } }\n        return '';\n    }\n    function nullStelle(u8, p, breit) {\n        if (breit) {\n            for (var i = p; i + 1 < u8.length; i += 2) { if (u8[i] === 0 &#038;&#038; u8[i + 1] === 0) { return i; } }\n            return u8.length;\n        }\n        for (var j = p; j < u8.length; j++) { if (u8[j] === 0) { return j; } }\n        return u8.length;\n    }\n    function base64(s) {\n        s = s.replace(\/[^A-Za-z0-9+\\\/=]\/g, '');\n        var roh = typeof atob === 'function' ? atob(s) : Buffer.from(s, 'base64').toString('binary');\n        var u8 = new Uint8Array(roh.length);\n        for (var i = 0; i < roh.length; i++) { u8[i] = roh.charCodeAt(i); }\n        return u8;\n    }\n\n    \/\/ ---- Bilder -------------------------------------------------------------\n    function bildMasse(u8) {\n        if (u8.length >= 24 && u8[0] === 0x89 && u8[1] === 0x50 && u8[2] === 0x4E && u8[3] === 0x47) {\n            return { typ: 'PNG', breite: be32(u8, 16), hoehe: be32(u8, 20) };\n        }\n        if (u8.length >= 4 && u8[0] === 0xFF && u8[1] === 0xD8) {\n            var p = 2;\n            while (p + 9 < u8.length) {\n                if (u8[p] !== 0xFF) { p++; continue; }\n                var m = u8[p + 1];\n                if (m === 0xFF) { p++; continue; }\n                if (m === 0xD8 || m === 0x01 || (m >= 0xD0 && m <= 0xD7)) { p += 2; continue; }\n                if (m >= 0xC0 && m <= 0xCF &#038;&#038; m !== 0xC4 &#038;&#038; m !== 0xC8 &#038;&#038; m !== 0xCC) {\n                    return { typ: 'JPEG', hoehe: be16(u8, p + 5), breite: be16(u8, p + 7) };\n                }\n                if (m === 0xDA) { break; }\n                p += 2 + be16(u8, p + 2);\n            }\n            return { typ: 'JPEG', breite: null, hoehe: null };\n        }\n        if (u8.length >= 2 && u8[0] === 0x42 && u8[1] === 0x4D) { return { typ: 'BMP', breite: null, hoehe: null }; }\n        if (u8.length >= 3 && u8[0] === 0x47 && u8[1] === 0x49 && u8[2] === 0x46) { return { typ: 'GIF', breite: le16(u8, 6), hoehe: le16(u8, 8) }; }\n        return { typ: null, breite: null, hoehe: null };\n    }\n    function bildEintrag(daten, ort, bildtyp, breite, hoehe) {\n        var m = bildMasse(daten);\n        return { ort: ort, bildtyp: bildtyp === undefined ? null : bildtyp, typ: m.typ,\n                 breite: m.breite || breite || null, hoehe: m.hoehe || hoehe || null, bytes: daten.length };\n    }<\/p>\n<p>    function neueTags() { return { titel: [], kuenstler: [], isrc: [], cover: [] }; }<\/p>\n<p>    \/\/ ---- ID3v2 und ID3v1 ----------------------------------------------------\n    function entSync(u8) {\n        var raus = new Uint8Array(u8.length), n = 0;\n        for (var i = 0; i < u8.length; i++) {\n            raus[n++] = u8[i];\n            if (u8[i] === 0xFF &#038;&#038; u8[i + 1] === 0x00) { i++; }\n        }\n        return raus.subarray(0, n);\n    }\n\n    function id3Rahmen(id, b, tags) {\n        if (!b.length) { return; }\n        var art = b[0], breit = art === 1 || art === 2, e;\n        if (id === 'TIT2' || id === 'TT2') { tags.titel.push({ wert: text(b.subarray(1), art), ort: 'id3' }); }\n        else if (id === 'TPE1' || id === 'TP1') { tags.kuenstler.push({ wert: text(b.subarray(1), art), ort: 'id3' }); }\n        else if (id === 'TSRC' || id === 'TRC') { tags.isrc.push({ wert: text(b.subarray(1), art), ort: 'tsrc' }); }\n        else if (id === 'TXXX' || id === 'TXX') {\n            e = nullStelle(b, 1, breit);\n            var beschreibung = text(b.subarray(1, e), art);\n            if (\/^isrc$\/i.test(beschreibung)) {\n                tags.isrc.push({ wert: text(b.subarray(e + (breit ? 2 : 1)), art), ort: 'txxx' });\n            }\n        } else if (id === 'APIC') {\n            var m = nullStelle(b, 1, false);\n            var bildtyp = b[m + 1];\n            e = nullStelle(b, m + 2, breit);\n            tags.cover.push(bildEintrag(b.subarray(e + (breit ? 2 : 1)), 'id3', bildtyp));\n        } else if (id === 'PIC') {\n            e = nullStelle(b, 5, breit);\n            tags.cover.push(bildEintrag(b.subarray(e + (breit ? 2 : 1)), 'id3', b[4]));\n        }\n    }\n\n    \/\/ Gibt die Laenge des Tags zurueck, 0 wenn an o keiner beginnt.\n    function id3Lesen(u8, o, tags) {\n        if (o + 10 > u8.length || u8[o] !== 0x49 || u8[o + 1] !== 0x44 || u8[o + 2] !== 0x33) { return 0; }\n        var ver = u8[o + 3], flaggen = u8[o + 5], groesse = syncsafe(u8, o + 6);\n        if (ver < 2 || ver > 4) { return 0; }\n        var d = u8.subarray(o + 10, Math.min(u8.length, o + 10 + groesse));\n        if ((flaggen & 0x80) && ver < 4) { d = entSync(d); }\n        var p = 0;\n        if ((flaggen &#038; 0x40) &#038;&#038; ver >= 3) { p += ver === 4 ? syncsafe(d, 0) : be32(d, 0) + 4; }\n        var kopf = ver === 2 ? 6 : 10;\n        while (p + kopf <= d.length) {\n            var id, fg;\n            if (ver === 2) { id = str(d, p, 3); fg = (d[p + 3] << 16) | (d[p + 4] << 8) | d[p + 5]; }\n            else { id = str(d, p, 4); fg = ver === 4 ? syncsafe(d, p + 4) : be32(d, p + 4); }\n            if (!\/^[A-Z0-9]{3,4}$\/.test(id) || fg <= 0 || p + kopf + fg > d.length) { break; }\n            var format = ver >= 3 ? d[p + 9] : 0;\n            var inhalt = d.subarray(p + kopf, p + kopf + fg);\n            var gesperrt = ver === 4 ? (format & 0x0C) : (ver === 3 ? (format & 0xC0) : 0);\n            if (ver === 4 && (format & 0x02)) { inhalt = entSync(inhalt); }\n            if (ver === 4 && (format & 0x01)) { inhalt = inhalt.subarray(4); }\n            if (!gesperrt) { id3Rahmen(id, inhalt, tags); }\n            p += kopf + fg;\n        }\n        return 10 + groesse + (flaggen & 0x10 ? 10 : 0);\n    }<\/p>\n<p>    function id3v1Lesen(u8, tags) {\n        if (u8.length < 128) { return; }\n        var o = u8.length - 128;\n        if (str(u8, o, 3) !== 'TAG') { return; }\n        var titel = text(u8.subarray(o + 3, o + 33), 0), kuenstler = text(u8.subarray(o + 33, o + 63), 0);\n        if (titel) { tags.titel.push({ wert: titel, ort: 'id3v1' }); }\n        if (kuenstler) { tags.kuenstler.push({ wert: kuenstler, ort: 'id3v1' }); }\n    }\n\n    \/\/ ---- Vorbis-Kommentar und FLAC-Bild -------------------------------------\n    function flacBild(u8, p, ende, tags, ort) {\n        if (p + 32 > ende) { return; }\n        var bildtyp = be32(u8, p), ml = be32(u8, p + 4);\n        var q = p + 8 + ml, bl = be32(u8, q);\n        q += 4 + bl;\n        var breite = be32(u8, q), hoehe = be32(u8, q + 4), dl = be32(u8, q + 16);\n        var daten = u8.subarray(q + 20, Math.min(ende, q + 20 + dl));\n        tags.cover.push(bildEintrag(daten, ort, bildtyp, breite, hoehe));\n    }<\/p>\n<p>    function vorbisKommentar(u8, p, ende, tags) {\n        if (p + 8 > ende) { return; }\n        p += 4 + le32(u8, p);\n        var anzahl = le32(u8, p);\n        p += 4;\n        for (var k = 0; k < anzahl &#038;&#038; p + 4 <= ende; k++) {\n            var l = le32(u8, p);\n            if (p + 4 + l > ende) { break; }\n            var eintrag = u8.subarray(p + 4, p + 4 + l);\n            p += 4 + l;\n            var gleich = -1;\n            for (var i = 0; i < eintrag.length &#038;&#038; i < 64; i++) { if (eintrag[i] === 0x3D) { gleich = i; break; } }\n            if (gleich < 1) { continue; }\n            var name = str(eintrag, 0, gleich).toUpperCase();\n            if (name === 'METADATA_BLOCK_PICTURE') {\n                var bild = base64(str(eintrag, gleich + 1, eintrag.length - gleich - 1));\n                flacBild(bild, 0, bild.length, tags, 'vorbis_bild');\n            } else if (name === 'COVERART') {\n                tags.cover.push(bildEintrag(base64(str(eintrag, gleich + 1, eintrag.length - gleich - 1)), 'vorbis_bild', 3));\n            } else if (name === 'TITLE' || name === 'ARTIST' || name === 'ISRC') {\n                var wert = utf8(eintrag.subarray(gleich + 1)).trim();\n                if (name === 'TITLE') { tags.titel.push({ wert: wert, ort: 'vorbis' }); }\n                else if (name === 'ARTIST') { tags.kuenstler.push({ wert: wert, ort: 'vorbis' }); }\n                else { tags.isrc.push({ wert: wert, ort: 'vorbis' }); }\n            }\n        }\n    }\n\n    \/\/ ---- WAV, RF64, AIFF: Kopf, Tags und Abtastwerte ------------------------\n    \/\/ Liest die Werte selbst, damit nichts umgerechnet wird, und haelt dabei fest,\n    \/\/ welche Bits ueberhaupt vorkommen (Bit-Padding) und ob Gleitkommawerte genau\n    \/\/ auf ein 16- oder 24-Bit-Raster fallen.\n    function pcmLesen(puffer, start, laenge, anzahl, bytes, art, klein) {\n        var rahmen = Math.floor(laenge \/ (bytes * anzahl)), raus = [], c, i, k, v, s;\n        for (c = 0; c < anzahl; c++) { raus.push(new Float32Array(rahmen)); }\n        var oder = 0, alle16 = true, alle24 = true, n = rahmen * anzahl;\n        if (art === 'float') {\n            var dvf = new DataView(puffer, start, n * bytes);\n            for (i = 0, k = 0; i < rahmen; i++) {\n                for (c = 0; c < anzahl; c++, k++) {\n                    v = bytes === 4 ? dvf.getFloat32(k * 4, klein) : dvf.getFloat64(k * 8, klein);\n                    raus[c][i] = v;\n                    if (alle24) {\n                        s = v * 8388608;\n                        if (s !== Math.floor(s)) { alle24 = false; alle16 = false; }\n                        else if (alle16) { s = v * 32768; if (s !== Math.floor(s)) { alle16 = false; } }\n                    }\n                }\n            }\n            return { kanaele: raus, oder: null, alle16: alle16, alle24: alle24 };\n        }\n        var teiler = Math.pow(2, bytes * 8 - 1);\n        if (klein &#038;&#038; (bytes === 2 || bytes === 4)) {\n            var stueck = puffer.slice(start, start + n * bytes);\n            var a = bytes === 2 ? new Int16Array(stueck) : new Int32Array(stueck);\n            for (i = 0, k = 0; i < rahmen; i++) {\n                for (c = 0; c < anzahl; c++, k++) { v = a[k]; oder |= v; raus[c][i] = v \/ teiler; }\n            }\n            return { kanaele: raus, oder: oder, alle16: null, alle24: null };\n        }\n        var u8 = new Uint8Array(puffer, start, n * bytes), dv = new DataView(puffer, start, n * bytes), o = 0;\n        for (i = 0; i < rahmen; i++) {\n            for (c = 0; c < anzahl; c++) {\n                if (bytes === 1) { v = klein ? u8[o] - 128 : dv.getInt8(o); }\n                else if (bytes === 2) { v = dv.getInt16(o, klein); }\n                else if (bytes === 3) {\n                    v = klein ? (u8[o] | (u8[o + 1] << 8) | (u8[o + 2] << 16)) : ((u8[o] << 16) | (u8[o + 1] << 8) | u8[o + 2]);\n                    if (v &#038; 0x800000) { v -= 0x1000000; }\n                } else { v = dv.getInt32(o, klein); }\n                oder |= v;\n                raus[c][i] = v \/ teiler;\n                o += bytes;\n            }\n        }\n        return { kanaele: raus, oder: oder, alle16: null, alle24: null };\n    }\n\n    function riffInfo(u8, p, ende, tags) {\n        while (p + 8 <= ende) {\n            var id = str(u8, p, 4), l = le32(u8, p + 4);\n            var wert = text(u8.subarray(p + 8, Math.min(ende, p + 8 + l)), 3);\n            if (id === 'INAM') { tags.titel.push({ wert: wert, ort: 'riff' }); }\n            else if (id === 'IART') { tags.kuenstler.push({ wert: wert, ort: 'riff' }); }\n            else if (id === 'ISRC') { tags.isrc.push({ wert: wert, ort: 'riff_isrc' }); }\n            p += 8 + l + (l &#038; 1);\n        }\n    }\n\n    function wavLesen(puffer, info) {\n        var u8 = new Uint8Array(puffer), n = u8.length, p = 12, fmt = null, ds64 = null, daten = null;\n        while (p + 8 <= n) {\n            var id = str(u8, p, 4), l = le32(u8, p + 4);\n            if (id === 'ds64' &#038;&#038; l >= 16) { ds64 = le32(u8, p + 16) + le32(u8, p + 20) * 4294967296; }\n            if (id === 'data' && l === 0xFFFFFFFF && ds64 !== null) { l = ds64; }\n            if (id === 'fmt ') {\n                var tag = le16(u8, p + 8);\n                if (tag === 0xFFFE && l >= 26) { tag = le16(u8, p + 32); }\n                fmt = { tag: tag, anzahl: le16(u8, p + 10), rate: le32(u8, p + 12), bits: le16(u8, p + 22) };\n            } else if (id === 'data') {\n                daten = { start: p + 8, laenge: Math.min(l, n - p - 8) };\n            } else if (id === 'LIST' && str(u8, p + 8, 4) === 'INFO') {\n                riffInfo(u8, p + 12, Math.min(n, p + 8 + l), info.tags);\n            } else if (id === 'id3 ' || id === 'ID3 ') {\n                id3Lesen(u8, p + 8, info.tags);\n            }\n            if (l === 0xFFFFFFFF) { break; }\n            p += 8 + l + (l & 1);\n        }\n        if (!fmt) { info.fehler = 'fmt'; return info; }\n        info.rate = fmt.rate;\n        info.kanaeleAnzahl = fmt.anzahl;\n        info.bits = fmt.bits;\n        info.gleitkomma = fmt.tag === 3;\n        info.verlustfrei = fmt.tag === 1 || fmt.tag === 3;\n        info.ganzzahl = fmt.tag === 1;\n        var bytes = fmt.bits \/ 8;\n        if (!daten || !info.verlustfrei || !fmt.anzahl || (fmt.tag === 1 && [1, 2, 3, 4].indexOf(bytes) === -1) ||\n            (fmt.tag === 3 && bytes !== 4 && bytes !== 8)) {\n            info.fehler = daten ? 'codec' : 'data';\n            return info;\n        }\n        var w = pcmLesen(puffer, daten.start, daten.laenge, fmt.anzahl, bytes, fmt.tag === 3 ? 'float' : 'int', true);\n        info.kanaele = w.kanaele;\n        info.oder = w.oder; info.alle16 = w.alle16; info.alle24 = w.alle24;\n        return info;\n    }<\/p>\n<p>    function erweitert80(u8, o) {\n        var exp = be16(u8, o) & 0x7FFF, hi = be32(u8, o + 2), lo = be32(u8, o + 6);\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, info, aifc) {\n        var u8 = new Uint8Array(puffer), n = u8.length, p = 12, comm = null, ssnd = null;\n        while (p + 8 <= n) {\n            var id = str(u8, p, 4), l = be32(u8, p + 4);\n            if (id === 'COMM') {\n                comm = { anzahl: be16(u8, p + 8), bits: be16(u8, p + 14), rate: Math.round(erweitert80(u8, p + 16)), art: 'NONE' };\n                if (aifc &#038;&#038; l >= 22) { comm.art = str(u8, p + 26, 4); }\n            } else if (id === 'SSND') {\n                var versatz = be32(u8, p + 8);\n                ssnd = { start: p + 16 + versatz, laenge: Math.min(l - 8 - versatz, n - p - 16 - versatz) };\n            } else if (id === 'NAME') { info.tags.titel.push({ wert: text(u8.subarray(p + 8, p + 8 + l), 0), ort: 'aiff' }); }\n            else if (id === 'AUTH') { info.tags.kuenstler.push({ wert: text(u8.subarray(p + 8, p + 8 + l), 0), ort: 'aiff' }); }\n            else if (id === 'ID3 ' || id === 'id3 ') { id3Lesen(u8, p + 8, info.tags); }\n            p += 8 + l + (l & 1);\n        }\n        if (!comm) { info.fehler = 'COMM'; return info; }\n        info.rate = comm.rate;\n        info.kanaeleAnzahl = comm.anzahl;\n        var art = comm.art, gleit = \/^(fl32|FL32|fl64|FL64)$\/.test(art);\n        info.bits = gleit ? (art.slice(2) === '32' ? 32 : 64) : comm.bits;\n        info.gleitkomma = gleit;\n        info.ganzzahl = art === 'NONE' || art === 'twos' || art === 'sowt';\n        info.verlustfrei = info.ganzzahl || gleit;\n        if (!info.verlustfrei) { info.format = 'AIFC (' + art.trim() + ')'; info.verlustfrei = null; info.fehler = 'codec'; return info; }\n        if (!ssnd) { info.fehler = 'SSND'; return info; }\n        var bytes = gleit ? info.bits \/ 8 : Math.ceil(comm.bits \/ 8);\n        var w = pcmLesen(puffer, ssnd.start, ssnd.laenge, comm.anzahl, bytes, gleit ? 'float' : 'int', art === 'sowt');\n        info.kanaele = w.kanaele;\n        \/\/ Bei weniger als voller Byte-Breite (etwa 20 Bit) sind die unteren Bits laut Norm leer.\n        info.oder = w.oder === null ? null : w.oder >> (bytes * 8 - comm.bits);\n        info.alle16 = w.alle16; info.alle24 = w.alle24;\n        return info;\n    }<\/p>\n<p>    \/\/ ---- FLAC ---------------------------------------------------------------\n    function flacLesen(u8, o, info) {\n        var p = o + 4, letzter = false;\n        info.format = 'FLAC';\n        info.verlustfrei = true;\n        info.ganzzahl = true;\n        while (!letzter && p + 4 <= u8.length) {\n            var kopf = u8[p], typ = kopf &#038; 0x7F, l = (u8[p + 1] << 16) | (u8[p + 2] << 8) | u8[p + 3];\n            letzter = (kopf &#038; 0x80) !== 0;\n            var d = p + 4, ende = Math.min(u8.length, d + l);\n            if (typ === 0 &#038;&#038; l >= 18) {\n                info.rate = (u8[d + 10] << 12) | (u8[d + 11] << 4) | (u8[d + 12] >> 4);\n                info.kanaeleAnzahl = ((u8[d + 12] >> 1) & 7) + 1;\n                info.bits = (((u8[d + 12] & 1) << 4) | (u8[d + 13] >> 4)) + 1;\n                info.abtastwerte = (u8[d + 13] & 0x0F) * 4294967296 + be32(u8, d + 14);\n            } else if (typ === 4) { vorbisKommentar(u8, d, ende, info.tags); }\n            else if (typ === 6) { flacBild(u8, d, ende, info.tags, 'flac_bild'); }\n            p = ende;\n        }\n        return info;\n    }<\/p>\n<p>    \/\/ ---- Ogg ----------------------------------------------------------------\n    function oggPakete(u8, hoechstens) {\n        var pakete = [], teile = [], p = 0;\n        function verbinden() {\n            var l = 0, i;\n            for (i = 0; i < teile.length; i++) { l += teile[i].length; }\n            var r = new Uint8Array(l), o = 0;\n            for (i = 0; i < teile.length; i++) { r.set(teile[i], o); o += teile[i].length; }\n            teile = [];\n            return r;\n        }\n        while (p + 27 <= u8.length &#038;&#038; pakete.length < hoechstens) {\n            if (str(u8, p, 4) !== 'OggS') { break; }\n            var segmente = u8[p + 26], q = p + 27 + segmente;\n            for (var s = 0; s < segmente &#038;&#038; pakete.length < hoechstens; s++) {\n                var l = u8[p + 27 + s];\n                teile.push(u8.subarray(q, Math.min(u8.length, q + l)));\n                q += l;\n                if (l < 255) { pakete.push(verbinden()); }\n            }\n            p = q;\n        }\n        return pakete;\n    }\n\n    function oggLesen(u8, info) {\n        var pakete = oggPakete(u8, 6);\n        if (!pakete.length) { info.fehler = 'ogg'; return info; }\n        var erst = pakete[0];\n        if (erst[0] === 1 &#038;&#038; str(erst, 1, 6) === 'vorbis') {\n            info.format = 'Ogg Vorbis'; info.verlustfrei = false;\n            info.kanaeleAnzahl = erst[11]; info.rate = le32(erst, 12);\n            if (pakete[1] &#038;&#038; pakete[1][0] === 3 &#038;&#038; str(pakete[1], 1, 6) === 'vorbis') {\n                vorbisKommentar(pakete[1], 7, pakete[1].length, info.tags);\n            }\n        } else if (str(erst, 0, 8) === 'OpusHead') {\n            info.format = 'Opus'; info.verlustfrei = false;\n            info.kanaeleAnzahl = erst[9]; info.rate = 48000;\n            if (pakete[1] &#038;&#038; str(pakete[1], 0, 8) === 'OpusTags') {\n                vorbisKommentar(pakete[1], 8, pakete[1].length, info.tags);\n            }\n        } else if (erst[0] === 0x7F &#038;&#038; str(erst, 1, 4) === 'FLAC' &#038;&#038; str(erst, 9, 4) === 'fLaC') {\n            var s = 17;\n            info.format = 'Ogg FLAC'; info.verlustfrei = true; info.ganzzahl = true;\n            info.rate = (erst[s + 10] << 12) | (erst[s + 11] << 4) | (erst[s + 12] >> 4);\n            info.kanaeleAnzahl = ((erst[s + 12] >> 1) & 7) + 1;\n            info.bits = (((erst[s + 12] & 1) << 4) | (erst[s + 13] >> 4)) + 1;\n            for (var i = 1; i < pakete.length; i++) {\n                var t = pakete[i][0] &#038; 0x7F, l = (pakete[i][1] << 16) | (pakete[i][2] << 8) | pakete[i][3];\n                if (t === 4) { vorbisKommentar(pakete[i], 4, Math.min(pakete[i].length, 4 + l), info.tags); }\n                else if (t === 6) { flacBild(pakete[i], 4, Math.min(pakete[i].length, 4 + l), info.tags, 'flac_bild'); }\n            }\n        } else { info.fehler = 'ogg'; }\n        return info;\n    }\n\n    \/\/ ---- MP4 \/ M4A ----------------------------------------------------------\n    function mp4Lesen(u8, info) {\n        var schluessel = null;\n        info.format = 'MP4';\n        function eintrag(name, von, bis) {\n            var werte = [], frei = null, p = von;\n            while (p + 8 <= bis) {\n                var g = be32(u8, p), n = str(u8, p + 4, 4);\n                if (g < 8 || p + g > bis) { break; }\n                if (n === 'name') { frei = str(u8, p + 12, g - 12); }\n                else if (n === 'data') { werte.push({ typ: be32(u8, p + 8) & 0xFFFFFF, daten: u8.subarray(p + 16, p + g) }); }\n                p += g;\n            }\n            var schl = name;\n            if (name === '----' && frei) { schl = '----:' + frei; }\n            werte.forEach(function (w) {\n                if (schl === '\u00a9nam' || \/^(title|com\\.apple\\.quicktime\\.title)$\/i.test(schl)) {\n                    info.tags.titel.push({ wert: utf8(w.daten).trim(), ort: schl.length === 4 ? 'mp4' : 'mdta' });\n                } else if (schl === '\u00a9ART' || \/^(artist|com\\.apple\\.quicktime\\.artist)$\/i.test(schl)) {\n                    info.tags.kuenstler.push({ wert: utf8(w.daten).trim(), ort: schl.length === 4 ? 'mp4' : 'mdta' });\n                } else if (schl === 'covr' || \/artwork$\/i.test(schl)) {\n                    info.tags.cover.push(bildEintrag(w.daten, 'mp4', 3));\n                } else if (\/^----:isrc$\/i.test(schl)) {\n                    info.tags.isrc.push({ wert: utf8(w.daten).trim(), ort: 'mp4_frei' });\n                } else if (\/(^|\\.)isrc$\/i.test(schl)) {\n                    info.tags.isrc.push({ wert: utf8(w.daten).trim(), ort: 'mdta' });\n                }\n            });\n        }\n        function ilst(von, bis) {\n            var p = von;\n            while (p + 8 <= bis) {\n                var g = be32(u8, p);\n                if (g < 8 || p + g > bis) { break; }\n                var name = str(u8, p + 4, 4);\n                if (u8[p + 4] === 0 && u8[p + 5] === 0 && schluessel) {\n                    var nr = be32(u8, p + 4);\n                    name = schluessel[nr - 1] || name;\n                }\n                eintrag(name, p + 8, p + g);\n                p += g;\n            }\n        }\n        function kaesten(von, bis, tiefe) {\n            var p = von;\n            while (p + 8 <= bis &#038;&#038; tiefe < 12) {\n                var g = be32(u8, p), name = str(u8, p + 4, 4), innen = p + 8;\n                if (g === 1) { g = be32(u8, p + 8) * 4294967296 + be32(u8, p + 12); innen = p + 16; }\n                if (g === 0) { g = bis - p; }\n                if (g < 8 || p + g > bis) { break; }\n                var ende = p + g;\n                if (\/^(moov|trak|mdia|minf|stbl|udta)$\/.test(name)) { kaesten(innen, ende, tiefe + 1); }\n                else if (name === 'meta') {\n                    \/\/ iTunes: volle Box mit vier Bytes Version; QuickTime: ohne.\n                    kaesten(str(u8, innen + 4, 4) === 'hdlr' ? innen : innen + 4, ende, tiefe + 1);\n                } else if (name === 'mdhd') {\n                    var v = u8[innen];\n                    info.rate = v === 1 ? be32(u8, innen + 20) : be32(u8, innen + 12);\n                } else if (name === 'stsd' && !info.codec) {\n                    var e = innen + 8, c = e + 8;\n                    info.codec = str(u8, e + 4, 4);\n                    info.kanaeleAnzahl = be16(u8, c + 16);\n                    var tiefeBits = be16(u8, c + 18);\n                    if (info.codec === 'alac') {\n                        var innenAlac = c + 28;\n                        if (str(u8, innenAlac + 4, 4) === 'alac') { tiefeBits = u8[innenAlac + 17] || tiefeBits; }\n                        info.bits = tiefeBits;\n                    } else if (info.codec === 'fLaC') { info.bits = tiefeBits; }\n                } else if (name === 'keys') {\n                    var anzahl = be32(u8, innen + 4), q = innen + 8;\n                    schluessel = [];\n                    for (var k = 0; k < anzahl &#038;&#038; q + 8 <= ende; k++) {\n                        var kl = be32(u8, q);\n                        if (kl < 8) { break; }\n                        schluessel.push(str(u8, q + 8, kl - 8));\n                        q += kl;\n                    }\n                } else if (name === 'ilst') { ilst(innen, ende); }\n                p = ende;\n            }\n        }\n        kaesten(0, u8.length, 0);\n        if (info.codec === 'alac' || info.codec === 'fLaC') { info.verlustfrei = true; info.ganzzahl = true; info.format = info.codec === 'alac' ? 'ALAC (MP4)' : 'FLAC (MP4)'; }\n        else if (info.codec === 'mp4a' || info.codec === 'Opus' || info.codec === '.mp3') { info.verlustfrei = false; info.format = info.codec === 'mp4a' ? 'AAC (MP4)' : info.codec + ' (MP4)'; }\n        else { info.verlustfrei = null; }\n        return info;\n    }\n\n    \/\/ ---- MP3 und AAC (ADTS) -------------------------------------------------\n    function mpegLesen(u8, info) {\n        var start = 0, l;\n        while ((l = id3Lesen(u8, start, info.tags)) > 0) { start += l; }\n        id3v1Lesen(u8, info.tags);\n        var bis = Math.min(u8.length - 4, start + 65536);\n        for (var j = start; j < bis; j++) {\n            if (u8[j] !== 0xFF || (u8[j + 1] &#038; 0xE0) !== 0xE0) { continue; }\n            var version = (u8[j + 1] >> 3) & 3, schicht = (u8[j + 1] >> 1) & 3;\n            if (schicht === 0 && (u8[j + 1] & 0xF6) === 0xF0) {\n                var idx = (u8[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) { continue; }\n                info.format = 'AAC (ADTS)'; info.rate = raten[idx]; info.verlustfrei = false;\n                info.kanaeleAnzahl = ((u8[j + 2] & 1) << 2) | (u8[j + 3] >> 6);\n                return info;\n            }\n            var ri = (u8[j + 2] >> 2) & 3, bri = u8[j + 2] >> 4;\n            if (version === 1 || schicht === 0 || ri === 3 || bri === 15 || bri === 0) { continue; }\n            var basis = [44100, 48000, 32000][ri];\n            info.format = schicht === 1 ? 'MP3' : 'MPEG Audio';\n            info.rate = version === 3 ? basis : (version === 2 ? basis \/ 2 : basis \/ 4);\n            info.kanaeleAnzahl = (u8[j + 3] >> 6) === 3 ? 1 : 2;\n            info.verlustfrei = false;\n            return info;\n        }\n        info.fehler = 'format';\n        return info;\n    }<\/p>\n<p>    \/\/ Liest Kopf, Tags und - bei WAV und AIFF - die Abtastwerte.\n    function lesen(puffer) {\n        var u8 = new Uint8Array(puffer);\n        var info = { format: null, verlustfrei: null, ganzzahl: false, gleitkomma: false, rate: null, bits: null,\n                     kanaeleAnzahl: null, kanaele: null, oder: null, alle16: null, alle24: null,\n                     tags: neueTags(), fehler: null, codec: null };\n        if (u8.length < 16) { info.fehler = 'format'; return info; }\n        var k4 = str(u8, 0, 4), art = str(u8, 8, 4);\n        if ((k4 === 'RIFF' || k4 === 'RF64' || k4 === 'BW64') &#038;&#038; art === 'WAVE') { info.format = k4 === 'RIFF' ? 'WAV' : k4; return wavLesen(puffer, info); }\n        if (k4 === 'FORM' &#038;&#038; (art === 'AIFF' || art === 'AIFC')) { info.format = art; return aiffLesen(puffer, info, art === 'AIFC'); }\n        if (k4 === 'OggS') { return oggLesen(u8, info); }\n        if (str(u8, 4, 4) === 'ftyp') { return mp4Lesen(u8, info); }\n        var o = 0, l;\n        while ((l = id3Lesen(u8, o, info.tags)) > 0) { o += l; }\n        if (str(u8, o, 4) === 'fLaC') { return flacLesen(u8, o, info); }\n        info.tags = neueTags();\n        return mpegLesen(u8, info);\n    }<\/p>\n<p>    \/\/ Bei dekodierten FLAC-Dateien: stehen alle Werte auf dem Raster der\n    \/\/ angegebenen Bit-Tiefe, laesst sich die Oder-Maske auch hier bilden.\n    function oderAusGleitkomma(kanaele, bits) {\n        if (!bits || bits > 24) { return null; }\n        var f = Math.pow(2, bits - 1), oder = 0;\n        for (var c = 0; c < kanaele.length; c++) {\n            var d = kanaele[c];\n            for (var i = 0; i < d.length; i++) {\n                var s = d[i] * f;\n                if (s !== Math.floor(s)) { return null; }\n                oder |= s;\n            }\n        }\n        return oder;\n    }\n\n    function wirksameBits(oder, bits) {\n        if (oder === null || oder === undefined || !bits) { return null; }\n        if (oder === 0) { return 0; }\n        var frei = 0;\n        while (frei < bits &#038;&#038; ((oder >> frei) & 1) === 0) { frei++; }\n        return bits - frei;\n    }<\/p>\n<p>    \/\/ ---- Messung in Scheiben ------------------------------------------------\n    function messungAnlegen(kanaele, fs, schwelleVoll) {\n        var anzahl = kanaele.length, n = anzahl ? kanaele[0].length : 0, k = [];\n        for (var c = 0; c < anzahl; c++) {\n            k.push({ summe: 0, spitze: 0, tp: 0, voll: 0, stellen: 0, lauf: 0, flach: -1, ueber: 0, erster: -1, letzter: -1 });\n        }\n        return { kanaele: kanaele, fs: fs, n: n, anzahl: anzahl, schwelleVoll: schwelleVoll, k: k,\n                 kanal: 0, pos: 0, ring: new Float64Array(24), rp: 0, diffMax: 0 };\n    }\n\n    function tpNachlauf(m, s) {\n        var P0 = TP_PHASEN[0], P1 = TP_PHASEN[1], P2 = TP_PHASEN[2], P3 = TP_PHASEN[3];\n        for (var nach = 0; nach < 12; nach++) {\n            m.ring[m.rp] = 0; m.ring[m.rp + 12] = 0;\n            var o = m.rp + 12, q0 = 0, q1 = 0, q2 = 0, q3 = 0;\n            for (var t = 0; t < 12; t++) {\n                var v = m.ring[o - t];\n                q0 += P0[t] * v; q1 += P1[t] * v; q2 += P2[t] * v; q3 += P3[t] * v;\n            }\n            m.rp = m.rp === 11 ? 0 : m.rp + 1;\n            s.tp = Math.max(s.tp, Math.abs(q0), Math.abs(q1), Math.abs(q2), Math.abs(q3));\n        }\n        s.tp = Math.max(s.tp, s.spitze);\n    }\n\n    \/\/ Verarbeitet hoechstens \"budget\" Abtastwerte; true, wenn alle Kanaele durch sind.\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], s = m.k[c], ende = Math.min(m.n, m.pos + budget);\n            var ring = m.ring, rp = m.rp, sv = m.schwelleVoll;\n            var summe = s.summe, spitze = s.spitze, tp = s.tp, voll = s.voll, stellen = s.stellen, lauf = s.lauf, flach = s.flach;\n            var ueber = s.ueber, erster = s.erster, letzter = s.letzter;\n            var links = c === 1 &#038;&#038; m.anzahl === 2 ? m.kanaele[0] : null, diffMax = m.diffMax;\n            for (var i = m.pos; i < ende; i++) {\n                var x = d[i], ax = x < 0 ? -x : x;\n                summe += x;\n                if (ax > spitze) { spitze = ax; }\n                \/\/ Geclippt ist, was flach abgeschnitten ist: derselbe Betrag an der\n                \/\/ Vollaussteuerung mehrmals hintereinander. Schwankende Werte ueber 1\n                \/\/ in einer Gleitkommadatei sind keine Stelle, sondern Ueberschreitung.\n                if (ax >= sv) {\n                    voll++;\n                    if (ax === flach) { lauf++; } else { lauf = 1; flach = ax; }\n                    if (lauf === 3) { stellen++; }\n                } else { lauf = 0; flach = -1; }\n                if (ax > 1) { ueber++; }\n                if (ax > STILLE) { if (erster < 0) { erster = i; } letzter = i; }\n                if (links) { var df = x - links[i]; if (df < 0) { df = -df; } if (df > diffMax) { diffMax = df; } }<\/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, 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.rp = rp; m.diffMax = diffMax;\n            s.summe = summe; s.spitze = spitze; s.tp = tp; s.voll = voll; s.stellen = stellen; s.lauf = lauf; s.flach = flach;\n            s.ueber = ueber; s.erster = erster; s.letzter = letzter;\n            if (m.pos >= m.n) {\n                tpNachlauf(m, s);\n                m.kanal++; m.pos = 0; m.ring = new Float64Array(24); m.rp = 0;\n            }\n        }\n        return m.kanal >= m.anzahl;\n    }<\/p>\n<p>    function messungAuswerten(m) {\n        var e = { n: m.n, fs: m.fs, anzahl: m.anzahl, dauer: m.n \/ m.fs, spitze: 0, tp: 0, voll: 0, stellen: 0,\n                  ueber: 0, dc: 0, erster: -1, letzter: -1, anfangWert: 0, endeWert: 0,\n                  gleich: false, stumm: [] };\n        for (var c = 0; c < m.anzahl; c++) {\n            var s = m.k[c], d = m.kanaele[c];\n            e.spitze = Math.max(e.spitze, s.spitze);\n            e.tp = Math.max(e.tp, s.tp);\n            e.voll += s.voll; e.stellen += s.stellen; e.ueber += s.ueber;\n            e.dc = Math.max(e.dc, m.n ? Math.abs(s.summe \/ m.n) : 0);\n            if (s.erster >= 0 && (e.erster < 0 || s.erster < e.erster)) { e.erster = s.erster; }\n            if (s.letzter > e.letzter) { e.letzter = s.letzter; }\n            if (m.n) {\n                e.anfangWert = Math.max(e.anfangWert, Math.abs(d[0]));\n                e.endeWert = Math.max(e.endeWert, Math.abs(d[m.n - 1]));\n            }\n        }\n        for (var k = 0; k < m.anzahl; k++) { if (m.k[k].spitze === 0 &#038;&#038; e.spitze > 0) { e.stumm.push(k + 1); } }\n        e.gleich = m.anzahl === 2 && e.spitze > 0 && m.diffMax === 0;\n        return e;\n    }<\/p>\n<p>    function messen(kanaele, fs, schwelleVoll) {\n        if (!kanaele || !kanaele.length || !kanaele[0].length || !fs) { return null; }\n        var m = messungAnlegen(kanaele, fs, schwelleVoll);\n        while (!messungSchritt(m, 1 << 30)) { \/* weiter *\/ }\n        return messungAuswerten(m);\n    }\n\n    \/\/ Ab diesem Betrag steht ein Wert auf Vollaussteuerung.\n    function vollSchwelle(info) {\n        if (info.ganzzahl &#038;&#038; info.bits) { var f = Math.pow(2, info.bits - 1); return (f - 1) \/ f; }\n        return 1;\n    }\n\n    \/\/ ---- Befunde ------------------------------------------------------------\n    function db(wert) { return wert > 0 ? 20 * Math.log10(wert) : -Infinity; }\n    function runden(wert, stellen) { var f = Math.pow(10, stellen); return Math.round(wert * f) \/ f; }<\/p>\n<p>    function isrcNormal(s) { return String(s || '').replace(\/^ISRC[:\\s]*\/i, '').replace(\/[\\s\\-]\/g, '').toUpperCase(); }\n    function isrcGut(s) { return \/^[A-Z]{2}[A-Z0-9]{3}[0-9]{7}$\/.test(isrcNormal(s)); }<\/p>\n<p>    \/\/ Gibt je Pruefpunkt {id, stufe, schluessel, werte, wert} zurueck; die Texte\n    \/\/ setzt die Oberflaeche ein. stufe: ok, hinweis, problem, info, offen.\n    function befunde(info, e, wirksam) {\n        var liste = [];\n        function b(id, stufe, schluessel, werte, wert) {\n            liste.push({ id: id, stufe: stufe, schluessel: schluessel, werte: werte || {}, wert: wert === undefined ? null : wert });\n        }\n        \/\/ Format\n        if (info.verlustfrei === true) { b('format', 'ok', 'b_format_verlustfrei', { format: info.format }, info.format); }\n        else if (info.verlustfrei === false) { b('format', 'problem', 'b_format_verlustbehaftet', { format: info.format }, info.format); }\n        else { b('format', 'hinweis', 'b_format_unbekannt', { format: info.format || '' }, info.format); }\n        \/\/ Abtastrate\n        if (!info.rate) { b('rate', 'offen', 'b_offen_kopf'); }\n        else if (RATEN_APPLE.indexOf(info.rate) !== -1) { b('rate', 'ok', 'b_rate_ok', { rate: info.rate }, { hz: info.rate }); }\n        else { b('rate', 'problem', 'b_rate_andere', { rate: info.rate }, { hz: info.rate }); }\n        \/\/ Bit-Tiefe\n        if (info.verlustfrei === false) { b('bits', 'info', 'b_bits_verlustbehaftet'); }\n        else if (!info.bits) { b('bits', 'offen', 'b_offen_kopf'); }\n        else if (info.gleitkomma) { b('bits', 'hinweis', 'b_bits_float', { bits: info.bits }, { bits: info.bits, float: true }); }\n        else if (info.bits === 24) { b('bits', 'ok', 'b_bits_24', { bits: 24 }, { bits: 24 }); }\n        else if (info.bits === 16) { b('bits', 'ok', 'b_bits_16', { bits: 16 }, { bits: 16 }); }\n        else if (info.bits < 16) { b('bits', 'problem', 'b_bits_wenig', { bits: info.bits }, { bits: info.bits }); }\n        else { b('bits', 'hinweis', 'b_bits_andere', { bits: info.bits }, { bits: info.bits }); }\n        \/\/ Wirksame Bits (Bit-Padding)\n        if (info.verlustfrei !== false &#038;&#038; e) {\n            if (e.spitze === 0) { b('wirksam', 'offen', 'b_wirksam_stille'); }\n            else if (info.gleitkomma &#038;&#038; info.alle16) { b('wirksam', 'problem', 'b_wirksam_float16', { bits: info.bits }, { bits: 16 }); }\n            else if (info.gleitkomma &#038;&#038; info.alle24) { b('wirksam', 'ok', 'b_wirksam_float24', { bits: info.bits }, { bits: 24 }); }\n            else if (info.gleitkomma) { b('wirksam', 'ok', 'b_wirksam_float', { bits: info.bits }, { bits: info.bits, float: true }); }\n            else if (wirksam === null || wirksam === undefined) { b('wirksam', 'offen', 'b_wirksam_offen'); }\n            else if (wirksam >= info.bits) { b('wirksam', 'ok', 'b_wirksam_voll', { bits: info.bits }, { bits: wirksam }); }\n            else if (info.bits > 16 && wirksam <= 16) { b('wirksam', 'problem', 'b_wirksam_aufgefuellt', { wirksam: wirksam, bits: info.bits }, { bits: wirksam }); }\n            else { b('wirksam', 'hinweis', 'b_wirksam_weniger', { wirksam: wirksam, bits: info.bits }, { bits: wirksam }); }\n        }\n        \/\/ Kanaele\n        var anzahl = e ? e.anzahl : info.kanaeleAnzahl;\n        if (!anzahl) { b('kanaele', 'offen', 'b_offen_kopf'); }\n        else if (anzahl === 2) { b('kanaele', 'ok', 'b_kanaele_2', {}, anzahl); }\n        else if (anzahl === 1) { b('kanaele', 'hinweis', 'b_kanaele_1', {}, anzahl); }\n        else { b('kanaele', 'hinweis', 'b_kanaele_mehr', { n: anzahl }, anzahl); }\n\n        if (!e) { b('messung', 'offen', 'b_offen_ton'); return liste; }\n\n        b('dauer', 'info', 'b_dauer', {}, { s: e.dauer });\n        \/\/ Spitzen\n        if (e.voll > 0) { b('spitze', 'hinweis', 'b_spitze_voll', { n: e.voll }, { dbfs: db(e.spitze) }); }\n        else { b('spitze', 'ok', 'b_spitze_ok', {}, { dbfs: db(e.spitze) }); }\n        if (e.stellen > 0) { b('clipping', 'problem', 'b_clipping', { n: e.stellen }, { n: e.stellen }); }\n        else { b('clipping', 'ok', 'b_clipping_keine', {}, { n: 0 }); }\n        if (!info.ganzzahl) {\n            if (e.ueber > 0) { b('ueber', 'problem', 'b_ueber', { n: e.ueber }, { n: e.ueber }); }\n            else { b('ueber', 'ok', 'b_ueber_keine', {}, { n: 0 }); }\n        }\n        var tpDb = runden(db(e.tp), 1);\n        if (tpDb > 0) { b('truepeak', 'problem', 'b_tp_problem', {}, { dbtp: db(e.tp) }); }\n        else if (tpDb > -1) { b('truepeak', 'hinweis', 'b_tp_hinweis', {}, { dbtp: db(e.tp) }); }\n        else { b('truepeak', 'ok', 'b_tp_ok', {}, { dbtp: db(e.tp) }); }\n        \/\/ Gleichanteil\n        if (e.dc >= DC_GRENZE) { b('dc', 'hinweis', 'b_dc_hoch', {}, { dbfs: db(e.dc) }); }\n        else { b('dc', 'ok', 'b_dc_ok', {}, { dbfs: db(e.dc) }); }\n        \/\/ Stille und Spruenge\n        if (e.erster < 0) {\n            b('stille_anfang', 'problem', 'b_stille_ganz', {}, null);\n        } else {\n            var vorne = e.erster \/ e.fs, hinten = (e.n - 1 - e.letzter) \/ e.fs;\n            if (vorne >= STILLE_ANFANG_S) { b('stille_anfang', 'hinweis', 'b_stille_anfang_lang', {}, { s: vorne }); }\n            else { b('stille_anfang', 'ok', 'b_stille_anfang_ok', {}, { s: vorne }); }\n            if (hinten >= STILLE_ENDE_S) { b('stille_ende', 'hinweis', 'b_stille_ende_lang', {}, { s: hinten }); }\n            else { b('stille_ende', 'ok', 'b_stille_ende_ok', {}, { s: hinten }); }\n        }\n        var vorneSprung = e.anfangWert >= SPRUNG, hintenSprung = e.endeWert >= SPRUNG;\n        var sprungWert = { anfang: db(e.anfangWert), ende: db(e.endeWert) };\n        if (vorneSprung && hintenSprung) { b('sprung', 'hinweis', 'b_sprung_beide', {}, sprungWert); }\n        else if (vorneSprung) { b('sprung', 'hinweis', 'b_sprung_anfang', {}, sprungWert); }\n        else if (hintenSprung) { b('sprung', 'hinweis', 'b_sprung_ende', {}, sprungWert); }\n        else { b('sprung', 'ok', 'b_sprung_ok', {}, sprungWert); }\n        \/\/ Kanalvergleich\n        if (e.anzahl >= 2) {\n            if (e.stumm.length) { b('kanalvergleich', 'problem', 'b_kanal_stumm', { kanal: e.stumm.join(', ') }, null); }\n            else if (e.gleich) { b('kanalvergleich', 'hinweis', 'b_kanal_gleich', {}, null); }\n            else { b('kanalvergleich', 'ok', 'b_kanal_ok', {}, null); }\n        }\n        return liste;\n    }<\/p>\n<p>    function tagBefunde(tags) {\n        var liste = [];\n        function b(id, stufe, schluessel, werte, wert) {\n            liste.push({ id: id, stufe: stufe, schluessel: schluessel, werte: werte || {}, wert: wert === undefined ? null : wert });\n        }\n        function erster(feld) { for (var i = 0; i < feld.length; i++) { if (feld[i].wert) { return feld[i]; } } return null; }\n        var t = erster(tags.titel), k = erster(tags.kuenstler);\n        if (t) { b('titel', 'ok', 'b_titel_da', { ort: t.ort }, t.wert); } else { b('titel', 'info', 'b_titel_fehlt'); }\n        if (k) { b('kuenstler', 'ok', 'b_kuenstler_da', { ort: k.ort }, k.wert); } else { b('kuenstler', 'info', 'b_kuenstler_fehlt'); }\n\n        var codes = tags.isrc.filter(function (x) { return x.wert; });\n        var richtig = codes.filter(function (x) { return \/^(tsrc|vorbis|mp4_frei|mdta)$\/.test(x.ort) &#038;&#038; isrcGut(x.wert); });\n        var txxx = codes.filter(function (x) { return x.ort === 'txxx' &#038;&#038; isrcGut(x.wert); });\n        var riff = codes.filter(function (x) { return x.ort === 'riff_isrc'; });\n        var verschieden = {};\n        codes.forEach(function (x) { if (isrcGut(x.wert) &#038;&#038; x.ort !== 'riff_isrc') { verschieden[isrcNormal(x.wert)] = 1; } });\n        var falsch = codes.filter(function (x) { return x.ort !== 'riff_isrc' &#038;&#038; !isrcGut(x.wert); });\n        if (Object.keys(verschieden).length > 1) { b('isrc', 'problem', 'b_isrc_mehrere', { n: Object.keys(verschieden).length }, Object.keys(verschieden).join(', ')); }\n        else if (falsch.length) { b('isrc', 'problem', 'b_isrc_form', { ort: falsch[0].ort }, falsch[0].wert); }\n        else if (richtig.length) { b('isrc', 'ok', 'b_isrc_ok', { ort: richtig[0].ort }, isrcNormal(richtig[0].wert)); }\n        else if (txxx.length) { b('isrc', 'hinweis', 'b_isrc_txxx', { ort: 'txxx' }, isrcNormal(txxx[0].wert)); }\n        else if (riff.length) { b('isrc', 'hinweis', 'b_isrc_riff', { ort: 'riff_isrc' }, riff[0].wert); }\n        else { b('isrc', 'hinweis', 'b_isrc_fehlt'); }<\/p>\n<p>        var bilder = tags.cover.slice().sort(function (x, y) { return (x.bildtyp === 3 ? 0 : 1) - (y.bildtyp === 3 ? 0 : 1); });\n        var c = bilder[0];\n        if (!c) { b('cover', 'info', 'b_cover_fehlt'); }\n        else if (c.typ !== 'JPEG' && c.typ !== 'PNG') { b('cover', 'problem', 'b_cover_format', { typ: c.typ || '?' }, c); }\n        else if (!c.breite || !c.hoehe) { b('cover', 'hinweis', 'b_cover_masse', {}, c); }\n        else if (c.breite !== c.hoehe) { b('cover', 'problem', 'b_cover_quadrat', { breite: c.breite, hoehe: c.hoehe }, c); }\n        else if (c.breite < COVER_MINDEST) { b('cover', 'problem', 'b_cover_klein', { breite: c.breite, hoehe: c.hoehe }, c); }\n        else if (c.breite < COVER_EMPFOHLEN) { b('cover', 'hinweis', 'b_cover_mittel', { breite: c.breite, hoehe: c.hoehe }, c); }\n        else { b('cover', 'ok', 'b_cover_ok', { breite: c.breite, hoehe: c.hoehe }, c); }\n        return liste;\n    }\n\n    function gesamtUrteil(liste) {\n        var p = 0, h = 0;\n        liste.forEach(function (x) { if (x.stufe === 'problem') { p++; } else if (x.stufe === 'hinweis') { h++; } });\n        return { stufe: p ? 'problem' : (h ? 'hinweis' : 'ok'), probleme: p, hinweise: h };\n    }\n    \/\/ KERN-ENDE --------------------------------------------------------------\n\n    function el(id) { return document.getElementById(id); }\n    function satz(schluessel, werte) {\n        var s = T[schluessel] || '';\n        return s.replace(\/\\{(\\w+)\\}\/g, function (_, k) { return werte &#038;&#038; werte[k] !== undefined ? werte[k] : ''; });\n    }\n    var NB = '\u00a0';\n    function zahl(wert, stellen) {\n        if (wert === -Infinity) { return '\u2212\u221e'; }\n        if (wert === null || wert === undefined || !isFinite(wert)) { return '\u2013'; }\n        var s = Number(wert).toFixed(stellen === undefined ? 1 : stellen);\n        if (\/^-0(\\.0+)?$\/.test(s)) { s = s.slice(1); }\n        s = s.replace('-', '\u2212');\n        return T.dezimal === ',' ? s.replace('.', ',') : s;\n    }\n    function ganz(n) {\n        var s = String(Math.round(n)), t = T.tausender || '';\n        return t ? s.replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, t) : s;\n    }\n    function dauerText(s) {\n        if (s < 60) { return zahl(s, 2) + NB + 's'; }\n        var min = Math.floor(s \/ 60), rest = s - min * 60;\n        return min + NB + 'min ' + zahl(rest, 1) + NB + 's';\n    }\n    function ortText(ort) { return T['ort_' + ort] || ort; }\n\n    \/\/ Anzeigewert je Pruefpunkt\n    function wertText(x) {\n        var w = x.wert;\n        if (w === null || w === undefined) { return '\u2013'; }\n        switch (x.id) {\n            case 'format': return String(w);\n            case 'rate': return zahl(w.hz \/ 1000, w.hz % 1000 ? 1 : 0) + NB + 'kHz';\n            case 'bits': case 'wirksam': return w.bits + NB + 'Bit' + (w.float ? ' ' + T.wert_float : '');\n            case 'kanaele': return String(w);\n            case 'dauer': return dauerText(w.s);\n            case 'spitze': case 'dc': return zahl(w.dbfs, 1) + NB + 'dBFS';\n            case 'truepeak': return zahl(w.dbtp, 1) + NB + 'dBTP';\n            case 'clipping': case 'ueber': return ganz(w.n);\n            case 'stille_anfang': case 'stille_ende': return zahl(w.s, w.s < 10 ? 2 : 1) + NB + 's';\n            case 'sprung': return zahl(w.anfang, 1) + ' \/ ' + zahl(w.ende, 1) + NB + 'dBFS';\n            case 'cover': return w.breite &#038;&#038; w.hoehe ? w.breite + ' \u00d7 ' + w.hoehe + NB + 'px' : (w.typ || '\u2013');\n            default: return String(w);\n        }\n    }\n    function befundText(x) {\n        var werte = {};\n        Object.keys(x.werte).forEach(function (k) {\n            var v = x.werte[k];\n            if (k === 'ort') { werte[k] = ortText(v); }\n            else if (k === 'rate') { werte[k] = zahl(v \/ 1000, v % 1000 ? 1 : 0) + NB + 'kHz'; }\n            else if (typeof v === 'number') { werte[k] = ganz(v); }\n            else { werte[k] = v; }\n        });\n        return satz(x.schluessel, werte);\n    }\n\n    function tagZeilen(tags) {\n        var zeilen = [];\n        tags.titel.forEach(function (t) { zeilen.push([T.f_titel, t.wert, ortText(t.ort)]); });\n        tags.kuenstler.forEach(function (t) { zeilen.push([T.f_kuenstler, t.wert, ortText(t.ort)]); });\n        tags.isrc.forEach(function (t) { zeilen.push([T.f_isrc, t.wert, ortText(t.ort)]); });\n        tags.cover.forEach(function (c) {\n            var w = (c.typ || '?') + (c.breite &#038;&#038; c.hoehe ? ', ' + c.breite + ' \u00d7 ' + c.hoehe + NB + 'px' : '') + ', ' + ganz(c.bytes) + NB + 'Byte';\n            zeilen.push([T.f_cover, w, ortText(c.ort)]);\n        });\n        return zeilen;\n    }\n\n    function bericht(ergebnis) {\n        var z = [T.bericht_kopf + ': ' + (ergebnis.name || '')];\n        ergebnis.liste.forEach(function (x) {\n            z.push('[' + T['stufe_' + x.stufe] + '] ' + T['p_' + x.id] + ': ' + wertText(x) + ' \u2013 ' + befundText(x));\n        });\n        var g = ergebnis.gesamt;\n        z.push(satz('gesamt_' + g.stufe, { p: g.probleme, h: g.hinweise }));\n        return z.join('\\n');\n    }\n\n    \/\/ ---- Darstellung -------------------------------------------------------\n    function darstellen(ergebnis) {\n        var liste = el('rp-liste'), tags = el('rp-tags'), gesamt = el('rp-gesamt');\n        if (!liste) { return ergebnis; }\n        var g = ergebnis.gesamt;\n        gesamt.className = 'rp-gesamt rp-g-' + g.stufe;\n        gesamt.textContent = '';\n        var kopf = document.createElement('div');\n        kopf.className = 'rp-gesamt-kopf';\n        kopf.textContent = satz('gesamt_' + g.stufe, { p: g.probleme, h: g.hinweise });\n        var txt = document.createElement('div');\n        txt.className = 'rp-klein';\n        txt.textContent = satz('gesamt_text_' + g.stufe, { name: ergebnis.name || '' });\n        gesamt.appendChild(kopf); gesamt.appendChild(txt);\n\n        liste.textContent = '';\n        ergebnis.liste.forEach(function (x) {\n            var tr = document.createElement('tr');\n            tr.setAttribute('data-id', x.id);\n            var td1 = document.createElement('td');\n            var marke = document.createElement('span');\n            marke.className = 'rp-stufe rp-s-' + x.stufe;\n            marke.textContent = T['stufe_' + x.stufe];\n            var name = document.createElement('div');\n            name.className = 'rp-name';\n            name.textContent = T['p_' + x.id];\n            td1.appendChild(marke); td1.appendChild(name);\n            var td2 = document.createElement('td'); td2.className = 'rp-wert'; td2.textContent = wertText(x);\n            var td3 = document.createElement('td'); td3.className = 'rp-text'; td3.textContent = befundText(x);\n            tr.appendChild(td1); tr.appendChild(td2); tr.appendChild(td3);\n            liste.appendChild(tr);\n        });\n\n        tags.textContent = '';\n        var zeilen = tagZeilen(ergebnis.info.tags);\n        if (!zeilen.length) { zeilen.push([T.wert_keine_tags, '', '']); }\n        zeilen.forEach(function (z) {\n            var tr = document.createElement('tr');\n            z.forEach(function (w, i) {\n                var td = document.createElement('td');\n                if (i > 0) { td.className = 'rp-wert'; }\n                td.textContent = w;\n                tr.appendChild(td);\n            });\n            tags.appendChild(tr);\n        });\n        el('rp-ausgabe').hidden = false;\n        el('rp-fehler').hidden = true;\n        return ergebnis;\n    }<\/p>\n<p>    function fehlerZeigen(s) {\n        var f = el('rp-fehler');\n        if (!f) { return; }\n        f.textContent = s;\n        f.hidden = false;\n        el('rp-ausgabe').hidden = true;\n        el('rp-status').textContent = '';\n    }<\/p>\n<p>    \/\/ ---- Ablauf ------------------------------------------------------------\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); } }<\/p>\n<p>    function messenAsync(kanaele, fs, sv, fortschritt) {\n        return new Promise(function (ok, fehler) {\n            if (!kanaele || !kanaele.length || !kanaele[0].length || !fs) { ok(null); return; }\n            var m = messungAnlegen(kanaele, fs, sv), 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    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, erledigt = false;\n            try { ctx = new Ctx(1, 1, rate); } catch (e) { ctx = new Ctx(1, 1, 48000); }\n            function gut(buf) { if (!erledigt) { erledigt = true; ok(buf); } }\n            function schlecht(e) { if (!erledigt) { erledigt = true; fehler(e || new Error('dekodieren')); } }\n            setTimeout(function () { schlecht(new Error('zeit')); }, 60000);\n            try {\n                var p = ctx.decodeAudioData(puffer, gut, schlecht);\n                if (p &#038;&#038; typeof p.then === 'function') { p.then(gut, schlecht); }\n            } catch (e) { schlecht(e); }\n        });\n    }\n\n    var letztes = null;\n    \/\/ optionen.dekodieren: false laesst den Decoder des Browsers aus (Pruefung).\n    function verarbeiten(puffer, name, optionen) {\n        optionen = optionen || {};\n        var info;\n        try { info = lesen(puffer); } catch (x) { info = null; }\n        if (!info || (info.fehler &#038;&#038; !info.format)) { fehlerZeigen(T.fehler_format); return Promise.resolve(null); }\n        var status = el('rp-status');\n        var wegZumTon;\n        if (info.kanaele) { wegZumTon = Promise.resolve(info.kanaele); }\n        else if (optionen.dekodieren === false || info.fehler === 'codec') { wegZumTon = Promise.resolve(null); }\n        else {\n            if (status) { status.textContent = T.status_dekodieren; }\n            var rate = info.rate &#038;&#038; info.rate >= 3000 && info.rate <= 768000 ? info.rate : 48000;\n            wegZumTon = dekodieren(puffer.slice(0), rate).then(function (buf) {\n                var k = [];\n                for (var c = 0; c < buf.numberOfChannels; c++) { k.push(buf.getChannelData(c)); }\n                if (buf.sampleRate !== info.rate) { info.rateDekodiert = buf.sampleRate; }\n                if (info.verlustfrei &#038;&#038; info.ganzzahl &#038;&#038; !info.rateDekodiert) { info.oder = oderAusGleitkomma(k, info.bits); }\n                return k;\n            }, function () { return null; });\n        }\n        return wegZumTon.then(function (kanaele) {\n            var fs = info.rateDekodiert || info.rate;\n            return messenAsync(kanaele, fs, vollSchwelle(info), function (anteil) {\n                if (status) { status.textContent = satz('status_messen', { p: Math.round(anteil * 100) }); }\n            });\n        }).then(function (e) {\n            var wirksam = info.ganzzahl ? wirksameBits(info.oder, info.bits) : null;\n            var liste = befunde(info, e, wirksam).concat(tagBefunde(info.tags));\n            var ergebnis = { name: name || '', info: info, messung: e, liste: liste, gesamt: gesamtUrteil(liste) };\n            letztes = ergebnis;\n            if (status) { status.textContent = ''; }\n            return darstellen(ergebnis);\n        });\n    }\n\n    function dateiNehmen(datei) {\n        if (!datei) { return; }\n        var leser = new FileReader();\n        el('rp-status').textContent = T.status_lesen;\n        leser.onload = function () {\n            verarbeiten(leser.result, datei.name).then(null, function () { fehlerZeigen(T.fehler_lesen); });\n        };\n        leser.onerror = function () { fehlerZeigen(T.fehler_lesen); };\n        try { leser.readAsArrayBuffer(datei); } catch (x) { fehlerZeigen(T.fehler_lesen); }\n    }\n\n    window.LW_TEST = window.LW_TEST || {};\n    window.LW_TEST.RP = {\n        T: T, lesen: lesen, id3Lesen: id3Lesen, vorbisKommentar: vorbisKommentar, bildMasse: bildMasse,\n        mp4Lesen: mp4Lesen, oggLesen: oggLesen, pcmLesen: pcmLesen, messen: messen, vollSchwelle: vollSchwelle,\n        wirksameBits: wirksameBits, oderAusGleitkomma: oderAusGleitkomma, befunde: befunde, tagBefunde: tagBefunde,\n        gesamtUrteil: gesamtUrteil, isrcGut: isrcGut, isrcNormal: isrcNormal, wertText: wertText,\n        befundText: befundText, bericht: bericht, verarbeiten: verarbeiten, zahl: zahl,\n        letztes: function () { return letztes; }\n    };\n\n    var zone = el('rp-zone'), eingabe = el('rp-datei'), knopf = el('rp-knopf');\n    if (!zone || !eingabe || !knopf) { 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('rp-ueber'); });\n    });\n    ['dragleave', 'drop'].forEach(function (typ) {\n        zone.addEventListener(typ, function (ev) { ev.preventDefault(); zone.classList.remove('rp-ueber'); });\n    });\n    zone.addEventListener('drop', function (ev) {\n        ev.preventDefault();\n        dateiNehmen(ev.dataTransfer &#038;&#038; ev.dataTransfer.files &#038;&#038; ev.dataTransfer.files[0]);\n    });\n    var kopieren = el('rp-kopieren'), kopiert = el('rp-kopiert');\n    if (kopieren) {\n        kopieren.addEventListener('click', function () {\n            if (!letztes) { return; }\n            var t = bericht(letztes);\n            if (navigator.clipboard &#038;&#038; navigator.clipboard.writeText) {\n                navigator.clipboard.writeText(t).then(function () { kopiert.textContent = T.kopiert; },\n                    function () { kopiert.textContent = T.kopieren_fehler; });\n            } else {\n                kopiert.textContent = T.kopieren_fehler;\n            }\n        });\n    }\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reads a WAV, AIFF, FLAC, MP3, M4A or Ogg file in the browser and lists what stands between it and a delivery: format, sample rate and bit depth against Apple&#8217;s requirements, bit padding, clipping, true peak, DC offset, silence and jumps at both ends, and title, artist, ISRC and cover from the tags.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":38,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[92901],"class_list":["post-20128","page","type-page","status-publish","hentry","tag-music-production"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Release File Checker: sample rate, bit depth, peaks, silence, ISRC and cover before the upload - 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\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Release File Checker: sample rate, bit depth, peaks, silence, ISRC and cover before the upload - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Reads a WAV, AIFF, FLAC, MP3, M4A or Ogg file in the browser and lists what stands between it and a delivery: format, sample rate and bit depth against Apple&#039;s requirements, bit padding, clipping, true peak, DC offset, silence and jumps at both ends, and title, artist, ISRC and cover from the tags.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-23T16:54:01+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=\"2 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\\\/\",\"name\":\"Release File Checker: sample rate, bit depth, peaks, silence, ISRC and cover before the upload - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-09-23T16:48:07+00:00\",\"dateModified\":\"2026-09-23T16:54:01+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\\\/#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\\\/en\\\/toolbox\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Release File Checker: sample rate, bit depth, peaks, silence, ISRC and cover before the upload\"}]},{\"@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":"Release File Checker: sample rate, bit depth, peaks, silence, ISRC and cover before the upload - 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\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/","og_locale":"en_US","og_type":"article","og_title":"Release File Checker: sample rate, bit depth, peaks, silence, ISRC and cover before the upload - Lukas Wojcik - Blog","og_description":"Reads a WAV, AIFF, FLAC, MP3, M4A or Ogg file in the browser and lists what stands between it and a delivery: format, sample rate and bit depth against Apple's requirements, bit padding, clipping, true peak, DC offset, silence and jumps at both ends, and title, artist, ISRC and cover from the tags.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/","og_site_name":"Lukas Wojcik - Blog","article_modified_time":"2026-09-23T16:54:01+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":"2 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/","name":"Release File Checker: sample rate, bit depth, peaks, silence, ISRC and cover before the upload - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-09-23T16:48:07+00:00","dateModified":"2026-09-23T16:54:01+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/release-file-checker-sample-rate-bit-depth-peaks-isrc-cover\/#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\/en\/toolbox\/"},{"@type":"ListItem","position":3,"name":"Release File Checker: sample rate, bit depth, peaks, silence, ISRC and cover before the upload"}]},{"@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\/20128","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=20128"}],"version-history":[{"count":1,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/20128\/revisions"}],"predecessor-version":[{"id":20131,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/20128\/revisions\/20131"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/38"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=20128"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=20128"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}