利用AVFoundation框架實現錄音和播放(AVAudioSession、AVAudioRecorder、AVAudioPlayer)
最近實現了一個簡單功能,類似微信傳送語音,按下錄音,鬆開結束錄音;並且可播放;
效果圖:
Demo下載地址:
需要匯入
#import <AVFoundation/AVFoundation.h>
利用此框架中的
AVAudioRecorder和AVAudioPlayer來錄音和播放
以下是AVAudioRecorder錄音的使用方法:
- - (IBAction)downAction:(id)sender {
- //按下錄音
- if ([self canRecord]) {
- NSError *error = nil;
-
//必須真機上測試,模擬器上可能會崩潰
- recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL URLWithString:playName] settings:recorderSettingsDict error:&error];
- if (recorder) {
- //是否允許重新整理電平表,預設是off
- recorder.meteringEnabled = YES;
- //建立檔案,並準備錄音
-
[recorder prepareToRecord];
- //開始錄音
- [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);
- }
- }
- }
- - (IBAction)upAction:(id)sender {
- //鬆開 結束錄音
- //錄音停止
- [recorder stop];
- recorder = nil;
- //結束定時器
- [timer invalidate];
- timer = nil;
- //圖片重置
- soundLodingImageView.image = [UIImage imageNamed:[volumImages objectAtIndex:0]];
- }
以下是AVAudioPlayer播放器的使用方法:
- - (IBAction)playAction:(id)sender {
- NSError *playerError;
- //播放
- player = nil;
- player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:playName] error:&playerError];
- if (player == nil)
- {
- NSLog(@"ERror creating player: %@", [playerError description]);
- }else{
- [player play];
- }
- }
如果是7.0,第一次執行會提示,是否允許使用麥克風:
7.0需要設定:
- if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0"] != NSOrderedAscending)
- {
- //7.0第一次執行會提示,是否允許使用麥克風
- AVAudioSession *session = [AVAudioSession sharedInstance];
- NSError *sessionError;
- //AVAudioSessionCategoryPlayAndRecord用於錄音和播放
- [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
- if(session == nil)
- NSLog(@"Error creating session: %@", [sessionError description]);
- else
- [session setActive:YES error:nil];
- }
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