
ModAudio
Replace game audio with your own mixtape.
Date uploaded | 9 months ago |
Version | 1.1.0 |
Download link | Marioalexsan-ModAudio-1.1.0.zip |
Downloads | 9954 |
Dependency string | Marioalexsan-ModAudio-1.1.0 |
This mod requires the following mods to function

BepInEx-BepInExPack
BepInEx pack for Mono Unity games. Preconfigured and ready to use.
Preferred version: 5.4.2100
Nessie-EasySettings
A mod API for easily adding options to the settings menu.
Preferred version: 1.2.0
README
ModAudio
Replace game audio with your own mixtape.
You can use this mod to make audio pack mods, or to just replace audio on a whim.
Currently, .ogg
, .mp3
and .wav
formats are supported, although .ogg
and .mp3
are preferred due to reduced file size.
How to use
ModAudio loads custom audio and routing information from any custom mods you have in the plugins folder (BepInEx/plugins/YourTeam-YourModName/audio
).
It uses a combination of audio files and the __routes.txt
configuration file located in the audio folder of a mod.
It also loads custom audio from its own folder (BepInEx/plugins/Marioalexsan-ModAudio/audio
). You can place your custom audio in here if you don't want to make a standalone mod.
Where is this audio folder located???
When using r2modman, you can find BepInEx in your profile folder for ATLYSS ("Settings" button, first option in the list). For example: C:\Users\USERNAME\AppData\Roaming\r2modmanPlus-local\ATLYSS\profiles\Default
.
If you've installed BepInEx manually, then go to Steam's ATLYSS install path. For example: C:\Program Files (x86)\Steam\steamapps\common\ATLYSS
.
From the r2modman profile or the Steam install, navigate to BepInEx/plugins/Marioalexsan-ModAudio/audio
, or your custom mod's audio folder.
Audio mod example
Note: There are already plenty of audio mods created that use ModAudio on Thunderstore. If something in here is confusing, you can do "Manual download" for those audio mods to take a look at how they package their assets.
Here's an example of an audio mod that uses all of the features combined:
mod folder structure under BepInEx/plugins
ModFolder
|- audio
|- __routes.txt
|- _mu_flyby.mp3
|- darkest_dungeon_combat.mp3
|- risk_of_rain_2_boss.wav
|- team_fortress_2_loadout.ogg
|- cod_hitsound_01.mp3
|- cod_hitsound_02.mp3
|- cod_hitsound_03.mp3
__routes.txt file contents
# Empty lines and lines that start with a hashtag are ignored
# Musics
_mu_wonton5 = darkest_dungeon_combat
_mu_ekca = risk_of_rain_2_boss
_mu_selee = team_fortress_2_loadout
# Hit sounds
weaponHit_Normal(average) = cod_hitsound_01
weaponHit_Normal(average) = cod_hitsound_02 / 0.5
weaponHit_Normal(average) = cod_hitsound_03 / 0.5
weaponHit_Normal(average) = ___default___ / 2
This audio mod will do the following:
- play
_mu_flyby.mp3
instead of_mu_flyby
(main menu music) - implicitly because the file name matches the clip - play
darkest_dungeon_combat.mp3
instead of_mu_wonton5
(Grove combat) - play
risk_of_rain_2_boss.wav
instead of_mu_ekca
(Grove boss music) - play
team_fortress_2_loadout.ogg
instead of_mu_selee
(Character selection music) - play one of
cod_hitsound_01.mp3
,cod_hitsound_02.mp3
orcod_hitsound_03.mp3
for Normal type average weapon hits- the total weight for this is
1 (implicit) + 0.5 + 0.5 + 2 = 4
cod_hitsound_01.mp3
will play with a1 / 4 * 100% = 25%
chancecod_hitsound_02.mp3
will play with a0.5 / 5 * 100% = 12.5%
chancecod_hitsound_03.mp3
will play with a0.5 / 5 * 100% = 12.5%
chance- the original, unmodified sound clip will play with a
2 / 4 * 100% = 50%
chance
- the total weight for this is
How does this work???
Direct replacement
You can replace audio clips directly if the file name matches the clip name.
- Choose a clip you want to replace (for example,
_mu_wonton5
- Crescent Grove's action music). - Take your custom audio, and rename it so that it has the same name as the clip (for example,
coolmusic.mp3
->_mu_wonton5.mp3
). - Place the audio file in the
audio
folder.
Reroute the clip in __routes.txt
You can do custom replacements by specifying replacement information in the __routes.txt
file.
- Choose a clip you want to replace (for example,
_mu_wonton5
- Crescent Grove's action music). - Take your custom audio, and put it in the
audio
folder (for example,coolmusic.mp3
). The audio file can have any name you want. - Create a
__routes.txt
file in theaudio
folder, or open it with Notepad if it already exists. - Add the clip name and the file name without the extension, separeted by
=
on a new line :_mu_wonton5 = coolmusic
.
This will tell ModAudio to play coolmusic.mp3
every time _mu_wonton5
would play in the game.
If you add multiple lines that have use the same clip, then ModAudio will play one of them randomly.
For example, firstbossmusic.mp3
and secondbossmusic.mp3
will each play about half of the time:
_mu_wonton5 = firstbossmusic
_mu_wonton5 = secondbossmusic
If you want clips to play with different chances, you can specify a number as a weight for the random roll. Separate it from the audio file name with a /
.
For example, firstbossmusic
will play 2/3rds of the time (1.0 / (1.0 + 0.5)), and secondbossmusic
will play 1/3rd of the time (0.5 / (1.0 + 0.5)):
_mu_wonton5 = firstbossmusic / 1.0
_mu_wonton5 = secondbossmusic / 0.5
If you don't specify a weight, then it defaults to 1.
Finally, if you want to tell ModAudio to play the original clip, you can use the special ___default___
identifier instead of a file name:
_mu_wonton5 = ___default___ / 0.8
_mu_wonton5 = _mu_wonton5_remix / 0.2
This will play the default boss music with an 80% chance, and a remixed version with a 20% chance.
Packaging your audio mods for Thunderstore / r2modman
When packaging your mods for Thunderstore / r2modman, you need to put your audio
folder that contains your audio and __routes.txt
under the plugins
folder in the zip.
This is to make sure that r2modman won't flatten all of your files into the root directory, which might cause issues.
Here's an example of how your ZIP package should look like:
yourmod.zip
|- manifest.json
|- README.md
|- CHANGELOG.md
|- icon.png
|- plugins
|- audio
|- __routes.txt
|- _mu_flyby.mp3
|- someaudio.ogg
|- someotheraudio.wav
r2modman will take all of your content from the ZIP's plugins
and put it as-is in the mod folder, thus preserving the folder structure that ModAudio wants.
Also, your manifest.json should have ModAudio listed as a dependency, with the latest version being preferable:
manifest.json
{
"name": "YourModName",
"description": "Cool sounds and stuff",
"version_number": "1.0.0",
"dependencies": [
"BepInEx-BepInExPack-5.4.2100",
"Marioalexsan-ModAudio-1.1.0"
],
"website_url": "https://github.com/Marioalexsan/AtlyssMods"
}
Do not include the ModAudio DLL in your own mod. It's not needed, and it might cause issues with loading. You just need the dependency string in manifest.json.
Advanced usage
Multiple audio mods
If you use multiple audio mods that replace the same audio clips, ModAudio will effectively combine them into one.
For example, if you have two mods that replace the Main Menu music, then each of them will have a 50% chance to play.
If the first mod's replacement has a weight of 1, and the second one has a weight of two, then it will be a 33% / 67% chance split for either of them to play.
Custom audio folder location
If you are making a custom plugin and need to load audio information from a different folder, you can use the ModAudio.LoadModAudio()
method.
For example, this will tell ModAudio to load audio from C:\\mycoolmusicpath
for your mod, instead of trying to read the audio
folder next to the DLL.
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
[BepInDependency("Marioalexsan.ModAudio")]
public class MyCustomAudio : BaseUnityPlugin
{
private void Awake()
{
Marioalexsan.ModAudio.ModAudio.Instance.LoadModAudio(this, "C:\\mycoolmusicpath");
}
}
Audio override
In the mod's configuration file (BepInEx/config/Marioalexsan.ModAudio.cfg
), you can set OverrideCustomAudio
to true
to enable audio overrides.
This will cause anything you place under BepInEx/plugins/ModAudio/audio
to completely override what any other mods are trying to do.
For example, if there is a mod that replaces a lot of sounds, but you want to keep the main menu music intact, you can enable that option and specify this in __routes.txt
:
_mu_flyby = ___default___
This will make it play the vanilla music every time, ignoring whatever other mods have defined.
Extensive logging
Set ExtensiveLogging
to true
in the configuration file to have ModAudio log sounds played, sounds replaced, and lots of other debug information.
This is spammy, but it can be useful to debug what is happening with your mods, or find out what sounds are being played in the world.
The logging is done to BepInEx's console and BepInEx/LogOutput.log
.
Mod Compatibility
This mod version targets ATLYSS v1.6.2b. Compatibility with other game versions is not guaranteed, especially for updates with major changes.
CHANGELOG
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[4.1.1] - 2025-Oct-17
Fixed
- Crashes originating from AudioStopped() caused by reuse / overwrite of a cached array within a recursion
[4.1.0] - 2025-Oct-09
Added
- Populated more fields on some of the existing Lua proxies, allowing scripts to properly get those values
- Added new properties for the
route
parameter in target route methods that can be used to get details about the current audio source:originalClipName
- original clip name, string, readonlyclipName
- current clip name, string, readonlygameObjectName
- the name of the game object on which the audio is playing, string, readonlyhasGameObjectInHierarchy(name: string)
- returns a boolean indicating whenever the object with the given name is present in the hierarchy- this must be called using the
:
Lua syntax, likeroute:hasGameObjectInHierarchy('_AUDIO')
- this function can be expensive, so cache the result in a local variable
- this must be called using the
getGameObjectHierarchy()
- returns an array of strings that contains the game object hierarchy of this audio source- this must be called using the
:
Lua syntax, likelocal gameObjects = route:getGameObjectHierarchy()
- this function can be expensive, so cache the result in a local variable
- this must be called using the
- Audio debug menu logs now support searching via text for specific entries
Changed
- EasterEggsEnabled (an undocumented configuration option for easter eggs) has been added as an option in EasySettings for easier access
Fixed
- Fixed an issue where fixing scripts that have missing methods and soft reloading them wouldn't reapply the methods to the current pack
[4.0.0] - 2025-OCt-06 - "Don't talk to me about scripting"
Removed
- JS scripting and Jint have been fully removed
- Existing packs with JS scripts will need to be converted to use Lua instead
Added
- Added Lua scripting using a fork for Lua-CSharp (https://github.com/Marioalexsan/Lua-CSharp-Atlyss)
- Scripts are loaded from
__routes.lua
, similar to how the previous implementation worked with__routes.js
- The API syntax is similar; small differences include modules being available as globals rather than module imports
- Proxies for game objects (Player, PlayerCombat) are currently more limited due to requiring being written by hand, this will be fixed in future updates
- Scripts are loaded from
- Added an option to have an audio pack start in a disabled state
- can be set with
%enabledbydefault false
in__routes.txt
- audio packs with this setting will be disabled when booting the game for the first time
- users that enable the pack afterwards will still have it start enabled on subsequent boots
- mainly to be used for combined mods (Homebrewery + ModAudio, etc.) that wish to be less intrusive with their audio components
- can be set with
- Added an option to disable ModAudio completely
- This acts as if all audio packs are disabled, and ModAudio will also skip any audio engine changes, making it act as vanilla
- & BugAudio
- Added an option to reload audio scripts from EasySettings
- This allows prototyping and reloading scripts from disk without having to also reload the audio data
Changed
- Somewhat improved performance of scripting (mostly thanks to the scripting backend change)
- Somewhat improved general performance of the mod
[3.3.0] - 2025-Sep-17
Deprecated
- JS scripting is deprecated, and is planned to be removed in the next major release
- The reason is severe performance and memory usage issues from Jint even for simple scripts
- This will likely be replaced with another scripting tech (possibly MoonSharp / Lua) as soon as I figure out a good alternative
Changed
- Added a detection rate setting for playOnAwake sources that allows adjusting its performance impact
- It has four options: Realtime (instant), Fast (100ms), Medium (500ms) and Slow(2500ms)
- The current default option is Fast (100ms); versions prior to this acted as if they used Realtime (instant)
- Slower options reduce CPU load, but may fail to properly replace / overlay playOnAwake sources
Fixed
- One shot sources now correctly stop when AudioSource.Stop() is called on the original audio source (either by the game or other mods)
- Routing now correctly skips over routes that are already present in a chain
- Fix some performance issues with non-script routing code
[3.2.0] - 2025-Aug-30
Added
- Added a
force_play
effect: setting~ force_play : true
will cause audio to be forced to play as part of dynamic targeting, even if the audio source is stopped at the moment- This can be useful for enforcing that the switched music will play when the group is changed; for example, going from a stopped day music for Outer Sanctum, to a playing day music for Chapel of the Elders's subregion
[3.1.2] - 2025-Aug-30
Fixed
- Fixed
force_loop
not working as expected with multiple routes - Fixed
force_loop
still usingfalse
as a default value, causing it to force looping off by default instead of using existing loop settings
[3.1.1] - 2025-Aug-30
Fixed
- Fixed a skill issue from the mod author where the changelog and readme for 3.1.0 was not updated
[3.1.0] - 2025-Aug-30
Changed
~ force_loop : false
will now force audio sources to not loop if it's specified; previously, it was a no-op as far as audio sources were concerned~ force_loop : true
will still force looping on, and not specifying the property will make the audio source use whatever looping properly it normally uses
Fixed
- Fixed audio packs displaying their name as "<null>" when calling
console.log()
in the global scope of the JavaScript module - Fixed route numbers (volume, pitch, weights, etc.) incorrectly using commas on some PCs with cultures / languages that normally use commas for decimal separators
[3.0.2] - 2025-Aug-26
Fixed
- Fixed overlays not playing at all.
- Fixed aggroed creeps not being tracked correctly for scripts (mostly relevant for Combat Audio Pack).
- Fixed engine reloads causing some one shot sources to continuously "lose" volume and get silenced with multiple reloads. This includeed changing audio pack active state in EasySettings.
- Tiny optimizations on some methods, nothing too significant though.
[3.0.1] - 2025-Aug-25
Changed
- Added a WIP tab to the debug menu that shows current count of audio sources in the level, and count of audio sources tracked by ModAudio
Fixed
- Fixed an issue where one shot audio sources wouldn't get cleaned up properly, causing increasingly worse performance while staying in a level
[3.0.0] - 2025-Aug-24 - "The Bugfest Development Hell Update"
Trust me when I say that these changelogs won't make too much sense, or that they might be incomplete.
If you want to find out how to use some of the new features, you'll either have to
look through the mod's code, or look at examples from upcoming audio packs to understand them.
These features will be documented properly at a later point in time.
Removed
- Removed unused and undocumented route options:
filter_by_sources
,filter_by_object
- These features can instead be implemented via the JS scripting engine
- Removed automatic replacement of clips based on audio files matching vanilla files
- If you have an audio pack that used this, you now have to explicitly do the replacement in the audio pack config
- Removed option to specify routes via JSON configuration in
modaudio.config.json
, since it was largely unused- This might be replaced by JS script configurations when full scripting support happens
Added
- IMPORTANT: ModAudio now has experimental support for scripting via JavaScript!
- Scripts are specified with a
__routes.js
file alongside__routes.txt
- Added the
target_group_script
parameter for routes, which specifies a script function that will select a group of target clips to play based on the conditions you specify - Added the
enable_dynamic_targeting
parameter for routes, which specifies that audio will be updated dynamically usingtarget_group_script
; this allows for dynamic map music or conditional routing
- Scripts are specified with a
- Added a route effect to force audio sources to loop even if the original tracks didn't loop
- usable using the
force_loop
effect:source = replacement ~ force_loop : true
- usable using the
- Added multiple previously unavailable route effects. Their effects are documented in
AudioPackConfig.cs
. The full list of effects is now as follows:link_overlay_and_replacement
- true/falserelative_replacement_effects
- true/falseoverlay_stops_if_source_stops
- true/falserelative_overlay_effects
- true/falseoverlays_ignore_restarts
- true/falsetarget_group_script
- stringenable_dynamic_targeting
- true/falsereplacement_weight
(alternativelyweight
) - numbervolume
- numberpitch
- numberforce_loop
- true/false
- Added a global option for audio packs to specify a script function to be called every frame via
%updatescript
- Example:
%updatescript pack_update
- This allows you to check game state and track various conditions, or call engine methods
- Example:
- Added a fifth parameter for replacement clips - this represents a group that can be selected by
target_group_script
- New syntax:
clipname : weight : volume : pitch : group
- This does not apply to overlays; they cannot receive groups
- New syntax:
- Comments can now be used anywhere, and will apply until the end of the line, and you can now also format your routes across multiple lines by using a terminating
\
character- Example:
# Combat music modaudio_map_crescentroad_action = <atlyss>_mu_wonton5 # Everything after hashtags is ignoerd # My cool route _mu_haven \ # This is a comment, we're using \ to separate the route across multiple lines = | ___default___ : 1.0 : 1.0 : 1.0 : nonarena \ # This is another comment | <atlyss>_mu_hell02 : 1.0 : 1.0 : 1.0 : arena \ ~ | target_group_script : target_group_sanctumarena \ | enable_dynamic_targeting : true \ | smooth_dynamic_targeting : true # This does not end with a \ character since it's the end of the route
- Example:
- Within lists in
__routes.txt
(marked by the|
character), you can now use a leading or trailing pipe character for formatting purposes without it causing issues - ModAudio now allows you to use vanilla clips as part of replacements and overlays. You can specify a vanilla clip by prepending it with
<atlyss>
- For example:
_mu_haven = <atlyss>_mu_wonton5
- For example:
- ModAudio now tries to add placeholder empty audio sources for maps that do not have them, which should allow defining custom music for them.
- The exact custom source names that you need to use in
__routes.txt
will be logged in the console - For example:
- day music:
modaudio_map_{clean map name}_day
- night music:
modaudio_map_{clean map name}_night
- action music:
modaudio_map_{clean map name}_action
- examples:
modaudio_map_tuulvalley_action
,modaudio_map_executionerstomb_action
,modaudio_map_crescentroad_action
- day music:
- The exact custom source names that you need to use in
- ModAudio now allows forcing action music to play as part of maps, via JS scripts
- Added chain routing functionality - by using
~ chain_route : true
, you can tell ModAudio that the replacement of your route should be rerouted again through other packs- This can be useful when you reroute audio to vanilla clips, and want those vanilla clips to actually play whichever custom clips the user has from other packs
- There is a limit of 4 max chained routes
Changed
- IMPORTANT: A new debug menu has been implemented, which can be opened by using the
DebugMenuButton
key configuration (see EasySettings or the config file)- All audio logging functionality has been moved to the debug menu
- As a side effect, this means that the BepInEx console will no longer be cluttered by ModAudio logs
- The debug menu also includes additional debug information about currently loaded audio packs
- Any audio packs that have encountered errors will be displayed in red - check the error / warning messages in the audio logs to understand what went wrong
- The distance filter has been removed temporarily - this will be reimplemented in a later update
- ModAudio now tries to implement / fix "null" audio sources, so that they play correctly
- You might notice that Sanctum Arena and Executioner's Tomb now play background music, unlike before
- Changed vanilla audio mixing logic for map instances so that the transition from action music to day / night music is smoother
Fixed
- Fixed hard boss music not playing / routing properly (notably Valdur in Crescent Grove)
- Fixed an issue with custom clip volumes not applying to in-memory audio files
[2.2.2] - 2025-May-22
Fixed
- Fixed replacement weight not working as expected with
source = replacement / weight
routes - Switched from UnityWebRequestMultimedia to NAudio for loading audio files (previously was only used for streaming)
- This should fix an issue where some MP3 files wouldn't load / play correctly, but audio might load slower as a result
- Fixed mod crashing if an exception is thrown while reading route files
- Fixed warning log spam when EasySettings version would differ from the expected version (should now warn at most once)
Changed
- Audio packs should now be sorted by display name in EasySettings
[2.2.1] - 2025-Mar-31
Fixed
- Fixed an error that would occur rarely when sounds are stopped
- (Hopefully) Fixed an error that would occur rarely when reloading audio
[2.2.0] - 2025-Mar-30
Added
- Added a button in EasySettings that opens the custom / user audio pack folder for easy access
Fixed
- Fixed some overlay sounds being interrupted mid-playthrough when played from components that have particle system components
- Technial details: The sound is played from a game object higher in the hierarchy as a workaround for the game object being disabled by the particle system
- Fixed routing failing occasionally when using
___nothing___
as a replacement - Fixed game object names being erroneously set to "oneshot" for display purposes
- It will now use the correct object name and instead append "oneshot" to logged lines for played audio
[2.1.0] - 2025-Mar-29
Added
- Added an effect option "overlays_ignore_restarts" for
__routes.txt
routes (originalClip @ overlayClip ~ overlays_ignore_restarts : true
)- Settings this as true will make it so that an overlay sound is played only once if an audio source is restarted multiple times.
Changed
- Slightly improved load times by skipping loading audio data for audio packs that are disabled
- Mod assembly now uses an embedded PDB instead of a separate PDB file for debugging
Fixed
- Fixed some playOnAwake sources being logged an extra time in the console
- Fixed pitch and volume being tracked and applied incorrectly for cast effect sounds
[2.0.1] - 2025-Mar-19
Changed
- LogAudioPlayed is now false by default
- The PDB is now supplied alongside the mod for debug purposes
Fixed
- Fixed crash caused by audio sources with no output audio mixer group
- Added a bit of error handling
[2.0.0] - 2025-Mar-16
Added
- Added volume and pitch controls for tracks
- Added "overlay" sounds that play alongside a track instead of replacing it completely
- Rewrote the __routes.txt pack format to support the changes from above
- Added a modaudio.config.json pack format that allows full control over pack loading
- Improved load performance by streaming audio for large clips (over 1MB) that use .ogg, .mp3 or .wav
- Added support for Nessie's EasySettings mod
- Added support for reloading audio packs when using EasySettings, allowing you to add, remove and edit packs while you're playing
- Added support for toggling audio packs on and off when using EasySettings
- Added options for toggling console logging on and off when using EasySettings
- Improved audio logging, and added a configuration setting to filter out audio that plays too far from the player (when the main player is available)
- Added options to filter logged audio based on its audio group
Changed
- Audio logging now includes way more information in a concise manner
Fixed
- Fixed post-boss defeat music not being replaceable
- Fixed functionality being broken for audio sources that utilized
playOnAwake
for playing
Removed
- OverrideCustomAudio option has been removed. It may or may not return in the future, but you can always manually edit the packs you download in case you need adjustments
[1.1.0] - 2025-Jan-01
Added
- You can now specify multiple audio files per source clip in __routes.txt. ModAudio will play one at random, based on their weights.
- Each clip in __routes.txt can now specify a weight using the format
source = replacement / weight
(where weight is a decimal number, i.e.1.0
). This is used for random rolls when there are multiple clips present. - You can now use the
___default___
identifier when replacing audio. This allows you to include the vanilla audio as a clip that should be played randomly. - Added an
OverrideCustomAudio
option that can be used to override any custom audio from mods with whatever you specify in ModAudio itself. __routes.txt
now supports line comments. Lines starting with#
will be treated as a comment and ignored.
[1.0.4] - 2024-Dec-28
Changed
- Improved audio loading from root folder, it will now also load audio from root if there's at least one clip with a known vanilla name
[1.0.3] - 2024-Dec-27
Changed
- The mod will now load assets from mods that don't have a DLL plugin. It will load any audio from the "audio" folder (i.e.
BepInEx/plugins/Your-Mod/audio
) if it exists. It will also load audio from the root folder (BepInEx/plugins/Your-Mod/
) if there is a__routes.txt
file present
Fixed
- Fixed an issue related to using the mod without any audio under
ModAudio/audio
[1.0.2] - 2024-Dec-23
Changed
- Removed NAudio dependency in favor of UnityWebRequestMultimedia
Fixed
- Audio played via
AudioSource.PlayOneShot
should now be replaced correctly
[1.0.1] - 2024-Dec-23
Added
- Option to enable verbose logging of audio sources that are being replaced, and audio sources that are loaded in
Changed
- Improve reliability of audio replacement when scenes are loaded
[1.0.0] - 2024-Dec-22
Changed
Initial mod release