swift 录制多个音频 并将音频转换为mp3 并合成多个mp3文件为一个文件
我的需求是可以录制多个文件,最后生成的文件格式为mp3形式,查了下各种资料,因为swift无法直接将音频录制为mp3格式,所以最后我采取的解决方案为先将每个单独的文件转为mp3,最后逐一合并形成一个mp3文件
首先第一步录制 简单说明下:
参考: http://www.jianshu.com/p/09af208a5663(感谢) http://www.hangge.com/blog/cache/detail_772.html(感谢)
1. 音频配置,我尝试了下尽可能多加各种配置最后有问题,测试成功的配置如下
1 2 3 4 5 6 | recorderSeetingsDic : [ String : Any ] = [ AVSampleRateKey : NSNumber ( value : 16000 ), //采样率 AVFormatIDKey : NSNumber ( value : kAudioFormatLinearPCM ), //音频格式 AVNumberOfChannelsKey : NSNumber ( value : 2 ), //通道数 AVEncoderAudioQualityKey : NSNumber ( value : AVAudioQuality . min . rawValue ) //录音质量 ] |
2,录制实现(部分代码)
1 2 | //音频路径 let audioPath = ( NSSearchPathForDirectoriesInDomains (. documentDirectory , . userDomainMask , true ). first ?. appending ( "/test.caf" ))! |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | let session : AVAudioSession = AVAudioSession . sharedInstance () try ! session . setCategory ( AVAudioSessionCategoryPlayAndRecord ) try ! session . setActive ( true ) do { recorder = try AVAudioRecorder ( url : URL ( string : audioPath )!, settings : recorderSeetingsDic ) //开启仪表计数功能 recorder !. isMeteringEnabled = true //准备录音 recorder !. prepareToRecord () recorder ?. record () } catch let err { print ( "录音失败:\( err . localizedDescription )" ) } |
第二步将录制的多个caf格式音频转为一个mp3文件,(最初采用的方式是将多个.caf文件合成为m4a文件的 但是找不到m4a转为mp3的解决方法,用lame也行不通,很郁闷)
1.首先要编译lame 这是将.caf转为mp3第一步,有很多可以参考的方式
我参考的编译方式:http://blog.csdn.net/cating1314/article/details/46046497(感谢)
我们需要编译过后的libmp3lame.a与lame.h两个文件,都在编译过后的thin-lame与fat-lame文件中。根据自己的需求选取,我采用的是fat-lame中的两个文件
不想编译的话可以采用我的,应该也不会有啥问题 https://pan.baidu.com/s/1dFepCIl 提取密码:reck
2转换以及合成方法 (转换与合成均采用OC写的)
参考: https://github.com/CharmingLee/RecordingDemo(感谢)
定义了两个文件convertMp3.h与convertMp3.m
convertMp3.h文件
1 2 3 4 5 6 7 | # import < UIKit / UIKit . h > @ interface convertMp3 : NSObject - ( void ) audioPCMtoMP3 :( NSString *) audioFileSavePath mp3File :( NSString *) mp3FilePath mergeFile :( NSString *) mergeFilePath ; @ end |
convertMp3.m文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | # import "convertMp3.h" # import "lame.h" # define KFILESIZE ( 1 * 1024 * 1024 ) @ interface convertMp3 () @ end @ implementation convertMp3 - ( void ) audioPCMtoMP3 :( NSString *) audioFileSavePath mp3File :( NSString *) mp3FilePath mergeFile :( NSString *) mergeFilePath { @ try { int read , write ; FILE * pcm = fopen ([ audioFileSavePath cStringUsingEncoding : 1 ], "rb" ); //source 被转换的音频文件位置 fseek ( pcm , 4 * 1024 , SEEK_CUR ); //skip file header FILE * mp3 = fopen ([ mp3FilePath 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 , 16000 ); lame_set_VBR ( lame , vbr_off ); lame_init_params ( lame ); do { read = ( int ) 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 { //从第二个文件才开始合并 第一个文件mergeFilePath与mp3FilePath相同 即第一个转换后的文件地址就是最终合成的完整的mp3文件地址 if ([ mergeFilePath isEqualToString : mp3FilePath ] == NO ) { [ self pieceFileA : mergeFilePath withFileB : mp3FilePath ]; } } } - ( BOOL ) pieceFileA :( NSString *) filePathA withFileB :( NSString *) filePathB { //合成过后的文件路径 之后不管多少文件 都是在这个filePathA文件之后继续写入的 NSString * synthesisFilePath = filePathA ; // 更新的方式读取文件A NSFileHandle * handleA = [ NSFileHandle fileHandleForUpdatingAtPath : synthesisFilePath ]; [ handleA seekToEndOfFile ]; NSDictionary * fileBDic = [[ NSFileManager defaultManager ] attributesOfItemAtPath : filePathB error : nil ]; long long fileSizeB = fileBDic . fileSize ; // 大于xM分片拼接xM if ( fileSizeB > KFILESIZE ) { // 分片 long long pieces = fileSizeB / KFILESIZE ; // 整片 long long let = fileSizeB % KFILESIZE ; // 剩余片 long long sizes = pieces ; // 有余数 if ( let > 0 ) { // 加多一片 sizes += 1 ; } NSFileHandle * handleB = [ NSFileHandle fileHandleForReadingAtPath : filePathB ]; for ( int i = 0 ; i < sizes ; i ++) { [ handleB seekToFileOffset : i * KFILESIZE ]; NSData * tmp = [ handleB readDataOfLength : KFILESIZE ]; [ handleA writeData : tmp ]; } [ handleB synchronizeFile ]; // 大于xM分片读xM(最后一片可能小于xM) } else { [ handleA writeData :[ NSData dataWithContentsOfFile : filePathB ]]; } [ handleA synchronizeFile ]; NSLog (@ "合并完成" ); // 将B文件删除 // [[NSFileManager defaultManager] removeItemAtPath:filePathB error:nil]; return YES ; } @ end |
在桥接文件中加入头文件 #import "convertMp3.h"
3.调用OC文件开始合并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //合并 func transferAudio ( num : Int ) { let documentDirectoryURL = FileManager . default . urls ( for : . documentDirectory , in : . userDomainMask ). first ! as NSURL let stringDate = BaseController (). getCurrentTimeMillisecond () let mp3Path : URL = ( documentDirectoryURL . appendingPathComponent ( BaseController (). randomMD5 ( str : stringDate ) + ".mp3" )! as URL as NSURL ) as URL ! if num == 0 { self . audioPath = mp3Path . path } convertMp3 (). audioPCMtoMP3 ( self . audioLocalUrls [ num ], mp3File : mp3Path . path , mergeFile : self . audioPath ) if num == self . audioLocalUrls . count - 1 { self . uploadAudio () //上传 } else { let order = num + 1 transferAudio ( num : order ) } } |
调用transferAudio(num:0) 开始一个一个caf文件转换合并 这样流程走通了 录制多个caf文件 逐个将其转为mp3 在合并为一个完整mp3文件,刚接触swift没多长时间 东拼西凑实现的功能 如果有哪里不对 希望及时告诉我 谢谢,希望对其它有次需求的人有个参考,毕竟折磨我很长时间
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 《HelloGitHub》第 106 期
· 数据库服务器 SQL Server 版本升级公告
· 深入理解Mybatis分库分表执行原理
· 使用 Dify + LLM 构建精确任务处理应用