注:本文为原创,转载请注明出处。来自CSDN,作者:Midfar Sun
最近研究了如何在iOS上绘制wav波形图。查了很多资料,都没能找到一个很完整的介绍,我这里总结一下一些经验。
首先需要了解wav的这3个重要指标:采样率、采样位数、声道数。下面以16KHz, 16Bit, 单声道为例来说明。
采样率:(也称为采样速度或者采样频率)定义了每秒从连续信号中提取并组成离散信号的采样个数,单位用赫兹(Hz)来表示。采样频率的倒数是采样周期(也称为采样时间),它表示采样之间的时间间隔。采样率为16KHz,表明每秒钟采样有16K次,即0.001秒内采集16个值。
采样位数:即采样值或取样值,用来衡量声音波动变化的参数,是指声卡在采集和播放声音文件时所使用数字声音信号的二进制位数。声卡的位客观地反映了数字声音信号对输入声音信号描述的准确程度。16Bit表示用计算机的16位(即2字节)来标示一个值。
声道数:是指支持能不同发声的音响的个数。常见的有单声道和双声道。
比特率:每秒传送的比特(bit)数,等于采样率*采样位数,单位为bps(Bit Per Second)。示例音频的比特率为256kbps。
C语言没有提供专门的wav音频文件处理框架,因此,我们只能通过读取文件的2进制值来分析wav音频的波形。这里就需要对wav音频的格式有一定的了解。
wav音频分为文件头和数据块两大部分。
表1 WAV文件的文件头
偏移地址 |
字节数 |
类型 |
内容 |
00H~03H |
4 |
字符 |
资源交换文件标志(RIFF)注意字符大小写! |
04H~07H |
4 |
长整数 |
从下个地址开始到文件尾的总字节数 |
08H~0BH |
4 |
字符 |
WAV文件标志(WAVE)注意字符大小写! |
0CH~0FH |
4 |
字符 |
波形格式标志(fmt)注意字符大小写! |
10H~13H |
4 |
整数 |
过滤字节(一般为00000010H) |
14H~15H |
2 |
整数 |
格式种类(值为1时,表示数据为线性PCM编码) |
16H~17H |
2 |
整数 |
通道数,单声道为1,双声音为2 |
18H~1BH |
4 |
长整数 |
采样频率 |
1CH~1FH |
4 |
长整数 |
波形数据传输速率(每秒平均字节数) |
20H~21H |
2 |
整数 |
数据的调整数(按字节计算) |
22H~23H |
2 |
整数 |
样本数据位数 |
表2 WAV声音文件的数据块
偏移地址 |
字节数 |
类型 |
内容 |
24H~27H |
4 |
字符 |
数据标志符(data)注意字符大小写! |
28H~2BH |
4 |
长整型 |
采样数据总数 |
2CH... |
... |
... |
采样数据 |
WAVE文件是由若干个Chunk组成的。按照在文件中的出现位置包括:
RIFF WAVE Chunk,位置00H~0BH。
Format Chunk,以'fmt'作为标示。一般情况下Size为16,此时最后附加信息没有;如果为18则最后多了2个字节的附加信息。主要由一些软件制成的wav格式中含有该2个字节的附加信息。位置0CH ~23H。
Fact Chunk,可选字段,一般当wav文件由某些软件转化而成,则包含该Chunk。
Data Chunk,是真正保存wav数据的地方,以'data'作为该Chunk的标示。然后是数据的大小。紧接着就是wav数据。从24H开始。
其中除了Fact Chunk外,其他三个Chunk是必须的。每个Chunk有各自的ID,位于Chunk最开始位置,作为标示,而且均为4个字节。并且紧跟在ID后面的是Chunk大
小(去除ID和Size所占的字节数后剩下的其他字节数目),4个字节表示,低字节表示数值低位,高字节表示数值高位。
在了解了wav文件的结构后,我们就可以轻松的取出wav的相应信息了。接下来就是绘制曲线,我们使用Quartz来绘图。苹果官方提供了一套例子,参考
http://developer.apple.com/library/ios/#samplecode/QuartzDemo/Introduction/Intro.html
其中关键是QuartzView和QuartzLines这两个文件。在QuartzLines.m文件中可以找到我们想要的绘制曲线方法,可以作为参考。
接下来的事情就很简单了,我们只需要从2CH的位置开始,每2个字节(与采样位数有关)的取数据,然后画到屏幕中。因此,音频数据点的个数为:采样数据总数/2。如果需要将图像绘制到界面上,x坐标范围10~310,y坐标范围200~400,只需要将这些点做个映射就行了。
附件:在我的资源下载
参考示例QuartzLines.m(为了达到更好的显示效果,对y进行了放大处理)
http://download.csdn.net/detail/midfar/4040612
注意:
源码中只考虑了没有Fact Chunk的情况。实际上在读取data的时候应该加上判断
if(strcmp(id, "data")!=0){//not eq data
//TODO 读取Fact Chunk
//结构是4字节的Fact Chunk数据长度+Fact Chunk数据
}