告诉你一个音频压缩算法,让你的Flash减小四倍

我们生活中最常见的音频压缩算法就是我们的mp3,早些年大家都喜欢下载这种类型的音乐,一首歌3-4M字节,空间占用少,毕竟那时候的MP3播放器还是256M或者512M的时候。

图片

单片机中的音频播放

音频播放在小型单片机系统中也会经常用到,最早我做的地铁广播系统中就是用的单片机播放的音频。这人中音频播放大体上有两种方式,一种是使用vs1003这样的外置模组来进行数据解码,另一种就是单片机自己解码。

单片机的运算能力有限,因此靠单片机来计算MP3还是一项比较沉重的工作,所以,一般的小型系统中,我们往往不使用MP3格式的音频,而是直接播放wav格式的音频。

说白了,wav格式就是纯音频数据存储,没有进行过压缩的,也就意味着不需要解码,这样一来,单片机只需要将两通道的音频数据丢到IIS缓存区即可。

没有IIS外设的怎么办?那就用DAC,12bit的DAC播放个音频指示或人声完全没问题。

什么?连DAC也没有,让我想想。。。

那就用PWM吧,配置一个PWM,占空比可调,具备10bit可调范围的PWM,硬件方面,就在引脚上加一个RC滤波,一样可以播放好听的效果音乐。

图片

PWM也没有吗?

那也没关系,有IO口就能搞,不过今天重点不是说这个,重点是想说,这个wav文件还是有点大,我们需要对他做一些压缩算法来存储到我们可怜的Flash空间中。

音频格式初探

ADPCM算法是一种针对16bit的声音波形数据的一种有损的压缩算法,他将声音流中每次采样的16bit数据,转变成4bit来存储,所以它的压缩比为1:4。

想想,音频文件可以节省4倍,这得省好几毛钱了,而且这种压缩算法简单,51单片机就能轻松应对,因此这中压缩算法是一种地空间消耗,高质量音频获得的好途径。

ADPCM主要是针对连续的波形数据,它保存的是波形的变化情况,以达到描述整个波形的目的。

先科普一下音频信号存储的知识。

图片

一般我们在PC机的游戏中或者公交车报站器中使用到的声音,都是提前录制好的,这种录制就是一种模拟转数字的过程,因此这里面涉及到了香浓的采样定理。

我们一般对于音频的采样率会设置在44.1Khz,根据香浓定律,我们可以还原出22KHz的声音,这已经是大多数人耳朵能分辨的频率的上限了。也有异能人士可以听到更高,或者跟老柴我一样最高只能分辨到16KHz,因人而异吧。

知道了采样频率,我们再看一下对于一个升压,我们需要将他量化存储,到底需要多少个bit才能表示真个声音中的大大小小的赋值呢?这个一般我们音乐是按照16bit处理的,也不是想多高就能多高,还得看采样的ADC强不强。

有了一个一个的升压赋值和采样频率,只要我们按照频率把赋值送给扬声器,我们就可以听到录制的音乐了。

在wav中,数据的存储非常的简单粗暴,就一个挨着一个的放,所以我们定好时间挨着个的取数据就可以了。

其实,8bit的采样深度就足够人耳享用的了,比如win95的开机音乐。16bit就已经算是高音质了,现在很多游戏中采用16bit,单片机系统中,比如报站什么的8bit足够了。

有了声音的基本存储,我们来看看如何压缩。

ADPCM算法

因为声音一般是连续的,也就是频率足够快的情况下,前后两个采样值之间的差异会比较小。

我们就利用这个特性来对数据进行压缩,也就是对两次采样值的差再做一次量化,由于这个差值比较小,因此我们可以使用更少的bit来存储,这样就实现了压缩的结果。

图片

如上图所示,直接存储的方式中,我们存储的数据是ABC三个16bit的数据,如果按照ADPCM压缩算法,我们存储的将是相对值,也就是B-A,C-B这样的更小的值,当然重新播放的时候,我们要有一个初值。

其实初值就是0,声音怎么也得是慢慢变大的,不然喇叭受得了,你耳朵也受不了。

接下来,我们看如何二次量化。

如果我们想压缩为4bit,那么也就是一共16个等级,你可以平均分配,但显然这样做很不明智,有大神发明了两种重新量化的定律,叫A Law和u Law。其实就是非线性量化,至于做成什么样的非线性,这得研究人耳朵对音乐的敏感性了,不在我们讨论范围内。

我们来看一个图,大致了解下两个Law的不同。

图片

乍一看其实没太大区别,所以这种非线性量化其实也挺随意的。

由于信号量噪比的不恒定而影响信号质量,为了对不同的信号强度保持信号量噪比恒定,在理论上要求压缩特性为对数特性。

为了使信号量噪比保持恒定,引入A压缩律与μ压缩律以及相应的近似算法-13折线法和15折线法。一般来说,U律的15折线比A律的13折线,各个段落的斜率都相差2倍,所以小信号的信号量噪比也比A律大一倍,但是对于大信号来说,u律比a律差。

无论哪个折线法,到我们写程序的时候都变成了一个数组而已,我们就根据采样值的差值落到那个区间内来定义他的编码值。

最后我附上代码,大家可以琢磨一下,需要详细讨论可以后台私我,也可以加入我的星球讨论。

图片

代码

压缩:

int index = 0, prev_sample = 0;

while (还有数据要处理) {
    cur_sample = getnextsample();       // 得到当前的采样数据
    delta = cur_sample-prev_sample;     // 计算出和上一个的增量
    if(delta < 0) delta=-delta,sb=8;  
    else sb=0;                        // sb 保存的是符号位

    code = 4*delta/step_table[index]; // 根据 steptable[] 得到一个 0~7 的值
    if (code > 7) code = 7;                // 它描述了声音强度的变化量

    index += index_adjust[code];    // 根据声音强度调整下次取steptable 的序号
    if(index < 0) index=0;            // 便于下次得到更精确的变化量的描述
    else if (index > 88) index = 88;

    prev_sample = cur_sample;

    outputode(code|sb);                 // 加上符号位保存起来
    }

解压缩

int index = 0, cur_sample = 0;  //信号从0开始,否则耳朵不保

while (还有数据要处理) {
    code=getnextcode();           // 得到下一个数据

    if ((code & 8) != 0) sb=1 
    else sb=0;

    code &= 7;                      // 将 code 分离为数据和符号

    delta=(step_table[index]*code) /4 + step_table[index] / 8;
    // 后面加的一项是为了减少误差

    if (sb == 1) delta =- delta;

    cur_Sample += delta;            // 计算出当前的波形数据
    if (cur_sample > 32767) cur_sample = 32767;
    else if (cur_sample < -32768) cur_sample = -32768;

    output_sample(cur_sample);

    index += index_adjust[code];
    if (index < 0) index = 0;
    if (index > 88) index = 88;
  }

附表

int index_adjust[8] = {-1,-1,-1,-1,2,4,6,8};
int step_table[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 };

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
音频压缩算法有很多种,其中比较常见的有MP3、AAC、OGG等。这里我给您提供一个基于51单片机的简单音频压缩算法示例。 该算法主要是通过对音频信号进行采样和量化来实现压缩。具体步骤如下: 1. 采样:将模拟音频信号转换成数字信号,以便于数字处理。 2. 量化:将采样后的数字信号按照一定的量化规则进行处理,将其分为若干个量化等级,以减小数据量。 3. 压缩:将量化后的数据进行编码,以进一步减小数据量。 4. 存储:将压缩后的数据存储到储存介质中。 以下是一个基于51单片机的音频压缩算法示例程序: ``` #include <reg51.h> typedef unsigned char uchar; typedef unsigned int uint; sbit ADC_CS = P1^0; //AD转换器片选引脚 sbit DAC_CS = P1^1; //DA转换器片选引脚 sbit SPI_SCK = P1^2; //SPI时钟引脚 sbit SPI_MOSI = P1^3; //SPI数据引脚 sbit SPI_MISO = P1^4; //SPI数据引脚 uchar code CompressTable[16] = { //压缩表 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; uchar code DecompressTable[16] = { //解压表 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }; uchar Compress(uchar sample) { //压缩函数 uchar i; for (i = 0; i < 16; i++) { if (sample <= CompressTable[i]) { return i; } } return 0x00; } uchar Decompress(uchar code) { //解压函数 return DecompressTable[code]; } void SPI_SendByte(uchar dat) { //SPI发送函数 uchar i; SPI_MISO = 0; for (i = 0; i < 8; i++) { SPI_SCK = 0; SPI_MOSI = (dat & 0x80) ? 1 : 0; dat <<= 1; SPI_SCK = 1; } } void Delay(uint t) { //延时函数 uint i; for (i = 0; i < t; i++); } void main() { uchar sample, code; ADC_CS = 0; DAC_CS = 0; while (1) { ADC_CS = 1; Delay(100); ADC_CS = 0; SPI_SendByte(0x01); sample = SPI_MISO; code = Compress(sample); SPI_SendByte(code); sample = Decompress(code); SPI_SendByte(sample); DAC_CS = 1; Delay(100); DAC_CS = 0; } } ``` 以上是一个简单的音频压缩算法示例,仅供参考。实际应用中,需要根据具体的需求和硬件条件进行优化和改进。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

轩哥谈芯

坐下来,喝杯茶吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值