Source file unity-csharp/audio/MusicStemLayers.cs from the GDnD code examples. Download raw file
// GDnD wiki example (2 of 2: AdaptiveMusicPlayer.cs, MusicStemLayers.cs)
// Demonstrates: vertical layering (vertical remixing) in plain Unity, with no FMOD or Wwise.
// Every stem of one piece (pad, percussion, bass, brass...) is exported at the same length
// and tempo, given its own looping AudioSource, and started on the SAME dspTime with
// AudioSource.PlayScheduled so the stems stay locked together. Intensity then just fades
// stems in and out; nothing is ever stopped or restarted, so the layers never drift.
// SetIntensity(0) = only stem 0 audible, SetIntensity(2) = stems 0, 1 and 2, and so on.
// Related pages: [[unity-adaptive-music-crossfade]], [[interactive-music-techniques]],
// [[unity-audiosource]]
// Unity 6 (6000.x). Built-in audio only (UnityEngine.AudioModule).
using UnityEngine;
namespace GDnD.Audio
{
public class MusicStemLayers : MonoBehaviour
{
[Tooltip("One AudioSource per stem, quietest/most basic first. Loop on, Play On Awake off.")]
[SerializeField] private AudioSource[] stems;
[SerializeField, Range(0f, 1f)] private float stemVolume = 0.8f;
[Tooltip("Seconds for a stem to fade fully in or out.")]
[SerializeField] private float fadeSeconds = 1.5f;
[SerializeField] private int startIntensity = 0;
private float[] targets;
private void Start()
{
targets = new float[stems.Length];
double startDsp = AudioSettings.dspTime + 0.2; // shared start time for every stem
foreach (AudioSource stem in stems)
{
stem.loop = true;
stem.volume = 0f;
stem.PlayScheduled(startDsp);
}
SetIntensity(startIntensity);
for (int i = 0; i < stems.Length; i++) stems[i].volume = targets[i]; // no fade at start
}
/// <summary>Call from game code, e.g. when enemies notice the player.</summary>
public void SetIntensity(int level)
{
for (int i = 0; i < stems.Length; i++)
targets[i] = i <= level ? stemVolume : 0f;
}
private void Update()
{
float step = stemVolume / Mathf.Max(fadeSeconds, 0.01f) * Time.unscaledDeltaTime;
for (int i = 0; i < stems.Length; i++)
stems[i].volume = Mathf.MoveTowards(stems[i].volume, targets[i], step);
}
}
}