diff --git a/source/sound/ym2612.c b/source/sound/ym2612.c index 2832542..d3c05e2 100644 --- a/source/sound/ym2612.c +++ b/source/sound/ym2612.c @@ -14,12 +14,13 @@ ** ** 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 +** More informations here: http://gendev.spritesmind.net/forum/viewtopic.php?t=386 ** ** - removed unused multichip support ** - added YM2612 Context external access functions -** - added LFO phase update in CH3 special mode (Warlock birds, Alladin bug sound) -** - fixed internal timers emulation -** - added Attack Rate immediate update on register write (Batman & Robin intro) +** - implemented LFO phase update in CH3 special mode (Warlock birds, Alladin bug sound) +** - improved internal timers emulation +** - fixed Attack Rate update in some specific case (Batman & Robin intro) ** - 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 ** - 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) ** - adjusted some EG rates ** - modified address/data port behavior +** - fixed LFO implementation (only 11 bits of precision) ** ** TODO: complete SSG-EG documentation ** @@ -582,9 +584,8 @@ typedef struct UINT32 eg_timer_add; /* step of eg_timer */ 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 - but LFO works with one more bit of a precision so we really need 4096 elements */ - UINT32 fn_table[4096]; /* fnumber->increment counter */ + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers */ + UINT32 fn_table[2048]; /* fnumber->increment counter */ UINT32 fn_max; /* max increment (required for calculating phase overflow) */ /* 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; INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + LFO_PM ]; - + 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; - UINT32 fn = block_fnum & 0xfff; + /* increase FNUM register value */ + UINT32 fn = (block_fnum + lfo_fn_table_index_offset) & 0x7ff; - /* keyscale code */ - int kc = (blk<<2) | opn_fktable[fn >> 8]; + /* recalculate keyscale code */ + 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]; - + /* (frequency) phase overflow (credits to Nemesis) */ 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) { UINT32 block_fnum = CH->block_fnum; - + UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; 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 */ { - 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; - UINT32 fn = block_fnum & 0xfff; - - /* keyscale code */ - int kc = (blk<<2) | opn_fktable[fn >> 8]; + /* increase FNUM register value */ + UINT32 fn = (block_fnum + lfo_fn_table_index_offset)& 0x7ff; - /* (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)); /* (frequency) phase overflow (credits to Nemesis) */ @@ -1316,7 +1319,6 @@ INLINE void chan_calc(FM_CH *CH) UINT32 AM = LFO_AM >> CH->ams; - m2 = c1 = c2 = mem = 0; *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 */ *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, c2); - /* store current MEM */ CH->mem_value = mem; @@ -1631,14 +1632,17 @@ static void OPNSetPres(int pres) /* make time tables */ init_timetables(dt_tab); - /* 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 are 2048 FNUMs that can be generated using FNUM/BLK registers */ /* calculate fnumber -> increment counter table */ - for(i = 0; i < 4096; i++) + for(i = 0; i < 2048; i++) { /* freq table for octave 7 */ /* 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) */ @@ -1843,7 +1847,7 @@ INLINE void OPNWriteReg(int r, int v) /* keyscale code */ CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; /* 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 */ CH->block_fnum = (blk<<11) | fn; @@ -1862,7 +1866,7 @@ INLINE void OPNWriteReg(int r, int v) /* keyscale code */ ym2612.OPN.SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; /* 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.CH[2].SLOT[SLOT1].Incr=-1; }