## Phase Generator

An investigation into the behaviour of the Yamaha OPL3 chipset.
With a view to a more accurate emulator

### Re: Phase Generator

The Vibrato is horrible to work with.

I believe I know what it's doing, but I don't quite have all the figures yet to back up my suspicion.

The amount of phase shift introduced by the vibrato, has to be proportional to the frequency of the sound. In fact the chip uses an approximation, and it's quite a broad approximation at that.

The FM LFO has a wavelength of 8192 samples, and operates in eight different ways, each for 1024 samples. If I draw up a little list of values, and then go on to explain them, each value is applied for 1024 samples, then it starts again.

+2, +3, +2, 0, -2, -3, -2, 0

In my list, the amplitude of the number represents how many significant bits of F-NUM are added or subtracted to make the phase modulation, the sign of the number indicates whether that's an addition or a subtraction.

So for each of the first 1024 samples, the F-NUM value is modified by adding to it the most significant 2 bits (i.e. the 10-bit F-NUM is shifted 8 bits to the right and then added to F-NUM)
For the next 1024 samples, the F-NUM value is modified by adding to it the most significant 3 bits (i.e. the 10-bit F-NUM is shifted 7 bits to the right and then added to F-NUM)

In later samples, the same values will be subtracted rather than added, which means that the waveform drifts in and out of it's original phase over the 8192 sample wavelength.

Note that the above figures are for the greater depth option, the documentation says this is 14%. For the 7% option the shifts are 1 bit further, (+1, +2, +1, 0, -1, -2, -1, 0)

Note that for F-NUM < 128 there is no discernable vibrato at all. This is perhaps significant because you can produce a note from an F-NUM < 128 which is the same frequency as another note with F-NUM > 128 by changing the BLOCK value. But although they are the same frequency, they will show different levels of vibrato.

The MAME emulator does all of this using a 128 entry lookup table. The lookup depends on the most significant 3 bits of F-NUM, the DVB and the 10th bit of the LFO. The resultant looked up value is added to the increment prior to the BLOCK and MULT shifting. I'm sure that this lookup is efficient, and may be the best approach for efficiency, but it doesn't explain where the figures come from. I think that the figures in that lookup table can be explained by what I've described above.

Because this phase modulation depends on F-NUM, it has to be calculated per-sample. Unlike the AM tremolo, for which two values can be calculated for the entire device just once every 64 samples, the VIB values have to be calculated per operator, per sample.

carbon14

Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

### Re: Phase Generator

I don't know if an example is helpful.

If I have F-NUM = 128, BLOCK = 3
without vibrato, the increment is 512 per sample; Which is enough to advance the waveform 1 step per sample.

With deep vibrato enabled.

The F-NUM is 128 + (128 >> 8) for the first 1024 samples. Which is 128. The increment is 512
For the next 1024 samples it is 128 + (128 >> 7) or 129. The increment is 516
for the next 1024 samples it is 128 + (128 >> 8) or 128. The increment is 512
for the next 1024 samples it is 128 + 0
for the next 1024 samples it is 128 - (128 >> 8). The increment is 512
for the next 1024 samples it is 128 - (128 >> 7) or 127. The increment is 508

etc

So for a period in the 8192 samples, the waveform runs slightly faster, and then half a cycle later, it runs correspondingly slower.

If we change F-NUM to 256 and BLOCK to 2, we have the same fundamental frequency, but now the more more significant bits of F-NUM are 010 instead of 001. This leads to the following:
initially the increment is 256 + (256 >> 8) or 257 and an increment of 514
then the increment is 256 + (256 >> 7) or 258 and an increment of 516
then 514
then 512
then 510
then 508
then 510
then 512

carbon14

Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

### Re: Phase Generator

I thought vibrato to be harder to solve, I haven't even figured out where to start with it. Also amazing that only 3 bits are used and added to FNUM domain, as it could have been done in the phase increment domain with more bits to get more steps while keeping the range.

Yes it makes sense it is proportional to the frequency (FNUM) because that is how it is in real world and synthesizers. For example the portamento depth can be given in semitones and perhaps the vibrato depth is given in cents (100th of a semitone, 1200 cents is octave). So a given amount of cents for 440Hz tone is more in hertz or phase increments than same amount of cents or phase increments for a 220Hz tone because of the logarithmic scale. So they may have mixed cents and percents in documentation. As it seems, for large vibrato depth with FNUM=0x380, vibrato range of +/- 7 meanins 0.78 *percent*, but it rounds being 14 *cents*.

In case you are wondering why low values of FNUM like 128 barely cause any vibrato, the whole idea of the chip is to give the base note with FNUM, and change the octave with BLOCK, perhaps do some tricks with MULTI to make it interesting. Rarely MULTI is changed during a tune, it is more specific to instrument characteristics, while played notes have their FNUMs are taken from note table and BLOCK set by octave.

To get the most accurate frequency, the largest FNUM values as possible are used for different notes. As you have seen the use of Note Select (NTS) bit in register 0x08, it selects what kind of FNUM values are encountered within an octave. You can select with NTS that all FNUM values within an octave have their MSB (0x200) set, so the split point is taken from the second most highest bit (0x100), meaning all FNUM values 0x300..0x3FF are the high notes and all FNUM values 0x200..0x2FF are the low notes. The other selection with NTS bit is to select FNUM values so that notes 0x200..0x3FF are the high half notes of an octave and notes 0x000..0x1FF are the low half notes of an octave. It makes things a bit more hard to use just 8-bit FNUM values as the chip features expect 9 or 10-bit values.

Oh and the vibrato increment based on FNUM does not need to be recalculated every sample, only every Nth sample (1024 you say) when vibrato phase changes, or except if FNUM has been changed. What is best depends on what kind of interface you have for your emulator, but it can be recalculated for each sample as well.

Btw, for what I understand, does it go like this? The vibrato phase could just internally be how much the 3 MSBs are shifted before summing to phase.
Code: Select all
`    uint8_t vibshiftlut[8]={0,1,3,1,0,1,3,1};    int8_t vibsignlut[8]={1,1,1,-1,-1,-1,-1,1};    VIBINC=FNUM>>7; // leave 3 MSB bits    VIBINC=VIBINC>>vibshiftlut[vibphase&0x07]; // shift out bits based on vibrato phase    VIBFNUM=FNUM+vibsignlut[vibphase&0x07]*VIBINC; // add with sign    // for every 1024 samples, vibphase++;    // tables for large vibrato depth, it seems small vibrato depth is the table value shifted one more.    // so for FNUM=0x380 the vibrato values are +7, +3, +0, -3, -7, -3, -0, +3, matching your findings and MAME.    `

It also surprises me the phase increments are in the range of -7..+7, and not -8..+7, so in this case normal 2s complement is used instead of 1s complement.
opl3

Posts: 55
Joined: Sun Sep 26, 2010 8:11 pm

### Re: Phase Generator

I think your code fragment is correct. As to which value is the first value in the lut, I don't know. I'm assuming that the LFO runs from the moment the chip is powered, so I'm not sure how I could actually reliably detect the lut value. Although it might be possible. I'll think about it.

Anyway, below is my code fragment. It calculates the vib-phase change per sample, but that's really a reflection of the emulator architecture that's in my head. As you say, if you trap changes to FNUM, then you can recalculate it there. I used a switch rather than an lut.

I took my original line of
Code: Select all
`phaseGen += ((((FNUM << BLOCK) >> 1) * multTable[MULT & 0xF]) >> 1);`
and replaced FNUM with pginc which is FNUM with the Vib modulation

Code: Select all
`      lfoVibCounter++;      pginc = FNUM;      if (VIB)      {         switch (lfoVibCounter & 0x1C00)         {            case 0x0400:            case 0x0C00:               pginc += FNUM >> (DVB?8:9);               break;            case 0x0800:               pginc += FNUM >> (DVB?7:8);               break;            case 0x1400:            case 0x1C00:               pginc -= FNUM >> (DVB?8:9);               break;            case 0x1800:               pginc -= FNUM >> (DVB?7:8);               break;         }      }      phaseGen += ((((pginc << BLOCK) >> 1) * multTable[MULT1 & 0xF]) >> 1);`

carbon14

Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

Previous