JavaScript / HTML5 中的音效
Posted
技术标签:
【中文标题】JavaScript / HTML5 中的音效【英文标题】:Sound effects in JavaScript / HTML5 【发布时间】:2010-12-28 09:14:10 【问题描述】:我正在使用 html5 来编写游戏;我现在遇到的障碍是如何播放音效。
具体要求很少:
播放和混合多种声音, 多次播放相同的样本,可能会重叠播放, 在任何时候中断样本的播放, 最好播放包含(低质量)原始 PCM 的 WAV 文件,但我当然可以转换这些文件。我的第一个方法是使用 HTML5 <audio>
元素并在我的页面中定义所有声音效果。 Firefox 播放 WAV 文件的效果非常好,但多次调用 #play
并不能真正多次播放样本。根据我对 HTML5 规范的理解,<audio>
元素还可以跟踪播放状态,这就解释了原因。
我的直接想法是克隆音频元素,因此我创建了以下小型 javascript 库来为我做这件事(取决于 jQuery):
var Snd =
init: function()
$("audio").each(function()
var src = this.getAttribute('src');
if (src.substring(0, 4) !== "snd/") return;
// Cut out the basename (strip directory and extension)
var name = src.substring(4, src.length - 4);
// Create the helper function, which clones the audio object and plays it
var Constructor = function() ;
Constructor.prototype = this;
Snd[name] = function()
var clone = new Constructor();
clone.play();
// Return the cloned element, so the caller can interrupt the sound effect
return clone;
;
);
;
所以现在我可以从 Firebug 控制台执行 Snd.boom();
并播放 snd/boom.wav
,但我仍然无法多次播放同一个样本。看来<audio>
元素实际上更像是一种流媒体功能,而不是用来播放音效的东西。
有没有一种我想念的聪明方法来实现这一点,最好只使用 HTML5 和 JavaScript?
我还要提一下,我的测试环境是 Ubuntu 9.10 上的 Firefox 3.5。我尝试过的其他浏览器——Opera、Midori、Chromium、Epiphany——产生了不同的结果。有些什么都不播放,有些抛出异常。
【问题讨论】:
哈!我还将一个旧的 Macintosh 游戏移植到 HTML5。想知道你正在克隆哪一个? 这是 ARASHI 计划,暴风雨克隆。 什么是“混合”声音? 你完成了吗?我们能看到吗? 很遗憾,没有。我有不完成业余项目的诀窍。 :( 有一个 git repo ,但我已经好几年没碰过了:github.com/stephank/arashi-js 【参考方案1】:HTML5 Audio
对象
您无需为<audio>
元素而烦恼。 HTML 5 让您可以直接访问Audio
objects:
var snd = new Audio("file.wav"); // buffers automatically when created
snd.play();
当前版本的规范不支持混用。
要多次播放相同的声音,请创建Audio
对象的多个实例。您也可以在播放完毕后在对象上设置snd.currentTime=0
。
由于 JS 构造函数不支持回退 <source>
元素,您应该使用
(new Audio()).canPlayType("audio/ogg; codecs=vorbis")
测试浏览器是否支持Ogg Vorbis。
如果您正在编写游戏或音乐应用程序(不仅仅是播放器),您会想要使用 more advanced Web Audio API,现在是 supported by most browsers。
【讨论】:
Audio
对象是自动缓冲的。它们的设计类似于 Image
对象,因此老式的图像预加载技术也适用于音频。
这也适用于 ios。请注意,您需要使用某种用户操作(例如,单击按钮)来启动声音。您不能只在 window.load() 上执行 snd.play()
。
我还在使用<audio>
标签,因为它更可控。但无论如何感谢您的信息!
如果不是因为 html5 元素允许多个来源,我更喜欢这种方法,因此如果浏览器不支持 mp3,您可以回退到 ogg/wav。它需要在 javascript 中使用一些技巧来完成相同的操作。
在 iOS 6 上支持自动播放:您可以在 window.load() 上使用简单的 snd.play() 来启动声音。【参考方案2】:
网络音频 API
编辑:截至 2021 年 12 月,Web Audio API 在 Chrome、Firefox、Safari 和所有其他主要浏览器(当然不包括 IE)中基本上是 supported。
截至 2012 年 7 月,Chrome 现已支持 Web Audio API,Firefox 至少部分支持,并计划从第 6 版开始添加到 IOS。
尽管Audio
元素足够健壮,可以通过编程方式用于基本任务,但它从来没有打算为游戏和其他复杂应用程序提供完整的音频支持。它旨在允许将单个媒体嵌入到页面中,类似于img
标签。尝试将它用于游戏有很多问题:
Audio
元素常见的时间差
每个声音实例都需要一个 Audio
元素
加载事件尚不完全可靠
没有常见的音量控制、没有衰减、没有过滤器/效果
这里有一些很好的资源可以帮助您开始使用 Web Audio API:
MDN文档 Getting Started With WebAudio文章 FieldRunners WebAudio Case Study 也不错【讨论】:
这是一个比公认的更好的答案,因为它专注于问题的正确解决方案(WebAudio API),以及音频元素不是一个好的解决方案的原因,而不是试图破解与音频元素一起糟糕的解决方案【参考方案3】:howler.js
对于游戏创作,最好的解决方案之一是使用一个库来解决我们在为 Web 编写代码时面临的许多问题,例如 howler.js。 howler.js 将伟大的(但低级的)Web Audio API 抽象为一个易于使用的框架。如果 Web Audio API 不可用,它将尝试回退到 HTML5 Audio Element。
var sound = new Howl(
urls: ['sound.mp3', 'sound.ogg']
).play();
// it also provides calls for spatial/3d audio effects (most browsers)
sound.pos3d(0.1,0.3,0.5);
wad.js
另一个很棒的库是wad.js,它对于制作合成音频特别有用,例如音乐和效果。例如:
var saw = new Wad(source : 'sawtooth')
saw.play(
volume : 0.8,
wait : 0, // Time in seconds between calling play() and actually triggering the note.
loop : false, // This overrides the value for loop on the constructor, if it was set.
pitch : 'A4', // A4 is 440 hertz.
label : 'A', // A label that identifies this note.
env : hold : 9001,
panning : [1, -1, 10],
filter : frequency : 900,
delay : delayTime : .8
)
Sound for Games
另一个类似于 Wad.js 的库是“Sound for Games”,它更专注于效果制作,同时通过相对独特(也许更简洁的感觉)API 提供一组类似的功能:
function shootSound()
soundEffect(
1046.5, //frequency
0, //attack
0.3, //decay
"sawtooth", //waveform
1, //Volume
-0.8, //pan
0, //wait before playing
1200, //pitch bend amount
false, //reverse bend
0, //random pitch range
25, //dissonance
[0.2, 0.2, 2000], //echo array: [delay, feedback, filter]
undefined //reverb array: [duration, decay, reverse?]
);
总结
这些库中的每一个都值得一看,无论您是需要播放单个声音文件,还是创建自己的基于 html 的音乐编辑器、效果生成器或视频游戏。
【讨论】:
无法评论游戏开发,但我将其用于界面中的一些次要音频反馈,因此它绝对是一种享受。快速、简单、简单。 非常好的框架,在移动端也能很好地工作。你必须用 touchstart 来触发它......但是一旦完成它就可以开箱即用:) howler.js 非常棒,到目前为止。【参考方案4】:在某些情况下,您可能还想使用它来检测 HTML 5 音频:
http://diveintohtml5.ep.io/everything.html
HTML 5 JS 检测功能
function supportsAudio()
var a = document.createElement('audio');
return !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
【讨论】:
感谢您注意到这一点。我从 has.js 中学到了这个方法,但我发现它在 Firefox 中存在一些问题,而且根据 has.js 源代码显然在 Opera 中也是如此。 (参考:github.com/phiggins42/has.js/issues/48github.com/phiggins42/has.js/blob/master/detect/audio.js#L19)【参考方案5】:这是一种可以同时播放相同声音的方法。与预加载器结合使用,一切就绪。这至少适用于 Firefox 17.0.1,还没有用其他任何东西测试过。
// collection of sounds that are playing
var playing=;
// collection of sounds
var sounds=step:"knock.ogg",throw:"swing.ogg";
// function that is used to play sounds
function player(x)
var a,b;
b=new Date();
a=x+b.getTime();
playing[a]=new Audio(sounds[x]);
// with this we prevent playing-object from becoming a memory-monster:
playing[a].onended=function()delete playing[a];
playing[a].play();
将此绑定到键盘键,然后享受:
player("step");
【讨论】:
没有。我认为为每个单独的声音播放创建一个新对象根本不是一个解决方案。这是一种快速而肮脏的解决方法,但可以做得更好。 @Pawel 没错,这不应该用作播放声音的主要方法,但据我所知,示例中的原理是多次播放相同声音的唯一方法同时,或如 OP 所说,“重叠播放”。 (不需要浏览器插件。)我在当前项目中的实际实现比我发布的示例代码要复杂得多。【参考方案6】:要多次播放同一个样本,难道不能这样做吗:
e.pause(); // Perhaps optional
e.currentTime = 0;
e.play();
(e
是音频元素)
也许我完全误解了你的问题,你想让音效同时播放多次吗?那么这是完全错误的。
【讨论】:
没错,我需要同时多次播放同一个样本。 是的,这会多次播放同一个实例,但对于查看此答案的人来说,请注意这不会导致“重叠播放”,正如 OP 所说的那样。【参考方案7】:听起来你想要的是多声道声音。假设您有 4 个通道(就像在真正的 16 位游戏中一样),我还没有开始使用 HTML5 音频功能,但您不只需要 4 个 元素,然后循环使用播放下一个音效?你试过吗?发生什么了?如果可行:要同时播放更多声音,只需添加更多 元素。
我之前在没有 HTML5 元素的情况下使用了来自 http://flash-mp3-player.net/ 的一个小 Flash 对象,我写了一个音乐测验 (http://webdeavour.appspot.com/),并在用户单击按钮时用它来播放音乐片段对于这个问题。最初我每个问题都有一个播放器,并且可以将它们重叠播放,所以我将其更改为只有一个播放器,我指向不同的音乐片段。
【讨论】:
这是一个有趣的想法,使用 <audio>元素作为渠道。当我将两个元素放在一个页面上时,我可以同时播放它们。看起来我正在做的克隆实际上并没有达到我的预期,因为在我的原始代码中,播放两个样本也可以。只是不是两次相同的样本。我将不得不对此进行更多试验,然后再得到结果! 是的,它被称为克隆而不是复制可能有一个微妙的原因。也许克隆仍然与原版共享某些内容,从而阻止它们同时播放。 作为参考,重复使用相同的 元素是行不通的,至少在 Firefox 中是这样。您可以设置“src”属性,但它实际上不会加载不同的样本。【参考方案8】:查看jai (-> mirror)(javascript 音频接口)网站。从他们的源代码来看,他们似乎在反复调用play()
,并提到他们的库可能适合在基于 HTML5 的游戏中使用。
您可以触发多个音频事件 同时,可以使用 用于创建 Javascript 游戏,或 有发言权 背景音乐
【讨论】:
【参考方案9】:这是一个想法。将某一类声音的所有音频加载到一个单独的音频元素中,其中 src 数据是一个连续音频文件中的所有样本(可能希望在两者之间保持一些静音,这样你就可以用更少的超时时间来捕捉和剪切样本出血到下一个样本的风险)。然后,寻找样本并在需要时播放。
如果您需要多个其中之一来播放,您可以创建一个具有相同 src 的附加音频元素,以便将其缓存。现在,您实际上拥有多个“轨道”。您可以将曲目组与您喜欢的资源分配方案(如循环等)一起使用。
您还可以指定其他选项,例如在该资源可用时将声音排入轨道以播放或剪切当前播放的样本。
【讨论】:
【参考方案10】:http://robert.ocallahan.org/2011/11/latency-of-html5-sounds.html
http://people.mozilla.org/~roc/audio-latency-repeating.html
对我来说在 Firefox 和 Chrome 中运行良好。
要停止您开始的声音,请执行 var sound = document.getElementById("shot").cloneNode(true); 声音.play(); 然后 sound.pause();
【讨论】:
【参考方案11】:我建议使用SoundJS,这是我帮助开发的一个库。它允许您编写一个适用于任何地方的单一代码库,SoundJS 可以根据需要选择网络音频、html 音频或 flash 音频。
它会让你做你想做的所有事情:
播放和混合多种声音, 多次播放同一个样本,可能会重叠播放 在任何时候中断样本的播放 播放包含(取决于浏览器支持)的 WAV 文件希望对您有所帮助。
【讨论】:
那个库非常棒,我真的很喜欢 sfx 生成器。是时候做一个网页了 Saiko download for anyone interested【参考方案12】:不可能使用单个 <audio>
元素进行多镜头播放。为此,您需要使用多个元素。
【讨论】:
【参考方案13】:我在编写音乐盒卡生成器时遇到了这个问题。从不同的库开始,但每次都以某种方式出现故障。普通音频实现的延迟很糟糕,没有多次播放……最终使用了低延迟库 + 声音管理器:
http://lowlag.alienbill.com/ 和 http://www.schillmania.com/projects/soundmanager2/
您可以在此处查看实现: http://musicbox.grit.it/
我为多浏览器播放生成了 wav + ogg 文件。这款音乐盒播放器可在 ipad、iphone、Nexus、Mac、PC 上响应...适合我。
【讨论】:
这是一款不错的音乐盒应用!你有 git 仓库吗?我可能会和我们的孩子一起使用它!【参考方案14】:我知道这完全是 hack,但我认为我应该添加我不久前放在 github 上的这个示例开源音频库......
https://github.com/run-time/jThump
点击下面的链接后,在主行键上键入以播放布鲁斯即兴演奏(也可以同时键入多个键等)
使用 jThump 库的示例 >> http://davealger.com/apps/jthump/
它的工作原理是使<iframe>
不可见元素加载一个播放声音 onReady() 的页面。
这当然不理想,但您可以仅根据创造力 +1 这个解决方案(事实上它是开源的并且可以在我尝试过的任何浏览器中运行)我希望这至少可以让其他人搜索一些想法。
:)
【讨论】:
为什么有人反对这个?对我来说似乎是一个可行的选择。 这是一个非常 hacky 的解决方案,但我已经使用这个库制作了一个示例 html 5 画布游戏,所以它仍然可以工作......使用这个库的游戏就在这里 -- beepbox.net 【参考方案15】:您可以随时尝试AudioContext
,它的支持有限,但它是web audio api 工作草案的一部分。如果你打算在未来发布一些东西,这可能是值得的。如果你只为 chrome 和 Firefox 编程,那你就是金子。
【讨论】:
【参考方案16】:var AudioContextFunc = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContextFunc();
var player=new WebAudioFontPlayer();
var instrumVox,instrumApplause;
var drumClap,drumLowTom,drumHighTom,drumSnare,drumKick,drumCrash;
loadDrum(21,function(s)drumClap=s;);
loadDrum(30,function(s)drumLowTom=s;);
loadDrum(50,function(s)drumHighTom=s;);
loadDrum(15,function(s)drumSnare=s;);
loadDrum(5,function(s)drumKick=s;);
loadDrum(70,function(s)drumCrash=s;);
loadInstrument(594,function(s)instrumVox=s;);
loadInstrument(1367,function(s)instrumApplause=s;);
function loadDrum(n,callback)
var info=player.loader.drumInfo(n);
player.loader.startLoad(audioContext, info.url, info.variable);
player.loader.waitLoad(function () callback(window[info.variable]));
function loadInstrument(n,callback)
var info=player.loader.instrumentInfo(n);
player.loader.startLoad(audioContext, info.url, info.variable);
player.loader.waitLoad(function () callback(window[info.variable]));
function uhoh()
var when=audioContext.currentTime;
var b=0.1;
player.queueWaveTable(audioContext, audioContext.destination, instrumVox, when+b*0, 60, b*1);
player.queueWaveTable(audioContext, audioContext.destination, instrumVox, when+b*3, 56, b*4);
function applause()
player.queueWaveTable(audioContext, audioContext.destination, instrumApplause, audioContext.currentTime, 54, 3);
function badumtss()
var when=audioContext.currentTime;
var b=0.11;
player.queueWaveTable(audioContext, audioContext.destination, drumSnare, when+b*0, drumSnare.zones[0].keyRangeLow, 3.5);
player.queueWaveTable(audioContext, audioContext.destination, drumLowTom, when+b*0, drumLowTom.zones[0].keyRangeLow, 3.5);
player.queueWaveTable(audioContext, audioContext.destination, drumSnare, when+b*1, drumSnare.zones[0].keyRangeLow, 3.5);
player.queueWaveTable(audioContext, audioContext.destination, drumHighTom, when+b*1, drumHighTom.zones[0].keyRangeLow, 3.5);
player.queueWaveTable(audioContext, audioContext.destination, drumSnare, when+b*3, drumSnare.zones[0].keyRangeLow, 3.5);
player.queueWaveTable(audioContext, audioContext.destination, drumKick, when+b*3, drumKick.zones[0].keyRangeLow, 3.5);
player.queueWaveTable(audioContext, audioContext.destination, drumCrash, when+b*3, drumCrash.zones[0].keyRangeLow, 3.5);
<script src='https://surikov.github.io/webaudiofont/npm/dist/WebAudioFontPlayer.js'></script>
<button onclick='badumtss();'>badumtss</button>
<button onclick='uhoh();'>uhoh</button>
<button onclick='applause();'>applause</button>
<br/><a href='https://github.com/surikov/webaudiofont'>More sounds</a>
【讨论】:
【参考方案17】:Web Audio API 是适合这项工作的工具。加载声音文件和播放它涉及的工作很少。幸运的是,有很多图书馆可以简化这项工作。由于对声音感兴趣,我还创建了一个名为 musquito 的库,您也可以查看它。
目前它只支持淡入淡出的音效,我正在研究其他的东西,比如 3D 空间化。
【讨论】:
【参考方案18】:所选答案适用于除 IE 之外的所有应用程序。我写了一个tutorial 来说明如何让它跨浏览器工作。这是我写的函数:
function playSomeSounds(soundPath)
var trident = !!navigator.userAgent.match(/Trident\/7.0/);
var net = !!navigator.userAgent.match(/.NET4.0E/);
var IE11 = trident && net
var IEold = (navigator.userAgent.match(/MSIE/i) ? true : false);
if (IE11 || IEold)
document.all.sound.src = soundPath;
else
var snd = new Audio(soundPath); // buffers automatically when created
snd.play();
您还需要在 html 页面中添加以下标签:
<bgsound id="sound">
最后你可以调用函数并简单地通过这里的路径:
playSomeSounds("sounds/welcome.wav");
【讨论】:
以上是关于JavaScript / HTML5 中的音效的主要内容,如果未能解决你的问题,请参考以下文章