{
  "openapi": "3.1.1",
  "info": {
    "title": "Audio Loudness Normalizer API",
    "version": "1.0.0",
    "summary": "Measure LUFS and true peak, then normalise to Spotify, podcast or EBU R 128 targets.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://audioloudnessnormalizer.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Measure LUFS and true peak, then normalise to Spotify, podcast or EBU R 128 targets.",
        "description": "Measures integrated loudness (LUFS), true peak (dBTP) and loudness range (LRA) with ffmpeg's EBU R 128 meter, then normalises the file in two passes to a streaming, podcast or broadcast target with a true-peak limit. Shows the measured values before and after, with a player and a download.\n\nMethodology:\n- Pass 1 measures the file with ffmpeg's loudnorm filter (ITU-R BS.1770 / EBU R 128 meter): integrated loudness (LUFS), true peak (dBTP, oversampled), loudness range (LRA) and the gating threshold.\n- Pass 2 feeds the measured values back into loudnorm with linear=true: if the whole file can reach the target with one gain change without exceeding the true-peak limit, only the gain changes (linear). Otherwise loudnorm switches to dynamic mode, which compresses and limits — the result reports which one happened.\n- loudnorm works at 192 kHz internally; the output is resampled back to the original sample rate. The podcast-mono preset also mixes to mono.\n- Pass 3 measures the finished file again, so the \"after\" values are real measurements of the download, not the targets (for files over 15 minutes the normaliser's own output report is used instead, to stay within the time limit). Files up to 45 minutes are accepted.\n- Spotify (−14 LUFS, true peak below −1 dB) and EBU R 128 (−23 LUFS) are published targets; YouTube ≈ −14 and Apple ≈ −16 LUFS are widely used measured/recommended values, not contractual specs. Podcast −16 LUFS stereo / −19 LUFS mono are common industry recommendations.\n\nVery short files (under about 3 seconds) give less reliable integrated loudness, because the meter gates in 400 ms blocks.",
        "tags": [
          "media"
        ],
        "x-mcp-tool": "audio_loudness_normalizer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AudioLoudnessNormalizerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Normalise a test tone to the Spotify target",
                  "value": {
                    "file": {
                      "filename": "tone.mp3",
                      "contentType": "audio/mpeg",
                      "contentBase64": "SUQzAwAAAAAANFRJVDIAAAALAAAAVGVzdCB0b25lAFRQRTEAAAALAAAAdXNlZnVsd2ViAAAAAAAAAAAAAAD/4zjAAAAAAAAAAAAASW5mbwAAAA8AAAAJAAADYABVVVVVVVVVVVVVVWpqampqampqampqgICAgICAgICAgICVlZWVlZWVlZWVlaqqqqqqqqqqqqqqwMDAwMDAwMDAwMDV1dXV1dXV1dXV1erq6urq6urq6urq//////////////8AAAAATGF2ZiBsYW1lAAAAAAAAAAAAAAAAJAJgAAAAAAAAA2DhWsr/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4xjEAAyokqh5SAAAD22/4d3d3v/RESv5d3f5SSwbgLgLgLg3PlAxw/yhz9/R7+j38HAxkAffWBDmgH+Ud39CgQkKRFiBEz//4xjECA3w6qDBlDgA5MkBJoc7/EBhyQNsGAOBS4BzxgokpgaxKNMIQcg9HhsNv/x0ajU4GhL/hIGhKdBq////////////////4xjECw8QpjgB3gAA+GhGAAYBYAxfoGgIGBqDgYSYPhhOi9GJrekZAAshh+g1GCWCYAgOwqAOYCAAJbFkl+rfoA//b7/L5fX/4xjECQqwjo5YCbRi/n3qiICUpOT5DnL9WR4Ms63+X06P/v9H/+//+nJf+t9GXtsQBtdfrcl5/f+V1eIwQL/nESYRBr0i8sr/4xjEGQtIloJYCnZigON9/lP9n9b/T9v37q/9P2//1fRV///5O3qXQOATAhMCmwMhwSPQYQHRBgcjAEgOoWAuAIB4YAGRACL/4xjEJgpYpjQABvxEbLUb6v///jGAGAADfQMAIBwMCwEgEAvAxGjLAx9m1A6uxbA3pD+AwbAgAwDAJACBeAkAoj4ZgQohCDD/4xjENw04pkABVgAAAR/itBmDQVp/WLIJwRwGKJAwM/DsNjcZwDM5yDpBHgAgGAy0hSC/v8A3wDWMsT8L2PP//zMe5udL5uv/4xjEPRaRlozJlWgAL///+brMwdB8Bn//oB8BmiY1//9BMaaJuNf//3IVBd2AA//aqqp/9VVVOdqOJAwVLAwCARKyIBJV/QX/4xjEHQ1AmqR5xggBBn/0FCv8FBn/FBQp/xQYK/oKFHfiSf/9TEFNRTQuMSAoYWxwaGEgMClVVVVVVVVVVVVVVVVVVVVVVVU="
                    },
                    "preset": "spotify",
                    "format": "wav"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudioLoudnessNormalizerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "AudioLoudnessNormalizerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "file": {
            "title": "Audio file",
            "description": "MP3, WAV, FLAC, Ogg/Opus, M4A, AIFF; max 100 MB and 45 minutes. Processed on our server in a private temporary folder that is deleted afterwards; the result can be downloaded for about 2 hours. Upload as {filename, contentType?, contentBase64}. Max 102400 KiB. Accepts .mp3,.wav,.flac,.ogg,.oga,.opus,.m4a,.aac,.aif,.aiff,.wma,.mp4,.mov,.webm,.mkv,audio/*.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "preset": {
            "title": "Target",
            "type": "string",
            "enum": [
              "spotify",
              "youtube",
              "apple",
              "podcast-stereo",
              "podcast-mono",
              "ebu",
              "custom"
            ],
            "default": "spotify"
          },
          "targetLufs": {
            "title": "Custom integrated loudness",
            "description": "Only used with \"Custom target\".",
            "type": "number",
            "minimum": -40,
            "maximum": -5,
            "default": -16,
            "x-unit": "LUFS"
          },
          "truePeak": {
            "title": "Custom true-peak limit",
            "description": "Only used with \"Custom target\".",
            "type": "number",
            "minimum": -9,
            "maximum": 0,
            "default": -1,
            "x-unit": "dBTP"
          },
          "lra": {
            "title": "Loudness range target",
            "description": "Higher keeps more dynamics. If the source range is larger, ffmpeg compresses it.",
            "type": "number",
            "minimum": 1,
            "maximum": 50,
            "default": 11,
            "x-unit": "LU"
          },
          "format": {
            "title": "Output format",
            "type": "string",
            "enum": [
              "same",
              "mp3",
              "m4a",
              "ogg",
              "opus",
              "flac",
              "wav"
            ],
            "default": "same"
          },
          "bitrate": {
            "title": "Bitrate (lossy formats)",
            "type": "string",
            "enum": [
              "32",
              "48",
              "64",
              "96",
              "128",
              "160",
              "192",
              "224",
              "256",
              "320"
            ],
            "default": "192"
          }
        },
        "required": [
          "file"
        ]
      },
      "AudioLoudnessNormalizerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "preset",
              "target",
              "before",
              "after",
              "normalizationType",
              "afterSource",
              "gainDb",
              "outputFormat",
              "sampleRate",
              "channels",
              "durationSeconds",
              "outputBytes",
              "artifact"
            ],
            "properties": {
              "preset": {
                "type": "string"
              },
              "target": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "integratedLufs",
                  "truePeakDbtp",
                  "lra"
                ],
                "properties": {
                  "integratedLufs": {
                    "type": "number"
                  },
                  "truePeakDbtp": {
                    "type": "number"
                  },
                  "lra": {
                    "type": "number"
                  }
                }
              },
              "before": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "integratedLufs",
                  "truePeakDbtp",
                  "lra",
                  "thresholdLufs"
                ],
                "properties": {
                  "integratedLufs": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "truePeakDbtp": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "lra": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "thresholdLufs": {
                    "type": [
                      "number",
                      "null"
                    ]
                  }
                }
              },
              "after": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "integratedLufs",
                  "truePeakDbtp",
                  "lra",
                  "thresholdLufs"
                ],
                "properties": {
                  "integratedLufs": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "truePeakDbtp": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "lra": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "thresholdLufs": {
                    "type": [
                      "number",
                      "null"
                    ]
                  }
                }
              },
              "normalizationType": {
                "type": "string",
                "description": "linear = one gain change; dynamic = loudnorm also compressed/limited the audio."
              },
              "afterSource": {
                "type": "string",
                "enum": [
                  "measured",
                  "loudnorm-report"
                ],
                "description": "measured = the encoded file was measured again (files up to 15 min)."
              },
              "gainDb": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "outputFormat": {
                "type": "string"
              },
              "sampleRate": {
                "type": "integer"
              },
              "channels": {
                "type": "integer"
              },
              "durationSeconds": {
                "type": "number"
              },
              "outputBytes": {
                "type": "integer"
              },
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}