[gen~] additive synthesis efficiency problem

James Harkins's icon

A student used an LLM to populate a gen~ with an additive approach to plucked string synthesis.

It's a bit CPU-heavier than I'd like (well, kind of a lot CPU heavier, particularly if the goal is to introduce polyphony later).

An "elapsed" counter is initialized when a trigger signal is received ("prev <= 0 && now > 0"); otherwise, it's incremented by 1/sr. Then a loop drives it:

limit = min(20000, f1*256);
i = 1;

for (fn = f1; fn < limit; fn += f1) {
    i += 1;

    // Modal force amplitude
    an = sign * K * sin(i * 3.14159265359 * p) / i;

    // Individual decay time
    dn = tau0 * pitchDecay / pow(i, alpha);

    // Modal oscillation and exponential decay
    sn = an * exp(-elapsed / dn)
         * cos(6.28318530718 * fn * elapsed);

    // Accumulate
    sum = sum + sn;
}

I'm leaving out a few details that are not relevant to the code structure.

I'm not sure of the relationship between the code box contents and the eventual llvm stuff, so I'm at a bit of a loss to suggest how to make it more efficient. If it were standard imperative code, I would perhaps cache the 'an' values and not do sin() every time, and replace the pow and exp with cascaded multiplications. Not sure how gen~ handles those.

Any ideas?

hjh

Roman Thilenius's icon

sin() ist very cheap on modern processors, given that the poor thing might end up in a [poly~ foo up 16] i don´t think a lookuptable will be any better.

MakePatchesNotWar's icon

Computers really don't like division. I would see if you can replace them with multipliers especially when used in heavy math functions such as here. If you know the limit its 1/n before your loop and just multiply it by i*1/n within the loop itself.

Source:
https://cycling74.com/forums/need-help-cleaning-up-a-gen-patch

At one point in the thread here i removed 8-ish divi...ders? and got a performance increase of 10% in that case so that was pretty convincing of the benefit of doing so