ios实现录制 mp3文件

作者: 亚洲醋王丶 | 来源:发表于2016-12-20 22:59 被阅读240次

    最近公司做项目,突然让我去实现一个录制MP3的功能,我当时一想这还不简单,苹果有现成的框架,随机几句话就搞定,说着就揽下了这个活,可谁知,坑就在后面........
    我就打算先顺手写一个 demo, 导入什么 AV 框架,创建音频管理器,设置一些参数啊,就搞定了,结果苹果这个巨坑,特么不支持 mp3 录制。

    原来参数是这样写的:

        NSDictionary *settings = @{
                                   AVFormatIDKey:@(kAudioFormatMPEGLayer3),
                                   AVLinearPCMBitDepthKey:@(8), // 位深度
                                   AVSampleRateKey:@(8000),
                                   AVNumberOfChannelsKey:@(1)
                                   };
        
        self.recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:nil];
    

    录制完了去沙盒一看,尼玛,啥都没有,瞬间懵逼了,后来上网搜索了一下才知道,苹果不支持 mp3 录制,但是却能够播放 mp3,好奇葩的设定....
    没办法只能去网络上去搜索,关于转换的教程也是有好几篇,可是一看都是拷贝别人的,运行都报错,而且有的 demo 都是2012年的,现在的 xcode 都没法跑了,更别说使用.
    当时都准备放弃了,在想如何找措辞去推掉这个任务.想了好久还是先放弃这个念头,毕竟这是上头交给的任务,还是完成比较好,最后我还是去网上搜索,发现有个第三方框架,叫做lame,也是比较老的框架了,不过还是很强大的,最后我就利用这个框架,再参考网络上其他人的分享,终于把这个录制实现了.

    分享一下实现的思路:

    开始还是通过苹果官方提供的方法录制成了一个 caf 音频,这是苹果支持的格式,然后再把这个 caf 通过第三方框架 lame 转化为 mp3 格式.

    整体实现的步骤:

    1.老套路,导入 AV 框架和 lame 头文件
    2.创建相关的属性

    @interface ViewController ()
    {
        
        //音频存储的路径
        NSString *_strCAFPath;
        NSString *_strMp3Path;
        
        //录制的音频器
        AVAudioRecorder *_avRecorder;
    
        
        NSMutableArray *_arrayCAFFiles;
        NSMutableArray *_arrayMp3Files;
        
       //这两个属性是按钮的状态判定
        //正在记录
        BOOL _isRecording;
        //正在转换
        BOOL _isConverting;    
    }
    @property (weak, nonatomic) IBOutlet UIButton *recordBtn;
    @end
    

    3.初始化要做的事情,创建相应的文件目录

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //1.获取沙盒路径
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        
        //2.存储 caf 的路径和 mp3的路径
        _strCAFPath = [[NSString alloc] initWithFormat:@"%@/%@",documentsDirectory,@"CAF"];
        _strMp3Path = [[NSString alloc] initWithFormat:@"%@/%@",documentsDirectory,@"Mp3"];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        
        //3.创建各自的文件夹
        [fileManager createDirectoryAtPath:_strCAFPath withIntermediateDirectories:YES attributes:nil error:nil];
        [fileManager createDirectoryAtPath:_strMp3Path withIntermediateDirectories:YES attributes:nil error:nil];
    }
    
    

    4.按钮点击的事件

    //用于切换按钮的两种状态 开始和暂停
        _isRecording=!_isRecording;
        //如果是暂停 意为正在录制
        if (_isRecording) {
            [self.recordBtn setTitle:@"停止" forState:UIControlStateNormal];
            //取出系统的时间
            NSDateFormatter *fileNameFormatter = [[NSDateFormatter alloc] init];
            [fileNameFormatter setDateFormat:@"yyyyMMddhhmmss"];
            //把系统时间的字符格式应用为录制文件的名称
            NSString *fileName = [fileNameFormatter stringFromDate:[NSDate date]];
            fileName = [fileName stringByAppendingString:@".caf"];
            NSString *cafFilePath = [_strCAFPath stringByAppendingPathComponent:fileName];
            //创建音频设备需要的 URL
            NSURL *cafURL = [NSURL fileURLWithPath:cafFilePath];
            NSError *error;
            NSLog(@"cafURL:%@" ,cafURL);
            //设置录制音频需要的一些参数
            NSDictionary *recordFileSettings = [NSDictionary
                                                dictionaryWithObjectsAndKeys:
                                                [NSNumber numberWithInt:AVAudioQualityMin],
                                                AVEncoderAudioQualityKey, //音频质量
                                                [NSNumber numberWithInt:16],
                                                AVEncoderBitRateKey,
                                                [NSNumber numberWithInt: 2], //声音通道,这里必须为双通道 否则无法转换为 mp3
                                                AVNumberOfChannelsKey,
                                                [NSNumber numberWithFloat:44100.0], //采样率
                                                AVSampleRateKey,
                                                nil];
            //判断音频设备是否存在
            if (!_avRecorder) {
                _avRecorder = [[AVAudioRecorder alloc] initWithURL:cafURL settings:recordFileSettings error:&error];
            }else {
                if ([_avRecorder isRecording]) {
                    [_avRecorder stop];
                }
                _avRecorder=Nil;
                _avRecorder = [[AVAudioRecorder alloc] initWithURL:cafURL settings:recordFileSettings error:&error];
            }
            if (_avRecorder) {
                [_avRecorder prepareToRecord];
                _avRecorder.meteringEnabled = YES;
                [_avRecorder record];
                NSLog(@"正在录制");
                self.lastRecordFileName=fileName;
            }
            //否则设置为录制
        }else {
            [self.recordBtn setTitle:@"录制" forState:UIControlStateNormal];
            if (_avRecorder) {
                [_avRecorder stop];
                _avRecorder=Nil;
                //在录制完苹果支持的 caf 文件之后 开始转换
               # [self toMp3:self.lastRecordFileName]; //转换代码
            }
        }
    

    5.转换的重要代码

    //转换为 mp3 格式的重要代码
    - (void)toMp3:(NSString*)cafFileName
    {
        NSString *cafFilePath =[_strCAFPath stringByAppendingPathComponent:cafFileName];
        
        //根据当前的时间来命名录制的名称
        NSDateFormatter *fileNameFormat=[[NSDateFormatter alloc] init];
        [fileNameFormat setDateFormat:@"yyyyMMddhhmmss"];
        NSString *mp3FileName = [fileNameFormat stringFromDate:[NSDate date]];
        mp3FileName = [mp3FileName stringByAppendingString:@".mp3"];
        NSString *mp3FilePath = [_strMp3Path stringByAppendingPathComponent:mp3FileName];
    
        //开始转换
        
        int read, write;
        //转换的源文件位置
        FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb");
        //转换后保存的位置
        FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb");
        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, 44100);
        lame_set_VBR(lame, vbr_default);
        lame_init_params(lame);
        //doWhile 循环
        do {
            read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
            if (read == 0){
                //这里面的代码会在最后调用 只会调用一次
                write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
                //NSLog(@"read0%d",write);
            }
            else{
                //这个 write 是写入文件的长度 在此区域内会一直调用此内中的代码 一直再压缩 mp3文件的大小,直到不满足条件才退出
                write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
                
                //写入文件  前面第一个参数是要写入的块 然后是写入数据的长度 声道 保存地址
                fwrite(mp3_buffer, write, 1, mp3);
                  //NSLog(@"read%d",write);
            }
        } while (read != 0);
        lame_close(lame);
                fclose(mp3);
                fclose(pcm);
    }
    

    实现就这样了,其实没什么难度,主要是这个 lame 框架的使用有点麻烦,都是用 c 语言写的,相对于 c 语言功底较好的人来说,看起来应该分简单。
    有需要的可以试试看,唯一的缺点是转换之后的 mp3音质不是太好,希望有大神能够解决.

    放上 demo 地址 :https://git.oschina.net/yangmaosheng/ConvertCafToMp3.git
    大家记得点赞!

    相关文章

      网友评论

        本文标题:ios实现录制 mp3文件

        本文链接:https://www.haomeiwen.com/subject/acefvttx.html