Modding sound
Tutorials
- Forum post: Modding Sound & Music
- Audio Modding Guide for Bannerlord
- Oficial guide - Adding Custom Sounds
- Lesser Scholar Youtube Tutorial Ep 12: Sound Effects
- Bannerlord Music Modding Tutorial : Pt. 1 by SixxTailsHD
Resources
Various
Folder structure
In ...\Modules\*YOUR_MOD*\
\ModuleData\module_sounds.xml - custom definitions for our own sounds
\ModuleData\project.mbproj - tells engine to include our module_sounds.xml
\ModuleSounds - our audio files (.ogg, .wav)
File format
module_sound.xml
<?xml version="1.0" encoding="utf-8"?>
<base xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" type="module_sound">
<module_sounds>
<module_sound name="YOUR_SOUND_ID" is_2d="true" sound_category="ui" path="SOUND_FILE_NAME.WAV|OGG" />
</module_sounds>
</base>
Pitch
Can set pitch for the sound like this: min_pitch_multiplier="0.9" max_pitch_multiplier="1.1"
If values are different - random value between min-max is selected. If you need constant pitch - make both values equal.
project.mbproj
<?xml version="1.0" encoding="utf-8"?>
<base xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" type="solution">
<file id="soln_module_sound" name="ModuleData/module_sounds.xml" type="module_sound" />
</base>
Sound Categories
From: \Native\ModuleData\module_sounds.xml
Category | Duration | Comment |
---|---|---|
mission_ambient_bed | persistent | General scene ambient |
mission_ambient_3d_big | persistent | Loud ambient sounds e.g. barn fire |
mission_ambient_3d_medium | persistent | Common ambient sounds e.g. fireplace |
mission_ambient_3d_small | persistent | Quiet ambient sounds e.g. torch |
mission_material_impact | max 4 sec. | Weapon clash kind of sounds |
mission_combat_trivial | max 2 sec. | Damage extra foley like armor layer |
mission_combat | max 8 sec. | Damage or bow release sounds |
mission_foley | max 4 sec. | Other kind of motion fillers |
mission_voice_shout | max 8 sec. | War cries, shouted orders, horse whinnies etc. |
mission_voice | max 4 sec. | Grunts, exertions etc. |
mission_voice_trivial | max 4 sec. | Small exertions like jumping |
mission_siege_loud | max 8 sec. | Big explosions, destructions |
mission_footstep | max 1 sec. | Human footsteps, foley |
mission_footstep_run | max 1 sec. | Human running (contributes to BASS) |
mission_horse_gallop | max 2 sec. | Loud mount gallop footsteps (contributes to BASS) |
mission_horse_walk | max 1 sec. | Quiet mount footsteps, walk |
ui | max 4 sec. | All UI sounds |
alert | max 10 sec. | Pseudo-3D in-mission alerts like warning bells |
campaign_node | persistent | Campaign map point sound like rivers etc. |
campaign_bed | persistent | Campaign amboent bed |
- Sounds that dont have valid categories wont be played!
How to play a sound
Read sound index
Several ways to play a sound
Worked ok:
Did not work:
Works:
SoundEvent eventRef = SoundEvent.CreateEvent(soundIndex, mission.Scene);
eventRef.SetPosition(_mission.MainAgent.Position);
eventRef.Play();
is2D option
Setting is2D to true makes it play at a constant volume throughout the entire scene.
is_2d="true" in XML
Sound in menus
args.MenuContext.SetPanelSound("event:/ui/panels/settlement_village");
args.MenuContext.SetAmbientSound("event:/map/ambient/node/settlements/2d/village");
Sound Events
Possible to play sound (events) with InformationManager.AddQuickInformation(text, 0, null, string SoundEventPath)
You need to null the SoundEvent when you leave the mission.
Same with MultiSelectionInquiryData and in the menus
In GauntletUI: HandleQueryCreated played with SoundEvent.PlaySound2D(soundEventPath);
soundEventPath looks like:
"event:/ui/notification/levelup"
Defined in [GAME_FOLDER]\Sounds\GUIDs.txt
soundEventPath examples
event:/ui/notification/army_created
event:/ui/notification/army_dispersion
event:/ui/notification/child_born
event:/ui/notification/coins_negative
event:/ui/notification/coins_positive
event:/ui/notification/death
event:/ui/notification/hideout_found
event:/ui/notification/kingdom_decision
event:/ui/notification/levelup
event:/ui/notification/marriage
event:/ui/notification/peace
event:/ui/notification/quest_fail
event:/ui/notification/quest_finished
event:/ui/notification/quest_start
event:/ui/notification/quest_update
event:/ui/notification/relation
Some info about events
- the event:/ derives from the FMOD event paths that the game uses to play sound events
- however when we mod the game’s audio we don’t have access to the FMOD project and their events, so we have to replace entire events with singular oneshots
- the event:/ is part of an event path
- for example, “event:/Ambience/Rain” would be an event called Rain located in a folder called Ambience
Sound examples
ui/mission sounds
ui/multiplayer sounds
ui/notification sounds
alerts sounds
Issues with sound volume using SoundEvent
Windwhistle: My findings so far:
When using SoundEvent to play a custom sound in module_sounds.xml from the sound category "mission_ambient_3d_small" and "mission_ambient_3d_medium", if the attribute is_2d is set to false, the volume of the sound at max volume (close to the sound source) is directly related to the distance at which it was spawned to the player.
So, if you set the position of the SoundEvent FARTHER from the player, it will be FAINTER at MAX volume. If you set the position of the SoundEvent CLOSER to the player, it will be LOUDER at MAX volume.
It draws confusion, as I do not mean that the sound becomes fainter as you travel farther away from it. That's normal. I am not talking about that. There's some videos if you scroll up that depict what I am talking about, because it's pretty hard to describe.
To fix this: I just set is_2d to true instead of false, and the sound plays how I wanted it to play.
Why: I don't know. I'm going to test it out in a regular scene/mission to see if it's my code somehow, or if it's a genuine bug. It might not even be a bug but intentional, but it'd be weird if it were.
EDIT: it also suffers from the same problem in a regular scene. Setting the position repeatedly on tick also does not alleviate the issue. Making the file format .wav also did not help.
Delayed stop to prevent ambient sounds from looping
private async void DelayedStop(SoundEvent eventRef)
{
await Task.Delay(soundDuration);
eventRef.Stop();
}
How to get sound duration?
Stop looping example
Short sounds for agents in Mission
agent.MakeVoice(SkinVoiceManager.VoiceType.Grunt, SkinVoiceManager.CombatVoiceNetworkPredictionType.NoPrediction);
SkinVoiceManager.VoiceType examples
- Grunt - eeh
- Jump - yah
- Yell - eyah, hey, yeee
- Pain - uh, mhmph
- Death - uuuuhmmm
- Stun - yyy, aaaa, eeee
- Fear - yaaaaah, aaaah
- Climb - ?
- Focus - (silence?)
- Debacle - aaaaaaah, shriek/screech
- and many others...
Custom Music
Create your own soundtrack.xml in YOURMOD/music/soundtrack.xml
using System;
using HarmonyLib;
using psai.net;
using TaleWorlds.Engine;
using TaleWorlds.MountAndBlade;
using TaleWorlds.ModuleManager;
namespace Patches
{
public static class YOURMODModulePath
{
public static string YOURMODROOTPath
{
get
{
return ModuleHelper.GetModuleFullPath("YOURMOD");
}
}
}
[HarmonyPatch]
public static class MBMusicManagerPatches
{
[HarmonyPrefix]
[HarmonyPatch(typeof(MBMusicManager), "Initialize")]
public static void OverrideCreation()
{
if (!NativeConfig.DisableSound)
{
string corePath = YOURMODModulePath.YOURMODROOTPath + "music/soundtrack.xml";
PsaiCore.Instance.LoadSoundtrackFromProjectFile(corePath);
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(MBMusicManager), "ActivateMenuMode")]
public static bool UseTowMenuMusicId(ref MBMusicManager __instance)
{
Random rnd = new Random();
int index = rnd.Next(401, 401);
typeof(MBMusicManager).GetProperty("CurrentMode").SetValue(__instance, MusicMode.Menu);
PsaiCore.Instance.MenuModeEnter(index, 0.5f);
return false;
}
}
}
Custom Music in Taverns
By culture:
Sandbox\ModuleData\settlement_tracks.xml
Custom Music in Main Menu
Source here
- Find the music
- Convert to OGG
- In \Mount & Blade II Bannerlord\music\Soundtrack.XML find Maintheme and under it: <TotalLengthInSamples>
-
Change the number to your sample length using:
or multiply 44305 X {Your song length in seconds}
-
Backup \Mount & Blade II Bannerlord\music\PC\Maintheme.ogg
- Rename your music ogg file to Maintheme.ogg and place it in \Mount & Blade II Bannerlord\music\PC\
Game Sound Settings
Get/set Sound/Music Volume:
NativeOptions.GetConfig(NativeOptions.NativeOptionsType.SoundVolume)
NativeOptions.GetConfig(NativeOptions.NativeOptionsType.MusicVolume)
NativeOptions.SetConfig(NativeOptions.NativeOptionsType.SoundVolume, _soundVolume);
NativeOptions.SetConfig(NativeOptions.NativeOptionsType.MusicVolume, _musicVolume);
Manage Game Music
Stop the music with fade-out:
Continue playing the music:
NAudio
Install NAudio
RMB on your project:
Quote
Artem: For everyone that tries to add sounds to the game and is annoyed by the games restrictions, like sounds being too short, too quiet etc.
https://github.com/naudio/NAudio
using NAudio.Wave;
private AudioFileReader audioFile;
public static async void PlayCustomSound()
{
if (this.outputDevice == null) this.outputDevice = new WaveOutEvent();
try
{
//string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string fullName = Directory.GetParent(Directory.GetParent(Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName).FullName).FullName;
this.audioLocation = System.IO.Path.Combine(fullName, "ModuleSounds\\");
this.audioFile = new AudioFileReader(this.audioLocation + "encounter_river_pirates_9.wav");
this.audioFile.Volume = NativeOptions.GetConfig(NativeOptions.NativeOptionsType.SoundVolume);
this.outputDevice.Init(this.audioFile);
PsaiCore.Instance.StopMusic(true, 1.5f); // stop native Music with fade-out
this.audioFile.Seek(0L, SeekOrigin.Begin);
this.outputDevice.Play();
// restore native Music
await Task.Delay(4500); // change 4500 to the length of your sound file in ms
PsaiCore.Instance.ReturnToLastBasicMood(true);
}
catch {
// "ERROR: can't play custom sound. Missing folders/files? "
}
}
// stop the sound
if (outputDevice != null)
{
outputDevice.Stop();
outputDevice.Dispose();
}
Implementation example: Artem's Assassination Mod
hunharibo: I expanded on the NAudio integration idea a lot in TOR. Made a MixingSampleProvider so you can actually play multiple sound sources at the same time if you wanted to. It only takes 48khz stereo vorbis files though. LINK. Also has rudimentary 3d spatial sound with stereo panning and distance attenuation