• 常用的开源MP3编解码器


    前言

    由于工作需要,需对MP3进行编解码处理,研究了几款开源的MP3编解码器。相对于FFMPEG来说,这几款都属于轻量级的编解码器,更容易移植。

    LAME

    源码下载链接:https://sourceforge.net/projects/lame/
    支持MP3编解码。编码出来的MP3音色纯厚、空间宽广、低音清晰、细节表现良好,它独创的心理音响模型技术保证了CD音频还原的真实性,配合VBR和ABR参数,音质几乎可以媲美CD音频,但文件体积却非常小,是目前主流的编码器。

    MAD

    MAD(libmad)是一个开源的高精度MPEG音频解码库,支持MPEG-1标准。libmad提供24-bit的PCM输出,完全定点计算,非常适合在没有浮点支持的嵌入式硬件平台上使用。使用libmad提供的一系列API可以实现MP3文件的解码。
    源码下载链接:https://sourceforge.net/projects/mad/
    例程minimad.c是在运行前将整个MP3文件读入内存中进行处理,不适合MP3流未知的场景,需改成边解码边写入MP3的形式,即每次读入1K MP3数据,解码完成再读入1K,又不影响播放的连续性,方便在资源紧张的嵌入式系统中运用。
    libmad中的mad_decoder_run()进行解码时,首先会检测待解码缓冲区中是否存在数据,有则解码,没有则调用input()函数进行装载数据,并返回MAD_FLOW_CONTINUE表示还存在数据,解码完成后调用output()函数进行处理,如此循环…直到input()函数返回MAD_FLOW_STOP表示该MP3数据流已经完全加载,output()函数输出后,表示该MP3文件已完成全部解码操作。
    input()函数如下,每次调用读入FRAME_SIZE_MP3字节数据:

    static
    enum mad_flow input(void *data,
    		    struct mad_stream *stream)
    {
      PT_Mp3Info ptMp3Info = (PT_Mp3Info)data;
      int ret;
      int restLen;   // unprocessed data's size
      int readLen;
    
      if (!feof(fin)) {
        restLen = stream->bufend - stream->next_frame;
        memcpy(ptMp3Info->inMp3, ptMp3Info->inMp3+ptMp3Info->inLen-restLen, restLen);
        readLen = FRAME_SIZE_MP3 - restLen;
        int readn = fread(ptMp3Info->inMp3+restLen, sizeof(char), readLen, fin);
        ptMp3Info->inLen = restLen + readn;
        mad_stream_buffer(stream, ptMp3Info->inMp3, ptMp3Info->inLen);
        ret = MAD_FLOW_CONTINUE;
      }
      else {
        ret = MAD_FLOW_STOP;
      }
    
      return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    完整代码如下:

    
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include "mad.h"
    
    #define FRAME_SIZE_MP3  (1024)
    
    typedef struct _Mp3Info {
      unsigned char inMp3[FRAME_SIZE_MP3];
      unsigned int  inLen;
    }T_Mp3Info, *PT_Mp3Info;
    
    static FILE *fin  = NULL;
    static FILE *fout = NULL;
    static int decode(PT_Mp3Info ptMp3Info);
    
    
    int main(int argc, char *argv[])
    {
      if (argc != 3) {
        printf("%s  \n", argv[0]);
        return -1;
      }
      
      fin  = fopen(argv[1], "r");
      fout = fopen(argv[2], "wb+");
    
      T_Mp3Info tMp3Info; 
      decode(&tMp3Info);
    
      fclose(fin);
      fclose(fout);
    
      return 0;
    }
    
    
    /*
     * This is the input callback. The purpose of this callback is to (re)fill
     * the stream buffer which is to be decoded. In this example, an entire file
     * has been mapped into memory, so we just call mad_stream_buffer() with the
     * address and length of the mapping. When this callback is called a second
     * time, we are finished decoding.
     */
    
    static
    enum mad_flow input(void *data,
    		    struct mad_stream *stream)
    {
      PT_Mp3Info ptMp3Info = (PT_Mp3Info)data;
      int ret;
      int restLen;   // unprocessed data's size
      int readLen;
    
      if (!feof(fin)) {
        restLen = stream->bufend - stream->next_frame;
        memcpy(ptMp3Info->inMp3, ptMp3Info->inMp3+ptMp3Info->inLen-restLen, restLen);
        readLen = FRAME_SIZE_MP3 - restLen;
        int readn = fread(ptMp3Info->inMp3+restLen, sizeof(char), readLen, fin);
        ptMp3Info->inLen = restLen + readn;
        mad_stream_buffer(stream, ptMp3Info->inMp3, ptMp3Info->inLen);
        ret = MAD_FLOW_CONTINUE;
      }
      else {
        ret = MAD_FLOW_STOP;
      }
    
      return ret;
    }
    
    /*
     * The following utility routine performs simple rounding, clipping, and
     * scaling of MAD's high-resolution samples down to 16 bits. It does not
     * perform any dithering or noise shaping, which would be recommended to
     * obtain any exceptional audio quality. It is therefore not recommended to
     * use this routine if high-quality output is desired.
     */
    
    static inline
    signed int scale(mad_fixed_t sample)
    {
      /* round */
      sample += (1L << (MAD_F_FRACBITS - 16));
    
      /* clip */
      if (sample >= MAD_F_ONE)
        sample = MAD_F_ONE - 1;
      else if (sample < -MAD_F_ONE)
        sample = -MAD_F_ONE;
    
      /* quantize */
      return sample >> (MAD_F_FRACBITS + 1 - 16);
    }
    
    /*
     * This is the output callback function. It is called after each frame of
     * MPEG audio data has been completely decoded. The purpose of this callback
     * is to output (or play) the decoded PCM audio.
     */
    
    static
    enum mad_flow output(void *data,
    		     struct mad_header const *header,
    		     struct mad_pcm *pcm)
    {
      unsigned int nchannels, nsamples;
      mad_fixed_t const *left_ch, *right_ch;
    
      /* pcm->samplerate contains the sampling frequency */
    
      nchannels = pcm->channels;
      nsamples  = pcm->length;
      left_ch   = pcm->samples[0];
      right_ch  = pcm->samples[1];
    
      while (nsamples--) {
        signed int sample;
    
        /* output sample(s) in 16-bit signed little-endian PCM */
    
        sample = scale(*left_ch++);
        char high = (sample >> 0) & 0xff;
        char low  = (sample >> 8) & 0xff;
    //    putchar((sample >> 0) & 0xff);
    //    putchar((sample >> 8) & 0xff);
        fwrite(&high, sizeof(char), 1, fout);
        fwrite(&low, sizeof(char), 1, fout);
    
        if (nchannels == 2) {
          sample = scale(*right_ch++);
    //      putchar((sample >> 0) & 0xff);
    //      putchar((sample >> 8) & 0xff);
          high = (sample >> 0) & 0xff;
          low  = (sample >> 8) & 0xff;
          fwrite(&high, sizeof(char), 1, fout);
          fwrite(&low, sizeof(char), 1, fout);
        }
        
      }
    
      return MAD_FLOW_CONTINUE;
    }
    
    /*
     * This is the error callback function. It is called whenever a decoding
     * error occurs. The error is indicated by stream->error; the list of
     * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
     * header file.
     */
    
    static
    enum mad_flow error(void *data,
    		    struct mad_stream *stream,
    		    struct mad_frame *frame)
    {
      PT_Mp3Info ptMp3Info = (PT_Mp3Info)data;
    
      fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %lu\n",
    	  stream->error, mad_stream_errorstr(stream),
    	  stream->this_frame - ptMp3Info->inMp3);
    
      /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */
    
      return MAD_FLOW_CONTINUE;
    }
    
    /*
     * This is the function called by main() above to perform all the decoding.
     * It instantiates a decoder object and configures it with the input,
     * output, and error callback functions above. A single call to
     * mad_decoder_run() continues until a callback function returns
     * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
     * signal an error).
     */
    
    static
    int decode(PT_Mp3Info ptMp3Info)
    {
      struct mad_decoder decoder;
      int result;
    
      if (ptMp3Info == NULL) {
        printf("ptMp3Info is NULL\n");
        return -1;
      }
    
      /* configure input, output, and error functions */
    
      mad_decoder_init(&decoder, ptMp3Info,
    		   input, 0 /* header */, 0 /* filter */, output,
    		   error, 0 /* message */);
    
      /* start decoding */
    
      result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
    
      /* release the decoder */
    
      mad_decoder_finish(&decoder);
    
      return result;
    }
    
    
    • 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
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207

    tinymp3

    支持MP3编解码,代码量少,适合在单片机上移植。
    源码下载链接:https://github.com/cpuimage/tinymp3

    minimp3

    仅支持MP3解码,只有一个头文件,适合在单片机上移植。
    源码下载链接:https://github.com/lieff/minimp3
    minimp3的使用只需调用一个函数即可实现解码

    int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_sample_t *pcm, mp3dec_frame_info_t *info);
    
    • 1

    消耗的 MP3 数据的大小在定义的mp3dec_frame_info_t结构中的frame_bytes字段中返回,必须在下一次解码器调用之前从输入缓冲区中删除对应于 frame_bytes 字段的数据。
    解码函数返回已解码样本的数量samples。可能出现以下情况:
    0: 在输入缓冲区中未找到 MP3 数据
    384: Layer 1
    576: MPEG 2 Layer 3
    1152: Otherwise

    samples 和 frame_bytes 字段值:
    samples > 0 和 frame_bytes > 0: 成功解码
    samples == 0 和 frame_bytes > 0: 解码器跳过了 ID3 或无效数据
    samples == 0 和 frame_bytes == 0: 数据不足

    参考代码如下:

    #include 
    #include 
    #include 
    #include 
    
    
    #define MINIMP3_IMPLEMENTATION 
    #include "minimp3.h"
    
    int main(int argc, char *argv[])
    {
    	unsigned char *inMp3 = NULL;
    	int totalLen = 0;
    	
    	if (argc != 3) {
    		printf("%s  \n", argv[0]);
    		return -1;
    	}
    	//打开MP3文件
    	FILE* fin = fopen(argv[1], "r");
    
    	//获取MP3文件长度
    	fseek(fin, 0, SEEK_END);
    	totalLen = (int)ftell(fin);
    
    	//读取整个MP3文件
    	fseek(fin, 0, SEEK_SET);
    	inMp3 = malloc(totalLen);
    	fread(inMp3, 1, totalLen, fin);
        fclose(fin);
    	
    	//定义mp3dec_frame_info_t
    	mp3dec_frame_info_t info;
    	short outPcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
    	int inLen = 0;
    
    	//逐帧解码
    	int samples = mp3dec_decode_frame(&mp3d, inMp3, totalLen, outPcm, &info);
    	while(samples) {
    		fwrite(outPcm, sizeof(short), samples, fout);
    		inLen += info.frame_bytes;
    		samples = mp3dec_decode_frame(&mp3d, inMp3 + inLen, totalLen - inLen, outPcm, &info);
    	}
        
    	free(inMp3);
    	inMp3 = NULL;
    	
    	fclose(fout);
    	
    	return 0;
    }
    
    
    • 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
  • 相关阅读:
    SSE图像算法优化系列三十二:Zhang\Guo图像细化算法的C语言以及SIMD指令优化
    云服务器ECS价格表出炉——阿里云
    数学建模——相关系数
    探索AI作画算法的原理:从深度学习到创造性艺术
    「动态规划dp」
    方太集团合同档案管理平台,让数字化成果深度利用、可查可验
    【博客545】从交换机视角看四种报文:广播、组播、未知单播、已知单播
    2022最新分享:企业级Android Framework学习路线,打破面试“面试造火箭,入厂拧螺丝“难点
    第6章:MATLAB文本数据处理进阶篇的目录 (MATLAB入门课程)
    【常识】回调函数
  • 原文地址:https://blog.csdn.net/liang_zhaocong/article/details/127828585