<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>iPhoneGeek 爱疯极客 &#187; OpenAL</title>
	<atom:link href="http://www.iphone-geek.cn/tag/openal/feed" rel="self" type="application/rss+xml" />
	<link>http://www.iphone-geek.cn</link>
	<description>iPhone 新闻，编程，技巧与提示，代码，教程</description>
	<lastBuildDate>Thu, 08 Dec 2011 01:18:52 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>OpenAL同时处理大量声音</title>
		<link>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/openal%e5%90%8c%e6%97%b6%e5%a4%84%e7%90%86%e5%a4%a7%e9%87%8f%e5%a3%b0%e9%9f%b3</link>
		<comments>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/openal%e5%90%8c%e6%97%b6%e5%a4%84%e7%90%86%e5%a4%a7%e9%87%8f%e5%a3%b0%e9%9f%b3#comments</comments>
		<pubDate>Thu, 27 May 2010 02:10:54 +0000</pubDate>
		<dc:creator>bagusflyer</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[音频]]></category>
		<category><![CDATA[OpenAL]]></category>

		<guid isPermaLink="false">http://www.iphone-geek.cn/?p=718</guid>
		<description><![CDATA[（注：这是我在编写MozartBrain时，参考过的一篇文章，虽然年代久远，但对于学习OpenAL编程还是具有指导意义。）

今天要讨论的话题是OpenAL同时处理大量的音效。当你使用OpenAL进行声音播放时，能够同时播放的声音有一个上限（换而言之，最大的source数目）。在iPhone上能够通过下面代码获得的最大source数目大约为32：

1alGenSources&#40;1, &#38;sourceID&#41;;

当你要求超过最大数目的source时，上面的语句将失败，但它并不产生错误。因此，不要要求超过32个source（注：这是iPhone OS 2.2上的数目，其他版本的OS可能会有些差别）

所以，这意味着什么？它意味着任何时候不管什么原因都不要同时播放超过32个音效。这确实是一个问题，如果你的目标是通过分别播放各种乐器来模拟整个交响乐团的演奏，那么你最好不要在iPhone上尝试这样做。
我以前的文章中提到建议为每个音效（音效缓存）分配一个source并在需要播放时都调用此source。这在大部分情形下都可以正常工作。但是，如果在你的应用程序中有超过32个音效时，你应该怎么做？我记得曾经提到过：你应该将未用的source移交给将要被播放的buffer。


这实际上非常简单，我将展示一下怎样实现！

首先，你需要预先创建并加载source。这并不是必须的，但我希望在我想要播放声音时，能够立刻进行，所以我必须预先准备好。

123456789101112131415161718// 注意: MAX_SOURCES 是你需要预先加载的source数目
&#160;// 应该小于32
-&#40;void&#41;preloadSources
&#160;&#123;
&#160; &#160; &#160;// lazy init of my data structure
&#160; &#160; &#160;if &#40;sources == nil&#41; sources = &#91;&#91;NSMutableArray alloc&#93; init&#93;;
&#160; &#160; &#160;// we want to allocate all the sources we will need up front
&#160; &#160; &#160;NSUInteger sourceCount = MAX_SOURCES;
&#160; &#160; &#160;NSInteger sourceIndex;
&#160; &#160; &#160;NSUInteger sourceID;

&#160; &#160; &#160;// build a [...]]]></description>
			<content:encoded><![CDATA[<p>（注：这是我在编写MozartBrain时，参考过的一篇文章，虽然年代久远，但对于学习OpenAL编程还是具有指导意义。）</p>
<p><br class="spacer_" /></p>
<p>今天要讨论的话题是OpenAL同时处理大量的音效。当你使用OpenAL进行声音播放时，能够同时播放的声音有一个上限（换而言之，最大的source数目）。在iPhone上能够通过下面代码获得的最大source数目大约为32：</p>
<p><br class="spacer_" /></p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">alGenSources<span style="color: #002200;">&#40;</span>1, <span style="color: #002200;">&amp;</span>sourceID<span style="color: #002200;">&#41;</span>;</div></td></tr></tbody></table></div>
<p><br class="spacer_" /></p>
<p>当你要求超过最大数目的source时，上面的语句将失败，但它并不产生错误。因此，不要要求超过32个source（注：这是iPhone OS 2.2上的数目，其他版本的OS可能会有些差别）</p>
<p><br class="spacer_" /></p>
<p>所以，这意味着什么？它意味着任何时候不管什么原因都不要同时播放超过32个音效。这确实是一个问题，如果你的目标是通过分别播放各种乐器来模拟整个交响乐团的演奏，那么你最好不要在iPhone上尝试这样做。</p>
<p>我以前的文章中提到建议为每个音效（音效缓存）分配一个source并在需要播放时都调用此source。这在大部分情形下都可以正常工作。但是，如果在你的应用程序中有超过32个音效时，你应该怎么做？我记得曾经提到过：你应该将未用的source移交给将要被播放的buffer。</p>
<p><br class="spacer_" /></p>
<p><span id="more-718"></span></p>
<p>这实际上非常简单，我将展示一下怎样实现！</p>
<p><br class="spacer_" /></p>
<p>首先，你需要预先创建并加载source。这并不是必须的，但我希望在我想要播放声音时，能够立刻进行，所以我必须预先准备好。</p>
<p><br class="spacer_" /></p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #11740a; font-style: italic;">// 注意: MAX_SOURCES 是你需要预先加载的source数目</span><br />
&nbsp;<span style="color: #11740a; font-style: italic;">// 应该小于32</span><br />
<span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>preloadSources<br />
&nbsp;<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// lazy init of my data structure</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>sources <span style="color: #002200;">==</span> <span style="color: #a61390;">nil</span><span style="color: #002200;">&#41;</span> sources <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/"><span style="color: #400080;">NSMutableArray</span></a> alloc<span style="color: #002200;">&#93;</span> init<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// we want to allocate all the sources we will need up front</span><br />
&nbsp; &nbsp; &nbsp;NSUInteger sourceCount <span style="color: #002200;">=</span> MAX_SOURCES;<br />
&nbsp; &nbsp; &nbsp;NSInteger sourceIndex;<br />
&nbsp; &nbsp; &nbsp;NSUInteger sourceID;<br />
<br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// build a bunch of sources and load them into our array.</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span>sourceIndex <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>; sourceIndex &lt; sourceCount; sourceIndex<span style="color: #002200;">++</span><span style="color: #002200;">&#41;</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;alGenSources<span style="color: #002200;">&#40;</span>1, <span style="color: #002200;">&amp;</span>amp;sourceID<span style="color: #002200;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #002200;">&#91;</span>sources addObject<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a> numberWithUnsignedInt<span style="color: #002200;">:</span>sourceID<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #002200;">&#125;</span><br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p><br class="spacer_" /></p>
<p>这实际上很简单，只是让openAL创建一系列 source 并保存它们的 ID。 你可以通过向alGenSources传递所需source数目，让 openAL一次创建所有source，然后将它们存储于 C 数组中,但这里我不想调用诸如malloc/free之类的函数，所有使用的是objc-c的方法。</p>
<p><br class="spacer_" /></p>
<p>老的声音播放方法如下：</p>
<p><br class="spacer_" /></p>
<p>// 获取声音ID，然后启动声音播放</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp;<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>playSound<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a><span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>soundKey<br />
&nbsp;<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp;<a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a> <span style="color: #002200;">*</span> numVal <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>soundDictionary objectForKey<span style="color: #002200;">:</span>soundKey<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>numVal <span style="color: #002200;">==</span> <span style="color: #a61390;">nil</span><span style="color: #002200;">&#41;</span> <span style="color: #a61390;">return</span>;<br />
&nbsp; &nbsp; &nbsp;NSUInteger sourceID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>numVal unsignedIntValue<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp;alSourcePlay<span style="color: #002200;">&#40;</span>sourceID<span style="color: #002200;">&#41;</span>;<br />
&nbsp;<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p><br class="spacer_" /></p>
<p>但是由于以前我们是为每个buffer都分配一个source。现在我们不再会有预先分配好的source，所以必须在声音播放时进行分配。现在的播放方法如下：</p>
<p><br class="spacer_" /></p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span>NSUInteger<span style="color: #002200;">&#41;</span>playSound<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a><span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>soundKey gain<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>ALfloat<span style="color: #002200;">&#41;</span>gain pitch<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>ALfloat<span style="color: #002200;">&#41;</span>pitch loops<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>loops<br />
&nbsp;<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp;ALenum err <span style="color: #002200;">=</span> alGetError<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#41;</span>; <span style="color: #11740a; font-style: italic;">// clear error code</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// first, find the buffer we want to play</span><br />
&nbsp; &nbsp; &nbsp;<a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a> <span style="color: #002200;">*</span> numVal <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>soundLibrary objectForKey<span style="color: #002200;">:</span>soundKey<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>numVal <span style="color: #002200;">==</span> <span style="color: #a61390;">nil</span><span style="color: #002200;">&#41;</span> <span style="color: #a61390;">return</span> <span style="color: #2400d9;">0</span>;<br />
&nbsp; &nbsp; &nbsp;NSUInteger bufferID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>numVal unsignedIntValue<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// now find an available source</span><br />
&nbsp; &nbsp; &nbsp;NSUInteger sourceID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>self _nextAvailableSource<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// make sure it is clean by resetting the source buffer to 0</span><br />
&nbsp; &nbsp; &nbsp;alSourcei<span style="color: #002200;">&#40;</span>sourceID, AL_BUFFER, 0<span style="color: #002200;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// attach the buffer to the source</span><br />
&nbsp; &nbsp; &nbsp;alSourcei<span style="color: #002200;">&#40;</span>sourceID, AL_BUFFER, bufferID<span style="color: #002200;">&#41;</span>;          <span style="color: #11740a; font-style: italic;">// set the pitch and gain of the source</span><br />
&nbsp; &nbsp; &nbsp;alSourcef<span style="color: #002200;">&#40;</span>sourceID, AL_PITCH, pitch<span style="color: #002200;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp;alSourcef<span style="color: #002200;">&#40;</span>sourceID, AL_GAIN, gain<span style="color: #002200;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// set the looping value</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>loops<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;alSourcei<span style="color: #002200;">&#40;</span>sourceID, AL_LOOPING, AL_TRUE<span style="color: #002200;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #002200;">&#125;</span> <span style="color: #a61390;">else</span> <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;alSourcei<span style="color: #002200;">&#40;</span>sourceID, AL_LOOPING, AL_FALSE<span style="color: #002200;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #002200;">&#125;</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// check to see if there are any errors</span><br />
&nbsp; &nbsp; &nbsp;err <span style="color: #002200;">=</span> alGetError<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>err <span style="color: #002200;">!=</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #002200;">&#91;</span>self _error<span style="color: #002200;">:</span>err note<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Error Playing Sound!&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #a61390;">return</span> <span style="color: #2400d9;">0</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #002200;">&#125;</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// now play!</span><br />
&nbsp; &nbsp; &nbsp;alSourcePlay<span style="color: #002200;">&#40;</span>sourceID<span style="color: #002200;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #a61390;">return</span> sourceID; <span style="color: #11740a; font-style: italic;">// return the sourceID so I can stop loops easily</span><br />
&nbsp;<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p><br class="spacer_" /></p>
<p>请注意，这个新方法不再直接从soundLibrary中获取sourceID，所以我们必须保存buffer而不是source。等等，下面一行代码在做什么：</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #11740a; font-style: italic;">// now find an available source</span><br />
NSUInteger sourceID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>self _nextAvailableSource<span style="color: #002200;">&#93;</span>;</div></td></tr></tbody></table></div>
<p><br class="spacer_" /></p>
<p>这实际上是调用我自己的方法，查找未被使用的source，然后返回，请看代码：</p>
<p><br class="spacer_" /></p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span>NSUInteger<span style="color: #002200;">&#41;</span>_nextAvailableSource <br />
<span style="color: #002200;">&#123;</span><br />
&nbsp;     NSInteger sourceState; <span style="color: #11740a; font-style: italic;">// a holder for the state of the current source</span><br />
&nbsp;     <span style="color: #11740a; font-style: italic;">// first check: find a source that is not being used at the moment.</span><br />
&nbsp;     <span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a> <span style="color: #002200;">*</span> sourceNumber <span style="color: #a61390;">in</span> sources<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span><br />
&nbsp;         alGetSourcei<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span>sourceNumber unsignedIntValue<span style="color: #002200;">&#93;</span>, AL_SOURCE_STATE, <span style="color: #002200;">&amp;</span>sourceState<span style="color: #002200;">&#41;</span>;<br />
&nbsp;         <span style="color: #11740a; font-style: italic;">// great! we found one! return it and shunt</span><br />
&nbsp;         <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>sourceState <span style="color: #002200;">!=</span> AL_PLAYING<span style="color: #002200;">&#41;</span> <span style="color: #a61390;">return</span> <span style="color: #002200;">&#91;</span>sourceNumber unsignedIntValue<span style="color: #002200;">&#93;</span>;<br />
&nbsp;     <span style="color: #002200;">&#125;</span><br />
&nbsp;     <span style="color: #11740a; font-style: italic;">// in the case that all our sources are being used, we will find the first non-looping source</span><br />
&nbsp;     <span style="color: #11740a; font-style: italic;">// and return that.</span><br />
&nbsp;     <span style="color: #11740a; font-style: italic;">// first kick out an error</span><br />
      NSLog<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;available source overrun, increase MAX_SOURCES&quot;</span><span style="color: #002200;">&#41;</span>;<br />
&nbsp;     NSInteger looping;<br />
&nbsp;     <span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a> <span style="color: #002200;">*</span> sourceNumber <span style="color: #a61390;">in</span> sources<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span><br />
&nbsp;         alGetSourcei<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span>sourceNumber unsignedIntValue<span style="color: #002200;">&#93;</span>, AL_LOOPING, <span style="color: #002200;">&amp;</span>looping<span style="color: #002200;">&#41;</span>;<br />
&nbsp;         <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">!</span>looping<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span><br />
&nbsp;             <span style="color: #11740a; font-style: italic;">// we found one that is not looping, cut it short and return it</span><br />
&nbsp;             NSUInteger sourceID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>sourceNumber unsignedIntValue<span style="color: #002200;">&#93;</span>;<br />
&nbsp;             alSourceStop<span style="color: #002200;">&#40;</span>sourceID<span style="color: #002200;">&#41;</span>;<br />
&nbsp;             <span style="color: #a61390;">return</span> sourceID;<br />
&nbsp;         <span style="color: #002200;">&#125;</span><br />
&nbsp;     <span style="color: #002200;">&#125;</span><br />
&nbsp;     <span style="color: #11740a; font-style: italic;">// what if they are all loops? arbitrarily grab the first one and cut it short</span><br />
&nbsp;     <span style="color: #11740a; font-style: italic;">// kick out another error</span><br />
      NSLog<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;available source overrun, all used sources looping&quot;</span><span style="color: #002200;">&#41;</span>;<br />
&nbsp;     NSUInteger sourceID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>sources objectAtIndex<span style="color: #002200;">:</span>0<span style="color: #002200;">&#93;</span> unsignedIntegerValue<span style="color: #002200;">&#93;</span>;<br />
&nbsp;     alSourceStop<span style="color: #002200;">&#40;</span>sourceID<span style="color: #002200;">&#41;</span>;<br />
&nbsp;     <span style="color: #a61390;">return</span> sourceID;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p><br class="spacer_" /></p>
<p>在 99% 的情况下，它将在第一个循环时找到合适的source并返回。根据我的经验即使在大量使用音效的程序中，你也很少会遇到几个声音同时播放的情形（除非你的音频采样非常长），所以它将很快返回。</p>
<p><br class="spacer_" /></p>
<p>（译者注：实际上，并非完全正确，在我编写的一个未发表的吉他乐器游戏中，我的声音引擎经常遇到大量音效同时播放的情况。之所以会有这种情况发生，是由于你必须在适当的时候<strong>关闭</strong>声音。是的，播放声音非常容易，但要关闭，就有相当的学问。如果你在声音播放的中途直接关闭，那么我可以保证，你将听到爆音。我在App Store下载了许多乐器软件，有几个会产生明显的爆音，就是由于这个原因。要解决这个问题，可能有许多方法。经过我的实验，我的方法是在停止一个声音前，采用定时器逐渐减小音量或将souce移动到很远的地方，确保听不见，但不要停止声音。哪怕声音的音量只有0.00001，如果你关闭它，你仍然会听到爆音。你必须让声音采样自然结束，所以你采用的音效一定不能过长。这就是为什么有许多source会同时存在的原因。为减小source的同时使用的数量，造成source不够用的情况，还必须采用一些其他的技术，比如在将音量减小到接近0（注意不能为0，如果是0，就等同于关闭音量，会产生破音）时，将buffer中的指针指向buffer的结尾处，或采用streaming的方法，另外还可以采用source重用的技术。当然，这些不在本文讨论的范围之内了）</p>
<p><br class="spacer_" /></p>
<p>但是，如果所有source还在播放怎么办？有几个选择：容易的方法：增加max_sources直到问题解决；稍难一点的方法（仍然很容易）：实时生成新的source。</p>
<p><br class="spacer_" /></p>
<p>但是如果你用完了32个source，仍然没有适当的source可用时，最后几行代码将进行处理（译者注，如我上面注释的，这种解决方法并不完美，它很有可能会产生破音。但这只是一个简单的示例，你应根据具体情况进行处理），它假设新声音比旧声音更重要而且循环音比音效更重要：我们采用两个步骤找到需要停止的声音。</p>
<p><br class="spacer_" /></p>
<p>首先我们找到一个非循环音，停止它如何返回其source。第二种情况如果有MAX个循环音在同时播放，那么找到第一个source，停止并返回其source （译者注：其实这也是与你程序的具体情况相关的，你可能会需要采用特殊的算法来决定需要停止的source，比如最远的声音等）。</p>
<p><br class="spacer_" /></p>
<p>（译者注：实际上音效处理的算法是非常复杂，完全看你程序的具体情况。这里的代码只是简单的示例而已。还是那句话：<strong>播放声音很容易，但要正确地关闭声音，一定要仔细考虑</strong>！）</p>
<p><br class="spacer_" /></p>
<h5>原文见：<a href="http://benbritten.com/2009/05/02/lots-and-lots-of-sounds-in-openal/">Lots and Lots of sounds in openAL</a></h5>
]]></content:encoded>
			<wfw:commentRss>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/openal%e5%90%8c%e6%97%b6%e5%a4%84%e7%90%86%e5%a4%a7%e9%87%8f%e5%a3%b0%e9%9f%b3/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>跨平台代码分享之七 &#8211; 一个简单的音效引擎</title>
		<link>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/%e8%b7%a8%e5%b9%b3%e5%8f%b0%e4%bb%a3%e7%a0%81%e5%88%86%e4%ba%ab%e4%b9%8b%e4%b8%83-%e4%b8%80%e4%b8%aa%e7%ae%80%e5%8d%95%e7%9a%84%e9%9f%b3%e6%95%88%e5%bc%95%e6%93%8e</link>
		<comments>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/%e8%b7%a8%e5%b9%b3%e5%8f%b0%e4%bb%a3%e7%a0%81%e5%88%86%e4%ba%ab%e4%b9%8b%e4%b8%83-%e4%b8%80%e4%b8%aa%e7%ae%80%e5%8d%95%e7%9a%84%e9%9f%b3%e6%95%88%e5%bc%95%e6%93%8e#comments</comments>
		<pubDate>Wed, 06 Jan 2010 06:18:19 +0000</pubDate>
		<dc:creator>bagusflyer</dc:creator>
				<category><![CDATA[基础]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[音频]]></category>
		<category><![CDATA[跨平台]]></category>
		<category><![CDATA[OpenAL]]></category>
		<category><![CDATA[开源项目]]></category>
		<category><![CDATA[教程]]></category>

		<guid isPermaLink="false">http://www.iphone-geek.cn/?p=727</guid>
		<description><![CDATA[游戏都需要一个声音引擎，一般来说，有三种类型：

播放音效。音效一般是比较简短的声音文件。
播放背景音乐。
乐器，音效模拟。

&#160;
本文介绍一个简单跨平台的音效引擎，主要针对第一种类型。实际上，也可以适用于第二种类型，但由于背景音乐文件较大，占用内存较大，所以本文介绍的方法并不是最为有效率的（特别是对于像iPhone之类内存较为紧张的平台，但仍然可用）。可以在本引擎的基础上加以改进，比如采用“流”（streaming）技术。而第三种类型，可能涉及到DSP（数字处理技术），比较复杂，另外在不同平台有一些不同的技术，如Windows下的VST/VSTi，Mac OSX下的AU等，不在本文的讨论范围。
&#160;
本文介绍的音效引擎是基于OpenAL的，并且是我在许多项目中使用的引擎的简化部分（去除错误处理及乐器模拟部分）。
&#160;
有关OpenAL的使用，网上有许多教程，比如：OpenAL编程教程等。iPhone上的OpenAL使用，本网站也有几篇文章。所以这里不再赘述。
&#160;
本引擎的目的，是简化声音播放接口。最简单的情况下可以仅仅使用两条语句，就播放一段音效。而繁复的OpenAL初始化功能等都由引擎自动完成。下例展示了怎样播放一段音效的最为典型的调用：
&#160;
123audio::CSound sound&#40;&#34;sound.wav&#34;&#41;;

sound.play&#40;&#41;;
&#160;
本引擎由三个类和一个公共函数构成。audio::CEngine是引擎的核心部分，它是一个单例类（Singleton)，负责直接与OpenAL接口，用户无需与其打交道。audio::CBuffer类对应于OpenAL中的缓存，通过audio::openAudioData函数打开音效文件，创建内部缓存，用户也无需直接使用。audio::CSound是用户直接使用的类，它提供了诸如play（），stop（），setVolume（）和setPosition（）几个接口函数。如需扩展功能，添加一些其他功能，可以直接加在此类中，并在audio::CEngine中实现。
&#160;
以上三个类可以直接使用在不同平台中。与平台相关的代码，都在audio::openAudioData()中。它的作用是打开不同的声音文件。对于Windows，我使用了一个开源项目库libsndfile，用来打开诸如wav，caf，aiff，flac，ogg等常用音效文件（注意：mp3并未包括其中。由于使用mp3的软件需要支付一定许可证费用，本人也不喜欢使用）。而对于Mac OSX（iPhone），则使用了core audio。这里大家可能会有一个疑问，既然libsndfile也是跨平台的，为什么在iPhone上不使用它呢？确实，libsndfile支持Windows，Linux，Mac OSX，Sun Solaris等平台，而iPhone并不支持ogg等格式，使用libsndfile不正好填补这个空白？是的，我也是这样想的，但是，有两点原因初始我暂时没有使用它：

没有现成的iPhone库。由于libsndfile需要使用makefile由GCC编译（我曾试过使用Xcode编译，没有成功），我也不是这方面的专家。但是，libsndfile在Mac OSX下编译没有问题，所以只需稍作修改，在为iPhone进行编译也不是没有可能的事情。
由于其许可证的限制。libsndfile是基于LGPL的，它要求使用者采用动态连接库，而众所周知，iPhone（官方）是不支持动态链接的。如果使用静态链接，则需要随软件发布源代码或目标代码。其实，这也不是无法克服的，LGPL只要求发布与libsnd相关的目标代码，所以对你的软件并无影响。对吗？

&#160;
本文给出了Windows下和iPhone下的应用。在Windows下使用需要下载OpenAL SDK和libsndfile。为方便编译好的libsndfile随示例程序给出给出。（你需要修改一下VS 项目文件中的有关OpenAL的头文件和库文件路径，另外，在OpenAL include目录中创建一个OpenAL目录，并将.h复制到其下，这样我就不需要修改#include语句了。）
&#160;
示例程序audiolib下载。（项目在test目录下）
]]></description>
			<content:encoded><![CDATA[<p>游戏都需要一个声音引擎，一般来说，有三种类型：</p>
<ol>
<li>播放音效。音效一般是比较简短的声音文件。</li>
<li>播放背景音乐。</li>
<li>乐器，音效模拟。</li>
</ol>
<p>&nbsp;</p>
<p>本文介绍一个简单跨平台的音效引擎，主要针对第一种类型。实际上，也可以适用于第二种类型，但由于背景音乐文件较大，占用内存较大，所以本文介绍的方法并不是最为有效率的（特别是对于像iPhone之类内存较为紧张的平台，但仍然可用）。可以在本引擎的基础上加以改进，比如采用“流”（streaming）技术。而第三种类型，可能涉及到DSP（数字处理技术），比较复杂，另外在不同平台有一些不同的技术，如Windows下的VST/VSTi，Mac OSX下的AU等，不在本文的讨论范围。</p>
<p>&nbsp;</p>
<p>本文介绍的音效引擎是基于OpenAL的，并且是我在许多项目中使用的引擎的简化部分（去除错误处理及乐器模拟部分）。</p>
<p>&nbsp;</p>
<p>有关OpenAL的使用，网上有许多教程，比如：<a href="http://www.devmaster.net/articles/openal-tutorials/lesson1.php">OpenAL编程教程</a>等。iPhone上的OpenAL使用，本网站也有几篇文章。所以这里不再赘述。</p>
<p>&nbsp;</p>
<p>本引擎的目的，是简化声音播放接口。最简单的情况下可以仅仅使用两条语句，就播放一段音效。而繁复的OpenAL初始化功能等都由引擎自动完成。下例展示了怎样播放一段音效的最为典型的调用：</p>
<p>&nbsp;</p>
<div class="codecolorer-container cpp mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">audio<span style="color: #008080;">::</span><span style="color: #007788;">CSound</span> sound<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;sound.wav&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
sound.<span style="color: #007788;">play</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>本引擎由三个类和一个公共函数构成。audio::CEngine是引擎的核心部分，它是一个单例类（Singleton)，负责直接与OpenAL接口，用户无需与其打交道。audio::CBuffer类对应于OpenAL中的缓存，通过audio::openAudioData函数打开音效文件，创建内部缓存，用户也无需直接使用。audio::CSound是用户直接使用的类，它提供了诸如play（），stop（），setVolume（）和setPosition（）几个接口函数。如需扩展功能，添加一些其他功能，可以直接加在此类中，并在audio::CEngine中实现。</p>
<p>&nbsp;</p>
<p>以上三个类可以直接使用在不同平台中。与平台相关的代码，都在audio::openAudioData()中。它的作用是打开不同的声音文件。对于Windows，我使用了一个开源项目库<a href="http://www.mega-nerd.com/libsndfile/">libsndfile</a>，用来打开诸如wav，caf，aiff，flac，ogg等常用音效文件（<strong>注意：mp3并未包括其中。由于使用mp3的软件需要支付一定许可证费用，本人也不喜欢使用</strong>）。而对于Mac OSX（iPhone），则使用了core audio。这里大家可能会有一个疑问，既然libsndfile也是跨平台的，为什么在iPhone上不使用它呢？确实，libsndfile支持Windows，Linux，Mac OSX，Sun Solaris等平台，而iPhone并不支持ogg等格式，使用libsndfile不正好填补这个空白？是的，我也是这样想的，但是，有两点原因初始我暂时没有使用它：</p>
<ol>
<li>没有现成的iPhone库。由于libsndfile需要使用makefile由GCC编译（我曾试过使用Xcode编译，没有成功），我也不是这方面的专家。但是，libsndfile在Mac OSX下编译没有问题，所以只需稍作修改，在为iPhone进行编译也不是没有可能的事情。</li>
<li>由于其许可证的限制。libsndfile是基于LGPL的，它要求使用者采用动态连接库，而众所周知，iPhone（官方）是不支持动态链接的。<strong>如果使用静态链接，则需要随软件发布源代码或目标代码。</strong>其实，这也不是无法克服的，LGPL只要求发布与libsnd相关的目标代码，所以对你的软件并无影响。对吗？</li>
</ol>
<p>&nbsp;</p>
<p>本文给出了Windows下和iPhone下的应用。在Windows下使用需要下载<a href="http://connect.creativelabs.com/openal/Downloads/oalinst.zip">OpenAL SDK</a>和<a href="http://www.mega-nerd.com/libsndfile/">libsndfile</a>。为方便编译好的libsndfile随示例程序给出给出。（你需要修改一下VS 项目文件中的有关OpenAL的头文件和库文件路径，另外，在OpenAL include目录中创建一个OpenAL目录，并将.h复制到其下，这样我就不需要修改#include语句了。）</p>
<p>&nbsp;</p>
<p>示例程序<a href="../wp-content/uploads/2010/01/audiolib.zip">audiolib</a>下载。（项目在test目录下）<a href="http://www.iphone-geek.cn/wp-content/uploads/2010/01/audiolib.zip"></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/%e8%b7%a8%e5%b9%b3%e5%8f%b0%e4%bb%a3%e7%a0%81%e5%88%86%e4%ba%ab%e4%b9%8b%e4%b8%83-%e4%b8%80%e4%b8%aa%e7%ae%80%e5%8d%95%e7%9a%84%e9%9f%b3%e6%95%88%e5%bc%95%e6%93%8e/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>OpenAL系列之二 &#8211; 为什么要避免使用alBuferDataStatic</title>
		<link>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/openal%e7%b3%bb%e5%88%97%e4%b9%8b%e4%ba%8c-%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e9%81%bf%e5%85%8d%e4%bd%bf%e7%94%a8albuferdatastatic</link>
		<comments>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/openal%e7%b3%bb%e5%88%97%e4%b9%8b%e4%ba%8c-%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e9%81%bf%e5%85%8d%e4%bd%bf%e7%94%a8albuferdatastatic#comments</comments>
		<pubDate>Thu, 31 Dec 2009 11:08:04 +0000</pubDate>
		<dc:creator>bagusflyer</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[音频]]></category>
		<category><![CDATA[OpenAL]]></category>
		<category><![CDATA[提示与技巧]]></category>

		<guid isPermaLink="false">http://www.iphone-geek.cn/?p=704</guid>
		<description><![CDATA[我收到Ken一封Email，这位仁兄浏览了我博客的中有关OpenAL的文章，遇到一些问题请求我的帮助。他在加载缓存后重复使用时遇到了一些OpenAL神秘的错误。
&#160;
所以我阅读了他寄给我的代码片段，我注意到：
&#160;
123// use the static buffer data API

alBufferDataStaticProc&#40;buffer, format, data, size, freq&#41;;
&#160;
啊哈! 这可能就是你问题的根源！除非你真正需要，alBufferDataStatic应该完全被避免。你怎样才知道你需要使用它？有一条金桂玉律：如果你不知道这个问题的答案，那么你就不需要它。
&#160;

（我不希望看上去对Ken太过严苛，这个问题很普遍，所以我决定写这篇文章（类似于我呼吁绝不要使用随Apple发布的示例代码一样））
&#160;
那我为什么会认为要避免使用它？我相信你一定听说过要获得更好的性能，你应该使用alBufferDataStatic()，对吗？不！
&#160;
那好，alBufferDataStatic()是什么？alBufferDataStatic将所有缓存数据的内存管理责任完全推给了你的代码。
&#160;
我重复一遍：当使用alBufferDataStatic()时，你应该对所有内存管理负责。
&#160;
这就是所谓的性能提升。
&#160;
等等！真的吗？这就是alBufferDataStatic的所作所为吗？
&#160;
是的。
&#160;
iPhone是共享内存空间的，它处理内存的方式以及当你的程序使用过多的内存时而被抛弃的方式是十分特殊的。它要求你密切注意及处理你使用的内存。那意味着如果你希望使你的第一人称射击游戏可以稳定地保持在30FPS，你必须从iPhone榨取最后一个字节内存，因此你可能需要花费几个星期来调整你的声音代码使得你可以随时密切控制内存使用情况。
&#160;
对于其他 %99.9999可能需要使用OpenAL的程序（即使你使用了大量的音效），使用alBufferDataStatic()只不过是给你带来额外的麻烦而已。这里有另一条规则：如果你不打算专门雇佣一个全职开发者处理音效代码，那么你不需要使用alBufferDataStatic()。
&#160;
快速阅读一下有关alBufferDataStatic()的技术文档。
&#160;
这是什么意思？
&#160;
意思是: 当你使用alBufferData()时，你将加载声音数据到局部缓存（你使用malloc建立的缓存），当你调用alBufferData()时， OpenAL 将复制所有字节到它自己的缓存区。然后，你要释放局部缓存，OpenGL现在拥有自己的缓存。其缺点是：在很短的一段时间内，你将拥有两份声音数据在内存中。优点是：内存管理非常非常容易。
&#160;
如果你的程序在缓存复制时崩溃，那么你可能需要alBufferDataStatic()。但是更有可能的是你的内存管理机制在什么地方十分糟糕。（另外需要注意如果你加载一个很大的声音数据到内存中，那么我可以告诉你，这是错误的，你应该采用数据流来加载数据）。
&#160;
好了，你已经绝对地肯定你要管理你自己的声音内存？可能需要使用alBufferDataStatic()，但是首先：这里有另一条金桂玉律：如果你正在读我这篇文章来决定是否使用它；那么不要使用。赶快找到这条代码，赶快删除它。直接删除。
&#160;
很明显，我在用一种夸张的方式试图说明我的观点。我不知道你的代码，但这里有一些我在我的代码中经常出现的bug：
&#160;
忘记释放对象导致内存泄漏，最终导致程序崩溃。
&#160;
不小心释放了一个自动释放对象，引起崩溃。
&#160;
不小心释放了一个从属于其他对象的对象，使程序随后（很久很久以后）崩溃。
&#160;
malloc了一些内存，忘记free，引起泄漏，最终崩溃。
&#160;
free了一些 malloc的内存，然后试图再使用，引起程序完全疯狂。
&#160;
这个列表还可以继续。但是你看到一个共同点没有？内存。为什么我在不必要的情况下还要承担更多内存管理的责任？为什么开发者还要一如既往地使他们的生活变得更糟糕？
&#160;
这里有些陈词滥调：
&#160;
更少的代码 == 更少的bug  更多的代码== 更多的bug。 因此代码越少越好。
&#160;
不要添加你现在不需要的代码。（这很常见，你可能会想：我可能以后做X时会需要一个方法，所以我现在就写出来，不必等到用时在写。）错！“未来”当你需要时，你需要的是Y，而不是X，你还是需要重写。或者更有可能是你永远也不需要用到X，你只是浪费了时间在不必要的代码上。
&#160;
不要过早地进行优化。使代码可以工作为先。然后再找到你需要优化的地方。如果试图过早优化，那么最终的结果是你花费了所有的时间在使一个方法十分伟大（直到后来才知道此方法仅仅几秒钟才被调用一次，所以这是一段最缺乏效率的代码而且没有人会注意到它。）
&#160;
所以，使用alBufferDataStatic()将打破我可爱的全部三条陈词滥调。
&#160;
不要这样做。
&#160;
你只会让你自己图添悲伤而且你的代码会问题多多。请使用alBufferData()。它可以工作。它很容易。资深的使用OpenAL的工程师保证使用alBufferData()没有内存问题。如果你打算移植你的代码，alBufferData()还是可以工作！alBufferData() 使你呼吸更新鲜！
&#160;
我已经在6个程序中使用了OpenAL代码。一些是游戏，一些是实用程序。一个没有使用alBufferDataStatic()的“音板”式程序同时播放32个声音（这是目前允许的最大数目，可能以后仍然是），它能够处理庞大的声音文件（通过数据流）。是不是这个超级疯狂代码实现的超难任务可以算是一种技能？
&#160;
不。恰恰相反。我尽量使我的程序保存简单。
&#160;
保持代码简单，不要使用不需要的东西。
&#160;
原文见：alBufferDataStatic: why you should avoid it
]]></description>
			<content:encoded><![CDATA[<p>我收到Ken一封Email，这位仁兄浏览了我博客的中有关OpenAL的文章，遇到一些问题请求我的帮助。他在加载缓存后重复使用时遇到了一些OpenAL神秘的错误。</p>
<p>&nbsp;</p>
<p>所以我阅读了他寄给我的代码片段，我注意到：</p>
<p>&nbsp;</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #11740a; font-style: italic;">// use the static buffer data API</span><br />
<br />
alBufferDataStaticProc<span style="color: #002200;">&#40;</span>buffer, format, data, size, freq<span style="color: #002200;">&#41;</span>;</div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>啊哈! 这可能就是你问题的根源！除非你真正需要，alBufferDataStatic应该完全被避免。你怎样才知道你需要使用它？有一条金桂玉律：如果你不知道这个问题的答案，那么你就不需要它。</p>
<p>&nbsp;</p>
<p><span id="more-704"></span><br />
（我不希望看上去对Ken太过严苛，这个问题很普遍，所以我决定写这篇文章（类似于我呼吁绝不要使用随Apple发布的示例代码一样））</p>
<p>&nbsp;</p>
<p>那我为什么会认为要避免使用它？我相信你一定听说过要获得更好的性能，你应该使用alBufferDataStatic()，对吗？不！</p>
<p>&nbsp;</p>
<p>那好，alBufferDataStatic()是什么？alBufferDataStatic将所有缓存数据的内存管理责任完全推给了你的代码。</p>
<p>&nbsp;</p>
<p>我重复一遍：当使用alBufferDataStatic()时，<strong>你</strong>应该对所有内存管理负责。</p>
<p>&nbsp;</p>
<p>这就是所谓的性能提升。</p>
<p>&nbsp;</p>
<p>等等！真的吗？这就是alBufferDataStatic的所作所为吗？</p>
<p>&nbsp;</p>
<p>是的。</p>
<p>&nbsp;</p>
<p>iPhone是共享内存空间的，它处理内存的方式以及当你的程序使用过多的内存时而被抛弃的方式是十分特殊的。它要求你密切注意及处理你使用的内存。那意味着如果你希望使你的第一人称射击游戏可以稳定地保持在30FPS，你必须从iPhone榨取最后一个字节内存，因此你可能需要花费几个星期来调整你的声音代码使得你可以随时密切控制内存使用情况。</p>
<p>&nbsp;</p>
<p>对于其他 %99.9999可能需要使用OpenAL的程序（即使你使用了大量的音效），使用alBufferDataStatic()只不过是给你带来额外的麻烦而已。这里有另一条规则：如果你不打算专门雇佣一个全职开发者处理音效代码，那么你不需要使用alBufferDataStatic()。</p>
<p>&nbsp;</p>
<p>快速阅读一下有关alBufferDataStatic()的技术文档。</p>
<p>&nbsp;</p>
<p>这是什么意思？</p>
<p>&nbsp;</p>
<p>意思是: 当你使用alBufferData()时，你将加载声音数据到局部缓存（你使用malloc建立的缓存），当你调用alBufferData()时， OpenAL 将复制所有字节到它自己的缓存区。然后，你要释放局部缓存，OpenGL现在拥有自己的缓存。其缺点是：在很短的一段时间内，你将拥有两份声音数据在内存中。优点是：内存管理非常非常容易。</p>
<p>&nbsp;</p>
<p>如果你的程序在缓存复制时崩溃，那么你可能需要alBufferDataStatic()。但是更有可能的是你的内存管理机制在什么地方十分糟糕。（另外需要注意如果你加载一个很大的声音数据到内存中，那么我可以告诉你，这是错误的，你应该采用数据流来加载数据）。</p>
<p>&nbsp;</p>
<p>好了，你已经绝对地肯定你要管理你自己的声音内存？可能需要使用alBufferDataStatic()，但是首先：这里有另一条金桂玉律：如果你正在读我这篇文章来决定是否使用它；那么不要使用。赶快找到这条代码，赶快删除它。直接删除。</p>
<p>&nbsp;</p>
<p>很明显，我在用一种夸张的方式试图说明我的观点。我不知道你的代码，但这里有一些我在我的代码中经常出现的bug：</p>
<p>&nbsp;</p>
<p>忘记释放对象导致内存泄漏，最终导致程序崩溃。</p>
<p>&nbsp;</p>
<p>不小心释放了一个自动释放对象，引起崩溃。</p>
<p>&nbsp;</p>
<p>不小心释放了一个从属于其他对象的对象，使程序随后（很久很久以后）崩溃。</p>
<p>&nbsp;</p>
<p>malloc了一些内存，忘记free，引起泄漏，最终崩溃。</p>
<p>&nbsp;</p>
<p>free了一些 malloc的内存，然后试图再使用，引起程序完全疯狂。</p>
<p>&nbsp;</p>
<p>这个列表还可以继续。但是你看到一个共同点没有？内存。为什么我在不必要的情况下还要承担更多内存管理的责任？为什么开发者还要一如既往地使他们的生活变得更糟糕？</p>
<p>&nbsp;</p>
<p>这里有些陈词滥调：</p>
<p>&nbsp;</p>
<p>更少的代码 == 更少的bug  更多的代码== 更多的bug。 因此代码越少越好。</p>
<p>&nbsp;</p>
<p>不要添加你现在不需要的代码。（这很常见，你可能会想：我可能以后做X时会需要一个方法，所以我现在就写出来，不必等到用时在写。）错！“未来”当你需要时，你需要的是Y，而不是X，你还是需要重写。或者更有可能是你永远也不需要用到X，你只是浪费了时间在不必要的代码上。</p>
<p>&nbsp;</p>
<p>不要过早地进行优化。使代码可以工作为先。然后再找到你需要优化的地方。如果试图过早优化，那么最终的结果是你花费了所有的时间在使一个方法十分伟大（直到后来才知道此方法仅仅几秒钟才被调用一次，所以这是一段最缺乏效率的代码而且没有人会注意到它。）</p>
<p>&nbsp;</p>
<p>所以，使用alBufferDataStatic()将打破我可爱的全部三条陈词滥调。</p>
<p>&nbsp;</p>
<p>不要这样做。</p>
<p>&nbsp;</p>
<p>你只会让你自己图添悲伤而且你的代码会问题多多。请使用alBufferData()。它可以工作。它很容易。资深的使用OpenAL的工程师保证使用alBufferData()没有内存问题。如果你打算移植你的代码，alBufferData()还是可以工作！alBufferData() 使你呼吸更新鲜！</p>
<p>&nbsp;</p>
<p>我已经在6个程序中使用了OpenAL代码。一些是游戏，一些是实用程序。一个没有使用alBufferDataStatic()的“音板”式程序同时播放32个声音（这是目前允许的最大数目，可能以后仍然是），它能够处理庞大的声音文件（通过数据流）。是不是这个超级疯狂代码实现的超难任务可以算是一种技能？</p>
<p>&nbsp;</p>
<p>不。恰恰相反。我尽量使我的程序保存简单。</p>
<p>&nbsp;</p>
<p>保持代码简单，不要使用不需要的东西。</p>
<p>&nbsp;</p>
<p>原文见：<a href="http://benbritten.com/2009/09/20/albufferdatastatic-why-you-should-avoid-it/">alBufferDataStatic: why you should avoid it</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/openal%e7%b3%bb%e5%88%97%e4%b9%8b%e4%ba%8c-%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e9%81%bf%e5%85%8d%e4%bd%bf%e7%94%a8albuferdatastatic/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenAL系列之一 &#8211; iPhone上的OpenAL音频</title>
		<link>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/openal%e7%b3%bb%e5%88%97%e4%b9%8b%e4%b8%80-iphone%e4%b8%8a%e7%9a%84openal%e9%9f%b3%e9%a2%91</link>
		<comments>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/openal%e7%b3%bb%e5%88%97%e4%b9%8b%e4%b8%80-iphone%e4%b8%8a%e7%9a%84openal%e9%9f%b3%e9%a2%91#comments</comments>
		<pubDate>Thu, 19 Nov 2009 05:21:58 +0000</pubDate>
		<dc:creator>bagusflyer</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[音频]]></category>
		<category><![CDATA[OpenAL]]></category>
		<category><![CDATA[教程]]></category>

		<guid isPermaLink="false">http://www.iphone-geek.cn/?p=259</guid>
		<description><![CDATA[译者注：这是我最喜欢的iPhone OpenAL教程之一，总共有好几篇文章，我会逐步翻译。
&#160;
随着保密协议的解除，我们可以开始公开讨论iPhone的代码了。我觉得讨论一下我&#8220;入侵&#8221;iPhone世界以来遇到的问题以及我是如何修复它们的可能是一个不错的话题。
&#160;
我目前正在编写一个iPhone游戏，它完全是以openGLES为基础并采用OpenAL音频。今天我想谈谈OpenAL
&#160;

今天我只讨论不到30秒的音频以及音效和短循环音。在iPhone上播放的音频需要正确的格式（或者说audio toolbox可以处理的多种音频格式中的一种，但如果你从正确的格式声音开始，iPhone在播放时就不需要进行处理了）。
&#160;
所以，打开你的命令行终端输入：

&#160;  /usr/bin/afconvert -f caff -d LEI16@44100 inputSoundFile.aiff outputSoundFile.caf  &#160;

&#160;
你可能会问这到底是在干什么？这是将文件转换为Little-Endian（低地址低字节） 16位，采样率44，100的格式。通常存储为.caf。
&#160;
好了！我们现在有了正确格式的.caf文件，可以开始做些什么了。
&#160;
有多种方法在iPhone上播放音频，有一种&#8217;简单&#8217;的方式，还有有一些&#8217;难&#8217;的方法&#8230;&#8230;我先快速地讨论一下简单的方式，然后再讨论一下使用OpenAL的&#8217;难的方法&#8217;。
&#160;
让iPhone发声的最快（最容易）的方法是使用音频系统服务：
12345NSString* path = &#91;&#91;NSBundle mainBundle&#93; pathForResource:@&#34;soundEffect1&#34; ofType:@&#34;caf&#34;&#93;; &#160;
NSURL* afUrl = &#91;NSURL fileURLWithPath:path&#93;;
UInt32 soundID; 
AudioServicesCreateSystemSoundID&#40;&#40;CFURLRef&#41;afUrl,&#38;soundID&#41;; &#160;
AudioServicesPlaySystemSound &#40;soundID&#41;;
&#160;
这对于象点击按钮和简单UI互动之类的任务已经足够好用。但是，对于任何更复杂一点的任务（比如：游戏）而言，这简直是毫无用处。它确实会立即开始播放，但若要指定的音效与游戏的特定帧相配合的话，这种方法基本上是没有用处。（我确实曾经使用上述方法实现过我的声音引擎，然而当我将其加载到iPhone播放声音时，要么声音会晚许多帧，要么干脆整个程序会停顿，等待audio toolbox将音频加载到缓存中，实在太糟糕了）。
&#160;
为了更好地控制音频，你需要使用OpenAL，audioUnits或audioQueue。
&#160;
我决定使用OpenAL以便我的代码具有更好的跨平台能力，并且通过学习怎样使用OpenAL，我还可以将这些技术运用在iPhone外的其他平台。（而且象我这样的代码雇佣兵，我觉得具有OpenAL经验比audioQueue经验更有市场）（另外由于我已经熟悉了openGL，openAL十分类似，而audio units和audio queue的代码有那么点丑陋）。
&#160;
所以，这是个超快速的openAL教程，但绝对是实现由openAL产生静态声音的最低要求。
&#160;
OpenAL实在是十分简单，它由3个实体构成：Listener（听者），Source（音源）和Buffer（缓存）。
&#160;
 Listener就是你。任何可以被Listener&#8220;听到&#8221;的声音都是来自扬声器。openAL允许你指定Listener相对于Source的位置，但是本例中我们忽略不计。我们只是针对最基本的静态声音。但是请记住&#8220;Listener&#8221;的概念，在你处理更复杂的情况时，你可以任意移动此对象。本文中就不做过多介绍。
&#160;
Source: 本质上类似与扬声器,它将产生Listener可以&#8220;听&#8221;到的声音。像Listener一样，你可以通过移动Source来获得groovy位置效应。本文的示例也没有涉及此部分。
&#160;
Buffer: 就是我们播放的声音。它保存原始的音频数据。
&#160;
有两个很重要的对象：device（设备）和 context（环境）。Device实际上设播放声音的硬件。而Context是当前所有声音在其中播放的&#8220;会话（session）&#8221;（你可以将其想象成包括所有sources和listener的房间。或者声音通过其播放的空气，或其他&#8230;&#8230;这就是Context。）
&#160;
它们在一起是怎么运作的：（最基本）
1) 获取device
  2) 将context关联到device
  3) 将数据放入buffer
  4) 将buffer链接到一个source
  5) 播放source
&#160;
就这么简单! 假定你的openAL实现对于listener是适当的缺省状态而且你不指定listener和source位置，上述流程工作得很好（在iPhone上就是如此）。
&#160;
好了，我们看看代码:
1234567891011121314151617&#160; &#160; // define these somewhere, [...]]]></description>
			<content:encoded><![CDATA[<p>译者注：这是我最喜欢的iPhone OpenAL教程之一，总共有好几篇文章，我会逐步翻译。</p>
<p>&nbsp;</p>
<p>随着保密协议的解除，我们可以开始公开讨论iPhone的代码了。我觉得讨论一下我&ldquo;入侵&rdquo;iPhone世界以来遇到的问题以及我是如何修复它们的可能是一个不错的话题。</p>
<p>&nbsp;</p>
<p>我目前正在编写一个iPhone游戏，它完全是以openGLES为基础并采用OpenAL音频。今天我想谈谈OpenAL</p>
<p>&nbsp;</p>
<p><span id="more-259"></span></p>
<p>今天我只讨论不到30秒的音频以及音效和短循环音。在iPhone上播放的音频需要正确的格式（或者说audio toolbox可以处理的多种音频格式中的一种，但如果你从正确的格式声音开始，iPhone在播放时就不需要进行处理了）。</p>
<p>&nbsp;</p>
<p>所以，打开你的命令行终端输入：</p>
<blockquote>
<p>&nbsp;  /usr/bin/afconvert -f caff -d LEI16@44100 inputSoundFile.aiff outputSoundFile.caf  &nbsp;</p>
</blockquote>
<p>&nbsp;</p>
<p>你可能会问这到底是在干什么？这是将文件转换为Little-Endian（低地址低字节） 16位，采样率44，100的格式。通常存储为.caf。</p>
<p>&nbsp;</p>
<p>好了！我们现在有了正确格式的.caf文件，可以开始做些什么了。</p>
<p>&nbsp;</p>
<p>有多种方法在iPhone上播放音频，有一种&#8217;简单&#8217;的方式，还有有一些&#8217;难&rsquo;的方法&#8230;&#8230;我先快速地讨论一下简单的方式，然后再讨论一下使用OpenAL的&#8217;难的方法&#8217;。</p>
<p>&nbsp;</p>
<p>让iPhone发声的最快（最容易）的方法是使用音频系统服务：</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a><span style="color: #002200;">*</span> path <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSBundle_Class/"><span style="color: #400080;">NSBundle</span></a> mainBundle<span style="color: #002200;">&#93;</span> pathForResource<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;soundEffect1&quot;</span> ofType<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;caf&quot;</span><span style="color: #002200;">&#93;</span>; &nbsp;<br />
<a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span style="color: #400080;">NSURL</span></a><span style="color: #002200;">*</span> afUrl <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span style="color: #400080;">NSURL</span></a> fileURLWithPath<span style="color: #002200;">:</span>path<span style="color: #002200;">&#93;</span>;<br />
UInt32 soundID; <br />
AudioServicesCreateSystemSoundID<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#40;</span>CFURLRef<span style="color: #002200;">&#41;</span>afUrl,<span style="color: #002200;">&amp;</span>soundID<span style="color: #002200;">&#41;</span>; &nbsp;<br />
AudioServicesPlaySystemSound <span style="color: #002200;">&#40;</span>soundID<span style="color: #002200;">&#41;</span>;</div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>这对于象点击按钮和简单UI互动之类的任务已经足够好用。但是，对于任何更复杂一点的任务（比如：游戏）而言，这简直是毫无用处。它确实会立即开始播放，但若要指定的音效与游戏的特定帧相配合的话，这种方法基本上是没有用处。（我确实曾经使用上述方法实现过我的声音引擎，然而当我将其加载到iPhone播放声音时，要么声音会晚许多帧，要么干脆整个程序会停顿，等待audio toolbox将音频加载到缓存中，实在太糟糕了）。</p>
<p>&nbsp;</p>
<p>为了更好地控制音频，你需要使用OpenAL，audioUnits或audioQueue。</p>
<p>&nbsp;</p>
<p>我决定使用OpenAL以便我的代码具有更好的跨平台能力，并且通过学习怎样使用OpenAL，我还可以将这些技术运用在iPhone外的其他平台。（而且象我这样的代码雇佣兵，我觉得具有OpenAL经验比audioQueue经验更有市场）（另外由于我已经熟悉了openGL，openAL十分类似，而audio units和audio queue的代码有那么点丑陋）。</p>
<p>&nbsp;</p>
<p>所以，这是个超快速的openAL教程，但绝对是实现由openAL产生静态声音的最低要求。</p>
<p>&nbsp;</p>
<p>OpenAL实在是十分简单，它由3个实体构成：Listener（听者），Source（音源）和Buffer（缓存）。</p>
<p>&nbsp;</p>
<p> Listener就是你。任何可以被Listener&ldquo;听到&rdquo;的声音都是来自扬声器。openAL允许你指定Listener相对于Source的位置，但是本例中我们忽略不计。我们只是针对最基本的静态声音。但是请记住&ldquo;Listener&rdquo;的概念，在你处理更复杂的情况时，你可以任意移动此对象。本文中就不做过多介绍。</p>
<p>&nbsp;</p>
<p>Source: 本质上类似与扬声器,它将产生Listener可以&ldquo;听&rdquo;到的声音。像Listener一样，你可以通过移动Source来获得groovy位置效应。本文的示例也没有涉及此部分。</p>
<p>&nbsp;</p>
<p>Buffer: 就是我们播放的声音。它保存原始的音频数据。</p>
<p>&nbsp;</p>
<p>有两个很重要的对象：device（设备）和 context（环境）。Device实际上设播放声音的硬件。而Context是当前所有声音在其中播放的&ldquo;会话（session）&rdquo;（你可以将其想象成包括所有sources和listener的房间。或者声音通过其播放的空气，或其他&#8230;&#8230;这就是Context。）</p>
<p>&nbsp;</p>
<p>它们在一起是怎么运作的：（最基本）</p>
<p>1) 获取device<br />
  2) 将context关联到device<br />
  3) 将数据放入buffer<br />
  4) 将buffer链接到一个source<br />
  5) 播放source</p>
<p>&nbsp;</p>
<p>就这么简单! 假定你的openAL实现对于listener是适当的缺省状态而且你不指定listener和source位置，上述流程工作得很好（在iPhone上就是如此）。</p>
<p>&nbsp;</p>
<p>好了，我们看看代码:</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// define these somewhere, like in your .h file &nbsp;</span><br />
&nbsp; &nbsp; ALCcontext<span style="color: #002200;">*</span> mContext; &nbsp;ALCdevice<span style="color: #002200;">*</span> mDevice; &nbsp;<br />
&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// start up openAL &nbsp;</span><br />
&nbsp; &nbsp; <span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>initOpenAL &nbsp;<br />
&nbsp; &nbsp; <span style="color: #002200;">&#123;</span> &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// Initialization &nbsp; </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; mDevice <span style="color: #002200;">=</span> alcOpenDevice<span style="color: #002200;">&#40;</span><span style="color: #a61390;">NULL</span><span style="color: #002200;">&#41;</span>; <br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// select the &quot;preferred device&quot; &nbsp;&nbsp; </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>mDevice<span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#123;</span> &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// use the device to make a context &nbsp; &nbsp; &nbsp; &nbsp; </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mContext<span style="color: #002200;">=</span>alcCreateContext<span style="color: #002200;">&#40;</span>mDevice,<span style="color: #a61390;">NULL</span><span style="color: #002200;">&#41;</span>; &nbsp;&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// set my context to the currently active one &nbsp; &nbsp; &nbsp; </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; alcMakeContextCurrent<span style="color: #002200;">&#40;</span>mContext<span style="color: #002200;">&#41;</span>; &nbsp;&nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#125;</span> &nbsp;<br />
&nbsp; &nbsp; <span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>很容易理解吧。获得&ldquo;缺省&rdquo;device，然后用它建立一个Context！完成。</p>
<p>&nbsp;</p>
<p>下一步：将数据放入buffer， 这有一点复杂:</p>
<p>&nbsp;</p>
<p>首先：打开音频文件</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; <span style="color: #11740a; font-style: italic;">// get the full path of the file &nbsp;</span><br />
&nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a><span style="color: #002200;">*</span> fileName <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSBundle_Class/"><span style="color: #400080;">NSBundle</span></a> mainBundle<span style="color: #002200;">&#93;</span> pathForResource<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;neatoEffect&quot;</span> ofType<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;caf&quot;</span><span style="color: #002200;">&#93;</span>; &nbsp;<br />
&nbsp; <span style="color: #11740a; font-style: italic;">// first, open the file &nbsp;</span><br />
&nbsp; AudioFileID fileID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>self openAudioFile<span style="color: #002200;">:</span>fileName<span style="color: #002200;">&#93;</span>;</div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>等一下！什么是openAudioFile: 方法？</p>
<p>&nbsp;</p>
<p>  这里就是:</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #11740a; font-style: italic;">// open the audio file &nbsp;</span><br />
<span style="color: #11740a; font-style: italic;">// returns a big audio ID struct &nbsp;</span><br />
<span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span>AudioFileID<span style="color: #002200;">&#41;</span>openAudioFile<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a><span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>filePath &nbsp;<br />
<span style="color: #002200;">&#123;</span> &nbsp; <br />
&nbsp; &nbsp; AudioFileID outAFID; &nbsp;&nbsp; <br />
&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// use the NSURl instead of a cfurlref cuz it is easier &nbsp; &nbsp; </span><br />
&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span style="color: #400080;">NSURL</span></a> <span style="color: #002200;">*</span> afUrl <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span style="color: #400080;">NSURL</span></a> fileURLWithPath<span style="color: #002200;">:</span>filePath<span style="color: #002200;">&#93;</span>; <br />
&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// do some platform specific stuff.. &nbsp;</span><br />
<span style="color: #6e371a;">#if TARGET_OS_IPHONE &nbsp;&nbsp; </span><br />
&nbsp; &nbsp; OSStatus result <span style="color: #002200;">=</span> AudioFileOpenURL<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#40;</span>CFURLRef<span style="color: #002200;">&#41;</span>afUrl, kAudioFileReadPermission, 0, <span style="color: #002200;">&amp;</span>outAFID<span style="color: #002200;">&#41;</span>; &nbsp;<br />
<span style="color: #6e371a;">#else &nbsp; </span><br />
&nbsp; &nbsp; OSStatus result <span style="color: #002200;">=</span> AudioFileOpenURL<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#40;</span>CFURLRef<span style="color: #002200;">&#41;</span>afUrl, fsRdPerm, 0, <span style="color: #002200;">&amp;</span>outAFID<span style="color: #002200;">&#41;</span>; &nbsp;<br />
<span style="color: #6e371a;">#endif &nbsp;&nbsp; &nbsp; </span><br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>result <span style="color: #002200;">!=</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;cannot openf file: %@&quot;</span>,filePath<span style="color: #002200;">&#41;</span>; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #a61390;">return</span> outAFID; &nbsp;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>这很简单：我们从主资源包获得文件路径，然后将其传递给本方法，本方法检查运行的平台并使用audio toolkit的方法AudioFileOpenURL()来产生一个AudioFileID。</p>
<p>&nbsp;</p>
<p>下面做什么？对，从文件中获取实际音频数据。我们要先计算出有多少数据在文件中：</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// find out how big the actual audio data is &nbsp;</span><br />
&nbsp; &nbsp; UInt32 fileSize <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>self audioFileSize<span style="color: #002200;">:</span>fileID<span style="color: #002200;">&#93;</span>;</div></td></tr></tbody></table></div>
<p>我们需要用到的另一个很实用的方法：</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #11740a; font-style: italic;">// find the audio portion of the file &nbsp;</span><br />
<span style="color: #11740a; font-style: italic;">// return the size in bytes &nbsp;</span><br />
<span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span>UInt32<span style="color: #002200;">&#41;</span>audioFileSize<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>AudioFileID<span style="color: #002200;">&#41;</span>fileDescriptor &nbsp;<br />
<span style="color: #002200;">&#123;</span> &nbsp; <br />
&nbsp; &nbsp; UInt64 outDataSize <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>; &nbsp; &nbsp; <br />
&nbsp; &nbsp; UInt32 thePropSize <span style="color: #002200;">=</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/sizeof.html"><span style="color: #a61390;">sizeof</span></a><span style="color: #002200;">&#40;</span>UInt64<span style="color: #002200;">&#41;</span>; &nbsp;&nbsp; <br />
&nbsp; &nbsp; OSStatus result <span style="color: #002200;">=</span> AudioFileGetProperty<span style="color: #002200;">&#40;</span>fileDescriptor, kAudioFilePropertyAudioDataByteCount, <span style="color: #002200;">&amp;</span>thePropSize, <span style="color: #002200;">&amp;</span>outDataSize<span style="color: #002200;">&#41;</span>; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span><span style="color: #002200;">&#40;</span>result <span style="color: #002200;">!=</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;cannot find file size&quot;</span><span style="color: #002200;">&#41;</span>; &nbsp;&nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #a61390;">return</span> <span style="color: #002200;">&#40;</span>UInt32<span style="color: #002200;">&#41;</span>outDataSize; &nbsp;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>它使用了一个神秘的方法AudioFileGetProperty()来计算出文件中有多少数据并将其放入outDataSize变量。太棒了, 下一步！</p>
<p>&nbsp;</p>
<p>现在我们已准备好将数据从文件复制到openAL缓存中：</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #11740a; font-style: italic;">// this is where the audio data will live for the moment &nbsp;</span><br />
<span style="color: #a61390;">unsigned</span> <span style="color: #a61390;">char</span> <span style="color: #002200;">*</span> outData <span style="color: #002200;">=</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/malloc.html"><span style="color: #a61390;">malloc</span></a><span style="color: #002200;">&#40;</span>fileSize<span style="color: #002200;">&#41;</span>;<br />
<span style="color: #11740a; font-style: italic;">// this where we actually get the bytes from the file and put them &nbsp;</span><br />
<span style="color: #11740a; font-style: italic;">// into the data buffer &nbsp;</span><br />
OSStatus result <span style="color: #002200;">=</span> noErr; &nbsp;<br />
result <span style="color: #002200;">=</span> AudioFileReadBytes<span style="color: #002200;">&#40;</span>fileID, <span style="color: #a61390;">false</span>, 0, <span style="color: #002200;">&amp;</span>fileSize, outData<span style="color: #002200;">&#41;</span>; &nbsp;<br />
AudioFileClose<span style="color: #002200;">&#40;</span>fileID<span style="color: #002200;">&#41;</span>; &nbsp; <span style="color: #11740a; font-style: italic;">//close the file </span><br />
<br />
<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>result <span style="color: #002200;">!=</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; NSLog<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;cannot load effect: %@&quot;</span>,fileName<span style="color: #002200;">&#41;</span>; &nbsp;<br />
&nbsp; &nbsp; <br />
NSUInteger bufferID; &nbsp;<br />
<span style="color: #11740a; font-style: italic;">// grab a buffer ID from openAL &nbsp;</span><br />
alGenBuffers<span style="color: #002200;">&#40;</span>1, <span style="color: #002200;">&amp;</span>bufferID<span style="color: #002200;">&#41;</span>; &nbsp; <br />
<span style="color: #11740a; font-style: italic;">// jam the audio data into the new buffer &nbsp;</span><br />
alBufferData<span style="color: #002200;">&#40;</span>bufferID,AL_FORMAT_STEREO16,outData,fileSize,44100<span style="color: #002200;">&#41;</span>; &nbsp; &nbsp;<span style="color: #11740a; font-style: italic;">// save the buffer so I can release it later &nbsp;</span><br />
<span style="color: #002200;">&#91;</span>bufferStorageArray addObject<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a> numberWithUnsignedInteger<span style="color: #002200;">:</span>bufferID<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#93;</span>;</div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>好了，上面我们做了很多事情（实际上，并不多）。分配一下空间给数据，使用audio toolkit中的AudioFileReadBytes()函数从文件中读取字节到分配好的内存块中。然后调用alGenBuffers()产生一个有效的bufferID，再调用alBufferData()加载数据块到openAL buffer的缓存中。</p>
<p>&nbsp;</p>
<p>这里我硬编码了格式和频率等数据。如果你是像文章开始介绍的那样使用afconvert命令生成的音频文件，那么你已经知道它们的格式和采样率了。然而，如果你想要支持各种音频格式和频率， 你最好要构建一个类似于audioFileSize:的方法，但使用kAudioFilePropertyDataFormat获取格式然后转换为适当的AL_FORMAT,，而获得频率可能更复杂些（译者注：常用的频率无非就是22050，44100，48000几种了）。我太懒了所以只确定我使用的文件格式正确就可以了。</p>
<p>下面我将此ID放入一个NSArray以备参考，你可以以后随时使用。</p>
<p>&nbsp;</p>
<p>好，我们现在准备好了缓存区。是将它连到source的时候了。</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">NSUInteger sourceID; &nbsp; <br />
<span style="color: #11740a; font-style: italic;">// grab a source ID from openAL &nbsp;</span><br />
alGenSources<span style="color: #002200;">&#40;</span>1, <span style="color: #002200;">&amp;</span>sourceID<span style="color: #002200;">&#41;</span>; &nbsp; <br />
<span style="color: #11740a; font-style: italic;">// attach the buffer to the source &nbsp;</span><br />
alSourcei<span style="color: #002200;">&#40;</span>sourceID, AL_BUFFER, bufferID<span style="color: #002200;">&#41;</span>; &nbsp;<br />
<span style="color: #11740a; font-style: italic;">// set some basic source prefs &nbsp;</span><br />
alSourcef<span style="color: #002200;">&#40;</span>sourceID, AL_PITCH, 1.0f<span style="color: #002200;">&#41;</span>; &nbsp;<br />
alSourcef<span style="color: #002200;">&#40;</span>sourceID, AL_GAIN, 1.0f<span style="color: #002200;">&#41;</span>; &nbsp;<br />
<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>loops<span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; alSourcei<span style="color: #002200;">&#40;</span>sourceID, AL_LOOPING, AL_TRUE<span style="color: #002200;">&#41;</span>; &nbsp;<br />
&nbsp;<br />
<span style="color: #11740a; font-style: italic;">// store this for future use &nbsp;</span><br />
<span style="color: #002200;">&#91;</span>soundDictionary setObject<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a> numberWithUnsignedInt<span style="color: #002200;">:</span>sourceID<span style="color: #002200;">&#93;</span> forKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;neatoSound&quot;</span><span style="color: #002200;">&#93;</span>; &nbsp; <br />
<span style="color: #11740a; font-style: italic;">// clean up the buffer &nbsp;if (outData) &nbsp;</span><br />
<span style="color: #002200;">&#123;</span> &nbsp; <br />
&nbsp; &nbsp; <a href="http://www.opengroup.org/onlinepubs/009695399/functions/free.html"><span style="color: #a61390;">free</span></a><span style="color: #002200;">&#40;</span>outData<span style="color: #002200;">&#41;</span>; &nbsp;&nbsp; &nbsp; <br />
&nbsp; &nbsp; outData <span style="color: #002200;">=</span> <span style="color: #a61390;">NULL</span>; &nbsp;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>像缓存一样，我们也需要从openAL获取一个有效的sourceID。一旦我们获得了sourceID我们就可以将source和缓存联系起来了。最后我们还要进行一些缓存基本设定。如果我们想循环播放，还要设定AL_LOOPING为true。 缺省时，播放是不循环的，所以忽略它就好。然后我将此ID存入到字典数据结构中，以便可以根据名称查找ID。</p>
<p>&nbsp;</p>
<p>最后，清除临时内存。</p>
<p>&nbsp;</p>
<p>大功即将告成！现在我们只剩下播放功能了：</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #11740a; font-style: italic;">// the main method: grab the sound ID from the library &nbsp;</span><br />
<span style="color: #11740a; font-style: italic;">// and start the source playing &nbsp;</span><br />
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>playSound<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a><span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>soundKey &nbsp;<br />
<span style="color: #002200;">&#123;</span> &nbsp; <br />
&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a><span style="color: #002200;">*</span> numVal <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>soundDictionary objectForKey<span style="color: #002200;">:</span>soundKey<span style="color: #002200;">&#93;</span>; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>numVal <span style="color: #002200;">==</span> <span style="color: #a61390;">nil</span><span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">return</span>; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; NSUInteger sourceID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>numVal unsignedIntValue<span style="color: #002200;">&#93;</span>; &nbsp;&nbsp; <br />
&nbsp; &nbsp; alSourcePlay<span style="color: #002200;">&#40;</span>sourceID<span style="color: #002200;">&#41;</span>; &nbsp;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>就是它了， alSourcePlay()&#8230;&#8230; 很简单吧。如果声音不循环，那么它将会自然停止。如果是循环的，你可能需要停止它：</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>stopSound<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a><span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>soundKey &nbsp;<br />
<span style="color: #002200;">&#123;</span> &nbsp; <br />
&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a><span style="color: #002200;">*</span> numVal <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>soundDictionary objectForKey<span style="color: #002200;">:</span>soundKey<span style="color: #002200;">&#93;</span>; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>numVal <span style="color: #002200;">==</span> <span style="color: #a61390;">nil</span><span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">return</span>; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; NSUInteger sourceID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>numVal unsignedIntValue<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; alSourceStop<span style="color: #002200;">&#40;</span>sourceID<span style="color: #002200;">&#41;</span>; &nbsp;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>以上基本上就是使用openAL在iPhone播放声音的最快速和简单的方法（至少我是这样认为的）。</p>
<p>&nbsp;</p>
<p>最后，我们要做些清理工作：</p>
<div class="codecolorer-container objc mac-classic" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>cleanUpOpenAL<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>sender &nbsp;<br />
<span style="color: #002200;">&#123;</span> &nbsp; <br />
&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// delete the sources &nbsp; </span><br />
&nbsp; &nbsp; <span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a><span style="color: #002200;">*</span> sourceNumber <span style="color: #a61390;">in</span> <span style="color: #002200;">&#91;</span>soundDictionary allValues<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; <span style="color: #002200;">&#123;</span> &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; NSUInteger sourceID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>sourceNumber unsignedIntegerValue<span style="color: #002200;">&#93;</span>; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; alDeleteSources<span style="color: #002200;">&#40;</span>1, <span style="color: #002200;">&amp;</span>sourceID<span style="color: #002200;">&#41;</span>; &nbsp;&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #002200;">&#125;</span> &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>soundDictionary removeAllObjects<span style="color: #002200;">&#93;</span>; &nbsp;<br />
&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// delete the buffers &nbsp; </span><br />
&nbsp; &nbsp; <span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/"><span style="color: #400080;">NSNumber</span></a><span style="color: #002200;">*</span> bufferNumber <span style="color: #a61390;">in</span> bufferStorageArray<span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; <span style="color: #002200;">&#123;</span> &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; NSUInteger bufferID <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>bufferNumber unsignedIntegerValue<span style="color: #002200;">&#93;</span>; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; alDeleteBuffers<span style="color: #002200;">&#40;</span>1, <span style="color: #002200;">&amp;</span>bufferID<span style="color: #002200;">&#41;</span>; &nbsp;&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #002200;">&#125;</span> &nbsp; <br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>bufferStorageArray removeAllObjects<span style="color: #002200;">&#93;</span>; &nbsp;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// destroy the context &nbsp;&nbsp; &nbsp; </span><br />
&nbsp; &nbsp; alcDestroyContext<span style="color: #002200;">&#40;</span>mContext<span style="color: #002200;">&#41;</span>; &nbsp;&nbsp; <br />
&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// close the device &nbsp; &nbsp; </span><br />
&nbsp; &nbsp; alcCloseDevice<span style="color: #002200;">&#40;</span>mDevice<span style="color: #002200;">&#41;</span>; &nbsp;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<p>注意：在实际应用中你可能有不只一个source（我的每个buffer都有一个source，但我只有8组声音所以不会有什么问题）。而可以使用的source数目是有上限的。我不知道iPhone上的实际数字，但可能是16或32之类。（译者注：我有一个应用程序使用了30个source，没有什么问题）。处理此类问题的方法是加载你的缓存，然后动态分配给下一个可用的source（即没有正在进行播放的source）。</p>
<p>&nbsp;</p>
<p>参考资料： <a href="http://connect.creativelabs.com/openal/Documentation/OpenAL_Programmers_Guide.pdf" onclick="javascript:pageTracker._trackPageview('/outbound/article/http://connect.creativelabs.com/openal/Documentation/OpenAL_Programmers_Guide.pdf');">openAL programmers guide</a>。</p>
<p>&nbsp;</p>
<h5>原文见：<a title="Permanent Link to openAL sound on the iPhone" href="http://benbritten.com/2008/11/06/openal-sound-on-the-iphone/" rel="bookmark">openAL sound on the iPhone</a> 作者：Ben Britten Smith</h5>
]]></content:encoded>
			<wfw:commentRss>http://www.iphone-geek.cn/%e7%bc%96%e7%a8%8b/openal%e7%b3%bb%e5%88%97%e4%b9%8b%e4%b8%80-iphone%e4%b8%8a%e7%9a%84openal%e9%9f%b3%e9%a2%91/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

