单片机(Arduino)自制录音、播放器(二)播放器篇
前一篇给大家分享了录放机设计的大致流程,那么这篇我们将先专注于如何实现声音播放器,具体内容如下:
1. 声音播放器原理与知识点介绍
2. 硬件电路的实现
3.软件的设计
【声音播放器原理与知识点介绍】
1.声音播放器的原理
Pulse Code Modulation)数据,并通过DAC(Digital Analog Converter)转换输出单声道或者双声道的模拟信号,经过功率放大芯片后,输出到耳机、外放等音源设备。具体流程图如下:
在我们的方案中,MP3解码的这一环在PC上替代实现了。同时,由于Atmega328P不具有DAC,我们不得不用PWM占空比调制+积分滤波电路来获得模拟信号输出,这也是我们和好的MP3播放器在音质之间的一大差距。
2.PCM(Pulse Code Modulation)数据格式
关于PCM数据格式的详细讲解,请点击标题的超链接查看百度百科,这里我就不再赘述。这里需要强调几个关键的点:
1)采样率和采样精度
对原始音源(WAV或者MP3)采取合适的采样率和采样精度来进行重采样,对于我们的音频播放有着重要的关系。由于选取的是Atemega328P, 单片机的主频和定时器的速度有限。在此案例,采样频率应同我们的播放频率保持一致,否则如果采样频率大于播放频率,那么效果会变成了慢速播放(本来1s的数据2s才播完),反之则是快进。因此,采样率和采样精度有着如下公式的限制:
其中f(clk)是定时器的主频, n为采样精度, fs为采样频率。这个公式我们会在接下来更为详细的铺开讲解,在这里我们只需要选定合适的采样频率和采样精度。
依照标准无损的WAV格式, 音源数据为16bit, 44.1KHz.那么我们的主频则需要有
这显然是不可接受的,因为我们的主频限制在16MHZ。同时,由于比特率达到了44.1k*16 bps, FLASH 64Mbit的存储不能满足需求。
如将采样精度降低为8bit,则所需主频大概降到了12MHz.然而,44.1K的采样率对于我们来说过大,因外设喇叭的频响根本没有达到。适当降低采样率来提升采样精度对我们来说是可取的。那么,如何选择呢?
16Mhz = 15625 * 1024。10bit采样,15625HZ!! 这对于我们来说是十分合适的,因为根据下图等响度曲线(Equi-loudness Curves),人耳对频率为3-4KHz左右的声音最敏感。根据香农采样定理,fs>2fmax时信号可通过低通滤波器还原。当我们进行15.6kHZ重采样后,转化为PWM信号后,后级使用4K左右的滤波器,就可以较好的匹配。然而,大多数的音频软件不支持10bit采样,我们采用取16位的高10位进行处理,这样虽然会多占用一些空间,但可以更好的满足我们的需求。
2)无符号和有符号的区别
量化后的抽样信号在一定的取值范围内仅有有限个可取的样值,且信号正、负幅度分布的对称性使正、负样值的个数相等,正、负向的量化级对称分布。
举个例子,假如采用8bit无符号的PCM编码, 那么振幅的最大值应该是255,最小值为0.如果声音信号是正弦波,那么基准线应该在+128的位置(如下图),若采用16bit有符号PCM编码,那么基准线应该为0,幅值应该为-32768-32767(如下图)
8bit unsigned
16bit signed
3)单声道和多声道、大端序和小端序
I 当我们的音源输出可以是耳机或者立体音响时,采用双声道可以进一步的提升音质。然而,这里我们采用的是单声道配单个喇叭。
II 大端序和小端序是指当采样精度为16bit或更多时,数据的高位在先还是低位在先。
对于单双声道、大小端序的形象表述,我们可以用如下表来理解。假设我们采用的是双声道,在某一个采样点内,两个声道的振幅为
(43707,52445) 43707(10) = 0xAABB 52445(10) = 0xCCDD , 那么数据的排列方式应该为
【硬件电路的实现】
在上篇文章中已经提到,声音播放器的流程实现如下图所示。我们这节具体讲一下音乐播放器偏硬件的方案细节
1.FLASH 与 Arduino 采用的通信是SPI 通信, 3.3V标准输出电压,按照通常SPI的连接方式即可,不再赘述
2.二阶低通滤波器的作用是将带有占空比的PWM信号转化为模拟信号,其主要起到的是积分器的作用。在此案例,我们用脉宽的长度来调制振幅,那么只要经过积分电路,就可以将其还原成原先的模拟信号。经测试,二阶的滤波器的RC值被选择为 47KΩ 和1nF。下图为RC在线计算器算出的截止频率:
后级电路如下:
【程序实现】
<SimpleSDAudio> https://www.arduino.cn/thread-2944-1-1.html 此网址介绍了如何使用simpleSDAudio做一个用SD卡播放的播放器。我将SD卡读写的程序部分换为了flash读写,大家可以参考SimpleSDAudio 的程序来做。
注意:
if (flags & _BV(MUSIC_F_PLAYING))
{
if (_bufferAvailable > 1)
{
tmp_value16 = *_pBufout++; //小端序的代码,高位在后,低位在前
tmp_value16 += (*_pBufout++) << 8;
memcpy(mybuffer+i,&tmp_value16,2);
#if 0
tmp_value16 <<= 2; // 这里用于调整音量的大小
#endif
tmp_value16 >>= 6;// 右移6位,将16bit的采样的低6位舍弃
tmp_value16 += 512; //调整基准电位值为2.5V: 512/1024*5 = 2.5V
OCR1A = tmp_value16;//设置占空比
_bufferAvailable -= 2;
}
if (_pBufout >= _pBufoutend)
_pBufout -= _Bufsize;
}
至此,音乐播放器的要点已经介绍完毕,希望对大家有帮助,下一篇我们将继续介绍录音的解决方案