谈到这个点的时候,我首先想到的是苹果公司是如何从濒临破产到重新起步直至现在这个状况的。进入我脑海的是这么一些名字:iMAC,iPod,iPod,以及 iPod
当然,说苹果靠做 MP3 播放器打了一个翻身仗未免太过于 TROLL,事实上更关键的原因在于乔布斯很早就预见了网络销售的可行性,促使他打造了 AppStore 这个平台。不过这些细节大可不必在意,关键点在于:苹果确实做的一手好 MP3. 所以顺理成章的,iPod 优越的音频特性,在很大程度上转接到了 iPhone 身上。理论上我们可以认真的说:iPhone 是当前手机平台上音频出色表现最出色的一款,没有之一。
那么,iPhone 的音频系统究竟出色在哪里?从硬件上来说,再好的手机你也不可能驱动一个功率30瓦的音响吧(阻抗原因),没有30瓦的音响怎么听音乐?咯咯咯咯咯。而就算是耳机,大家也清楚的明白,苹果的耳机质量那必然不是市面上最好的。毕竟前有漫步者后有索尼。从硬件的角度上讲,iPhone 没有可能,也没有这个资格领先其他手机一个世纪。我们说 iPhone 的音频系统很出色的真正原因是因为它的软件系统。我们今天要谈到的就是其中一个。
首先,想像一个场景:用户起床,打开自己的 iPhone 音乐,泡上咖啡,然后打开一个有声读物。这个时候他设定的起床铃响了,他把它关掉。于此同时,来了一个电话。以上这个场景的过程可能不过十秒钟。在这十秒钟内,用户期待的是一个光滑的音频衔接。比如在有声读物播放的时候,背景音乐不要停。闹铃按掉或者电话接完之后,所有的声音都要回复正常。对于程序来说(就比如这个有声读物程序),这是一种吹毛求疵的体验,然而乔布斯(作为一个嬉皮士)正是给苹果设计了这样一个东西:我们要做的,就是满足用户这些需求。
iPhone 的音频系统都统一的处于操作系统的管理之下。在程序中我们能够调用的接口是一个单例 AVAudioSession 或者一系列 AudioSession 的 C 函数。以 AVAudioSession 为例,通过这个单例我们可以配置自己的音频播放能力,调控其余音频的混流,以及应对音频中断。程序可以通过几行代码向操作系统发送请求,然后让操作系统来做出选择,进行最终的调控。大体上,我们的程序需要应对的事情,就是以下几种。如果能设置好的话,那么这个程序在音频系统上来说,就是一个负责任的程序。
1. 设置你的音频能力
- (BOOL)setCategory:(NSString *)category error:(NSError **)outError;
根据设置的 category 的不同,你的程序会有不同的音频优先级以及音频能力。默认的情况下,你的程序只有播放能力没有输入能力,并且你的程序不会允许其他程序播放声音,同时,静音开关会把你的程序静音掉。而通过 category 的设置,你的程序可以对这三个行为特征都做出改变。比如一个录音程序就会把自己配置为 AVAudioSessionCategoryRecord 这样就可以避免在录音过程中遭受锁屏,系统警告声的音响。
还有值得一提的就是,只有 AVAudioSessionPlayAndRecord 才允许修改默认音频播放设备:听筒还是外放。所以,通过音频设备来做音频导向的同学们请注意:你们完全找错了方向。
2.打开/关闭你的音频系统
当你执行任何音频操作的时候,比如播放一段音乐,或者停止播放一段音乐。你都有责任把这个行为告知系统,这样才能够确保系统能够正确的根据你的音频能力设置以及其他你正在运行的程序的音频能力设置,正确的做出混音,消音等操作。
- (BOOL)setActive:(BOOL)active error:(NSError **)outError;
当你把自己的音频置为 On 的时候,系统就会对其他正在播放音频的程序发送中断。特别的是,如果你关闭了自己的音频的系统。任何有责任感的程序都会获取到这个消息,从而重新开始自己的任务。这点对于保持用户延续性的音乐播放至关重要。
3.处理音频中断
手机上最常见的中断就是电话。或者也有用户切到程序外面打开音乐播放器的情况。特别是在电话的过程中,用户必然会丧失自己对音频系统的控制。也就是说被赶下了主干道。在之前的系统上我们可以使用 AVAudioSessionDelegate 来监听这个时间,在 6.0 上我们有 AVAudioSessionInterruptionNotification 这么一个东西(当然我说不清楚为什么苹果要做这样一个改动,可能他们真的把音频这一块儿看的特别的重)。通过回调函数我们可以在中断来临的时候做出正确的反应。最好的例子就是在拒绝接听电话的时候:此时程序没有进入后台,最好的办法就是让自己的
AVAudioSession 重启,然后继续自己的音乐播放。
当然,要注意了:如果通过 C 的函数来写的话,中断的回调只能在初始化也就是 AudioSessoinInitialize 里面设置!(我想这也就是苹果在 6.0 的系统里提供新方法的原因之一吧?)
当然,最后还有一点。千万别忘记在自己的 info.plist 里面加上 App plays audio 这一项,否则说了那么多都是白搭!