使用libmp3lame库编码mp3

本文介绍如何利用libmp3lame库进行MP3编码,包括库的编译、关键参数设置及编码流程,并解析不同MPEG版本间的参数兼容性。

使用libmp3lame库编码mp3

LAME

lame是一个有名的开源mp3编码库,但是目前网上使用lame库的教程基本都是直接贴一篇代码,没有任何的解释,而每个使用者需要编码的需求都不一样,这些所谓的教程基本没什么作用。 
这篇文章将会介绍如何调用lame库的接口编码出mp3。不同于目前网上的大多数lame教程,本文不会干巴巴的贴一屏幕代码,而是尽量对lame库提供的各种参数设置的接口做讲解。让读者能够举一反三,根据自己的需求编码出各种格式的mp3。

lame库编译

lame对Linux的编译支持比较好,但是对于vc的支持基本停留在10年以前。所幸编码库的代码只有几个文件(下载的代码还包括命令行程序代码和gui程序代码,我们在此只需要编码库的代码,在libmp3lame目录中),自己新建个项目包含进来编译就行。

MP3

mp3(MPEG Layer III)这种格式在生活中很常见,但是mp3有很多种参数,这里讨论一下mp3编码所必须知道的一些参数。

  • 采样率(sampleRate):采样率越高声音的还原度越好。
  • 比特率(bitrate):每秒钟的数据量,越高音质越好。
  • 声道数(channels):声道的数量,通常只有单声道和双声道,双声道即所谓的立体声。
  • 比特率控制模式:ABR、VBR、CBR,这3中模式含义很容易查询到,不在赘述。

MPEG Layer III

MPEG有几个版本的协议,不同版本的协议能够支持的参数能力是不同的。编码库的使用者必须清楚不同版本的区别才能正确的设置参数。

有以下3个版本的协议,MPEG1、MPEG2、MPEG2.5。其中MPEG2.5是非官方的标准,但是流传广泛,所以基本也都支持。他们的区别主要集中在支持的比特率和采样率不同。

采样率支持(Hz)

MPEG1 MPEG2 MPEG2.5
44100 22050 11025
48000 24000 12000
32000 16000 8000

比特率支持(bit/s)

MPEG1 MPEG2 MPEG2.5
32 8 8
40 16 16
48 24 24
56 32 32
64 40 40
80 48 48
96 56 56
112 64 64
128 80  
160 96  
192 112  
224 128  
256 144  
320 160  

编码

流程

使用lame库只需要包含lame.h头文件,编码mp3基本上遵循以下的流程,

初始化编码参数

  • lame_init:初始化一个编码参数的数据结构,给使用者用来设置参数。

设置编码参数

  • lame_set_in_samplerate:设置被输入编码器的原始数据的采样率。
  • lame_set_out_samplerate:设置最终mp3编码输出的声音的采样率,如果不设置则和输入采样率一样。
  • lame_set_num_channels :设置被输入编码器的原始数据的声道数。
  • lame_set_mode :设置最终mp3编码输出的声道模式,如果不设置则和输入声道数一样。参数是枚举,STEREO代表双声道,MONO代表单声道。
  • lame_set_VBR:设置比特率控制模式,默认是CBR,但是通常我们都会设置VBR。参数是枚举,vbr_off代表CBR,vbr_abr代表ABR(因为ABR不常见,所以本文不对ABR做讲解)vbr_mtrh代表VBR。
  • lame_set_brate:设置CBR的比特率,只有在CBR模式下才生效。
  • lame_set_VBR_mean_bitrate_kbps:设置VBR的比特率,只有在VBR模式下才生效。

其中每个参数都有默认的配置,如非必要可以不设置。这里只介绍了几个关键的设置接口,还有其他的设置接口可以参考lame.h(lame的文档里只有命令行程序的用法,没有库接口的用法)。

初始化编码器器

lame_init_params:根据上面设置好的参数建立编码器

编码PCM数据

  • lame_encode_bufferlame_encode_buffer_interleaved:将PCM数据送入编码器,获取编码出的mp3数据。这些数据写入文件就是mp3文件。
  • 其中lame_encode_buffer输入的参数中是双声道的数据分别输入的,lame_encode_buffer_interleaved输入的参数中双声道数据是交错在一起输入的。具体使用哪个需要看采集到的数据是哪种格式的,不过现在的设备采集到的数据大部分都是双声道数据是交错在一起。
  • 单声道输入只能使用lame_encode_buffer,把单声道数据当成左声道数据传入,右声道传NULL即可。
  • 调用这两个函数时需要传入一块内存来获取编码器出的数据,这块内存的大小lame给出了一种建议的计算方式:采样率/20+7200。

结束编码

  • lame_encode_flush:刷新编码器缓冲,获取残留在编码器缓冲里的数据。这部分数据也需要写入mp3文件

写入VBRTAG

  • lame_mp3_tags_fid:向一个文件指针中写入XING规范的VBRTAG。
  • VBRTAG的作用是记录整个mp3的一些信息,通常用于VBR模式下的编码,因为VBR模式下比特率不固定,无法直接计算出播放的时长和跳跃点,所以在mp3的开头部分插入一个VBRTAG。
  • VBRTAG有几种规范,但是lame支持的是最通用的XING规范。
  • 注意lame_mp3_tags_fid函数的参数需要一个FILE *类型代表要写入的文件,这个文件一定是之前编码时写入了mp3数据的文件,VBRTAG是需要卸载mp3的开头的,之前的编码过程中会自动空出写入VBRTAG所需要的空间,这个函数内会自动寻找合适的文件偏移然后覆盖,所以当前的文件偏移是无关紧要的,但是打开文件的时候一定要以读写模式打开。
  • 注意我提到了之前的编码过程中会自动空出写入VBRTAG所需要的空间,所以如果结束编码后不调用lame_mp3_tags_fid写入VBRTAG就会导致这部分内容为空,虽然不影响播放,但是会影响很多播放器对于时长和跳跃点的计算。
  • 那么对于非VBR模式也需要写入VBRTAG吗?是的,lame对于非VBR模式也会预留出VBRTAG的空间,所以非VBR模式的编码最后也需要写入VBRTAG。

销毁编码器

  • lame_close销毁编码器,释放资源。

参数的有效性

不规范的参数

对于编码器的参数设置,所能接受的参数值并不是任意的。上一节的表格中列出了编码器器能够支持的参数值,如果我们设置的参数值不在其中,那么编码器会自动帮我们选择一个最近的值。

不一致的版本

但是如果仔细看了上面表格中的参数后会发现一个问题,每个版本支持的参数范围不一致,假如设置了MPEG2的比特率又设置了MPEG1的采样率那么会发生什么?

这里先给结论,lame库会优先服从采样率(这里指的是输出采样率)设置,根据采样率选择协议版本,然后在这个版本所能支持的比特率中选一个和设置比特率最接近的。 
这个结论是我研究了lame的源码后分析得到的,lame的文档中并没有对此有任何描述,网上也没有任何相关的资料描述这一问题,接下来给出源码分析,如果不关心的可以跳过这一节。


/* 
 * 0: MPEG-2 LSF
 * 1: MPEG-1
 * 2: MPEG-2.5 LSF FhG extention                  (1995-07-11 shn)
 */

typedef enum {
    MPEG_2 = 0,
    MPEG_1 = 1,
    MPEG_25 = 2
} MPEG_t;

const int bitrate_table[3][16] = {
    {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1}, /* MPEG 2 */
    {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1}, /* MPEG 1 */
    {0, 8, 16, 24, 32, 40, 48, 56, 64, -1, -1, -1, -1, -1, -1, -1}, /* MPEG 2.5 */
};

const int samplerate_table[3][4] = {
    {22050, 24000, 16000, -1}, /* MPEG 2 */
    {44100, 48000, 32000, -1}, /* MPEG 1 */
    {11025, 12000, 8000, -1}, /* MPEG 2.5 */
};
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

这是lame库中的参数能力表,lame库最终会从表中选择和设置值最接近的值。

下面的代码是在CBR模式下确定比特率的代码


if (gfp->VBR == vbr_off && gfp->compression_ratio > 0) {

        if (gfp->samplerate_out == 0)
            gfp->samplerate_out = map2MP3Frequency((int) (0.97 * gfp->samplerate_in)); /* round up with a margin of 3% */

        /* choose a bitrate for the output samplerate which achieves
         * specified compression ratio
         */
        gfp->brate = gfp->samplerate_out * 16 * cfg->channels_out / (1.e3 * gfp->compression_ratio);

        /* we need the version for the bitrate table look up */
        cfg->samplerate_index = SmpFrqIndex(gfp->samplerate_out, &cfg->version);

        if (!cfg->free_format) /* for non Free Format find the nearest allowed bitrate */
            gfp->brate = FindNearestBitrate(gfp->brate, cfg->version, gfp->samplerate_out);
    }

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
int
map2MP3Frequency(int freq)
{
    if (freq <= 8000)
        return 8000;
    if (freq <= 11025)
        return 11025;
    if (freq <= 12000)
        return 12000;
    if (freq <= 16000)
        return 16000;
    if (freq <= 22050)
        return 22050;
    if (freq <= 24000)
        return 24000;
    if (freq <= 32000)
        return 32000;
    if (freq <= 44100)
        return 44100;

    return 48000;
}

int
BitrateIndex(int bRate,      /* legal rates from 32 to 448 kbps */
             int version,    /* MPEG-1 or MPEG-2/2.5 LSF */
             int samplerate)
{                       /* convert bitrate in kbps to index */
    int     i;
    if (samplerate < 16000)
        version = 2;
    for (i = 0; i <= 14; i++) {
        if (bitrate_table[version][i] > 0) {
            if (bitrate_table[version][i] == bRate) {
                return i;
            }
        }
    }
    return -1;
}

/* convert samp freq in Hz to index */

int
SmpFrqIndex(int sample_freq, int *const version)
{
    switch (sample_freq) {
    case 44100:
        *version = 1;
        return 0;
    case 48000:
        *version = 1;
        return 1;
    case 32000:
        *version = 1;
        return 2;
    case 22050:
        *version = 0;
        return 0;
    case 24000:
        *version = 0;
        return 1;
    case 16000:
        *version = 0;
        return 2;
    case 11025:
        *version = 0;
        return 0;
    case 12000:
        *version = 0;
        return 1;
    case 8000:
        *version = 0;
        return 2;
    default:
        *version = 0;
        return -1;
    }
}
 
  • 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
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 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
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  1. 可以看到首先如果没有设置输出采样率,调用map2MP3Frequency根据输入采样率算出输出采样率,默认和输入采样率一致的。
  2. 然后根据输出采样率调用SmpFrqIndex计算出MPEG版本,这里注意将MPEG2和MPEG2.5都视为MPEG2。
  3. 根据已经设置的比特率、输出采样率、版本调用FindNearestBitrate去寻找合适的比特率。FindNearestBitrate在相应的版本的能力范围中找一个最接近的值的索引。

接下来分析一下VBR情况下的处理


if (gfp->samplerate_out) {
        if (gfp->samplerate_out < 16000) {
            gfp->VBR_mean_bitrate_kbps = Max(gfp->VBR_mean_bitrate_kbps, 8);
            gfp->VBR_mean_bitrate_kbps = Min(gfp->VBR_mean_bitrate_kbps, 64);
        }
        else if (gfp->samplerate_out < 32000) {
            gfp->VBR_mean_bitrate_kbps = Max(gfp->VBR_mean_bitrate_kbps, 8);
            gfp->VBR_mean_bitrate_kbps = Min(gfp->VBR_mean_bitrate_kbps, 160);
        }
        else {
            gfp->VBR_mean_bitrate_kbps = Max(gfp->VBR_mean_bitrate_kbps, 32);
            gfp->VBR_mean_bitrate_kbps = Min(gfp->VBR_mean_bitrate_kbps, 320);
        }
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

可以看到这里没有查表,直接是和采样率对应的版本允许的比特率的范围比较。这是因为VBR模式下比特率不固定,只要不超出范围即可。

<think>好的,我现在需要回答用户关于如何使用libmp3lame API实现MP3编码的问题,并且要提供示例代码教程。首先,我得回忆一下libmp3lame的基本用法和相关步骤。 用户之前提到了在Unity中使用Lame For Unity的示例代码,但现在他们想直接使用libmp3lame的API,所以可能需要更底层的C语言示例。我需要确保提供的代码符合他们的需求,并且步骤清晰。 首先,我应该考虑libmp3lame的基本编码流程。通常,编码MP3需要初始化编码器、配置参数、处理PCM数据、刷新缓冲区,最后关闭编码器。这些步骤需要详细说明,确保用户能够理解每个部分的作用。 然后,我需要检查是否需要包含头文件和链接文件。用户可能在使用不同的平台,比如Windows或Linux,所以可能需要提到如何安装或获取libmp3lame的开发。例如,在Ubuntu上安装lame-dev包,或者在Windows上配置项目路径。 接下来,编写示例代码。代码应该包括初始化lame参数,设置编码参数如采样率、声道数、比特率,然后处理输入的PCM数据。需要注意的是,输入数据可能是16位的整型,而lame_encode_buffer_interleaved函数可能需要换。此外,处理完所有数据后,必须调用lame_encode_flush来确保所有缓冲数据都被写入输出文件。 在代码注释方面,要详细解释每个函数的作用,比如lame_init、lame_set_num_channels、lame_set_brate等,以及如何正确使用它们。还要提醒用户处理错误情况,比如文件打开失败或编码初始化失败的情况。 另外,需要提到编译时的链接选项,比如使用-lmp3lame来链接。这对新手来说可能容易忽略,所以必须明确说明。 用户可能遇到的问题包括内存泄漏、参数配置错误或数据格式不匹配,所以在注意事项里要强调释放资源和正确设置参数的重要性。例如,确保在最后调用lame_close来释放资源,避免内存泄漏。 最后,参考之前的引用内容,用户可能对高比特率下的音质感兴趣,可以提到设置更高的比特率如320kbps以获得更好的音质,如引用[3]所述。同时,结合引用[2]中提到的LAME的配置选项,强调参数设置对音质的影响。 总结步骤后,生成相关问题,覆盖编码流程、参数影响、错误处理和应用场景,这样用户可以有进一步的学习方向。需要确保问题与主题相关,并且能够引导用户深入理解libmp3lame使用。</think>### 逐步解答 使用 libmp3lame API 实现 MP3 编码的核心步骤包括**初始化编码器**、**配置参数**、**处理 PCM 数据**和**释放资源**。以下是详细实现流程: --- #### **1. 环境准备** - **安装依赖**: 在 Linux 系统中通过 `sudo apt-get install libmp3lame-dev` 安装开发包; 在 Windows 系统中需手动下载预编译并配置头文件路径和链接路径[^3]。 --- #### **2. 编码流程与示例代码** ```c #include <stdio.h> #include <lame/lame.h> int main() { // 初始化 LAME 编码lame_global_flags *gfp = lame_init(); if (!gfp) { fprintf(stderr, "初始化编码器失败\n"); return -1; } // 配置参数(示例:44.1kHz 立体声 128kbps) lame_set_in_samplerate(gfp, 44100); lame_set_num_channels(gfp, 2); // 2=立体声,1=单声道 lame_set_brate(gfp, 128); // 目标比特率(kbps) lame_set_quality(gfp, 5); // 质量等级(0=最高,9=最低) // 初始化编码器参数 if (lame_init_params(gfp) < 0) { fprintf(stderr, "参数配置失败\n"); lame_close(gfp); return -1; } // 打开输入文件(PCM 数据)和输出文件MP3) FILE *pcm = fopen("input.pcm", "rb"); FILE *mp3 = fopen("output.mp3", "wb"); if (!pcm || !mp3) { fprintf(stderr, "文件打开失败\n"); return -1; } // 缓冲区配置 const int PCM_SIZE = 1152 * 2; // 每帧采样数(LAME 推荐值) const int MP3_SIZE = 1152 * 5; // MP3 缓冲区大小 short pcm_buffer[PCM_SIZE]; unsigned char mp3_buffer[MP3_SIZE]; // 编码循环 int read, write; do { read = fread(pcm_buffer, sizeof(short), PCM_SIZE, pcm); if (read == 0) write = lame_encode_flush(gfp, mp3_buffer, MP3_SIZE); else write = lame_encode_buffer_interleaved(gfp, pcm_buffer, read/2, mp3_buffer, MP3_SIZE); fwrite(mp3_buffer, sizeof(unsigned char), write, mp3); } while (read != 0); // 释放资源 lame_close(gfp); fclose(pcm); fclose(mp3); return 0; } ``` --- #### **3. 关键参数说明** - **采样率与声道数**:需与输入 PCM 数据完全匹配,否则可能产生噪音[^2]; - **比特率模式**:可通过 `lame_set_VBR(gfp, vbr_default)` 启用动态比特率(VBR); - **质量等级**:数值越低,编码时间越长但音质越好[^3]。 --- #### **4. 编译与运行** - **Linux 编译命令**: ```bash gcc encode.c -lmp3lame -o encoder ``` - **Windows 编译**:需在 Visual Studio 中配置 `libmp3lame.lib` 的链接路径。 --- #### **5. 注意事项** 1. **内存管理**:确保调用 `lame_close()` 避免内存泄漏; 2. **数据对齐**:PCM 数据需为 16 位有符号整型(16-bit signed PCM); 3. **错误处理**:检查所有文件操作和 API 返回值。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值