iOS 使用AVFoundation实现语音的录制和播放
iOS的AVFoundation框架中的AVAudioRecorder和AVAudioPlayer可以实现语音的录制和播放功能demo下载
AVAudioRecorder
AVAudioRecorder 的初始化方法是
- (nullable instancetype)initWithURL:(NSURL *)url settings:(NSDictionary<NSString *, id> *)settings error:(NSError **)outError;
- url: 录制的语音文件保存的路径,文件的类型是由这个参数值的file extension推测的。
- settings: 对audio recored的设置。在iOS7中,默认的设置是
{ AVFormatIDKey = 1819304813;
AVLinearPCMBitDepthKey = 16;
AVLinearPCMIsBigEndianKey = 0;
AVLinearPCMIsFloatKey = 0;
AVLinearPCMIsNonInterleaved = 0;
AVNumberOfChannelsKey = 2;
AVSampleRateKey = 44100;}
下面的三个key适用于所有的语音格式:
AVFormatIDKey
:格式的标识,它对应的是个枚举值。
CF_ENUM(AudioFormatID)
{
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',
kAudioFormatAMR_WB = 'sawb',
kAudioFormatAudible = 'AUDB',
kAudioFormatiLBC = 'ilbc',
kAudioFormatDVIIntelIMA = 0x6D730011,
kAudioFormatMicrosoftGSM = 0x6D730031,
kAudioFormatAES3 = 'aes3',
kAudioFormatEnhancedAC3 = 'ec-3'
};
AVSampleRateKey
: 采样率,44.1kHZ和标准的CD Audio是相同的,除非你需要一个高保真的录音,你不需要这样高的采样率,大部分的音频软件只能特定的速率像32KHZ,24KHZ,16KHZ,12KHZ.8KHZ是电话采样率,对一般的录音已经足够了。
AVNumberOfChannelsKey
:通道数。设成2的话是双声道。iPhone只有一个麦克风,一个单声道的通道足够了,它把你的数据需求削减了一半。
参考
开始录音的代码:
NSString * url = NSTemporaryDirectory();
url = [url stringByAppendingString:[NSString stringWithFormat:@"%f.wav", [[NSDate date] timeIntervalSince1970]]];
NSMutableDictionary * settings = @{}.mutableCopy;
[settings setObject:[NSNumber numberWithFloat:8000.0] forKey:AVSampleRateKey];
[settings setObject:[NSNumber numberWithInt: kAudioFormatLinearPCM] forKey:AVFormatIDKey];
[settings setObject:@1 forKey:AVNumberOfChannelsKey];//设置成一个通道,iPnone只有一个麦克风,一个通道已经足够了
[settings setObject:@16 forKey:AVLinearPCMBitDepthKey];//采样的位数
self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:url] settings:settings error:&error];
self.audioRecorder.delegate = self;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];
self.audioRecorder.meteringEnabled = YES;
BOOL success = [self.audioRecorder record];
if (success) {
NSLog(@"录音开始成功");
}else{
NSLog(@"录音开始失败");
}
遇到的问题
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];
这个是设置AVAudioSession的category的,如果不设置的话,在模拟器上success是YES,但是在真机上是NO。同样,在用AVAudioPlayer播放语音时要设置category为AVAudioSessionCategoryPlayback
。
停止录音
[self.audioRecorder stop];
调用这个方法后,会走下面的代理方法
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
NSURL * url = recorder.url;
}
获取录音过程中的分贝
要在录音过程中获取分贝数,要在录音前先把AVAudioRecorder的属性meteringEnabled设置成YES.
在录音开始后,可以设置一个定时器获取分贝数
_metesTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(setVoiceImage) userInfo:nil repeats:YES];
-(void)setVoiceImage{
if (self.audioRecorder.isRecording) {
[self.audioRecorder updateMeters];
float peakPower = [self.audioRecorder peakPowerForChannel:0];
NSLog(@"%f", peakPower);
}
}
AVAudioRecorder
的averagePowerForChannel
和peakPowerForChannel
方法返回的是分贝数据,数值在-160 – 0之间
播放语音
可以将录音后获取的url传入下面的方法中:
- (void)playAudioWithURL:(NSURL *)url{
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
NSError * error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
self.audioPlayer.delegate = self;
BOOL success = [self.audioPlayer play];
if (success) {
NSLog(@"播放成功");
}else{
NSLog(@"播放失败");
}
}
我在demo里封装了一个ZMAudioManager的类,方便进行语音的播放和录制:
开始录音
[[ZMAudioManager shareInstance] startRecordingWithFileName:[NSString stringWithFormat:@"%f.wav", [[NSDate date] timeIntervalSince1970]] completion:^(NSError *error) {
if (error) {
else{
}
}];
停止录音,recordPath是录音文件存放的路径,aDuration是录音时长
[[ZMAudioManager shareInstance] stopRecordingWithType:ZMAudioRecordeAMRType completion:^(NSString *recordPath, NSInteger aDuration, NSError *error) {
if (error) {
UIAlertView *a = [[UIAlertView alloc] initWithTitle:@"error" message:error.domain delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil];
[a show];
}else{
}];
播放录音,audioPath是录音文件存放的路径,录音播放完后会走completion回调
[[ZMAudioManager shareInstance] playAudioWithPath:audioPath completion:^(NSError *error) {
}];
在实际开发中,考虑到要和android之间通信,android支持amr,不支持wav;iOS支持wav,不支持amr。通常iOS客户端会进行amr和wav的互相转换。
在demo里EMVoiceConverter文件包含了wav和amr互相转换的方法:
+ (int)amrToWav:(NSString*)_amrPath wavSavePath:(NSString*)_savePath;
+ (int)wavToAmr:(NSString*)_wavPath amrSavePath:(NSString*)_savePath;