【问题标题】:Programmatically increase the pitch of an array of audio samples以编程方式增加音频样本数组的音高
【发布时间】:2011-07-06 14:03:07
【问题描述】:

音频计算界的好心人,您好,

我有一组代表录音的样本。假设它在 44100Hz 时为 5 秒。我将如何以更高的音调回放这个?是否可以动态增加和减少音高?就像让音高慢慢增加到两倍速度,然后再降下来。

换句话说,我想录制并播放它,就好像它正在被 d.j '刮擦'。

始终欢迎使用伪代码。我会用 C 来写这个。

谢谢,


编辑 1

请允许我澄清我的意图。我想将播放保持在 44100Hz,因此我需要在播放前处理样本。这也是因为我想将音高增加的音频与以正常速率运行的音频混合

以另一种方式表达,也许我需要以某种方式在相同数量的样本上缩小音频?那样播放的时候声音会更快?


编辑 2

另外,我想自己做这件事。请不要使用库(除非您觉得我可以通过代码挑选并找到有趣的东西)。


编辑 3

一段用 C 编写的示例代码,它接受 2 个参数(样本数组和音高因子),然后返回一个新音频数组,这太棒了!


PS 我已经开始赏金不是因为我认为已经给出的答案是无效的。我只是认为获得有关该主题的更多反馈会很好。



赏金奖

老实说,我希望我可以将赏金分配给几个不同的答案,因为它们有很多我认为非常有帮助的。特别感谢 Daniel 向我传递了一些代码,以及 AShelly 和 Hotpaw2 提供了如此详细的回复。

虽然我最终使用了 datageist 引用的 another SO question 的答案,但最终还是将奖项授予了他。

再次感谢大家!

【问题讨论】:

  • 请澄清 EDIT 3. 像 DJ 那样抓唱片会导致文件长度不同。例如如果 DJ 以两倍的速度旋转唱片,则录制该声音的文件的时间长度将是原始长度的一半。这是你想要的吗?
  • @hotpaw2。是的,对此感到抱歉。我已经更正了 EDIT 3。确实它会是长度的一半,而这正是我正在寻找的。谢谢:)

标签: audio signal-processing pitch


【解决方案1】:

如果您想轻松完成此操作,请参阅 AShelly 的建议[编辑:事实上,无论如何,请先尝试]。如果你需要好的质量,你基本上需要一个phase vocoder

相位声码器的基本思想是找到声音所包含的频率,根据需要更改这些频率并重新合成声音。所以一个残酷的简化是:

  1. 运行 FFT
  2. 按一个因子更改所有频率
  3. 运行逆 FFT

如果您要自己实现此功能,您绝对应该阅读a thorough explanation of how a phase vocoder works。该算法确实需要比上面的三步简化更多的考虑。

当然,ready-made 实现 exist,但从我收集的问题来看,你想自己做这件事。

【讨论】:

  • 我喜欢第二个链接,它的插图很好。但我有点困惑:在所描述的算法中,在逆 FFT 之后,你会得到“一个信号现在要么在时间上被拉伸或压缩,而且音高没有改变”。你是说要改变音高(因为这是OP想要的),你需要在FFT之后修改频率数据?这是否就像在进行逆运算时重新解释垃圾箱的宽度一样简单?
  • @AShelly:首先音频在保持音高的同时被拉伸/压缩,然后加速/减速以获得适当的长度。这导致想要的音高变化。我的粗略简化可能无法直接实施;我认为就是这个想法,但这样做是为了获得正确的阶段。我自己没有实现声码器的经验,但有一些与其他 FFT/DCT 用法有关。因此,我试图指出一个好的来源。
  • 另外,如果重要的话,我绝对建议先尝试 AShelly 的算法。
  • @dancek 使用这种技术的一个缺点可能是 fft 的块长度属性。它一次处理完整长度的音频块,在频域中处理它们并将结果转换回时域。为了获得 OP 要求的动态(快速)变化的音高效果,块必须非常小,或者(推荐)每个样本必须被多个块识别。块在时域中重叠和交叉混合,可能会引入可听的伪像。因此,基于时域的解决方案会更可行。
  • @user492238:当然需要使用窗口函数;见my link to an explanation of a phase vocoder algorithm。这种技术实际上似乎被称为short-time Fourier transform
【解决方案2】:

一种方法是在原始波形中保留一个浮点索引,并将插值样本混合到输出波形中。

//Simulate scratching of `inwave`: 
// `rate` is the speedup/slowdown factor. 
// result mixed into `outwave`
// "Sample" is a typedef for the raw audio type.
void ScratchMix(Sample* outwave, Sample* inwave, float rate)
{
   float index = 0;
   while (index < inputLen)
   {
      int i = (int)index;          
      float frac = index-i;      //will be between 0 and 1
      Sample s1 = inwave[i];
      Sample s2 = inwave[i+1];
      *outwave++ += s1 + (s2-s1)*frac;   //do clipping here if needed
      index+=rate;
   }

}

如果您想即时更改rate,您也可以这样做。

如果这会在 rate > 1 时产生噪声伪影,请尝试使用此技术替换 *outwave++ += s1 + (s2-s1)*frac; (from this question)

*outwave++ = InterpolateHermite4pt3oX(inwave+i-1,frac);

在哪里

public static float InterpolateHermite4pt3oX(Sample* x, float t)
{
    float c0 = x[1];
    float c1 = .5F * (x[2] - x[0]);
    float c2 = x[0] - (2.5F * x[1]) + (2 * x[2]) - (.5F * x[3]);
    float c3 = (.5F * (x[3] - x[0])) + (1.5F * (x[1] - x[2]));
    return (((((c3 * t) + c2) * t) + c1) * t) + c0;
}

在“Windows Startup.wav”中使用系数为 1.1 的线性插值技术的示例。原版在上,加速版在下:

它可能在数学上并不完美,但听起来应该,并且应该可以很好地满足 OP 的需求..

【讨论】:

  • 这是行不通的,因为音频不是屏幕上的像素 - 并且会在输出信号中给您带来难以置信且难以忍受的声音伪影和噪音。此外,您不需要裁剪,因为权重总和为 100%,因此不会溢出。
  • @Daniel Mošmondor,这 确实 工作,假设 inwave 是单个 pcm 通道,并且您的因素不是那么极端以至于您得到混叠。我有一个 ruby​​ 工作程序来证明它。如果你绘制样本与时间的关系图,你会得到一个波形。插值点可以为您提供该波形的不同表示,这是完全有效的。
  • 至于剪辑,我添加了该评论以处理 OP 的评论,即他想将拉伸的样本混合到另一个轨道中。此代码处理outwave 已包含良好数据的情况。
  • 我仍然建议 OP 先尝试一下。模拟在背景轨道上刮擦一条轨道可能不需要最纯粹的变换。至少,这种方法很容易上手,如果需要的话,可以很容易地替换更复杂的过滤器。
  • @Daniel:音频的隐式样条插值比线性插值要好一点,但也没有那么糟糕,它们会给你“难以置信的和难以忍受的声音伪影和噪音”。正如 Quertie 指出的那样,您可以使用过滤器消除此过程中存在的工件。
【解决方案3】:

是的,这是可能的。

但这不是一小段伪代码。您要求的是时间音高修改算法,这是相当大且复杂的 DSP 代码量,以获得不错的结果。

这是Time Pitch stretching overview from DSP Dimensions。您也可以 Google 搜索相位声码器算法。

添加:

如果您想“从头开始”,就像 DJ 在物理转盘上使用 LP 所做的那样,您不需要修改时间音高。刮擦会以相同的量改变音高和演奏速度(不是独立的,因为需要修改时间音高)。

结果数组的长度不会相同,但会随着俯仰/速度的变化而变短或变长。

您可以更改音高,并以相同的比率使声音播放更快或更慢,只需使用适当过滤的插值对信号进行重新采样即可。只需通过您所需的速率变化通过浮点加法移动每个采样点,而不是 1.0,然后在该点过滤和插值数据。使用窗口 Sinc 插值内核进行插值,低通滤波器转换频率低于原始和插值局部采样率的较低值,将工作得相当好。在网上搜索“windowed Sinc interpolation”会返回很多合适的结果。

您需要一种包含低通滤波器的插值方法,否则您会听到可怕的混叠噪声。 (如果您的原始声音文件已经被严重低通滤波了 10 倍或更长时间,则可能是例外情况。)

【讨论】:

    【解决方案4】:

    看看 Nosredna 对这个(非常相似的)SO 问题的回答中的“大象”论文: How do you do bicubic (or other non-linear) interpolation of re-sampled audio data?

    从第 37 页开始提供示例实现,作为参考,AShelly 的答案对应于线性插值(在同一页上)。稍作调整,论文中的任何其他公式都可以插入到该框架中。

    要评估给定插值方法的质量(并了解使用“更便宜”方案的潜在问题),请查看此页面:

    http://www.discodsp.com/highlife/aliasing/

    对于比您可能想要处理的更多理论(使用源代码),这也是一个很好的参考:

    https://ccrma.stanford.edu/~jos/resample/

    【讨论】:

      【解决方案5】:

      This 帮助我进行了重新采样,这与您需要从另一侧查看的内容相同。

      如果找不到代码,请联系我,我有一个很好的 C 例程。

      【讨论】:

      • 我看不到,FIR 在这里有什么帮助? OP 正在寻找的是以 nonlinear 方式改变音频。基本上,FIR 的作用是改变音频中几个频率的幅度,而不是转换固有频率,信号是由什么组成的?
      【解决方案6】:

      降低和提高音高就像以低于或高于 44.1kHz 的速率播放样本一样简单。这将产生更慢/更快的录音声音,但您需要添加真实录音的“刮擦”。

      【讨论】:

      • +1 我认为这是最简单的解决方案。大多数音频硬​​件都非常容易支持播放速率的改变。
      • @user492238 通用音频硬件在应用程序之间共享并以固定速率运行。操作系统提供了软件混音器,以给人一种“拥有”硬件的印象。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-17
      • 2016-05-15
      • 1970-01-01
      相关资源
      最近更新 更多