AudioBox

Play your own music in Project Zomboid. A Java agent that opens the game’s sound engine to Lua, a player mod built on it, optional extras, and a tool for making album packs. Minimum tested build: 42.21.

AudioBox Agent

A Java agent that loads with the game and exposes the game’s own FMOD calls to Lua as the global AudioBoxFMOD. It exposes FMOD and nothing else: it finds and plays no music by itself. Mods decide what to play. Player, Extras and any mod of your own use the same global. The jar ships inside AudioBox Player, as common/audiobox-agent.jar.

Why it exists

Vanilla Lua can play sounds declared in item scripts, through sound emitters. It needs every sound declared ahead of time, and it has no way to seek, report position or length, pause and resume, muffle one channel, apply a low-pass or DSP effect, change distances at runtime, check whether a channel went virtual or how audible it is, read the environment parameters, or handle voice buffers.

It also has a memory cost. In the decompiled 42.21 game code, the file loader opens each sound with mode 16 (18 when looping), never sets the stream flag, and never releases the result, so everything it loads stays loaded. FMOD’s documentation says a file opened without a stream flag is decoded in full into memory. At 44.1 kHz stereo, three minutes of 16-bit audio is about 32 MB, so one twelve-track album loaded that way would hold roughly 380 MB, and every further album adds its own share. The decoding itself has not been measured in game.

The agent opens files by path at runtime with the stream flag (mode 144), so FMOD decodes a small buffer as the track plays instead of the whole file. Player and Extras release each sound when its track stops, so memory use stays flat however much music you own. Your music is not limited to what scripts declared at startup, and tracks can be sought, paused, muffled and synced between players, which the vanilla system cannot do at all.

Install and check

  1. Copy audiobox-agent.jar from AudioBox Player’s common folder into the game’s install folder, next to ProjectZomboid64.json.
  2. Add this as the first entry of vmArgs in ProjectZomboid64.json. It has to be there or in the .bat file; Steam’s Launch Options field cannot load it:
    "-javaagent:audiobox-agent.jar",
  3. If you start the game with ProjectZomboid64.bat instead, add -javaagent:audiobox-agent.jar right after ".\jre64\bin\java.exe" on both of its java lines.
  4. Replace the jar only with the game fully closed. The agent writes a log file beside the jar.
  5. In the Lua console, pairs(AudioBoxFMOD) lists every exposed function name. If a newly added function is nil, the game is still running an older jar.

The server-side Lua of Player and Extras never calls the agent, so a dedicated server does not need the line (not yet tried on a real server). The global is present again after every Lua reload. Guard your calls with if AudioBoxFMOD then so your mod still loads without the agent.

Only trust the official agent

A Java agent runs inside the game with the game’s full permissions. If a mod ships its own copy of audiobox-agent.jar, do not trust it: a modified agent can do more than bridge FMOD to Lua, including running arbitrary code. Take the agent only from the official AudioBox Player download. Mods built on the agent should list AudioBox Player as a required mod and never bundle a copy of their own.

The 35 functions

Every public static of AudioBoxFMOD is exposed to Lua under its own name, and handles are plain numbers passed back in. “Observed in game” means it appears in the project’s proven-facts record. Calls on a finished channel return 3.

CallReturnsDoesStatus
FMOD_System_CreateSound(String path, long mode)longOpens a file by path and returns a sound handle. 0 means failure.Observed in game
FMOD_System_PlaySound(long sound, boolean paused)longStarts a sound and returns a channel handle.Observed in game
FMOD_Channel_SetVolume(long channel, float volume)intSets the channel volume.Observed in game
FMOD_Channel_SetPaused(long channel, boolean paused)intPauses or resumes.Observed in game
FMOD_Channel_Stop(long channel)intStops the channel.Observed in game
FMOD_Channel_IsPlaying(long channel)booleanFalse once a track reaches its natural end.Observed in game
FMOD_Sound_Release(long sound)intFrees the sound.Observed in game
FMOD_Channel_Set3DAttributes(long channel, float x, y, z, vx, vy, vz)intPlaces the channel in the world; volume and panning follow.Observed in game
FMOD_Channel_Set3DMinMaxDistance(long channel, float min, float max)intSets the fade distances.Observed in game
FMOD_Channel_Set3DLevel(long channel, float level)intLevel 0 flattens panning.Observed in game
FMOD_Sound_GetLength(long sound, long timeunit)longLength; unit 1 is milliseconds.Observed in game
FMOD_Channel_GetPosition(long channel, int postype)longCurrent position; type 1 is milliseconds.Observed in game
FMOD_Channel_SetPosition(long channel, long position)intSeeks mid-playback.Observed in game
FMOD_Channel_Set3DOcclusion(long channel, float, float)intMuffles a channel. Player uses it for walls and vehicles.Observed in game
FMOD_Channel_SetPriority(long channel, int priority)intPriority 0 keeps a channel from going virtual under sound load.Observed in game
FMOD_Channel_IsVirtual(long channel)booleanWhether FMOD has made the channel virtual (silent).Observed in game
FMOD_GetGlobalParameter(String name)floatReads a game environment parameter. Unknown names return -999.Observed in game
FMOD_Channel_SetPitch(long channel, float pitch)intPlayback speed. Player uses it for fast forward, rewind and sync.Used by Player and Extras
FMOD_Channel_SetLoopCount(long channel, int count)int-1 loops forever (open the sound with loop flag 2).Used by Player and Extras
FMOD_Sound_SetLoopCount(long sound, int count)intLoop count on the sound.Not used by a shipped mod yet
FMOD_Channel_SetMode(long channel, long mode)intChanges a channel’s mode flags, for example 2D (8) or 3D (16).Not used by a shipped mod yet
FMOD_Channel_SetPan(long channel, float pan)intStereo pan.Not used by a shipped mod yet
FMOD_Channel_Set3DSpread(long channel, float spread)int3D spread angle.Not used by a shipped mod yet
FMOD_Channel_Set3DDopplerLevel(long channel, float level)intDoppler effect amount.Not used by a shipped mod yet
FMOD_Channel_GetAudibility(long channel)floatHow audible the channel currently is.Not used by a shipped mod yet
FMOD_Channel_SetLowPassGain(long channel, float gain)intLow-pass gain on a channel.Not used by a shipped mod yet
FMOD_System_CreateDSPByType(long type)longCreates a DSP effect and returns its handle.Not used by a shipped mod yet
FMOD_Channel_AddDSP(long channel, int index, long dsp)intAdds a DSP to a channel.Not used by a shipped mod yet
FMOD_Channel_RemoveDSP(long channel, long dsp)intRemoves a DSP from a channel.Not used by a shipped mod yet
FMOD_Channel_GetNumDSPs(long channel)intCounts the DSPs on a channel.Not used by a shipped mod yet
FMOD_DSP_SetParameterFloat(long dsp, int index, float value)intSets a float parameter.Not used by a shipped mod yet
FMOD_DSP_SetParameterInt(long dsp, int index, int value)intSets an integer parameter.Not used by a shipped mod yet
FMOD_DSP_SetWetDryMix(long dsp, float, float, float)intSets the wet and dry mix.Not used by a shipped mod yet
FMOD_DSP_SetBypass(long dsp, long bypass)intBypasses a DSP.Not used by a shipped mod yet
FMOD_DSP_Release(long dsp)intFrees a DSP.Not used by a shipped mod yet

Mode flags

Values read from the game’s own constants: stream 128, 3D 16, 2D 8, loop 2, accurate time 16384. Add them together. Modes run in game include 136, 144 (stream plus 3D), 16520 and 16522.

Environment parameters

The game’s global parameters Inside (-1 in a vehicle, 1 in a house), RoomSize, ClosestWallDistance and RainIntensity can be read by name and describe the listener. RoomType stayed nan in testing and the cause is not traced.

Formats

mp3, ogg and FLAC are proven in game. Other formats that the game’s fmod.dll can decode have not been tried. The game’s engine cannot play Opus.

Example: stream, place, seek, clean up

if AudioBoxFMOD then
  local sound = AudioBoxFMOD.FMOD_System_CreateSound("C:/Music/track.ogg", 144)
  if sound ~= 0 then
    local ch = AudioBoxFMOD.FMOD_System_PlaySound(sound, true)
    AudioBoxFMOD.FMOD_Channel_SetPriority(ch, 0)
    AudioBoxFMOD.FMOD_Channel_SetVolume(ch, 0.8)
    AudioBoxFMOD.FMOD_Channel_Set3DAttributes(ch, x, y, z, 0, 0, 0)
    AudioBoxFMOD.FMOD_Channel_Set3DMinMaxDistance(ch, 1, 20)
    AudioBoxFMOD.FMOD_Channel_SetPaused(ch, false)

    local lengthMs = AudioBoxFMOD.FMOD_Sound_GetLength(sound, 1)
    local posMs = AudioBoxFMOD.FMOD_Channel_GetPosition(ch, 1)
    AudioBoxFMOD.FMOD_Channel_SetPosition(ch, 90000)

    AudioBoxFMOD.FMOD_Channel_Stop(ch)
    AudioBoxFMOD.FMOD_Sound_Release(sound)
  end
end

Example: loop a track and speed it up

local sound = AudioBoxFMOD.FMOD_System_CreateSound(path, 144 + 2)
local ch = AudioBoxFMOD.FMOD_System_PlaySound(sound, false)
AudioBoxFMOD.FMOD_Channel_SetLoopCount(ch, -1)
AudioBoxFMOD.FMOD_Channel_SetPitch(ch, 1.04)

Limits

Only calls that take and return plain values are exposed, because most of FMOD’s getters want raw native addresses and a bad address can crash the game. Calls that need pointers or buffers (ID3 tags, voice recording, raw sample buffers) need a helper written in the agent first, and are not available yet.