Page 1 of 1

AM LFO - Tremolo

PostPosted: Wed Sep 14, 2011 12:37 pm
by carbon14
I have two algorithms for the AM LFO. There is one global LFO for AM and it operates at a fixed frequency of approximately 3.7 Hz with one of two depths controlled by DAM
It can be applied or not per operator. The output of the LFO must be calculated once per sample and if selected, it is added to the logarithmic amplitude of the operator before the exponential lookup.

I present two algorithms that acheive the desired results. The first algorithm looks more complicated, but tests suggest to me that it's more efficient, and I think it more closely reflects the way the silicon is actually built. That's purely educated speculation on my part. The second algorithm is somewhat cleaner in lines but runs about 10% more slowly for me.

Code: Select all
#define DAM   1

unsigned char lfoStepper = 0;
unsigned char lfoAmCounter = 0;
unsigned char lfoDir = 0;
unsigned char lfoAm;

void runLfoAm(void)
{
   lfoStepper++;
   if (!(lfoStepper & 0x3f))
   {
      if (lfoDir)
         if (lfoAmCounter)
            lfoAmCounter--;
         else
         {
            lfoAmCounter++;
            lfoDir = 0;
         }
      else
         if (lfoAmCounter == 105)
         {
            lfoAmCounter--;
            lfoDir = 1;
         }
         else
            lfoAmCounter++;
   }

   if (DAM)
      lfoAm = (lfoAmCounter >> 2) << 3;
   else
      lfoAm = (lfoAmCounter >> 4) << 3;
}


Code: Select all
#define DAM   1

unsigned short int lfoAmCounter1 = 0;
unsigned char lfoDir1 = 0;
unsigned char lfoAm1;

void runLfoAm(void)
{
   if (lfoDir)
      if (lfoAmCounter <= 32)
         lfoDir = 0;
      else
         lfoAmCounter--;
   else
      if (lfoAmCounter >= 6751)
         lfoDir = 1;
      else
         lfoAmCounter++;
   if (DAM)
      lfoAm = (lfoAmCounter >> 8) << 3;
   else
      lfoAm = (lfoAmCounter >> 10) << 3;
}


In each case, the value of runLfoAm() is called once per sample, and then the value of lfoAm is available to modulate the amplitude of an operator.

Re: AM LFO - Tremolo

PostPosted: Wed Sep 14, 2011 12:46 pm
by carbon14
A couple of things worth noting.

The period of the LFO is 13440 samples, which is a number with prime factors of 2, 3, 5 and 7. I don't know if there's any significance to that, perhaps it just seemed a value that gives a good sound

The YMF262 datasheet says that the two depths of modulation available are 1dB and 4.8dB I've had real difficulty working out what that means, but using a decibel calculator I found on the net, the figures do seem to stack up.

with DAM at 0, the modulation varies from 0 to 12% of the amplitude. In otherwords, the peak volume is unchanged from that with no AM, and the minimum volume is 88% of the peak.
with DAM at 1, the modulation varies from 0 to 43% of the amplitude. So the minimum volume is 57% of the peak.

Re: AM LFO - Tremolo

PostPosted: Wed Sep 14, 2011 8:33 pm
by opl3
I confirm the 13440 samples. Very unusual length indeed. The level changes every X multiple of 64 samples, where X is usually 4 but the maximum attenuation is only 3 and zero attenuation 7. You could do that with a ping-pong counter increased every 64 samples, but I used a look-up table with 210 entries, indexed by phase counter incremented by one every sample and wrapping around at 13440 to 0, then the counter shifted 6 bits to index the table. It might be more efficient to handle the indexing somehow differently so the table is not read every sample but every 64 samples.

Based on your code, it should output identical values. 6751>>8 has a value of 26, and it goes for 192 samples, and it has a value of 0 for 448 samples, and all levels between that go on for 256 samples. And the smaller depth value is just the larger depth value >> 2, cutting the bits off before scaling.

So your implementation matches mine and MAME emulator. I assume the LFO is global and runs free, so all operators use the same global value for level.

Re: AM LFO - Tremolo

PostPosted: Thu Sep 15, 2011 2:41 pm
by carbon14
I see no reason to think that the LFO is not global.

The first of my algorithms is a ping pong counter triggered by a 6 bit counter. If I had to build this out of logic gates, that's the way that I would do it. We know from the decapsulation research that there isn't a 210 entry lookup table on the chip.

I found such a lookup table in one of the emulator source codes, but it was just a printout, so I'm not sure which emulator it was, and it could be as much as 8 years since I printed it.