private void envelope_generator_calc(_MultiPCM ptChip, _slot_t slot) { Int32 octave = ((slot.regs[3] >> 4) - 1) & 0xf; Int32 rate; if ((octave & 8) != 0) { octave = octave - 16; } if (slot.sample.key_rate_scale != 0xf) { rate = (octave + slot.sample.key_rate_scale) * 2 + ((slot.regs[3] >> 3) & 1); } else { rate = 0; } slot.envelope_gen.attack_rate = (Int32)get_rate(ptChip.attack_step, (UInt32)rate, slot.sample.attack_reg); slot.envelope_gen.decay1_rate = (Int32)get_rate(ptChip.decay_release_step, (UInt32)rate, slot.sample.decay1_reg); slot.envelope_gen.decay2_rate = (Int32)get_rate(ptChip.decay_release_step, (UInt32)rate, slot.sample.decay2_reg); slot.envelope_gen.release_rate = (Int32)get_rate(ptChip.decay_release_step, (UInt32)rate, slot.sample.release_reg); slot.envelope_gen.decay_level = 0xf - slot.sample.decay_level; }
private Int32 envelope_generator_update(_slot_t slot) { switch (slot.envelope_gen.state) { case _STATE.ATTACK: slot.envelope_gen.volume += slot.envelope_gen.attack_rate; if (slot.envelope_gen.volume >= (0x3ff << EG_SHIFT)) { slot.envelope_gen.state = _STATE.DECAY1; if (slot.envelope_gen.decay1_rate >= (0x400 << EG_SHIFT)) //Skip DECAY1, go directly to DECAY2 { slot.envelope_gen.state = _STATE.DECAY2; } slot.envelope_gen.volume = 0x3ff << EG_SHIFT; } break; case _STATE.DECAY1: slot.envelope_gen.volume -= slot.envelope_gen.decay1_rate; if (slot.envelope_gen.volume <= 0) { slot.envelope_gen.volume = 0; } if (slot.envelope_gen.volume >> EG_SHIFT <= (slot.envelope_gen.decay_level << (10 - 4))) { slot.envelope_gen.state = _STATE.DECAY2; } break; case _STATE.DECAY2: slot.envelope_gen.volume -= slot.envelope_gen.decay2_rate; if (slot.envelope_gen.volume <= 0) { slot.envelope_gen.volume = 0; } break; case _STATE.RELEASE: slot.envelope_gen.volume -= slot.envelope_gen.release_rate; if (slot.envelope_gen.volume <= 0) { slot.envelope_gen.volume = 0; slot.playing = 0; } break; default: return(1 << TL_SHIFT); } return(linear_to_exp_volume[slot.envelope_gen.volume >> EG_SHIFT]); }
//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++) { outputs[0][j] = 0; outputs[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_t slot = ptChip.slots[sl]; if (slot.playing != 0 && slot.muted == 0) { UInt32 vol = (slot.total_level >> TL_SHIFT) | (slot.pan << 7); UInt32 spos = slot.offset >> TL_SHIFT; UInt32 step = slot.step; Int32 csample = 0; Int32 fpart = (Int32)(slot.offset & ((1 << TL_SHIFT) - 1)); Int32 sample;// = (csample * fpart + slot.prev_sample * ((1 << SHIFT) - fpart)) >> SHIFT; if ((slot.format & 8) != 0) // 12-bit linear { UInt32 adr = slot.Base + (spos >> 2) * 6; switch (spos & 3) { case 0: { // ab.c .... .... Int16 w0 = (short)(read_byte(ptChip, adr) << 8 | ((read_byte(ptChip, adr + 1) & 0xf) << 4)); csample = w0; break; } case 1: { // ..C. AB.. .... Int16 w0 = (short)((read_byte(ptChip, adr + 2) << 8) | (read_byte(ptChip, adr + 1) & 0xf0)); csample = w0; break; } case 2: { // .... ..ab .c.. Int16 w0 = (short)(read_byte(ptChip, adr + 3) << 8 | ((read_byte(ptChip, adr + 4) & 0xf) << 4)); csample = w0; break; } case 3: { // .... .... C.AB Int16 w0 = (short)((read_byte(ptChip, adr + 5) << 8) | (read_byte(ptChip, adr + 4) & 0xf0)); csample = w0; break; } } } else { csample = (Int16)(read_byte(ptChip, slot.Base + spos) << 8); } sample = (csample * fpart + slot.prev_sample * ((1 << TL_SHIFT) - fpart)) >> TL_SHIFT; if ((slot.regs[6] & 7) != 0) // Vibrato enabled { step = (uint)(step * pitch_lfo_step(slot.pitch_lfo)); step >>= TL_SHIFT; } slot.offset += step; if (slot.offset >= (slot.sample.end << TL_SHIFT)) { slot.offset = slot.sample.loop << TL_SHIFT; } if ((spos ^ (slot.offset >> TL_SHIFT)) != 0) { slot.prev_sample = csample; } if ((slot.total_level >> TL_SHIFT) != slot.dest_total_level) { slot.total_level += (uint)slot.total_level_step; } if ((slot.regs[7] & 7) != 0) // Tremolo enabled { sample = sample * amplitude_lfo_step(slot.amplitude_lfo); sample >>= TL_SHIFT; } sample = (sample * envelope_generator_update(slot)) >> 10; smpl += (left_pan_table[vol] * sample) >> TL_SHIFT; smpr += (right_pan_table[vol] * sample) >> TL_SHIFT; } } outputs[0][i] = smpl; outputs[1][i] = smpr; } }
private void write_slot(_MultiPCM ptChip, _slot_t 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. // patched to load all sample data here, so registers 6 and 7 aren't overridden by KeyOn -Valley Bell init_sample(ptChip, slot.sample, (ushort)(slot.regs[1] | ((slot.regs[2] & 1) << 8))); write_slot(ptChip, slot, 6, slot.sample.lfo_vibrato_reg); write_slot(ptChip, slot, 7, slot.sample.lfo_amplitude_reg); 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.freq_step_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.playing = 1; slot.Base = slot.sample.start; slot.offset = 0; slot.prev_sample = 0; slot.total_level = slot.dest_total_level << TL_SHIFT; slot.format = slot.sample.format; envelope_generator_calc(ptChip, slot); slot.envelope_gen.state = _STATE.ATTACK; slot.envelope_gen.volume = 0; if (ptChip.sega_banking != 0) { slot.Base &= 0x1fffff; if (slot.Base >= 0x100000) { if ((slot.Base & 0x080000) != 0) { slot.Base = (slot.Base & 0x07ffff) | ptChip.bank1; } else { slot.Base = (slot.Base & 0x07ffff) | ptChip.bank0; } } } } else { if (slot.playing != 0) { if (slot.sample.release_reg != 0xf) { slot.envelope_gen.state = _STATE.RELEASE; } else { slot.playing = 0; } } } } break; case 5: //TL+Interpolation { slot.dest_total_level = (UInt32)((data >> 1) & 0x7f); if ((data & 1) == 0) //Interpolate TL { if ((slot.total_level >> TL_SHIFT) > slot.dest_total_level) { slot.total_level_step = ptChip.total_level_steps[0]; // decrease } else { slot.total_level_step = ptChip.total_level_steps[1]; // increase } } else { slot.total_level = slot.dest_total_level << TL_SHIFT; } } break; case 6: //LFO freq+PLFO { if (data != 0) { lfo_compute_step(ptChip, slot.pitch_lfo, (UInt32)(slot.regs[6] >> 3) & 7, (UInt32)(slot.regs[6] & 7), 0); lfo_compute_step(ptChip, slot.amplitude_lfo, (UInt32)(slot.regs[6] >> 3) & 7, (UInt32)(slot.regs[7] & 7), 1); } } break; case 7: //ALFO { if (data != 0) { lfo_compute_step(ptChip, slot.pitch_lfo, (UInt32)(slot.regs[6] >> 3) & 7, (UInt32)(slot.regs[6] & 7), 0); lfo_compute_step(ptChip, slot.amplitude_lfo, (UInt32)(slot.regs[6] >> 3) & 7, (UInt32)(slot.regs[7] & 7), 1); } } break; } }