原创 基于SAM3U + CooCox CoOS + UCGUI的MP3设计(2)

2010-4-26 19:04 2365 7 7 分类: MCU/ 嵌入式

之前介绍了MP3的硬件平台,下面介绍它的软件设计方案。


首先介绍一下MP3最核心的功能:播放WAVE文件和MP3文件,这两种文件采用不同的方式进行播放。


1. 播放WAVE文件。


WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个字节便是“RIFF”WAVE文件由文件头和数据体两大部分组成。其中文件头又分为RIFFWAV文件 标识段和声音数据格式说明段两部分。


MP3 Player设计实例中,通过读取SD卡里的WAVE文件进行播放,关于具体如何读取SD卡里面的文件,这里不详细介绍。


我们在从SD卡中读取了WAVE文件后,第一步就要对WAVE文件头进行分析,从中得出该WAVE文件的采样频率,位率,通道数,文件长度等等。


WAVE文件头说明表:  
  偏移地址     字节数    数据类型          
  00H                4            char             "RIFF"标志
  04H                4          long int           文件长度
  08H                4            char              "WAVE"标志
  0CH               4            char              "fmt"标志
  10H               4                              过渡字节(不定)
  14H               2             int                格式类别(10HPCM形式的声音数据)
  16H               2             int                通道数,单声道为1,双声道为2
  18H               2             int                采样率(每秒样本数),表示每个通道的播放速度,
  1CH              4          long int           波形音频数据传送速率,其值为通道数×每秒数据位数×每样本的数据位数/8。播放软件利用此值可以估计缓冲区的大小。
  20H              2               int               数据块的调整数(按字节算的),其值为通道数×每样本的数据位值/8。播放软件需要一次处理多个该值大小的字节数据,以便将其值用于缓冲区的调整。
  22H              2                              每样本的数据位数,表示每个声道中各个样本的数据位数。如果有多个声道,对每个声道而言,样本大小都一样。
  24H              4           char              数据标记符"data
  28H              4         long int           语音数据的长度


知道WAVE文件头的格式后,再读取里面的数据是很简单的。


WAVE文件去掉文件头之后,剩下的是PCM格式编码的数据。PCM 脉冲编码调制是Pulse Code Modulation的缩写,是数字通信的编码方式之一。模拟信号数字化必须经过三个过程,即抽样、量化和编码,PCM编码的主要过程是将话音、图像等模 拟信号每隔一定时间进行取样,使其离散化,同时将抽样值按分层单位四舍五入取整量化,同时将抽样值按一组二进制码来表示抽样脉冲的幅值,以实现话音数字化。这里通过SSC接口把PCM数据传输给WM8731解码芯片,而WM8731解码芯片能够直接把PCM数据进行DAC转换后输出到耳机。


需要说一下的是,这里的SSCSynchronous Serial Controller)是ATMEL的同步串行控制器,它提供与外部器件的同步通信,支持许多用于音频及电信应用中常用的串行同步通信协议,如I2S,短帧同步,长帧同步等。


       这里需要注意的是对WM8731进行控制的部分,从硬件电路图上可以看出WM8731连接到SAM3U处理器的TWI0接口和SSC接口上了。其中通过TWI0接口给WM8731发送控制命令,如控制它的输出通道,音量的选择等等;通过SSC接口给WM8731发送数据,数据发送的频率与要播放音乐的采样频率和通道数有关,所以我们在播放音乐之前要重新配置SSC的时钟分频,不然的话播放出来的音乐就走调了,要么太快,要么太慢。


2. 播放MP3文件


       WM8731解码器不能直接解码播放mp3文件,如果我们要播放mp3文件的话,必须先把它解码成PCM格式的数据,然后才能通过WM8731进行播放。


       现在就碰到一个问题了,如何选择合适的mp3解码算法,自己开发那不大可能,耗时又耗力。


       为了寻找合适的mp3解码算法,这总共花费了我们大概一周的时候,最后通过综合比较,我们选择了libmad音频解码库。MADlibmad)是一个开源的高精度MPEG音频解码库,支持MPEG-1标准。libmad提供24-bitPCM输出,完全定点计算,非常适合在没有浮点支持的嵌入式硬件平台上使用。使用libmad提供的一系列API可以实现MP3文件的解码。关键是开源,可以让我们更好的对它的结构进行分析。


       libmad的移植其实很简单的,标准的libmad mp3解码库是由很多.c文件组成的,使用C语言的特点就体现出现了,我们移植时不需要做很大的修改,我们只需把MP3数据输入进去,该解码库就会自动解码输出PCM格式的数据。


libmad中的主要数据结构




主要数据结构


作用


struct mad_stream


存放解码前的Bitstream数据


struct mad_synth


存放解码合成滤波后的PCM数据


struct mad_pcm


定义了音频的采样率,声道个数和PCM采样数据,用来初始化音频


struct mad_frame


记录MPEG帧解码后PCM数据的数据结构,其中的mad_header 来记录MPEG帧的基本信息,比如MPEG层数、声道模式、流比特率、采样比特率。声道模式包括单 声道、双声道、联合立体混音道以及一般立体声。


MP3解码流程如图所示。


>>1269085022_6bd53a5e.jpg >


图4


SD卡中读入MP3文件比特流之后,即开始解码。整个解码过程,首先需进行头信息解码和帧边信息解码,即进行同步及差错检查过程。之后根据记录的相关信息进行尺度因子解码、哈夫曼解码。然后再进行逆量化、重排序、立体声解码、混淆缩减、IMDCT、频率反转、合成多相滤波等一系列过程的处理,最后得出左右声道的PCM码流。然后我们就可以把PCM数据送至WM8731芯片,实现播放。


具体的解码算法原理,本人研究也不是很深,有兴趣的读者可以参考网上的资料或者相关书籍,以及libmad解码库的详细说明。我给大家推荐一个网站,我觉得他这里面分析的很清晰:


http://mp4tech.net/document/audiocomp/0000298.asp


我们把libmad mp3解码库移植过来之后,就碰到一个很严重的问题了,在播放MP3的时候很不流畅,播放一段时间后就要等待2~3s才能继续播放。


MP3解码过程使用到了三个缓冲区,一个输入缓冲InputBuffer,存放MP3数据,和两个容量相同的输出缓冲,存放PCM数据。其中是通过SSC中断来每次输出32位的PCM数据到WM8731的,如下:


void SSC_IRQHandler(void)


{


       AT91C_BASE_SSC0->SSC_THR= Alias_Stream_Buff[SBNo][Out_Data_Offset++];


       if ((Out_Data_Offset >= Play_Size))


       {


              SBNo = 1 - SBNo;


             Out_Data_Offset = 0;


             ReadNextBytes = 1;


             AT91C_BASE_SSC0->SSC_IDR |= AT91C_SSC_TXRDY;


       }


}


也就是说只有在SSC中断被触发时才输出数据(只有在32位的数据播放完了之后才触发中断),其他的时候(两次中断间隔期间)我们可以对MP3数据进行解码,宏观上做到边播放边解码。


解码过程使用其中的一个输出缓冲区,中断中的播放过程使用的是另外一个输出缓冲区。解码过程不断的从SD卡读音频数据,送输入缓冲进行解码,解码缓冲区满了之后,就交换一下缓冲区,这样两者就不会发生冲突了。


现在问题就是我们在播放完一个缓冲区后,另一个缓冲区中的数据还没有解码完成,这个时候就必须得等待,只有在它解码完成之后才能继续播放,这段时间的延迟就是造成mp3播放很卡的罪魁祸首。简而言之,就是解码的速度跟不上播放的速度。


现在给我们提出了一个要求,就是如何优化mp3解码,提高它的解码速度。由于mp3的解码算法并不是我们自己写的,因此如果从mp3解码方面找原因是很困难的,最后只能查找外部的原因,对程序进行优化。


下面,给大家介绍一下我们所采取的措施:


1)提供系统的主时钟


够傻吧,我们竟然把这个至关重要的地方给忘了,默认配置的主时钟频率是84MHz,后来把主时钟频率调高到了95.428MHz(处理器时钟频率最高为96MHz,经验证,采取96MHz后系统就死机了)。优化后,mp3的解码速度大大提高,但是听着还是有一点点卡,还要继续优化了。


2把解码mp3过程中用到三个主要的结构体全部存放到了内部SRAM里。


开始时解码用到的个主要结构体变量mad_streammad_synthmad_pcmmad_frame(总共有22kB)存放在处理器外部的PSRAM中,现在把它们全部移到处理器内部的SRAM中。原先之所以把它们放在外部的PSRAM中,是因为处理器内部的SRAM只有52KB,比较小,现在必须要做出妥协了,把内部SRAM里面的数据搬移到外部PSRAM中。


这是因为我们在实验过程中发现处理器内部SRAM的读写速度大约是外部PSRAM的两倍,这点很重要,因为在MP3解码过程中对这些变量的使用非常频繁。在这一点解决后,播放mp3基本上不卡了。


3)使用编译器对代码进行优化


我们使用的是RealView MDK作为开发工具因此采用了MDK自带的优化等级对代码进行优化(使用的是优化级别3)。我们把所有MP3解码文件经过优化后编译成了一个库,这样使用变得很方便,而且在播放MP3文件的时候很流畅,完全没有问题。


上面的这三点优化是最重要的,像其他的一些小的手工优化就不在这多说了。


那么到目前为止,MP3的核心部分已经设计完成了,已经可以播放WAVEMP3文件,但这还远远部分,离我们最初提出的目标还很遥远。我们要求在播放MP3的时候能够显示歌词,进度,时间,还要支持触摸屏的控制,这些功能虽然用中断也可以实现,但是做起来会很麻烦,这让我们不得不想起要用到实时操作系统。关于实时操作系统的添加和MP3界面的设计下次再介绍吧。

文章评论0条评论)

登录后参与讨论
我要评论
0
7
关闭 站长推荐上一条 /2 下一条