private Int32 EG_Update(_SLOT slot) { switch (slot.EG.state) { case _STATE.ATTACK: slot.EG.volume += slot.EG.AR; if (slot.EG.volume >= (0x3ff << EG_SHIFT)) { slot.EG.state = _STATE.DECAY1; if (slot.EG.D1R >= (0x400 << EG_SHIFT)) //Skip DECAY1, go directly to DECAY2 { slot.EG.state = _STATE.DECAY2; } slot.EG.volume = 0x3ff << EG_SHIFT; } break; case _STATE.DECAY1: slot.EG.volume -= slot.EG.D1R; if (slot.EG.volume <= 0) { slot.EG.volume = 0; } if (slot.EG.volume >> EG_SHIFT <= (slot.EG.DL << (10 - 4))) { slot.EG.state = _STATE.DECAY2; } break; case _STATE.DECAY2: slot.EG.volume -= slot.EG.D2R; if (slot.EG.volume <= 0) { slot.EG.volume = 0; } break; case _STATE.RELEASE: slot.EG.volume -= slot.EG.RR; if (slot.EG.volume <= 0) { slot.EG.volume = 0; slot.Playing = 0; } break; default: return(1 << SHIFT); } return(lin2expvol[slot.EG.volume >> EG_SHIFT]); }
private void EG_Calc(_MultiPCM ptChip, _SLOT slot) { Int32 octave = ((slot.Regs[3] >> 4) - 1) & 0xf; Int32 rate; if ((octave & 8) != 0) { octave = octave - 16; } if (slot.Sample.KRS != 0xf) { rate = (octave + slot.Sample.KRS) * 2 + ((slot.Regs[3] >> 3) & 1); } else { rate = 0; } slot.EG.AR = (Int32)Get_RATE(ptChip.ARStep, (UInt32)rate, slot.Sample.AR); slot.EG.D1R = (Int32)Get_RATE(ptChip.DRStep, (UInt32)rate, slot.Sample.DR1); slot.EG.D2R = (Int32)Get_RATE(ptChip.DRStep, (UInt32)rate, slot.Sample.DR2); slot.EG.RR = (Int32)Get_RATE(ptChip.DRStep, (UInt32)rate, slot.Sample.RR); slot.EG.DL = 0xf - slot.Sample.DL; }
//static STREAM_UPDATE( MultiPCM_update ) public void MultiPCM_update(byte ChipID, Int32[][] outputs, Int32 samples) { //MultiPCM *ptChip = (MultiPCM *)param; _MultiPCM ptChip = MultiPCMData[ChipID]; Int32[][] datap = new Int32[2][]; Int32 i, sl; datap[0] = outputs[0]; datap[1] = outputs[1]; for (int j = 0; j < samples; j++) { datap[0][j] = 0; datap[1][j] = 0; } //memset(datap[0], 0, sizeof(*datap[0]) * samples); //memset(datap[1], 0, sizeof(*datap[1]) * samples); for (i = 0; i < samples; ++i) { Int32 smpl = 0; Int32 smpr = 0; for (sl = 0; sl < 28; ++sl) { _SLOT slot = ptChip.Slots[sl]; if (slot.Playing != 0 && slot.Muted == 0) { UInt32 vol = (slot.TL >> SHIFT) | (slot.Pan << 7); UInt32 adr = slot.offset >> SHIFT; Int32 sample; UInt32 step = slot.step; Int32 csample = (Int16)(ptChip.ROM[(slot.Base + adr) & ptChip.ROMMask] << 8); Int32 fpart = (Int32)(slot.offset & ((1 << SHIFT) - 1)); sample = (csample * fpart + slot.Prev * ((1 << SHIFT) - fpart)) >> SHIFT; if ((slot.Regs[6] & 7) != 0) //Vibrato enabled { step = (UInt32)(step * PLFO_Step(slot.PLFO)); step >>= SHIFT; } slot.offset += step; if (slot.offset >= (slot.Sample.End << SHIFT)) { slot.offset = slot.Sample.Loop << SHIFT; } if ((adr ^ (slot.offset >> SHIFT)) != 0) { slot.Prev = csample; } if ((slot.TL >> SHIFT) != slot.DstTL) { slot.TL += (UInt32)slot.TLStep; } if ((slot.Regs[7] & 7) != 0) //Tremolo enabled { sample = sample * ALFO_Step(slot.ALFO); sample >>= SHIFT; } sample = (sample * EG_Update(slot)) >> 10; smpl += (LPANTABLE[vol] * sample) >> SHIFT; smpr += (RPANTABLE[vol] * sample) >> SHIFT; } } /*#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x) * datap[0][i]=ICLIP16(smpl); * datap[1][i]=ICLIP16(smpr);*/ datap[0][i] = smpl; datap[1][i] = smpr; } }
private void WriteSlot(_MultiPCM ptChip, _SLOT slot, Int32 reg, byte data) { slot.Regs[reg] = data; switch (reg) { case 0: //PANPOT slot.Pan = (UInt32)((data >> 4) & 0xf); break; case 1: //Sample //according to YMF278 sample write causes some base params written to the regs (envelope+lfos) //the game should never change the sample while playing. { _Sample Sample = ptChip.Samples[slot.Regs[1]]; WriteSlot(ptChip, slot, 6, Sample.LFOVIB); WriteSlot(ptChip, slot, 7, Sample.AM); } break; case 2: //Pitch case 3: { UInt32 oct = (UInt32)(((slot.Regs[3] >> 4) - 1) & 0xf); UInt32 pitch = (UInt32)(((slot.Regs[3] & 0xf) << 6) | (slot.Regs[2] >> 2)); pitch = ptChip.FNS_Table[pitch]; if ((oct & 0x8) != 0) { pitch >>= (Int32)(16 - oct); } else { pitch <<= (Int32)oct; } slot.step = (UInt32)(pitch / ptChip.Rate); } break; case 4: //KeyOn/Off (and more?) { if ((data & 0x80) != 0) //KeyOn { slot.Sample = ptChip.Samples[slot.Regs[1]]; slot.Playing = 1; slot.Base = slot.Sample.Start; slot.offset = 0; slot.Prev = 0; slot.TL = slot.DstTL << SHIFT; EG_Calc(ptChip, slot); slot.EG.state = _STATE.ATTACK; slot.EG.volume = 0; if (slot.Base >= 0x100000) { if ((slot.Pan & 8) != 0) { slot.Base = (slot.Base & 0xfffff) | (ptChip.BankL); } else { slot.Base = (slot.Base & 0xfffff) | (ptChip.BankR); } } } else { if (slot.Playing != 0) { if (slot.Sample.RR != 0xf) { slot.EG.state = _STATE.RELEASE; } else { slot.Playing = 0; } } } } break; case 5: //TL+Interpolation { slot.DstTL = (UInt32)((data >> 1) & 0x7f); if ((data & 1) == 0) //Interpolate TL { if ((slot.TL >> SHIFT) > slot.DstTL) { slot.TLStep = TLSteps[0]; //decrease } else { slot.TLStep = TLSteps[1]; //increase } } else { slot.TL = slot.DstTL << SHIFT; } } break; case 6: //LFO freq+PLFO { if (data != 0) { LFO_ComputeStep(ptChip, slot.PLFO, (UInt32)(slot.Regs[6] >> 3) & 7, (UInt32)(slot.Regs[6] & 7), 0); LFO_ComputeStep(ptChip, slot.ALFO, (UInt32)(slot.Regs[6] >> 3) & 7, (UInt32)(slot.Regs[7] & 7), 1); } } break; case 7: //ALFO { if (data != 0) { LFO_ComputeStep(ptChip, slot.PLFO, (UInt32)(slot.Regs[6] >> 3) & 7, (UInt32)(slot.Regs[6] & 7), 0); LFO_ComputeStep(ptChip, slot.ALFO, (UInt32)(slot.Regs[6] >> 3) & 7, (UInt32)(slot.Regs[7] & 7), 1); } } break; } }