using System; namespace MAME.Core { public class YM2413 { public struct OPLL_SLOT { public uint ar; public uint dr; public uint rr; public byte KSR; public byte ksl; public byte ksr; public byte mul; public uint phase; public uint freq; public byte fb_shift; public int[] op1_out; public byte eg_type; public byte state; public uint TL; public int TLL; public int volume; public uint sl; public byte eg_sh_dp; public byte eg_sel_dp; public byte eg_sh_ar; public byte eg_sel_ar; public byte eg_sh_dr; public byte eg_sel_dr; public byte eg_sh_rr; public byte eg_sel_rr; public byte eg_sh_rs; public byte eg_sel_rs; public uint key; public uint AMmask; public byte vib; public uint wavetable; } public struct OPLL_CH { public OPLL_SLOT[] SLOT; public uint block_fnum; public uint fc; public uint ksl_base; public byte kcode; public byte sus; } public struct YM2413Struct { public OPLL_CH[] P_CH; public byte[] instvol_r; public uint eg_cnt; public uint eg_timer; public uint eg_timer_add; public uint eg_timer_overflow; public byte rhythm; public uint lfo_am_cnt; public uint lfo_am_inc; public uint lfo_pm_cnt; public uint lfo_pm_inc; public uint noise_rng; public uint noise_p; public uint noise_f; public byte[][] inst_tab; public OPLL_UPDATEHANDLER UpdateHandler; //void * UpdateParam; public uint[] fn_tab; public byte address; public byte status; public int index; public int clock; public int rate; public double freqbase; } private static uint[] ksl_tab = new uint[8 * 16] { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,4,6,8, 10,12,14,16, 0,0,0,0, 0,6,10,14, 16,20,22,24, 26,28,30,32, 0,0,0,10, 16,22,26,30, 32,36,38,40, 42,44,46,48, 0,0,16,26, 32,38,42,46, 48,52,54,56, 58,60,62,64, 0,16,32,42, 48,54,58,62, 64,68,70,72, 74,76,78,80, 0,32,48,58, 64,70,74,78, 80,84,86,88, 90,92,94,96, 0,48,64,74, 80,86,90,94, 96,100,102,104, 106,108,110,112 }; private static uint[] sl_tab = new uint[16] { 0*8, 1*8, 2*8,3 *8,4 *8,5 *8,6 *8, 7*8, 8*8, 9*8,10*8,11*8,12*8,13*8,14*8,15*8 }; private static byte[] eg_inc = new byte[15 * 8] { 0,1, 0,1, 0,1, 0,1, 0,1, 0,1, 1,1, 0,1, 0,1, 1,1, 0,1, 1,1, 0,1, 1,1, 1,1, 1,1, 1,1, 1,1, 1,1, 1,1, 1,1, 1,2, 1,1, 1,2, 1,2, 1,2, 1,2, 1,2, 1,2, 2,2, 1,2, 2,2, 2,2, 2,2, 2,2, 2,2, 2,2, 2,4, 2,2, 2,4, 2,4, 2,4, 2,4, 2,4, 2,4, 4,4, 2,4, 4,4, 4,4, 4,4, 4,4, 4,4, 8,8, 8,8, 8,8, 8,8, 0,0, 0,0, 0,0, 0,0, }; private static byte[] eg_rate_select = new byte[96] { 14*8,14*8,14*8,14*8,14*8,14*8,14*8,14*8, 14*8,14*8,14*8,14*8,14*8,14*8,14*8,14*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 0*8,1*8,2*8,3*8, 4*8,5*8,6*8,7*8, 8*8,9*8,10*8,11*8, 12*8,12*8,12*8,12*8, 12*8,12*8,12*8,12*8,12*8,12*8,12*8,12*8, 12*8,12*8,12*8,12*8,12*8,12*8,12*8,12*8, }; private static byte[] eg_rate_shift = new byte[96] { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 13,13,13,13, 12,12,12,12, 11,11,11,11, 10,10,10,10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; private static byte[] mul_tab = new byte[16] { 1, 1*2, 2*2, 3*2, 4*2, 5*2, 6*2, 7*2, 8*2, 9*2,10*2,10*2,12*2,12*2,15*2,15*2 }; public delegate void OPLL_UPDATEHANDLER(int min_interval_us); private static int[] tl_tab; private static uint[] sin_tab; private static byte[] lfo_am_table = new byte[210] { 0,0,0,0,0,0,0, 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 5,5,5,5, 6,6,6,6, 7,7,7,7, 8,8,8,8, 9,9,9,9, 10,10,10,10, 11,11,11,11, 12,12,12,12, 13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16, 17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20, 21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24, 25,25,25,25, 26,26,26, 25,25,25,25, 24,24,24,24, 23,23,23,23, 22,22,22,22, 21,21,21,21, 20,20,20,20, 19,19,19,19, 18,18,18,18, 17,17,17,17, 16,16,16,16, 15,15,15,15, 14,14,14,14, 13,13,13,13, 12,12,12,12, 11,11,11,11, 10,10,10,10, 9,9,9,9, 8,8,8,8, 7,7,7,7, 6,6,6,6, 5,5,5,5, 4,4,4,4, 3,3,3,3, 2,2,2,2, 1,1,1,1 }; private static sbyte[] lfo_pm_table = new sbyte[64] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,-1, 0, 0, 0, 2, 1, 0,-1,-2,-1, 0, 1, 3, 1, 0,-1,-3,-1, 0, 1, 4, 2, 0,-2,-4,-2, 0, 2, 5, 2, 0,-2,-5,-2, 0, 2, 6, 3, 0,-3,-6,-3, 0, 3, 7, 3, 0,-3,-7,-3, 0, 3, }; private static byte[][] table = new byte[19][] { new byte[]{0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, new byte[]{0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, new byte[]{0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13 }, new byte[]{0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23 }, new byte[]{0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27 }, new byte[]{0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18 }, new byte[]{0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13 }, new byte[]{0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17 }, new byte[]{0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07 }, new byte[]{0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, new byte[]{0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07 }, new byte[]{0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22 }, new byte[]{0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12 }, new byte[]{0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44 }, new byte[]{0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3 }, new byte[]{0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13 }, new byte[]{0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d }, new byte[]{0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 }, new byte[]{0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 } }; public static YM2413Struct OPLL; private static int num_lock = 0; private static int[] output; private static int outchan; private static uint LFO_AM; private static int LFO_PM; private static int limit(int val, int max, int min) { if (val > max) { return max; } else if (val < min) { return min; } else { return val; } } private static void advance_lfo() { OPLL.lfo_am_cnt += OPLL.lfo_am_inc; if (OPLL.lfo_am_cnt >= ((uint)210 << 24)) OPLL.lfo_am_cnt -= ((uint)210 << 24); LFO_AM = (uint)(lfo_am_table[OPLL.lfo_am_cnt >> 24] >> 1); OPLL.lfo_pm_cnt += OPLL.lfo_pm_inc; LFO_PM = (int)((OPLL.lfo_pm_cnt >> 24) & 7); } private static void advance() { uint i; OPLL.eg_timer += OPLL.eg_timer_add; while (OPLL.eg_timer >= OPLL.eg_timer_overflow) { OPLL.eg_timer -= OPLL.eg_timer_overflow; OPLL.eg_cnt++; for (i = 0; i < 9 * 2; i++) { switch (OPLL.P_CH[i / 2].SLOT[i & 1].state) { case 5: if ((OPLL.eg_cnt & ((1 << OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_dp) - 1)) == 0) { OPLL.P_CH[i / 2].SLOT[i & 1].volume += eg_inc[OPLL.P_CH[i / 2].SLOT[i & 1].eg_sel_dp + ((OPLL.eg_cnt >> OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_dp) & 7)]; if (OPLL.P_CH[i / 2].SLOT[i & 1].volume >= 0xff) { OPLL.P_CH[i / 2].SLOT[i & 1].volume = 0xff; OPLL.P_CH[i / 2].SLOT[i & 1].state = 4; OPLL.P_CH[i / 2].SLOT[i & 1].phase = 0; } } break; case 4: if ((OPLL.eg_cnt & ((1 << OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_ar) - 1)) == 0) { OPLL.P_CH[i / 2].SLOT[i & 1].volume += (~OPLL.P_CH[i / 2].SLOT[i & 1].volume * (eg_inc[OPLL.P_CH[i / 2].SLOT[i & 1].eg_sel_ar + ((OPLL.eg_cnt >> OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_ar) & 7)])) >> 2; if (OPLL.P_CH[i / 2].SLOT[i & 1].volume <= 0) { OPLL.P_CH[i / 2].SLOT[i & 1].volume = 0; OPLL.P_CH[i / 2].SLOT[i & 1].state = 3; } } break; case 3: if ((OPLL.eg_cnt & ((1 << OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_dr) - 1)) == 0) { OPLL.P_CH[i / 2].SLOT[i & 1].volume += eg_inc[OPLL.P_CH[i / 2].SLOT[i & 1].eg_sel_dr + ((OPLL.eg_cnt >> OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_dr) & 7)]; if (OPLL.P_CH[i / 2].SLOT[i & 1].volume >= OPLL.P_CH[i / 2].SLOT[i & 1].sl) OPLL.P_CH[i / 2].SLOT[i & 1].state = 2; } break; case 2: if (OPLL.P_CH[i / 2].SLOT[i & 1].eg_type != 0) { } else { if ((OPLL.eg_cnt & ((1 << OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_rr) - 1)) == 0) { OPLL.P_CH[i / 2].SLOT[i & 1].volume += eg_inc[OPLL.P_CH[i / 2].SLOT[i & 1].eg_sel_rr + ((OPLL.eg_cnt >> OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_rr) & 7)]; if (OPLL.P_CH[i / 2].SLOT[i & 1].volume >= 0xff) OPLL.P_CH[i / 2].SLOT[i & 1].volume = 0xff; } } break; case 1: if ((i & 1) != 0 || (((OPLL.rhythm & 0x20) != 0) && (i >= 12))) { if (OPLL.P_CH[i / 2].SLOT[i & 1].eg_type != 0) { if (OPLL.P_CH[i / 2].sus != 0) { if ((OPLL.eg_cnt & ((1 << OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_rs) - 1)) == 0) { OPLL.P_CH[i / 2].SLOT[i & 1].volume += eg_inc[OPLL.P_CH[i / 2].SLOT[i & 1].eg_sel_rs + ((OPLL.eg_cnt >> OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_rs) & 7)]; if (OPLL.P_CH[i / 2].SLOT[i & 1].volume >= 0xff) { OPLL.P_CH[i / 2].SLOT[i & 1].volume = 0xff; OPLL.P_CH[i / 2].SLOT[i & 1].state = 0; } } } else { if ((OPLL.eg_cnt & ((1 << OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_rr) - 1)) == 0) { OPLL.P_CH[i / 2].SLOT[i & 1].volume += eg_inc[OPLL.P_CH[i / 2].SLOT[i & 1].eg_sel_rr + ((OPLL.eg_cnt >> OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_rr) & 7)]; if (OPLL.P_CH[i / 2].SLOT[i & 1].volume >= 0xff) { OPLL.P_CH[i / 2].SLOT[i & 1].volume = 0xff; OPLL.P_CH[i / 2].SLOT[i & 1].state = 0; } } } } else { if ((OPLL.eg_cnt & ((1 << OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_rs) - 1)) == 0) { OPLL.P_CH[i / 2].SLOT[i & 1].volume += eg_inc[OPLL.P_CH[i / 2].SLOT[i & 1].eg_sel_rs + ((OPLL.eg_cnt >> OPLL.P_CH[i / 2].SLOT[i & 1].eg_sh_rs) & 7)]; if (OPLL.P_CH[i / 2].SLOT[i & 1].volume >= 0xff) { OPLL.P_CH[i / 2].SLOT[i & 1].volume = 0xff; OPLL.P_CH[i / 2].SLOT[i & 1].state = 0; } } } } break; default: break; } } } for (i = 0; i < 9 * 2; i++) { if (OPLL.P_CH[i / 2].SLOT[i & 1].vib != 0) { byte block; uint fnum_lfo = 8 * ((OPLL.P_CH[i / 2].block_fnum & 0x01c0) >> 6); uint block_fnum = OPLL.P_CH[i / 2].block_fnum * 2; int lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + fnum_lfo]; if (lfo_fn_table_index_offset != 0) { block_fnum += (uint)lfo_fn_table_index_offset; block = (byte)((block_fnum & 0x1c00) >> 10); OPLL.P_CH[i / 2].SLOT[i & 1].phase += (OPLL.fn_tab[block_fnum & 0x03ff] >> (7 - block)) * OPLL.P_CH[i / 2].SLOT[i & 1].mul; } else { OPLL.P_CH[i / 2].SLOT[i & 1].phase += OPLL.P_CH[i / 2].SLOT[i & 1].freq; } } else { OPLL.P_CH[i / 2].SLOT[i & 1].phase += OPLL.P_CH[i / 2].SLOT[i & 1].freq; } } OPLL.noise_p += OPLL.noise_f; i = OPLL.noise_p >> 16; OPLL.noise_p &= 0xffff; while (i != 0) { if ((OPLL.noise_rng & 1) != 0) OPLL.noise_rng ^= 0x800302; OPLL.noise_rng >>= 1; i--; } } private static int op_calc(uint phase, uint env, int pm, uint wave_tab) { uint p; p = (env << 5) + sin_tab[wave_tab + ((((int)((phase & ~0xffff) + (pm << 17))) >> 16) & 0x3ff)]; if (p >= 0x1600) return 0; return tl_tab[p]; } private static int op_calc1(uint phase, uint env, int pm, uint wave_tab) { uint p; int i; i = (int)((phase & ~0xffff) + pm); p = (env << 5) + sin_tab[wave_tab + ((i >> 16) & 0x3ff)]; if (p >= 0x1600) return 0; return tl_tab[p]; } private static uint volume_calc(int chan, int slot) { uint i1; i1 = (uint)(OPLL.P_CH[chan].SLOT[slot].TLL + (uint)OPLL.P_CH[chan].SLOT[slot].volume + (LFO_AM & OPLL.P_CH[chan].SLOT[slot].AMmask)); return i1; } private static void chan_calc(int chan) { uint env; int out1; int phase_modulation; env = volume_calc(chan, 0); out1 = OPLL.P_CH[chan].SLOT[0].op1_out[0] + OPLL.P_CH[chan].SLOT[0].op1_out[1]; OPLL.P_CH[chan].SLOT[0].op1_out[0] = OPLL.P_CH[chan].SLOT[0].op1_out[1]; phase_modulation = OPLL.P_CH[chan].SLOT[0].op1_out[0]; OPLL.P_CH[chan].SLOT[0].op1_out[1] = 0; if (env < 0xb0) { if (OPLL.P_CH[chan].SLOT[0].fb_shift == 0) out1 = 0; OPLL.P_CH[chan].SLOT[0].op1_out[1] = op_calc1(OPLL.P_CH[chan].SLOT[0].phase, env, (out1 << OPLL.P_CH[chan].SLOT[0].fb_shift), OPLL.P_CH[chan].SLOT[0].wavetable); } outchan = 0; env = volume_calc(chan, 1); if (env < 0xb0) { int outp = op_calc(OPLL.P_CH[chan].SLOT[1].phase, env, phase_modulation, OPLL.P_CH[chan].SLOT[1].wavetable); output[0] += outp; outchan = outp; } } private static void rhythm_calc(uint noise) { int out1; uint env; int phase_modulation; env = volume_calc(6, 0); out1 = OPLL.P_CH[6].SLOT[0].op1_out[0] + OPLL.P_CH[6].SLOT[0].op1_out[1]; OPLL.P_CH[6].SLOT[0].op1_out[0] = OPLL.P_CH[6].SLOT[0].op1_out[1]; phase_modulation = OPLL.P_CH[6].SLOT[0].op1_out[0]; OPLL.P_CH[6].SLOT[0].op1_out[1] = 0; if (env < 0xb0) { if (OPLL.P_CH[6].SLOT[0].fb_shift == 0) out1 = 0; OPLL.P_CH[6].SLOT[0].op1_out[1] = op_calc1(OPLL.P_CH[6].SLOT[0].phase, env, (out1 << OPLL.P_CH[6].SLOT[0].fb_shift), OPLL.P_CH[6].SLOT[0].wavetable); } env = volume_calc(6, 1); if (env < 0xb0) output[1] += op_calc(OPLL.P_CH[6].SLOT[1].phase, env, phase_modulation, OPLL.P_CH[6].SLOT[1].wavetable) * 2; env = volume_calc(7, 0); if (env < 0xb0) { byte bit7 = (byte)(((OPLL.P_CH[7].SLOT[0].phase >> 16) >> 7) & 1); byte bit3 = (byte)(((OPLL.P_CH[7].SLOT[0].phase >> 16) >> 3) & 1); byte bit2 = (byte)(((OPLL.P_CH[7].SLOT[0].phase >> 16) >> 2) & 1); byte res1 = (byte)((bit2 ^ bit7) | bit3); uint phase = (uint)(res1 != 0 ? (0x200 | (0xd0 >> 2)) : 0xd0); byte bit5e = (byte)(((OPLL.P_CH[8].SLOT[1].phase >> 16) >> 5) & 1); byte bit3e = (byte)(((OPLL.P_CH[8].SLOT[1].phase >> 16) >> 3) & 1); byte res2 = (byte)(bit3e | bit5e); if (res2 != 0) phase = (0x200 | (0xd0 >> 2)); if ((phase & 0x200) != 0) { if (noise != 0) phase = 0x200 | 0xd0; } else { if (noise != 0) phase = 0xd0 >> 2; } output[1] += op_calc(phase << 16, env, 0, OPLL.P_CH[7].SLOT[0].wavetable) * 2; } env = volume_calc(7, 1); if (env < 0xb0) { byte bit8 = (byte)(((OPLL.P_CH[7].SLOT[0].phase >> 16) >> 8) & 1); uint phase = (uint)(bit8 != 0 ? 0x200 : 0x100); if (noise != 0) phase ^= 0x100; output[1] += op_calc(phase << 16, env, 0, OPLL.P_CH[7].SLOT[1].wavetable) * 2; } env = volume_calc(8, 0); if (env < 0xb0) output[1] += op_calc(OPLL.P_CH[8].SLOT[0].phase, env, 0, OPLL.P_CH[8].SLOT[0].wavetable) * 2; env = volume_calc(8, 1); if (env < 0xb0) { byte bit7 = (byte)(((OPLL.P_CH[7].SLOT[0].phase >> 16) >> 7) & 1); byte bit3 = (byte)(((OPLL.P_CH[7].SLOT[0].phase >> 16) >> 3) & 1); byte bit2 = (byte)(((OPLL.P_CH[7].SLOT[0].phase >> 16) >> 2) & 1); byte res1 = (byte)((bit2 ^ bit7) | bit3); uint phase = (uint)(res1 != 0 ? 0x300 : 0x100); byte bit5e = (byte)(((OPLL.P_CH[8].SLOT[1].phase >> 16) >> 5) & 1); byte bit3e = (byte)(((OPLL.P_CH[8].SLOT[1].phase >> 16) >> 3) & 1); byte res2 = (byte)(bit3e | bit5e); if (res2 != 0) phase = 0x300; output[1] += op_calc(phase << 16, env, 0, OPLL.P_CH[8].SLOT[1].wavetable) * 2; } } private static int init_tables() { int i, x; int n; double o, m; for (x = 0; x < 0x100; x++) { m = (1 << 16) / Math.Pow(2, (x + 1) * ((128.0 / 0x400) / 4.0) / 8.0); m = Math.Floor(m); n = (int)m; n >>= 4; if ((n & 1) != 0) n = (n >> 1) + 1; else n = n >> 1; tl_tab[x * 2 + 0] = n; tl_tab[x * 2 + 1] = -tl_tab[x * 2 + 0]; for (i = 1; i < 11; i++) { tl_tab[x * 2 + 0 + i * 2 * 0x100] = tl_tab[x * 2 + 0] >> i; tl_tab[x * 2 + 1 + i * 2 * 0x100] = -tl_tab[x * 2 + 0 + i * 2 * 0x100]; } } for (i = 0; i < 0x400; i++) { m = Math.Sin(((i * 2) + 1) * Math.PI / 0x400); if (m > 0.0) o = 8 * Math.Log(1.0 / m) / Math.Log(2); else o = 8 * Math.Log(-1.0 / m) / Math.Log(2); o = o / ((128.0 / 0x400) / 4); n = (int)(2.0 * o); if ((n & 1) != 0) n = (n >> 1) + 1; else n = n >> 1; sin_tab[i] = (uint)(n * 2 + (m >= 0.0 ? 0 : 1)); if ((i & 0x200) != 0) sin_tab[1 * 0x400 + i] = 0x1600; else sin_tab[1 * 0x400 + i] = sin_tab[i]; } return 1; } private static void OPLL_initalize() { int i; //OPLL_init_save(); OPLL.freqbase = (OPLL.rate != 0) ? ((double)OPLL.clock / 72.0) / OPLL.rate : 0; for (i = 0; i < 1024; i++) { OPLL.fn_tab[i] = (uint)((double)i * 64 * OPLL.freqbase * (1 << (16 - 10))); } OPLL.lfo_am_inc = (uint)(0x40000 * OPLL.freqbase); OPLL.lfo_pm_inc = (uint)(0x4000 * OPLL.freqbase); OPLL.noise_f = (uint)(0x10000 * OPLL.freqbase); OPLL.eg_timer_add = (uint)(0x10000 * OPLL.freqbase); OPLL.eg_timer_overflow = (uint)0x10000; } private static void KEY_ON(int chan, int slot, uint key_set) { if (OPLL.P_CH[chan].SLOT[slot].key == 0) { OPLL.P_CH[chan].SLOT[slot].state = 5; } OPLL.P_CH[chan].SLOT[slot].key |= key_set; } private static void KEY_OFF(int chan, int slot, uint key_clr) { if (OPLL.P_CH[chan].SLOT[slot].key != 0) { OPLL.P_CH[chan].SLOT[slot].key &= key_clr; if (OPLL.P_CH[chan].SLOT[slot].key == 0) { if (OPLL.P_CH[chan].SLOT[slot].state > 1) OPLL.P_CH[chan].SLOT[slot].state = 1; } } } private static void CALC_FCSLOT(int chan, int slot) { int ksr; uint SLOT_rs; uint SLOT_dp; OPLL.P_CH[chan].SLOT[slot].freq = OPLL.P_CH[chan].fc * OPLL.P_CH[chan].SLOT[slot].mul; ksr = OPLL.P_CH[chan].kcode >> OPLL.P_CH[chan].SLOT[slot].KSR; if (OPLL.P_CH[chan].SLOT[slot].ksr != ksr) { OPLL.P_CH[chan].SLOT[slot].ksr = (byte)ksr; if ((OPLL.P_CH[chan].SLOT[slot].ar + OPLL.P_CH[chan].SLOT[slot].ksr) < 16 + 62) { OPLL.P_CH[chan].SLOT[slot].eg_sh_ar = eg_rate_shift[OPLL.P_CH[chan].SLOT[slot].ar + OPLL.P_CH[chan].SLOT[slot].ksr]; OPLL.P_CH[chan].SLOT[slot].eg_sel_ar = eg_rate_select[OPLL.P_CH[chan].SLOT[slot].ar + OPLL.P_CH[chan].SLOT[slot].ksr]; } else { OPLL.P_CH[chan].SLOT[slot].eg_sh_ar = 0; OPLL.P_CH[chan].SLOT[slot].eg_sel_ar = 13 * 8; } OPLL.P_CH[chan].SLOT[slot].eg_sh_dr = eg_rate_shift[OPLL.P_CH[chan].SLOT[slot].dr + OPLL.P_CH[chan].SLOT[slot].ksr]; OPLL.P_CH[chan].SLOT[slot].eg_sel_dr = eg_rate_select[OPLL.P_CH[chan].SLOT[slot].dr + OPLL.P_CH[chan].SLOT[slot].ksr]; OPLL.P_CH[chan].SLOT[slot].eg_sh_rr = eg_rate_shift[OPLL.P_CH[chan].SLOT[slot].rr + OPLL.P_CH[chan].SLOT[slot].ksr]; OPLL.P_CH[chan].SLOT[slot].eg_sel_rr = eg_rate_select[OPLL.P_CH[chan].SLOT[slot].rr + OPLL.P_CH[chan].SLOT[slot].ksr]; } if (OPLL.P_CH[chan].sus != 0) SLOT_rs = 16 + (5 << 2); else SLOT_rs = 16 + (7 << 2); OPLL.P_CH[chan].SLOT[slot].eg_sh_rs = eg_rate_shift[SLOT_rs + OPLL.P_CH[chan].SLOT[slot].ksr]; OPLL.P_CH[chan].SLOT[slot].eg_sel_rs = eg_rate_select[SLOT_rs + OPLL.P_CH[chan].SLOT[slot].ksr]; SLOT_dp = 16 + (13 << 2); OPLL.P_CH[chan].SLOT[slot].eg_sh_dp = eg_rate_shift[SLOT_dp + OPLL.P_CH[chan].SLOT[slot].ksr]; OPLL.P_CH[chan].SLOT[slot].eg_sel_dp = eg_rate_select[SLOT_dp + OPLL.P_CH[chan].SLOT[slot].ksr]; } private static void set_mul(int slot, int v) { OPLL.P_CH[slot / 2].SLOT[slot & 1].mul = mul_tab[v & 0x0f]; OPLL.P_CH[slot / 2].SLOT[slot & 1].KSR = (byte)((v & 0x10) != 0 ? 0 : 2); OPLL.P_CH[slot / 2].SLOT[slot & 1].eg_type = (byte)(v & 0x20); OPLL.P_CH[slot / 2].SLOT[slot & 1].vib = (byte)(v & 0x40); OPLL.P_CH[slot / 2].SLOT[slot & 1].AMmask = (uint)((v & 0x80) != 0 ? ~0 : 0); CALC_FCSLOT(slot / 2, slot & 1); } private static void set_ksl_tl(int chan, int v) { int ksl; ksl = v >> 6; OPLL.P_CH[chan].SLOT[0].ksl = (byte)(ksl != 0 ? 3 - ksl : 31); OPLL.P_CH[chan].SLOT[0].TL = (uint)((v & 0x3f) << 1); OPLL.P_CH[chan].SLOT[0].TLL = (int)(OPLL.P_CH[chan].SLOT[0].TL + (OPLL.P_CH[chan].ksl_base >> OPLL.P_CH[chan].SLOT[0].ksl)); } private static void set_ksl_wave_fb(int chan, int v) { int ksl; OPLL.P_CH[chan].SLOT[0].wavetable = (uint)(((v & 0x08) >> 3) * 0x400); OPLL.P_CH[chan].SLOT[0].fb_shift = (byte)((v & 7) != 0 ? (v & 7) + 8 : 0); ksl = v >> 6; OPLL.P_CH[chan].SLOT[1].ksl = (byte)(ksl != 0 ? 3 - ksl : 31); OPLL.P_CH[chan].SLOT[1].TLL = (int)(OPLL.P_CH[chan].SLOT[1].TL + (OPLL.P_CH[chan].ksl_base >> OPLL.P_CH[chan].SLOT[1].ksl)); OPLL.P_CH[chan].SLOT[1].wavetable = (uint)(((v & 0x10) >> 4) * 0x400); } private static void set_ar_dr(int slot, int v) { OPLL.P_CH[slot / 2].SLOT[slot & 1].ar = (uint)((v >> 4) != 0 ? 16 + ((v >> 4) << 2) : 0); if ((OPLL.P_CH[slot / 2].SLOT[slot & 1].ar + OPLL.P_CH[slot / 2].SLOT[slot & 1].ksr) < 16 + 62) { OPLL.P_CH[slot / 2].SLOT[slot & 1].eg_sh_ar = eg_rate_shift[OPLL.P_CH[slot / 2].SLOT[slot & 1].ar + OPLL.P_CH[slot / 2].SLOT[slot & 1].ksr]; OPLL.P_CH[slot / 2].SLOT[slot & 1].eg_sel_ar = eg_rate_select[OPLL.P_CH[slot / 2].SLOT[slot & 1].ar + OPLL.P_CH[slot / 2].SLOT[slot & 1].ksr]; } else { OPLL.P_CH[slot / 2].SLOT[slot & 1].eg_sh_ar = 0; OPLL.P_CH[slot / 2].SLOT[slot & 1].eg_sel_ar = 13 * 8; } OPLL.P_CH[slot / 2].SLOT[slot & 1].dr = (uint)((v & 0x0f) != 0 ? 16 + ((v & 0x0f) << 2) : 0); OPLL.P_CH[slot / 2].SLOT[slot & 1].eg_sh_dr = eg_rate_shift[OPLL.P_CH[slot / 2].SLOT[slot & 1].dr + OPLL.P_CH[slot / 2].SLOT[slot & 1].ksr]; OPLL.P_CH[slot / 2].SLOT[slot & 1].eg_sel_dr = eg_rate_select[OPLL.P_CH[slot / 2].SLOT[slot & 1].dr + OPLL.P_CH[slot / 2].SLOT[slot & 1].ksr]; } private static void set_sl_rr(int slot, int v) { OPLL.P_CH[slot / 2].SLOT[slot & 1].sl = sl_tab[v >> 4]; OPLL.P_CH[slot / 2].SLOT[slot & 1].rr = (uint)((v & 0x0f) != 0 ? 16 + ((v & 0x0f) << 2) : 0); OPLL.P_CH[slot / 2].SLOT[slot & 1].eg_sh_rr = eg_rate_shift[OPLL.P_CH[slot / 2].SLOT[slot & 1].rr + OPLL.P_CH[slot / 2].SLOT[slot & 1].ksr]; OPLL.P_CH[slot / 2].SLOT[slot & 1].eg_sel_rr = eg_rate_select[OPLL.P_CH[slot / 2].SLOT[slot & 1].rr + OPLL.P_CH[slot / 2].SLOT[slot & 1].ksr]; } private static void load_instrument(int chan, int slot, int inst) { set_mul(slot, (int)OPLL.inst_tab[inst][0]); set_mul(slot + 1, (int)OPLL.inst_tab[inst][1]); set_ksl_tl(chan, (int)OPLL.inst_tab[inst][2]); set_ksl_wave_fb(chan, (int)OPLL.inst_tab[inst][3]); set_ar_dr(slot, (int)OPLL.inst_tab[inst][4]); set_ar_dr(slot + 1, (int)OPLL.inst_tab[inst][5]); set_sl_rr(slot, (int)OPLL.inst_tab[inst][6]); set_sl_rr(slot + 1, (int)OPLL.inst_tab[inst][7]); } private static void update_instrument_zero(int r) { int chan; int chan_max; chan_max = 9; if ((OPLL.rhythm & 0x20) != 0) chan_max = 6; switch (r) { case 0: for (chan = 0; chan < chan_max; chan++) { if ((OPLL.instvol_r[chan] & 0xf0) == 0) { set_mul(chan * 2, OPLL.inst_tab[0][0]); } } break; case 1: for (chan = 0; chan < chan_max; chan++) { if ((OPLL.instvol_r[chan] & 0xf0) == 0) { set_mul(chan * 2 + 1, OPLL.inst_tab[0][1]); } } break; case 2: for (chan = 0; chan < chan_max; chan++) { if ((OPLL.instvol_r[chan] & 0xf0) == 0) { set_ksl_tl(chan, OPLL.inst_tab[0][2]); } } break; case 3: for (chan = 0; chan < chan_max; chan++) { if ((OPLL.instvol_r[chan] & 0xf0) == 0) { set_ksl_wave_fb(chan, OPLL.inst_tab[0][3]); } } break; case 4: for (chan = 0; chan < chan_max; chan++) { if ((OPLL.instvol_r[chan] & 0xf0) == 0) { set_ar_dr(chan * 2, OPLL.inst_tab[0][4]); } } break; case 5: for (chan = 0; chan < chan_max; chan++) { if ((OPLL.instvol_r[chan] & 0xf0) == 0) { set_ar_dr(chan * 2 + 1, OPLL.inst_tab[0][5]); } } break; case 6: for (chan = 0; chan < chan_max; chan++) { if ((OPLL.instvol_r[chan] & 0xf0) == 0) { set_sl_rr(chan * 2, OPLL.inst_tab[0][6]); } } break; case 7: for (chan = 0; chan < chan_max; chan++) { if ((OPLL.instvol_r[chan] & 0xf0) == 0) { set_sl_rr(chan * 2 + 1, OPLL.inst_tab[0][7]); } } break; } } static void OPLLWriteReg(int r, int v) { int inst; int chan; int slot; r &= 0xff; v &= 0xff; switch (r & 0xf0) { case 0x00: { switch (r & 0x0f) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: OPLL.inst_tab[0][r & 0x07] = (byte)v; update_instrument_zero(r & 7); break; case 0x0e: { if ((v & 0x20) != 0) { if ((OPLL.rhythm & 0x20) == 0) { chan = 6; slot = chan * 2; load_instrument(chan, slot, 16); chan = 7; slot = chan * 2; load_instrument(chan, slot, 17); OPLL.P_CH[chan].SLOT[0].TL = (uint)(((OPLL.instvol_r[chan] >> 4) << 2) << 1); OPLL.P_CH[chan].SLOT[0].TLL = (int)(OPLL.P_CH[chan].SLOT[0].TL + (OPLL.P_CH[chan].ksl_base >> OPLL.P_CH[chan].SLOT[0].ksl)); chan = 8; slot = chan * 2; load_instrument(chan, slot, 18); OPLL.P_CH[chan].SLOT[0].TL = (uint)(((OPLL.instvol_r[chan] >> 4) << 2) << 1); OPLL.P_CH[chan].SLOT[0].TLL = (int)(OPLL.P_CH[chan].SLOT[0].TL + (OPLL.P_CH[chan].ksl_base >> OPLL.P_CH[chan].SLOT[0].ksl)); } if ((v & 0x10) != 0) { KEY_ON(6, 0, 2); KEY_ON(6, 1, 2); } else { KEY_OFF(6, 0, unchecked((uint)(~2))); KEY_OFF(6, 1, unchecked((uint)(~2))); } if ((v & 0x01) != 0) KEY_ON(7, 0, 2); else KEY_OFF(7, 0, unchecked((uint)(~2))); if ((v & 0x08) != 0) KEY_ON(7, 1, 2); else KEY_OFF(7, 1, unchecked((uint)(~2))); if ((v & 0x04) != 0) KEY_ON(8, 0, 2); else KEY_OFF(8, 0, unchecked((uint)(~2))); if ((v & 0x02) != 0) KEY_ON(8, 1, 2); else KEY_OFF(8, 1, unchecked((uint)(~2))); } else { if ((OPLL.rhythm & 0x20) == 1) { chan = 6; slot = chan * 2; load_instrument(chan, slot, OPLL.instvol_r[chan] >> 4); chan = 7; slot = chan * 2; load_instrument(chan, slot, OPLL.instvol_r[chan] >> 4); chan = 8; slot = chan * 2; load_instrument(chan, slot, OPLL.instvol_r[chan] >> 4); } KEY_OFF(6, 0, unchecked((uint)(~2))); KEY_OFF(6, 1, unchecked((uint)(~2))); KEY_OFF(7, 0, unchecked((uint)(~2))); KEY_OFF(7, 1, unchecked((uint)(~2))); KEY_OFF(8, 0, unchecked((uint)(~2))); KEY_OFF(8, 1, unchecked((uint)(~2))); } OPLL.rhythm = (byte)(v & 0x3f); } break; } } break; case 0x10: case 0x20: { int block_fnum; chan = r & 0x0f; if (chan >= 9) chan -= 9; if ((r & 0x10) != 0) { block_fnum = (int)((OPLL.P_CH[chan].block_fnum & 0x0f00) | (uint)v); } else { block_fnum = (int)((uint)((v & 0x0f) << 8) | (OPLL.P_CH[chan].block_fnum & 0xff)); if ((v & 0x10) != 0) { KEY_ON(chan, 0, 1); KEY_ON(chan, 1, 1); } else { KEY_OFF(chan, 0, unchecked((uint)(~1))); KEY_OFF(chan, 1, unchecked((uint)(~1))); } OPLL.P_CH[chan].sus = (byte)(v & 0x20); } if (OPLL.P_CH[chan].block_fnum != block_fnum) { byte block; OPLL.P_CH[chan].block_fnum = (uint)block_fnum; OPLL.P_CH[chan].kcode = (byte)((block_fnum & 0x0f00) >> 8); OPLL.P_CH[chan].ksl_base = ksl_tab[block_fnum >> 5]; block_fnum = block_fnum * 2; block = (byte)((block_fnum & 0x1c00) >> 10); OPLL.P_CH[chan].fc = OPLL.fn_tab[block_fnum & 0x03ff] >> (7 - block); OPLL.P_CH[chan].SLOT[0].TLL = (int)(OPLL.P_CH[chan].SLOT[0].TL + (OPLL.P_CH[chan].ksl_base >> OPLL.P_CH[chan].SLOT[0].ksl)); OPLL.P_CH[chan].SLOT[1].TLL = (int)(OPLL.P_CH[chan].SLOT[1].TL + (OPLL.P_CH[chan].ksl_base >> OPLL.P_CH[chan].SLOT[1].ksl)); CALC_FCSLOT(chan, 0); CALC_FCSLOT(chan, 1); } } break; case 0x30: { byte old_instvol; chan = r & 0x0f; if (chan >= 9) chan -= 9; old_instvol = OPLL.instvol_r[chan]; OPLL.instvol_r[chan] = (byte)v; OPLL.P_CH[chan].SLOT[1].TL = (uint)((v & 0x0f) << 3); OPLL.P_CH[chan].SLOT[1].TLL = (int)(OPLL.P_CH[chan].SLOT[1].TL + (OPLL.P_CH[chan].ksl_base >> OPLL.P_CH[chan].SLOT[1].ksl)); if ((chan >= 6) && ((OPLL.rhythm & 0x20) != 0)) { if (chan >= 7) { OPLL.P_CH[chan].SLOT[0].TL = (uint)((OPLL.instvol_r[chan] >> 4) << 3); OPLL.P_CH[chan].SLOT[0].TLL = (int)(OPLL.P_CH[chan].SLOT[0].TL + (OPLL.P_CH[chan].ksl_base >> OPLL.P_CH[chan].SLOT[0].ksl)); } } else { if ((old_instvol & 0xf0) == (v & 0xf0)) return; slot = chan * 2; load_instrument(chan, slot, OPLL.instvol_r[chan] >> 4); } } break; default: break; } } private static int OPLL_LockTable() { num_lock++; if (num_lock > 1) return 0; if (init_tables() == 0) { num_lock--; return -1; } return 0; } private static void OPLL_UnLockTable() { if (num_lock != 0) num_lock--; } private static void OPLLResetChip() { int c, s; int i; OPLL.eg_timer = 0; OPLL.eg_cnt = 0; OPLL.noise_rng = 1; for (i = 0; i < 19; i++) { for (c = 0; c < 8; c++) { OPLL.inst_tab[i][c] = table[i][c]; } } OPLLWriteReg(0x0f, 0); for (i = 0x3f; i >= 0x10; i--) OPLLWriteReg(i, 0x00); for (c = 0; c < 9; c++) { for (s = 0; s < 2; s++) { OPLL.P_CH[c].SLOT[s].wavetable = 0; OPLL.P_CH[c].SLOT[s].state = 0; OPLL.P_CH[c].SLOT[s].volume = 0xff; } } } private static YM2413Struct OPLLCreate(int clock, int rate, int index) { OPLL_LockTable(); OPLL = new YM2413Struct(); OPLL.index = index; OPLL.clock = clock; OPLL.rate = rate; OPLL_initalize(); OPLLResetChip(); return OPLL; } private static void OPLLDestroy() { OPLL_UnLockTable(); } /*private static void OPLLSetUpdateHandler(OPLL_UPDATEHANDLER UpdateHandler, void* param) { OPLL.UpdateHandler = UpdateHandler; OPLL.UpdateParam = param; }*/ private static void OPLLWrite(int a, int v) { if ((a & 1) == 0) { OPLL.address = (byte)(v & 0xff); } else { if (OPLL.UpdateHandler != null) { //OPLL.UpdateHandler(OPLL.UpdateParam, 0); } OPLLWriteReg(OPLL.address, v); } } private static byte OPLLRead(int a) { if ((a & 1) == 0) { return OPLL.status; } return 0xff; } public static void ym2413_start(int clock) { int rate = clock / 72; ym2413_init(clock, rate, 0); } public static void ym2413_init(int clock, int rate, int index) { OPLLCreate(clock, rate, index); } private static void ym2413_shutdown() { OPLLDestroy(); } public static void ym2413_reset_chip() { OPLLResetChip(); } public static void ym2413_write(int a, int v) { OPLLWrite(a, v); } private static byte ym2413_read(int a) { return (byte)(OPLLRead(a) & 0x03); } /*private static void ym2413_set_update_handler(OPLL_UPDATEHANDLER UpdateHandler, void* param) { OPLLSetUpdateHandler(UpdateHandler, param); }*/ public unsafe static void ym2413_update_one(int offset, int length) { byte rhythm = (byte)(OPLL.rhythm & 0x20); int i; for (i = 0; i < length; i++) { int mo, ro; output[0] = 0; output[1] = 0; advance_lfo(); chan_calc(0); chan_calc(1); chan_calc(2); chan_calc(3); chan_calc(4); chan_calc(5); if (rhythm == 0) { chan_calc(6); chan_calc(7); chan_calc(8); } else { rhythm_calc((OPLL.noise_rng >> 0) & 1); } mo = output[0]; ro = output[1]; mo = limit(mo, 32767, -32768); ro = limit(ro, 32767, -32768); Sound.ym2413stream.streamoutput_Ptrs[0][offset + i] = mo; Sound.ym2413stream.streamoutput_Ptrs[1][offset + i] = ro; advance(); } } } }