win mobile 5播放mp3音乐的方法(2)--libmad库的使用篇

win mobile 5播放mp3音乐的方法(2)--libmad库的使用篇  

2010-07-16 15:14:26|  分类: wince |  标签: |字号 订阅


既然libmad已经可以顺利的移植到win mobile平台上了,那么如何使用这个libmad库呢?很遗憾,linux平台下面对于libmad的文档描述不是非常清晰。呵呵,按照他们的思维就是一切都在代码里面了。但是对于普通的开发者来说,可能只是涉及到对库的使用,而不是为了使用一个库去学习mp3的编码、解码原理(如果能够通过这个库弄明白了这些,自然再好不过了)。

我仔细的查看了一下这个libmad的相关开源工程。发现了一个很有趣的项目,它就是——madlld-1.1p1(我下载的最新版本,不知道现在是否又有更新的版本出来了)可以google一下,然后直接下载源代码。这个项目的说明如下:
This program is a "how to" about libmad's low-level API. The intended use of this program is to serve as tutorial or demonstration for programmers wiling to learn how the currently undocumented library API can be used. As a side effect the program can be used to decode MPEG audio streams to raw PCM samples.
这个项目本身就是做为一个向导或者入门的文件,教会大家如何使用libmad的底层API函数的。因此,把这个项目的代码拿过来仔细分析一下,libmad库的使用方法就变得非常简单了。它里面的代码写得非常不错,很精炼,可以很容易的移植到win mobile平台上去。madlld项目的核心部分由3个.c文件组成,最关键的代码在madlld.c这个文件中的MpegAudioDecoder函数,该函数的定义如下:
 
static int MpegAudioDecoder(FILE *InputFp, FILE *OutputFp)
 
现在就把libmad库的API核心部分使用的代码贴出来,简单分析一下备忘:
       
       
  1. // 这里用8K的输出缓冲区,这对于mp3转pcm16仅限于文件操作的程序来说是足够的 
  2. // 但是对于编解码边播放的应用场合而言就太小了,根本不够 
  3. #define INPUT_BUFFER_SIZE (5*8192) 
  4. #define OUTPUT_BUFFER_SIZE 8192 /* Must be an integer multiple of 4. */ 
  5. static int MpegAudioDecoder(FILE *InputFp, FILE *OutputFp) 
  6. struct mad_stream Stream; 
  7. struct mad_frame Frame; 
  8. struct mad_synth Synth; 
  9. mad_timer_t    Timer; 
  10. unsigned char   InputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD], 
  11.        OutputBuffer[OUTPUT_BUFFER_SIZE], 
  12.        *OutputPtr=OutputBuffer, 
  13.        *GuardPtr=NULL; 
  14. const unsigned char *OutputBufferEnd=OutputBuffer+OUTPUT_BUFFER_SIZE; 
  15. int      Status=0, 
  16.        i; 
  17. unsigned long   FrameCount=0; 
  18. bstdfile_t    *BstdFile; 
  19. /* First the structures used by libmad must be initialized. */ 
  20. mad_stream_init(&Stream); 
  21. mad_frame_init(&Frame); 
  22. mad_synth_init(&Synth); 
  23. mad_timer_reset(&Timer); 
  24. /* Decoding options can here be set in the options field of the 
  25. * Stream structure. 
  26. */ 
  27. /* {1} When decoding from a file we need to know when the end of 
  28. * the file is reached at the same time as the last bytes are read 
  29. * (see also the comment marked {3} bellow). Neither the standard 
  30. * C fread() function nor the POSIX read() system call provides 
  31. * this feature. We thus need to perform our reads through an 
  32. * interface having this feature, this is implemented here by the 
  33. * bstdfile.c module. 
  34. */ 
  35. // 这里说了那么多其实就是想要一个文件读取函数,在读取的同时知道是否到达文件末尾。 
  36. // 普通的fread是不行的,只有当前读取是否成功,对于在当前的读取完成后是否到达文件 
  37. // 末尾没有任何提示。所以这里采用了自己封装的文件操作函数。 
  38. BstdFile=NewBstdFile(InputFp); 
  39. if(BstdFile==NULL) 
  40.    fprintf(stderr,"%s: can't create a new bstdfile_t (%s).\n", 
  41.      ProgName,strerror(errno)); 
  42.    return(1); 
  43. /* This is the decoding loop. */ 
  44. do 
  45.    /* The input bucket must be filled if it becomes empty or if 
  46.    * it's the first execution of the loop. 
  47.    */ 
  48.    if(Stream.buffer==NULL || Stream.error==MAD_ERROR_BUFLEN) 
  49.    { 
  50.     size_t    ReadSize, 
  51.         Remaining; 
  52.     unsigned char *ReadStart; 
  53.     /* {2} libmad may not consume all bytes of the input 
  54.     * buffer. If the last frame in the buffer is not wholly 
  55.     * contained by it, then that frame's start is pointed by 
  56.     * the next_frame member of the Stream structure. This 
  57.     * common situation occurs when mad_frame_decode() fails, 
  58.     * sets the stream error code to MAD_ERROR_BUFLEN, and 
  59.     * sets the next_frame pointer to a non NULL value. (See 
  60.     * also the comment marked {4} bellow.) 
  61.     * 
  62.     * When this occurs, the remaining unused bytes must be 
  63.     * put back at the beginning of the buffer and taken in 
  64.     * account before refilling the buffer. This means that 
  65.     * the input buffer must be large enough to hold a whole 
  66.     * frame at the highest observable bit-rate (currently 448 
  67.     * kb/s). XXX=XXX Is 2016 bytes the size of the largest 
  68.     * frame? (448000*(1152/32000))/8 
  69.     */ 
  70.     if(Stream.next_frame!=NULL) 
  71.     { 
  72.      // 这里使用了内存分块使用的方法,写得非常精练 
  73.      // 主要用于处理当前数据不够一帧,在读入新数据的时候 
  74.      // 要把没有用完的数据放到开头,然后接着读入新的数据 
  75.      // 编写过网络程序的朋友应该一目了然了,这就是数据包的 
  76.      // “拼接”操作嘛 
  77.      Remaining=Stream.bufend-Stream.next_frame; 
  78.      memmove(InputBuffer,Stream.next_frame,Remaining); 
  79.      ReadStart=InputBuffer+Remaining; 
  80.      ReadSize=INPUT_BUFFER_SIZE-Remaining; 
  81.     } 
  82.     else 
  83.      ReadSize=INPUT_BUFFER_SIZE, 
  84.       ReadStart=InputBuffer, 
  85.       Remaining=0; 
  86.     /* Fill-in the buffer. If an error occurs print a message 
  87.     * and leave the decoding loop. If the end of stream is 
  88.     * reached we also leave the loop but the return status is 
  89.     * left untouched. 
  90.     */ 
  91.     // 读取数据,没什么可说的,读取的长度如果小于等于零,就报错 
  92.     ReadSize=BstdRead(ReadStart,1,ReadSize,BstdFile); 
  93.     if(ReadSize<=0) 
  94.     { 
  95.      if(ferror(InputFp)) 
  96.      { 
  97.       fprintf(stderr,"%s: read error on bit-stream (%s)\n", 
  98.         ProgName,strerror(errno)); 
  99.       Status=1; 
  100.      } 
  101.      if(feof(InputFp)) 
  102.       fprintf(stderr,"%s: end of input stream\n",ProgName); 
  103.      break; 
  104.     } 
  105.     /* {3} When decoding the last frame of a file, it must be 
  106.     * followed by MAD_BUFFER_GUARD zero bytes if one wants to 
  107.     * decode that last frame. When the end of file is 
  108.     * detected we append that quantity of bytes at the end of 
  109.     * the available data. Note that the buffer can't overflow 
  110.     * as the guard size was allocated but not used the the 
  111.     * buffer management code. (See also the comment marked 
  112.     * {1}.) 
  113.     * 
  114.     * In a message to the mad-dev mailing list on May 29th, 
  115.     * 2001, Rob Leslie explains the guard zone as follows: 
  116.     * 
  117.     *    "The reason for MAD_BUFFER_GUARD has to do with the 
  118.     *    way decoding is performed. In Layer III, Huffman 
  119.     *    decoding may inadvertently read a few bytes beyond 
  120.     *    the end of the buffer in the case of certain invalid 
  121.     *    input. This is not detected until after the fact. To 
  122.     *    prevent this from causing problems, and also to 
  123.     *    ensure the next frame's main_data_begin pointer is 
  124.     *    always accessible, MAD requires MAD_BUFFER_GUARD 
  125.     *    (currently 8) bytes to be present in the buffer past 
  126.     *    the end of the current frame in order to decode the 
  127.     *    frame." 
  128.     */ 
  129.     // 这里是判断文件是否结束,并且加入所谓的MAD_BUFFER_GUARD 
  130.     // 用于明确地告诉解码器,已经没有数据了 
  131.     if(BstdFileEofP(BstdFile)) 
  132.     { 
  133.      GuardPtr=ReadStart+ReadSize; 
  134.      memset(GuardPtr,0,MAD_BUFFER_GUARD); 
  135.      ReadSize+=MAD_BUFFER_GUARD; 
  136.     } 
  137.     /* Pipe the new buffer content to libmad's stream decoder 
  138.              * facility. 
  139.     */ 
  140.     mad_stream_buffer(&Stream,InputBuffer,ReadSize+Remaining); 
  141.     Stream.error=0; 
  142.    } 
  143.    /* Decode the next MPEG frame. The streams is read from the 
  144.    * buffer, its constituents are break down and stored the the 
  145.    * Frame structure, ready for examination/alteration or PCM 
  146.    * synthesis. Decoding options are carried in the Frame 
  147.    * structure from the Stream structure. 
  148.    * 
  149.    * Error handling: mad_frame_decode() returns a non zero value 
  150.    * when an error occurs. The error condition can be checked in 
  151.    * the error member of the Stream structure. A mad error is 
  152.    * recoverable or fatal, the error status is checked with the 
  153.    * MAD_RECOVERABLE macro. 
  154.    * 
  155.    * {4} When a fatal error is encountered all decoding 
  156.    * activities shall be stopped, except when a MAD_ERROR_BUFLEN 
  157.    * is signaled. This condition means that the 
  158.    * mad_frame_decode() function needs more input to complete 
  159.    * its work. One shall refill the buffer and repeat the 
  160.    * mad_frame_decode() call. Some bytes may be left unused at 
  161.    * the end of the buffer if those bytes forms an incomplete 
  162.    * frame. Before refilling, the remaining bytes must be moved 
  163.    * to the beginning of the buffer and used for input for the 
  164.    * next mad_frame_decode() invocation. (See the comments 
  165.    * marked {2} earlier for more details.) 
  166.    * 
  167.    * Recoverable errors are caused by malformed bit-streams, in 
  168.    * this case one can call again mad_frame_decode() in order to 
  169.    * skip the faulty part and re-sync to the next frame. 
  170.    */ 
  171.    // 这里就是真正的解码部分了,含有对错误的处理方法 
  172.    if(mad_frame_decode(&Frame,&Stream)) 
  173.    { 
  174.     if(MAD_RECOVERABLE(Stream.error)) 
  175.     { 
  176.      /* Do not print a message if the error is a loss of 
  177.      * synchronization and this loss is due to the end of 
  178.      * stream guard bytes. (See the comments marked {3} 
  179.      * supra for more informations about guard bytes.) 
  180.      */ 
  181.      if(Stream.error!=MAD_ERROR_LOSTSYNC || 
  182.        Stream.this_frame!=GuardPtr) 
  183.      { 
  184.       fprintf(stderr,"%s: recoverable frame level error (%s)\n", 
  185.         ProgName,MadErrorString(&Stream)); 
  186.       fflush(stderr); 
  187.      } 
  188.      continue; 
  189.     } 
  190.     else 
  191.      // 这里的这个错误其实不是错误,只是当前帧的数据不完整,需要再次读入 
  192.      // 剩下的部分与新读入的部分“拼接”成为一个完整的帧再解码 
  193.      if(Stream.error==MAD_ERROR_BUFLEN) 
  194.       continue; 
  195.      else 
  196.      { 
  197.       fprintf(stderr,"%s: unrecoverable frame level error (%s).\n", 
  198.         ProgName,MadErrorString(&Stream)); 
  199.       Status=1; 
  200.       break; 
  201.      } 
  202.    } 
  203.    /* The characteristics of the stream's first frame is printed 
  204.    * on stderr. The first frame is representative of the entire 
  205.    * stream. 
  206.    */ 
  207.    if(FrameCount==0) 
  208.     // 这里的PrintFrameInfo用于在解码器接触第一帧的时候,输出一些 
  209.     // mp3的采样率、通道个数之类的信息用的 
  210.     if(PrintFrameInfo(stderr,&Frame.header)) 
  211.     { 
  212.      Status=1; 
  213.      break; 
  214.     } 
  215.    /* Accounting. The computed frame duration is in the frame 
  216.    * header structure. It is expressed as a fixed point number 
  217.    * whole data type is mad_timer_t. It is different from the 
  218.    * samples fixed point format and unlike it, it can't directly 
  219.    * be added or subtracted. The timer module provides several 
  220.    * functions to operate on such numbers. Be careful there, as 
  221.    * some functions of libmad's timer module receive some of 
  222.    * their mad_timer_t arguments by value! 
  223.    */ 
  224.    FrameCount++; 
  225.    mad_timer_add(&Timer,Frame.header.duration); 
  226.    /* Between the frame decoding and samples synthesis we can 
  227.    * perform some operations on the audio data. We do this only 
  228.    * if some processing was required. Detailed explanations are 
  229.    * given in the ApplyFilter() function. 
  230.    */ 
  231.    // 这里是自定义filter用的,可以用于混音或者特殊音效之类的操作 
  232.    // 如果只是单纯的mp3解码的话,这个filter完全可以不用 
  233.    if(DoFilter) 
  234.     ApplyFilter(&Frame); 
  235.    /* Once decoded the frame is synthesized to PCM samples. No errors 
  236.    * are reported by mad_synth_frame(); 
  237.    */ 
  238.    mad_synth_frame(&Synth,&Frame); 
  239.    /* Synthesized samples must be converted from libmad's fixed 
  240.    * point number to the consumer format. Here we use unsigned 
  241.    * 16 bit big endian integers on two channels. Integer samples 
  242.    * are temporarily stored in a buffer that is flushed when 
  243.    * full. 
  244.    */ 
  245.    // 这里就是把short类型数据存入output缓冲区 
  246.    for(i=0;i<Synth.pcm.length;i++) 
  247.    { 
  248.     signed short Sample; 
  249.     /* Left channel */ 
  250.     Sample=MadFixedToSshort(Synth.pcm.samples[0][i]); 
  251.     // ---------> 这里需要特别注意endian的问题 
  252.     *(OutputPtr++)=Sample>>8;   
  253.     *(OutputPtr++)=Sample&0xff; 
  254.     /* Right channel. If the decoded stream is monophonic then 
  255.     * the right output channel is the same as the left one. 
  256.     */ 
  257.     if(MAD_NCHANNELS(&Frame.header)==2) 
  258.      Sample=MadFixedToSshort(Synth.pcm.samples[1][i]); 
  259.     // --------->这里需要特别注意endian的问题 
  260.     *(OutputPtr++)=Sample>>8;   
  261.     *(OutputPtr++)=Sample&0xff; 
  262.     /* Flush the output buffer if it is full. */ 
  263.     if(OutputPtr==OutputBufferEnd) 
  264.     { 
  265.      // 这里就是把结果写入pcm文件了,直接的raw数据写入 
  266.      if(fwrite(OutputBuffer,1,OUTPUT_BUFFER_SIZE,OutputFp)!=OUTPUT_BUFFER_SIZE) 
  267.      { 
  268.       fprintf(stderr,"%s: PCM write error (%s).\n", 
  269.         ProgName,strerror(errno)); 
  270.       Status=2; 
  271.       break; 
  272.      } 
  273.      OutputPtr=OutputBuffer; 
  274.     } 
  275.    } 
  276. }while(1); 
  277. /* The input file was completely read; the memory allocated by our 
  278. * reading module must be reclaimed. 
  279. */ 
  280. BstdFileDestroy(BstdFile); 
  281. /* Mad is no longer used, the structures that were initialized must 
  282.      * now be cleared. 
  283. */ 
  284. mad_synth_finish(&Synth); 
  285. mad_frame_finish(&Frame); 
  286. mad_stream_finish(&Stream); 
  287. /* If the output buffer is not empty and no error occurred during 
  288.      * the last write, then flush it. 
  289. */ 
  290. if(OutputPtr!=OutputBuffer && Status!=2) 
  291.    size_t BufferSize=OutputPtr-OutputBuffer; 
  292.    // 这里就是如果还剩下一部分数据,但是mp3文件又结束了 
  293.    // 就在这个地方把已经解出来的数据写入到pcm文件中,从这里可以看出 
  294.    // 作者做程序态度是很严谨的,hoho,其实这块数据丢掉,一般人听不出来 
  295.    if(fwrite(OutputBuffer,1,BufferSize,OutputFp)!=BufferSize) 
  296.    { 
  297.     fprintf(stderr,"%s: PCM write error (%s).\n", 
  298.       ProgName,strerror(errno)); 
  299.     Status=2; 
  300.    } 
  301. /* Accounting report if no error occurred. */ 
  302. if(!Status) 
  303.    char Buffer[80]; 
  304.    /* The duration timer is converted to a human readable string 
  305.    * with the versatile, but still constrained mad_timer_string() 
  306.    * function, in a fashion not unlike strftime(). The main 
  307.    * difference is that the timer is broken into several 
  308.    * values according some of it's arguments. The units and 
  309.    * fracunits arguments specify the intended conversion to be 
  310.    * executed. 
  311.    * 
  312.    * The conversion unit (MAD_UNIT_MINUTES in our example) also 
  313.    * specify the order and kind of conversion specifications 
  314.    * that can be used in the format string. 
  315.    * 
  316.    * It is best to examine libmad's timer.c source-code for details 
  317.    * of the available units, fraction of units, their meanings, 
  318.    * the format arguments, etc. 
  319.    */ 
  320.    // 最后输出一下总结信息,例如共解压了多少帧,用了多长时间之类的 
  321.    mad_timer_string(Timer,Buffer,"%lu:%02lu.%03u", 
  322.        MAD_UNITS_MINUTES,MAD_UNITS_MILLISECONDS,0); 
  323.    fprintf(stderr,"%s: %lu frames decoded (%s).\n", 
  324.      ProgName,FrameCount,Buffer); 
  325. /* That's the end of the world (in the H. G. Wells way). */ 
  326. return(Status); 
注意需要特别说明的地方是在代码中,我做了标记的地方:
// ---------> 这里需要特别注意endian的问题
*(OutputPtr++)=Sample>>8;  
*(OutputPtr++)=Sample&0xff;
在win mobile开发中,这里需要特别注意,如果直接用这个在unix下面开发的代码的话,它解码出来的pcm码流就是little endian的,而对于win mobile的waveout api来说,他们需要big endian的(呵呵,对啦,就是这个问题害得我用了整整一个上午的时间来调试各个环节,总算找到了!!)
同样的解码文件在little endian和big endian的波形显示效果如下图所示:
显然,只有把output_buffer中的little endian数据改为big endian才能够被win mobile接受。
修改的方法其实很简单(呵呵,找到这个问题还是很复杂的!汗。。。)
// ---------> 这里需要特别注意endian的问题
*(OutputPtr++)=Sample&0xff;
*(OutputPtr++)=Sample>>8;
把这两句前后位置修改一下即可!
哈哈,简单吧!!libmad学会使用以后,就可以很方便的把mp3文件转换成为pcm码流。下一篇就是如何在win mobile 5里面播放pcm码流的方法了。
加油,还差一步就可以通过自己的程序在win mobile上面播放mp3文件了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值