您如何对重新采样的音频数据进行双三次(或其他非线性)插值?

Posted

技术标签:

【中文标题】您如何对重新采样的音频数据进行双三次(或其他非线性)插值?【英文标题】:How do you do bicubic (or other non-linear) interpolation of re-sampled audio data? 【发布时间】:2010-11-10 16:08:24 【问题描述】:

我正在编写一些以不同速度播放 WAV 文件的代码,以便波形更慢且音调更低,或者更快且音调更高。我目前正在使用简单的线性插值,如下所示:

            int newlength = (int)Math.Round(rawdata.Length * lengthMultiplier);
            float[] output = new float[newlength];

            for (int i = 0; i < newlength; i++)
            
                float realPos = i / lengthMultiplier;
                int iLow = (int)realPos;
                int iHigh = iLow + 1;
                float remainder = realPos - (float)iLow;

                float lowval = 0;
                float highval = 0;
                if ((iLow >= 0) && (iLow < rawdata.Length))
                
                    lowval = rawdata[iLow];
                
                if ((iHigh >= 0) && (iHigh < rawdata.Length))
                
                    highval = rawdata[iHigh];
                

                output[i] = (highval * remainder) + (lowval * (1 - remainder));
            

这很好用,但只有在我降低播放频率(即放慢播放速度)时才会听起来不错。如果我在播放时提高音高,这种方法往往会产生高频伪影,大概是因为样本信息的丢失。

我知道双三次和其他插值方法不仅使用我的代码示例中的两个最接近的样本值进行重新采样,但我找不到任何可以插入的好的代码示例(最好是 C#)来替换我的线性插值方法在这里。

有谁知道任何好的例子,或者任何人都可以写一个简单的双三次插值方法?如果需要,我会赏金。 :)

更新:这里有几个插值方法的 C# 实现(感谢 Donnie DeBoer 的第一个和 nosredna 的第二个):

    public static float InterpolateCubic(float x0, float x1, float x2, float x3, float t)
    
        float a0, a1, a2, a3;
        a0 = x3 - x2 - x0 + x1;
        a1 = x0 - x1 - a0;
        a2 = x2 - x0;
        a3 = x1;
        return (a0 * (t * t * t)) + (a1 * (t * t)) + (a2 * t) + (a3);
    

    public static float InterpolateHermite4pt3oX(float x0, float x1, float x2, float x3, float t)
    
        float c0 = x1;
        float c1 = .5F * (x2 - x0);
        float c2 = x0 - (2.5F * x1) + (2 * x2) - (.5F * x3);
        float c3 = (.5F * (x3 - x0)) + (1.5F * (x1 - x2));
        return (((((c3 * t) + c2) * t) + c1) * t) + c0;
    

在这些函数中,x1 是您尝试估计的点之前的样本值,x2 是您的点之后的样本值。 x0 在 x1 的左侧,x3 在 x2 的右侧。 t 从 0 变为 1,是您正在估计的点与 x1 点之间的距离。

Hermite 方法似乎工作得很好,并且似乎在一定程度上降低了噪音。更重要的是,当波加速时听起来会更好。

【问题讨论】:

对二维信号(即图像)做的不是双三次吗?您肯定是指一维(即音频)信号的三次方吗?不过我可能是错的...... @Goz:对于 1D 音频,它可能只是“立方”(我不知道)。我的部分问题是我看到的所有代码示例都是针对 2D 图形的,而我只需要一个 D。 @MusiGenesis,是的,您不想使用图形解决方案。耳朵很挑剔。您想使用一种能够提供大信噪比的解决方案。请参阅下面的答案。 我刚刚重读并注意到您将提供赏金。伙计,我怎么这么快就回答了? 我还没有选择答案,所以赏金可能还会出现。我可能会提出我的全部代表。 【参考方案1】:

我最喜欢的音频插值资源(尤其是在重采样应用程序中)是Olli Niemitalo's "Elephant" paper。

我已经使用了其中的几个,它们听起来很棒(比直接立方解决方案要好得多,后者相对嘈杂)。有样条形式、Hermite 形式、瓦特形式、抛物线形式等。它们是从 音频 的角度进行讨论的。这不仅仅是典型的朴素多项式拟合。

并且包含代码!

要决定使用哪个,您可能需要从第 60 页上的表格开始,该表格将算法分组到运算符复杂性(多少个乘法和多少个相加)。然后在最佳信噪比解决方案中进行选择——以您的耳朵为指导做出最终选择。 注意:一般来说,越高信噪比越好。

【讨论】:

伟大的链接,再次。我现在正在研究它。我刚刚从另一个答案中实现了三次插值,听起来很糟糕。 如果您使用其中任何一个 SNR 超过 60,您应该听不到伪影。如果你这样做了,你可能在实施时犯了一个错误。并且不要低估您可以轻松搞砸哪一点是哪一点以及您的“中间”值是什么。我搞砸了我的第一次尝试。你甚至可能弄乱了立方体。它有助于绘制输入和输出的部分。 :-) 呵呵,好像我总是犯错! 另外,请原谅我的密度,但链接的文章推荐这些插值器用于过采样数据,但我正在处理无法过采样的预录制 WAV 文件(除非我误解了学期?)。作者说过采样留给读者,所以我可能需要在这里再问一个问题。 当您加速时,奈奎斯特附近的任何频率都可能对您造成影响。您必须在加速之前进行低通,否则您将剪掉属于那里的声音,而不仅仅是折叠的声音(这可能是您缺乏亮度的原因)。假设您有一个想要在 44100 播放的样本。您有一个想要以双倍速度播放的样本。把低通截止点放在 11025,做滤波器,然后加速。您不希望任何频率超过 Nyquist。【参考方案2】:
double InterpCubic(double x0, double x1, double x2, double x3, double t)

   double a0, a1, a2, a3;

   a0 = x3 - x2 - x0 + x1;
   a1 = x0 - x1 - a0;
   a2 = x2 - x0;
   a3 = x1;

   return a0*(t^3) + a1*(t^2) + a2*t + a3;

其中 x1 和 x2 是在其中插值的样本,x0 是 x1 的左邻居,x3 是 x2 的右邻居。 t 为 [0, 1],表示 x1 和 x2 之间的插值位置。

【讨论】:

我喜欢。我要试试这个。 注意:由于三次插值使用 4 个样本(其中 2 个被插在它们的 2 个最近的邻居之间),您必须弄清楚如何处理第一个插值区间和最后一个插值区间波形数据。通常,人们只是在左右两侧发明幻影样本。 没问题。我上面的示例使用右侧的幻像样本。 嗯,它有效,但不幸的是,它听起来比线性插值还要糟糕。线性插值似乎会产生更多的一般低级嘶嘶声,而这个三次公式往往会产生高音调的振铃。 如果您使用频谱分析仪查看处理过的样本,您会发现嘶嘶声是本底噪声。振铃可能是来自三次调制幅​​度的一些添加频率的结果。【参考方案3】:

老实说,对于音频而言,三次插值通常并不比线性插值好多少。改善线性插值的一个简单建议是使用抗混叠滤波器(在插值之前或之后,取决于您是缩短信号还是延长信号)。另一种选择(虽然计算成本更高)是 sinc 插值,它可以以非常高的质量完成。

作为WDL 的一部分,我们发布了一些简单的 LGPL 重采样代码,可以同时执行这两项操作(请参阅 resample.h)。

【讨论】:

是的,我同意。我最终回到了直线插值,因为对线性插值的改进基本上是难以察觉的。【参考方案4】:

您正在寻找polynomial interpolation。这个想法是,您在要插值的点周围选择一些已知数据点,使用这些数据点计算插值多项式,然后找出多项式的值和插值点。

还有其他方法。如果你能忍受数学,看看signal reconstruction,或谷歌“信号插值”。

【讨论】:

我可以忍受数学,但主要是我在寻找代码示例(最好是 C# 或 C)。

以上是关于您如何对重新采样的音频数据进行双三次(或其他非线性)插值?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用音频重采样器对 IF 信号进行重采样

您如何获得 Windows 音频播放的当前采样率?

猪:如何重新采样时间序列数据?

音频重采样实现原理

如何对数据集进行子采样

将音频缓冲区从 44100 重新采样到 16000