基于libmad的MP3解码播放器

     libmad:是一个开源的高精度mpeg音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中,可以看到绝大部分该库的数据结构和 API 等。

     PCM编码:即为脉冲代码调制编码。
PCM通过抽样,量化,编码三个步骤将连续的模拟信号转换成数字编码。

libmad中的主要数据结构:

主要数据结构 作用
struct mad_stream 存放解码前的Bitstream数据
struct mad_synth 存放解码合成滤波后的PCM数据
struct mad_pcm 定义了音频的采样率,声道个数和PCM采样数据,用来初始化音频
struct mad_frame 记录MPEG帧解码后PCM数据的数据结构,其中的mad_header用来记录MPEG帧的基本信息,比如MPEG层数、声道模式、流比特率、采样比特率。声道模式包括单声道、双声道、联合立体混音道以及一般立体声。

      MAD通过回调函数机制来实现解码,每个回调函数会返回一个枚举类型mad_flow,通过mad_flow可以控制解码的过程。在未经处理的情况下,MAD一般输出32bit,以little endian格式存放在mad_fixed_t中的数据。但是大多数的声卡并能支持输出高达32bit精度的数据,因而还必须对mad_fixed_t进行量化,圆滑处理以及抖动,使到采样信号降到16bit精度。MAD负责的只是解码的过程,它工作过程是:从外部获取输入,逐帧解码,在解码的过程中返回信息,然后得到解码结果。开发人员要手动设置输入输出。


在libmad中提供了一个解码源程序minimad.c,实现了将MP3文件解码成pcm数据,并将其数据显示在终端上。


     现在就以该源码程序为例,来写出我们自己的基于libmad的MP3播放器。
在我们打开我们的音频程序之时同时也打开我们的音频设备"/dev/dsp"。

 


  1. static int sfd;  
  2. if((sfd = open("/dev/dsp", O_WRONLY)) < 0)   
  3. {  
  4.     printf("can not open device!!!/n");  
  5.     return 1;  

    一般来说,我们的MP3文件都是立体音,有2个声道,由于要把pcm采样后并处理的数据放入一个char型的数组,而并行的左右声道的每个采样要在字符数组中处理成2个,所以字符数组中的数据的个数应该是pcm音频采样数的4倍。又因为把左右声道的数据合在一个字符数组里串行处理,所以播放的速度应该是pcm音频采样率的两倍。


  1. static 
  2. enum mad_flow output(void *data,  
  3.              struct mad_header const *header, struct mad_pcm *pcm)  
  4. {  
  5.     unsigned int nchannels, nsamples, n;  
  6.     mad_fixed_t const *left_ch, *right_ch;  
  7.     unsigned char Output[6912], *OutputPtr;  
  8.     int fmt, wrote, speed;  
  9.  
  10.     nchannels = pcm->channels;  
  11.     n = nsamples = pcm->length;  
  12.     left_ch = pcm->samples[0];  
  13.     right_ch = pcm->samples[1];  
  14.  
  15.     fmt = AFMT_S16_LE;  
  16.     speed = pcm->samplerate * 2;    /*播放速度是采样率的两倍 */ 
  17.     ioctl(sfd, SNDCTL_DSP_SPEED, &(speed));  
  18.     ioctl(sfd, SNDCTL_DSP_SETFMT, &fmt);  
  19.     ioctl(sfd, SNDCTL_DSP_CHANNELS, &(pcm->channels));  
  20.     OutputPtr = Output;  
  21.     while (nsamples--) {  
  22.     signed int sample;  
  23.     sample = scale(*left_ch++);  
  24.     *(OutputPtr++) = sample >> 0;  
  25.     *(OutputPtr++) = sample >> 8;  
  26.     if (nchannels == 2) {  
  27.         sample = scale(*right_ch++);  
  28.         *(OutputPtr++) = sample >> 0;  
  29.         *(OutputPtr++) = sample >> 8;  
  30.     }  
  31.     }  
  32.     n *= 4;         /*数据长度为pcm音频采样的4倍 */ 
  33.     OutputPtr = Output;  
  34.     while (n) {  
  35.     wrote = write(sfd, OutputPtr, n);  
  36.     OutputPtr += wrote;  
  37.     n -= wrote;  
  38.     }  
  39.     OutputPtr = Output;  
  40.     return MAD_FLOW_CONTINUE;  
这样就可以实现我们的播放器了.....

下面就以一个简单的实例来说明问题:

 


  1. # include <stdio.h>  
  2. # include <stdlib.h>  
  3. # include <unistd.h>  
  4. # include <sys/stat.h>  
  5. # include <sys/mman.h>  
  6. # include <sys/soundcard.h>  
  7. # include <sys/ioctl.h>  
  8. # include <sys/fcntl.h>  
  9. # include <sys/types.h>  
  10. # include <mad.h>  
  11. struct buffer {  
  12.     unsigned char const *start;  
  13.     unsigned long length;  
  14. };  
  15. static int sfd;         /*声音设备的描述符 */ 
  16. static int decode(unsigned char const *, unsigned long);  
  17. int main(int argc, char *argv[])  
  18. {  
  19.     struct stat stat;  
  20.     void *fdm;  
  21.     char const *file;  
  22.     int fd;  
  23.     file = argv[1];  
  24.     fd = open(file, O_RDONLY);  
  25.     if ((sfd = open("/dev/dsp", O_WRONLY)) < 0) {  
  26.     printf("can not open device!!!/n");  
  27.     return 5;  
  28.     }  
  29.     ioctl(sfd, SNDCTL_DSP_SYNC, 0); /*此句可以不要 */ 
  30.     if (fstat(fd, &stat) == -1 || stat.st_size == 0)  
  31.     return 2;  
  32.     fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);  
  33.     if (fdm == MAP_FAILED)  
  34.     return 3;  
  35.     decode(fdm, stat.st_size);  
  36.     if (munmap(fdm, stat.st_size) == -1)  
  37.     return 4;  
  38.     ioctl(sfd, SNDCTL_DSP_RESET, 0);  
  39.     close(sfd);  
  40.     return 0;  
  41. }  
  42. static 
  43. enum mad_flow input(void *data, struct mad_stream *stream)  
  44. {  
  45.     struct buffer *buffer = data;  
  46.     if (!buffer->length)  
  47.     return MAD_FLOW_STOP;  
  48.     mad_stream_buffer(stream, buffer->start, buffer->length);  
  49.     buffer->length = 0;  
  50.     return MAD_FLOW_CONTINUE;  
  51. }  
  52. /*这一段是处理采样后的pcm音频 */ 
  53. static inline signed int scale(mad_fixed_t sample)  
  54. {  
  55.     sample += (1L << (MAD_F_FRACBITS - 16));  
  56.     if (sample >= MAD_F_ONE)  
  57.     sample = MAD_F_ONE - 1;  
  58.     else if (sample < -MAD_F_ONE)  
  59.     sample = -MAD_F_ONE;  
  60.     return sample >> (MAD_F_FRACBITS + 1 - 16);  
  61. }  
  62. static 
  63. enum mad_flow output(void *data,  
  64.              struct mad_header const *header, struct mad_pcm *pcm)  
  65. {  
  66.     unsigned int nchannels, nsamples, n;  
  67.     mad_fixed_t const *left_ch, *right_ch;  
  68.     unsigned char Output[6912], *OutputPtr;  
  69.     int fmt, wrote, speed;  
  70.  
  71.     nchannels = pcm->channels;  
  72.     n = nsamples = pcm->length;  
  73.     left_ch = pcm->samples[0];  
  74.     right_ch = pcm->samples[1];  
  75.  
  76.     fmt = AFMT_S16_LE;  
  77.     speed = pcm->samplerate * 2;    /*播放速度是采样率的两倍 */ 
  78.     ioctl(sfd, SNDCTL_DSP_SPEED, &(speed));  
  79.     ioctl(sfd, SNDCTL_DSP_SETFMT, &fmt);  
  80.     ioctl(sfd, SNDCTL_DSP_CHANNELS, &(pcm->channels));  
  81.     OutputPtr = Output;  
  82.     while (nsamples--) {  
  83.     signed int sample;  
  84.     sample = scale(*left_ch++);  
  85.     *(OutputPtr++) = sample >> 0;  
  86.     *(OutputPtr++) = sample >> 8;  
  87.     if (nchannels == 2) {  
  88.         sample = scale(*right_ch++);  
  89.         *(OutputPtr++) = sample >> 0;  
  90.         *(OutputPtr++) = sample >> 8;  
  91.     }  
  92.     }  
  93.     n *= 4;         /*数据长度为pcm音频采样的4倍 */ 
  94.     OutputPtr = Output;  
  95.     while (n) {  
  96.     wrote = write(sfd, OutputPtr, n);  
  97.     OutputPtr += wrote;  
  98.     n -= wrote;  
  99.     }  
  100.     OutputPtr = Output;  
  101.     return MAD_FLOW_CONTINUE;  
  102. }  
  103.  
  104. static 
  105. enum mad_flow error(void *data,  
  106.             struct mad_stream *stream, struct mad_frame *frame)  
  107. {  
  108.     return MAD_FLOW_CONTINUE;  
  109. }  
  110.  
  111. static 
  112. int decode(unsigned char const *start, unsigned long length)  
  113. {  
  114.     struct buffer buffer;  
  115.     struct mad_decoder decoder;  
  116.     int result;  
  117.     buffer.start = start;  
  118.     buffer.length = length;  
  119.     mad_decoder_init(&decoder, &buffer, input, 0, 0, output, error, 0);  
  120.     mad_decoder_options(&decoder, 0);  
  121.     result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);  
  122.     mad_decoder_finish(&decoder);  
  123.     return result;  
  124. }  

 



     本文转自 驿落黄昏 51CTO博客,原文链接:http://blog.51cto.com/yiluohuanghun/867922,如需转载请自行联系原作者



上一篇:第一个 spring Boot 应用通过Docker 来实现构建、运行、发布


下一篇:《iOS 6高级开发手册(第4版)》——2.3节秘诀:监测Documents文件夹