游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

iPhone游戏引擎编写之音频和性能(3)

发布时间:2012-02-28 14:09:06 Tags:,,,,

作者:Simon Yeung

简介

每一款游戏都应该拥有一个音频系统。在我自己的引擎中共有两种类型的声音:3D音效和背景音乐(BGM)。之所以区分了这两种声音类型是因为我们能够在硬件上解码BGM。使用iOS上的音频序列(Audio Queue)便能在一个简单的项目中播放BGM。(点击此处阅读文章第一第二第四部分

音效

通过使用OpenAL(与OpenGL类似的一种API)能够播放出音效。引擎中有一个音频线程,所有的OpenAL声音都是通过这条线呈现出来,并且它将通过指令缓冲区(与图形编程中的缓冲区类似)与主线联系在一起。举个例子来说,在主线更新期间,游戏逻辑也许需要一个爆破音,然后下达一个音频指令,并将其推送到指令缓冲区中。当音频线发现缓冲区中有一个指令时,它便会开始执行这一指令并激活OpenAL 调用。因为考虑到OpenAL调用可能会拖延调用线的运行,我配置了音频缓冲器和音频线。

BGM

iPhone有能够使用苹果音频序列API解码音频的硬件。通过这一API,你可以使用AudioQueueNewOutput()创造出一个音频输出序列而播放出声音,并提供如抽样率等相关的音频文件描述。你可以调用AudioFileOpenURL()函数获得这种描述。不幸的是我的项目却不适合使用这种方法,因为当游戏世界的区块流动传输时,我的音频文件也已经加载到内存中,所以我不希望只是为了获得音频文件的相关描述而调用AudioFileOpenURL()再次开启音频文件。所以我便决定自己收集数据,并以AAC压缩方法获取苹果CAF文件格式,因为它是一种开放格式,并且苹果的Mac设备带有一种命令行工具,能够将其它格式的文件转换成该格式。(游戏邦注:在iPhone中,音频序列只能够使用硬件解码压缩1首歌。而如果你想要播放更多音频,就需要再次使用软件解码的方法。)

CAF文件格式

与WAV文件格式一样,CAF文件格式也被划分为不同区块,如描述块(储存抽样率,每帧的频道等)以及数据块(储存音频样本数据)。我们需要获得CAF文件内部的数据并使用AudioQueue播放BGM。

样本截图(from 4.bp.blogspot)

样本截图(from 4.bp.blogspot)

苹果音频序列

为了使用音频序列API播放音频,我们需要经历以下几个步骤:

1.使用AudioQueueNewOutput()创造音频输出。

2.通过AudioQueueSetProperty()——它提供了音频格式所需要的Magic cookie属性,为新创建序列设置属性。

3.使用AudioQueueAddPropertyListener()创造“属性监听器”以掌握音频序列中任何变化,如完成播放等。

4.通过AudioQueueAllocateBufferWithPacketDescriptions()重新分配音频序列的储存器,以包含数据包描述内容。

5.在设立了描述块后,通过AudioQueueEnqueueBuffer()将音频样本数据整合进音频序列中。

6.随后,我们需要通过AudioQueuePrime()让硬件去解码音频样本。

7.最后便可以使用AudioQueueStart()播放音频。

停止音频序列也需要3大步骤:

1.调用AudioQueueStop()停止播放。

2.通过AudioQueueRemovePropertyListener()删除上述第三个步骤所设立的“属性监听器”。

3.最后,调用AudioQueueDispose()释放所有音频序列资源。

在iPhone上使用OpenAL播放3D音效其实与其它平台上的做法差不多,而播放BGM却比较麻烦,因为来自苹果的样本代码只提供了一个特定的文件路径(告诉我们如何播放一个音频),而不是加载于内存中的音频文件,所以我便需要自己想办法获取音频文件描述。

性能对于游戏来说非常重要,它能够有效地维持游戏中不断变化的频率。所以我将在此谈论的是如何追踪游戏性能。然而因为我的游戏还未完成,所以我只能用样本占位符资产配置引擎,而且我现在所描述的数据可能跟成品游戏有所出入。不管怎样,我都会努力阐述我第一次的引擎配置尝试(游戏邦注:以下所有的数据取自iPhone 3G)。

分析XCode OpenGL ES

伴随Xcode4的OpenGL ES分析器能够用于分析iPhone上的图像性能。它能够察觉到多余的状态改变并提供适当的建议以完善渲染性能。除此之外它也能够帮助发现一些漏洞,如忘记设定纹理状态以使用mip-map。

分析器(from 1.bp.blotspot)

分析器(from 1.bp.blotspot)

在使用分析器后,我发现了2种能够有效完善引擎渲染性能的方法。第一种便是在渲染每个画面后使用“EXT_discard_framebuffer”扩展。

discardFB(from 3.bp.blogspot)

discardFB(from 3.bp.blogspot)

在使用分析器之前我甚至不知道iPhone拥有OpenGL扩展功能;它能够帮助我们将帧率从20.8毫秒每帧提高到19.2毫秒每帧。而第二种方法便是使用后备缓冲格式以降低填充率。

compactFB(from blogspot)

compactFB(from blogspot)

设置了后备缓冲区格式(从RGBA8888变化到RGB565)后,每一帧所需要的时间从19.2毫秒减少到18.52毫秒。

性能图表

性能图表(from altdevblogaday)

性能图表(from altdevblogaday)

我也创造了一个性能图表以呈现引擎中每个子系统所使用的时间。创造关于游戏的波浪曲线图很简单,并且我可以在此观察子系统的变化。

结论

使用上述两种方法能够帮助我们有效地追踪性能问题并提高帧率。

游戏邦注:原文发表于2011年9-10月,所涉事件及数据以当时为准。

本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

Writing an iPhone Game Engine (Part 5- Audio)

Simon Yeung

Introduction

Every games should have an audio system. In my little engine, it supports 2 types of sound: 3D effects sounds and BGM. Separate this 2 types because the BGM can be decoded on hardware. A sample project is provided for playing the BGM using the Audio Queue on iOS.

Effects sounds

Effect sounds are played using OpenAL, which is an API similar to the OpenGL. In the engine, there has an audio thread where all OpenAL calls are executed in this thread and this thread is communicate with main thread through a command buffer which is similar the one used in graphics programming. For example, during main thread update, the game logic may request to play an explosion sound, then an audio command is made and this command is pushed to the command buffer. When the audio thread find that there is a command inside the buffer, the command is executed and initiate an OpenAL call. I set up the audio buffer and thread because the OpenAL call may stall the calling thread according to this article.

BGM

On iPhone, there are hardware to decode audio which can be used through the Apple’s Audio Queue API. By this API, you can play sound by creating an audio queue output using AudioQueueNewOutput() providing the audio file description such as sample rate. You can get this description by calling the AudioFileOpenURL(). Unfortunately, this is not suitable in my case as my audio file is already loaded in the memory when the game world tile is streamed, I don’t want to called the AudioFileOpenURL() to open the audio file again to get the description of the audio file only. So I decided to get this data by myself and I picked the Apple CAF file format with the AAC compression method because it is an open format and Mac machine have a command line tools to convert file into this file format. (Note that on iPhone, the Audio Queue can only decompress 1 song using hardware decoding. If more audio need to be played, it will fall back to use software decoding.)

CAF file format

Just like the WAV file format, the CAF file format is divided into many different chunks, such as description chunk(which store the sample rate, channels per frame, …) and the data chunk(which store the audio sample data). The specification of CAF can be found here. We need to get those data inside the CAF file to playback the BGM using AudioQueue. For details, you can take a look at the AudioCAFHelper.cpp in the sample project.

Screen Shot from Sample Project

Apple Audio Queue

To playback the audio using Audio Queue API, we need a couple of steps:

1.An audio output need to be created using AudioQueueNewOutput().

2.We need to set the property of the newly created queue by AudioQueueSetProperty() which supply the Magic cookie property which is required by the audio format.

3.A property listener should be set up using AudioQueueAddPropertyListener() to listen to the event occurs in the audio queue such as the playback is finished.

4.We need to allocate memory for audio queue to contain the packet description by AudioQueueAllocateBufferWithPacketDescriptions().

5.After setting up the description, the audio sample data need to be put into the audio queue by AudioQueueEnqueueBuffer().

6.After that, we need to tell the hardware to decode the audio samples by AudioQueuePrime().

7.Finally, the audio are ready to be playback using AudioQueueStart().

To stop an audio queue, 3 steps are needed:

1.AudioQueueStop() need to be called to stop the playback.

2.The property listener set up in step 3 above needed to be removed by AudioQueueRemovePropertyListener().

3.Finally, AudioQueueDispose() is called to release all the audio queue resources.

You may refer to the sample project to have a full understand on how to use the audio queue, especially on the part to enqueue the audio sample to audio queue buffer.

Conclusion

Playback 3D effects sound using OpenAL on iPhone is similar to the other platform, while playback the BGM takes some efforts because I need to get the audio file description by myself as the sample code from Apple only provide how to play back an audio by specifying a file path but not the audio file that is loaded in memory. Hope that my sample code can help someone faces the same problem with me.

Writing an iPhone Game Engine (Part 6- Performance)

Introduction

Performance of a game is very important, it is important to maintain a constant frame rate of a game. So I will talk about how I keep track of the performance of the game. However, the game is not finished, I can only profile the engine with sample placeholder assets, the profiled data might not be match with the final data after the game is finished. Anyway, I will try to talk about my first attempt to profile my engine. (All the data are measured with iPhone 3G).

XCode OpenGL ES Analysis

For profiling the graphics performance on iPhone, there is an OpenGL ES analyzer that comes with Xcode 4. It can detect redundant state changes and give some advice to improve rendering performance. It also help me to spot some bugs such as forgetting to set the texture states to use mip-map.

After using the analyzer, I discover that 2 recommendations that improve my engine rendering performance significantly. The first suggestion is to use the EXT_discard_framebuffer extension after rendering each frame.

Before using the analyzer, I did not notice iPhone have this OpenGL extension. This helps the frame rate from 20.8ms per frame to 19.2ms per frame. The second suggestion is to use a more compact back-buffer format to reduce the fill rate.

After setting the back-buffer format from RGBA8888 to RGB565, the time taken for each frame reduced from 19.2ms per frame to 18.52ms per frame.

Performance Graph

I also generate a  performance graph to display how much time each subsystem is used in the engine. It is easier to have a graph to spot spikes of the game so that I can investigate which subsystem cast the spikes.

Conclusion

The above 2 techniques are used to track down the performance issues and increase the frame rate. However, as the game is still under development, there are still some area, such as physics and script, which can be optimized. Those optimization will be done when most of the game is completed. I am going to stop writing this series as most of the problems I have encountered during development are mentioned. May be I will write a postmortem after the game is finished, but this will not in the near future as there are still tons of art-work have not been done yet. Hope you all enjoyed the series.(source:altdevblogaday part 5,part 6


上一篇:

下一篇: