{"id":29721289,"url":"https://github.com/francisrussell/zoog","last_synced_at":"2025-09-19T20:46:37.394Z","repository":{"id":42523601,"uuid":"326041718","full_name":"FrancisRussell/zoog","owner":"FrancisRussell","description":"Updates Opus headers and R128 tags to normalize playback volume for non-R128-aware players.","archived":false,"fork":false,"pushed_at":"2024-04-01T23:41:01.000Z","size":496,"stargazers_count":17,"open_issues_count":6,"forks_count":3,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2025-09-08T02:52:33.155Z","etag":null,"topics":["audio","normalization","ogg","opus","vorbis","xiph"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FrancisRussell.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-01-01T19:22:02.000Z","updated_at":"2025-04-09T10:17:31.000Z","dependencies_parsed_at":"2025-07-24T14:51:33.666Z","dependency_job_id":"8e9c21ce-9aef-4282-8fa9-fbb31a572697","html_url":"https://github.com/FrancisRussell/zoog","commit_stats":{"total_commits":359,"total_committers":2,"mean_commits":179.5,"dds":"0.0027855153203342198","last_synced_commit":"cfe0db2efa329152f91c81c0aa8fd83dfd5cac38"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/FrancisRussell/zoog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrancisRussell%2Fzoog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrancisRussell%2Fzoog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrancisRussell%2Fzoog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrancisRussell%2Fzoog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FrancisRussell","download_url":"https://codeload.github.com/FrancisRussell/zoog/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrancisRussell%2Fzoog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275998868,"owners_count":25567391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-19T02:00:09.700Z","response_time":108,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","normalization","ogg","opus","vorbis","xiph"],"created_at":"2025-07-24T14:40:03.645Z","updated_at":"2025-09-19T20:46:37.363Z","avatar_url":"https://github.com/FrancisRussell.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zoog: Zero Opus Output Gain\n\n[![Zoog crate](https://img.shields.io/crates/v/zoog)](https://crates.io/crates/zoog)\n[![Zoog documentation](https://docs.rs/zoog/badge.svg)](https://docs.rs/zoog)\n\nZoog is a Rust library that consists of functionality that can be used to\ndetermine the loudness of an Ogg Opus file and also to rewrite that file with\nnew internal gain information as well as loudness-related comment tags. It also\nhas functionality for purely manipulating comment tags of both Ogg Opus and Ogg\nVorbis files.\n\nZoog currently contains two tools, `opusgain` and `zoogcomment`. `opusgain` can\nbe used to:\n\n* set the output gain value located in the Opus binary header inside Opus files\n  so that the file plays at the loudness of the original encoded audio, or of\n  that consistent with the\n  [ReplayGain](https://en.wikipedia.org/wiki/ReplayGain)  or [EBU R\n  128](https://en.wikipedia.org/wiki/EBU_R_128) standards.\n\n* write the Opus comment tags used by some music players to decide\nwhat volume to play an Opus-encoded audio file at.\n\nIt is intended to solve the \"Opus plays too quietly\" problem.\n\n`zoogcomment` can be used to list, replace or modify the comment tags of Ogg\nOpus and Ogg Vorbis files. Its usage is roughly based on that of\n`vorbiscomment` though many options have different naming for improved clarity.\n\nAlthough `zoog` exposes a library, its API is unstable and this package is\nreleased on [crates.io](https://crates.io/) primarily to allow access to the\ncommand-line tools. The API is documented however, and the reading the source\nmay prove useful to anyone else wishing to work with Ogg Opus files.\n\n## `opusgain`\n\n`opusgain` adjusts the Opus binary header for playback at a specific volume and\nwill always generate the `R128_TRACK_GAIN` tag and the `R128_ALBUM_GAIN` tag\n(when in album mode) such that files will play at an appropriate volume in\nplayers that support these tags, and at a more appropriate volume in players\nthat don't. Existing `R128_ALBUM_GAIN` tags will be stripped when not in album\nmode.\n\n`opusgain` (unlike its predecessor `zoog`) decodes Opus audio in order to\ndetermine its volume so that it's possible to be certain that all generated\ngain values are correct without making assumptions about their existing values.\n\nThe following options are available (run `opusgain --help` for usage):\n\n* `-p PRESET, --preset=PRESET`\n\n  It is recommended to specify this value explicitly, as the default\n  may change.\n\n  * `original`: Set the output gain in the Opus binary header to 0dB. In\n    players that do not support `R128` tags, this will cause the Opus file to\n    play back at the volume of the originally encoded source. You may want this\n    if you prefer volume normalization to only occur via tags.\n\n  * `rg`: Set the output gain in the Opus binary header to the value that\n    ensures playback will occur at -18 LUFS, which should match the loudness of\n    ReplayGain normalized files.  This is probably the best option when you\n    have a player that doesn't know about Opus `R128` tags, but:\n    * does support ReplayGain for the other file formats you use, and/or\n    * the files you play have been adjusted in a player-agnostic way\n      ([mp3gain](http://mp3gain.sourceforge.net/) and\n      [aacgain](http://aacgain.altosdesign.com/) can do this) to the ReplayGain\n      reference volume.\n\n  * `r128`: Set the output gain in the Opus binary header to the value that\n    ensures playback will occur at -23 LUFS, which should match the loudness of\n    files produced by `opusenc` from FLAC files which contained ReplayGain\n    information. This is the gain level intended by the Opus authors.\n\n  * `no-change`: Do not change the output gain in the Opus binary header.\n\n* `-o MODE, --output-gain-mode=MODE`\n\n  * `auto`: Set the output gain in the Opus binary header such that each track\n    is album-normalized in album mode, or track-normalized otherwise. In album\n    mode, this results in all tracks having the same output gain value as well\n    as the same `R128_ALBUM_GAIN` tag.\n\n  * `track`: Set the output gain in the Opus binary header such that each track\n    is track-normalized, even if album mode is enabled. In album mode, this\n    results in all tracks being given different output gain values as well as\n    different `R128_ALBUM_GAIN` tags, but their `R128_TRACK_GAIN` tags will be\n    identical.  Unless you know what you're doing, you probably don't want this\n    option.\n\n* `-a, --album`: Enables album mode. This causes `R128_ALBUM_GAIN` tags to also be\n  generated. These tell players that support these tags what gain to apply so\n  that each track in the album maintains its relative loudness. By default the\n  output gain value for each file will be set to identical values in order to\n  apply the calculated album gain, but this behaviour can be overridden using\n  the `--output-gain-mode` option.\n\n* `-j N, --num-threads=N`: Use `N` threads for processing. The default is to use the\n  number of cores detected on the system. Larger numbers will be rounded down\n  to this value. To avoid high disk space usage during processing, or a large\n  number of temporary files left around after an error, only one file will be\n  rewritten at a time regardless of the number of threads.\n\n* `-c, --clear`: Remove all `R128` tags from the specified files. The output\n  gain of each file is unchanged, regardless of the specified preset.\n\n* `-M`, `--minimize-mtime-change`: Attempts to apply the smallest increment\n  possible (filesystem dependent) to the modification time of the file. This is\n  deliberately not a preserve in order to avoid misleading backup/data-transfer\n  programs that use this information to determine if a file has changed. On\n  modern filesystems (ext4, APFS, btrfs) this is typically a nanosecond, but\n  could be up to two seconds on older filesystems (ext3, FAT32).\n\n* `-n, --dry-run`: Displays the same output that `opusgain` would otherwise\n  produce, but does not make any changes to the supplied files.\n\n* `-I PATH_PROCESSING_MODE, --interpret-paths=PATH_PROCESSING_MODE`\n\n  This option controls how the supplied paths are interpreted.\n\n  * `files-singles` (default): Each supplied path must be to a file. Files are\n    normalized independently, with any album tags being removed. It is an error\n    to supply a folder.\n\n  * `files-album`: Each supplied path must be to a file. Files are treated as\n    all belonging to the same album and album normalization tags will be\n    generated.  It is an error to supply a folder. This option is equivalent to\n    supplying the `-a` or `--album` flag.\n  \n  * `folders-are-albums`: Each file path supplied is treated as a single, with\n    album tags being removed. Each supplied folder path is explored (including\n    recursive traversal of sub-folders) with all found Opus files being treated\n    as part of a single album. This option therefore makes it possible to apply\n    multiple album normalizations in a single `opusgain` invocation. Which\n    files inside folders are considered Ogg Opus files is controlled by the\n    `--file-extensions` option.\n\n    For example, if you had the files:\n\n    ```\n    a.opus\n    album1/b.opus\n    album1/c.opus\n    album2/e.opus\n    album2/f.opus\n    d.opus\n    ```\n\n    Then `opusgain -I folders-are-albums *` in that folder would be expanded\n    (by the shell on Linux/MacOS or by `opusgain` on Windows) to `opusgain -I\n    folders-are-albums a.opus album1 album2 d.opus` with `a.opus` and `d.opus`\n    being treated as singles and `album1` and `album2` being normalized as\n    independent albums.\n\n* `-e FILE_EXTENSIONS`, `--file-extensions=FILE_EXTENSIONS`: When folders are\n  passed on the command line to be searched for files, this option determines\n  what file extensions are considered to be Ogg Opus files. Multiple extensions\n  may be supplied separated by commas. By default this value is `opus`, but\n  `ogg,opus` could be supplied to assume that all found `.ogg` files are Ogg\n  Opus as well.\n\nIf the internal gain and tag values are already correct for the specified files,\n`opusgain` will avoid rewriting them.\n\nSequentially multiplexed or \"chained\" Ogg Opus streams are not supported.\n\n`opusgain` supports Unix shell style wildcards under Windows, where wildcards\nmust be handled by the application rather than expanded by the shell.\n\n## `zoogcomment`\n\n`zoogcomment` can be used to delete, append, replace and list the comments\nlocated in an Ogg Opus or Ogg Vorbis file.\n\nThe following options are available (run `zoogcomment --help` for usage):\n\n* `-l, --list`: List all tags in the file in `NAME=VALUE` format. This will be to\n  standard output unless `-O` is specified.\n\n* `-m, --modify`: Tags specified using `-t` or `-I` will be appended to the\n  specified file. Tags matching patterns specified using `-d` will be\n  removed from the existing tags on the file.\n\n* `-r, --replace`: All existing tags in the file will be removed and will be\n  replaced with those specified using `-t` or `-I`.\n\n* `-t NAME=VALUE, --tag NAME=VALUE`. The specified tag is will be added to the\n  file in modify or replace mode.\n\n* `-d NAME[=VALUE], --delete NAME[=VALUE]`. Specifies either a tag name, or a\n  name-value mapping to be deleted. All tags that match the pattern will be\n  removed, not just the first. This option is only valid in modify mode.\n\n* `-e, --escapes`: In all tag input/output either on the command-line or\n  to/from a file escapes will be used for line-feeds (`\\n`), carriage returns\n  (`\\r`), backslashes (`\\\\`) and the null character (`\\0`). All other escapes\n  are invalid. This option makes it possible to specify tags which contain\n  newlines which would otherwise fail to be parsed correctly from a comment\n  file.\n\n* `-I COMMENT_FILE, --tags-in COMMENT_FILE`: In the modify and replace modes,\n  the tags to added will be read from this file in addition to those specified\n  on the command line. Tags are read in `NAME=VALUE` format, with one tag per\n  line. If `-` is specified for the file name, tags will be read from\n  standard input.\n\n* `-O COMMENT_FILE, --tags-out COMMENT_FILE`: In list mode, tags will be\n  written to this file. Tags are written in `NAME=VALUE` format, with one tag per\n  line. If `-` is specified for the file name, tags will be written to standard\n  output.\n\n* `-M`, `--minimize-mtime-change`: Attempts to apply the smallest increment\n  possible (filesystem dependent) to the modification time of the file. This is\n  deliberately not a preserve in order to avoid misleading backup/data-transfer\n  programs that use this information to determine if a file has changed. On\n  modern filesystems (ext4, APFS, btrfs) this is typically a nanosecond, but\n  could be up to two seconds on older filesystems (ext3, FAT32).\n\n* `-n, --dry-run`: Displays the same output that `zoogcomment` would otherwise\n  produce, but does not make any changes to the filesystem.\n\n`zoogcomment` only has knowledge of UTF-8. Usage on systems where UTF-8 is not\nthe character encoding scheme in use may encounter issues.\n\n## Build Instructions \n\nIf you do not have Cargo, install it by following the instructions\n[here](https://doc.rust-lang.org/cargo/getting-started/installation.html).\n\nClone the Git repository:\n\n```$ git clone https://github.com/FrancisRussell/zoog.git```\n\nInside the cloned repository:\n\n```cargo build```\n\nor \n\n```cargo build --release```\n\nfor a release build.\n\nBuilt binaries can be found in `target/debug` or `target/release`.\n\n## Installation via `cargo`\n\nAt the command line, simply run\n```\n$ cargo install zoog\n```\n\n`opusgain` and `zoogcomment` should now be available in the path.\n\n## Releases\n\nZoog binaries for Windows, MacOS and Linux can be found on the [releases\npage](https://github.com/FrancisRussell/zoog/releases/). Only the Linux\nbinaries have undergone any testing at present.\n\n## About Ogg Opus Volume Normalization\n\n### Background\n\nOpus-encoded audio files contain an [‘output\ngain’](https://tools.ietf.org/html/rfc7845) value which describes a gain to be\napplied when decoding the audio. This value appears to exist in order to ensure\nthat loudness changes to Opus files are *always* applied, rather than being\ndependent on decoder support for tags such as `REPLAYGAIN_TRACK_GAIN` and\n`REPLAYGAIN_ALBUM_GAIN` which are used in Ogg Vorbis, but *not* Opus.\n\nThe in-header value was intended to correspond to the album gain with\n[RFC 7845](https://tools.ietf.org/html/rfc7845) defining the tag\n`R128_TRACK_GAIN` for single-track normalization. It seems the original intent\nof the output gain was to eliminate the need for an album gain tag, however\n`R128_ALBUM_GAIN` was later added for album normalization.\n\n### The problem\n\nWhen encoding an Opus stream using `opusenc` from a FLAC stream which has\nembedded ReplayGain tags, the resulting Opus stream will have the output gain\nfield set in the Opus header. The gain value will be chosen using [EBU R\n128](https://en.wikipedia.org/wiki/EBU_R_128) with a loudness value of -23\n[LUFS](https://en.wikipedia.org/wiki/LKFS), which is 5 dB quieter than\nReplayGain.\n\nThe presence of either `R128_TRACK_GAIN` or `R128_ALBUM_GAIN` tags will allow\nplayers that support these to play tracks at an appropriate volume. However, in\naudio players that do not support these tags, track will likely sound extremely\nquiet (unless your entire music collection is normalized to -23 LUFS).\n\nEven more problematically, using `opusenc` with a FLAC file that does not have\nembedded ReplayGain tags will produce a file that plays at the original volume\nof the source audio. This difference in behaviour means that it's not possible\nfor players that do not support `R128` tags to assume that different Opus files will\nplay at a similar volume, despite the presence of the internal gain header.\n\nEven if a player does support the `R128` tags, this is not enough to correctly\nplay Opus files at the right volume. In the case described above, `opusenc`\nwill use the internal gain to apply album normalization, meaning that it does\nnot generate a `R128_ALBUM_GAIN` tag. Without this, it's not possible for a\nmusic player to play a track at album volume without again assuming that the\ninternal gain corresponds to an album normalization at -23 LUFS.\n\n## Q \u0026 A\n\n### How is loudness calculated?\n\nLoudness is calculated using [ITU-R\nBS.1770](https://en.wikipedia.org/wiki/LKFS). This is the standard used by [EBU\nR 128](https://en.wikipedia.org/wiki/EBU_R_128) for measuring loudness and the\none intended for use when calculating Opus `R128` tags.\n\n### What's with the name `zoog`?\n\n`zoog` stands for \"Zero Opus Output Gain\" and was the name of the original tool\nin this project. It served a similar purpose to `opusgain` but used the existing\n`R128` tags to determine file volume rather than decoding audio directly.\n\n`zoog` was deprecated because the issues around whether it is possible to\nassume that a track is album normalized made it possible to break album\nnormalization if it occured via the ouput gain value and not the\n`R128_ALBUM_GAIN` tag.\n\n### When should I use `opusgain` versus `loudgain`\n\nIf you only play Opus files in players which support `R128` tags, then use\n[loudgain](https://github.com/Moonbase59/loudgain).\n\nYou should use `opusgain` if you play Ogg Opus files in players that do not\nsupport `R128` tags and would like them to play at either their original\nvolume, or at the volumes suggested by ReplayGain or EBU R 128.\n\nOnce you have set the internal gains of a set of Opus files to the desired\nvalues, then `loudgain` is likely preferable for any future tag updates related to\nnormalization.\n\n### How can I check if `opusgain` is working correctly?\n\nApplying `opusgain` to various test files then reviewing the diagnostic output\nand `R128` tags generated by [loudgain](https://github.com/Moonbase59/loudgain)\nwhen applied to the rewritten files is helpful in this regard.\n\n## Disclaimer\n\nPlease see LICENSE. Unless you have a source you can easily reconstruct your\nOgg Opus and/or Vorbis files from, the author recommends making a backup of any\nfiles you intend to modify first, and running `opusinfo` afterwards on any\nprocessed files.\n\n## References\n\n1. [RFC 7845 - Ogg Encapsulation for the Opus Audio Codec](https://www.rfc-editor.org/rfc/rfc7845.txt)\n2. [Vorbis I Specification](https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html)\n3. [Ogg Vorbis identification header](https://wiki.xiph.org/OggVorbis)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrancisrussell%2Fzoog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrancisrussell%2Fzoog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrancisrussell%2Fzoog/lists"}