Here's a video with an example of this:
This guide will describe the process of creating something like this, using this music as an example.
Some minor notes:
- This will only cover stereo music and not mono audio.
- This will only cover OGG files, although step 4 can apply to any music format that has multiple tracks (including SPC, MIDI, etc.)
- Audacity will be assumed to be used. Or, more specifically, Tenacity, but the steps should be the same in both.
1. Setting Up an Audacity Project
You must create an Audacity project where each track of your music is one separate stereo layer, as such:

You should also be sure to save it as a proper Audacity project file, to make it more convenient to change later if necessary.
2. Exporting
By default, Audacity will crush all the tracks into one when exporting as an OGG file. There is a setting to change this, called "advanced mixing options":


When exporting, you'll get the following additional prompt:

In a simple case like this, where one track in Audacity corresponds to one track in the export, it can be left as-is. However, if that is not the case, you will have to manually pair each L/R pair on the left to a pair of channels on the right.
Afterward, it can be exported as normal (including looping).
3. Setting Metadata
If you play it in-game, currently, it will play all the tracks together with no way of controlling them individually. To fix this, you need to add metadata to the music field in-editor.
You may be familiar with adding a "gain" option to an SPC file by adding extra settings after the path. This works similarly.
For my three-track example, I used this:
Code: Select all
Rainy Day.ogg|m1;c2;r3
"c2" is the number of channels per track (so 2 for stereo audio, 1 for mono);
"r3" is the number of tracks (so 3 in this case).
In practice, you'll probably only want to change r.
OGGs support up to 32 channels, so you can go wild (although maybe keep file size in mind).
4. Making it Dynamic
Now, we can use Lua code to actually make it dynamic. This is done through this pair of functions:
Code: Select all
Audio.MusicInstChannelMute(trackIndex)
Audio.MusicInstChannelUnmute(trackIndex)
In my example, the tracks are 0 (the base layer), 1 (the "working" layer) and 2 (the "treasure" layer).
You can call these from pretty much wherever you want in your Lua code.
By default, all tracks will be unmuted.
To give a useful example, here is code (for a luna.lua file) that mutes track 1 when the level starts, but unmutes it when a certain event is triggered:
Code: Select all
function onStart()
Audio.MusicInstChannelMute(1)
end
function onEvent(eventName)
if eventName == "example event" then
Audio.MusicInstChannelUnmute(1)
end
end