const NUMBER_OF_VOICES = 8;
const VOICE_SCALE = 1 + Math.sqrt(NUMBER_OF_VOICES);
const vibratoFrequency = new Float64Array(bufferSize).fill(5);

const SimpleSynth = ({ input, attack, decay, sustain, release, transpose, output }) => {
  output.fill(0);
  const voices = useRoundRobinVoicing(input, NUMBER_OF_VOICES);

  const vibratoOscillator = useOscillator(Waveform.Sine);
  const vibratoOutput = vibratoOscillator(vibratoFrequency);

  for (let v = 0; v < voices.length; v ++) {
    const oscillator = useOscillator(Waveform.Sawtooth, true);
    const processEnvelope = useEnvelope();
    const gate = voices[v].gate;
    const trigger = voices[v].trigger;
    const frequency = useMonoSignal(voices[v].frequency.length);
    for (let i = 0; i < frequency.length; i ++) {
      frequency[i] = (
        voices[v].frequency[i] *
        Math.pow(
          2,
          (
            input.ccs.pitchBend[i] +
            (vibratoOutput[i] * (input.ccs.modulationWheel[i] + input.ccs.channelPressure[i]))
          ) / 12
        )
      );
    }
    const velocity = voices[v].velocity;
    const oscillatorOutput = oscillator(frequency);
    const amplitude = processEnvelope(trigger, gate, attack, decay, sustain, release);
    for (let i = 0; i < oscillatorOutput.length; i ++) {
      output[i] += oscillatorOutput[i] * amplitude[i] * velocity[i] / VOICE_SCALE;
    }
  }
}

this.constructDevice = () => constructPerChannelDevice({
  implementation: SimpleSynth,
  inputs: {
    input: midi('In'),
    attack: adsrTimeSetting('Attack', 1),
    decay: adsrTimeSetting('Decay', 100),
    sustain: zeroOneSetting('Sustain', 0.5),
    release: adsrTimeSetting('Release', 50),
    transpose: numberSetting('Transpose', { defaultValue: 0, min: -1, max: 1 })
  },
  outputs: {
    output: stereo('Out')
  }
});

