3
$\begingroup$

I have been working on a physical modeling string (eg. guitar/piano) synthesizer which is nearing completion. It is based on a modal array of resonant bandpasses, where each bandpass is set by Q and frequency to a given mode/partial of resonance.

The basic architecture can be summarized by this simplified diagram (in reality there would be 50-100+ bandpasses):

enter image description here

To summarize, an exciter signal (eg. impulse) is fed into the array of resonant bandpasses. Their outputs are summed. To simulate external damping effects, for example from the instrument body or a hand muting the string (palm muting), a filtered or fractionated sample of the prior sample's summed output is subtracted from each of the bandpasses' inputs to damp them accordingly.

This works from a musical standpoint. However, the feedback damping loops are pushing higher frequency bandpasses into overload on aggressive damping settings. This can be solved by increasing sample rate, but this is computationally unfeasible.

In general, resonant bandpasses have a behavior I have identified where they will self-oscillate or overload if a large percent of their prior sample's output is subtracted from their input above a certain frequency relative to the sample rate.

This can be demonstrated just by setting up a single resonant bandpass in a similar damping feedback loop with an impulse exciter. Roughly, if I recall correctly, at about 48 kHz sampling, the bandpass will overload if you subtract 100% of its z_1 output from its input at above ~8 kHz. Increasing the sampling rate to 96 kHz moves the threshold for overload up to ~17 kHz.

I think this is happening because at higher frequencies, the amplitude difference between one sample and the next becomes more significant, and at a certain point it becomes so significant that the difference itself can start driving the bandpass.

The problem is I don't see an obvious solution besides increasing the sample rate to maybe 4x or 8x the typical 44.1 kHz or 48 kHz which becomes massively costly for such a complex synthesizer.

With a single bandpass and fractionated damping loop it is actually easy just to do a simple approach of testing at what frequency cutoff you begin getting overload. Then you can restrict the fraction you allow subtraction of at that frequency. eg. At 9 kHz can at most subtract 80% of prior sample's input. At 11 kHz can at most subtract 30% of prior sample's input. You can build a simple table like this to protect the bandpasses.

The problem is because I am using the summed outputs of many bandpasses in the feedback loop, the behavior seems less predictable. It depends on the number of bandpasses in the array and the note being played as they all have parameters to sound different. Furthermore when you're dealing with filtered feedback loops it's practically impossible to quantify how much you're subtracting at any given frequency (it might cost as much CPU to figure out how much you're subtracting at each frequency bandpass as to just oversample).

So as far as I can tell, I am basically stuck with oversampling as my only solution. The final alternative is to switch it over to a filtered delay loop architecture (Karplus Strong) like this (where the delay is 1/noteFrequency):

enter image description here

That type of architecture does not seem capable of self-oscillation, since it actually depends on the prior sample's output to be input each sample. And if you subtract it completely (full damping) it just kills the loop. It is not capable of overload no matter how aggressively you damp it or what frequency it's set to. The problem with this architecture is it has massive flaws in terms of the quality of synthesis you can attain. So it's just trading one set of problems (CPU limits) for another (bad sound) which is not favorable.

So I think I'm basically stuck with increasing sample rate as the solution. I am just curious if anyone knows or can think of another solution to this problem. Or if I am correct that this is the only practical solution so I don't waste any more time trying to think of another solution that doesn't exist.

Thanks.

$\endgroup$
1
$\begingroup$

In general, resonant bandpasses have a behavior I have identified where they will self-oscillate or overload if a large percent of their prior sample's output is subtracted from their input above a certain frequency relative to the sample rate.

Or, worded in another way, resonant bandpass filter have a property where if, through feedback, you turn them into oscillators they will oscillate.

If you wish to change the properties of your resonant bandpass filters (to, for instance, change the Q), then rather than playing feedback games, you should change the values of their coefficients.

I can't tell you the details of how to do that in a way that sounds good, because I do communications and control systems stuff, and practically no audio DSP. But I can tell you that it's a problem that other people have solved. I can also tell you that the biggest problem you have is that suddenly increasing or decreasing a parameter value can "pump" the output, so if you change that parameter value significantly slower than the resonant frequency of the filter, you'll probably be fine.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks Tim. Yeah, in principle most effects could be done by adjusting the Q's of the bandpasses in the arrays directly. But it is prohibitive. Say for example I have one array for horizontal string vibrations and another array for vertical vibrations, with slightly different tuning. Then I need to couple them, which involves taking a bit from each and transferring to the other (and vice versa) every sample. It would be near impossible to calculate the exact Q changes that would result in each array. Feedback manipulation solves this. I will live with oversampling or switch to Karplus-Strong. $\endgroup$ – mike Dec 25 '19 at 7:46
1
$\begingroup$

You have build a classical feedback loop system here and the regular stability criteria apply.

You could try to calculate the poles of the closed-loop transfer function and make sure they are inside the unit circle. However, given your topology that would be a fair bit of work.

Easiest would probably to make sure that your loop gain is smaller than unity for all frequencies at all filter settings.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks Hilmar. "Easiest would probably to make sure that your loop gain is smaller than unity for all frequencies at all filter settings." I think this is exactly what I need to do. But I'm not sure how to do it. Let's say I am in bandpass#40 set to a resonant freq of say 3 kHz. I have that bandpass's prior sample's output for reference. I also have the sample that is going to be subtracted from its input next (a percent of the total array's sum for example). Is there any way I can calculate if that feedback subtraction is going to trigger a problem and attenuate it accordingly? Thanks again. $\endgroup$ – mike Dec 25 '19 at 21:25
  • $\begingroup$ You need to calculate the overall open-loop transfer function from "sum_out" on the left to "sound_out" on the right for your block diagram. You can ignore the "impulse" input. Basically that's the sum of all the resonant bandpasses (in whatever way these exactly are defined) cascaded with your damping filter which is really just a first order lowpass. $\endgroup$ – Hilmar Dec 26 '19 at 15:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.