首頁

2013年6月16日 星期日

***用HTML5 >>>**Audio API开发游戏音乐***~

***用HTML5 >>>
**Audio API开发游戏音乐***~

*介绍
        音频在很大程度
上使得多媒体体验非
常引人注目。如果你
曾经尝试在关闭声音
的情况下看电影,
你就很可能已经注意到 了这一点。
        游戏也不例外!我最喜爱的视频游戏
的回忆里包含了音乐和声效。在二十年后
的今天,大多情况下,当玩我最爱的游戏时
,我仍然不能把“塞尔达”里近藤浩二
乐曲马特大气的暗黑配乐从我的头脑里
驱逐掉。这同样适用于音效,例如魔兽里
单位实时点击的响应,以及任天堂的
经典例子。
        游戏的音频提出了一些有趣的挑战。要创建令人着迷的游戏音乐,设计人员需要调节潜在的不可预知的状态。实际上,部分游戏能持续未知的时间长度,声音可以与环境互动,并以复杂的方式混合起来,例如室内效果和相对声音定位。最后,可能有大量的一次播放声效,这需要不错的混合效果和在渲染时没有性能损失。

网页上的游戏音频

        简单的游戏使用<audio>标签可能就足
够了。然而,许多浏览器提供的简陋实现
导致音频毛刺和高延迟的出现。这可能只
是暂时性的问题,因为厂商们都在努力改
进各自的实现。要了解<audio>标签的支持
情况,我们可以使用areweplayingyet.org
提供的优秀测试工具。
        一旦深入<audio>标签规范,就会清楚了
解到有很多事情根本不能用它实现。这并不
奇怪,因为它主要被设计来支持多媒体播放。
这些限制包括:
  • 无法为声音信号使用滤波器
  • 无法访问原始的PCM(宇捷:即WAV)数据
  • 没有来源和听众位置、方向的概念
  • 没有细粒度的计时

        在下文中,我将深入介绍一些用
WebAudio API编写游戏音频方面的内容。
入门教程里可以了解到此API的简单介绍。

背景音乐

        游戏里往往有循环播放的背景音乐。
例如,一个背景音轨如下:

        如果你的循环音乐很短并且已知,会
相当的烦人。当玩家被困在一个区域或者
关卡上,会同时连续播放相同的背景音乐
,我们可能需要逐渐淡出来防止让玩家厌烦
。另一种策略是,根据游戏中的上下文,
把不同的音效强度通过逐渐的淡入淡出
混合起来。
        如果你的玩家在一个史诗般的BOSS
关卡里,可能需要对几个不同的情绪范围
进行混音,例如从艺术氛围到有心理暗示的
氛围再到激烈的氛围。音乐合成软件通常
允许你通过选择音轨集合来导出几种混音
(它们具有同样长度)。这样音轨之间就
有某种内部一致性,避免出现从一个音轨
切换到另一个时出现不和谐的转换过渡。

然后,利用WebAudio API,你可以使用
某些类例如BufferLoader通过XHR导入所有这
些音效样本(这在介绍网络音频API的文章
中进行了深入介绍)。加载音效需要时间,
所以这些在游戏中使用的音效在每一关开始
时,应该在页面加载时同时载入,或者在播
放器播放时增量加载。
        接下来,你需要为每个节点创建一个源,
并为每个源创建一个增益节点,连接图如下:
完成之后,你可以在一个循环中同时
回放这些音效源,因为它们都具有相同的
长度,WebAudio API将保证它们保持一致。
由于最后的BOSS战时音效风格会变得相近
或更不同,游戏可以使用类似于下面的增量
算法来改变链中各节点对应的增益值:
  1. // Assume gains is an array of AudioGain
  2. Node, normVal is the intensity  
  3. // between 0 and 1.  
  4. var value = normVal * (gains.length - 1);  
  5. // First reset gains on all nodes.  
  6. for (var i = 0; i < gains.length; i++) {  
  7.   gains[i].gain.value = 0;  
  8. }  
  9. // Decide which two nodes we are currently 
  10.  between, and do an equal  
  11. // power crossfade between them.  
  12. var leftNode = Math.floor(value);  
  13. // Normalize the value between 0 and 1.  
  14. var x = value - leftNode;  
  15. var gain1 = Math.cos(x * 0.5*Math.PI);  
  16. var gain2 = Math.cos((1.0 - x) * 0.5*Math.PI);  
  17. // Set the two gains accordingly.  
  18. gains[leftNode].gain.value = gain1;  
  19. // Check to make sure that there's 
  20. a right node.  
  21. if (leftNode < gains.length - 1) {  
  22.   // If there is, adjust its gain.  
  23.   gains[leftNode + 1].gain.value = gain2;  
  24. }  
        在上述方法中,有两个音效源同时播放
,我们使用同等功率的曲线(如介绍所述)
从它们之间淡入淡出。下面的示例使用了这
一策略,演示的背景音乐在魔
兽争霸2的主题上逐渐增强:



缺少的环节:Web Audio的Audio标签

        现在许多游戏开发商为背景音乐使用
<audio>标签,因为它非常适合流媒体内容。
现在你可以通过<audio>标签把内容带入网络
音频的上下文。
        <audio>标签支持流媒体相当有用,
因为它可以让你立即播放背景音乐,而无
须等待下载所有内容。在网络音频API支持
音频流之后,你可以操作或分析它们。
下面的例子为通过<audio>标签播放的音乐
使用了一个低通滤波器:
  1. var audioElement = document.query
  2. Selector('audio');  
  3. var mediaSourceNode = context.create
  4. MediaElementSource(audioElement);  
  5. // Create the filter  
  6. var filter = context.createBiquadFilter();  
  7. // Create the audio graph.  
  8. mediaSourceNode.connect(filter);  
  9. filter.connect(context.destination);  
        关于<audio>标签和网络音频API整合
更多的讨论,可以看看这篇短文

音效

        游戏经常在响应用户输入或者游戏状态
改变时播放声音效果。但是像背景音乐一样
,音效可以很快的让用户厌倦。 为了避免这
种情况,最好有一个音效池放置相似但是不
同的音效。 这可以从轻微变化到急剧变化
间通过固定长度来过渡,像魔兽系列里点击
各单位的时候。
        游戏音效的另外一个关键点是可以同
时有多个。想象一下,你与多个演员拍摄
枪战时。每个机枪每秒触发多次,造成几
十个音效同时播放。从多个源同时播放音效
,还要对音效源精确计时,是网络音频API
真正的亮点。
        下面的例子演示了由多个单独子弹样
本组成的机枪,其创建了多个播放时间
错开的声源。
  1. var time = context.currentTime;  
  2. for (var i = 0; i < rounds; i++) {  
  3.   var source = this.makeSource
  4. (this.buffers[M4A1]);  
  5.   source.noteOn(time + i * interval);  
  6. }  
        下面是这个代码的效果:

如果你觉得声音太响了,我感到抱歉。
我们将在后面的章节讨论测量和动态压缩。
        现在,如果你游戏里所有的机枪都像
这样响起,那将相当无聊。当然,它们会基
于目标的距离和相对位置而有所差异
(稍后讨论),但即使这样做可能还不够。
幸运的是,网络音频API提供了对上面的
示例进行轻松调整的方式,主要有两种:
1.    发射子弹时间上微妙的变化
2.    改变每个音效的播放速率
(同时改变音高),以更好地模拟现实
世界中的随机性。
        这两种方法的效果如下:

        对于这些技术在现实生活中的实际例子
,可以看看台球桌的演示 ,它采用了随机抽
样和变化的播放速率来表现更有趣的球
的碰撞声。

3D定位音效

        游戏往往设定在一个2D或者3D的世界
里。在这样的情况下,立体定位的音频可以
大大增加沉浸感的体验。幸运的是,网络音
频API带来了内置硬件加速的位置音频特性,
可以直接的使用。 顺便说一下,你应该确保
有立体声扬声器(最好是耳机)来运行下面
的例子。 在下面的示例中,你可以通过在画
布上滚动鼠标滚轮来更改声源的角度。

源代码
        上面的例子中,有一个监听者在画
布正中(人的图标),同时鼠标控制声源
(喇叭图标)的位置,这是使用
AudioPannerNode实现这种效果的简单
例子。它的基本思想是通过设置音频信号
源的位置响应鼠标的移动,如下所示:
  1. PositionSample.prototype.changePosition 
  2.  = function(position) {  
  3.   // Position coordinates are in normalized 
  4.  canvas coordinates  
  5.   // with -0.5 < x, y < 0.5  
  6.   if (position) {  
  7.     if (!this.isPlaying) {  
  8.       this.play();  
  9.     }  
  10.     var mul = 2;  
  11.     var x = position.x / this.size.width;  
  12.     var y = -position.y / this.size.height;  
  13.     this.panner.setPosition(x * mul, y * 
  14.  mul, -0.5);  
  15.   } else {  
  16.     this.stop();  
  17.   }  
  18. };  
        关于网络音频空间化处理需要
了解的事情:
  • 监听者默认在原点(0,0,0)。
  • 网络音频位置API没有单位,所以我引
  • 入了一个乘数使得演示的声效更好。
  • 网络音频采用Y-型直角坐标系(和大多
  • 数计算机图形系统相反)。 这就是为什
  • 么我在上面的代码片段进行了y轴的变换。

高级:音锥 

        定位模型非常强大,而且相当先进,
主要基于OpenAL。详细信息请查看上述
规范的第3和第4节。

在有单一的AudioListener连接到网络
音频API的情况下,它可以通过位置和方向
配置空间。每个源可以通过一个Audio
PannerNode(音频声像节点)来使得音频
输入空间化。声像节点有位置和方向,以
及距离和方向性模型。
        距离模型指定的增益取决于和源的接近
程度,而方向模型可以通过指定内外锥来
配置,以决定监听者在内部锥里,在内外
锥之间,或在外部锥之外时增益的大小
(通常为负值)。
  1. var panner = context.createPanner();  
  2. panner.coneOuterGain = 0.5;  
  3. panner.coneOuterAngle = 180;  
  4. panner.coneInnerAngle = 0;  
        虽然我的例子在2D空间,但是这种模
式很容易推广到三维。例如3D声音空间化
的例子可以看看这个位置演示。另外对于
位置来说,网络音频模型也可以选择多普勒
频移的速度。这个例子展示了多普勒效应
的详细信息。
        关于这一主题的更多信息,可以阅读
混合定位音频和WebGL的详细教程 。

室内效果和滤波器

        在现实中,声音被感觉的方式很大程度
上取决于声音所在的房间。相同吱吱作响的
门在地下室与大型的开放式大厅里相比会
发出相当不同的声音。高产值的游戏将会
模仿这些影响,因为为每个环境创建一套
独立的音效是相当昂贵的,并且会产生相
当多的材料和大量的游戏数据。
        严格地说,描述原始声音和现实中所听
到之间不同的音频术语是脉冲响应。这些脉
冲响应可以被精心录制,其实也有网站为了
方便你的使用存放了许多这种预先录制的
脉冲响应文件(作为音频方式存储)。
        对于如何从一个给定的环境创建脉冲
响应的更多信息,可以通读网络音频API规
范卷积部分的“录音设置”一节。
        更重要的是针对我们的目标,网络音频
API提供了一个简单的方法来在我们的声音
里应用脉冲响应,
即通过使用ConvolverNode的方式。
  1. // Make a source node for the sample.  
  2. var source = context.createBufferSource();  
  3. source.buffer = this.buffer;  
  4. // Make a convolver node for the impulse 
  5.  response.  
  6. var convolver = context.createConvolver();  
  7. convolver.buffer = this.impulse
  8. ResponseBuffer;  
  9. // Connect the graph.  
  10. source.connect(convolver);  
  11. convolver.connect(context.destination);  
        下面的示例展示了一些不同脉冲响应
下的军事演讲:




        还可以看看网络音频API规范页面上的
房间效果演示,以及这个让你控制通过一
个伟大的爵士标准混合干(原料)和湿
(通过卷积处理)的例子。

最后的倒计时

        现在你已经创建了一个游戏,添加了
位置音频,而且现在在你的图里有大量的
同时播放的AudioNodes。 太棒了,但是还有
一件事要考虑:
        由于多种声音互相叠加起来播放,你
可能会发现在某种情况下,声音超过了扬声
器的最大承受能力。就像图像超出了画布
边界的情况一样,声音也会在波形超过最
大阈值时进行削波,导致明显的失真。波形
看起来会像下面这样:
        这里有一个真实削波的例子。波形
看起来相当糟糕:
        听起来也很糟糕:

        听到像上面这样严重扭曲的音乐是很
严重的事,或者与此相反,过分的混合会
迫使听众调大音量。如果你现在有这种
情况,你真的需要立即解决它!

检测削波

        从技术角度看,削波发生在任何一个
通道的信号值超出有效范围即-1和1之间时
。一旦检测到削波反生时,视觉反馈会非常
有用。要可靠的实现这点,可以把
JavaScriptAudioNode放到你的图里。
音频图将会按如下进行设置:
  1. // Assume entire sound output is being 
  2. piped through the mix node.  
  3. var meter = context.createJavaScript
  4. Node(2048, 1, 1);  
  5. meter.onaudioprocess = processAudio;  
  6. mix.connect(meter);  
  7. meter.connect(context.destination);  
        同时通过下面的processAudio方法可以 
检测到削波:
  1. function processAudio(e) {  
  2.   var buffer = e.inputBuffer.getChannelData(0);  
  3.    
  4.   var isClipping = false;  
  5.   // Iterate through buffer to check if any 
  6.  of the |values| exceeds 1.  
  7.   for (var i = 0; i < buffer.length; i++) {  
  8.     var absValue = Math.abs(buffer[i]);  
  9.     if (absValue >= 1) {  
  10.       isClipping = true;  
  11.       break;  
  12.     }  
  13.   }  
  14. }  
        在通常情况下要小心,因为性能方面
的原因,不要过度的使用  
JavaScriptAudioNode。 在这种情况下,
一种替代的方法是为getByteFrequencyData在音
频图里加入RealtimeAnalyserNode,在渲染时通过requestAnimationFrame来检测。这个方法更有效
,但会错过多数信号(包括有可能削波的地方)
,因为渲染最多发生60次,而音频信号的变化
更为迅速。
        因为削波的检测非常重要,未来我们很
可能将看到网络音频API节点内置MeterNode

防止削波

        通过调整主要AudioGainNode的增益,你
可以控制混音的水平来防止削波。 然而在实
践中,因为你游戏中所播放的声音可能取
决于大量因素,所以决定主增益值来防止
所有情况下的削波是相当困难的。在通常
情况下,你应该调整增益来预期最坏的
情况,但这是一门艺术,而不是科学。
        要知道这是具体如何实现的,下面是一
个示例,在此你可以调整主增益。如果增益
设置过高,会导致声音削波。监视器会变成
红色来给出削波的视觉反馈。下面的音响生
态环境是Disco Dan的混音作品,原曲
是由Yasunori Mitsuda所做的伟大的
“超时空之轮”。
  
新增說明文字






加一点糖

        音乐和游戏制作中经常使用效果器来平
滑信号和控制尖峰。此功能在网络音频
世界里可以通过DynamicsCompressorNode 来
实现,可以在你的音频图加入一个更响亮,
更丰富,更饱满的音色,这也有利于削波。
直接引用规范里的话,这个节点
“...降低了信号最响亮部分的体积,并提升
了最柔软部分的音量... 尤其重要的是在游戏
和音乐应用里,当大量独立的声音播放时,
控制信号整体水平,并有助于避免削波。”
        使用动态压缩通常来说是一个好主意,
尤其是在游戏的设置里,正如前面所讨论的
一样,你并不知道到底此时什么声音将会
何时播放。DinahMoe实验室的Plink是很好
的例子,因为声音的回放完全取决于你和
其他参与者。效果器在大多数情况下是有
用的,除了一些罕见的情况外,而这种情况
下你可以使用已经精心调整过,并且听起来
“恰到好处”的曲目。
        它的实现是一件简单的事情,只需要在
你的音频图里把DynamicsCompressorNode作
为目标前的最
后一个节点添加进去。
  1. // Assume the output is all going through 
  2. the mix node.  
  3. var compressor = context.createDynamics
  4. Compressor();  
  5. mix.connect(compressor);  
  6. compressor.connect(context.destination);  
        关于动态压缩的更多细节,Wikipedia上
的这篇文章非常翔实。
        总结一下,仔细检查削波,通过插入
主增益节点来防止它的出现。然后使用动态
效果器节点来收紧整个混音。你的音频图
可能看起来像这样:

结论

        以上内容涵盖了我认为使用网络音频
API来开发游戏音乐最重要的方面。有了
这些技术,可以在你的浏览器上构建真正
有吸引力的音频体验。在我结束本文之前,
给你一个提示:如果你的浏览器标签使
page visibility API切换到了后台,一定
要让声音暂停,否则你会为用户提供一个
潜在的令人厌烦的体验。
        对于关于网络音频的其他信息,需要
入门的文章进行更多了解。如果你有问题
,看看它是否已经在网络音频FAQ里得到
解答。最后,如果你有其他问题,
可以在Stack Overflow上的web-audio
标签下提问。
        在本文结束前,让我为你展示网络音
频API现在在实际游戏里的用途:

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&7

沒有留言:

張貼留言


if you like make fds, wellcome you here~~anytime***

my free place for everyones who want the good software,

come & download them~ wellcome!!