Matthias Müller
Matthias Müller

Reputation: 23

more efficient linear interpolation?

I'm writing a granular synthesis engine with linear interpolation. The problem is that interpolation as currently implemented is roughly 10% heavier on the CPU than the processing without interpolation.

I am posting a simplified sample program showcasing the processing I apply to an interleaved sample buffer. Is there any way I could make the calculation inside the interpolation function more efficient and thus less heavy on the CPU? The function works perfectly and results in very smooth sounding audio. It's just the CPU consumption that should be optimized.

I write code in my free time and I'm therefore not a very experienced programmer. I can't seem to wrap my head around this. Been struggling with this for weeks now.

I've edited the code to correct a type mismatch of the dist variable. Another edit to avoid int/long truncated results.

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]);
}

Upvotes: 1

Views: 2135

Answers (1)

uesp
uesp

Reputation: 6204

There is one notable issue in your current code. The two lines:

dist = (i / newlength) * oldlength;

will always return 0 because i and newlength are both integers so i / newlength will return an integer result. To fix it simply do:

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

Next, how are you measuring that the interpolation code takes 10% more CPU? The code as is executes so fast that any time/profile measurement would be mostly meaningless. This is also combined with the fact that any decent compiler could probably optimize the loops entirely away in a release build.

I made two quick changes to your code to make benchmarking results more meaningful. One is to repeat the interpolation tests multiple times like:

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);
    }
}

Adjust the value of NUMTESTS as needed to give you at least several seconds of runtime per test. The second is to make sure the compiler cannot optimize the loops away. At the end do something like:

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

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

With these changes in VS2013 I get identical run times of 6.6 seconds for both the interpolation and non-interpolation loops (using 100,000 test loops as above).

Upvotes: 1

Related Questions