在iPhone游戏中播放声音 - 减速问题

4

我正在使用AVAudioPlayer在我的应用程序中的单独线程上播放声音效果。目前,我的应用程序相当简单,我有一个UIScrollView用于滚动背景,以及几个CALayers来表示玩家和敌人在简单的侧卷屏幕游戏中(类似马里奥或功夫等)...我可以向左和向右移动玩家,视图随之滚动而没有任何问题。我有一个“攻击”按钮,它会播放一个声音效果...但是很多时候当我播放声音效果时,我会在图形上出现一点小卡顿。这在使用Quartz的iPhone上是否正常?有没有办法让它更加流畅,而无需开始调查OpenGL?

8个回答

4
使用NSData dataWithContentsOfMappedFile代替dataWithContentsOfFile。
另外,AVAudioPlayer处理自己的线程,您不需要创建它。 [AVAudioPlayer play]在开始播放后返回,而不是在播放完成后返回。
还要记得释放AVAudioPlayer,例如,如果您只想播放一次声音,则应在audioPlayerDidFinishPlaying:successfully:代理中实现并在那里释放它。

2

我也遇到了减速的问题。为了解决这个问题,我首先在音频加载时以音量0播放并暂停它 - 这比使用prepareToPlay更有效,后者似乎仍然会导致减速:

player.volume = 0.0;
[player play];
[player pause];
player.volume = 1.0;

当需要播放声音时,我只需使用[player play];。然后,每个更新周期,我循环遍历所有活动声音并捕获即将结束的声音,并重新开始播放它们,而不是让它们过期 - 这避免了从头开始启动声音的开销和减速。此示例在声音结束前1/15秒停止声音:

for ({each player in active sounds})
{
  if ((player.duration - player.currentTime) <= (1.0/15.0))
  {
    [player pause];
    player.currentTime = 0.0;
  }
}

现在它已经准备好进行下一次播放了。似乎工作得很好!

更新:结果发现第一次播放声音时仍然有些暂停。我认为,以零音量播放实际上并没有做任何事情,但我将其更改为以0.0001的音量播放,然后暂停-虽然听不到,但似乎能够完成准备声音的工作。


2

起初我也遇到了同样的问题。根据文档,每次声音播放完成后都需要调用 "prepareToPlay" 方法。采用 AVAudioPlayerDelegate 协议并添加以下方法即可实现。

-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
    [player prepareToPlay];
}

2

你是如何播放声音的?如果我猜的话,每次播放声音时,操作系统实际上会从磁盘加载它并播放。

更新:

好的,你真的不需要创建一个新线程来播放声音。这样做可能会导致游戏卡顿,并且也是多余的 - AVAudioPlayer的播放方法是异步的 - 它立即返回,而不是在声音结束时返回。


1
(为了长度和展示代码,我使用回答而不是评论):
我的声音播放方法如下:
- (void)playSoundThreaded:(id)soundPlayer
{
  if(soundPlayer == nil)
    return;

  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  [(AVAudioPlayer*)soundPlayer play];
  [pool release];
}

- (void)playSound:(AVAudioPlayer*)player
{
  if(player == nil)
    return;

  [NSThread detachNewThreadSelector:@selector(playSoundThreaded:)
    toTarget:self withObject:(id)player];
}

声音的加载方式如下:

fxPlayer = [[AVAudioPlayer alloc] initWithData:
  [NSData dataWithContentsOfFile:
    [[NSBundle mainBundle] pathForResource:@"voicehiya" ofType:@"caf"]] error:NULL];

我们有一些线索表明声音每次都从磁盘重新加载...但不知道为什么会这样或如何防止它。


1

我认为这个线程过于复杂化了你的播放器。试试这个:

/* setup the sound player */
AVAudioPlayer *player1=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL     fileURLWithPath:audioFilePath] error:NULL];
player1.numberOfLoops = 0;
player1.volume=1.0f;
[player1 prepareToPlay];

然后playSound变成

[player play];

不要忘记正确设置AudioSession类别。


1
为什么不直接使用AudioServicesPlaySystemSound呢?这个函数非常适合播放音效。
我在我的游戏(vConqr)中使用它,该游戏也基于UIScrollView,并且有许多其他视图(因此所有视图都有自己的CALayer),但它并不会对性能产生显著影响。

AudioServicesPlaySystemSound没有独立音量控制。我最初是这样做的,它没有杂音。 - GoatRider
分别控制每个音效的音量真的值得吗?大多数人只是想让它们开启或关闭。 - philsquared
当然,这取决于应用程序。我正在开发一个模拟器,其中球互相弹跳- 我希望每次碰撞的音量与碰撞的速度成比例。 - GoatRider
啊,那样的话我同意你的观点。 - philsquared

0

我已经调查了几天了,感谢我在工作中的一个项目。我为我的代码添加了一些自定义的分析,并发现播放声音的调用导致了OpenGL游戏的帧率问题。根据我的发现,我有一些建议:

  1. 使用AVAudioPlayer播放mp3格式的背景音乐。

  2. 使用OpenAL播放音效。

  3. 对于音效,使用苹果的音频转换命令行工具将您的声音转换为.caf格式。这是一种无损压缩格式,不需要CPU在将其发送到声卡之前将其解压缩到内存中。它可以直接进行,节省时间。使用该工具的说明可以在此处找到。(滚动到页面底部)

  4. 使用最低的比特深度,但仍然听起来不错。你真的不需要128 kbps - 对于很多事情,你可能只需要20 kbps就足够了。更低的比特率=更少的处理=更快的声音。

如果我发现其他有用的东西,我会让你知道的!祝你好运!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接