錄音實現方式

錄音

除了上面說的,在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

AVAudioRecorderSnapshot

在這個示例中將實行一個完整的錄音控制,包括錄音、暫停、恢復、中止,同時還會實時展現用戶錄音的聲音波動,當用戶點擊完中止按鈕還會自動播放錄音文件。程序的構建主要分爲如下幾步:github

  1. 設置音頻會話類型爲AVAudioSessionCategoryPlayAndRecord,由於程序中牽扯到錄音和播放操做。
  2. 建立錄音機AVAudioRecorder,指定錄音保存的路徑而且設置錄音屬性,注意對於通常的錄音文件要求的採樣率、位數並不高,須要適當設置以保證錄音文件的大小和效果。
  3. 設置錄音機代理以便在錄音完成後播放錄音,打開錄音測量保證可以實時得到錄音時的聲音強度。(注意聲音強度範圍-160到0,0表明最大輸入)
  4. 建立音頻播放器AVAudioPlayer,用於在錄音完成以後播放錄音。
  5. 建立一個定時器以便實時刷新錄音測量值並更新錄音強度到UIProgressView中顯示。
  6. 添加錄音、暫停、恢復、中止操做,須要注意錄音的恢復操做實際上是有音頻會話管理的,恢復時只要再次調用record方法便可,無需手動管理恢復時間等。

下面是主要代碼:網絡

//
//  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

AVAudioRecorder

音頻隊列服務

你們應該已經注意到了,不管是前面的錄音仍是音頻播放均不支持網絡流媒體播放,固然對於錄音來講這種需求可能不大,可是對於音頻播放來講有時候就很 有必要了。AVAudioPlayer只能播放本地文件,而且是一次性加載因此音頻數據,初始化AVAudioPlayer時指定的URL也只能是 File URL而不能是HTTP URL。固然,將音頻文件下載到本地而後再調用AVAudioPlayer來播放也是一種播放網絡音頻的辦法,可是這種方式最大的弊端就是必須等到整個音 頻播放完成才能播放,而不能使用流式播放,這每每在實際開發中是不切實際的。那麼在iOS中如何播放網絡流媒體呢?就是使用AudioToolbox框架 中的音頻隊列服務Audio Queue Services。框架

使用音頻隊列服務徹底能夠作到音頻播放和錄製,首先看一下錄音音頻服務隊列:函數

recording_architecture_2x

一個音頻服務隊列Audio Queue有三部分組成:oop

三個緩衝器Buffers:每一個緩衝器都是一個存儲音頻數據的臨時倉庫。ui

一個緩衝隊列Buffer Queue:一個包含音頻緩衝器的有序隊列。

一個回調Callback:一個自定義的隊列回調函數。

聲音經過輸入設備進入緩衝隊列中,首先填充第一個緩衝器;當第一個緩衝器填充滿以後自動填充下一個緩衝器,同時會調用回調函數;在回調函數中須要將緩衝器中的音頻數據寫入磁盤,同時將緩衝器放回到緩衝隊列中以便重用。下面是Apple官方關於音頻隊列服務的流程示意圖:

recording_callback_function_2x

相似的,看一下音頻播放緩衝隊列,其組成部分和錄音緩衝隊列相似。

playback_architecture_2x

可是在音頻播放緩衝隊列中,回調函數調用的時機不一樣於音頻錄製緩衝隊列,流程恰好相反。將音頻讀取到緩衝器中,一旦一個緩衝器填充滿以後就放到緩衝 隊列中,而後繼續填充其餘緩衝器;當開始播放時,則從第一個緩衝器中讀取音頻進行播放;一旦播放完以後就會觸發回調函數,開始播放下一個緩衝器中的音頻, 同時填充第一個緩衝器放;填充滿以後再次放回到緩衝隊列。下面是詳細的流程:

playback_callback_function_2x

固然,要明白音頻隊列服務的原理並不難,問題是如何實現這個自定義的回調函數,這其中咱們有大量的工做要作,控制播放狀態、處理異常中斷、進行音頻 編碼等等。因爲牽扯內容過多,並且不是本文目的,若是之後有時間將另開一篇文章重點介紹,目前有不少第三方優秀框架能夠直接使用,例如AudioStreamerFreeStreamer。因爲前者當前只有非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訂閱、播放中斷等不少強大的功能,甚至還包含了一個音頻分析器,有興趣的朋友能夠訪問官網查看詳細用法

相關文章
相關標籤/搜索