1. 程式人生 > >利用AVFoundation框架實現錄音和播放(AVAudioSession、AVAudioRecorder、AVAudioPlayer)

利用AVFoundation框架實現錄音和播放(AVAudioSession、AVAudioRecorder、AVAudioPlayer)

最近實現了一個簡單功能,類似微信傳送語音,按下錄音,鬆開結束錄音;並且可播放;

效果圖:


Demo下載地址:

需要匯入

#import <AVFoundation/AVFoundation.h>

利用此框架中的

AVAudioRecorder和AVAudioPlayer來錄音和播放

以下是AVAudioRecorder錄音的使用方法:

  1. - (IBAction)downAction:(id)sender {  
  2.     //按下錄音
  3.     if ([self canRecord]) {  
  4.         NSError *error = nil;  
  5.         //必須真機上測試,模擬器上可能會崩潰
  6.         recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL URLWithString:playName] settings:recorderSettingsDict error:&error];  
  7.         if (recorder) {  
  8.             //是否允許重新整理電平表,預設是off
  9.             recorder.meteringEnabled = YES;  
  10.             //建立檔案,並準備錄音
  11.             [recorder prepareToRecord];  
  12.             //開始錄音
  13.             [recorder record];  
  14.             //啟動定時器,為了更新電平
  15.             timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(levelTimer:) userInfo:nil repeats:YES];  
  16.         } else
  17.         {  
  18.             int errorCode = CFSwapInt32HostToBig ([error code]);  
  19.             NSLog(@"Error: %@ [%4.4s])" , [error localizedDescription], (char*)&errorCode);  
  20.         }  
  21.     }  
  22. }  
  23. - (IBAction)upAction:(id)sender {  
  24.     //鬆開 結束錄音
  25.     //錄音停止
  26.     [recorder stop];  
  27.     recorder = nil;  
  28.     //結束定時器
  29.     [timer invalidate];  
  30.     timer = nil;  
  31.     //圖片重置
  32.     soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:0]];  
  33. }  

以下是AVAudioPlayer播放器的使用方法:

  1. - (IBAction)playAction:(id)sender {  
  2.     NSError *playerError;  
  3.     //播放
  4.     player = nil;  
  5.     player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:playName] error:&playerError];  
  6.     if (player == nil)  
  7.     {  
  8.         NSLog(@"ERror creating player: %@", [playerError description]);  
  9.     }else{  
  10.         [player play];  
  11.     }  
  12. }  

如果是7.0,第一次執行會提示,是否允許使用麥克風:

7.0需要設定:

  1. if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0"] != NSOrderedAscending)  
  2.     {  
  3.         //7.0第一次執行會提示,是否允許使用麥克風
  4.         AVAudioSession *session = [AVAudioSession sharedInstance];  
  5.         NSError *sessionError;  
  6.         //AVAudioSessionCategoryPlayAndRecord用於錄音和播放
  7.         [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];  
  8.         if(session == nil)  
  9.             NSLog(@"Error creating session: %@", [sessionError description]);  
  10.         else
  11.             [session setActive:YES error:nil];  
  12.     }  

ok!完美,perfect!

補充:新建專案工程,首先匯入AV Foundation框架,此處使用ARC=YES,匯入相關圖片檔案,建立ViewController,帶上xib檔案,把AppDelegate的rootViewController交給ViewController,通過ib檔案載入檢視。在ViewController中匯入<AVFoundation/AVFoundation.h>庫,定義如下變數和例項方法:

{
   IBOutlet UIImageView *soundLodingImageView;//從IB連結的一個UIImageView
   IBOutlet UIButton *playBtn;//從IB連結的button
   AVAudioRecorder *recorder; //錄音器
   AVAudioPlayer *player; //播放器
   NSDictionary *recorderSettingsDict;//播放器的配置新增到一個字典裡
   NSTimer *timer; //定時器
   NSMutableArray *volumImages; //圖片組
   double lowPassResults;//建立一個double型別的lowPassResults獲取最低分貝值
   NSString *playName; //錄音名字
}
- (IBAction)downAction:(id)sender;//點選開始錄音執行的方法
- (IBAction)upAction:(id)sender;//鬆手錄音結束的方法
- (IBAction)playAction:(id)sender;//播放音訊
 

實現Demo中AV Foundation的方法:【此處使用的時AVAudioSession類】

1、AVAudioSession 直接繼承與NSObjectà AVAudioSession : NSObject;        

2、建立會話session,呼叫AVAudioSession的類方法+ (id)sharedInstance;返回一個單例例項

3、- (BOOL)setCategory:(NSString *)category error:(NSError **)outError;

AVAudioSession中設定音訊型別的例項方法,返回一個BOOL型別;

setCategory需要返回一個NSString型別的分類屬性的值,呼叫失敗返回error:

AVF_EXPORT NSString *const AVAudioSessionCategoryPlayAndRecord,表示用於錄音和播放;(一般選這個,錄製和播放都可以使用)

AVF_EXPORT NSString *constAVAudioSessionCategoryRecord,表示錄製音訊;

還有幾個不常用的類別:新增背景聲音如雨、汽車發動機噪聲等混合與其他音樂,使用背景音樂型別並停止其他音樂播放(這個類別在錄製和播放語音時會使用,前提條件是存在音樂播放軟體的情況下),使用硬體編解碼器或訊號處理器。

4、//呼叫AVFoundation框架的setActive方法這是會話活動:

- (BOOL)setActive:(BOOL)activeerror:(NSError **)outError;返回一個BOOL型別

5、設定錄音器和播放器

recorderSettingsDict =[[NSDictionary alloc]initWithObjectsAndKeys:

   [NSNumbernumberWithInt:kAudioFormatMPEG4AAC],AVFormatIDKey,

   [NSNumbernumberWithInt:1000.0],AVSampleRateKey,

   [NSNumbernumberWithInt:2],AVNumberOfChannelsKey,

   [NSNumbernumberWithInt:8],AVLinearPCMBitDepthKey,

   [NSNumbernumberWithBool:NO],AVLinearPCMIsBigEndianKey,

   [NSNumbernumberWithBool:NO],AVLinearPCMIsFloatKey,nil];

     /*錄音設定,初始化播放器,

    AVFormatIDKey->格式的ID,格式使用了CoreAudio庫中的CoreAudioTypes類的一組列舉方法,例子中使用的AAC的格式這裡需要注意iOS常用的幾種音訊格式,詳見下文【iOS系統支援音訊格式及編碼要求】和【CoreAudioTypes類中音訊格式列舉值】

    AVSampleRateKey->音訊取樣率,

    AVNumberOfChannelsKey->關鍵渠道,通道,

    AVLinearPCMBitDepthKey->取樣位數,預設為16,

    AVLinearPCMIsBigEndianKey->大端還是小端,是記憶體的組織方式

AVLinearPCMIsFloatKey,nil]->取樣訊號是否是浮點數NO*/

-----一切就緒,開始設定點選錄音,鬆開停止並儲存音訊,點選播放功能啦----

6、點選按鈕觸發錄音功能,開啟記錄方法,則初始化錄音器,定義捕獲音訊的儲存路徑並設定播放器,初始化AV Foundation下的AVAudioRecorder類,直接繼承自NSObject;

初始化呼叫-(id)initWithURL:(NSURL *)url settings:(NSDictionary *)settings error:(NSError**)outError;例項方法,傳一個url進去,設定記錄檔案的副檔名,如果存在則覆蓋;

7、錄音器初始化成功,開啟計量器,呼叫AVFoundation框架的prepareToRecord方法建立檔案,準備記錄,該方法會自動記錄[recorder prepareToRecord];啟動AVFoundation的記錄或回覆檔案方法[recorderrecord],開啟定時器,定時器啟動後呼叫levelTimer方法。

8、鬆開後結束錄音,直接使用[recorder stop],此時注意要把recorder設定為nil,由於使用了ARC,是系統自動管理記憶體,所以如果recorder停止呼叫後將不再指向記憶體區域,為了防止偶然呼叫到野指標,安全起見還是需要把值設為nil,指向一片不存在的記憶體。錄音結束的同時呼叫定時器也停止,同樣定時器也要把指標設為nil。

9、點選播放已經錄製好的音訊,在初始化player播放器時使用AV Foundation框架下的AVAudioPlayer類,讀取儲存在本地的錄音格式檔案,點選執行[player play];

----基本的按鈕功能也都寫好了,接下來是定時器呼叫的levelTimer方法設定分貝和不同分貝顯示的圖片----

10、peakPowerForChannel 峰值功率通道,返回峰值功率分貝對於一個給定的通道,AVFoundation的方法。

  //call to refresh meter values重新整理平均和峰值功率,此計數是以對數刻度計量的,lowPassResults是最低值,初始值為0 ,
   [recorder updateMeters];//錄音器呼叫重新整理記值的方法
   const double ALPHA = 0.05;//alpha值在物理分貝表示中+0.05為最小分貝數,-0.05為最大分貝數,+80為最小分貝數,-80為最大分貝數
       doublepeakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
       lowPassResults= ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;

以上兩套演算法公式尚不明白其運算過程和使用方法,暫時只要記住怎麼用吧,lowPassResults是最小分貝值,通過計算得到一個最小分貝值來判斷圖片的顯示;

11、7.0新增方法,判斷是否允許使用麥克風requestRecordPermission

-(BOOL)canRecord
{
   __block BOOL bCanRecord = YES;
   if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0"]!= NSOrderedAscending)
    {
    //- (BOOL)respondsToSelector:(SEL)Selector;在NSObject和NSProxy中都存在,在NSProxy中是一個類方法,此處使用的是它的類方法
    AVAudioSession*audioSession = [AVAudioSession sharedInstance];     if([audioSessionrespondsToSelector:@selector(requestRecordPermission:)]){
    //performSelector選擇執行器,requestRecordPermission:會話類別等。例子:AV音訊會話類別記錄,AV音訊會話類別和記錄,AVFoundation的一個例項方法。
[audioSession performSelector:@selector(requestRecordPermission:)withObject:^(BOOL granted) {
                if (granted) {
                    bCanRecord = YES;
                }
                else {
                    bCanRecord = NO;
   //返回主執行緒執行UI操作,此處可以不使用回撥,安全起見回撥主函式執行
   dispatch_async(dispatch_get_main_queue(),^{
   [[[UIAlertViewalloc] initWithTitle:nil
   message:@"app需要訪問您的麥克風。\n請啟用麥克風-設定/隱私/麥克風"
   delegate:nil
   cancelButtonTitle:@"關閉"
  otherButtonTitles:nil] show];
                    });
                }
           }];
       }
    }
   return bCanRecord;
}

【注】Demo的url設定可以決定是在真機執行還是模擬器執行。

[[AVAudioRecorder alloc] initWithURL:[NSURLfileURLWithPath:playName] settings:recorderSettingsDict error:&error];

如果要在模擬器執行,需要將NSURL的獲取方式改為fileURLWithPath,若在真機執行則改為URLWithPath。

 【iOS系統支援音訊格式及編碼要求:】

AAC (16 至 320 Kbps)、Protected AAC (來自 iTunes Store)、MP3 (16 至 320 Kbps)、MP3 VBR、Audible (formats 2、3、4)、Apple Lossless、AIFF 及 WAV

The audio technologies in iOS support thefollowing audio formats:

•     AAC

•     Apple Lossless (ALAC)

•     A-law

•     IMA/ADPCM (IMA4)

•     Linear PCM

•     µ-law

•     DVI/Intel IMA ADPCM

•     Microsoft GSM 6.10

•     AES3-2003

【CoreAudioTypes類中音訊格式列舉值:】

     kAudioFormatLinearPCM               = 'lpcm',

   kAudioFormatAC3                    = 'ac-3',

   kAudioFormat60958AC3               = 'cac3',

   kAudioFormatAppleIMA4              = 'ima4',

   kAudioFormatMPEG4AAC               = 'aac ',

   kAudioFormatMPEG4CELP              = 'celp',

   kAudioFormatMPEG4HVXC              = 'hvxc',

   kAudioFormatMPEG4TwinVQ            = 'twvq',

   kAudioFormatMACE3                  = 'MAC3',

   kAudioFormatMACE6                  = 'MAC6',

   kAudioFormatULaw                    = 'ulaw',

   kAudioFormatALaw                   = 'alaw',

   kAudioFormatQDesign                = 'QDMC',

   kAudioFormatQDesign2               = 'QDM2',

   kAudioFormatQUALCOMM               = 'Qclp',

   kAudioFormatMPEGLayer1              = '.mp1',

   kAudioFormatMPEGLayer2             = '.mp2',

   kAudioFormatMPEGLayer3             = '.mp3',

   kAudioFormatTimeCode               = 'time',

   kAudioFormatMIDIStream             = 'midi',

   kAudioFormatParameterValueStream    = 'apvs',

   kAudioFormatAppleLossless          = 'alac',

   kAudioFormatMPEG4AAC_HE            = 'aach',

   kAudioFormatMPEG4AAC_LD            = 'aacl',

   kAudioFormatMPEG4AAC_ELD           = 'aace',

   kAudioFormatMPEG4AAC_ELD_SBR       = 'aacf',

   kAudioFormatMPEG4AAC_ELD_V2        = 'aacg',   

   kAudioFormatMPEG4AAC_HE_V2         = 'aacp',

   kAudioFormatMPEG4AAC_Spatial       = 'aacs',

   kAudioFormatAMR                    = 'samr',

   kAudioFormatAudible                = 'AUDB',

   kAudioFormatiLBC                   = 'ilbc',

   kAudioFormatDVIIntelIMA            = 0x6D730011,

   kAudioFormatMicrosoftGSM           = 0x6D730031,

   kAudioFormatAES3                   = 'aes3'

viewController.m

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    /*UIDevice->UI裝置 ,currentDevice->當前的裝置,systemVersion->系統版本,compare->比較*/
    //判斷當前裝置的系統版本是否在7.0及以上版本
    if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0"] != NSOrderedAscending)
    {
        //7.0第一次執行會提示,是否允許使用麥克風
        AVAudioSession *session = [AVAudioSession sharedInstance];
        //建立會話這個共享類session
        NSError *sessionError;
        //AVAudioSessionCategoryPlayAndRecord用於錄音和播放
        //setCategory//設定音訊型別
        [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
        //如果會話為空
        if(session == nil)
            NSLog(@"Error creating session: %@", [sessionError description]);
        //列印error說明
        else
        //呼叫AVFoundation框架的setActive方法
            [session setActive:YES error:nil];
    }
    //NSSearchPathForDirectoriesInDomains搜尋路徑中的目錄域,NSDocumentDirectory文件目錄,
    NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    //錄音儲存格式為aac格式
    playName = [NSString stringWithFormat:@"%@/play.aac",docDir];
    
    /*錄音設定,初始化播放器,
     AVFormatIDKey->格式的ID,
     AVSampleRateKey->音訊取樣率,
     AVNumberOfChannelsKey->關鍵渠道,通道,
     AVLinearPCMBitDepthKey->取樣位數,預設為16,
     AVLinearPCMIsBigEndianKey->大端還是小端,是記憶體的組織方式
     AVLinearPCMIsFloatKey,nil]->取樣訊號是否是浮點數NO
     */
    recorderSettingsDict =[[NSDictionary alloc] initWithObjectsAndKeys:
                                         [NSNumber numberWithInt:kAudioFormatMPEG4AAC],AVFormatIDKey,
                                         [NSNumber numberWithInt:1000.0],AVSampleRateKey,
                                         [NSNumber numberWithInt:2],AVNumberOfChannelsKey,
                                         [NSNumber numberWithInt:8],AVLinearPCMBitDepthKey,
                                         [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
                                         [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
                                         nil];
    //音量圖片陣列
    volumImages = [[NSMutableArray alloc]initWithObjects:@"RecordingSignal001",@"RecordingSignal002",@"RecordingSignal003",
                   @"RecordingSignal004", @"RecordingSignal005",@"RecordingSignal006",
                   @"RecordingSignal007",@"RecordingSignal008",   nil];
    

}


//Button的點選實現方法
- (IBAction)downAction:(id)sender {
    //按下錄音
    if ([self canRecord]) {
        NSError *error = nil;
        //必須真機上測試,模擬器上可能會崩潰,如果要在模擬器執行,需要將NSURL的獲取方式改為fileURLWithPath,若在真機執行則改為URLWithPath;
        //如果按鈕觸發時可以開啟記錄方法,則初始化錄音器,定義捕獲音訊的儲存路徑並設定播放器
        recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:playName] settings:recorderSettingsDict error:&error];
        if (recorder) {
            recorder.meteringEnabled = YES;//啟用計量
            //呼叫AVFoundation框架的prepareToRecord方法建立檔案,準備記錄,該方法會自動記錄
            [recorder prepareToRecord];
            //啟動AVFoundation的記錄或回覆檔案方法
            [recorder record];
            //啟動定時器
            timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(levelTimer:) userInfo:nil repeats:YES];
            
        } else
        {
            int errorCode = CFSwapInt32HostToBig ([error code]);
            NSLog(@"Error: %@ [%4.4s])" , [error localizedDescription], (char*)&errorCode);
//            localizedDescription 本地化的描述
        }
    }
}

- (IBAction)upAction:(id)sender {
    //鬆開 結束錄音
    
    //錄音停止
    [recorder stop];
    recorder = nil;
    //結束定時器
    [timer invalidate];
    timer = nil;
    //圖片重置
    soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:0]];
}
//
- (IBAction)playAction:(id)sender {
    
    NSError *playerError;
    
    //播放
    player = nil;
    //初始化player播放器
    player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:playName] error:&playerError];
    
    if (player == nil)
    {
        NSLog(@"ERror creating player: %@", [playerError description]);
    }else{
        [player play];
    }
    
}
//peakPowerForChannel 峰值功率通道,返回峰值功率分貝對於一個給定的通道,AVFoundation的方法,字面理解為峰值的冪,從其網路上搜索後得到上面的答案,但其結果並不是分貝,而是一個從0-1的波動值
-(void)levelTimer:(NSTimer*)timer_
{
    //call to refresh meter values重新整理平均和峰值功率,此計數是以對數刻度計量的,-160表示完全安靜,0表示最大輸入值,lowPassResults是最低值,初始值為0 ,
    //alpha=0.05
    [recorder updateMeters];//錄音器呼叫重新整理記值的方法
    const double ALPHA = 0.05;//alpha值在物理分貝表示中+0.05為最小分貝數,-0.05為最大分貝數,+80為最小分貝數,-80為最大分貝數
	double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
	lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
    //x=a*b+(c-a)*x;
	NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);
    
    if (lowPassResults>=0.8) {
        soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:7]];
    }else if(lowPassResults>=0.7){
        soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:6]];
    }else if(lowPassResults>=0.6){
        soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:5]];
    }else if(lowPassResults>=0.5){
        soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:4]];
    }else if(lowPassResults>=0.4){
        soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:3]];
    }else if(lowPassResults>=0.3){
        soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:2]];
    }else if(lowPassResults>=0.2){
        soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:1]];
    }else if(lowPassResults>=0.1){
        soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:0]];
    }else{
        soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:0]];
    }

}

//判斷是否允許使用麥克風7.0新增的方法requestRecordPermission
-(BOOL)canRecord
{
    __block BOOL bCanRecord = YES;
    if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0"] != NSOrderedAscending)
    {
        //- (BOOL)respondsToSelector:(SEL)Selector;在NSObject和NSProxy中都存在,在NSProxy中是一個類方法,此處使用的是它的類方法
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        if ([audioSession respondsToSelector:@selector(requestRecordPermission:)]) {
            //performSelector選擇執行器,requestRecordPermission:會話類別等。例子:AV音訊會話類別記錄,AV音訊會話類別和記錄,AVFoundation的一個例項方法。
            [audioSession performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {
                if (granted) {
                    bCanRecord = YES;
                }
                else {
                    bCanRecord = NO;
                    //返回主執行緒執行UI操作,安全起見回撥主函式執行
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [[[UIAlertView alloc] initWithTitle:nil
                                                    message:@"app需要訪問您的麥克風。\n請啟用麥克風-設定/隱私/麥克風"
                                                   delegate:nil
                                          cancelButtonTitle:@"關閉"
                                          otherButtonTitles:nil] show];
                    });
                }
            }];
        }
    }
    return bCanRecord;
}
@end