音頻會話(Audio Session)
音頻會話是應用程序和操作系統之間的中間人。應用程序不需要具體知道怎樣和音頻硬件交互的細節,只需要把所需的音頻行為委托給音頻會話管理即可。
- 音頻會話分類(Audio Session Category)
AV Foundation把音頻行為分成了七類。各個分類有著不同的作用,因此各個分類用在不同的功能場景。
音頻分類_1.jpg
以上七個分類能夠滿足大部分的需求,但是開發者也可以通過使用選項(options)和模式(modes)自定義,增強和修改原有的分類功能。
配置音頻會話
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
// 音頻會話分類(catehory)和模式(mode)一起決定了應用要使用音頻的方式,也可以說是定制音頻的行為。通常在激活音頻會話之前設置分類和模式。也可以在激活音頻會話時設置分類和模式,但是這樣會立即路由(route)。
// 如果分類設置成功,但會也是,反則返回no
if([audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]){
// 配置成功
}else{
// 配置失敗
}
音頻錄制
AV Foundation提供了AVAudioRecorder類,用于從內置麥克風或者外置音頻設備錄制音頻。
創建AVAudioRecorder需要提供以下數據信息:
- 錄制的音頻要存放的位置,也就是本地URL。
- 音頻格式、采樣率、聲道等配置信息,這些信息使用字典保存。
- NSError指針,用于捕捉初始化時可能出現的錯誤。
創建和準備AudioRecorder對象:
+(instancetype)audioRecordertoPrepare:(NSString *)audioName error:(NSError *)error{
NSString *audioDirectory = ;
if(audioDirectory){
NSString *audioPath = ;
NSDictionary *audioSettings = @{AVFormatIDKey:@(kAudioFormatMPEG4AAC),
AVSampleRateKey:@22050.0f,
AVNumberOfChannelsKey:@1
};
AVAudioRecorder *audioRecorder = [[AVAudioRecorder alloc] initWithURL:audioURL settings:audioSettings error:&error];
// prepareToRecord方法根據URL創建文件,并且執行底層Audio Queue初始化的必要過程,將錄制啟動時的延遲降到最低。
if([audioRecorder prepareToRecord]){
return audioRecorder;
}
}
return nil;
}
- 音頻格式
kAudioFormatMPEG4AAC
壓縮格式能在顯著減小文件的同時,保證音頻的質量。同時Android也支持aac
音頻格式。 - 采樣率
采樣率越高,文件越大,質量越好,反之,文件小,質量相對差一些,但是低于普通的音頻,人耳并不能明顯的分辨出好壞。最終選取哪一種采樣率,由我們的耳朵來判斷。建議使用標準的采樣率,8000、16000、22050、44100。 - 通道數
AVNumberOfChannelsKey
用于指定記錄音頻的通道數。1為單聲道,2為立體聲。除非使用外部硬件進行錄制,否則通常使用單聲道錄制。 - 其他配置
在AV Foundation Audio Session Settings Constants中查看完整的鍵列表。
音頻錄制控制
- 啟動、恢復音頻錄制
[AVAudioRecorder record];
```
- 暫停音頻錄制
[AVAudioRecorder pause];
- 結束錄制
[AVAudioRecorder stop];
- 刪除錄制的音頻文件,刪除之前必須先停止錄制
[self.audioRecorder deleteRecording];
錄制和刪除錄制文件方法返回布爾值,如果操作成功,返回YES,操作失敗返回NO,所以在實際的開發中,還應該考慮操作失敗狀態的處理,給用戶以友好的提示。
同時AVAudioRecorder
也提供了AVAdudioRecorderDelegate
協議,用于接收音頻錄制完成、音頻錄制過程發生錯誤的事件。雖然也有定義音頻錄制過程被中斷的方法,但是已經棄用。所以要監聽諸如系統來電,鬧鐘響鈴,Facetime……導致的音頻錄制終端事件,使用AVAudioSession
通知代替。
AVAudioDelegate定義的接口:
@optional
/* 錄制完成或者調用stop時,回調用這個方法。但是如果是系統中斷錄音,則不會調用這個方法。 */
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag;
/* 在錄制時出現錯誤時調用這個代理方法 */
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError * __nullable)error;
#if TARGET_OS_IPHONE
/* AVAudioRecorder INTERRUPTION NOTIFICATIONS 已廢棄 - 使用 AVAudioSession 替代. */
/* audioRecorderBeginInterruption: is called when the audio session has been interrupted while the recorder was recording. The recorded file will be closed. */
- (void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder NS_DEPRECATED_IOS(2_2, 8_0);
/* audioRecorderEndInterruption:withOptions: is called when the audio session interruption has ended and this recorder had been interrupted while recording. */
/* Currently the only flag is AVAudioSessionInterruptionFlags_ShouldResume. */
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder withOptions:(NSUInteger)flags NS_DEPRECATED_IOS(6_0, 8_0);
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder withFlags:(NSUInteger)flags NS_DEPRECATED_IOS(4_0, 6_0);
/* audioRecorderEndInterruption: is called when the preferred method, audioRecorderEndInterruption:withFlags:, is not implemented. */
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder NS_DEPRECATED_IOS(2_2, 6_0);
AVAudioSession通知
/** 注冊音頻錄制中斷通知 */
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(handleNotification:) name:AVAudioSessionInterruptionNotification object:nil];
// 接收錄制中斷事件通知,并處理相關事件
-(void)handleNotification:(NSNotification *)notification{
NSArray *allKeys = notification.userInfo.allKeys;
// 判斷事件類型
if([allKeys containsObject:AVAudioSessionInterruptionTypeKey]){
AVAudioSessionInterruptionType audioInterruptionType = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
switch (audioInterruptionType) {
case AVAudioSessionInterruptionTypeBegan:
self.statusLabel.text = @"錄音被打斷…… 開始";
break;
case AVAudioSessionInterruptionTypeEnded:
self.statusLabel.text = @"錄音被打斷…… 結束";
break;
}
}
// 判斷中斷的音頻錄制是否可恢復錄制
if([allKeys containsObject:AVAudioSessionInterruptionOptionKey]){
AVAudioSessionInterruptionOptions shouldResume = [[notification.userInfo valueForKey:AVAudioSessionInterruptionOptionKey] integerValue];
if(shouldResume){
self.statusLabel.text = @"錄音被打斷…… 結束 可以恢復錄音了";
}
}
}