Я пишу код, который воспроизводит файлы 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.
Кажется, что метод Эрмита работает довольно хорошо и, похоже, несколько снижает шум. Что еще более важно, кажется, что звук становится лучше, когда волна ускоряется.