I've spent a lot of time building a modal guitar/string synth based on resonant bandpasses where there is one bandpass per mode. It's a nice method because it's very easy to tune each mode's frequency/decay directly. But I'm running into some limitations so I want to try to recreate it as a Karplus Strong waveguide model.
I've got a basic Karplus Strong synth set up for this. The biggest thing I don't understand is how to tune the decays of the partials. In modal synthesis, I would use an equation to calculate all the decays of the each partial by their frequency (ie. set the Q values of reso bandpasses, ie. Where Q = pi * time_to_reach_1/e_amplitude * frequency).
In Karplus Strong, on the other hand, you set the decay times by running the delayed signal through a filter to attenuate the signal by a varying amount by frequency:
I have a function that can graph the Q values (or times for exponential decay to 1/e amplitude) I want for every frequency for each note. Is there any way I can rapidly/efficiently/automatically turn that into a filter function I can use equivalently in Karplus Strong?
Thanks.
Converting a function for decay times/rates by frequency into a filter for Karplus Strong (waveguide)?

mikejm
 KVRist
 88 posts since 5 Apr, 2017

signalsmith
 KVRer
 9 posts since 5 Jul, 2018
Re: Converting a function for decay times/rates by frequency into a filter for Karplus Strong (waveguide)?
So for each partial, you have the the time to decay to 1/e amplitude? Great  let's call that K[f].
First, transform each one to get the amplitudereduction after one period of the fundamental (T = 1/F). I believe this should look like A[f] = exp(T/K[f]).
What you actually need inside the KarplusStrong loop is a filter A such that A[f] matches the above values. Every cycle of the fundamental, this will decay the partials by the same amount as your exponentialdecay would.
Now, exactly how you design/implement that filter is its own question. A zerophase filter (FIR) avoids phase issues, which would detune your partials relative to each other. However, if your decay times aren't smooth, you also need lots of accuracy (so that you can target each partial separately), and zerophase filters require latency (which by can't be longer than the loop itself). This design (starting from partials) also ends up with a different filter for each note/pitch.
That's the general idea, though.
First, transform each one to get the amplitudereduction after one period of the fundamental (T = 1/F). I believe this should look like A[f] = exp(T/K[f]).
What you actually need inside the KarplusStrong loop is a filter A such that A[f] matches the above values. Every cycle of the fundamental, this will decay the partials by the same amount as your exponentialdecay would.
Now, exactly how you design/implement that filter is its own question. A zerophase filter (FIR) avoids phase issues, which would detune your partials relative to each other. However, if your decay times aren't smooth, you also need lots of accuracy (so that you can target each partial separately), and zerophase filters require latency (which by can't be longer than the loop itself). This design (starting from partials) also ends up with a different filter for each note/pitch.
That's the general idea, though.

mikejm
 KVRist
 88 posts since 5 Apr, 2017
Re: Converting a function for decay times/rates by frequency into a filter for Karplus Strong (waveguide)?
Thanks man. That's super helpful. You know I've been thinking back and forth on this and these approaches both have pros and cons. With modal synthesis it's so easy to set the frequencies, inharmonicity, and decay times on the fly. You just pay for it with having to run hundreds and hundreds of bandpasses and you can run into frequency limitations with the number of partials you can muster.signalsmith wrote: ↑Wed Nov 06, 2019 5:06 amSo for each partial, you have the the time to decay to 1/e amplitude? Great  let's call that K[f].
First, transform each one to get the amplitudereduction after one period of the fundamental (T = 1/F). I believe this should look like A[f] = exp(T/K[f]).
What you actually need inside the KarplusStrong loop is a filter A such that A[f] matches the above values. Every cycle of the fundamental, this will decay the partials by the same amount as your exponentialdecay would.
Now, exactly how you design/implement that filter is its own question. A zerophase filter (FIR) avoids phase issues, which would detune your partials relative to each other. However, if your decay times aren't smooth, you also need lots of accuracy (so that you can target each partial separately), and zerophase filters require latency (which by can't be longer than the loop itself). This design (starting from partials) also ends up with a different filter for each note/pitch.
That's the general idea, though.
On the other hand with Karplus/waveguide you might get more efficiency off the start, and you can do more complex damping/routing because you can damp at multiple points in the signal flow and get outputs from those points, but then you pay for it in the struggle to build on the fly filters for the decays like you're describing and you end up having to cascade allpasses to simulate inharmonicity (which still tends to run into error at high frequencies even with the best published methods unless you're using a lot of them). And you have to deal with delay compensation like you're talking about.
I think there would be no reasonable way to automatically generate an FIR to "best fit" a smooth but complex equation of decay times to 1/e by frequency on the fly, right? So I'd have to generate filters for that in advance (ie. one per note) and save them all. It would be a lot less flexible eg. if changing other parameters of the string simulation.
I think I'm going to try to make it work using my current modal approach just by finding efficiencies where I can. If I get stuck or run out of CPU completely despite my best efforts I'll have to find a way to make this work instead. I appreciate the basic concepts. I'll have to read about FIR filters. I don't know much about making filters. I am just using prebuilt ones from GitHub etc. at the moment.

mystran
 KVRAF
 5443 posts since 12 Feb, 2006 from Helsinki, Finland
Re: Converting a function for decay times/rates by frequency into a filter for Karplus Strong (waveguide)?
Leastsquare fitting is actually reasonably fast, especially if you always sample the spectrum using the same grid, in which case you can compute LDL factorisation for the pseudoinverse in advance, but since the error can oscillate somewhat (even for a smooth target), it's hard to guarantee that such a filter keeps the feedback loop stable.mikejm wrote: ↑Wed Nov 06, 2019 5:45 pmI think there would be no reasonable way to automatically generate an FIR to "best fit" a smooth but complex equation of decay times to 1/e by frequency on the fly, right? So I'd have to generate filters for that in advance (ie. one per note) and save them all. It would be a lot less flexible eg. if changing other parameters of the string simulation.
Loop latency also quickly becomes an issue. With 16 taps at 44.1kHz, your pitch is already capped at 5.5kHz (ie. 8 samples of latency). I would personally just stick to IIR filters and if you're going to use allpass filters for inharmonicity anyway, these could be made to serve the dual purpose of damping by moving the zeroes closer to the unit circle (although the design of such a setup is probably even harder).
edit: that said, it might indeed be easier to use modal synthesis
If you'd like Signaldust to return, please ask Katinka Tuisku to resign.

MadBrain
 KVRian
 968 posts since 1 Dec, 2004
Re: Converting a function for decay times/rates by frequency into a filter for Karplus Strong (waveguide)?
There are ways to do modal synthesis with lots of modes more efficiently. For instance, you can implement modal synthesis on the top of FFT (additive synthesis): instead of calculating each mode at audio rate (which gets slow with lots of modes), you calculate each mode by adding it to the FFT's spectrum. There's a paper where they use a filter bank for this (similar to how MP3 works).

mystran
 KVRAF
 5443 posts since 12 Feb, 2006 from Helsinki, Finland
Re: Converting a function for decay times/rates by frequency into a filter for Karplus Strong (waveguide)?
There's two things to watch out for here: first, if your modes don't fall on the spectrum bins exactly, then you need a sincscatter to completely avoid timealiasing at FFT block boundaries (which can be approximated, but this leads to the usual "spectral artefacts"). Also, if you want reasonable modulation rates, you'll need to overlap the blocks heavily, which can quickly lead to a situation where spectral interpolation and bruteforce DFT on persample basis can actually end up faster.MadBrain wrote: ↑Fri Nov 08, 2019 9:21 pmThere are ways to do modal synthesis with lots of modes more efficiently. For instance, you can implement modal synthesis on the top of FFT (additive synthesis): instead of calculating each mode at audio rate (which gets slow with lots of modes), you calculate each mode by adding it to the FFT's spectrum. There's a paper where they use a filter bank for this (similar to how MP3 works).
That said, there's a large searchspace of potential solutions and I've only explored a few of them.
If you'd like Signaldust to return, please ask Katinka Tuisku to resign.