A-frame通过其<sound>
component提供易于使用且功能强大的音频功能。
在为我的游戏(正在进行)中使用各种声音选项(例如原生html5)之后,我得出的结论是A帧声音是最佳选择,因为它会自动提供空间化声音(例如,随着头部旋转而变化) ,以及靠近声源的强度变化 - 增加VR存在的东西,以及定义简单html标签的成本。
不幸的是,A帧不提供淡出功能,可以在停止时逐渐减小声音,从而可以在某些波形上产生明显的声音和恼人的点击,尤其是。声音长度可变,波形本身不呈锥形(例如太空船的推力)。这是一个带有计算机音频的well known problem 。
我能够找到一些html5 audio solutions和一个非常好的three.js音频three.js audio solution,但我找不到特定的A帧。
在A帧中逐渐减少声音以减少/消除此点击的最佳方法是什么?
答案 0 :(得分:0)
A帧sound
音频包装three.js positional audio API,后者又包含原生html 5音频。大多数解决方案都是针对纯html5或纯粹的three.js量身定制的。由于A帧是两个api的混合体,所提供的解决方案都不适合A帧。
在提出两件错误的开始之后,我发现了tween.js,它不仅内置于A-frame(甚至不必下载库),而且还是一个用于了解其他形式的计算机动画的有用API。我提供了这里的主要解决方案以及a plunker,希望其他人能找到有用的东西。
请注意,您不需要为子弹射击等短暂爆发声音执行此操作。这些声音具有固定的寿命,因此大概创建波形的人确保将它们逐渐变细。另外,我只处理淡出,不淡化因为我需要的声音只有淡出的问题。一般的解决方案也包括fadein。
1)我们开始创建一个真实的基本场景,我们可以在其上播放音频:
<a-scene>
<a-assets>
<audio id="space-rumble" src="https://raw.githubusercontent.com/vt5491/public/master/assets/sounds/space-rumble.ogg" type="audio/ogg"></audio>
crossorigin="anonymous"
type="audio/ogg"></audio>
</a-assets>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"
sound="src: #space-rumble; volume: 0.9"
></a-box>
</a-scene>
此解决方案中的立方体和场景实际上只是占位符 - 您不需要进入VR模式来单击按钮并测试声音。
2)代码提供了三个按钮:一个用于启动声音,一个用于&#34; hard&#34;使用A帧默认设置停止,第三个设置为&#34; easy&#34;使用补间停止它以将其逐渐减小到零。第四个输入允许您改变锥度时间。虽然它可能看起来像相当多的代码,但请记住,50%只是按钮的html样板,并且不是解决方案的一部分&#34;正确&#34;:
// created 2017-10-04
function init() {
let main = new Main();
}
function Main() {
let factory = {};
console.log("entered main");
factory.boxEntity = document.querySelector('a-box');
factory.sound = factory.boxEntity.components.sound;
factory.volume = {vol: factory.sound.data.volume};
factory.boxEntity.addEventListener('sound-loaded', ()=> {console.log('sound loaded')});
factory.startBtn =document.querySelector('#btn-start');
factory.startBtn.onclick = ( function() {
this.sound.stopSound();
let initVol = factory.sound.data.volume;
this.volume = {vol: initVol}; //need to do this every time
this.sound.pool.children[0].setVolume(initVol);
console.log(`onClick: volume=${this.sound.pool.children[0].getVolume()}`);
this.sound.currentTime = 0.0;
if( this.tween) {
this.tween.stop();
}
this.sound.playSound();
}).bind(factory);
factory.hardStopBtn =document.querySelector('#btn-hard-stop');
factory.hardStopBtn.onclick = (function() {
this.sound.stopSound();
}).bind(factory);
factory.easyStopBtn =document.querySelector('#btn-easy-stop');
factory.easyStopBtn.onclick = (function() {
let sound = factory.sound;
this.tween = new TWEEN.Tween(this.volume);
this.tween.to(
{vol: 0.0}
, document.querySelector('#fade-out-duration').value);
this.tween.onUpdate(function(obj) {
console.log(`onUpdate: this.vol=${this.vol}`);
sound.pool.children[0].setVolume(this.vol);
console.log(`onUpdate: pool.children[0].getVolume=${sound.pool.children[0].getVolume()}`);
});
// Note: do *not* bind to parent context as tween passes it's info via 'this'
// and not just via callback parms.
// .bind(factory));
this.tween.onComplete(function() {
sound.stopSound();
console.log(`tween is done`);
});
this.tween.start();
// animate is actually optional in this case. Tween will count down on it's
// own clock, but you might want to synchronize with your other updates. If this
// is an a-frame component, then you can just use the 'tick' method.
this.animate();
}).bind(factory);
factory.animate = () => {
let id = requestAnimationFrame(factory.animate);
console.log(`now in animate`);
let result = TWEEN.update();
// cancelAnimationFrame is optional. You might want to invoke this to avoid
// the overhead of repeated animation calls. If you are putting this in an
// a-frame 'tick' callback, and there's other tick activity, you
// don't want to call this.
if(!result) cancelAnimationFrame(id);
}
return factory;
}
以下是一些需要注意的相关事项。
我正在调用一些原生的A帧级别调用:
sound.playSound()
sound.stopSound()
和一个html5级别的电话:
this.sound.currentTime = 0.0;
但大多数&#34;工作&#34;在三个级别的电话中:
this.sound.pool.children[0].setVolume(initVol);
这确实让它有点混乱,但没有单一的api&#34;完成&#34;因此我不得不使用这三个。特别是,我们必须在由A-frame包裹的级别上做很多事情。我通过查看aframe source for the sound component
了解了大部分内容 Aframe允许每个声音使用多个线程,这样您就可以在前一个声音完成之前触发相同的声音。这由声音组件上的poolSize
属性控制。我只处理第一个声音。我应该像这样循环池元素:
this.pool.children.forEach(function (sound) {
..do stuff
}
});
但到目前为止,做第一个效果还不错。时间将证明这是否可持续。
我选择使用工厂对象模式实现所有功能,而不是将所有方法和变量放在全局文档空间中。如果您在Angular2中实现或作为本机A帧组件实现,那么这将模仿您将拥有的环境。我提到这个是因为我们现在有嵌套在嵌套在包装内的函数的回调&#34; main&#34;功能。因此要注意&#34;这个&#34;绑定可以发挥作用。我将大多数支持函数绑定到工厂对象,但是不绑定补间回调,因为它们在&#34; this&#34;中传递信息。上下文,而不是通过parms传递。我不得不求助于闭包来获取对包含类的实例变量的访问。这只是标准的javascript&#34;回调地狱&#34;事情,但请记住,如果你不小心,它可能会让人感到困惑。
如果您已经有一个勾选功能,请使用它来呼叫TWEEN.update()
。如果你只是淡出声音,那么让动画循环一直运行就太过分了,所以在这个例子中我动态地启动和停止动画循环。
Tweens也可以用jquery流畅的API样式链接。
使用tween.js逐步淘汰声音绝对是一种正确的解决方案。它需要考虑很多开销和设计考虑因素。它比我之前使用的本机html5调用感觉更快,更流畅,更健壮。但是,很明显,在应用程序级别上工作并不是一件容易的事。在Tween.js中实现的淡出属性似乎应该是A帧声音组件本身的一部分。但在那之前,也许有些人会发现我在这里提供的某些东西在某种形式上是有用的。我现在只是在学习html音频,所以如果我觉得这看起来比实际上更难,那就道歉了。