Best way to init audio engine v2 without interrupting code flow

Hi everyone!

I have a small problem with audio engine initialization in my game. My frontend audio manager initialization is currently like this (audio manager is my custom wrapper)

    // Custom audio manager init
    this.audioManager = new AudioManager({
      masterVolume: 3.0,
      duckingFactor: 0.5,
      audioEngine: await CreateAudioEngineAsync(),
      scene: this.scene,
      assetPath: assetPath,
    })
    
    // This next call just has 
    // this.audioEngine.listener.attach(this.scene.activeCamera)
    // await this.audioEngine.unlockAsync()
    await this.audioManager.boot()

However, the await calls seem to block any further code execution until the first gesture is provided, and this causes some problems, like rendering of stuff not starting before the gesture.

What I would like to do is this:

  • Just init audio engine v2 without waiting
  • Eventually there will be user gesture => start playing sounds/music after that
  • Not big deal if there are no sounds/music before the gesture

Any advices on what would be the correct way to achieve the above? Ruthlessly pinging @docEdub again :smiling_face_with_horns:

1 Like

cc @docEdub

You’ll probably still want to await the call to CreateAudioEngineAsync(), which resolves without a user gesture, but you are correct that the call to audioEngine.unlockAsync() will not resolve until a user gesture occurs. The way it’s handled in the examples is to call the audio init code from an async function that is not awaited, so it doesn’t block the renderer:

var createScene = async function () {
    var scene = new BABYLON.Scene(engine);

    // Init scene here...

    // Init audio with async function that is not awaited so we don't block.
    (async () => {
        const audioEngine = await BABYLON.CreateAudioEngineAsync();
        const gunshot = await BABYLON.CreateSoundAsync("gunshot", "https://playground.babylonjs.com/sounds/gunshot.wav");

        // Wait for the audio engine to unlock
        await audioEngine.unlockAsync();

        gunshot.play();
    })();

    return scene;
}

See https://playground.babylonjs.com/#1BZK59#10 for the full example.

So the idea here is to init all the sounds and buses before awaiting audioEngine.unlockAsync, and then call play on them after unlockAsync resolves from a user gesture and unblocks. You can init the sounds after unlockAsync resolves, but there will be more lag time between unlockAsync and play if you do that, and it’s not necessary. The sound can be created just fine before a user gesture, it just can’t be played until after a user gesture.

Let me know if that’s not clear :slight_smile:

1 Like

Sorry for slow reply, doing so much stuff for my game simultaneously…

I pretty much did what you instructed and it seems to work! There is a slight weird thing though; I think the CreateAudioEngineAsync does something with the dom, since I see some “wobblyness” in the UI for a split second. My whole app/game is pretty much in the position absolute/fixed, and there seems to be something happening in the bottom of that div. Do you create some custom elements or something that might effect the dom?

Edit:
Here is an example of the flash, this is bottom of the browser screen, happened after page refresh:
teeeeeeee

It could be the sound lock/unlock icon that is displayed and this mask.

You can try initializing in the audio engine with : disableDefaultUI: true

this.audioEngine = await BABYLON.CreateAudioEngineAsync({ listenerEnabled: true, disableDefaultUI: true});

That"s the idea I have, but maybe it"s something else.

3 Likes

Woah I think the disableDefaultUI fixed the problem now! Thank you! :star_struck:

2 Likes