From c8c71b9bff53bb2fc69cf77318cefd8e4e80fa11 Mon Sep 17 00:00:00 2001 From: ekeeke31 Date: Fri, 24 Jul 2009 16:55:32 +0000 Subject: [PATCH] fixed LFO implementation (YM2612) fixed regression with EG update being done too early (YM2612) --- source/sound/ym2612.c | 205 ++++++++++++++++++++++-------------------- 1 file changed, 107 insertions(+), 98 deletions(-) diff --git a/source/sound/ym2612.c b/source/sound/ym2612.c index 472cd74..d3abe6b 100644 --- a/source/sound/ym2612.c +++ b/source/sound/ym2612.c @@ -14,11 +14,14 @@ ** ** 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 +** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386 ** ** - removed unused multichip support ** - added YM2612 Context external access functions -** - implemented LFO phase update in CH3 special mode (Warlock birds, Alladin bug sound) +** - fixed LFO implementation (Spider-Man & Venom : Separation Anxiety intro,Warlock birds, Alladin bug sound): +** .added support for CH3 special mode +** .fixed LFO update: it is done after output calculation, like EG/PG updates +** .fixed LFO on/off behavior: LFO is reset when switched ON and holded at its current level when switched OFF (AM & PM can still be applied) ** - improved internal timers emulation ** - fixed Attack Rate update in some specific case (Batman & Robin intro) ** - fixed EG behavior when Attack Rate is maximal @@ -31,7 +34,7 @@ ** - adjusted some EG rates ** - modified address/data port behavior ** -** TODO: complete SSG-EG documentation +** TODO: fix SSG-EG documentation, BUSY flag support ** ** @@ -134,6 +137,11 @@ #define FREQ_MASK ((1<>LFO_SH & 127;*/ - + /* increment LFO counter */ + /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ ym2612.OPN.lfo_cnt += ym2612.OPN.lfo_inc; - + + /* LFO current position */ pos = ( ym2612.OPN.lfo_cnt >> LFO_SH) & 127; - - /* update AM when LFO output changes */ - - /*if (prev_pos != pos)*/ - /* actually I can't optimize is this way without rewritting chan_calc() - to use chip->lfo_am instead of global lfo_am */ - { - - /* triangle */ - /* AM: 0 to 126 step +2, 126 to 0 step -2 */ - if (pos<64) - LFO_AM = (pos&63) * 2; - else - LFO_AM = 126 - ((pos&63) * 2); - } + /* triangle */ + /* AM: 0 to 126 step +2, 126 to 0 step -2 */ + if (pos<64) + LFO_AM = pos * 2; + else + LFO_AM = 126 - ((pos&63) * 2); /* PM works with 4 times slower clock */ - /*prev_pos >>= 2;*/ - pos >>= 2; - /* update PM when LFO output changes */ - /*if (prev_pos != pos)*/ /* can't use global lfo_pm for this optimization, must be chip->lfo_pm instead*/ - { - LFO_PM = pos; - } - + LFO_PM = pos >> 2; } - else + /* when LFO is disabled, current level is held (fix Spider-Man & Venom : Separation Anxiety) */ + /*else { LFO_AM = 0; LFO_PM = 0; - } + }*/ } @@ -1237,13 +1227,15 @@ INLINE void update_ssg_eg_channel(FM_SLOT *SLOT) INLINE void update_phase_lfo_slot(FM_SLOT *SLOT , INT32 pms, UINT32 block_fnum) { - UINT32 fnum_lfo = (block_fnum & 0x7f0) << 4; /* ((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 ]; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { - UINT8 blk = (block_fnum >> 11) & 7; - UINT32 fn = (block_fnum*2 + (UINT32)lfo_fn_table_index_offset) & 0xfff; + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; /* keyscale code */ int kc = (blk<<2) | opn_fktable[fn >> 8]; @@ -1266,18 +1258,20 @@ 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; /* ((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 ]; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; - UINT8 blk = (block_fnum >> 11) & 7; - UINT32 fn = (block_fnum*2 + (UINT32)lfo_fn_table_index_offset) & 0xfff; - + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; + /* keyscale code */ int kc = (blk<<2) | opn_fktable[fn >> 8]; - + /* (frequency) phase increment counter */ int fc = (ym2612.OPN.fn_table[fn]>>(7-blk)); @@ -1435,7 +1429,7 @@ INLINE void refresh_fc_eg_chan(FM_CH *CH ) } /* initialize time tables */ -static void init_timetables(const UINT8 *dttable ) +static void init_timetables(const UINT8 *dttable, double freqbase) { int i,d; double rate; @@ -1445,7 +1439,7 @@ static void init_timetables(const UINT8 *dttable ) { for (i = 0;i <= 31;i++) { - rate = ((double)dttable[d*32 + i]) * SIN_LEN * ym2612.OPN.ST.freqbase * (1<= ym2612.OPN.eg_timer_overflow) @@ -1974,31 +1990,19 @@ void YM2612Update(int length) advance_eg_channel(&ym2612.CH[5].SLOT[SLOT1]); } - /* calculate FM */ - chan_calc(&ym2612.CH[0]); - chan_calc(&ym2612.CH[1]); - chan_calc(&ym2612.CH[2]); - chan_calc(&ym2612.CH[3]); - chan_calc(&ym2612.CH[4]); - if (ym2612.dacen) - { - /* DAC Mode */ - *(ym2612.CH[5].connect4) += ym2612.dacout; - } - else chan_calc(&ym2612.CH[5]); - - lt = (out_fm[0] & ym2612.OPN.pan[0]); - rt = (out_fm[0] & ym2612.OPN.pan[1]); - lt += (out_fm[1] & ym2612.OPN.pan[2]); - rt += (out_fm[1] & ym2612.OPN.pan[3]); - lt += (out_fm[2] & ym2612.OPN.pan[4]); - rt += (out_fm[2] & ym2612.OPN.pan[5]); - lt += (out_fm[3] & ym2612.OPN.pan[6]); - rt += (out_fm[3] & ym2612.OPN.pan[7]); - lt += (out_fm[4] & ym2612.OPN.pan[8]); - rt += (out_fm[4] & ym2612.OPN.pan[9]); - lt += (out_fm[5] & ym2612.OPN.pan[10]); - rt += (out_fm[5] & ym2612.OPN.pan[11]); + /* 6-channels mixing */ + lt = ((out_fm[0]) & ym2612.OPN.pan[0]); + rt = ((out_fm[0]) & ym2612.OPN.pan[1]); + lt += ((out_fm[1]) & ym2612.OPN.pan[2]); + rt += ((out_fm[1]) & ym2612.OPN.pan[3]); + lt += ((out_fm[2]) & ym2612.OPN.pan[4]); + rt += ((out_fm[2]) & ym2612.OPN.pan[5]); + lt += ((out_fm[3]) & ym2612.OPN.pan[6]); + rt += ((out_fm[3]) & ym2612.OPN.pan[7]); + lt += ((out_fm[4]) & ym2612.OPN.pan[8]); + rt += ((out_fm[4]) & ym2612.OPN.pan[9]); + lt += ((out_fm[5]) & ym2612.OPN.pan[10]); + rt += ((out_fm[5]) & ym2612.OPN.pan[11]); /* limiter */ Limit(lt,MAXOUT,MINOUT); @@ -2060,19 +2064,24 @@ int YM2612ResetChip(void) int i; OPNSetPres(6*24); - OPNWriteMode(0x27,0x30); /* mode 0 , timer reset */ - ym2612.OPN.eg_timer = 0; - ym2612.OPN.eg_cnt = 0; - ym2612.OPN.ST.status = 0; - ym2612.OPN.ST.mode = 0; + ym2612.OPN.eg_timer = 0; + ym2612.OPN.eg_cnt = 0; + + LFO_AM = 0; + LFO_PM = 0; + ym2612.OPN.lfo_cnt = 0; + ym2612.OPN.ST.TAC = 0; ym2612.OPN.ST.TBC = 0; + + OPNWriteMode(0x27,0x30); OPNWriteMode(0x26,0x00); OPNWriteMode(0x25,0x00); OPNWriteMode(0x24,0x00); reset_channels(&ym2612.CH[0] , 6 ); + for(i = 0xb6 ; i >= 0xb4 ; i-- ) { OPNWriteReg(i ,0xc0); @@ -2087,7 +2096,7 @@ int YM2612ResetChip(void) /* DAC mode clear */ ym2612.dacen = 0; ym2612.dacout = 0; - + return 0; }