iOS錄音和音頻播放


簡(jiǎn)介

最近公司研發(fā)了一個(gè)語(yǔ)音識(shí)別的框架,但這個(gè)框架是后端識(shí)別,所以需要手機(jī)端錄音,錄音后將音頻文件通轉(zhuǎn)成NSData類型,然后通過(guò)bsae64轉(zhuǎn)碼傳給后臺(tái)進(jìn)行語(yǔ)音識(shí)別,識(shí)別的文字再傳回給前端,雖然我認(rèn)為這樣做體驗(yàn)并不好,畢竟請(qǐng)求是耗時(shí)的,并且iOS10以后,自帶語(yǔ)音識(shí)別框架,但畢竟是公司研發(fā)的框架,所以就配合做了一下測(cè)試。

iOS錄音和音頻播放用到時(shí)AVFoundation.h框架中的,AVAudioRecorder類和AVAudioPlayer,其中AVAudioPlayer播放器只能播放本地音頻文件,如果要在線播放需要用AVPlayer,其配合AVPlayerLayer類還可實(shí)現(xiàn)視頻播放。另外支持播放進(jìn)度監(jiān)聽(tīng)。

AVAudioRecorder錄音

AVAudioRecorder錄音的主要步驟:

創(chuàng)建錄音文件保存路徑,并設(shè)置相關(guān)屬性(是一個(gè)字典),然后實(shí)例化AVAudioRecorder錄音對(duì)象,該對(duì)象必須是全局變量。

使用AVAudioSession的單例對(duì)象(音頻會(huì)話對(duì)象)來(lái)設(shè)置當(dāng)前音頻會(huì)話狀態(tài)和會(huì)話類別。

錄音對(duì)象設(shè)置開(kāi)始錄音,錄音結(jié)束后會(huì)調(diào)用錄音完成的代理方法。

//導(dǎo)入頭文件

#import <AVFoundation/AVFoundation.h>

#import "GTMBase64.h"

@interface ViewController ()<AVAudioRecorderDelegate,AVAudioPlayerDelegate>{

? ? NSURL * recordUrl;

? ? AVAudioRecorder * audioRecorder;//必須為全局變量

? ? AVAudioPlayer * player;//必須為全局變量

? ? }

//開(kāi)始錄音

- (void)startRecord

{

? ? //刪除上次生成的文件,保留最新文件

? ? NSFileManager *fileManager = [NSFileManager defaultManager];

? ? //默認(rèn)就是wav格式,是無(wú)損的

? ? if ([fileManager fileExistsAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]]) {

? ? ? ? [fileManager removeItemAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"] error:nil];

? ? }

? ? //錄音設(shè)置

? ? NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];

? ? //設(shè)置錄音格式 AVFormatIDKey==kAudioFormatLinearPCM

? ? [recordSetting setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];

? ? //設(shè)置錄音采樣率(Hz) 如:AVSampleRateKey==8000/44100/96000(影響音頻的質(zhì)量), 采樣率必須要設(shè)為11025才能使轉(zhuǎn)化成mp3格式后不會(huì)失真

? ? [recordSetting setValue:[NSNumber numberWithFloat:16000] forKey:AVSampleRateKey];

? ? //錄音通道數(shù) 1 或 2 ,要轉(zhuǎn)換成mp3格式必須為雙通道

? ? [recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];

? ? //線性采樣位數(shù) 8、16、24、32

? ? [recordSetting setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];

? ? //錄音的質(zhì)量

? ? [recordSetting setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];

? ? // 設(shè)置錄制音頻采用高位優(yōu)先的記錄格式

? ? [recordSetting setValue:[NSNumber numberWithBool:YES] forKey:AVLinearPCMIsBigEndianKey];

? ? // 設(shè)置采樣信號(hào)采用浮點(diǎn)數(shù)

? ? [recordSetting setValue:[NSNumber numberWithBool:YES] forKey:AVLinearPCMIsFloatKey];

? ? //存儲(chǔ)錄音文件

? ? NSURL * recordUrl = [NSURL URLWithString:[NSTemporaryDirectory()stringByAppendingPathComponent:@"recordAudio.wav"]];

? ? //初始化錄音對(duì)象

? ? NSError * error;

? ? audioRecorder = [[AVAudioRecorder alloc] initWithURL:recordUrl settings:recordSetting error:&error];

? ? if (error) {

? ? ? ? NSLog(@"%@",error.description);

? ? ? ? return;

? ? }

? ? audioRecorder.delegate = self;

? ? //開(kāi)啟音量檢測(cè)

? ? audioRecorder.meteringEnabled = YES;

? ? AVAudioSession * audioSession = [AVAudioSession sharedInstance];//得到音頻會(huì)話單例對(duì)象

? ? //如果不是正在錄音

? ? if (![audioRecorder isRecording]) {

? ? ? ? [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];//設(shè)置類別,表示該應(yīng)用同時(shí)支持播放和錄音

? ? ? ? [audioSession setActive:YES error:nil];//激活當(dāng)前應(yīng)用的音頻會(huì)話,此時(shí)會(huì)阻斷后臺(tái)音樂(lè)的播放.

? ? ? ? [audioRecorder prepareToRecord];//準(zhǔn)備錄音

? ? ? ? [audioRecorder record];//開(kāi)始錄音

? ? ? ? //暫停錄音

//? ? ? ? [audioRecorder pause];

? ? }

}

//結(jié)束錄音

- (void)endRecord

{

? ? [audioRecorder stop];? ? ? ? ? ? //錄音停止

}

//錄音結(jié)束后代理

-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{

? ? [[AVAudioSession sharedInstance] setActive:NO error:nil];//一定要在錄音停止以后再關(guān)閉音頻會(huì)話管理(否則會(huì)報(bào)錯(cuò)),此時(shí)會(huì)延續(xù)后臺(tái)音樂(lè)播放

? ? if (!flag) return;

? ? //請(qǐng)求接口傳給后臺(tái)

? ? [self postwavdata];

}

注意:

錄音時(shí)音頻文件類型后綴必須為.caf、.aif、.wav中的一種,不能是.mp3,但播放是可以播放.mp3文件,可以導(dǎo)入githu上的一個(gè)C語(yǔ)言框架lame轉(zhuǎn)成mp3音頻文件。

將音頻文件轉(zhuǎn)成NSData然后用bsae64加密傳給服務(wù)器,用系統(tǒng)自帶的base64EncodedStringWithOptions編碼方法后臺(tái)可能解析有問(wèn)題,主要是有空格和換行,可以多encode一次轉(zhuǎn)義去掉這些特殊符號(hào),也可以用第三方框架GTMBase64,請(qǐng)求數(shù)據(jù)時(shí)候request的Content-Type要設(shè)置為application/json,否則后臺(tái)可能結(jié)束不到音頻轉(zhuǎn)碼后端參數(shù)。


//設(shè)置request的Content-Type為application/json

[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

//轉(zhuǎn)成base64編碼格式上傳服務(wù)器

-(NSString *)wavToBASE64{

NSData * wavData = [NSData dataWithContentsOfFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]];

NSString * encodedImageStr = [GTMBase64 encodeBase64Data:wavData];

}

AVAudioPlayer播放音頻文件。

AVAudioPlayer播放音頻文件主要步驟:

獲取音頻文件URL或者音頻文件數(shù)據(jù);

初始化AVAudioPlayer音頻播放器全局對(duì)象;

設(shè)置相關(guān)屬性,并播放文件。

相關(guān)代碼

————————————————


//導(dǎo)入頭文件

#import <AVFoundation/AVFoundation.h>

#import "GTMBase64.h"

@interface ViewController ()<AVAudioRecorderDelegate,AVAudioPlayerDelegate>{

? ? NSURL * recordUrl;

? ? AVAudioRecorder * audioRecorder;//必須為全局變量

? ? AVAudioPlayer * player;//必須為全局變量

? ? }

//開(kāi)始錄音

- (void)startRecord

{

? ? //刪除上次生成的文件,保留最新文件

? ? NSFileManager *fileManager = [NSFileManager defaultManager];

? ? //默認(rèn)就是wav格式,是無(wú)損的

? ? if ([fileManager fileExistsAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]]) {

? ? ? ? [fileManager removeItemAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"] error:nil];

? ? }

? ? //錄音設(shè)置

? ? NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];

? ? //設(shè)置錄音格式 AVFormatIDKey==kAudioFormatLinearPCM

? ? [recordSetting setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];

? ? //設(shè)置錄音采樣率(Hz) 如:AVSampleRateKey==8000/44100/96000(影響音頻的質(zhì)量), 采樣率必須要設(shè)為11025才能使轉(zhuǎn)化成mp3格式后不會(huì)失真

? ? [recordSetting setValue:[NSNumber numberWithFloat:16000] forKey:AVSampleRateKey];

? ? //錄音通道數(shù) 1 或 2 ,要轉(zhuǎn)換成mp3格式必須為雙通道

? ? [recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];

? ? //線性采樣位數(shù) 8、16、24、32

? ? [recordSetting setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];

? ? //錄音的質(zhì)量

? ? [recordSetting setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];

? ? // 設(shè)置錄制音頻采用高位優(yōu)先的記錄格式

? ? [recordSetting setValue:[NSNumber numberWithBool:YES] forKey:AVLinearPCMIsBigEndianKey];

? ? // 設(shè)置采樣信號(hào)采用浮點(diǎn)數(shù)

? ? [recordSetting setValue:[NSNumber numberWithBool:YES] forKey:AVLinearPCMIsFloatKey];

? ? //存儲(chǔ)錄音文件

? ? NSURL * recordUrl = [NSURL URLWithString:[NSTemporaryDirectory()stringByAppendingPathComponent:@"recordAudio.wav"]];

? ? //初始化錄音對(duì)象

? ? NSError * error;

? ? audioRecorder = [[AVAudioRecorder alloc] initWithURL:recordUrl settings:recordSetting error:&error];

? ? if (error) {

? ? ? ? NSLog(@"%@",error.description);

? ? ? ? return;

? ? }

? ? audioRecorder.delegate = self;

? ? //開(kāi)啟音量檢測(cè)

? ? audioRecorder.meteringEnabled = YES;

? ? AVAudioSession * audioSession = [AVAudioSession sharedInstance];//得到音頻會(huì)話單例對(duì)象

? ? //如果不是正在錄音

? ? if (![audioRecorder isRecording]) {

? ? ? ? [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];//設(shè)置類別,表示該應(yīng)用同時(shí)支持播放和錄音

? ? ? ? [audioSession setActive:YES error:nil];//激活當(dāng)前應(yīng)用的音頻會(huì)話,此時(shí)會(huì)阻斷后臺(tái)音樂(lè)的播放.

? ? ? ? [audioRecorder prepareToRecord];//準(zhǔn)備錄音

? ? ? ? [audioRecorder record];//開(kāi)始錄音

? ? ? ? //暫停錄音

//? ? ? ? [audioRecorder pause];

? ? }

}

//結(jié)束錄音

- (void)endRecord

{

? ? [audioRecorder stop];? ? ? ? ? ? //錄音停止

}

//錄音結(jié)束后代理

-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{

? ? [[AVAudioSession sharedInstance] setActive:NO error:nil];//一定要在錄音停止以后再關(guān)閉音頻會(huì)話管理(否則會(huì)報(bào)錯(cuò)),此時(shí)會(huì)延續(xù)后臺(tái)音樂(lè)播放

? ? if (!flag) return;

? ? //請(qǐng)求接口傳給后臺(tái)

? ? [self postwavdata];

}

注意:

錄音時(shí)音頻文件類型后綴必須為.caf、.aif、.wav中的一種,不能是.mp3,但播放是可以播放.mp3文件,可以導(dǎo)入githu上的一個(gè)C語(yǔ)言框架lame轉(zhuǎn)成mp3音頻文件。

將音頻文件轉(zhuǎn)成NSData然后用bsae64加密傳給服務(wù)器,用系統(tǒng)自帶的base64EncodedStringWithOptions編碼方法后臺(tái)可能解析有問(wèn)題,主要是有空格和換行,可以多encode一次轉(zhuǎn)義去掉這些特殊符號(hào),也可以用第三方框架GTMBase64,請(qǐng)求數(shù)據(jù)時(shí)候request的Content-Type要設(shè)置為application/json,否則后臺(tái)可能結(jié)束不到音頻轉(zhuǎn)碼后端參數(shù)。

————————————————

//設(shè)置request的Content-Type為application/json

[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

//轉(zhuǎn)成base64編碼格式上傳服務(wù)器

-(NSString *)wavToBASE64{

NSData * wavData = [NSData dataWithContentsOfFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]];

NSString * encodedImageStr = [GTMBase64 encodeBase64Data:wavData];

}


AVAudioPlayer播放音頻文件。

AVAudioPlayer播放音頻文件主要步驟:

獲取音頻文件URL或者音頻文件數(shù)據(jù);

初始化AVAudioPlayer音頻播放器全局對(duì)象;

設(shè)置相關(guān)屬性,并播放文件。

相關(guān)代碼


//開(kāi)始播放音頻文件

- (void)playWav{

//獲取音頻文件url

//? NSURL * url = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]];

//獲取錄音數(shù)據(jù)

NSData * wavData = [NSData dataWithContentsOfFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]];

NSError * error;

//? ? AVAudioPlayer * player = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];

? player = [[AVAudioPlayer alloc]initWithData:wavData error:&error];

? player.delegate = self;

? if (error) {

? ? ? NSLog(@"語(yǔ)音播放失敗,%@",error);

? ? ? return;

? }

? ? //播放器的聲音會(huì)自動(dòng)切到receiver,所以聽(tīng)起來(lái)特別小,如果需要從speaker出聲,需要自己設(shè)置。

? [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];

? // 單獨(dú)設(shè)置音樂(lè)的音量(默認(rèn)1.0,可設(shè)置范圍為0.0至1.0,兩個(gè)極端為靜音、系統(tǒng)音量):

? player.volume = 1.0;

//? ? 修改左右聲道的平衡(默認(rèn)0.0,可設(shè)置范圍為-1.0至1.0,兩個(gè)極端分別為只有左聲道、只有右聲道):

? player.pan = -1;

//? ? 設(shè)置播放速度(默認(rèn)1.0,可設(shè)置范圍為0.5至2.0,兩個(gè)極端分別為一半速度、兩倍速度):

? player.rate = 2.0;

//? ? 設(shè)置循環(huán)播放(默認(rèn)1,若設(shè)置值大于0,則為相應(yīng)的循環(huán)次數(shù),設(shè)置為-1可以實(shí)現(xiàn)無(wú)限循環(huán)):

? player.numberOfLoops = 0;

//? ? player.currentTime = 0;

? //調(diào)用prepareToPlay方法,這樣可以提前獲取需要的硬件支持,并加載音頻到緩沖區(qū)。在調(diào)用play方法時(shí),減少開(kāi)始播放的延遲。

? [player prepareToPlay];

//? ? 開(kāi)始播放音樂(lè):

? [player play];


? }

//播放完成代理

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{

? if (flag) {

? ? ? NSLog(@"停止播放");

? ? ? //調(diào)用pause或stop來(lái)暫停播放,這里的stop方法的效果也只是暫停播放,不同之處是stop會(huì)撤銷prepareToPlay方法所做的準(zhǔn)備。

? ? ? [player stop];

? ? ? player = nil;

? }

}


最后


給大家推薦一個(gè)優(yōu)秀的iOS交流平臺(tái),平臺(tái)里的伙伴們都是非常優(yōu)秀的iOS開(kāi)發(fā)人員,我們專注于技術(shù)的分享與技巧的交流,大家可以在平臺(tái)上討論技術(shù),交流學(xué)習(xí)。歡迎大家的加入(想要進(jìn)入的可加小編微信1367798518)


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容