首页 > 评测 > 第一篇 数字音频处理系统的基本原理

第一篇 数字音频处理系统的基本原理

嵌入式   音频处理   EBCS   
  • 作者:zhanzr21
  • 来源:21ic
  • [导读]
  • 每个板子都能歌唱

写在前面:

21ic打算携手资(tu)深(ding)直男癌晚期工程师zhanzr21,来给大家讲一讲嵌入式系统与音频处理的故事。

关于zhanzr21

曾经混迹于两岸三地,摸爬滚打在前端后端,搞过学术上过班。现在创业中,欢迎各种撩

点击链接加入群【嵌入式音频信号处理】:https://jq.qq.com/?_wv=1027&k=45wk8Ks

嵌入式音频专用资料代码分享:https://pan.baidu.com/s/1dFh5pWd

本期活动地址:https://bbs.21ic.com/icview-1698510-1-1.html

 

 1.声音

从物理学的角度来看,声音就是介质中传播的振动.从生理学的角度来看,声音就是听觉神经接收到的脉冲.从工程的角度来看,声音就是连续的电压变化与内存中的一组组变量.人耳朵能感知的声音频率范围为20Hz-20KHz.超出此频率的称之为超声频(波),低于此的称之次声频(波).深究声音的物理学,生理学,心理学的属性不是本文所能涵盖的内容,如同此系列的前言所言,本文只从工程的角度来理解声音.在这个角度所理解的声音也称之为音频(信号/数据).

对于工程师来说,关心的是音频的录制,处理,混合与再生.这个过程中的音频可以用两个元素来表达:幅度与时间.当然从两个元素衍生出很多其他复合元素,时域的音频也时常转到频率来处理.但是从表面来看,音频的输入与输出都可用幅度与时间这两个元素来表达.

讲得哲学一点,音频就是一个维度上的变化(区别于视频的二维变化,真3D视频的三维变化).数字音频就是这种一维变化的离散近似还原.比如任何编程语言中的一维数组可以表达一段音频.播放两个采样之间的时间间隔决定了音频采样率.下图为一段4bit采样的正弦音频数据.

声音的数字存在形式.jpg

图 声音的数字存在形式

上图显示的是一种最典型的音频采集的例子,最高幅度的声音强度被表示为1111,最低的为0000.实际中使用的音频采样系统,一般使用8bit至24bit的采样深度.

沈从文先生说过,电影的美不如绘画之美,绘画的美不如文字之美,文字的美不如声音之美,声音之美不如数学之美.这段话说的有点过于玄妙,有的唯心主义的色彩.大致意思是越简单的变化形式越是难以捉磨,也就显得更加神秘迷人.用工程师的语言来翻译就是:低维变化因为形式简单,而使得变化较为二维三维变化更加能表达人类的美学观点.数字音频处理可以说结合了音乐与数学,算是美学中比较高端的内容了.

2.数字音频系统的基本组成

全模拟的音频系统也是存在的,比如传统的AM/FM广播系统属于全模拟的音频系统.这里不多评论.但是只要涉及到复杂一点的数学处理,那么音频必然要先转换为数据才能进行处理.随着计算能力与带宽的飞速发展,现在说的音频系统一般都指的是数字音频系统.这样的系统的特点就是,仅仅在声音的入口出口两个点,音频作为模拟形式存在,中间都作为数字形式被进行滤波,放大,各种处理.一般而言,数字音频系统组成如此:

数字音频系统的组成.jpg

图 数字音频系统的组成

下面看一看ST的F769-Discovery开发板上的音频系统组成部分:

STM32F769I-DISCO板子上的音频处理系统.jpg

图 STM32F769-Discovery板子上的音频处理系统组成

[1] LineIn输入接口

[2] LineOut输出接口

[3] 音频Codec(主要相当于DAC,也包含一路ADC)

[4],[5] SPDIF输入输出,一种特殊的信号格式,后面章节中详叙

[6] STM32F769作为处理器,虽然不是专门DSP系列,计算能力也能胜任音频DSP的功能. 也包括DFSDM接口,相当于音频ADC.

[7] SD卡,存储设备

[8] 网口,可以获取数据或者上传数据

[9] MEMS Micphone作为模拟输入源

这个系统的原理图将在后续文章中进行更详细的分析.

3.音频的形状,Play with it!

闲话少说,现在开始动手试着感觉一下音频形状与味道.使用代码来生成一段音频数据,比如生成一段1.5秒的600Hz的正弦波信号(Python 3.5环境):

#!/bin/python

#Example source code for 21ic

#Default runs in Python 3.5 Environment

#Author: zhanzr21

#Description: This Example demonstrates how to generate raw audio data.

#

import os

import math

TEST_SAMPLE_RATE = 22050

TEST_SAMPLE_LEN_SEC = 1.5

TEST_SAMPLE_NUM = int(TEST_SAMPLE_RATE * TEST_SAMPLE_LEN_SEC)

CHAN_NO = 1

AUDIO_HZ = 600

AUDIO_CYCLE = (TEST_SAMPLE_RATE/AUDIO_HZ)

test_amp_gain = 0.75

INT16_MAX = 32767

f=open('test.raw',mode='wb')

for i in range(0, TEST_SAMPLE_NUM):

test_sample = int(INT16_MAX * test_amp_gain * (math.sin(math.pi*2*(i%AUDIO_CYCLE)/AUDIO_CYCLE)))

test_ba = bytearray()

test_ba.append(test_sample&0x00ff)

test_ba.append((test_sample>>8)&0x00ff)

f.write(test_ba)

f.close()

 

小贴士:最简单的Py thon使用教程

 

Python的官方版本下载地址:

https://www.python.org/downloads/ ,本文

关于一般数据处理使用Python 3开发环境.

这里插播一下简单的实验步骤:

下载安装后,点这里启动IDLE:

11.jpg

新建一个文件:

22.jpg

将这里代码复制进去新打开的窗口,按F5运

行,运行之前提示要保存.建议单独建立一个

Python代码文件夹保存要使用的源代码.

Python语言对Tab要求很严格,如果网页上

的代码不能运行请使用后面附带的源代码

使用IDLE打开按F5直接运行.

 

运行后,相对路径为源代码路径,生成文件在

该目录下找.如果运行有误在第一个窗口中
会有显示.

代码比较简单,不多介绍.只提一句,要生成的音频振动频率为AUDIO_HZ, 用频率与采样率来计算AUDIO_CYCLE,每个周期的角度变化为2*Pi.

生成的正弦波形状.jpg

图 生成的正弦波形状

如果要生成方波呢,把上面代码关键处改成这样就可以了:

test_sample = int(test_amp_gain * (INT16_MAX if ((i%AUDIO_CYCLE)>(AUDIO_CYCLE/2)) else INT16_MIN))

生成的方波形状.jpg

图 生成的方波形状

如果要生成锯齿波,这样改:

test_sample = int(test_amp_gain * (INT16_MIN + (i%AUDIO_CYCLE)*((INT16_MAX-INT16_MIN)/AUDIO_CYCLE)))

生成的锯齿波形状.jpg

图 生成的锯齿波形状

如果要生成三角波,这样改:

test_sample = int(test_amp_gain * (INT16_MIN + (i%AUDIO_CYCLE)*(2*(INT16_MAX-INT16_MIN)/AUDIO_CYCLE)) if ((i%AUDIO_CYCLE)<(AUDIO_CYCLE/2)) else (INT16_MAX - (i%AUDIO_CYCLE)*(2*(INT16_MAX-INT16_MIN)/AUDIO_CYCLE)))

生成的三角波形状.jpg

图 生成的三角波形状

上面都是连续信号的还原[非数学意义上的连续信号],要产生那种报警用的嘟嘟嘟信号,还要间歇地加一些空白区,比如要产生200ms的间歇性数据.添加两个定义:

PULSE_HZ = 5

PULSE_CYCLE = (TEST_SAMPLE_RATE/PULSE_HZ)

再将数据生成代码改成:

test_sample = int(INT16_MAX * test_amp_gain * (math.sin(math.pi*2*(i%AUDIO_CYCLE)/AUDIO_CYCLE))) if (0==(i//PULSE_CYCLE)%2) else 0

报警声音频形状.jpg

图 生成的间歇性报警音频形状

上面的波形幅度都是一致的,再看看幅度随着时间衰减与增加的效果.

数据生成代码改成:

test_sample = int(INT16_MAX *

((2*i)/TEST_SAMPLE_NUM if i <(TEST_SAMPLE_NUM//2) else (2-(2*i)/TEST_SAMPLE_NUM)) *

(math.sin(math.pi*2*(i%AUDIO_CYCLE)/AUDIO_CYCLE)))

生成的幅度先增强后减弱的正弦音频形状.jpg

图 生成的幅度先增强后减弱的正弦音频形状(正弦要拉长时间轴才看得出)

当然还可以生成各种形状,这里就留给大家发挥想象力了.比如将生成数据那一行改成这样:

test_sample = random.randint(INT16_MIN, INT16_MAX)

会生成怎样的波形呢? 留给大家做实验.

这里顺便再提一下,本文所有代码都会有附件提供以便读者方便实验.一般数据处理性质的代码为python代码,复杂的数据处理为Matlab/Octave代码,有必要还会提供C/C++代码.嵌入式系统的将会给出硬件原理图与配套代码.

这个生成的test.raw文件的大小为66150字节.是这么计算的:

raw_file_size = 采样率 * 声道数 * (声道位宽/8) * 音频持续长度

看看上面的代码就知道: 我们定义的采样率 = 22050, 声道为单声道, 位宽为16bit, 持续长度为1.5秒.

这里称之为raw文件,意思为原始格式,也有称为binaryx文件的. 对于这种数据,生成者与使用者都要知道数据的含义才行.和嵌入式开发中用来烧录的binary文件是一个道理,事实上后面的章节中会将此文件直接烧录到板子上进行播放.我们这里可以试着使用烧写工具Jlink打开这个文件:

Jlink打开生成的原始文件test.raw.jpg

图 Jlink打开生成的原始文件test.raw

事实上就是现在就可以连接上板子,烧录进去.但是烧在哪个地址,如何使用,将在后面的章节中介绍.

4.音频处理常用软件Audacity简介与导入RAW数据

可能上面给读者贴图的时,就有读者要问了,这个生成的数据怎么弄成波形的.

答案是:用Audacity这个软件显示的.虽然不用它也有很多办法来完成本文的很多任务,但是本文的主旨是讲解音频的处理的数学与信号处理部分的原理,能利用的工具都要利用起来以提高学习与工作的效率.

先简单介绍一下这个软件,因为后面很多地方都要用到它.简单言之,Audacity是个音频的编辑器,就像文本编辑器是写文章的工具一样.Audacity是个跨平台的软件,不管你用什么系统都能用.怎么安装这里就不多讲,网址在这里:

http://www.audacityteam.org/

它的功能包括:

· 导入各种音频数据(比如我们刚刚生成的Raw格式,后面要讲的wav,aiff,mp3,ogg,flac,aac等)

· 录音/播放

· 复制,粘贴,剪切(比如大家熟知很多文本编辑器的快捷操作都能使用,Home到一段音频的开始,End到结尾,Ctrl+C, Ctrl+P, Ctrl+X,鼠标拖拉,Ctrl+滑轮缩放等等)

· 格式转换

· 生成某些效果(如延迟,回音,变速,滤波等等)

· 频谱分析

等等等,具体的功能我们用到的时再介绍.

这里先说说怎么导入我们刚刚生成的数据.

导入RAW数据.jpg

图 导入RAW数据

选中刚刚生成的raw文件,弹出选项筐:

导入的选项.jpg

图 导入的选项

因为是raw数据,文件中没有任何关于此音频的信息,所以这里要手工填入我们生成数据的参数.填写好之后,点Import就能看到波形了.此时可以各种操作,放大局部,剪切,播放啊.大家可以试试听听同样频率的正弦波,方波,锯齿波效果有何差异.也可以试着改改生成数据代码,试试不同数据的效果.

5.音频的采样率

上面我们代码中设定的采样率为22050,这个频率属于常用的音频采样率之一(虽然很多比较新的声卡已经将最低输入采样率提升到了44100).采样率的意思是对音频信号进行采样的频率,这个频率在音频中间处理与最后播放都要用到.比如作者PC上VIA HD Audio声卡支持的采样率可以在音频设备的驱动中看到:

PC音频设备的采样率设置.jpg

图 PC音频设备的采样率设置

对于使用内部音频外设的嵌入式设备(如F769的内部SAI),支持的采样率跟CPU使用的PLL设定有关.如果使用外部音频IC,那么支持的采样率跟外部音频IC使用的晶体或者时钟输入有关.关于采样率配置的计算,后面章节中会介绍.

如果将22050的数据以44100的频率播放,那么听到的就是频率快了一倍的效果.相反以11025的频率播放则是频率慢了一倍的效果.在使用Audacity导入的时读者可以试验一下这种效果.读者可能对ADC都有一定的理解,采样率越高,对原信号的还原度也就越高.但是占用的存储与传输带宽也越高.这也是后文要讲的音频压缩的缘由之一.这里我们改改代码,以96K的采样率生成同样频率振动正弦波的音频数据.

在生成正弦波形数据的代码中改这一句:

TEST_SAMPLE_RATE = 96000

当然也把生成文件名改一改,以免覆盖之前的生成结果:

f=open('test_2.raw',mode='wb')

运行,导入,看看两个采样率的波形:

图 22050采样率的波形对比96000采样率的波形.jpg

图 22050采样率的波形对比96000采样率的波形

可以看出来下面的形状明显要光滑一点,但是这个例子中两种采样率的数据可能听不出差别来.因为实验音频振动的频率为600Hz,根据Nyquist采样定理,1.2K以上的采样频率已经能够基本还原这个振动了.22050比600高的多,所以听起来差别不大.读者可以改变一下代码,比如生成振动频率为12K的数据,那么这两种采样率生成的效果是可以听出来差别的.

对于比较简单的嵌入式系统来讲,或者早期带宽不富裕的时代,除了压缩的措施,在不怎么重视音频还原度的应用场合(比如电话)采样率与采样深度总是越小越好.比如熟悉通信系统的人可能会听说过"一路PSTN的带宽"这样的术语.这里说的就是一路最简单的电话语音传输带宽:64kbps.这个是8K的采样率8bit的采样宽度得来的带宽需求.8K怎么得来的呢,原来一般人说话声音的主要部分频谱最高只能达到3400Hz,普通人大概是85Hz-1100Hz这个范围.那么根据Nyquist的公式3.4K乘以2再加上一点余量就是8K的采样率.8K的采样率被认为能传输绝大多数的声音内容.这是一段讲话(8K,Signed16bit PCM)的频谱:

一段讲话音频的频谱.jpg

图 一段讲话音频的频谱

可以看频谱集中在400Hz至1000Hz左右. 关于频谱的介绍将会放在后面章节.此处提到频谱是为了说明对于采样语音音频,8000Hz一般被认为是最低采样率. 而对于音乐,最低采样率的要求则要高的多.比如所谓的CD音质的采样率为44100Hz, 所谓的DVD音质的采样率为48000Hz.

6.小结

本文介绍了声音的基本知识与用代码生成raw格式的基本音频数据.还介绍了音频编辑软件Audacity以及使用此工具查看原始音频数据.由于是第一篇,笔者对篇幅长短没有什么概念,不知道写的多了还是少了,这一点非常需要读者反馈.

  • 本文系21ic原创,未经许可禁止转载!

网友评论