如何对重新采样的音频数据进行双三次(或其他非线性)插值?
-
13-09-2019 - |
题
我正在编写一些以不同速度播放 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 方法似乎效果很好,并且似乎在一定程度上减少了噪声。更重要的是,当波浪加速时,听起来似乎更好。
解决方案
我最喜欢的音频插值(特别是在重采样应用)资源是奥利Niemitalo的“大象”的论文。
我已经使用了几个这些和他们的声音了不起(比直立方解决方案,这是相对嘈杂好得多)。有花键形式,埃尔米特形式,Watte,抛物线形等,并它们从一个音频讨论点的图。这不仅是典型的天真的多项式拟合。
和代码包含!
要决定使用哪一种,你可能要到60页,其中群体的算法集成到运营商的复杂性(多少乘法,多少增加了)上启动与表。那么最好的信号与噪声的解决方案中进行选择 - 用你的耳朵为指导,以做出最终选择。 注意:一般来说,在更高强> SNR越好
其他提示
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的左邻,且X 3是X 2的右邻。 t为[0,1],表示x1和x2之间的内插位置。
真的,三次插值不是一般比线性好得多用于音频。提高你的线性插值一个简单的建议是使用抗混叠滤波器(插值之前或之后,这取决于你是否缩短信号或延长它)。另一个选项(尽管更耗费计算)是正弦内插值,它可以以非常高的质量来完成。
我们已经发布了一些简单的,LGPL代码重新取样,可以做这两个作为 WDL 部分(见resample.h)。