+fixed LFO implementation according to Nemesis description (UNTESTED, need feedback)

This commit is contained in:
ekeeke31 2009-06-15 16:23:12 +00:00
parent 256d534ea6
commit 0c8c30e3a2

View File

@ -14,12 +14,13 @@
** **
** 2006~2009 Eke-Eke (Genesis Plus GX): ** 2006~2009 Eke-Eke (Genesis Plus GX):
** Credits to Nemesis (@spritesmind.net), most of those fixes came from his tests on a Model 1 Sega Mega Drive ** Credits to Nemesis (@spritesmind.net), most of those fixes came from his tests on a Model 1 Sega Mega Drive
** More informations here: http://gendev.spritesmind.net/forum/viewtopic.php?t=386
** **
** - removed unused multichip support ** - removed unused multichip support
** - added YM2612 Context external access functions ** - added YM2612 Context external access functions
** - added LFO phase update in CH3 special mode (Warlock birds, Alladin bug sound) ** - implemented LFO phase update in CH3 special mode (Warlock birds, Alladin bug sound)
** - fixed internal timers emulation ** - improved internal timers emulation
** - added Attack Rate immediate update on register write (Batman & Robin intro) ** - fixed Attack Rate update in some specific case (Batman & Robin intro)
** - fixed EG behavior when Attack Rate is maximal ** - fixed EG behavior when Attack Rate is maximal
** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation ** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation
** - added EG output immediate update on register writes ** - added EG output immediate update on register writes
@ -29,6 +30,7 @@
** - implemented correct SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Six & many others) ** - implemented correct SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Six & many others)
** - adjusted some EG rates ** - adjusted some EG rates
** - modified address/data port behavior ** - modified address/data port behavior
** - fixed LFO implementation (only 11 bits of precision)
** **
** TODO: complete SSG-EG documentation ** TODO: complete SSG-EG documentation
** **
@ -582,9 +584,8 @@ typedef struct
UINT32 eg_timer_add; /* step of eg_timer */ UINT32 eg_timer_add; /* step of eg_timer */
UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 3 samples (on real chip) */ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 3 samples (on real chip) */
/* there are 2048 FNUMs that can be generated using FNUM/BLK registers /* 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 */ UINT32 fn_table[2048]; /* fnumber->increment counter */
UINT32 fn_table[4096]; /* fnumber->increment counter */
UINT32 fn_max; /* max increment (required for calculating phase overflow) */ UINT32 fn_max; /* max increment (required for calculating phase overflow) */
/* LFO */ /* LFO */
@ -1237,20 +1238,21 @@ INLINE void update_phase_lfo_slot(FM_SLOT *SLOT , INT32 pms, UINT32 block_fnum)
{ {
UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + LFO_PM ]; INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + LFO_PM ];
if (lfo_fn_table_index_offset) /* LFO phase modulation active */ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
{ {
block_fnum = block_fnum*2 + lfo_fn_table_index_offset; /* retrieve BLOCK register value */
UINT8 blk = (block_fnum >> 11) & 7;
UINT8 blk = (block_fnum&0x7000) >> 12; /* increase FNUM register value */
UINT32 fn = block_fnum & 0xfff; UINT32 fn = (block_fnum + lfo_fn_table_index_offset) & 0x7ff;
/* keyscale code */ /* recalculate keyscale code */
int kc = (blk<<2) | opn_fktable[fn >> 8]; int kc = (blk<<2) | opn_fktable[fn >> 7];
/* (frequency) phase increment counter */ /* recalculate (frequency) phase increment counter */
int fc = (ym2612.OPN.fn_table[fn]>>(7-blk)) + SLOT->DT[kc]; int fc = (ym2612.OPN.fn_table[fn]>>(7-blk)) + SLOT->DT[kc];
/* (frequency) phase overflow (credits to Nemesis) */ /* (frequency) phase overflow (credits to Nemesis) */
if (fc < 0) fc += ym2612.OPN.fn_max; if (fc < 0) fc += ym2612.OPN.fn_max;
@ -1266,21 +1268,22 @@ INLINE void update_phase_lfo_slot(FM_SLOT *SLOT , INT32 pms, UINT32 block_fnum)
INLINE void update_phase_lfo_channel(FM_CH *CH) INLINE void update_phase_lfo_channel(FM_CH *CH)
{ {
UINT32 block_fnum = CH->block_fnum; UINT32 block_fnum = CH->block_fnum;
UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + LFO_PM ]; INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + LFO_PM ];
if (lfo_fn_table_index_offset) /* LFO phase modulation active */ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
{ {
block_fnum = block_fnum*2 + lfo_fn_table_index_offset; /* retrieve BLOCK register value */
UINT8 blk = (block_fnum >> 11) & 7;
UINT8 blk = (block_fnum&0x7000) >> 12; /* increase FNUM register value */
UINT32 fn = block_fnum & 0xfff; UINT32 fn = (block_fnum + lfo_fn_table_index_offset)& 0x7ff;
/* keyscale code */
int kc = (blk<<2) | opn_fktable[fn >> 8];
/* (frequency) phase increment counter */ /* 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)); int fc = (ym2612.OPN.fn_table[fn]>>(7-blk));
/* (frequency) phase overflow (credits to Nemesis) */ /* (frequency) phase overflow (credits to Nemesis) */
@ -1316,7 +1319,6 @@ INLINE void chan_calc(FM_CH *CH)
UINT32 AM = LFO_AM >> CH->ams; UINT32 AM = LFO_AM >> CH->ams;
m2 = c1 = c2 = mem = 0; m2 = c1 = c2 = mem = 0;
*CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
@ -1356,7 +1358,6 @@ INLINE void chan_calc(FM_CH *CH)
if( eg_out < ENV_QUIET ) /* SLOT 4 */ if( eg_out < ENV_QUIET ) /* SLOT 4 */
*CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, c2); *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, c2);
/* store current MEM */ /* store current MEM */
CH->mem_value = mem; CH->mem_value = mem;
@ -1631,14 +1632,17 @@ static void OPNSetPres(int pres)
/* make time tables */ /* make time tables */
init_timetables(dt_tab); init_timetables(dt_tab);
/* there are 2048 FNUMs that can be generated using FNUM/BLK registers /* 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 */
/* calculate fnumber -> increment counter table */ /* calculate fnumber -> increment counter table */
for(i = 0; i < 4096; i++) for(i = 0; i < 2048; i++)
{ {
/* freq table for octave 7 */ /* freq table for octave 7 */
/* OPN phase increment counter = 20bit */ /* OPN phase increment counter = 20bit */
ym2612.OPN.fn_table[i] = (UINT32)( (double)i * 32 * ym2612.OPN.ST.freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */
/* where sample clock is M/144 */
/* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */
/* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */
ym2612.OPN.fn_table[i] = (UINT32)( (double)i * 64 * ym2612.OPN.ST.freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
} }
/* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */
@ -1843,7 +1847,7 @@ INLINE void OPNWriteReg(int r, int v)
/* keyscale code */ /* keyscale code */
CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; CH->kcode = (blk<<2) | opn_fktable[fn >> 7];
/* phase increment counter */ /* phase increment counter */
CH->fc = ym2612.OPN.fn_table[fn*2]>>(7-blk); CH->fc = ym2612.OPN.fn_table[fn]>>(7-blk);
/* store fnum in clear form for LFO PM calculations */ /* store fnum in clear form for LFO PM calculations */
CH->block_fnum = (blk<<11) | fn; CH->block_fnum = (blk<<11) | fn;
@ -1862,7 +1866,7 @@ INLINE void OPNWriteReg(int r, int v)
/* keyscale code */ /* keyscale code */
ym2612.OPN.SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; ym2612.OPN.SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7];
/* phase increment counter */ /* phase increment counter */
ym2612.OPN.SL3.fc[c] = ym2612.OPN.fn_table[fn*2]>>(7-blk); ym2612.OPN.SL3.fc[c] = ym2612.OPN.fn_table[fn]>>(7-blk);
ym2612.OPN.SL3.block_fnum[c] = (blk<<11) | fn; //fn; ym2612.OPN.SL3.block_fnum[c] = (blk<<11) | fn; //fn;
ym2612.CH[2].SLOT[SLOT1].Incr=-1; ym2612.CH[2].SLOT[SLOT1].Incr=-1;
} }