#ifdef DSP_CPP

inline void DSP::voice_output(voice_t& v, bool channel) {
  //apply left/right volume
  int amp = (state.t_output * (int8)VREG(voll + channel)) >> 7;

  //add to output total
  state.t_main_out[channel] += amp;
  state.t_main_out[channel] = sclamp<16>(state.t_main_out[channel]);

  //optionally add to echo total
  if(state.t_eon & v.vbit) {
    state.t_echo_out[channel] += amp;
    state.t_echo_out[channel] = sclamp<16>(state.t_echo_out[channel]);
  }
}

void DSP::voice_1(voice_t& v) {
  state.t_dir_addr = (state.t_dir << 8) + (state.t_srcn << 2);
  state.t_srcn = VREG(srcn);
}

void DSP::voice_2(voice_t& v) {
  //read sample pointer (ignored if not needed)
  uint16 addr = state.t_dir_addr;
  if(!v.kon_delay) addr += 2;
  uint8 lo = smp.apuram[(uint16)(addr + 0)];
  uint8 hi = smp.apuram[(uint16)(addr + 1)];
  state.t_brr_next_addr = ((hi << 8) + lo);

  state.t_adsr0 = VREG(adsr0);

  //read pitch, spread over two clocks
  state.t_pitch = VREG(pitchl);
}

void DSP::voice_3(voice_t& v) {
  voice_3a(v);
  voice_3b(v);
  voice_3c(v);
}

void DSP::voice_3a(voice_t& v) {
  state.t_pitch += (VREG(pitchh) & 0x3f) << 8;
}

void DSP::voice_3b(voice_t& v) {
  state.t_brr_byte   = smp.apuram[(uint16)(v.brr_addr + v.brr_offset)];
  state.t_brr_header = smp.apuram[(uint16)(v.brr_addr)];
}

void DSP::voice_3c(voice_t& v) {
  //pitch modulation using previous voice's output

  if(state.t_pmon & v.vbit) {
    state.t_pitch += ((state.t_output >> 5) * state.t_pitch) >> 10;
  }

  if(v.kon_delay) {
    //get ready to start BRR decoding on next sample
    if(v.kon_delay == 5) {
      v.brr_addr   = state.t_brr_next_addr;
      v.brr_offset = 1;
      v.buf_pos    = 0;
      state.t_brr_header = 0;  //header is ignored on this sample
    }

    //envelope is never run during KON
    v.env = 0;
    v.hidden_env = 0;

    //disable BRR decoding until last three samples
    v.interp_pos = 0;
    v.kon_delay--;
    if(v.kon_delay & 3) v.interp_pos = 0x4000;

    //pitch is never added during KON
    state.t_pitch = 0;
  }

  //gaussian interpolation
  int output = gaussian_interpolate(v);

  //noise
  if(state.t_non & v.vbit) {
    output = (int16)(state.noise << 1);
  }

  //apply envelope
  state.t_output = ((output * v.env) >> 11) & ~1;
  v.t_envx_out = v.env >> 4;

  //immediate silence due to end of sample or soft reset
  if(REG(flg) & 0x80 || (state.t_brr_header & 3) == 1) {
    v.env_mode = env_release;
    v.env = 0;
  }

  if(state.every_other_sample) {
    //KOFF
    if(state.t_koff & v.vbit) {
      v.env_mode = env_release;
    }

    //KON
    if(state.kon & v.vbit) {
      v.kon_delay = 5;
      v.env_mode = env_attack;
    }
  }

  //run envelope for next sample
  if(!v.kon_delay) envelope_run(v);
}

void DSP::voice_4(voice_t& v) {
  //decode BRR
  state.t_looped = 0;
  if(v.interp_pos >= 0x4000) {
    brr_decode(v);
    v.brr_offset += 2;
    if(v.brr_offset >= 9) {
      //start decoding next BRR block
      v.brr_addr = (uint16)(v.brr_addr + 9);
      if(state.t_brr_header & 1) {
        v.brr_addr = state.t_brr_next_addr;
        state.t_looped = v.vbit;
      }
      v.brr_offset = 1;
    }
  }

  //apply pitch
  v.interp_pos = (v.interp_pos & 0x3fff) + state.t_pitch;

  //keep from getting too far ahead (when using pitch modulation)
  if(v.interp_pos > 0x7fff) v.interp_pos = 0x7fff;

  //output left
  voice_output(v, 0);
}

void DSP::voice_5(voice_t& v) {
  //output right
  voice_output(v, 1);

  //ENDX, OUTX and ENVX won't update if you wrote to them 1-2 clocks earlier
  state.endx_buf = REG(endx) | state.t_looped;

  //clear bit in ENDX if KON just began
  if(v.kon_delay == 5) state.endx_buf &= ~v.vbit;
}

void DSP::voice_6(voice_t& v) {
  state.outx_buf = state.t_output >> 8;
}

void DSP::voice_7(voice_t& v) {
  //update ENDX
  REG(endx) = (uint8)state.endx_buf;
  state.envx_buf = v.t_envx_out;
}

void DSP::voice_8(voice_t& v) {
  //update OUTX
  VREG(outx) = (uint8)state.outx_buf;
}

void DSP::voice_9(voice_t& v) {
  //update ENVX
  VREG(envx) = (uint8)state.envx_buf;
}

#endif
