Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 119 additions & 9 deletions emotecraft/creatingemotes/creatingmusic.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,129 @@
---
sidebar_position: 3
description: "How to create music for emotes using Note Block Studio."
description: "How to give an emote a sound, and how to prepare the .opus file it needs."
---

# Creating Music for Emotes
You can make music for your emotes using [Note Block Studio](https://noteblock.studio/)!

Once that is done run the program, you should be greeted with a blank song. Here you can create the song
When you are done the song click save and save it to the emotes folder as \{nameOfEmoteFile\}.nbs.
An emote can carry its own sound. Put an `.opus` file in the emotes folder next to the emote and give it the
same name, and Emotecraft plays it whenever the emote plays — for you and for everyone who sees you.

```text
emotes/
wave.json
wave.opus
```

The sound travels with the emote, so other players hear it without having to download anything themselves.

## What .opus is

Nothing special, and nothing invented for this mod. Opus is an ordinary audio format, the same one Discord and
most voice chats use, and `.opus` is simply an audio file holding it. Emotecraft asks for it because it stays
small: a few hundred kilobytes for a whole song, which is what makes sending the sound along with the emote
possible at all.

Playing one back depends on your system:

| | |
|---|---|
| Windows 10 and 11 | opens as is |
| Linux, Android | opens as is |
| macOS | QuickTime and Music cannot, [VLC](https://www.videolan.org/vlc/) can |
| Anywhere | drag the file into a Chrome or Firefox window |

That browser trick is the quickest way to check a file without installing anything, and the converter below
lets you listen to the result before you download it at all.

## Making the file

The easiest way is the **[Emote Sound Converter](/opus-converter)**. Drop in any audio file, choose how it
should sound, and download the result. It runs entirely in your browser, nothing is uploaded anywhere, and it
will not let you make a file the mod would reject.

If you would rather do it yourself, [ffmpeg](https://ffmpeg.org/) does the same job:

```bash
ffmpeg -i song.mp3 -af "pan=mono|c0=0.5*c0+0.5*c1,alimiter=level=disabled:limit=0.9" \
-c:a libopus -b:a 32k -vbr constrained -ar 48000 wave.opus
```

Do not reach for `-ac 1` to make it mono. It adds the two channels together without halving them, which
is 6 dB of gain the track never asked for, and everything above full scale comes back as crackling. The
`pan` filter mixes them properly, and the limiter catches the overshoot the encoder itself adds at low
bitrates. `level=disabled` matters: without it the limiter puts the level straight back.

The sound has to be **mono**. Everything else below is a limit rather than a requirement.

## Looping

By default the sound plays once, even if the emote itself repeats forever. To make it loop, add a
`LOOPSTART` tag saying which sample it should jump back to. There are 48000 samples in a second, so
`LOOPSTART=96000` restarts from the two second mark:

```bash
ffmpeg -i song.mp3 -af "pan=mono|c0=0.5*c0+0.5*c1,alimiter=level=disabled:limit=0.9" \
-c:a libopus -b:a 32k -vbr constrained -ar 48000 -metadata LOOPSTART=96000 wave.opus
```

Use `LOOPSTART=0` to simply start over from the beginning. A later value is for tracks with an intro that
should only be heard once. The converter has a checkbox for this and works the number out for you.

Both ends are honoured to the sample. An encoder pads its last frame out to a whole 20 ms, and that padding
is dropped rather than played, so a track that was seamless stays seamless.

The sound and the animation loop independently, each on its own length, so they drift apart over time unless
they happen to line up.

## Limits

| | |
|---|---|
| Channels | mono only |
| Size | 1 MB, shared with the emote itself |
| Bitrate | 96 kbps, and low enough to fit the size limit |
| Length | 10 minutes |

The size limit is the one you will actually run into: it is the largest packet a Minecraft server will pass
along, and the animation has to fit in it too. At 32 kbps that is roughly four and a half minutes of music,
which is plenty for most emotes. If the sound does not fit, the emote still plays — just silently for
everyone else.

## Volume

Emotecraft measures how loud each sound is and evens them out, so that a quiet emote and a loud one sit at
the same level. Do not normalise or amplify the file yourself; leave it as it is. Players who prefer the
original loudness can turn this off with **Normalize the volume of emote sounds** in the options.

If the file carries an `R128_TRACK_GAIN` tag, that is used instead of measuring, which saves a little work
when the emote loads.

## Note Block Studio (legacy)

Older versions used [Note Block Studio](https://noteblock.studio/) songs saved as
`\{nameOfEmoteFile\}.nbs`. Current versions no longer play them.

They are still passed along, though, so an emote can carry both files at once:

```text
emotes/
wave.json
wave.opus
wave.nbs
```

Everyone hears something. Players on a current version get the `.opus`; players on an older one get the
`.nbs`, because the mod keeps sending it to anyone who cannot read Opus. A server passes on whichever one
each player is able to play, so a single pack works across versions.

If you are publishing an emote that already has a `.nbs`, keeping it costs a few kilobytes and loses nothing.
Only the `.opus` is sent to modern clients; the `.nbs` travels alongside it when an emote is stored, and on
its own when the other side is old.

Emotes from [EmotecraftLibrary (RedlanceEmotes)](https://emotes.redlance.org/) need none of this: the library
generates the `.opus` for every emote that has a `.nbs`, so downloads already carry both.

# Useful links
- [Emote Sound Converter](/opus-converter) - Turn any audio file into an emote sound, in your browser.
- [Note Block World](https://noteblock.world/) - The largest public community centered around Minecraft note blocks.
- [Note Block Tool](https://github.com/RaphiMC/NoteBlockTool/releases/latest) - Tool for importing, exporting, batch manipulating and playing Minecraft note block songs.

# Limitations
- In 1.21.5+ nearly every feature is supported
- In versions lower you have only default instruments and 2.5 octaves
- Only nbs is supported by the main mod; you can use [EmoteTweaks](https://modrinth.com/mod/emote-tweaks) to play `.wav` files, but it's unstable.
32 changes: 32 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
"dependencies": {
"@docusaurus/core": "^3.9.2",
"@docusaurus/preset-classic": "^3.9.2",
"@ffmpeg/ffmpeg": "^0.12.15",
"@ffmpeg/util": "^0.12.2",
"@mdx-js/react": "^3.1.1",
"clsx": "^2.1.1",
"prism-react-renderer": "^2.4.1",
Expand Down
87 changes: 87 additions & 0 deletions src/pages/opus-converter.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
.drop {
border: 2px dashed var(--ifm-color-emphasis-300);
border-radius: var(--ifm-global-radius);
padding: 2.5rem 1rem;
text-align: center;
cursor: pointer;
transition: border-color 0.15s ease, background-color 0.15s ease;
}

.drop:hover,
.dropOver {
border-color: var(--ifm-color-primary);
background-color: var(--ifm-color-emphasis-100);
}

.dropTitle {
display: block;
font-weight: 600;
margin-bottom: 0.25rem;
}

.hidden {
display: none;
}

.field {
margin-bottom: 1rem;
}

.field label {
display: block;
font-weight: 600;
margin-bottom: 0.25rem;
}

.field select,
.field input[type='text'] {
width: 100%;
padding: 0.5rem 0.6rem;
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: var(--ifm-global-radius);
background-color: var(--ifm-background-color);
color: var(--ifm-font-color-base);
font: inherit;
font-size: 0.95rem;
}

.hint {
color: var(--ifm-color-emphasis-700);
font-size: 0.875rem;
margin: 0.35rem 0 0;
}

.field label.toggle {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0;
cursor: pointer;
}

.toggle input {
width: 1.05rem;
height: 1.05rem;
margin: 0;
accent-color: var(--ifm-color-primary);
}

.summary {
width: 100%;
margin-bottom: 1rem;
}

.summary td {
border: 0;
padding: 0.25rem 0;
}

.summary td:last-child {
text-align: right;
font-variant-numeric: tabular-nums;
}

.player {
width: 100%;
margin-bottom: 1rem;
}
Loading