項(xiàng)目中需要將pcm音頻文件轉(zhuǎn)碼為mp3格式后上傳, 在網(wǎng)上查找轉(zhuǎn)碼mp3的資料, 應(yīng)用時(shí)轉(zhuǎn)碼出來的mp3音頻聲音比較尖銳(像機(jī)器聲音/女聲), 最后發(fā)現(xiàn)是聲道數(shù)不統(tǒng)一導(dǎo)致的.
項(xiàng)目中使用的單聲道, 16000的采樣率進(jìn)行錄音, 網(wǎng)上資料默認(rèn)使用的 lame_encode_buffer_interleaved 這個(gè)方法進(jìn)行轉(zhuǎn)碼, 導(dǎo)致出現(xiàn)上述問題
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 左右聲道數(shù)據(jù)交替?zhèn)魅脒M(jìn)行轉(zhuǎn)碼
轉(zhuǎn)碼核心代碼如下:
/***
* pcm 文件轉(zhuǎn)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); //跳過源文件的信息頭,不然在開頭會(huì)有爆破音
FILE *fmp3 = fopen([mp3Path cStringUsingEncoding:NSASCIIStringEncoding], "wb");
int channelCount = 1; // 聲道數(shù), 跟錄音時(shí)配置一樣的
lame = lame_init();
lame_set_in_samplerate(lame, 16000); //設(shè)置采樣率, 需要跟錄音時(shí)的采樣率相同
lame_set_num_channels(lame, channelCount); //聲道,不設(shè)置默認(rèn)為雙聲道
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); // 單聲道音頻轉(zhuǎn)碼
} else {
write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); // 多聲道音頻轉(zhuǎn)碼
}
}
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;
}
}