Shine mp3定点编码算法的WAV.C程序
在分析WAV.C程序,再此先介绍一下WAV文件格式,WAV格式是微软公司开发的一种声音文件格式,也叫波形声音文件,以RIFF格式为标准的,是最早的数字音频格式WAV格式支持许多压缩算法,支持多种音频位数、采样频率和声道。WAVE文件由若干个Chunk组成的。按照在文件中的出现位置包括:RIFF WAVE Chunk, Format Chunk, Fact Chunk(可选),Data Chunk。其中除了Fact Chunk是可选的其它的未必选,具体结构见图2:
------------------------------------------------
| RIFF WAVE Chunk |
| ID = 'RIFF' |
| RiffType = 'WAVE' |
------------------------------------------------
| Format Chunk |
| ID = 'fmt ' |
------------------------------------------------
| Fact Chunk(optional) |(可选的)
| ID = 'fact' |
------------------------------------------------
| Data Chunk |
| ID = 'data' |
------------------------------------------------
图2 Wav格式包含Chunk示例
每个Chunk有各自的ID,位于Chunk最开始位置,作为标示,而且均为4个字节。并且紧跟在ID后面的是Chunk大小(去除ID和Size所占的字节数后剩下的其他字节数目),4个字节表示,低字节表示数值低位,高字节表示数值高位。
1.RIFF WAVE Chunk 格式
| ID | 4 Bytes |'RIFF'|+|Size|4 Bytes| +|Type|4 Bytes| 'WAVE' |
其中'RIFF'作为RIFF WAVE Chunk 标示,然后紧跟着为size字段,该size是整个wav文件大小减去ID和Size所占用的字节数,即FileLen - 8 = Size。然后是Type字段,为'WAVE',表示是wav文件。可以看下面一个WAV文件的例子
52 49 46 46 F2 5A 0E 00 57 41 56 45 66 6D 74 20
10 00 00 00 01 00 01 00 44 AC 00 00 88 58 01 00
02 00 10 00 04 00 00 00 52 01 00 00 64 61 74 61
CE 5A 0E 00 FF FF FF FF FF FF FF FF FF FF FF FF
...
其中 52 49 46 46 对应ASCII码为 'RIFF',表示chunk 的辨别码(ID)为'RIFF'占4 Bytes。
00 0E 5A F2 :表示辨别码为 'RIFF'的chunk的数据长度为000E5AF2h占4 Bytes。
57 41 56 45 :对应的ASCII码为WAVE占4 Bytes。
2.Format Chunk 格式
| ID | 4 Bytes | 'fmt ' |
--------------------------------------------------------------------
| Size | 4 Bytes | 数值为16或18,18则最后又附加信息|
--------------------------------------------------------------------
| FormatTag | 2 Bytes | 编码方式,一般为0x0001 |
--------------------------------------------------------------------
| Channels | 2 Bytes | 声道数目,1--单声道;2--双声道|
-------------------------------------------------------------------- |
| SamplesPerSec | 4 Bytes | 采样频率|
--------------------------------------------------------------------
| AvgBytesPerSec| 4 Bytes | 每秒所需字节数|
--------------------------------------------------------------------
| BlockAlign | 2 Bytes | 数据块对齐单位(每个采样需要的字节数) |
--------------------------------------------------------------------
| BitsPerSample | 2 Bytes | 每个采样需要的bit数|
--------------------------------------------------------------------
| | 2 Bytes | 附加信息(可选,通过Size来判断有无)|
--------------------------------------------------------------------
以'fmt '作为标示。一般情况下Size为16,此时最后附加信息没有;如果为18,则最后多了2个字节的附加信息。同样以上面的数据为例:
66 6D 74 20 : 对应ASCII码为'fmt' t后有空格,表示chunk 的辨别码(ID)为'fmt'占4 Bytes。
00 00 00 10 : 表示辨别码为 'fmt'的chunk的数据长度为16 ,占4 Bytes。
00 01 :代表编码方式即为记录声音的格式代号为WAVE_FORMAT_PCM占2 Bytes。
00 01 : 记录声音的频道数为1,即为单声道,占2 Bytes。
00 00 AC 44 : 定义采样频率为44100Hz。
00 01 58 88 : 记录每秒钟的数据量,每秒的数据量是00015888h。
00 02 : 表示数据块对齐单位,对其单位为2,占2 Bytes。
00 10 : 表示每个采样需要的bit数,比特数为16,占2 Bytes。
因为size数值为16 所以最后无附加信息;
3. Fact Chunk
| ID | 4 Bytes | 'fact' |+ | Size| 4 Bytes |数值为4 |+| data | 4 Bytes |
Fact Chunk是可选字段,一般当wav文件由某些软件转化成的,则包含该Chunk。
如例子中的数据:
00 00 00 04 : 对应ASCII码为 'fact',表示chunk 的辨别码(ID)为'fact'占4 Bytes。
00 00 01 52 : 代表真实得数据采样量的多少,长度为338。
4.Data Chunk
| ID|4 Bytes|'data'|+| Size | 4 Bytes | +| data |
Data Chunk是真正保存wav数据的地方,以'data'作为该Chunk的标示。然后是数据的大小。紧接着就是wav数据。
如例子中的数据:
64 61 74 61 :对应ASCII码为 'data',表示chunk 的辨别码(ID)为'data'占4 Bytes。
00 0E 5A CE :表示Data Chunk的数据长度为940750。
以上是对WAV文件格式的介绍,根据以上内容我们就很容易理解Shine mp3定点编码算法的WAV.C程序。
(1)在程序中wave_open(raw, mono_from_stereo)函数是打开wav文件,并检测是否为正确的wav文件格式即判定RIFF WAVE Chunk 格式,并且确定编码方式、声道数目、采样频率、每秒所需字节数、数据块对齐单位以及每个采样所需要的bit数,即确定Format Chunk 格式。
(2)wave_get(void)函数,此函数是每次从WAV文件读取n个数据存在buffer中,并判断wav文件数据是否全部读出。此过程读取的即为Data Chunk所包含的数据。
(3)Fir滤波器组对存储在buffer中的数据,按照gr以及声道数处理得到的buffer数据。
以上过程采用FPGA实现时,按照 MPEG-1单声道模式,每一帧(frame)中有2个gr,共1152个样本(sample),每一个gr有576个样本,将经过AD 采样的PCM数据以十六进制的形式存储在32*512的双口的BRAM中,也即不再做WAV文件格式的确定,直接将PCM数据写入BRAM同时读取BRAM的数据。在此部分根据声道数以及gr数将fir滤波器组的循环加窗在此部分实现。对采样得到的数据循环加窗C程序的处理过程如下 :
/* polyphase filtering 多相滤波, 对时域信号进行滤波*/
(1).
for(gr=0; gr<2; gr++) // 这里选定gr =1;
{
for(ch=config.wave.channels; ch--; ) // 声道 ch为单声道
{
/* 每次处理576个样本 18 == ( 576 / 32 ) */
for(i=0; i<18; i++ )
L3_window_filter_subband(&buffer[ch],&l3_sb_sample[ch][gr+1][i][0],ch);
}
}
* l3_sb_sample[ch][gr+1][i][0]加窗滤波后得到的32个子带信号
* 每次处理得到32个子带信号,要得到576个样本,就需要从buffer中读取处理18次
L3_window_filter_subband(&buffer[ch],&l3_sb_sample[ch][gr+1][i][0],ch);
{
(2).
if(config.mpeg.channels == 1)
{ /* mono data, use upper then lower */
for (i=15;i>=0;i--)//(i=0;i<=15;i++)
{ x[k][(2*i)+off[k]+1] = (**buffer) << 16;
x[k][(2*i)+off[k]] = ((*(*buffer)++) >> 16) << 16;
}
}
*这里根据单声道模式,每次从buffer中读取16个32进制数,并将每个高16位与低16
*位分开组成两个新的32进制数。即组成了32个32进制数。
(3).
for (i=HAN_SIZE; i--; ) //HAN_SIZE = 512
{z[i] = mul(x[k][(i+off[k])&(HAN_SIZE-1)],ew[i]);
}
off[k] = (off[k] + 480) & (HAN_SIZE-1); //
}
*off 在此对循环加窗起到的妙用如下
*off = 0-480-448-416-384-352-320-288-256-224-192-160-128-96-64-32-511
* z[i] = x[(i+off[k])&(HAN_SIZE-1)] * ew[i]
0-511 = 0-31,32-511 * 0-511
0-511 = 480-511,0-479 * 0-511
0-511 = ... * 0-511
* 如上一共会加18次窗
>