Cabbage Logo
Back to Cabbage Site

Question about FM implementation

I’ve been working on an FM synth, and as part of my research, I read some papers from Chowning about his implementations. I also read some things by Barry Truax and how he used FM synthesis, grouping the carrier/modulation ratios into sets that range from harmonic to inharmonic.

I’ve also discovered, after some trial and error with my own code, and much other reading, that there are 3 different types of FM (or Phase Modulation). Exponential, Linear, and Linear through zero. I’ve listened to the differences between these types, and its that both exponential and linear will shift the pitch upwards, as the modulation index increases.

Currently, this is the code I’m using for my operator.

opcode FMOp, a, aaii
    aCPS, aMod, iPhase, iWaveform xin
    
    xout table3:a(phasor:a(aCPS, iPhase) + aMod, iWaveform, 1, 0, 1)
endop

Pretty much the basic FM/PM synth. But it does result in a upward shift in the pitch as the modulation index increases.

I was curious if anyone knows of any papers or something that discuss how the linear through zero method is implemented?

Thanks,

Mike

I just forward this to Victor who has a far greater knowledge of these things than I ever will, his response is below:

It’s not obvious from that bit of code how he can get an upward shift. If he has a complete instrument showing it, then I could give an opinion. I would expect that if the aCPS and the frequency of the mod signal aMod stay fixed, changing the mod index would not glide any of the partials.

Over to you :slight_smile:

to have kinda trough zero, you can hipass modulator signal, that will keep original pitch of carrier

Thanks, Rory! Unfortunately, I’m going to be down for a few days. I’m moving! I’ll prepare something when done. I’m suspecting that maybe I’m pushing the mod index further than what Victor is assuming.

I did find this last night. If you can access VCV, you can get an idea of what I’m talking about. Basically, this operator doesn’t show any kind of shift even when the index is pushed quite high.

Thanks again, Rory!

If the modulator is a sine wave this won’t make any difference?

How high are you pushing it? Might it just be aliasing you are hearing?

xout table3:a(phasor:a(aCPS, iPhase) + aMod, iWaveform, 1, 0, 1)
```here you produce phase shift, try switch to raw index in that case.
to make fm should be

xout table3:a(phasor:a(aCPS+aMod, iPhase) , iWaveform, 1, 0, 1)

I’m actually using phase modulation, so the addition of the modulator needs to remain where it is.

I actually worded that wrong, it should have been “this operator does show a shift”… And yes, I’m not sure if you get this, but I am basically using my carriers and modulators at an amplitude of 1, which I guess is quite high, as in my tests, I found that this distortion begins at around .2.

I guess it could be aliasing, but I am just curious how that VCV plugin I linked can be pushing the index. You can hear the results, and the sound keeps getting brighter and brighter, without any shift in pitch.

I really need to make an example of what I am talking about…

Yes :rofl: Post it when you get a chance. :+1:

remind me to include a video of what I am talking about with VCV…

1 Like

OK, it was bugging me, and I took a few minutes to write an example… And low and behold, no pitch shift. While there is some distortion, it isn’t what I thought.

<Cabbage>
form caption("Untitled") size(400, 300), guiMode("queue"), pluginId("def1")
keyboard bounds(8, 158, 381, 95)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1

opcode FMOp, a, aaii
    aCPS, aMod, iPhase, iWaveform xin
    
    xout table3:a(phasor:a(aCPS, iPhase) + aMod, iWaveform, 1, 0, 1)
endop

giSine ftgen 0, 0, 8192, 10, 1

;instrument will be triggered by keyboard widget
instr 1
    iDur = p3
    iCPS = p4
    iAmp = p5
    
    iCarR = p6
    iModR = p7
    iIndex = p8
    
    aNull init 0
    aModEnv = linseg(0, iDur / 2, 1, iDur / 2, 0) * iIndex
    aCarEnv = linseg(0, (iDur / 8), 1, (iDur / 8) * 6, 1, (iDur / 8), 0) * iAmp
    
    aModPitch = a(k(iCPS * iModR))
    aCarPitch = a(k(iCPS * iCarR))
    
    aMod = FMOp:a(aModPitch, aNull, 0, giSine) * aModEnv
    aCar = FMOp:a(aCarPitch,  aMod, 0, giSine) * aCarEnv
    
    outs aCar, aCar
endin

</CsInstruments>
<CsScore>
;causes Csound to run for about 7000 years...
i 1 0 10 110 1   1 1 1
i 1 + 10 110 1   1 1 2
i 1 + 10 110 1   1 1 3

e 32

;f0 z
</CsScore>
</CsoundSynthesizer>

Guess I’ll just go back to work… But I will still wonder why the VCV oscilliator is so much smoother in its transitions…

Does it improve with ksmps=1? Afaik, most VCV modules use frame based processing.

I’ll try that… But I was also thinking that I was experiencing this issue if add another modulator… Well, just did that, and I still can’t make the pitch shift. I feel I’m going crazy.