/* ----- key on ----- */ void OPL_KEYON(OPL_SLOT SLOT) { /* sin wave restart */ SLOT.Cnt = 0; /* set attack */ SLOT.evm = ENV_MOD_AR; SLOT.evs = SLOT.evsa; SLOT.evc = EG_AST; SLOT.eve = EG_AED; }
/* set multi,am,vib,EG-TYP,KSR,mul */ void set_mul(int slot,int v) { OPL_CH CH = P_CH[slot >> 1]; OPL_SLOT SLOT = CH.SLOT[slot & 1]; SLOT.mul = MUL_TABLE[v & 0x0f]; SLOT.KSR = (v & 0x10) != 0 ? (byte)0 : (byte)2; SLOT.eg_typ = (byte)((v & 0x20) >> 5); SLOT.vib = (byte)(v & 0x40); SLOT.ams = (byte)(v & 0x80); CALC_FCSLOT(CH,SLOT); }
/* set ksl & tl */ void set_ksl_tl(int slot,int v) { OPL_CH CH = P_CH[slot >> 1]; OPL_SLOT SLOT = CH.SLOT[slot & 1]; int ksl = v >> 6; /* 0 / 1.5 / 3 / 6 db/OCT */ SLOT.ksl = ksl != 0 ? (byte)(3 - ksl) : (byte)31; SLOT.TL = (int)((v & 0x3f) * (0.75 / EG_STEP())); /* 0.75db step */ if ((mode & 0x80) == 0) { /* not CSM latch total level */ SLOT.TLL = (int)(SLOT.TL + (CH.ksl_base >> SLOT.ksl)); } }
/* ----- key off ----- */ void OPL_KEYOFF(OPL_SLOT SLOT) { if (SLOT.evm > ENV_MOD_RR) { /* set envelope counter from envleope output */ // WORKAROUND: The Kyra engine does something very strange when // starting a new song. For each channel: // // * The release rate is set to "fastest". // * Any note is keyed off. // * A very low-frequency note is keyed on. // // Usually, what happens next is that the real notes is keyed // on immediately, in which case there's no problem. // // However, if the note is again keyed off (because the channel // begins on a rest rather than a note), the envelope counter // was moved from the very lowest point on the attack curve to // the very highest point on the release curve. // // Again, this might not be a problem, if the release rate is // still set to "fastest". But in many cases, it had already // been increased. And, possibly because of inaccuracies in the // envelope generator, that would cause the note to "fade out" // for quite a long time. // // What we really need is a way to find the correct starting // point for the envelope counter, and that may be what the // commented-out line below is meant to do. For now, simply // handle the pathological case. if (SLOT.evm == ENV_MOD_AR && SLOT.evc == EG_AST) { SLOT.evc = EG_DED; } else if ((SLOT.evc & EG_DST) == 0) { //SLOT.evc = (ENV_CURVE[SLOT.evc>>ENV_BITS]<<ENV_BITS) + EG_DST; SLOT.evc = EG_DST; } SLOT.eve = EG_DED; SLOT.evs = SLOT.evsr; SLOT.evm = ENV_MOD_RR; } }
/* ---------- frequency counter for operater update ---------- */ void CALC_FCSLOT(OPL_CH CH,OPL_SLOT SLOT) { int ksr; /* frequency step counter */ SLOT.Incr = CH.fc * SLOT.mul; ksr = CH.kcode >> SLOT.KSR; if (SLOT.ksr != ksr) { SLOT.ksr = (byte)ksr; /* attack , decay rate recalcration */ SLOT.evsa = SLOT.AR(ksr); SLOT.evsd = SLOT.DR(ksr); SLOT.evsr = SLOT.RR(ksr); } SLOT.TLL = (int)(SLOT.TL + (CH.ksl_base >> SLOT.ksl)); }
/* set sustain level & release rate */ void set_sl_rr(int slot,int v) { OPL_CH CH = P_CH[slot >> 1]; OPL_SLOT SLOT = CH.SLOT[slot & 1]; int sl = v >> 4; int rr = v & 0x0f; SLOT.SL = SL_TABLE[sl]; if (SLOT.evm == ENV_MOD_DR) { SLOT.eve = SLOT.SL; } SLOT.RR = new Func <int,int>(index => DR_TABLE[index + rr << 2]); SLOT.evsr = SLOT.RR(SLOT.ksr); if (SLOT.evm == ENV_MOD_RR) { SLOT.evs = SLOT.evsr; } }
/* set attack rate & decay rate */ void set_ar_dr(int slot,int v) { OPL_CH CH = P_CH[slot >> 1]; OPL_SLOT SLOT = CH.SLOT[slot & 1]; int ar = v >> 4; int dr = v & 0x0f; SLOT.AR = ar != 0 ? new Func <int,int>(index => AR_TABLE[index + ar << 2]) : RATE_0; SLOT.evsa = SLOT.AR(SLOT.ksr); if (SLOT.evm == ENV_MOD_AR) { SLOT.evs = SLOT.evsa; } SLOT.DR = dr != 0 ? new Func <int,int>(index => DR_TABLE[index + dr << 2]) : RATE_0; SLOT.evsd = SLOT.DR(SLOT.ksr); if (SLOT.evm == ENV_MOD_DR) { SLOT.evs = SLOT.evsd; } }
/* return : envelope output */ uint OPL_CALC_SLOT(OPL_SLOT SLOT) { /* calcrate envelope generator */ if ((SLOT.evc += SLOT.evs) >= SLOT.eve) { switch (SLOT.evm) { case ENV_MOD_AR: /* ATTACK . DECAY1 */ /* next DR */ SLOT.evm = ENV_MOD_DR; SLOT.evc = EG_DST; SLOT.eve = SLOT.SL; SLOT.evs = SLOT.evsd; break; case ENV_MOD_DR: /* DECAY . SL or RR */ SLOT.evc = SLOT.SL; SLOT.eve = EG_DED; if (SLOT.eg_typ) { SLOT.evs = 0; } else { SLOT.evm = ENV_MOD_RR; SLOT.evs = SLOT.evsr; } break; case ENV_MOD_RR: /* RR . OFF */ SLOT.evc = EG_OFF; SLOT.eve = EG_OFF + 1; SLOT.evs = 0; break; } } /* calcrate envelope */ return(SLOT.TLL + ENV_CURVE[SLOT.evc >> ENV_BITS] + (SLOT.ams ? ams : 0)); }
/* ----- key off ----- */ void OPL_KEYOFF(OPL_SLOT SLOT) { if (SLOT.evm > ENV_MOD_RR) { /* set envelope counter from envleope output */ // WORKAROUND: The Kyra engine does something very strange when // starting a new song. For each channel: // // * The release rate is set to "fastest". // * Any note is keyed off. // * A very low-frequency note is keyed on. // // Usually, what happens next is that the real notes is keyed // on immediately, in which case there's no problem. // // However, if the note is again keyed off (because the channel // begins on a rest rather than a note), the envelope counter // was moved from the very lowest point on the attack curve to // the very highest point on the release curve. // // Again, this might not be a problem, if the release rate is // still set to "fastest". But in many cases, it had already // been increased. And, possibly because of inaccuracies in the // envelope generator, that would cause the note to "fade out" // for quite a long time. // // What we really need is a way to find the correct starting // point for the envelope counter, and that may be what the // commented-out line below is meant to do. For now, simply // handle the pathological case. if (SLOT.evm == ENV_MOD_AR && SLOT.evc == EG_AST) SLOT.evc = EG_DED; else if ((SLOT.evc & EG_DST) == 0) //SLOT.evc = (ENV_CURVE[SLOT.evc>>ENV_BITS]<<ENV_BITS) + EG_DST; SLOT.evc = EG_DST; SLOT.eve = EG_DED; SLOT.evs = SLOT.evsr; SLOT.evm = ENV_MOD_RR; } }
/* return : envelope output */ uint OPL_CALC_SLOT(OPL_SLOT SLOT) { /* calcrate envelope generator */ if ((SLOT.evc += SLOT.evs) >= SLOT.eve) { switch (SLOT.evm) { case ENV_MOD_AR: /* ATTACK . DECAY1 */ /* next DR */ SLOT.evm = ENV_MOD_DR; SLOT.evc = EG_DST; SLOT.eve = SLOT.SL; SLOT.evs = SLOT.evsd; break; case ENV_MOD_DR: /* DECAY . SL or RR */ SLOT.evc = SLOT.SL; SLOT.eve = EG_DED; if (SLOT.eg_typ) { SLOT.evs = 0; } else { SLOT.evm = ENV_MOD_RR; SLOT.evs = SLOT.evsr; } break; case ENV_MOD_RR: /* RR . OFF */ SLOT.evc = EG_OFF; SLOT.eve = EG_OFF + 1; SLOT.evs = 0; break; } } /* calcrate envelope */ return SLOT.TLL + ENV_CURVE[SLOT.evc >> ENV_BITS] + (SLOT.ams ? ams : 0); }
/* ---------- frequency counter for operater update ---------- */ void CALC_FCSLOT(OPL_CH CH, OPL_SLOT SLOT) { int ksr; /* frequency step counter */ SLOT.Incr = CH.fc * SLOT.mul; ksr = CH.kcode >> SLOT.KSR; if (SLOT.ksr != ksr) { SLOT.ksr = (byte)ksr; /* attack , decay rate recalcration */ SLOT.evsa = SLOT.AR(ksr); SLOT.evsd = SLOT.DR(ksr); SLOT.evsr = SLOT.RR(ksr); } SLOT.TLL = (int)(SLOT.TL + (CH.ksl_base >> SLOT.ksl)); }