Babylon.js 深入 - 第 2 章 - 声音(1)

呼延鸿畅
2023-12-01

Babylon.js 深入 - 第 2 章 - 声音(1)

声音

Babylon.js 声音引擎基于 Web Audio 规范,要使用它,您需要使用与 Web Audio 兼容的浏览器。声音引擎提供环境、空间和定向声音。

如何播放声音和音乐

Babylon.js 声音引擎基于 Web Audio 规范。我们决定不对音频标签或其他机制提供兼容。因此,要使用我们的声音引擎,您需要使用与 Web Audio 兼容的浏览器。尽管如此,如果你在不兼容的浏览器上使用它,它不会影响我们引擎的其余部分,它只会不播放任何声音。

声音引擎提供环境声音、空间声音和定向声音。它可以通过代码或加载 .babylon 文件来创建。正如您将看到的,它遵循和引擎其余部分一样的简单而强大的设计理念。

支持的声音格式和浏览器的声音格式一样。通常至少是 .mp3.wav

创建环境声音或音乐

这是创建作为环境(非空间化)播放的声音或音乐的代码:

// 加载声音并在准备好后自动播放
var music = new BABYLON.Sound("Music", "music.wav", scene, null, {
    loop: true,
    autoplay: true
});
  • 1 个参数:声音的名称。
  • 2 个参数:要加载的声音的 URL
  • 3 个参数:附加声音的场景。
  • 4 个参数:一旦声音准备好播放,函数就会被回调,我们稍后会看到。
  • 5 个参数:一个 JSON 对象,提供我们将详细介绍的各种选项。但是您已经可以理解所提供的 2 个选项的意思。

简单的音乐播放的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        var createScene = function () {
            var scene = new BABYLON.Scene(engine);
            var camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);
            // 加载声音,准备好后自动播放
            var music = new BABYLON.Sound("Violons", "./violons11.wav", scene, null, {
                loop: true,
                autoplay: true
            });
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

处理准备播放回调函数

使用 URL 调用 BABYLON.Sound() 构造函数会生成 2 个阶段:

  • .wav.mp3 文件是使用异步 XHR 从您的网络服务器加载的。
  • 一旦加载,声音就会被网络音频异步解码。如果成功,它将触发您提供的回调函数。

这是一个示例代码:

var music = new BABYLON.Sound("Music", "music.wav", scene, function () {
    // 声音已下载和解码
    music.play();
});

此代码从 Web 服务器加载 music.wav 文件,对其进行解码并使用 play() 函数在回调函数中播放一次。如果没有传递参数,play() 函数会立即播放声音。您可以提供 number 类型的参数使其在 x 秒后播放。

加载和使用回调播放声音的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        var createScene = function () {
            var scene = new BABYLON.Scene(engine);
            var camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);
            var music = new BABYLON.Sound("Violons", "./violons11.wav", scene,
                function () {
                    // 声音已下载和解码
                    music.play();
                }
            );
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

在鼠标单击或按键时播放声音

如果您左键单击或按空格键,此示例代码会播放枪声:

var gunshot = new BABYLON.Sound("gunshot", "./gunshot.wav", scene);
window.addEventListener("mousedown", function (evt) {
    // 左键点击开火
    if (evt.button === 0) {
        gunshot.play();
    }
});
window.addEventListener("keydown", function (evt) {
    // 按空格键开火
    if (evt.keyCode === 32) {
        gunshot.play();
    }
});

通过交互播放声音的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        var createScene = function () {
            var scene = new BABYLON.Scene(engine);
            var camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);
            var gunshot = new BABYLON.Sound("gunshot", "./gunshot.wav", scene);
            window.addEventListener("mousedown", function (evt) {
                // 左击开火
                if (evt.button === 0) {
                    gunshot.play();
                }
            });
            window.addEventListener("keydown", function (evt) {
                // 按空格键开火
                if (evt.keyCode === 32) {
                    gunshot.play();
                }
            });
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

一些基本属性

您可以通过设置选项对象的 volume 或通过 setVolume() 函数设置声音的音量。您可以以相同的方式设置播放速率。

通过 onended 事件,您还可以在声音播放完毕时收到通知。

这是一个混合所有这些的简单示例代码:

var volume = 0.1;
var playbackRate = 0.5;
var gunshot = new BABYLON.Sound("Gunshot", "./gunshot-1.wav", scene, null, {
    playbackRate: playbackRate,
    volume: volume
});
gunshot.onended = function () {
    if (volume < 1) {
        volume += 0.1;
        gunshot.setVolume(volume);
    }
    playbackRate += 0.1;
    gunshot.setPlaybackRate(playbackRate);
};

声音首先使用 0.5 的播放率和 0.1 的音量创建。每次播放声音时,都会调用 onended 函数,并且音量和播放速率会增加。

除了设置特定声音的音量,您还可以使用音频引擎的 setGlobalVolume() 函数设置 Babylon.js 播放的所有声音的全局音量。

BABYLON.Engine.audioEngine.setGlobalVolume(0.5);

播放声音精灵

声音精灵是声音文件的一部分。您可以通过定义偏移量和时长(以秒为单位)在创建声音时定义声音精灵:

var soundSprite = new BABYLON.Sound(
    "Violons",
    "./6sounds.mp3",
    scene,
    null, {
        loop: true,
        autoplay: true,
        length: 9.200,
        offset: 14.000
    }
);

播放声音精灵1的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        var createScene = function () {
            var scene = new BABYLON.Scene(engine);
            var camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);
            var soundSprite = new BABYLON.Sound("Violons", "./6sounds.mp3", scene, null, {
                loop: true,
                autoplay: true,
                length: 9.200,
                offset: 14.000
            });
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

播放声音精灵2的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        var createScene = function () {
            var scene = new BABYLON.Scene(engine);
            var camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);
            // 加载声音文件,设置参数和播放结束时的回调函数
            var theSounds = new BABYLON.Sound("allSounds", "./6sounds.mp3", scene, null, {
                autoplay: false
            });
            // 播放声音时设置为 1
            var isPlaying = 0;
            // 声音文件中六个声音的开始和长度
            var soundArray = [
                [0.0, 5.000],
                [5.100, 6.600],
                [12.000, 1.600],
                [14.000, 9.200],
                [23.000, 7.900],
                [31.000, 2.800],
            ];
            // 播放结束时运行的函数
            theSounds.onended = function () {
                isPlaying = 0;
                console.log("not playing");
            };
            // 结束声音文件加载和设置
            // 创建一个简单的两个按钮 UI 来播放所有声音和播放随机声音
            var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
            var UiPanel = new BABYLON.GUI.StackPanel();
            UiPanel.width = "220px";
            UiPanel.fontSize = "14px";
            UiPanel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
            UiPanel.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER;
            advancedTexture.addControl(UiPanel);
            var button = BABYLON.GUI.Button.CreateSimpleButton("but1", "Play All Sounds");
            button.paddingTop = "10px";
            button.width = "150px";
            button.height = "50px";
            button.color = "white";
            button.background = "green";
            button.onPointerDownObservable.add(() => {
                if (isPlaying === 0) {
                    isPlaying = 1
                    theSounds.play();
                }
            });
            UiPanel.addControl(button);
            var button1 = BABYLON.GUI.Button.CreateSimpleButton("but2", "Play Random Sound");
            button1.paddingTop = "10px";
            button1.width = "150px";
            button1.height = "50px";
            button1.color = "white";
            button1.background = "green";
            button1.onPointerDownObservable.add(() => {
                if (isPlaying === 0) {
                    isPlaying = 1;
                    // 创建随机数 0->5
                    var aRand0 = Math.floor(Math.random() * 6);
                    theSounds.play(0, soundArray[aRand0][0], soundArray[aRand0][1]);
                }
            });
            UiPanel.addControl(button1);
            // 结束 ui 创建和播放功能
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

同时播放多个声音并同步

为此,只有在确定所有声音都准备好播放时,您才需要对所有声音调用 play() 方法。

var music1 = new BABYLON.Sound(
    "Violons11",
    "./violons11.wav",
    scene,
    soundReady, {
        loop: true
    }
);
var music2 = new BABYLON.Sound(
    "Violons18",
    "./violons18.wav",
    scene,
    soundReady, {
        loop: true
    }
);
var music3 = new BABYLON.Sound(
    "Cellolong",
    "./cellolong.wav",
    scene,
    soundReady, {
        loop: true
    }
);
var soundsReady = 0;
function soundReady() {
    soundsReady++;
    if (soundsReady === 3) {
        music1.play();
        music2.play();
        music3.play();
    }
}

一起播放声音的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        var createScene = function () {
            var scene = new BABYLON.Scene(engine);
            var camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);
            var music1 = new BABYLON.Sound("Violons11", "./violons11.wav", scene,
                soundReady, {
                    loop: true
                });
            var music2 = new BABYLON.Sound("Violons18", "./violons18.wav", scene,
                soundReady, {
                    loop: true
                });
            var music3 = new BABYLON.Sound("Cellolong", "./cellolong.wav", scene,
                soundReady, {
                    loop: true
                });
            var soundsReady = 0;

            function soundReady() {
                soundsReady++;
                if (soundsReady === 3) {
                    music1.play();
                    music2.play();
                    music3.play();
                }
            }
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

从 ArrayBuffer 加载声音

如果您使用自己提供的 ArrayBuffer 调用构造函数,则可以绕过第一阶段(嵌入 XHR 请求)。

这是一个演示它的示例代码:

var gunshotFromAB;
loadArrayBufferFromURL("./gunshot.wav");
function loadArrayBufferFromURL(urlToSound) {
    var request = new XMLHttpRequest();
    request.open("GET", urlToSound, true);
    request.responseType = "arraybuffer";
    request.onreadystatechange = function () {
        if (request.readyState == 4) {
            if (request.status == 200) {
                gunshotFromAB = new BABYLON.Sound(
                    "FromArrayBuffer",
                    request.response,
                    scene,
                    soundReadyToBePlayed
                );
            }
        }
    };
    request.send(null);
}
function soundReadyToBePlayed() {
    gunshotFromAB.play();
}

ArrayBuffer 加载声音的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        var createScene = function () {
            var scene = new BABYLON.Scene(engine);
            var camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);
            var gunshotFromAB;
            loadArrayBufferFromURL("./gunshot.wav");

            function loadArrayBufferFromURL(urlToSound) {
                var request = new XMLHttpRequest();
                request.open('GET', urlToSound, true);
                request.responseType = "arraybuffer";
                request.onreadystatechange = function () {
                    if (request.readyState == 4) {
                        if (request.status == 200) {
                            gunshotFromAB = new BABYLON.Sound("FromArrayBuffer", request.response, scene,
                                soundReadyToBePlayed);
                        }
                    }
                };
                request.send(null);
            }

            function soundReadyToBePlayed() {
                gunshotFromAB.play();
            }
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

从麦克风加载声音的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        var createScene = function () {
            var scene = new BABYLON.Scene(engine);
            var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
            camera.setTarget(BABYLON.Vector3.Zero());
            camera.attachControl(canvas, true);
            var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
            light.intensity = 0.7;
            var sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);
            const constraints = window.constraints = {
                audio: true,
                video: false
            };

            function handleSuccess(stream) {
                const audioTracks = stream.getAudioTracks();
                console.log('Got stream with constraints:', constraints);
                console.log('Using audio device: ' + audioTracks[0].label);
                stream.oninactive = function () {
                    console.log('Stream ended');
                };
                // 使变量可用于浏览器控制台
                window.stream = stream;
                var bjsSound = new BABYLON.Sound("mic", stream, scene);
                bjsSound.attachToMesh(sphere);
                bjsSound.play();
            }

            function handleError(error) {
                console.log('navigator.getUserMedia error: ', error);
            }
            navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

使用 Assets Manager 加载声音

资产管理器非常有用,因为它为您提供了一些很棒的功能,例如:加载屏幕。

var music1, music2, music3;
// Assets manager
var assetsManager = new BABYLON.AssetsManager(scene);
var binaryTask = assetsManager.addBinaryFileTask(
    "Violons18 task",
    "./violons18.wav"
);
binaryTask.onSuccess = function (task) {
    music1 = new BABYLON.Sound("Violons18", task.data, scene, soundReady, {
        loop: true
    });
};
var binaryTask2 = assetsManager.addBinaryFileTask(
    "Violons11 task",
    "./violons11.wav"
);
binaryTask2.onSuccess = function (task) {
    music2 = new BABYLON.Sound("Violons11", task.data, scene, soundReady, {
        loop: true
    });
};
var binaryTask3 = assetsManager.addBinaryFileTask(
    "Cello task",
    "./cellolong.wav"
);
binaryTask3.onSuccess = function (task) {
    music3 = new BABYLON.Sound("Cello", task.data, scene, soundReady, {
        loop: true
    });
};
var soundsReady = 0;
function soundReady() {
    soundsReady++;
    if (soundsReady === 3) {
        music1.play();
        music2.play();
        music3.play();
    }
}
assetsManager.load();

使用 Asset Manager 加载声音的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        var createScene = function () {
            var scene = new BABYLON.Scene(engine);
            var camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);
            var music1, music2, music3;
            // Assets manager
            var assetsManager = new BABYLON.AssetsManager(scene);
            var binaryTask = assetsManager.addBinaryFileTask("Violons18 task", "./violons18.wav");
            binaryTask.onSuccess = function (task) {
                music1 = new BABYLON.Sound("Violons18", task.data, scene, soundReady, {
                    loop: true
                });
            }
            var binaryTask2 = assetsManager.addBinaryFileTask("Violons11 task", "./violons11.wav");
            binaryTask2.onSuccess = function (task) {
                music2 = new BABYLON.Sound("Violons11", task.data, scene, soundReady, {
                    loop: true
                });
            }
            var binaryTask3 = assetsManager.addBinaryFileTask("Cello task", "./cellolong.wav");
            binaryTask3.onSuccess = function (task) {
                music3 = new BABYLON.Sound("Cello", task.data, scene, soundReady, {
                    loop: true
                });
            }
            var soundsReady = 0;

            function soundReady() {
                soundsReady++;
                if (soundsReady === 3) {
                    music1.play();
                    music2.play();
                    music3.play();
                }
            }
            assetsManager.load();
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>
 类似资料: