更有效的线性插值?

时间:2015-01-22 12:18:32

标签: c audio interpolation linear

我正在编写一个带线性插值的粒度合成引擎。问题是当前实现的插值在CPU上比没有插值的处理重约10%。

我发布了一个简化的示例程序,展示了我应用于交错样本缓冲区的处理。有没有什么方法可以使interpolation函数内的计算更有效率,从而减轻CPU的负担?该功能完美运行,可产生非常流畅的声音。它只是应该优化的CPU消耗。

我在空闲时间编写代码,因此我不是一位经验丰富的程序员。我似乎无法绕过这个。几个星期以来一直在努力。

我已编辑代码以更正dist变量的类型不匹配。另一个编辑,以避免int / long截断结果。

int main(void) {
    long i;
    long oldlength = 5000;
    long newlength = 10000;
    double dist;
    double oldbuffer[oldlength]; // assume the buffer contains values
    double newbuffer[newlength];

    // method with linear interpolation
    for (i = 0; i < newlength; i++) {
        dist = ((double)i / (double)newlength) * (double)oldlength;
        newbuffer[i] = interpolation(dist, oldbuffer, 1, 0);
    }

    // method without linear interpolation
    for (i = 0; i < newlength; i++) {
        dist = ((double)i / (double)newlength) * (double)oldlength;
        newbuffer[i] = oldbuffer[(long)dist];
    }

    return 0;
}

double interpolation(double dist, float *buffer, short chcount, short ch) {
    //  dist    =   current read position in buffer
    //  buffer  =   interleaved sample buffer
    //  chcount =   channels contained in sample buffer
    //  ch      =   channel to be read from buffer

    long i = (long)dist; // get truncated index
    dist -= (long)dist; // calculate fraction value for interpolation

    return buffer[i * chcount + ch] + dist * (buffer[(i + 1) * chcount + ch] - buffer[i * chcount + ch]);
}

1 个答案:

答案 0 :(得分:1)

您当前的代码中存在一个值得注意的问题。这两行:

dist = (i / newlength) * oldlength;

将始终返回0,因为inewlength都是整数,因此i / newlength将返回整数结果。要解决它,只需:

dist = ((double) i / newlength) * oldlength;

接下来,您如何测量插值代码需要多10%的CPU?代码执行得如此之快,以至于任何时间/配置文件测量都将毫无意义。这也与任何体面的编译器可能在发布版本中完全优化循环这一事实相结合。

我对您的代码进行了两次快速更改,以使基准测试结果更有意义。一种是多次重复插值测试,如:

long NUMTESTS = 100000;
...
for (int j = 0; j < NUMTESTS; ++j) {
    for (i = 0; i < newlength; i++) {
        dist = ((double) i / newlength) * oldlength;
        newbuffer[i] = interpolation(dist, oldbuffer, 1, 0);
    }
}

根据需要调整NUMTESTS的值,以便为每个测试提供至少几秒的运行时间。第二是确保编译器无法优化循环。最后做一些事情:

double sum = 0;
...
for (i = 0; i < newlength; ++i)
{
    sum += newbuffer[i];
}

printf("buffer = %f\n", sum);

在VS2013中进行这些更改后,插值和非插值循环(使用上述100,000个测试循环)的相同运行时间为6.6秒。