汉扬编程 C语言入门 Python数字信号处理应用:声音和信号

Python数字信号处理应用:声音和信号

信号代表随着时间变化的量。这个定义是非常抽象的,所以我们从具象的例子——声音开始。声音源自于空气压力的改变。声音信号代表的是空气压力随着时间的变化。

Python数字信号处理应用:声音和信号

传声器是测量上述变化并产生表示所测声音的电信号的设备。扬声器是通过输入的电信号产生声音的设备。传声器和扬声器都被称为换能器( transducer ) ,因为它们将信号从一种形式转化成另一种形式,也就是变换了能量的形式。

Python数字信号处理应用:声音和信号

这本书重点关注信号处理,包括信号的合成、转换和分析。本书将着重于声音信号,但是其方法也同样适用于电信号、机械振动和其他各个领域的信号。

Python数字信号处理应用:声音和信号

这套方法也同样适用于随空间而不是时间变化的信号,比如远足路线上的海拔变化。还适用于不止一个维度的信号,比如图像–读者可以将它想象为在二维空间中变化的信号。又或者电影,它是二维空间中随着时间变化的信号。

Python数字信号处理应用:声音和信号

本书从简单的一维声音信号开始。

Python数字信号处理应用:声音和信号

本章中的代码在这本书资料库的chap01.ipynb中。同时你也可以通过访问/thinkdsp01获取。

Python数字信号处理应用:声音和信号

1.1周期信号

Python数字信号处理应用:声音和信号

先从周期信号开始,周期信号是在一段时间之后重复出现的信号。比如,你在敲钟时候,钟会振动从而产生声音。把这段声音录制下来,然后绘制出转换后的信号,具体如图1-1所示。

Python数字信号处理应用:声音和信号

这个信号和三角函数相似,也就是说其形状和正弦三角函数的形状一样。

Python数字信号处理应用:声音和信号

图1-1中的信号是周期性的。本书所选择的时段显示了3个完整的重复,这也被称为循环。每个循环的时长被称为周期,图1.1中信号的周期大约2.3ms.

Python数字信号处理应用:声音和信号

图1-1 钟声录音片段

Python数字信号处理应用:声音和信号

信号的频率是每秒钟内周期的数量,即周期的倒数。频率的单位是每秒钟循环数,或者称为赫兹(Hertz) ,英文符号为“Hz"。

Python数字信号处理应用:声音和信号

图1-1所示的信号的频率大约为439 Hz ,比交响音乐对音标准的440 Hz稍低。这个音符的音名叫作A,或者更确切地说是A4,如果你对“科学音调记号法”不熟悉,这里的数字后缀表示的是音符所在的八度。A4意思是中央C上面的那个A ,而A5则比其高出一个八度。详见

Python数字信号处理应用:声音和信号

/wiki/Sientific_pitch_notation。

Python数字信号处理应用:声音和信号

音叉产生的是正弦信号,因为线形的变化是一种简单的谐波运动形式。大部分乐器产生的是周期信号但是这些信号的形状不是正弦式的。例如,图1-2所示的是小提琴演奏鲍凯利尼E大调第五弦乐五重奏第三乐章的录音片段。

Python数字信号处理应用:声音和信号

图1-2所示的信号是周期性的,但是信号的形状更加复杂。周期信号的形状被称为波形。大部分乐器产生的波形比正弦信号的复杂。波形的形状决定了音乐的音色,也就是我们对声音品性的感受。对于比正弦信号更复杂的复杂波形,人们主观上认为它更加富有内涵、更温暖而有趣。

Python数字信号处理应用:声音和信号

图1-2 小提琴录音片段

Python数字信号处理应用:声音和信号

1.2频谱分析

Python数字信号处理应用:声音和信号

本书中最重要的主题是频谱分析,在这个意义下,任何信号都可以表示成一系列不同频率的正弦信号的叠加和。

Python数字信号处理应用:声音和信号

本书中最重要的数学概念是离散傅里叶变换( Discrete Fourier Transform, DFT ) , DFT也就是将信号转换成频谱。频谱是指相加产生信号的正弦波的集合。

Python数字信号处理应用:声音和信号

本书中最重要的算法是快速傅里叶变换( Fast Fourier Transform, FFT ) ,它是计算离散傅里叶变换的一种高效方式。

Python数字信号处理应用:声音和信号

例如,图1-3显示的是图1-2中小提琴录音的频谱。x轴表示的是合成这个信号的频率范围,y轴表示各个频率元素的强度,或者说是振幅。

Python数字信号处理应用:声音和信号

其中频率最低的元素被称为基频。图1-2所示信号的基频在440 Hz左右(实际上要低一点,或者说"降调") 。

Python数字信号处理应用:声音和信号

图1-3 小提琴录音片段的频谱

此信号中,基频具有最高的幅度,所以它也是主频。通常情况下,感知到的声音的音高是由其基频决定的,即使它不是主频。

频谱中的其他峰值的频率还有880、1320、1760和2 200 ,它们是基频的整数倍。这些频率元素被称为谐波,因为它们在乐理概念上跟基频和谐:

880Hz是A5的频率,比基频高出一个八度。一个八度意味着频率的两倍;

1320 Hz接近E6 ,也就是A5的纯五度。如果读者对“纯五度”这样的音程概念不熟悉,可以参考://en.wikipedia.org/wiki/interval_( music) :

1760是A6的频率,比基频高出两个八度;

2200接近C#7,E跟A6是大三度关系。

这些谐波共同构成了A大调和弦,虽然它们并不在同一个八度内。其中的一些音只是近似,因为西方音乐已经按平均律作了调整(参见/wiki/ Equal_ temperament )。

在给定谐波和其各自的振幅之后,读者就可以通过加和正弦波来重建信号了。这我们将在后文中讲解。

1.3信号

我写的Python模块thinkdsp.py中包含信号和频谱分析的类和函数。读者可以在本书的仓库中找到这个模块(参见前言中"代码示例的使用")。

thinkdsp提供了一个显示信号的类,名为Signal,这个类是多个信号类型的父类,包括Sinusoid ,下面又包含正弦和弦信号。

thinkdsp提供产生正弦和余弦信号的函数,调用方法如下:

freq是频率,单位为Hz, amp是振幅,单位未指定,将其设定为1.0意思是我们能录制和回放的振幅的最大值。

offset是相位差,按弧度定义。相位差定义了信号周期的开始。比如, offset=0的正弦信号从sin 0开始,也就是从0开始。offset=π/2则从sinπ/2开始,也就是从1开始。

各信号都有_add_方法。所以读者可以对它们使用+操作:

其结果为SumSignal ,表示了两个或者多个信号的叠加和。

Signal实际上是数学函数的Python表示。大部分的信号( Signal )在全部的值下做了定义-从负无穷到正无穷。

读者如果不对Signal进行求值,那它就没什么作用。在这个语境下, "求值"的意思是,根据一系列的时间点ts ,来计算出对应的信号值ys,我在Wave对象中使用封装了表示ts和ys的NumPy数组。

Wave表示的是信号在一系列时间点下求出的值。每个时间点被称为帧(这是借用了电影和视频的概念)。测度本身被称为样本,虽然"帧"和"样本”在一些情况下可以互换使用。

Signal提供了make-wave,后者返回一个新的Wave对象:

duration参数指的是Wave的长度,单位为秒。star意为开始的时间,单位也是秒。framerate是每秒帧数,它是一个整数,意思也就是每秒内的样本数。

11025帧每秒是在音频文件格式中最常使用的几个帧率之一,这些音频文件包括波形音频文件(Waveform Audio File, WAV )和MP3。

这个例子对信号从t-0到-0.5进行求值,共取得5 513个间距相等的帧( 5513是11 025的一半)。帧间的时间差被称为时间步长,值为1/11 025s,约等于91us.

Wave提供了plot方法,使用方法为pyplot,读者可以这样绘制波形:

pyplot是matplotlib的一部分。很多Python发布版都含有它,否则读者就需要自行安装了。

对于freq=440 ,在0.5秒之内有220个周期,所以其绘制结果看起来会像一大块色块。要放大显示几个周期的波形,读者可以使用segment,它复制了Wave的一段,然后返回一个新波形:

period是Signal的属性,它返回的是信号的周期,单位为秒。

start和duration的单位都是秒。这个例子从mix中复制了前3个周期。其结果是一个Wave对象。

如果我们绘制segment,它的结果如图1-4所示。这个信号包含两个频率元素,所以它比音叉的的信号更为复杂,但是比小提琴的简单。

图1-4 两个三角函数混合信号的片段

1.4 波形的读写

Thinkdsp提供的read_wave可以读取WAV文件并返回一个Wave对象:

Wave提供了write函数,它能写WAV文件:

读者能通过媒体播放器播放WAV文件,从而听到这个Wave对象。在Unix系统上我使用的是aplay,它是一个简单鲁棒的播放器,很懂Linux发行版都有它。

Thinkdsp同时还提供play_wave,它能以子进程运行媒体播放器:

一般情况下,我使用的是aplay,读者当然也可以使用其他播放器。

1.5 频谱

Wave提供了make_spectrum,它返回的是Spectrum:

而Spectrum提供了plot:

我所写的thinkplot模块提供了pyplot中一些函数的封装。它包含于本书的Git仓库中(参见前言的"代码示例的使用")。

Spectrum提供了3种修改频谱的方法。

low_pass,它加载一个低通滤波器,也就是说高于某个给定的截止频率的频率元素被按照一定因数衰减(也就是在大小上降低)了。

high_pass,它加载了一个高通滤波器,也就是说低于某个截止频率的元素被衰减量。

Band_stop,它让处于两个截止频率之间的波段内的频率元素衰减了。

下面的例子将所有高于600的频率衰减了99%:

低通滤波器会移除明亮的高频声音信号,所以其结果的声音比较压抑而且昏暗。读者可以将Spectrum转化回Wave ,然后播放来感受一下:

play方法将波形写入一个文件并播放它。如果读者使用Jupyter Notebook ,就可以使用make_audio ,它会创建一个音频部件用于播放声音。

1.6波形对象

thinkasp.py里面并没有什么复杂的东西。其提供的大部分函数只是对NumPy和SciPy函数的稀薄封装( thin wrappers )。

thinkdsp的初始库包括Signal、wave和Spectrum 。给定Signal,读者就能创建一个wave。给定一个wave,读者就能创建一个spectrum,反之亦然。其关系如图1-5所示。

Wave对象包括3个特性:包含信号参数的NumPy数组vs ;信号开始采样和取值的时间点数组ts;每单位时间的采样数framerate,时间的单位通常是秒,但也可以不是。在我所列举的一些例子中,时间的单位都是天。

图1-5 thinkdsp中各类之间的关系

Wave还提供了3个只读属性: start, end和duration.如果读者修改了 ts ,这些属性也会相应改变。读者可以通过直接修改ts和ys来改变波形。例如:

第一行将波形扩大了2倍,让它听起来更响亮。第二行将被波形按时间移动了,让它晚一秒开始。

此外wave还提供了执行通用操作的方法。例如,与上面相同的两个变换可以写成以下形式:

读者可以在/thinkdsp.html阅读关于这些方法和其他内容的文档。

1.7 信号对象

signal是一个父类,它向各种信号提供通用的函数,比如make_wave 。子类继承了这些方法并提供evaluate ,也就是在给定的时间序列内对信号取值。

例如,Sinusoid是signal的子类,定义如下:

_init_的参数有:

freg

频率,其含义为每秒周期数,单位是Hz.

amp

幅度。幅度的单位比较随意,通常设定为1.0 ,对应为传声器的最大输入或给扬声器的最大输出。

offset

其含义为信号周期的起始。offset的单位为弧度。

func

它是一个Python函数,用来对指定时间点的信号求值。通常它不是np.sin就是np.cos ,对应的分别是正弦信号和余弦信号。

跟很多初始化方法一样,它也是把参数存起来以备未来使用。

Signal提供了make_wave ,如下所示:

start 和duration 分别表示开始和持续的时间,单位为秒。 framerate是每秒帧数(采样数)。

n代表采样的数量,而ts是采样时间的 Numpy 数组。

为计算ys , make_wave 需要引用evaluate ,它是由Sinusoid提供的:

下面我们来逐步地解析这个函数。

1. self.freg 是频率,代表每秒周期数,而ts的各个元素都是以秒计的,所以它们的乘积是从起始时间开始的周期数。

2.P12是一个常数,其值为21,乘上P12之后就把周期转换成了相位。读者可以把相位理解为弧度形式的"从起始时间开始的周期数",而每个周期的弧度是2π。

3. self.offset 是t=0时刻的相位。其作用是把信号在时域上向左或者向右移动一定距离。

4·如果self.func是np.sin 或者np.cos ,其值便是处于-1~1。

5·乘上self.amp之后产生的信号范围为- self.amp到self.amp。

在数学意义上, evaluate 的形式如下:

y = Acos (2π ft +фo)

其中A是幅度,是频率,是时间, ф0是相位偏移。这里看起来像是我写了大段的代码,而仅仅为了对一个简单的表达式求值,但是正如我们将要看到的,这段代码提供了处理所有信号的构架,而不仅是三级函数信号的。

本文节选自《Python数字信号处理应用》

本书介绍了使用Python语言实现数字信号处理的方法,内容共有11章,以Python代码为示例由浅入深地向读者介绍了数字信号处理的相关知识及其应用。书中涉及周期信号及其频谱、波形的谐波结构、非周期信号以及频谱图、噪声、自相关函数、离散余弦变换和离散傅里叶变换、滤波、卷积、微分与积分、调制采样等数字信号处理相关技术。

本文来自网络,不代表汉扬编程立场,转载请注明出处:http://www.hyzlch.com/cjia/6573.html

C语言操作符详细介绍(干货教学内容!!!)

(编程语言数据类型)C语言编程第8讲——彻底掌握C语言的数据类型

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

返回顶部