New Documentation: An authoritative reference on the YM2612
Moderator: BigEvilCorporation
-
- Interested
- Posts: 19
- Joined: Sun Oct 12, 2008 10:45 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.
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.
-
- Very interested
- Posts: 2440
- Joined: Tue Dec 05, 2006 1:37 pm
- Location: Estonia, Rapla City
- Contact:
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
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
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
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
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
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen
-
- Very interested
- Posts: 2440
- Joined: Tue Dec 05, 2006 1:37 pm
- Location: Estonia, Rapla City
- Contact:
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...
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
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen
-
- Very interested
- Posts: 745
- Joined: Sat Dec 15, 2007 7:49 am
- Location: Kazakhstan, Pavlodar
-
- Very interested
- Posts: 2440
- Joined: Tue Dec 05, 2006 1:37 pm
- Location: Estonia, Rapla City
- Contact:
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.
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
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen
-
- Very interested
- Posts: 745
- Joined: Sat Dec 15, 2007 7:49 am
- Location: Kazakhstan, Pavlodar
-
- Very interested
- Posts: 2440
- Joined: Tue Dec 05, 2006 1:37 pm
- Location: Estonia, Rapla City
- Contact:
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.
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
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen
-
- Very interested
- Posts: 145
- Joined: Sun Jan 28, 2007 2:01 am
- Location: DCEvolution.net
- Contact:
Did you ever post the info on LFO / phase modulation ? (I am asking because I might have overlooked something)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.
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.Did you ever post the info on LFO / phase modulation ? (I am asking because I might have overlooked something)
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:
(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.)-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.
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 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./* 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 */
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.
(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.)
Thanks to "all of you" anyway, makes everything easier to understand
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) ?Frequency modulation is applied directly to the base fnum value, which is an 11-bit number
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 ?
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.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
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.
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.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 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.
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.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.
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;
}
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},
};