flutter3d
Showcase Changelog 38 packages API reference

Voice limiting

since 0.1.0 Simulation, audio and XR

A device only mixes so many sounds well before a scene stops having a foreground. AudioScene(maxVoices:) caps how many voices exist at once, and priority decides which ones survive when there are more candidates than slots.

Step 1: A scene with three voices #

SoundDef.priority breaks the tie a device's own limit cannot: a shout outranks a footstep even when the footstep is closer.

// A scene with room for three voices, asked for five.
final scene = AudioScene(backend: SilentBackend(), maxVoices: 3);
const footstep = SoundDef(name: 'footstep', asset: 'step.wav', priority: 0);
const shout = SoundDef(name: 'shout', asset: 'shout.wav', priority: 10);

Step 2: Ask for five #

Four footsteps and one shout, in a scene that only has room for three.

for (var i = 0; i < 4; i++) {
  scene.play(footstep, Vector3(i.toDouble(), 0.0, 0.0));
}
final loudOne = scene.play(shout, Vector3(0.0, 0.0, 3.0));
const requested = 5;
scene.update(AudioListener());

Step 3: See who kept a voice #

After update, only the highest-priority, loudest sounds still hold a voice. A one-shot that lost the vote is not merely quiet, it is removed outright: there is nothing left for it to finish.

// A one-shot that lost the vote for a voice is dropped on the same
// update that decided it, which is why what is left over answers both
// questions at once.
final gotVoice = scene.voiceCount;
final shoutWon = loudOne.audibleGain > 0.0;

The shout, ranked far above every footstep, is one of the three that made it through.

Step 4: Crowd the scene #

The blue block is the listener; the coloured balls are the sounds asking to be heard, footsteps at growing distances round it and one shout across the room. A lit ball got a voice and a dark one lost the vote. The shout wins over every footstep because it is the higher priority, and among the footsteps the nearer ones win, however many ask. Raise or lower Voices allowed and watch the lit ones change; the purple bar is how many voices are in use.

// Ask again with whatever the sliders say: a scene with room for `voices`
// voices, a crowd of footsteps at growing distances, and a shout.
final AudioScene scene = AudioScene(
  backend: SilentBackend(),
  maxVoices: voices.round(),
);
const SoundDef footstep = SoundDef(
  name: 'footstep',
  asset: 'step.wav',
  priority: 0,
);
const SoundDef shout = SoundDef(
  name: 'shout',
  asset: 'shout.wav',
  priority: 10,
);
final List<SoundEmitter> steps = <SoundEmitter>[
  for (var i = 0; i < asking.round(); i++)
    scene.play(
      footstep,
      Vector3(
        (1.5 + 0.35 * i) * math.cos(i * 2.4),
        0.0,
        (1.5 + 0.35 * i) * math.sin(i * 2.4),
      ),
    ),
];
final SoundEmitter? loud = shouting
    ? scene.play(shout, Vector3(0.0, 0.0, -3.0))
    : null;
scene.update(AudioListener());