iOS开发之录音功能开发2019-06-06

项目开发需求

  • 应用场景:项目中用到了答疑提问的功能,这个功能的应用就是学员端提交答案和教师端回答问题,两端配合使用,那么考虑到学员提问的时候对问题描述不清晰以及在教师回答问题的时候更加方便快速,所以就加上了录音答疑的功能。

音频相关知识介绍:

  1. 文件格式(不同的文件格式,可保存不同的编码格式的文件)
    1.1 WAV:
    特点:音质最好的格式,对应PCM编码
    适用:多媒体开发,保存音乐和音效素材。
    1.2 MP3:
    特点:音质好,压缩比比较高,被大量软件和硬件支持。
    适用:适合用于比较高要求的音乐欣赏。
    1.3 caf:
    特点:适用于几乎iOS中所有的编码格式。

  2. 编码格式
    2.1 PCM
    PCM:脉冲编码调制,是一种非压缩音频数字化技术,是一种未压缩的原音重现,数字模式下,音频的初始化信号是PCM.
    2.2 MP3
    2.3 AAC
    AAC:其实是“高级音频编码(advanced audio coding)”的缩写,他是被设计用来取代MPC格式的。
    2.4 HE-AAC
    HE-AAC是AAC的一个超集,这个“High efficiency”,HE-AAC是专门为低比特率所优化的一种音频编码格式。
    2.5 AMR
    AMR全称是“Adaptive Multi-Rate”,它也是另一个专门为“说话(speech)”所优化的编码格式,也是适合低比特率环境下采用。
    2.6 ALAC
    它全称是“Apple Lossless”,这是一种没有任何质量损失的音频编码方式,也就是我们说的无损压缩。
    2.7 IMA4
    IMA4:这是一个在16-bit音频文件下按照4:1的压缩比来进行压缩的格式。

考虑到服务端要保证iOS端和安卓端的同样的效果,因此在iOS端录音所生成的Caf格式录音文件转码为Mp3后进行上传,这样就保证两端音频播放的一致性。

功能实现核心代码

因为我会在文章的最后面附上完整的demo,因此这里只展示核心代码部分,以及注意事项。

  • 创建录音并设置参数
#pragma mark - 创建录音
-(void)createRecorder{
    
    // Setup audio session
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
    
    if ([session respondsToSelector:@selector(requestRecordPermission:)]) {
        
        [session requestRecordPermission:^(BOOL available) {
            
            if (available) {
            }
            else
            {
                dispatch_async(dispatch_get_main_queue(), ^{
                
                    [[[UIAlertView alloc] initWithTitle:@"无法录音" message:@"请在“设置-隐私-麦克风”选项中允许xx访问你的麦克风" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show];
                    return ;
                });
            }
        }];
        
    }
    
    //录音参数设置设置
    NSMutableDictionary *settingDic = [[NSMutableDictionary alloc] init];
    //设置录音格式
    [settingDic setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
    //设置录音采样率(Hz) 如:AVSampleRateKey==8000/44100/96000(影响音频的质量)//11025.0
    [settingDic setValue:[NSNumber numberWithFloat:11025.0] forKey:AVSampleRateKey];
    //录音通道数  1 或 2
    [settingDic setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
    //线性采样位数  8、16、24、32
    [settingDic setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
    //录音的质量
//    [settingDic setValue:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey];
    [settingDic setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];
    
    //录音文件最终地址
    _recordURL=[CDPAudioRecorder getAudioRecordFilePath];
    NSError *error=nil;
    
    //初始化AVAudioRecorder
    _recorder = [[AVAudioRecorder alloc] initWithURL:_recordURL settings:settingDic error:&error];
    //开启音量分贝数监测
    _recorder.meteringEnabled = YES;
    _recorder.delegate=self;

    if (_recorder&&[_recorder prepareToRecord]) {
        
    }
    else{
        DLog(@"CDPAudioRecorder录音初始化失败error:%@",error);
    }
}
  • 录音后会生成Caf格式的文件,然后把文件转化为Mp3格式,
    现在最主流的就是用lame框架来实现啦
    不过lame只支持32位的,这对64位的很坑
    不过我们可以下载lame的源码然后自己转成64位的就可以用啦
    下面就是步骤,跟着动起来吧!
    一、下载lame的最新版。地址https://sourceforge.net/projects/lame/files/lame/
    这个就行啦

二、还需要下载一个脚本,地址https://github.com/kewlbear/lame-ios-build

三、这两个就是我下载的两个文件夹,在桌面上

23011-37ba520e94644c33.png

四、打开lame-ios-build-master 文件,把里面的build-lame.sh 放到lame-3.99.5 文件夹中

23011-4abbd324556af223.png

五、最重要的来啦。打开build-lame.sh文件,找到如图所示的地方,注释很清楚啦,保存关闭

23011-97dfa49b36385464.png

六、打开终端,cd到lame-3.99.5这个目录下,就是下载lame源码的目录
输入

chmod 777 build-lame.sh

再输入

./build-lame.sh

然后等待编译,差不多不用1分钟就编译完了

七、打开lame-3.99.5目录,在里面会看到两个文件夹

23011-653317a8338eab4e.png
thin-lame目录下有几个文件夹,里面放的是不同指令下的我们要用到的.h和.a文件
fat-lame目录下则是不同指令下合并编译出来的.h和.a文件,也就是我们最终会用到的

拿到我们想要的就可以去转MP3啦

录音转换为mp3格式:

- (void)audio_PCMtoMP3 {
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filePath = [path stringByAppendingPathComponent:@"CDPAudioFiles/CDPAudioRecord.caf"];
    NSString *audioPath = [LameTool audioToMP3:filePath isDeleteSourchFile:NO];
    if ([audioPath isEqualToString:@"fail"]) {
        NSLog(@"上传失败");
    } else {
        self.audioString = audioPath;
        [_recorder setRecordURL:[NSURL URLWithString:audioPath]];
    }
    NSLog(@"转码amr格式成功----文件地址为:%@",filePath);
}
+ (NSString *)audioToMP3: (NSString *)sourcePath isDeleteSourchFile:(BOOL)isDelete {
    
    // 输入路径
    NSString *inPath = sourcePath;
    // 判断输入路径是否存在
    NSFileManager *fm = [NSFileManager defaultManager];
    if (![fm fileExistsAtPath:sourcePath]) {
        NSLog(@"文件不存在");
        return @"fail";
    }
    // 输出路径
    NSString *outPath = [[sourcePath stringByDeletingPathExtension] stringByAppendingString:@".mp3"];
    @try {
        int read, write;
        
        FILE *pcm = fopen([inPath cStringUsingEncoding:1], "rb");  //source 被转换的音频文件位置
        
        if(pcm == NULL) {
            NSLog(@"file not found");
            return @"fail";
        }
        fseek(pcm, 4*1024, SEEK_CUR);                                   //skip file header
        FILE *mp3 = fopen([outPath cStringUsingEncoding:1], "wb");  //output 输出生成的Mp3文件位置
        
        const int PCM_SIZE = 8192;
        const int MP3_SIZE = 8192;
        short int pcm_buffer[PCM_SIZE*2];
        unsigned char mp3_buffer[MP3_SIZE];
        
//        lame_t lame = lame_init();
//        lame_set_in_samplerate(lame, 8000);//8000/11025.0//44100
//        lame_set_VBR(lame, vbr_default);
//        lame_init_params(lame);

        
        lame_t lame = lame_init();
        lame_set_num_channels(lame, 2);//设置1为单通道,默认为2双通道
        lame_set_in_samplerate(lame, 11025.0);//11025.0
        //lame_set_VBR(lame, vbr_default);
        lame_set_brate(lame, 16);
        lame_set_mode(lame, 3);
        lame_set_quality(lame, 2);
        lame_init_params(lame);

        
        do {
            //size_t size = (size_t)(2 * sizeof(short int));
            //read = fread(pcm_buffer, size, PCM_SIZE, pcm);
            read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
            if (read == 0)
                write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
            else
                write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
            
            fwrite(mp3_buffer, write, 1, mp3);
            
        } while (read != 0);
        
        lame_close(lame);
        fclose(mp3);
        fclose(pcm);
    }
    @catch (NSException *exception) {
        NSLog(@"%@",[exception description]);
    }
    @finally {
        NSLog(@"MP3生成成功:");
        if (isDelete) {
            
            NSError *error;
            [fm removeItemAtPath:sourcePath error:&error];
            if (error == nil) {
                NSLog(@"删除源文件成功");
            }
        }
        return outPath;
    }
}

注意事项:这里的几个参数配置:创建录音的时候频率为多少,转化为mp3的时候就使用多少:
录音:

//设置录音采样率(Hz) 如:AVSampleRateKey==8000/44100/96000(影响音频的质量)//11025.0
   [settingDic setValue:[NSNumber numberWithFloat:11025.0] forKey:AVSampleRateKey];
   //录音通道数  1 或 2

转化:

lame_t lame = lame_init();
        lame_set_num_channels(lame, 2);//设置1为单通道,默认为2双通道
        lame_set_in_samplerate(lame, 11025.0);//11025.0
        //lame_set_VBR(lame, vbr_default);
        lame_set_brate(lame, 16);
        lame_set_mode(lame, 3);
        lame_set_quality(lame, 2);
        lame_init_params(lame);

到此就结束了,简单的demo如下:

https://github.com/Wululu6/AudioRecordDemo.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容