Frequency modulation feedback behaviour

Hello, I’m looking for some help understanding how different oscillators respond to FM. I’m just starting to mess around with plugin development, and while I know a little C++ I’m very new to DSP, so apologies if I’m missing anything obvious.

As I understand it, most oscillators are working by constantly advancing a phase variable, that cycles between some numbers (between -0.5 and 0.5 for example), and that phase value is passed to something that produces the actual audio wave, like a lookup table or a sin function. I’ve been messing around with the plugin development tutorial example and have a 2 operator FM synth, where one operator oscillates another via the modulators value being added to the phase as an offset

phase += freq * time + fmMod

This is working quite well for what I want it to do, but I’m sure there’ll be some more sensible way of doing this.

Where I’m running into trouble is understanding how to implement feedback on one of the operators. I can get an operator to modulate itself, but it doesn’t respond the way I’d like. As I increase the feedback intensity, it reduces the overall pitch of the oscillator and gradually evolves from a standard sine wave to a more pinched, pulsey wave. This is the same behaviour as when the sin output from the VCV VCO-1 is patched into it’s own FM input, as with a few other oscillator modules I’ve tried it with. What I’d like to achieve is the same kind of feedback that can be found in Bogaudio’s FM-OP and Valley Audio’s Dexter. Unfortunately the source code for both of these are a bit too complex for me to be able to figure out what they’re doing differently.

This is the Bogaudio oscillator core

And here’s the Valley Audio oscillator:

With Bogaudio’s sine oscillator, there’s an option to set the modulation type to linear or exponential, with the exponential setting having the behaviour I want, but exponential mode still looks to me to be adding a phase offset.

Would appreciate being pointed in the right direction here!

1 Like

The only FM Synth I have studied in detail is the Yamaha Operator Type-L as seen in the OPL, OPL-2 and OPL-3 chips used in PC soundcards.

For those devices, the feedback works simply by taking the output of one of the operators, and adding it linearly to the phase shift of the operator. With feedback turned up to the max, the phase shift can be as much as ±4π, but when you dial down the feedback this attenuates the feedback signal path.

The Yamaha chips I mentioned are pure digital devices of course, and there is a subtlety which is that the TWO previous output samples are summed to provide the feedback signal.

I’ve been messing round with this for a few days trying to get my sin oscillators to behave this way to no avail, but the solution was very simple in the end. I’d been implementing the feedback the same way I’d been adding regular FM: when I was accumulating the phase:

phase += freq * time + fmMod + feedbackAmount * previousSample;

if (phase >= 0.5f)
{
	phase -= 1.f;
}
else if (phase <= -0.5f)
{
	phase += 1.f;
}

sine = sin(2.f * M_PI * phase);

I tried various permutations on this but every time when feedback was added it would shift the fundamental pitch down. Eventually when enough feedback was added the oscillator would lower enough to “stall”, and in some of the permutations wouldn’t start back up again since a zero had been introduced somewhere.

I found that most of the oscillator modules available in the Rack library had similar “pinched sine” behaviour when they were self-patched to feedback:

As opposed to the more sawtooth style wave that feedback on Bogaudios modules showed:

This sawtooth style feedback is what several FM VSTs have (Dexed, Arturia DX7, Ableton Operator).

It turns all that needed to change was where I was adding the feedback. Introducing it within the sin() function provides exactly what I’m looking for!

phase += freq * time + fmMod;

if (phase >= 0.5f)
{
	phase -= 1.f;
}
else if (phase <= -0.5f)
{
	phase += 1.f;
}

sine = sin(2.f * M_PI * phase + feedbackAmount * previousSample);

This is probably the sort of thing that is obvious if you know anything about DSP but it took me a while to realise, hopefully this post will save anyone else the trouble I went through.

2 Likes

Rarely is anything obvious in DSP.

@Omri_Cohen did a nice video recently about linear, exponential, and through-zero FM. It’s not specifically geared toward writing DSP, but it provides some explanation for the different behaviors you encountered:

anyway the feedback behaviour you are describing is not the one implemented in dx7 synths. The dx7 operators have a delay buffer of the last sample to reduce correlations