公司项目中涉及到语音录制的功能,在录音格式方面遇到一些小问题现在拿出来与大家分享一下。
众所周知,iOS 音频录制是不支持AMR格式的。但 Android 好像是默认是AMR格式的。两边格式不同必然有一方做出妥协的。这里只简单介绍一下iOS 格式转码的方法。

1、音频录制简介

AVFoundation框架中AVAudioRecorder类专门处理录音操作,它支持多种音频格式。这里以AVAudioRecorder录制音频,AVAudioPlayer播放音频。 在创建录音时除了指定路径外还必须指定录音设置信息,因为录音时必须知道录音文件的格式、采样率、通道数、每个采样点的位数等信息,但是也并不是所有的信息都必须设置,通常只需要几个常用设置。关于录音设置详见帮助文档中的“AV Foundation Audio Settings Constants”。

2、相关库导入

查了好久 转码好使的就是opencore-amr 编译的静态库这里已经为大家整理好了。
点击这里下载
打开是酱紫滴!导入项目中就OK了。

新项目有BUG!!!
开发遇到BUG很正常,请详细阅读报错信息。我只遇到这一个错先给大家展示出来。

选中你的Targets 选中Build Setting 点击搜索框搜索BitCode 改成No 就OK了。
另外还需要导入两个库
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
引入头文件
#import "VoiceConverter.h"

3、音频录制

  • 开始录音
    //根据当前时间生成文件名self.recordFileName = [ViewController GetCurrentTimeString];//获取路径NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];NSInteger timeStamp = [[NSDate date] timeIntervalSince1970] * 1000;self.recordFilePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.wav", @(timeStamp)]];NSDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithFloat: 8000.0],AVSampleRateKey, //采样率[NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey,[NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,//采样位数 默认 16[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,//通道的数目//                                   [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,//采样信号是整数还是浮点数//                                   [NSNumber numberWithInt: AVAudioQualityMedium],AVEncoderAudioQualityKey,//音频编码质量nil];//初始化录音self.recorder = [[AVAudioRecorder alloc]initWithURL:[NSURL fileURLWithPath:self.recordFilePath]settings:recordSettingerror:nil];//准备录音if ([self.recorder prepareToRecord]){[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error:nil];[[AVAudioSession sharedInstance] setActive:YES error:nil];//开始录音if ([self.recorder record]){//UI操作}}else{NSLog(@"音频格式出错,Recorder---%@",self.recorder);}
  • 停止录音
if (self.recorder.isRecording){//停止录音[self.recorder stop];}
  • 生成当前时间字符串
+(NSString*)GetCurrentTimeString{NSDateFormatter *dateformat = [[NSDateFormatter  alloc]init];[dateformat setDateFormat:@"yyyyMMddHHmmss"];return [dateformat stringFromDate:[NSDate date]];
}
  • 生成文件路径
+(NSString*)GetPathByFileName:(NSString *)_fileName ofType:(NSString *)_type{ NSString *directory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
NSString* fileDirectory = [[[directory stringByAppendingPathComponent:_fileName]stringByAppendingPathExtension:_type]stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];return fileDirectory;
}

4、格式转换

  • WAV转AMR
NSString *amrPath = [ViewController GetPathByFileName:self.recordFileName ofType:@"amr"];if ([VoiceConverter ConvertAmrToWav:self.recordFilePath wavSavePath:amrPath]) {//UI操作 播放音频}else{NSLog(@"wav转amr失败");}
  • AMR转WAV
  NSString  *convertedPath = [ViewController GetPathByFileName:[self.recordFileName stringByAppendingString:@"_AmrToWav"] ofType:@"wav"];if ([VoiceConverter ConvertAmrToWav:amrPath wavSavePath:convertedPath]){//UI操作 播放音频}else{NSLog(@"amr转wav 转换失败");}

5、总结

由于时间关系暂时写这么多 ,后面上传会给大家补上。(未完待续。。。。)

最近在倒腾语音,遇到一些问题,再次记录一下,方便日后使用。

0.ios录制。

1.ios录制语音转化为 amr格式,至于为什么用amr,就不在此赘述了。

2.在静音状态下播放语音。

3.播放过后,整体音量变小。

4.听筒播放和喇叭播放切换。

1.ios录制

使用AVAudioRecorder, 即可完成录音操作。

NSString* pcm_path = @”路径“;
            _recordedTmpFile = [[NSURL alloc] initFileURLWithPath:pcm_path];
            //LinearPCM 是iOS的一种无损编码格式,但是体积较为庞大
            //录音设置
            NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] init];
            //录音格式 无法使用
            [recordSettings setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey: AVFormatIDKey];
            //采样率
            [recordSettings setValue :[NSNumber numberWithFloat:8000.0] forKey: AVSampleRateKey];//44100.0
            //通道数
            [recordSettings setValue :[NSNumber numberWithInt:1] forKey: AVNumberOfChannelsKey];
            //线性采样位数
            //[recordSettings setValue :[NSNumber numberWithInt:16] forKey: AVLinearPCMBitDepthKey];
            //音频质量,采样质量
            [recordSettings setValue:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey];
            
            //Setup the recorder to use this file and record to it.
            _recorder = [[ AVAudioRecorder alloc] initWithURL:_recordedTmpFile settings:recordSettings error:nil];
            _recorder.delegate = self;
            _recorderTime = 0.0f;
        [_recorder prepareToRecord];
        [_recorder record];

2.格式转换。
此处主要使用ibopencore-amr库(Go)。下载下来进行编译,编译脚本网上也可以找到。此处提供懒人模式,密码: 89py。
文件编译好过后,正常导入使用即可。
在使用过成功遇到的一个问题就是。安卓录制的是非amr文件,导致转码失败。此时在读取需要转码文件的时候,看看文件的头部。是不是“

#!AMR\n”。

使用方法:直接调用amrFileCodec.h下面的

EncodeWAVEFileToAMRFile,

DecodeAMRFileToWAVEFile就可以进行转码了。

3.用户静音状态下播放

//不会停止其他程序播放,当设备设置为静音时,无法播放。锁屏会停止播放
    NSString * const AVAudioSessionCategoryAmbient = @"AVAudioSessionCategoryAmbient";
    //停止其他程序播放,当设备设置为静音时,
    NSString *const AVAudioSessionCategorySoloAmbient = @"AVAudioSessionCategorySoloAmbient";
    //停止播放其他程序音频,设备设置为静音的时候,音频依然会继续播放。
    NSString *const AVAudioSessionCategoryPlayback = @"AVAudioSessionCategoryPlayback";
    //只能进行录音
    NSString *const AVAudioSessionCategoryRecord = @"AVAudioSessionCategoryRecord";
    //同时进行录音和播放
    NSString *const AVAudioSessionCategoryPlayAndRecord = @"AVAudioSessionCategoryPlayAndRecord";
    //音频处理,不能进行录制和播放
    NSString *const AVAudioSessionCategoryAudioProcessing = @"AVAudioSessionCategoryAudioProcessing";
        AVAudioSession * audioSession = [AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: nil];

可以根据实际情况,选择合适自己的。

4.音量
此处需要设置一下,可能会出现音量变小的问题。

AVAudioSession * audioSession = [AVAudioSession sharedInstance];
        [audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];

5.听筒和喇叭的切换

需要监听听筒旁边的设备,当设备被挡住时,也就是在接听电话状态。

[[UIDevice currentDevice] setProximityMonitoringEnabled:YES];
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(senderStateChange:)
     name:@"UIDeviceProximityStateDidChangeNotification"
    error:nil];

- (void)senderStateChange:(NSNotificationCenter*)not
    {
        if ([[UIDevice currentDevice] proximityState] == YES)
        {
            NSLog(@"Device is close to user");
            [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
            
        }
        else
        {
            NSLog(@"Device is not close to user");
            [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
            
        }
    }

后记:
下载中有.cpp文件,可以把文件改成点.m。都是c语言的语法。或者是把引用该文件的文件名改成.mm。
最后是需要把自己的语音上传后台,然后再下载下来进行播放即可。
为了避免广告嫌疑,自己找找。有第三方平台,可以上传第三方,然后在下载下来播放。这样自己的后台工作量就简单多了。
--------------------

结合以上两个例子,本人应用实例(不细细解释了,.h要添加类库以及遵守recorder代理协议)

#pragma mark--测试区域-----
-(void)testClick{NSLog(@"已点击登录按钮,正常开始录音");[_recorder record];[self performSelector:@selector(stopRecorder) withObject:nil afterDelay:5];
}
-(void)stopRecorder{[_recorder stop];//    NSString* path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"tmp/aaa"];//    NSURL* url = [NSURL fileURLWithPath:path];//    NSData *data = [NSData dataWithContentsOfURL:url];//    [data writeToFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp/aaa"] atomically:YES];NSLog(@"voicerecorder stop");}
-(void)configRecorder{//设置音频会话NSError *sessionError;[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:&sessionError];if (sessionError){NSLog(@"Error creating session: %@",[sessionError description]);}else{[[AVAudioSession sharedInstance] setActive:YES error:&sessionError];}NSError *error = nil;NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc]init];[recordSetting setObject:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];[recordSetting setObject:[NSNumber numberWithFloat:8000] forKey:AVSampleRateKey];[recordSetting setObject:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
//    [recordSetting setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];//是否使用浮点数采样[recordSetting setObject:@(YES) forKey:AVLinearPCMIsFloatKey];[recordSetting setObject:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey];NSString* path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"aaa.wav"];NSURL* url = [NSURL fileURLWithPath:path];_recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:nil];_recorder.meteringEnabled = YES;_recorder.delegate = self;if ([_recorder prepareToRecord]) {[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error:nil];[[AVAudioSession sharedInstance] setActive:YES error:nil];}}
-(void)ChangeWithInfo:(NSDictionary*)dic{NSString *amrPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"bbb.amr"];NSString* wavPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"aaa.wav"];NSString* newWavPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"ccc.wav"];if ([VoiceConvert ConvertWavToAmr:wavPath amrSavePath:amrPath]) {//UI操作 播放音频//上传NSLog(@"wav转amr成功");[self startUploadWithToken:dic localName:@"localTempAudio" cloudName:@"ios_audio" fileURL:amrPath];if ([VoiceConvert ConvertAmrToWav:amrPath wavSavePath:newWavPath]) {//UI操作 播放音频//上传NSLog(@"amr再转wav成功");[self startUploadWithToken:dic localName:@"localTempAudio" cloudName:@"ios_audio" fileURL:amrPath];}else{NSLog(@"amr再转wav失败");}}else{NSLog(@"wav转amr失败");}}
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{NSLog(@"录音完成,写入文件文称,可以上传");if (flag) {NSLog(@"录音完成成功,写入文件文称,可以上传");NSString* path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"aaa.wav"];NSDictionary *params = @{@"type":@"1",@"videoType":@"wav",@"anchorid":@"90602998"};[[AFRequestManager shareManager] postAudioPath:path params:params AndFlag0Php1WWW2java:1 WithDataName:@"audio" FileName:@"ios_audio" successPost:^(id  _Nonnull successPost) {NSLog(@"audio success:%@",successPost);if ([successPost isKindOfClass:[NSDictionary class]]&&successPost[@"data"]&&[[NSString stringWithFormat:@"%@",successPost[@"status"]] isEqualToString:@"100000"]) {//先判断状态码NSDictionary*info = [NSDictionary dictionaryWithDictionary:successPost[@"data"]];//在判断参数if (info[@"id"]&&info[@"uploadToken"]) {[self ChangeWithInfo:info];}}} errorResponse:^(NSError * _Nonnull error) {NSLog(@"audio error:%@",error);}];}else{NSLog(@"录音完成失败,写入文件文称,可以上传");}
}
- (void)startUploadWithToken:(NSDictionary *)tokenDict localName:(NSString *)localName cloudName:(NSString *)cloudName fileURL:(NSString *)path{NSString * token = @"";if (tokenDict[@"uploadToken"] != nil && ![tokenDict[@"uploadToken"] isKindOfClass:[NSNull class]]) {token = tokenDict[@"uploadToken"];}NSNumber * ID;if (tokenDict[@"id"] != nil && ![tokenDict[@"id"] isKindOfClass:[NSNull class]]) {ID = tokenDict[@"id"];}WCSUploadObjectRequest *uploadRequest = [[WCSUploadObjectRequest alloc] init];uploadRequest.token = token;uploadRequest.key = localName;uploadRequest.fileName = cloudName;uploadRequest.fileURL = [NSURL URLWithString:path];[uploadRequest setUploadProgress:^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {CGFloat precent = (CGFloat)totalBytesSent/(CGFloat)totalBytesExpectedToSend;NSLog(@"progress - %@,%@,%f.2", @(totalBytesSent), @(totalBytesExpectedToSend),precent);dispatch_async(dispatch_get_main_queue(), ^{NSDictionary * dict = @{@"progress":[NSNumber numberWithFloat:precent]};
//            [[NSNotificationCenter defaultCenter] postNotificationName:kShareVideoUpdateProgressNotification object:nil userInfo:dict];});}];[[self.wcsClient uploadRequest:uploadRequest] continueWithBlock:^id _Nullable(WCSTask<WCSUploadObjectResult *> * _Nonnull task) {if (task.error) {NSLog(@"===ERROR===\r\n%@", task.error);dispatch_async(dispatch_get_main_queue(), ^{[RemindView showHUDWithText:@"上传失败" onView:kYBKeyWindow];});} else {/*taskResult - {bucket = "11-vod"; 1fsize = 1013859; 1hash = "FthrKaoXR-EWP5daJkF4fPv-KNWr"; 1key = "2690104-20171117171405.mp4"; 1mimeType = "video/mp4"; 1url = "http://wsrecord.xxxx.com/2690104-20171117171405.mp4"; 1}*/NSLog(@"taskResult - %@", task.result.results);NSDictionary * dictionary;@try {dictionary = @{@"results" : task.result.results,@"ID" : ID};} @catch (NSException *exception) {
#ifdef DEBUGNSLog(@"exception - %@",exception.description);NSAssert(!exception, exception.description);
#endifdictionary = [NSDictionary dictionary];} @finally {///请求服务器,H5地址///这里面通过AFN请求,会回到主线程发送通知[[AFRequestManager shareManager] requestUploadResultWithDict:dictionary CompletionHandler:^(NSString *url) {
//                    [[NSNotificationCenter defaultCenter] postNotificationName:kShareVideoSetupShareStateNotification object:nil userInfo:@{@"url":url}];}];}}return nil;}];
}
- (WCSClient *)wcsClient {if (!_wcsClient) {_wcsClient = [[WCSClient alloc]initWithBaseURL:[NSURL URLWithString:@"http://jjhgame.up3.v1.wcsapi.com"] andTimeout:10];}return _wcsClient;
}

更多问题加Q群讨论:565191947

iOS 音频录制AMR和WAV互转(支持64位)相关推荐

  1. IOS音频格式之AMR和WAV互转(更新支持64位)

    转自:http://blog.csdn.net/gf771115/article/details/45643271 //-----------------2015.3.20-------------- ...

  2. Unity4.6.2发布 支持64位iOS

    IL2CPP是Unity内部开发的一款创新型脚本技术.它能极大地提高项目中所有脚本的性能,并且完美兼容当前iOS构建所使用的Mono-AOT解决方案. 我们可以使用IL2CPP将脚本快速移植到新平台( ...

  3. iOS 音频录制、播放(本地、网络)

    文章目录一.录音机(AVAudioRecorder)1.简介2.如何使用3.具体实现(开始.暂停.停止.播放 四个功能)4.附件实现demo二.播放音频1.播放本地音频文件(AVAudioPlayer ...

  4. Unity3d使用高通Vuforia发布IOS工程不支持64位的一些解决办法

    1.将Unit升级至4.6.x或5.0.x,将Vuforia差距升级到最新版本(vuforia-unity-mobile-android-ios-4-0-105 ) 2.平台Other Setting ...

  5. iOS录音转码:amr转wav,wav转amr

    iOS 录音转码:amr到wav,wav到am: 这里就把iOS录制的wav文件转成amr,我们采用的是libopencore框架. 关于libopencore,大家可以搜索:iOS音频格式AMR和W ...

  6. iOS开发32位与64位的坑

    众所周知,苹果是从iPhone 5s开始对全线移动产品使用64位架构,那么如果App需要兼容32位和64位的机型,就需要注意它们之间的区别. 下面我们首先看看基本数据类型的区别: 32位编译器 cha ...

  7. Unity 4.6.2 iOS 64位支持

    今天,我们宣布Unity 4.6.2的公开发布, 现在可以下载 . 这是使用我们新的IL2CPP技术的首个公开发布的iOS 64位支持版本 . (Today we announce the publi ...

  8. iOS上应用如何兼容32位系统和64位系统

    在苹果推出iPhone5S时,64位的应用就走到了眼前.当时就看见苹果官方资料宣布iOS7.x的SDK支持了64位的应用,而且内置的应用都已经是64位. 我记得自己刚刚接触电脑时还有16位的系统,指针 ...

  9. ios自建服务器降级,开发者发布苹果iOS 64位设备降级工具:支持恢复到关闭验证固件...

    IT之家讯12月19日消息,iOS越狱开发者tihmstar宣布即将发布一款新的工具Prometheus(普罗米修斯,"偷火者"),他宣称这款工具支持苹果64位iOS设备升级或降级 ...

最新文章

  1. jsp超链接中怎么跳转页面跳转_JSP页面跳转方法小结
  2. Node.js 官方Path模块简介
  3. vue生命周期(列表详解)
  4. C# 使用多个异步方法
  5. nginx资源定向 css js路径问题
  6. HDFS机架感知概念及配置实现
  7. ubuntu下安装Node.js(源码安装)
  8. uni-app中的样式
  9. 《趣学JavaScript——教孩子学编程》——1.5 本章小结
  10. Python_日记 序列化和反序列化
  11. crypto_policy_set_aes_cm_128_hmac_sha1_32/crypto_policy_set_aes_cm_128_hmac_sha1_80找不到
  12. 《轻松读懂spring》之 IOC的主干流程(上)
  13. 字体和font-family对照表
  14. 相亲交友小程序开发方案及源码
  15. python爬取链家深圳全部二手房的详细信息
  16. 记一次nacos获取配置失败的坑
  17. 什么是P = NP?问题
  18. ldo低压差线性稳压器电路解析
  19. 【系统分析师之路】2008年上系统分析师上午综合知识真题
  20. 2018远景能源笔试

热门文章

  1. 【思行合一,共达彼岸】 – 梦想不止步,我为你的成长负责
  2. PAT 乙级 1069 微博转发抽奖 (20 分)
  3. php 自动换行,用php绘制海报时,大段文字如何自动换行
  4. 辽宁本溪:群猴趁动物园搬迁出逃闯进市长办公室
  5. 2022新农村别墅自建房CAD设计施工图纸+总共几千套
  6. 四国军棋界面开发(2) 让棋子动起来
  7. html5 ,form表单提交不跳转
  8. [编程题] 数字游戏
  9. 数据可视化就应该这么做!
  10. 网线的制作过程与应用