├── README.md └── RecorderDemo ├── RecorderDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── xuxiwen.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── xuxiwen.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── RecorderDemo.xcscheme │ └── xcschememanagement.plist ├── RecorderDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── ConvertAudio │ ├── ConvertAudioFile.h │ ├── ConvertAudioFile.m │ └── Lame │ │ └── lame.framework │ │ ├── Headers │ │ └── lame.h │ │ └── lame ├── Info.plist ├── PlayerManager │ ├── PlayerManager.h │ └── PlayerManager.m ├── ViewController.h ├── ViewController.m └── main.m ├── RecorderDemoTests ├── Info.plist └── RecorderDemoTests.m └── RecorderDemoUITests ├── Info.plist └── RecorderDemoUITests.m /README.md: -------------------------------------------------------------------------------- 1 | # iOS 使用 Lame 转码 MP3 的最正确姿势 2 | 3 | ## 前言 4 | 5 | * 最近在项目中, 做有关 **AVAudioRecorder** 的录音开发, 需要把录制的格式转成 MP3, 遇到了转码之后的MP3文件, 无法获取正确的时长问题. 6 | * 为了解决这个问题, 真的是反复来修改录音配置, 浪费了不知道多少的时间来分析这个问题. 7 | * 中间我去某某群去找大神提问问题,结果遭到了鄙视, 都统统质疑我的录音配置, 最后甩给我一个demo, 结果我一测试, 也是一样的问题, 我就呵呵了. 8 | * 所以, 我今天来写一篇文章来认真剖析这个问题, 为什么起名 ? **iOS 使用 Lame 转码 MP3 的最正确姿势 !** 是因为我在百度搜索到的各种有关于 **Lame** 转码的代码, 至少很大一部分 都是不完全正确的. 9 | 10 | ## 概述 11 | 12 | 我将会在本篇文章分析以下几点内容 13 | 14 | * AVAudioRecorder 配置 和 Lame 编码压缩配置 15 | * 解决录音时长读取不正确的问题 16 | * 边录制边转码的实现 17 | * 测试 Demo 18 | 19 | ## AVAudioRecorder 配置 和 Lame 编码压缩配置 20 | ### AVAudioRecorder 配置的注意事项 21 | 22 | 23 | > 关于 AVAudioRecorder 录音的相关配置 和 Lame 包的编译工作, 这里忽略不讲, 主要是想说一下需要注意的地方 24 | 25 | * Lame 的转码压缩, 是把录制的 PCM 转码成 MP3, 所以录制的 `AVFormatIDKey` 设置成 `kAudioFormatLinearPCM` , 生成的文件可以是 caf 或者 wav. 26 | * [caf](http://baike.baidu.com/link?url=TsCl2mxLZvWORN0CnhwPqjxElPDDREWgTyVrIkxWHyoOjbtYnn2kSW2qaliPHSUCHNOyNFbjRGfKqwmkgn08WK) 文件是 Mac OS X 原本支持的众多音频格式中最新增加的一种. iPhone 短信就是这种格式, 录制出的文件会比较大. 27 | * `AVNumberOfChannelsKey` 必须设置为双声道, 不然转码生成的 MP3 会声音尖锐变声. 28 | * `AVSampleRateKey` 必须保证和转码设置的相同. 29 | 30 | ### Lame 编码压缩 的相关配置 31 | 32 | - 我们需要录音源文件路径和生成MP3的路径 `FILE *pcm` 和 `FILE *mp3`, 33 | 34 | ``` 35 | //source 被转换的音频文件位置 36 | FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb"); 37 | //skip file header 跳过 PCM header 能保证录音的开头没有噪音 38 | fseek(pcm, 4*1024, SEEK_CUR); 39 | //output 输出生成的Mp3文件位置 40 | FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb+"); 41 | ``` 42 | 43 | * 通过 `fopen` 需要注意打开文件的模式. 👇 是扩展的 的 C 语言的 文件打开模式, 为什么要说这些, 比如 我使用 wb 来打开 mp3, 就意味着我只允许写数据, 而如果你有对文件的读取操作,将会出现错误, 这也是我被坑过的地方. 44 | 45 | ``` 46 | C 语言的 文件打开模式 47 | 48 | w+以纯文本方式读写,而wb+是以二进制方式进行读写。 49 | mode说明: 50 | w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。 51 | w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。 52 | wb 只写方式打开或新建一个二进制文件,只允许写数据。 53 | wb+ 读写方式打开或建立一个二进制文件,允许读和写。 54 | r 打开只读文件,该文件必须存在,否则报错。 55 | r+ 打开可读写的文件,该文件必须存在,否则报错。 56 | rb+ 读写方式打开一个二进制文件,只允许读写数据。 57 | a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留) 58 | a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留) 59 | ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。 60 | 加入b 字符用来告诉函数库打开的文件为二进制文件,而非纯文字文件。 61 | ``` 62 | 63 | * 然后是 `lame_init()` 来初始化, ` lame_set_num_channels(lame,1)` 默认转码为2双通道, 设置单声道会更大程度减少压缩后文件的体积. 64 | * 接下来 是执行一个 do while 的循环来反复读取 `FILE* stream ` , 直到 read != 0 , 结束转码,释放 `lame_close(lame); fclose(mp3); fclose(pcm);` 65 | 66 | ## 解决录音时长读取不正确的问题 67 | 68 | > Lame 的转码配置网上有很多, 网上可以搜到很多相关的代码, 作为小白 copy 使用, 由于不懂源码实现,直接拿来用就出现了不可预料的问题. 我出现的播放时间不准确的问题, 无论是 AVPlayer 或者 AVAudioPlayer 均无法读取正确的长度, 要么是多几秒, 要么是少几秒, 还可能是超过10s的的误差, 但是播放的过程中, 定时器的计数 会和 总时间显示不吻合, 就比如 一个显示 2:30 的录音, 活生生 放到了 2:50, 你能想象是多么的尴尬Bug. 69 | 70 | ### 问题猜测 71 | 72 | **我把录制完成的文件, 使用 iTunes 来播放可以显示出正确的长度, 但是使用 QuickTime Player 会出现和 AVPlayer 一样的错误时长 !!!** 73 | 74 | - 所以分析造成这个问题的原因可能是: 75 | * 1. AVPlayer 不能正确读取长度 76 | * 2. MP3的编码出现了错误... 77 | 78 | - 然后网上也有人遇到了同样的问题,给出的解决方法是换一种 AVPlayer 读取方法: 79 | 我总结了 AVPlayer 获取总时长的以下方法 ,结果测试 结果都是相近, 80 | 81 | * way 1 82 | 83 | ``` 84 | CMTime time = _player.currentItem.duration; 85 | if (time.timescale == 0) { 86 | return 0; 87 | } 88 | return time.value / time.timescale; 89 | ``` 90 | 91 | * way 2 92 | 93 | ``` 94 | if (self.player && self.player.currentItem && self.player.currentItem.asset) { 95 | return CMTimeGetSeconds(self.player.currentItem.asset.duration); 96 | 97 | } else{ 98 | return 0; 99 | } 100 | 101 | ``` 102 | 103 | * way 3 104 | 105 | ``` 106 | AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:self.playingURL options:nil]; 107 | CMTime audioDuration = audioAsset.duration; 108 | float audioDurationSeconds = CMTimeGetSeconds(audioDuration); 109 | return (NSInteger)audioDurationSeconds; 110 | ``` 111 | 112 | - 其中 , 使用 [Asset](http://blog.csdn.net/qingyuan159/article/details/53085302) 可以解决获取总时间是 NA 的这种错误情况. 实际中我并没有出现过. 113 | - 我的测试中 AVPlayer 使用这几个方法, 均无法得到正确的值, 所以应该就是生成文件的问题了. 114 | 115 | ### 了解MP3编码格式 116 | 117 | 然后,通过对[MP3编码格式](http://blog.csdn.net/xiahouzuoxin/article/details/7860631)调研, 了解到如下信息: 118 | 119 | * MP3使用的是动态码率方式,而这种方式每一帧的长度应该是不等的。那会不会是 **AVPlayer** 是把文件当做每帧相等的方式来计算的总时间,所以才不对? 120 | * 不断输出 AVPlayer duration来看, 每次都会有不同的结果, 而 AVPlayer 是支持Mp3 VBR格式文件播放的。所以应该还是我们的生成的文件有问题 121 | * 了解到 MP3 VBR头这个东西,有它记录了整个文件的帧总数量,就能直接算出duration.所以是不是我们Lame编码的时候,没有写入 VBR 头 呢. 122 | 123 | ### Lame 源码分析 124 | 125 | 126 | * 搜索 Lame 源码 **VBR**关键字可以得到 127 | 128 | ``` 129 | /* 130 | 1 = write a Xing VBR header frame. 131 | default = 1 132 | this variable must have been added by a Hungarian notation Windows programmer :-) 133 | */ 134 | int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int); 135 | int CDECL lame_get_bWriteVbrTag(const lame_global_flags *); 136 | ``` 137 | 138 | * 源码写的很简单, 就是设置了 `gfp->write_lame_tag`值, 看看所有调用 `write_lame_tag` 的地方吧。第一个就找到了`lame_encode_mp3_frame(..)`函数。这不就是用来每次灌buffer给lame做MP3编码的方法嘛!也就是说每次都会给给帧添加VBR信息,这和之前看的编码资料描述的一样。 139 | 140 | * 接下来, 就是需要找到写入VBR头的函数, 搜索源码可得 `PutLameVBR()` 被调用在`lame_get_lametag_frame()`函数里, 然后我们来看看这个函数: 141 | 142 | 143 | ``` 144 | /* 145 | * OPTIONAL: 146 | * lame_mp3_tags_fid will rewrite a Xing VBR tag to the mp3 file with file 147 | * pointer fid. These calls perform forward and backwards seeks, so make 148 | * sure fid is a real file. Make sure lame_encode_flush has been called, 149 | * and all mp3 data has been written to the file before calling this 150 | * function. 151 | * NOTE: 152 | * if VBR tags are turned off by the user, or turned off by LAME because 153 | * the output is not a regular file, this call does nothing 154 | * NOTE: 155 | * LAME wants to read from the file to skip an optional ID3v2 tag, so 156 | * make sure you opened the file for writing and reading. 157 | * NOTE: 158 | * You can call lame_get_lametag_frame instead, if you want to insert 159 | * the lametag yourself. 160 | */ 161 | void CDECL lame_mp3_tags_fid(lame_global_flags *, FILE* fid); 162 | ``` 163 | 164 | * 原来这个函数是应该在lame_encode_flush()之后调, 当所有数据都写入完毕了再调用。仔细想想也很合理, 这时才能确定文件的总帧数。 165 | 166 | ### 问题解决 167 | 168 | * 现在的思路就比较清晰了, 由于在Lame编码的过程中, 我们没有对VBR头进行写入, 导致了 AVPlayer duration 以每帧相同的方式来计算出现的错误. 169 | * 解决方法是, 在lame文件全部写入之后, lame释放之前, 使用 `lame_mp3_tags_fid` 写入 VBR 头文件, 测试通过, 读取时间正常. 170 | * 而这行代码 `lame_mp3_tags_fid` 我在 网上搜索的各种配置中发现都没有写. 171 | 172 | ## 边录制边转码的实现 173 | 174 | > 通常我们是在录制结束之后, 再进行转码; 当录制的时间较长, 会消耗的时间比较长. 用户需要等待转码结束后,才能操作; 但是如果我们使用边录制,边转码的方式, 开另外一个线程同时进行转码,则几乎没有等待的时间,效率上会比较的高. 175 | 176 | 177 | * 核心代码实现 178 | 179 | ``` 180 | do { 181 | curpos = ftell(pcm); 182 | long startPos = ftell(pcm); 183 | fseek(pcm, 0, SEEK_END); 184 | long endPos = ftell(pcm); 185 | long length = endPos - startPos; 186 | fseek(pcm, curpos, SEEK_SET); 187 | 188 | if (length > PCM_SIZE * 2 * sizeof(short int)) { 189 | 190 | if (!isSkipPCMHeader) { 191 | //Uump audio file header, If you do not skip file header 192 | //you will heard some noise at the beginning!!! 193 | fseek(pcm, 4 * 1024, SEEK_CUR); 194 | isSkipPCMHeader = YES; 195 | NSLog(@"skip pcm file header !!!!!!!!!!"); 196 | } 197 | 198 | read = (int)fread(pcm_buffer, 2 * sizeof(short int), PCM_SIZE, pcm); 199 | write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); 200 | fwrite(mp3_buffer, write, 1, mp3); 201 | NSLog(@"read %d bytes", write); 202 | } else { 203 | [NSThread sleepForTimeInterval:0.05]; 204 | NSLog(@"sleep"); 205 | } 206 | 207 | } while (! weakself.stopRecord); 208 | ``` 209 | 210 | * 边录边转码, 只是我们在录制结果后,重新开一个线程来进行文件的转码, 211 | * 当录音进行中时, 会持续读取到指定大小文件,进行编码, 读取不到,则线程休眠 212 | * 在 while 的条件中, 我们收到 录音结束的条件,则会结束 do while 的循环. 213 | * 我们需要在录制结束后发送一个信号, 让 do while 跳出循环 214 | 215 | ## 测试 Demo 216 | 217 | > 为了让遇到相同问题的人, 能够更加对这些问题有一点的了解, 我会 在这里贴一个我测试的Demo 这只是一个实例程序, 并不具备完整的逻辑功能, 请熟知. 218 | 219 | - 关于Demo, 可以在 ViewController 中 `#define ENCODE_MP3 1` 使用 1 和 0 , 来测试普通转码 和 边录制 边转码. 220 | - `ConvertAudioFile` 是录音转码封装的源码 221 | - 边录边转的用法 222 | 223 | ``` 224 | [[ConvertAudioFile sharedInstance] conventToMp3WithCafFilePath:self.cafPath 225 | mp3FilePath:self.mp3Path 226 | sampleRate:ETRECORD_RATE callback:^(BOOL result) 227 | { 228 | NSLog(@"---- 转码完成 --- result %d ---- ", result); 229 | }];; 230 | 231 | ``` 232 | 233 | - 录制完成转码的用法 234 | 235 | ``` 236 | [ConvertAudioFile conventToMp3WithCafFilePath:self.cafPath 237 | mp3FilePath:self.mp3Path 238 | sampleRate:ETRECORD_RATE callback:^(BOOL result) 239 | { 240 | NSLog(@"---- 转码完成 --- result %d ---- ", result); 241 | }]; 242 | 243 | ``` 244 | 245 | - Demo 见 文章底部, 如果Demo 有什么不理解 和 不准确的地方,还麻烦指正... 246 | 247 | ## 结语 248 | 249 | > 由于时间有限, 我并不会 写太多细致的内容, 只是对这几天的研究做一个总结,和列举一些注意事项,如果在做音频录制转码中遇到相同的问题,则会有比较大的帮助. 250 | 251 | ### 总结 252 | 253 | 这次解决这个问题,让我受益匪浅, 很多地方的收获是超过问题本身的: 254 | 255 | * 在使用别人的示范代码时,如果不进行一定的剖析;当出现问题的时间,会比较的难判断问题的来源 256 | * iOS的相关技术博客,现在网上可以搜到很多相关示范代码, 但是由于很多人可能也是贴出了并不是很准确的东西, 相关给别人带来了错误的示范. 257 | * 作为 iOS 开发者, 对很多东西,如果想要有更加深层次的理解,则需要 1. 计算机基础扎实 2. iOS底层理解够深 3.架构设计模式理解够深 4.代码平时写的必须够优雅 258 | * Google 会比 Baidu 靠谱呀; 虽然我之前也是这么想的,但这次对我有帮助的文章均来自 Google, 相反Baidu 给了很多错误的示范. 259 | 260 | ### Link 261 | 262 | * [Demo - GitHub](https://github.com/CivelXu/iOS-Lame-Audio-transcoding.git) 263 | * [简书](http://www.jianshu.com/p/971fff236881) 264 | * [个人博客](http://civelxu.com/2016/07/04/iOS%20使用%20Lame%20转码%20MP3%20的最正确姿势/) 265 | 266 | ### 致谢 267 | 268 | > 对我有帮助的文章 269 | 270 | * https://itony.me/365.html 271 | * http://www.jianshu.com/p/57f38f075ba0 272 | 273 | 274 | 275 | 276 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E76CBD3E1F16FC48006993AA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E76CBD3D1F16FC48006993AA /* main.m */; }; 11 | E76CBD411F16FC48006993AA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E76CBD401F16FC48006993AA /* AppDelegate.m */; }; 12 | E76CBD441F16FC48006993AA /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E76CBD431F16FC48006993AA /* ViewController.m */; }; 13 | E76CBD471F16FC48006993AA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E76CBD451F16FC48006993AA /* Main.storyboard */; }; 14 | E76CBD491F16FC48006993AA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E76CBD481F16FC48006993AA /* Assets.xcassets */; }; 15 | E76CBD4C1F16FC48006993AA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E76CBD4A1F16FC48006993AA /* LaunchScreen.storyboard */; }; 16 | E76CBD571F16FC48006993AA /* RecorderDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E76CBD561F16FC48006993AA /* RecorderDemoTests.m */; }; 17 | E76CBD621F16FC48006993AA /* RecorderDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = E76CBD611F16FC48006993AA /* RecorderDemoUITests.m */; }; 18 | E7CE68611F17518A00A5142A /* ConvertAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = E7CE685D1F17518A00A5142A /* ConvertAudioFile.m */; }; 19 | E7CE68661F1751B800A5142A /* PlayerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E7CE68651F1751B800A5142A /* PlayerManager.m */; }; 20 | E7CE68731F18683100A5142A /* lame.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7CE68721F18683100A5142A /* lame.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | E76CBD531F16FC48006993AA /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = E76CBD311F16FC48006993AA /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = E76CBD381F16FC48006993AA; 29 | remoteInfo = RecorderDemo; 30 | }; 31 | E76CBD5E1F16FC48006993AA /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = E76CBD311F16FC48006993AA /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = E76CBD381F16FC48006993AA; 36 | remoteInfo = RecorderDemo; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | E76CBD391F16FC48006993AA /* RecorderDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RecorderDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | E76CBD3D1F16FC48006993AA /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 43 | E76CBD3F1F16FC48006993AA /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 44 | E76CBD401F16FC48006993AA /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 45 | E76CBD421F16FC48006993AA /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 46 | E76CBD431F16FC48006993AA /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 47 | E76CBD461F16FC48006993AA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | E76CBD481F16FC48006993AA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | E76CBD4B1F16FC48006993AA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | E76CBD4D1F16FC48006993AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | E76CBD521F16FC48006993AA /* RecorderDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RecorderDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | E76CBD561F16FC48006993AA /* RecorderDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RecorderDemoTests.m; sourceTree = ""; }; 53 | E76CBD581F16FC48006993AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | E76CBD5D1F16FC48006993AA /* RecorderDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RecorderDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | E76CBD611F16FC48006993AA /* RecorderDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RecorderDemoUITests.m; sourceTree = ""; }; 56 | E76CBD631F16FC48006993AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | E7CE685C1F17518A00A5142A /* ConvertAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConvertAudioFile.h; sourceTree = ""; }; 58 | E7CE685D1F17518A00A5142A /* ConvertAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConvertAudioFile.m; sourceTree = ""; }; 59 | E7CE68641F1751B800A5142A /* PlayerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayerManager.h; sourceTree = ""; }; 60 | E7CE68651F1751B800A5142A /* PlayerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayerManager.m; sourceTree = ""; }; 61 | E7CE68721F18683100A5142A /* lame.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = lame.framework; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | E76CBD361F16FC48006993AA /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | E7CE68731F18683100A5142A /* lame.framework in Frameworks */, 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | E76CBD4F1F16FC48006993AA /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | E76CBD5A1F16FC48006993AA /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | E76CBD301F16FC48006993AA = { 91 | isa = PBXGroup; 92 | children = ( 93 | E76CBD3B1F16FC48006993AA /* RecorderDemo */, 94 | E76CBD551F16FC48006993AA /* RecorderDemoTests */, 95 | E76CBD601F16FC48006993AA /* RecorderDemoUITests */, 96 | E76CBD3A1F16FC48006993AA /* Products */, 97 | ); 98 | sourceTree = ""; 99 | }; 100 | E76CBD3A1F16FC48006993AA /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | E76CBD391F16FC48006993AA /* RecorderDemo.app */, 104 | E76CBD521F16FC48006993AA /* RecorderDemoTests.xctest */, 105 | E76CBD5D1F16FC48006993AA /* RecorderDemoUITests.xctest */, 106 | ); 107 | name = Products; 108 | sourceTree = ""; 109 | }; 110 | E76CBD3B1F16FC48006993AA /* RecorderDemo */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | E7CE68631F1751B800A5142A /* PlayerManager */, 114 | E7CE685B1F17518A00A5142A /* ConvertAudio */, 115 | E76CBD3F1F16FC48006993AA /* AppDelegate.h */, 116 | E76CBD401F16FC48006993AA /* AppDelegate.m */, 117 | E76CBD421F16FC48006993AA /* ViewController.h */, 118 | E76CBD431F16FC48006993AA /* ViewController.m */, 119 | E76CBD451F16FC48006993AA /* Main.storyboard */, 120 | E76CBD481F16FC48006993AA /* Assets.xcassets */, 121 | E76CBD4A1F16FC48006993AA /* LaunchScreen.storyboard */, 122 | E76CBD4D1F16FC48006993AA /* Info.plist */, 123 | E76CBD3C1F16FC48006993AA /* Supporting Files */, 124 | ); 125 | path = RecorderDemo; 126 | sourceTree = ""; 127 | }; 128 | E76CBD3C1F16FC48006993AA /* Supporting Files */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | E76CBD3D1F16FC48006993AA /* main.m */, 132 | ); 133 | name = "Supporting Files"; 134 | sourceTree = ""; 135 | }; 136 | E76CBD551F16FC48006993AA /* RecorderDemoTests */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | E76CBD561F16FC48006993AA /* RecorderDemoTests.m */, 140 | E76CBD581F16FC48006993AA /* Info.plist */, 141 | ); 142 | path = RecorderDemoTests; 143 | sourceTree = ""; 144 | }; 145 | E76CBD601F16FC48006993AA /* RecorderDemoUITests */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | E76CBD611F16FC48006993AA /* RecorderDemoUITests.m */, 149 | E76CBD631F16FC48006993AA /* Info.plist */, 150 | ); 151 | path = RecorderDemoUITests; 152 | sourceTree = ""; 153 | }; 154 | E7CE685B1F17518A00A5142A /* ConvertAudio */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | E7CE685C1F17518A00A5142A /* ConvertAudioFile.h */, 158 | E7CE685D1F17518A00A5142A /* ConvertAudioFile.m */, 159 | E7CE685E1F17518A00A5142A /* Lame */, 160 | ); 161 | path = ConvertAudio; 162 | sourceTree = ""; 163 | }; 164 | E7CE685E1F17518A00A5142A /* Lame */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | E7CE68721F18683100A5142A /* lame.framework */, 168 | ); 169 | path = Lame; 170 | sourceTree = ""; 171 | }; 172 | E7CE68631F1751B800A5142A /* PlayerManager */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | E7CE68641F1751B800A5142A /* PlayerManager.h */, 176 | E7CE68651F1751B800A5142A /* PlayerManager.m */, 177 | ); 178 | path = PlayerManager; 179 | sourceTree = ""; 180 | }; 181 | /* End PBXGroup section */ 182 | 183 | /* Begin PBXNativeTarget section */ 184 | E76CBD381F16FC48006993AA /* RecorderDemo */ = { 185 | isa = PBXNativeTarget; 186 | buildConfigurationList = E76CBD661F16FC48006993AA /* Build configuration list for PBXNativeTarget "RecorderDemo" */; 187 | buildPhases = ( 188 | E76CBD351F16FC48006993AA /* Sources */, 189 | E76CBD361F16FC48006993AA /* Frameworks */, 190 | E76CBD371F16FC48006993AA /* Resources */, 191 | ); 192 | buildRules = ( 193 | ); 194 | dependencies = ( 195 | ); 196 | name = RecorderDemo; 197 | productName = RecorderDemo; 198 | productReference = E76CBD391F16FC48006993AA /* RecorderDemo.app */; 199 | productType = "com.apple.product-type.application"; 200 | }; 201 | E76CBD511F16FC48006993AA /* RecorderDemoTests */ = { 202 | isa = PBXNativeTarget; 203 | buildConfigurationList = E76CBD691F16FC48006993AA /* Build configuration list for PBXNativeTarget "RecorderDemoTests" */; 204 | buildPhases = ( 205 | E76CBD4E1F16FC48006993AA /* Sources */, 206 | E76CBD4F1F16FC48006993AA /* Frameworks */, 207 | E76CBD501F16FC48006993AA /* Resources */, 208 | ); 209 | buildRules = ( 210 | ); 211 | dependencies = ( 212 | E76CBD541F16FC48006993AA /* PBXTargetDependency */, 213 | ); 214 | name = RecorderDemoTests; 215 | productName = RecorderDemoTests; 216 | productReference = E76CBD521F16FC48006993AA /* RecorderDemoTests.xctest */; 217 | productType = "com.apple.product-type.bundle.unit-test"; 218 | }; 219 | E76CBD5C1F16FC48006993AA /* RecorderDemoUITests */ = { 220 | isa = PBXNativeTarget; 221 | buildConfigurationList = E76CBD6C1F16FC48006993AA /* Build configuration list for PBXNativeTarget "RecorderDemoUITests" */; 222 | buildPhases = ( 223 | E76CBD591F16FC48006993AA /* Sources */, 224 | E76CBD5A1F16FC48006993AA /* Frameworks */, 225 | E76CBD5B1F16FC48006993AA /* Resources */, 226 | ); 227 | buildRules = ( 228 | ); 229 | dependencies = ( 230 | E76CBD5F1F16FC48006993AA /* PBXTargetDependency */, 231 | ); 232 | name = RecorderDemoUITests; 233 | productName = RecorderDemoUITests; 234 | productReference = E76CBD5D1F16FC48006993AA /* RecorderDemoUITests.xctest */; 235 | productType = "com.apple.product-type.bundle.ui-testing"; 236 | }; 237 | /* End PBXNativeTarget section */ 238 | 239 | /* Begin PBXProject section */ 240 | E76CBD311F16FC48006993AA /* Project object */ = { 241 | isa = PBXProject; 242 | attributes = { 243 | LastUpgradeCheck = 0830; 244 | ORGANIZATIONNAME = xuxiwen; 245 | TargetAttributes = { 246 | E76CBD381F16FC48006993AA = { 247 | CreatedOnToolsVersion = 8.3.3; 248 | DevelopmentTeam = B78UJ3KK2S; 249 | ProvisioningStyle = Automatic; 250 | }; 251 | E76CBD511F16FC48006993AA = { 252 | CreatedOnToolsVersion = 8.3.3; 253 | DevelopmentTeam = U82TZNQ998; 254 | ProvisioningStyle = Automatic; 255 | TestTargetID = E76CBD381F16FC48006993AA; 256 | }; 257 | E76CBD5C1F16FC48006993AA = { 258 | CreatedOnToolsVersion = 8.3.3; 259 | DevelopmentTeam = U82TZNQ998; 260 | ProvisioningStyle = Automatic; 261 | TestTargetID = E76CBD381F16FC48006993AA; 262 | }; 263 | }; 264 | }; 265 | buildConfigurationList = E76CBD341F16FC48006993AA /* Build configuration list for PBXProject "RecorderDemo" */; 266 | compatibilityVersion = "Xcode 3.2"; 267 | developmentRegion = English; 268 | hasScannedForEncodings = 0; 269 | knownRegions = ( 270 | en, 271 | Base, 272 | ); 273 | mainGroup = E76CBD301F16FC48006993AA; 274 | productRefGroup = E76CBD3A1F16FC48006993AA /* Products */; 275 | projectDirPath = ""; 276 | projectRoot = ""; 277 | targets = ( 278 | E76CBD381F16FC48006993AA /* RecorderDemo */, 279 | E76CBD511F16FC48006993AA /* RecorderDemoTests */, 280 | E76CBD5C1F16FC48006993AA /* RecorderDemoUITests */, 281 | ); 282 | }; 283 | /* End PBXProject section */ 284 | 285 | /* Begin PBXResourcesBuildPhase section */ 286 | E76CBD371F16FC48006993AA /* Resources */ = { 287 | isa = PBXResourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | E76CBD4C1F16FC48006993AA /* LaunchScreen.storyboard in Resources */, 291 | E76CBD491F16FC48006993AA /* Assets.xcassets in Resources */, 292 | E76CBD471F16FC48006993AA /* Main.storyboard in Resources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | E76CBD501F16FC48006993AA /* Resources */ = { 297 | isa = PBXResourcesBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | }; 303 | E76CBD5B1F16FC48006993AA /* Resources */ = { 304 | isa = PBXResourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | ); 308 | runOnlyForDeploymentPostprocessing = 0; 309 | }; 310 | /* End PBXResourcesBuildPhase section */ 311 | 312 | /* Begin PBXSourcesBuildPhase section */ 313 | E76CBD351F16FC48006993AA /* Sources */ = { 314 | isa = PBXSourcesBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | E7CE68661F1751B800A5142A /* PlayerManager.m in Sources */, 318 | E7CE68611F17518A00A5142A /* ConvertAudioFile.m in Sources */, 319 | E76CBD441F16FC48006993AA /* ViewController.m in Sources */, 320 | E76CBD411F16FC48006993AA /* AppDelegate.m in Sources */, 321 | E76CBD3E1F16FC48006993AA /* main.m in Sources */, 322 | ); 323 | runOnlyForDeploymentPostprocessing = 0; 324 | }; 325 | E76CBD4E1F16FC48006993AA /* Sources */ = { 326 | isa = PBXSourcesBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | E76CBD571F16FC48006993AA /* RecorderDemoTests.m in Sources */, 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | }; 333 | E76CBD591F16FC48006993AA /* Sources */ = { 334 | isa = PBXSourcesBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | E76CBD621F16FC48006993AA /* RecorderDemoUITests.m in Sources */, 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | }; 341 | /* End PBXSourcesBuildPhase section */ 342 | 343 | /* Begin PBXTargetDependency section */ 344 | E76CBD541F16FC48006993AA /* PBXTargetDependency */ = { 345 | isa = PBXTargetDependency; 346 | target = E76CBD381F16FC48006993AA /* RecorderDemo */; 347 | targetProxy = E76CBD531F16FC48006993AA /* PBXContainerItemProxy */; 348 | }; 349 | E76CBD5F1F16FC48006993AA /* PBXTargetDependency */ = { 350 | isa = PBXTargetDependency; 351 | target = E76CBD381F16FC48006993AA /* RecorderDemo */; 352 | targetProxy = E76CBD5E1F16FC48006993AA /* PBXContainerItemProxy */; 353 | }; 354 | /* End PBXTargetDependency section */ 355 | 356 | /* Begin PBXVariantGroup section */ 357 | E76CBD451F16FC48006993AA /* Main.storyboard */ = { 358 | isa = PBXVariantGroup; 359 | children = ( 360 | E76CBD461F16FC48006993AA /* Base */, 361 | ); 362 | name = Main.storyboard; 363 | sourceTree = ""; 364 | }; 365 | E76CBD4A1F16FC48006993AA /* LaunchScreen.storyboard */ = { 366 | isa = PBXVariantGroup; 367 | children = ( 368 | E76CBD4B1F16FC48006993AA /* Base */, 369 | ); 370 | name = LaunchScreen.storyboard; 371 | sourceTree = ""; 372 | }; 373 | /* End PBXVariantGroup section */ 374 | 375 | /* Begin XCBuildConfiguration section */ 376 | E76CBD641F16FC48006993AA /* Debug */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | ALWAYS_SEARCH_USER_PATHS = NO; 380 | CLANG_ANALYZER_NONNULL = YES; 381 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 382 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 383 | CLANG_CXX_LIBRARY = "libc++"; 384 | CLANG_ENABLE_MODULES = YES; 385 | CLANG_ENABLE_OBJC_ARC = YES; 386 | CLANG_WARN_BOOL_CONVERSION = YES; 387 | CLANG_WARN_CONSTANT_CONVERSION = YES; 388 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 389 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 390 | CLANG_WARN_EMPTY_BODY = YES; 391 | CLANG_WARN_ENUM_CONVERSION = YES; 392 | CLANG_WARN_INFINITE_RECURSION = YES; 393 | CLANG_WARN_INT_CONVERSION = YES; 394 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 395 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 396 | CLANG_WARN_UNREACHABLE_CODE = YES; 397 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 398 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 399 | COPY_PHASE_STRIP = NO; 400 | DEBUG_INFORMATION_FORMAT = dwarf; 401 | ENABLE_STRICT_OBJC_MSGSEND = YES; 402 | ENABLE_TESTABILITY = YES; 403 | GCC_C_LANGUAGE_STANDARD = gnu99; 404 | GCC_DYNAMIC_NO_PIC = NO; 405 | GCC_NO_COMMON_BLOCKS = YES; 406 | GCC_OPTIMIZATION_LEVEL = 0; 407 | GCC_PREPROCESSOR_DEFINITIONS = ( 408 | "DEBUG=1", 409 | "$(inherited)", 410 | ); 411 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 412 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 413 | GCC_WARN_UNDECLARED_SELECTOR = YES; 414 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 415 | GCC_WARN_UNUSED_FUNCTION = YES; 416 | GCC_WARN_UNUSED_VARIABLE = YES; 417 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 418 | MTL_ENABLE_DEBUG_INFO = YES; 419 | ONLY_ACTIVE_ARCH = YES; 420 | SDKROOT = iphoneos; 421 | TARGETED_DEVICE_FAMILY = "1,2"; 422 | }; 423 | name = Debug; 424 | }; 425 | E76CBD651F16FC48006993AA /* Release */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | ALWAYS_SEARCH_USER_PATHS = NO; 429 | CLANG_ANALYZER_NONNULL = YES; 430 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 431 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 432 | CLANG_CXX_LIBRARY = "libc++"; 433 | CLANG_ENABLE_MODULES = YES; 434 | CLANG_ENABLE_OBJC_ARC = YES; 435 | CLANG_WARN_BOOL_CONVERSION = YES; 436 | CLANG_WARN_CONSTANT_CONVERSION = YES; 437 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 438 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 439 | CLANG_WARN_EMPTY_BODY = YES; 440 | CLANG_WARN_ENUM_CONVERSION = YES; 441 | CLANG_WARN_INFINITE_RECURSION = YES; 442 | CLANG_WARN_INT_CONVERSION = YES; 443 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 444 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 445 | CLANG_WARN_UNREACHABLE_CODE = YES; 446 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 447 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 448 | COPY_PHASE_STRIP = NO; 449 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 450 | ENABLE_NS_ASSERTIONS = NO; 451 | ENABLE_STRICT_OBJC_MSGSEND = YES; 452 | GCC_C_LANGUAGE_STANDARD = gnu99; 453 | GCC_NO_COMMON_BLOCKS = YES; 454 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 455 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 456 | GCC_WARN_UNDECLARED_SELECTOR = YES; 457 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 458 | GCC_WARN_UNUSED_FUNCTION = YES; 459 | GCC_WARN_UNUSED_VARIABLE = YES; 460 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 461 | MTL_ENABLE_DEBUG_INFO = NO; 462 | SDKROOT = iphoneos; 463 | TARGETED_DEVICE_FAMILY = "1,2"; 464 | VALIDATE_PRODUCT = YES; 465 | }; 466 | name = Release; 467 | }; 468 | E76CBD671F16FC48006993AA /* Debug */ = { 469 | isa = XCBuildConfiguration; 470 | buildSettings = { 471 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 472 | DEVELOPMENT_TEAM = B78UJ3KK2S; 473 | FRAMEWORK_SEARCH_PATHS = ( 474 | "$(inherited)", 475 | "$(PROJECT_DIR)/RecorderDemo/ConvertAudio/Lame", 476 | ); 477 | INFOPLIST_FILE = RecorderDemo/Info.plist; 478 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 479 | LIBRARY_SEARCH_PATHS = ( 480 | "$(inherited)", 481 | "$(PROJECT_DIR)/RecorderDemo/ConvertAudio/Lame", 482 | ); 483 | PRODUCT_BUNDLE_IDENTIFIER = com.developer.xuxiwen.RecorderDemo77; 484 | PRODUCT_NAME = "$(TARGET_NAME)"; 485 | }; 486 | name = Debug; 487 | }; 488 | E76CBD681F16FC48006993AA /* Release */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 492 | DEVELOPMENT_TEAM = B78UJ3KK2S; 493 | FRAMEWORK_SEARCH_PATHS = ( 494 | "$(inherited)", 495 | "$(PROJECT_DIR)/RecorderDemo/ConvertAudio/Lame", 496 | ); 497 | INFOPLIST_FILE = RecorderDemo/Info.plist; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 499 | LIBRARY_SEARCH_PATHS = ( 500 | "$(inherited)", 501 | "$(PROJECT_DIR)/RecorderDemo/ConvertAudio/Lame", 502 | ); 503 | PRODUCT_BUNDLE_IDENTIFIER = com.developer.xuxiwen.RecorderDemo77; 504 | PRODUCT_NAME = "$(TARGET_NAME)"; 505 | }; 506 | name = Release; 507 | }; 508 | E76CBD6A1F16FC48006993AA /* Debug */ = { 509 | isa = XCBuildConfiguration; 510 | buildSettings = { 511 | BUNDLE_LOADER = "$(TEST_HOST)"; 512 | DEVELOPMENT_TEAM = U82TZNQ998; 513 | INFOPLIST_FILE = RecorderDemoTests/Info.plist; 514 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 515 | PRODUCT_BUNDLE_IDENTIFIER = com.developer.xuxiwen.RecorderDemoTests; 516 | PRODUCT_NAME = "$(TARGET_NAME)"; 517 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RecorderDemo.app/RecorderDemo"; 518 | }; 519 | name = Debug; 520 | }; 521 | E76CBD6B1F16FC48006993AA /* Release */ = { 522 | isa = XCBuildConfiguration; 523 | buildSettings = { 524 | BUNDLE_LOADER = "$(TEST_HOST)"; 525 | DEVELOPMENT_TEAM = U82TZNQ998; 526 | INFOPLIST_FILE = RecorderDemoTests/Info.plist; 527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 528 | PRODUCT_BUNDLE_IDENTIFIER = com.developer.xuxiwen.RecorderDemoTests; 529 | PRODUCT_NAME = "$(TARGET_NAME)"; 530 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RecorderDemo.app/RecorderDemo"; 531 | }; 532 | name = Release; 533 | }; 534 | E76CBD6D1F16FC48006993AA /* Debug */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | DEVELOPMENT_TEAM = U82TZNQ998; 538 | INFOPLIST_FILE = RecorderDemoUITests/Info.plist; 539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 540 | PRODUCT_BUNDLE_IDENTIFIER = com.developer.xuxiwen.RecorderDemoUITests; 541 | PRODUCT_NAME = "$(TARGET_NAME)"; 542 | TEST_TARGET_NAME = RecorderDemo; 543 | }; 544 | name = Debug; 545 | }; 546 | E76CBD6E1F16FC48006993AA /* Release */ = { 547 | isa = XCBuildConfiguration; 548 | buildSettings = { 549 | DEVELOPMENT_TEAM = U82TZNQ998; 550 | INFOPLIST_FILE = RecorderDemoUITests/Info.plist; 551 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 552 | PRODUCT_BUNDLE_IDENTIFIER = com.developer.xuxiwen.RecorderDemoUITests; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | TEST_TARGET_NAME = RecorderDemo; 555 | }; 556 | name = Release; 557 | }; 558 | /* End XCBuildConfiguration section */ 559 | 560 | /* Begin XCConfigurationList section */ 561 | E76CBD341F16FC48006993AA /* Build configuration list for PBXProject "RecorderDemo" */ = { 562 | isa = XCConfigurationList; 563 | buildConfigurations = ( 564 | E76CBD641F16FC48006993AA /* Debug */, 565 | E76CBD651F16FC48006993AA /* Release */, 566 | ); 567 | defaultConfigurationIsVisible = 0; 568 | defaultConfigurationName = Release; 569 | }; 570 | E76CBD661F16FC48006993AA /* Build configuration list for PBXNativeTarget "RecorderDemo" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | E76CBD671F16FC48006993AA /* Debug */, 574 | E76CBD681F16FC48006993AA /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | E76CBD691F16FC48006993AA /* Build configuration list for PBXNativeTarget "RecorderDemoTests" */ = { 580 | isa = XCConfigurationList; 581 | buildConfigurations = ( 582 | E76CBD6A1F16FC48006993AA /* Debug */, 583 | E76CBD6B1F16FC48006993AA /* Release */, 584 | ); 585 | defaultConfigurationIsVisible = 0; 586 | defaultConfigurationName = Release; 587 | }; 588 | E76CBD6C1F16FC48006993AA /* Build configuration list for PBXNativeTarget "RecorderDemoUITests" */ = { 589 | isa = XCConfigurationList; 590 | buildConfigurations = ( 591 | E76CBD6D1F16FC48006993AA /* Debug */, 592 | E76CBD6E1F16FC48006993AA /* Release */, 593 | ); 594 | defaultConfigurationIsVisible = 0; 595 | defaultConfigurationName = Release; 596 | }; 597 | /* End XCConfigurationList section */ 598 | }; 599 | rootObject = E76CBD311F16FC48006993AA /* Project object */; 600 | } 601 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo.xcodeproj/project.xcworkspace/xcuserdata/xuxiwen.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CivelXu/iOS-Lame-Audio-transcoding/62bce5dbf0966246d2fa4ed832b20050c0e1fb4b/RecorderDemo/RecorderDemo.xcodeproj/project.xcworkspace/xcuserdata/xuxiwen.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo.xcodeproj/xcuserdata/xuxiwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo.xcodeproj/xcuserdata/xuxiwen.xcuserdatad/xcschemes/RecorderDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo.xcodeproj/xcuserdata/xuxiwen.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RecorderDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | E76CBD381F16FC48006993AA 16 | 17 | primary 18 | 19 | 20 | E76CBD511F16FC48006993AA 21 | 22 | primary 23 | 24 | 25 | E76CBD5C1F16FC48006993AA 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // RecorderDemo 4 | // 5 | // Created by xuxiwen on 2017/7/13. 6 | // Copyright © 2017年 xuxiwen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // RecorderDemo 4 | // 5 | // Created by xuxiwen on 2017/7/13. 6 | // Copyright © 2017年 xuxiwen. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 | 43 | 56 | 69 | 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 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/ConvertAudio/ConvertAudioFile.h: -------------------------------------------------------------------------------- 1 | // 2 | // ConvertAudioFile.h 3 | // Expert 4 | // 5 | // Created by xuxiwen on 2017/3/21. 6 | // Copyright © 2017年 xuxiwen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ConvertAudioFile : NSObject 12 | 13 | 14 | /** 15 | get instance obj 16 | 17 | @return ConvertAudioFile instance 18 | */ 19 | + (instancetype)sharedInstance; 20 | 21 | /** 22 | ConvertMp3 23 | 24 | @param cafFilePath caf FilePath 25 | @param mp3FilePath mp3 FilePath 26 | @param sampleRate sampleRate (same record sampleRate set) 27 | @param callback callback result 28 | */ 29 | - (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath 30 | mp3FilePath:(NSString *)mp3FilePath 31 | sampleRate:(int)sampleRate 32 | callback:(void(^)(BOOL result))callback; 33 | 34 | /** 35 | send end record signal 36 | */ 37 | - (void)sendEndRecord; 38 | 39 | 40 | 41 | // Use this FUNC convent to mp3 after record 42 | + (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath 43 | mp3FilePath:(NSString *)mp3FilePath 44 | sampleRate:(int)sampleRate 45 | callback:(void(^)(BOOL result))callback; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/ConvertAudio/ConvertAudioFile.m: -------------------------------------------------------------------------------- 1 | // 2 | // ConvertAudioFile.m 3 | // Expert 4 | // 5 | // Created by xuxiwen on 2017/3/21. 6 | // Copyright © 2017年 xuxiwen. All rights reserved. 7 | // 8 | 9 | #import "ConvertAudioFile.h" 10 | #import 11 | 12 | @interface ConvertAudioFile () 13 | @property (nonatomic, assign) BOOL stopRecord; 14 | @property (nonatomic, assign) BOOL isSleep; 15 | @end 16 | 17 | @implementation ConvertAudioFile 18 | 19 | /** 20 | get instance obj 21 | 22 | @return ConvertAudioFile instance 23 | */ 24 | + (instancetype)sharedInstance { 25 | static ConvertAudioFile *instance = nil; 26 | static dispatch_once_t onceToken; 27 | dispatch_once(&onceToken, ^{ 28 | instance = [[ConvertAudioFile alloc] init]; 29 | }); 30 | return instance; 31 | } 32 | 33 | /** 34 | ConvertMp3 35 | 36 | @param cafFilePath caf FilePath 37 | @param mp3FilePath mp3 FilePath 38 | @param sampleRate sampleRate (same record sampleRate set) 39 | @param callback callback result 40 | */ 41 | - (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath 42 | mp3FilePath:(NSString *)mp3FilePath 43 | sampleRate:(int)sampleRate 44 | callback:(void(^)(BOOL result))callback 45 | { 46 | 47 | NSLog(@"convert begin!!"); 48 | __weak typeof(self) weakself = self; 49 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 50 | 51 | weakself.stopRecord = NO; 52 | 53 | @try { 54 | 55 | int read, write; 56 | 57 | FILE *pcm = fopen([cafFilePath cStringUsingEncoding:NSASCIIStringEncoding], "rb"); 58 | FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:NSASCIIStringEncoding], "wb+"); 59 | 60 | const int PCM_SIZE = 8192; 61 | const int MP3_SIZE = 8192; 62 | short int pcm_buffer[PCM_SIZE * 2]; 63 | unsigned char mp3_buffer[MP3_SIZE]; 64 | 65 | lame_t lame = lame_init(); 66 | lame_set_in_samplerate(lame, sampleRate); 67 | lame_set_VBR(lame, vbr_default); 68 | lame_init_params(lame); 69 | 70 | long curpos; 71 | BOOL isSkipPCMHeader = NO; 72 | 73 | do { 74 | curpos = ftell(pcm); 75 | long startPos = ftell(pcm); 76 | fseek(pcm, 0, SEEK_END); 77 | long endPos = ftell(pcm); 78 | long length = endPos - startPos; 79 | fseek(pcm, curpos, SEEK_SET); 80 | 81 | if (length > PCM_SIZE * 2 * sizeof(short int)) { 82 | 83 | if (!isSkipPCMHeader) { 84 | //Uump audio file header, If you do not skip file header 85 | //you will heard some noise at the beginning!!! 86 | fseek(pcm, 4 * 1024, SEEK_CUR); 87 | isSkipPCMHeader = YES; 88 | NSLog(@"skip pcm file header !!!!!!!!!!"); 89 | } 90 | 91 | read = (int)fread(pcm_buffer, 2 * sizeof(short int), PCM_SIZE, pcm); 92 | write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); 93 | fwrite(mp3_buffer, write, 1, mp3); 94 | NSLog(@"read %d bytes", write); 95 | weakself.isSleep = NO; 96 | } else { 97 | weakself.isSleep = YES; 98 | NSLog(@"sleep"); 99 | } 100 | 101 | } while (!weakself.stopRecord || !weakself.isSleep); 102 | 103 | read = (int)fread(pcm_buffer, 2 * sizeof(short int), PCM_SIZE, pcm); 104 | write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE); 105 | 106 | NSLog(@"read %d bytes and flush to mp3 file", write); 107 | lame_mp3_tags_fid(lame, mp3); 108 | 109 | lame_close(lame); 110 | fclose(mp3); 111 | fclose(pcm); 112 | } 113 | @catch (NSException *exception) { 114 | NSLog(@"%@", [exception description]); 115 | if (callback) { 116 | callback(NO); 117 | } 118 | } 119 | @finally { 120 | if (callback) { 121 | callback(YES); 122 | } 123 | NSLog(@"convert mp3 finish!!! %@", mp3FilePath); 124 | } 125 | }); 126 | 127 | } 128 | 129 | /** 130 | send end record signal 131 | */ 132 | - (void)sendEndRecord { 133 | self.stopRecord = YES; 134 | } 135 | 136 | 137 | 138 | #pragma mark - ---------------------------------- 139 | 140 | // 这是录完再转码的方法, 如果录音时间比较长的话,会要等待几秒... 141 | // Use this FUNC convent to mp3 after record 142 | 143 | + (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath 144 | mp3FilePath:(NSString *)mp3FilePath 145 | sampleRate:(int)sampleRate 146 | callback:(void(^)(BOOL result))callback 147 | { 148 | 149 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 150 | 151 | @try { 152 | int read, write; 153 | 154 | FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb"); //source 被转换的音频文件位置 155 | fseek(pcm, 4*1024, SEEK_CUR); //skip file header 156 | FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb+"); //output 输出生成的Mp3文件位置 157 | 158 | const int PCM_SIZE = 8192; 159 | const int MP3_SIZE = 8192; 160 | short int pcm_buffer[PCM_SIZE*2]; 161 | unsigned char mp3_buffer[MP3_SIZE]; 162 | 163 | lame_t lame = lame_init(); 164 | lame_set_num_channels(lame,1);//设置1为单通道,默认为2双通道 165 | lame_set_in_samplerate(lame, sampleRate); 166 | lame_set_VBR(lame, vbr_default); 167 | lame_init_params(lame); 168 | 169 | do { 170 | 171 | read = (int)fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm); 172 | if (read == 0) { 173 | write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE); 174 | 175 | } else { 176 | write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); 177 | } 178 | 179 | fwrite(mp3_buffer, write, 1, mp3); 180 | 181 | } while (read != 0); 182 | 183 | lame_mp3_tags_fid(lame, mp3); 184 | 185 | lame_close(lame); 186 | fclose(mp3); 187 | fclose(pcm); 188 | } 189 | @catch (NSException *exception) { 190 | NSLog(@"%@",[exception description]); 191 | if (callback) { 192 | callback(NO); 193 | } 194 | } 195 | @finally { 196 | NSLog(@"-----\n MP3生成成功: %@ ----- \n", mp3FilePath); 197 | if (callback) { 198 | callback(YES); 199 | } 200 | } 201 | }); 202 | } 203 | 204 | @end 205 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/ConvertAudio/Lame/lame.framework/Headers/lame.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface to MP3 LAME encoding engine 3 | * 4 | * Copyright (c) 1999 Mark Taylor 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Library General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Library General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Library General Public 17 | * License along with this library; if not, write to the 18 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 | * Boston, MA 02111-1307, USA. 20 | * https://github.com/wuqiong/mp3lame-for-iOS 21 | */ 22 | 23 | /* $Id: lame.h,v 1.189.2.1 2012/01/08 23:49:58 robert Exp $ */ 24 | 25 | #ifndef LAME_LAME_H 26 | #define LAME_LAME_H 27 | 28 | /* for size_t typedef */ 29 | #include 30 | /* for va_list typedef */ 31 | #include 32 | /* for FILE typedef, TODO: remove when removing lame_mp3_tags_fid */ 33 | #include 34 | 35 | #if defined(__cplusplus) 36 | extern "C" { 37 | #endif 38 | 39 | typedef void (*lame_report_function)(const char *format, va_list ap); 40 | 41 | #if defined(WIN32) || defined(_WIN32) 42 | #undef CDECL 43 | #define CDECL __cdecl 44 | #else 45 | #define CDECL 46 | #endif 47 | 48 | #define DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1 49 | 50 | typedef enum vbr_mode_e { 51 | vbr_off=0, 52 | vbr_mt, /* obsolete, same as vbr_mtrh */ 53 | vbr_rh, 54 | vbr_abr, 55 | vbr_mtrh, 56 | vbr_max_indicator, /* Don't use this! It's used for sanity checks. */ 57 | vbr_default=vbr_mtrh /* change this to change the default VBR mode of LAME */ 58 | } vbr_mode; 59 | 60 | 61 | /* MPEG modes */ 62 | typedef enum MPEG_mode_e { 63 | STEREO = 0, 64 | JOINT_STEREO, 65 | DUAL_CHANNEL, /* LAME doesn't supports this! */ 66 | MONO, 67 | NOT_SET, 68 | MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ 69 | } MPEG_mode; 70 | 71 | /* Padding types */ 72 | typedef enum Padding_type_e { 73 | PAD_NO = 0, 74 | PAD_ALL, 75 | PAD_ADJUST, 76 | PAD_MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ 77 | } Padding_type; 78 | 79 | 80 | 81 | /*presets*/ 82 | typedef enum preset_mode_e { 83 | /*values from 8 to 320 should be reserved for abr bitrates*/ 84 | /*for abr I'd suggest to directly use the targeted bitrate as a value*/ 85 | ABR_8 = 8, 86 | ABR_320 = 320, 87 | 88 | V9 = 410, /*Vx to match Lame and VBR_xx to match FhG*/ 89 | VBR_10 = 410, 90 | V8 = 420, 91 | VBR_20 = 420, 92 | V7 = 430, 93 | VBR_30 = 430, 94 | V6 = 440, 95 | VBR_40 = 440, 96 | V5 = 450, 97 | VBR_50 = 450, 98 | V4 = 460, 99 | VBR_60 = 460, 100 | V3 = 470, 101 | VBR_70 = 470, 102 | V2 = 480, 103 | VBR_80 = 480, 104 | V1 = 490, 105 | VBR_90 = 490, 106 | V0 = 500, 107 | VBR_100 = 500, 108 | 109 | 110 | 111 | /*still there for compatibility*/ 112 | R3MIX = 1000, 113 | STANDARD = 1001, 114 | EXTREME = 1002, 115 | INSANE = 1003, 116 | STANDARD_FAST = 1004, 117 | EXTREME_FAST = 1005, 118 | MEDIUM = 1006, 119 | MEDIUM_FAST = 1007 120 | } preset_mode; 121 | 122 | 123 | /*asm optimizations*/ 124 | typedef enum asm_optimizations_e { 125 | MMX = 1, 126 | AMD_3DNOW = 2, 127 | SSE = 3 128 | } asm_optimizations; 129 | 130 | 131 | /* psychoacoustic model */ 132 | typedef enum Psy_model_e { 133 | PSY_GPSYCHO = 1, 134 | PSY_NSPSYTUNE = 2 135 | } Psy_model; 136 | 137 | 138 | /* buffer considerations */ 139 | typedef enum buffer_constraint_e { 140 | MDB_DEFAULT=0, 141 | MDB_STRICT_ISO=1, 142 | MDB_MAXIMUM=2 143 | } buffer_constraint; 144 | 145 | 146 | struct lame_global_struct; 147 | typedef struct lame_global_struct lame_global_flags; 148 | typedef lame_global_flags *lame_t; 149 | 150 | 151 | 152 | 153 | /*********************************************************************** 154 | * 155 | * The LAME API 156 | * These functions should be called, in this order, for each 157 | * MP3 file to be encoded. See the file "API" for more documentation 158 | * 159 | ***********************************************************************/ 160 | 161 | 162 | /* 163 | * REQUIRED: 164 | * initialize the encoder. sets default for all encoder parameters, 165 | * returns NULL if some malloc()'s failed 166 | * otherwise returns pointer to structure needed for all future 167 | * API calls. 168 | */ 169 | lame_global_flags * CDECL lame_init(void); 170 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 171 | #else 172 | /* obsolete version */ 173 | int CDECL lame_init_old(lame_global_flags *); 174 | #endif 175 | 176 | /* 177 | * OPTIONAL: 178 | * set as needed to override defaults 179 | */ 180 | 181 | /******************************************************************** 182 | * input stream description 183 | ***********************************************************************/ 184 | /* number of samples. default = 2^32-1 */ 185 | int CDECL lame_set_num_samples(lame_global_flags *, unsigned long); 186 | unsigned long CDECL lame_get_num_samples(const lame_global_flags *); 187 | 188 | /* input sample rate in Hz. default = 44100hz */ 189 | int CDECL lame_set_in_samplerate(lame_global_flags *, int); 190 | int CDECL lame_get_in_samplerate(const lame_global_flags *); 191 | 192 | /* number of channels in input stream. default=2 */ 193 | int CDECL lame_set_num_channels(lame_global_flags *, int); 194 | int CDECL lame_get_num_channels(const lame_global_flags *); 195 | 196 | /* 197 | scale the input by this amount before encoding. default=1 198 | (not used by decoding routines) 199 | */ 200 | int CDECL lame_set_scale(lame_global_flags *, float); 201 | float CDECL lame_get_scale(const lame_global_flags *); 202 | 203 | /* 204 | scale the channel 0 (left) input by this amount before encoding. default=1 205 | (not used by decoding routines) 206 | */ 207 | int CDECL lame_set_scale_left(lame_global_flags *, float); 208 | float CDECL lame_get_scale_left(const lame_global_flags *); 209 | 210 | /* 211 | scale the channel 1 (right) input by this amount before encoding. default=1 212 | (not used by decoding routines) 213 | */ 214 | int CDECL lame_set_scale_right(lame_global_flags *, float); 215 | float CDECL lame_get_scale_right(const lame_global_flags *); 216 | 217 | /* 218 | output sample rate in Hz. default = 0, which means LAME picks best value 219 | based on the amount of compression. MPEG only allows: 220 | MPEG1 32, 44.1, 48khz 221 | MPEG2 16, 22.05, 24 222 | MPEG2.5 8, 11.025, 12 223 | (not used by decoding routines) 224 | */ 225 | int CDECL lame_set_out_samplerate(lame_global_flags *, int); 226 | int CDECL lame_get_out_samplerate(const lame_global_flags *); 227 | 228 | 229 | /******************************************************************** 230 | * general control parameters 231 | ***********************************************************************/ 232 | /* 1=cause LAME to collect data for an MP3 frame analyzer. default=0 */ 233 | int CDECL lame_set_analysis(lame_global_flags *, int); 234 | int CDECL lame_get_analysis(const lame_global_flags *); 235 | 236 | /* 237 | 1 = write a Xing VBR header frame. 238 | default = 1 239 | this variable must have been added by a Hungarian notation Windows programmer :-) 240 | */ 241 | int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int); 242 | int CDECL lame_get_bWriteVbrTag(const lame_global_flags *); 243 | 244 | /* 1=decode only. use lame/mpglib to convert mp3/ogg to wav. default=0 */ 245 | int CDECL lame_set_decode_only(lame_global_flags *, int); 246 | int CDECL lame_get_decode_only(const lame_global_flags *); 247 | 248 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 249 | #else 250 | /* 1=encode a Vorbis .ogg file. default=0 */ 251 | /* DEPRECATED */ 252 | int CDECL lame_set_ogg(lame_global_flags *, int); 253 | int CDECL lame_get_ogg(const lame_global_flags *); 254 | #endif 255 | 256 | /* 257 | internal algorithm selection. True quality is determined by the bitrate 258 | but this variable will effect quality by selecting expensive or cheap algorithms. 259 | quality=0..9. 0=best (very slow). 9=worst. 260 | recommended: 2 near-best quality, not too slow 261 | 5 good quality, fast 262 | 7 ok quality, really fast 263 | */ 264 | int CDECL lame_set_quality(lame_global_flags *, int); 265 | int CDECL lame_get_quality(const lame_global_flags *); 266 | 267 | /* 268 | mode = 0,1,2,3 = stereo, jstereo, dual channel (not supported), mono 269 | default: lame picks based on compression ration and input channels 270 | */ 271 | int CDECL lame_set_mode(lame_global_flags *, MPEG_mode); 272 | MPEG_mode CDECL lame_get_mode(const lame_global_flags *); 273 | 274 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 275 | #else 276 | /* 277 | mode_automs. Use a M/S mode with a switching threshold based on 278 | compression ratio 279 | DEPRECATED 280 | */ 281 | int CDECL lame_set_mode_automs(lame_global_flags *, int); 282 | int CDECL lame_get_mode_automs(const lame_global_flags *); 283 | #endif 284 | 285 | /* 286 | force_ms. Force M/S for all frames. For testing only. 287 | default = 0 (disabled) 288 | */ 289 | int CDECL lame_set_force_ms(lame_global_flags *, int); 290 | int CDECL lame_get_force_ms(const lame_global_flags *); 291 | 292 | /* use free_format? default = 0 (disabled) */ 293 | int CDECL lame_set_free_format(lame_global_flags *, int); 294 | int CDECL lame_get_free_format(const lame_global_flags *); 295 | 296 | /* perform ReplayGain analysis? default = 0 (disabled) */ 297 | int CDECL lame_set_findReplayGain(lame_global_flags *, int); 298 | int CDECL lame_get_findReplayGain(const lame_global_flags *); 299 | 300 | /* decode on the fly. Search for the peak sample. If the ReplayGain 301 | * analysis is enabled then perform the analysis on the decoded data 302 | * stream. default = 0 (disabled) 303 | * NOTE: if this option is set the build-in decoder should not be used */ 304 | int CDECL lame_set_decode_on_the_fly(lame_global_flags *, int); 305 | int CDECL lame_get_decode_on_the_fly(const lame_global_flags *); 306 | 307 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 308 | #else 309 | /* DEPRECATED: now does the same as lame_set_findReplayGain() 310 | default = 0 (disabled) */ 311 | int CDECL lame_set_ReplayGain_input(lame_global_flags *, int); 312 | int CDECL lame_get_ReplayGain_input(const lame_global_flags *); 313 | 314 | /* DEPRECATED: now does the same as 315 | lame_set_decode_on_the_fly() && lame_set_findReplayGain() 316 | default = 0 (disabled) */ 317 | int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int); 318 | int CDECL lame_get_ReplayGain_decode(const lame_global_flags *); 319 | 320 | /* DEPRECATED: now does the same as lame_set_decode_on_the_fly() 321 | default = 0 (disabled) */ 322 | int CDECL lame_set_findPeakSample(lame_global_flags *, int); 323 | int CDECL lame_get_findPeakSample(const lame_global_flags *); 324 | #endif 325 | 326 | /* counters for gapless encoding */ 327 | int CDECL lame_set_nogap_total(lame_global_flags*, int); 328 | int CDECL lame_get_nogap_total(const lame_global_flags*); 329 | 330 | int CDECL lame_set_nogap_currentindex(lame_global_flags* , int); 331 | int CDECL lame_get_nogap_currentindex(const lame_global_flags*); 332 | 333 | 334 | /* 335 | * OPTIONAL: 336 | * Set printf like error/debug/message reporting functions. 337 | * The second argument has to be a pointer to a function which looks like 338 | * void my_debugf(const char *format, va_list ap) 339 | * { 340 | * (void) vfprintf(stdout, format, ap); 341 | * } 342 | * If you use NULL as the value of the pointer in the set function, the 343 | * lame buildin function will be used (prints to stderr). 344 | * To quiet any output you have to replace the body of the example function 345 | * with just "return;" and use it in the set function. 346 | */ 347 | int CDECL lame_set_errorf(lame_global_flags *, lame_report_function); 348 | int CDECL lame_set_debugf(lame_global_flags *, lame_report_function); 349 | int CDECL lame_set_msgf (lame_global_flags *, lame_report_function); 350 | 351 | 352 | 353 | /* set one of brate compression ratio. default is compression ratio of 11. */ 354 | int CDECL lame_set_brate(lame_global_flags *, int); 355 | int CDECL lame_get_brate(const lame_global_flags *); 356 | int CDECL lame_set_compression_ratio(lame_global_flags *, float); 357 | float CDECL lame_get_compression_ratio(const lame_global_flags *); 358 | 359 | 360 | int CDECL lame_set_preset( lame_global_flags* gfp, int ); 361 | int CDECL lame_set_asm_optimizations( lame_global_flags* gfp, int, int ); 362 | 363 | 364 | 365 | /******************************************************************** 366 | * frame params 367 | ***********************************************************************/ 368 | /* mark as copyright. default=0 */ 369 | int CDECL lame_set_copyright(lame_global_flags *, int); 370 | int CDECL lame_get_copyright(const lame_global_flags *); 371 | 372 | /* mark as original. default=1 */ 373 | int CDECL lame_set_original(lame_global_flags *, int); 374 | int CDECL lame_get_original(const lame_global_flags *); 375 | 376 | /* error_protection. Use 2 bytes from each frame for CRC checksum. default=0 */ 377 | int CDECL lame_set_error_protection(lame_global_flags *, int); 378 | int CDECL lame_get_error_protection(const lame_global_flags *); 379 | 380 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 381 | #else 382 | /* padding_type. 0=pad no frames 1=pad all frames 2=adjust padding(default) */ 383 | int CDECL lame_set_padding_type(lame_global_flags *, Padding_type); 384 | Padding_type CDECL lame_get_padding_type(const lame_global_flags *); 385 | #endif 386 | 387 | /* MP3 'private extension' bit Meaningless. default=0 */ 388 | int CDECL lame_set_extension(lame_global_flags *, int); 389 | int CDECL lame_get_extension(const lame_global_flags *); 390 | 391 | /* enforce strict ISO compliance. default=0 */ 392 | int CDECL lame_set_strict_ISO(lame_global_flags *, int); 393 | int CDECL lame_get_strict_ISO(const lame_global_flags *); 394 | 395 | 396 | /******************************************************************** 397 | * quantization/noise shaping 398 | ***********************************************************************/ 399 | 400 | /* disable the bit reservoir. For testing only. default=0 */ 401 | int CDECL lame_set_disable_reservoir(lame_global_flags *, int); 402 | int CDECL lame_get_disable_reservoir(const lame_global_flags *); 403 | 404 | /* select a different "best quantization" function. default=0 */ 405 | int CDECL lame_set_quant_comp(lame_global_flags *, int); 406 | int CDECL lame_get_quant_comp(const lame_global_flags *); 407 | int CDECL lame_set_quant_comp_short(lame_global_flags *, int); 408 | int CDECL lame_get_quant_comp_short(const lame_global_flags *); 409 | 410 | int CDECL lame_set_experimentalX(lame_global_flags *, int); /* compatibility*/ 411 | int CDECL lame_get_experimentalX(const lame_global_flags *); 412 | 413 | /* another experimental option. for testing only */ 414 | int CDECL lame_set_experimentalY(lame_global_flags *, int); 415 | int CDECL lame_get_experimentalY(const lame_global_flags *); 416 | 417 | /* another experimental option. for testing only */ 418 | int CDECL lame_set_experimentalZ(lame_global_flags *, int); 419 | int CDECL lame_get_experimentalZ(const lame_global_flags *); 420 | 421 | /* Naoki's psycho acoustic model. default=0 */ 422 | int CDECL lame_set_exp_nspsytune(lame_global_flags *, int); 423 | int CDECL lame_get_exp_nspsytune(const lame_global_flags *); 424 | 425 | void CDECL lame_set_msfix(lame_global_flags *, double); 426 | float CDECL lame_get_msfix(const lame_global_flags *); 427 | 428 | 429 | /******************************************************************** 430 | * VBR control 431 | ***********************************************************************/ 432 | /* Types of VBR. default = vbr_off = CBR */ 433 | int CDECL lame_set_VBR(lame_global_flags *, vbr_mode); 434 | vbr_mode CDECL lame_get_VBR(const lame_global_flags *); 435 | 436 | /* VBR quality level. 0=highest 9=lowest */ 437 | int CDECL lame_set_VBR_q(lame_global_flags *, int); 438 | int CDECL lame_get_VBR_q(const lame_global_flags *); 439 | 440 | /* VBR quality level. 0=highest 9=lowest, Range [0,...,10[ */ 441 | int CDECL lame_set_VBR_quality(lame_global_flags *, float); 442 | float CDECL lame_get_VBR_quality(const lame_global_flags *); 443 | 444 | /* Ignored except for VBR=vbr_abr (ABR mode) */ 445 | int CDECL lame_set_VBR_mean_bitrate_kbps(lame_global_flags *, int); 446 | int CDECL lame_get_VBR_mean_bitrate_kbps(const lame_global_flags *); 447 | 448 | int CDECL lame_set_VBR_min_bitrate_kbps(lame_global_flags *, int); 449 | int CDECL lame_get_VBR_min_bitrate_kbps(const lame_global_flags *); 450 | 451 | int CDECL lame_set_VBR_max_bitrate_kbps(lame_global_flags *, int); 452 | int CDECL lame_get_VBR_max_bitrate_kbps(const lame_global_flags *); 453 | 454 | /* 455 | 1=strictly enforce VBR_min_bitrate. Normally it will be violated for 456 | analog silence 457 | */ 458 | int CDECL lame_set_VBR_hard_min(lame_global_flags *, int); 459 | int CDECL lame_get_VBR_hard_min(const lame_global_flags *); 460 | 461 | /* for preset */ 462 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 463 | #else 464 | int CDECL lame_set_preset_expopts(lame_global_flags *, int); 465 | #endif 466 | 467 | /******************************************************************** 468 | * Filtering control 469 | ***********************************************************************/ 470 | /* freq in Hz to apply lowpass. Default = 0 = lame chooses. -1 = disabled */ 471 | int CDECL lame_set_lowpassfreq(lame_global_flags *, int); 472 | int CDECL lame_get_lowpassfreq(const lame_global_flags *); 473 | /* width of transition band, in Hz. Default = one polyphase filter band */ 474 | int CDECL lame_set_lowpasswidth(lame_global_flags *, int); 475 | int CDECL lame_get_lowpasswidth(const lame_global_flags *); 476 | 477 | /* freq in Hz to apply highpass. Default = 0 = lame chooses. -1 = disabled */ 478 | int CDECL lame_set_highpassfreq(lame_global_flags *, int); 479 | int CDECL lame_get_highpassfreq(const lame_global_flags *); 480 | /* width of transition band, in Hz. Default = one polyphase filter band */ 481 | int CDECL lame_set_highpasswidth(lame_global_flags *, int); 482 | int CDECL lame_get_highpasswidth(const lame_global_flags *); 483 | 484 | 485 | /******************************************************************** 486 | * psycho acoustics and other arguments which you should not change 487 | * unless you know what you are doing 488 | ***********************************************************************/ 489 | 490 | /* only use ATH for masking */ 491 | int CDECL lame_set_ATHonly(lame_global_flags *, int); 492 | int CDECL lame_get_ATHonly(const lame_global_flags *); 493 | 494 | /* only use ATH for short blocks */ 495 | int CDECL lame_set_ATHshort(lame_global_flags *, int); 496 | int CDECL lame_get_ATHshort(const lame_global_flags *); 497 | 498 | /* disable ATH */ 499 | int CDECL lame_set_noATH(lame_global_flags *, int); 500 | int CDECL lame_get_noATH(const lame_global_flags *); 501 | 502 | /* select ATH formula */ 503 | int CDECL lame_set_ATHtype(lame_global_flags *, int); 504 | int CDECL lame_get_ATHtype(const lame_global_flags *); 505 | 506 | /* lower ATH by this many db */ 507 | int CDECL lame_set_ATHlower(lame_global_flags *, float); 508 | float CDECL lame_get_ATHlower(const lame_global_flags *); 509 | 510 | /* select ATH adaptive adjustment type */ 511 | int CDECL lame_set_athaa_type( lame_global_flags *, int); 512 | int CDECL lame_get_athaa_type( const lame_global_flags *); 513 | 514 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 515 | #else 516 | /* select the loudness approximation used by the ATH adaptive auto-leveling */ 517 | int CDECL lame_set_athaa_loudapprox( lame_global_flags *, int); 518 | int CDECL lame_get_athaa_loudapprox( const lame_global_flags *); 519 | #endif 520 | 521 | /* adjust (in dB) the point below which adaptive ATH level adjustment occurs */ 522 | int CDECL lame_set_athaa_sensitivity( lame_global_flags *, float); 523 | float CDECL lame_get_athaa_sensitivity( const lame_global_flags* ); 524 | 525 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 526 | #else 527 | /* OBSOLETE: predictability limit (ISO tonality formula) */ 528 | int CDECL lame_set_cwlimit(lame_global_flags *, int); 529 | int CDECL lame_get_cwlimit(const lame_global_flags *); 530 | #endif 531 | 532 | /* 533 | allow blocktypes to differ between channels? 534 | default: 0 for jstereo, 1 for stereo 535 | */ 536 | int CDECL lame_set_allow_diff_short(lame_global_flags *, int); 537 | int CDECL lame_get_allow_diff_short(const lame_global_flags *); 538 | 539 | /* use temporal masking effect (default = 1) */ 540 | int CDECL lame_set_useTemporal(lame_global_flags *, int); 541 | int CDECL lame_get_useTemporal(const lame_global_flags *); 542 | 543 | /* use temporal masking effect (default = 1) */ 544 | int CDECL lame_set_interChRatio(lame_global_flags *, float); 545 | float CDECL lame_get_interChRatio(const lame_global_flags *); 546 | 547 | /* disable short blocks */ 548 | int CDECL lame_set_no_short_blocks(lame_global_flags *, int); 549 | int CDECL lame_get_no_short_blocks(const lame_global_flags *); 550 | 551 | /* force short blocks */ 552 | int CDECL lame_set_force_short_blocks(lame_global_flags *, int); 553 | int CDECL lame_get_force_short_blocks(const lame_global_flags *); 554 | 555 | /* Input PCM is emphased PCM (for instance from one of the rarely 556 | emphased CDs), it is STRONGLY not recommended to use this, because 557 | psycho does not take it into account, and last but not least many decoders 558 | ignore these bits */ 559 | int CDECL lame_set_emphasis(lame_global_flags *, int); 560 | int CDECL lame_get_emphasis(const lame_global_flags *); 561 | 562 | 563 | 564 | /************************************************************************/ 565 | /* internal variables, cannot be set... */ 566 | /* provided because they may be of use to calling application */ 567 | /************************************************************************/ 568 | /* version 0=MPEG-2 1=MPEG-1 (2=MPEG-2.5) */ 569 | int CDECL lame_get_version(const lame_global_flags *); 570 | 571 | /* encoder delay */ 572 | int CDECL lame_get_encoder_delay(const lame_global_flags *); 573 | 574 | /* 575 | padding appended to the input to make sure decoder can fully decode 576 | all input. Note that this value can only be calculated during the 577 | call to lame_encoder_flush(). Before lame_encoder_flush() has 578 | been called, the value of encoder_padding = 0. 579 | */ 580 | int CDECL lame_get_encoder_padding(const lame_global_flags *); 581 | 582 | /* size of MPEG frame */ 583 | int CDECL lame_get_framesize(const lame_global_flags *); 584 | 585 | /* number of PCM samples buffered, but not yet encoded to mp3 data. */ 586 | int CDECL lame_get_mf_samples_to_encode( const lame_global_flags* gfp ); 587 | 588 | /* 589 | size (bytes) of mp3 data buffered, but not yet encoded. 590 | this is the number of bytes which would be output by a call to 591 | lame_encode_flush_nogap. NOTE: lame_encode_flush() will return 592 | more bytes than this because it will encode the reamining buffered 593 | PCM samples before flushing the mp3 buffers. 594 | */ 595 | int CDECL lame_get_size_mp3buffer( const lame_global_flags* gfp ); 596 | 597 | /* number of frames encoded so far */ 598 | int CDECL lame_get_frameNum(const lame_global_flags *); 599 | 600 | /* 601 | lame's estimate of the total number of frames to be encoded 602 | only valid if calling program set num_samples 603 | */ 604 | int CDECL lame_get_totalframes(const lame_global_flags *); 605 | 606 | /* RadioGain value. Multiplied by 10 and rounded to the nearest. */ 607 | int CDECL lame_get_RadioGain(const lame_global_flags *); 608 | 609 | /* AudiophileGain value. Multipled by 10 and rounded to the nearest. */ 610 | int CDECL lame_get_AudiophileGain(const lame_global_flags *); 611 | 612 | /* the peak sample */ 613 | float CDECL lame_get_PeakSample(const lame_global_flags *); 614 | 615 | /* Gain change required for preventing clipping. The value is correct only if 616 | peak sample searching was enabled. If negative then the waveform 617 | already does not clip. The value is multiplied by 10 and rounded up. */ 618 | int CDECL lame_get_noclipGainChange(const lame_global_flags *); 619 | 620 | /* user-specified scale factor required for preventing clipping. Value is 621 | correct only if peak sample searching was enabled and no user-specified 622 | scaling was performed. If negative then either the waveform already does 623 | not clip or the value cannot be determined */ 624 | float CDECL lame_get_noclipScale(const lame_global_flags *); 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | /* 633 | * REQUIRED: 634 | * sets more internal configuration based on data provided above. 635 | * returns -1 if something failed. 636 | */ 637 | int CDECL lame_init_params(lame_global_flags *); 638 | 639 | 640 | /* 641 | * OPTIONAL: 642 | * get the version number, in a string. of the form: 643 | * "3.63 (beta)" or just "3.63". 644 | */ 645 | const char* CDECL get_lame_version ( void ); 646 | const char* CDECL get_lame_short_version ( void ); 647 | const char* CDECL get_lame_very_short_version ( void ); 648 | const char* CDECL get_psy_version ( void ); 649 | const char* CDECL get_lame_url ( void ); 650 | const char* CDECL get_lame_os_bitness ( void ); 651 | 652 | /* 653 | * OPTIONAL: 654 | * get the version numbers in numerical form. 655 | */ 656 | typedef struct { 657 | /* generic LAME version */ 658 | int major; 659 | int minor; 660 | int alpha; /* 0 if not an alpha version */ 661 | int beta; /* 0 if not a beta version */ 662 | 663 | /* version of the psy model */ 664 | int psy_major; 665 | int psy_minor; 666 | int psy_alpha; /* 0 if not an alpha version */ 667 | int psy_beta; /* 0 if not a beta version */ 668 | 669 | /* compile time features */ 670 | const char *features; /* Don't make assumptions about the contents! */ 671 | } lame_version_t; 672 | void CDECL get_lame_version_numerical(lame_version_t *); 673 | 674 | 675 | /* 676 | * OPTIONAL: 677 | * print internal lame configuration to message handler 678 | */ 679 | void CDECL lame_print_config(const lame_global_flags* gfp); 680 | 681 | void CDECL lame_print_internals( const lame_global_flags *gfp); 682 | 683 | 684 | /* 685 | * input pcm data, output (maybe) mp3 frames. 686 | * This routine handles all buffering, resampling and filtering for you. 687 | * 688 | * return code number of bytes output in mp3buf. Can be 0 689 | * -1: mp3buf was too small 690 | * -2: malloc() problem 691 | * -3: lame_init_params() not called 692 | * -4: psycho acoustic problems 693 | * 694 | * The required mp3buf_size can be computed from num_samples, 695 | * samplerate and encoding rate, but here is a worst case estimate: 696 | * 697 | * mp3buf_size in bytes = 1.25*num_samples + 7200 698 | * 699 | * I think a tighter bound could be: (mt, March 2000) 700 | * MPEG1: 701 | * num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512 702 | * MPEG2: 703 | * num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256 704 | * 705 | * but test first if you use that! 706 | * 707 | * set mp3buf_size = 0 and LAME will not check if mp3buf_size is 708 | * large enough. 709 | * 710 | * NOTE: 711 | * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels 712 | * will be averaged into the L channel before encoding only the L channel 713 | * This will overwrite the data in buffer_l[] and buffer_r[]. 714 | * 715 | */ 716 | int CDECL lame_encode_buffer ( 717 | lame_global_flags* gfp, /* global context handle */ 718 | const short int buffer_l [], /* PCM data for left channel */ 719 | const short int buffer_r [], /* PCM data for right channel */ 720 | const int nsamples, /* number of samples per channel */ 721 | unsigned char* mp3buf, /* pointer to encoded MP3 stream */ 722 | const int mp3buf_size ); /* number of valid octets in this 723 | stream */ 724 | 725 | /* 726 | * as above, but input has L & R channel data interleaved. 727 | * NOTE: 728 | * num_samples = number of samples in the L (or R) 729 | * channel, not the total number of samples in pcm[] 730 | */ 731 | int CDECL lame_encode_buffer_interleaved( 732 | lame_global_flags* gfp, /* global context handlei */ 733 | short int pcm[], /* PCM data for left and right 734 | channel, interleaved */ 735 | int num_samples, /* number of samples per channel, 736 | _not_ number of samples in 737 | pcm[] */ 738 | unsigned char* mp3buf, /* pointer to encoded MP3 stream */ 739 | int mp3buf_size ); /* number of valid octets in this 740 | stream */ 741 | 742 | 743 | /* as lame_encode_buffer, but for 'float's. 744 | * !! NOTE: !! data must still be scaled to be in the same range as 745 | * short int, +/- 32768 746 | */ 747 | int CDECL lame_encode_buffer_float( 748 | lame_global_flags* gfp, /* global context handle */ 749 | const float pcm_l [], /* PCM data for left channel */ 750 | const float pcm_r [], /* PCM data for right channel */ 751 | const int nsamples, /* number of samples per channel */ 752 | unsigned char* mp3buf, /* pointer to encoded MP3 stream */ 753 | const int mp3buf_size ); /* number of valid octets in this 754 | stream */ 755 | 756 | /* as lame_encode_buffer, but for 'float's. 757 | * !! NOTE: !! data must be scaled to +/- 1 full scale 758 | */ 759 | int CDECL lame_encode_buffer_ieee_float( 760 | lame_t gfp, 761 | const float pcm_l [], /* PCM data for left channel */ 762 | const float pcm_r [], /* PCM data for right channel */ 763 | const int nsamples, 764 | unsigned char * mp3buf, 765 | const int mp3buf_size); 766 | int CDECL lame_encode_buffer_interleaved_ieee_float( 767 | lame_t gfp, 768 | const float pcm[], /* PCM data for left and right 769 | channel, interleaved */ 770 | const int nsamples, 771 | unsigned char * mp3buf, 772 | const int mp3buf_size); 773 | 774 | /* as lame_encode_buffer, but for 'double's. 775 | * !! NOTE: !! data must be scaled to +/- 1 full scale 776 | */ 777 | int CDECL lame_encode_buffer_ieee_double( 778 | lame_t gfp, 779 | const double pcm_l [], /* PCM data for left channel */ 780 | const double pcm_r [], /* PCM data for right channel */ 781 | const int nsamples, 782 | unsigned char * mp3buf, 783 | const int mp3buf_size); 784 | int CDECL lame_encode_buffer_interleaved_ieee_double( 785 | lame_t gfp, 786 | const double pcm[], /* PCM data for left and right 787 | channel, interleaved */ 788 | const int nsamples, 789 | unsigned char * mp3buf, 790 | const int mp3buf_size); 791 | 792 | /* as lame_encode_buffer, but for long's 793 | * !! NOTE: !! data must still be scaled to be in the same range as 794 | * short int, +/- 32768 795 | * 796 | * This scaling was a mistake (doesn't allow one to exploit full 797 | * precision of type 'long'. Use lame_encode_buffer_long2() instead. 798 | * 799 | */ 800 | int CDECL lame_encode_buffer_long( 801 | lame_global_flags* gfp, /* global context handle */ 802 | const long buffer_l [], /* PCM data for left channel */ 803 | const long buffer_r [], /* PCM data for right channel */ 804 | const int nsamples, /* number of samples per channel */ 805 | unsigned char* mp3buf, /* pointer to encoded MP3 stream */ 806 | const int mp3buf_size ); /* number of valid octets in this 807 | stream */ 808 | 809 | /* Same as lame_encode_buffer_long(), but with correct scaling. 810 | * !! NOTE: !! data must still be scaled to be in the same range as 811 | * type 'long'. Data should be in the range: +/- 2^(8*size(long)-1) 812 | * 813 | */ 814 | int CDECL lame_encode_buffer_long2( 815 | lame_global_flags* gfp, /* global context handle */ 816 | const long buffer_l [], /* PCM data for left channel */ 817 | const long buffer_r [], /* PCM data for right channel */ 818 | const int nsamples, /* number of samples per channel */ 819 | unsigned char* mp3buf, /* pointer to encoded MP3 stream */ 820 | const int mp3buf_size ); /* number of valid octets in this 821 | stream */ 822 | 823 | /* as lame_encode_buffer, but for int's 824 | * !! NOTE: !! input should be scaled to the maximum range of 'int' 825 | * If int is 4 bytes, then the values should range from 826 | * +/- 2147483648. 827 | * 828 | * This routine does not (and cannot, without loosing precision) use 829 | * the same scaling as the rest of the lame_encode_buffer() routines. 830 | * 831 | */ 832 | int CDECL lame_encode_buffer_int( 833 | lame_global_flags* gfp, /* global context handle */ 834 | const int buffer_l [], /* PCM data for left channel */ 835 | const int buffer_r [], /* PCM data for right channel */ 836 | const int nsamples, /* number of samples per channel */ 837 | unsigned char* mp3buf, /* pointer to encoded MP3 stream */ 838 | const int mp3buf_size ); /* number of valid octets in this 839 | stream */ 840 | 841 | 842 | 843 | 844 | 845 | /* 846 | * REQUIRED: 847 | * lame_encode_flush will flush the intenal PCM buffers, padding with 848 | * 0's to make sure the final frame is complete, and then flush 849 | * the internal MP3 buffers, and thus may return a 850 | * final few mp3 frames. 'mp3buf' should be at least 7200 bytes long 851 | * to hold all possible emitted data. 852 | * 853 | * will also write id3v1 tags (if any) into the bitstream 854 | * 855 | * return code = number of bytes output to mp3buf. Can be 0 856 | */ 857 | int CDECL lame_encode_flush( 858 | lame_global_flags * gfp, /* global context handle */ 859 | unsigned char* mp3buf, /* pointer to encoded MP3 stream */ 860 | int size); /* number of valid octets in this stream */ 861 | 862 | /* 863 | * OPTIONAL: 864 | * lame_encode_flush_nogap will flush the internal mp3 buffers and pad 865 | * the last frame with ancillary data so it is a complete mp3 frame. 866 | * 867 | * 'mp3buf' should be at least 7200 bytes long 868 | * to hold all possible emitted data. 869 | * 870 | * After a call to this routine, the outputed mp3 data is complete, but 871 | * you may continue to encode new PCM samples and write future mp3 data 872 | * to a different file. The two mp3 files will play back with no gaps 873 | * if they are concatenated together. 874 | * 875 | * This routine will NOT write id3v1 tags into the bitstream. 876 | * 877 | * return code = number of bytes output to mp3buf. Can be 0 878 | */ 879 | int CDECL lame_encode_flush_nogap( 880 | lame_global_flags * gfp, /* global context handle */ 881 | unsigned char* mp3buf, /* pointer to encoded MP3 stream */ 882 | int size); /* number of valid octets in this stream */ 883 | 884 | /* 885 | * OPTIONAL: 886 | * Normally, this is called by lame_init_params(). It writes id3v2 and 887 | * Xing headers into the front of the bitstream, and sets frame counters 888 | * and bitrate histogram data to 0. You can also call this after 889 | * lame_encode_flush_nogap(). 890 | */ 891 | int CDECL lame_init_bitstream( 892 | lame_global_flags * gfp); /* global context handle */ 893 | 894 | 895 | 896 | /* 897 | * OPTIONAL: some simple statistics 898 | * a bitrate histogram to visualize the distribution of used frame sizes 899 | * a stereo mode histogram to visualize the distribution of used stereo 900 | * modes, useful in joint-stereo mode only 901 | * 0: LR left-right encoded 902 | * 1: LR-I left-right and intensity encoded (currently not supported) 903 | * 2: MS mid-side encoded 904 | * 3: MS-I mid-side and intensity encoded (currently not supported) 905 | * 906 | * attention: don't call them after lame_encode_finish 907 | * suggested: lame_encode_flush -> lame_*_hist -> lame_close 908 | */ 909 | 910 | void CDECL lame_bitrate_hist( 911 | const lame_global_flags * gfp, 912 | int bitrate_count[14] ); 913 | void CDECL lame_bitrate_kbps( 914 | const lame_global_flags * gfp, 915 | int bitrate_kbps [14] ); 916 | void CDECL lame_stereo_mode_hist( 917 | const lame_global_flags * gfp, 918 | int stereo_mode_count[4] ); 919 | 920 | void CDECL lame_bitrate_stereo_mode_hist ( 921 | const lame_global_flags * gfp, 922 | int bitrate_stmode_count[14][4] ); 923 | 924 | void CDECL lame_block_type_hist ( 925 | const lame_global_flags * gfp, 926 | int btype_count[6] ); 927 | 928 | void CDECL lame_bitrate_block_type_hist ( 929 | const lame_global_flags * gfp, 930 | int bitrate_btype_count[14][6] ); 931 | 932 | #if (DEPRECATED_OR_OBSOLETE_CODE_REMOVED && 0) 933 | #else 934 | /* 935 | * OPTIONAL: 936 | * lame_mp3_tags_fid will rewrite a Xing VBR tag to the mp3 file with file 937 | * pointer fid. These calls perform forward and backwards seeks, so make 938 | * sure fid is a real file. Make sure lame_encode_flush has been called, 939 | * and all mp3 data has been written to the file before calling this 940 | * function. 941 | * NOTE: 942 | * if VBR tags are turned off by the user, or turned off by LAME because 943 | * the output is not a regular file, this call does nothing 944 | * NOTE: 945 | * LAME wants to read from the file to skip an optional ID3v2 tag, so 946 | * make sure you opened the file for writing and reading. 947 | * NOTE: 948 | * You can call lame_get_lametag_frame instead, if you want to insert 949 | * the lametag yourself. 950 | */ 951 | void CDECL lame_mp3_tags_fid(lame_global_flags *, FILE* fid); 952 | #endif 953 | 954 | /* 955 | * OPTIONAL: 956 | * lame_get_lametag_frame copies the final LAME-tag into 'buffer'. 957 | * The function returns the number of bytes copied into buffer, or 958 | * the required buffer size, if the provided buffer is too small. 959 | * Function failed, if the return value is larger than 'size'! 960 | * Make sure lame_encode flush has been called before calling this function. 961 | * NOTE: 962 | * if VBR tags are turned off by the user, or turned off by LAME, 963 | * this call does nothing and returns 0. 964 | * NOTE: 965 | * LAME inserted an empty frame in the beginning of mp3 audio data, 966 | * which you have to replace by the final LAME-tag frame after encoding. 967 | * In case there is no ID3v2 tag, usually this frame will be the very first 968 | * data in your mp3 file. If you put some other leading data into your 969 | * file, you'll have to do some bookkeeping about where to write this buffer. 970 | */ 971 | size_t CDECL lame_get_lametag_frame( 972 | const lame_global_flags *, unsigned char* buffer, size_t size); 973 | 974 | /* 975 | * REQUIRED: 976 | * final call to free all remaining buffers 977 | */ 978 | int CDECL lame_close (lame_global_flags *); 979 | 980 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 981 | #else 982 | /* 983 | * OBSOLETE: 984 | * lame_encode_finish combines lame_encode_flush() and lame_close() in 985 | * one call. However, once this call is made, the statistics routines 986 | * will no longer work because the data will have been cleared, and 987 | * lame_mp3_tags_fid() cannot be called to add data to the VBR header 988 | */ 989 | int CDECL lame_encode_finish( 990 | lame_global_flags* gfp, 991 | unsigned char* mp3buf, 992 | int size ); 993 | #endif 994 | 995 | 996 | 997 | 998 | 999 | 1000 | /********************************************************************* 1001 | * 1002 | * decoding 1003 | * 1004 | * a simple interface to mpglib, part of mpg123, is also included if 1005 | * libmp3lame is compiled with HAVE_MPGLIB 1006 | * 1007 | *********************************************************************/ 1008 | 1009 | struct hip_global_struct; 1010 | typedef struct hip_global_struct hip_global_flags; 1011 | typedef hip_global_flags *hip_t; 1012 | 1013 | 1014 | typedef struct { 1015 | int header_parsed; /* 1 if header was parsed and following data was 1016 | computed */ 1017 | int stereo; /* number of channels */ 1018 | int samplerate; /* sample rate */ 1019 | int bitrate; /* bitrate */ 1020 | int mode; /* mp3 frame type */ 1021 | int mode_ext; /* mp3 frame type */ 1022 | int framesize; /* number of samples per mp3 frame */ 1023 | 1024 | /* this data is only computed if mpglib detects a Xing VBR header */ 1025 | unsigned long nsamp; /* number of samples in mp3 file. */ 1026 | int totalframes; /* total number of frames in mp3 file */ 1027 | 1028 | /* this data is not currently computed by the mpglib routines */ 1029 | int framenum; /* frames decoded counter */ 1030 | } mp3data_struct; 1031 | 1032 | /* required call to initialize decoder */ 1033 | hip_t CDECL hip_decode_init(void); 1034 | 1035 | /* cleanup call to exit decoder */ 1036 | int CDECL hip_decode_exit(hip_t gfp); 1037 | 1038 | /* HIP reporting functions */ 1039 | void CDECL hip_set_errorf(hip_t gfp, lame_report_function f); 1040 | void CDECL hip_set_debugf(hip_t gfp, lame_report_function f); 1041 | void CDECL hip_set_msgf (hip_t gfp, lame_report_function f); 1042 | 1043 | /********************************************************************* 1044 | * input 1 mp3 frame, output (maybe) pcm data. 1045 | * 1046 | * nout = hip_decode(hip, mp3buf,len,pcm_l,pcm_r); 1047 | * 1048 | * input: 1049 | * len : number of bytes of mp3 data in mp3buf 1050 | * mp3buf[len] : mp3 data to be decoded 1051 | * 1052 | * output: 1053 | * nout: -1 : decoding error 1054 | * 0 : need more data before we can complete the decode 1055 | * >0 : returned 'nout' samples worth of data in pcm_l,pcm_r 1056 | * pcm_l[nout] : left channel data 1057 | * pcm_r[nout] : right channel data 1058 | * 1059 | *********************************************************************/ 1060 | int CDECL hip_decode( hip_t gfp 1061 | , unsigned char * mp3buf 1062 | , size_t len 1063 | , short pcm_l[] 1064 | , short pcm_r[] 1065 | ); 1066 | 1067 | /* same as hip_decode, and also returns mp3 header data */ 1068 | int CDECL hip_decode_headers( hip_t gfp 1069 | , unsigned char* mp3buf 1070 | , size_t len 1071 | , short pcm_l[] 1072 | , short pcm_r[] 1073 | , mp3data_struct* mp3data 1074 | ); 1075 | 1076 | /* same as hip_decode, but returns at most one frame */ 1077 | int CDECL hip_decode1( hip_t gfp 1078 | , unsigned char* mp3buf 1079 | , size_t len 1080 | , short pcm_l[] 1081 | , short pcm_r[] 1082 | ); 1083 | 1084 | /* same as hip_decode1, but returns at most one frame and mp3 header data */ 1085 | int CDECL hip_decode1_headers( hip_t gfp 1086 | , unsigned char* mp3buf 1087 | , size_t len 1088 | , short pcm_l[] 1089 | , short pcm_r[] 1090 | , mp3data_struct* mp3data 1091 | ); 1092 | 1093 | /* same as hip_decode1_headers, but also returns enc_delay and enc_padding 1094 | from VBR Info tag, (-1 if no info tag was found) */ 1095 | int CDECL hip_decode1_headersB( hip_t gfp 1096 | , unsigned char* mp3buf 1097 | , size_t len 1098 | , short pcm_l[] 1099 | , short pcm_r[] 1100 | , mp3data_struct* mp3data 1101 | , int *enc_delay 1102 | , int *enc_padding 1103 | ); 1104 | 1105 | 1106 | 1107 | /* OBSOLETE: 1108 | * lame_decode... functions are there to keep old code working 1109 | * but it is strongly recommended to replace calls by hip_decode... 1110 | * function calls, see above. 1111 | */ 1112 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1113 | #else 1114 | int CDECL lame_decode_init(void); 1115 | int CDECL lame_decode( 1116 | unsigned char * mp3buf, 1117 | int len, 1118 | short pcm_l[], 1119 | short pcm_r[] ); 1120 | int CDECL lame_decode_headers( 1121 | unsigned char* mp3buf, 1122 | int len, 1123 | short pcm_l[], 1124 | short pcm_r[], 1125 | mp3data_struct* mp3data ); 1126 | int CDECL lame_decode1( 1127 | unsigned char* mp3buf, 1128 | int len, 1129 | short pcm_l[], 1130 | short pcm_r[] ); 1131 | int CDECL lame_decode1_headers( 1132 | unsigned char* mp3buf, 1133 | int len, 1134 | short pcm_l[], 1135 | short pcm_r[], 1136 | mp3data_struct* mp3data ); 1137 | int CDECL lame_decode1_headersB( 1138 | unsigned char* mp3buf, 1139 | int len, 1140 | short pcm_l[], 1141 | short pcm_r[], 1142 | mp3data_struct* mp3data, 1143 | int *enc_delay, 1144 | int *enc_padding ); 1145 | int CDECL lame_decode_exit(void); 1146 | 1147 | #endif /* obsolete lame_decode API calls */ 1148 | 1149 | 1150 | /********************************************************************* 1151 | * 1152 | * id3tag stuff 1153 | * 1154 | *********************************************************************/ 1155 | 1156 | /* 1157 | * id3tag.h -- Interface to write ID3 version 1 and 2 tags. 1158 | * 1159 | * Copyright (C) 2000 Don Melton. 1160 | * 1161 | * This library is free software; you can redistribute it and/or 1162 | * modify it under the terms of the GNU Library General Public 1163 | * License as published by the Free Software Foundation; either 1164 | * version 2 of the License, or (at your option) any later version. 1165 | * 1166 | * This library is distributed in the hope that it will be useful, 1167 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 1168 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1169 | * Library General Public License for more details. 1170 | * 1171 | * You should have received a copy of the GNU Library General Public 1172 | * License along with this library; if not, write to the Free Software 1173 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 1174 | */ 1175 | 1176 | /* utility to obtain alphabetically sorted list of genre names with numbers */ 1177 | void CDECL id3tag_genre_list( 1178 | void (*handler)(int, const char *, void *), 1179 | void* cookie); 1180 | 1181 | void CDECL id3tag_init (lame_t gfp); 1182 | 1183 | /* force addition of version 2 tag */ 1184 | void CDECL id3tag_add_v2 (lame_t gfp); 1185 | 1186 | /* add only a version 1 tag */ 1187 | void CDECL id3tag_v1_only (lame_t gfp); 1188 | 1189 | /* add only a version 2 tag */ 1190 | void CDECL id3tag_v2_only (lame_t gfp); 1191 | 1192 | /* pad version 1 tag with spaces instead of nulls */ 1193 | void CDECL id3tag_space_v1 (lame_t gfp); 1194 | 1195 | /* pad version 2 tag with extra 128 bytes */ 1196 | void CDECL id3tag_pad_v2 (lame_t gfp); 1197 | 1198 | /* pad version 2 tag with extra n bytes */ 1199 | void CDECL id3tag_set_pad (lame_t gfp, size_t n); 1200 | 1201 | void CDECL id3tag_set_title(lame_t gfp, const char* title); 1202 | void CDECL id3tag_set_artist(lame_t gfp, const char* artist); 1203 | void CDECL id3tag_set_album(lame_t gfp, const char* album); 1204 | void CDECL id3tag_set_year(lame_t gfp, const char* year); 1205 | void CDECL id3tag_set_comment(lame_t gfp, const char* comment); 1206 | 1207 | /* return -1 result if track number is out of ID3v1 range 1208 | and ignored for ID3v1 */ 1209 | int CDECL id3tag_set_track(lame_t gfp, const char* track); 1210 | 1211 | /* return non-zero result if genre name or number is invalid 1212 | result 0: OK 1213 | result -1: genre number out of range 1214 | result -2: no valid ID3v1 genre name, mapped to ID3v1 'Other' 1215 | but taken as-is for ID3v2 genre tag */ 1216 | int CDECL id3tag_set_genre(lame_t gfp, const char* genre); 1217 | 1218 | /* return non-zero result if field name is invalid */ 1219 | int CDECL id3tag_set_fieldvalue(lame_t gfp, const char* fieldvalue); 1220 | 1221 | /* return non-zero result if image type is invalid */ 1222 | int CDECL id3tag_set_albumart(lame_t gfp, const char* image, size_t size); 1223 | 1224 | /* lame_get_id3v1_tag copies ID3v1 tag into buffer. 1225 | * Function returns number of bytes copied into buffer, or number 1226 | * of bytes rquired if buffer 'size' is too small. 1227 | * Function fails, if returned value is larger than 'size'. 1228 | * NOTE: 1229 | * This functions does nothing, if user/LAME disabled ID3v1 tag. 1230 | */ 1231 | size_t CDECL lame_get_id3v1_tag(lame_t gfp, unsigned char* buffer, size_t size); 1232 | 1233 | /* lame_get_id3v2_tag copies ID3v2 tag into buffer. 1234 | * Function returns number of bytes copied into buffer, or number 1235 | * of bytes rquired if buffer 'size' is too small. 1236 | * Function fails, if returned value is larger than 'size'. 1237 | * NOTE: 1238 | * This functions does nothing, if user/LAME disabled ID3v2 tag. 1239 | */ 1240 | size_t CDECL lame_get_id3v2_tag(lame_t gfp, unsigned char* buffer, size_t size); 1241 | 1242 | /* normaly lame_init_param writes ID3v2 tags into the audio stream 1243 | * Call lame_set_write_id3tag_automatic(gfp, 0) before lame_init_param 1244 | * to turn off this behaviour and get ID3v2 tag with above function 1245 | * write it yourself into your file. 1246 | */ 1247 | void CDECL lame_set_write_id3tag_automatic(lame_global_flags * gfp, int); 1248 | int CDECL lame_get_write_id3tag_automatic(lame_global_flags const* gfp); 1249 | 1250 | /* experimental */ 1251 | int CDECL id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text); 1252 | 1253 | /* experimental */ 1254 | int CDECL id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text); 1255 | 1256 | #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1257 | #else 1258 | /* experimental */ 1259 | int CDECL id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text); 1260 | 1261 | /* experimental */ 1262 | int CDECL id3tag_set_comment_ucs2(lame_t gfp, char const *lang, 1263 | unsigned short const *desc, unsigned short const *text); 1264 | 1265 | /* experimental */ 1266 | int CDECL id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue); 1267 | #endif 1268 | 1269 | /* experimental */ 1270 | int CDECL id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue); 1271 | 1272 | /* experimental */ 1273 | int CDECL id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text); 1274 | 1275 | /* experimental */ 1276 | int CDECL id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text); 1277 | 1278 | 1279 | /*********************************************************************** 1280 | * 1281 | * list of valid bitrates [kbps] & sample frequencies [Hz]. 1282 | * first index: 0: MPEG-2 values (sample frequencies 16...24 kHz) 1283 | * 1: MPEG-1 values (sample frequencies 32...48 kHz) 1284 | * 2: MPEG-2.5 values (sample frequencies 8...12 kHz) 1285 | ***********************************************************************/ 1286 | 1287 | extern const int bitrate_table [3][16]; 1288 | extern const int samplerate_table [3][ 4]; 1289 | 1290 | /* access functions for use in DLL, global vars are not exported */ 1291 | int CDECL lame_get_bitrate(int mpeg_version, int table_index); 1292 | int CDECL lame_get_samplerate(int mpeg_version, int table_index); 1293 | 1294 | 1295 | /* maximum size of albumart image (128KB), which affects LAME_MAXMP3BUFFER 1296 | as well since lame_encode_buffer() also returns ID3v2 tag data */ 1297 | #define LAME_MAXALBUMART (128 * 1024) 1298 | 1299 | /* maximum size of mp3buffer needed if you encode at most 1152 samples for 1300 | each call to lame_encode_buffer. see lame_encode_buffer() below 1301 | (LAME_MAXMP3BUFFER is now obsolete) */ 1302 | #define LAME_MAXMP3BUFFER (16384 + LAME_MAXALBUMART) 1303 | 1304 | 1305 | typedef enum { 1306 | LAME_OKAY = 0, 1307 | LAME_NOERROR = 0, 1308 | LAME_GENERICERROR = -1, 1309 | LAME_NOMEM = -10, 1310 | LAME_BADBITRATE = -11, 1311 | LAME_BADSAMPFREQ = -12, 1312 | LAME_INTERNALERROR = -13, 1313 | 1314 | FRONTEND_READERROR = -80, 1315 | FRONTEND_WRITEERROR = -81, 1316 | FRONTEND_FILETOOLARGE = -82 1317 | 1318 | } lame_errorcodes_t; 1319 | 1320 | #if defined(__cplusplus) 1321 | } 1322 | #endif 1323 | #endif /* LAME_LAME_H */ 1324 | 1325 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/ConvertAudio/Lame/lame.framework/lame: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CivelXu/iOS-Lame-Audio-transcoding/62bce5dbf0966246d2fa4ed832b20050c0e1fb4b/RecorderDemo/RecorderDemo/ConvertAudio/Lame/lame.framework/lame -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSMicrophoneUsageDescription 6 | 请求使用麦克风 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/PlayerManager/PlayerManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerManager.h 3 | // MusicDemo 4 | 5 | 6 | 7 | 8 | #import 9 | 10 | typedef enum : NSUInteger { 11 | ETPlayer_Original, 12 | ETPlayer_UnkonwError, 13 | ETPlayer_ReadyToPlay, 14 | ETPlayer_Playing, 15 | ETPlayer_PlayFailed, 16 | ETPlayer_Pause, 17 | ETPlayer_Stop, 18 | ETPlayer_Loading, 19 | ETPlayer_FinishedPlay, 20 | } ETPlayerStatus; 21 | 22 | @protocol ETPlayerDelagate 23 | @optional 24 | - (void)currentPlayerStatus:(ETPlayerStatus)playerStatus; 25 | @end 26 | 27 | @interface PlayerManager : NSObject 28 | 29 | // instance method 30 | + (instancetype)sharedInstance; 31 | 32 | /** 33 | Play music by URL 34 | 35 | @param voiceURL voiceURL description 36 | */ 37 | - (void)playWithVoiceURL:(NSURL *)voiceURL; 38 | 39 | // pause 40 | - (void)pause; 41 | // stop 42 | - (void)stop; 43 | // seek to time 44 | - (void)seekToNewTime:(NSUInteger)newTime; 45 | // judge is Playing now 46 | - (BOOL)isPlaying; 47 | // rest palyer 48 | - (void)restPlay; 49 | 50 | // 当前时间(秒数) 51 | @property (nonatomic, assign) NSInteger currentTime; 52 | // 总时间(秒数) 53 | @property (nonatomic, assign) NSInteger finishTime; 54 | 55 | @property (nonatomic, assign) ETPlayerStatus status; 56 | 57 | @property (nonatomic, weak) id delegate; 58 | @end 59 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/PlayerManager/PlayerManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerManager.m 3 | // MusicDemo 4 | 5 | 6 | #import "PlayerManager.h" 7 | #import 8 | 9 | #ifdef DEBUG 10 | # define LogEx( s, ... ) NSLog( @"<%@:(%s,%d)> \n%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __FUNCTION__, __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] ) 11 | 12 | #else 13 | #define LogEx(s, ...) ; 14 | 15 | #endif 16 | 17 | #define isValidString(string) (string && [string isEqualToString:@""] == NO) 18 | 19 | @interface PlayerManager() 20 | @property (nonatomic, strong) AVPlayer *player; 21 | @property (nonatomic, strong) AVPlayerItem *item; 22 | @property (nonatomic, strong) NSURL *playingURL; 23 | @end 24 | 25 | @implementation PlayerManager 26 | // instance method 27 | + (instancetype)sharedInstance 28 | { 29 | static PlayerManager *manager = nil; 30 | static dispatch_once_t onceToken; 31 | dispatch_once(&onceToken, ^{ 32 | manager = [[PlayerManager alloc] init]; 33 | }); 34 | return manager; 35 | } 36 | 37 | // default init 38 | - (instancetype)init 39 | { 40 | self = [super init]; 41 | if (self) { 42 | _player = [[AVPlayer alloc] init]; 43 | } 44 | return self; 45 | } 46 | 47 | /** 48 | Play music by URL 49 | 50 | @param voiceURL voiceURL description 51 | */ 52 | - (void)playWithVoiceURL:(NSURL *)voiceURL { 53 | 54 | AVAudioSession *audioSession = [AVAudioSession sharedInstance]; 55 | NSError *err = nil; 56 | [audioSession setCategory :AVAudioSessionCategoryPlayback error:&err]; 57 | 58 | if (!self.playingURL || ![self.playingURL isEqual:voiceURL]) { 59 | [self restPlay]; 60 | [self playerSetWithURL:voiceURL]; 61 | self.playingURL = voiceURL; 62 | } else { 63 | if (self.status == ETPlayer_Pause) { 64 | [self resumePlay]; 65 | } else if (self.status == ETPlayer_FinishedPlay || self.status == ETPlayer_Stop) { 66 | [self seekToNewTime:0]; 67 | [self resumePlay]; 68 | } 69 | } 70 | } 71 | 72 | - (void)playerSetWithURL:(NSURL *)url { 73 | 74 | if (!url || !isValidString(url.absoluteString)) { 75 | LogEx(@"------ play url is error"); 76 | return; 77 | } 78 | 79 | if (self.item) { // if avplayer has a item 80 | [self removeObserverFromPlayerItem:self.item]; 81 | self.item = nil; 82 | AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:url]; 83 | [_player replaceCurrentItemWithPlayerItem:item]; 84 | self.item = item; 85 | 86 | } else { // init new item 87 | AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:url]; 88 | _player = [AVPlayer playerWithPlayerItem:item]; 89 | self.item = item; 90 | } 91 | [self addObserverToPlayerItem:self.item]; 92 | [_player play]; 93 | } 94 | 95 | // 播放 96 | - (void)resumePlay 97 | { 98 | [_player play]; 99 | LogEx(@"======= play resume ======="); 100 | 101 | } 102 | // 暂停 103 | - (void)pause 104 | { 105 | [_player pause]; 106 | self.status = ETPlayer_Pause; 107 | LogEx(@"======= play pause ======="); 108 | if ([self respondsDelegate]) { [self.delegate currentPlayerStatus:ETPlayer_Pause]; } 109 | } 110 | 111 | // stop 112 | - (void)stop { 113 | [_player pause]; 114 | [self seekToNewTime:0]; 115 | self.status = ETPlayer_Stop; 116 | LogEx(@"======= play stop ======="); 117 | if ([self respondsDelegate]) { [self.delegate currentPlayerStatus:ETPlayer_Stop]; } 118 | } 119 | // 跳转到某一秒 120 | - (void)seekToNewTime:(NSUInteger)newTime 121 | { 122 | /* 123 | value 代表的是总帧数 124 | timescale 代表的是每秒有多少帧 125 | */ 126 | // 使用currentTime为了获取当前的歌曲每秒有多少帧,因为每一首歌的品质不同,每秒有多少帧也不同 127 | CMTime time = _player.currentTime; 128 | time.value = newTime * time.timescale; 129 | [_player seekToTime:time]; 130 | } 131 | - (NSInteger)currentTime 132 | { 133 | CMTime time = _player.currentTime; 134 | // 当AVPlayer还没有读取到歌曲信息的时候,此时歌曲的当前时间为0,并且CMTime结构体的四个成员变量都是0, 135 | // 数学中0不可作为分母,同样代码里面0作为分母也会出问题。 136 | if (time.timescale == 0) { 137 | return 0; 138 | } 139 | return time.value / time.timescale; 140 | } 141 | 142 | - (NSInteger)finishTime 143 | { 144 | 145 | CMTime time = _player.currentItem.duration; 146 | if (time.timescale == 0) { 147 | return 0; 148 | } 149 | return time.value / time.timescale; 150 | 151 | // to get correct duration time 152 | // if (self.player && self.player.currentItem && self.player.currentItem.asset) { 153 | // return CMTimeGetSeconds(self.player.currentItem.asset.duration); 154 | // 155 | // } else{ 156 | // return 0; 157 | // } 158 | // 159 | 160 | // AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:self.playingURL options:nil]; 161 | // CMTime audioDuration = audioAsset.duration; 162 | // float audioDurationSeconds = CMTimeGetSeconds(audioDuration); 163 | // return (NSInteger)audioDurationSeconds; 164 | } 165 | 166 | - (BOOL)isPlaying 167 | { 168 | // 可以根据值去判断播放还是暂停 169 | if (_player.rate == 0.0) { 170 | return NO; 171 | } 172 | return YES; 173 | } 174 | 175 | - (void)restPlay { 176 | if ([self isPlaying]) { 177 | [self pause]; 178 | } else if (self.item) { 179 | [self removeObserverFromPlayerItem:self.item]; 180 | self.item = nil; 181 | } 182 | self.status = ETPlayer_Original; 183 | LogEx(@"======= player reset ======="); 184 | } 185 | /** 186 | * 给AVPlayerItem添加监控 187 | * 188 | * @param playerItem AVPlayerItem对象 189 | */ 190 | - (void)addObserverToPlayerItem:(AVPlayerItem *)playerItem 191 | { 192 | if (playerItem) { 193 | [playerItem addObserver:self 194 | forKeyPath:@"status" 195 | options:NSKeyValueObservingOptionNew 196 | context:nil]; 197 | //监控网络加载情况属性 198 | [playerItem addObserver:self 199 | forKeyPath:@"loadedTimeRanges" 200 | options:NSKeyValueObservingOptionNew 201 | context:nil]; 202 | //给AVPlayerItem添加播放完成通知 203 | [[NSNotificationCenter defaultCenter] addObserver:self 204 | selector:@selector(playbackFinished:) 205 | name:AVPlayerItemDidPlayToEndTimeNotification 206 | object:playerItem]; 207 | } 208 | } 209 | 210 | - (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem 211 | { 212 | if (playerItem) { 213 | [playerItem removeObserver:self forKeyPath:@"status"]; 214 | [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; 215 | [[NSNotificationCenter defaultCenter]removeObserver:self 216 | name:AVPlayerItemDidPlayToEndTimeNotification 217 | object:playerItem]; 218 | } 219 | } 220 | 221 | - (void)observeValueForKeyPath:(NSString *)keyPath 222 | ofObject:(id)object 223 | change:(NSDictionary *)change 224 | context:(void *)context 225 | { 226 | AVPlayerItem *playerItem = object; 227 | if ([keyPath isEqualToString:@"status"]) { 228 | AVPlayerStatus status = [[change objectForKey:@"new"] intValue]; 229 | switch (status) { 230 | case AVPlayerStatusUnknown: 231 | { 232 | self.status = ETPlayer_UnkonwError; 233 | LogEx(@"======= player UnkonwError ======="); 234 | if ([self respondsDelegate]) { [self.delegate currentPlayerStatus:ETPlayer_UnkonwError]; } 235 | } 236 | break; 237 | case AVPlayerStatusReadyToPlay: 238 | { 239 | self.status = ETPlayer_ReadyToPlay; 240 | LogEx(@"======= ready to play ======="); 241 | if ([self respondsDelegate]) { [self.delegate currentPlayerStatus:ETPlayer_ReadyToPlay]; } 242 | 243 | CMTime time = _player.currentItem.duration; 244 | float value1 = time.value / time.timescale; 245 | 246 | float value2 = CMTimeGetSeconds(self.player.currentItem.asset.duration); 247 | 248 | 249 | AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:self.playingURL options:nil]; 250 | CMTime audioDuration = audioAsset.duration; 251 | float value3 = CMTimeGetSeconds(audioDuration); 252 | 253 | NSLog(@"正在播放,_player.currentItem.duration 总长度:%.2f", value1); 254 | NSLog(@"正在播放,_player.currentItem.asset.duration总长度:%.2f", value2); 255 | NSLog(@"正在播放,audioAsset.duration 总长度:%.2f", value3); 256 | 257 | } 258 | break; 259 | case AVPlayerStatusFailed: 260 | { 261 | self.status = ETPlayer_PlayFailed; 262 | LogEx(@"======= play failed ======="); 263 | if ([self respondsDelegate]) { [self.delegate currentPlayerStatus:ETPlayer_PlayFailed]; } 264 | } 265 | break; 266 | default: 267 | break; 268 | } 269 | 270 | } else if([keyPath isEqualToString:@"loadedTimeRanges"]) { 271 | double timeInterval = [self availableDuration]; 272 | CMTime duration = playerItem.duration; 273 | double totalDuration = CMTimeGetSeconds(duration); 274 | 275 | NSLog(@"timeInterval:%f",timeInterval); 276 | NSLog(@"totalDuration:%f",totalDuration); 277 | 278 | if (timeInterval < 1.5 * totalDuration) { 279 | self.status = ETPlayer_Loading; 280 | LogEx(@"======= play loding ======="); 281 | if ([self respondsDelegate]) { [self.delegate currentPlayerStatus:ETPlayer_Loading]; } 282 | }else if ([self isPlaying]) { 283 | self.status = ETPlayer_Playing; 284 | LogEx(@"======= playing ======="); 285 | if ([self respondsDelegate]) { [self.delegate currentPlayerStatus:ETPlayer_Playing]; } 286 | } else { 287 | self.status = ETPlayer_Loading; 288 | LogEx(@"======= play loading ======="); 289 | if ([self respondsDelegate]) { [self.delegate currentPlayerStatus:ETPlayer_Loading]; } 290 | } 291 | } 292 | } 293 | 294 | - (double)availableDuration{ 295 | NSArray *loadedTimeRanges = self.player.currentItem.loadedTimeRanges; 296 | NSValue *value = loadedTimeRanges.firstObject; 297 | CMTimeRange timeRange = value.CMTimeRangeValue; 298 | double startSeconds = CMTimeGetSeconds(timeRange.duration); 299 | double durationSeconds = CMTimeGetSeconds(timeRange.duration); 300 | return startSeconds + durationSeconds; 301 | } 302 | 303 | - (BOOL)respondsDelegate { 304 | if (self.delegate && [self.delegate respondsToSelector:@selector(currentPlayerStatus:)]) { 305 | return YES; 306 | } 307 | return NO; 308 | } 309 | 310 | // listen play finish 311 | - (void)playbackFinished:(NSNotification *)notification { 312 | LogEx(@"======= play finish ======="); 313 | if ([self respondsDelegate]) { 314 | [self.delegate currentPlayerStatus:ETPlayer_FinishedPlay]; 315 | } 316 | self.status = ETPlayer_FinishedPlay; 317 | } 318 | 319 | 320 | @end 321 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // RecorderDemo 4 | // 5 | // Created by xuxiwen on 2017/7/13. 6 | // Copyright © 2017年 xuxiwen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // RecorderDemo 4 | // 5 | // Created by xuxiwen on 2017/7/13. 6 | // Copyright © 2017年 xuxiwen. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import 11 | #import "PlayerManager.h" 12 | #import "ConvertAudioFile.h" 13 | 14 | #define isValidString(string) (string && [string isEqualToString:@""] == NO) 15 | #define ETRECORD_RATE 11025.0 16 | #define ENCODE_MP3 1 17 | 18 | 19 | @interface ViewController () 20 | 21 | @property (weak, nonatomic) IBOutlet UILabel *timeLabel; 22 | 23 | @property (nonatomic,strong) NSString *mp3Path; 24 | @property (nonatomic,strong) NSString *cafPath; 25 | 26 | @property (nonatomic, strong) NSTimer *timer; 27 | @property (weak, nonatomic) IBOutlet UIButton *recordButton; 28 | 29 | @property (nonatomic, strong) AVAudioRecorder *audioRecorder; 30 | 31 | - (IBAction)recoredButton:(id)sender; 32 | 33 | @end 34 | 35 | @implementation ViewController 36 | { 37 | NSInteger time; 38 | } 39 | - (void)viewDidLoad { 40 | [super viewDidLoad]; 41 | } 42 | 43 | /** 44 | * 获得录音机对象 45 | * 46 | * @return 录音机对象 47 | */ 48 | - (AVAudioRecorder *)audioRecorder{ 49 | if (!_audioRecorder) { 50 | //7.0第一次运行会提示,是否允许使用麦克风 51 | AVAudioSession *session = [AVAudioSession sharedInstance]; 52 | NSError *sessionError; 53 | //AVAudioSessionCategoryPlayAndRecord用于录音和播放 54 | [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError]; 55 | if(session == nil) 56 | NSLog(@"Error creating session: %@", [sessionError description]); 57 | else 58 | [session setActive:YES error:nil]; 59 | 60 | //创建录音文件保存路径 61 | NSURL *url= [self getSavePath]; 62 | //创建录音格式设置 63 | NSDictionary *setting = [self getAudioSetting]; 64 | //创建录音机 65 | NSError *error=nil; 66 | _audioRecorder = [[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error]; 67 | _audioRecorder.delegate=self; 68 | _audioRecorder.meteringEnabled=YES;//如果要监控声波则必须设置为YES 69 | [_audioRecorder prepareToRecord]; 70 | if (error) { 71 | NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription); 72 | return nil; 73 | } 74 | } 75 | return _audioRecorder; 76 | } 77 | 78 | /** 79 | * 取得录音文件设置 80 | * 81 | * @return 录音设置 82 | */ 83 | - (NSDictionary *)getAudioSetting{ 84 | NSMutableDictionary *dicM = [NSMutableDictionary dictionary]; 85 | [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey]; 86 | [dicM setObject:@(ETRECORD_RATE) forKey:AVSampleRateKey]; 87 | [dicM setObject:@(2) forKey:AVNumberOfChannelsKey]; 88 | [dicM setObject:@(16) forKey:AVLinearPCMBitDepthKey]; 89 | [dicM setObject:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey]; 90 | return dicM; 91 | } 92 | 93 | /** 94 | * 取得录音文件保存路径 95 | * 96 | * @return 录音文件路径 97 | */ 98 | -(NSURL *)getSavePath{ 99 | // 在Documents目录下创建一个名为FileData的文件夹 100 | NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"AudioData"]; 101 | NSLog(@"%@",path); 102 | 103 | NSFileManager *fileManager = [NSFileManager defaultManager]; 104 | BOOL isDir = FALSE; 105 | BOOL isDirExist = [fileManager fileExistsAtPath:path isDirectory:&isDir]; 106 | if(!(isDirExist && isDir)) 107 | 108 | { 109 | BOOL bCreateDir = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; 110 | if(!bCreateDir){ 111 | NSLog(@"创建文件夹失败!"); 112 | } 113 | NSLog(@"创建文件夹成功,文件路径%@",path); 114 | } 115 | NSString *fileName = @"record"; 116 | NSString *cafFileName = [NSString stringWithFormat:@"%@.caf", fileName]; 117 | NSString *mp3FileName = [NSString stringWithFormat:@"%@.mp3", fileName]; 118 | 119 | NSString *cafPath = [path stringByAppendingPathComponent:cafFileName]; 120 | NSString *mp3Path = [path stringByAppendingPathComponent:mp3FileName]; 121 | 122 | self.mp3Path = mp3Path; 123 | self.cafPath = cafPath; 124 | 125 | NSLog(@"file path:%@",cafPath); 126 | 127 | NSURL *url=[NSURL fileURLWithPath:cafPath]; 128 | return url; 129 | } 130 | 131 | - (void)cleanCafFile { 132 | 133 | if (isValidString(self.cafPath)) { 134 | NSFileManager *fileManager = [NSFileManager defaultManager]; 135 | BOOL isDir = FALSE; 136 | BOOL isDirExist = [fileManager fileExistsAtPath:self.cafPath isDirectory:&isDir]; 137 | if (isDirExist) { 138 | [fileManager removeItemAtPath:self.cafPath error:nil]; 139 | NSLog(@" xxx.caf file already delete"); 140 | } 141 | } 142 | } 143 | 144 | - (void)cleanMp3File { 145 | 146 | if (isValidString(self.mp3Path)) { 147 | NSFileManager *fileManager = [NSFileManager defaultManager]; 148 | BOOL isDir = FALSE; 149 | BOOL isDirExist = [fileManager fileExistsAtPath:self.mp3Path isDirectory:&isDir]; 150 | if (isDirExist) { 151 | [fileManager removeItemAtPath:self.mp3Path error:nil]; 152 | NSLog(@" xxx.mp3 file already delete"); 153 | } 154 | } 155 | } 156 | 157 | 158 | - (void)convertMp3 { 159 | 160 | 161 | [[ConvertAudioFile sharedInstance] conventToMp3WithCafFilePath:self.cafPath 162 | mp3FilePath:self.mp3Path 163 | sampleRate:ETRECORD_RATE callback:^(BOOL result) 164 | { 165 | NSLog(@"---- 转码完成 --- result %d ---- ", result); 166 | }];; 167 | 168 | 169 | } 170 | 171 | 172 | - (IBAction)recoredButton:(id)sender { 173 | 174 | // 重置录音机 175 | if (_audioRecorder) { 176 | [self cleanMp3File]; 177 | [self cleanCafFile]; 178 | _audioRecorder = nil; 179 | time = 0; 180 | [self destoryTimer]; 181 | } 182 | 183 | if (![self.audioRecorder isRecording]) { 184 | AVAudioSession *session = [AVAudioSession sharedInstance]; 185 | NSError *sessionError; 186 | //AVAudioSessionCategoryPlayAndRecord用于录音和播放 187 | [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError]; 188 | if(session == nil) 189 | NSLog(@"Error creating session: %@", [sessionError description]); 190 | else 191 | [session setActive:YES error:nil]; 192 | 193 | 194 | 195 | self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 196 | target:self 197 | selector:@selector(record) 198 | userInfo:nil 199 | repeats:YES]; 200 | [self.audioRecorder record]; 201 | 202 | NSLog(@"录音开始"); 203 | 204 | #if ENCODE_MP3 205 | [[ConvertAudioFile sharedInstance] conventToMp3WithCafFilePath:self.cafPath 206 | mp3FilePath:self.mp3Path 207 | sampleRate:ETRECORD_RATE 208 | callback:^(BOOL result) 209 | { 210 | if (result) { 211 | NSLog(@"mp3 file compression sucesss"); 212 | } 213 | }]; 214 | #endif 215 | 216 | } else { 217 | 218 | NSLog(@"is recording now ...."); 219 | } 220 | 221 | } 222 | 223 | - (void)record { 224 | time ++; 225 | self.timeLabel.text = [self timeFormatted:(int)time]; 226 | } 227 | 228 | 229 | - (IBAction)stopRecord:(id)sender { 230 | 231 | if ([self.audioRecorder isRecording]) { 232 | NSLog(@"完成"); 233 | [self destoryTimer]; 234 | [self.audioRecorder stop]; 235 | } 236 | 237 | #if !ENCODE_MP3 238 | [ConvertAudioFile conventToMp3WithCafFilePath:self.cafPath 239 | mp3FilePath:self.mp3Path 240 | sampleRate:ETRECORD_RATE 241 | callback:^(BOOL result) { 242 | NSLog(@"转码结果 ------ %d", result); 243 | }]; 244 | 245 | #endif 246 | } 247 | 248 | 249 | 250 | - (IBAction)playVoice:(id)sender { 251 | 252 | if ([self.audioRecorder isRecording]) { 253 | NSLog(@"-------- 正在录制中..."); 254 | [self stopRecord:nil]; 255 | return; 256 | } 257 | 258 | NSFileManager *fileManager = [NSFileManager defaultManager]; 259 | BOOL isDir = FALSE; 260 | BOOL isDirExist = [fileManager fileExistsAtPath:self.cafPath isDirectory:&isDir]; 261 | if (!isDirExist) { 262 | NSLog(@"-------- 录音文件不存在..."); 263 | return; 264 | } 265 | 266 | NSURL *url = [NSURL fileURLWithPath:self.mp3Path]; 267 | 268 | [[PlayerManager sharedInstance] playWithVoiceURL:url]; 269 | 270 | if (self.timer) { 271 | [self destoryTimer]; 272 | } 273 | self.timer = [NSTimer scheduledTimerWithTimeInterval:1 274 | target:self 275 | selector:@selector(play) 276 | userInfo:nil 277 | repeats:YES]; 278 | } 279 | 280 | - (void)play { 281 | self.timeLabel.text = [NSString stringWithFormat:@"%@ / %@", 282 | [self timeFormatted:[PlayerManager sharedInstance].currentTime], 283 | [self timeFormatted:[PlayerManager sharedInstance].finishTime]]; 284 | } 285 | 286 | - (IBAction)pausePaly:(id)sender { 287 | [[PlayerManager sharedInstance] pause]; 288 | } 289 | 290 | 291 | - (void)destoryTimer { 292 | if (self.timer) { 293 | [self.timer invalidate]; 294 | self.timer = nil; 295 | NSLog(@"----- timer destory"); 296 | } 297 | } 298 | 299 | 300 | - (NSString *)timeFormatted:(NSInteger)totalSeconds { 301 | 302 | NSInteger seconds = totalSeconds % 60; 303 | NSInteger minutes = (totalSeconds / 60) % 60; 304 | NSInteger hours = totalSeconds / 3600; 305 | if (hours <= 0) { 306 | return [NSString stringWithFormat:@"%02ld:%02ld",(long)minutes, (long)seconds]; 307 | } 308 | return [NSString stringWithFormat:@"%02ld:%02ld:%02ld",(long)hours, (long)minutes, (long)seconds]; 309 | } 310 | 311 | /* audioRecorderDidFinishRecording:successfully: is called when a recording has been finished or stopped. This method is NOT called if the recorder is stopped due to an interruption. */ 312 | - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag { 313 | if (flag) { 314 | NSLog(@"----- 录音 完毕"); 315 | 316 | #if ENCODE_MP3 317 | [[ConvertAudioFile sharedInstance] sendEndRecord];; 318 | #endif 319 | 320 | } 321 | } 322 | 323 | 324 | - (void)didReceiveMemoryWarning { 325 | [super didReceiveMemoryWarning]; 326 | // Dispose of any resources that can be recreated. 327 | } 328 | @end 329 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // RecorderDemo 4 | // 5 | // Created by xuxiwen on 2017/7/13. 6 | // Copyright © 2017年 xuxiwen. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemoTests/RecorderDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // RecorderDemoTests.m 3 | // RecorderDemoTests 4 | // 5 | // Created by xuxiwen on 2017/7/13. 6 | // Copyright © 2017年 xuxiwen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RecorderDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation RecorderDemoTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RecorderDemo/RecorderDemoUITests/RecorderDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // RecorderDemoUITests.m 3 | // RecorderDemoUITests 4 | // 5 | // Created by xuxiwen on 2017/7/13. 6 | // Copyright © 2017年 xuxiwen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RecorderDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation RecorderDemoUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------