赤き心の花や咲くらん
ギトオメ君のメモです。
ARM或MCU解码PCM播放WAV
  1. 音频的采集
  2. WAV文件是什么
  3. 解码!PWM来模拟DA输出
  4. 硬件怎么设计
  5. つづく

纯理论,任何带有的PWM控制器的ARM芯片(其他内核架构也可)模拟DA输出,播放PCM格式的音频数据的方法。

音频的采集


通常使用三个参数来表示声音,量化位数取样频率采样点振幅
量化位数分为8位16位24位三种,声道有单声道立体声之分。
单声道振幅数据为n 1矩阵点,立体声为n 2矩阵点,取样频率一般有11025Hz(11kHz)22050Hz(22kHz)44100Hz(44kHz)三种。
大概了解音频采样即可。
主要明白,就是把传感器(麦克风)在某瞬间(某周期内,用取样频率算)的电压值采集进来,用ADC转换成对应的数字位数存下来,就是采样量化了。

WAV文件是什么

WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范。
用于保存Windows平台的音频信息资源,被Windows平台及其应用程序所广泛支持,该格式也支持MSADPCM、CCITT A LAW等多种压缩运算法。
支持多种音频数字,取样频率和声道,标准格式化的WAV文件和CD格式一样,也是44.1K的取样频率,16位量化数字,因此在声音文件质量和CD相差无几!
WAV打开工具是WINDOWS的媒体播放器。

不过尽管音质出色,但在压缩后的文件体积过大!相对其他音频格式而言是一个缺点。
其文件大小的计算方式为:WAV格式文件所占容量

(B) = (取样频率 X量化位数X 声道) X 时间 / 8 (字节 = 8bit)

每一分钟WAV格式的音频文件的大小为10MB,其大小不随音量大小及清晰度的变化而变化。
WAV是最接近无损的音乐格式,所以文件大小相对也比较大。

所有的WAV都有一个文件头,这个文件头音频流的编码参数。
数据块的记录方式是little-endian字节顺序,标志符并不是字符串而是单独的符号。
所谓的little-endian就是小端序,举个例子:
0x55AA,这样一个16bit的数据,在WAV文件中存储的形式就是 : AA 55
所以在使用这个数据的时候, 需要将

data = (0x55 << 8) | (0xAA);

如果采样频率是8bit的,就不用转换了。

关于WAV文件头格式

偏移地址 大小 数据块类型 内容
00H~03H 4 4字符 资源交换文件标志(RIFF)
04H~07H 4 长整数 从下个地址开始到文件尾的总字节数
08H~0BH 4 4字符 WAV文件标志(WAVE)
0CH~0FH 4 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 整数 DATA数据块长度,字节
22H~23H 2 整数 PCM位宽
24H~27H 4 4字符 “fact”,该部分一下是可选部分,即可能有,可能没有
28H~2BH 4 长整数 size,数值为4

关于WAV文件尾需要特别强调

有些文件是添加了结尾的<LIST>这样开头的文件尾的,包含了一些录制的公司的信息,日期,等等…
所以对于这样的文件尾还是需要做相应的处理..
总的来说,提取真实有效的数据段就好了(剔除头和尾)。

当然,在我的实验中,我采取的方式是,将8bit的数据段提取出来后,放进这样一个数组里面。

const unsigned char datap[] = {};

这样,编译的时候,这个数组的区域自然会被编译器放到FLASH当中。当然,也可以不放在flash中,因为实在太占空间了。
会有更好的办法,这是后话,待会儿再说。

当然,有的盆友就不明白了,都看不到这个数据,怎么提取…
别担心,当然有好用的工具啦..

Binary Viewer,这是一个在windows环境中的一个以二进制查看文件的工具,能够显示偏移量,hex,ascii,格式化输出等等..
可以自定义数据输出格式的功能也是很棒,并且这个软件才1.8M。

能看数据了,还不够,这样看数据来识别WAV文件是什么采样率,什么bit位,都太不直观了..
于是又有mediainfo这个软件,能够显示WAV文件的详细信息.采样率,量化bit什么都看到了..

所以你觉得有了这些就够了吗? 不!
还有个最重要的软件,Audition
这个软件不仅能够修改WAV的采样频率,还能修改量化bit的大小,简直就是个神器。重要的是,能够看到每个采样点的波形点…
简直…太棒辣..

解码!PWM来模拟DA输出

现在开始说PWM模拟DA输出。对于玩过单片机的人来说,PWM并不陌生,即,脉冲宽度调制。实际上PWM出来的,是一个方波。
大家都知道,但是它有一个周期,所以有周期,那么就有频率了。好,请记住此处是PWM频率!与之前所说的音频采样频率不是同一个东西。

那么占空比是什么呢?占空比就是在一个方波周期内,高电平脉冲宽度所占有周期的百分比..也不能说是百分比,总之就是那个意思了…
如果把一个周期分成0xff份,那0x01,高电平就占有1/255的周期时间。那么使用这个比例去设置电压,假设所使用的单片机使用的是5V电压基准,那么设置占空比0x01,则

5V/255*1 = 0.019607843137255V

所以现在你算出来,把PWM周期分成255份,然后将占空比设置为1/255。
现在拿万用表量一下,现在PWM输出的电压值是不是0.019607843137255V。
对吧,没错吧…
那么再试试将占空比设置为128,再用万用表测量一下…
对吧,2.5V有了吧..

这样,我们就能够使用PWM模拟DA输出了。既然这样能够调节占空比来输出不同的电压了,那么我们应该使用什么样的频率来输出PWM呢?
当然是结合MCU的频率,高于WAV文件的采样频率更好更好。之前看了别的文章,说是两倍于采样频率即可(我的实验中采用两倍)。
到这个地方,请记住,这个PWM频率,是用来产生模拟DA电压的频率,不是播放音频文件的频率或音频文件的采样频率!

那么我们播放音频文件到底使用什么样的节奏(频率)呢?
当然是使用采用频率啦,不然播放出来的音乐总感觉哪里不对,要么就是一片杂音。
为了达到准确的播放频率,那就取决于我们定时器中断的时间了。
比如,1000us进一次中断,那么我们播放音频文件的频率就是1kHz。
如果我们的WAV文件的采样率是1kHz,且数据位是8bit。
那么每次进中断(此处的中断,使用的是定时器中断)的时候,就按照8bit的数据,调整不同的占空比。


1000us改变一次电压,就在横纵坐标(横轴是时间,纵轴是电压)。
这样一个一个时间-电压点打出来,就在横纵坐标上画出来了音频文件的曲线。
Audition打开所播放的音频波形和画出来的音频曲线大概对比一下就能发现,图像是一模一样的…
好,现在应该就能够听见喇叭出来的每秒的声音了吧..

硬件怎么设计

MCU引脚—->功放—>滤波器—>喇叭。
Google一下,网络上很多很多的示范电路…
我就不贴图了,对。
是因为我…..
懒!

关于之前提到的,不要存flash里面的问题。不够用当然要想办法,用SD卡吧,SPI读写,毕竟大多数人都是这么干的..
有时候从众也不是一件坏事..

つづく


是,没错,我用MCU放了一首张学友的《爱我别走》。
音质,简直毁了…
对于想做得更考究的朋友,除了在硬件上需要有所优化。
在软件上需要使用更高采样率的音频源(44.1k)。

另外还有一种玩法,就是从PCM的原始数据(WAV)上着手,使用算法将midi的音算出来。这种玩法的好处是,
非常节省资源,非常适合在MCU等资源紧缺的器件上使用。