Skip to content
MOCG3DPublic

About

Spatial stereo music for Minecraft — binaural sources, music regions, playlists, synced lyrics and a permission-aware control panel. Built on Simple Voice Chat. Supports 1.12.2 through 26.3.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

Vmse logo

Vmse — spatial music for Minecraft servers

Download on Modrinth Release 1.2.0 中文文档

Minecraft Java Simple Voice Chat License


Vmse plays music through Simple Voice Chat's positional audio channels, so the sound really does come from a place in the world: two sources at ±45° that turn with your head, a proper binaural stage, and a filter that keeps other players from hearing what is meant for you.

It also does the boring parts: playlists, music regions, downloads from a URL or a Bilibili page, and lyrics that stay in step with the audio.


What you get

Spatial stereo Two sources placed either side of the listener with inter-aural delay, head shadow and crossfeed. The image stays put when you turn, because the sources are recomputed from your yaw every tick — with velocity prediction so it does not lag at sprint speed.

mono / stereo / wide, chosen per player with /vmse surround.

5.1 was removed in 3.0. Six streams cost three times the bandwidth and, in a game where everyone wears headphones, sounded worse than two well-processed sources. The binaural renderer does the work instead.

Low bandwidth by default A player who has never run /vmse surround gets one mono stream on a static channel: half the packets, no position updates at all, and no chance of drift. Running /vmse surround stereo opts that player into the spatial path for good.

Eight effect presets off, studio, headphone, wide, hall, bass, vocal, night — real DSP: biquad EQ, stereo width, crossfeed, early reflections, reverb, compressor and a limiter that actually holds. viper, dolby, 3d and a few other names are accepted as aliases.

Music regions Circle or square zones that start a playlist when someone walks in and stop when they leave. Region playback has its own volume, layout and effect.

Playlists Sequential, shuffle, loop, loop-one. Shuffle uses a bag, so a twenty-song playlist really plays twenty different songs before repeating one. Stored as YAML, per player or server-wide.

Downloads /vmse download <link> <name> handles a direct audio URL, or — with yt-dlp configured — a Bilibili, YouTube or SoundCloud page. The response's Content-Type decides the file extension, so a .mp3 link that actually serves Opus is stored correctly instead of producing a file nothing can decode. Private addresses, file:// URLs and oversized downloads are refused.

Downloaded packs (.zip)

A download link can point at a zip containing tracks and a lyric sheet. The archive is treated as hostile from the first byte:

  • entry names may not contain .., may not be absolute, and may not use backslashes or drive letters
  • only audio and lyric extensions are allowed; anything else rejects the pack
  • the first bytes of every entry are checked: MZ, ELF, Mach-O, Java class and #! signatures reject the archive
  • limits on entry count, per-entry size, total size and compression ratio stop zip bombs
  • the declared size is not trusted; bytes are counted while writing

Anything that fails is deleted along with its staging directory. A pack that passes is unpacked into the cache, the archive is deleted, same-named files are replaced, and playback starts immediately. /vmse download puts the tracks in the music folder with the lyric beside them instead.

Built-in song recognition /vmse index rebuild fingerprints every track in the music folder; the index lives in plugins/Vmse/fingerprints.bin and nothing is uploaded anywhere. /vmse identify <file> then answers "which of my songs is this, and how far in?" - a 20 second clip cut from 60 s into a track comes back as that track at 60.0 s with 94% confidence.

The fingerprint is the classic spectral-peak-pair design: find the strongest peaks, hash each pair together with the gap between them, and let thousands of matching hashes vote on one offset. It survives re-encoding, volume changes and noise because it never compares waveforms.

/vmse lyrics <file> uses it: if the file is a recording of something already in the library, that song's lyric timeline is reused and moved to the right place instead of being guessed from energy. Only when nothing matches does it fall back to segmentation.

A song with no lyric file at all gets one the first time it is played: the timeline is written next to the music and reused from then on. An existing lyric file is never overwritten - a hand-made LRC or JSON wins, because the JSON takes priority when displaying and a generated placeholder would silently replace the real words.

Lyrics come from the network, not from guessing

There is no "make up a timeline from the waveform" mode any more. That path was removed: measured against a real 53-line sheet it landed 3.5 seconds out on average and 9.4 seconds out at worst, which is a wrong lyric on screen, not a feature. A wrong line is worse than no line.

What happens instead, in order:

  1. A lyric file next to the track, or in plugins/Vmse/lyrics/, is used as is.
  2. Otherwise the track is looked up on LRCLIB and NetEase, in that order.
  3. Every candidate is scored: title 55%, artist 25%, length 20%. A search result that merely shares a title scores low and is thrown away.
  4. The sheet itself has to fit the file: at least four lines, monotonic timings, nothing running more than eight seconds past the end, and not stopping in the first third. A live version, a cover or a remix fails that check.
  5. Only an accepted sheet is written, as mc_lyrics JSON, and it is reused from then on.

Tested on 反乌托邦 by 乌托邦P with no local lyric file: found on the network, 53 lines, first at 0 ms, "反乌托邦中的争夺" at 15 900 ms, "在这反乌托邦里" at 129 870 ms - identical to the hand-made LRC. A same-titled decoy by a different artist was ranked below the threshold and rejected.

Lyrics mc_lyrics v1 JSON (one start_ms / end_ms per line, gaps hide the line), plain LRC, or generated on the spot. Displayed on the action bar, title, boss bar or chat, with optional per-word highlighting.

Selectors that behave me, all, @a, @s, @p, @r, @e and player names, all resolved the way vanilla does. all is a selector, not a player called "all".

Permissions you can actually edit plugins/Vmse/permissions.yml declares every sub-command as admin, player or disabled. /vmse perms shows the effective table, /vmse reload perms applies it without a restart.


Install

  1. Install Simple Voice Chat — it is required, Vmse uses its audio API.
  2. Drop VMSE-1.2.0-1.19-1.21.11.jar into plugins/.
  3. Start the server. config.yml, permissions.yml and the language files are written on first run.
  4. Put music in plugins/Vmse/music/ — decoded natively, no external tools: mp3, ogg, wav, flac, aiff, au and raw PCM. FLAC uses a built-in decoder that is bit-exact against the reference encoder. Add ffmpeg for m4a/alac, aac, wma, ape, wavpack, opus, dsd and amr.
  5. /vmse help.

Optional but recommended: set network.yt-dlp and network.ffmpeg in the config if you want page links and the wider format list.


Commands

Command What it does
/vmse play [target] <song> [-original] Play a file; -original bypasses every effect and cache
/vmse url <target> <link> Play a link without saving it
/vmse stop / pause [target] Stop or pause
/vmse list [page] Browse the music folder
/vmse effect <list|set|info> [preset] [target] Effect presets
/vmse surround [target] [mono|stereo|wide] Choose a layout
/vmse playlist <create|delete|add|remove|mode|play|stop|list> Playlists
/vmse region <create|remove|list|info|bind|track|volume|layout> Music regions
/vmse wand Get the wand (region and source selection)
/vmse download <link> <name> Download audio
/vmse lyrics <song> Generate a lyric timeline
/vmse identify <file> Work out which song a recording is
/vmse index <status|list|rebuild> Build or inspect the fingerprint index
/vmse vmsg <song> Send as a voice message
/vmse confirm [cancel] Confirm a large broadcast
/vmse stats Engine state: frames, late packets, cache
/vmse perms Show the permission table
/vmse reload <all|config|lang|perms> Reload

Targets accept me, all, @a, @s, @p, @r or a player name. Leaving the target out means yourself.

The wand

/vmse wand gives you a golden hoe. Left-click air to switch mode.

  • Region mode — left-click one corner, right-click the other. A confirmation prompt shows the area and the estimated number of affected chunks.
  • Source mode — right-click a block inside a region to place a sound source.

Configuration

The interesting parts of config.yml:

playback:
  low-bandwidth-default: true   # mono static channel until /vmse surround
  pcm-cache-max-mb: 192         # decoded audio kept in memory

surround:
  default-layout: stereo
  stereo-separation: 1.2        # half the distance between sources, in blocks
  position-lead-ms: 60          # velocity prediction, fixes sprint lag
  position-epsilon: 0.06        # skip updates smaller than this

network:
  yt-dlp: ''                    # path to yt-dlp, empty disables page links
  ffmpeg: ''                    # path to ffmpeg, empty disables extra formats
  max-download-mb: 80

lyrics:
  lookup-online: true           # try LRCLIB when there is no sidecar file
  auto-generate: true           # build a timeline after /vmse download

Building

mvn -Pmc1194 clean package     # 1.19 - 1.21.11   -> target/VMSE-1.2.0-1.19-1.21.11.jar
mvn -Pmc1165 clean package     # 1.16.5 - 1.18.2  -> target/VMSE-1.2.0-1.16.5-1.18.2.jar
mvn -Pmc1122 clean package     # 1.12 - 1.12.2    -> target/VMSE-1.2.0-1.12.2.jar
mvn -Pmc263  clean package     # 26.1 - 26.3      -> target/VMSE-1.2.0-26.1-26.3.jar
Jar Server Java
VMSE-1.2.0-1.12.2.jar 1.12 - 1.12.2 8
VMSE-1.2.0-1.16.5-1.18.2.jar 1.16.5 - 1.18.2 8 - 17
VMSE-1.2.0-1.19-1.21.11.jar 1.19 - 1.21.11 17 - 21
VMSE-1.2.0-26.1-26.3.jar 26.1 - 26.3 25

1.13 to 1.15 are skipped because Simple Voice Chat itself has no build for them: there is no way to play spatial audio there at all.

Each jar is compiled against that server's own API, so no jar relies on a method that only exists in a newer version. The code base is Java 8 compatible throughout, which is what makes the 1.12 jar possible: records, switch expressions and the Java 9+ collection factories are gone, replaced by a small J8 helper class.

Where a version genuinely lacks an API, the call goes through guarded reflection instead of a compile-time dependency: hex colours fall back to the closest vanilla colour on 1.12-1.15, sendActionBar degrades to the chat bar, the wand marker uses lore instead of a persistent data container, and the Voice Messages integration is entirely reflective so 1.12 does not need that mod to exist at all.

All four jars were then started on a real server with Simple Voice Chat 2.6.27: 1.12.2 on Java 8, 1.16.5 on Java 11, 1.18.2 and 1.19.4 on Java 17, 1.20.6, 1.21.1 and 1.21.11 on Java 21, and 26.3 on Java 25. Each loaded the plugin, registered with Simple Voice Chat and answered /vmse stats, with no exceptions. Merging eight jars into four is justified by measurement, not by hope: a jar built against the 1.19.4 API still loads on 26.3.

Tests

./test/run-tests.sh

156 headless assertions over the parts that do not need a server: playlist modes and shuffle, LRC parsing, energy segmentation, lyric alignment, JSON output, audio resampling, WAV and raw PCM decoding, FLAC (bit-exact against a reference-encoded sample), fingerprint matching and index round-tripping, layout maths, effect presets and the downloader's URL guards. It runs on a phone.


Licence

AGPL-3.0. If you run a modified copy as a public service, publish the changes.

About

Spatial stereo music for Minecraft — binaural sources, music regions, playlists, synced lyrics and a permission-aware control panel. Built on Simple Voice Chat. Supports 1.12.2 through 26.3.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages