在 iPhone 应用或者是游戏的开发过程中,对声音的支持是必不可少的。在我做过的几个应用中,每个都涉及到音效,所以在这里做个简单的归纳,很多都是引用自《iPhone Application Programming Guide》 (需要有 Apple ID 才能打开链接),加了一些实际使用的经验。

iPhone OS 主要提供以下了几种播放音频的方法:

  • System Sound Services
  • AVAudioPlayer 类
  • Audio Queue Services
  • OpenAL


1. System Sound Services

System Sound Services 是最底层也是最简单的声音播放服务,调用 AudioServicesPlaySystemSound 这个方法就可以播放一些简单的音频文件,使用此方法只适合播放一些很小的提示或者警告音,因为它有很多限制:

■ 声音长度要小于 30 秒
■ In linear PCM 或者 IMA4 (IMA/ADPCM) 格式的
■ 打包成 .caf, .aif, 或者 .wav 的文件
■ 不能控制播放的进度
■ 调用方法后立即播放声音
■ 没有循环播放和立体声控制

另外,它还可以调用系统的震动功能,方法也很简单。具体的代码可以参考官方的示例 SysSound
,但是官方的示例只有一些简单的用法,从文档中我们发现可以通过 AudioServicesAddSystemSoundCompletion 方法为音频播放添加 CallBack 函数,有了 CallBack 函数我们可以解决不少问题,比如可以克服 System Sound Services 本身不支持循环播放的问题。以下代码可以实现一个在程序中循环播放的背景音乐:

 1 static  void  completionCallback (SystemSoundID  mySSID) {
2 // Play again after sound play completion
3 AudioServicesPlaySystemSound(mySSID);
4 }
5 - (void ) playSound {
6 // Get the main bundle for the app
7 CFBundleRef mainBundle;
8 SystemSoundID soundFileObject;
9 mainBundle = CFBundleGetMainBundle ();
10
11 // Get the URL to the sound file to play
12 CFURLRef soundFileURLRef = CFBundleCopyResourceURL (
13 mainBundle,
14 CFSTR ("background" ),
15 CFSTR ("wav" ),
16 NULL
17 );
18 // Create a system sound object representing the sound file
19 AudioServicesCreateSystemSoundID (
20 soundFileURLRef,
21 &soundFileObject
22 );
23 // Add sound completion callback
24 AudioServicesAddSystemSoundCompletion (soundFileObject, NULL , NULL ,
25 completionCallback,
26 (void *) self );
27 // Play the audio
28 AudioServicesPlaySystemSound(soundFileObject);
29
30 }
 
 
另外一段:
 #include <AudioToolbox/AudioToolbox.h>
#include <CoreFoundation/CoreFoundation.h>
// Define a callback to be called when the sound is finished
// playing. Useful when you need to free memory after playing.
static void MyCompletionCallback (
SystemSoundID mySSID,
void * myURLRef
) {
AudioServicesDisposeSystemSoundID (mySSID);
CFRelease (myURLRef); //CFURLCreateWithFileSystemPath()创建的需释放

CFRunLoopStop (CFRunLoopGetCurrent());
}
int main (int argc, const char * argv[]) {
// Set up the pieces needed to play a sound.
SystemSoundID mySSID;
CFURLRef myURLRef;
myURLRef = CFURLCreateWithFileSystemPath (
kCFAllocatorDefault,
kCFURLPOSIXPathStyle,
FALSE
);
// create a system sound ID to represent the sound file
OSStatus error = AudioServicesCreateSystemSoundID (myURLRef,
&mySSID);
// Register the sound completion callback.
// Again, useful when you need to free memory after playing.
AudioServicesAddSystemSoundCompletion (
mySSID,
NULL,
NULL,
MyCompletionCallback,
(void *) myURLRef
);
// Play the sound file.
AudioServicesPlaySystemSound (mySSID);
// Invoke a run loop on the current thread to keep the application
// running long enough for the sound to play; the sound completion
// callback later stops this run loop.
CFRunLoopRun ();
return 0;
}

 

转自:javascript:void(0)

对于简单的、无混音音频,AVAudio ToolBox框架提供了一个简单的C语言风格的音频服务。你可以使用AudioservicesPlaySystemSound函数来播放简单的声音。要遵守以下几个规则:

1.音频长度小于30秒

2.格式只能是PCM或者IMA4

3.文件必须被存储为.caf、.aif、或者.wav格式

4.简单音频不能从内存播放,而只能是磁盘文件

除了对简单音频的限制外,你对于音频播放的方式也基本无法控制。一旦音频播放就会立即开始,而且是当前电话使用者设置的音量播放。你将无法循环播放声音,也无法控制立体声效果。不过你还是可以设置一个回调函数,在音频播放结束时被调用,这样你就可以对音频对象做清理工作,以及通知你的程序播放结束。

#import <AudioToolbox/AudioToolbox.h>  
#import <CoreFoundation/CoreFoundation.h>
//当音频播放完毕会调用这个函数
static void SoundFinished(SystemSoundID soundID,void* sample){
/*播放全部结束,因此释放所有资源 */
AudioServicesDisposeSystemSoundID(sample);
CFRelease(sample); //alloc创建的需释放
CFRunLoopStop(CFRunLoopGetCurrent());
}
//主循环
int main(){
/*系统音频ID,用来注册我们将要播放的声音*/
SystemSoundID soundID;
NSURL* sample = [[NSURL alloc]initWithString:@"sample.wav"];

OSStatus err = AudioServicesCreateSystemSoundID(sample, &soundID);
if (err) {
NSLog(@"Error occurred assigning system sound!");
return -1;
}
/*添加音频结束时的回调*/
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, SoundFinished,sample);
/*开始播放*/
AudioServicesPlaySystemSound(soundID);
CFRunLoopRun();
return 0;
}

个人觉得这个音频服务有点鸡肋,不过它肯定有它的用武之地,比如我们要播放一个自定义的警告音或者消息提示,用音频服务肯定比其他的方法节省资源。

 
 
2. AVAudioPlayer 类


AVAudioPlayer 是 AVFoundation.framework 中定义的一个类,所以使用要先在工程中引入 AVFoundation.framework。我们可以把 AVAudioPlayer 看作是一个高级的播放器,它支持广泛的音频格式,主要是以下这些格式:

■ AAC
■ AMR(AdaptiveMulti-Rate, aformatforspeech)
■ ALAC(AppleLossless)
■ iLBC(internetLowBitrateCodec, anotherformatforspeech)
■ IMA4(IMA/ADPCM)
■ linearPCM(uncompressed)
■ µ-lawanda-law
■ MP3(MPEG-1audiolayer3

AVAudioPlayer 可以播放任意长度的音频文件、支持循环播放、可以同步播放多个音频文件、控制播放进度以及从音频文件的任意一点开始播放等,更高级的功能可以参考 AVAudioPlayer 的文档 。要使用 AVAudioPlayer 的对象播放文件,你只需为其指定一个音频文件并设定一个实现了 AVAudioPlayerDelegate 协议的 delegate 对象。这里举一个简单的例子,和上一个例子一样,实现一直循环播放的背景音乐:

 1 - (void ) playBackgroundSoundEffect {
2 NSString *soundFilePath =
3 [[NSBundle mainBundle] pathForResource: @"background"
4 ofType: @"wav" ];
5 NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
6 AVAudioPlayer *newPlayer =
7 [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
8 error: nil ];
9 [fileURL release];
10 self .player = newPlayer;
11 [newPlayer release];
12 [self .player prepareToPlay];
13
14 [self .player setDelegate: self ];
15 self .player.numberOfLoops = -1 ; // Loop playback until invoke stop method
16 [self .player play];
17 }

可以看到,只要将 AVAudioPlayer 的 numberOfLoops 属性设为负数,音频文件就会一直循环播放直到调用 stop 方法。

AVAudioPlayer 同样支持 Callback,这是 AVAudioPlayerDelegate 的一个可选 delegate 方法:

- (void ) audioPlayerDidFinishPlaying: (AVAudioPlayer *) player successfully: (BOOL ) flag { 
if (player == self .player && flag == YES) {
NSLog(@"Playback finish." );
}
}

另外,你可以随时控制 AVAudioPlayer 对象的播放、暂停以及停止,通过判断对象的状态,分别调用 play、pause 和 stop 方法即可:

- (IBAction) playOrPause: (id ) sender { 
// if playing, pause
if (self .player.playing) {
[self .player pause];
// if stopped or paused, start playing
} else {
[self .player play];
}

虽然 AVAudioPlayer 可以播放很多格式,但是我们在实际开发过程中还是最好使用一些没有压缩的格式,比如 WAVE 文件,这样可以减少系统处理单元的资源占用,以便更好的完成程序的其他功能。另外,在使用 AVAudioPlayer 连续播放 mp3 这类经过压缩的音频文件时,在连接处可能出现一定的间隔时间。

3. Audio Queue Services

如果以上两种音频播放的解决方案都无法满足你的需求,那么我想你肯定需要使用 Audio Queue Services。使用 Audio Queue Services 对音频进行播放,你可以完全实现对声音的控制。例如,你可以在声音数据从文件读到内存缓冲区后对声音进行一定处理再进行播放,从而实现对音频的快速/慢速 播放的功能。

因为 Audio Queue Services 相对复杂很多,Apple 官方已经把它整理为一本书了,具体可以参考 Audio Queue Services Programming Guide 和 SpeakHere 的程序示例。

4. OpenAL

OpenAL 是一套跨平台的开源的音频处理接口,与图形处理的 OpenGL 类似,它为音频播放提供了一套更加优化的方案。它最适合开发游戏的音效,用法也与其他平台下相同。

iPhone 支持 OpenAL 1.1,我没有在实际开发中使用过,具体的文档可以参考 OpenAL 的网站 http://openal.org 和 oalTouch 的程序示例。