Skip to main content

The .bemuse File Format

The .bemuse file format is created for efficient distribution of sound assets (.ogg) files over the web browser. Before we created the .bemuse file format, we considered the following more conventional approaches:

  • Distribute .ogg files individually: This would lead to hundreds of small files per song. This can make loading slow, due to the many HTTP requests.

  • Distribute all sounds packaged in a .zip archive: This would lead to files that are about 20 megabytes large. In case of a download failure, the whole 20 megabyte file would have to be downloaded again.

The .bemuse file format aims to strike a balance between these two extremes. We bundle many .ogg files into chunks of around 1.4 MB. There are two types of file:

  • metadata.json (index file)
  • *.bemuse (sound data chunk file)

metadata.json (index file)

{
files: Array<{
name: string
ref: [index: number, start: number, end: number]
}>
refs: Array<{
path: string
hash: string
size: number
}>
}

The metadata file is JSON-based.

  • The files array contains a list of all available sound files.
    • name The sound file name, e.g. snare.ogg.
    • ref An array of three numbers that define the sound data:
      • The index of the chunk at which the sound data resides.
      • The index of the first byte in the chunk payload where the sound data starts.
      • The index of the last byte in the chunk payload where the sound data ends.
  • The refs array contains info about where each chunk is located.
    • path Relative URL where the chunk file can be downloaded.
    • hash MD5 hash of the chunk file content.
    • size Size of the file in bytes.

*.bemuse (sound data chunk file)

This file is a binary format.

  • First 10 bytes: Signature string: BEMUSEPACK.
  • Next 4 bytes: All zeroes (legacy reasons).
  • Chunk payload follows. This can contain arbitrary data.

Example

{
"files": [
{ "name": "beat.ogg", "ref": [0, 0, 1596631] },
{ "name": "bgm.ogg", "ref": [1, 0, 1574274] },
{ "name": "synth.ogg", "ref": [2, 0, 1350505] },
{ "name": "cymbal.ogg", "ref": [2, 1350505, 1415370] },
{ "name": "snare.ogg", "ref": [2, 1415370, 1430427] }
],
"refs": [
{
"path": "oggs.0.f7f0e987.bemuse",
"hash": "f7f0e987c5b9070b29d66b74c4b77609",
"size": 1596645
},
{
"path": "oggs.1.03981ebc.bemuse",
"hash": "03981ebc7f52705fb44063de5a79930b",
"size": 1574288
},
{
"path": "oggs.2.8ed2dece.bemuse",
"hash": "8ed2dece0eef7cd65195d7ef6a72708d",
"size": 1430441
}
]
}

For example, if you want to access the contents of snare.ogg, the following steps can be followed:

  1. Look for the entry whose name is snare.ogg.
  2. Look at the ref. In this case, [2, 1415370, 1430427].
  3. Look at refs[file.ref[0]].path. In this case, oggs.2.8ed2dece.bemuse.
  4. Download that file and take the .slice(file.ref[1] + 14, file.ref[2] + 14).