正在做的项目中需要 STM32 从 SD 卡中读取语音文件进行播放,因此需要对
语音进行解码,刚开始就一直使用 Speex 的音频压缩格式,最近发现,在进行语
音格式转换时,我们不能很好地分析 spx 格式音频文件的文件头,这样就会导致
语音的播放出现问题。由于 WAV 采用 PCM 编码,音质也十分不错,于是考虑用
STM32 对 WAV 格式音频文件进行解码,上周末开始找资料和编程,其中也遇到了
不少问题,不过功夫不负有心人,最终还是顺利的跑起来了。先将资料和编程过
程整理成本文,供大家一起学习和进步。
WAV 文件格式是一种重要的用于存放声音文件的文件格式,尽管现在有 MP3,RAM
等压缩效率更高的声音文件格式,并且广泛被音乐文件所采用,但是又很多的应
用程序仍然采用 WAV 文件格式。由于 WAV 文件没有采用压缩技术,所以它的文件
很庞大,一般都在几 MB 以上。但也正是因为没有采用压缩技术,声音的采样数
据很容易被读出来,便于用作其他的处理。
废话不多说了,我们直接去解析 WAV 文件格式吧。
WAV 格式符合 RIFF(Resource interchange File Format)规范。所有的
WAV 都有一个头文件,这个头文件音频流的编码参数。
表 1、WAV 文件的文件头
接下来我们用已经编好的程序来读取一个 WAV 文件的文件头和数据块,看看各个
表 2、WAV 声音文件的数据块
内容都表示什么含义。
图 1、WAV 源文件
图 2、用 WinHex 软件解析 WAV
图 3、STM32 读取 WAV 的信息
头文件样例说明:
Ø “52 49 46 46”这个是 Ascii 字符“RIFF”,这部分是固定格式,表明这是一个 WAVE
文件头。
Ø “24 33 AE 00”这个是我的 WAV 文件的数据大小,这个大小包括除了前面 4
个字节的所有字节,也就是等于文件总字节数减去 8。得到图 3 中的 11416356。
11416356+8=11416364Byte=10.88Mb。
Ø “57 41 56 45 66 6D 74 20”,也是 Ascii 字符“WAVEfmt”,这部分是固定
格式。以后是 PCMWAVEFORMAT 部分。
Ø “10 00 00 00”,这是一个 DWORD,对应数字 16,这个对应定义中的
PCMWAVEFORMAT 部分的大小,可以看到后面的这个段内容正好是 16 个字节。
当为 16 时,最后是没有附加信息的,当为数字 18 时,最后多了两个字节的
附加信息。
Ø “01 00”,这是一个 WORD,对应定义为编码格式(WAVE_FORMAT_PCM 格式用
的就是这个)。
Ø “01 00”,这是一个 WORD,对应数字 1,表示声道数为 1,是个单声道 WAV,
当值为 2 时为立体声 WAV。
Ø “22 56 00 00”对应数字 22050,代表的是采样频率 220505,采样率(每秒
样本数)表示每个通道的播放速度。
Ø “44 AC 00 00”对应数字 44100,代表的是每秒的数据量,波形音频数据传
送数率,其值为通道数×每秒样本数×每个样本的数据位数/8。播放软件利
用此值可以估计缓冲区的大小。
Ø “02 00:”对应数字是 2,表示块对齐的内容。数据块的调整数(按字节算),
其值为通道数×每个样本的数据位置/8.播放软件需要一次处理多个改值大
小的字节数据,以便将其值用于缓冲区的调整。
Ø “10 00”,此数值为 16,采样大小为 16bits,每样本数据位数,表示每个声
道中各个样本的数据位数。如果有多个声道,对每个声道而言,样本大小都
一样。
Ø “64 61 74 61”,这个是 Ascii 字符“data”,表示头结束,开始数据区域。
Ø “00 33 AE 00”,十六进制数是“0xAE3300”,对应十进制 11416320,是数
据区的开头以后的数据总数。
再往后就是真正的 WAV 文件数据体了,头文件分析到此。
常见的声音文件主要有两种,分别对应单声道(11.025KHz 采样率、8Bit 的
采样值)和双声道(44.1KHz 采样率、16Bit 的采样值)。采样率是指:声
音信号在“模->数”转换过程中单位时间内采样的次数。采样值是指每一次
采样周期内声音模拟信号的积分值。
对于单声道声音文件,采样数据位 8 位的短整数;而对于双声道立体声声音
文件,每次采样数据位一个 16 位的整数,高 8 为和低 8 位分别代表左右两
个声道。
WAVE 文件数据块包含以脉冲编码调制(PCM)格式表示样本。WAVE 文件是由
样本组织而成的。在单声道 WAVE 文件中,声道 0 代表左声道,声道 1 代表
右声道。在多声道 WAVE 文件中,样本是交替出现的。
PCM 数据的存放方式:
样本 1 样本 2
8 位单声道 0 声道 0 声道
8 位立体声 0 声道(左)1 声道(右) 0 声道(左) 1 声道(右)
16 位单声道 0 声道低 0 声道高 0 声道低 0 声道高
16 位立体声 0 声道(左)低 0 声道(左)高 1 声道(右)低 1 声道(右)高
系统硬件组成比较简单,可以分为液晶显示,LED 指示,USB 输入,SD 卡,
电源供电,音频功放和按键等,如图 3-1 所示:
图 3-1 系统组成框图
SD 卡电路:
SD 卡采用 SPI 驱动。
USB 电路:
采用 SGM7222 做转换开关,识别 ID 的电压值来选择是作为 IAP 下载还是用
于 USB 接口
音频功放电路:
充电和系统电源:
程序编写主要有三个部分:定时器初始化,DAC 初始化,定时器中断服务程序,
WAV 播放程序。
定时器初始化:
void Timerx_Init(u16 arr,u16 psc)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC->APB1ENR|=1<<1;//TIM3 时钟使能
TIM3->ARR=arr; //设定计数器自动重装值
TIM3->PSC=psc; //预分频器 7200,得到 10KHz 的计数时钟
TIM3->DIER|=1<<0; //允许更新中断
TIM3->DIER|=1<<6; //允许触发中断
TIM3->CR1|=0x01; //使能定时器 3
//优先级设置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
DAC 初始化:
#include "dac.h"
extern u16 digital;
void MyDAC_Init(void)//DAC channel1 Configuration
{
unsigned int tmpreg1=0,tmpreg2=0;
RCC->APB2ENR|=1<<2;//使能 PORTA 时钟
RCC->APB1ENR|=RCC_APB1Periph_DAC;//使能 DAC 时钟
GPIOA->CRL&=0XFFF0FFFF;
GPIOA->CRL|=0X00040000;//PA4 浮空输入
tmpreg1=DAC->CR;//Get the DAC CR value
tmpreg1&=~(CR_CLEAR_Mask<