除了上面說的,在AVFoundation框架中還要一個AVAudioRecorder類專門處理錄音操做,它一樣支持多種音頻格式。與AVAudioPlayer相似,你徹底能夠將它當作是一個錄音機控制類,下面是經常使用的屬性和方法:html
屬性 | 說明 |
@property(readonly, getter=isRecording) BOOL recording; | 是否正在錄音,只讀 |
@property(readonly) NSURL *url | 錄音文件地址,只讀 |
@property(readonly) NSDictionary *settings | 錄音文件設置,只讀 |
@property(readonly) NSTimeInterval currentTime | 錄音時長,只讀,注意僅僅在錄音狀態可用 |
@property(readonly) NSTimeInterval deviceCurrentTime | 輸入設置的時間長度,只讀,注意此屬性一直可訪問 |
@property(getter=isMeteringEnabled) BOOL meteringEnabled; | 是否啓用錄音測量,若是啓用錄音測量能夠得到錄音分貝等數據信息 |
@property(nonatomic, copy) NSArray *channelAssignments | 當前錄音的通道 |
對象方法 | 說明 |
- (instancetype)initWithURL:(NSURL *)url settings:(NSDictionary *)settings error:(NSError **)outError | 錄音機對象初始化方法,注意其中的url必須是本地文件url,settings是錄音格式、編碼等設置 |
- (BOOL)prepareToRecord | 準備錄音,主要用於建立緩衝區,若是不手動調用,在調用record錄音時也會自動調用 |
- (BOOL)record | 開始錄音 |
- (BOOL)recordAtTime:(NSTimeInterval)time | 在指定的時間開始錄音,通常用於錄音暫停再恢復錄音 |
- (BOOL)recordForDuration:(NSTimeInterval) duration | 按指定的時長開始錄音 |
- (BOOL)recordAtTime:(NSTimeInterval)time forDuration:(NSTimeInterval) duration | 在指定的時間開始錄音,並指定錄音時長 |
- (void)pause; | 暫停錄音 |
- (void)stop; | 中止錄音 |
- (BOOL)deleteRecording; | 刪除錄音,注意要刪除錄音此時錄音機必須處於中止狀態 |
- (void)updateMeters; | 更新測量數據,注意只有meteringEnabled爲YES此方法纔可用 |
- (float)peakPowerForChannel:(NSUInteger)channelNumber; | 指定通道的測量峯值,注意只有調用完updateMeters纔有值 |
- (float)averagePowerForChannel:(NSUInteger)channelNumber | 指定通道的測量平均值,注意只有調用完updateMeters纔有值 |
代理方法 | 說明 |
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag | 完成錄音 |
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error | 錄音編碼發生錯誤 |
AVAudioRecorder不少屬性和方法跟AVAudioPlayer都是相似的,可是它的建立有所不一樣,在建立錄音機時除了指定路徑外還必 須指定錄音設置信息,由於錄音機必須知道錄音文件的格式、採樣率、通道數、每一個採樣點的位數等信息,可是也並非全部的信息都必須設置,一般只須要幾個常 用設置。關於錄音設置詳見幫助文檔中的「AV Foundation Audio Settings Constants」。ios
下面就使用AVAudioRecorder建立一個錄音機,實現了錄音、暫停、中止、播放等功能,實現效果大體以下:git
在這個示例中將實行一個完整的錄音控制,包括錄音、暫停、恢復、中止,同時還會實時展現用戶錄音的聲音波動,當用戶點擊完中止按鈕還會自動播放錄音文件。程序的構建主要分爲如下幾步:github
下面是主要代碼:網絡
//
// ViewController.m
// AVAudioRecorder
//
// Created by Kenshin Cui on 14/03/30.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//
#import "ViewController.h" #import <AVFoundation/AVFoundation.h> #define kRecordAudioFile @"myRecord.caf" @interface ViewController ()<AVAudioRecorderDelegate> @property (nonatomic,strong) AVAudioRecorder *audioRecorder;//音頻錄音機 @property (nonatomic,strong) AVAudioPlayer *audioPlayer;//音頻播放器,用於播放錄音文件 @property (nonatomic,strong) NSTimer *timer;//錄音聲波監控(注意這裏暫時不對播放進行監控) @property (weak, nonatomic) IBOutlet UIButton *record;//開始錄音 @property (weak, nonatomic) IBOutlet UIButton *pause;//暫停錄音 @property (weak, nonatomic) IBOutlet UIButton *resume;//恢復錄音 @property (weak, nonatomic) IBOutlet UIButton *stop;//中止錄音 @property (weak, nonatomic) IBOutlet UIProgressView *audioPower;//音頻波動 @end @implementation ViewController #pragma mark - 控制器視圖方法 - (void)viewDidLoad { [super viewDidLoad]; [self setAudioSession]; } #pragma mark - 私有方法 /** * 設置音頻會話 */ -(void)setAudioSession{ AVAudioSession *audioSession=[AVAudioSession sharedInstance]; //設置爲播放和錄音狀態,以即可以在錄製完以後播放錄音 [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; [audioSession setActive:YES error:nil]; } /** * 取得錄音文件保存路徑 * * @return 錄音文件路徑 */ -(NSURL *)getSavePath{ NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; urlStr=[urlStr stringByAppendingPathComponent:kRecordAudioFile]; NSLog(@"file path:%@",urlStr); NSURL *url=[NSURL fileURLWithPath:urlStr]; return url; } /** * 取得錄音文件設置 * * @return 錄音設置 */ -(NSDictionary *)getAudioSetting{ NSMutableDictionary *dicM=[NSMutableDictionary dictionary]; //設置錄音格式 [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey]; //設置錄音採樣率,8000是電話採樣率,對於通常錄音已經夠了 [dicM setObject:@(8000) forKey:AVSampleRateKey]; //設置通道,這裏採用單聲道 [dicM setObject:@(1) forKey:AVNumberOfChannelsKey]; //每一個採樣點位數,分爲八、1六、2四、32 [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey]; //是否使用浮點數採樣 [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey]; //....其餘設置等 return dicM; } /** * 得到錄音機對象 * * @return 錄音機對象 */ -(AVAudioRecorder *)audioRecorder{ if (!_audioRecorder) { //建立錄音文件保存路徑 NSURL *url=[self getSavePath]; //建立錄音格式設置 NSDictionary *setting=[self getAudioSetting]; //建立錄音機 NSError *error=nil; _audioRecorder=[[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error]; _audioRecorder.delegate=self; _audioRecorder.meteringEnabled=YES;//若是要監控聲波則必須設置爲YES if (error) { NSLog(@"建立錄音機對象時發生錯誤,錯誤信息:%@",error.localizedDescription); return nil; } } return _audioRecorder; } /** * 建立播放器 * * @return 播放器 */ -(AVAudioPlayer *)audioPlayer{ if (!_audioPlayer) { NSURL *url=[self getSavePath]; NSError *error=nil; _audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error]; _audioPlayer.numberOfLoops=0; [_audioPlayer prepareToPlay]; if (error) { NSLog(@"建立播放器過程當中發生錯誤,錯誤信息:%@",error.localizedDescription); return nil; } } return _audioPlayer; } /** * 錄音聲波監控定製器 * * @return 定時器 */ -(NSTimer *)timer{ if (!_timer) { _timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES]; } return _timer; } /** * 錄音聲波狀態設置 */ -(void)audioPowerChange{ [self.audioRecorder updateMeters];//更新測量值 float power= [self.audioRecorder averagePowerForChannel:0];//取得第一個通道的音頻,注意音頻強度範圍時-160到0 CGFloat progress=(1.0/160.0)*(power+160.0); [self.audioPower setProgress:progress]; } #pragma mark - UI事件 /** * 點擊錄音按鈕 * * @param sender 錄音按鈕 */ - (IBAction)recordClick:(UIButton *)sender { if (![self.audioRecorder isRecording]) { [self.audioRecorder record];//首次使用應用時若是調用record方法會詢問用戶是否容許使用麥克風 self.timer.fireDate=[NSDate distantPast]; } } /** * 點擊暫定按鈕 * * @param sender 暫停按鈕 */ - (IBAction)pauseClick:(UIButton *)sender { if ([self.audioRecorder isRecording]) { [self.audioRecorder pause]; self.timer.fireDate=[NSDate distantFuture]; } } /** * 點擊恢復按鈕 * 恢復錄音只須要再次調用record,AVAudioSession會幫助你記錄上次錄音位置並追加錄音 * * @param sender 恢復按鈕 */ - (IBAction)resumeClick:(UIButton *)sender { [self recordClick:sender]; } /** * 點擊中止按鈕 * * @param sender 中止按鈕 */ - (IBAction)stopClick:(UIButton *)sender { [self.audioRecorder stop]; self.timer.fireDate=[NSDate distantFuture]; self.audioPower.progress=0.0; } #pragma mark - 錄音機代理方法 /** * 錄音完成,錄音完成後播放錄音 * * @param recorder 錄音機對象 * @param flag 是否成功 */ -(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{ if (![self.audioPlayer isPlaying]) { [self.audioPlayer play]; } NSLog(@"錄音完成!"); } @end
運行效果:app
你們應該已經注意到了,不管是前面的錄音仍是音頻播放均不支持網絡流媒體播放,固然對於錄音來講這種需求可能不大,可是對於音頻播放來講有時候就很 有必要了。AVAudioPlayer只能播放本地文件,而且是一次性加載因此音頻數據,初始化AVAudioPlayer時指定的URL也只能是 File URL而不能是HTTP URL。固然,將音頻文件下載到本地而後再調用AVAudioPlayer來播放也是一種播放網絡音頻的辦法,可是這種方式最大的弊端就是必須等到整個音 頻播放完成才能播放,而不能使用流式播放,這每每在實際開發中是不切實際的。那麼在iOS中如何播放網絡流媒體呢?就是使用AudioToolbox框架 中的音頻隊列服務Audio Queue Services。框架
使用音頻隊列服務徹底能夠作到音頻播放和錄製,首先看一下錄音音頻服務隊列:函數
一個音頻服務隊列Audio Queue有三部分組成:oop
三個緩衝器Buffers:每一個緩衝器都是一個存儲音頻數據的臨時倉庫。ui
一個緩衝隊列Buffer Queue:一個包含音頻緩衝器的有序隊列。
一個回調Callback:一個自定義的隊列回調函數。
聲音經過輸入設備進入緩衝隊列中,首先填充第一個緩衝器;當第一個緩衝器填充滿以後自動填充下一個緩衝器,同時會調用回調函數;在回調函數中須要將緩衝器中的音頻數據寫入磁盤,同時將緩衝器放回到緩衝隊列中以便重用。下面是Apple官方關於音頻隊列服務的流程示意圖:
相似的,看一下音頻播放緩衝隊列,其組成部分和錄音緩衝隊列相似。
可是在音頻播放緩衝隊列中,回調函數調用的時機不一樣於音頻錄製緩衝隊列,流程恰好相反。將音頻讀取到緩衝器中,一旦一個緩衝器填充滿以後就放到緩衝 隊列中,而後繼續填充其餘緩衝器;當開始播放時,則從第一個緩衝器中讀取音頻進行播放;一旦播放完以後就會觸發回調函數,開始播放下一個緩衝器中的音頻, 同時填充第一個緩衝器放;填充滿以後再次放回到緩衝隊列。下面是詳細的流程:
固然,要明白音頻隊列服務的原理並不難,問題是如何實現這個自定義的回調函數,這其中咱們有大量的工做要作,控制播放狀態、處理異常中斷、進行音頻 編碼等等。因爲牽扯內容過多,並且不是本文目的,若是之後有時間將另開一篇文章重點介紹,目前有不少第三方優秀框架能夠直接使用,例如AudioStreamer、FreeStreamer。因爲前者當前只有非ARC版本,因此下面不妨使用FreeStreamer來簡單演示在線音頻播放的過程,固然在使用以前要作以下準備工做:
1.拷貝FreeStreamer中的Reachability.h、Reachability.m和Common、astreamer兩個文件夾中的內容到項目中。
2.添加FreeStreamer使用的類庫:CFNetwork.framework、AudioToolbox.framework、AVFoundation.framework
、libxml2.dylib、MediaPlayer.framework。
3.若是引用libxml2.dylib編譯不經過,須要在Xcode的Targets-Build Settings-Header Build Path中添加$(SDKROOT)/usr/include/libxml2。
4.將FreeStreamer中的FreeStreamerMobile-Prefix.pch文件添加到項目中並將Targets-Build Settings-Precompile Prefix Header設置爲YES,在Targets-Build Settings-Prefix Header設置爲$(SRCROOT)/項目名稱/FreeStreamerMobile-Prefix.pch(由於Xcode6默認沒有pch文件)
而後就能夠編寫代碼播放網絡音頻了:
//
// ViewController.m
// AudioQueueServices
//
// Created by Kenshin Cui on 14/03/30.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
// 使用FreeStreamer實現網絡音頻播放
#import "ViewController.h" #import "FSAudioStream.h" @interface ViewController () @property (nonatomic,strong) FSAudioStream *audioStream; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self.audioStream play]; } /** * 取得本地文件路徑 * * @return 文件路徑 */ -(NSURL *)getFileUrl{ NSString *urlStr=[[NSBundle mainBundle]pathForResource:@"劉若英 - 原來你也在這裏.mp3" ofType:nil]; NSURL *url=[NSURL fileURLWithPath:urlStr]; return url; } -(NSURL *)getNetworkUrl{ NSString *urlStr=@"http://192.168.1.102/liu.mp3"; NSURL *url=[NSURL URLWithString:urlStr]; return url; } /** * 建立FSAudioStream對象 * * @return FSAudioStream對象 */ -(FSAudioStream *)audioStream{ if (!_audioStream) { NSURL *url=[self getNetworkUrl]; //建立FSAudioStream對象 _audioStream=[[FSAudioStream alloc]initWithUrl:url]; _audioStream.onFailure=^(FSAudioStreamError error,NSString *description){ NSLog(@"播放過程當中發生錯誤,錯誤信息:%@",description); }; _audioStream.onCompletion=^(){ NSLog(@"播放完成!"); }; [_audioStream setVolume:0.5];//設置聲音 } return _audioStream; } @end
其實FreeStreamer的功能很強大,不只僅是播放本地、網絡音頻那麼簡單,它還支持播放列表、檢查包內容、RSS訂閱、播放中斷等不少強大的功能,甚至還包含了一個音頻分析器,有興趣的朋友能夠訪問官網查看詳細用法