我正在编写一些以不同速度播放 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#) 。
有谁知道任何好的例子,或者有人可以编写一个简单的双三次插值方法吗?如果有必要的话我会悬赏这个。 :)
Update:这里有几个插值方法的 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 方法似乎效果很好,并且似乎在一定程度上降低了噪声。更重要的是,当波浪加速时,听起来似乎更好。