65.9K
CodeProject 正在变化。 阅读更多。
Home

解锁HTML5音频的强大功能

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2012年6月8日

CPOL

9分钟阅读

viewsIcon

22736

本文将介绍在 Web 应用程序中使用 <audio> 标签的一些最佳实践,并包含来自真实网站的实用技巧。

30 天开发一个 Windows 8 应用

声音是我们生活背景的一部分。如今,HTML5 <audio> 元素使 Web 开发人员能够在其应用程序中嵌入声音。控件的灵活性以及与平台其余部分的集成,使得从简单的声音效果、背景音频到游戏体验和更复杂的音频引擎等多种场景成为可能。

这篇博文将介绍在 Web 应用程序中使用 <audio> 标签的一些最佳实践,并包含来自真实网站的实用技巧。

向页面添加音频元素

第一步是在页面中添加音频元素。您可以通过在标记中声明 <audio> 标签,通过在 JavaScript 代码中实例化一个新的音频元素,或者通过在页面中嵌入音频流来实现。

<audio src="audio/sample.mp3" autoplay>

</audio>

运行实时演示

var audio = document.createElement("audio");
if (audio != null && audio.canPlayType && audio.canPlayType("audio/mpeg"))
{
audio.src = "audio/sample.mp3";
audio.play();
}

运行实时演示

<audio src="data:audio/mpeg,ID3%02%00%00%00%00%..." autoplay>

</audio>

运行实时演示

第一种方法允许您在页面加载期间初始化音频组件。第二种方法提供了更大的灵活性和更好的网络流量管理,因为它将音频剪辑的加载推迟到应用程序生命周期中的特定时间。第三种方法(不太推荐)是将音频文件作为 data-uri 嵌入到页面中,从而减少到服务器的请求次数。

请注意,即使您通过 JavaScript 生成的音频元素尚未实际添加到 DOM 树中(如上面的代码片段所示),您也可以播放它。但是,将音频元素添加到页面将允许您显示默认的控件栏

虽然本文未涵盖,但您可以支持多种音频文件格式。另外,如果您在服务器上托管音频文件,请记住在服务器端为 mp3 文件(“audio/mpeg”)注册 MIME 类型。例如,这是 Internet Information Services (IIS) 上的设置。

在播放前预加载音频

一旦有了音频元素,您就可以选择最佳的预加载策略。HTML5 <audio> 规范描述了一个preload 属性,该属性有三种可能的值:

  • “none”:提示用户代理作者不期望用户需要媒体资源,或者服务器希望最大限度地减少不必要的流量。
    如果您的场景是一个带有每个帖子音频文件的播客博客,那么此选项特别有效,因为它会减少初始预加载带宽。一旦用户播放文件(无论是通过默认的视觉控件还是 JavaScript 方法load()play()),浏览器将开始获取音频流。
  • “metadata”:提示用户代理作者不期望用户需要媒体资源,但获取资源元数据(尺寸、持续时间等)是合理的。
    如果您正在构建一个音频播放器控件并且需要有关音频剪辑的基本信息,但尚不需要播放它,则推荐此选项。
  • “auto”:提示用户代理用户代理可以优先考虑用户的需求,而不会对服务器造成风险,包括乐观地下载整个资源。
    如果您正在构建一个游戏,这种方法可能是最好的,因为它允许您在实际开始游戏体验之前预加载所有音频剪辑。

请注意,当您以编程方式设置音频元素的src 属性时,浏览器会将preload 属性设置为“auto”(除非另有说明)。因此,如果您的场景需要不同的值,请确保在设置src 之前的代码行中指定它。

您可以通过运行此页面并使用 F12 开发者工具(网络选项卡)来预览这三个选项对网络的影响。为了调试,您可以模拟新的调用并通过选中“始终从服务器刷新”菜单来禁用本地缓存。

preload=none

preload=metadata

preload=auto

虽然此属性对于初始化阶段非常有用,但您可能还需要知道浏览器何时实际下载了音频剪辑并且已准备好播放。您可以通过监听“canplaythrough”事件来获取此信息;一旦用户代理估计如果现在开始播放,媒体资源可以以当前播放速率一直渲染到结束而无需停止进一步缓冲,就会调用此事件。

var audio = document.createElement("audio");
audio.src = "audio/sample.mp3";
audio.addEventListener("canplaythrough", function () {
alert('The file is loaded and ready to play!');
}, false);

运行实时演示

循环

音频场景中另一个常见的请求是循环播放音频剪辑的能力。使用 HTML5 <audio>,您可以使用“loop”属性来实现此目的;此设置将无限循环您的剪辑,直到用户或应用程序激活pause() 音频控件。

<audio src="audio/sample.mp3" autoplay loop>

</audio>

运行实时演示

另一种循环播放音频文件的方法是在音频剪辑结束时以编程方式调用play() 方法;这样做最终可以让您管理每次循环之间的延迟。

var audio = document.createElement("audio");
audio.src = "piano/3C.mp3";
audio.addEventListener('ended', function () {
// Wait 500 milliseconds before next loop
setTimeout(function () { audio.play(); }, 500);
}, false);
audio.play();

运行实时演示

请注意,在声音实际结束之前对音频元素执行的任何play() 调用都不会生效。如果您想“取消并重新开始”当前声音,则需要重置currentTime

var audio = null;
audio = document.createElement("audio");
audio.src = "piano/3C.mp3";
audio.addEventListener('ended', function () {
audio.play();
}, false);
function play() {
audio.play();
}
function restart() {
audio.currentTime = 0;
audio.play();
}

运行实时演示

多个音频标签

如果您的场景需要同时多次播放相同的音频文件(即,带有声音重叠),则可以通过创建指向同一文件的多个音频标签来实现此结果。显然,如果您同时使用不同的音频文件,相同的方法也适用。正如我们在此帖子前面解释的,您可以以编程方式添加它们,也可以在标记中实例化它们。

以下代码片段展示了如何使用标记加载和播放多个音频文件。音频样本的长度相同;执行结束时,它们将从开头开始循环。当您在 Internet Explorer 9 中播放它们时,您可以注意到它们在各种循环中自动同步。您会注意到这 5 种声音的组合播放起来就像之前演示中使用的音频文件(“sample.mp3”)。

<body>
<audio src="audio/Bass.mp3" autoplay loop>
</audio>
<audio src="audio/Drum.mp3" autoplay loop>
</audio>
<audio src="audio/Crunch.mp3" autoplay loop>
</audio>
<audio src="audio/Guitar.mp3" autoplay loop>
</audio>
<audio src="audio/Pizzicato.mp3" autoplay loop>
</audio>
</body>

运行实时演示

虽然这种方法非常简单明了,但在大多数情况下,开发人员更喜欢以编程方式创建音频剪辑。以下代码片段展示了如何使用代码动态添加 3 个音频剪辑。当您将它们一起播放时,您将获得 C 大调和弦!

AddNote("3C");
AddNote("3E");
AddNote("3G");
function AddNote(name) {
var audio = document.createElement("audio");
audio.src = "piano/" + name + ".mp3";
audio.autoplay = true;
}

运行实时演示

这种代码模式适用于任何浏览器,并且可以让您构建非常引人注目的场景!

需要牢记的是,随着您的应用程序或游戏变得更加复杂,您最终可能会达到两个限制:可以在同一页面上预加载的音频元素数量以及可以同时播放的音频元素数量。

这些数字取决于浏览器和您 PC 的功能。根据我的经验,Internet Explorer 9 可以同时处理数十个并发音频元素而没有任何问题。其他浏览器表现不佳 - 当您循环播放多个文件时,您可能会遇到明显的延迟和失真

同步策略

根据网络的特性,您应始终考虑添加标签、获取内容并准备播放之间涉及的延迟。特别是,当您处理多个文件时,每个文件可能准备播放的时间早晚不同。例如,这里有一个使用前面提到的 3 个文件从本地主机加载的捕获。

如您在Timings 列中所示,不同的文件可能在不同时间准备就绪。

一种非常常见的同步策略是首先预加载所有文件。一旦它们都准备就绪,您就可以快速遍历一个循环并开始播放它们。

var audios = [];
var loading = 0;
AddNote("2C");
AddNote("2E");
AddNote("2G");
AddNote("3C");
AddNote("3E");
AddNote("3G");
AddNote("4C");
function AddNote(name) {
loading++;
var audio = document.createElement("audio");
audio.loop = true;
audio.addEventListener("canplaythrough", function () {
loading--;
if (loading == 0) // All files are preloaded
StartPlayingAll();
}, false);
audio.src = "piano/" + name + ".mp3";
audios.push(audio);
}
function StartPlayingAll() {
for (var i = 0; i < audios.length; i++)
audios[i].play();
}

运行实时演示

现在让我们把一切都整合起来!下面的演示模拟了一个弹奏《雅克弟兄》(也称为 Brother John、Brother Peter…或 Fra Martino)的钢琴。页面开始获取所有音符,显示它们在客户端预加载时的进度。一旦它们都准备就绪,歌曲就开始并持续循环播放。

运行实时演示

真实网站中的音频

现在我们已经了解了处理多个音频文件的常见模式,我想重点介绍一些网站作为 <audio> 标签最佳实践用法的示例。

Pirates Love Daises: www.pirateslovedaisies.com

另一篇博文中,我谈到了 Pirates Love Daises,这是 Grant Skinner 开发的一款出色的 HTML5 游戏。除了出色的游戏玩法和引人注目的视觉效果外,Grant 的团队还开发了一个复杂的音频库,可在游戏中播放多个音频样本。主要逻辑封装在AudioManager类中。如前所述,在实际开始游戏之前,该网站会预加载所有音频剪辑并在初始加载屏幕中显示累积进度。该网站还考虑了下载音频文件时发生网络超时或错误的场景。

addAudioChannel:function(b,a,f){
var h=document.createElement("audio");
if(f!=true){
this.currAsset=h;
this.timeoutId=setTimeout($.proxy(this,"handleAudioTimeout"),e.AUDIO_TIMEOUT);
h.addEventListener("canplaythrough",$.proxy(this,"handleAudioComplete"),false);
h.addEventListener("error",$.proxy(this,"handleAudioError"),false)
}
h.setAttribute("id",a);
h.setAttribute("preload","auto");
$("<source>").attr("src",b).appendTo(h);
$("<source>").attr("src",b.split(".mp3")[0]+".ogg").appendTo(h);
document.body.appendChild(h)
}
,handleAudioComplete:function(b){
if(LoadedAssets.getAsset(b.target.id)!=true){
LoadedAssets.addAsset(b.target.id,true);
clearTimeout(this.timeoutId);
this.calculatePercentLoaded(true)
}
}
,handleAudioError:function(b){
trace("Error Loading Audio:",b.target.id);
LoadedAssets.addAsset(b.target.id,true);
clearTimeout(this.timeoutId);
this.calculatePercentLoaded(true)
}
,handleAudioTimeout:function(){
trace("Audio Timed Out:",this.currAsset.id);
LoadedAssets.addAsset(this.currAsset.id,true);
this.calculatePercentLoaded(true)
}

运行实时演示

Grant 目前正在开发一个声音库项目,该项目将允许开发人员将其声音引擎逻辑用于任何其他应用程序。期待!

Firework (by Mike Tompkins): www.beautyoftheweb.com/firework

Firework 演示尤其有趣,因为它允许您同时与多个音轨交互,动态更改每个音轨的音量。此外,当您与音频通道交互时,界面会动态响应不同的输入或设置。

这次音频标签是在 HTML 标记中声明的(只有 6 个音轨)。通过监听canplaythrough事件以编程方式跟踪进度。一旦所有音频文件都准备好播放,一个循环就会遍历列表并开始播放。

video.addEventListener('canplaythrough', onCanPlayAudio, false);
for (var i = 0; i < 5; i++) {
var aud = document.getElementById("aud" + i); 
targetVolumes.push(0);
aud.volume = 0;
audioTags.push({
"tag": aud,
"ready": false
});
aud.addEventListener('canplaythrough', onCanPlayAudio, false);
}
// Set audio/video tracks
document.getElementById("tompkins").src = MediaHelper.GetVideoUrl("Firework_3");
for (var i = 0; i < audioTracks.length; i++) {
document.getElementById("aud" + i).src = MediaHelper.GetAudioUrl(audioTracks[i]);
}

运行实时演示

开发人员在此情况下还决定将音量初始设置为 0,并在体验准备好播放后动态将其增加到 1。根据您的声卡和驱动程序的质量,这个小技巧可以减少听到音频开始时的初始“咔哒”声的可能性。

BeatKeep: www.beatkeep.net

最后一个场景可能是此处示例中最复杂的。在此示例中,您可以使用鼓机构建自己的歌曲,并循环播放多个音频剪辑。在此应用程序中,完美同步音频通道和敏捷的缓冲系统以加载多个剪辑至关重要。

鼓机让您完全控制节奏和拍号;通过使用复杂的计时器逻辑和绑定模型,最终结果是一个非常流畅的体验!

结论

我鼓励您使用 Internet Explorer 9 或其他浏览器尝试本文中的所有示例和应用程序,并告诉我们您的体验!您可以在此处下载本文使用的所有示例代码。

如果您想了解更多关于音频和视频控件的信息,我建议您观看“您需要了解的 5 件事才能立即开始使用 <audio> 和 <video>”或阅读 MSDN 上的这些相关文章

感谢DoubleDominant 提供本文使用的音频剪辑,以及感谢Grant SkinnerArchetype 带来的出色的 HTML5 体验。

© . All rights reserved.