iOS pcm轉(zhuǎn)mp3出現(xiàn)尖銳聲

項目中需要將pcm音頻文件轉(zhuǎn)碼為mp3格式后上傳, 在網(wǎng)上查找轉(zhuǎn)碼mp3的資料, 應(yīng)用時轉(zhuǎn)碼出來的mp3音頻聲音比較尖銳(像機(jī)器聲音/女聲), 最后發(fā)現(xiàn)是聲道數(shù)不統(tǒng)一導(dǎo)致的.

項目中使用的單聲道, 16000的采樣率進(jìn)行錄音, 網(wǎng)上資料默認(rèn)使用的 lame_encode_buffer_interleaved 這個方法進(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); //跳過源文件的信息頭,不然在開頭會有爆破音
        FILE *fmp3 = fopen([mp3Path cStringUsingEncoding:NSASCIIStringEncoding], "wb");
        
        int channelCount = 1;   // 聲道數(shù), 跟錄音時配置一樣的
        
        lame = lame_init();
        lame_set_in_samplerate(lame, 16000); //設(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;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容