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();
  }
}
