当前位置: 首页 > 工具软件 > Babylon > 使用案例 >

19 Babylonjs入门进阶 设置声音

锺离宸
2023-12-01

Babylon.js声音引擎基于Web Audio规范。官方不决定提供音频标签和其他回退的备用机制。因此,要使用声音引擎,必须使用与Web Audio兼容的浏览器(一般支持WebGL的浏览器都支持)。尽管如此,如果你在不兼容的浏览器上使用,也不会出现异样,只是没有声音播放而已。声音引擎提供背景音频(ambient sound),空间音频(spatialized sound)和定向音频(directional sound)。它可以通过代码或者加载.babylon文件来创建。它将遵循您将要看到的引擎其它部分一样的简单而强大的理念。支持的音频格式是浏览器支持的音频格式。它通常是.mp3和.wav。

创建背景音

一下是创建声音或音乐为背景声音(非空间化)的代码:

// 加载一个声音并自动循化播放
var music = new BABYLON.Sound("Music", "music.wav", scene, null, { loop: true, autoplay: true });
  • 第一个参数:声音的名称。
  • 第二个参数:要加载的声音的URL。
  • 第三个参数:附加声音的场景。
  • 第四个参数:回调函数,一旦音频文件加载完成,这个函数将被触发,稍后会有演示。
  • 第五个参数:提供各种选项的JSON对象,我们将详细介绍。但您已经可以理解所提供的2个选项的目标。

您可以在官网查看第一个最普通的示例:点击这里

添加回调

使用BABYLON.Sound()构造函数加载音频会产出两个阶段:

  1. 使用异步XHR从web服务加载.wav和.mp3文件
  2. 一旦加载成功,声音就会被音频对象异步解析。解析成功,教会触发你自定义的回调函数。

这是一个普通的代码:

var music = new BABYLON.Sound("Music", "music.wav", scene,
 function () {
  // 音频在被下载和解析之后
  music.play();
 }
);

通过鼠标或者键盘触发声音

如果你点击鼠标左键或者键盘空格键,这个示例代码会发出枪声:

var gunshot = new BABYLON.Sound("gunshot", "sounds/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();
    }
});

基础设置

你可以通过配置选项或者通过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.playbackRate = playbackRate;
};

首先将速率设置为0.5和音量设置为0.1 。每次声音播放完成,将触发onended函数,音量和播放速率都会增加。
你可以使用音频引擎的setGlobalVolume()函数设置Babylon.js播放的所有声音的全局音量,而不是根据特定声音设置音量。

BABYLON.Engine.audioEngine.setGlobalVolume(0.5);

同时播放多个声音

首先需要确定所有的音频文件已经加载成功,然后在所有的音频对象上调用play()即可。

var music1 = new BABYLON.Sound("Violons11", "sounds/violons11.wav", scene,
    soundReady, { loop: true });
var music2 = new BABYLON.Sound("Violons18", "sounds/violons18.wav", scene,
    soundReady, { loop: true });
var music3 = new BABYLON.Sound("Cellolong", "sounds/cellolong.wav", scene,
    soundReady, { loop: true });

var soundsReady = 0;

function soundReady() {
    soundsReady++;
    if (soundsReady === 3) {
        music1.play();
        music2.play();
        music3.play();
    }
}

从ArrayBuffer加载声音

如果你使用自己提供ArrayBuffer调用构造函数,则可以绕过第一个阶段(使用ajax请求文件)。
一下是示例代码:

var gunshotFromAB;
loadArrayBufferFromURL("sounds/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();
}

使用AssetsManager加载音频

AssetsManager非常有用,因为它可以处理一些很棒的功能,比如屏幕加载

var music1, music2, music3;

// Assets manager
var assetsManager = new BABYLON.AssetsManager(scene);

var binaryTask = assetsManager.addBinaryFileTask("Violons18 task", "sounds/violons18.wav");
binaryTask.onSuccess = function (task) {
    music1 = new BABYLON.Sound("Violons18", task.data, scene, soundReady, { loop: true });
}

var binaryTask2 = assetsManager.addBinaryFileTask("Violons11 task", "sounds/violons11.wav");
binaryTask2.onSuccess = function (task) {
    music2 = new BABYLON.Sound("Violons11", task.data, scene, soundReady, { loop: true });
}

var binaryTask3 = assetsManager.addBinaryFileTask("Cello task", "sounds/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();

创建空间3D声音

要将声音转换为空间声音,你需要通过选项指定:

var music = new BABYLON.Sound("music", "music.wav", scene, null, { loop: true, autoplay: true, spatialSound: true });

空间声音的默认属性是:

  • distanceModel(声音距离衰减)默认使用“ 线性 ”方程。其他选项是“ 反向 ”或“ 指数 ”。
  • maxDistance设置为100。这意味着一旦距离声音超过100个单位,音量将为0.你将无法听到当前的声音
  • panningModel设置为“ HRTF ”。该规范说:“ 一种更高质量的空间化算法,使用卷积测量来自人类受试者的脉冲响应。这种平移方法呈现立体声输出 “。这是使用耳机时的最佳算法。另一个可用选项是“等功率 ”。

maxDistance仅在使用“ 线性 ”衰减时使用。否则,您可以使用rolloffFactor和refDistance选项调整其他模型的衰减。两者都默认设置为1,但您当然可以更改它。
例如:

var music = new BABYLON.Sound("music", "music.wav",
    scene, null, {
        loop: true, autoplay: true, spatialSound: true,
        distanceModel: "exponential", rolloffFactor: 2
    });

在场景中,声音的默认位置是原点(0,0,0)。要修改它,请使用setPosition()函数:

music.setPosition(new BABYLON.Vector3(100, 0, 0));

为了更好的理解,请查看官方示例,
我们可以使用键盘和鼠标移动相机位置。每个声音都由一个紫色的球体表示。当接近一个球体时,你会听到一个音乐。距离球心距离越近,声音越大,反之,距离越远,声音越小。

将声音绑定到模型上面

这可能是处理场景中3D声音的最简单的方法。只需要创建一个BABYLON.Sound,将其添加到现有的模型上面即可完成!如果网格正在移动,声音将随之移动。你根本不用整什么:

var music = new BABYLON.Sound("Violons", "sounds/violons11.wav", scene, null, { loop: true, autoplay: true });

// 声音将跟随着模型位置移动
music.attachToMesh(box);

在音频对象上调用attachToMesh()函数会将其自动转换为空间3D声音。使用上面的代码,你将使用默认的配置:maxDistance为100的声音线性衰减和“HRTF”类型的音频算法。

创建固定方向的3D声音

默认情况下,声音是向所有的方向传递的。如果你有需要,可以自定义方向传播的方向。
注意:定向声音只适合绑定到模型的声音。
示例代码:

var music = new BABYLON.Sound("Violons", "violons11.wav", scene, null, { loop: true, autoplay: true });
music.setDirectionalCone(90, 180, 0);
music.setLocalDirectionToMesh(new BABYLON.Vector3(1, 0, 0));
music.attachToMesh(box);

setDirectionalCone有3个参数:

  • coneInnerAngle:内锥的大小(以度为单位)-以一个方向沿设置的角度环绕一圈360度的范围
  • coneOuterAngle:外锥的大小(以度为单位)
  • coneOuterGain:当你在外锥体之外时声音的音量(介于0.0和1.0之间)

声音椎体的外锥度数必须必内锥大,否则会出现错误并且定向声音不起作用。
setLocalDirectionToMesh是设置声音椎体在模型的哪个方向。默认情况下,是(1,0,0)

示例中,你如果进入椎体的朝向范围,将会听到音乐,如果不在,将不会有音乐,因为我们将coneOuterGain设置为0 。

创建自定义衰减

如果要使用特定算法管理衰减(或Web Audio中的距离模型),可以使用Babylon.js自定义衰减功能绕过本机Web Audio衰减。

注意: Web Audio是“ 硬件加速 ”。这意味着它主要由设备上的专用音频芯片通过本机代码(浏览器)处理。然后,这在3D实时渲染的性能方面几乎没有任何成本。切换到自定义衰减将使用基于JavaScript的Babylon.js距离计算,并且速度会变慢。

此外,自定义衰减仅适用于空间声音(显然),但也适用于连接到Babylon.js网格的声音。也就是说,让我们现在查看代码来做到这一点。首先,您必须在选项中指定它:

// Create and load the sound async
var music = new BABYLON.Sound("Music", "music.wav", scene, null, { loop: true, autoplay: true, useCustomAttenuation: true });

您将切换到内部Babylon.js数学计算。默认的自定义衰减功能是线性的。
要创建自己的逻辑,需要这样的代码:

// 创建一个自定义衰减函数,距离模型越近,声音越小
// 距离越远,声音越大
music.setAttenuationFunction(function (currentVolume, currentDistance, maxDistance, refDistance, rolloffFactor) {
    return currentVolume * currentDistance / maxDistance;
});

你可以通过这5个参数,实现你的自定义的衰减函数。只需要返回一个应用于声音的数字即可。
这个例子逻辑有点奇怪,因为距离模型越远,声音越大。这里查看示例
此外,Firefox目前在处理正确线性衰减的Web Audio实现中存在一个错误。这可以通过使用Babylon.js默认线性自定义衰减来修复。

这是以前的示例代码,现在可以在Firefox中正常运行:

https://www.babylonjs-playground.com/#2AH4YH#2

从.babylon文件中加载声音

目前只有3DS Max导出器可以直接将声音导出到.babylon。
要从.babylon文件中加载声音,您需要在场景对象上使用getSoundByName()函数。
这是一个示例代码:

var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);
BABYLON.SceneLoader.Load("TestScene/", "testsound.babylon", engine, function (newScene) {
    newScene.executeWhenReady(function () {
        newScene.activeCamera.attachControl(canvas);

		//从场景对象上面获取到声音对象
        var gunshotSound = newScene.getSoundByName("gunshot-1.wav");
        window.addEventListener("keydown", function (evt) {
            if (evt.keyCode === 32 && gunshotSound) {
                    gunshotSound.play();
            }
        });

        engine.runRenderLoop(function () {
            newScene.render();
        });
    });
}, function (progress) {
    // 待办事项:向用户提供进度反馈
});

按空格键将播放枪声。

使用声轨

在几个声轨上分离音乐和声音更好的管理声音的音量可能很有用。它还将在未来版本中特定声轨效果。
默认情况下,Babylon.js创建一个BABYLON.SoundTrack对象为一个声轨。我们可以将实例化的音频对象添加到这个音轨上。

var soundTrack = new BABYLON.SoundTrack(scene);
soundTrack.AddSound(cellolong);
soundTrack.AddSound(violons11);

使用此代码,“ cellolong ”和“ violons11 ”声音将从主Babylon.js音轨移动到此特定音轨。现在,这意味着您可以独立于主轨道更改此轨道的音量,从而更改这两个声音的音量。
使用AddSound()函数会将音频对象从原来的容器中移动到指定的新的声轨中:

var soundTrack1 = new BABYLON.SoundTrack(scene);
soundTrack1.AddSound(cellolong);
soundTrack1.AddSound(violons11);

var soundTrack2 = new BABYLON.SoundTrack(scene);
soundTrack2.AddSound(violons11);

“ violons11 ”声音最终只会出现在“ soundTrack2 ”中。

使用分析器

您可以轻松实时分析音频。
了解其工作原理的最简单代码是:

var myAnalyser = new BABYLON.Analyser(scene);
BABYLON.Engine.audioEngine.connectToAnalyser(myAnalyser);
myAnalyser.drawDebugCanvas();

这将连接到音频引擎的全局音量,并将所有一起播放的声音的频率绘制到屏幕顶部的2D画布显示中。
您可以更改调试画布的位置和大小,并在声道而不是全局音频引擎上使用分析器:

var myAnalyser = new BABYLON.Analyser(scene);
soundTrack1.connectToAnalyser(myAnalyser);
myAnalyser.DEBUGCANVASSIZE.width = 160;
myAnalyser.DEBUGCANVASSIZE.height = 100;
myAnalyser.DEBUGCANVASPOS.x = 40;
myAnalyser.DEBUGCANVASPOS.y = 30;
myAnalyser.drawDebugCanvas();

您也可以称自己为分析器功能,以创建自己的使用方法。
这是一个完整的示例

 类似资料: