学习ios第一个练手功能就是给已有产品加上语音通信功能,能够互通ios与android。这里给出自己的一些心得,希望能给他人一些参考。
类似产品使用的技术
目前支持的开源第三方库也就只有 ilbc和speex了
考虑采用的方案
speex 需要Android和ios都进入转码,工作量太大,不采用。 剩下的方案就是在amr和ilbc上选择了,android支持amr,低版本不支持ilbc, ios高版本(4.3)只支持ilbc,不能支持amr。 刚开始的测试方案使用的是android将语音转ilbc, 由于我对于android开发不是太熟悉,在同事的帮助下一直没有转换成功,现在想想可能是处理问题,如果能转换成或,这种方案应该是最方便的。
将语音录成原始pcm码
注意这里,虽然录制是pcm码,但出来的文件ios依然会封装一层,将其包装成pcf格式。所以就有了第二步。
将pcf中的pcm码取出来
使用libopencore库将其编成amr格式,这时可以发送给android端播放了。
将android版本发送过来的amr解码出来
播放原始pcm即可
代码文件都放在github上了,有需要的可以参考一下。 https://github.com/hhuai/ios_util
--------------------------------------------------------------
真正用到的例子
https://github.com/guange2015/ios-amr
--------------------------------------------------------------
国外专门的博客
http://www.raywenderlich.com/69365/audio-tutorial-ios-file-data-formats-2014-edition
--------------------------------------------------------------
我是采用的AVAudioRecorder这个框架来进行录音
这个录音跟官方网站上的speakHere有些区别,最大的区别是,这个必须要录制完成才能处理文件,而speakhere示例是可以实现边录制边上传的效果。
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
引入框架,这是使用录音功能的基本配备
先说明一点,默认AVAudioRecorder录制后的格式是.caf,而大部分的播放器都是不支持这个格式的,下面一段设置是可以让录制格式是wav的格式
NSDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0],AVSampleRateKey, //采样率
[NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey,
[NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,//采样位数 默认 16
[NSNumber numberWithInt: 2], AVNumberOfChannelsKey,//通道的数目
[NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,//大端还是小端 是内存的组织方式
[NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,nil];//采样信号是整数还是浮点数
NSURL *recordedTmpFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent: [NSString stringWithFormat: @"%.0f.%@", [NSDate timeIntervalSinceReferenceDate] * 1000.0, @"wav"]]]; //文件名的设置
//Setup the recorder to use this file and record to it.
AVAudioRecorder *recorder = [[ AVAudioRecorder alloc] initWithURL:recordedTmpFile settings:recordSetting error:&error];
[recorder prepareToRecord];
[recorder record];
下面代码应该是当前.m文件加载时候就设置
AVAudioSession * audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: &error]; //设置音频类别,这里表示当应用启动,停掉后台其他音频
[audioSession setActive:YES error: &error];//设置当前应用音频活跃性
---------------------------------------------------------------------------------
//参数指定录音文件名和路径 -(void)startRecordWithPath:(NSString *)path { NSError * err = nil; AVAudioSession *audioSession = [AVAudioSession sharedInstance]; [audioSession setCategory :AVAudioSessionCategoryPlayAndRecord error:&err]; if(err){ NSLog(@"audioSession: %@ %d %@", [err domain], [err code], [[err userInfo] description]); return; } [audioSession setActive:YES error:&err]; err = nil; if(err){ NSLog(@"audioSession: %@ %d %@", [err domain], [err code], [[err userInfo] description]); return; } /* ios 几种格式录音大小 Here are the results for few encoding supported by iPhone. Size of audio file in KB of duration 10 sec. kAudioFormatMPEG4AAC : 164, kAudioFormatAppleLossless : 430, kAudioFormatAppleIMA4 : 475, kAudioFormatULaw : 889, kAudioFormatALaw : 889, Among these kAudioFormatMPEG4AAC is having smallest size */ //默认AVAudioRecorder录制后的格式是.caf // NSMutableDictionary * recordSetting = [NSMutableDictionary dictionary]; // [recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey]; //caf // [recordSetting setValue:[NSNumber numberWithFloat:16000.0] forKey:AVSampleRateKey]; //采样率 // [recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey]; //大部分的播放器都是不支持这个格式的,下面一段设置是可以让录制格式是wav的格式 // NSDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys: // [NSNumber numberWithFloat: 44100.0],AVSampleRateKey, //采样率 // [NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey, // [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,//采样位数 默认 16 // [NSNumber numberWithInt: 2], AVNumberOfChannelsKey,//通道的数目 // [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,//大端还是小端 是内存的组织方式 // [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,nil];//采样信号是整数还是浮点数 // NSDictionary *recordSetting = @{AVFormatIDKey : @(kAudioFormatLinearPCM), AVEncoderBitRateKey:@(16),AVEncoderAudioQualityKey : @(AVAudioQualityMax), AVSampleRateKey : @(8000.0), AVNumberOfChannelsKey : @(1)}; self.recordPath = path; NSURL * url = [NSURL fileURLWithPath:self.recordPath]; err = nil; NSData * audioData = [NSData dataWithContentsOfFile:[url path] options: 0 error:&err]; if(audioData) { NSFileManager *fm = [NSFileManager defaultManager]; [fm removeItemAtPath:[url path] error:&err]; } err = nil; if(self.recorder){[self.recorder stop];self.recorder = nil;} self.recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:&err]; if(!_recorder){ NSLog(@"recorder: %@ %d %@", [err domain], [err code], [[err userInfo] description]); UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Warning" message: [err localizedDescription] delegate: nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; return; } [_recorder setDelegate:self]; [_recorder prepareToRecord]; _recorder.meteringEnabled = YES; BOOL audioHWAvailable = audioSession.inputIsAvailable; if (! audioHWAvailable) { UIAlertView *cantRecordAlert = [[UIAlertView alloc] initWithTitle: @"提醒" message: @"设备无录音功能" delegate: nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [cantRecordAlert show]; return; } //[_recorder recordForDuration:(NSTimeInterval) 60]; self.recordTime = 0; [self resetTimer]; timer_ = [NSTimer scheduledTimerWithTimeInterval:WAVE_UPDATE_FREQUENCY target:self selector:@selector(updateMeters) userInfo:nil repeats:YES]; NSLog(@"------- timer_ : %@",timer_); [self showVoiceHudOrHide:YES]; } -(void) stopRecordWithCompletionBlock:(void (^)())completion { dispatch_async(dispatch_get_main_queue(),completion); // [self.recorder stopRecording]; //停止录音 [self resetTimer]; [self showVoiceHudOrHide:NO]; }