FM Synthesis (YM3812)

120 Views Asked by At

I was having a look at the manual for the Yamaha YM3812 and YMF262 and wanting to build a small software synthesizer in C++ and JS.

You can have a look at what I've made so far of a small FM Tone Generator in JS on CodePen here:

https://codepen.io/john-ernest/pen/LYXpNKr

Here are links to the manuals for the YM3812 and YMF262 for reference:

YM3812: https://c64.xentax.com/images/LSI-2438124-Yamaha-YM3812-OPL2-Application-Manual.pdf

YMF262: https://map.grauw.nl/resources/sound/yamaha_ymf262.pdf

The YM3812 manual on page 2 shows 2 equations:

F = A*sin(wct + I*sin(wmt))

The Feedback equation is:

F = A*sin(wct + BF) 

where B I presume comes from a range of values on page 19:

0, pi/16, pi/8, pi/4, pi/2, pi, 2pi, 4pi

If you'll look in the "Output PCM Code" textarea section of the FM Tone Generator you'll see a piece of JS code that gets eval'd and it looks like so:

let x2p = x*2.0*Math.PI;
let m1 = mWaveform(x2p*mFreq, A1);
let c1 = cWaveform(x2p*cFreq + m1, A2);
let f1 = cWaveform(x2p*cFreq + feedback*c1, A2);
if (feedback != 0) {
  value = f1;
}
else {
  value = c1;
}      

if the modulator and carrier waveforms are both sine waves and feedback is set to 7 (4*PI) then the above would be equivalent to:

let value = A2*Math.sin(x*2.0*Math.PI*cFreq + A1*Math.sin(x*2.0*Math.PI*mFreq));
value = A2*Math.sin(x*2.0*Math.PI*cFreq + 4*Math.PI*value);

That doesn't sound right for feedback though, I should be getting noise at feedback 7 (4*PI), with modulator and carrier set to sine waves at 130.81Hz (C-3), going by an AdLib tracker I'm comparing to in DosBox.

This sounds closer to what I hear in DosBox but doesn't seem right since no documentation seems to mention re-running the feedback multiple times, I made it do 10 iterations at the selected feedback on the modulator:

let x2p = x*2.0*Math.PI;
let m1 = mWaveform(x2p*mFreq, A1);
let iterations = 10;
if (feedback != 0) {
  for(let i = 0; i < iterations; i++) {
    m1 = mWaveform(x2p*mFreq + feedback*m1);
  }
}
let c1 = cWaveform(x2p*cFreq + m1, A2);
value = c1;

I've had a look at some emulator sources e.g. Mame but it's a tad hard to follow since they use lookup tables to emulate the YM3812 exactly, and I don't have anything set up for said projects for step debugging, whereas I'm making a more generalized synth and later tracker or MML interpreter that can do pulse, triangle, and other waveforms.

0

There are 0 best solutions below