音频处理中的变调和时间拉伸(一)

2023-10-31 23:30

本文主要是介绍音频处理中的变调和时间拉伸(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

介绍

很多用过磁带或者塑胶唱片的人都会对于一种现象感到熟悉:当我们快放或者慢放音频的时候,如果我们用两倍速度快放,除了会使得音频播放时间减少一半,还会使得音高升高一个八度,听起来音频中的人声会很像卡通动画人物的声音。同时,如果慢放会使得播放时长增长并且降低相应比例的音高。

以前使用模拟音频录音技术的时候,这种现象可以通过设置错误的播放速度来复现。现在,在数字信号处理领域,同样的现象可以通过重采样来实现。

重采样会同时改变音频的播放时长与音高,但是有时人们会有需求:只改变音高或者只改变播放时长。这类技术会被称为:

  • time/pitch scaling,
  • time/pitch shifting,
  • time stretching.

应用

Time scaling 可以慢放音乐速度,方便大家跟着跳舞或者拍视频再或者练习乐器。慢放讲话录音可以帮助人们转写,或者学习语言,盲人可能会使用快放来播放一些音频书籍来节约时间。视频网站上的倍速播放页需要音频跟着加倍但是不会变调。

类似的,在卡拉OK或者练习唱歌的时候,调整音乐的音调或者key,可以更好的匹配演唱者的声线。或者用来调音,百万调音师你们懂得。

最后,有些人可能想要通过改变他们自己声音的音调来实现身份隐藏。

实现方式

目前有两种实现上述技术的基本方式,即在时域处理或者频域处理

时域处理方式直接操作采样数据,比如后面要介绍的SOLA算法。时域处理的优势在于实现非常直接(straight-forward),因为音频数据处理时的采样格式跟它播放时或者录制时相同。缺点在于会产生一些人造回响导致失真,并且随着更大的时间调整,失真更明显。比如时间伸缩超过15%时。

频域处理是将采样声音转换为短时的 频率/振幅 成分然后在频域信息上来做伸缩,相位声码器是这类方法的一个典型。频域处理的优势在于可以支持更复杂的声音调整给出更好的听感,因为人类听力根本上只是基于频率的。

然而,由于它们明显的强大和优雅,频域处理实现起来更加复杂,计算复杂度更高。所以受限于计算资源,比如cpu速度和内存等。

SOLA

SOLA即 Synchronous-OverLap-Add,同步交叠相加法。通过将声音数据切成一系列的很短的长约几十到几百毫秒的片段,然后将这些片段通过一定的手段:跳过某些内容或者重复某些内容,重新排列起来达到比原始音频更长或者更短的播放时间。使用相同思路的算法还有TDHS(Time-Domain Harmonic Sampling),WSOLA和PSOLA,他们的区别在于实现细节。

为了避免在两个片段连接处的声音出现过于明显的断裂感,两个片段会有一定的重叠部分,所以声音的振幅从一个片段到另一个片段是渐变的,所以SOLA名字中有OverLap-Add部分。

最简单的SOLA实现可以使用统一的片段长度,然后每隔一段均匀的间隔来取一段片段。如果你想让声音缩短10%的长度,假设我们使用100毫秒(+叠加的长度)的片段长度,然后以110毫秒为间隔,从原始音频中取片段,然后将这些片段通过叠加连接起来,你就获得了想要的效果。同样的如果要延长10%的长度,选择100毫秒的片段,然后每隔90毫秒取一个片段,最后连接起来即可。是不是很简单呢?

然而,实际应用中的SOLA实现起来并没有那么简单。选取片段的时候不管片段内容的话,即使采用了渐变叠加的方法还是会由于过大的不连贯而出现噪音。(注:这里的不连贯是指的采样点不构成一个波形,虽然其实采样点实际上是连贯的,但是不构成波形的话,发出的声音也是噪音)

实现考虑

为了满足音质要求,SOLA在实现上需要在选择片段时,使得相邻片段之间交叠部分尽量相似。

实际上,音频流每次处理一个片段,为了使得相邻两个片段之间更匹配,在选择下一个片段的时候,可以在一个合适的窗口范围内来寻找。一种寻找最匹配片段的方法是通过计算上一个片段结尾部分和窗口内的待选片段的开头部分的互相关性,具有最高互相(cross-correlation)关值的两个片段的头尾是最相似的。这些片段最后通过交叠的方式连接在一起,形成了新的音频流,并且与原始的音频流长度不同。

SOLA的总体算法如下图所示。图中坐标轴的范围是可以任意设定的,所代表的时间单位仅做示意。在算法执行过程中,原始的音频会被切成合适长度的若干片段。新的片段会在与前一片段合适的间隔后被选出,从而获得想要的伸缩效果。

图1
在图1中,第一个片段从0开始,然后长度是7个时间单位,首尾各有一个时间单位的交叠部分。每隔9个时间单位取一个片段,最终的时间伸缩比例为 (7-2)/9 = 0.555,也就表示相对原始音频缩短了44.5%的时长。

然而,实际上在取片段时,并不是严格按照名义上的时间间隔来取的,而是取自以时间间隔处为中心的一个窗口范围内。比如图1中的“New Sequence”实际上是取自第8个时间单位到第10个时间单位之间,以便让新的片段和前一个片段在交叠时可以更好的重合。在图1下半部分我们可以看到交叠后的结果。

互相关函数对于评估音频片段的相似度很有效,并且也易于实现。同时,还有一些其它的相似度测量函数也被提出。其中一种方式是将片段的边缘与声音的节拍对齐,如果声音有一定的节拍并且可以被检测到,那么这种方式可以降低产生的类似混响感的人工处理痕迹。还有一种方式是评估频域频谱的相似度,而不是时域的波形相似度。

SOLA只需要基础的加法和乘法,因此可以使用整型或者定点数运算来实现,防止浮点数运算不被支持或者效率不高。

多声道处理

处理立体声时,片段交叠操作应当是在多个声道同样的位置进行的,如果我们单独处理每个声道,最终可能会造成声道之间错位。

所以我们可以同时处理所有声道,比如,可以将所有声道相加后再通过互相关函数找到共同的最优的片段获取点。

使用SOLA进行变调

SOLA只有拉伸功能,但是如果和重采样技术结合起来就可以实现变调功能了,很简单,假如要升高八度,只需要下采样音频到原始长度的一半,然后再使用SOLA拉伸到原始长度即可。

修音痕迹与参数

使用SOLA处理过的音频会有一点点回声的痕迹,这种痕迹会随着拉伸的幅度的增大而变得更加明显。

有一种减轻回声痕迹的方式是通过根据被处理音频的基本频率来选择处理片段时长。(这里的基本频率不是f0,意思是被处理音频的节拍频率)

处理时窗口范围的选择也会影响音质,越宽的窗口范围一般会获得更好的交叠音频的匹配度。但是,太宽的话产生的音频会不稳定,听起来像是在“漂移”。

交叠长度,一般是整个处理片段长度的一部分。通常使用线性的响度变化会表现的更好。

这篇关于音频处理中的变调和时间拉伸(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/318855

相关文章

解决docker目录内存不足扩容处理方案

《解决docker目录内存不足扩容处理方案》文章介绍了Docker存储目录迁移方法:因系统盘空间不足,需将Docker数据迁移到更大磁盘(如/home/docker),通过修改daemon.json配... 目录1、查看服务器所有磁盘的使用情况2、查看docker镜像和容器存储目录的空间大小3、停止dock

5 种使用Python自动化处理PDF的实用方法介绍

《5种使用Python自动化处理PDF的实用方法介绍》自动化处理PDF文件已成为减少重复工作、提升工作效率的重要手段,本文将介绍五种实用方法,从内置工具到专业库,帮助你在Python中实现PDF任务... 目录使用内置库(os、subprocess)调用外部工具使用 PyPDF2 进行基本 PDF 操作使用

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

Python异常处理之避免try-except滥用的3个核心原则

《Python异常处理之避免try-except滥用的3个核心原则》在Python开发中,异常处理是保证程序健壮性的关键机制,本文结合真实案例与Python核心机制,提炼出避免异常滥用的三大原则,有需... 目录一、精准打击:只捕获可预见的异常类型1.1 通用异常捕获的陷阱1.2 精准捕获的实践方案1.3

Pandas处理缺失数据的方式汇总

《Pandas处理缺失数据的方式汇总》许多教程中的数据与现实世界中的数据有很大不同,现实世界中的数据很少是干净且同质的,本文我们将讨论处理缺失数据的一些常规注意事项,了解Pandas如何表示缺失数据,... 目录缺失数据约定的权衡Pandas 中的缺失数据None 作为哨兵值NaN:缺失的数值数据Panda

C++中处理文本数据char与string的终极对比指南

《C++中处理文本数据char与string的终极对比指南》在C++编程中char和string是两种用于处理字符数据的类型,但它们在使用方式和功能上有显著的不同,:本文主要介绍C++中处理文本数... 目录1. 基本定义与本质2. 内存管理3. 操作与功能4. 性能特点5. 使用场景6. 相互转换核心区别

java时区时间转为UTC的代码示例和详细解释

《java时区时间转为UTC的代码示例和详细解释》作为一名经验丰富的开发者,我经常被问到如何将Java中的时间转换为UTC时间,:本文主要介绍java时区时间转为UTC的代码示例和详细解释,文中通... 目录前言步骤一:导入必要的Java包步骤二:获取指定时区的时间步骤三:将指定时区的时间转换为UTC时间步

Python动态处理文件编码的完整指南

《Python动态处理文件编码的完整指南》在Python文件处理的高级应用中,我们经常会遇到需要动态处理文件编码的场景,本文将深入探讨Python中动态处理文件编码的技术,有需要的小伙伴可以了解下... 目录引言一、理解python的文件编码体系1.1 Python的IO层次结构1.2 编码问题的常见场景二

Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧

《Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧》本文将通过实际代码示例,深入讲解Python函数的基本用法、返回值特性、全局变量修改以及异常处理技巧,感兴趣的朋友跟随小编一起看看... 目录一、python函数定义与调用1.1 基本函数定义1.2 函数调用二、函数返回值详解2.1 有返

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建