最近一个做安卓的朋友在项目中遇到了类似微信发送语音消息的功能,就进行了一些讨论,录音功能很简单,但为了实现录制的音频文件在多个平台都能正常播放(eg:iOS端录制的文件能够上传服务器后支持在网站或者安卓端播放),一般要将音频文件转换成MP3格式。而且转换成MP3格式后文件体积将会变小很多。
在网上搜索资料后,发现关于音频文件格式转换的资料并不是很多,自己就相关资料整理一下写一个小demo以便以后用到之时能够返回查阅。
PCM转MP3格式一般使用lame,首先要对lame库进行编译生成我们需要的库文件和头文件。
1. 编译lame生成库文件和头文件
编译步骤在这篇文章有详细说明,此处不在赘述。
2. 录音准备
要先实现录音,需要AVAudioRecorder对象
- (AVAudioRecorder *)recoder
{
if (!_recoder) {
//存放录音文件的地址
NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString * filePath = [path stringByAppendingPathComponent:@"123.caf"];
self.recordeFilePath = filePath;
NSURL * url = [NSURL URLWithString:filePath];
self.url = url;
//录音设置
NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] init];
//设置录音格式
[recordSettings setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey: AVFormatIDKey];
//采样率 采样率必须要设为11025才能使转化成mp3格式后不会失真
[recordSettings setValue :[NSNumber numberWithFloat:11025.0] forKey: AVSampleRateKey];//44100.0
//通道数要转换成MP3格式必须为双通道
[recordSettings setValue :[NSNumber numberWithInt:2] forKey: AVNumberOfChannelsKey];
//音频质量,采样质量
[recordSettings setValue:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey];
//创建录音对象
_recoder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:nil];
[_recoder prepareToRecord];
}
return _recoder;
}
3. 音频文件转码成MP3格式
在demo中我实现的是录完之后转码,但实际上边录边转速度将会更快。对边录边转的实现可参考这篇文章
lame实现转码的重要代码如下:
- (void)convertToMp3
{
NSString *fileName = [NSString stringWithFormat:@"/%@.mp3", @"test"];
NSString *filePath = [[NSHomeDirectory() stringByAppendingFormat:@"/Documents/"] stringByAppendingPathComponent:fileName];
NSLog(@"%@",filePath);
_mp3Url = [NSURL URLWithString:filePath];
@try {
int read,write;
//只读方式打开被转换音频文件
FILE *pcm = fopen([self.recordeFilePath cStringUsingEncoding:1], "rb");
fseek(pcm, 4 * 1024, SEEK_CUR);//删除头,否则在前一秒钟会有杂音
//只写方式打开生成的MP3文件
FILE *mp3 = fopen([filePath cStringUsingEncoding:1], "wb");
const int PCM_SIZE = 8192;
const int MP3_SIZE = 8192;
short int pcm_buffer[PCM_SIZE * 2];
unsigned char mp3_buffer[MP3_SIZE];
//这里要注意,lame的配置要跟AVAudioRecorder的配置一致,否则会造成转换不成功
lame_t lame = lame_init();
lame_set_in_samplerate(lame, 11025.0);//采样率
lame_set_VBR(lame, vbr_default);
lame_init_params(lame);
do {
//以二进制形式读取文件中的数据
read = (int)fread(pcm_buffer, 2 * sizeof(short int), PCM_SIZE, pcm);
if (read == 0)
write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
else
write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
//二进制形式写数据到文件中 mp3_buffer:数据输出到文件的缓冲区首地址 write:一个数据块的字节数 1:指定一次输出数据块的个数 mp3:文件指针
fwrite(mp3_buffer, write, 1, mp3);
} while (read != 0);
lame_close(lame);
fclose(mp3);
fclose(pcm);
} @catch (NSException *exception) {
NSLog(@"%@",[exception description]);
} @finally {
NSLog(@"MP3生成成功!!!");
}
}
4. 实现过程中遇到的坑
在模拟器上调试的时候录制的音频文件可以正常播放,但在真机上录制的音频文件播放时却没有声音。通过设置音频会话分类解决:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[audioSession setActive:YES error:nil];
demo地址,也放入了我编译的lame库