Babylon.js の「Playing Sounds and Music」のサンプルを 2つほど見てみる

以下の Babylon.js 8.0 の記事からもリンクが貼られていた、音関連の話題です。

●Introducing Babylon.js 8.0. Our mission is to build one of the most… | by Babylon.js | Mar, 2025 | Medium
　https://babylonjs.medium.com/introducing-babylon-js-8-0-77644b31e2f9

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/8f6d91e5-f378-4899-b9fd-ec2eb96098c0.png)

上記の記事内の説明を見ると「オーディオエンジンが刷新された」とのことで、既存のオーディオエンジンが大幅にアップグレードされたようです。具体的には、Web Audio API の機能をより活用できるように進化していたり、利用者が扱いやすいものになっているようです。

今回、公式が提供しているオーディオエンジン関連のサンプルを、少し見ていきます。


## 公式情報を見ていく

上記の記事で、以下の記載がありました。

Check out a demo: https://aka.ms/babylon8AudioEnginev2Demo
Learn more here: https://aka.ms/babylon8AudioEnginev2Doc

この 2つのうち、2つ目のほう（公式ドキュメント）を見てみます。

### 公式ドキュメント

上記のリンク先となる、公式ドキュメントのページは以下になります。

●Playing Sounds and Music | Babylon.js Documentation
　https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/45e520b9-6779-4a11-82f6-672b1f5407f1.png)


目次を見ると、以下のような内容が並んでいます。

- Creating an audio engine
- Playing a sound
- Streaming a sound
- Sound instances
- Looping playback
- Volume
- Stereo pan
- Spatial audio
   - Attaching meshes
- Audio buses
   - Main audio buses
   - Intermediate audio buses
- Analyzer
- Sound buffers
- Using browser-specific audio codecs
- Browser autoplay considerations
   - Playing sounds after the audio engine is unlocked
   - Unmute button
- Feature requests and bug fixes


### 公式サンプル 1つ目を見てみる

今回、これらを見ていく中で、最初に出てくる以下のサンプルなどを見てみます。

●AudioEngineV2 | Babylon.js Playground
　https://playground.babylonjs.com/#1BZK59#10

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/bc8ac067-f044-449e-85eb-c04028b75989.png)

#### コード

サンプル 1つ目のコードは、以下の通りです。

```javascript
var createScene = async function () {
    // Create a basic scene
    var scene = new BABYLON.Scene(engine);
    scene.clearColor = BABYLON.Color3.BlackReadOnly;
    scene.createDefaultEnvironment({
        createGround: false,
        createSkybox: false,
        createDefaultCameraOrLight: false,
    });
    scene.createDefaultCameraOrLight(true, true, false);
    scene.activeCamera.alpha = Math.PI / 2;
    scene.activeCamera.radius = 0.05;

    BABYLON.AppendSceneAsync("https://playground.babylonjs.com/scenes/BoomBox.glb", scene);

    // Load the sound and play it when the audio engine is unlocked.
    (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;
};
```

#### 動作確認

動作としては「モデルなどのロードが終わった後、画面左上に出ているアイコン（以下の赤矢印で示した部分）を押すと、音が再生される」というシンプルなものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/da64883d-f3ca-45cb-970e-7fc2afc0ab77.png)

#### 音を扱うコード

音を扱う部分は、上記のコードの中の以下になると思われます。

```javascript
    // Load the sound and play it when the audio engine is unlocked.
    (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();
    })();
```

[公式ドキュメントの説明](https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic/#creating-an-audio-engine)を見ると、「非同期関数（async functionを作成してすぐに実行しているものの、await を使ってオーディオエンジンの "ロック解除" を待つ」という処理になっているようです。

これは「ブラウザの制約により、ユーザーが何か操作をするまで、音の再生がブロックされる」という仕様に対応したもののようです。

今回で言うと、「画面上のアイコンをクリック」＝「音の再生が可能になるトリガー（ユーザーの操作）」です。


### 公式サンプル 2つ目

次は、以下の公式サンプル 2つ目です。

●AudioEngineV2 | Babylon.js Playground
　https://playground.babylonjs.com/#1BZK59#11

表示される内容、音を鳴らすトリガーは 1つ目のサンプルと同じです。

画面上のアイコンを押すと、0 から数字をカウントアップしていく音声が流れます。


#### コード

コードは、以下のとおりです。

```javascript
var createScene = async function () {
    // Create a basic scene
    var scene = new BABYLON.Scene(engine);
    scene.clearColor = BABYLON.Color3.BlackReadOnly;
    scene.createDefaultEnvironment({
        createGround: false,
        createSkybox: false,
        createDefaultCameraOrLight: false,
    });
    scene.createDefaultCameraOrLight(true, true, false);
    scene.activeCamera.alpha = Math.PI / 2;
    scene.activeCamera.radius = 0.05;

    BABYLON.AppendSceneAsync("https://playground.babylonjs.com/scenes/BoomBox.glb", scene);

    // Load a streaming sound and play it when the audio engine is unlocked
    (async () => {
        const audioEngine = await BABYLON.CreateAudioEngineAsync();
        const narration = await BABYLON.CreateStreamingSoundAsync("narration", "https://assets.babylonjs.com/sound/testing/60-count.mp3");

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

        narration.play();
    })();

    return scene;
};
```

音を扱う部分は、以下になるようです。

```javascript
    // Load a streaming sound and play it when the audio engine is unlocked
    (async () => {
        const audioEngine = await BABYLON.CreateAudioEngineAsync();
        const narration = await BABYLON.CreateStreamingSoundAsync("narration", "https://assets.babylonjs.com/sound/testing/60-count.mp3");

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

        narration.play();
    })();
```

#### 実装内容

実装内容について、公式ドキュメントの記載を見てみます。

今回の音の再生では「ストリーミング再生」が行われているようです。
この機能では、ブラウザの [HTMLMediaElement](https://developer.mozilla.org/ja/docs/Web/API/HTMLMediaElement) を活用しているようです。

再生方法がストリーミングの場合、再生時には音声ファイルの一部だけをメモリに保持して、全体をオーディオバッファに読み込む前に再生を開始できます。そのため、長時間の音声ファイルを再生する際に使うメモリを節約できたり、BGM や 長尺のナレーションなどに向いているとのことです。


## おわりに

今回、Babylon.js の「Playing Sounds and Music」のサンプルを 2つほど見てみました。

JavaScript で音を扱う話（ブラウザ上のものと Node.js を使ったもの）は、以下を使ったもの等を過去に色々と試しましたが、今回の記事で書いた Babylon.js のオーディオエンジンの機能も扱っていければと思います。

- [p5.sound](https://p5js.org/reference/p5.sound/)
- [howler.js](https://howlerjs.com/)
- [Tone.js](https://tonejs.github.io/)
- [play-sound - npm](https://www.npmjs.com/package/play-sound)
- [node-wav-player - npm](https://www.npmjs.com/package/node-wav-player)