项目中需要将pcm音频文件转码为mp3格式后上传, 在网上查找转码mp3的资料, 应用时转码出来的mp3音频声音比较尖锐(像机器声音/女声), 最后发现是声道数不统一导致的.
项目中使用的单声道, 16000的采样率进行录音, 网上资料默认使用的 lame_encode_buffer_interleaved 这个方法进行转码, 导致出现上述问题
lame.h 中说明如下:
/*
* input pcm data, output (maybe) mp3 frames.
* This routine handles all buffering, resampling and filtering for you.
*
* return code number of bytes output in mp3buf. Can be 0
* -1: mp3buf was too small
* -2: malloc() problem
* -3: lame_init_params() not called
* -4: psycho acoustic problems
*
* The required mp3buf_size can be computed from num_samples,
* samplerate and encoding rate, but here is a worst case estimate:
*
* mp3buf_size in bytes = 1.25*num_samples + 7200
*
* I think a tighter bound could be: (mt, March 2000)
* MPEG1:
* num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512
* MPEG2:
* num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256
*
* but test first if you use that!
*
* set mp3buf_size = 0 and LAME will not check if mp3buf_size is
* large enough.
*
* NOTE:
* if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels
* will be averaged into the L channel before encoding only the L channel
* This will overwrite the data in buffer_l[] and buffer_r[].
*
*/
int CDECL lame_encode_buffer (
lame_global_flags* gfp, /* global context handle */
const short int buffer_l [], /* PCM data for left channel */
const short int buffer_r [], /* PCM data for right channel */
const int nsamples, /* number of samples per channel */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
const int mp3buf_size ); /* number of valid octets in this
stream */
/*
* as above, but input has L & R channel data interleaved.
* NOTE:
* num_samples = number of samples in the L (or R)
* channel, not the total number of samples in pcm[]
*/
int CDECL lame_encode_buffer_interleaved(
lame_global_flags* gfp, /* global context handlei */
short int pcm[], /* PCM data for left and right
channel, interleaved */
int num_samples, /* number of samples per channel,
_not_ number of samples in
pcm[] */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
int mp3buf_size ); /* number of valid octets in this
stream */
lame_encode_buffer_interleaved 左右声道数据交替传入进行转码
转码核心代码如下:
/***
* pcm 文件转mp3文件
*/
- (BOOL)convertPcm:(NSString *)pcmPath toMp3:(NSString *)mp3Path {
@try {
FILE *fpcm = fopen([pcmPath cStringUsingEncoding:NSASCIIStringEncoding], "rb");
if (fpcm == NULL) {
return false;
}
// fseek(fpcm, 1024*4, SEEK_CUR); //跳过源文件的信息头,不然在开头会有爆破音
FILE *fmp3 = fopen([mp3Path cStringUsingEncoding:NSASCIIStringEncoding], "wb");
int channelCount = 1; // 声道数, 跟录音时配置一样的
lame = lame_init();
lame_set_in_samplerate(lame, 16000); //设置采样率, 需要跟录音时的采样率相同
lame_set_num_channels(lame, channelCount); //声道,不设置默认为双声道
lame_set_VBR(lame, vbr_default);
// lame_set_mode(lame, 0);
lame_set_quality(lame, 2);
lame_init_params(lame);
const int PCM_SIZE = 8192;//
const int MP3_SIZE = 8192; //
short int pcm_buffer[PCM_SIZE*channelCount];
unsigned char mp3_buffer[MP3_SIZE];
int read, write;
do {
read = fread(pcm_buffer, channelCount*sizeof(short int), PCM_SIZE, fpcm);
if (read == 0) {
write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
} else {
if (channelCount == 1) {
write = lame_encode_buffer(lame, pcm_buffer, NULL, read, mp3_buffer, MP3_SIZE); // 单声道音频转码
} else {
write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); // 多声道音频转码
}
}
fwrite(mp3_buffer, write, 1, fmp3);
} while (read != 0);
lame_mp3_tags_fid(lame, fmp3);
lame_close(lame);
fclose(fmp3);
fclose(fpcm);
} @catch (NSException *exception) {
NSLog(@"catch exception, %@", exception);
return false;
} @finally {
return true;
}
}