AudioToolbox-播放和录制音频

使用System Sound Service播放音效

参考 http://www.cnblogs.com/kenshincui/p/4186022.html

步骤

  1. 调用AudioServicesCreateSystemSoundID( CFURLRef inFileURL, SystemSoundID* outSystemSoundID)函数获得系统声音ID。
  2. 如果需要监听播放完成操作,则使用AudioServicesAddSystemSoundCompletion( SystemSoundID inSystemSoundID, CFRunLoopRef inRunLoop, CFStringRef inRunLoopMode, AudioServicesSystemSoundCompletionProc inCompletionRoutine, void* inClientData)方法注册回调函数。
  3. 调用AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID)或者AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID) 方法播放音效(后者带有震动效果)。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#import <AudioToolbox/AudioToolbox.h>

/**
* 播放完成回调函数
*
* @param soundID 系统声音ID
* @param clientData 回调时传递的数据
*/
void soundCompleteCallback(SystemSoundID soundID,void * clientData){
NSLog(@"播放完成...");
}

/**
* 播放音效文件
*
* @param name 音频文件名称
*/
-(void)playSoundEffect:(NSString *)name{
NSString *audioFile=[[NSBundle mainBundle] pathForResource:name ofType:nil];
NSURL *fileUrl=[NSURL fileURLWithPath:audioFile];
//1.获得系统声音ID
SystemSoundID soundID=0;
/**
* inFileUrl:音频文件url
* outSystemSoundID:声音id(此函数会将音效文件加入到系统音频服务中并返回一个长整形ID)
*/
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);
//如果需要在播放完之后执行某些操作,可以调用如下方法注册一个播放完成回调函数
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);
//2.播放音频
AudioServicesPlaySystemSound(soundID);//播放音效
// AudioServicesPlayAlertSound(soundID);//播放音效并震动
}

AVAudioPlayer播放音频

AVAudioPlayer 简介

创建

一般通过 使用本地音频文件的 NSURL 或者包含音频的内存的 NSData。

1
2
- (nullable instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError;
- (nullable instancetype)initWithData:(NSData *)data error:(NSError **)outError;

使用 prepareToPlay 方法 可以取得需要的音频硬件并预加载 Audio Queue 的缓冲区。在创建的时候调用可以降低调用 play 方法和听到声音输出之间的延迟。

对播放进行控制

常见的 playstoppause的播放暂停功能。
其中 stop 和 pause 的区别是 stop 方法会撤销调用 prepareToPlay时所做的设置,而 pause 方法不会。

除此之外还有:

  • 修改播放器的音量 (volume):从0.0(静音)到1.0(最大音量之间)。
  • 修改播放器的 pan 值:允许使用立体声播放声音,从 -1.0(极左)到 1.0(极右)。默认 0.0(居中)。
  • 调整播放率 (rate):需要配合 enableRate 使用。范围从 0.5(半数)到2.0(2倍数)。
  • 无缝循环 (numberOfLoops): 播放 n+1次,若 n<-1 则播放无限次直到被停止。
  • 音频计量: 可以读取音量力度的平均值和峰值。

配置音频会话,后台播放

默认会话类型为 AVAudioSessionCategorySoloAmbient 这个类型不能后台播放

添加后台播放 -设置会话:

1
2
3
4
5
6
7
8
9
10
   AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error;
//设置会话
if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"Category Error: %@", [error localizedDescription]);
}
//激活会话
if (![session setActive:YES error:&error]) {
NSLog(@"Activation Error: %@", [error localizedDescription]);
}

添加后台播放 -修改info.plist :

设置后台运行模式:在plist文件中添加Required background modes,并且设置item 0=App plays audio or streams audio/video using AirPlay(其实可以直接通过Xcode在Project Targets-Capabilities-Background Modes中设置)

处理中断

使用设备的过程中,经常会出现电话呼入等情况,默认情况下,中断发生时音频会慢慢消失暂停,但是当中断结束时,音频不会自动开始。

接收中断事件,并作出处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
		//接收中断
NSNotificationCenter *nsnc = [NSNotificationCenter defaultCenter];
[nsnc addObserver:self
selector:@selector(handleInterrruption:)
name:AVAudioSessionInterruptionNotification
object:[AVAudioSession sharedInstance]];

//处理中断
-(void)handleInterrruption:(NSNotification*)noti{
NSDictionary* info = noti.userInfo;

AVAudioSessionInterruptionType type= [[info objectForKey:@"AVAudioSessionInterruptionOptionKey"] unsignedIntegerValue];
if(type == AVAudioSessionInterruptionTypeBegan){
//中断开始
}
else{
//中断结束
}
}

对线路改变的响应

默认情况下,播放音频时插入耳机,音频输出变为耳机并播放;断开耳机时,音频输出变为扬声器并播放。 希望改为断开耳机的时候停止播放

接收线路变化通知,并作出处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
		//注册通知
NSNotificationCenter *nsnc = [NSNotificationCenter defaultCenter];
[nsnc addObserver:self
selector:@selector(handleRouteChange:)
name:AVAudioSessionRouteChangeNotification
object:[AVAudioSession sharedInstance]];

//处理通知
- (void)handleRouteChange:(NSNotification *)notification {

NSDictionary *info = notification.userInfo;

AVAudioSessionRouteChangeReason reason =
[info[AVAudioSessionRouteChangeReasonKey] unsignedIntValue];

if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {

AVAudioSessionRouteDescription *previousRoute =
info[AVAudioSessionRouteChangePreviousRouteKey];

AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
NSString *portType = previousOutput.portType;

//原设备为耳机则暂停播放
if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
//停止播放
}
}
}

AVAudioRecorder 录制音频

初始化

1
- (nullable instancetype)initWithURL:(NSURL *)url settings:(NSDictionary<NSString *, id> *)settings error:(NSError **)outError;

参数分别表示

  • url,用于表示音频流写入文件的本地文件URL
  • setting,包含录音会话键值信息的NSDictionary对象
    • AVFormatIDKey:写入的音频格式
    • AVSampleRateKey:采样率 。一般使用8000、16000、22050或44100
    • AVNumberOfChannelsKey:通道数,1为单声道,2表示立体声
  • outError,捕捉初始化阶段各种错误的NSError指针

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
NSString *tmpDir = NSTemporaryDirectory();
NSString *filePath = [tmpDir stringByAppendingPathComponent:@"memo.caf"];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];

NSDictionary *settings = @{
AVFormatIDKey : @(kAudioFormatAppleIMA4),
AVSampleRateKey : @44100.0f,
AVNumberOfChannelsKey : @1,
AVEncoderBitDepthHintKey : @16,
AVEncoderAudioQualityKey : @(AVAudioQualityMedium)
};

NSError *error;
self.recorder = [[AVAudioRecorder alloc] initWithURL:fileURL settings:settings error:&error];
if (self.recorder) {
self.recorder.delegate = self;
self.recorder.meteringEnabled = YES;
[self.recorder prepareToRecord];
} else {
NSLog(@"Error: %@", [error localizedDescription]);
}

配置音频会话

默认AVAudioSessionCategorySoloAmbient 不支持音频输入,改为AVAudioSessionCategoryPlayAndRecord

对录制进行控制

  • 录制暂停停止:record,pause,stop;
  • 恢复录制:pause之后调用record,AVAudioSession会帮你记录上次录音的地方并追加录音
  • 获取时间:currentTime录制时长(秒),不能使用kvo监测

Audio Metering测量音频

AVAudioRecoder 和 AVAudioPlayer 可以对音频进行测量。Audio Metering可以让开发者读取音频的平均分贝和峰值分贝

获取音频分贝数据

  • 设置录音器的 meteringEnabled属性为true
  • 每次获取的时候 调用 updateMeters 方法获取最新的值
  • - (float)peakPowerForChannel:(NSUInteger)channelNumber; 获取峰值分贝(对数形式的 -160 到 0)
  • - (float)averagePowerForChannel:(NSUInteger)channelNumber; 获取平均分贝(对数形式的 -160 到 0)