fm synthesys

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

hi,

i'm trying some fm approches for my synth (and/or sampler).
here's what i have right now:

freq += fmDepth * fmInput * 200.0f; // linear fm: 1V/200hz
pitch += fmDepth * fmInput * 12.0f; // exponential fm: 1V/oct

questions are:
1. what are the usual "hz" to use for linear fm? i've set 200hz, but i've basically choiced it random. is there any standard as 1v/oct for exponential fm?
2. what if i'd like to implement "phase fm"? phase += fmDepth * fmInput * ... what? range [0, 1]? not sure about the range due to input signal
3. what if i'd like to use "linear fm through zero"? could I just use freq = std::abs(freq + fmDepth * fmInput * 200.0f)?
4. pedantic question: aren't them all, in the end, "phase modulation"? because what change is the increment of phase due to different freq/pitch offset. wasn't PM a better term to describe FM? :P

thanks :)

Post

1. Usually you take the FM input and scale it by modulation and that's it. [edit: to clarify, there is usually a user parameter called "modulation index" that is used to scale the modulation evelopes or whatever you have, but the parameter is part of a patch rather than something fixed set at development time]

2. The difference between FM and PM (which is what most digital FM synths do) is that true frequency modulation adds the modulation term into the phase-increment, while phase-modulation adds the modulation term directly into the phase used to lookup the waveform. In PM the phase-accumulator itself is not modulated, rather just the phase-offset used to lookup (or compute) the actual waveform.

3. The "through-zero FM" refers to the fact that if you are doing true FM (ie. actually modifying the phase increment) then the increment itself can become negative: the oscillator runs backwards if the modulation is deep enough. Your average analog oscillator can only run in one direction so those that can support "through-zero FM" usually need some extra circuitry for this. For a digital implementation, you just need to make sure that you can wrap around in both directions.

4. See question 2.

Post

freq += fmDepth * fmInput * 200.0f;
Uhm, I don't think this will lead you far.

Either use audio-rate modulation

Code: Select all

y = sin(2 pi f t + fminput(t) * fmDepth)
or use same frequency for modulator, ie.

Code: Select all

y = sin(2 pi f t + sin(2 pi f t) * fmDepth)
Using integer-ratio frequencies leads to pleasing and controlled tones.
Blog ------------- YouTube channel
Tricky-Loops wrote: (...)someone like Armin van Buuren who claims to make a track in half an hour and all his songs sound somewhat boring(...)

Post

mystran wrote: Mon Sep 21, 2020 12:29 pm 1. Usually you take the FM input and scale it by modulation and that's it. [edit: to clarify, there is usually a user parameter called "modulation index" that is used to scale the modulation evelopes or whatever you have, but the parameter is part of a patch rather than something fixed set at development time]
not sure what do you mean here :) maybe I misuderstood the whole concept. the input signal will range (let say) between -5/5v. each "V" will modulate the carriers freq by an amount right? in the case of exponential fm (i.e. by pitch), its 1 octave per V. in the case of freq, how much Hz per V? i place 200, but can be whatever value right?

DJ Warmonger wrote: Mon Sep 21, 2020 12:44 pm
freq += fmDepth * fmInput * 200.0f;
Uhm, I don't think this will lead you far.

Either use audio-rate modulation

Code: Select all

y = sin(2 pi f t + fminput(t) * fmDepth)
or use same frequency for modulator, ie.

Code: Select all

y = sin(2 pi f t + sin(2 pi f t) * fmDepth)
Using integer-ratio frequencies leads to pleasing and controlled tones.
isn't this audio-rate mod?

Code: Select all

processSample() {
	// sin
	float sinOutput = std::sin(mPhase * g2PIf);

	// fm
	float fmDepth = getParamValue(FM_DEPTH_PARAM);
	float fmInput = getVoltage(FM_IN_INPUT);

	// freq
	float freq = getParamValue(FREQ_PARAM);
	freq += fmDepth * fmInput * 200.0f; // linear fm: 1V/200hz

	// pitch
	float pitch = 1.0f; // test case

	// phase
	float phaseIncrement = freq * pitch * getSampleTime();
	mPhase += phaseIncrement;
	mPhase -= std::floor(mPhase);
}

Post

Derozer wrote: Mon Sep 21, 2020 1:15 pm not sure what do you mean here :) maybe I misuderstood the whole concept. the input signal will range (let say) between -5/5v. each "V" will modulate the carriers freq by an amount right? in the case of exponential fm (i.e. by pitch), its 1 octave per V. in the case of freq, how much Hz per V? i place 200, but can be whatever value right?
You're describing absolute linear FM (which is rarely used in analog synths at least).
1) "absolute" linear FM: sin(2*pi*t*(f+mod_index_in_Hz*modulator))
2) "relative" linear FM: sin(2*pi*t*f*(1+mod_index*modulator))
3) exponential FM: sin(2*pi*t*f*exp(mod_index*modulator))
3) PM: sin(2*pi*t*f + mod_index*modulator))

AFAIK, in analog synths "absolute" linear FM was used on very few occasions for so called linear detune. Otherwise, "relative" linear FM is referred to as linear FM, which is easy to implement with pretty much any VCO except some used in vintage ARP synths, as long as mod index is kept small enough. As you will notice, when mod_index*modulator drops bellow -1 you get negative frequency for carrier. Oscillator that can work with negative frequencies are called TZ-VCOs (simple to implement if you use triangle core VCO, not nearly as simple to implement it without artifacts).

BTW, PM and FM are equivalent just in stationary case with sine-wave modulator. FM is actually equivalent to integral of PM, so, if modulator is saw wave, or you apply lets say envelope modulation of modulator level, they are very much not equivalent.

Post

urosh wrote: Mon Sep 21, 2020 2:15 pm BTW, PM and FM are equivalent just in stationary case with sine-wave modulator. FM is actually equivalent to integral of PM, so, if modulator is saw wave, or you apply lets say envelope modulation of modulator level, they are very much not equivalent.
Actually they are not quite equivalent even in the stationary case: the integration has 6dB/octave low-pass slope, so the modulation depth in terms of PM varies by frequency.

Post

urosh wrote: Mon Sep 21, 2020 2:15 pm You're describing absolute linear FM (which is rarely used in analog synths at least).
1) "absolute" linear FM: sin(2*pi*t*(f+mod_index_in_Hz*modulator))
2) "relative" linear FM: sin(2*pi*t*f*(1+mod_index*modulator))
3) exponential FM: sin(2*pi*t*f*exp(mod_index*modulator))
3) PM: sin(2*pi*t*f + mod_index*modulator))
what is mod_index? the modulator signal is not "internal" of the plug, but come from external, as input. so i don't have any index/accumulator informations, just the amplitude of each incoming sample :borg:

Post

mystran wrote: Mon Sep 21, 2020 2:34 pm Actually they are not quite equivalent even in the stationary case: the integration has 6dB/octave low-pass slope, so the modulation depth in terms of PM varies by frequency.
Yeah, I meant "you could get same output only in the case of static sin/cos modulator *if you mess with mod index and phase of carrier*".

PM is actually not complicated at all in analog domain (if one is wiling to neglect some artifacts), so this got me thinking, is band-limited PM feasible in digital domain if I stick to saw/tri/square for modulator and carrier? (Output waveform is peace-wise linear, so it should be possible to blep it)

Post

Derozer wrote: Mon Sep 21, 2020 2:38 pm what is mod_index?
what ever you choose. If mod_index*modulator goes beyond +- 1 you will have to implement TZ osc, and that should be about it.

Post

urosh wrote: Mon Sep 21, 2020 2:47 pmPM is actually not complicated at all in analog domain (if one is wiling to neglect some artifacts), so this got me thinking, is band-limited PM feasible in digital domain if I stick to saw/tri/square for modulator and carrier? (Output waveform is peace-wise linear, so it should be possible to blep it)
Sure (same with FM). You might need to use higher order bleps (blamps etc) though, which tend to grow in amplitude with frequency. Self-modulated FM saw is quite interesting, since it generates an exp, leading to the question, whether exp is bandlimited per se ;)

Post

now i have even more confusion in my mind.
it seems that this sound pretty "FM" (with feedback integrated):

Code: Select all

float output = std::sin(mPhase * g2PIf + mPrevOutput * fmFeedback + fmInput * fmDepth)
but i have NO IDEA which kind of FM is this.
it doesn't deal at all with "phase-increment", so i don't think this is "true" FM (as mystran tell me)? PM? but there are no lookup :O haha im pretty confused

Post

Derozer wrote: Mon Sep 21, 2020 3:12 pm

Code: Select all

float output = std::sin(mPhase * g2PIf + mPrevOutput * fmFeedback + fmInput * fmDepth)
but i have NO IDEA which kind of FM is this.
It's PM (with feedback)

Post

Z1202 wrote: Mon Sep 21, 2020 2:56 pm Sure (same with FM). You might need to use higher order bleps (blamps etc) though, which tend to grow in amplitude with frequency.
Ok, I don't get it, how does blamp help with FM? I figured out that in case of saw or square PMing saw it it should be not that hard, because we only get to antialias discontinuities, but I'm puzzled by FM case (because I'm not sure that only antialiasing concern are just discontinuities)
Z1202 wrote: Mon Sep 21, 2020 2:56 pm Self-modulated FM saw is quite interesting, since it generates an exp, leading to the question, whether exp is bandlimited per se ;)
Do you mean, if we replace linear ramp in saw wave with expo segment, is it bandlimited if discontinuity is handled by blep?

Post

so if thats PM, i still don't get this:
mystran wrote: Mon Sep 21, 2020 12:29 pm 3. The "through-zero FM" refers to the fact that if you are doing true FM (ie. actually modifying the phase increment) then the increment itself can become negative: the oscillator runs backwards if the modulation is deep enough. Your average analog oscillator can only run in one direction so those that can support "through-zero FM" usually need some extra circuitry for this. For a digital implementation, you just need to make sure that you can wrap around in both direction
if i "offset" directly the phase (instead of phase increment) i end up with... well... change phase in both case :)

thus, also doing mPhase + fmDepth * fmInput could result in negative phase (example: mPhase = 0.2, fmInput = -0.8), not only using "true" fm and modulate phase increment :O

in any case, sin() will always wrap automatically... don't see the problem, probably i've misunderstand again hehe

Post

urosh wrote: Mon Sep 21, 2020 5:12 pm Ok, I don't get it, how does blamp help with FM? I figured out that in case of saw or square PMing saw it it should be not that hard, because we only get to antialias discontinuities, but I'm puzzled by FM case (because I'm not sure that only antialiasing concern are just discontinuities)
Say we FM rather than PM a saw by a saw. In that case the segments become parabolic and there are discontinuities in the first derivative whenever the modulator jumps.
urosh wrote: Mon Sep 21, 2020 5:12 pm
Z1202 wrote: Mon Sep 21, 2020 2:56 pm Self-modulated FM saw is quite interesting, since it generates an exp, leading to the question, whether exp is bandlimited per se ;)
Do you mean, if we replace linear ramp in saw wave with expo segment, is it bandlimited if discontinuity is handled by blep?
Not just a blep but bleps of all possible orders (infinitely many ones in theory). The question is whether this is going to produce a properly antialiased signal, which I personally tend to take as a more generic definition of bandlimitedness, since it allows to apply it also to the signals which do not have Fourier transform.

Post Reply

Return to “DSP and Plugin Development”