auto YM2612::Channel::Operator::trigger(bool state) -> void { if(keyOn == state) return; //no change keyOn = state; envelope.state = Release; updateEnvelope(); if(keyOn) { //restart phase and envelope generators phase.value = 0; ssg.invert = false; envelope.state = Attack; updateEnvelope(); if(envelope.rate >= 62) { //skip attack and possibly decay stages envelope.value = 0; envelope.state = envelope.sustainLevel ? Decay : Sustain; updateEnvelope(); } } else if(ssg.enable && ssg.attack != ssg.invert) { //SSG-EG key-off envelope.value = 0x200 - envelope.value; } updateLevel(); } auto YM2612::Channel::Operator::runEnvelope() -> void { uint sustain = envelope.sustainLevel < 15 ? envelope.sustainLevel << 5 : 0x3f0; if(ym2612.envelope.clock & (1 << envelope.divider) - 1) return; uint value = ym2612.envelope.clock >> envelope.divider; uint step = envelope.steps >> ((~value & 7) << 2) & 0xf; if(ssg.enable) step <<= 2; //SSG results in a 4x faster envelope if(envelope.state == Attack) { uint next = envelope.value + (~uint16(envelope.value) * step >> 4) & 0x3ff; if(next <= envelope.value) { envelope.value = next; } else { envelope.value = 0; envelope.state = envelope.value < sustain ? Decay : Sustain; updateEnvelope(); } } else if(!ssg.enable || envelope.value < 0x200) { envelope.value = min(envelope.value + step, 0x3ff); if(envelope.state == Decay && envelope.value >= sustain) { envelope.state = Sustain; updateEnvelope(); } } updateLevel(); } auto YM2612::Channel::Operator::runPhase() -> void { phase.value += phase.delta; //advance wave position if(!(ssg.enable && envelope.value >= 0x200)) return; //SSG loop check if(!ssg.hold && !ssg.alternate) phase.value = 0; if(!ssg.hold || ssg.attack == ssg.invert) ssg.invert ^= ssg.alternate; if(envelope.state == Attack) { //do nothing; SSG is meant to skip the attack phase } else if(envelope.state != Release && !ssg.hold) { //if still looping, reset the envelope envelope.state = Attack; if(envelope.attackRate >= 62) { envelope.value = 0; envelope.state = envelope.sustainLevel ? Decay : Sustain; } updateEnvelope(); } else if(envelope.state == Release || (ssg.hold && ssg.attack == ssg.invert)) { //clear envelope once finished envelope.value = 0x3ff; } updateLevel(); } auto YM2612::Channel::Operator::updateEnvelope() -> void { uint key = min(max((uint)pitch.value, 0x300), 0x4ff); uint ksr = (octave.value << 2) + ((key - 0x300) >> 7); uint rate = 0; if(envelope.state == Attack) rate += (envelope.attackRate << 1); if(envelope.state == Decay) rate += (envelope.decayRate << 1); if(envelope.state == Sustain) rate += (envelope.sustainRate << 1); if(envelope.state == Release) rate += (envelope.releaseRate << 1); rate += (ksr >> 3 - envelope.keyScale) * (rate > 0); rate = min(rate, 63); auto& entry = envelopeRates[rate >> 2]; envelope.rate = rate; envelope.divider = entry.divider; envelope.steps = entry.steps[rate & 3]; } auto YM2612::Channel::Operator::updatePitch() -> void { //only channel[2] allows per-operator frequencies //implemented by forcing mode to zero (single frequency) for other channels //in single frequency mode, operator[3] frequency is used for all operators pitch.value = channel.mode ? pitch.reload : channel[3].pitch.reload; octave.value = channel.mode ? octave.reload : channel[3].octave.reload; updatePhase(); updateEnvelope(); //due to key scaling } auto YM2612::Channel::Operator::updatePhase() -> void { uint key = min(max((uint)pitch.value, 0x300), 0x4ff); uint ksr = (octave.value << 2) + ((key - 0x300) >> 7); uint tuning = detune & 3 ? detunes[(detune & 3) - 1][ksr & 7] >> (3 - (ksr >> 3)) : 0; uint lfo = ym2612.lfo.clock >> 2 & 0x1f; uint pm = 4 * vibratos[channel.vibrato][lfo & 15] * (-lfo >> 4); uint msb = 10; while(msb > 4 && ~pitch.value & 1 << msb) msb--; phase.delta = pitch.value + (pm >> 10 - msb) << 6 >> 7 - octave.value; phase.delta = (!detune.bit(2) ? phase.delta + tuning : phase.delta - tuning) & 0x1ffff; phase.delta = (multiple ? phase.delta * multiple : phase.delta >> 1) & 0xfffff; } auto YM2612::Channel::Operator::updateLevel() -> void { uint lfo = ym2612.lfo.clock & 0x40 ? ym2612.lfo.clock & 0x3f : ~ym2612.lfo.clock & 0x3f; uint depth = tremolos[channel.tremolo]; bool invert = ssg.attack != ssg.invert && envelope.state != Release; uint10 value = ssg.enable && invert ? 0x200 - envelope.value : 0 + envelope.value; outputLevel = ((totalLevel << 3) + value + (lfoEnable ? lfo << 1 >> depth : 0)) << 3; } auto YM2612::Channel::power() -> void { leftEnable = 1; rightEnable = 1; algorithm = 0; feedback = 0; vibrato = 0; tremolo = 0; mode = 0; for(auto& op : operators) { op.keyOn = 0; op.lfoEnable = 0; op.detune = 0; op.multiple = 0; op.totalLevel = 0; op.outputLevel = 0x1fff; op.output = 0; op.prior = 0; op.pitch.value = 0; op.pitch.reload = 0; op.pitch.latch = 0; op.octave.value = 0; op.octave.reload = 0; op.octave.latch = 0; op.phase.value = 0; op.phase.delta = 0; op.envelope.state = Release; op.envelope.rate = 0; op.envelope.divider = 11; op.envelope.steps = 0; op.envelope.value = 0x3ff; op.envelope.keyScale = 0; op.envelope.attackRate = 0; op.envelope.decayRate = 0; op.envelope.sustainRate = 0; op.envelope.sustainLevel = 0; op.envelope.releaseRate = 1; op.ssg.enable = 0; op.ssg.attack = 0; op.ssg.alternate = 0; op.ssg.hold = 0; op.ssg.invert = 0; op.updatePitch(); op.updateLevel(); } }