UPF 音频信号处理笔记(全)
001:课程预告片 🎵

在本节课中,我们将简要了解《音乐应用中的音频信号处理》这门课程的核心内容、教学团队以及你将学习到的令人兴奋的技术。
音乐不仅可以聆听和欣赏,也可以从工程学的角度进行研究。
我叫Jerra,教授音频处理课程已超过25年。我曾在斯坦福大学为不同背景的学生举办过多次暑期研讨会,学员包括音乐家、工程师以及人文学科背景的人士。目前,我在巴塞罗那庞培法布拉大学为计算机科学和电气工程专业的学生授课。通过这门《音乐应用中的音频信号处理》课程,我尝试将所有这些经验带入大规模开放在线课程这个迷人的新世界。
大家好,我叫Julia Smith,我在计算机音乐与声学研究中心(我们称之为CCRMA)教授信号处理课程。我的课程旨在从信号处理的角度理解音乐技术的工作原理,这涉及一些数学、一些软件,并希望带来很多乐趣。我们期待在课程中见到你。
接下来,让我展示一个我们在课程中将学习到的更具实践性的例子。
这是一个我演奏大提琴声音的频谱图,让我们听一下这个声音。
从频谱中,我们可以识别出声音的基频,其公式可表示为 f0。然后可以将其合成为一个正弦波,我们也来听一下。
我们还可以检测出该基频的谐波。但声音中不止有这些谐波。
我们可以从原始信号中减去谐波,得到剩余的频谱,并听听这个剩余部分是什么。
这个残差频谱图可以用我们所谓的随机模型来近似。然后,我们可以将谐波模型和随机模型组合起来。这种表示方法非常有用且强大,我们可以用它做很多事情。
例如,我们可以在重新合成声音时,改变我演奏的一些音符。让我们听听看。
或者,我们可以通过任意改变谐波和时间拉伸声音来生成一些奇怪的声音,就像这样,让我们听听看。
可能的变换方式是无限的,实际上只受限于你自己的创造力。
对我们来说,准备这门课程是一次迷人的经历。我希望它对你们许多人都有用。如果你热爱音乐和技术,这就是为你准备的课程。你将学到很多东西,并在此过程中获得乐趣。所以,我期待在课堂上见到你。

本节课中,我们一起了解了《音乐应用中的音频信号处理》课程的概貌、讲师背景以及一个核心应用示例:通过分析声音的谐波与随机成分,并利用f0(基频)等概念,我们可以对声音进行分解、建模和创造性的再合成。这为后续深入学习音频信号处理技术开启了大门。
002:课程介绍与概览 🎵

在本节课中,我们将学习这门课程的基本信息、目标、结构以及所需资源。课程旨在为对音乐声音分析与处理感兴趣的学员提供信号处理与编程技术的入门知识。
课程团队介绍 👥
上一节我们介绍了课程的基本定位,本节中我们来认识一下教学团队。
课程由两位主讲教师和两位助教共同负责。
- 朱利叶斯·史密斯教授:来自斯坦福大学音乐与声学计算机研究中心(CCRMA),是本课程主讲教师的博士生导师,也是音频信号处理领域知识的首要来源。
- 哈维尔·塞拉教授:来自巴塞罗那庞培法布拉大学,领导一个音乐技术研究小组,是本课程的主讲教师。
- J·斯里尼瓦萨穆尔蒂与桑·卡布卢拉蒂:两位助教,目前均在巴塞罗那在哈维尔·塞拉教授的指导下攻读博士学位,研究方向为音乐与音频技术。
尽管展示了一些表演照片,但教学团队并非专业演奏家。团队的核心驱动力是对音乐的热爱,以及致力于开发对音乐有实际意义的工程工具。
课程内容与目标 🎯

认识了教学团队后,我们来看看这门课程具体教授什么以及适合谁学习。
课程主要涵盖三个核心领域:信号处理、音频音乐以及相关的编程技术,最终目标是帮助学员理解并创造声音。
本课程适合任何有兴趣学习如何分析与处理音乐声音的人。但学员需要具备一定的技术背景,能够理解数学公式、使用一门编程语言以及操作声音处理软件。学员不一定必须是工程专业学生。
课程结构与评估方式 📚
了解了课程目标,接下来我们详细说明课程是如何组织的。
课程为期10周,每周聚焦一个特定主题,并通过三种视角进行阐述:
- 理论讲座:从数学和信号处理的角度进行讲解。
- 编程讲座:从应用角度出发,教授如何实现相关算法。
- 演示讲座:展示这些技术的实际应用及其对声音和音乐的意义。
此外,每周还会提供一些高级主题和参考资料,以便学员探索相关领域。
以下是每周的评估方式:
- 每周测验:计5分。
- 每周编程作业:计10分。
- 两次问卷调查(课程开始与结束时各一次):各计1分,共计2分。我们非常鼓励学员填写。
开放资源与许可 📄
明确了学习路径后,我们必须了解课程所使用的全部资源。
本课程遵循开放共享原则,所有材料均采用开放许可。
- 课程材料:所有视频、幻灯片及其他创建的材料均在知识共享署名-非商业性使用-相同方式共享(CC BY-NC-SA) 许可下提供。
- 软件:课程中开发和使用的所有软件均基于GNU通用公共许可证(GPL) 免费提供。
- 参考资料:大部分非原创参考资料来自维基百科等开放资源库。
- 音频素材:所有使用的声音样本均来自采用知识共享许可的网站 freesound.org。
总结与下节预告 ✅
本节课中,我们一起学习了《音乐应用中的音频信号处理》课程的概况。我们介绍了教学团队,明确了课程的内容、目标与受众,梳理了以周为单位、多视角结合的教学结构与评估方式,并强调了课程所采用的开放资源原则。
在深入课程具体内容之前,请务必访问课程网站以获取更详细的信息,确保充分了解课程背景与运行方式。

本节内容到此结束,我们下节课再见。
003:音频信号处理导论 🎵

在本节课中,我们将学习音频信号处理的基本概念,并概览其在音乐领域的常见应用。我们将介绍音频信号处理是什么,并探讨其在存储、压缩、效果变换、合成以及音频描述等方面的具体应用。

什么是音频信号处理? 🎛️
音频信号处理是一个工程领域,专注于通过计算方法有意地改变声音。我们可以用一个系统框图来表示这个概念:一个音频信号处理系统能够接收一个音频信号作为输入,然后通过一些控制(可以是人工控制或自动控制),生成另一个信号。这个输出信号可以是另一个音频信号,也可以是其他类型的信息。
由于音频信号(声音)可以用数字或模拟格式进行电子表示,因此信号处理可以在任一领域进行。模拟处理器直接对电信号进行操作,而数字处理器则对信号的二进制表示进行数学运算。
模拟信号与数字信号 📊
模拟声音通常是电信号,其电压水平代表了声音的空气压力波形。它是一个连续函数,如上图顶部所示。另一方面,数字表示将压力波形表达为二进制数字,因此是一个离散函数,如下图底部所示。
数字表示允许使用微处理器和计算机。尽管从模拟到数字的转换可能存在损失,但大多数现代音频系统都采用这种方法,因为数字信号处理技术比基于模拟的技术更强大、更高效。

音频信号处理的应用领域
上一节我们介绍了音频信号处理的基本定义和信号类型。本节中,我们来看看它在音乐领域的一些核心应用。

声音存储 💾
音频信号处理的一个应用是声音的存储,即录音与回放。声波(如人声、环境声音或音乐)的数字表示可以作为电或机械记录存储在介质中,并可以从这些记录中重新创建。例如,CD光盘可以编码、记录音频信号的数字表示。
数据压缩 📉
另一个信号处理应用是数据压缩,也称为音频编码。其目标是减少数字音频流的带宽需求和音频文件的存储大小。

压缩技术有两种类型:无损压缩(不丢失任何信息)和有损压缩(会丢失一些信息,但希望丢失的信息在感知上不相关)。
以下是一个感知音频编码器的例子:
# 伪代码示例:将WAV文件转换为MP3
input_file = "audio.wav" # 未压缩的原始数字音频文件
output_file = "audio.mp3" # 经过感知编码压缩后的文件
audio_coder.encode(input_file, output_file, format="mp3")
它接收一个WAV格式的文件(这是声音在数字域的直接表示,未经压缩),通过一个感知音频编码器,能够将该文件转换为更小的MP3格式文件。MP3是一种基于声音感知特性的高度压缩格式。本课程将涵盖的许多基本技术正是这类音频压缩系统的基础。
声音效果与变换 🎚️

一大类应用涉及执行声音变换。这类应用用于后期制作和音乐创作。
例如,Audacity应用程序包含多个插件,可以进行多种音频信号变换。左边是一个动态压缩器,右边是一个混响器。这是两种可以通过音频信号处理技术实现的变换,但还有很多其他类型,包括均衡器、镶边器、相位器、合唱、变速、时间拉伸、人声效果、3D音频效果、变形等等。本课程将针对其中一些应用进行讲解,所解释的大多数方法在这一领域都有广泛的应用潜力。
声音合成 🎹
信号处理的传统用途之一与声音合成有关,其目的是通过模仿现有声音或创造新的音色来生成声音。
以下是三种不同合成技术的框图:
- 减法合成:从一个丰富的声音开始,通过滤除该声音的某些成分来创造另一个声音。
- 调频合成:基于一个振荡器调制另一个振荡器,调制振荡器的频率,从而利用这种技术获得非常多样的声音。
- 加法合成:也许是最直观的合成类型,因为它基于将一些正弦波(振荡器)相加,从而通过简单声音的叠加来创造复杂声音。

当然,多年来还开发了许多其他合成技术,包括粒子合成、物理建模、波表合成、采样或频谱合成等。我们将在课程中开发和使用其中一些技术,特别是加法合成和减法合成。
声音描述与分析 🔍
我想提到的最后一组应用与声音描述有关。这些技术用于分析音频信号,目的是描述和建模声音的有意义的特征。
这是一个在过去几年中极大扩展的领域,在信息检索或音乐信息检索领域非常重要。例如,这里展示了一个算法的框图,该算法能够从音频信号中提取一些有意义的音乐概念,特别是乐曲的调性(这与乐曲片段的和谐或和弦有关)。它从一个音频信号开始,执行不同的分析步骤,最终能够识别出乐曲的调性。
当我们谈论描述时,可以对一个特定的音频录音进行多种类型的描述。通常我们指的是不同层次的描述:
- 低层描述符:例如响度、音色、音高,这些很大程度上直接来自音频信号。
- 中层描述符:更具音乐意义,涉及节奏、和声、旋律等概念。
- 高层描述符:更接近我们对音乐的感知,可能涉及流派、乐曲情感或相似性等概念。
声音的自动描述将是本课程所涵盖主题的一个重要应用。我们虽然不会深入讲解中高层特征的提取,但我们在低层特征分析上的讨论,是整个声音和音乐描述主题的基础。
参考资料与软件工具 📚

关于音频信号处理的概述和信息有很多。维基百科的“音频信号处理”词条是一个很好的概述参考。
我们在课堂上将使用的所有软件都是开源的,例如我展示过截图的Audacity程序,可以从其官网下载。
最后,本课程中我们将使用的所有幻灯片和代码也以开放许可提供。幻灯片和文档采用知识共享“署名-非商业性使用-相同方式共享”许可,我们将使用的代码采用GNU通用公共许可证。所有代码和幻灯片都可以从指定的GitHub账户获取。

总结 📝

本节课中,我们一起学习了音频信号处理的基本定义,了解了模拟与数字信号的区别,并概览了其在音乐存储、数据压缩、效果变换、声音合成以及音频描述与分析等多个关键领域的应用。希望这次概述能激发你继续学习本课程的兴趣,并让你相信为此付出的努力是值得的。我们下节课再见。
004:课程大纲 📚



在本节课中,我们将概述整个课程将要学习的核心主题。尽管这些内容仅覆盖音频信号处理领域的很小一部分,但它们对于理解音频处理中的关键概念和方法,尤其是在音乐应用中的系统,至关重要。

上一节我们介绍了音频信号处理领域的概况,作为课程的引入和动机。本节中,我们将具体勾勒出课程将涵盖的主题。

以下是本课程十周内容的具体安排:
第一周:课程介绍与数学基础
本周我们将介绍音频信号处理领域的背景,概述课程大纲,并在下一节课中回顾学习本课程所需的基础数学知识。

第二周:离散傅里叶变换
我们将从介绍离散傅里叶变换开始。这个变换是我们后续所有工作的基础。我们将深入理解其每一个组成部分:
- DFT方程:
X[k] = Σ_{n=0}^{N-1} x[n] * e^{-j2πkn/N} - 复指数
- DFT中的标量积
- 复正弦波和实正弦波的DFT
- 逆DFT:
x[n] = (1/N) Σ_{k=0}^{N-1} X[k] * e^{j2πkn/N}

第三周:傅里叶变换的性质与实现
为了更好地理解DFT,我们将学习傅里叶变换的基本定理和性质:
- 线性、移位、对称和卷积
- 能量守恒与分贝
- 相位、卷绕与零填充
- 快速傅里叶变换:实现DFT的算法
- 结合零相位窗的FFT
- 构建一个完整的分析-合成系统
第四周:短时傅里叶变换
我们将学习短时傅里叶变换,这是第一个可用于分析随时间变化的真实声音的傅里叶变换版本。我们将介绍本课程第一个具有实际用途的完整分析-合成系统:
- STFT方程
- 分析窗
- FFT大小与跳跃大小
- 理解STFT的核心:时频权衡
- 逆短时傅里叶变换

第五周:正弦模型
在STFT的基础上,我们将构建正弦模型:
- 正弦模型方程
- 从频域/频谱视角看正弦波
- 如何寻找正弦波:谱峰的概念
- 在时变频谱(谱图)中分析时变正弦波
- 基于正弦建模的分析-合成系统
第六周:谐波模型
我们将假设声音是谐波的,从而发展出更强大的谐波模型,即在正弦模型上增加所有正弦波都是基频整数倍的约束:
- 谐波模型方程
- 区分三个易混淆的概念:正弦波、分音、谐波
- 单音与复音信号
- 如何检测谐波:基频检测


第七周:正弦+残差模型
许多声音无法用纯正弦波或谐波很好地建模,因此我们需要处理起振、非线性、噪声等部分。为此,我们引入正弦+残差的建模思想:
- 用正弦波加残差来建模声音
- 随机模型:如何使用随机信号的概念建模
- 如何用随机模型近似声音
- 构建正弦/谐波 + 残差模型
- 如何用随机信号的概念对残差进行建模

第八周:应用一:声音变换
本课程聚焦两类应用,本周介绍第一类:声音变换。我们将回顾已介绍的模型,并展示利用它们可能实现的变换类型:
- 短时傅里叶变换:滤波、变形
- 正弦模型:时间与频率缩放
- 谐波+残差模型:音高移调
- 谐波+随机模型:时间拉伸与变形

第九周:应用二:声音描述
本周介绍第二个核心应用:声音描述。利用已学技术,我们可以提取与描述声音相关的特征:
- 从频谱分析中获取音频特征
- 如何使用这类技术描述更复杂的音乐或信号
- 处理声音集合(而非单个声音)的分析概念
第十周:课程回顾与拓展
在最后一周,我们将:
- 回顾所有模型
- 概述课程中未涵盖的相关主题
- 介绍其他音频声音模型及非音频模型
- 总结并展望音乐音频信号处理之外的领域


所有课程内容均可在线上获取,并采用开放许可,请随时查阅。


本节课中,我们一起快速浏览了课程大纲,概述了将要学习的理论主题。从下一节课开始,我们将正式深入音频信号处理的精彩世界。感谢关注,我们下节课再见。
005:基础数学 📐
在本节课中,我们将学习理解后续课程内容所必需的基础数学概念。这些概念包括正弦波、复数、欧拉公式、复正弦波、序列的点积、奇偶函数以及卷积。掌握这些知识将为深入学习音频信号处理打下坚实基础。
正弦波 📈
我们的第一个基本概念是正弦波。正弦函数或正弦波是一种描述平滑、重复振荡的数学曲线。它在物理学、工程学等许多领域经常出现。

正弦波的方程如下:
x(n) = A * cos(ωnT + φ)
其中:
- A 是正弦波的振幅。
- ω 是角频率,单位为弧度。
- nT 是时间,
n是时间索引,T是采样周期。 - φ 是初始相位,单位也是弧度。
频率也可以用赫兹表示。要将角频率 ω 转换为赫兹频率 f,使用公式:f = ω / (2π)。

正弦波最常见的视觉表示是振幅随时间变化的波形图。例如,我们可以用Python代码生成一个频率为1000赫兹的正弦波:
import numpy as npA = 0.8 # 振幅
f = 1000 # 频率 (Hz)
phi = 0 # 初始相位
Fs = 44100 # 采样率# 生成时间数组
t = np.arange(-0.002, 0.002, 1/Fs)
# 生成正弦波
x = A * np.cos(2 * np.pi * f * t + phi)
复数 🔢
接下来是复数的概念。复数由两部分构成:实部和虚部。

一个复数 z 可以表示为:z = a + jb
其中:
- a 是实部。
- b 是虚部。
- j 是虚数单位,定义为
j² = -1。
复数通常在复平面上表示,其中横轴是实部,纵轴是虚部。模为1的所有复数构成的圆称为单位圆。

复数的两种形式
复数有两种表达形式:直角坐标形式和极坐标形式。
- 直角坐标形式:直接给出实部
a和虚部b,即z = a + jb。 - 极坐标形式:将复数视为一个从原点出发的向量,用模
A和角度θ表示。

两种形式可以互相转换:
- 模 A = √(a² + b²)
- 角度 θ = arctan(b / a)
- 实部 a = A * cos(θ)
- 虚部 b = A * sin(θ)
极坐标形式在进行复数的加法和乘法运算时更为直观,我们将在课程中频繁使用。
欧拉公式与复正弦波 🔄
现在,让我们把正弦函数和复数结合起来。欧拉公式建立了复数直角坐标和极坐标之间一个非常有用的关系。
公式如下:e^(jφ) = cos(φ) + j sin(φ)
这个公式表明,一个模为1的复指数 e^(jφ) 可以分解为实部 cos(φ) 和虚部 j sin(φ)。这个公式是理解离散傅里叶变换的基础。
利用正弦函数、复数和欧拉公式,我们可以引入复正弦波。
一个复正弦波函数 x̄(n) 可以表示为:x̄(n) = A * e^(j(ωn + φ))
根据欧拉公式,它等价于:A * [cos(ωn + φ) + j sin(ωn + φ)]
我们通常处理的是实信号(实正弦波),但傅里叶变换处理的是复正弦波。一个关键的数学技巧是:一个实正弦波可以由两个复正弦波相加得到。
具体公式为:A * cos(ωn + φ) = (A/2) * e^(j(ωn + φ)) + (A/2) * e^(-j(ωn + φ))
这样,两个复正弦波的虚部相互抵消,只留下我们感兴趣的实部。
由于复正弦波需要三维空间才能完整绘制,通常的替代方法是将其实部和虚部作为两个独立的函数画出。
序列的点积(标量积)⚖️
另一个需要熟悉的概念是序列的点积(或标量积)。这是一种在两个等长序列之间进行的代数运算,结果是一个单一数值。
代数定义如下:给定两个序列 x(n) 和 y(n),它们的点积为:
<x, y> = Σ [x(n) * y*(n)]
其中 y*(n) 表示 y(n) 的复共轭。
点积的一个重要性质是:当两个序列正交时,它们的点积为 0。即,如果 <x, y> = 0,则 x 与 y 正交。
从几何角度看,点积可以理解为将一个序列投影到另一个序列上。这个操作是离散傅里叶变换的核心运算之一。
奇函数与偶函数 ⚖️
在信号分析中,我们还会遇到奇函数和偶函数的概念。
- 偶函数:满足
f(-n) = f(n)。图形关于Y轴对称。余弦函数cos(ωn)是一个典型的偶函数。 - 奇函数:满足
f(-n) = -f(n)。图形关于原点对称。正弦函数sin(ωn)是一个典型的奇函数。
理解函数的奇偶性对于分析信号的对称性很有帮助。
序列的卷积 🌀
最后要提到的概念是序列的卷积。卷积是一种对两个序列进行的数学运算,产生第三个序列,可以将其视为对原始序列之一的修改版本。
卷积的方程如下(离散形式):
(x₁ * x₂)(n) = Σ [x₁(k) * x₂(n - k)]
卷积运算与互相关类似,常用于实现声音的滤波。理解卷积也有助于理解傅里叶变换的许多性质。

从图形上看,两个序列卷积的结果通常是一个比原始序列更平滑的函数。
参考资料与总结 📚

本节课所强调的概念在许多教科书和在线资源中都有详细阐述。常用的参考资料包括维基百科的相关条目,以及Julius O. Smith III关于离散傅里叶变换数学基础的网站,后者非常推荐大家阅读。

总结:在本节课中,我们一起回顾了贯穿本课程所需的基础数学概念,包括正弦波、复数及其运算、欧拉公式、复正弦波、序列的点积、函数的奇偶性以及卷积。请确保在开始傅里叶变换等核心主题之前,对这些概念有基本的把握。扎实的数学基础将使我们后续的学习之路更加顺畅。
006:Audacity入门教程 🎧
在本节课中,我们将学习如何使用Audacity,这是一款免费、开源的音频编辑软件。我们将介绍其基本界面、如何打开和播放音频文件、如何进行波形和频谱图的可视化,以及如何应用简单的音频效果。
概述

Audacity是一款跨平台的音频软件,由志愿者团队维护。它功能强大且易于使用,非常适合用于音频编辑和基础分析。本节课将引导你完成软件的安装、基本操作和核心功能。
下载与安装

首先,你需要下载并安装Audacity。
- 访问Audacity官方网站或通过搜索引擎找到其SourceForge页面。
- 在下载页面选择适合你操作系统的版本进行下载和安装。
- 本课程演示使用的是Ubuntu(Linux发行版)环境。
界面与基本操作
现在,让我们打开Audacity并熟悉其界面。

打开音频文件
- 启动Audacity后,点击菜单栏的 文件 -> 打开。
- 浏览并选择一个音频文件(例如课程提供的
piano_Do_W.wav文件)。 - 软件会询问是否创建副本,建议选择“制作副本”,以便在不影响原文件的情况下进行操作。

打开文件后,主界面会显示该音频的波形。界面底部会显示该文件的格式信息,例如:
- 声道:单声道 (Mono)
- 采样率:44100 Hz
- 位深度:32位浮点数 (Audacity内部处理格式)
播放与导航
- 播放:点击工具栏的播放按钮即可播放整个音频。若要播放特定片段,先用鼠标在波形图上拖拽选中区域,再点击播放。
- 缩放与移动:使用工具栏的放大镜工具或快捷键可以缩放波形视图,方便查看细节。
时间显示
精确的时间信息对音频处理至关重要。
- 波形图横轴以秒为单位。
- 选中一个片段后,可以在界面左下方的状态栏看到更精确的起始时间和选区长度。
- 建议将时间显示格式设置为“毫秒”,以获得最精确的读数。例如,一个选区可能起始于
402毫秒,长度为220毫秒。
音频可视化
除了波形图,Audacity还提供了强大的频谱图功能,帮助我们分析声音的频率成分。
使用频谱图
- 在波形图左侧的下拉菜单中,将显示模式从“波形”切换为“频谱图”。
- 频谱图以颜色深浅表示不同频率的强度,横轴是时间,纵轴是频率。
- 通过频谱图,我们可以清晰地看到声音的谐波结构以及音符的起始位置,这比单纯看波形更直观。
调整频谱图参数
频谱图的分析效果可以通过参数进行调整。
- 点击菜单 编辑 -> 首选项。
- 在“频谱图”部分,可以修改如窗口大小、最大频率等参数。
- 例如,将最大频率设置为
1500 Hz,频谱图将只显示该频率以下的部分,这有助于聚焦观察低频内容。
音频效果处理
Audacity内置了多种音频效果,并支持通过插件扩展更多功能。效果菜单位于菜单栏的“效果”下。
应用均衡器效果
让我们尝试一个简单的效果——均衡器(EQ)。

- 选中一段音频或整个音轨。
- 点击 效果 -> 均衡器。
- 均衡器界面提供了多种预设曲线。例如,选择“电话”预设,它会模拟电话线的频响特性,衰减低频和高频。
- 点击“应用”后,音频即被处理。在波形图上变化可能不明显,但切换到频谱图视图,可以清晰地看到高频部分(以及调整视图后的低频部分)的强度已被衰减。
总结与资源

本节课我们一起学习了Audacity的基础操作。我们介绍了如何:
- 下载和安装软件。
- 打开、播放和导航音频文件。
- 使用波形图和频谱图进行音频可视化。
- 应用简单的音频效果(如均衡器)。
Audacity的功能远不止于此,你可以通过其官方文档探索更多编辑功能,如剪切、粘贴、混音等。
课程使用的钢琴等音频样本可以从 freesound.org 等网站获取。

希望你能够下载并使用Audacity,通过简单的编辑练习为后续课程做好准备。
007:声波可视化分析入门 🎵
在本节课中,我们将要学习一款名为 Sonic Visualizer 的音频分析与可视化工具。这款工具与之前介绍的 Audacity 形成互补,更侧重于研究和分析声音的高级特性。
上一节我们介绍了 Audacity,它是一款非常适合进行简单编辑和声音可视化的声音编辑器。本节中,我们来看看另一款声音工具——Sonic Visualizer。
获取与安装 Sonic Visualizer
Sonic Visualizer 由伦敦玛丽女王大学开发,支持所有主流平台,并以非常开放的 GNU 通用公共许可证发布。这是一个非常优秀的工具,强烈建议你安装并使用它,这对本课程的学习将大有裨益。
首先,我们来看看如何获取它。
如果你在谷歌搜索 “Sonic Visualizer”,会找到其官方网站 sonicvisualiser.org。网站上提供了大量相关信息、使用该工具的项目,以及一个重要的插件系统——Vamp 插件系统。许多开发者利用这个系统为 Sonic Visualizer 开发了各种分析工具,因此我们可以使用大量声音分析算法和工具。
在下载页面,你可以获取适用于各个平台的版本。本课程使用 Ubuntu 系统,因此我们下载 Ubuntu 版本。我已经完成了下载,让我们直接进入程序。
主界面与基本操作
这是 Sonic Visualizer 的主界面。我们要做的第一件事是打开一个声音文件。


我打开了存放课程所有文件的目录。例如,让我们使用这个 speech_female 声音文件。
这是波形图。当然,你可以进行缩放。它有两个声道,这很好。我们可以播放它。这是 “Venera” 的发音。
导航功能相当出色和强大。我们可以浏览整个声音,并放大到想要分析的特定部分。
窗格与图层概念
Sonic Visualizer 的一个基本概念是窗格和图层。窗格基本上就是这些窗口,例如,我们现在显示波形的就是一个窗格。
让我们打开另一个窗格,例如我们之前在 Audacity 中也提到过的频谱图。

在这里,分析参数的控制稍微容易一些,我们在右侧面板就可以调整。例如,我们可以增加窗口大小,然后可以更容易地放大,看到这个人声的谐波。我们还能看到静音区域,以及波形中颜色较深、对应此处频谱图中较暗区域的辅音部分。

这相当不错。例如,我们可以关闭这个窗格,然后打开另一个非常有用的窗格——频谱窗格,它本质上是频谱图的一个切片。

如果我们移动波形中的光标,频谱图会随之变化。默认情况下,水平刻度(称为 bins)是对数刻度。我个人更喜欢使用线性刻度。这就是频谱的线性刻度。通常,你会希望以优化整个显示范围的方式缩放或显示频谱,这是一种很好的显示方式。
这非常直观,有助于我们理解声音的特性。例如,在这里,处于声音的元音部分,我们可以看到谐波的峰值,这里看到的形状大致是声音的共振峰。这很好。
当然,我们可以非常容易地更改参数。

插件系统

让我们回到频谱图窗格。

现在,我们想谈谈它提供的插件系统。在这里,我们以一种相当合适的方式显示频谱图。
我已经安装了几个插件。首先,我们可以访问 Sonic Visualizer 的插件页面,那里描述了其插件系统的工作原理,甚至包括如何为其开发插件,并提供了一个使用该系统开发的插件列表,数量相当多。

我下载了一些。回到 Sonic Visualizer 程序,在 “变换” 菜单下,可以看到我已安装的所有插件列表。
例如,让我们使用这个 “音频音高检测器” 插件。点击它,会出现分析界面。这个插件由 Poziier 开发,由 Chris Cannam 移植。它使用了一种我们将在课程中讨论的、用于检测声音基频的算法——YIN 算法。
这是一个使用快速傅里叶变换的实现。我们点击确定。
这个插件会分析整个声音,并显示基频。它会以图层的形式显示在频谱图上方。这就是我之前提到的另一个概念——图层。图层是在同一窗格上叠加显示多种信息的方式。

在这里,我们看到了频谱图,以及叠加在其上的、分析得到的音高曲线图层。如果我们放大,可以清晰地看到这条绿色的音高曲线。我们将在课程中讨论它,但可以看到它识别出了声音的第一谐波。在一些没有谐波的区域,它显然会感到困惑,但在有谐波的人声部分,它能相当容易地找到基频。如果我们增大频谱图的窗口大小,使谱线更清晰,这一点可能会更明显。
我们可以看到,音高分析在识别这个人声的基频方面做得不错。
总结与回顾
关于 Sonic Visualizer 我想介绍的内容就是这些。让我们回到幻灯片。
你可以访问 Sonic Visualizer 页面获取程序信息。它有一个专门的 Vamp 插件页面,可以从 Sonic Visualizer 页面访问。我们提到了将在后续课程中介绍的 YIN 音高检测算法,并有一些相关参考文献。这个音高示例声音来自 FreeSound 网站。
本节课到此结束。我们介绍了另一款声音可视化工具,它将对我们课程中将要讨论的更高级、更科学的可视化分析非常有用。
非常感谢,我们下节课再见。


本节课中我们一起学习了:
- Sonic Visualizer 的获取、安装与基本界面。
- 核心概念:窗格(用于显示不同视图的窗口)和图层(在同一窗格上叠加信息)。
- 基本操作:打开文件、查看波形、频谱图和瞬时频谱。
- 强大的插件系统(Vamp 插件),并以音高检测插件为例进行了演示。
- 该工具在高级音频分析与研究中的应用潜力。
008:SMS工具简介
在本节课中,我们将学习专门为本课程开发的一套音频处理工具——SMS工具。这套工具基于Python编写,将在后续的编程课程和演示中被深入使用。我们将了解如何获取、运行这些工具,并通过几个简单的例子来体验其核心功能。
工具获取与运行环境

上一节我们介绍了Audacity和Sonic Visualizer,本节中我们来看看专为本课程设计的SMS工具。
这套工具托管在一个GitHub代码仓库中。我建议您下载最新的版本,因为我会持续更新和修复其中的问题。您可以下载一个压缩包文件,并按照说明进行编译。在Ubuntu系统下,这个过程并不复杂,具体的细节我们会在编程课程中详细讨论。
SMS工具是一套命令行工具,没有可以直接点击的图标。您需要通过终端来访问它们。以下是如何进入工具目录并运行主界面的基本步骤:

- 打开终端,默认会进入您的主目录。
- 下载SMS工具包后,会生成一个名为
sms-tools-master的目录。 - 使用
cd命令进入该目录。 - 工具的主要演示界面位于
software/models子目录下。 - 通过执行
python modelsGUI.py命令来启动图形界面。
以下是进入目录并启动界面的命令示例:
cd sms-tools-master
cd software/models
python modelsGUI.py
核心分析/合成模型界面
启动 modelsGUI.py 后,您将看到一个图形界面,其中集成了本课程将讨论的所有核心分析/合成模型。
以下是该界面中包含的主要模型列表:
- DFT:离散傅里叶变换。
- STFT:短时傅里叶变换。
- 正弦模型:用于分析声音中的确定性成分。
- 谐波模型:专门分析谐波结构。
- 随机模型:用于分析声音中的噪声成分。
- 信号+残差模型:结合了确定性和剩余成分。
- 谐波+随机模型:这是最复杂的模型之一,将声音分解为谐波和随机两部分。
短时傅里叶变换示例
让我们以短时傅里叶变换为例。在界面中选择“STFT”模型,并加载一个音频文件(例如一个管弦乐片段)。使用默认参数点击“计算”后,程序会进行分析。
分析结果会显示原始输入信号的波形、计算得到的频谱幅度图和相位图。最后,程序会重新合成一个输出信号。通过播放可以发现,合成信号与原始信号听起来基本一致,这说明该分析-合成系统作为一个恒等系统工作良好。
谐波+随机模型示例
接下来,我们尝试更复杂的“谐波+随机”模型。加载一个萨克斯短语音频,并使用默认参数进行分析。
分析完成后,界面会显示:
- 原始输入声音。
- 分析表示,其中同时展示了谐波成分和被称为“随机表示”的成分。
- 合成后的输出声音,其波形与原始信号非常相似。
这个模型的关键在于它将声音分解为两个部分:
- 正弦(谐波)部分:捕捉了声音中大部分有音高的、有趣的成分。我们可以单独聆听这部分。
- 残差(随机)部分:被建模为一个随机信号,它包含了诸如呼吸声、气流声等在许多声音中都非常相关的成分。这部分听起来比较柔和,但确实存在。
声音变换界面

除了分析合成,SMS工具还提供了基于上述模型对声音进行变换的功能。
让我们关闭模型界面,进入变换工具目录。通过执行 python transformationsGUI.py 来启动变换界面。
以下是变换界面提供的一些主要功能列表:
- 使用STFT进行变形。
- 使用正弦模型进行变换。
- 使用谐波模型进行变换。
- 使用随机模型进行变换。
- 使用谐波+随机模型进行变换或变形。
声音变换实践
我们选择“谐波+随机”变换,并加载一个简短的萨克斯短语。
首先,使用“恒等参数”(即所有变换参数设置为不改变声音的状态)进行处理并聆听,以确保系统工作正常。这相当于频率缩放为1,时间缩放为线性。
然后,我们可以尝试各种变换:
- 移调:将频率缩放参数设置为2(即升高一个八度),然后聆听变换后的声音。
- 频率拉伸:让声音的谐波部分随着时间逐渐变得不谐和。例如,设置频率拉伸参数从1.01变化到1.1,聆听产生的“失谐”效果。
- 时间拉伸:改变声音的时长。例如,让声音前半段加速(时间值变小),后半段恢复原速,从而产生先快后慢的效果。
这些变换让我们能够以创造性的方式操纵声音的各个方面。
总结与展望
本节课中,我们一起学习了SMS工具集的基本情况。在本次第一周的三节演示课中,我们总共介绍了三种工具:Audacity、Sonic Visualizer和我们自主开发的SMS工具。
希望这些内容能让您对可用于处理音频的工具类型有一个概览,并激发您深入学习本课程、理解其背后原理的兴趣。在后续的演示课中,我们将从实际应用角度介绍这些工具;而在编程课和理论课中,我们将深入探讨所有这些技术背后的原理。

这就是今天的全部内容,我们下节课再见。
009:Python入门 🐍
在本节课中,我们将学习Python编程语言的基础知识。Python是我们将在整个课程中,特别是在所有编程作业中,使用的高级编程语言。请注意,这不是一门专门的编程课程,我们只会编写简单的程序。你不需要有广泛的编程背景,但请确保你对Python和Ubuntu系统有一定程度的熟悉,以便完成作业并理解我们将要进行的操作。
概述 📋

本节课程将介绍Python编程环境,包括如何安装必要的软件包、使用交互式Shell以及编写简单的脚本。我们将重点介绍对音频信号处理至关重要的几个Python扩展库。
访问Python官方网站 🌐
首先,访问Python官方网站 Python.org。这是Python的主页,你可以在此找到大量教程和信息。
在Ubuntu中检查Python
在Ubuntu系统中,Python通常已预装。打开终端并输入 python 命令,你将看到已安装的Python版本。例如,可能会显示 2.7.6。这表明Python已正确安装,你可以立即在此开始输入命令并与Python交互。
Python有许多不同版本,特别是较新的 3.x 版本。但在我们的课程中,需要使用 2.7 版本,因为许多我们需要的软件包仅支持 2.7。请确保你在Ubuntu上安装并能够运行 2.7.x 版本的Python。
安装必要的Python软件包 📦

除了基本的Python安装,我们还需要使用几个重要的软件包,即函数集合。为了安装它们,我们需要使用Ubuntu的标准安装应用程序 apt-get。安装需要超级用户权限,因此需要使用 sudo 命令。
以下是需要安装的软件包列表:
- 基础开发工具:用于编译和构建其他软件包。
- IPython:我们将使用的交互式Shell。
- NumPy:用于数组处理的包。我们所有的声音数据都将以数组形式处理。
- Matplotlib:用于绘制声音波形和各种分析结果的绘图包。
- SciPy:信号处理包,我们将使用其中的许多函数。
- Cython:用于与C代码接口的包。我们的一些函数是用C语言编写的,为了编译它们,需要Cython。
所有这些包都是知名的Python扩展,你可以找到大量相关信息。请确保安装它们,我们将在课程中稍作介绍。
开始使用IPython 🚀
现在,让我们开始使用IPython。在终端输入 ipython 命令启动它。它会显示所使用的Python版本(例如 2.7.6)。在这里,你可以获得一个交互式Shell,可以开始输入命令和执行操作。

例如,你可以进行标准的算术运算,声明变量并对它们进行操作。
为了处理声音,我们需要处理数组。为此,必须导入数组操作包 NumPy。我们通常会给它起一个简短的别名以便引用。
import numpy as np
现在,我们可以声明一个变量 a 作为一个数组。调用 np.array 函数来创建包含不同数字的数组。
a = np.array([1, 2, 3, 4, 5])
输入 a 可以显示这个数组。我们可以访问数组的元素,例如第三个元素 a[2],或者访问从索引1到3的所有元素 a[1:4]。
如果我们想绘制这个数组,就需要 matplotlib。我们需要导入 matplotlib 中负责二维绘图的模块 pyplot。
import matplotlib.pyplot as plt
现在,我们可以使用 pyplot 的 plot 函数来显示数组 a。
plt.plot(a)
这会生成一个显示对象。为了展示图形,需要输入 show 命令。
plt.show()
使用文本编辑器编写脚本 📝
如果我们想编写更复杂的程序,可以使用文本编辑器。Gedit 是Ubuntu的默认文本编辑器。在这里,我们可以输入之前在交互式Shell中执行的Python命令。
例如,我们可以从导入 numpy 并命名为 np 开始。然后,声明一个变量 a 作为 numpy 数组。接着,对这个数组进行一些操作。例如,创建一个新数组 b,它是 a 的反向版本。我们可以通过使用步长为 -1 的索引切片来实现。
import numpy as np
a = np.array([1, 2, 3, 4, 5])
b = a[::-1]
print(b)
现在,将其保存到主目录中,例如命名为 reverse.py。这样就保存了一个Python脚本文件。

如果我们在IPython中,并且位于主目录下,可以看到保存的文件 reverse.py。我们可以通过输入 run reverse 命令来执行这个文件。执行后,它会返回 print 命令的输出,即反向后的数组 b。
通过这种方式,我们可以开发更复杂的程序,并可以从IPython交互式地执行它们。这是一种非常强大的开发和交互方式。
总结 ✨
本节课中,我们一起学习了Python的基础知识。我们介绍了Python,并推荐访问 Python.org 网站获取更多信息。我们使用 IPython 作为交互式Shell,可以在其中编程并执行文件。最后,我们介绍并讨论了几个扩展基础Python功能的软件包,特别是用于处理数组的 NumPy、用于信号处理工具的 SciPy 以及用于绘图的 Matplotlib。

今天的内容是对Python非常基础的介绍。希望你能有时间自行深入探索,查看一些教程和进一步的信息。下节课我们将扩展这些介绍,并开始具体讨论如何处理声音数据。
010:Python与声音 🎵
在本节课中,我们将学习如何使用Python来处理音频信号。我们将涵盖读取音频文件、处理音频数据以及将结果写入新文件的基本操作。
上一节我们介绍了Python语言,它是一种功能强大的高级语言,非常适合我们进行各种应用和编程。本节中,我们来看看如何利用Python进行具体的音频处理。

启动Python环境
首先,我们需要打开终端并启动Python的交互式编程环境iPython。
# 在终端中启动iPython
ipython
读取音频文件
为了读取音频文件,我们需要使用一个名为sipi的包,它包含了处理WAV文件的功能。
# 从sipi包中导入读取WAV文件的函数
from sipi.io.wfile import read
read函数需要输入文件名作为参数,并返回两个值:采样率(FS)和包含音频样本的数组(x)。
# 读取音频文件
FS, x = read('flute.A4.wav')
现在,变量FS存储了采样率(例如44100 Hz),变量x存储了音频样本(例如16位整数数组)。
分析音频数据
我们可以开始询问关于这个音频文件的一些信息。例如,要获取音频样本的数量,我们可以使用以下命令:
# 获取音频样本的数量
sample_count = x.size
要计算音频的持续时间(以秒为单位),我们可以将样本数量除以采样率。为了得到精确的浮点数结果,我们需要将其中一个数字转换为浮点数。
# 计算音频持续时间(秒)
duration = x.size / float(FS)
可视化音频波形
为了绘制音频波形,我们需要导入matplotlib绘图库。
# 导入matplotlib绘图库
import matplotlib.pyplot as plt
然后,我们可以绘制音频样本的波形图。
# 绘制音频波形
plt.plot(x)
plt.show()
然而,这种显示方式以样本数为横轴,不够直观。我们希望横轴以秒为单位。为此,我们需要创建一个时间轴。
# 导入numpy库以创建时间轴
import numpy as np# 创建时间轴(秒)
t = np.arange(x.size) / float(FS)# 以时间为横轴绘制波形
plt.plot(t, x)
plt.show()
现在,我们可以在图形界面中缩放和导航,更清晰地观察音频的振荡细节。
处理音频片段
我们可以选择音频数组中的一部分进行处理。例如,提取从第44100个样本开始的1000个样本。
# 提取音频片段
y = x[44100:45100]
我们可以对这个新数组y进行各种操作。例如,计算其绝对值的最大值,这有助于了解音频信号的峰值。
# 计算音频片段绝对值的最大值
max_value = np.max(np.abs(y))
我们还可以计算所有样本的总和。由于音频信号围绕零值振荡,总和可能接近零。
# 计算音频片段所有样本的总和
total_sum = np.sum(y)
写入音频文件
处理完音频后,我们通常需要将结果保存为新文件。为此,我们需要从sipi包中导入写入函数。
# 从sipi包中导入写入WAV文件的函数
from sipi.io.wfile import write
write函数接受文件名、采样率和音频数据数组作为参数。
# 将音频片段写入新文件
write('test.wav', FS, y)
播放音频文件

在iPython环境中,我们可以使用Unix系统的play命令来播放音频文件,以检查处理结果。
# 播放原始音频文件
!play flute.A4.wav# 播放新生成的音频文件
!play test.wav
总结

本节课中我们一起学习了使用Python进行音频处理的基础知识。我们介绍了如何利用sipi包读取和写入WAV文件,如何使用matplotlib绘制音频波形,以及如何借助numpy处理和分析音频数据。掌握这些基本操作后,我们已经可以开始进行一些有趣的音频处理实验。当然,要深入音频信号处理领域,我们还需要学习更多知识。我们下节课再见。
011:SMS工具软件 🎼
在本节课中,我们将学习SMS工具软件。这是为本课程开发的Python工具集,我们将深入理解并使用它们。
在之前的两次编程课程中,我们介绍了Python以及如何读取和处理声音的基本工具。本节中,我们来看看SMS工具。
概述
SMS源自“频谱建模合成”(Spectral Modeling Synthesis)。这是很久以前我为这类工具起的名字,并一直沿用至今。
这些工具可以从GitHub获取。GitHub是一个在线的软件仓库,也是一个拥有庞大社区的优秀平台,许多免费软件项目都使用这个仓库。
SMS工具结构
以下是SMS工具包的主要目录结构。
lectures:存放理论课的幻灯片以及用于生成幻灯片中所有图表的代码。software:本课程的主要软件代码,也是我们当前关注的重点。sounds:存放课程中使用的所有声音文件。workspace:供你练习的工作区。
如果我们打开software目录,你会看到课程的核心软件模型。
models:我们将要使用的核心模型,例如DFT、谐波模型、SDFT等,以及对这些模型应用变换的代码。interfaces:用于调用这些模型的函数和文件,也就是我们将要执行的函数。
安装与配置
回到根目录,有一个readme文件,它描述了基本的安装步骤。
首先,它指出我们需要Python 2.7以及安装一系列软件包(我们在第一讲中已完成)。其次,我们需要编译一些C函数。
为了完成所有步骤,首先要做的是下载包含所有文件的zip压缩包。点击下载后,它会创建一个本地文件。
我已经完成了这一步。在终端中,我的主目录下有一个sms-tools-master目录,里面包含了所有内容。
继续按照readme文件的指示,我需要执行一个编译命令。我需要进入software/models/utilFunctions_C目录并执行该命令。

我已经执行过了,所以它已经编译完成。你应该不会遇到任何问题。
基本上,我们现在已经准备就绪。如果不想深入编码,我们可以直接执行接口文件,就像在另一节演示课中介绍的那样,执行模型或变换的GUI界面。
深入核心模型
现在,我想深入models目录。这里包含了我们将要学习的核心模型。
让我们用文本编辑器打开其中一个。例如,进入software/models目录,打开sineModel.py文件。
sineModel是其中一个模型,它基本上将声音分析为正弦波的和。在这段代码的开头,导入了所需的包,然后定义了一系列函数,这些函数实现了正弦模型的不同方面。
让我们关注其中一个函数,例如sineModelAnal。这个函数以输入声音数组和决定执行何种分析的控制参数作为主要输入参数。
它执行一系列操作,遍历整个声音,并返回三个变量。这些变量基本上存储了声音中所有正弦波的频率、幅度和相位。这些都是存储了所有信息的大型矩阵。这些在开头的注释文本中都有解释,说明了它的功能、输入参数和返回值。
但是,要调用这个函数,我们使用另一个文件。我们转到interfaces/modelsInterface目录,在这里我们会找到sineModel_function.py文件。
这才是我们实际调用以执行正弦分析的文件。这个文件同样导入了所需的包集,然后有一个名为main的单一函数。
为了让这个文件能够被执行而不必手动调用函数,这里有这样一行命令,它基本上表示当我们执行这个文件时,它应该默认执行main函数。
所有输入参数都有默认值。它有输入文件的默认值,以及其他所有参数的默认值。当然,我们可以在这里修改所有这些值。
然后,在这个函数内部,它执行正弦分析的所有步骤:读取声音文件,获取分析窗,执行短时傅里叶变换,然后执行sineModelAnal函数(即我们刚才看到的函数)。它从之前的所有执行步骤中获取了所有的值和变量,因此我们可以直接调用该函数,它返回存储正弦波频率、幅度和相位的三个变量。之后我们可以重新合成声音,并将声音再次写入输出文件。
顺便提一下,这种读写声音文件的方式是我之前提到的soundfile库的一个包装函数。事实上,如果我们查看models目录下的utilFunctions.py文件中的read和write函数,它基本上就像我们上节课做的那样读取输入声音,但现在它会进行归一化,将其转换为浮点数,并归一化到-1到1之间的值。这是处理声音时,将声音作为-1到1之间浮点数的惯例。在写入文件时,我们必须逆转这个过程,重新创建16位整数。
执行与验证
这就是这个文件功能的基本概述。我们可以执行它。我们可以转到interfaces/modelsInterface目录,然后输入:
python sineModel_function.py
这将执行这个main文件。它首先会绘制分析结果,然后会将一个声音文件写入一个名为output_sounds的目录中。
如果我们查看这里,有一个名为output_sounds的目录。输入ls output_sounds,它会显示bend_sineModel.wav,这就是生成的结果文件。
要播放它,我们只需输入:

play output_sounds/bend_sineModel.wav
这就是我们通过这次分析-合成过程生成的合成声音。
当然,这也可以在IPython中完成。如果我们输入ipython进入交互模式,我们也可以通过输入run sineModel_function来执行这个文件,它会做同样的事情:执行该函数,然后显示结果并保存声音文件。
在IPython中运行的优势在于,如果出现错误,或者我们真的想探索函数的中间步骤,我们可以做到,我们可以运行调试器等。这对我们来说将非常有用。

总结

本节课中,我们一起学习了SMS工具集的介绍,它托管在GitHub上。请随时前往下载并安装。
为了安装它,除了之前完成的安装步骤外,我们唯一需要做的就是编译提到的C函数。
今天我们还简要描述了一个将要分析的模型——正弦模型。我们执行了它,并看到了如何通过它分析和合成声音。
以上就是对SMS工具的简要概述。结合另一节演示课(我在其中演示了界面并实际分析和变换了一些声音),希望你能够了解这些工具以及我们将在课程中学到的内容。

非常感谢,我们下节课再见。
012:离散傅里叶变换(全)🎵
在本节课中,我们将开始学习音频信号处理中最基础、最重要的主题之一:离散傅里叶变换。理解这个核心概念是掌握后续所有内容的关键。我们将从离散傅里叶变换的基本方程入手,并详细解释构成该方程的两个基本组成部分:复指数函数和标量积。

核心方程:离散傅里叶变换(DFT)🧮
这就是整个课程中最重要的方程,请务必仔细理解。
在方程中,你可以看到 x[n],这是我们的输入信号,即一系列声音样本。它被一个复指数函数(一个复正弦波)相乘。我们逐样本相乘:一个声音样本乘以复正弦波的一个样本。然后,我们对大写的 N(我们计算的样本数量)进行求和。最终,我们得到结果 X[k],这就是我们的频谱。索引 k 指的是频率索引。
因此,我们有:
- n 是离散时间索引。
- k 是离散频率索引。
- 输入信号有 N 个样本,输出频谱也有 N 个频率点。
如果我们想将这些频率理解为弧度频率,需要将 k 乘以 2π 再除以 N,这正好是复指数函数中的指数部分。如果我们想将其转换为以赫兹为单位的频率,只需将索引 k 除以 N,再乘以采样频率 Fs 即可。
公式:
X[k] = Σ_{n=0}^{N-1} x[n] * e^{-j(2π/N)kn}
DFT 实例分析🎹

让我们看一个例子。上方的图是输入信号 x[n],在本例中是一段双簧管声音的一系列样本。

当我们计算其 DFT 时,会得到一个复数函数 X,它可以用极坐标形式表示,即包含幅度和相位。让我们先听一下这个声音。
(此处为双簧管声音)
在频谱中,我们可以看到幅度谱和相位谱。在幅度谱中,我们可以识别出这个声音的谐波,即那些明显的峰值,这清楚地表明这是一个谐波丰富的声音。在相位谱中,我们可以看到这些正弦波在周期长度内、相对于样本序列持续时间的相位位置(以弧度表示)。


基础函数:复指数正弦波📈
正如 DFT 方程所示,输入信号 X 与一系列复指数函数(复正弦波)相乘。这些正弦波是 DFT 的基础函数,是我们将在输入信号中识别和测量的成分。
其中一个复正弦波是 s_k。利用欧拉公式,我们可以将其表示为一个复指数:
s_k[n] = e^{-j(2π/N)kn} = cos((2π/N)kn) - j*sin((2π/N)kn)
这就是该复指数的实部和虚部。
- 如果 DFT 的大小 N=4,我们将有 4 个输入样本,因此也需要 4 个长度为 4 的正弦波作为基础函数。输出也将有 4 个频率点。
- s_0:频率 k=0,所有样本值均为 1(直流分量)。
- s_1:频率 k=1,一个周期的复正弦波。
- s_2:频率 k=2。
- s_3:频率 k=3。
一个大小为 4 的信号将被投影到这 4 个正弦波上。


- 如果信号大小 N=8,我们将需要 8 个这样的正弦波。从 s_0(直流分量)开始,到 s_1(一个周期的正弦/余弦),周期数逐渐增加。这里存在某种对称性,频率并不会一直增加到 8 个周期,而是会回绕。这些就是进行 8 点 DFT 时使用的 8 个复正弦波。

标量积视角🔍

DFT 方程也可以从标量积的角度来理解。我们实际上是在计算输入信号 x[n] 与 N 个复指数函数之间的标量积。
公式:
X[k] = <x, s_k> = Σ_{n=0}^{N-1} x[n] * s_k*[n]
让我们看一个例子。假设 N=4,信号 x 为四个样本 [1, -1, 1, -1]。我们计算这个信号与之前幻灯片中四个正弦波(s_0 到 s_3)中每一个的标量积:
- 与 s_0 的标量积:结果为 0。这意味着该信号中不存在频率 k=0(直流分量)。
- 与 s_1 的标量积:结果为 0。这意味着频率 k=1 也不存在。
- 与 s_2 的标量积:结果为 4。这意味着信号 x 与这个正弦波完全一致,该频率成分很强。
- 与 s_3 的标量积:结果为 0。
因此,我们计算出了 x[n] 的 DFT,结果是:X[2] = 4,其他 X[k] = 0。这表明信号中只存在频率 k=2。

更复杂的信号示例📊

让我们用一个更大的信号来演示。这是一个简单的信号,前四个样本为 1,后四个样本为 -1,类似于一个矩形波。
我们计算这个信号与之前提到的 8 个复正弦波(s_0 到 s_7)的标量积(即 DFT)。结果是一个复数频谱,可以用极坐标(幅度和相位)显示。
从结果中可以看到:
- 频率 k=0 不存在。
- 频率 k=1 非常强。
- 频率 k=3、k=5、k=7 也存在。
- 相位信息表明了这些正弦波在时间上的相对位置。
这就是 DFT 运算的本质,它将成为我们后续所有处理的核心。


延伸资源与总结📚
你可以在维基百科或 Julius Smith 的网站上找到更多关于 DFT 的信息。从本讲开始,我们将使用来自 Freesound 的音频样本,所有代码将遵循标准的 Creative Commons 和 GPL 许可。


本节课我们一起学习和解释了 DFT 方程。希望你已经意识到它并非那么复杂。即使你觉得它有些复杂,但它在音频信号处理中的应用极其广泛,因此值得投入时间去掌握。
我们将在本主题的第二部分继续深入探讨 DFT。感谢你的关注,下节课再见。


013:离散傅里叶变换(第二部分)🎵

在本节课中,我们将继续学习离散傅里叶变换。上一节我们介绍了DFT的基本方程,本节中我们将看看当输入信号是复正弦波或实正弦波时,DFT如何工作,并讲解逆DFT。

DFT回顾与工作原理
DFT可以理解为将信号投影到一组有限的复正弦波上。因此,它能够识别信号中每种正弦波成分的含量。
从方程来看,内积的概念表达了这一思想:当我们将信号 x[n] 投影到复正弦波 e^{-j2πkn/N} 上时,我们本质上是在测量信号中该正弦波的含量。

以下是计算DFT的核心公式:
X[k] = Σ_{n=0}^{N-1} x[n] * e^{-j2πkn/N}
计算单个复正弦波的DFT

为了更好地理解这个概念,我们先解释如何计算单个复正弦波的DFT。

假设输入信号 x₁[n] 是一个长度为 N 的复正弦波,其频率由索引 k₀ 表示。我们将这个信号代入DFT方程。

以下是计算过程:

x₁[n] = e^{j2πk₀n/N}
X₁[k] = Σ_{n=0}^{N-1} e^{j2πk₀n/N} * e^{-j2πkn/N}= Σ_{n=0}^{N-1} e^{j2π(k₀ - k)n/N}
这是一个几何级数求和,有闭合形式。通过检查方程可以发现,当 k ≠ k₀ 时,分母不为零,分子为零,因此输出 X[k] 为零。当 k = k₀ 时,输出 X[k₀] = N。
这意味着,对于一个恰好是DFT基函数之一的复正弦波,其DFT结果仅在 k = k₀ 处有一个非零值 N。

任意频率复正弦波的DFT
然而,上述情况非常特殊。让我们看一个更现实的例子:计算任意频率复正弦波的DFT。
假设信号 x₂[n] 是一个复指数信号,但其频率 f₀ 不是DFT正弦波频率的整数倍。将其代入DFT方程后,我们同样得到一个几何级数,但其形式不如前例简洁。

在这种情况下,DFT的幅度谱在所有 k 上都有正值,但在频率 f₀ 附近(例如 k=7 和 k=8 之间)会出现一个突出的峰值,其余位置的值则小得多。
实正弦波的DFT
让我们更进一步,计算更接近真实声音的实正弦波的DFT。这稍微复杂一些。

我们取一个实正弦波信号 x₃[n] = A₀ * cos(2πk₀n/N)。利用欧拉公式,我们可以将这个余弦表示为两个复正弦波之和。
由于DFT是线性运算,其实正弦波的DFT可以表示为这两个复正弦波各自DFT的和。
以下是关键推导:

cos(2πk₀n/N) = (1/2)(e^{j2πk₀n/N} + e^{-j2πk₀n/N})
X₃[k] = DFT{ (A₀/2) * e^{j2πk₀n/N} } + DFT{ (A₀/2) * e^{-j2πk₀n/N} }
因此,结果将在 k = k₀ 和 k = -k₀ 两个频率位置出现幅度为 A₀/2 的峰值,其余 k 值为零。对于非整数频率的情况,则会在正负频率附近各出现一个“凸起”。
逆离散傅里叶变换
DFT的一个重要特性是可逆性,这意味着我们可以从其频谱中恢复原始信号。这就是逆DFT。
逆DFT的方程如下:
x[n] = (1/N) * Σ_{k=0}^{N-1} X[k] * e^{j2πkn/N}


与DFT的主要区别在于:
- 复指数指数项为正,而非共轭(负指数)。
- 包含一个归一化因子
1/N。
从概念上讲,这是一个合成过程:我们重新生成之前识别出的正弦波,将它们组合起来以重建原始信号。
对于实信号,其频谱是共轭对称的。因此,要恢复原始信号,通常只需要正频率一半的频谱信息。我们可以利用对称性生成完整的频谱,然后应用逆DFT方程,即可得到原始的实正弦波信号。
总结

本节课中我们一起学习了离散傅里叶变换的进一步应用。在第一部分,我们介绍了DFT方程;在第二部分,我们探讨了当输入是正弦波时DFT的工作原理,并解释了逆DFT。

我们了解到:
- DFT将信号分解为特定复正弦波的组合。
- 对于恰好匹配DFT基频的复正弦波,其DFT结果是一个单一峰值。
- 对于任意频率的复正弦波或实正弦波,其DFT结果会在对应频率附近扩散。
- 逆DFT允许我们从频谱完美地重建原始信号。

理解这些概念将为后续学习更复杂的音频信号处理技术打下坚实基础。我们将在下一讲中继续深入这些概念。
014:分析声音 🎵
在本节课中,我们将学习如何使用离散傅里叶变换来分析一个真实的声音。我们将以音叉的声音为例,通过实践操作来理解DFT如何揭示声音的频率成分。

概述
我们将使用Audacity录制音叉的声音,并通过其内置的频谱图功能进行初步观察。随后,我们将把声音文件导入到SMS工具包中,利用DFT模型进行更深入的分析,并将结果与一个纯电子合成的正弦波进行比较。

录制与初步观察
首先,我们在Audacity中录制音叉的声音。音叉能产生非常清晰的音调,这使其成为为其他乐器调音的理想工具。
以下是录制和初步处理的步骤:
- 打开Audacity,点击录音按钮,通过麦克风录制音叉的声音。
- 删除录音中非音叉声音的部分。
- 选择音量最大的片段进行放大观察。放大后,波形呈现出类似正弦曲线的形态。
在之前介绍Audacity时,我们提到过可以查看频谱图。频谱图本质上是随时间变化的DFT。音叉声音的频谱图显示出一条非常明显的谱线,频率大约在500赫兹左右,这基本上就是我们听到的频率。实际上,这个音叉标注的频率是440赫兹。
深入分析准备

为了进行更深入的分析,我们需要清理并导出声音文件。
- 在声音的开头和结尾添加一小段静音,这有助于后续程序的识别和分析。
- 将文件导出为WAV格式,采样率为44100 Hz,单声道,并命名为
tuning_fork.wav。
使用SMS工具进行DFT分析
接下来,我们通过终端进入SMS工具包的 models 子目录,启动包含所有模型的交互界面。我们将首先使用DFT模型。

以下是分析步骤:

- 加载我们刚刚保存的
tuning_fork.wav文件。 - 使用默认参数对声音进行分析。分析结果会显示输入声音的片段、该片段的DFT幅度谱,以及经过逆DFT重建后的波形。
- 在幅度谱中,可以清晰地看到一个主峰。将光标置于其上,显示频率大约在440赫兹。同时,频谱中也存在其他强度较弱的分量。

为了对比,我们打开一个预先合成的440赫兹电子正弦波文件,并进行同样的DFT分析。虽然听起来与音叉声音相似,但两者的频谱图看起来有所不同:音叉的频谱看起来更“嘈杂”,而电子正弦波的频谱则有一个非常清晰的主峰,其余部分则微弱得多。
调整分析参数

我们可以调整分析参数来观察不同的效果。例如,我们可以改变分析帧的大小。

- 将分析样本数从默认值改为更大的数值(例如1001个样本)。
- 重新计算DFT。虽然由于样本数增多,频谱的细节看起来会有所不同,但整体的形状保持不变。
- 我们也可以调整分析的起始时间点。由于这是一个近乎纯的正弦波,在不同时间点分析的频谱形状基本一致。
总结

本节课中,我们一起学习了如何分析真实声音。我们使用Audacity录制并初步观察了音叉的声音,然后利用SMS工具包中的DFT模型进行了深入分析。通过将音叉的DFT结果与纯电子正弦波的DFT结果进行对比,我们直观地理解了理想正弦波与实际物理声源产生的“正弦”声音在频谱上的差异。希望这能让你对DFT处理实际声音的能力有一个具体的认识。
015:Freesound简介 🎵
在本节课中,我们将学习一个名为Freesound的在线音频数据库。这是一个为音频处理课程提供声音素材的重要资源,我们将了解如何搜索、下载以及上传声音文件。
概述
Freesound是一个由巴塞罗那庞培法布拉大学音乐技术组发起并维护的网站,拥有超过20,000个声音样本和每日超过50,000的活跃用户。它是一个基于社区贡献和共享的音频资源平台。


访问与搜索声音


首先,我们访问Freesound网站。在搜索框中输入关键词,例如“flute note”,网站会返回包含这些关键词(标签或描述中)的所有声音。例如,搜索“flute note”会找到1074个相关声音。


以下是搜索声音的基本步骤:
- 在搜索栏输入关键词。
- 浏览搜索结果列表。
- 点击感兴趣的声音进入详情页。
声音详情页

每个声音条目都包含详细的信息。详情页通常包括声音的描述、录制方式、关键特征以及标签。此外,还提供声音的技术信息,如采样率和比特深度。
声音详情页通常包含以下信息:
- 描述:关于声音内容和录制方式的文字说明。
- 标签:用于分类和搜索的关键词。
- 技术信息:包括文件格式、时长、采样率等。
- 播放控件:用于在线预览声音。
基于相似性的搜索
Freesound提供了一个有趣的功能,即通过声音的频谱相似性来查找其他声音。例如,一个陶笛(ocarina)的声音可能被系统认为与某些长笛声音频谱相似。点击“相似声音”选项,系统会列出频谱特征相近的声音,并按相似度排序。

浏览与发现声音
除了关键词搜索,还可以通过其他方式浏览声音。在“声音”页面,可以按标签或“包”来浏览。一个特别的功能是“地理标签”,可以查找在世界特定地点录制的声音。例如,搜索“Barcelona”可以找到2300多个在该城市录制的声音。

下载声音
要下载声音,需要先登录账户。在声音详情页点击“下载”按钮,文件将以原始上传的格式保存到本地。本课程使用的声音都标记了“ASP_course”标签,方便集中查找和下载。

上传声音
我们鼓励用户上传对课程有参考价值或经过处理的有趣声音,并为其打上“ASP_course”标签以便他人查找。
上一节我们介绍了如何下载声音,本节中我们来看看如何上传自己的声音。
以下是上传声音的步骤:
- 准备声音文件。例如,我们可以使用Audacity生成一个440Hz、时长5秒的锯齿波,并添加淡入淡出效果。
# 示例:生成一个440Hz的锯齿波概念 # 频率 = 440 Hz # 时长 = 5 秒 # 波形 = 锯齿波 - 在Freesound网站点击“上传声音”按钮。
- 选择文件并上传。
- 填写声音描述,例如“这是一个使用Audacity生成的440Hz、5秒长的锯齿波”。
- 添加相关标签,如“sawtooth”、“440Hz”、“synthesis”。
- 选择许可证。通常推荐使用“署名许可”,这意味着使用者需要注明素材来源。
- 提交上传。上传后会有审核人员检查描述是否准确并确保没有法律问题,之后声音便会出现在网站上。
关于知识共享许可
了解音频素材的许可协议非常重要。Freesound主要使用知识共享许可协议,这是一种非常适合创意作品共享的许可方式。常见的类型有:
- CC0(公共领域):放弃所有权利,可自由使用。
- 署名:使用时需注明作者。
- 非商业性使用:禁止用于商业用途。
总结

本节课我们一起学习了Freesound平台的基本使用方法。我们了解了如何搜索、浏览和下载声音,特别是通过频谱相似性进行查找的功能。更重要的是,我们学习了如何准备并上传自己的声音文件,包括如何添加描述、标签以及选择合适的知识共享许可协议。希望你能充分利用这个资源,不仅从中获取素材,也积极贡献自己的声音,丰富整个社区。
016:正弦波编程实践 🎵
在本节课中,我们将学习如何用Python编程实现两种类型的正弦波:实正弦波和复正弦波。正弦波是理解离散傅里叶变换(DFT)的基础,也是音频信号处理的核心。
概述
我们将使用Python的NumPy和Matplotlib库来生成和可视化正弦波。实正弦波用于表示时域中的连续信号,而复正弦波是DFT分析中的基本组成部分。
实正弦波的实现
实正弦波是一个实数序列,可以通过余弦函数计算得出。其公式为:
x(t) = A * cos(2πft + φ)
其中,A是振幅,f是频率,t是时间索引,φ是初始相位。

以下是实现实正弦波的步骤:
首先,我们需要导入必要的Python库。
import matplotlib.pyplot as plt
import numpy as np
接下来,我们定义正弦波的参数并生成时间序列。
# 定义正弦波参数
A = 0.8 # 振幅
f0 = 1000 # 频率 (Hz)
phi = np.pi / 2 # 初始相位 (π/2)
Fs = 44100 # 采样率 (Hz)# 生成时间数组,从-0.002秒到0.002秒
t = np.arange(-0.002, 0.002, 1/Fs)# 计算实正弦波
x = A * np.cos(2 * np.pi * f0 * t + phi)
最后,我们绘制生成的波形。
# 绘制波形
plt.figure(figsize=(10, 4))
plt.plot(t, x)
plt.axis([-0.002, 0.002, -0.8, 0.8])
plt.xlabel('时间 (秒)')
plt.ylabel('振幅')
plt.title('实正弦波')
plt.grid(True)
plt.show()
复正弦波的实现
复正弦波是DFT中的基本元素,它是一个复数序列,其公式为:
s[n] = e^(j * 2π * k * n / N)
其中,k是频率索引(周期数),n是时间索引,N是DFT的大小(序列长度)。
上一节我们介绍了实正弦波,本节中我们来看看如何生成和可视化复正弦波。
以下是实现复正弦波的步骤:
首先,我们定义复正弦波的参数。
N = 500 # DFT大小/序列长度
k = 3 # 频率索引(周期数)# 生成整数时间索引数组,从 -N/2 到 N/2
n = np.arange(-N/2, N/2)
然后,我们计算复正弦波。
# 计算复正弦波
s = np.exp(1j * 2 * np.pi * k * n / N)
为了可视化,我们可以分别绘制其实部和虚部。
# 绘制复正弦波的实部(余弦波)
plt.figure(figsize=(12, 4))plt.subplot(1, 2, 1)
plt.plot(n, np.real(s))
plt.axis([-N/2, N/2, -1, 1])
plt.xlabel('索引 n')
plt.ylabel('振幅')
plt.title('复正弦波实部 (余弦)')
plt.grid(True)# 绘制复正弦波的虚部(正弦波)
plt.subplot(1, 2, 2)
plt.plot(n, np.imag(s))
plt.axis([-N/2, N/2, -1, 1])
plt.xlabel('索引 n')
plt.ylabel('振幅')
plt.title('复正弦波虚部 (正弦)')
plt.grid(True)plt.tight_layout()
plt.show()

DFT中的复正弦波


在DFT中,我们使用固定长度N的复正弦波。频率索引k决定了在N个样本内包含多少个完整周期。让我们看一个更接近典型DFT的例子,使用较小的N值。
以下是调整参数后的示例:

# 使用更小的N值,模拟DFT中的情况
N_dft = 32
k_dft = 5n_dft = np.arange(-N_dft/2, N_dft/2)
s_dft = np.exp(1j * 2 * np.pi * k_dft * n_dft / N_dft)# 绘制
plt.figure(figsize=(10, 4))
plt.stem(n_dft, np.imag(s_dft), basefmt=" ", use_line_collection=True)
plt.axis([-N_dft/2, N_dft/2, -1, 1])
plt.xlabel('索引 n')
plt.ylabel('振幅')
plt.title(f'DFT中的复正弦波 (N={N_dft}, k={k_dft})')
plt.grid(True)
plt.show()
总结
本节课中我们一起学习了如何用Python编程生成两种正弦波。
- 我们实现了实正弦波,它用于在时域中表示连续的音频信号。
- 我们实现了复正弦波,它是离散傅里叶变换(DFT)的基石,由复数指数函数定义。
- 我们使用了 NumPy 库进行高效的数组计算,以及 Matplotlib 库进行数据可视化。
- 我们了解到,在DFT中,复正弦波的长度N是固定的,频率索引k直接对应信号中包含的周期数。

掌握正弦波的编程实现是深入理解音频信号处理中更高级概念(如即将学习的DFT)的关键第一步。
017:DFT编程实现 🎵
在本节课中,我们将学习如何用Python编程实现离散傅里叶变换及其逆变换。我们将从基础公式出发,逐步编写代码,并通过可视化结果来验证实现的正确性。
概述

离散傅里叶变换是音频信号处理的核心工具之一。它能够将时域信号转换为频域表示,揭示信号的频率成分。本节编程课的目标是理解DFT的数学公式,并将其转化为可运行的Python代码。我们将实现两个核心函数:正向DFT和逆向DFT。
DFT正向变换的实现
上一节我们介绍了DFT的理论基础,本节中我们来看看如何用代码实现它。正向DFT的公式如下:
X[k] = Σ (n=0 to N-1) x[n] * e^(-j2πk*n/N)
以下是实现正向DFT的步骤:
- 导入必要的Python库。
- 初始化一个空数组来存储频谱结果。
- 遍历所有频率索引k。
- 为每个k值生成对应的复指数向量。
- 计算输入信号与该复指数向量的内积(点乘),得到X[k]。
- 将结果存入输出数组。
对应的核心代码如下:
import numpy as np
import matplotlib.pyplot as pltdef DFT(x):N = len(x)X = np.array([]) # 初始化输出频谱数组kv = np.arange(-N//2, N//2) # 频率索引,从-N/2到N/2for k in kv:# 创建复指数基向量s = np.exp(1j * 2 * np.pi * k / N * np.arange(N))# 计算内积(点乘并求和)Xk = np.sum(x * np.conj(s))X = np.append(X, Xk)return X
测试DFT:从复指数信号到实数信号
为了验证DFT代码的正确性,我们使用不同的输入信号进行测试。
首先,我们生成一个复指数信号作为输入。以下是生成和测试信号的代码:
# 设置参数
N = 64 # 信号长度
k0 = 7 # 频率索引# 生成复指数信号
n = np.arange(N)
x_complex = np.exp(1j * 2 * np.pi * k0 * n / N)# 计算DFT
X_complex = DFT(x_complex)# 绘制频谱(取模值)
plt.plot(np.arange(-N//2, N//2), np.abs(X_complex))
plt.show()
运行代码后,频谱图会在频率索引7处显示一个峰值,其余位置为0,这证实了我们的输入是一个纯净的复正弦波。
接下来,我们将输入信号改为更常见的实数余弦信号。
# 生成实数余弦信号
x_real = np.cos(2 * np.pi * k0 * n / N)# 计算DFT
X_real = DFT(x_real)# 绘制频谱
plt.plot(np.arange(-N//2, N//2), np.abs(X_real))
plt.show()
对于实数信号,其频谱会呈现对称的两个峰值,分别位于正负频率索引处(如+7和-7)。这符合理论预期,即实数信号可以分解为两个共轭复指数之和。
最后,我们测试一个非整数频率的信号,观察频谱泄漏现象。

k0_non_int = 7.5 # 非整数频率
x_leakage = np.cos(2 * np.pi * k0_non_int * n / N)
X_leakage = DFT(x_leakage)
plt.plot(np.arange(-N//2, N//2), np.abs(X_leakage))
plt.show()
此时,频谱不再是一个单一的尖峰,能量会扩散到多个频率点上,这就是频谱泄漏,在理论课中已经解释过其原因。
DFT逆向变换的实现
在实现了正向变换并验证了其正确性后,我们接下来实现逆向DFT,以从频域信号恢复时域信号。逆向DFT的公式如下:

x[n] = (1/N) * Σ (k=0 to N-1) X[k] * e^(j2πk*n/N)
以下是实现逆向DFT的步骤:
- 初始化一个空数组来存储重建的时域信号。
- 遍历所有时间索引n。
- 为每个n值,对频谱X[k]与对应的复指数进行加权求和。
- 将求和结果乘以归一化因子1/N。
- 将结果存入输出数组。
对应的核心代码如下:
def IDFT(X):N = len(X)y = np.array([]) # 初始化输出时域信号数组nv = np.arange(-N//2, N//2) # 时间索引kv = np.arange(-N//2, N//2) # 频率索引for n in nv:# 创建复指数基向量s = np.exp(1j * 2 * np.pi * n / N * kv)# 计算加权和,并乘以归一化因子 1/Nyn = (1.0/N) * np.sum(X * s)y = np.append(y, yn)return y

我们可以用之前得到的频谱X_real来测试逆向变换:
# 执行逆向DFT
y_reconstructed = IDFT(X_real)# 绘制重建的信号与原信号进行对比
plt.plot(nv, np.real(y_reconstructed)) # 取实部,因为理论结果应为实数
plt.xlabel(‘Sample Index’)
plt.title(‘Reconstructed Signal from IDFT’)
plt.show()
运行代码后,绘制出的重建信号应与原始的余弦信号x_real一致,从而验证了正向和逆向DFT配对的正确性。
总结

本节课中我们一起学习了离散傅里叶变换的编程实现。我们首先实现了正向DFT,将时域信号转换为频域表示,并使用复指数信号、实数余弦信号以及非整数频率信号测试了代码,观察了不同的频谱特性。随后,我们实现了逆向DFT,成功地从频谱中恢复了原始时域信号,完成了整个变换的闭环。实现过程中,我们主要依赖了NumPy进行数组运算和Matplotlib进行结果可视化。理解这些基础方程的代码实现是迈向更高级音频处理应用的第一步。
018:傅里叶性质1 🎵

在本节课中,我们将深入学习离散傅里叶变换的四个核心性质:线性性、移位性、对称性和卷积。理解这些性质对于深入掌握DFT的行为至关重要,尤其是在处理实际音频信号时。
上一节我们介绍了离散傅里叶变换的基本概念。本节中,我们将探讨其关键数学性质,这些性质决定了DFT如何对信号进行操作和变换。
线性性 📈

线性性是许多数学运算(包括DFT)的一个非常方便的性质。它意味着DFT是一个“行为良好”的运算。
具体来说,如果我们从两个信号 x1[n] 和 x2[n] 的线性组合开始,那么在频域(频谱域)中,其结果也对应于各个输入信号频谱的线性组合。
以下是证明过程。我们从两个信号线性组合的DFT开始:

DFT{a * x1[n] + b * x2[n]}
我们可以轻松地将求和项分成两部分,一部分用于 a * x1[n],另一部分用于 b * x2[n]。由于标量值 a 和 b 不依赖于 n,我们可以将它们移到求和符号之外。因此,我们得到:
a * DFT{x1[n]} + b * DFT{x2[n]}

公式表示:
DFT{a·x1[n] + b·x2[n]} = a·X1[k] + b·X2[k]
我们可以用两个真实信号来举例说明。假设信号 x1 具有给定的幅度和频率,信号 x2 是另一个具有不同幅度和频率的正弦波。我们可以分别计算这两个信号的DFT,得到它们的幅度谱。如果将这两个幅度谱相加,得到左侧图中的结果。然而,如果我们先计算两个正弦波之和 (x1 + x2) 的DFT,得到的结果将与分别计算DFT后再相加的结果完全相同。这本质上是DFT线性性质的结果。
移位性 🔄

移位意味着移动信号中的样本点。对于DFT,移位性质是指:如果我们在时域中将信号 x[n] 移动 n0 个样本,那么在频域中,其频谱 X[k] 将乘以一个复指数因子。
同样,这很容易证明。我们从移位信号的DFT开始:
DFT{x[n - n0]}
我们进行变量替换,令 m = n - n0。然后,我们可以将复指数项拆分为两个部分:一个与 m 相关,另一个与 -n0 相关。因此,我们可以看到,我们得到了信号 x[m] 的DFT(即 X[k]),再乘以一个与 m 无关的复指数因子 e^{-j2πkn0/N}。


公式表示:
DFT{x[n - n0]} = X[k] · e^{-j2πk n0 / N}

我们也可以看一个例子。这里有两个信号 x1 和 x2,其中 x2 是 x1 移动了两个样本的版本(这是一个正弦波的一个周期)。如果我们计算这两个信号的DFT,会发现它们的幅度谱(幅度谱的绝对值)完全相同,没有任何差异。这是因为乘以频谱的复指数因子不影响幅度谱的幅度值。但是,相位谱则明显不同,两条线具有不同的斜率。这是因为频谱中的复指数乘法影响了相位值,但不影响幅度值。这就是DFT移位性质的一个例证。
对称性 🪞

在DFT中,存在一系列对称性,这些对称性对我们将要进行的许多操作非常有用。
以下是几种重要的对称情况:
- 如果信号是实信号(我们将要处理的信号类型),那么其DFT得到的复频谱,其实部具有偶对称性,虚部具有奇对称性。从极坐标表示(幅度和相位)来看,幅度谱具有偶对称性,相位谱具有奇对称性。
- 如果信号不仅是实信号,同时其本身在时域也具有偶对称性,那么其DFT的实部具有偶对称性,而虚部全部为零。从极坐标表示来看,幅度谱具有相同的偶对称性,相位谱全部为零。
我们可以从一个三角形波的例子中看到这一点。三角形是一个偶函数(当然也是实函数)。我们观察到其频谱的实部是偶对称的,虚部全部为零;幅度谱是偶对称的,相位角全部为零。
我们也可以在真实信号中展示这些对称性。这是一个女高音人声的片段。如果我们观察其极坐标形式的频谱(幅度和相位),幅度谱显示出这种偶对称性:右侧部分恰好是左侧部分关于零点的镜像。相位谱则显示出奇对称性,右侧部分相对于左侧部分似乎是反转的。

卷积 ⚙️
最后,我们来讨论与卷积相关的性质。该性质指出:如果在时域中对两个信号进行卷积运算,那么在频域中,其结果对应于两个信号各自频谱的乘积。
证明过程同样直接。我们从两个信号卷积的DFT开始,代入卷积公式,并分离变量。其中一部分涉及 x1,另一部分本质上是信号 x2 移位后的DFT。正如我们刚刚解释的,时域移位在频域对应于频谱乘以一个复指数因子。因此,我们最终得到 X1[k] 和 X2[k] 的乘积。

公式表示:
DFT{x1[n] * x2[n]} = X1[k] · X2[k]
与大多数性质一样,这也是一个可逆的性质。这意味着,如果在时域中将两个信号相乘,那么在频域中,其结果对应于两个频谱的卷积。
以下是该性质的两种视角示例:
- 时域乘法对应频域卷积:我们从两个信号
x1和x2开始,分别计算它们的DFT(这里显示的是幅度谱)。如果将这两个频谱进行卷积,会得到右侧图中的形状。然而,相同的形状也可以通过先将两个时域信号x1和x2相乘,再计算其DFT来获得。 - 滤波应用:这是滤波中非常常见的例子。假设我们有一个海洋声音(一个非常嘈杂的信号)和一个空间脉冲响应(一个很短的声学特性信号)。标准的滤波操作是在时域中将这两个信号进行卷积。但在DFT的背景下,我们可以计算海洋声音的频谱
X1[k]和脉冲响应的频谱X2[k]。卷积操作可以通过将这两个频谱相乘来实现。这意味着,滤波既可以在时域中通过卷积两个信号来完成,也可以在频域中通过将两个频谱相乘来完成。这是一个我们可以利用DFT的非常方便的特性。

总结 📝

本节课中,我们一起学习了离散傅里叶变换的四个基本性质:线性性、移位性、对称性和卷积。线性性确保了信号组合在时域和频域的一致性;移位性揭示了时域平移对频谱相位的影响;对称性描述了实信号及其特殊形式(如偶对称信号)频谱的内在结构;而卷积性质则建立了时域卷积与频域乘法之间的等价关系,这是数字滤波等音频处理技术的核心基础。理解这些性质是深入掌握DFT并将其有效应用于音乐和音频分析的关键一步。

在下一部分课程中,我们将继续探讨DFT的更多性质。
019:傅里叶性质(二)📊
在本节课中,我们将继续学习离散傅里叶变换(DFT)的更多重要性质。这些性质对于音频处理工作至关重要。我们将探讨能量守恒、分贝表示、相位解卷绕、零填充、快速傅里叶变换(FFT)以及结合零相位窗的分析与综合概念。

上一节我们介绍了DFT的一些基本性质,本节中我们来看看更多实用的高级性质。
能量守恒 ⚖️
能量守恒性质指的是,信号在时域和频域中的能量可以用相同的方式测量,并且数值相等。我们可以在任一域中计算能量。

能量定义为信号绝对值的平方和。在频域中,如果我们同样计算绝对值平方和,并除以归一化因子 N,我们将得到相同的值。
公式:
时域能量:E_time = sum(|x[n]|^2)
频域能量:E_freq = (1/N) * sum(|X[k]|^2)
例如,一个时域信号的能量计算值为11.8。在频域中,对DFT结果的绝对值平方求和并除以N,我们得到完全相同的值11.8。
分贝表示 🔊
与能量相关的概念是振幅。无论是在时域还是频域,当我们获得信号的频谱极坐标表示时,振幅通过计算绝对值(线性度量)获得。然而,对于声音,将振幅转换为对数尺度的分贝值是一种更直观的表示方式。
分贝定义为 20 * log10(|信号|)。

因此,从原始的时域信号,我们可以得到线性表示的频谱振幅。而使用分贝刻度来可视化频域振幅,则更为直观。
相位与解卷绕 🔄
信号的频谱包含振幅(现在用分贝计算)和相位。相位解卷绕是一种表示DFT相位谱的方法,使其更易于可视化和理解。
原始相位谱计算为频谱复数值的角度,其可视化结果通常显得杂乱。解卷绕通过在有间断处添加 2π 来平滑相位曲线。由于相位被限制在 0 到 2π 之间,当它超过 2π 时会回绕到 0。解卷绕让其以自然方式增长,从而得到更平滑、更易解读的函数。

零填充 🧩
零填充意味着在信号末尾添加零值。在DFT的上下文中,在一个域进行零填充会在另一个域产生插值信号。
例如,从一个大小为8的信号 x 开始,其DFT结果大小也为8。如果我们计算这8个样本加上8个零样本(总大小为16)的DFT,那么大小为8的原始幅度谱样本点保持不变,但在它们之间会出现插值点,使得频谱可视化更平滑。如果零填充到 N=32,则会得到更多插值点,频谱曲线更加平滑。

快速傅里叶变换(FFT)⚡
DFT的计算可能相当耗时。快速傅里叶变换是DFT方程的一种高效实现,它通过利用对称性来加速计算。
FFT要求输入信号的长度为 2的幂次方。基于此,算法(如经典的Cooley-Tukey算法)可以将计算分组,利用对称性进行成对计算,从而大幅提升效率。
以下是标准DFT与FFT计算时间的对比示例:
- 在普通笔记本电脑上,一个Python实现的DFT,当大小增加到16000时,计算时间接近2分钟。
- 而使用Python内置的高效FFT实现(底层为C语言),计算16000个样本的FFT只需不到1毫秒。

FFT的计算时间复杂度为 O(N log N),远低于DFT的指数级增长。
结合零相位窗使用FFT 🪟
为了使用FFT,我们需要输入信号长度为2的幂。但我们希望计算任意长度信号的频谱。为此,我们首先进行零填充,然后应用零相位窗。
具体步骤如下:
- 从一个给定长度的声音片段开始(例如401个样本)。
- 确定下一个2的幂长度(例如512)。
- 进行零填充,但并非简单地在末尾加零。我们采用零相位窗方式:将信号“中心”样本置于缓冲区的左侧(索引0处),然后是正时间样本(直到中间点),接着是负时间样本。这样就将信号打包进了所谓的FFT缓冲区。
- 对此缓冲区调用FFT,然后计算分贝表示的幅度谱和解卷绕后的相位谱。
这样可视化得到的幅度谱对称性良好且平滑,相位谱也呈现奇对称性且非常平滑。这得益于零相位窗消除了未对中样本可能引起的移位失真,以及解卷绕带来的平滑效果。
分析与综合 🎵

现在,我们可以将所学知识整合起来,进行DFT的分析与综合操作。
流程如下:
- 分析:从原始信号开始,计算其FFT,并正确表示为幅度谱和相位谱。由于对称性,通常只需显示正频率部分。
- 综合:利用正频率部分的幅度和相位信息,进行逆傅里叶变换,即可重建原始信号。
如果操作正确,输入信号和重建的输出信号的值应该完全相同。

总结 📝

本节课中我们一起学习了DFT的多个关键性质:能量守恒、分贝表示、相位解卷绕、零填充、快速傅里叶变换及其与零相位窗的结合使用,最后将这些概念整合到音频的分析与综合框架中。掌握这些性质对于后续处理更复杂的音频信号至关重要。

在下一讲中,我们将以此为基础,开始处理更复杂的声音信号。
020:周期信号 📈
在本节课中,我们将要学习周期信号的基本概念。周期信号是理解大多数信号以及傅里叶分析的基础。我们将通过生成和分析不同类型的周期信号,来直观地理解其特性。

生成基本周期信号

上一节我们介绍了课程背景,本节中我们来看看如何使用Audacity生成基本的周期信号。
最基础的周期信号是我们已经见过的正弦波。让我们生成一个500赫兹的正弦波。

以下是生成正弦波的步骤:
- 选择“生成”菜单下的“音调”。
- 设置波形为“正弦波”。
- 设置频率为 500 Hz。
- 设置持续时间为5秒。
- 设置振幅为0.8。

生成了我们的正弦波。我们可以播放它来听一听。
为了观察其周期性,我们可以放大波形。这里我们可以看到正弦波的周期性振荡。如果我们进一步放大,将开始看到信号中的采样点。这是一个离散信号,我们以44.1kHz的采样率生成了它,因此每秒有44100个采样点。
理解周期与频率
为了理解周期性的概念,我们需要测量周期长度。
周期性的含义是存在一个不断重复的周期或声音循环。这是一个正弦波的周期。在这里,我们可以看到所选区域的长度是0.002秒,也就是2毫秒。
我们可以通过Python终端将其用作计算器,将周期长度转换为频率。频率是周期的倒数。

以下是计算频率的公式:
频率 = 1 / 周期
所以,1 / 0.002 得到500赫兹,这正是这个正弦波的频率。
我们可能还想检查一个周期内有多少个采样点。要计算这个,我们应该从采样率开始。
以下是计算一个周期内采样点数量的公式:
采样点数 = 采样率 × 周期时长
所以,44100 × 0.002 得到88.2,这是一个周期内的采样点数。当然,它应该是一个整数,所以一个周期大约有88个采样点。
不同频率的正弦波
现在,让我们生成另一个不同频率的正弦波。例如,生成一个5000赫兹的正弦波。

这是5000赫兹的正弦波。听起来明显更高。我们也可以放大观察其周期性。但这里我们看到波形并不那么平滑。这是因为每个周期内的采样点更少,因此我们没有得到一个非常平滑的版本。
一个周期内有多少个采样点呢?实际上并不多,这里我们甚至可以数出来,大约有8到9个采样点。这很合理,因为500赫兹时有88个采样点,现在频率是它的10倍(5000赫兹),采样点数就会减少到大约十分之一,即8或9个。
采样点数量与频率之间的关系非常重要,并且与采样率相关。采样率越高,对于相同频率,每个周期内的采样点就越多。随着频率升高,在44100赫兹采样率下,每个周期内的采样点会变少,因此波形看起来甚至不像正弦波,尽管它确实是正弦波。
非正弦波的周期信号
让我们看看另一种周期信号,它不同于正弦波。我们将生成一个音调,但这次生成一个锯齿波。
我们回到500赫兹,振幅不要设得太高,因为这是一个频谱丰富的音色,否则会非常响亮。
这是一个锯齿波波形。我们可以播放它。如果我们放大这个波形,会看到它非常有周期性。由于采样率足够高且频率较低,如果我们放大,每个周期内会有很多采样点。

在这种情况下,我们测量到的周期将是相同的,对应500赫兹。但这并不意味着只有一个500赫兹的频率。实际上,这个波形包含许多频率,它以500赫兹为基频,并包含其倍数,因此它是一个谐波声音。
我们如何验证这一点呢?可以通过频谱分析来检查。在Audacity中,我们可以绘制频谱。

现在我们可以可视化这个锯齿波的频谱。清楚地看到它是一个相当复杂的频谱,其中有许多峰值。为了理解这一点,最好与我们开始时生成的正弦波进行比较。
这是我们开始时生成的正弦波,如果我们对它进行与锯齿波相同的频谱计算,现在会看到它明显非常不同。正弦波的频谱在500赫兹处只有一个主要的峰值,而这个锯齿波的频谱有许多峰值,对应这个谐波信号中存在的所有频率。

频率变化的周期信号
现在让我们做一些更复杂的事情。生成一个信号,但其频率不是恒定的,而是随时间变化。这通常被称为啁啾信号。
让我们生成一个正弦啁啾信号,频率从我们提到的两个频率之间变化,例如从500赫兹上升到5000赫兹。

振幅始终保持0.8,持续5秒。现在我们得到了一种滑音或啁啾信号。当然,我们可以播放它。这是一个频率上升的声音。
我们放大观察,会看到周期随时间不断变化。在开始处,周期会相当长(对应500赫兹,2毫秒),而到结束时,周期会小得多(是开始的十分之一)。
当然,我们可以通过分析这个信号的频谱来可视化这一点。如果我们选择开始部分并分析频谱,会看到频率大约在500赫兹。如果我们到信号的结尾部分并重新绘制频谱,会发现峰值已经偏移,移向了5000赫兹附近。它也是一个单一的峰值,但频率更高。

总结与展望
本节课中我们一起学习了周期信号。我们主要使用了Audacity,讨论了电子合成的周期信号。这些信号非常适合进行实验,因为我们知道如何生成它们,因此在分析时我们知道预期会看到什么。
在下一个演示课程中,我们将使内容更复杂。我们将实际分析更复杂的信号,那些可能部分具有周期性、部分不具有周期性的声音,以更真实地反映实际声音的复杂性。


021:复杂声音分析 🎵
在本节课中,我们将学习如何使用音频分析工具来观察和理解复杂声音信号。我们将从简单的周期性信号过渡到更复杂的真实声音,例如语音、复音乐器以及管弦乐,并探索在时域和频域中分析它们的方法。
上一节我们介绍了周期性信号的分析。本节中,我们将看看如何分析更为复杂的真实声音。
使用Sonic Visualizer分析语音
我们将从Sonic Visualizer开始,并打开一个语音文件进行分析。
这是一个男性语音的声音。我们可以听到:“Do you hear me? They don't lie at all.”
我们可以放大声音的不同部分,会发现有些部分更具周期性,而有些部分则完全没有周期性。
例如,单词结尾的“N”音。我们可以选择并播放它:“Doong”。它包含不同的段落。
在中间部分,声音更具“嗓音”特性。放大后,我们可以看到它相当具有周期性,虽然随时间变化,但存在重复且演变的模式。
相反,在这个声音起始的“T”音(爆破音)部分,则明显是非周期性的。我们看到它相当嘈杂,是一个典型的非周期性辅音。
为了强化这些概念,我们可以使用频谱分析。在Sonic Visualizer中,有“窗格”的概念,我们可以显示不同的窗格,其中一个就是频谱图。
这是声音在光标特定位置处的频谱。如上一课所述,最好以线性刻度可视化水平轴,并调整动态范围以便观察。
在元音类型的区域,我们可以看到对应于该声音谐波的峰值。
相反,如果我们移动到起始的“T”音部分,频谱明显更嘈杂,更难观察。
通过在此处更改DFT大小(例如改为2048个样本),我们或许能看得更清楚一些。
分析复音乐器声音

让我们看另一个更复杂的声音,例如这个大提琴二重奏。


这个声音包含两个音符。它不是单音乐器声音,而是复音声音。我们可以清楚地听到两个频率,一个相当稳定,另一个则是一个上下起伏的小音阶。
如果我们放大此处,会发现很难看到任何周期性,因为两个声音同时播放。在某些区域,两个音符的频率可能相互增强,我们或许能看到一些周期性振荡,但这通常相当困难。
在频域中,如果我们计算频谱窗格并显示频谱,再次设置为线性刻度并适当缩放,我们可以看到情况在变化。
我们可以识别出这两个旋律一起演奏时产生的谐波。如果能够放大这个区域(可惜Sonic Visualizer不允许),我们将看到两个同时发声的声部各自的不同谐波。
显然,在频域中,我们可以尝试识别在时域中困难得多的特征。我们可以逐步查看声音并找出不同的方面。



分析管弦乐声音
最后,让我展示一个更为复杂的声音,我认为即使是频谱分析也难以完全理解它,例如这个管弦乐声音。
这是一个完整的管弦乐队,包含许多弦乐器,是一个中国管弦乐团在演奏,还有打击乐器等等。


如果我们放大,几乎无法理解其中的任何部分。它基本上看起来像随机噪声,但其中当然包含大量结构。
希望频谱分析能让我们从中理解一些东西。因此,我们打开频谱窗格,再次更改为线性刻度,使其更平滑,并尝试获得更好的可视化效果。
这非常困难,但我们会发现这里有一些明显的峰值,通过它们我们可以理解一些突出的声部。我们将尝试理解它,但这当然非常困难。

事实上,这超出了本课程将进行的频谱分析范畴。分析如此复杂的信号需要技术,这仍然是开放的研究问题,非常具有挑战性。

总结
本节课中我们一起学习了使用Sonic Visualizer分析不同类型的真实声音。
我们分析了语音、包含两个旋律同时演奏的大提琴声音,以及管弦乐声音。
我们看到,分析真实声音并不容易。真实声音非常复杂。语音可能是最简单的,当有几个旋律同时演奏时我们或许能尝试理解一些东西,但在完整的管弦乐中,这确实非常困难。
无论如何,我们不应气馁,我们将尝试从中找出意义。在接下来的课程中,我们将学习如何处理它们。
今天的内容是对上一节周期性信号的补充,我们尝试分析了复杂声音,并使用了Sonic Visualizer,它允许我们比Audacity更好地可视化一些频谱分析。

下节课我们将介绍我们的工具——我们开发的SMS工具,尝试看看用这些工具是否能在分析声音方面做更多的事情。
022:频谱分析实践 🎵
在本节课中,我们将学习如何使用SMS工具进行离散傅里叶变换分析,以理解真实音频信号(如小提琴声音)的频谱特性。我们将通过调整分析参数,观察不同时间点和不同分析长度下的频谱变化。



回顾与工具介绍
在之前的演示课程中,我们使用Audacity和Sonic Visualizer分析了电子周期信号和真实声音的频谱。本节我们将继续这一主题,利用专门为此课程开发的SMS工具进行DFT分析,以深入理解声音。
SMS工具提供了一个交互式界面,允许我们灵活地调整分析参数并直观地查看结果。

加载与分析小提琴声音
首先,我们使用Audacity打开一个小提琴声音文件,以获得声音的整体概览。这是一个持续约2秒、相对稳定的小提琴音符。
为了进行更深入的分析,我们将使用SMS工具的模型界面。通过命令行进入相应目录并运行 python modelsGUI.py 即可启动界面。
在界面中,我们加载小提琴声音文件,并选择使用DFT模块进行分析。以下是初始分析步骤:
- 选择分析参数:我们首先设置分析窗口大小为512个样本(FFT大小需为2的幂次方),并从声音的中间部分(例如第1秒处)开始截取。
- 执行计算:计算该片段的DFT(使用FFT算法),并显示结果。
分析结果包含以下部分:
- 时域波形:显示被分析的512个样本(加窗后)。
- 幅度谱:显示正频率部分(0 Hz 至 采样率/2,即22050 Hz),幅度以分贝(dB)为单位。
- 相位谱:显示解缠绕后的相位,单位是弧度,以便观察更平滑的变化。
- 重建信号:通过对频谱进行逆变换得到的时域信号,由于加窗操作,它与原始输入片段并不完全相同。
调整分析参数以优化频谱


初始分析(512点FFT)的频谱可能无法清晰显示声音的谐波结构。这是因为频率分辨率不足,无法区分紧密相邻的频率成分。
为了提高频率分辨率,我们将FFT大小增加到下一个2的幂次方,即1024点,并重新计算。
# 示例:将FFT大小从512调整为1024
FFT_size = 1024

使用1024点FFT进行分析后,幅度谱中清晰地出现了多个峰值,这些峰值对应着小提琴声音的谐波。这表明增加分析长度可以获得更详细的频谱信息。

分析声音的不同部分:起始与稳态
声音在不同时间点可能具有不同的特性。小提琴声音的起始部分(起振阶段)与中间的稳态部分在听感和波形上都有差异。我们通过DFT分析来观察这种差异。
我们将分析位置从声音中间(第1秒)调整到起始部分(第0秒),并使用相同的1024点FFT进行分析。
对比两次分析结果可以发现:
- 稳态部分:频谱中的谐波峰值清晰、突出。
- 起始部分:频谱显得更杂乱,谐波不如稳态部分明显。这是因为起振阶段包含更多琴弓摩擦的噪声成分,这些时域上的不规则性在频谱中表现为更随机和不规则的形状。


理解分析参数:窗口大小与FFT大小
在DFT分析中,从输入信号中截取的样本数(窗口大小)与进行FFT计算的点数(FFT大小)可以不同。FFT大小决定了频率分辨率和计算出的频率点数量。

例如,我们可以从输入信号中截取800个样本,但在计算时使用1024点的FFT(不足的部分补零)。这与直接截取1024个样本进行分析相比,频率分辨率相同(因为FFT大小都是1024),但前者由于实际信号信息更少,频谱的清晰度可能略逊于后者。

深入解读频谱信息
在获得清晰的频谱图(如使用1024点FFT分析稳态部分)后,我们可以进一步解读其中的信息。
利用Python图形界面的缩放功能,我们可以聚焦于频谱的特定区域,例如前5000 Hz。
- 识别谐波与基频:在幅度谱中,可以观察到一系列等间距的峰值。将光标移动到第一个峰值处,其频率读数约为 240 Hz,这很可能就是该声音的基频。下一个峰值约为480 Hz(即2 * 240 Hz),后续峰值依次为基频的整数倍,这些就是谐波。
- 公式:
谐波频率 = n * 基频,其中n = 1, 2, 3, ...
- 公式:
- 查看相位信息:在相位谱的相同频率位置(如240 Hz, 480 Hz),可以读取对应谐波在分析中心时刻(本例中为第1秒)的相位值(单位为弧度)。这提供了谐波在时域中相对位置的另一维度信息。
通过SMS工具的交互界面,我们可以方便地缩放和测量,从而直观地识别出声音各个频率成分的幅度和相位,这比使用Sonic Visualizer或Audacity进行类似操作更为直接和灵活。
总结

本节课中,我们一起学习了如何使用SMS工具的DFT模块分析真实音频信号。我们以一个小提琴声音为例,实践了以下内容:

- 加载声音并设置DFT分析参数(窗口大小、FFT大小、分析位置)。
- 通过增加FFT大小来提高频率分辨率,从而更清晰地观察声音的谐波结构。
- 比较声音不同部分(起振段与稳态段)的频谱差异,理解时域特性如何在频谱中体现。
- 区分了分析窗口大小与FFT大小的概念。
- 学习了如何从幅度谱中识别基频和谐波,并从相位谱中读取对应频率的相位信息。

通过本周的三个演示课程,我们初步掌握了用于声音分析、时域可视化和频谱可视化的不同工具。当然,还有更多内容有待探索,下周我们将继续学习更深入的主题。
023:傅里叶性质编程实践

在本节课中,我们将学习如何利用傅里叶变换的性质,通过编程来分析音频信号。我们将使用Python及其相关库,实现一个能够计算并可视化信号频谱的程序。
概述
我们将从设置编程环境开始,逐步实现一个三角函数的傅里叶变换,并探讨信号中心化对相位谱的影响。接着,我们将把同样的方法应用于一个真实的音频文件片段,学习如何正确地进行零填充、使用对数刻度显示幅度谱以及对相位谱进行解卷绕处理。
环境设置与基础实现
首先,我们需要进入工作目录并启动一个交互式的Python环境。我们将使用iPython,并预先导入matplotlib库以便绘图。
# 启动iPython并预加载matplotlib
%pylab
在文本编辑器中,我们编写一个简单的脚本。该脚本导入必要的库,生成一个三角波信号,并计算其离散傅里叶变换。
import numpy as np
from scipy import signal
from scipy.fftpack import fft# 生成一个包含15个样本的三角波
x = signal.triang(15)
# 计算DFT(当长度非2的幂时,使用标准DFT算法)
X = fft(x)
# 计算幅度谱和相位谱
mX = abs(X)
pX = np.angle(X)
执行此脚本后,我们可以在iPython中查看和绘制生成的变量。三角波信号x及其幅度谱mX和相位谱pX现在可供使用。
绘制三角波信号:
plot(x)
幅度谱显示出对称性,但需要理解的是,在DFT中,频谱是循环的。数组的前半部分对应正频率,后半部分对应负频率。
绘制相位谱时,我们发现它并非为零,这与三角波作为偶函数的预期不符。这是因为我们的时间信号x并非以零点为中心。
信号中心化与相位谱
上一节我们介绍了三角波的频谱,并发现其相位谱非零。本节中我们来看看如何通过将信号在时域中心化来获得零相位谱。
我们修改脚本,在计算FFT之前,先将三角波信号中心化到时间零点。
# 创建与x同长的全零数组作为FFT缓冲区
fftbuffer = np.zeros(len(x))
# 将三角波的后半部分放在缓冲区开头(正时间)
fftbuffer[:len(x)//2] = x[len(x)//2:]
# 将三角波的前半部分放在缓冲区末尾(负时间)
fftbuffer[-(len(x)-len(x)//2):] = x[:len(x)//2]# 对中心化后的缓冲区进行FFT
X = fft(fftbuffer)
mX = abs(X)
pX = np.angle(X)
执行新脚本后,再次绘制中心化后的信号fftbuffer和其相位谱pX。此时,信号以零点为中心,其相位谱理论上应为零。由于数值计算误差,相位值会是非常接近于零的极小值。
分析真实音频信号
我们已经学会了如何对合成信号进行频谱分析。本节中我们来看看如何将这些技术应用于一个真实的音频文件。
我们编写一个新的脚本,用于读取一个音频文件,选取其中一段,并对其进行加窗和频谱分析。
import numpy as np
from scipy.fftpack import fft
import sys
sys.path.append(‘../software/models/‘) # 添加工具包路径
from utilFunctions import wavread# 定义参数
M = 501 # 窗口大小(样本数)
N = 1024 # FFT大小(2的幂)
# 计算半长,用于中心化
hM1 = int(np.floor((M+1)/2))
hM2 = int(np.floor(M/2))# 读取音频文件
fs, x = wavread(‘../sounds/soprano-E4.wav‘)
# 选取片段(从第5000个样本开始)
x1 = x[5000:5000+M]
# 应用汉明窗以减少频谱泄漏
xw = x1 * np.hamming(M)# 创建FFT缓冲区并进行中心化零填充
fftbuffer = np.zeros(N)
fftbuffer[:hM1] = xw[hM2:]
fftbuffer[-(hM2):] = xw[:hM2]# 计算FFT
X = fft(fftbuffer)
# 计算幅度谱(dB刻度)和相位谱
mX = 20 * np.log10(abs(X))
pX = np.unwrap(np.angle(X)) # 解卷绕相位
执行此脚本后,我们可以绘制原始音频、加窗后的片段、中心化零填充后的缓冲区,以及最重要的——其对数幅度谱和解卷绕后的相位谱。通常,我们只绘制频谱的前一半(从0到N/2),因为对于实信号,后半部分是冗余的。
以下是分析真实音频信号时的一些关键步骤列表:
- 读取与切片:使用
wavread函数读取WAV文件,并选取感兴趣的短时段。 - 加窗:应用如汉明窗之类的窗函数,以减少频谱泄漏。
- 中心化与零填充:将信号样本重新排列,使其在时域上以零点为中心,并用零填充至预定的FFT长度
N(通常为2的幂)。 - 计算FFT:使用高效的FFT算法计算频谱。
- 后处理与可视化:将幅度谱转换为分贝(dB)刻度以便观察,对相位谱进行解卷绕使其连续,并仅绘制有效的前半部分频谱。

总结
本节课中我们一起学习了如何将傅里叶变换的理论性质应用于编程实践。我们实现了对三角波和真实音频信号的频谱分析,关键点包括:
- 理解了信号时域中心化对获得零相位谱的重要性。
- 掌握了使用零填充技术来适配FFT算法(要求长度为2的幂)的方法。
- 学会了以对数(dB)刻度可视化幅度谱,以及对相位谱进行解卷绕,从而得到更清晰、更有意义的频谱图。
- 熟悉了
iPython交互环境与NumPy、SciPy、matplotlib等库在音频信号处理中的基本用法。

通过本次编程实践,我们为后续更复杂的音频分析任务打下了坚实的基础。
024:03_03_02 DFT模型实现 🎵

在本节课中,我们将学习SMS工具包中用于实现离散傅里叶变换(DFT)分析与合成的实际代码。我们将深入探讨DFTmodel函数,了解它如何从声音片段中提取频谱,并重构出声音。
概述
我们将介绍SMS工具包中models子目录下的dftModel.py文件。该文件包含三个核心函数,分别用于完整的DFT分析合成、独立的分析以及独立的合成。我们将通过一个测试程序来演示这些函数的使用,并可视化分析结果。
DFT模型函数详解
上一节我们介绍了DFT的基本概念,本节中我们来看看其在SMS工具包中的具体实现。
dftModel.py文件定义了三个主要函数:
dftModel:该函数实现了完整的分析与合成流程。输入为原始声音信号x、窗函数w和FFT大小N,输出为合成后的声音信号y。dftAnal:该函数仅实现分析部分。输入同样为x、w和N,输出为幅度谱mX和相位谱pX。dftSynth:该函数仅实现合成部分。输入为幅度谱mX、相位谱pX和窗函数长度M,输出为合成信号y。
分析函数 (dftAnal) 流程
以下是dftAnal函数的核心步骤:
- 声明与初始化变量:为计算准备必要的变量和数组。
- 窗函数归一化:对输入的窗函数进行归一化处理。
- 加窗:将输入信号
x与窗函数w相乘。 - 零相位窗:将加窗后的信号置于一个长度为
N的缓冲区中心,实现零相位窗。 - 计算FFT:对缓冲区信号执行快速傅里叶变换(FFT)。
- 计算频谱:从FFT结果中计算对数幅度谱
mX和解卷绕后的相位谱pX。
合成函数 (dftSynth) 流程
以下是dftSynth函数的核心步骤:
- 声明与初始化:初始化输出信号数组等变量。
- 重建完整频谱:由于
dftAnal只返回正频率部分的频谱,dftSynth需要根据对称性重建负频率部分。- 正频率的复数表示由幅度和相位通过欧拉公式计算:
complex = mX * exp(j * pX)。 - 负频率部分是正频率的共轭对称。
- 正频率的复数表示由幅度和相位通过欧拉公式计算:
- 计算逆FFT:对重建的完整复数频谱执行逆FFT(IFFT)。
- 提取实部:IFFT结果通常是复数,我们取其实部作为时域信号。
- 去除零相位窗效应:对信号进行移位,抵消分析时零相位窗操作的影响。
- 归一化:应用必要的归一化因子(如除以窗函数能量)以得到正确的幅度。


测试程序与结果可视化


为了测试这些函数,我们创建了一个小程序。以下是程序的主要步骤:
- 导入包:指定工具包路径,导入
utilFunctions和dftModel模块。 - 读取音频:读取一个钢琴声音文件,并将其转换为浮点数数组。
- 生成窗函数:使用
get_window函数生成一个长度为511的汉明窗。 - 截取片段:从输入声音中截取一个起始于0.2秒、长度为
M的片段。 - 执行分析:调用
dftAnal函数,传入声音片段、窗函数和FFT大小(1024),得到幅度谱和相位谱。 - 执行合成:将得到的频谱送入
dftSynth函数进行合成,并对输出进行归一化。

运行测试程序后,我们可以绘制以下图像进行验证:

- 原始输入信号:完整的钢琴声音波形。
- 分析片段:起始于0.2秒的音频片段。
- 窗函数:应用的汉明窗形状。
- 幅度谱:分析得到的频谱幅度(0到
N/2,即512点)。 - 相位谱:解卷绕后的相位谱。

此外,SMS工具包还提供了一个更完善的dftModel_function.py文件,它集成了分析、合成并生成格式规范的图表。运行该文件,可以得到包含时间域波形、频率域幅度谱(dB标度)和相位谱、以及加窗后输出信号的完整图表。我们可以放大图表的特定区域,以观察某个谐波分量的细节行为。
总结

本节课我们一起学习了SMS工具包中DFT分析合成模型的实现代码。我们详细了解了dftAnal和dftSynth函数的工作流程,包括加窗、零相位处理、FFT/IFFT计算以及频谱的对称性重建。通过测试程序,我们实践了如何使用这些函数处理音频片段并可视化频谱结果。理解这些代码对于掌握DFT至关重要,并为下一周学习短时傅里叶变换——一个能够分析任意长度声音的分析合成框架——奠定了坚实的基础。
025:短时傅里叶变换-1 🎵
在本节课中,我们将学习短时傅里叶变换。这是分析时变音频信号的关键技术。我们将首先介绍其基本方程,然后重点讨论分析窗的作用。
概述
到目前为止,我们已经学习了如何计算信号的频谱,即离散傅里叶变换。然而,真实的声音无法用单一的频谱来表示。声音会随时间变化,我们需要捕捉这种时间变化。短时傅里叶变换就是我们的解决方案。
短时傅里叶变换方程

上一节我们介绍了DFT,本节中我们来看看短时傅里叶变换方程。它本质上是DFT的一个修改版本,有几个重要区别。
基本方程如下:
X(l, k) = Σ_{n=0}^{N-1} [w(n) * x(n + lH)] * e^{-j2πkn/N}
输入信号 x(n) 不再是直接输入,而是与分析窗 w(n) 相乘后的一个片段。变量 l 是帧编号,H 是跳跃大小。这意味着输出将是一个频谱序列,每个频谱对应声音的一个不同时间片段。

为了强调零相位窗的概念,从现在起,我们通常将时间索引指定为从 -N/2 到 N/2-1,使其始终以0为中心。这样就不会产生任何相位变化或时移。


理解加窗过程

为了更好地理解对声音加窗的效果,让我们看一个对实正弦波加窗并计算其频谱的例子。

从一个实正弦波开始:
x(n) = A_0 * cos(2πk_0 n / N)

它可以表示为两个复指数的和。代入STFT方程并加窗后,由于DFT的线性,我们可以将其拆分为两个方程。最终结果是分析窗的频谱,频率偏移了输入信号的频率,并乘以输入信号的振幅。

通过图示,我们可以更好地理解这个过程。顶部是窗函数,下方是输入的正弦信号。窗函数的频谱(例如汉宁窗)是一个以零为中心的幅度谱。加窗正弦波的DFT结果,其形状与窗函数的频谱相同,但出现在正弦波的正负频率处,并带有正弦波的相位。
分析窗的重要性
从以上讨论中,我们可以认识到分析窗在正弦波及任何声音频谱中的重要性。显然,我们需要花些时间来解释各种窗函数。

分析窗通常是一个实函数,并且关于原点对称。最简单的窗是矩形窗。其频域幅度谱具有我们称之为sinc函数的形状。我们主要关注两个方面:主瓣的宽度和最高旁瓣的电平。
以下是音频信号处理中常用的几种窗函数列表:
- 矩形窗:
w(n) = 1, 主瓣宽度为2个频点,最高旁瓣电平为-13.3 dB。 - 汉宁窗:
w(n) = 0.5 - 0.5 * cos(2πn/(N-1)), 主瓣宽度为4个频点,最高旁瓣电平为-31.5 dB。 - 汉明窗:
w(n) = 0.54 - 0.46 * cos(2πn/(N-1)), 主瓣宽度为4个频点,最高旁瓣电平为-42.7 dB。 - 布莱克曼窗:
w(n) = 0.42 - 0.5*cos(2πn/(N-1)) + 0.08*cos(4πn/(N-1)), 主瓣宽度为6个频点,最高旁瓣电平为-58.1 dB。 - 布莱克曼-哈里斯窗:由四个余弦项求和构成,主瓣宽度为8个频点,最高旁瓣电平为-92 dB。
每种窗都在主瓣宽度和旁瓣电平之间提供了不同的折衷方案。理想情况是拥有最窄的主瓣和最低的旁瓣。
窗函数应用对比

现在,让我们比较一下将不同窗函数应用于同一段声音的效果。我们取一段固定长度的声音片段,分别应用矩形窗、汉明窗和布莱克曼窗。得到的频谱明显不同。对于这种分析,布莱克曼窗可能效果最好,它呈现出更平滑的频谱,谐波峰值也更清晰可辨。

总结


本节课中,我们一起学习了短时傅里叶变换的第一部分内容。我们解释了STFT的基本方程,并重点讨论了分析窗的作用。在下一部分,我们将继续探讨这个话题。
026:短时傅里叶变换(第二部分)🎵
在本节课中,我们将继续学习短时傅里叶变换。我们将回顾核心概念,并深入探讨窗口大小、FFT大小、跳跃步长等关键参数的选择,以及它们如何共同影响时频分辨率。最后,我们将学习如何通过逆变换从频谱中重建原始信号,并构建一个完整的分析-合成系统。

回顾:STFT与加窗
上一节我们介绍了短时傅里叶变换的基本概念。我们知道,STFT是DFT的时变版本,而加窗是理解STFT的关键。例如,对于同一段小提琴声音,使用矩形窗和布莱克曼窗得到的幅度谱截然不同。

窗口大小的影响
窗口大小对分析结果有显著影响。以下是不同窗口大小的效果对比:
- 小窗口(如128点):得到的幅度谱信息较少,细节不足。
- 大窗口(如1024点):得到的幅度谱包含更多细节,能更清晰地展示声音的谐波结构。
因此,选择窗口大小时需要在时间分辨率和频率分辨率之间进行权衡。
窗口的奇偶性

窗口大小可以是奇数或偶数,这主要影响相位谱:
- 偶数大小窗口:无法以零点为中心完全对称,导致相位谱存在斜率。
- 奇数大小窗口:可以以零点为中心完美对称,从而获得零相位值。

因此,在可能的情况下,我们倾向于使用奇数大小的窗口。


FFT大小与零填充
FFT大小可以独立于窗口大小进行选择,这通过零填充实现。零填充可以增加频谱的采样点数,使频谱图更平滑,便于观察频谱特征。
- FFT大小等于窗口大小:得到基本的幅度谱。
- FFT大小大于窗口大小(如4倍):通过零填充,得到更平滑、插值更细致的频谱,有助于更精确地识别频率成分。

跳跃步长与重叠

STFT分析中,窗口每次向前移动的样本数称为跳跃步长或Hop Size。跳跃步长会影响分析帧之间的重叠程度,进而影响最终合成信号的质量。
跳跃步长的选择应确保所有分析窗叠加后的和尽可能为一个常数,以避免合成时产生调制失真。例如:
- 跳跃步长为窗口长度的一半时,叠加后的函数存在明显的振荡。
- 跳跃步长为窗口长度的四分之一时,叠加后的函数在中间部分接近一条直线,这是更理想的情况。
时频权衡
将上述概念应用于分析一个复杂的钢琴乐句时,我们可以直观地看到时频权衡效应:
- 小窗口:具有良好的时间分辨率,能清晰捕捉音符的起振瞬态(垂直方向特征明显),但频率分辨率较差,谐波(水平线条)较模糊。
- 大窗口:具有良好的频率分辨率,能清晰显示谐波结构(水平线条清晰),但时间分辨率较差,音符的起振变得模糊。
相位谱信息
除了幅度谱,相位谱也包含重要信息。通常我们观察相位谱的导数,其中颜色平坦的区域表示相位稳定(如谐波处),颜色变化剧烈的区域表示相位快速变化。
逆短时傅里叶变换
与DFT不同,STFT是可逆的。我们可以通过逆STFT从幅度谱和相位谱中重建原始信号。核心方法是重叠相加法:
- 对每一帧的频谱
X_l[k]进行逆DFT,得到时域帧y_w[l, n]。 - 将所有时域帧按其时间位置进行移位并对齐。
- 将所有移位后的帧相加,合成完整的输出信号
y[n]。
合成过程能否完美重建原始信号,取决于分析时使用的窗函数和跳跃步长是否满足完全重构条件。
完整的分析-合成系统
基于STFT和逆STFT,我们可以构建一个完整的分析-合成系统。该系统的工作流程如下:
- 分析:输入信号
x[n]经过加窗、分帧,对每一帧进行FFT,得到幅度谱和相位谱。 - 处理:(可选)在频域对频谱进行修改。
- 合成:对每一帧进行逆FFT,得到加窗的时域帧,然后通过重叠相加法合成最终输出信号
y[n]。

当窗函数和跳跃步长满足完全重构条件时,输出信号 y[n] 将与输入信号 x[n] 完全相同。

总结

本节课中,我们一起深入学习了短时傅里叶变换的第二部分。我们探讨了窗口大小、FFT大小、跳跃步长等关键参数的选择及其对时频分析结果的影响,理解了著名的“时频权衡”原理。最重要的是,我们学习了如何通过逆STFT和重叠相加法从频谱中完美重建原始信号,从而构建出一个完整的音频分析-合成系统框架。

掌握STFT是进行更高级音频处理的基础。在接下来的编程实践课程中,我们将把这些理论知识付诸实践。
027:频谱图分析实践
在本节课中,我们将学习如何使用短时傅里叶变换及其可视化形式——频谱图,来分析一个具体的音频信号。我们将通过分析一个女高音演唱的片段,来深入理解STFT的参数设置及其对分析结果的影响。
概述
频谱图是短时傅里叶变换输出的可视化结果,它展示了信号随时间变化的频谱。本节课将通过Sonic Visualizer和SMS Tools两个工具,分析一个带有颤音的女高音演唱片段,以理解STFT的工作原理和参数选择的重要性。
使用Sonic Visualizer进行分析
首先,我们使用Sonic Visualizer打开并聆听需要分析的音频。这是一个音调较高、带有明显颤音的女高音演唱音符。
为了更深入地理解这个声音,我们打开频谱图面板。在频谱图中,我们可以看到一些清晰的信息:水平线对应着声音的谐波,而上下振荡的线条则对应着演唱中的颤音。
为了进行更细致的分析,我们打开另一个面板来查看单个时间点的频谱。我们首先在线性刻度下查看,并使用与STFT相同的窗函数,设置窗长为1024个样本。这个频谱图的一个切片清晰地显示了多个峰值,这些峰值对应着频谱图中的水平谐波线。
现在,让我们改变一些参数。例如,将窗长改为256个样本。此时,我们看到的频谱形状变得平滑许多,无法分辨出单个谐波,只能看到整体的共振峰结构。共振峰是声道共振的结果,正是它让我们能够区分不同的元音。
如果我们回到能分辨谐波的窗长设置,并在时间轴上移动,会发现谐波的变化比共振峰的变化更为明显。
接下来,我们尝试改变窗函数的类型。在首选项的分析选项卡中,将当前的黑曼窗改为矩形窗。结果显示,单个频谱的噪声基底变得很高,谐波难以辨认;在频谱图中则出现了垂直的线条和失真。这表明对于此类声音,黑曼窗是更优的选择。
使用SMS Tools进行分析
现在,我们使用SMS Tools进行同样的分析。SMS Tools提供了计算单帧频谱和时变频谱图的功能。
我们首先分析同一个女高音声音在0.5秒处的单帧频谱。使用与之前相同的参数:1024点的FFT大小和窗长,以及黑曼窗。计算得到的频谱清晰地显示了谐波对应的峰值,并且我们还看到了之前未显示的相位谱,以及通过对该频谱进行逆FFT重建回来的加窗信号。
同样,我们可以改变参数。将窗长和FFT大小都改为256,得到的频谱就平滑得多,信息量减少。SMS Tools界面的一个优势是可以独立控制窗长和FFT大小。例如,我们可以设置FFT大小为1024,而窗长为801。这样,我们虽然使用了更少的样本,但由于进行了零填充,频率分辨率仍然很好,频谱形状也相当平滑。
接下来,我们从短时傅里叶变换的角度,即频谱图,来观察整个信号。我们使用相同的参数:1024点的窗长和FFT大小。跳数需要远小于窗长以确保正确的重叠相加重建,对于1024点的黑曼窗,我们至少需要四分之一的重叠,因此设置跳数为256。计算得到的频谱图结果良好,重建出的声音与原始声音非常接近。
然而,如果跳数设置不正确,例如将跳数设置为与窗长相同(1024),重建就会出错。输出信号会出现以帧率为频率的调制,因为帧与帧之间没有正确的重叠来平滑过渡。这清楚地表明,我们需要一个更小的跳数值。
参数探索与总结
总之,我鼓励大家尝试调整这些参数:改变窗长、FFT大小、跳数以及窗函数类型,并在DFT和STFT之间切换。我相信这能帮助你很好地掌握这些不同参数对频谱图可视化以及信号重建效果的影响。
本节课中,我们一起使用Sonic Visualizer分析了人声,并可视化了频谱图与单帧频谱。我们也使用SMS Tools的界面完成了相同的分析。所使用的女高音声音来自Freesound。
希望这次实践能让你对STFT有更直观的认识,并理解使用这类技术来可视化特定声音(在本例中是女高音)是多么有用。当然,这只是更复杂分析的开始。在下一次实践课中,我们将分析一个结构更复杂的时变声音,并探讨如何利用频谱图分析来获取其中的信息。


感谢你的关注,我们下节课再见。
028:分析声音 🎵

在本节课中,我们将继续学习如何使用短时傅里叶变换分析声音。上一节我们分析了女高音的声音,本节中我们将以钢琴声音为例,从另一个视角理解短时傅里叶变换的应用。
分析钢琴声音 🎹
我们使用Sonic Visualizer软件打开一个钢琴声音文件。这是一个包含五个音符的简单钢琴乐句,声音清晰。
以下是分析步骤:
- 打开SMS工具中的短时傅里叶变换模块。
- 加载钢琴声音文件
piano.wav。

设置分析参数 ⚙️
上一节我们提到,布莱克曼窗是一个不错的选择。我们将继续使用它。
- 窗口大小:钢琴音高不像人声那么高,因此需要更大的窗口尺寸。我们从1501开始。通常我们使用奇数大小的窗口,这样窗口可以以0为中心,对相位分析尤其方便。
- FFT大小:FFT大小必须大于窗口大小。为了计算效率,我们通常使用2的幂次方。大于1500的2的幂是2048,这是一个合适的尺寸。
- 跳跃大小:对于布莱克曼窗,跳跃大小应确保窗口正确重叠,至少为窗口大小的四分之一。1500的四分之一大约是325。
使用这些参数进行计算后,我们得到输入声音、幅度谱图、相位谱图和重建的输出。
重建的声音听起来很好,这意味着跳跃大小和窗口大小的选择是正确的,确保了正确的重叠。
在相位谱图中,我们看到清晰的垂直线,这些线对应着音符的起振阶段。这表明在起振期间,相位发生了显著变化。在音符的稳定阶段,我们看到更多的水平结构,这意味着谐波保持了某种可识别的相位连续性。
在幅度谱图中,我们可以清晰地看到红色的谐波线。随着声音演变,钢琴作为一种打击乐器,在起振阶段能量更高,谐波更丰富。随着时间的推移,谐波衰减,尤其是高频谐波衰减得更快,而低频谐波保留得更多。声音的起振过程也清晰可见。
深入细节:时间-频率权衡 🔍

现在,让我们放大观察中间音符的细节,从起振前一点到音符结束。
放大后,我们可以看到分析的离散化特性。垂直方向上,每个竖条对应一个分析帧,即跳跃大小的样本数(325个样本)。水平方向上,我们也看到离散化的横线,由于FFT使用了2048个样本,频率分辨率较好,这些横线更细。
接下来,我们使用另一组参数进行计算,以观察区别。
- 窗口大小:201个样本(更小)。
- FFT大小:256。
- 跳跃大小:50(约为窗口大小的四分之一)。
计算后,我们得到新的分析结果。由于保持了跳跃大小与窗口大小的比例关系,合成输出与原始声音相同。
现在,放大到与之前相同的区域进行比较。第一个分析具有较好的频率分辨率和相对较差的时间分辨率。第二个分析则具有较好的时间分辨率和较差的频率分辨率。
这揭示了短时傅里叶变换的一个核心概念:时间-频率权衡。在选择分析参数时,需要在时间分辨率和频率分辨率之间做出取舍,这取决于所要分析的特定声音。
分析起振与稳态阶段 📊


为了进一步理解声音特性,我们分析声音在起振阶段和稳态阶段的单个频谱。



首先,在起振时刻(约1.54秒)计算DFT,参数与之前相同(窗口1501,FFT 2048)。然后,在稳态阶段(起振后约100毫秒,约1.64秒)用相同参数再次计算。
比较这两个频谱图(上图为起振阶段,下图为稳态阶段),我们可以观察到:
- 时域:两者有明显区别。
- 频域:在起振阶段,谐波不那么清晰明确,因为声音刚开始,谐波尚未完全建立。在稳态阶段,频谱峰值更加清晰明确。此外,在起振阶段,高频部分的噪声基底能量更高,即高频更响亮;而在稳态阶段,低频谐波明显更突出。

这是一种利用短时傅里叶变换进行声音分析、理解特定声音片段的好方法。

总结 📝

本节课中我们一起学习了如何使用SMS工具和短时傅里叶变换分析钢琴声音。我们探讨了参数设置(如窗口大小、FFT大小和跳跃大小)对分析结果的影响,特别是时间-频率权衡这一核心概念。通过对比声音在起振阶段和稳态阶段的频谱,我们获得了对钢琴声音特性的深入理解。希望本次分析能让你对短时傅里叶变换这一工具,以及钢琴这一有趣乐器的声音有更深的体会。
029:04_03_01_窗函数(第一部分)🎵


在本节课中,我们将从编程的角度探讨短时傅里叶变换的一个基本元素——分析窗函数。我们将学习如何生成不同的窗函数,计算其频谱,并观察窗函数如何影响正弦信号的频谱分析。
概述
窗函数是短时傅里叶变换的核心。它决定了我们如何截取音频信号的一小段进行分析。不同的窗函数具有不同的频谱特性,如主瓣宽度和旁瓣衰减水平,这些特性直接影响频谱分析的精度和分辨率。
窗函数的生成与频谱分析
首先,我们需要导入必要的Python库,并生成一个窗函数。以下代码展示了如何生成汉宁窗并计算其频谱。
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt# 定义窗长度
M = 63
# 生成汉宁窗
window = sp.signal.get_window(‘hann‘, M)
上一节我们介绍了如何生成一个基本的窗函数。本节中,我们来看看如何为FFT计算准备这个窗函数,特别是进行零相位窗处理和补零操作。
为了将窗函数中心对齐到零频位置以进行FFT分析,我们需要执行以下步骤:
- 创建一个FFT缓冲区(例如512个样本)。
- 将窗函数的后半部分放在缓冲区的开头,前半部分放在缓冲区的末尾。
# 计算窗的中间点,处理奇偶长度
hM1 = int(np.floor((M+1)/2))
hM2 = int(np.floor(M/2))# 创建FFT缓冲区并执行零相位窗移位
N = 512
fft_buffer = np.zeros(N)
fft_buffer[:hM2] = window[hM1:]
fft_buffer[N-hM1:] = window[:hM1]
准备好缓冲区后,我们就可以计算其频谱了。
# 计算FFT
X = np.fft.fft(fft_buffer)
# 计算幅度谱(转换为分贝)和相位谱
absX = np.abs(X)
# 避免对零值取对数
absX[absX < np.finfo(float).eps] = np.finfo(float).eps
mX = 20 * np.log10(absX)
pX = np.angle(X)
为了更直观地可视化频谱,我们通常将零频分量调整到频谱图的中心。
# 调整频谱显示,将零频移至中心
mX = np.fft.fftshift(mX)
pX = np.fft.fftshift(pX)
运行上述代码后,我们可以绘制窗函数本身、其在FFT缓冲区中的位置以及最终的幅度谱和相位谱。一个零相位窗的相位谱在通带内应接近0(模2π)。
不同窗函数的特性比较
窗函数的主要特性可以通过其频谱来观察,特别是主瓣宽度和最高旁瓣电平。
以下是几种常见窗函数的特性描述:
- 汉宁窗:主瓣宽度约为4个频点(bin),最高旁瓣电平约为-31 dB。
- 汉明窗:主瓣宽度同样约为4个频点,但最高旁瓣电平更低,约为-42 dB。
- 布莱克曼窗:主瓣更宽,约为6个频点,旁瓣抑制更好,最高旁瓣电平约为-58 dB。
- 布莱克曼-哈里斯窗:主瓣最宽,约为8个频点,但旁瓣抑制能力最强,最高旁瓣电平可低至-92 dB。
通过修改代码中的窗函数名称,我们可以直观地比较这些差异。选择窗函数时,需要在频率分辨率(主瓣宽度)和频谱泄漏抑制(旁瓣电平)之间进行权衡。
窗函数对信号频谱分析的影响
理解了窗函数自身的特性后,我们来看看它对实际信号分析的影响。当我们分析一个加窗后的正弦信号时,在频谱中看到的实际上是窗函数的频谱,它被搬移到了正弦信号的频率位置上。
为了简化分析过程,我们可以使用SMS工具包中的dftAnal函数。该函数封装了加窗、补零和FFT计算等步骤。
# 导入SMS工具包中的DFT分析函数
import sys
sys.path.append(‘../software/models/‘)
import dftModel as DFT# 生成一个正弦信号
fs = 44100
f = 5000
t = np.arange(101) / fs
x = np.sin(2 * np.pi * f * t)# 使用汉明窗进行分析
window = sp.signal.get_window(‘hamming‘, 101)
N = 1024
mX, pX = DFT.dftAnal(x, window, N)

运行分析后,我们得到正弦信号的频谱。该频谱以5000 Hz为中心,形状与我们之前看到的窗函数频谱一致。信号的幅度信息也体现在频谱的幅度值中。
如果我们更换窗函数,例如使用布莱克曼-哈里斯窗,频谱的形状会随之改变,主瓣变宽,旁瓣被显著抑制。这再次验证了窗函数特性直接决定了我们观察到的信号频谱形态。
总结

本节课中,我们一起学习了窗函数在频谱分析中的关键作用。我们掌握了如何用Python生成和可视化不同窗函数,并理解了主瓣宽度和旁瓣电平这两个核心概念。通过分析加窗的正弦信号,我们认识到信号频谱是窗函数频谱在信号频率处的搬移。深入理解窗函数是掌握短时傅里叶变换及其他频谱分析技术的基础。在下一次编程课中,我们将把这些知识整合起来,实现完整的短时傅里叶变换。
030:短时傅里叶变换编程实现 🎵
在本节课中,我们将学习如何编程实现短时傅里叶变换。我们将从理论过渡到实践,使用Python和SMS工具包来构建一个完整的STFT分析-合成系统。我们将详细讲解核心代码,并演示如何可视化频谱结果。
上一节我们介绍了窗函数及其频谱特性,本节中我们来看看如何利用窗函数来实现完整的短时傅里叶变换。
概述:STFT系统与实现

短时傅里叶变换的核心思想是将一个长音频信号分割成多个短片段(帧),对每个片段应用窗函数后进行傅里叶变换,从而得到一系列随时间变化的频谱。其核心公式可以表示为:

X(l, k) = Σ_{n=0}^{N-1} [x(n + lH) * w(n)] * e^{-j2πkn/N}
其中:
x(n)是原始音频信号。w(n)是窗函数(如汉明窗)。l是帧索引。H是帧移(hop size)。N是FFT大小。k是频率索引。
整个STFT处理流程可以看作一个信号处理系统:首先对加窗后的信号片段进行FFT分析,得到复频谱;然后可以通过逆FFT和重叠相加法(Overlap-Add)来合成重建原始信号。
SMS工具包中的STFT实现
SMS工具包中的STFT实现主要包含在 stft.py 文件中。以下是该文件的核心函数结构:
# stft.py 中的主要函数
def stft(x, fs, w, N, H):# 完整的分析-合成函数# 返回合成后的音频def stftAnal(x, w, N, H):# 仅分析函数# 返回幅度谱和相位谱序列def stftSynth(mY, pY, w, N, H):# 仅合成函数# 根据幅度谱和相位谱重建音频
stft 函数是分析-合成的核心。它接收原始音频数组 x、采样率 fs、窗函数 w、FFT大小 N 和帧移 H 作为参数。其内部是一个循环,指针 pin 以 H 为步长遍历整个音频,在每一帧位置调用 dftAnal 函数进行分析。


使用STFT函数分析音频
以下是一个调用 stftAnal 函数分析音频文件的示例脚本步骤:
- 导入必要的包和函数。
- 定义参数:包括音频文件路径、窗函数类型、窗长、FFT大小和帧移。
- 读取音频文件。
- 生成窗函数。
- 调用
stftAnal函数进行分析,得到幅度谱矩阵mX和相位谱矩阵pX。
运行脚本后,我们可以检查得到的变量。例如,mX.shape 会返回 (238, 512),表示共有238帧,每帧有512个频谱值(对应正频率部分)。
可视化频谱结果
我们可以通过绘图来直观地观察STFT的分析结果。
- 绘制单帧频谱:可以选取特定帧(如第50帧)的幅度谱
mX[50, :]或相位谱pX[50, :]进行绘制。对于长笛声音,我们能在幅度谱中看到清晰的谐波结构。 - 绘制时频谱图:为了观察整个信号频谱随时间的变化,我们需要绘制时频谱图。可以使用
pcolormesh函数来绘制矩阵。为了符合习惯(时间从左到右,频率从下到上),通常需要先将频谱矩阵mX转置。
# 绘制时频谱图的示例
plt.pcolormesh(mX.T) # 转置矩阵以调整坐标轴方向
plt.xlabel(‘时间 (帧)‘)
plt.ylabel(‘频率 (bin)‘)
plt.title(‘幅度谱时频图‘)
plt.show()
完整的分析-合成与图形界面
stft_function.py 文件提供了一个更完整的示例,它集成了分析、合成、绘图和文件输出功能。该脚本的 main 函数执行以下步骤:
- 读取输入音频。
- 进行STFT分析。
- 进行STFT合成,重建音频。
- 将合成音频写入新的WAV文件。
- 绘制输入音频、幅度谱、相位导数谱和输出音频的波形图。


运行此脚本后,会生成一个合成音频文件(如 pianoSTFT.wav),其听起来应与原始音频几乎完全相同,这验证了STFT分析-合成过程的正确性。
总结

本节课中我们一起学习了短时傅里叶变换的编程实现。我们深入探讨了SMS工具包中STFT函数的工作原理,包括其分析、合成以及可视化频谱的方法。通过动手编写和运行代码,我们理解了如何将音频信号分割成帧、加窗、进行FFT变换,以及如何通过逆变换和重叠相加法重建信号。请务必亲自尝试修改代码中的参数(如窗函数、帧移),并观察其对分析结果和合成质量的影响,从而加深对STFT从编程角度的理解。
031:正弦模型一 🎵
在本节课中,我们将学习正弦模型。这是一种在短时傅里叶变换基础上构建的、更高层次的音频表示方法。它通过牺牲部分STFT的精确性,换取了更高的灵活性和抽象能力。
上一节我们讨论了短时傅里叶变换。本周,我们将进一步探讨如何获得更高层次的音频表示。

正弦模型介绍

正弦模型的核心思想很简单:将声音表示为多个时变正弦波的和。
该模型可以用以下公式表示:
x(n) = Σ_{r=1}^{R} A_r(n) cos(2π f_r(n) n + φ_r(n))
其中,R 是正弦波的数量,每个正弦波都有随时间变化的瞬时振幅 A_r(n)、频率 f_r(n) 和相位 φ_r(n)。
频域中的正弦波
我们更关心在频域(频谱)中表示这些正弦波。让我们看看这是如何实现的。

从信号 x 开始,它是一个实正弦波。对其加窗版本进行DFT变换。正弦波可以表示为两个复正弦波的和,再乘以我们使用的窗函数。作为两个复指数的和,我们可以将其拆分为两个对称的、独立的DFT分量。
本质上,这是窗函数变换的移位版本。第一个分量是窗函数 W 的DFT,其频率索引发生了偏移。另一个分量是相同的窗函数,但向正频率方向偏移,并且由相同的振幅进行缩放。

频谱中的正弦波示例
以下是一个正弦波及其频谱的示例。

这是一个频率为440 Hz的正弦波。在频谱中,我们只绘制正频率部分。我们看到一个以440 Hz为中心的峰值,在主瓣区域的相位是平坦的,这对应于正弦波在位置0处的相位。
现在,让我们看一个更复杂的例子:由两个正弦波组成的声音。

这两个正弦波频率分别为440 Hz和490 Hz。在时域中,我们可以看到调制现象。在频谱中,我们看到两个正弦波各自的贡献,即两个峰值,以及它们各自的相位。
接下来,我们看一个真实声音的例子:双簧管的声音。
这是一个演奏A4音符(基频约440 Hz)的双簧管声音。在频谱中,我们可以清晰地看到许多正弦波,它们是声音的谐波。这里我们只绘制了前4000 Hz,即前几个谐波。

如何检测正弦波参数
一个在频谱中识别正弦波的简单方法是关注频谱幅度、其位置和高度。位置对应频率,高度对应正弦波的振幅。
因此,我们将正弦波视为幅度谱中的一个峰值。问题在于,幅度谱的分辨率是离散且有限的。我们能获得的最大频率分辨率是两个频率采样点(两个bin)之间距离的一半。
但我们可以做得更好。一种方法是使用零填充和频谱插值。通过零填充获得更大的FFT,从而得到更多采样点;我们也可以直接对结果采样点进行插值,以进一步细化频率和振幅的估计值。
频谱峰值检测与窗函数
为了检测频谱峰值,我们必须理解窗函数的影响。最重要的因素是窗长。
一个重要的概念是主瓣在频谱域中的带宽 B_F(以赫兹为单位)。它定义为窗函数主瓣宽度(以采样点为单位 B_S)乘以采样率 f_s,再除以窗长 M:
B_F = (B_S * f_s) / M

如果我们考虑两个想要分辨的频率 f_{k+1} 和 f_k,其绝对差值 Δf 就是我们想要分辨的频率间隔。为了使两个窗函数的主瓣在频谱中不重叠,从而将这两个频率视为独立的峰值,窗长 M 必须满足:
M ≥ (B_S * f_s) / Δf
在许多情况下,这个频率差对应于基频 f_0。对于谐波声音,两个连续谐波之间的距离等于基频。因此,如果我们想分辨基频,窗函数的主瓣带宽(以赫兹计)必须小于或等于这个基频。此时,窗长 M 需满足:
M ≥ (B_S * f_s) / f_0
如果用周期 P(以采样点为单位)表示,则为:
M ≥ B_S * P
示例分析


让我们看一个例子。假设使用汉明窗(B_S = 4),采样率为44100 Hz,我们想分辨440 Hz和490 Hz这两个频率(Δf = 50 Hz)。
计算所需的最小窗长:
M ≥ (4 * 44100) / 50 = 3528 个采样点

如果我们取3528个采样点,并计算其DFT(配合零填充以获得平滑频谱),可以清晰地看到两个独立的峰值,每个都对应一个汉明窗的变换。
对于双簧管声音(基频约440 Hz),如果我们取 M = 401 个采样点(约4个周期),并计算其加汉明窗后的DFT(零填充至N=1024),可以清晰地看到分离的谐波,每个谐波对应一个主瓣。

如果我们将窗长增加到 M = 801 个采样点,主瓣会变得更窄,我们甚至能在两个主瓣之间看到旁瓣,因为更大的窗长使我们能够分辨比基频更小的频率间隔。
总结

本节课我们一起学习了正弦模型的基本概念。这是一种将声音表示为多个时变正弦波之和的模型。我们探讨了如何在频域中表示这些正弦波,将其视为频谱中的峰值,并理解了窗函数(特别是窗长)对分辨这些峰值的关键影响。核心在于,为了分辨两个频率,窗长必须足够大,以确保窗函数主瓣的带宽小于待分辨的频率差。

在接下来的两节理论课中,我们将完成如何识别这些频谱峰值以及如何利用它们进行合成的讲解。
032:正弦模型(二)📈
概述

在本节课中,我们将继续学习正弦模型。上一节我们介绍了正弦模型的基本概念及其在频谱域中的可视化。本节中,我们将深入探讨如何在频谱中检测峰值(即正弦波),以及如何随时间跟踪这些变化的频率分量。

模型回顾与峰值检测
上一节我们介绍了正弦模型,它本质上是多个时变正弦波的和。我们强调了在频域中检测这些正弦波的重要性,因此需要选择合适的窗函数长度,以便在频谱中清晰地分离出各个正弦波对应的峰值。
例如,对于频率为440 Hz和490 Hz的两个正弦波,我们需要一个长度为5291个样本的窗,才能使它们的主瓣在频谱中分离开。

那么,我们如何测量频谱中这些正弦波的值呢?
峰值测量与局部最大值
如果窗长选择正确,信号中的每个正弦波都会在频谱中对应一个峰值。要测量每个正弦波的频率和幅度,我们只需找到峰值的顶点及其高度。
我们定义一个峰值为幅度谱中的一个局部最大值。具体来说,在位置 k0 处,如果其幅度值大于前一个位置 k0-1 和后一个位置 k0+1 的幅度值,那么 k0 就是一个局部最大值。

在下图中,蓝色叉号标记了这些局部最大值。

通过这个步骤,我们可以获得峰值的位置 k0、幅度 Mx[k0] 和相位 Px[k0]。
频谱分辨率问题与解决方案
然而,上述方法存在一个问题:频谱分辨率不足。从视觉上可以明显看出,峰值的位置可能不够精确。

解决分辨率问题的一个方法是进行零填充。我们使用一个远大于窗长的FFT尺寸。例如,在上例中,我们将FFT尺寸增加到原来的2倍(窗长保持不变,但在其后补零)。这会产生更平滑的频谱,此时计算出的局部最大值(蓝色叉号)位置更准确,看起来更接近峰值的中心,因此幅度和相位的测量也会更精确。
但如果想要足够好的分辨率,就需要大幅增加FFT尺寸,这在计算上会非常昂贵。那么,还有其他解决方案吗?

抛物线插值法
是的,我们可以使用一种更经济的插值方法,例如抛物线插值。抛物线函数的形状与分析窗频谱主瓣尖端的形状在分贝尺度上非常相似。
抛物线的方程如下:
y = a(x - p)^2 + b
其中 p 是抛物线的中心,a 控制曲率,b 是偏移量。
要对一个频谱峰值进行抛物线插值,我们可以使用该峰值幅度最高的三个点。例如,使用幅度谱在位置 k-1, k, k+1 的值,可以将其视为一个抛物线的三个最高点 (x-1, y-1), (x0, y0), (x1, y1)。

然后,我们可以应用抛物线方程来找到这个抛物线的中心。中心的频率位置 kp 可以通过以下公式计算:
kp = k + (y[k-1] - y[k+1]) / (2 * (y[k-1] - 2*y[k] + y[k+1]))

接着,我们可以将这个插值后的 kp 代入抛物线方程,得到修正后的幅度值 A(kp)。通过这种方式,我们可以细化峰值的位置。我们可以将抛物线插值与零填充结合使用,以获得更好的结果。
下图展示的就是结合了零填充和抛物线插值的例子。如果放大观察,你会发现它比单纯零填充或没有零填充的结果要好得多。


这样,我们就能相当准确地测量出频谱中所有正弦波的频率、幅度和相位:
- 频率:通过公式计算
kp,然后乘以(Fs / N)转换为赫兹。 - 幅度:使用抛物线插值公式计算。
- 相位:可以直接读取
kp附近位置的相位值,通常不需要插值。
时变正弦波的跟踪
以上讨论的都是单帧频谱中的值。但正弦模型是针对时变声音的,即频率和幅度随时间变化的正弦波。我们该如何处理呢?
我们需要处理频谱图,并在其中寻找随时间变化的频率轨迹。
我们将时变正弦波定义为频谱图中一条稳定的峰值轨迹。这种变化是受约束的,我们定义的是那些随时间演变、但变化不大的峰值轨迹。这种稳定性可以通过连续帧之间频率和幅度的变化来衡量。在实践中,我们主要关注频率和幅度的变化,但相位的时间-频率导数也是一个非常有价值的观察指标。

一个帧中的峰值属于某条轨迹的条件由以下公式定义:
一个在帧 l 中频率为 Fp(l) 的峰值,如果其与前一帧 l-1 中某轨迹频率 Ftrack(l-1) 的绝对差值小于一个阈值,那么它就可以成为该轨迹的一部分。
|Fp(l) - Ftrack(l-1)| < 阈值
同时,该轨迹必须已经存在一定的时间长度。如果轨迹过短,可能只是一个短暂的瞬态,而非真正的正弦分量。

下图是一个在Bendir(一种北非/土耳其等地使用的鼓)声音中跟踪正弦波的例子。这是一种非谐波声音,结构复杂,但包含可以跟踪的正弦分量(部分音)。图中的黑线就对应着跟踪到的频率轨迹。

我们还可以在相位谱中观察这些轨迹。如果我们绘制相位谱(或相位导数谱),并将频率轨迹(黑线)叠加在上面,会得到有趣的结果。可以看到,黑线所在的区域颜色更清晰,这些正是相位变化较小、相位连续性更强的位置。这表明相位信息对于跟踪正弦波也非常重要,因为它能帮助我们判断一个分量的稳定性。
总结与下节预告
本节课我们一起学习了正弦模型的核心部分:如何在频谱中精确检测峰值,以及如何在时间维度上跟踪这些峰值以构成时变正弦波的轨迹。这是一个建立在短时傅里叶变换之上的声音表示方法,它能减少信息量并滤除非共振的频谱成分。

然而,要有效地将其用于声音分析,比使用STFT需要更深入的理解,特别是关于频谱和窗函数的知识。希望你现在已经开始掌握这些概念。
要完成对正弦模型的讲解,我们还需要描述其合成部分。这将是下一节课的内容。
期待下次再见!👋


033:正弦模型3
在本节课中,我们将完成正弦模型的学习,重点介绍其合成部分。我们将回顾模型的核心概念,探讨如何使用加法合成生成声音,并最终构建一个完整的分析-合成系统。
上一节我们介绍了正弦模型的分析部分。本节中,我们来看看如何利用分析结果来合成声音。

模型回顾与频谱轨迹
正弦模型认为声音是多个时变正弦波的总和,其表达式如下:
公式: x(t) = Σ A_k(t) * cos(2π * f_k(t) * t + φ_k(t))

其中,x(t) 是输出信号,A_k(t)、f_k(t) 和 φ_k(t) 分别是第 k 个正弦波的时变幅度、频率和相位。
展示分析结果的一个好方法,是在声音的幅度谱图上叠加绘制这些时变正弦波的频率轨迹。下图展示了一个长笛声音的谱图及其上方的正弦波频率轨迹线。这些轨迹线直观地呈现了正弦模型对声音的分解。

正弦波合成(加法合成)
从分析得到的参数值出发,我们想要重新合成声音。合成正弦波的标准方法是使用加法合成。
以下是加法合成器的标准框图:它包含一系列振荡器。每个振荡器根据输入的幅度和频率参数,生成一个时变正弦波,然后将所有这些正弦波相加,得到输出信号。
那么,如何生成每个独立的正弦波呢?
时域直接合成
最直接的方法是直接使用正弦函数。例如,在Python中可以这样实现:
代码:
import numpy as npdef generate_sinusoid(amplitude, frequency, phase, duration, sample_rate):t = np.arange(0, duration, 1/sample_rate)signal = amplitude * np.cos(2 * np.pi * frequency * t + phase)return signal
然而,对于复杂信号,可能需要同时合成数百个正弦波,且每个样本点都需要计算,这种方法计算开销较大。
频域合成(利用DFT)
我们可以利用离散傅里叶变换在频域进行更高效的合成。基本思想是:从正弦波的幅度谱和相位谱出发,对其进行逆DFT来得到时域信号。
公式: x[n] = IDFT{ X[k] },其中 X[k] 是正弦波在频域的表示。
但这仅适用于频率恰好是DFT离散频率点的情况。对于任意频率的正弦波,其频谱并非单根谱线,而是取决于所使用的窗函数。
为了在频域生成一个任意频率的正弦波,我们需要生成一个窗函数的频谱,并将其移动到目标频率,同时乘以正确的幅度和相位。
公式: X[k] = A * W[k - k0] * e^(jφ),其中 W[k] 是窗函数的频谱,k0 对应目标频率,A 和 φ 是幅度和相位。
下图展示了使用两种不同窗函数(汉宁窗和布莱克曼-哈里斯窗)时,同一正弦波的频谱及其逆DFT结果。可以看到,时域信号都受到了窗函数的影响。

频域合成的一个巨大优势是,并非所有频谱样本都具有相同的重要性。我们可以利用这一点来提高效率。
利用主瓣进行高效合成
窗函数频谱的能量主要集中在主瓣。对于布莱克曼-哈里斯窗,其主瓣仅包含约8个样本。如果只取这8个主瓣样本进行逆DFT,我们就能高效地生成一个正弦波。
实验表明,使用布莱克曼-哈里斯窗主瓣合成信号的信噪比非常高(约102 dB),对于音频应用来说失真可以忽略不计。因此,布莱克曼-哈里斯窗是频域正弦波合成的良好选择。
要合成多个正弦波,只需在频域叠加它们各自的主瓣即可。
公式: X_total[k] = Σ A_i * W_BH[k - k_i] * e^(jφ_i)
下图示例展示了在频域生成三个不同频率和幅度的正弦波(分别位于1000 Hz, 4000 Hz, 8000 Hz),将它们的主瓣频谱叠加后进行逆DFT,得到了这三个正弦波相加的时域信号(当然,也乘上了布莱克曼-哈里斯窗)。
完整的分析-合成系统
现在,我们可以将上述思想整合成一个完整的正弦模型分析-合成系统。
以下是构建该系统的步骤概述:
-
分析阶段:
- 输入原始信号
x[n]。 - 乘以分析窗,计算短时傅里叶变换,得到幅度谱和相位谱。
- 在幅度谱中检测峰值,获取其频率、幅度和相位。
- 根据频率连续性等约束,在时间上跟踪这些峰值,形成正弦轨迹。
- 输入原始信号
-
合成阶段:
- 根据每条正弦轨迹的参数(频率、幅度、相位),在频域生成对应的布莱克曼-哈里斯窗主瓣频谱。
- 将所有主瓣频谱相加,得到合成信号的频域表示。
- 计算逆傅里叶变换,得到加窗的时域信号。
- 进行“去窗”操作:除以布莱克曼-哈里斯窗函数,再乘以一个三角窗函数。此操作允许我们以25%的重叠率进行叠加,从而重建完整的时域信号。
这个系统能够较好地复现原始声音中由正弦波构成的部分。让我们听一个邦戈鼓声音的分析-合成示例。
(原始邦戈鼓声)
(合成后的邦戈鼓声)
仔细聆听会发现,尤其在瞬态部分,合成音与原始音存在一些差异。这是我们后续课程中将尝试解决的问题。

总结与展望
本节课中,我们一起学习了正弦模型的合成部分。我们探讨了加法合成的原理,比较了时域和频域合成方法的优劣,并重点介绍了利用布莱克曼-哈里斯窗主瓣进行高效频域合成的技术。最后,我们构建了一个完整的正弦模型分析-合成系统。

通过这三讲的理论学习,我们从短时傅里叶变换出发,掌握了如何在谱图上分析正弦波,并利用这种简化的频谱表示(正弦模型)进行声音的重新合成。这种模型不仅可用于音频压缩,其本身也是一种对许多应用非常有用的高级声音表示方法。

在接下来的课程中,我们将利用声音的更多特性,在模型中施加更多约束,以期获得对各类应用更有价值的分析-合成结果。

参考资料:关于加法合成,可自行查阅维基百科。朱利亚的在线书籍中也包含相关材料。


034:正弦模型实践指南 🎵

在本节课中,我们将从实践角度学习正弦模型。上一节我们介绍了正弦模型的理论基础,本节中我们将使用SMS工具,通过实际操作来理解如何应用正弦模型分析音频信号。
概述
正弦模型是一种将音频信号分解为一系列正弦波(即正弦分量)的方法。每个正弦分量由三个核心参数描述:频率、幅度和相位。在分析时,模型会从信号的频谱中识别出主要的峰值,并将它们作为正弦分量进行追踪和建模。
从DFT模型开始
首先,我们从一个简单的440Hz正弦波开始分析。使用DFT(离散傅里叶变换)模型,我们可以观察其频谱。
以下是分析步骤:
- 打开一个440Hz的正弦波音频。
- 使用汉宁窗(Hann window)。
- 设置窗口大小为511,FFT大小为1024。
- 计算并观察其幅度谱、相位谱以及从频谱重建的信号。
在频谱中,我们可以看到代表该正弦波的单一峰值。正弦模型的目标正是自动找到这个峰值的频率、幅度和相位。
应用正弦模型分析单音
现在,我们切换到正弦模型来分析同一个440Hz正弦波。
以下是正弦模型的关键参数设置:
- 幅度阈值:设定为-50 dB。模型将只寻找幅度在最大能量50分贝以内的峰值。
- 最大正弦波数量:设为150(对于单音绰绰有余)。
- 频率追踪偏差:设置最低频率偏差为10 Hz,并允许偏差随频率升高而增加。
计算后,我们可能会发现分析结果中出现了不止一条频率线。这是因为频谱的旁瓣也被当作峰值捕捉了进来。为了只捕捉主峰,我们需要将幅度阈值调整得更严格,例如设为-20 dB。调整后,分析结果将只显示一条清晰的440Hz频率线。
分析更复杂的信号:双正弦波
为了理解参数如何影响复杂信号的分析,我们接下来分析一个由440Hz和490Hz正弦波叠加而成的信号。

在理论课中我们学过,为了在频谱上区分两个频率相近的正弦波,需要选择合适的窗口大小。计算公式为:
窗口大小M ≥ (窗函数主瓣宽度系数 × 采样频率) / 欲分辨的频率差
对于汉宁窗(主瓣宽度系数为4),采样率44100 Hz,频率差50 Hz,计算得到的最小窗口大小约为3528。我们将其设为奇数值3529,并设置足够大的FFT尺寸(如4096)。将幅度阈值设为-30 dB后进行分析,可以清晰地看到两条分别代表440Hz和490Hz的稳定频率轨迹。
如果窗口大小设置得过小(例如1800),FFT尺寸也不足(2048),即使合成出的声音听起来尚可,分析得到的频率轨迹也会变得混乱和不稳定,无法清晰区分两个正弦分量。这说明了正确设置分析参数对于准确建模至关重要。
分析真实乐器声音
最后,我们使用正弦模型分析一个真实的乐器声音——一个440Hz(A4)的双簧管音符。
对于具有丰富谐波的乐音,我们需要调整参数:
- 窗口类型与大小:使用主瓣更宽的布莱克曼窗(主瓣宽度系数为6)。根据公式计算,分辨基频440Hz所需的窗口大小约为601。
- 幅度阈值:由于谐波能量衰减,需要设置更低的阈值(如-80 dB)以捕捉到较弱的高次谐波。
- 最小轨迹长度:可以设置为0.05秒,以过滤掉短暂的杂散轨迹。
- 频率追踪偏差:需要设置合适的偏差值,以在时间上正确追踪各谐波频率的变化。
使用这些参数进行分析,我们可以在频谱图中看到一系列水平的谐波轨迹。合成的音频与原始声音非常接近。
如果我们将幅度阈值设置得过高(如-30 dB),模型将无法捕捉到能量较弱的谐波,导致合成声音缺失高频成分,听起来与原始声音有显著差异。

总结

本节课中,我们一起通过SMS工具实践了正弦模型的应用。我们从简单的单音开始,逐步分析了双音和复杂的真实乐器声音,演示了窗口大小、幅度阈值、频率追踪偏差等关键参数对分析结果的影响。
正弦模型的核心在于从信号频谱中提取并追踪正弦分量。掌握这些参数的设置,对于准确分析不同特性的声音至关重要。建议你尝试分析更多声音,亲身体验不同参数带来的效果,这将为后续学习更复杂的音频处理技术打下坚实基础。



我们下节实践课将分析更复杂的声音,并深入探索正弦模型的更多细节。下次见!
035:分析声音 🎵

在本节课中,我们将学习如何使用正弦波模型分析一个更复杂的真实声音——印度打击乐器马杜鼓的声音。我们将通过实践操作,探索如何选择合适的分析参数,以获得良好的声音可视化和再合成效果。

上一节我们从实践角度讨论了正弦波模型。本节中,我们将通过分析一个更复杂的声音,来进一步理解如何调整模型参数。
声音样本介绍
我们将分析马杜鼓的声音。马杜鼓是一种来自印度南部的打击乐器,是卡纳提克音乐传统的一部分。我们使用的样本由音乐家A.J.演奏并上传。
以下是分析步骤:
步骤一:获取并试听声音
首先,我们需要获取马杜鼓的声音样本。我们可以在FreeSound.org等网站找到相关音频。我们选择了一个由A.J.上传的片段作为分析对象。

试听声音有助于我们了解其特性。马杜鼓能产生多种不同音质的声音,其演奏涉及复杂的节奏结构。
步骤二:初始短时傅里叶变换分析
我们使用SMS Tools工具开始分析。首先进行短时傅里叶变换,以获得声音的时频表示。
初始参数设置如下:
- 窗口类型:汉明窗(默认选择)
- 窗口大小:1000个采样点
- FFT大小:4096
- 跳跃大小:较小的值,以获得良好的时间分辨率
计算初始STFT后,我们观察频谱图。可以看到声音中存在一些稳定的共振峰(部分音)。通过测量两个连续共振峰之间的最小距离(例如,第一个在200Hz附近,第二个在400Hz附近),我们估算出所需的最小频率分辨率约为200Hz。
步骤三:计算合适的窗口大小
根据理论,我们可以根据所需的频率分辨率来估算更合适的窗口大小。公式如下:
窗口大小 = (窗口主瓣宽度 × 采样率) / 所需频率分辨率
对于汉明窗(主瓣宽度约为4),采样率44100Hz,所需分辨率200Hz:
窗口大小 ≈ (4 × 44100) / 200 = 882个采样点
因此,我们将窗口大小调整为约883个采样点,并相应减小跳跃大小以保持时间分辨率。
步骤四:应用正弦波模型分析
接下来,我们使用正弦波模型进行更精细的分析。在SMS Tools中打开正弦模型分析界面。
初始参数设置尝试:
- 窗口大小:883
- 幅度阈值:-60 dB
- 最短轨迹持续时间:0.05秒
- 最大正弦波数量:150
首次分析结果不理想,只检测到很少的正弦波,合成声音缺失严重。我们进行了以下调整:
- 降低幅度阈值:因为原始录音音量不大,许多成分能量较低。
- 缩短最短轨迹持续时间:因为鼓的击打声很短,需要捕捉短暂的轨迹。
调整后,分析结果和合成质量显著改善。
步骤五:权衡时间与频率分辨率
我们尝试增加窗口大小至3001个采样点,以获得更好的频率分辨率(能分辨更靠近的部分音)。虽然得到了更多的频谱线,但时间分辨率下降,导致合成声音的瞬态(如击打瞬间)变得模糊、不清晰。
因此,我们最终将窗口大小回调至1000个采样点,在频率分辨率和时间分辨率之间取得了较好的平衡。最终合成的声音质量不错,尽管在声音的瞬态部分,一些噪声能量可能未被正弦波完全捕捉,这将在后续课程中讨论。

本节课中,我们一起学习了如何使用正弦波模型分析马杜鼓的声音。我们实践了从STFT初步观察、计算合适窗口大小,到调整正弦模型参数(如幅度阈值、轨迹长度)的完整流程,并深入体会了音频分析中时间分辨率与频率分辨率之间的核心权衡。希望这次分析能帮助你更好地理解正弦波模型,同时也让你认识了马杜鼓这一迷人的乐器。
036:峰值检测 📊
在本节课中,我们将学习如何从音频信号的频谱中检测并测量正弦波的参数。我们将重点关注峰值检测技术,并使用抛物线插值方法来精确计算正弦波的频率、振幅和相位。
上一节我们介绍了正弦波模型及其在频域中的表示。本节中,我们来看看如何通过编程实现从频谱中自动检测峰值并测量其参数。
概述
正弦波在频谱中表现为一个尖峰。我们的目标是编写代码来定位这个峰值,并使用特定的数学方法精确测量出该正弦波的频率、振幅和相位。核心方法是抛物线插值。

以下是用于测量的核心公式:
-
精炼频率值(在0到N的索引范围内):
p = k + 0.5 * (X[k-1] - X[k+1]) / (X[k-1] - 2*X[k] + X[k+1])
其中,k是峰值点的索引,X是幅度谱。 -
转换为赫兹:
freqInHz = p * (samplingRate / N) -
精炼振幅值:
amplitude = X[k] - 0.25 * (X[k-1] - X[k+1]) * p -
精炼相位值:
通过在相位谱上进行线性插值获得。
代码实现步骤
以下是实现峰值检测与测量的主要步骤。
1. 导入必要库
首先,我们需要导入NumPy、Matplotlib、SciPy以及课程提供的工具函数。
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import get_window
import sys, os
sys.path.append(‘../software/models/‘)
from dftModel import dftAnal
import utilFunctions as UF
2. 读取音频文件并设置参数
我们读取一个包含440Hz正弦波的音频文件,并设置分析参数,如窗函数、窗长、FFT大小和检测阈值。
fs, x = UF.wavread(‘../sounds/sine-440.wav‘)
M = 501 # 窗长
N = 512 # FFT大小
t = -80 # 峰值检测阈值(分贝)
w = get_window(‘hamming‘, M) # 汉明窗
start = int(.8*fs) # 从音频中部开始分析
x1 = x[start:start+M]
3. 计算频谱
使用DFT计算选定音频片段的幅度谱和相位谱。
mX, pX = dftAnal(x1, w, N)
4. 峰值检测
调用工具函数中的peakDetection来定位幅度谱中所有高于阈值t的局部极大值点。
ploc = UF.peakDetection(mX, t)
pmag = mX[ploc] # 获取峰值点的幅度值
peakDetection函数的工作原理如下:
- 它遍历幅度谱(忽略首尾两点)。
- 检查每个点是否满足三个条件:高于阈值、高于前一个点、高于后一个点。
- 所有满足条件的点即被识别为峰值,并返回其索引位置。
5. 抛物线插值精炼参数
为了获得比FFT分辨率更精确的参数值,我们对检测到的每个峰值进行抛物线插值。
iploc, ipmag, ipphase = UF.peakInterp(mX, pX, ploc)
peakInterp函数执行以下操作:
- 对于每个峰值点
k,取其自身及左右相邻点(k-1, k, k+1)的幅度值。 - 使用抛物线插值公式计算精炼后的频率索引
p。 - 计算抛物线顶点的精炼幅度值。
- 在相位谱上对位置
p进行线性插值,得到精炼相位值。
6. 结果可视化
最后,我们将原始频谱和检测到的峰值(包括插值前后的位置)绘制出来进行对比。
freqaxis = fs * np.arange(N/2+1) / float(N)
plt.plot(freqaxis, mX)
plt.plot(fs * iploc / N, ipmag, marker=‘x‘, linestyle=‘‘, color=‘r‘)
plt.show()
实验与讨论
我们首先使用较小的FFT大小(N=512)进行分析。此时频率分辨率较低(约86Hz),检测到的峰值频率(约429Hz)与真实值(440Hz)偏差较大。
通过增大FFT大小(N=2048),分辨率提高到约21Hz,峰值频率读数改善到约430Hz,但仍不精确。
应用抛物线插值后,精炼的频率值达到了约439Hz,非常接近真实的440Hz。这证明了插值方法对于克服FFT固有分辨率限制的有效性。

对于更复杂的音频(包含多个正弦波),只需降低检测阈值,该代码即可检测并测量出频谱中的所有显著峰值。
总结

本节课中我们一起学习了音频信号处理中峰值检测的核心技术。我们实现了从频谱中自动定位正弦波峰值,并利用抛物线插值方法精确测量其频率、振幅和相位参数。这为后续分析真实音频中多个正弦分量,以及进行正弦模型的分析与合成打下了坚实基础。下一节编程课,我们将把这些技术应用于更复杂的真实声音。
037:正弦波合成 🎵
在本节课中,我们将学习如何根据频谱分析得到的峰值信息,在频域中合成正弦波,从而实现基于正弦模型的加法合成。
上一节编程课中,我们讨论了频谱峰值以及如何编程检测它们的值。这是实现基于正弦模型的分析-合成系统的开端。我们识别出的频谱峰值,理想情况下对应着声音中的正弦波成分。在本节课程中,我们将介绍如何根据这些峰值信息来合成正弦波,也就是如何进行加法合成。
我们将按照理论课中讨论的方法,在频域中进行合成。其核心思想是合成布莱克曼-哈里斯窗函数的主瓣,然后对其进行逆离散傅里叶变换。

以下公式展示了这一思想:我们通过对一系列主瓣之和进行逆DFT来合成声音 y。
y = IDFT( Σ 主瓣 )
在图中,我们可以看到已经生成的主瓣及其相位,然后通过逆变换生成合成的声音。接下来,我们将展示具体实现。首先,我们需要学习如何合成布莱克曼-哈里斯窗的一个主瓣。
以下代码片段展示了这一点。我们调用一个名为 genBhLobe 的函数,并传入我们想要生成的样本(频点)。由于我们只想生成主瓣,我们只传入其中心附近的8个频点。
# 示例:生成布莱克曼-哈里斯窗主瓣的8个中心频点
samples = [0, 1, 2, 3, 4, 5, 6, 7]
X = genBhLobe(samples)
首先,让我们看看这个来自 utilFunctions 文件的 genBhLobe 函数。这个函数直接在频域中实现了布莱克曼-哈里斯窗的生成。布莱克曼-哈里斯窗的频谱是四个sinc函数之和,这正是主循环所做的事情:它生成四个sinc函数并将它们加在一起。每个sinc函数都有来自布莱克曼-哈里斯窗方程的系数。
在这个sinc函数内部,我们调用另一个名为 sinc 的函数,它负责生成一个特定的sinc函数。同样,它只生成主瓣,而不是整个sinc函数。
通过这两个函数,我们能够生成布莱克曼-哈里斯窗的主瓣。运行这段代码,输出 X 是线性尺度下布莱克曼-哈里斯窗的8个样本值。中心是第五个样本,其值为1。如果我们取其绝对值,并计算 20*log10(),我们将在对数尺度下看到它。绘制出来,这就是布莱克曼-哈里斯窗主瓣在对数尺度下的形状。
如果我们读取的不是这些样本,而是其他序列的样本(例如,给数组中的每个值加上0.5),我们再次运行并绘图,会看到不同的样本。这基本上是在两个样本之间移动主瓣。这就是我们如何生成对应于不同频率的主瓣,因为我们正在这些样本之间移动这个主瓣。

现在,让我们使用SMS工具包中的一个函数来实现,这个函数叫做 genSpecSines。genSpecSines 接收一个包含频率、幅度和相位的输入数组,然后生成包含所有这些信息的频谱。
以下是使用该函数的代码示例:
# 生成一个4000Hz、0dB、相位0的正弦波频谱
freqs = [4000.0]
mags = [0.0]
phases = [0.0]
N = 512
X = genSpecSines(freqs, mags, phases, N)
在这段代码中,我生成了一个频率为4000赫兹、幅度为0分贝、相位为0的单一正弦波。我生成了大小为512个样本的整个FFT频谱,然后计算其绝对值并绘制其分贝值。运行后,我们得到一个频率在4000赫兹的正弦波主瓣。如果我们放大,会看到主瓣,并且其中心恰好被移动到了4000赫兹附近。进一步放大,会看到这个峰值的尖端基本上就在4000赫兹。
接下来,我们讨论如何从频域信号生成时域信号。我们需要对其进行逆傅里叶变换。在另一个脚本中,我实现了这一点。在调用 genSpecSines 之后,我调用逆FFT。然后,有一段代码用于“撤销”布莱克曼-哈里斯窗的加窗效应。在理论课中我们提到,为了获得良好的重叠效果且不需要太小的跳跃大小,最好撤销布莱克曼-哈里斯窗并应用一个三角窗。这部分代码生成了一个合成窗 sw,它是三角窗除以布莱克曼-哈里斯窗的结果。
sw = 三角窗 / 布莱克曼-哈里斯窗

然后,我们将这个合成窗乘以从逆DFT得到的结果。运行这段代码,我们可以绘制各个步骤。首先绘制频谱的绝对值,这是正弦波的复频谱(仅幅度部分),我们看到正弦波的正负两侧。x轴不是以赫兹为单位,但这将对应于4000频率。然后我们计算逆FFT并绘制 y,这是时域中经过布莱克曼-哈里斯窗加窗的正弦波,它围绕0点中心对称。之后,我们除以布莱克曼-哈里斯窗并乘以三角窗,得到结果 yw。这个信号将用于重叠相加合成步骤。原始窗口大小为512,现在变为256个样本,因此我们可以重叠128个样本。
我们已经看到了根据给定正弦波的频率值进行完整合成的过程。现在,让我们将其整合到一个真实声音片段的分析-合成中。
在 test5 中,我们读取一个双簧管声音的片段。我们将进行DFT分析,检测峰值,然后对峰值进行插值以获得更精确的正弦波位置,接着在频域中进行合成,再进行逆FFT并撤销加窗。这里会绘制得到的幅度频谱及其峰值。
运行此测试,我们得到双簧管声音的幅度频谱,并根据给定的阈值检测到一些峰值。放大后,我们看到十字标记对应于插值后的峰值。这里抛物线插值的效果非常显著,因为我们没有进行任何零填充,且FFT尺寸相当小。从这些十字标记中,我们得到每个峰值的幅度、频率和相位,然后可以计算其逆变换。我们可以绘制 y 数组,它包含了所有这些正弦波的零中心逆FFT结果。现在,我们将其中心化,撤销布莱克曼窗并乘以三角窗,可以绘制 yw,即经过三角窗加窗的信号。这个信号将用于双簧管声音分析-合成的重叠相加步骤。

本节课中,我们一起学习了正弦模型分析的另一个组成部分。我们完成了基于峰值的合成,之前我们讨论了如何实际找到峰值。目前我们还缺少的是将其整合到一个完整的、时变的分析-合成系统中,并处理这些正弦波轨迹的连续性问题。这将是下一节编程课的内容。
038:正弦模型编程实现
在本节课中,我们将学习如何将正弦模型的分析与合成部分整合成一个完整的系统。我们将使用SMS工具包中的代码,具体实现从音频信号中提取正弦轨迹并进行重新合成的过程。

概述
上一节我们讨论了正弦模型的峰值检测与合成。本节中,我们将把这两个部分结合起来,并加入缺失的组件——正弦轨迹跟踪,以实现对一个完整声音的分析与合成。我们将从原始信号 x 开始,经过标准的加窗、FFT处理,然后进行峰值检测以获取振幅、频率和相位。接着,我们将学习如何将这些峰值连接成随时间变化的轨迹,并最终合成出声音。
正弦模型分析函数
我们将使用SMS工具包中的 sineModelAnal 函数来完成核心分析工作。这个函数接收整个输入声音 x、采样率、窗函数、FFT大小、帧移等参数,并执行峰值检测与轨迹跟踪。
以下是该函数的关键参数:
maxnSines:允许同时存在的最大正弦波数量,默认值为100。minSineDur:轨迹的最小持续时间(秒),短于此值的轨迹将被删除,默认值为0.01。freqDevOffset:允许的帧间频率偏差基准值(赫兹),默认值为20。freqDevSlope:频率偏差随频率增加的斜率,允许高频有更大的变化。
函数的核心是一个循环,它对声音的每一帧执行以下操作:
- 使用FFT算法计算DFT。
- 进行峰值检测。
- 进行峰值插值。
- 将峰值分配到轨迹中。
正弦轨迹跟踪
轨迹跟踪是连接不同帧中峰值的关键步骤。sineTracking 函数负责此任务。
其核心逻辑如下:
- 对于已有的轨迹,算法会在新的一帧中寻找频率最接近的峰值。
- 如果该峰值的频率与轨迹预测频率的偏差在允许范围内(由
freqDevOffset和freqDevSlope控制),则该峰值将被加入此轨迹。 - 未被分配到任何现有轨迹的峰值,可能会作为新轨迹的起点。
- 如果一个轨迹在后续帧中没有找到匹配的峰值,它将会消失。
轨迹清理
分析完成后,系统会调用 cleanSineTracks 函数进行轨迹清理。
这个函数非常简单:
- 它遍历所有已生成的轨迹。
- 检查每条轨迹的持续时间。
- 如果轨迹长度小于
minSineDur参数设定的值,则将该轨迹的频率设置为零,从而将其删除。 - 这有助于消除由分析侧瓣或瞬时噪声产生的短促、不稳定的轨迹片段。
实践:运行分析脚本
我们可以运行一个测试脚本(例如 test6.py)来调用 sineModelAnal 函数并可视化结果。
运行脚本后,我们将看到以不同颜色绘制的正弦轨迹图。需要注意以下几点:
- 即使看起来是一条连续的线,也可能由不同颜色的线段组成,这代表旧的轨迹结束和新的轨迹开始。
- 大多数轨迹对应声音的谐波。
- 高频区域可能出现一些短暂的轨迹,它们可能对应噪声成分或分析产生的侧瓣。
通过调整 minSineDur 参数(例如从 0.1 改为 0.01),我们可以观察到更多、更短的轨迹被保留下来,其中可能包含更多我们不需要的伪影。
正弦模型合成
分析得到轨迹后,我们可以使用 sineModelSynth 函数进行合成。

该函数的工作流程是:
- 接收分析阶段得到的轨迹数据。
- 遍历所有轨迹。
- 为每个正弦分量生成主瓣(使用布莱克曼-哈里斯窗等)。
- 处理相位信息(可以使用分析得到的相位,也可以在合成时重新生成)。
- 最终通过逆FFT和重叠相加重建出时域音频信号。

SMS工具包中提供了一个集成的 sineModel 函数,它封装了完整的分析与合成流程,并可以绘制原始声音、正弦轨迹和合成声音的对比图。

运行此函数后,我们可以聆听合成出的声音(例如 bendir_sineModel.wav),并将其与原始声音进行比较,以评估模型的重建质量。
总结

本节课中,我们一起学习了正弦模型完整的编程实现流程。我们了解了如何使用 sineModelAnal 函数进行包含轨迹跟踪的分析,如何使用 cleanSineTracks 清理轨迹,以及如何使用 sineModelSynth 进行重新合成。通过SMS工具包提供的代码基础,我们已经可以探索许多有趣的音频处理应用。正弦模型是一个强大的工具,但它还可以被进一步扩展和约束。在下周的课程中,我们将讨论谐波模型,它将正弦模型限制为只跟踪谐波成分,这对于处理乐音非常有效,我们还将探索其他新的谱分析与声音合成方法。
039:谐波模型 🎵
概述
在本节课中,我们将学习谐波模型。这是一种比上周学习的正弦模型更灵活、更高层次的音频表示方法,专门用于处理由周期性或准周期性振荡产生的单一声源声音,例如许多乐器发出的声音。
上一节我们介绍了正弦模型,本节中我们来看看它的一个特例——谐波模型。
谐波模型介绍
谐波模型的方程与正弦模型非常接近,但有一个显著区别:所有正弦波的频率都是基频的整数倍。因此,该模型仅适用于由周期性或准周期性振荡产生的单一声源声音。
输出信号 y_h[n] 的公式如下:
y_h[n] = Σ_{r=1}^{R} A_r[n] cos(2π r f_0[n] n + φ_r[n])
其中,r 是整数,f_0[n] 是时变的基频。这意味着所有正弦波要么是基频,要么是基频的整数倍。
我们也可以在频域表达该模型。一个谐波声音的时变频谱是所有谐波时变频谱的总和。总体谐波频谱是所有这些窗函数的总和,每个窗函数都有一个振幅缩放因子 A_r 和一个频移因子 -r f_0,我们将这些窗函数放置在正弦波的适当位置。
这基本上就是我们在正弦模型中讨论的内容,但现在将频率限制为基频的整数倍。
谐波与非谐波声音示例
以下是两个具有不同特征的声音的频谱图示例。顶部的声音是一个非谐波声音(颤音琴音符),底部的声音是一个谐波声音(人声演唱元音“a”)。
以下是两个声音的听觉示例:
- 颤音琴声音:这是一个非常简单的音,但其谱线是非谐波的,这意味着它们不是基频的整数倍。
- 人声:这是一个更复杂的声音,其正弦波谱线非常接近基频的整数倍,这是谐波声音的典型结构。
正弦波、分音与谐波的区别
为了更好地理解谐波的概念,区分正弦波、分音和谐波非常重要。
- 正弦波:是一个数学函数,具有解析表示,源于数学观点。
- 分音:是信号的一个组成部分,它是周期性且稳定的,可以建模为一个缓慢时变的正弦波。这个概念来自对真实信号的分析。
- 谐波:是声音的一个分音,且是基频的整数倍。它也可以建模为缓慢时变的正弦波,但附加了必须是给定频率整数倍的限制。
观察相同信号的相位谱也有助于理解这些概念。相位平坦的区域通常对应稳定的正弦波分量。
单音与复音信号
为了使用谐波模型,我们需要识别出谐波的声源。
在复音信号的情况下,存在多个声源。例如,可能同时有几种乐器在演奏,其中一些可能是谐波的,一些可能不是。目标可能是识别出其中人声的谐波,但通过观察正弦波轨迹可能并不容易。
单音信号只包含一个声源,例如独唱的人声。在这种情况下,可以更清楚地看到时变的谐波,并且识别出的正弦波轨迹可以非常清晰地显示人声的谐波。
如何检测谐波?
问题在于,如何在单音信号或复音信号的某个声源中识别谐波?我们只关注谐波声音。
主要问题在于识别声源的基频,这将在下一讲中讨论。因此,这里我们假设已知声音的基频。
我们将专注于谐波的概念,并将其定义为一个频率接近基频整数倍的频谱峰值。
我们可以用以下公式来表达这个想法:一个峰值 f_p 被认为是谐波的条件是:
|f_p - r * f_0| < 阈值
同时,我们定义“稳定”的概念为已经存在了一段时间,即该谐波需要在当前帧之前的若干帧中持续存在。我们定义一个大写的 L 作为最小帧数,一个谐波需要存在这么多帧才能被定义为谐波。
谐波模型的实现
谐波模型的实现是对上周看到的正弦模型的一个修改。
以下是实现流程:
- 基频检测算法:这需要单独处理,我们将在下一讲讨论。
- 谐波检测:即我们讨论过的选择那些是基频谐波的峰值。
- 其余部分:与正弦模型完全相同。

分析-合成示例
这是一个使用谐波模型进行分析-合成的示例。对于之前听过的人声,其合成声音与原始声音非常接近。因此,对于这个声音,谐波模型效果很好。当然,在谐波不清晰的区域(如过渡段、辅音或静默处)可能会有些问题。

延伸阅读与资源
谐波振荡的概念在研究许多自然现象时经常出现,你可以在许多书籍和参考资料中找到。当然,在处理特定的音乐信号和声音的谐波性方面资料可能不多,但你仍然可以在维基百科和一些书籍中找到相关内容。
本节课中播放的声音来自 FreeSound 网站。本节展示的所有绘图代码都可以在 GitHub 代码库中找到。

总结
本节课中我们一起学习了谐波模型。我们可以用它来分析和合成信号。其应用可能性将比正弦模型大得多,我们将在后续看到一些例子。困难的一步在于基频的检测,这将是我们下一讲的内容。
040:基频检测 🎵
在本节课中,我们将学习如何检测音频信号中的基频。基频是谐波模型工作的关键,它决定了声音的音高。我们将探讨时域和频域两种主要的检测方法,并了解它们各自的优缺点。

上一讲我们介绍了谐波模型,并提到为了使该模型正常工作,我们需要能够检测声音的基频,即我们通常所说的音高。我们将交替使用“基频”和“音高”这两个术语,但严格来说,它们指的是不同的概念。F0是一个信号处理概念,而P是一个感知概念。对于本课程而言,F0是更合适的术语。

检测方法概述

人们提出了许多方法来识别声音的基频,这些方法可以分为两类:直接在时域信号上工作的方法和在频域频谱表示上工作的方法。
时域方法在单音信号上效果良好。
频域方法不仅可以在单音信号上工作,也可以在复音信号上工作,这是这类方法的一个重要优势。


理解基频检测
为了理解基频检测的概念,让我们观察一些声音及其频谱。
这是一个双簧管声音的片段。时域信号清楚地显示出周期性,我们可以识别出一个不断重复的周期。这个周期的长度,其倒数就是我们所说的基频。在频域,在幅度谱中,我们也看到了周期性,基本上两个连续峰值之间的距离就是基频。因此,我们也可以设想能够测量这一点的算法。我们可以在时域或频域进行测量,这可能并不太难。

相位在某些情况下可能有用,但我们暂时不深入讨论。


时域检测方法 🎹
一个钢琴单音具有清晰的音高,因此应该能够检测到它的F0。让我们听一个钢琴乐句。我们清楚地听到了这个声音的音高,但如果我们观察时域波形,情况就不那么明显了,似乎不容易识别这个声音的周期。在频谱中,情况稍好一些。我们看到一些具有周期性的峰值,因此我们可以设想一些能够利用这一点的算法。

如果我们处理复音信号,例如,这是一段声乐作品的片段。其中有多个声源,但人声是最突出的。在时域检测人声的基频基本上是不可能的。在频谱中,这也不容易,但我们会看到有一些算法试图在频域识别这个突出的人声,即这个谐波成分,并且它们做得相当不错。

自相关函数
要在时域检测基频,我们基本上需要识别其重复周期循环的长度。自相关函数是一种用于寻找重复模式的数学工具,它是信号与其自身的互相关。非正式地说,我们可以将其视为样本之间的相似性,作为它们之间时间滞后的函数。

在这个方程中,我们看到了一个带有某种加窗的自相关函数版本。我们为每个滞后时间L计算这个函数,尝试不同的滞后时间,其中L是整数值的样本。我们从一个特定的时间段(声音的一个片段)开始,将声音乘以延迟了该滞后时间的声音。当然,如果延迟为零,就是同一个信号。当我们取不同的滞后时间时,乘法结果会不同。
因此,我们将得到一个关于L的函数。这样,我们将测量声音片段与延迟了某个特定L的样本之间的相关性。让我们看一个具体的例子。

这是之前的双簧管声音,下方是自相关函数。我们清楚地看到,在滞后0处,值为1(完全相关)。然后随着滞后增加(这里我们用秒而不是样本来表示滞后时间,以便更容易与顶部信号对应),我们清楚地看到,在对应于一个周期(即0.002秒)的滞后处,存在一个局部最大值。

这显然是最大的局部最大值,因此这将是一个很好的指示,表明这就是周期,或者其倒数就是基频。然后,两个周期的滞后处也是一个局部最大值,但更小。由于存在加窗,这个值会随着滞后时间衰减。
可以说,对于如此清晰的周期性声音,自相关函数是测量周期(从而测量基频)的相当好的方法。

YIN算法
对于钢琴声音的情况,时域波形表现不那么好。对于这个我们能清楚听到音高的钢琴声音片段,如果我们绘制不同滞后时间的自相关函数,情况就不那么清晰了,存在多个峰值。实际上,最高峰对应的是基频,但很难设定一个阈值来明确决定哪个峰值最适合识别基频。
一种类似于自相关的方法是Shiini和Kaawhara提出的YIN算法。它基于差分方程,该方程类似于自相关,我们只是取具有给定滞后的样本之间的差值,然后平方求和。当滞后等于周期长度时,此函数为0,因此我们必须找到该函数的最小值。YIN算法在此处进行了一些额外的处理,以获得对该周期的良好测量,并且对于单音信号效果相当好。事实上,它已成为语音或单音乐器基频测量中非常常见的算法。
让我们看看它对特定声音的效果。这是之前听过的声乐声音的频谱图,这里我们绘制了函数,黑线是YIN算法检测到的基频。当然,它是在时域检测的,而不是在这个频谱图上。
让我们听一下检测到的基频。这相当不错,基本上它很好地跟踪了基频。但这类方法不适用于许多声音,尤其不适用于复音信号,因此我们必须转向频域。

频域检测方法
那么,频谱中的基频是什么?
我们已经看到了如何识别声音的正弦波和分音,例如在双簧管声音上,这些叉号是峰值,其中许多对应于该声音的分音或谐波。但是,这些峰值中的哪一个(或者频谱的其他部分)是基频?我们如何识别哪些是谐波分音,然后也许从这些信息中我们可以识别出哪个频率是这些分音的基频?


基频的定义
声音频谱中的基频F0,可以定义为最能解释频谱峰值的谐波序列的最大公约数。
我认为这是一个非常简洁的定义,事实上我们可以基于此开发算法。
这里我们看到双簧管声音和峰值的图,垂直绿线对应于一个谐波序列。实际上,它们对应于最能解释这些频谱峰值的谐波序列。因此,仅通过目视检查,我们清楚地看到绿线(它们都是第一条绿线的倍数)最接近谐波序列,尽管其中一些并不完全在峰值之上,并且有一些峰值未被考虑在内。

双向失配算法
频域中的基频检测问题可以表述为一个模式匹配问题,我们必须找到最符合频谱的谐波序列模式。Mahar和Bom提出的双向失配算法正是这样做的。

该算法的概念是,它测量实测峰值与理想谐波序列(预测峰值)之间的差异,反之亦然。因此,如果我们看图,我们在最右边看到实测峰值,这些是我们获得的峰值频率。然后我们想检查一个给定的预测谐波序列,一个给定的f0及其倍数,看它与这些实测峰值的接近程度,即它能在多大程度上解释这些实测峰值。所以我们要做的是测量这对值之间的距离。
因此,我们将测量从预测到实测的距离,以及从实测到预测的距离,这就是为什么称为“双向”失配,因为实际上这两个距离并不相同。
第一个方程是从预测到实测。我们取每个预测峰值或每个预测值,寻找最接近的实测峰值,并计算频率距离,然后根据幅度进行缩放。我们还有一个值,用于相对于高频来提升低频的重要性。这里有一些加权系数,允许我们根据想要处理的声音类型来调整这个方程。我们不会深入细节,但欢迎查阅文章或这个方程以更好地理解它。


然后我们进行另一个方向的测量,即从实测到预测的误差。因此,我们首先查看所有实测峰值,寻找最接近的理想峰值或理想谐波,再次计算距离,并应用一些加权因子。然后我们得到一个总误差,即这两个误差的总和,同样我们有一些加权系数,以便我们可以根据特定情况进行设置。Bahar和Mosam为这些系数和变量提出了一些值,这些值我们将使用。

算法示例

让我们举一个例子来更好地解释这个算法。例如,假设我们测量到一组峰值,具体来说,假设我们在200赫兹、300赫兹、500赫兹、600赫兹、700赫兹和800赫兹处测量到峰值。让我们检查不同的基频,检查以50赫兹为基础的谐波序列,另一个以100赫兹为基础,另一个以200赫兹为基础。
在这些指标中,我们看到对于这些不同的候选基频,从预测到实测和从实测到预测的不同误差。显然,100赫兹的结果最好。100赫兹是能最好解释这些峰值的谐波序列,尽管频率100实际上并不存在。这是该算法一个非常有趣的结果:基频不必是一个实测峰值,算法也能给出该特定点的值。


让我们举一个特定声音的误差函数例子,即我们一直在看的双簧管声音。在底部,我们有三个误差函数:蓝色是从预测到实测,绿色是从实测到预测,黑色是总误差,针对从0到1500赫兹的可能基频范围。基本上,我们扫描了所有频率范围,并以1赫兹为增量尝试了所有可能频率的算法。
我们清楚地看到有一个点存在最小值,一个局部最小值,当然它在440赫兹,这是这个双簧管声音的基频。这是一个简单的案例,因此在这里我们看到算法对于基频是440赫兹几乎没有疑问。

复杂信号与复音检测 🎶
但让我们看一个更复杂的声音,即之前提到的钢琴声音。这是双向失配算法识别出的该乐句最佳基频的结果。黑线是误差函数在不同音符随时间变化时的最小值。让我们先听钢琴声音,然后听这个作为正弦波的基频。嗯,存在一些毛刺,特别是在我们看到一些间隙的区域,但它在跟踪这个基频方面做得相当不错。
在复音信号中,这并不容易,要困难得多。
复音信号可以有许多声源,包括谐波和非谐波成分。复音信号中基频检测的思想是识别所有同时演奏的谐波乐器的基频,从而找到每一帧中存在的所有谐波序列。例如,在这张图中,我们展示了之前讨论过的、可以听到的信号中的谐波成分。
我们根据某种算法绘制了可能存在的谐波序列。有一个谐波求和公式,允许我们以类似于双向失配的方式测量不同谐波序列的强度,这些是最强的(或者说最响亮的)谐波序列,或者至少是候选谐波序列。因此,这些是那些谐波序列可能的基频。显然,我认为这并不完全正确,但这是对它的初步估计。
Salomon和Gomez提出了一种算法。在这种谐波求和轮廓上,能够识别哪个是主奏乐器或主唱声部,从而识别出这种情况下正在唱歌的突出谐波乐器,并且它做得相当好,就像在这个声音示例中一样。我将播放它为此声音找到的突出音高。这又相当不错了,可能有一些毛刺,特别是我看到一个毛刺,但它能够识别出整个声音中突出的音高。
算法参考文献
我在本课中提到的算法的最佳参考文献是提出它们的原始文章。因此,我鼓励你:对于YIN算法,查阅Shivinni和Kaawhara的文章;对于双向失配算法,阅读Mahara和Mosam的文章;对于Salomon和Gomez的旋律算法,查阅这篇IEEE Transactions文章,其中描述得相当详细。
此外,你可以找到其他信息,并且已经提出了许多用于基频和音高检测的算法。因此,我鼓励你在这方面多做一些研究,掌握这些思想背后的技术。


总结
在本讲中,我们介绍了基频检测的不同方法。这是一个尚未完全解决的研究问题,特别是对于复杂信号。为了不使事情过于复杂,我们将专注于单音信号,但从现在开始我们将解释的概念,通过将上一讲介绍的谐波模型与我们刚刚介绍的F0检测相结合,也应该适用于任何类型的信号,从而分析和合成谐波信号。但事情还没有结束。因此,我们下一讲再见,在那里我们将进一步探讨,并尝试看看当我们遇到谐波模型效果不佳的声音时会发生什么。


我们下节课见。
041:音高检测 🎵
在本节课中,我们将学习如何使用Sonic Visualizer及其插件来分析不同音频信号的音高。我们将分别对单音、语音和复音音乐进行音高检测,并比较时域和频域两种方法的差异。

概述
音高检测是音频信号处理中的一项基础任务,尤其对于具有明确基频的谐波声音至关重要。本节演示课将展示如何在Sonic Visualizer中利用不同插件来执行音高分析。
单音分析:时域方法
首先,我们分析一个清晰的女声演唱单音。这是一个音色均匀、音高明确的谐波声音。
以下是分析步骤:
- 在Sonic Visualizer中打开来自SMS工具集的女性演唱音频文件。
- 播放音频以确认其音高清晰可辨。
- 打开频谱图窗格,观察声音的谐波结构。通过增大窗长(如2048点),可以更清晰地看到代表基频的第一条谐波横线。
- 使用“audioio pitch detector”插件进行时域音高检测。该插件由Poroier开发,Chris Canan实现,我们选择其中的YIN算法。
- 采用默认参数进行计算。计算完成后,一条紫色曲线会叠加在频谱图上,追踪音高轮廓。

结果显示,YIN算法成功追踪了整个音高轮廓,仅在起始的无声段出现了一些偏差。通过查看具体数值,我们可以看到频率从约414Hz开始,下降到371Hz,最后又回升到约443Hz。
语音分析:应对复杂性
接下来,我们分析一个更具挑战性的男性语音信号。语音信号通常包含无声段、辅音等非谐波成分,音高检测更为复杂。
分析过程如下:
- 打开男性语音文件并播放。
- 生成该语音的频谱图,并使用较大的窗长(如2048点)来更好地观察低频区域的谐波横线。
- 使用相同的“audioio pitch detector”插件和YIN算法进行音高检测。
- 将结果曲线的颜色改为黑色以便观察。

检测结果整体良好,但在某些片段(尤其是无声段或过渡处)出现了跳变或不准确的音高值。这提示我们,通过调整静音阈值等参数,可能获得更精确的音高轨迹。
复音音乐分析:频域方法
最后,我们分析一段包含人声、打击乐、小提琴和持续低音的复音音乐片段。对于这种复杂信号,时域方法不再适用,必须采用频域方法。
以下是使用频域插件“Meloia”进行分析的步骤:

- 打开复音音乐文件并播放。
- 生成频谱图,并调整窗长和缩放以观察可能存在的多个音高线。
- 使用“Meloia”插件。该插件由MTG的Justin Salomon开发,其分析过程分为几步:
- 显著性函数计算:首先计算显著性函数,其水平轴并非赫兹,而是用于识别所有可能的基频候选。
- 轮廓查找:接着,从可能的基频中找出最突出的音高轮廓。
- 旋律轮廓提取:最后,通过“Meloia提取”算法,结合音高范围、帧长、帧移等参数(例如帧移为128个样本,精度很高),以及浊音容差等设置,提取出主要的旋律轮廓。
- 将得到的音高轮廓曲线改为黑色,并放大观察具体数值。
虽然无法在Sonic Visualizer中直接合成并聆听提取出的音高轮廓来验证,但通过视觉对比,可以看到“Meloia”算法在复音环境下对人声音高的追踪具有合理性。
总结
本节课中,我们一起学习了在Sonic Visualizer中使用不同插件进行音高检测的实践方法。
我们使用了两个主要插件:实现了YIN算法的“audioio pitch detector”,以及由MTG开发的、基于频域分析的“Meloia”插件。分析的声音样本来自Freesound。
音高检测在处理谐波声音的分析、理解和合成中是一个必要步骤。对于简单单音,时域方法(如YIN算法)效果很好;而对于复杂的复音音乐,则需要“Meloia”这类更先进的频域方法。希望本课为您提供了关于音高检测问题的实用视角。

在下节演示课中,我们将运用理论课所学的谐波模型,对声音进行基于谐波的分析。
042:谐波模型实践

在本节课中,我们将学习如何使用SMS工具包中的谐波模型来分析简单的谐波声音。我们将通过分析电子合成音和真实的小提琴音,来理解如何设置合适的分析参数,并最终合成声音以评估模型的质量。
概述
我们将从基础的DFT分析开始,逐步过渡到正弦模型,最后聚焦于谐波模型。通过这个过程,我们将学习如何根据声音特性选择窗口类型、窗口大小等关键参数,以实现高质量的分析与合成。
从DFT分析开始
首先,我们从一个简单的电子合成音——锯齿波开始分析。这是一个基频为440Hz(标准音A4)的清晰谐波声音。
以下是分析一个简单声音的步骤:
- 选择窗口类型:对于简单的电子音,窗口类型的选择并不关键。我们从汉宁窗开始。
- 计算窗口大小:窗口大小
M的计算公式为M = 4 * fs / f0,其中fs是采样率(44100 Hz),f0是基频(440 Hz)。计算结果是401个采样点,这对应了声音的4个周期。 - 设置FFT大小:为了获得平滑的频谱,我们使用比窗口大小大得多的FFT点数,例如2048点。
- 选择分析位置:对于这个1秒长的声音,我们选择在0.2秒的位置进行分析。
执行分析后,我们得到了该片段的频谱。在频谱中,我们可以清晰地看到对应于各次谐波的峰值,并且每个谐波处的相位都非常平坦和清晰。
应用正弦模型
上一节我们通过DFT观察了声音的频谱结构。现在,我们使用正弦模型来分析同一个锯齿波声音。
正弦模型需要设置额外的参数来识别声音中的“部分”(即正弦分量)。以下是关键参数设置:
- 幅度阈值:设置为-100 dB,以过滤掉频谱中能量过低的成分。
- 最小持续时间:对于稳定的电子音,此参数影响不大。
- 最大跟踪正弦波数量:设置为一个较大的数值,例如100。
- 频率偏差:对于非常稳定的声音,允许的频率变化可以设置得很小。
使用与DFT分析相同的窗口(汉宁窗,401点)进行分析,模型成功地识别出了声音中的谐波。然而,在极低频处,它也捕捉到了一些由分析窗口旁瓣引起的伪影。
为了观察旁瓣的影响,我们换用旁瓣更高的矩形窗,并增大窗口尺寸至600点。再次分析后,我们看到模型识别出了大量由旁瓣产生的正弦轨迹。尽管从合成的角度看,声音质量仍然不错,但从分析的角度看,这并不理想,因为它包含了分析过程带来的伪影。
核心:谐波模型分析
在了解了正弦模型后,本节我们来看看更专门的谐波模型。谐波模型强制要求识别出的正弦分量必须是某个基频的整数倍,这能有效过滤掉非谐波成分。
我们首先使用上一节中“不理想”的参数(矩形窗,600点)来运行谐波模型。除了窗口参数,还需要设置谐波模型特有的参数:
- 谐波数量:最大谐波数可由公式
N = fs/2 / f0计算。对于440Hz的基频,N = 22050 / 440 ≈ 50。 - 基频搜索范围:为了辅助基频检测算法,我们可以指定一个范围,例如100 Hz到600 Hz。
即使输入了包含旁瓣的分析结果,谐波模型也成功地约束了搜索,只识别出了真正的谐波。接着,我们换回理想的参数(汉宁窗,401点),得到了更干净、准确的分析结果。原始声音与合成声音听起来完全相同,证明模型捕捉到了所有关键的谐波信息。
分析真实乐器声音
现在,我们将模型应用于一个更真实、更复杂的声音——一个小提琴的B3音符(基频约246.94 Hz)。
首先,我们再次使用DFT来确定合适的分析参数。对于小提琴这种含有更多噪声成分的自然声音,使用主瓣更宽、旁瓣更低的布莱克曼窗是更好的选择。其窗口大小的计算公式为 M = 6 * fs / f0,计算结果约为1075个采样点。
使用布莱克曼窗和1075点的窗口进行分析后,频谱中的谐波峰值比使用汉宁窗时更加清晰可辨。
接下来,我们使用谐波模型来分析这个小提琴音。参数设置如下:

- 窗口:布莱克曼窗,1075点。
- 幅度阈值:-100 dB。
- 谐波数量:
N = 22050 / 246.94 ≈ 89。 - 基频搜索范围:200 Hz 到 300 Hz。
分析结果很好,模型识别出了所有谐波。合成的声音捕捉了原声的核心特质,听起来更干净、平滑,虽然在起振部分和亮度上与原声略有差异。
我们还可以通过改变参数来探索模型。例如,将分析的谐波数量限制为仅前5个,合成出的声音将是一个明显的低通滤波版本,失去了所有高频成分。

总结

本节课中,我们一起学习了如何利用SMS工具包中的谐波模型进行实践分析。我们从DFT基础出发,经历了正弦模型,最终深入应用了谐波模型。通过分析锯齿波和小提琴音,我们掌握了如何根据声音特性(如稳定性和频谱复杂度)选择窗口类型、计算窗口大小,并设置幅度阈值、谐波数量等关键参数。谐波模型通过强制谐波约束,能够有效地从声音中提取出谐波成分,并实现高质量的重新合成。在下一课中,我们将分析随时间变化的更复杂声音(如旋律),学习如何设置参数以捕捉谐波声音的时变特性。
043:谐波模型分析演示

在本节课中,我们将学习如何使用谐波模型分析一个真实的大提琴乐句。我们将通过调整参数来探索模型的潜力与局限,并观察不同设置对分析结果的影响。
上一节我们介绍了谐波模型的基本概念,本节中我们来看看如何将其应用于实际声音分析。
分析大提琴乐句

我们将分析一小段来自传统加泰罗尼亚歌曲《鸟之歌》的大提琴乐句。这是一个时变声音,因此我们需要使用短时傅里叶变换来理解它。
首先,我们在SMS Tools GUI中打开声音文件 celloPhrase.wav。
以下是初始分析参数的设置步骤:


- 选择布莱克曼窗。与汉明窗相比,其主瓣更宽,但旁瓣更低,可能更适合此声音。
- 设置窗长为1001个采样点。
- 设置FFT大小为1024。
- 设置跳跃大小为250(至少为窗长的四分之一)。
计算后,我们可以观察幅度谱和相位谱。
确定关键参数
为了准确分辨乐句中的谐波,我们需要确定最低的基频,因为它决定了谐波间的最小距离。
通过观察频谱图底部,我们发现乐句起始和结尾部分的频率最低。初始频谱的频率分辨率不足,我们通过增加FFT大小至4096来获得更平滑、采样点更密的频谱。
再次观察后,测得最低音符频率约为348 Hz,最高音符频率约为456 Hz。此信息对决定窗长至关重要。
根据最低频率计算周期长度:44100 / 340 ≈ 129 个采样点。对于布莱克曼窗,需要约6个周期长度来分辨谐波,因此窗长设为 6 * 129 ≈ 778 个采样点。
应用正弦模型
在应用谐波模型前,我们先使用正弦模型进行分析。
参数设置如下:
- 窗长: 778 采样点
- FFT大小: 4096
- 幅度阈值: -90 dB
- 最小轨迹持续时间: 0.1秒
- 最大频率偏差: 适当调高以应对音高变化
分析结果显示,谐波基本跟随旋律线,但在音符起奏和过渡部分,会出现短暂的轨迹中断或谐波间杂散成分。

应用谐波模型
现在,我们使用谐波模型进行更专门的分析。
参数设置如下:
- 窗长: 779 采样点
- FFT大小: 4096
- 幅度阈值: -90 dB
- 最小轨迹持续时间: 0.2 秒
- 最大谐波数量: 60(根据
22050 / 最低频率估算) - 基频搜索范围: 300 Hz 至 500 Hz(覆盖旋律范围)
- 误差阈值: 初始使用默认值
分析结果良好,谐波被清晰识别。然而,在音符过渡的起奏部分,由于声音含有噪声成分,基频追踪可能出现问题。
如果我们将误差阈值从7调整为更严格的2,模型在过渡段将无法确认基频,导致谐波轨迹出现间隙,合成声音也会出现断续。
总结
本节课中我们一起学习了如何使用SMS Tools GUI分析一个大提琴乐句。我们先后应用了短时傅里叶变换、正弦模型和谐波模型,并通过调整参数观察了分析结果的差异。

演示表明,谐波模型能有效分析谐波结构,但在处理如起奏等含有非谐波或噪声成分的声音段落时仍存在局限。这引出了我们下一周的主题:将正弦/谐波模型扩展,纳入残差或随机成分,以构建更通用、能处理更多声音类型的模型。
044:06_03_01 基频检测 🎵
在本节课中,我们将学习谐波模型实现的第一部分:基频检测。我们将重点讲解一种名为“双向失配”的算法,该算法通过比较频谱峰值与预测的谐波序列来估算声音的基频。
概述


上一节我们介绍了谐波模型的理论基础。本节中,我们来看看如何通过编程实现基频检测。核心任务是使用“双向失配”算法,从声音的频谱峰值中找出最匹配的谐波序列,从而确定其基频。
双向失配算法原理
该算法的核心思想是:给定一组从频谱中检测到的峰值(频率和幅度),以及一系列候选的基频,算法会计算每个候选基频对应的预测谐波序列与实测峰值之间的匹配误差。误差最小的候选频率即被判定为基频。
误差计算包含两个部分:
- 预测值到实测值的误差:衡量预测的谐波频率与最近的实测峰值之间的距离。
- 实测值到预测值的误差:衡量实测的峰值与最近的预测谐波频率之间的距离。
总误差是这两种误差的加权组合。算法会为每个候选基频计算一个总误差值。
代码实现详解
在SMS工具包的实用函数文件中,可以找到双向失配算法的实现。其核心是一个名为 twomismatch 的函数。
以下是该函数工作流程的简化描述:
def twomismatch(peakFreqs, peakMags, candidateFreqs):errorArray = [] # 存储每个候选基频的误差for f0 in candidateFreqs:# 1. 生成基于f0的谐波序列:harmonics = [f0, 2*f0, 3*f0, ...]# 2. 计算预测值到实测值的误差 (errorPredToMeas)# 3. 计算实测值到预测值的误差 (errorMeasToPred)# 4. 组合两种误差得到总误差 totalErrorerrorArray.append(totalError)# 找到最小误差对应的索引minErrorIndex = errorArray.index(min(errorArray))# 最佳基频即为该索引对应的候选频率bestF0 = candidateFreqs[minErrorIndex]return bestF0, errorArray
实际应用中,为了效率,我们通常使用C语言版本。Python版本有助于理解算法逻辑。
算法调用与候选频率生成
twomismatch 函数被另一个函数 f0Twm 封装。f0Twm 负责生成候选基频列表并调用核心算法。
以下是 f0Twm 的关键步骤:
- 接收频谱峰值、允许的最大误差、基频搜索范围(如50-2000 Hz)等参数。
- 从输入的峰值中,筛选出落在指定基频搜索范围内的频率作为候选基频。
- 调用
twomismatch函数计算所有候选频率的误差。 - 选择误差最小的候选频率作为最终检测到的基频。算法还包含简单的平滑跟踪机制,利用前一帧的基频来提高时间上的稳定性。
注意:当前的候选生成策略较为简单(直接使用峰值),为了更精确的结果,可以有更复杂的候选生成方法,但当前实现平衡了效率与效果。
单帧频谱分析示例
为了理解算法行为,我们可以编写一个脚本对单帧频谱进行分析。
脚本步骤:
- 加载一个声音(例如一个A4音高,440 Hz的乐器声)。
- 计算一帧信号的DFT(离散傅里叶变换)。
- 检测并插值频谱峰值。
- 在指定范围(如50-2000 Hz)内,将峰值频率作为候选基频。
- 调用修改后的
twomismatch函数,使其返回所有候选频率的误差数组。 - 绘制频谱和峰值,并打印候选频率及其对应误差。
运行示例后,我们可能看到类似以下输出:
候选基频 (Hz): [166.1, 440.0, 637.5, 833.2, 1030.8]
对应误差值: [4.8, -0.13, 2.1, 5.6, 3.9]
误差值越小(可能为负),表示匹配度越好。此例中,440 Hz的误差最小,被正确识别为基频。
完整音频文件分析
接下来,我们将算法应用于整个音频文件。这需要逐帧处理:
- 将音频分帧,使用滑动窗口。
- 对每一帧重复单帧分析过程:计算DFT、检测峰值、生成候选、调用
f0Twm。 - 收集每一帧计算出的基频。
在SMS工具包的谐波模型文件中,f0Detection 函数实现了上述完整流程。它处理整个声音,并加入了时间平滑约束,以确保基频轨迹的稳定性。

通过调整分析参数(如窗口大小、FFT大小),可以观察对检测结果的影响:
- 增大窗口和FFT尺寸:通常能提高频率分辨率,使峰值定位更准,从而得到更稳定、误差更小的基频轨迹。
- 参数示例:将窗口从1001点增加到2001点,FFT尺寸相应增加,可能会使基频估计值在439.9 Hz到440.04 Hz之间波动,误差范围比之前更小。

双簧管声音分析实例
最后,我们将此分析应用于一段真实的双簧管声音。运行分析后,我们可以绘制出基频随时间变化的轨迹。
观察结果可能显示:
- 基频围绕442 Hz轻微波动(演奏者可能略高于标准A4音高440 Hz)。
- 波动中既包含算法(峰值检测、插值)引入的微小误差,也真实反映了演奏中的自然颤音或乐器声学特性。
这种分析有助于我们同时理解算法的性能和声音本身的特征。

总结
本节课中我们一起学习了基频检测的关键算法——“双向失配”算法的原理与实现。我们了解了它如何通过比较谐波序列与频谱峰值来寻找最佳基频,并探讨了从单帧分析到完整音频处理的应用过程。通过调整分析参数,我们可以平衡计算效率与检测精度。基频检测是谐波模型分析的第一步,在下一讲中,我们将在此基础上引入完整的谐波模型,实现对声音的分析与合成。

感谢学习,我们下节课再见。
045:谐波模型 🎵
在本节课中,我们将学习如何将谐波模型的分析与合成部分整合起来。我们将从之前讨论过的基频检测算法出发,构建一个完整的谐波建模系统。


在上一节编程课中,我们讨论了基频检测算法,即如何识别谐波声音的基频。现在,我们已经准备好将整个谐波模型的分析与合成部分整合在一起。下图展示了我们将要解释的系统框图。它从我们在正弦波模型中已经见过的标准模块开始:对输入声音进行分窗处理,然后执行DFT变换并寻找频谱峰值。接下来的两个模块是谐波模型特有的:第一个是基频检测模块,它从峰值中识别出最佳的基频;第二个模块则基于该基频寻找谐波,这也是我们今天要讨论的内容。之后,信号进入合成阶段,其过程与正弦波模型完全相同:我们在频域生成正弦波,进行逆FFT变换,然后通过重叠相加法重建整个声音。

接下来,让我们进入代码部分。

文件 harmonymod.py 包含了谐波模型的大部分函数。基频检测的实际实现在 UT functions 文件中,但这里我们有一个包装函数,用于调用我们在上一讲中提到的基频检测算法。此外,我们还有即将讨论的谐波检测函数,以及一个完整的谐波模型函数 harmonicModel,它同时执行分析和合成。但该函数设计用于实时处理,因此不允许对轨迹进行清理。今天我们将分开讨论分析和合成,因为这样可以清理轨迹,并利用过去几帧的信息,从而获得更好的分析和合成结果。
让我们详细讨论 harmonicModelAnal 这个函数。它的输入参数包括分析所需的所有参数:样本输入数组、采样率、窗函数、窗大小、跳数大小、峰值检测阈值 t、允许的最大谐波数量 nH、基频检测的频率范围(最小基频 minf0 和最大基频 maxf0),以及作为基频检测算法一部分的误差阈值 minErr。对于谐波轨迹的连续性跟踪,我们还有两个参数:harmDevSlope 和 minDur。harmDevSlope 是允许的谐波偏离斜率,用于处理较高次谐波比较低次谐波允许更大偏离的情况;minDur 参数则用于控制谐波轨迹的最小持续时间,不接受短于此持续时间的轨迹。
实际的代码结构与正弦波模型非常相似。我们有一个主循环,遍历整个声音信号,持续分析频谱、寻找峰值并进行插值。从这里开始是谐波模型特有的代码:我们调用基频检测算法(即上一讲中提到的来自工具函数的算法)。接下来的几行代码确保新的基频与前一帧的基频不会相差太大,以保持一定的连续性。然后,我们调用 harmonicDetection 函数,该函数根据基频识别谐波。它的代码在上面。
让我们来看这个 harmonicDetection 函数。该函数在给定基频 f0 和频谱峰值的情况下,识别哪些峰值接近该基频的整数倍。我们还接受来自前一帧的一些信息作为输入,以便识别是否存在时间连续性,以及 deviation 参数,它表示我们允许的与完美谐波性以及与前帧完美连续性的偏离。
代码并不复杂。核心基本上是这三行,我们定义了与完美谐波性的偏离:我们将特定峰值与特定的完美谐波频率进行比较,同时也将峰值与来自前一帧的先前谐波进行比较。然后,我们基于这个谐波偏离斜率 harmDevSlope 定义一个阈值,只有在此阈值内的峰值才会被接受。因此,如果当前峰值在阈值内(考虑到频率和时间演变的跟踪),我们就接受该峰值作为一个谐波,并将其添加到我们创建的谐波数组中。这就是谐波检测的基本功能。
回到 harmonicModelAnal 函数。在谐波检测之后,有一些代码用于创建值数组,这些值不断累加到输出的整个矩阵中。最后,当整个过程完成(即遍历了整个声音)后,我们清理轨迹,删除持续时间小于给定值的轨迹,这与我们在正弦波分析中所做的相同。
我编写了一个脚本,使用这个谐波模型分析函数来分析一个声音。我们选取了这个小提琴声音,并指定了所有参数。参数开始变得相当多:窗函数、窗大小、FFT大小、阈值(这里是 -90 dB)、最小持续时间、最大谐波数、最小和最大基频、可接受的误差阈值,以及刚刚讨论过的偏离斜率。还有两个参数通常固定,因为它们更与合成相关:合成缓冲区的FFT大小 Ns 和跳数大小 H。由于合成FFT缓冲区的要求,跳数大小通常为其四分之一,所以我们通常设 H 为 128,在图形界面中通常不允许更改这两个参数。
然后,我们读取声音,获取窗函数,并调用 harmonicModelAnal 函数。这里我只是绘制了这些轨迹的频率。让我们运行它。首先保存,然后运行。这是我们获得的轨迹,它们看起来不错。如果我们放大到特定区域,可能会看得更清楚。当然,放大后,我们开始看到音高转换处的问题,尤其是当谐波频率越来越高时。这是因为坐标轴是线性刻度,而这些偏离本质上是按对数变化的。实际上,随着频率升高,变化会大得多,因为它们是按对数变化的。这就是为什么我们需要之前指定的偏离斜率 harmDevSlope。例如,如果我们减小这个斜率,从 0.01 改为 0.001,然后再次运行,我们会发现现在轨迹少了很多,尤其是在高频部分。它限制了在高频部分允许的偏离,因此不允许这种较大的偏离。这样,我们就对跟踪过程的实现有了相当的控制。
在SMS工具包中,有一个 harmonicModel 函数同时执行分析和合成,实际上,它也是从界面调用的文件。在这个文件中,我们有一个主函数,它包含我们讨论过的所有参数。除了我们已经执行的分析外,它还进行合成,然后将文件保存为合成文件,并进行绘图。让我们运行这个 harmonicModel 函数。这会花费稍长的时间,因为它必须进行分析和合成,然后它只绘制指定的前5000赫兹。在这里,我们看到所有这些随时间演变的轨迹,合成的声音至少从波形上看非常相似。让我们听听输出声音。它创建了一个名为 violin-harmonicModel.wav 的文件。我可以播放它。

这是原始文件的合成版本,效果相当好。当然,从下周开始我们会发现,实际上合成中遗漏了一些东西,主要是我们缺失的残差成分。但无论如何,关于谐波模型的内容就到这里。我们回到幻灯片。

我们基本上从实现的角度看了谐波模型,通过查看代码。所有这些代码都是用Python编写的。实际上,我们使用的基频检测算法有一个C语言版本的实现,但我们也有Python版本,所有这些都包含在SMS工具包中。我强烈建议你们去研究并运行它,如果愿意的话可以使用它。关于谐波模型的编程课就到此为止,希望这能让你们深入了解实现这类模型需要什么。下周内容会变得更复杂一些,因为我们将尝试看看这个模型遗漏了什么,并尝试对这个缺失部分进行建模,这将是残差或随机成分的概念。下周见。谢谢。


本节课中,我们一起学习了如何将谐波模型的各个部分整合成一个完整的分析-合成系统。我们详细探讨了谐波检测函数的实现,它如何基于检测到的基频来识别谐波,并考虑了时间连续性和频率偏离。我们还通过实际代码示例,看到了参数(如谐波偏离斜率)如何影响谐波跟踪的结果。最后,我们运行了完整的谐波模型,生成了合成声音,并预览了下一讲将涉及的残差成分概念。
046:随机模型 🎵

在本节课中,我们将学习一种新的声音分析模型——随机模型。我们将了解什么是随机信号,如何对它们进行建模,以及如何利用这些模型来分析和合成声音。

什么是随机信号?🎲
上一节我们介绍了正弦模型,本节中我们来看看随机模型。随机信号无法用确定性的方式描述,只能通过概率统计的方式来描述。统计信号处理领域专门研究这类信号。
以下是随机信号的一些关键概念:
- 概率分布:描述信号取值的可能性。
- 均值与方差:描述信号的平均水平和波动程度。
- 自相关函数:用于测量信号的周期性或重复模式的程度。公式为:
R(τ) = Σ x(t) * x(t+τ)。自相关函数值越低,信号越接近随机信号。 - 功率谱密度:描述信号功率在频域上的分布。可以看作是离散傅里叶变换(DFT)在点数趋于无穷时的极限:
P(ω) = lim(N→∞) (1/N) * |X(ω)|²。


如何对随机信号建模?🔧
随机模型的核心思想是,一个随机信号可以表示为白噪声与一个滤波器(近似于原信号)的卷积。
从时域看,模型公式为:
y[n] = x[n] * h[n]
其中,x[n]是白噪声,h[n]是滤波器的冲激响应,*表示卷积运算。
从频域看,卷积变为乘积:
Y(ω) = X(ω) * H(ω)
其中,X(ω)是白噪声的频谱,H(ω)是滤波器频率响应。
由于白噪声的幅度谱是平坦的(常数),其相位谱是随机的,因此我们可以将模型简化为:
- 用原信号幅度谱的平滑近似作为
|H(ω)|。 - 用随机数序列作为相位谱
∠Y(ω)。

这样,我们就得到了一个随机模型:平滑的幅度谱 + 随机相位。
如何近似信号的幅度谱?📊
随机模型分析的关键在于如何获得输入信号时变幅度谱的良好近似。以下是两种常见方法:

1. 线性预测编码(LPC)

LPC模型将信号近似为过去样本的线性组合,其公式为:
x̂[n] = Σ (a_k * x[n-k]),其中 k 从 1 到 P。
目标是找到一组系数 a_k,使误差 e[n] = x[n] - x̂[n] 的平方和最小。LPC能很好地逼近具有共振峰特性的信号(如人声)。
2. 低通滤波法(本课程采用的方法)
这是一种更简单直接的方法,我们通过对信号的DFT频谱进行低通滤波来获得平滑的幅度谱近似。
步骤如下:
- 计算信号帧的DFT,得到频谱
A[k]。 - 对
A[k]进行低通滤波,得到平滑后的频谱Ã[k](点数更少)。 - 为了合成,可能需要将
Ã[k]零填充到原始长度。

如何合成随机信号?🎹
当我们通过LPC或低通滤波法得到了幅度谱的近似(即滤波器系数)后,就可以进行合成。
1. 使用LPC系数合成

合成过程是使用得到的LPC滤波器系数来过滤白噪声。这可以通过直接型或格型等滤波器结构来实现,核心是IIR滤波方程:y[n] = u[n] - Σ (a_k * y[n-k]),其中 u[n] 是白噪声。
2. 使用低通滤波近似合成(本课程采用的方法)

这种方法更直接:
- 获得平滑的幅度谱近似
Ã[k]。 - 生成一个随机相位谱
φ[k]。 - 计算复频谱:
Y[k] = Ã[k] * e^(j*φ[k])。 - 对
Y[k]进行逆DFT,即可得到合成的声音帧。


完整的随机模型分析-合成系统 🔄
现在,让我们把分析和合成部分组合成一个完整的系统。
以下是系统的工作流程:
- 分析:输入信号
x[n]-> 分帧 -> 计算每帧的DFT幅度谱 -> 对幅度谱进行低通滤波,得到平滑的随机模型参数(压缩后的幅度系数)。 - 合成:对每帧的平滑幅度谱进行零填充插值 -> 为其生成随机相位 -> 进行逆DFT得到合成帧 -> 使用重叠相加法将所有帧组合成完整的输出信号。

这个系统能够捕捉并重建出原始随机信号(如海浪声、小提琴弓毛噪声)的整体听感特征,虽然波形细节不同,但感知上相似。
总结 📝

本节课中,我们一起学习了随机模型。我们了解到,对于许多不适合用正弦波描述的声音(如噪声类声音),随机模型提供了一种有效的分析和合成方法。其核心是用平滑的幅度谱近似加上随机相位来表征信号。我们介绍了LPC和低通滤波两种幅度谱近似方法,并构建了一个完整的分析-合成系统。

在下一讲中,我们将探讨如何将随机模型与我们之前学过的正弦模型结合起来,从而能够更全面、更强大地处理各种类型的声音。
047:正弦加残差建模 🎵
在本节课中,我们将学习如何将之前讨论的正弦/谐波模型与随机信号模型相结合,形成“正弦加残差”或“谐波加随机”的复合建模方法。我们将分解声音为两个部分:确定性的正弦/谐波部分和残差部分,并学习如何分别建模与合成。
上一节我们介绍了随机信号及其建模方法。本节中,我们将以此为基础,扩展之前讨论的正弦与谐波模型,引入“正弦/谐波加残差”或“正弦/谐波加随机”的建模概念。
正弦加残差模型
正弦加残差模型是正弦模型的扩展。我们假设正弦模型无法完全表示整个声音,并且存在一个在感知上相关的残差分量,这是一个需要保留的声音成分。
以下是该模型的数学表示:
x[n] = y_s[n] + x_r[n]
其中:
- x[n] 是原始信号。
- y_s[n] 是正弦分量,由一组时变的正弦波求和得到:y_s[n] = Σ A_l[n] cos(Φ_l[n])。
- x_r[n] 是残差信号。
残差信号 x_r[n] 通过从原始信号中减去合成出的正弦分量来计算:x_r[n] = x[n] - y_s[n]。

从频谱角度理解这个模型更为直观。整个信号的频谱可以看作是多个正弦波(其频谱是经频率平移和幅度缩放的窗函数频谱)的叠加,再加上残差分量的频谱。而残差频谱正是通过从原始信号频谱中减去正弦分量的频谱得到的。
谐波加残差分析示例
以下是分析单帧声音的步骤示例。

-
原始帧与谐波分析(左图):
- 左上图显示了一段长笛声音的加窗帧。
- 左下图展示了对该帧进行频谱分析(峰值检测)的结果,蓝色“x”标记识别出的谐波成分及其相位。
-
谐波合成与残差计算(右图):
- 右中图(浅红/浅青色)显示了根据识别出的谐波参数合成的频谱。
- 通过从原始频谱(使用相同窗参数重新计算)中减去这个合成谐波频谱,我们得到残差频谱(深红/深青色)。
- 对残差频谱进行逆傅里叶变换,得到时域的残差信号(右上图深蓝色波形)。这个残差并非简单的误差,它包含了声音中重要的特征成分(如呼吸声)。
完整的分析-合成系统
将上述单帧分析扩展到整个信号,我们得到以下系统框图:
- 输入信号 x[n]。
- 加窗与FFT:对信号加窗并进行FFT,得到幅度和相位频谱 X[k]。
- 峰值检测与基频估计:从频谱中检测峰值,并估计基频 f0。
- 谐波识别与合成:根据基频识别谐波,并使用布莱克曼-哈里斯窗合成谐波频谱 Y_h[k]。
- 残差频谱计算:使用与合成相同的窗参数对原始信号重新计算频谱,然后进行复数减法:X_r[k] = X[k] - Y_h[k],得到残差频谱。
- 合成与叠加:将谐波频谱与残差频谱相加,进行逆FFT和重叠相加,最终合成输出信号。
从残差到随机模型
直接得到的残差分量是一个完整的信号,难以进一步处理。我们需要用模型来近似它,这里我们引入随机模型。
于是,我们得到正弦加随机模型:
x[n] = y_s[n] + e[n]
其中 e[n] 不再是简单的减法残差,而是对残差进行随机建模后得到的随机分量。该随机分量可以理解为用白噪声通过一个滤波器的结果,该滤波器的频率响应近似于残差信号的频谱包络。
在频域中,随机分量的频谱可以表示为:
E[k] = M_s[k] * e^(j * φ_rand[k])
其中:
- M_s[k] 是对残差频谱幅度 |X_r[k]| 的平滑近似(频谱包络)。
- φ_rand[k] 是白噪声的随机相位谱。
以下是随机近似过程的频谱视图:

- 原始信号频谱(含谐波)。
- 合成谐波频谱 Y_h[k](浅红色)。
- 残差频谱 X_r[k](通过减法得到)。
- 对残差频谱幅度进行平滑,得到随机模型的幅度谱 M_s[k](曲线近似)。
完整的正弦加随机分析-合成系统
该系统在谐波加残差系统的基础上,增加了一个随机近似模块:
- 前几步与之前相同:计算原始频谱、检测谐波、合成谐波频谱、计算残差频谱。
- 随机近似:对残差频谱幅度 |X_r[k]| 进行平滑,得到随机模型的幅度包络 M_s[k]。
- 随机分量合成:生成具有随机相位 φ_rand[k] 的频谱,并将其幅度设置为 M_s[k],得到随机分量的频谱 E[k]。
- 合并与合成:将谐波频谱 Y_h[k] 与随机分量频谱 E[k] 相加,然后进行逆FFT和重叠相加,生成最终合成声音。
应用示例:萨克斯风声音分析
让我们听一个萨克斯风声音的分析与合成示例。
原始萨克斯风声音:

合成出的谐波分量:

合成出的随机分量(已放大音量):
将谐波与随机分量重新合成后的声音:

合成声音捕捉了原始声音中大部分感知上重要的特征。
总结与延伸阅读
本节课中,我们一起学习了本课程最先进的模型之一——正弦/谐波加残差(随机)模型。我们结合了之前所有的模型,发展出可用于多种声音和应用的分析与合成技术。
关于本讲主题的入门教程材料不多,但有大量研究文章提出了不同的分析策略。你可以在MTG网站的相关链接中找到许多已发表的关于这些主题的论文以供深入阅读。

下一讲,我们将聚焦于如何利用这些模型来对声音进行变换,创造有趣的新声音。
048:随机模型 🎵




在本节课中,我们将学习随机信号的概念,以及如何使用频谱近似法来建模这类信号。我们将通过实际操作,利用SMS工具包中的实现来探索随机模型的工作原理。

上一节我们介绍了随机信号的基本概念,本节中我们来看看如何使用SMS工具包中的随机模型进行实际的声音分析。
首先,我们打开SMS工具的界面,并使用短时傅里叶变换来分析一个声音。一个非常适合随机分析的声音是海浪声,因为它非常嘈杂,很适合这种近似方法。
以下是分析步骤:
- 打开一个海浪声音文件。
- 参数设置:窗口大小默认为1024,跳跃大小默认为512。
- 首先聆听原始声音。
- 然后计算其STFT。
分析结果显示,该海浪声的频谱幅度图非常“颗粒化”,缺乏重复性的结构。我们能观察到的整体结构是波浪起伏的形状,表现为图中红色区域的增加。此外,高频部分比低频部分更柔和,能量更多地集中在低频。在相位谱中,我们看到的基本上是随机数,没有特定的结构。放大观察幅度谱和相位谱,都进一步证实了其随机性。
现在,让我们看看之前讨论过的正弦模型在这个声音上的表现。我们使用正弦模型来分析同一个海浪声。
以下是参数设置与步骤:
- 窗口大小设置为1000。
- 跳跃大小为窗口的四分之一。
- 幅度阈值设为-80 dB。
- 正弦波数量设为200。
- 开始分析。
分析结果产生了大量分散在各处的正弦波频率,呈现出非常颗粒化、随机的方式。然而,用这个模型重新合成的声音听起来并不理想,带有一种原始声音中没有的“音调”质感。这说明正弦模型可能并不适合这类完全随机的声音。
因此,我们现在转向随机模型。再次打开海浪声文件。
随机模型的参数较少,主要包括跳跃大小和关键的“平滑近似因子”。这个因子决定了我们在频域上进行平滑的程度。例如,设为0.1意味着我们将FFT尺寸减少了90%,只保留10%的频率样本,同时相位谱将由随机数生成。
以下是操作步骤:
- 设置跳跃大小为64,以更好地捕捉时间变化。
- 设置平滑近似因子为0.05,以获得更精细的粒度。
- 计算随机模型近似。
- 聆听合成结果。
与原始STFT相比,近似后的幅度谱更加粗糙,水平和垂直方向上的细节都减少了。但合成出的声音虽然与原始声音不完全相同,却具有非常相似的水流质感。通过调整参数,我们可以获得不同精度的近似效果。
为了进一步理解,我们尝试对一个并非完全随机的声音(例如语音)使用随机模型近似。我们打开一个男性语音文件。
操作步骤如下:
- 设置跳跃大小为256。
- 设置平滑近似因子为0.2。
- 首先聆听原始语音。
- 然后计算其随机近似。
随机近似过程去除了相位谱(用随机数替代),并平滑了幅度谱。合成出的声音听起来像耳语,完全失去了原始语音中的音高信息,因为音高信息很大程度上蕴含在相位谱以及被平滑掉的幅度谱细节中。这个实验生动地展示了随机模型如何剥离声音中的确定性(如音高)成分,保留其随机性特征。

本节课中我们一起学习了如何使用SMS工具包中的随机模型来近似和分析声音。我们通过海浪声和语音的例子,观察了该模型如何通过平滑幅度谱和随机化相位谱来捕捉声音的随机性成分,并对比了其与正弦模型在不同类型声音上的适用性。


在接下来的演示课程中,我们将把随机模型与我们讨论过的其他模型结合起来使用。


我们下节课再见。
049:谐波加残差模型 🎵
在本节课中,我们将学习一种重要的频谱建模方法——谐波加残差模型。我们将结合前几周讨论过的正弦波与谐波分析,以及残差及其随机近似的概念,来分解和重构音频信号。

概述
本节课程将演示如何使用谐波加残差模型来分析声音。核心思想是:首先分析声音中的谐波成分,然后将其从原始信号中减去,从而得到残差信号。最后,我们可以将识别出的谐波与残差重新组合。
工具与初始设置
我们将使用课程中一直在开发和讨论的SMS工具。首先从DFT(离散傅里叶变换)开始,并使用SMS工具目录中的一个管风琴声音作为示例。
让我们先听一下这个声音。这是一个非常稳定、传统的管风琴标准音,音高为C3,频率约为264赫兹。
为了分析,我们需要选择一个合适的窗函数大小。由于这是一个非常稳定的音符,我们可以利用更长的窗来更好地分离谐波。对于Blackman窗,我们选择6个周期。
计算窗长的公式:
窗长 = (采样率 / 基频) * 周期数
代入数值:(44100 / 264) * 6 ≈ 1002 个样本。
为了便于计算,我们使用一个奇数的窗长,例如1003个样本。同时,我们使用2048点的FFT大小,并添加相当多的零填充。
频谱分析
计算完成后,我们观察频谱。我们分析了声音的6个周期,波形看起来非常正弦化。从幅度谱中可以看到,并没有非常多清晰的谐波,高频区域有相当多的能量,但并非都是清晰的正弦波。这表明声音中含有相当多的噪声或随机成分。
相位谱符合预期,重构的声音质量也相当好。这是一个不错的起点。考虑到声音的稳定性,我们可以尝试使用更大的窗长。
让我们尝试将窗长增加一倍,使用2006个样本,并将FFT大小增加到4096。这样我们分析了大约12个周期。与之前的频谱相比,现在的谐波定义更加清晰,背景成分也更多。更大的窗长有助于抑制非相干的随机成分,从而突出相干的谐波部分。
短时傅里叶变换分析

接下来,我们应用相同的参数进行短时傅里叶变换分析。我们打开管风琴声音,使用2006个样本的窗长和4096点的FFT大小。
我们需要选择一个跳跃大小。这里影响不大,为了效率,我们选择500个样本(约为窗长的四分之一)。计算完成后,我们得到了时频谱。重构的声音与原始声音非常相似。
观察频谱图,谐波线非常清晰,但谐波之间也存在不少成分。这是一个好的分析结果。
谐波分析
现在,我们使用相同的参数进行谐波分析。以下是分析参数的设置:
- 窗函数:Blackman
- 窗长:2006
- FFT大小:4096
- 幅度阈值:-90 dB(合理的起始值)
- 谐波最小持续时间:由于是长而稳定的音符,我们可以要求更长的轨迹,例如0.2秒甚至更长。
- 谐波数量:考虑到高次谐波会消失,30或40个谐波应该足够。
- 基频范围:130 Hz 到 300 Hz(确保能覆盖264 Hz)。
- 频率偏差:允许谐波偏离完美谐波关系的程度,我们先保留默认值。
计算完成后,我们找到了许多谐波。在起振和衰减部分,声音非常不稳定,这些部分的谐波可能应该被剔除。
一些谐波非常稳定,但另一些则非常不稳定。让我们听一下仅由谐波合成的声音。它听起来不错,但与原始声音相比,明显缺少了背景中的“空气感”或噪声成分。此外,一些不稳定的高次谐波可能实际上是在追踪噪声部分。
为了改进,我们可以通过减小频率偏差来更严格地限制谐波。例如,将偏差设置为0.001。这样剔除了一些不稳定的成分。
更好的方法是确保谐波轨迹足够长。我们将最小持续时间从0.2秒增加到1秒。这样,我们剔除了大量不稳定(无论是谐波还是其他)的高频成分。
现在再听谐波合成的声音,它听起来非常干净,当然不如原始声音丰富。


谐波加残差模型
现在,我们可以使用相同的参数进入谐波加残差分析,即从原始信号中减去这些谐波。
我们使用相同的声音和参数(窗长2006,FFT大小4096,阈值-90 dB,轨迹最小持续时间1秒,谐波数量30,基频范围130-350 Hz)。
计算完成后,我们得到了结果。图中显示了找到的谐波(黑线),而背景的频谱图就是残差。

我们可以分别聆听不同的成分。我们已经听过谐波部分了,现在来听一下残差。这是一个非常清晰的声音,正是谐波声音中所缺失的部分。显然,如果将这两个声音加在一起,就会得到原始声音。与原始声音对比,基本上是相同的。
这个声音很好地展示了谐波加残差模型的潜力。当然,对于随时间变化的更复杂声音,调整参数可能会更具挑战性。
总结
本节课我们一起学习了谐波加残差模型。我们使用SMS工具分析了稳定的管风琴音,通过调整窗长、谐波持续时间和频率偏差等参数,成功地将声音分解为谐波和残差两部分,并验证了模型的有效性。
这是本周讨论的模型中的一个实例。在下一个演示课程中,我们将讨论谐波加随机模型,学习如何将本节中听到的残差建模为一个随机信号,并探索其应用。

期待下节课再见。
050:谐波加随机模型 🎵

在本节课中,我们将学习谐波加随机模型。上一节我们介绍了谐波加残差模型,并分析了如何从原始声音中分离出谐波和残差。本节中,我们将更进一步,探讨如何用随机模型来近似表示残差信号,从而构建一个由谐波和随机分量组成的更简洁的声音表示。
声音分析准备
首先,我们使用SMS工具打开一个长笛声音文件。这是一个非常稳定的音符,音高为A4(440 Hz),并且带有明显的呼吸声,这将是随机分量的重要来源。
为了分析,我们需要选择合适的参数。对于稳定的声音,布莱克曼窗是一个好选择,因为它能提供较低的旁瓣和良好的信噪比。窗口大小的计算公式为:
窗口大小 = 6 × 采样率 / 基频
对于这个采样率为44100 Hz、基频为440 Hz的声音,计算出的窗口大小约为601个样本。为了获得更好的谐波分辨率,我们选择使用801个样本的窗口。FFT大小设置为2048,以获得更平滑的频谱。我们将在声音的中间部分进行分析。

谐波模型分析
现在,我们直接应用谐波模型进行分析。参数设置如下:窗口801,FFT大小2048。对于峰值检测和谐波检测,我们不需要在频谱中搜索太多谐波,因为数量不多。我们将谐波的最小持续时间设置为0.2秒,最大谐波数量设为40,基频搜索范围设为300-500 Hz,峰值检测误差设为默认值0.7,偏差设为0.02。

分析结果显示,我们得到了相当数量的谐波,其中一些在高频区域出现又消失。如果只显示前5000 Hz,我们可能看不到更高频率的细节。合成的谐波声音听起来相当准确,但与原始声音相比,缺少了原始声音中更明亮的品质,这是因为原始声音包含了所有其他高频成分。

我们可以调整参数,例如将偏差设为0.01,以允许更多不稳定的高频谐波出现。然而,这些高频“谐波”实际上并不稳定,更像是被噪声或呼吸声掩盖的成分。

谐波加残差模型
接下来,我们使用谐波加残差模型。使用相同的参数,我们得到谐波和残差。残差信号听起来非常像噪声,清晰地包含了起振瞬态和持续的呼吸噪声。这正是一个典型的随机信号。
谐波加随机模型
既然残差非常接近随机信号,我们就可以应用谐波加随机模型。我们使用相同的分析参数,并引入一个随机近似因子,默认值为0.1。这意味着它将整个频谱的分辨率量化为原来的十分之一。
分析后,我们看到频谱显示范围更广(0-14000 Hz),因此能看到更多的随机分量。谐波主要集中在低频部分,5000 Hz以上基本是随机分量。合成的随机分量虽然丢失了一些残差的细节,但确实保留了呼吸噪声的特性。当它与谐波分量重新合成时,声音听起来不错,但谐波分量在很大程度上掩盖了随机分量,需要仔细聆听才能分辨。
总结

本节课中,我们一起学习了谐波加随机模型,并使用SMS工具进行了实践。我们分析了长笛声音,看到如何用谐波分量和随机分量来共同表示一个声音。与谐波加残差模型的主要区别在于,随机表示为我们提供了更大的灵活性。在接下来的课程中,我们将对声音进行各种变换,而随机表示将使我们能够实现这些操作,这是单纯的残差表示所无法做到的。希望本节课能让你对谐波加随机模型的潜力有所了解。
051:随机模型 🎵
在本节课中,我们将学习如何对音乐中的随机信号进行建模。我们将介绍一种基于统计信号处理的随机模型,它特别适用于那些无法用正弦波或谐波分析很好描述的、具有噪声特性的声音。
上一节我们介绍了频谱建模的基本概念,本节中我们来看看如何具体实现随机模型。

核心概念与流程
随机模型的核心思想是:对于随机性强的信号,其相位谱并不重要,建模的重点在于幅度谱。基本流程如下:
- 对信号进行加窗和FFT变换,得到幅度谱。
- 对幅度谱进行平滑近似,得到“随机包络”。
- 合成时,根据平滑后的幅度谱,随机生成相位,重建复数频谱。
- 通过逆FFT和重叠相加,生成新的信号。
其核心公式和代码描述如下:
- 幅度谱平滑(降采样):使用
scipy.signal.resample函数对幅度谱进行降采样,减少数据量,保留总体趋势。# Mx 为原始幅度谱(dB),stocf 为随机因子(平滑/降采样因子) Mx_res = np.maximum(Mx, -200) Mx_stoc = signal.resample(Mx_res, int(Mx_res.size * stocf)) - 频谱重建与合成:将平滑后的幅度谱上采样回原始长度,并为其生成随机相位,以重建复数频谱。
# 上采样幅度谱回原始长度 N/2 M = np.interp(np.arange(0, N/2), np.arange(0, Mx_stoc.size)/stocf, Mx_stoc) # 生成随机相位 (0 到 2*pi) P = 2 * np.pi * np.random.rand(int(N/2)) # 重建正频率部分的复数频谱(转换为线性幅度) Y = np.power(10.0, M/20.0) * np.exp(1j * P) # 根据共轭对称性,重建完整频谱并做逆FFT Y_complete = np.concatenate((Y, np.conj(Y[-2:0:-1]))) x = np.fft.irfft(Y_complete) # 或使用 np.fft.ifft 并取实部
单帧信号的随机建模
以下是分析单帧信号并应用随机模型的步骤。
首先,我们需要导入必要的库并设置参数。
import numpy as np
import scipy.signal as signal
import matplotlib.pyplot as plt
from scipy.io import wavfile# 读取声音文件
fs, x = wavfile.read('ocean.wav')
# 定义参数
M = 256 # 窗长
N = 256 # FFT长度(无需补零)
stocf = 0.2 # 随机因子(降采样因子)
接着,我们选取信号的一帧进行分析。
# 生成汉宁窗
w = signal.get_window('hann', M)
# 选取一帧信号(例如从第10000个样本开始)
start = 10000
x_frame = x[start:start+M] * w
然后,计算该帧的幅度谱并进行随机近似。
# 计算FFT和幅度谱(dB)
X = np.fft.fft(x_frame, N)
mx = 20 * np.log10(np.abs(X[:N//2]))
# 对幅度谱进行随机近似(降采样)
mx_res = np.maximum(mx, -200)
mx_stoc = signal.resample(mx_res, int(mx_res.size * stocf))
最后,我们可以绘制原始频谱和平滑后的近似包络进行对比。
plt.figure(figsize=(10, 4))
plt.plot(mx, 'b', label='原始幅度谱')
# 拉伸平滑包络以便在同一尺度下对比
x_axis_stoc = np.arange(0, mx_stoc.size) / stocf
plt.plot(x_axis_stoc, mx_stoc, 'g', linewidth=2, label='随机近似包络')
plt.legend()
plt.title('单帧信号的随机模型近似')
plt.show()
从随机模型合成信号
在得到平滑的幅度包络后,我们可以用它来合成新的信号。
首先,将平滑包络上采样回原始频谱长度。
# 将平滑包络上采样回原始长度 N/2
M_y = np.interp(np.arange(0, N//2), np.arange(0, mx_stoc.size)/stocf, mx_stoc)
接着,生成随机相位并重建复数频谱。
# 生成随机相位
P_y = 2 * np.pi * np.random.rand(N//2)
# 重建正频率部分的复数频谱(转换为线性幅度)
Y = np.power(10.0, M_y/20.0) * np.exp(1j * P_y)
# 根据共轭对称性构建完整频谱
Y_complete = np.concatenate((Y, np.conj(Y[-2:0:-1])))
最后,进行逆FFT得到合成信号。
# 逆FFT得到合成信号
y = np.fft.ifft(Y_complete).real
# 绘制对比
plt.figure(figsize=(10, 4))
plt.plot(x_frame, 'g', alpha=0.7, label='原始信号帧')
plt.plot(y, 'r', alpha=0.7, label='合成信号帧')
plt.legend()
plt.title('单帧信号的合成结果对比')
plt.show()
对于随机信号,时域波形不必相同,重要的是幅度谱的总体分布特征。
完整声音的分析与合成
对于一段完整的声音,我们需要逐帧进行分析与合成,并将结果保存。
以下是实现完整声音随机建模的关键步骤。
首先,定义一个函数来分析整段音频,得到所有帧的随机包络。
def stochastic_analysis(x, H, N, stocf):"""对输入信号x进行随机模型分析。x: 输入信号H: 帧移N: FFT长度stocf: 随机因子返回: 随机包络矩阵 (帧数 x 包络长度)"""num_frames = int((len(x) - N) / H) + 1env_stoc = []for k in range(num_frames):# 加窗x_frame = x[k*H : k*H+N] * np.hanning(N)# 计算幅度谱mx = 20 * np.log10(np.abs(np.fft.fft(x_frame, N)[:N//2]))mx = np.maximum(mx, -200)# 随机近似mx_stoc = signal.resample(mx, int(mx.size * stocf))env_stoc.append(mx_stoc)return np.array(env_stoc)
接着,定义一个函数从分析得到的包络矩阵合成整段音频。
def stochastic_synthesis(env_stoc, H, N, stocf, fs):"""从随机包络合成信号。env_stoc: 随机包络矩阵H: 帧移N: FFT长度stocf: 随机因子fs: 采样率返回: 合成信号y"""y = np.zeros(len(env_stoc) * H + N)for k, env_frame in enumerate(env_stoc):# 上采样包络M_y = np.interp(np.arange(0, N//2), np.arange(0, env_frame.size)/stocf, env_frame)# 生成随机相位P_y = 2 * np.pi * np.random.rand(N//2)# 重建频谱Y = np.power(10.0, M_y/20.0) * np.exp(1j * P_y)Y_complete = np.concatenate((Y, np.conj(Y[-2:0:-1])))# 逆FFT并重叠相加y_frame = np.fft.ifft(Y_complete).real * np.hanning(N)y[k*H : k*H+N] += y_framereturn y
最后,调用这些函数处理完整音频并可视化结果。
# 参数设置
H = 128
N = 2 * H # FFT长度为窗长的两倍
stocf = 0.12# 分析
env_stoc = stochastic_analysis(x, H, N, stocf)
print(f"随机包络矩阵形状: {env_stoc.shape}") # (帧数, 包络长度)# 合成
y = stochastic_synthesis(env_stoc, H, N, stocf, fs)# 保存合成音频
wavfile.write('ocean_stochastic.wav', fs, y.astype(np.float32))# 可视化随机包络(频谱图形式)
plt.figure(figsize=(12, 4))
plt.imshow(env_stoc.T, aspect='auto', origin='lower', cmap='hot')
plt.colorbar(label='幅度 (dB)')
plt.xlabel('帧索引')
plt.ylabel('包络系数索引')
plt.title('随机模型分析结果(包络序列)')
plt.show()

总结

本节课中我们一起学习了随机模型在音频信号处理中的应用。我们了解到,对于像海浪声这样的随机性信号,可以通过分析其幅度谱的平滑包络,并配合随机生成的相位来进行有效的建模与合成。我们逐步实现了从单帧分析、单帧合成到完整音频处理的全过程,并看到了合成声音在感知上与原声的相似性。这个模型是后续构建“谐波+随机”混合模型的重要基础,它将用于对谐波建模后剩余的残差信号进行描述。
052:HPR模型实现 🎵
在本节课中,我们将学习如何实现谐波加残差模型。我们将从编程的角度,探讨如何从实际音频信号中减去正弦波(谐波),从而分离出残差成分。这是将前几周讨论的正弦/谐波建模思想与随机成分概念相结合的关键步骤。
概述

上一节我们介绍了谐波加残差模型的理论框架。本节中,我们来看看如何通过编程实现这一模型,特别是如何从原始信号中精确地减去已识别的谐波成分。
核心实现步骤
以下是实现HPR模型分析一个单帧信号的核心代码逻辑概述。我们将使用SMS工具包中的函数。
首先,导入必要的库并设置分析参数。
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import get_window
import sys
sys.path.append(‘../../software/models/‘)
import utilFunctions as UF
import harmonicModel as HM
import sineModel as SM
import stft
import IPython.display as ipd
接着,读取音频文件并定义关键参数,如分析位置、窗函数大小、FFT大小、峰值检测阈值、基频搜索范围等。
inputFile = ‘../../sounds/violin-B3.wav‘
fs, x = UF.wavread(inputFile)
M = 801
N = 2048
t = -80
minf0 = 200
maxf0 = 300
f0et = 7
harmDevSlope = 0.01
nH = 60
然后,计算分析窗,并确保使用零相位窗以使信号在时域完美对齐,这对于后续的频谱减法至关重要。
start = 40000
hM1 = int(np.floor((M+1)/2))
hM2 = int(np.floor(M/2))
w = get_window(‘blackman‘, M)
x1 = x[start-hM1:start+hM2]
对选定的音频片段进行FFT变换,检测频谱峰值,并通过抛物线插值提高精度。随后,使用双路失配算法检测基频,并识别其谐波。
mX, pX = stft.stftAnal(x1, w, N)
ploc = UF.peakDetection(mX, t)
iploc, ipmag, ipphase = UF.peakInterp(mX, pX, ploc)
ipfreq = fs * iploc / N
f0 = UF.f0Twm(ipfreq, ipmag, f0et, minf0, maxf0, 0)
hfreq, hmag, hphase = HM.harmonicDetection(ipfreq, ipmag, ipphase, f0, nH, [], fs, harmDevSlope)
识别出谐波后,我们在频域合成这些谐波的主瓣。
Ns = 512
H = 128
hNs = 256
Yh = UF.genSpecSines_p(hfreq, hmag, hphase, Ns, fs)
谐波减法
现在进入本周的核心内容:从原始信号中减去这些谐波。为此,我们需要使用与合成谐波时相同的窗函数(布莱克曼-哈里斯窗)重新计算原始信号的频谱。
以下是实现减法的关键步骤:
- 从原始信号中选取与合成窗相同长度(Ns)的片段。
- 应用布莱克曼-哈里斯窗并进行零相位中心化。
- 计算该片段的频谱。
- 执行复数频谱减法:
Xr = X2 - Yh。
x2 = x[start-hNs:start+hNs] * get_window(‘blackmanharris‘, Ns*2)
fftbuffer = np.zeros(Ns)
fftbuffer[:hNs] = x2[hNs:]
fftbuffer[hNs:] = x2[:hNs]
X2 = fft.fft(fftbuffer)
Xr = X2 - Yh
运行代码后,我们可以比较原始频谱、合成谐波频谱以及相减后得到的残差频谱。残差频谱清晰地显示了去除谐波后剩余的成分。
SMS工具包中的HPR模型
SMS工具包中提供了完整的HPR模型实现。在 models 目录下的 hprModel.py 文件中,主要包含三个函数:
hprModel_anal(): 执行分析,调用谐波分析函数和正弦波减法函数。hprModel_synth(): 执行合成,将合成的谐波与残差信号相加。hprModel(): 一个封装函数,依次执行分析和合成,并绘制中间结果。
其中,正弦波减法功能在 utilFunctions 目录下的 sineSubtraction() 函数中实现。它遍历每一帧,重新读取输入信号,使用指定窗函数,合成谐波频谱,然后从输入频谱中减去,最终通过逆变换和加窗得到时域残差信号。

我们可以运行工具包中提供的示例文件 hprModel_function.py 来体验整个过程。它会分析一段萨克斯风声音,分别生成并保存谐波信号、残差信号以及两者的合成信号。通过对比聆听,可以直观感受谐波成分(乐音)与残差成分(噪声或瞬态)的区别。
总结
本节课中,我们一起学习了谐波加残差模型的编程实现。我们掌握了从单帧信号中识别谐波、在频域合成谐波、并使用精确的窗函数对齐技术从原始频谱中减去谐波以得到残差的核心步骤。虽然从信号处理原理上看并不复杂,但在编程实现上需要格外小心,确保相位对齐和复数减法的正确性,才能得到有效的实数残差信号。

下一讲,我们将在此基础上更进一步,从编程角度探讨如何将计算得到的残差成分转换为随机成分,从而实现完整的谐波加随机模型。
053:HPS模型
在本节课中,我们将学习谐波加随机模型。上一节我们介绍了谐波加残差模型,本节中我们来看看如何将残差部分进一步建模为随机成分,从而获得更灵活、更紧凑的音频表示。
概述
HPS模型是HPR模型的扩展。其核心思想是将音频信号分解为谐波成分和随机成分。分析过程与HPR模型类似,但在获得残差频谱后,我们不再直接使用它,而是通过平滑其幅度谱来获得一个随机近似。合成时,则为该平滑后的幅度谱生成随机相位,并与谐波成分相加,以重构信号。

从HPR到HPS
上一节我们介绍了HPR模型及其在SMS工具包中的实现。现在,我们更进一步,探讨HPS模型。该模型的关键改进在于对残差部分进行随机建模。
以下是HPS模型分析流程的核心步骤:
- 执行谐波分析,提取谐波成分。
- 从原始信号中减去谐波成分,得到残差信号。
- 对残差信号的幅度谱进行平滑和下采样,得到随机模型的参数(包络)。
- 在合成阶段,为平滑后的幅度包络生成随机相位,并与谐波成分相加。
这种方法的优势在于,随机近似不仅减少了重建信号所需的信息量,使表示更紧凑,而且提供了更高的灵活性,便于后续对声音进行各种处理操作。
代码实现解析

我们从上一讲的代码开始,该代码使用长笛声音进行分析。它通过FFT和基频检测进行谐波分析,合成这些谐波,然后从原始信号中减去它们,从而得到残差。
本讲的重点在于对残差进行随机近似。以下是实现这一步骤的三行核心代码:

mXr = 20 * np.log10(abs(Xr[:hN])/N) # 转换为分贝尺度
stocEnv = resample(np.maximum(-200, mXr), mXr.size*stocf) # 下采样平滑
stocEnv = resample(stocEnv, hN) # 上采样回原始尺寸
首先,将残差频谱的幅度转换为分贝(dB)尺度。然后,调用 resample 函数对这个频谱进行下采样,下采样因子由 stocf(随机因子)控制,例如0.4意味着输出数组是原频谱的40%。最后,再次上采样回原始尺寸,以便后续合成。
resample 函数的工作原理
resample 函数使用基于傅里叶变换的方法进行重采样。其过程可以概括为以下步骤:
- 对输入信号执行FFT。
- 根据目标输出长度,对频谱进行截断(下采样)或补零(上采样)。
- 对修改后的频谱执行逆FFT。
这种方法为频谱插值或下采样提供了一种简单直接的方式。
可视化随机近似效果
运行代码后,我们可以绘制原始残差频谱和平滑后的包络进行对比。当设置 stocf = 0.4 时,绿色平滑线能较好地跟随原始信号的轮廓。如果将 stocf 改为 0.1,平滑效果会更强,得到的包络线将更加平滑,这体现了随机近似对频谱的平滑作用。
SMS工具包中的HPS模型
在SMS工具包中,HPS模型的实现包含分析、合成以及帧级处理函数。分析函数在HPR模型的基础上增加了一个输入参数 stocf。分析步骤包括谐波分析、计算残差,然后调用随机模型处理残差。
需要注意的是,随机模型使用的窗口尺寸通常较小(例如两倍的跳距大小),这有助于更好地捕捉残差成分中的瞬态信息,如音符的起振过程。

合成部分则分别合成正弦波成分和随机成分,最后将它们相加。工具包中的 hpsModel 函数整合了完整的分析与合成流程,并允许通过参数调整来观察不同设置下的效果。
参数调整与实践
我们可以直接调用 hpsModel 函数并修改其默认参数来进行实验。例如,将谐波检测的阈值从 -100 dB 改为 -40 dB,系统将检测到更少的谐波,更多的能量会被归类到随机成分中,从而改变合成声音的特性。

通过调整声音文件、最小谐波持续时间等参数,我们可以深入探索HPS模型的行为和影响。
总结

本节课我们一起学习了谐波加随机模型。作为本课程中最复杂的模型,HPS模型综合了谐波分析和随机建模的思想。理解这个模型,有助于融会贯通我们讨论过的许多音频分析与合成概念。该模型通过将信号分解为确定性的谐波和随机性的残差,为实现高质量、高灵活性的音频处理提供了强大的框架。
054:声音变换1 🎵
在本节课中,我们将学习如何将之前介绍的音频分析与合成技术付诸实践,用于实际的音乐应用。我们将重点探讨声音变换,即如何修改声音的表示形式,并重新合成为一个经过修改的新声音。

短时傅里叶变换与变换框架
上一节我们介绍了多种声音分析与合成技术。本节中,我们来看看如何利用短时傅里叶变换框架来实现声音变换。
下图展示了基本的处理流程:我们逐帧选取声音片段,计算其频谱(包括幅度谱和相位谱),然后引入一个变换模块。该模块会生成新的幅度谱和相位谱,最终通过逆变换合成出经过修改的声音。

我们将重点讨论两种基于此框架的变换:滤波和变形。同时,我们也会探讨正弦模型,以及如何用它来实现频率缩放和时间缩放。
基于短时傅里叶变换的滤波 🎛️
我们之前课程中讨论过滤波。滤波可以在时域通过卷积实现,也可以在频域通过乘法实现。这里我们使用短时傅里叶变换,在频谱域通过乘法应用滤波。
具体操作是:将输入声音每一帧的复数频谱与滤波器的频率响应(同样包含幅度和相位)相乘。严格来说,幅度谱是相乘,而相位谱是相加。
以下公式展示了这一思想。输出声音 Y[l] 的复数频谱,等于滤波器频率响应 H 与输入声音第 l 帧频谱 X[l] 的乘积:


Y[l] = H * X[l]
其中,幅度部分相乘:|Y[l]| = |H| * |X[l]|;相位部分相加:∠Y[l] = ∠H + ∠X[l]。
然后对 Y[l] 执行逆傅里叶变换,即可得到输出声音。
让我们看一个实例。左上角是一个很短的管弦乐声音片段。


我们计算了它的幅度谱(红色曲线)和相位谱。

同时,我们有一个滤波器的幅度谱(滤波器通常可以设为零相位,因此其相位影响常被忽略)。在应用时,我们将滤波器幅度谱与输入声音的幅度谱相乘(由于采用对数dB刻度,实际操作是相加)。右侧的形状就是两者相加后的结果,即修改后的频谱。相位谱基本保持不变。最后通过逆FFT得到输出声音 y[n]。

现在来看一个完整声音的例子。顶部是之前听过的完整管弦乐声音的声谱图。中间是我们要应用的滤波器幅度形状。我们对输入声音的每一帧幅度谱都乘以这个形状(在对数域是相加),得到了底部的声谱图。听一下结果声音,可以明显感觉到声音变柔和了,因为我们只让大约1000Hz附近的频率通过(这是一个带通滤波器),其他频率都被大幅衰减,整体能量减少了很多。
基于短时傅里叶变换的声音变形 🌀

接下来,我们使用短时傅里叶变换进行另一种变换:声音变形。
在变形中,我们从声音 x1 开始,对其进行短时傅里叶变换分析-再合成。在每一帧,我们将其幅度谱与另一个随时间变化的声音 x2 的幅度谱相乘。换句话说,我们对声音 x2 进行并行的短时傅里叶变换分析,但只使用其幅度谱,然后将其“塑造”到 x1 上。
以下公式与滤波概念相似,但这里的 x2 是时变的,拥有自己的帧索引 l:


Y[l] = |X2[l]| * |X1[l]| * exp(j * ∠X1[l])
我们只使用 x1 的相位谱。

让我们看一个实践例子。同样是之前的管弦乐声音及其频谱。现在,黑色曲线是另一个声音的平滑幅度谱,它会像管弦乐声音一样随时间变化。我们将这两个频谱在对数域相加,创建出一组新的频谱,然后进行逆FFT。

现在对这个时变声音进行操作。


顶部是管弦乐声。我们来听一下另一个声音 x2,这是一段男性语音。中间的声谱图是这段语音 x2 的平滑版本,它没有 x2 的细节,只有其大致形状。我们将这两个声谱图逐帧相加,得到了底部的声谱图 My。它既不是 x1,也不是 x2,而是两者的混合体。听听这个修改后的版本,它明显带有管弦乐的特征,但由于其幅度谱的整体形状来自语音,我们基本上能听懂语音内容。可以这样理解:仿佛管弦乐队正在通过这位男性的声道演奏,从而复现了语音的音素纹理。
基于正弦模型的变换 ⏱️🎚️
现在让我们转到之前已经介绍过的正弦模型。
我们再次通过峰值检测和正弦跟踪进行正弦模型分析。但这里有一个小变化:在进行变换时,我们不直接使用原始的相位值,因为相位非常敏感,变换时很难处理。因此,我们改为从频率值重新生成相位。
具体流程是:我们修改分析得到的正弦波的幅度和频率值,然后基于修改后的频率值重新生成相位。这些新的幅度、频率和再生相位将作为合成的输入,合成过程与之前相同。


我们对正弦分析输出的具体操作如下:
- 频率缩放:新的频率是输入频率乘以一个缩放因子
SF。 - 时间缩放:读取输入频率的速率由一个时间缩放因子控制,这允许我们减慢或加快读取声音的速度,从而实现时间伸缩。
- 幅度缩放:由于幅度在对数域(dB)操作,新的幅度是输入幅度加上一个幅度缩放因子。
- 相位再生:相位不是来自原始声音。它们从一个初始相位(可以是0或随机值)开始,在每一帧根据新的频率值累加生成,从而实现相位的自动展开。
f_out[l] = SF * f_in[l]
A_out[l] = A_scale + A_in[l] (dB域)
φ_out[l] = φ_out[l-1] + 2π * f_out[l] * hop_size / fs

例如,这是一个时间缩放包络。横轴是输入时间,纵轴是输出时间。通过改变输入时间的读取位置,我们修改了声音的长度。
让我们看一个时间缩放的具体例子。我们从一个马林巴琴声音开始,并用正弦模型分析它。顶部的声谱图显示了原始信号及其正弦轨迹。听一下原始声音。下面的声谱图是变换后的正弦轨迹。我们以不同的速度读取它们,因此时间轴差异很大:原始声音长约2秒,输出声音长约3秒,被拉伸了1.5倍。让我们听听再合成后的修改声音。我们改变了持续时间,并且不是恒定缩放,而是通过扭曲时间信息,使所有音符起始点间隔相似,从而让声音变得更长。这里的可能性是无限的,我们可以按任何方式调整这种映射关系。
对于另一种变换——应用于正弦波的频率缩放——我们同样需要一个时变的缩放包络。横轴是时间,纵轴是缩放因子。这意味着在声音开始时,我们将所有正弦波的频率乘以0.8;在声音结束时,乘以1.2。因此频率会从较低开始,最终变得较高。


让我们看一个具体声音的例子。这是我们已经听过的管弦乐声音,图中显示了它的正弦分析(原始声谱图和正弦轨迹)。下方是根据上述曲线变换后的正弦轨迹。可以看到,在开头正弦波更密集(频率更低),在结尾则更高(因为乘以了1.2)。让我们听听这个修改后的管弦乐声音。当然,这听起来不自然,因为通常不会这样做,并且会产生一些失真,但通过应用这类技术并进行优化,可以获得一些好的效果。
关于如何对声音应用这类变换的细节信息不多,但如果你查阅维基百科,可以找到一些关于音效、均衡、以及如何应用时间缩放和音高修改的初步参考资料。

总结
本节课中,我们一起学习了如何利用短时傅里叶变换和正弦模型进行特定的声音变换,包括滤波、变形、频率缩放和时间缩放。在下一节理论课中,我们将继续探讨其他已介绍的模型,并学习如何将它们用于其他类型的变换。



下节课再见。
055:声音转换-2


欢迎回到音乐应用中的音频信号处理课程。在上一节理论课中,我们开始讨论声音转换,特别是如何使用短时傅里叶变换和正弦模型来操控声音。在本节理论课中,我们将继续这个话题。
我们将探讨谐波加残差模型及其如何用于转换谐波频率,然后介绍谐波加随机模型及其如何用于时间拉伸和声音融合操作。这里的“融合”与使用短时傅里叶变换所能实现的效果有很大不同。当然,这些只是示例操作,这些模型的实际应用可能性要广泛得多。
谐波加残差模型

让我们从谐波加残差模型开始。这是我们之前见过的框图:从输入声音中,我们分析频谱,找到峰值,然后找到基频和谐波,并从原始信号中减去这些谐波。我们需要再次计算原始信号的频谱,以便从一个可以正确减去这些谐波的频谱中推导出残差。
转换操作应用于谐波的频率和振幅上,我们不对残差应用任何转换。为了对残差进行转换,为其建立一个模型是有意义的,这就是我们接下来要介绍的模型——谐波加随机模型。

在谐波上的转换,与正弦模型类似,只应用于频率和振幅,相位被忽略。我们将在转换后重新合成相位。这样做的主要原因是相位对转换非常敏感,处理起来非常困难,因此最好在转换后重新生成它们,效果通常不错。
转换后,我们可以将这些新的谐波与残差信号相加。由于残差信号不包含任何确定性或谐波信息,它们会很好地融合,从而产生一个具有原始残差特性但谐波已被修改的声音。
以下是可应用于这些谐波频率的几种常见转换类型:

- 频率平移:将所有频率乘以一个给定的因子。这对应于移调。例如,乘以2对应升高一个八度,乘以0.5对应降低一个八度。公式表示为:
f_new = f_original * scaling_factor。 - 频率偏移:将所有频率加上一个常数因子。这会将所有谐波偏移一个固定的量,从而可能产生非谐波系列的声音。
- 频率拉伸:应用一个依赖于谐波次数的频率变化。公式表示为:
f_new = f0 * (harmonic_number ^ scaling_factor),其中f0是基频。这意味着较高的谐波比较低的谐波改变得更多,可以创造出有趣的效果。
谐波加随机模型

现在,让我们转向谐波加随机模型。它与前一个模型非常相似,但现在转换既应用于残差信号的建模,也应用于谐波分量。

我们有一个随机近似,因此可以很容易地进行转换,因为这个模型大大简化了残差世界,并且在操控方面非常灵活。转换后,我们得到随机分量和谐波正弦波的频率、振幅,然后可以合成两者并将它们相加。
实际操作与我们在正弦模型中介绍的非常相似。现在我们引入对随机分量的类似转换。谐波的频率可以被一个因子缩放,读取这些频率值的时间也可以改变,以实现时间缩放操作。我们有一个缩放函数。振幅的处理方式相同,随机分量的处理方式也相同。相位是生成的,从初始相位开始,然后加上我们想要生成的每个谐波的频率,从而生成瞬时相位。
声音融合操作
现在,让我们讨论使用这种谐波加随机模型进行声音融合。这里我们简化了框图:有两个声音,x1和x2。我们将要做的是对这两种表示进行插值。

从x1中,我们获得谐波的频率、振幅和残差的随机近似。对声音2也做同样的事情。然后,我们对这两组函数进行插值:我们插值谐波的频率和幅度,并插值残差的随机包络。当然,我们可以通过生成随机分量和正弦分量来重新合成输出声音。

总结
在本节课中,我们一起学习了声音转换的进一步内容。我们探讨了谐波加残差模型如何用于频率转换,并引入了更强大的谐波加随机模型,该模型允许我们对谐波和随机残差都进行灵活的操作,例如时间拉伸和声音融合。
正如课程所强调的,这是一个非常应用的课题,需要通过编程和实践演示来更好地理解和掌握。鼓励大家尝试这些技术,培养自己对什么有效、什么无效的直觉。


感谢你的关注,我们下节课再见。
056:基于STFT的形态变换 🎵➡️🌊



在本节课中,我们将学习如何使用短时傅里叶变换(STFT)对两个声音进行形态变换(Morphing)。我们将以海浪声和男性语音为例,通过SMS工具,将它们的频谱特征进行融合,创造出新的声音效果。



课程概述


在理论课程中,我们讨论了用于声音变换的各种模型。本节演示课将从实践角度出发,主要利用SMS工具来理解和实现基于STFT的形态变换。我们将把海浪声作为基础声音,并将语音的频谱特征叠加其上。



声音分析与参数设置
首先,我们需要分析并理解将要进行变换的两个声音。我们将使用SMS工具中的分析模块。
分析海浪声
海浪声将作为基础声音(x1),我们主要会利用它的相位谱。其频谱的总体特征比细节更重要,因此参数设置可以相对宽松。
以下是分析海浪声的建议参数:
- 窗函数: 汉宁窗(Hann window)。这是一个简单的窗函数,便于实现50%的重叠。
- 窗大小: 512。无需过大,因为不追求高频率分辨率。
- 零相位窗: 不需要。使用奇数大小的窗也没有问题。
- 跳跃大小: 128。设置较小的跳跃大小以保留海浪声的时间信息。
- 零填充: 不需要。
使用这些参数计算STFT后,我们会看到一个类似波浪的、随机类型的频谱形状。
分析语音
接下来分析男性语音。我们不需要与海浪声使用完全相同的参数,但跳跃大小必须保持一致,以便后续进行逐帧的形态变换。
同样,由于我们只关心语音频谱的总体形状,而非精确的谐波细节,频率分辨率无需过高。因此,我们可以沿用相似的参数设置。
在语音的幅度谱图中,可以清晰地看到时间信息以及频谱形状,例如不同元音对应的共振峰结构。
使用SMS变换工具进行形态变换
完成声音分析后,我们进入SMS工具的变换接口目录,运行主界面文件 TranssGI。在打开的界面中,选择 “STFT Morph” 变换。
默认情况下,界面已加载海浪声和男性语音。现在,我们需要设置变换参数。
设置变换参数
核心的形态变换参数有两个:
- 平滑因子: 此参数用于在应用语音频谱前对其进行平滑处理,以捕获其总体特征,避免过于细节的波动。其值范围在0到1之间。
0表示最大程度的平滑(仅保留最粗略的形状),1表示不进行平滑。默认值0.5是一个不错的起点。 - 平衡因子: 此参数控制最终频谱中两个声音的混合比例。其值范围也在0到1之间。
0表示结果完全接近海浪声;1表示结果主要由平滑后的语音幅度谱决定。
执行变换并试听
例如,将平衡因子设置为0.8(即80%的语音频谱特征,20%的海浪声频谱特征),然后应用变换。
变换完成后,界面会显示:
- 原始海浪声(输入声音
x) - 海浪声的幅度谱图
- 形态变换后的频谱图(结合了海浪声和平滑后语音的频谱)
- 合成后的新声音
试听合成声音,你会听到清晰的语音内容,但由于相位信息完全来自海浪声,语音听起来像是一种“耳语”或气声,并混合了海浪的背景质感。
为了听到更多海浪声的特性,可以将平衡因子调低,例如设为0.2(20%语音,80%海浪)。此时,频谱图更接近海浪声,但仔细聆听,仍能辨识出语音的轮廓。有趣的是,一旦你事先知道这是语音,大脑会努力去解析它;但如果第一次听,可能完全无法理解。

总结



本节课中,我们一起学习了基于STFT的形态变换。我们首先使用SMS工具分析了海浪声和语音的频谱特性,然后利用SMS变换工具包中的STFT Morph接口,成功将这两个来自Freesound的声音进行了融合。通过调整平滑因子和平衡因子,我们可以获得各种不同的声音混合效果,这很好地展示了此类技术和工具的潜力。



在接下来的课程中,我们将转向正弦模型、谐波加残差模型以及随机模型,来探索其他类型的声音变换。
057:时间缩放 ⏱️






在本节课中,我们将要学习如何改变音频的时长,即时间缩放。我们将探讨两种不同的方法:一种是使用Audacity软件中的现成算法,另一种是使用基于正弦模型的SMS工具包进行更精细的控制。
上一节我们介绍了声音的变形技术,本节中我们来看看如何独立地改变声音的时长而不影响其音高。
概述:时间缩放的目标
时间缩放的目标是修改一段声音片段的长度。最直接的方法是改变播放速度,但这会同时改变声音的频率和时长。更理想的方法是只改变时长,同时保持声音原有的频率和音质不变。
使用Audacity进行时间缩放
以下是使用Audacity软件进行时间缩放的基本步骤。
1. 改变播放速度(简单方法)
这种方法直接修改样本的读取速度,会同时影响时长和频率。
- 在“效果”菜单中,选择“改变速度”。
- 例如,将速度提高30%,声音会变短、音调变高。
2. 使用滑动时间缩放/音高变换(高级方法)
这个算法基于正弦模型,可以独立控制时长和音高变化,质量更好。
- 在“效果”菜单中,选择“滑动时间缩放/音高变换”。
- 该算法允许设置起始和结束的时长变化百分比。
- 例如,可以设置起始时减慢22%,结束时加快30%。算法会基于正弦模型重新合成声音,在改变时长的同时保持音高不变。
使用SMS工具包进行时间缩放
现在,让我们转向我们自己的实现,使用基于正弦模型的SMS工具包来进行更自定义的时间缩放。
上一节我们使用了Audacity的黑盒算法,本节我们将手动设置参数,获得完全的控制权。
1. 正弦模型分析与再合成
首先,我们需要对原始声音进行正弦模型分析,并验证再合成的效果。
- 选择分析参数:对于钢琴这类谐波声音,需要设置合适的参数来区分频谱峰值。
- 窗函数类型:汉明窗
- 窗大小:例如1000个样本
- 峰值阈值:例如-100 dB
- 最大正弦波数量:例如200
- 执行分析与再合成:使用这些参数对原始钢琴音进行分析,并生成再合成的声音。通过对比原始音和再合成音,可以验证模型的有效性。
2. 应用时间缩放变换
在确认模型分析有效后,我们可以应用时间缩放变换。
- 设置频率缩放曲线:要只改变时间而不改变音高,需将频率缩放因子始终设置为1。
- 设置时间缩放曲线:通过定义时间映射点来控制时长的变化。
- 均匀拉伸:例如,将终点时间映射设置为2,即可将声音拉伸为原长的两倍。
- 非线性拉伸:可以设置多个控制点来实现复杂的时长变化。例如,起点映射为0,中点映射为0.5(加速),终点映射为1.3(减速),从而实现先加速后减速的效果。
总结
本节课中我们一起学习了时间缩放的两种实现方式。
- 我们首先使用Audacity软件演示了简单的速度改变和基于正弦模型的高级时间缩放算法。
- 接着,我们使用SMS工具包的正弦模型,通过手动设置分析参数和时间映射曲线,实现了自定义的、高质量的时间缩放变换。


正弦模型在时间缩放操作中展现出巨大潜力,这是一种被广泛应用于多种场景的、非常有趣的变换。




下一节课,我们将讨论如何改变声音的音高,这是与改变时长相辅相成的一项操作。
058:音高变化 🎵
在本节课中,我们将学习如何使用谐波加随机模型来改变声音的音高。我们将通过一个具体的萨克斯风声音示例,演示如何分析声音并应用音高变换。


概述

上一节我们讨论了如何使用正弦模型进行时间缩放。本节中,我们来看看如何使用谐波加随机模型来改变声音的频率,即实现音高变化。我们将学习如何分析声音以确定合适的模型参数,然后应用频率缩放和频率拉伸两种变换。
声音分析与参数设定
为了使用谐波加随机模型,我们需要先理解目标声音的特性。我们以一个萨克斯风声音为例。
以下是分析声音并确定模型参数的步骤:
- 观察频谱图:通过观察声音的频谱图,特别是基频(第一谐波)的变化,我们可以了解声音中基频的范围。这有助于我们确定合适的分析窗长。
- 确定频率范围:在频谱图中,我们可以找到最低和最高的基频值。例如,在这个萨克斯风声音中,最低音大约在450赫兹,最高音大约在650赫兹。
- 计算窗长:窗长需要足够长,以准确分析最低的频率成分。一个常用的经验法则是:窗长(采样点数) = (采样率 / 最低频率) × 主瓣宽度系数。对于布莱克曼窗,其主瓣宽度约为6个采样点。因此,窗长可计算为
(44100 / 400) * 6 ≈ 661。我们选择一个奇数长度,例如661。
使用SMS工具进行分析
现在,我们使用SMS工具中的谐波加随机模型来分析声音。
以下是需要设置的关键参数及其解释:
- 窗函数:
blackman(布莱克曼窗,旁瓣较低)。 - 窗长:
661(基于上述计算)。 - FFT大小:
2048(使用零填充以获得更平滑的频谱)。 - 峰值检测阈值:
-100(dB,用于检测谐波)。 - 最小正弦波持续时间:
0.1(秒)。 - 最大谐波数量:
100(一个足够覆盖所有可能谐波的安全值)。 - 基频搜索范围:
400到650(赫兹,基于频谱图观察)。 - 基频检测误差阈值:
7(允许的微小偏差)。 - 随机成分近似因子:
0.4(控制随机成分的平滑度)。
分析完成后,我们可以分别聆听合成出的谐波成分、随机成分以及它们的和,以验证分析的质量。
应用音高变换
分析完成后,我们就可以进入变换阶段。我们主要关注两种频率变换:频率缩放和频率拉伸。
1. 频率缩放
频率缩放会按一个比例因子整体平移所有谐波的频率,从而改变音高,同时(如果启用频谱包络保持)尽量保持声音的频谱形状或“音色”。
核心操作:
- 设置一个缩放因子,例如
0.8,表示将所有频率降低到原来的80%。 - 启用
频谱包络保持(设置为1),这会使变换后的声音听起来更自然。
代码示例(概念):
变换后频率 = 原始频率 × 缩放因子
2. 频率拉伸
频率拉伸会对谐波系列施加一个指数因子,使各次谐波不再保持等间距,从而产生非谐波频谱,改变声音的质感(例如,使其听起来更“金属化”)。
核心操作:
- 设置一个拉伸因子,例如从
1.0(起始,无拉伸)变化到1.1(结束)。 - 这会导致高频谐波比低频谐波移动得更多,破坏了谐波关系。
公式描述(概念):
第k次谐波变换后频率 = 基频 × k ^ 拉伸因子
当拉伸因子为1时,上式退变为标准的谐波关系。当拉伸因子大于1时,高频谐波间距会变大。
实践与探索
在演示中,我们首先应用了频率缩放(因子0.8),听到了音高降低但音色基本保持的萨克斯风声音。接着,我们应用了频率拉伸(因子从1.0线性增加到1.1),听到了声音逐渐从谐波变为非谐波、质感发生改变的效果。
你可以自由尝试这些参数:
- 尝试不同的频率缩放因子(如1.2来升高音高)。
- 尝试不同的频率拉伸曲线。
- 结合时间缩放参数,同时改变声音的时长和音高。
总结
本节课中,我们一起学习了如何使用谐波加随机模型进行音高变化。我们首先通过Sonic Visualizer等工具分析声音,确定模型参数;然后使用SMS工具进行分析与合成;最后,重点实践了频率缩放和频率拉伸两种变换,并理解了频谱包络保持参数对声音自然度的影响。音高变换是声音处理中的强大工具,谐波模型因其对声音结构的理解而在此类任务中表现出色。

在接下来的课程中,我们将探讨如何使用谐波加随机模型进行另一种有趣的变换——声音变形,即在两个声音的表示之间进行插值。
059:采用HPS的变形技术 🎵





在本节课中,我们将学习如何使用谐波加随机模型进行声音变形。我们将通过分析小提琴和女高音两种声音,并演示如何在这两种声音的参数之间进行插值,来实现不同于短时傅里叶变换的变形效果。
上一节我们介绍了使用短时傅里叶变换进行变形的方法,本节中我们来看看如何使用谐波加随机模型实现另一种形式的变形。该模型允许我们对声音的谐波频率、幅度以及随机成分进行独立的插值控制,从而提供更精细和多样化的变形可能性。
声音分析 🎻
要进行变形,首先需要对参与变形的每个声音进行良好的分析。我们将使用SMS工具中的谐波加随机模型。
以下是分析小提琴声音的步骤:
- 选择布莱克曼窗,因其旁瓣较低,适合分析稳态音符。
- 确定窗长。公式为:
窗长 = 6 * 采样率 / 基频。小提琴音符B3的基频约为246 Hz,采样率为44100 Hz,因此计算得到窗长约为1075个样本。 - 设置FFT大小为4096,以获得足够的零填充。
- 设置幅度阈值为-100 dB。
- 设置正弦轨迹最小持续时间为0.5秒,以获得更长的轨迹便于插值。
- 设置最大谐波数量为100,以捕获尽可能多的谐波成分。
- 设置基频检测范围为200至300 Hz。
- 设置基频检测误差阈值为7%。
- 设置频率偏差为0.1。
- 设置随机成分近似因子为0.8,以保留较高质量的残差信号。
完成分析后,可以分别试听重构的正弦成分和随机成分,以验证分析质量。
接下来,以相似的步骤分析女高音声音(E4,基频约330 Hz):
- 窗长计算为:
6 * 44100 / 330 ≈ 801个样本。 - 基频检测范围设置为250至400 Hz。
- 其他参数(如窗函数、FFT大小、谐波数量、随机因子等)保持与小提琴分析时一致,以确保后续插值的一致性。
执行变形 🧬
完成两个声音的分析后,即可进行变形处理。
以下是执行变形的具体步骤:
- 打开SMS工具的变换界面,选择HPS变形选项。
- 载入已分析好的两个声音(通常为默认设置)。
- 根据之前的分析结果,分别设置两个声音的分析参数,包括窗长、FFT大小、阈值、谐波数量等。
- 在变形界面中,我们可以独立控制三组参数的插值:
- 谐波频率:控制音高的演变。
- 谐波幅度:控制音色或共振峰结构的演变。
- 随机成分:控制噪声或气息声成分的演变。
- 通过设置插值包络,可以定义这些参数如何随时间从源声音过渡到目标声音。例如:
- 让频率始终保持为小提琴的频率,而幅度从女高音过渡到小提琴,则会得到具有女高音音色的小提琴音高效果。
- 让所有参数都从声音A(0时刻)线性过渡到声音B(1时刻),则会实现一个完整的声音变形过程。
- 应用变形并试听结果。可以观察到,由于模型参数是独立插值的,变形过程可能产生非常平滑或具有特定艺术效果的过渡,这与基于STFT的频谱直接混合有本质区别。



总结 📝





本节课中我们一起学习了使用谐波加随机模型进行声音变形。关键点在于,该模型将声音分解为可控的谐波和随机成分,允许我们对这些成分的参数进行独立和精细的插值。这使得变形技术不再局限于简单的频谱混合,而是可以创造出在音高、音色和纹理上都可控的中间声音。虽然都称为“变形”,但所选用的模型从根本上决定了技术所能提供的可能性和最终的艺术效果。
060:短时傅里叶变换转换 🎵

在本节课中,我们将学习如何利用短时傅里叶变换对音频信号进行转换。我们将重点介绍两种基本操作:频域滤波和声音形态混合。通过理解这些核心概念和代码实现,你将能够开始对音频的频谱特性进行修改和创造。
概述
上一节我们介绍了频谱建模方法的基本概念。本节中,我们来看看如何利用短时傅里叶变换对音频信号进行具体的转换操作。我们将通过分析代码,学习如何对单帧音频进行滤波,以及如何将两个声音的频谱进行混合。
频域滤波操作
首先,我们探讨如何对音频信号的一个帧进行滤波。其核心思想是在对声音进行STFT分析后,在频域对其幅度谱进行修改,然后再通过逆傅里叶变换合成新的声音。
以下是实现该过程的步骤:
- 读取音频信号:将音频文件加载到数组
X中。 - 定义窗函数:创建一个与FFT长度
N相同的窗函数(如汉宁窗)。 - 选取音频片段:从原始音频中截取一帧,长度为512个样本。
- 设计频域滤波器:在分贝尺度上定义一个平滑的形状作为滤波器。例如,可以使用一个倒置的汉宁窗形状,并将其乘以-60分贝以产生衰减效果。公式表示为:
filter = -60 * hanning_window - 计算频谱:计算选定音频片段的幅度谱
M和相位谱P。 - 应用滤波器:将滤波器形状叠加到原始幅度谱的特定频段上。例如,将长度为30个样本的滤波器以第40个频率样本为中心进行应用。
- 逆变换与合成:将修改后的幅度谱与原始相位谱结合,进行逆傅里叶变换,得到处理后的时域信号
y。
运行此代码后,可以观察到原始信号的波形、滤波器形状、原始幅度谱、修改后的幅度谱以及最终输出信号的波形。通过对比原始信号(绿色)和滤波后信号(蓝色),可以清晰地看到特定频率成分被衰减的效果。
声音形态混合
接下来,我们讨论一个更有趣的操作:声音形态混合。这个操作旨在将一个声音的频谱特征平滑地融合到另一个声音中。
以下是实现两个声音单帧混合的步骤:
- 准备两个声音:例如,一个雨声
sound1和一个女高音sound2。 - 选取对齐的帧:从两个声音中分别截取长度相同的音频片段。
- 计算频谱:分别计算两个片段的幅度谱
MX1和MX2。 - 平滑处理:对第二个声音的幅度谱
MX2进行平滑处理,以获取其大致的频谱包络。这可以通过下采样再上采样插值来实现,平滑因子控制平滑程度。 - 线性混合:将第一个声音的幅度谱与平滑后的第二个声音幅度谱进行线性组合。混合公式为:
M = (1 - balance) * MX1 + balance * MX2_smooth
其中,balance参数控制第二个声音的贡献比例。 - 合成新声音:使用混合后的幅度谱
M和第一个声音的相位谱P1进行逆傅里叶变换,生成混合后的音频帧y。
通过调整 balance 参数(例如从0.7改为0.1),可以观察到输出频谱更接近雨声(balance 值小)或更接近人声(balance 值大)。这直观地展示了频谱插值的效果。
完整音频文件的处理
上述操作是针对单帧的演示。在实际的SMS工具包中,stftTransformations.py 文件提供了可应用于整个音频文件的函数。
stft_filtering函数:实现了对整个音频信号进行频域滤波的流程,其核心是一个循环,对每一帧重复执行分析、滤波和合成操作。stft_morph函数:实现了对整个音频信号进行形态混合的流程。它接收两个声音信号、窗函数参数、平滑因子和平衡因子,并对所有帧执行混合操作。- 主程序接口:一个名为
main的函数集成了读取音频、STFT分析、形态混合、绘制频谱图(声谱图)以及输出最终音频文件的功能。通过调用此接口并调整参数(如balance=0.9),可以生成一个更接近人声特性的、基于海洋声音时长的新音频文件。

总结
本节课中我们一起学习了利用短时傅里叶变换进行音频转换的基础方法。我们掌握了两种核心操作:
- 频域滤波:通过修改特定频段的幅度谱来衰减或增强声音的某些成分。
- 声音形态混合:通过线性插值两个声音的幅度谱,创造出一个兼具两者频谱特征的新声音。

这些是基于频谱工具最简单但非常实用的转换类型,为许多音频应用提供了创意可能。在下一讲中,我们将探讨如何在正弦模型中使用SMS工具实现类似的转换。
061:正弦变换
在本节课中,我们将学习如何对正弦模型表示的声音进行变换。我们将通过分析SMS工具包中的代码,理解如何操作正弦分量的幅度和频率来实现时间拉伸和频率变换。
概述
在之前的课程中,我们介绍了频谱建模策略。本节我们将聚焦于正弦模型,探讨如何通过修改其分析参数(幅度和频率)来变换声音。我们将看到,大多数变换操作需要重新生成相位信息,而非使用原始相位。
正弦模型的相位处理

上一节我们介绍了正弦模型的整体框架。本节中我们来看看相位在合成过程中的作用。
正弦模型的合成通常不直接使用分析得到的原始相位。这是因为当改变声音的时间或频率时,相位关系会随之改变,原始相位不再适用。合成器会根据频率轨迹重新生成相位,以保持相位连续性。
以下是一个演示脚本,它分析了钢琴声音,并分别使用原始相位和重新生成的相位进行合成。
# 导入必要的包
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import wavfile
# ... 其他导入# 分析参数设置
window = ‘hamming’
M = 2048
N = 4096
t = -80
minSineDur = 0.1
maxnSines = 20
freqDevOffset = 10
freqDevSlope = 0.001# 读取音频文件
fs, x = wavfile.read(‘piano.wav’)# 调用正弦模型分析函数
hM1, hM2 = int(np.floor((M+1)/2)), int(np.floor(M/2))
x1 = np.append(np.zeros(hM2), x)
x1 = np.append(x1, np.zeros(hM2))
plt.plot(x1)
plt.show()# 分析得到频率、幅度和相位
tfreq, tmag, tphase = SM.sineModelAnal(x1, fs, window, N, H, t, maxnSines, minSineDur, freqDevOffset, freqDevSlope)# 使用原始相位进行合成
y = SM.sineModelSynth(tfreq, tmag, tphase, M, H, fs)
运行上述代码后,对比原始声音和用原始相位合成的声音,两者非常接近。然而,如果我们传入一个空数组代替相位参数,让合成器重新生成相位,结果会如何?
# 使用重新生成的相位进行合成(传入空相位数组)
y_regenerated = SM.sineModelSynth(tfreq, tmag, np.array([]), M, H, fs)
此时,合成声音的波形形状与原始声音差异显著,尽管听觉上可能仍相似。这表明相位信息对波形形态影响很大,但在正弦模型中,为了适应变换,我们选择重新生成相位。
正弦模型合成代码解析
为了理解相位是如何重新生成的,我们需要查看sineModelSynth函数中的相关代码。
# 在合成主循环中
if phase.size > 0:# 使用输入的相位synthesis_phase = phase[:, frame]
else:# 重新生成相位:基于前一帧相位和当前频率计算synthesis_phase = previous_phase + 2 * np.pi * freq[:, frame] * H / fs
代码逻辑是:如果输入了有效的相位数组,则直接使用;否则,算法从一个随机初始相位开始,后续每一帧的相位根据当前帧的频率和帧移H计算得到,从而保证变换后的相位连续性。
正弦变换的类型与实现
理解了相位处理后,我们来看看具体的变换操作。SMS工具包中的正弦变换主要分为两类。
以下是两种核心变换的实现思路:
1. 时间缩放
此变换通过操作正弦模型的帧序列来改变声音的时长。
- 原理:它接受频率和幅度轨迹,通过插入或跳过帧来延长或缩短声音。
- 核心操作:生成新的频率和幅度矩阵,在其中重复或删除原始帧,以匹配目标时长,同时保持帧率不变。
- 公式/代码概念:
new_frames = interpolate_or_repeat(original_frames, time_scaling_factor)
2. 频率缩放
此变换通过缩放每个正弦分量的频率来改变声音的音高。
- 原理:它接受频率轨迹,并将每一帧中所有正弦分量的频率乘以一个缩放因子。
- 核心操作:缩放因子可以随时间变化,实现对声音的连续变调。
- 公式:
f_transformed(t) = f_original(t) * scaling_factor(t)
变换流程集成
SMS工具包提供了一个集成的函数来执行完整的分析-变换-合成流程。

以下是该流程的步骤:
- 分析:使用默认参数对输入声音进行正弦模型分析,并绘制频谱等中间数据。
- 变换:调用上述的时间缩放或频率缩放函数,对分析得到的频率和幅度数据进行修改。
- 合成:使用修改后的数据(及重新生成的相位)进行合成,并输出音频文件。
调用该函数后,我们可以得到并对比原始声音谱图、重新合成的声音谱图以及变换后(如频率被缩放、时间被拉伸)的声音谱图。
总结
本节课中我们一起学习了正弦模型下的声音变换技术。我们了解到,由于变换操作会破坏原始的相位关系,因此合成时需要基于频率轨迹重新生成相位。我们探讨了两种基本的正弦变换:时间缩放(通过插帧改变时长)和频率缩放(通过乘因子改变音高),并通过代码了解了其实现原理。这些变换为我们提供了灵活的声音处理能力,能够创造出时间拉伸和音高变化的效果。

下一讲,我们将继续探讨另一种模型——谐波加随机模型——下的变换技术,特别是声音渐变(Morphing),并分析其实现代码。
062:HPST变换 🎵
在本节课中,我们将学习谐波加随机模型中最复杂的变换操作。我们将探讨如何对谐波成分和随机成分分别进行变换,并深入了解声音形态变换的实现原理。
上一节我们介绍了频谱建模的基本概念,本节中我们来看看如何对谐波加随机模型进行具体的变换操作。
概述

谐波加随机模型是我们在本课程中涵盖的最复杂的模型。其核心思想是将声音分解为谐波成分和随机成分,然后分别对它们进行变换处理。这种模型允许我们进行多种高级的声音操作,包括频率缩放、时间拉伸以及声音形态变换。
谐波变换
谐波变换专门处理模型中的谐波成分。在SMS工具包中,相关的代码位于 harmonicTransformations.py 文件中。该文件实现了三种主要的变换操作。
以下是三种核心的谐波变换:
- 频率缩放:对谐波频率进行统一的乘法缩放,实现声音的移调。
- 公式:
f_scaled = f_original * scale_factor
- 公式:
- 频率拉伸:以非谐波的方式拉伸频率,改变声音的频谱结构。
- 公式:
f_stretched = f_original * (stretch_factor ** harmonic_number)
- 公式:
- 音色保持:在变换频率的同时,调整谐波幅度以保持原始声音的频谱包络形状,从而保留音色。
组合变换与形态变换
除了单独的谐波变换,还有针对谐波与随机成分组合的变换。相关代码位于 HTransformations.py 文件中。
以下是两种主要的组合变换:
- 时间缩放:同时对谐波的频率、幅度以及随机包络进行时间维度的拉伸或压缩。其核心是通过插值或删减帧来实现。
- 声音形态变换:这是最复杂的变换。它需要分析两个声音,然后在它们的谐波参数(频率、幅度)和随机包络之间进行插值,从而生成一个介于两个原始声音之间的新声音。
形态变换的核心逻辑是寻找两个声音在同一帧中都存在的谐波,然后对这些共有的谐波参数进行插值计算。这要求两个声音的帧数、谐波数量以及随机包络的断点数量最好保持一致。
代码实现与使用
在SMS工具包中,形态变换的功能由 H_morph 函数封装。该函数内部主要分为两个部分:
- 分析函数:对两个输入声音进行分析,提取它们的谐波与随机参数。
- 变换与合成函数:对分析得到的参数进行插值变换,然后重新合成出新的声音。

你可以直接运行 H_morph.py 文件来体验整个流程,也可以单独导入文件并调用这两个函数进行更灵活的操作。
总结

本节课中我们一起学习了谐波加随机模型的变换技术。作为最复杂的模型,它能对符合谐波特性的声音进行丰富而有趣的变换。关键在于进行正确的分析,并妥善控制这些更复杂的声学表示。如果声音满足该模型的前提条件,我们能实现的声音处理可能性将非常巨大。
063:音频特征 🎵
在本节课中,我们将学习如何描述声音,具体介绍一系列可以从音频信号中提取的音频特征。我们将从单帧频谱中提取的特征开始,然后讨论需要多帧分析的特征。

上一周我们讨论了频谱建模技术在声音变换方面的应用。本节中,我们将转向另一类应用——声音描述。
音频特征提取概述
音频特征提取通常基于频谱分析。以下是其通用处理流程框图:


我们从信号 x 开始,用窗函数截取一段,然后通过快速傅里叶变换计算其频谱,得到幅度谱和相位谱。正是从这里,我们提取出音频信号的相关特征,这些特征对于特定应用至关重要。

音频特征分类
我们将使用开源音频分析库 Essentia 来提取音频特征。Essentia 包含大量用于音乐描述的算法。以下是其主要特征类别:
- 频谱描述符:直接从频谱中提取的特征,例如频带能量、梅尔频带、MFCC、LPC等。
- 时域描述符:直接从时域信号中提取的特征,例如时长、过零率。
- 音高相关描述符:通常也使用频谱技术,例如音高显著度、HPCP(谐波音级轮廓)、调性识别。
- 节奏描述符:用于识别节拍、速度、起始点等节奏相关信息。
- 音效描述符:描述声音的标准特性,例如起音、能量。
- 高级描述符:试图描述更高层次的音乐概念,例如可舞性、声音复杂度。
大多数特征是按帧计算的。接下来,我们来看一些具体例子。
单帧音频特征示例
我们将首先讨论与能量相关的描述符,然后是与频谱形状相关的描述符,最后介绍两个与音高信息相关的特征。
能量相关特征
音频帧的能量可以从幅度谱计算,也可以直接从时域信号计算。
- 能量:通过对幅度谱的平方求和计算。
能量 = Σ |X[k]|² - 均方根:能量的修正版本,是能量算术平均值的平方根。
RMS = sqrt( (1/N) * Σ |x[n]|² ) - 史蒂文斯幂定律(响度):一种试图模拟感知强度的度量,通过对3000赫兹纯音的声压级进行感知归一化来实现。这是一个简单的响度近似度量。

下图展示了从一段钢琴音中计算的这三个能量相关特征。它们高度相关,但各有不同,可用于测量声音的整体能量或响度。

频谱形状特征

上一节我们介绍了能量特征,本节中我们来看看如何描述声音的频谱形状。
-
频谱质心:表示频谱的“质量中心”,感知上与声音的明亮度印象高度相关。它计算为信号中频率的加权平均值。
频谱质心 = (Σ k * |X[k]|) / (Σ |X[k]|)
下图展示了一段男性语音及其频谱质心随时间的变化。无声或噪声部分质心较高,浊音部分质心较低。![]()
-
梅尔频率倒谱系数:以更复杂的方式表征频谱形状。MFCC 是幅度谱的一种表示,通过对在对数梅尔尺度上的幅度谱进行余弦变换得到。
梅尔尺度近似于人耳的频率分辨率,将感知频率与实际测量频率联系起来,使频谱特征更贴近我们的听觉感受。
处理流程为:幅度谱 -> 梅尔尺度滤波器组 -> 取对数 -> 离散余弦变换 -> MFCC系数。
通常使用前12个系数(第0个系数与能量相关,常被忽略)。第一个系数描述频谱的整体形状,更高阶的系数描述更细微的变化。MFCC 提供了频谱形状的紧凑向量表示。![]()
音高相关特征
现在,让我们探讨一些与声音音高信息相关的特征。
-
音高显著度:衡量信号中音高声音存在程度的度量。Essentia 中的实现从频谱峰值开始,通过求和每个可能基频的谐波加权能量,计算每个峰值处的显著度。通常取每帧中的最大显著度,来表示该帧存在良好音高声音的概率。
下图展示了一个中国管弦乐片段的音高显著度,可以可视化地估计每帧中音高声音的存在。![]()

-
色度特征与HPCP:色度是音乐感知和理论中的一个概念,代表了音高组织的内在循环性——不同八度中相同音高的音符具有相同的色度。谐波音级轮廓(HPCP)是色度特征的一种具体实现,它是信号能量在一组预定义音级上的分布。
分析将频谱折叠到一个八度内(通常分为12个半音),并计算每个音级(如C, C#, D等)的能量。
下图展示了大提琴双音片段的HPCP分析,可以清楚地看到持续存在的稳定音高(A和D)。![]()
多帧音频特征

前面介绍的特征主要基于单帧分析。现在,我们转向需要分析多帧序列(即声音片段)才能计算的特征。
以下是三个例子:
-
音频分割与起始点检测:通过计算衡量频率内容变化的频谱特征来实现。
- 频谱通量:比较连续频谱的差异,对其绝对值(L1范数)求和,得到频谱变化度量,可用于检测声音变化点。
频谱通量 = Σ | |X_t[k]| - |X_{t-1}[k]| | - 高频内容:检测高频成分的含量,并与前一帧比较。声音起始(起音)通常伴随着高频成分的增加,因此可用于起始点检测。
下图展示了同一段语音的频谱通量和归一化的起始点检测函数。
![]()
- 频谱通量:比较连续频谱的差异,对其绝对值(L1范数)求和,得到频谱变化度量,可用于检测声音变化点。
-
主要音高:在更复杂的声音中测量主要旋律音高。该算法从幅度谱和峰值开始,计算音高显著度,然后识别不同的音高轮廓,并从中选择最突出的一个作为主要旋律。下图展示了从一段复调音乐中提取的主要旋律线(绿色)。
-
特征统计聚合:大多数帧级特征可以在完整录音或声音片段上聚合,通过计算统计量来获得特征更整体的行为视图。通常计算矩:
- 一阶矩(均值):特征的平均值。
- 二阶矩(方差):特征的变化程度。
- 三阶矩(偏度):特征分布偏离对称(高斯)分布的程度。
通过这三个统计量,可以很好地了解特定音频特征在片段中的行为。
总结与参考资料
本节课中,我们一起学习了声音描述的应用,讨论了几种可以从音频信号中提取并用于表征声音的音频特征。我们涵盖了能量、频谱形状、音高相关的单帧特征,以及需要多帧分析的起始点检测、主要音高和统计聚合特征。
在下一讲中,我们将把这个特征提取的概念扩展到对声音集合进行表征。
参考资料:
- Essentia 库文档及其引用的原始论文是了解这些描述符的起点。
- 维基百科上关于频谱质心、MFCC、响度、HPCP、起始点检测和矩等主题有大量信息。
- 本讲座中展示图表的代码可在 SMS Tools 的 GitHub 页面上找到。

内容翻译整理自巴塞罗那庞培法布拉大学《音乐应用中的音频信号处理》课程第09讲。
064:声音与音乐描述 🎵
在本节课中,我们将学习如何描述单个声音以及声音集合。我们将介绍音乐信息平面的概念,并区分通用声音描述与音乐特定描述之间的差异。课程将涵盖声音集合的描述方法,包括相似性度量、聚类和分类,并简要探讨音乐描述中更高级、更复杂的挑战。
音乐信息平面 🎼

上一节我们介绍了音频特征,本节中我们来看看如何将这些特征置于一个更宏观的框架中,以理解音乐描述的不同层次。音乐信息平面是一个帮助我们理解音乐描述在不同语境下含义的框架。
它考虑了从物理层面到认知层面的不同抽象级别。
- 物理层面:这是最低的层面,涉及声音的基本物理属性。例如,频率、持续时间、频谱(及其特征如质心)和强度。
- 感官层面:这一层面将物理属性转化为人类的感知。例如,音高(对应频率)、感知时长(对应持续时间)、音色(对应频谱)和响度(对应强度)。
- 感知层面:这一层面涉及与音乐概念更相关的感知主题。例如,音高相关的连续或同时的音程(音符)、时间的结构化(如节拍)、音色的可识别特征(如频谱包络)以及音乐的力度。
- 形式化层面:这一层面以更形式化的方式讨论音乐概念。例如,旋律、调性、节奏型、速度、节拍、乐器或人声的识别,以及发音方式。
- 认知层面:这是最高层面,涉及人类主观的音乐聆听体验。例如,情感、音乐风格或语义概念。这一层面整合了所有较低层次的描述,并且具有很强的主观性和文化性。
我们将主要关注声音的低层次描述,目标是达到对用户应用有足够相关性的描述级别。
通用声音描述 🔊
如果我们想以通用的方式描述声音(例如在免费音效库中找到的声音),可以将音频特征分组。
以下是音频特征的主要类别:
- 音色相关特征:例如频谱质心、梅尔频率倒谱系数、高频内容等。
- 动态相关特征:主要指响度和录音电平。
- 音高相关特征:例如基频或音高显著性。
- 形态特征:描述声音随时间演变的特征,例如包络、起始速率等。
我们已经学习了许多这样的描述符。现在有趣的是,我们可以基于这些特征来分析声音集合。

描述声音集合 📊
上一节我们介绍了单个声音的特征,本节中我们来看看如何描述声音的集合。描述声音集合有多种方法,我们将聚焦于三个基本概念。
以下是描述声音集合的三个核心步骤:
- 相似性:这是最基本的概念。我们需要度量声音之间的相似性,才能形成集合或对其进行分组。
- 聚类:一旦能够度量相似性,我们就可以根据某些标准对声音进行聚类或分组。
- 分类:如果我们已知一些用于描述特定声音组的现有标签(类别),就可以对声音进行分类,为其分配类别。

声音的空间表示与距离度量
在我们的上下文中,声音集合可以用图表表示。我们将一个声音视为一组音频特征值。为了简化,假设一个声音只用两个特征表示,那么它可以被显示为二维空间中的一个点。

例如,水平轴可以是频谱质心的均值,垂直轴可以是梅尔频率倒谱系数第二个系数的均值。通过分析小提琴、长笛和小号的声音,我们可以看到这些乐器根据这两个特征在空间中形成了不同的区域。
为了在这个空间中操作,最基本的是测量声音(点)之间的距离。欧几里得距离是衡量多维空间中两点距离的最简单方法之一。
给定两个声音 P 和 Q,每个声音有 D 个特征,欧几里得距离公式为:
distance(P, Q) = sqrt( sum_{i=1}^{D} (P_i - Q_i)^2 )
在只有两个特征的二维空间中,这就是连接两点的线段的长度。
聚类声音:K均值算法
知道了如何测量距离,我们就可以对声音进行聚类。K均值是一种聚类算法。
如果我们给算法指定期望的聚类数量 K,它将创建聚类并返回每个聚类的均值(中心点)。

K均值聚类旨在将 N 个观测值(即声音)划分为 K 个聚类,每个观测值属于具有最近均值的聚类。这个均值充当了该聚类的原型。
寻找这些均值(聚类)的问题在计算上是困难的(NP难),但存在高效的经验算法能快速收敛到局部最优解。
其目标是最小化以下公式的总和,为每个聚类 k 找到均值 μ_k:
minimize sum_{k=1}^{K} sum_{x in S_k} ||x - μ_k||^2
其中 S_k 是第 k 个聚类中的点集。
通过迭代优化,算法可以从随机初始化的聚类中心开始,逐步调整,最终将声音聚类到特征相似的分组中。
分类声音:K近邻算法
最后,我们来讨论声音集合的分类。这意味着我们已知一些声音类别,目标是给定一个新声音,将其分类到已知的类别之一。
K近邻 分类器是用于此类任务的算法。

KNN实现的规则是:通过将查询声音分配给其 K 个最近邻居中最常见的类别来对其进行分类。
这个过程可以概括为:从查询声音(新声音)开始,计算它与所有已标记样本的欧几里得距离,选取距离最近的 K 个邻居,然后根据这 K 个邻居的类别进行多数投票,将得票最多的类别分配给查询声音。
这是一种简单但有效的对声音(或任何其他类型数据)进行分类的方法。
音乐描述 🎶
现在,如果我们转向音乐声音,即音乐片段的录音,需要分析的特征应该更具体,并且与音乐及有意义的概念更相关。
让我们尝试定义一些与音乐相关的特征或描述符类别。

以下是音乐相关的描述符类别:
- 音色相关描述符:例如乐器特征化、配器特征化,甚至音乐录音的混音方式。
- 旋律与和声相关描述符:包括乐句、动机、主音。对于非西方音乐传统,如印度音乐的拉格或土耳其音乐的玛卡姆。
- 节奏相关描述符:包括节奏型、速度、节拍。在许多音乐传统中,节拍循环的概念是思考节奏的重要方式。
- 结构描述符:描述音乐作品的结构,定义其段落或乐章。
这些描述通常不能仅通过音频分析获得。我们通常从音频特征开始,但必须开发结合多个特征的模型来捕捉每个概念的本质。这超出了本课程的范围,是一个活跃的研究领域。
音乐集合描述 🎧
描述音乐集合则更加困难。如果我们想执行与音乐相关的任务,音乐集合的描述可能非常复杂。这被称为音乐信息检索领域。

我们希望能够自动对音乐作品进行分类,并执行诸如推荐音乐或查找相关音乐等任务。
我们为声音讨论的概念在这里也适用,但必须进行调整。相似性仍然是基本概念,但我们可以从不同方面来定义它,例如节奏相似性、配器相似性、旋律相似性、和声相似性、结构相似性等。这些相似性显然不是简单的欧几里得距离,我们需要开发更复杂的相似性度量。
然后,我们可以根据不同的标准对音乐作品进行分类和聚类,例如按流派、风格、艺术家或音乐传统所属的学派进行分类。
这远远超出了本课程所能涵盖的内容,但它是我们所讨论内容的一个自然延续,也是一个引人入胜的话题。

延伸阅读与总结 📚
鉴于这是一个非常开放的研究问题,参考文献通常来自研究论文,大量研究被归类在音乐信息检索 领域。维基百科上关于音乐信息检索的条目是一个很好的起点。

对于我们所讨论的更具体的内容,您可以查看维基百科上关于欧几里得距离、K均值聚类或K近邻分类的特定条目。当然,这些只是众多聚类和分类策略中的两个例子,机器学习领域带来了许多执行此类任务的新可能性。
本节课中,我们开启了一扇通往庞大研究领域的大门,该领域旨在自动描述和组织大量的声音和音乐录音集合。我们只是介绍了一些可以用于开始研究此主题的基本概念和具体方法。

在编程讲座中,我们将展示一些如何实际进行这些操作的示例,但显然我们无法完全展现这个研究领域的全貌。希望您能对其有所了解。下节课我们将展示更多关于所有这些内容的演示和实际例子。
065:声音描述符提取演示
在本节课中,我们将学习如何使用 Sonic Visualizer 软件来提取和分析音频信号的特征描述符。我们将通过实际操作,演示如何计算起始点、梅尔频率倒谱系数和色度特征,从而直观地理解这些在理论课中介绍过的概念。



🎵 打开音频文件


首先,我们打开一个熟悉的钢琴音频文件。


🔌 安装与使用插件
Sonic Visualizer 的强大功能依赖于各种插件。我们可以从其官网下载。
以下是获取插件的步骤:
- 访问 Sonic Visualizer 网站。
- 进入 “Bump Plugins” 菜单。
- 从列表中选择并下载所需的插件集合。


这些插件大多用于分析声音的低层和中层特征。我已经下载了来自开发方 Queen Mary 大学的一些核心插件。
为了进行分析,我们需要先创建一个空白的图层来显示特征。

⏱️ 提取起始点特征
上一节我们介绍了如何准备分析环境,本节中我们来看看如何提取时间域特征,例如起始点。


在 “Transform” 菜单下,选择 “Analysis by Category”,里面分类列出了不同类型的特征。我们进入 “Time-Domain Features” 下的 “Onsets”。
这里有一个起始点检测插件,由 Queen Mary 大学开发。其参数包括使用的特征类型、窗口大小和跳跃大小等。在理论课中,我们提到过高频内容特征,这里我们选择它,并暂时使用默认参数。
点击确定后,软件会高效地计算出起始点函数。结果显示,该函数清晰地反映了信号的动态变化,峰值通常出现在音符的起振时刻。


如果我们想精确定位起始点的位置,可以使用同一插件组中的峰值拾取功能。
以下是设置峰值拾取参数的步骤:
- 再次选择高频内容作为特征。
- 调整灵敏度参数。首次分析时,算法可能将衰减部分误判为起始点。
- 将灵敏度调低(例如设为19%)后重新分析。
- 此时,检测到的起始点就准确地对应了每个音符的开始。

🗣️ 分析梅尔频率倒谱系数

接下来,我们打开一个语音文件,并分析其 MFCC 特征。


我们创建一个新图层,然后在 “Analysis by Category” 中选择 “Low-Level Features” 下的 “Mel-Frequency Coefficients”。

这个插件同样由 Queen Mary 大学提供。在音乐分析中,我们通常使用比语音分析更多的系数。对于语音,12个系数通常足够。我们可以选择是否显示第0个系数,它大致代表了信号的响度能量。我们保留显示它,并使用默认的频谱分析参数。

计算完成后,我们可以看到所有系数随时间变化的曲线。展开视图,还能看到具体的数值。第一个系数代表声音的响度,后续系数则描述了频谱包络的形状。虽然直观理解这些系数并不容易,但它们是语音识别和音色分析中非常紧凑且有效的描述符。


🎻 提取色度特征
最后,我们分析一个双弦演奏的大提琴声音,这个声音适合分析其和声色彩。
我们再次创建一个空白图层。这次,在 “Visualization” 分类下,选择 “Chromagram” 进行分析。
色度分析插件有几个关键参数:分析的频率范围、基准调音频率(用于在平均律上对齐滤波器)、每八度的划分数量(西方音乐通常为12个半音)。为了获得精细的频率分辨率,我们需要使用较大的窗口尺寸和跳跃尺寸。我们使用默认参数进行计算。
分析结果以色度图形式呈现。图中,横轴是时间,纵轴是12个音高类别(C, C#, D, ..., B)。我们可以看到,持续的低音 D 在整个过程中都很明显。同时,上方声部在 A 弦上演奏的一个小音阶也清晰可辨。色度分析将不同八度的相同音高类别折叠在一起,因此高八度的 D 音也被归入 D 类别中。这种表示方法对于研究和弦、调性等音乐属性非常有帮助。


📝 总结
本节课中,我们一起学习了如何使用 Sonic Visualizer 软件来提取和分析音频特征描述符。我们通过三个实例,实际操作了起始点检测、MFCC 提取和色度特征分析,将理论课的概念应用于具体的音频分析中。
这些特征不仅帮助我们直观地理解声音属性,更是进行声音比较、分类和更高级定量分析的基础。你可以访问 Sonic Visualizer 官网的插件页面,下载更多功能强大的分析工具。

下节演示课,我们将介绍与音频合成相关的另一个演示。谢谢大家。
066:Freesound 高级功能指南 🎵


在本节课中,我们将深入学习 Freesound 平台的一些高级功能。Freesound 是一个庞大的声音共享社区,拥有超过 22.4 万个声音样本。除了基本搜索,我们将探索如何利用标签、地理位置信息、声音分析描述符和混音关系来更精确地查找和管理声音资源,这对于完成本周的作业将非常有帮助。



浏览与搜索声音 🔍
上一节我们介绍了 Freesound 的基本概念,本节中我们来看看如何高效地浏览和搜索声音。





通过标签云搜索




Freesound 鼓励用户为上传的声音添加描述性标签。以下是利用标签云进行精确搜索的步骤:

- 在“声音”页面,找到“标签”区域。这里显示了用户最常使用的标签。
- 点击一个标签,例如
multisample,可以找到所有使用该标签的声音。 - 为了进一步筛选,可以点击另一个标签旁边的加号(如
string instrument),将两个标签组合搜索。 - 此时,标签云会更新,显示与当前组合标签最常一起出现的其他标签。字体越大,表示该标签的使用频率越高。通过这种方式,可以快速定位到特定类型的声音,例如小提琴或大提琴的采样。
通过地理位置标签搜索

并非所有声音都有地理位置标签,但对于那些在特定地点录制的声音,这是一个有用的筛选维度。

- 点击“浏览地理位置标签”选项。
- 页面会显示一张世界地图,声音密集的区域会以簇的形式显示,并标注数量。
- 放大特定区域(例如巴塞罗那),可以查看更精确的声源分布,甚至定位到具体街道或校园。




使用高级搜索筛选器
当基本关键词搜索(如 clarinet)返回结果过多时,可以使用高级搜索功能。




- 在搜索结果页面,点击“高级搜索”。
- 可以设置多种筛选条件,例如:
- 时长:设置为 0 到 5 秒,以找到短促的单音。
- 标签:添加
single-note标签,进一步筛选出单音符样本。
- 结合使用这些筛选器,可以高效地找到符合特定需求的声音素材。



基于内容的声音相似性查找 🎼




找到感兴趣的声音后,可以利用 Freesound 基于音频内容分析的“查找相似声音”功能。



- 在任意声音的详情页,点击“查找相似声音”。
- 系统会基于音频特征(而非文本标签),在整个数据库中寻找音色相近的声音。例如,一个单簧管的声音可能会找到其他单簧管音色,但也可能找到音色相似的瀑布声。
这个功能背后的技术,是通过 Essentia 音频分析库为每个声音计算出一系列音频描述符。这些描述符包括 MFCC(梅尔频率倒谱系数)、频谱质心等特征的统计值(如均值、方差、直方图)。相似度是通过比较这些描述符数据之间的距离来计算的。

核心概念公式:
声音相似度 = f(描述符向量A, 描述符向量B)
其中,描述符向量 包含了如 {MFCC均值, 频谱质心方差, ...} 等统计信息。




管理声音关系:混音树 🌳

Freesound 允许用户明确标注声音之间的衍生关系,这对于跟踪声音素材的来源和修改历史非常重要。


- 在用户个人主页或声音详情页,可以找到“混音树”图标。
- 点击后,会显示该声音的源文件以及由它衍生出的其他声音。例如,一个“语音残差”声音可能源自某个“语音”文件,而该“语音”文件又可能是从另一位用户的原始录音中剪辑、处理得来的。
- 明确标注这些关系,有助于构建清晰的声音素材谱系,尊重原作者的贡献,并方便后续的协作与使用。


总结



本节课中我们一起学习了 Freesound 平台的一系列高级功能。



我们首先探讨了如何超越简单关键词搜索,利用标签云和地理位置标签进行高效浏览。接着,我们介绍了如何使用高级搜索筛选器精确查找特定属性(如时长、标签)的声音。然后,我们深入了解了基于音频内容分析的“查找相似声音”功能,其核心是利用 Essentia 库计算的音频描述符进行相似度匹配。最后,我们学习了如何通过混音树来管理和追溯声音之间的衍生关系。

掌握这些功能将极大地提升你在 Freesound 上查找、筛选和管理声音素材的效率,并为本周需要处理音频分析描述符的作业打下坚实基础。Freesound 的价值源于社区的贡献,希望你在利用这些资源的同时,也能积极上传和标注优质的声音,丰富这个共享生态。
067:Essentia 入门简介 🎵
在本节课中,我们将学习如何使用 Essentia 库进行音频分析和音乐描述。Essentia 是一个强大的开源 C++ 库,专门用于音频分析和基于音频的音乐信息检索。我们将介绍如何安装 Essentia,并通过一个简单的 Python 示例来提取音频特征。
概述
Essentia 是一个用于音频分析和音乐信息检索的开源 C++ 库。它提供了大量算法和工具,可以方便地提取音频特征,并支持 Python 包装器,使得在 Python 环境中调用 C++ 库变得简单高效。本节课将指导您完成 Essentia 的安装过程,并通过一个计算 MFCC(梅尔频率倒谱系数)的示例来展示其基本用法。

安装 Essentia

上一节我们介绍了 Essentia 的基本概念,本节中我们来看看如何安装它。

首先,访问 Essentia 的官方网站 essentia.upf.edu,您可以在其中找到文档、下载链接以及其他相关信息。Essentia 的源代码托管在 GitHub 上。


以下是安装步骤:


- 克隆仓库:使用 Git 克隆 Essentia 的源代码仓库到本地计算机。
git clone https://github.com/MTG/essentia.git


- 安装依赖:Essentia 依赖于一些其他库,如 FFTW 和 FFmpeg。在 Ubuntu 系统中,可以使用以下命令安装这些依赖。
sudo apt-get install build-essential libyaml-dev libfftw3-dev libavcodec-dev libavformat-dev libavutil-dev libavresample-dev python-dev libsamplerate0-dev libtag1-dev

-
配置和编译:进入 Essentia 目录,运行配置和编译命令。
cd essentia ./waf configure ./waf -
安装:编译完成后,运行安装命令。
sudo ./waf install
安装完成后,您就可以在 Python 中使用 Essentia 了。
使用 Essentia 提取 MFCC 特征
现在我们已经安装了 Essentia,本节中我们将通过一个 Python 示例来学习如何使用它提取 MFCC 特征。
以下是一个计算音频文件 MFCC 特征的 Python 脚本的核心部分:
import essentia.standard as es
import numpy as np# 初始化算法
frameSize = 1024
hopSize = 512
spectrum = es.Spectrum(size=frameSize)
window = es.Windowing(size=frameSize, type='hann')
mfcc = es.MFCC(numberCoefficients=13, inputSize=frameSize/2 + 1)# 加载音频文件
loader = es.MonoLoader(filename='your_audio_file.wav', sampleRate=44100)
audio = loader()# 初始化输出数组
mfccs = []# 逐帧处理音频
for frame in es.FrameGenerator(audio, frameSize=frameSize, hopSize=hopSize):windowed_frame = window(frame)spec = spectrum(windowed_frame)mel_coeffs, mel_bands = mfcc(spec)mfccs.append(mel_coeffs)# 转换为 NumPy 数组以便进一步处理
mfccs = np.array(mfccs)
这段代码首先初始化了频谱计算、加窗和 MFCC 提取算法。然后,它加载一个音频文件,并逐帧计算 MFCC 系数,最后将结果存储在一个 NumPy 数组中。
Essentia 的提取器工具
除了直接使用算法,Essentia 还提供了一些预构建的“提取器”,可以一次性计算多种音频特征。
例如,streaming_extractor_freesound 提取器被 Freesound 项目用于为其音频库中的每个声音提取低级特征。您可以在 Essentia 的 build/src/examples 目录中找到这些已编译的提取器二进制文件。
使用方式如下:
./streaming_extractor_freesound input_audio.wav output_features.json
该命令会生成一个包含所有分析结果的 JSON 文件。

总结

本节课中我们一起学习了 Essentia 音频处理库的基础知识。我们介绍了 Essentia 的用途和特点,完成了在 Linux 系统上的安装步骤,并通过一个 Python 示例演示了如何提取 MFCC 特征。此外,我们还了解了 Essentia 提供的预构建特征提取器工具,这些工具可以高效地计算复杂的音频描述符。Essentia 功能强大,虽然深入使用可能涉及较多细节,但其 Python 接口为初学者提供了一个易于上手的入口。在接下来的课程中,我们将探索如何利用这些特征进行更深入的声音描述和分类。
068:Freesound API 使用指南 🎵

在本节课中,我们将学习如何通过编程方式访问和分析声音集合,这是进行声音描述、比较和分类的基础。为此,我们将重点介绍 Freesound API,这是一个强大的工具,允许我们以程序化的方式查询和获取Freesound数据库中的声音及其分析数据。
什么是API?🔌



API(应用程序编程接口)本质上是一种协议。对于Web API而言,它基于HTTP协议,允许我们通过发送特定的HTTP请求来查询数据库中的内容,而无需使用网页界面。这为我们以编程方式处理声音数据提供了可能。
探索Freesound API文档 📖
要学习如何使用API,我们首先需要访问Freesound网站的“开发”部分。文档中详细说明了如何访问不同类型的信息资源。
例如,在“搜索资源”部分,我们可以了解到如何进行文本搜索。这意味着我们可以通过编程方式执行标准的文本查询,并应用各种过滤器,从而访问Freesound中所有带有文本标签的信息。
但API的功能远不止于此。我们还可以基于声音的内容进行分析搜索,例如,根据其低层级声学特征进行筛选。

以下是一个API调用的示例URL,它展示了如何获取特定声音ID的完整信息摘要:
https://freesound.org/apiv2/sounds/<sound_id>/
此调用会返回一个JSON对象,包含该声音的核心信息(如ID、URL、文件名、用户标签、描述等)、技术细节(如比特率、时长)以及分析数据(如频谱特征统计信息)。
使用Python客户端库 🐍
为了更便捷地使用API,Freesound社区提供了多种语言的客户端库。我们将使用 Python客户端库。这个库封装了API调用,简化了我们的工作流程。
首先,我们需要获取该库的代码,并申请一个个人专用的 API密钥。这个密钥是访问API的凭证,需要妥善保管。
库的核心文件是 freesound.py,它定义了一系列用于执行API查询的类和方法。此外,examples.py 文件提供了实用的代码示例,展示了如何获取声音信息、分析数据或查找相似声音。

以下是一个基础的使用示例代码框架:
import freesound# 使用你的API密钥初始化客户端
client = freesound.FreesoundClient()
client.set_token(“<你的API密钥>”)# 获取特定ID的声音
sound = client.get_sound(12345)
print(sound.name)# 获取该声音的低层级特征分析结果
analysis = sound.get_analysis()
mfccs = analysis.lowlevel.mfcc.mean
print(mfccs)


实战:下载并分析声音集合 💻

在本次课程的作业中,我们提供了一个实用的Python脚本,用于简化从Freesound下载声音集合的过程。
该脚本的核心是一个 download_sounds_freesound 函数。你可以通过指定查询文本、标签、时长范围、API密钥、存储目录、结果数量和文件格式来调用它。

例如,以下代码会下载符合条件的前20个“bassoon”声音:
import sound_download
API_KEY = “<你的API密钥>”
sound_download.download_sounds_freesound(query=“bassoon”,tag=“multisample”,duration=(0, 5),api_key=API_KEY,output_dir=“./tmp”,top_n_results=20,feature_ext=“.mp3”)
运行后,脚本会在指定目录下为每种乐器创建子文件夹,并下载对应的MP3音频文件以及包含低层级特征描述(如频谱质心、MFCC等)的JSON文件。
可视化与分析 📊

下载声音集合后,我们可以使用作业中提供的另一个分析脚本进行探索。其中一个函数可以将所有声音的指定特征在二维平面上可视化。
例如,我们可以绘制所有声音的“频谱质心”与“第一个MFCC系数”的散点图:
import sound_analysis as sa
# 指定存储声音的目录和要绘制的特征索引
sa.plot_features(“./tmp”, descriptors=[0, 12])
生成的图表可能显示,低音乐器(如巴松管)的频谱质心集中分布在较低区域,而小提琴和吉他的数据点则可能混合在一起,这反映了它们在音色和音高上的某些相似性与差异性。通过结合听觉判断,我们可以更好地理解特征分布背后的声学含义。
总结与资源 📚


本节课我们一起学习了Freesound API的核心用途和基本操作方法。我们了解到,API是程序化访问大规模声音数据库的关键。通过Python客户端库,我们可以方便地搜索、下载声音并获取其丰富的分析特征,进而为声音的分类、聚类和描述等任务奠定基础。
核心资源链接:
- 申请API密钥:
https://freesound.org/apiv2/apply/ - API官方文档:
https://freesound.org/docs/api/ - Python客户端库:
https://github.com/MTG/freesound-python

要复现本节课的演示,请参考本周的课程作业,其中包含了完整的 sound_download.py 和 sound_analysis.py 脚本。掌握这些工具,你将能够在实际编程中灵活运用本周所学的声学分析与音乐描述知识。
069:超越音频处理 🎵

在本节课中,我们将探讨音乐应用音频信号处理课程之外的广阔领域。我们将回顾本课程已涉及但可深入研究的主题,并介绍一些虽不严格属于音频信号处理范畴,却能有效补充音乐信息分析的其他方法与研究方向。

上一节我们完成了对核心音频处理技术的系统学习,本节中我们来看看如何将视野拓展到更广阔的领域。
音频信号处理的深入方向 🔍
我们首先探讨在本课程基础上,音频信号处理领域内值得进一步深入研究的几个方向。以下是部分关键主题:
- 正弦波检测与估计:本课程对此问题进行了初步简化处理。实际上,存在许多更先进的技术和方法,可用于在复杂信号中更精确地检测和估计正弦波的参数。
- 部分(谐波)追踪:我们使用了一种相对简单的技术来追踪音频信号中的谐波或部分。该技术可进一步复杂化,发展出能更精确追踪部分随时间变化行为的更复杂方法。
- 瞬态建模:我们未涉及此主题。在分析和合成声音时,瞬态部分(如音符的起音或信号的急剧变化)通常更难建模。存在多种专门的方法来识别和处理这些瞬态,独立于可使用我们所学技术分析的稳态或更稳定的声音部分。
- 多分辨率分析:快速傅里叶变换的一个主要局限是其线性频率分辨率。然而,人类的听觉感知系统并非线性。因此,出现了许多考虑多分辨率的方法,旨在开发具有不同频率范围不同分辨率的频谱分析技术。实际上,这也可以通过FFT实现,本周的实验环节提供了相关探索选项。
- 残差分析:我们对信号的残差进行了相对简单的分析,使用了一个简单的随机模型来近似。在近似这个残差方面,无论是从感知角度还是声源角度,都有很大的发展空间,可以为特定类型的信号开发专门的模型。
- 合成方法:我们使用逆FFT高效地合成了正弦波和滤波噪声。此外,还存在许多其他合成正弦波和噪声的替代方法提案。
- 完全不同的建模方法:我们采用的是基于频谱的建模方法。除此之外,还存在大量其他可用于音乐应用的建模方法,例如基于物理建模的方法,或使用傅里叶变换之外的其他变换的方法。
总而言之,音频信号处理在音乐应用领域内容非常丰富。本课程提供的只是一个特定且有力的视角,但该领域远不止于此。
超越音频信号处理的其他领域 🌐

现在,让我们探讨一些真正超越传统音频信号处理范畴的主题。当我们希望从工程角度研究音乐时,所涉及的远不止音频信号本身。音乐是更复杂的现象,我们可以获取不同类型的数据来研究它。
例如,仅关注数据类型和信号,我们还可以分析乐谱或歌曲歌词,研究表演录像中的手势,并应用一些信号处理相关的方法来分析这些类型的信号。此外,我们还可以获得大量文本类型的数据、上下文或社区信息(如对音乐的评论和描述),从中提取有意义的音乐信息。
在分析这类数据的方法论方面,除了信号处理,数学和工程学的其他一些领域也非常有用:
- 统计分析:这是一个庞大的领域,我们已略有提及。其中许多有趣的方法可用于表征音乐等复杂现象。
- 模式分析:我们将许多此类数据视为某种时间信息,模式提取是一个非常重要的概念。在该领域,已出现许多通用的模式识别方法,可应用于这些音乐信号。
- 机器学习:我们上周介绍了用于声音聚类或分类的机器学习。该领域已发展出非常复杂的方法论,可应用于广泛的问题和数据,实现从数据中自动提取知识。
- 语义技术(语义网):这是一个较新的研究领域,为理解数据、提取数据信息带来了新方法。
- 网络分析:当拥有一个数据语料库并希望发现数据点之间的关系时,网络分析可以为我们提供关于这些关系结构的深刻见解。
- 本体论:这是计算机科学中源自语义网方法的一个领域,为结构化数据提供了很好的方式。开发本体、构建描述特定知识领域(此处是音乐)内实体关系的结构化方法,能极大地帮助我们描述和分析这些数据。
- 以用户为中心的研究:音乐不仅关乎数据,还涉及人与关系。因此,各种以用户为中心的研究(如感知、认知研究,以及关于人与音乐交互的实验)能为我们理解音乐提供深刻见解。
- 人机交互:界面研究也是一个成果丰硕的领域。我们与乐器、与音乐互动需要界面,研究这些界面及其与我们理解或使用音乐方式的关系,同样有助于我们理解和建模音乐信号。
以上只是一些重点介绍,希望能让你对相关领域有更广阔的视野。显然,我们的研究领域可以扩展到许多不同的方向、学科和方法论,这些领域周围存在着大量非常有趣的主题。
延伸阅读与参考资料 📚
由于涉及内容广泛,参考资料非常丰富。以下是一些起点:

- 在MTG的SMS页面上,可以找到一些信息,特别是延伸我们已学的分析合成技术、介绍其他声音建模、参数化和合成方法的文章链接。
- 最近发布的《音乐信息研究路线图》(链接已在课程中提供)也是一个很好的资源,概述了与获取音乐信息相关的各个研究方向,并汇集了所有这些不同的新研究领域。
- 关于统计学、机器学习、语义分析(也称为知识表示与推理)等方法论领域,可以在维基百科上找到大量参考资料。
- 对于用户研究类型,可以查阅音乐心理学、人机交互等领域的资料,其中不乏以音乐为核心但从不同视角进行研究的文献。
本节课是本周的一次讲座,旨在开阔我们在音频信号处理内部及外部的视野。希望这能为你指明新的探索方向和途径。


本节课中,我们一起回顾了音频信号处理中可深入探索的主题,并了解了分析音乐信息时可用的其他跨学科方法,从而对音乐技术领域形成了更全面的认识。
070:课程回顾与总结 🎵
在本节课中,我们将回顾整个课程的核心框架与关键概念。我们将从频谱图这一基本思想出发,逐一重温每周的主题,包括声音频谱、正弦与谐波模型、残差与随机成分,以及声音的变换与描述。最后,我们将简要展望音频信号处理领域的未来方向。

课程核心框架 🧩
上一节我们介绍了课程的整体目标,本节中我们来看看贯穿整个课程的基本分析框架。
我们所有分析与处理技术的起点,是声音的频谱表示。虽然声音的基本表示是时域波形,但对我们而言,其用处有限。因此,我们首先将声音转换到频域,即获取其频谱。这一做法有其感知上的动机,因为人类的听觉过程本身就在进行类似的分析。
我们的基本工作流程可以概括为以下步骤:
- 从输入信号
x[n]开始。 - 通过快速傅里叶变换 获得频谱。
- 从频谱中提取峰值,进而得到声音的部分音或谐波。
- 将这些成分从原始信号中减去,得到残差信号。

基于这个流程,我们可以进行特征分析以获取基频等信息,也可以对正弦成分和残差成分进行变换,从而合成出新的、经过修改的声音。如果不变换任何参数,理想情况下合成的声音应与原始声音非常相似。这个框架构成了我们所有分析、描述与合成技术的基础。
声音的频谱 📊
上一节我们概述了整体框架,本节中我们来看看第一个核心概念:声音的频谱。
我们有两种主要的频谱视图:
- 离散傅里叶变换:用于分析一个非常短的声音片段,得到一个单一的频谱。
- 短时傅里叶变换:用于分析随时间变化的声音,得到一系列连续的频谱,表示为
X_l[k]。
STFT可以视为我们的第一个有用的分析-合成模型,它是一个恒等系统,理论上可以捕获并完美重建任何声音。

正弦与谐波模型 🎶
在频谱分析的基础上,我们建立了正弦模型与谐波模型。

- 正弦模型:旨在捕获声音中具有稳定性和相干性的正弦成分,这些成分通常代表了声音声学特性中有意义的部分。
- 谐波模型:是正弦模型的进一步特化。对于大量声音(如乐音),其正弦成分之间存在谐波关系,即所有谐波频率都是基频的整数倍。利用这一约束,我们可以获得更强大、更紧凑的声音表示,为后续的描述与变换提供了巨大潜力。
残差与随机成分 🌫️
正弦或谐波成分并不能捕获声音的全部。当我们从原始声音中减去这些成分后,剩下的部分称为残差。
有时残差是感知上不重要的细微部分,可以丢弃。但在许多情况下,它是声音的重要组成部分,需要被保留和建模。我们可以选择直接保留残差信号,或者使用随机模型(如滤波白噪声)来近似它。这样就构成了一个完整的正弦+随机或谐波+随机模型,能够有效地建模许多类型的声音。

声音的变换 🎛️
当我们拥有了上述模型表示(包括正弦/谐波的频率、幅度、相位,以及随机成分)后,就可以对声音进行各种变换。
以下是一些常见的变换操作:
- 时间伸缩
- 音高移位
- 对参数进行任意修改
我们在课程中涵盖了一些基础变换,但实际应用中还存在更多可能性。

声音的描述 🏷️

除了变换声音,我们还探讨了如何描述声音。这是一个非常广阔的领域,我们仅触及了冰山一角。
声音描述涵盖了不同层次的抽象。我们主要关注的是较低层次的描述符,包括物理的、感官的以及一些感知层面的描述符,这些对于描述声音和音乐信号非常有用。这为我们利用频谱分析技术,去描述更完整的音乐作品乃至音乐库,打开了一扇大门。
超越本课程 🚀
在上一讲中,我们初步展望了本课程之外更广阔的天地。
音频信号处理领域远比我们在本课程中探讨的内容丰富,存在许多其他方法用于分析、描述和变换声音。更重要的是,我们试图理解的“声音与音乐”信息,其内涵远不止音频信号本身。近年来,一个新兴的探索方向是利用音频之外的其他信息源(如乐谱、文化背景、用户数据等)来分析和描述声音与音乐,这为未来开辟了非常有趣的新应用和发展领域。


总结 📝


本节课中我们一起回顾了整个《音乐应用中的音频信号处理》课程。我们从频谱图这一核心思想出发,系统重温了声音的频谱分析、正弦与谐波建模、残差处理、声音变换与描述等关键主题。最后,我们展望了结合多模态信息进行声音分析的未来方向。希望这次回顾能帮助你更好地理解课程的整体脉络和各主题之间的连贯性。
071:音乐技术研究组介绍 🎵
在本节课中,我们将了解本课程背后的研究团队——庞培法布拉大学音乐技术组(MTG)。我们将介绍该小组的研究领域、主要项目以及它们与课程内容的联系。

概述
我们已进入课程的最后一周。本周我们将探讨几个不同的小主题,旨在总结课程内容,并向大家介绍一些课程中未能深入探讨的领域,例如未来的研究方向,以及大家在完成本课程后可能感兴趣探索的主题。
在本节讲座中,我将向大家介绍我的研究团队——庞培法布拉大学的音乐技术组。目的是让大家更好地了解我们,特别是我们所从事的、与过去几周课程内容相关的研究类型。
大学与研究组背景
庞培法布拉大学位于西班牙东北部的巴塞罗那。大学在市区有三个校区,我们位于传播校区。我们隶属于工程系,但与新闻与视听传播系、语言学系等共同构成了一个跨学科的传播研究环境。
我们的研究组主要隶属于信息与通信技术系。因此,我们的教学职责涉及计算机科学、电气工程,甚至生物医学工程。我们教授与音乐、音频相关的课程,以及编程、机器学习等通用主题。
在研究生层面,我们提供更具体的项目。例如,我们开设了“声音与音乐计算”硕士项目,它非常契合我们研究组的方向。事实上,本Coursera课程正是该硕士项目中的一门课。当然,在硕士项目中,我们可以通过线下活动来补充在线教学,我认为这有助于更好地理解课程内容。
此外,我们还设有博士项目,学生在导师指导下进行论文研究。音乐技术组有许多博士生,他们与我们的教员合作,专注于我们研究的各种主题。

主要研究领域
接下来,让我介绍一下我们研究的核心内容。我们的研究大致可分为四个领域:

- 音频信号处理:这是本课程的核心内容。
- 声音与音乐描述:我们上周已有所提及。
- 音乐与高级交互:涉及为音乐应用开发交互界面。
- 声音与音乐的语义技术:处理比音频处理更高层次的技术,涉及文本相关信息,并能与音频处理方法很好地互补。
下面,我将逐一介绍这些领域,并举例说明我们的一些工作。

音频信号处理 🎤

这是我们最早开始的研究方向之一,至今仍非常活跃。
歌唱声音合成:我们与雅马哈公司合作开发了名为 Vocaloid 的歌唱声音合成器。大家可能听说过虚拟歌手 初音未来,她就是基于Vocaloid技术创造的。这项技术背后的核心,与我们课程中讨论的内容密切相关,它采用了频谱建模方法,分析并分离谐波,类似于我们学过的谐波加随机模型。

声音变换:另一个相关主题是实时变换声音,特别是歌唱声音。我们开发了实时系统,允许用户实时改变自己声音的特征。我们使用与课程中类似的技术,开发了用于博物馆的装置或插件来实现这些变换。
音源分离:这是我们近期开始研究的一个方向。音源分离是一个定义明确的问题,旨在从复调信号中分离出独立的声源,并尽可能使它们听起来清晰。虽然其方法体系与课程内容略有不同,但它是本课程内容的自然延伸。

声音与音乐描述 🎼
这个领域关注如何从声音中提取可用于描述声音或音乐的特征。
Essentia库:我们开发并维护一个名为 Essentia 的音频分析库。我们不断将新的研究成果融入其中,添加可用于描述声音的新特征。例如,可以在Sonic Visualizer等工具中可视化这些低层特征,这对许多任务非常有用。
音乐意义特征提取:在更高的抽象层次上,我们关注提取具有音乐意义的特征。例如:
- 和声/和弦:识别音乐中的和声进行。
- 结构分割:通过特征来分割录音,定义音乐的结构。
- 旋律特征:提取歌曲的主旋律音高,识别特定的音符或旋律地标。
- 节奏特征:寻找节奏结构的不同层次,从音符起始检测到更高层的节奏模式。
音乐收藏描述:这个方向不仅描述单个声音,还描述声音或音乐录音的集合。我领导的一个项目 CompMusic 就是典型例子。我们研究来自世界不同地区的五种音乐传统(如印度卡纳提克、中国京剧、土耳其马卡姆等),旨在开发能够描述特定音乐传统中旋律、节奏或语义特征的方法,并最终整合到一个名为 Dunya 的发现型程序中,用于导航这些音乐收藏。这个项目综合运用了信号处理、机器学习和语义分析技术。

音乐与高级交互 🎹
这个领域与本课程关联不大,主要涉及开发新的音乐交互界面。

Reactable:这是我们小组一个非常知名的成果。它是一个交互式音乐合成器,采用桌面界面,音乐家可以通过在桌上移动不同的物体来创作音乐,并伴有视觉反馈,为音乐制作提供了许多新的可能性。
声音与音乐的语义技术 🌐
这个领域关注与音频相关的文本信息(元数据)的处理。
Freesound平台:大家熟知的Freesound网站是一个进行语义技术研究的绝佳平台。例如,组织标签、在上传声音时推荐标签、搜索和推荐内容等任务,既可以基于音频内容完成,也可以基于文本元数据完成,后者就需要语义技术的支持。
AcousticBrainz项目:这是我们与MusicBrainz合作启动的一个新项目。它允许我们探索语义分析和语义技术。MusicBrainz是一个拥有结构化音乐信息的数据库。在AcousticBrainz中,我们为MusicBrainz中的每首歌曲或艺术家分配标识符,然后用户可以使用Essentia分析器分析相应的音频录音,并将分析结果(而非受版权保护的音乐文件本身)上传到我们的服务器。这样我们就拥有了一个庞大的已分析音乐数据库,为结合音频内容描述和语义分析开发新工具和想法提供了巨大潜力。
参考资料与总结
本节课中,我们一起了解了庞培法布拉大学音乐技术组的主要研究领域和代表性项目。如果你想了解更多信息,可以访问大学、系或MTG的网站。以下是一些相关链接供参考:
- Vocaloid的维基百科条目
- CompMusic项目网站
- Reactable乐器的维基百科条目
- AcousticBrainz项目网站

我们的研究远不止这些,网站上还有更多关于我们工作的链接和视频介绍。希望你对我们的研究感兴趣。谢谢大家,下节课再见。
072:课程总结与后续学习指南 🎓


在本节课中,我们将对《音乐应用中的音频信号处理》这门课程进行总结,并为希望深入学习的同学提供后续的学习方向与资源指引。

概述
这是本课程的最后一个视频讲座。首先,我想对所有坚持完成课程的同学表示感谢,是你们赋予了这次教学实验意义。这是我们首次开设此类在线课程,我们从中学习良多,并希望未来能运用这些经验来改进课程。同时,我们也感谢庞培法布拉大学和斯坦福大学提供的机构支持,以及我们所属的研究团队——音乐技术组和音乐与声学计算机研究中心。
后续学习资源指引
上一节我们回顾了课程历程,本节中我们将看看如何在本课程的基础上继续深入学习。以下是关于研究机构、学位项目、学术会议和期刊的一些信息。
研究机构
本领域目前已相当成熟,全球许多大学都设有相关研究机构。但由于这是一个跨学科领域,每个研究中心可能因其所属的院系、大学类型以及教职人员的兴趣背景而各有侧重。因此,在接触或试图加入某个中心之前,理解其具体背景和“个性”非常重要。
- 资源链接:您可以通过访问 SMcnetwork.org 网站,在其“资源”部分找到一个关于声音与音乐计算、音乐技术领域活跃研究中心的详细列表。
学位项目
在研究生教育项目方面,情况类似。能够提供专注于本领域课程的大型研究机构相对较少。
- 硕士项目:
- 庞培法布拉大学:提供声音与音乐计算硕士项目。该项目设在工程学院内,具有相应的工程背景。
- 斯坦福大学:提供音乐、科学与技术硕士项目。其计算机音乐与声学研究中心隶属于音乐学院,这赋予了项目不同的侧重点和背景。
- 博士项目:博士研究通常以论文为导向,因此,只要大学有相关领域的导师,就可以进行博士研究。我们两所大学都拥有大型研究团队,能为博士生提供良好的环境。选择时,关键是要理解特定院系的背景和导师的研究方向。
学术会议
参加学术会议是了解领域前沿、与其他研究者交流的重要途径。本领域有几个核心会议:
- 国际计算机音乐会议:历史最久,起源于美国,最初聚焦于技术用于音乐创作,现已涵盖计算机音乐的广泛议题。
- 声音与音乐计算会议:较新,起源于欧洲,更侧重于技术报告和讲座,反映了工程领域的视角。
- 数字音频效果会议:专注于音频信号处理,与本课程内容高度相关,是了解该领域进展的绝佳平台。
- 音乐信息检索会议:专注于音乐信息检索领域,涉及音乐描述、音频分析、机器学习等,对于希望利用技术描述音乐的同学非常有价值。
- 新音乐表达界面会议:专注于音乐技术中的人机交互方面,虽然本课程未涉及,但对于对交互设计感兴趣的同学是重要会议。
- 其他综合性会议:
- 音频工程学会:其会议常包含音频信号处理和音乐技术主题,并涉及工业应用。
- 电气电子工程师学会:其声学、语音与信号处理国际会议规模庞大,总有相当部分专注于音乐信息检索和音频信号处理。
- 计算机协会:其多媒体会议等也涵盖音频、图像、视频等多媒体数据分析。
学术期刊
期刊是了解领域深度研究的重要渠道。本领域有一些代表性期刊:
- 《计算机音乐期刊》:本领域最古老的期刊,强调音乐视角,但也涵盖广泛议题。
- 《新音乐研究期刊》:尽管名称如此,但非常关注音乐技术与音乐科学,近年来音乐信息检索主题占比很高。
- 综合性学会期刊:
- 音频工程学会期刊:发表大量相关论文。
- IEEE音频、语音与语言处理汇刊:总是包含相关重要文章。
- ACM多媒体计算、通信与应用汇刊:与本课程讨论内容关联密切。
资源查找与入门
要查找上述会议和期刊的具体链接,SMcnetwork.org 网站维护了相关列表,是很好的起点。此外,维基百科上关于“声音与音乐计算”的条目虽然可能不够全面,但可以作为了解该领域概况并寻找具体参考文献的入门。
总结
本节课中,我们一起回顾了课程,并探讨了深入学习的路径。我们介绍了全球范围内的相关研究机构、硕士与博士项目、核心学术会议以及重要期刊。希望这些信息能为你未来的探索提供指引。

最后,我想再次向所有人致以个人的感谢,感谢每一位让这门课程成为可能的人。特别要感谢Coursera提供了这个出色的平台,使我们能够传授知识并与大家互动。再见,再次感谢大家!
073:Dunia - 音乐探索原型系统
概述
在本节课中,我们将学习一个名为“Dunia”的音乐探索原型系统。该系统是“Commusica”研究项目的成果,旨在利用音频信号处理、机器学习和语义分析技术,帮助用户探索和理解特定音乐传统的录音收藏。我们将了解该项目的背景、目标、所研究的音乐传统,以及Dunia网站的主要功能和界面。
项目背景:Commusica
上一节我们介绍了Dunia系统,本节中我们来看看其背后的研究项目“Commusica”。

Commusica是一个已进行近三年的研究项目。该项目旨在从特定音乐传统的音频录音及相关信息出发,开发能够导航、探索和发现这些音乐的技术。其核心理念是采用文化特定方法,即利用对特定音乐传统的已有知识来更好地分析和描述音乐。
该项目选择了五种非西方的、与音乐信息检索领域通常关注的音乐类型差异较大的音乐传统。这些传统研究充分,信息丰富,是挑战现有技术并开发新技术的良好起点。
以下是该项目研究的五种音乐传统:
- 印度卡纳提克音乐:来自印度南部的古典音乐传统。
- 印度斯坦尼音乐:来自印度北部的古典音乐传统。
- 阿拉伯安达卢西亚音乐:来自北非马格里布地区的音乐传统。
- 中国京剧音乐:聚焦于京剧中的音乐部分。
- 土耳其马卡姆音乐:源自奥斯曼古典传统并在土耳其传承的音乐。

Dunia 网站导航
了解了项目背景后,本节我们来看看Dunia原型网站的具体界面和功能。
Dunia是一个研究原型网站,可通过URL dunia.commusica.upf.edu 访问。网站对公众开放,但音频录音因版权限制,需要申请研究登录账号才能在线收听。
网站主页面提供了通过元数据导航和搜索的功能。用户可以执行标准文本搜索,也可以探索为该音乐传统定义的相关实体。
以下是网站定义的核心实体概念:
- 艺术家:表演者的姓名。
- 音乐会:演出事件。
- 乐器:演奏使用的乐器。
- 拉格:旋律框架,公式表示为 Raga。
- 塔拉:节奏框架,公式表示为 Tala。
用户可以点击任何实体(如特定的拉格)进行搜索,网站将返回相关的录音和实体信息。
功能演示:以艺术家为例
在介绍了基本搜索功能后,我们通过一个具体例子来演示Dunia的探索流程。
例如,搜索艺术家“Amrutha Murali”。搜索结果会显示她的个人页面,其中包含从网络资源(如专注于卡纳提克音乐的网站Carnatic.com)获取的传记信息。所有元数据都来自MusicBrainz(一个开放的音乐元数据库),项目已将所有这些录音信息录入其中,使其成为人人可用的开放数据。
在艺术家页面,可以看到:
- 相似艺术家:基于语义分析(如是否属于同一流派或师从同一位古鲁)建立的相似性关系。
- 合作艺术家:曾与该艺术家在同场音乐会中合作演出的艺术家。
点击合作艺术家,可以链接到具体的音乐会页面。音乐会页面列出了该场演出的所有曲目(音轨)、参演艺术家列表,以及相似音乐会。相似音乐会通过比较两场音乐会中演奏曲目所使用的拉格和塔拉的重叠程度来确定,从而建立音乐上的关联。
录音页面与音频分析
从音乐会页面可以进入具体的录音(曲目)页面。这是展示研究成果的核心界面。
录音页面显示以下内容:
- 波形图:可缩放查看音频波形。
- 音高轨迹:自动从歌曲中分析得出的音高随时间变化的曲线。公式表示为 pitch(t)。
- 主音指示线:水平线表示印度音乐中作为参考音高的“主音”频率值。例如,本曲目自动分析出的主音是G,频率为196 Hz。
- 背景色频谱:显示整体音色的频谱图,提供音乐音色的可视化概览。
- 音高直方图:位于左侧,显示整首歌曲的音高分布。它揭示了歌曲中哪些音高被更加强调,是表征特定乐曲或歌手对某个拉格演唱音准的一种方式。
用户可以在播放音频的同时,观察这些可视化分析结果如何与音乐同步变化。
相似性探索与未来方向
最后,我们来看看Dunia系统在音乐探索方面的核心目标与未来方向。
录音页面的底部展示了相似录音的探索功能。这是系统的核心目标之一:探索歌曲内部以及跨歌曲的相似性。
目前,系统初步实现了在整个卡纳提克音乐录音收藏中,基于音高直方图的形状来寻找相似歌曲。系统会表征当前歌曲的音高分布形状,然后寻找在其他歌曲中关键方面可能相似的形状。
例如,系统可能找出同一拉格下被识别为有些相似的歌曲。这为用户提供了基于音乐内容本身(而非文本标签)进行导航和发现的可能性。
未来的目标是开发更复杂的探索模式,允许用户以更深入的方式导航音乐收藏,并确保这些探索方式对特定的音乐传统具有实际意义。
总结

本节课中,我们一起学习了Dunia音乐探索原型系统。我们了解了其背后的Commusica项目及其文化特定研究方法,熟悉了Dunia网站通过元数据实体(如拉格、塔拉、艺术家)进行导航的功能,并深入查看了录音页面提供的多种音频分析可视化(如音高轨迹、音高直方图)。最后,我们看到了系统基于内容相似性进行音乐探索的初步尝试及其未来发展方向。这个原型展示了如何将本课程所学的音频信号处理等技术,应用于构建有助于理解和探索特定音乐文化的实用工具。
074:AcousticBrainz 介绍 🎵
在本节课中,我们将介绍一个名为 AcousticBrainz 的新兴项目。该项目由音乐技术组与 MusicBrainz 合作开发,旨在通过众包方式收集和分析音乐录音数据,以建立一个可用于研究和探索的大型音乐特征数据库。

项目概述与目标
AcousticBrainz 是一个旨在收集音乐分析数据的倡议。这些数据来源于对音乐录音的分析,并由公众提供。其目标是尽可能多地收集数据,以创建一个可用于多种目的的音乐数据语料库,主要用于研究,也可用于不同方式的探索。
在音乐相关数据的研究中,一个主要困难是音频录音的获取。由于版权问题,音频录音不易获得,也无法被收集和共享用于研究。AcousticBrainz 的解决方案是通过众包来分发代码,让用户在自己的计算机上分析他们拥有的音乐收藏,然后上传分析结果数据。
为了实现这一目标,需要两个基本组件:
- 一种能够唯一识别每首被分析录音的方法,以便获取该录音的所有元数据信息。这个组件由 MusicBrainz 提供。
- 实际的音频分析功能,用于提取音频特征。这个组件由 Essentia 音频特征提取器提供。
项目进展与统计数据
自项目启动以来,AcousticBrainz 已经取得了显著进展。在短短几周内,已成功分析了超过一百万首曲目。
下图展示了自九月中旬以来数据收集量的演变过程:


项目初期,用户需要自行编译代码,使用门槛较高,因此数据增长缓慢。最近一周左右,项目提供了预编译的二进制文件,使得分析过程更加简便,从而迎来了数据的第二次快速增长。
图表中的顶部线条代表分析曲目的总数,浅绿色线条代表唯一曲目的数量(排除了不同格式或压缩版本的重复项)。目前,唯一曲目数量已接近80万首。



核心组件一:Essentia 音乐特征提取器
Essentia 是一个音频分析工具包。AcousticBrainz 使用了其中的“音乐特征提取器”。该提取器旨在提取可用于描述和表征音乐的低层级音频特征。
以下是 Essentia 音乐特征提取器的界面示例:

该提取器会输出一个 JSON 文件,其中包含整首音乐曲目的所有特征值及其统计信息。

JSON 文件结构示例:
{"metadata": { ... },"lowlevel": {"spectral_centroid": { "mean": 1500, "var": 200 },"mfcc": [ ... ],...},"tonal": { ... },"rhythm": { ... }
}

此外,AcousticBrainz 服务器还会基于这些低层级描述符,运用机器学习方法和分类技术,提取一些高层级描述符,并为曲目分配标签,以服务于更高层次的任务。


核心组件二:MusicBrainz 元数据库
MusicBrainz 是一个运行已久的项目,致力于收集音乐的元数据。它就像一个音乐百科全书,以开放许可的方式共享所有数据。
以下是 MusicBrainz 收集的部分统计数据:


MusicBrainz 的核心实体包括艺术家、发行版本、音轨和作品等。例如,它拥有约90万位艺术家的信息和约7000万首音轨的信息。AcousticBrainz 的分析上限正是这些在 MusicBrainz 中标识的音轨。


每个实体(如艺术家、音轨)都有一个唯一的标识符(MBID)。当引用特定音轨或艺术家时,只需引用其对应的 MBID,即可在所有相关服务或资料库中唯一地识别该实体。这是组织所有分析数据的绝佳方式。

实践操作:为音轨添加标识并进行分析
上一节我们介绍了 AcousticBrainz 的两个核心组件。本节中,我们通过一个具体例子来演示如何为音轨添加 MusicBrainz 标识符并进行音频分析。


我们以钢琴家 Kimiko Douglas-Ishizaka 演奏的《哥德堡变奏曲》录音为例。
首先,在 MusicBrainz 中搜索这位艺术家,找到对应的发行版本和音轨。每个音轨都有其唯一的 MBID。

例如,我们复制其中一个音轨(如第一变奏曲)的 MBID:fxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx(此处为示例ID)。


接下来,需要准备两个工具:
- AcousticBrainz 提交工具:用于分析音频特征的命令行程序。
- MusicBrainz Picard:用于为本地音频文件添加 MusicBrainz 标识符的图形化工具。

以下是使用 Picard 为本地 MP3 文件添加标识符的步骤:
- 将《哥德堡变奏曲》的 MP3 文件导入 Picard。
- 通过“聚类”功能将曲目分组。
- 在 MusicBrainz 数据库中手动查找并匹配对应的发行版本。
- 将匹配到的元数据(包含所有音轨的 MBID)保存到本地 MP3 文件的标签中。

完成标识后,使用 AcousticBrainz 提交工具分析整个目录:
./acousticbrainz-submit /path/to/goldberg-variations/
该工具会调用 Essentia 音乐特征提取器分析每个音轨,并将结果数据上传至 AcousticBrainz 数据库。
查看与分析结果


分析完成后,我们可以在 AcousticBrainz 网站上查看结果。
在 AcousticBrainz 网站输入特定音轨的 MBID,页面会显示来自 MusicBrainz 的元数据以及分析得到的声学数据。

显示的数据包括:
- 低层级数据:完整的 JSON 格式音频特征描述符。
- 高层级数据:通过机器学习模型推断出的信息,例如调性、乐器、情绪等。






此外,页面还尝试根据元数据链接到 Spotify、Deezer 或 SoundCloud 等流媒体服务,以便在线播放该录音(可能不是原始版本,但是同一作品)。


项目意义与资源获取

AcousticBrainz 是一个刚刚起步但令人兴奋的项目。它综合运用了我们课程中讨论的许多音频分析技术,并将其应用于分析海量音乐、理解大型音乐资料库的复杂场景中。



对于用户而言,既可以贡献自己音乐收藏的分析数据,也可以利用已有的数据。AcousticBrainz 网站提供了所有已分析数据的下载链接,包括低层级和高层级描述符的压缩包,并会随着数据增长持续更新。
相关资源链接:
- AcousticBrainz 官网:
https://acousticbrainz.org - MusicBrainz 官网:
https://musicbrainz.org - MusicBrainz Picard 工具:
https://picard.musicbrainz.org - Essentia 音乐特征提取器:
https://essentia.upf.edu

本节课中,我们一起学习了 AcousticBrainz 项目。这是一个通过结合 MusicBrainz 的元数据标识和 Essentia 的音频分析能力,以众包方式构建大规模音乐特征数据库的倡议。我们了解了其运作原理、核心组件,并通过一个实例演示了如何为音轨添加标识、进行分析并查看结果。该项目为音乐信息检索和音频信号处理研究提供了宝贵的开放数据资源。
