1理解音频会话。
所有的iOS应用程序都具有音频会话,无论其是否使用。默认音频会话来自以下一些配置:
1.激活了音频播放,但是音频录制未激活。
2.当用户切换响铃/静音开关到‘静音’模式时,应用程序播放的所有音频都会消息。
3.当设备显示解锁屏幕时,应用程序音频处于静音状态。
4.当应用程序播放音频时,所有后台播放的音频都会处于静音状态。
WechatIMG148.jpeg
2.配置音频会话
音频会话在应用的生命周期中是可以修改的。通常只配置一次,就是在应用启动的时候。配置音频会话的最佳位置就是应用程序委托的application:didFinishLaunchingWithOptions:方法。
AVAudioSession *ssion =[AVAudioSession sharedInstance];
NSError *error ;
if (![ssion setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"Category Error:%@",[error localizedDescription]);
}
if (![ssion setActive:YES error:&error]) {
NSLog(@"Activation Error:%@",[error localizedDescription]);
}
```
开发者可为音频的播放指定需要的音频会话,其中定制一些行为。最后告知该音频会话激活该配置。
#3.使用AVAudioPlayer播放音频
AVAudioPlayer构建于Core Audio中的C-based Audio Queue Services 的最顶层。所以它可以提供所有在Audio Queue Service中所能找到的核心功能,比如播放、循环甚至音频计量,但使用的是非常简单友好的OC接口,除非你需要从网络流中播放音频、需要访问原始音频样本或者需要非常低的时延,否则AVAudioPlayer都能胜任。
#创建AVAudioPlayer
使用包含要播放音频的内存版本的NSData,或者本地音频文件的NSURL。
//创建AVAudioPlayer
NSURL * fileUrl =[[NSBundle mainBundle] URLForResource:@"rock" withExtension:@"mp3"];
AVAudioPlayer * player =[[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:nil];
if (player) {
[player prepareToPlay];
}
在播放前调用preparePlay 原因是:这样做会取得需要的音频硬件并预加载Audio Queue的缓冲区。降低play延迟时间。
#对播放进行控制
player.volume = 1.0f;//修改播放器的音量,独立于系统的播放音量。0.-1从静音到最大。
player.pan =-1.0f;//允许使用立体声音播放:范围从-1.0到1.0 默认局中为0.
player.rate =0.5f;//调整播放速率:范围从0.5到2.0 半速到2倍。
player.numberOfLoops =2.0f;//实现无缝循环 当设置值大于0时,循环次数为设置的值 为-1时则是无限循环播放。
```
4.创建一个AVdio Looper项目
头文件。
@interface THVideoPlayer : NSObject
//播放
-(void)play;
//暂停
-(void)stop;
//修改速率
-(void)adjustRate:(float)rate;
//修改立体效果
-(void)adjustPan:(float)pan forPlayerAtIndex:(NSUInteger)index;
//修改声音
-(void)adjustVolume:(float)volume forPlayerAtIndex:(NSUInteger)index;
/*!
播放控制定义了play、stop和adjustRate方法,一起对三个播放器实例的播放行为进行控制。
*/
@end
实现文件内容。
#import "THVideoPlayer.h"
#import <AVFoundation/AVFoundation.h>
@interface THVideoPlayer ()
@property(nonatomic,assign) BOOL playing;
@property(nonatomic,strong)NSArray * players;
@end
@implementation THVideoPlayer
-(instancetype)init {
self =[super init];
if (self) {
AVAudioPlayer * guitarPlayer =[self playerForFile:@"guitar"];
AVAudioPlayer *bassPlayer =[self playerForFile:@"bass"];
AVAudioPlayer *drumPlayer=[self playerForFile:@"drums"];
_players =@[guitarPlayer,bassPlayer,drumPlayer];
}
return self;
}
-(AVAudioPlayer *)playerForFile:(NSString *)name {
NSURL * fileUrl =[[NSBundle mainBundle] URLForResource:name withExtension:@"caf"];
NSError * error ;
AVAudioPlayer * player =[[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:&error];
if (player) {
player.numberOfLoops = -1;
player.enableRate = YES;
[player prepareToPlay];
}else {
NSLog(@"Error creating player:%@",[error localizedDescription]);
}
return player;
}
-(void)play {
if (!self.playing) {
NSTimeInterval dalayTime =[self.players[0] deviceCurrentTime] +0.01;
for (AVAudioPlayer * player in self.players) {
[player playAtTime:dalayTime];
}
self.playing = YES;
}
//要对三个播放器实例的播放进行同步,需要捕捉当前设备时间并添加一个小延时,这样就会具有一个从开始播放时间计算的参照时间。通过对每个实例调用playAtTime:方法并传递延时参照时间,遍历播放器数组并开始播放。这保证了这些播放器在音频播放时始终c保持紧密同步。
}
-(void)stop {
if (self.playing) {
for (AVAudioPlayer * player in self.players) {
[player stop];
player.currentTime =0.0f;
}
self.playing = NO;
}
//stop方法很直接,如果音频正在播放,遍历播放器数组并对每一个对象调用stop方法。开发者还需要为设置每一个播放器的currentTime属性为0.0f,这样做会让播放进度回到音频文件的原点。
}
-(void)adjustRate:(float)rate {
for (AVAudioPlayer *player in self.players) {
player.rate = rate;
}
}
//控制单独的音频->代码中'adjust方法会收到一个浮点类型的调整值和一个用于表示所修改播放器的索引值。pan值q区间为-1.0(极左)到(1.0)极右,音量为区间0.0-1.0'
-(void)adjustPan:(float)pan forPlayerAtIndex:(NSUInteger)index {
if ([self isValidIndex:index]) {
AVAudioPlayer * player =self.players[index];
player.pan = pan;
}
}
-(void)adjustVolume:(float)volume forPlayerAtIndex:(NSUInteger)index {
if ([self isValidIndex:index]) {
AVAudioPlayer * player =self.players[index];
player.volume = volume;
}
}
//范围容错
-(BOOL)isValidIndex:(NSUInteger)index {
return index == 0 || index < self.players.count;
}
@end
注意此时并不能完全后台播放。需要注意打开info.plist添加一个新的Required background modes类型的数组,在其中添加名为App plays audio or streams audio/video using AirPlay
5.处理中断事件
当发生中断时,播放中的音频会慢慢消失和暂停。这一效果是系统实现。当中断事件结束后,当前播放器播放按钮处于不可用状态,音频并不能如期恢复。需要解决。
-(instancetype)init {
self =[super init];
if (self) {
AVAudioPlayer * guitarPlayer =[self playerForFile:@"guitar"];
AVAudioPlayer *bassPlayer =[self playerForFile:@"bass"];
AVAudioPlayer *drumPlayer=[self playerForFile:@"drums"];
_players =@[guitarPlayer,bassPlayer,drumPlayer];
//注册中断通知
NSNotificationCenter * nsnc =[NSNotificationCenter defaultCenter];
[nsnc addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
}
return self;
}
移除通知
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
处理通知事件
-(void)handleInterruption:(NSNotification *)notification {
//在处理通知的方法中,首先通过检索AVAudioSessionInterruptionTypeKey的值确定中断类型(type).返回值是AVAudioSessionInterruptionType,这是用于表示中断开始或结束的枚举类型。
NSDictionary *info =notification.userInfo;
AVAudioSessionInterruptionType type =[info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (type == AVAudioSessionInterruptionTypeBegan) {
//停止播放时候。可以用代理让控制器更新界面 操作一些其他事情
}else {
//中断事件结束了
AVAudioSessionInterruptionOptions options =[info [AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (options == AVAudioSessionInterruptionOptionShouldResume) {
//重新播放
[self play];
}
}
}
6.对线路改变的响应
在iOS设备上添加或移除音频输入、输出线路时,会发生线路改变。如插入耳机、断开USB麦克风。当这些事件发生时,音频会根据情况改变输入或暑输出线路。
//注册线路改变通知
[nsnc addObserver:self selector:@selector(handleRouteChange:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
处理线路改变事件->当拨出耳机中断播放
//判断通知原因
-(void)handleRouteChange:(NSNotification *)notification {
NSDictionary * info =notification.userInfo;
AVAudioSessionRouteChangeReason reason =[info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
//当老设备不可用。
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
//获取前一个线路的描述
AVAudioSessionRouteDescription * previous =info[AVAudioSessionRouteChangePreviousRouteKey];
//获取前一个设备的I/O接口属性。
AVAudioSessionPortDescription * previousOutput = previous.outputs[0];
NSString * portType = previousOutput.portType;
if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
//当拔出耳机的时候停止播放。
[self stop];
}
}
}
网友评论