音高校正的软件实现

信息处理 沥青
2022-02-25 11:39:47

我有一个关于声音音高校正物理学的问题。

我的背景是物理学,我主要活跃在physics.stackexchange 上。(查看我的个人资料。)

我的问题的背景:

在计算机录音之前,只有模拟录音。假设您是一名歌手,并且您录制了钢琴伴奏,但您想以较低的音调进行排练。一些录音带播放器具有可调节的磁带速度。调整磁带速度意味着音乐的音高和音乐的节奏同步变化。

我的理解是:随着计算机录音的出现:开发了具有独立改变音高和速度能力的软件。因此,可以在不改变速度的情况下改变音高,或者在不改变音高的情况下改变速度,或者是音高和速度的不同变化的所需组合。

鉴于我的物理背景(知道声音是如何叠加许多频率的),我对可以独立改变音高和速度的软件的存在感到困惑。我知道它存在,我使用过它,但我什至无法开始猜测它是如何实现的。

关于在电脑屏幕上查看波形:
几年前,我将音乐从一堆黑胶唱片复制到 CD。一些唱片有严重的划痕,在损坏的位置每转一圈都会造​​成 10 或 20 次响亮的爆裂声。现在有软件可以自动过滤掉这样的pop,但是我当时用的软件没有。所以我所做的是:我会一直放大到那个尖峰,有时我可以复制一个相邻的部分,只需几毫秒,然后用它来替换该持续时间的损坏部分。我永远无法让它无缝,粘贴的行为总是会留下一些神器。那个神器几乎听不见,所以没关系,但我永远无法让它无缝。

我提供这些信息是为了让你知道你可以在什么级别回答这个问题。


音高校正是如何实现的?它必须是一个与操作模拟录音时改变音高的方式完全不同的过程。

我认为这不可能是一个将音乐剪切并重新组合的过程;我的假设是这会产生大量的人工制品。




回应答案和评论。
在某些方面,我的期望已在答案中得到证实。原始版本必须在非常抽象的层面上进行解构和重新合成。重建/再合成水平的要求之一是高保真音高检测,但在现实世界中,音高在哪个八度音程中可能是不明确的。

我的印象是:当涉及到音高校正时,没有入门级的理解可以让你 90% 的进入,而只有 10% 的错误。

我的印象是:在理解音高校正算法时,从一开始就是深入探讨。

3个回答

@hotpaw2 为您提供了有关如何在时域中执行此操作以及如何尝试避免故障的好技巧...

基本技巧步骤是找到交叉淡入淡出的位置并再次拼接两个部分,您可以在波形中搜索颗粒最匹配的位置,我们通常应用某种自相关算法来查找波形中的周期性并剪切......现在您可以交叉淡入淡出尝试消除所有故障!

当您说话时,我们可以使用频域或时域方法单独更改音高或时间:

它必须是一个与操作模拟录音时改变音高的方式完全不同的过程

它记得我关于模拟录音的操作是当你将指尖放在乙烯基上并以不同的速度旋转它时,它会同时给你节奏和音高变化,它很容易在数字音频中重现,只需播放它不同的采样率,一个音高校正不能以这种方式工作,以保持速度不变,一种方法是改变音频速度(拉伸或收缩),它不会影响音高,复制颗粒以拉伸或切割颗粒以缩小你的音频(选择使用自相关对音频进行切片并应用交叉淡化以使其无故障),现在使用用于拉伸或收缩的反因子重新采样/插值,此过程为您提供音高转换音频而无需改变速度...

要知道使用什么因子来拉伸/收缩,我通常会从所有音符创建一个表频率,一个音高轨道平行运行以获取当前音高并与表音符进行比较,所以现在您知道在每个颗粒中使用的音高校正因子!

这种方法的大问题是你会失去所有的共振峰:-(

做音高修正,32年后,我还是喜欢keith Lent的方法,为什么???这个算法保持共振峰完整:-),这里的一个问题是你需要知道音高,而不仅仅是最好的自相关匹配:

在阅读了 Lent 和RBJ论文后,我尝试构建一个自动音高校正,我的简单 matlab 测试在这里:

https://www.youtube.com/watch?v=6ns5K1FHtd4

https://www.youtube.com/watch?v=MYxQVTwHK_o

好的,所以我将从最简单的时域音高变换(无 FFT)开始,它经常用于现场吉他效果。

正在发生的是两个并行的过程。一种是音高检测器,其中音频进出有一个计算出的周期长度,并由此可以计算出基频和音高(对数频率)。

另一个过程是具有可变分数采样延迟的移动抽头延迟线。理想情况下,这将是一些窗口正弦插值,但是,如果信号已经过采样(即带宽远低于奈奎斯特),那么线性插值可能就足够了。

input_index++;
input_index &= index_mask;       // modulo-2 arithmetic
delay_buffer[input_index] = input_sample;
...
delay += delay_increment;
integer_delay = (int)delay;
fractional_delay = delay - (float)integer_delay;
...
output_index = input_index - integer_delay;
output_index &= index_mask;
output_sample = delay_buffer[output_index--];
output_index &= index_mask;
output_sample += fractional_delay*(delay_buffer[output_index]-output_sample);   // this is linear interpolation

现在输入指针总是一个整数,但输出指针有一个整数和小数值。

现在,在升档时,您移动输出指针的速度比输入指针快,因此延迟必须减少降档时,延迟增加delay_increment

delay_increment =12cents/1200

在哪里cents是以 1/1200 八度为单位的音高偏移量。

现在这里是棘手的地方。如果输出指针的移动速度与输入指针的移动速度不同,最终您将在缓冲区中回绕,并得到一个讨厌的点击。因此,您必须在当前输出指针处开始淡出音频,并距离边缘较远的新输出指针处开始淡入音频。

对于无干扰拼接,当前输出延迟(淡出)和新输出延迟(淡入)之间的差异应该是音频的一个周期,并且该信息必须来自音高检测器。在将音高检测器与输出指针的平均延迟对齐时,有一些必要的令人讨厌的细节。

执行交叉淡入淡出(或拼接)的代码有点复杂,但如果你给我一些时间,我可能会在这里给你写一些。

它可以(但不一定)只是一个剪切和拼接的过程。

有(至少)两类主要的时间音调修改,时域和频域分析/再合成。您提到的一种是时域方法,其中(几乎)根据需要重复或删除整个音调音频周期以扩展或收缩时间。然后可以对波形进行部分重采样以调整频率。这对单声道声音或音轨效果更好。

除了切片之外,还可以通过多种方式移除(或不引入)一些伪影。可以对切片点处的时序进行子样本内插以最小化相位毛刺。拼接点周围的幅度可以通过渐变或交叉衰减进行调整,以最大限度地减少包络变化或毛刺。滤波可以应用于中间结果以去除任何新的频率伪影,或使平均频谱包络更接近原始频谱包络。可以应用卷积来尝试将房间(混响)重塑回原始房间大小。等等。