*介绍
音频在很大程度
上使得多媒体体验非
常引人注目。如果你
曾经尝试在关闭声音
的情况下看电影,
你就很可能已经注意到 了这一点。
游戏也不例外!我最喜爱的视频游戏
的回忆里包含了音乐和声效。在二十年后
的今天,大多情况下,当玩我最爱的游戏时
,我仍然不能把“塞尔达”里近藤浩二的
乐曲和马特大气的暗黑配乐从我的头脑里
驱逐掉。这同样适用于音效,例如魔兽里
单位实时点击的响应,以及任天堂的
经典例子。
游戏的音频提出了一些有趣的挑战。要创建令人着迷的游戏音乐,设计人员需要调节潜在的不可预知的状态。实际上,部分游戏能持续未知的时间长度,声音可以与环境互动,并以复杂的方式混合起来,例如室内效果和相对声音定位。最后,可能有大量的一次播放声效,这需要不错的混合效果和在渲染时没有性能损失。
网页上的游戏音频
简单的游戏使用<audio>
标签可能就足 够了。然而,许多浏览器提供的简陋实现
导致音频毛刺和高延迟的出现。这可能只
是暂时性的问题,因为厂商们都在努力改
进各自的实现。要了解
<audio>
标签的支持 情况,我们可以使用areweplayingyet.org所
提供的优秀测试工具。
一旦深入
<audio>
标签规范,就会清楚了 解到有很多事情根本不能用它实现。这并不
奇怪,因为它主要被设计来支持多媒体播放。
这些限制包括:
- 无法为声音信号使用滤波器
- 无法访问原始的PCM(宇捷:即WAV)数据
- 没有来源和听众位置、方向的概念
- 没有细粒度的计时
在下文中,我将深入介绍一些用
WebAudio API编写游戏音频方面的内容。
在入门教程里可以了解到此API的简单介绍。
背景音乐
游戏里往往有循环播放的背景音乐。例如,一个背景音轨如下:
如果你的循环音乐很短并且已知,会
相当的烦人。当玩家被困在一个区域或者
关卡上,会同时连续播放相同的背景音乐
,我们可能需要逐渐淡出来防止让玩家厌烦
。另一种策略是,根据游戏中的上下文,
把不同的音效强度通过逐渐的淡入淡出
混合起来。
如果你的玩家在一个史诗般的BOSS
关卡里,可能需要对几个不同的情绪范围
进行混音,例如从艺术氛围到有心理暗示的
氛围再到激烈的氛围。音乐合成软件通常
允许你通过选择音轨集合来导出几种混音
(它们具有同样长度)。这样音轨之间就
有某种内部一致性,避免出现从一个音轨
切换到另一个时出现不和谐的转换过渡。
然后,利用WebAudio API,你可以使用
某些类例如BufferLoader通过XHR导入所有这 些音效样本(这在介绍网络音频API的文章
中进行了深入介绍)。加载音效需要时间,
所以这些在游戏中使用的音效在每一关开始
时,应该在页面加载时同时载入,或者在播
放器播放时增量加载。
接下来,你需要为每个节点创建一个源,
并为每个源创建一个增益节点,连接图如下:
完成之后,你可以在一个循环中同时
回放这些音效源,因为它们都具有相同的 长度,WebAudio API将保证它们保持一致。
由于最后的BOSS战时音效风格会变得相近
或更不同,游戏可以使用类似于下面的增量
算法来改变链中各节点对应的增益值:
- // Assume gains is an array of AudioGain
- Node, normVal is the intensity
- // between 0 and 1.
- var value = normVal * (gains.length - 1);
- // First reset gains on all nodes.
- for (var i = 0; i < gains.length; i++) {
- gains[i].gain.value = 0;
- }
- // Decide which two nodes we are currently
- between, and do an equal
- // power crossfade between them.
- var leftNode = Math.floor(value);
- // Normalize the value between 0 and 1.
- var x = value - leftNode;
- var gain1 = Math.cos(x * 0.5*Math.PI);
- var gain2 = Math.cos((1.0 - x) * 0.5*Math.PI);
- // Set the two gains accordingly.
- gains[leftNode].gain.value = gain1;
- // Check to make sure that there's
- a right node.
- if (leftNode < gains.length - 1) {
- // If there is, adjust its gain.
- gains[leftNode + 1].gain.value = gain2;
- }
,我们使用同等功率的曲线(如介绍所述)
从它们之间淡入淡出。下面的示例使用了这
一策略,演示的背景音乐在魔
兽争霸2的主题上逐渐增强:
缺少的环节:Web Audio的Audio标签
现在许多游戏开发商为背景音乐使用<audio>
标签,因为它非常适合流媒体内容。 现在你可以通过
<audio>
标签把内容带入网络 音频的上下文。
<audio>标签支持流媒体相当有用,
因为它可以让你立即播放背景音乐,而无
须等待下载所有内容。在网络音频API支持
音频流之后,你可以操作或分析它们。
下面的例子为通过
<audio>
标签播放的音乐 使用了一个低通滤波器:
- var audioElement = document.query
- Selector('audio');
- var mediaSourceNode = context.create
- MediaElementSource(audioElement);
- // Create the filter
- var filter = context.createBiquadFilter();
- // Create the audio graph.
- mediaSourceNode.connect(filter);
- filter.connect(context.destination);
<audio>
标签和网络音频API整合 更多的讨论,可以看看这篇短文。
音效
游戏经常在响应用户输入或者游戏状态改变时播放声音效果。但是像背景音乐一样
,音效可以很快的让用户厌倦。 为了避免这
种情况,最好有一个音效池放置相似但是不
同的音效。 这可以从轻微变化到急剧变化
间通过固定长度来过渡,像魔兽系列里点击
各单位的时候。
游戏音效的另外一个关键点是可以同
时有多个。想象一下,你与多个演员拍摄
枪战时。每个机枪每秒触发多次,造成几
十个音效同时播放。从多个源同时播放音效
,还要对音效源精确计时,是网络音频API
真正的亮点。
下面的例子演示了由多个单独子弹样
本组成的机枪,其创建了多个播放时间
错开的声源。
- var time = context.currentTime;
- for (var i = 0; i < rounds; i++) {
- var source = this.makeSource
- (this.buffers[M4A1]);
- source.noteOn(time + i * interval);
- }
如果你觉得声音太响了,我感到抱歉。
我们将在后面的章节讨论测量和动态压缩。
现在,如果你游戏里所有的机枪都像
这样响起,那将相当无聊。当然,它们会基
于目标的距离和相对位置而有所差异
(稍后讨论),但即使这样做可能还不够。
幸运的是,网络音频API提供了对上面的
示例进行轻松调整的方式,主要有两种:
1. 发射子弹时间上微妙的变化
2. 改变每个音效的播放速率
(同时改变音高),以更好地模拟现实
世界中的随机性。
这两种方法的效果如下:对于这些技术在现实生活中的实际例子
,可以看看台球桌的演示 ,它采用了随机抽
样和变化的播放速率来表现更有趣的球
的碰撞声。
3D定位音效
游戏往往设定在一个2D或者3D的世界里。在这样的情况下,立体定位的音频可以
大大增加沉浸感的体验。幸运的是,网络音
频API带来了内置硬件加速的位置音频特性,
可以直接的使用。 顺便说一下,你应该确保
有立体声扬声器(最好是耳机)来运行下面
的例子。 在下面的示例中,你可以通过在画
布上滚动鼠标滚轮来更改声源的角度。
源代码
上面的例子中,有一个监听者在画 布正中(人的图标),同时鼠标控制声源
(喇叭图标)的位置,这是使用
AudioPannerNode实现这种效果的简单
例子。它的基本思想是通过设置音频信号
源的位置响应鼠标的移动,如下所示:
- PositionSample.prototype.changePosition
- = function(position) {
- // Position coordinates are in normalized
- canvas coordinates
- // with -0.5 < x, y < 0.5
- if (position) {
- if (!this.isPlaying) {
- this.play();
- }
- var mul = 2;
- var x = position.x / this.size.width;
- var y = -position.y / this.size.height;
- this.panner.setPosition(x * mul, y *
- mul, -0.5);
- } else {
- this.stop();
- }
- };
了解的事情:
- 监听者默认在原点(0,0,0)。
- 网络音频位置API没有单位,所以我引
- 入了一个乘数使得演示的声效更好。
- 网络音频采用Y-型直角坐标系(和大多
- 数计算机图形系统相反)。 这就是为什
- 么我在上面的代码片段进行了y轴的变换。
高级:音锥
定位模型非常强大,而且相当先进,主要基于OpenAL。详细信息请查看上述
规范的第3和第4节。
在有单一的AudioListener连接到网络
音频API的情况下,它可以通过位置和方向 配置空间。每个源可以通过一个Audio
PannerNode(音频声像节点)来使得音频
输入空间化。声像节点有位置和方向,以
及距离和方向性模型。
距离模型指定的增益取决于和源的接近
程度,而方向模型可以通过指定内外锥来
配置,以决定监听者在内部锥里,在内外
锥之间,或在外部锥之外时增益的大小
(通常为负值)。
- var panner = context.createPanner();
- panner.coneOuterGain = 0.5;
- panner.coneOuterAngle = 180;
- panner.coneInnerAngle = 0;
式很容易推广到三维。例如3D声音空间化
的例子可以看看这个位置演示。另外对于
位置来说,网络音频模型也可以选择多普勒
频移的速度。这个例子展示了多普勒效应
的详细信息。
关于这一主题的更多信息,可以阅读
混合定位音频和WebGL的详细教程 。
室内效果和滤波器
在现实中,声音被感觉的方式很大程度上取决于声音所在的房间。相同吱吱作响的
门在地下室与大型的开放式大厅里相比会
发出相当不同的声音。高产值的游戏将会
模仿这些影响,因为为每个环境创建一套
独立的音效是相当昂贵的,并且会产生相
当多的材料和大量的游戏数据。
严格地说,描述原始声音和现实中所听
到之间不同的音频术语是脉冲响应。这些脉
冲响应可以被精心录制,其实也有网站为了
方便你的使用存放了许多这种预先录制的
脉冲响应文件(作为音频方式存储)。
对于如何从一个给定的环境创建脉冲
响应的更多信息,可以通读网络音频API规
范卷积部分的“录音设置”一节。
更重要的是针对我们的目标,网络音频
API提供了一个简单的方法来在我们的声音
里应用脉冲响应,
即通过使用ConvolverNode的方式。
- // Make a source node for the sample.
- var source = context.createBufferSource();
- source.buffer = this.buffer;
- // Make a convolver node for the impulse
- response.
- var convolver = context.createConvolver();
- convolver.buffer = this.impulse
- ResponseBuffer;
- // Connect the graph.
- source.connect(convolver);
- convolver.connect(context.destination);
下的军事演讲:
还可以看看网络音频API规范页面上的
房间效果演示,以及这个让你控制通过一
个伟大的爵士标准混合干(原料)和湿
(通过卷积处理)的例子。
最后的倒计时
现在你已经创建了一个游戏,添加了位置音频,而且现在在你的图里有大量的
同时播放的AudioNodes。 太棒了,但是还有
一件事要考虑:
由于多种声音互相叠加起来播放,你
可能会发现在某种情况下,声音超过了扬声
器的最大承受能力。就像图像超出了画布
边界的情况一样,声音也会在波形超过最
大阈值时进行削波,导致明显的失真。波形
看起来会像下面这样:
看起来相当糟糕:
听到像上面这样严重扭曲的音乐是很
严重的事,或者与此相反,过分的混合会
迫使听众调大音量。如果你现在有这种
情况,你真的需要立即解决它!
检测削波
从技术角度看,削波发生在任何一个通道的信号值超出有效范围即-1和1之间时
。一旦检测到削波反生时,视觉反馈会非常
有用。要可靠的实现这点,可以把
JavaScriptAudioNode放到你的图里。
音频图将会按如下进行设置:
- // Assume entire sound output is being
- piped through the mix node.
- var meter = context.createJavaScript
- Node(2048, 1, 1);
- meter.onaudioprocess = processAudio;
- mix.connect(meter);
- meter.connect(context.destination);
processAudio
方法可以
检测到
削波:- function processAudio(e) {
- var buffer = e.inputBuffer.getChannelData(0);
- var isClipping = false;
- // Iterate through buffer to check if any
- of the |values| exceeds 1.
- for (var i = 0; i < buffer.length; i++) {
- var absValue = Math.abs(buffer[i]);
- if (absValue >= 1) {
- isClipping = true;
- break;
- }
- }
- }
的原因,不要过度的使用
JavaScriptAudioNode
。 在这种情况下,一种替代的方法是为
getByteFrequencyData
在音 频图里加入
RealtimeAnalyserNode
,在渲染时通过requestAnimationFrame
来检测
。这个方法更有效 ,但会错过多数信号(包括有可能削波的地方)
,因为渲染最多发生60次,而音频信号的变化
更为迅速。
因为削波的检测非常重要,未来我们很
可能将看到网络音频API节点内置
MeterNode
。防止削波
通过调整主要AudioGainNode的增益,你可以控制混音的水平来防止削波。 然而在实
践中,因为你游戏中所播放的声音可能取
决于大量因素,所以决定主增益值来防止
所有情况下的削波是相当困难的。在通常
情况下,你应该调整增益来预期最坏的
情况,但这是一门艺术,而不是科学。
要知道这是具体如何实现的,下面是一
个示例,在此你可以调整主增益。如果增益
设置过高,会导致声音削波。监视器会变成
红色来给出削波的视觉反馈。下面的音响生
态环境是Disco Dan的混音作品,原曲
是由Yasunori Mitsuda所做的伟大的
“超时空之轮”。
新增說明文字 |
加一点糖
音乐和游戏制作中经常使用效果器来平滑信号和控制尖峰。此功能在网络音频
世界里可以通过
DynamicsCompressorNode
来 实现,可以在你的音频图加入一个更响亮,
更丰富,更饱满的音色,这也有利于削波。
直接引用规范里的话,这个节点
“...降低了信号最响亮部分的体积,并提升
了最柔软部分的音量... 尤其重要的是在游戏
和音乐应用里,当大量独立的声音播放时,
控制信号整体水平,并有助于避免削波。”
使用动态压缩通常来说是一个好主意,
尤其是在游戏的设置里,正如前面所讨论的
一样,你并不知道到底此时什么声音将会
何时播放。DinahMoe实验室的Plink是很好
的例子,因为声音的回放完全取决于你和
其他参与者。效果器在大多数情况下是有
用的,除了一些罕见的情况外,而这种情况
下你可以使用已经精心调整过,并且听起来
“恰到好处”的曲目。
它的实现是一件简单的事情,只需要在
你的音频图里把DynamicsCompressorNode作
为目标前的最
后一个节点添加进去。
- // Assume the output is all going through
- the mix node.
- var compressor = context.createDynamics
- Compressor();
- mix.connect(compressor);
- compressor.connect(context.destination);
的这篇文章非常翔实。
总结一下,仔细检查削波,通过插入
主增益节点来防止它的出现。然后使用动态
效果器节点来收紧整个混音。你的音频图
可能看起来像这样:
结论
以上内容涵盖了我认为使用网络音频API来开发游戏音乐最重要的方面。有了
这些技术,可以在你的浏览器上构建真正
有吸引力的音频体验。在我结束本文之前,
给你一个提示:如果你的浏览器标签使
用page visibility API切换到了后台,一定
要让声音暂停,否则你会为用户提供一个
潜在的令人厌烦的体验。
对于关于网络音频的其他信息,需要
在入门的文章进行更多了解。如果你有问题
,看看它是否已经在网络音频FAQ里得到
解答。最后,如果你有其他问题,
可以在Stack Overflow上的web-audio
标签下提问。
在本文结束前,让我为你展示网络音
频API现在在实际游戏里的用途:
- Field Runners,以及有关一些技术
- 细节的文档。
- 愤怒的小鸟,最近换用了网络音频API
- 。到这个文档查看更多信息。
- Skid
- Racer,大量使用了立体音效。 *
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&7
沒有留言:
張貼留言
if you like make fds, wellcome you here~~anytime***
my free place for everyones who want the good software,
come & download them~ wellcome!!