reckfellENGINE

Buses: music, effects and volume settings

Working category sliders, linear gain and dB, mute, routing and saving a layout.

On this page

Split categories

Create Music, SFX and UI, each sending to Master. Assign AudioStreamPlayer.Bus by category instead of traversing the tree to adjust every player. Buses belong to the whole project. An index changes after insertion, removal or reordering; retain the name and call AudioServer.GetBusIndex(name) again before editing. A missing name returns -1; do not pass it to adjustment methods.

A player with a nonexistent bus name uses Master, which can hide a typo. Check names during project setup. A receiving bus must be left of its sender. Sending rightward or to a missing destination effectively falls back to Master; it does not create a feedback loop.

Linear gain and dB

Linear amplitudeLevel change
10 dB: unchanged gain
0.5approximately -6.02 dB
0.1-20 dB
0Silence: use mute instead of calculating log zero

For an amplitude ratio, dB = 20 × log10(gain). This is neither the power formula nor a perceptual loudness scale: half the amplitude does not guarantee sounding half as loud. A fader's 0 dB means unity gain; a digital peak's 0 dBFS means full scale. These are different quantities. The mixer can sum values above full scale; avoid overloading final output. Headroom and a limiter do not replace reasonable source balance.

Run a complete menu

Keep DemoTone.cs from the player example in the project. Add AudioMix.cs and MixerDemo.cs below. Create a level with a Control root, attach MixerDemo, build and run with F6. The tone plays through Music; the button tests UI. The Music slider does not change UI and the UI slider does not change Music. The SFX slider is ready for your effects; this example has no sound connected to it.

AudioMix.cs
C#
using System;
using Reckfell;

public static class AudioMix
{
    public static int EnsureBus(string name)
    {
        if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("A bus needs a name.", nameof(name));
        int existing = AudioServer.GetBusIndex(name);
        if (existing >= 0) return existing;
        AudioServer.AddBus();
        int index = AudioServer.BusCount - 1;
        AudioServer.SetBusName(index, name);
        AudioServer.SetBusSend(index, "Master");
        return index;
    }

    public static bool SetGain(string name, float linear)
    {
        if (!float.IsFinite(linear)) return false;
        int index = AudioServer.GetBusIndex(name);
        if (index < 0) return false;
        linear = Mathf.Clamp(linear, 0, 1);
        AudioServer.SetBusMute(index, linear == 0);
        if (linear > 0) AudioServer.SetBusVolumeDb(index, Mathf.LinearToDb(linear));
        return true;
    }

    public static float GetGain(string name)
    {
        int index = AudioServer.GetBusIndex(name);
        return index < 0 || AudioServer.IsBusMute(index) ? 0 : AudioServer.GetBusVolumeLinear(index);
    }
}
MixerDemo.cs
C#
using System;
using Reckfell;

public partial class MixerDemo : Control
{
    public AudioStreamPlayer Music { get; private set; } = null!;
    public AudioStreamPlayer Click { get; private set; } = null!;
    private bool _ru;

    public override void _Ready()
    {
        _ru = TranslationServer.GetLocale().StartsWith("ru", StringComparison.OrdinalIgnoreCase);
        foreach (string name in new[] { "Music", "SFX", "UI" }) AudioMix.EnsureBus(name);
        Music = new AudioStreamPlayer { Name = "Music", Bus = "Music", Stream = DemoTone.Create(220, loop: true), PlaybackType = AudioServer.PlaybackType.Stream };
        Click = new AudioStreamPlayer { Name = "Click", Bus = "UI", Stream = DemoTone.Create(880, .08), PlaybackType = AudioServer.PlaybackType.Stream };
        AddChild(Music); AddChild(Click);
        var layout = new VBoxContainer { Position = new Vector2(24, 24), CustomMinimumSize = new Vector2(280, 0) }; AddChild(layout);
        AddSlider(layout, "Music", _ru ? "Музыка" : "Music");
        AddSlider(layout, "SFX", _ru ? "Эффекты" : "Effects");
        AddSlider(layout, "UI", _ru ? "Интерфейс" : "Interface");
        var click = new Button { Text = _ru ? "Проверить UI" : "Test UI" }; layout.AddChild(click);
        click.Pressed += () => Click.Play(); Music.Play();
    }

    private static void AddSlider(VBoxContainer layout, string bus, string text)
    {
        layout.AddChild(new Label { Text = text });
        var slider = new HSlider { MinValue = 0, MaxValue = 1, Step = .01, Value = AudioMix.GetGain(bus) };
        layout.AddChild(slider); slider.ValueChanged += value => AudioMix.SetGain(bus, (float)value);
    }
}

EnsureBus creates only missing buses; it does not reset an existing bus's effects, routing or volume. SetGain rejects NaN/Infinity, checks the name and clamps its slider to 0…1. Zero mutes; a nonzero value unmutes and sets gain. This is the menu's policy, not an engine API limit: the mixer itself permits amplification above one. If a separate mute button exists, keep its state separately so moving the slider does not accidentally unmute the user's choice.

Mute does not stop music: playback time continues and unmuting reveals the current position. Use the player's StreamPaused for an actual pause. Solo isolates buses for diagnosis; it is not category volume control.

Save the mixer layout and player preferences

In the bottom Audio panel, create buses, set Send and add effects with Add Effect. Use Save Audio Bus Layout As... for a new layout. The default project layout is asset://default_bus_layout.rdata; audio/buses/default_bus_layout selects the layout. Runtime AudioServer changes affect current process memory and do not automatically save the project layout.

Store player preferences by stable category names and slider values in user:// with ConfigFile. Load them after preparing buses and validate numeric type and finiteness. Do not store bus indices or overwrite project resources with player preferences. Explicitly migrate old keys when renaming a category.

Diagnose a routing problem

SymptomCheck
A slider controls the wrong categoryA stale index after layout changes; look up by name
No change is audiblePlayer Bus, name typo, receiving bus and Master
Music returns at a different positionMute preserves advancing time; this is expected
Capture contains audio while the same bus is mutedEffects run before that bus's fader/mute; capture on a receiving bus to include a child's gain
Settings vanish after restartRuntime changes are not saved automatically
Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.