| name | flutter_soloud-mixing-bus |
| version | 1 |
| description | Teaches how to use flutter_soloud mixing buses (Bus/Buses) to route groups of sounds through a shared sub-mix for collective volume control and shared filters. Use when the user asks for separate music/SFX/UI volume sliders, grouped effects, ducking music under voice, routing sounds between categories at runtime, or a per-category audio mixer in a game or app using flutter_soloud. |
Mixing buses (sub-mixes) in flutter_soloud
A Bus is a virtual mixer channel: sounds are routed through it, so one
volume change or one filter affects everything on the bus. The canonical use
is separate Music / SFX / UI buses behind user volume sliders. Unlike
audioplayers/just_audio (one player per sound, no sub-mix concept), routing is
explicit: you create a bus, play the bus itself on the engine, then play
sounds onto it.
Minimal example
import 'package:flutter_soloud/flutter_soloud.dart';
Future<void> main() async {
await SoLoud.instance.init();
// 1. Create the bus (synchronous, cheap).
final sfxBus = SoLoud.instance.createMixingBus(name: 'SFX');
// 2. REQUIRED: play the bus on the engine. Until this call, the bus
// (and everything routed through it) is silent.
sfxBus.playOnEngine();
// 3. Route sounds through the bus.
final explosion = await SoLoud.instance.loadAsset('assets/explosion.mp3');
sfxBus.play(explosion); // synchronous, returns SoundHandle
// 4. Collective volume: set it on the bus's own handle, not per-sound.
SoLoud.instance.setVolume(sfxBus.soundHandle!, 0.5);
// 5. Shared filter on everything routed through the bus.
sfxBus.filters.echoFilter.activate();
// Cleanup: stops all sounds playing through the bus.
sfxBus.dispose();
}
The API shape
SoLoud.instance.createMixingBus({String name = ''}) → Bus. Synchronous;
registers the bus in the Buses() registry. First bus gets busId == 1;
busId == 0 always means "the main engine output".
Buses() singleton registry: Buses().buses (list of all live buses),
Buses().byName('SFX'), Buses().byId(1). Both lookups throw if not found
unless you pass orElse.
bus.playOnEngine({double volume = 1.0, bool paused = false}) →
SoundHandle. Makes the bus audible; stores the handle in
bus.soundHandle. Calling it again on the same bus stops the earlier
instance (only one bus voice at a time).
- Playing onto a bus — two equivalent ways:
bus.play(sound, {volume, pan, paused, looping, ...}) and its siblings
bus.playClocked, bus.playScheduled, bus.play3d, bus.play3dClocked,
bus.play3dScheduled — thin wrappers that forward to the SoLoud method
of the same name with busId set.
SoLoud.instance.play(sound, busId: sfxBus.busId) — every SoLoud
play method takes an int busId = 0 parameter.
bus.annexSound(handle) — re-parent a live voice (by its SoundHandle)
into this bus, e.g. move a sound already playing on the engine onto a
filtered bus mid-playback.
- Bus volume: there is no
bus.volume property. Use
SoLoud.instance.setVolume(bus.soundHandle!, v) /
SoLoud.instance.getVolume(bus.soundHandle!). This scales every voice
routed through the bus on top of each voice's own volume.
bus.filters — a FiltersSingle scoped to this bus; filters activated
here apply to all routed sounds only (engine-wide filters live on
SoLoud.instance.filters). See skill flutter_soloud-filters.
bus.setChannels({Channels channels = Channels.stereo}) — bus output
channel count (Channels.mono/stereo/quad/surround51/dolby71).
bus.getChannelVolume(channel) — approximate per-channel output level for
VU meters. Requires first;
returns 0 for out-of-range channels.
Everything above (except createMixingBus lookups and dispose) is
synchronous and returns SoundHandle directly — do not await them.
Traps
- Silent bus: forgetting
playOnEngine() is the #1 failure. Sounds
"play" (handles are created, voices count up) but nothing is heard.
bus.soundHandle stays null until then, so guard with
if (bus.soundHandle != null) before volume/filter-parameter calls.
- Do not invent
bus.volume / bus.setVolume(): they don't exist.
Volume goes through SoLoud.instance.setVolume(bus.soundHandle!, v).
Models coming from audioplayers habitually look for a per-player volume
setter — route them to the bus handle instead.
- No
await on bus play methods: bus.play(...) returns SoundHandle,
not a Future. SoLoud.instance.loadAsset is the async part; playback is
synchronous.
- Disposed bus throws: every
Bus method throws
SoLoudBusDisposedDartException after dispose(). Double-dispose also
throws — null out your reference after disposing.
- Create buses after
await SoLoud.instance.init(): the constructor calls
into the native engine, and playOnEngine() throws
SoLoudBackendNotInitedException when the engine isn't up.
byName/byId throw when missing: they use firstWhere; pass
orElse or check Buses().buses first if the bus may not exist.
- Voice limit: bus voices count against the engine's active-voice cap
(default 16, raise with
SoLoud.instance.setMaxActiveVoiceCount). Hitting
it is not an error — the new sound just doesn't play.
annexSound needs a live handle: annexing a finished/invalid handle is
a no-op on the native side; check sound.handles / isActive first.
- Filters need the bus handle for per-instance parameters: e.g.
bus.filters.pitchShiftFilter.shift(soundHandle: bus.soundHandle).value = 2.5;
after activate(). Plain .shift.value targets the global filter, not the
bus voice.
More depth
references/api.md — full signatures of all six bus play variants, the
Buses registry, exceptions, and the AudioManager (music/SFX/voice +
ducking) pattern.
- Runnable demo:
example/lib/mixing_bus/mixing_bus.dart in this repo —
creates buses, plays/annexes sounds, per-bus volume slider, pitch-shift
filter toggle, live voice counts.
- Upstream concepts: https://solhsa.com/soloud/mixbus.html
Keeping this skill current
This skill ships inside the flutter_soloud package, so upgrading flutter_soloud can carry a newer revision of it than the copy installed in the project. To check, run:
dart run flutter_soloud:skills --check
It reports the installed and bundled skill versions and exits non-zero when an update is available. Offer to update with dart run flutter_soloud:skills (which touches only the skills, never pubspec or build files).