Phase Generator

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

Phase Generator

Postby carbon14 » Wed Aug 03, 2011 11:05 am

I had an algorithm in mind for the basic phase generator. This is the part which takes the F-NUM and the BLOCK values and somehow generates the correct frequency waveform. It basically needs to cycle through a 10-bit output that feeds into the wave lookup table. The cycle is modified by the Feedback, the FM synthesis and the vibrator LFO, but leaving those modifiers aside, it simply has to cycle from 0 - 1023 at the correct rate.

I had a simple algorithm in mind, that simply adds the F-NUM value into a 20-bit counter, and the uses the BLOCK value to shift a 10-bit window in that counter to output. But it then occured to me that there was an alternative algorithm, wherein the 10-bit window is fixed and the F-NUM value is shifted before it is added to the counter. I wasn't sure which was correct. If the F-NUM and BLOCK values are fixed, then the results of the two algorithms are identical. But I realised that if the BLOCK value changes during the lifetime of a note, then there are subtle differences.

I experimented last night, and discovered that the second algorithm is correct. Which should have been obvious from the start, the first algorithm can result in step-changes to the phase when the BLOCK changes, and this would add noise to the signal. The second algorithm avoids this problem altogether.

Here is an example using the first, moveable window algorithm. We start with certain values for F-NUM and BLOCK. F-NUM is added to the counter each sample and then after a certain number of samples we change the value of BLOCK. The resulting output for the wave phase is given in the right-hand column

PGalg2.png
Moveable window
PGalg2.png (49.39 KiB) Viewed 7469 times


In the second algorith, the window is fixed, and when the BLOCK value changes, we use it to shift the value of F-NUM which is added to the counter.

PGalg1.png
Fixed window
PGalg1.png (48.28 KiB) Viewed 7467 times
User avatar
carbon14
 
Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

Re: Phase Generator

Postby opl3 » Wed Aug 03, 2011 2:50 pm

I agree with you on this. In my emulator code (as well as in many other emulators) it is always the 10 MSBs of the phase accumulator that is used to index the waveform table. Basic operation of DDS waveform generation. Here is some pseudocode.

Code: Select all
uint32_t phase;
uint32_t increment;

increment=(FNUM*MULTI)<<BLOCK;

output=waveform[(phase>>10)&0x3ff]; // 1024-entry waveform table
phase=phase+increment;


In actual code, it might be beneficial to shift numbers even more and have phase>>16 because it might be possible to compute it more efficiently.

However that is correct when MULTI=1, and I guess 1 or larger. The ambiguity is when MULTI=0.5.

It is either like this:
Code: Select all
if (MULTI==0)
 increment=(FNUM>>1)<<BLOCK; // drop 1 bit of FNUM first, then shift by BLOCK
else
  increment=(FNUM*MULTI)<<BLOCK;


or like this:
Code: Select all
if (MULTI==0)
    increment=(FNUM<<BLOCK)>>1; // shift by BLOCK first, then shift back 1 bit
else
  increment=(FNUM*MULTI)<<BLOCK;


I think it is the first one, as my ancient experiments showed that the waveform does not advance when BLOCK=0, MULTI=0.5x and FNUM is 0 or 1. If I am correct.

I could attach a tune rendered by my emulator code, but the file is too big. Does not support much features yet, but in case you are interested, I could upload the output somewhere. The emulator plays back a register dump file, that is created from thealibi.d00 tune by the original DOS player routine, but by writing the register writes into file instead of Adlib ports. Player updates registers 70 times per second, and then calls emulator to create 1/70 seconds worth of samples.

By the way, I shot myself in the foot by thinking that when modulator attack rate is 0, the attennuation is so high that the output is never anything else than 0, so it does not result into carrier phase changes in FM mode or carrier amplitude changes in AM mode. But negative part of the waveform is still -1 which does affect phase or amplitude, so make sure you use waveforms that are only positive.
opl3
 
Posts: 55
Joined: Sun Sep 26, 2010 8:11 pm

Re: Phase Generator

Postby carbon14 » Thu Aug 04, 2011 1:02 pm

Yes, I had noticed that the -ve part of the mod waveform has an effect on the phase of the carrier.

I haven't got to the MULT register yet. It's imminent on my list. The test you describe where F-NUM = 0, BLOCK = 0 and MULT = 0 was where I was heading.

Right now, my next test is with FB...

You need to store the last two outputs and use them for feedback. But they get reduced in size by the value of FB. The question is does that happen before you store them, or when you retrieve them to modulate the phase.

Changing the value of FB in the middle of a wave would show different results under the two options. In the first, you will end up with a sample that's modulated by one sample at the old FB and one sample at the new FB. Whereas under the second, the both of the feedback samples are modulated by the same amount, either both at the old FB or both at the new.

That's my next experiment.

I just wish I could find more time to do this. I'm getting in less than half an hour a week on this at the moment.
User avatar
carbon14
 
Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

Re: Phase Generator

Postby opl3 » Sat Sep 10, 2011 7:29 pm

I'd like to know if you have any more information about how operator feedback works?

I have gotten good results with the following pseudocode, but so far FNUM has been 0, so phase generator depends only on feedback.

Code: Select all
   
    if feedback_register>0 then
    {
      feedback_shift=feedback_register+1; // feedback shift is from 2 to 8 if register value nonzero     
      feedback_phase=(prev_out1+prev_out2)<<feedback_shift;
    }
    else
    {
       feedback_phase=0;
    }
   
    pcmval=makelookup(waveform, (phaseaccumulator+feedback_phase)>>10, attennuation);



Well because the phase increment is zero, phaseaccumulator is always zero as well, and thus feedback is so small that it does not affect phase unless feedback register is 5, 6, or 7. Values below 5 just cause the PCM output to be forever 12. When feedback is 5, 6 or 7, I get the same sequence of output values as from real chip.

However I am not sure what would happen in a situation where phase increment is nonzero, so is it really
Code: Select all
(phaseaccumulator+feedback_phase)>>10
or
(phaseaccumulator>>10)+(feedback_phase>>10)
or something else. That experiment would have to be conducted so that feedback values between 1 and 4 are also covered.

I also witnessed a timing based anomaly when trying to figure out the waveform table myself. I started a sound with additive modulation on one channel, so the outputs of two operators were summed together. It seems that setting the key-on bit right when the chip has processed the other operator but not the other yet, the output samples were not identical from both operators, the other operator lagged by one audio sample. But this has occured so far only once. Basically this explains if sometimes an instrument sounds different than usually, I think I have heard this on OPL2 when using some Creative FM Organ kind of software. This is something that will happen on a real chip, even OPL3, but not a wanted feature on an emulator nor easy to implement.

The next thing my emulator code really needs is the support for envelopes. So far I have the exact attack table values figured out except for the first level, by running the attack phase with each total level setting (64 times), but still have to work with how the envelope rate defines how much the envelope is changed per sample.
opl3
 
Posts: 55
Joined: Sun Sep 26, 2010 8:11 pm

Re: Phase Generator

Postby carbon14 » Mon Sep 12, 2011 2:05 pm

I've been really busy so although I did do some more work, I've not had a chance to write it up.

The feedback is not affected by the F-NUM value. The phase generator seems to work as a counter with perhaps 20 bits. Every sample you add to it a value which is the F-NUM, shifted by the BLOCK and multiplied by the MULT (kind of). Then the upper 10 bits of the counter give you the pure phase for this sample. You take that value, add in the feedback value (if any) or add in any FM-input from another operator, and that value (mod 1024) is now the phase value to lookup in the wave tables. At some point the VIB-LFO has some input here, but I've not got to that yet. I have figured out the AM-LFO but that simply affects the attenuation later on.

/*
I occasionally notice what looks like the same issue with the two operators being out by one. I know that the documentation for the chip says that it takes a minimum of 64 clock cycles to write to a register. which is about 4.44 microseconds. I know that my qbasic code makes no attempt to ensure that it doesn't go too fast (as if -- qbasic on my poor little 486 dx!). I seem to remember one of the opl3 emulator source files that I read said that this was a difference between the opl2 and the opl3, that the waiting was no longer necessary. It may be correct that the write times are shorter for the opl3, but the documentation is quite clear that they are not zero. Anyway I don't know if write timing issues are causing the occasional blip.
*/

Edit: Having re-read what you actually said. I understand the specific timing issue that you are referring to. Since it takes 288 clock cycles to process one sample, I'm sure that you can indeed write registers in the middle of sample processing. I would guess that it's possible to synchronise your writes to the chip if you really want to. The faster of the two programmable times ticks once every 4 samples. I'm guessing that its processing is synchronised to the sample clock rather than the master clock. I certainly don't think that I'm going to worry about it in an emulator.
User avatar
carbon14
 
Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

Re: Phase Generator

Postby carbon14 » Mon Sep 12, 2011 2:17 pm

I think I've only got two more experiments to do to pin down what I'll call the steady state behaviour of the sounds; i.e. continuous sustained tones. I need to work out VIB (and DVB) and I need to work out KSL (and NTS) The latter are reasonably well documented, so it should just be a case of confirming the expected behaviour.

That would be a milestone.

The next leg will have to be the envelope. I think there's a lot less going on with the envelopes than elsewhere, but where I've looked at them already I've found them a little confusing so far. As far as I can see that really just means worrying about AR, DR, SL, RR, KSR (and NTS again) and KON

leg's 3 and 4 would be the rhythm section, and the timers.
User avatar
carbon14
 
Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

Re: Phase Generator

Postby carbon14 » Tue Sep 13, 2011 12:40 pm

This is my rudimentary phase generator:

Code: Select all
#define FNUM   1
#define BLOCK   0
#define MULT   2

unsigned char multTable[16] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30};

int main(int argc, char *argv[])
{
   unsigned long int phaseGen = 0;

   for (i = 0; i < 4096; i++)
   {
      phaseGen += ((((FNUM << BLOCK) >> 1) * multTable[MULT & 0xF]) >> 1);
      printf("%d\n", (phaseGen >> 9) & 0x3FF);
   }
}


This is the basic phase counter. It successfully matches all of the conditions that I found where the phase does not advance. Typically where FNUM = 0 or 1 and BLOCK = 0, but also where MULT is low.

I have realised however, that this phase counter (stored in the phaseGen variable) has got to be per operator, because it advances with the MULT value. I think that this must be correct, if the phaseGen advanced per connection and then MULT was applied, then a value of MULT=0 could not prevent the phaseGen from advancing.

What this would mean however, is that by varying the value of MULT during a sound, you could end up with the two operators out of phase. I haven't attempted this so I don't know if this actually happens or not.


Having got a value from the basic phase counter, this value is then adjusted by the feedback or by any FM input from the previous operator.
User avatar
carbon14
 
Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

Re: Phase Generator

Postby carbon14 » Wed Sep 14, 2011 11:00 am

I checked last night, and the phase of the two operators are not linked.

I've always assumed that the phase of the operators starts synchronised whenever KON shifts from 0 to 1. But I've actually never tested for that.

It's certainly true that by manipulating MULT during a waveform you can shift the phase of the waveforms.

The mathematics of FM has always been a little beyond me. But I don't think that a phase shift has any audible impact on an FM synthesised sound. I do know however that such a phase shift in the additive mode can have a drastic impact on the resulting sound.


Anyway. The upshot is that it looks as though each operator must be maintaining it's own phase counter.
User avatar
carbon14
 
Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

Re: Phase Generator

Postby opl3 » Thu Sep 22, 2011 6:31 am

I confirm the phase increment calculations. And both operators have different phase counter as they have different multipliers per operator, but FNUM and BLOCK are shared per channel.

I had experimented this a few years ago but did not remember the exact results anymore, so I did some for loop sweeps yesterday with a real chip, where MULTI went between 0..3, BLOCK between 0..3 and FNUM between 0..7, so 128 waveforms in less than 30 seconds.

Basically a base increment for the channel is calculated with FNUM and BLOCK. Then the MULTI is applied per operator.

When BLOCK is 1 and MULTI is 1, the FNUM value is directly the phase increment.

Setting BLOCK to 0 will shift the least significant bit out from the base increment. This implies that the 10-bit FNUM value ends up in base increment value being a 9-bit value at minimum and 16-bit at maximum, as baseinc=(FNUM<<BLOCK)>>1;

Same applies when MULTI is 0, the least significant bit of base increment is shifted out.

Meaning, when both BLOCK and MULTI are 0, two bits are shifted out.

Thus the operator increment is then just multiplied with the multiplier, or in the case of multiplier being 0.5x, it gets truncated when shifted by 1. Your table lookup and shift algorithm does this.

Edit:

But this still does not clarify how many bits from channel's base increment is transmitted to operators for multiply, and how many bits of multiply result is used to add to the phase accumulator. For all we know is that the phase accumulator itself has 9 fractional bits and 10 bits for indexing the table and that is enough, but we do not know how many bits there are in the phase increment. For instance, Yamaha DX7 chips have 14 frequency information bits transmitted between EGS and OPS chips (and 12 bits of envelope information).

For example FNUM=512 BLOCK=7 results into 17-bit number of 0x10000 before shift, is the 16-bit word result gotten from 17-bit internal value of 0x10000>>1=0x8000 or 16-bit internal value of 0x0000>>1=0x0000, so it makes a difference what is used as the source for the multiplier.

Also the multiplier operation still needs to be experimented, as high BLOCK values might shift bits out and MULTI of 0.5x won't shift them back in but just gets high bit zeroed, so having BLOCK=7 MULTI=0.5x does not necessarily match having BLOCK=6 MULTI=1x or BLOCK=5 MULTI=2x even though the formula gives the same increment of 32.

Edit2:

No bits are cropped off - FNUM=512 BLOCK=7 results into base increment of 32768, so the formula is valid when no bits are lost during the FNUM<<BLOCK shift, it must be a variable that can hold all 17 bits, so that the 16-bit result is 32768. Verified this with a for loop where BLOCK varied 4,5,6,7, MULTI varied 0.5,1,2,4,8 and FNUM varied 512,256,128,64. And a very special test case to show that when FNUM=1023, BLOCK=7 and MULTI=8, the phase increment is 0x7FE00 or 1023<<9, which will start indexing the sine wave backwards. Anyway that is a 19-bit increment number added to phase accumulator, and if the increment or phase accumulator were larger, we cannot see that as the bits are beyond the bits used for indexing the waveform table. The formula is perfectly valid over all test cases I can now think of. And, the first output of that wave is taken at wavetab[1023] so it is negative, as if the first output were from wavetab[0], it would be positive. I am using AR=15 so the attack is immediate. So it seems that when KON is set, the phase accumulator is indeed zeroed, but the phase increment is first added to the accumulator before indexing the waveform, as shown from many of the waveforms. Or in fact it may be that the phase accumulator is pre-loaded with the increment when KON is set, it might be simpler to do that in hardware. But as long as the result is identical, it does not matter how we do the emulation, we can use different techniques in C code than in FPGA code what is simplest to implement.
opl3
 
Posts: 55
Joined: Sun Sep 26, 2010 8:11 pm

Re: Phase Generator

Postby carbon14 » Mon Oct 03, 2011 3:11 pm

I hadn't considered losing bits from the top.

Thanks for verifying that situation.


If I understand you correctly at the end of your second edit: The first sample that is generated after KON has a non-zero phase. So either the increment is added at the beginning of the process of generating a sample, or the counter is initially set to the increment at the KON point. I think that's what you said.
User avatar
carbon14
 
Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

Next

Return to Yamaha OPL-3 research

Who is online

Users browsing this forum: No registered users and 1 guest

cron