New Documentation: An authoritative reference on the YM2612

For anything related to sound (YM2612, PSG, Z80, PCM...)

Moderator: BigEvilCorporation

Shiru
Very interested
Posts: 786
Joined: Sat Apr 07, 2007 3:11 am
Location: Russia, Moscow
Contact:

Post by Shiru » Thu May 07, 2009 8:43 pm

Old chips has requirements for minimal operational frequency and don't work at lower frequencies, because their internal registers are dynamic memory-based.

Lord Nightmare
Interested
Posts: 19
Joined: Sun Oct 12, 2008 10:45 pm

Post by Lord Nightmare » Tue May 19, 2009 3:49 pm

BTW, could the differences on those graphs on the prior page, between the ym2612 and the sega asic version, be because the sega asic one has the 'off by one' error on the descending portion of the sine wave fixed?

LN
"When life gives you zombies.... *CHA-CHIK!* ...you make zombie-ade!"

GManiac
Very interested
Posts: 92
Joined: Thu Jan 29, 2009 2:05 am
Location: Russia

Post by GManiac » Tue May 19, 2009 4:34 pm

When I set AR=$FF and looked at growing amplitude of sine from 0 to MAX, I noticed that multiple harmonics have the same level at all levels of sine. And figured out that they are result of "ladder effect" - it is irregular meander, it contains all multiples of main frequency, not only odd ones, but odd multiples (especially "4*n+3" ones) are stronger.
But I don't know why harmonics are also reflected from level of 52781 Hz - is it result of irregularity of "ladder"?
And eventually what causes this "ladder effect"?

And what about slowing YM down? I don't see any reports :)

upd: TA-07 in my chinese MD2 also showed same "ladder", and spectrogram made by TmEE from his MD2 contained it too.

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Tue May 19, 2009 7:33 pm

some interesting info :

AamirM asked me to record the into music of Ariel the Little Mermaid (or whatever the title was), and when you just listen it, hihats are "Tss Tss", but when I recorded the sound they went "Blau Blau" like broken emulators :shock:

I am gonna try to see if recording the output of my sound card (the card has internal loopback setup) or microphone makes a difference.... this is fun :P
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Wed May 20, 2009 7:29 pm

http://www.fileden.com/files/2008/4/21/ ... /ARIEL.RAR

a recording of the effect, first 10 seconds is with mic, and that's how things would sound, last part is line-in or loopback and it sounds like broken emulator...
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

HardWareMan
Very interested
Posts: 745
Joined: Sat Dec 15, 2007 7:49 am
Location: Kazakhstan, Pavlodar

Post by HardWareMan » Thu May 21, 2009 4:26 am

Very interesting. I think the thing in the very high frequencies that are filtered and aliased when sampling.

notaz
Very interested
Posts: 193
Joined: Mon Feb 04, 2008 11:58 pm
Location: Lithuania

Post by notaz » Thu May 21, 2009 11:09 am

Doesn't it also happen with BGMs like Battletech, Comix Zone BGM5, etc?

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Thu May 21, 2009 11:14 am

Last time I did this, they did not, but then my sound mod was in different state (I'm improving it right now and it does not propose filtering on high frequencies that much over long cables than it did before).

what HWM said is probably the cause, aliasing+reflections of the freqs that can't be handled by the ADC at given sample rate cause it.
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

HardWareMan
Very interested
Posts: 745
Joined: Sat Dec 15, 2007 7:49 am
Location: Kazakhstan, Pavlodar

Post by HardWareMan » Fri May 22, 2009 3:19 am

TmEE co.(TM) wrote:what HWM said is probably the cause, aliasing+reflections of the freqs that can't be handled by the ADC at given sample rate cause it.
Exactly.

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Thu May 28, 2009 12:53 am

Some fun stuff :

I did major undercloking in my MD2... the lowest master clock I used was 3.6MHz (NTSC color carrier or something close), that means YM and 68K ran off ~500KHz and Z80 ~200KHz... YM output is nicely ~4000Hz then :)
I would like to try lower speeds but I have no smaller crystals.

I recorded some music off my sound system and when speeding it up 14 times, I get correct speed and pitch for the song so thigns work. due to HW nuances in analog area, of course things won't sound exactly same when sped up :)

The file is too big for me to upload (it would upload forever.....). I'll see how small I can get it, if it becomes small enough I'll put it up.
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Christuserloeser
Very interested
Posts: 145
Joined: Sun Jan 28, 2007 2:01 am
Location: DCEvolution.net
Contact:

Post by Christuserloeser » Mon Jun 01, 2009 12:45 pm

Nemesis wrote:The next info I post will be on the LFO. The current LFO implementation in MAME is mostly correct, but there is an error in the phase modulation implementation which I'll detail. I've also figured out the cause of the different sound in Battletech track 12, which was discussed awhile back in this thread. I'll post a detailed description on what causes the difference in sound, but it's actually a fairly complex explanation, so it'll be quite a long post. Apart from that, I've got some more testing to perform on the operator unit relating to the way algorithms are implemented, the DAC needs some attention, and I'm going to publish my own notes on the YM2612 test register, but after that, I'll be pretty much done with the YM2612.
Did you ever post the info on LFO / phase modulation ? (I am asking because I might have overlooked something)
http://www.DCEvolution.net - Gigabytes of free Dreamcast software for you

Image

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Mon Jun 15, 2009 5:34 am

Did you ever post the info on LFO / phase modulation ? (I am asking because I might have overlooked something)
Not yet. Sorry, I've been really short on free time lately, so I've had to dial back on this stuff for a while. If you've got a specific question, I should be able to answer it. In terms of writing up a full description of the LFO, I just don't think I've got the time right now.

What I will do is try and briefly describe what's wrong in the MAME implementation of frequency modulation. Actually, I think I'll just cut and paste from my raw testing notes:
-We have confirmed that MAME implements phase modulation incorrectly. Phase modulation is applied to the 11-bit fnum value before it is combined with the block data, or any other operations are carried out using it. This is done using the same process implemented in the MAME core, but without the apparent extra bit of precision which was added, which is also what breaks the precision loss when block data is 0. The incorrect calculations most likely originated from confusion over the sign extension of the phase modulation value when it was in the negative oscillation, which would create an apparent extra bit of precision, even when all significant bits had been discarded. By repeatedly disabling the LFO before it entered into the negative oscillation phase, and carefully measuring and monitoring the output from the YM2612, we were able to observe that frequency modulation values do indeed cut off after the 11th bit, not the phantom 12th bit which MAME creates.
(Never mind all the "we" references. That means I'm in agreement with myself over what I'm writing. I write all my notes as if I have some kind of multiple personality disorder, that way I can disagree or debate with myself in note form if I'm not entirely convinced of something.)

In a nutshell, when the author of frequency modulation in the MAME core was doing hardware tests, he made a mistake in his analysis which made him think frequency modulation had one more bit of precision than it really did. He made this mistake because frequency modulation applies a signed displacement value to the base frequency, and being a signed value, that displacement value is sign-extended. As a result of the sign-extension, the frequency modulation value always has at least one bit of precision, even if all the "real" bits have been lost. This error is the source of this (erroneous) statement in the MAME core:
/* there are 2048 FNUMs that can be generated using FNUM/BLK registers
but LFO works with one more bit of a precision so we really need 4096 elements */
There is no extra bit of precision with frequency modulation. Frequency modulation is applied directly to the base fnum value, which is an 11-bit number, and this is the very first step in the phase generator, before block data is even applied to fnum. I conducted a whole battery of tests using observable overflow conditions and precision limitations during the phase generator update cycle to prove this is the case, and the current implementation in MAME (and, IIRC, in Kega as well?) is measurably incorrect.

The good news is, the correct implementation of frequency modulation simplifies the whole process greatly. The frequency modulation stage just "slots in" at the beginning of the update cycle for the phase modulator, without requiring any changes to the existing pipeline. For MAME, this means all changes which have been made to treat fnum as a 12-bit value should be stripped out.

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Mon Jun 15, 2009 9:23 am

(Never mind all the "we" references. That means I'm in agreement with myself over what I'm writing. I write all my notes as if I have some kind of multiple personality disorder, that way I can disagree or debate with myself in note form if I'm not entirely convinced of something.)


:lol:

Thanks to "all of you" anyway, makes everything easier to understand

Frequency modulation is applied directly to the base fnum value, which is an 11-bit number
ok, so you mean that the value from "lfo_pm_table" (in the MAME core) should be added to the fnum value (11 bits), NOT to the block+fnum value (14 bits, identified by block_fnum in the MAME core) like it's done actually (well it's actuallty done on block_fnum << 1 because of this increased precision thing) ?

I mean, the "block" value is not affected by the modulation, is it ? I don't really understand that part of the MAME implementation, because fnum AND block value are recalculated after the addition of the phase modulation :?

Also in that case, I would think the result should be masked as a 11 bits value (by 0x7ff) ? but what if the resulted fnum becomes negative, does it have the sign bit extension or is it simply masked like with detune overflow ?

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Thu Jun 18, 2009 2:19 am

ok, so you mean that the value from "lfo_pm_table" (in the MAME core) should be added to the fnum value (11 bits), NOT to the block+fnum value (14 bits, identified by block_fnum in the MAME core) like it's done actually (well it's actuallty done on block_fnum << 1 because of this increased precision thing) ?

I mean, the "block" value is not affected by the modulation, is it ? I don't really understand that part of the MAME implementation, because fnum AND block value are recalculated after the addition of the phase modulation
The MAME core is pretty messy, and I've forgotten most of what I knew about exactly how MAME implemented the phase generator, so I'm hesitant to try and give specific advice on exactly how to modify the MAME core to fix is up. MAME crams the block and fnum data into a single variable, it uses a large lookup table to calculate frequency modulation, it tries to cache the final phase increment value and only recalculate it when it changes, and it has multiple update functions for the phase generator which are called at different times under different circumstances. All of these decisions are designed to try and get more speed out of the core, but they also mean making changes is a lot more painful than it should be.

What I can tell you is the frequency modulation adjustment is applied directly to fnum, before block data is applied, and this is an 11-bit calculation. In a pure implementation, your phase generator update function should be a series of six discrete steps:
1. Get the current value of fnum (11-bit result)
2. Apply frequency modulation to the fnum value (11-bit result)
3. Shift fnum by the block data to obtain the initial phase increment value (17-bit result)
4. Apply detune to the phase increment value (17-bit result)
5. Apply the frequency multiplier to the phase increment value (20-bit result)
6. Add the final phase increment value to the phase counter (20-bit result)
It's a lot more complicated than this in the MAME core, but this is how the phase generator is updated each cycle in the YM2612.
Also in that case, I would think the result should be masked as a 11 bits value (by 0x7ff) ? but what if the resulted fnum becomes negative, does it have the sign bit extension or is it simply masked like with detune overflow ?
Phase modulation can't cause a negative overflow like with detune, since the "amplitude" of the phase modulation is dependent on the initial fnum data itself, specifically, the upper 6 bits of fnum. If all upper six bits of fnum are 0, phase modulation won't adjust fnum at all. Due to the way phase modulation is applied to fnum, fnum will always be larger than the amount that would be subtracted from fnum during the peak of the negative oscillation of the phase modulation.

Phase modulation can cause a positive overflow with fnum however. If all bits of fnum are set for example, the positive oscillation of phase modulation will cause an overflow. In this case, the upper bits are lost. The result is an 11-bit result, so a positive overflow will cause fnum to wrap around from a very large value to a very small value.

In other words, just mask the result of the addition between fnum and the phase modulation value with an 11-bit mask, and you'll get the correct result. Or at least, that's how it would be without the extra bit of precision that's been added into the MAME core.

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Thu Jun 18, 2009 8:54 am

It's a lot more complicated than this in the MAME core, but this is how the phase generator is updated each cycle in the YM2612.
Not really that much, just that the phase increment value is only recalculated when needed (i.e some registers changed) and that the fnum value is shifted by the block value immediately when the block/fnum register is updated, again on optimization purpose.

The reason why the block/fnum value is also stored is because of LFO: when LFO is active, the phase increment value is indeed recalculated on each update cycle instead and frequency modulation is applied before block shifting (yes, in that case, this means that some redundant calculation has been done so it's less efficient when LFO is active !)

Anyway, here's how I modified the phase update function in that case:

Code: Select all

  UINT32 fnum_lfo   = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
  INT32  lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + LFO_PM ];

  if (lfo_fn_table_index_offset)  /* LFO phase modulation active */
  {
    /* retrieve BLOCK register value */
    UINT8 blk = (block_fnum >> 11) & 7;

    /* apply phase modulation to FNUM register value */
    UINT32 fn  = (block_fnum + (UINT32)lfo_fn_table_index_offset) & 0x7ff;

    /* recalculate keyscale code */
    int kc = (blk<<2) | opn_fktable[fn >> 7];

    /* recalculate (frequency) phase increment counter */
    int fc = (ym2612.OPN.fn_table[fn]>>(7-blk)) + SLOT->DT[kc];

    /* (frequency) phase overflow (max. 17 bits) */
    if (fc < 0) fc += ym2612.OPN.fn_max;

    /* update phase */
    SLOT->phase += (fc * SLOT->mul) >> 1;
  }
  else  /* LFO phase modulation  = zero */
  {
    SLOT->phase += SLOT->Incr;
  }
lfo_fn_table_index_offset is a value from the LFO table, which indeed depends on the FNUM higher 7 bits, LFO depth (0~7) and LFO current step (0~31). It is negative value for steps above 16.

I assume the MAME base LFO table is correct:

Code: Select all

static const UINT8 lfo_pm_output[7*8][8]={
/* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */
/* FNUM BIT 4: 000 0001xxxx */
/* DEPTH 0 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 1 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 2 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 3 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 4 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 5 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 6 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 7 */ {0,   0,   0,   0,   1,   1,   1,   1},

/* FNUM BIT 5: 000 0010xxxx */
/* DEPTH 0 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 1 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 2 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 3 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 4 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 5 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 6 */ {0,   0,   0,   0,   1,   1,   1,   1},
/* DEPTH 7 */ {0,   0,   1,   1,   2,   2,   2,   3},

/* FNUM BIT 6: 000 0100xxxx */
/* DEPTH 0 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 1 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 2 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 3 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 4 */ {0,   0,   0,   0,   0,   0,   0,   1},
/* DEPTH 5 */ {0,   0,   0,   0,   1,   1,   1,   1},
/* DEPTH 6 */ {0,   0,   1,   1,   2,   2,   2,   3},
/* DEPTH 7 */ {0,   0,   2,   3,   4,   4,   5,   6},

/* FNUM BIT 7: 000 1000xxxx */
/* DEPTH 0 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 1 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 2 */ {0,   0,   0,   0,   0,   0,   1,   1},
/* DEPTH 3 */ {0,   0,   0,   0,   1,   1,   1,   1},
/* DEPTH 4 */ {0,   0,   0,   1,   1,   1,   1,   2},
/* DEPTH 5 */ {0,   0,   1,   1,   2,   2,   2,   3},
/* DEPTH 6 */ {0,   0,   2,   3,   4,   4,   5,   6},
/* DEPTH 7 */ {0,   0,   4,   6,   8,   8, 0xa, 0xc},

/* FNUM BIT 8: 001 0000xxxx */
/* DEPTH 0 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 1 */ {0,   0,   0,   0,   1,   1,   1,   1},
/* DEPTH 2 */ {0,   0,   0,   1,   1,   1,   2,   2},
/* DEPTH 3 */ {0,   0,   1,   1,   2,   2,   3,   3},
/* DEPTH 4 */ {0,   0,   1,   2,   2,   2,   3,   4},
/* DEPTH 5 */ {0,   0,   2,   3,   4,   4,   5,   6},
/* DEPTH 6 */ {0,   0,   4,   6,   8,   8, 0xa, 0xc},
/* DEPTH 7 */ {0,   0,   8, 0xc,0x10,0x10,0x14,0x18},

/* FNUM BIT 9: 010 0000xxxx */
/* DEPTH 0 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 1 */ {0,   0,   0,   0,   2,   2,   2,   2},
/* DEPTH 2 */ {0,   0,   0,   2,   2,   2,   4,   4},
/* DEPTH 3 */ {0,   0,   2,   2,   4,   4,   6,   6},
/* DEPTH 4 */ {0,   0,   2,   4,   4,   4,   6,   8},
/* DEPTH 5 */ {0,   0,   4,   6,   8,   8, 0xa, 0xc},
/* DEPTH 6 */ {0,   0,   8, 0xc,0x10,0x10,0x14,0x18},
/* DEPTH 7 */ {0,   0,0x10,0x18,0x20,0x20,0x28,0x30},

/* FNUM BIT10: 100 0000xxxx */
/* DEPTH 0 */ {0,   0,   0,   0,   0,   0,   0,   0},
/* DEPTH 1 */ {0,   0,   0,   0,   4,   4,   4,   4},
/* DEPTH 2 */ {0,   0,   0,   4,   4,   4,   8,   8},
/* DEPTH 3 */ {0,   0,   4,   4,   8,   8, 0xc, 0xc},
/* DEPTH 4 */ {0,   0,   4,   8,   8,   8, 0xc,0x10},
/* DEPTH 5 */ {0,   0,   8, 0xc,0x10,0x10,0x14,0x18},
/* DEPTH 6 */ {0,   0,0x10,0x18,0x20,0x20,0x28,0x30},
/* DEPTH 7 */ {0,   0,0x20,0x30,0x40,0x40,0x50,0x60},

};

Post Reply