const DrumVoice = ({
  wavetable,
  trigger,
  gate,
  velocity,
  output,
  basePitch,
  modPitch,
  wtTime,
  pitchTime,
  gainTime,
  filterTime
}) => {
  const filterEnvelope = useEnvelope(Curve.Log2, false);
  const gainEnvelope = useEnvelope(Curve.Log2, false);
  const pitchEnvelope = useEnvelope(Curve.Log2, false);
  const wtPosEnvelope = useEnvelope(Curve.Linear, false);

  const oscillator = useWavetableReader(wavetable, WavetableWarpMode.Off);

  const wtPos = wtPosEnvelope(
    trigger,
    gate,
    useConstantBuffer(0),
    wtTime,
    useConstantBuffer(0),
    wtTime,
  );

  subtract(useConstantBuffer(1), wtPos, wtPos);

  const pitch = pitchEnvelope(
    trigger,
    gate,
    useConstantBuffer(0),
    pitchTime,
    useConstantBuffer(0),
    pitchTime,
  );

  const frequency = useMonoSignal();
  for (let i = 0; i < pitch.length; i ++) {
    frequency[i] = pitchToHz((pitch[i] * modPitch[i]) + basePitch[i]);
  }
  const gain = gainEnvelope(
    trigger,
    gate,
    useConstantBuffer(1 * MS_TO_SAMPLES),
    gainTime,
    useConstantBuffer(0),
    gainTime
  );

  const oscillatorOutput = oscillator(frequency, wtPos, useConstantBuffer(0), useConstantBuffer(0), trigger);

  const filterFrequency = filterEnvelope(
    trigger,
    gate,
    useConstantBuffer(0),
    filterTime,
    useConstantBuffer(0),
    filterTime
  );

  for (let i = 0; i < filterFrequency.length; i ++) {
    filterFrequency[i] = zeroOneToHz((filterFrequency[i] * 0.75) + 0.25);
  }

  const voiceOutput = useMonoSignal();

  const filter = useBiQuadFilter();

  const filterOutput = filter(oscillatorOutput, BiQuadFilterMode.LowPass, filterFrequency, useConstantBuffer(0.2), useConstantBuffer(0));

  multiply(filterOutput, gain, voiceOutput);

  add(output[0], voiceOutput, output[0]);
  add(output[1], voiceOutput, output[1]);
}

const RattleVoice = (trigger, gate, velocity, output, attackTime, decayTime) => {
  const gainEnvelope = useEnvelope(Curve.Log2, false);

  const gain = gainEnvelope(
    trigger,
    gate,
    attackTime,
    decayTime,
    useConstantBuffer(0),
    decayTime
  );

  multiply(gain, gain, gain);
  multiply(gain, useConstantBuffer(0.35), gain);

  const noise = useOscillator(Waveform.Noise);
  const voiceOutput = useMonoSignal();
  const filter = useBiQuadFilter();
  const shaper = useBiQuadFilter();
  const filtered = filter(noise(useConstantBuffer(8000)), BiQuadFilterMode.BandPass, useConstantBuffer(3000), useConstantBuffer(.25), useConstantBuffer(0));
  const shaped = shaper(filtered, BiQuadFilterMode.EQBand, useConstantBuffer(4000), useConstantBuffer(.5), useConstantBuffer(1.5));
  multiply(shaped, gain, voiceOutput);

  add(output[0], voiceOutput, output[0]);
  add(output[1], voiceOutput, output[1]);
}

const hatToneFrequencies = [953, 950, 1023, 1135, 1258].map((x) => x * 1.3);

const CymbalVoice = (settings) => {
  const { wavetable, trigger, gate, velocity, output, attackTime, decayTime } = settings;
  const zero = useConstantBuffer(0);
  const gainEnvelope = useEnvelope(Curve.Log2, false);
  const carrierWtPosEnvelope = useEnvelope(Curve.Linear, false);
  const modulatorWtPosEnvelope = useEnvelope(Curve.Linear, false);

  const gain = gainEnvelope(
    trigger,
    gate,
    attackTime,
    decayTime,
    useConstantBuffer(0),
    decayTime,
  );
  multiply(gain, gain, gain);
  multiply(gain, useConstantBuffer(0.4), gain);

  const carrierWtPos = carrierWtPosEnvelope(
    trigger,
    gate,
    useConstantBuffer(0),
    useConstantBuffer(decayTime),
    useConstantBuffer(0),
    useConstantBuffer(decayTime),
  );
  subtract(useConstantBuffer(1), carrierWtPos, carrierWtPos);

  const modulatorWtPos = modulatorWtPosEnvelope(
    trigger,
    gate,
    useConstantBuffer(0),
    useConstantBuffer(decayTime),
    useConstantBuffer(0),
    useConstantBuffer(decayTime),
  )
  subtract(useConstantBuffer(1), modulatorWtPos, modulatorWtPos);

  const summedTones = useMonoSignal();
  summedTones.fill(0);

  const osc1 = useWavetableReader(wavetable, WavetableWarpMode.Off);
  const osc2 = useWavetableReader(wavetable, WavetableWarpMode.Off);
  const osc3 = useWavetableReader(wavetable, WavetableWarpMode.Off);
  const osc4 = useWavetableReader(wavetable, WavetableWarpMode.Off);
  const osc5 = useWavetableReader(wavetable, WavetableWarpMode.Off);

  const osc1Freq = settings.carrierRoot;

  const osc2Freq = useMonoSignal();
  const scaledDelta1 = useMonoSignal();
  multiply(settings.carrier1Delta, settings.carrierScale, scaledDelta1);
  add(osc1Freq, scaledDelta1, osc2Freq);

  const osc3Freq = useMonoSignal();
  const scaledDelta2 = useMonoSignal();
  multiply(settings.carrier2Delta, settings.carrierScale, scaledDelta2);
  add(osc2Freq, scaledDelta2, osc3Freq);

  const osc4Freq = useMonoSignal();
  const scaledDelta3 = useMonoSignal();
  multiply(settings.carrier3Delta, settings.carrierScale, scaledDelta3);
  add(osc3Freq, scaledDelta3, osc4Freq);

  const osc5Freq = useMonoSignal();
  const scaledDelta4 = useMonoSignal();
  multiply(settings.carrier4Delta, settings.carrierScale, scaledDelta4);
  add(osc4Freq, scaledDelta4, osc5Freq);

  add(summedTones, osc1(osc1Freq, carrierWtPos, zero, zero, trigger), summedTones);
  add(summedTones, osc2(osc2Freq, carrierWtPos, zero, zero, trigger), summedTones);
  add(summedTones, osc3(osc3Freq, carrierWtPos, zero, zero, trigger), summedTones);
  add(summedTones, osc4(osc4Freq, carrierWtPos, zero, zero, trigger), summedTones);
  add(summedTones, osc5(osc5Freq, carrierWtPos, zero, zero, trigger), summedTones);

  const ringOsc1 = useWavetableReader(wavetable, WavetableWarpMode.Off);
  const ringOsc2 = useWavetableReader(wavetable, WavetableWarpMode.Off);
  const ring1 = ringOsc1(settings.modulator1, modulatorWtPos, useConstantBuffer(0), useConstantBuffer(0), trigger);
  const ring2 = ringOsc2(settings.modulator2, modulatorWtPos, useConstantBuffer(0), useConstantBuffer(0), trigger);

  const bandpass1 = useBiQuadFilter();
  const bandpass2 = useBiQuadFilter();
  const highpass = useBiQuadFilter();

  const bandpassed1 = bandpass1(summedTones, BiQuadFilterMode.BandPass, settings.band1Freq, settings.band1Res, useConstantBuffer(0));
  const bandpassed2 = bandpass2(summedTones, BiQuadFilterMode.BandPass, settings.band2Freq, settings.band2Res, useConstantBuffer(0));

  multiply(ring1, bandpassed1, ring1);
  multiply(ring2, bandpassed2, ring2);
  add(ring1, ring2, summedTones);

  highpassed = highpass(summedTones, BiQuadFilterMode.HighPass, useConstantBuffer(1000), useConstantBuffer(1), useConstantBuffer(0));
  multiply(highpassed, gain, highpassed);
  add(output[0], highpassed, output[0]);
  add(output[1], highpassed, output[1]);
}

const hatPitchTiming = {
  54: {
    cymbalDecay: 200 * MS_TO_SAMPLES,
    cymbalAttack: 0,
    noiseDecay: 100 * MS_TO_SAMPLES,
    noiseAttack: 0 * MS_TO_SAMPLES,
    noiseGain: 1,
    cymbalGain: 1,
  },
  56: {
    cymbalDecay: 100 * MS_TO_SAMPLES,
    cymbalAttack: 4 * MS_TO_SAMPLES,
    noiseDecay: 25 * MS_TO_SAMPLES,
    noiseAttack: 4 * MS_TO_SAMPLES,
    noiseGain: 0.25,
    cymbalGain: 0.5,
  },
  58: {
    cymbalDecay: 1600 * MS_TO_SAMPLES,
    cymbalAttack: 0,
    noiseDecay: 150 * MS_TO_SAMPLES,
    noiseAttack: 0 * MS_TO_SAMPLES,
    noiseGain: 1,
    cymbalGain: 1,
  },
};

const DigitalDrums = (settings) => {
  const { input, output, kick, snare, hat, hiTom, lowTom, midTom, flrTom, kickWavetable, snareWavetable, hatWavetable, tomWavetable } = settings;
  output[0].fill(0);
  output[1].fill(0);

  kick[0].fill(0);
  kick[1].fill(0);

  snare[0].fill(0);
  snare[1].fill(0);

  hat[0].fill(0);
  hat[1].fill(0);

  hiTom[0].fill(0);
  hiTom[1].fill(0);

  lowTom[0].fill(0);
  lowTom[1].fill(0);

  midTom[0].fill(0);
  midTom[1].fill(0);

  flrTom[0].fill(0);
  flrTom[1].fill(0);



  const kickVoice = useSingleVoice(input, [48], 2);
  const snareVoice = useSingleVoice(input, [50, 51, 52], 2);
  const hatVoice = useSingleVoice(input, [54,56,58], 2);

  const hiTomVoice = useSingleVoice(input, [59], 2);
  const midTomVoice = useSingleVoice(input, [57], 2);
  const lowTomVoice = useSingleVoice(input, [55], 2);
  const flrTomVoice = useSingleVoice(input, [53], 2);

  const kickModPitch = useMonoSignal();
  subtract(settings.kickAttackPitch, settings.kickResonantPitch, kickModPitch);

  const kickPitchTime = useMonoSignal();
  multiply(kickVoice.velocity, useConstantBuffer(50 * MS_TO_SAMPLES), kickPitchTime);
  add(kickPitchTime, useConstantBuffer(100 * MS_TO_SAMPLES), kickPitchTime);

  DrumVoice({
    ...kickVoice,
    wavetable: kickWavetable,
    output: kick,
    basePitch: settings.kickResonantPitch,
    modPitch: kickModPitch,
    wtTime: settings.kickWavetableScanTime,
    pitchTime: settings.kickPitchDropTime,
    gainTime: settings.kickAmplitudeDecayTime,
    filterTime: settings.kickFilterDropTime,
  });

  add(output[0], kick[0], output[0]);
  add(output[1], kick[1], output[1]);

  const snareSkin1 = useStereoSignal();
  snareSkin1[0].fill(0);
  snareSkin1[1].fill(0);

  DrumVoice({
    ...snareVoice,
    wavetable: snareWavetable,
    output: snareSkin1,
    basePitch: settings.snareSkin1Pitch,
    modPitch: useConstantBuffer(.6),
    wtTime: settings.snareSkin1WavetableScanTime,
    pitchTime: useConstantBuffer(300 * MS_TO_SAMPLES),
    gainTime: settings.snareSkin1Decay,
    filterTime: settings.snareSkin1Decay,
  });

  multiply(snareSkin1[0], settings.snareSkin1Level, snareSkin1[0]);
  multiply(snareSkin1[1], settings.snareSkin1Level, snareSkin1[1]);
  add(snare[0], snareSkin1[0], snare[0]);
  add(snare[1], snareSkin1[1], snare[1]);

  const snareSkin2 = useStereoSignal();

  const ringPitch = useMonoSignal();
  add(settings.snareSkin2Offset, settings.snareSkin1Pitch, ringPitch);
  snareSkin2[0].fill(0);
  snareSkin2[1].fill(0);

  DrumVoice({
    ...snareVoice,
    wavetable: snareWavetable,
    output: snareSkin2,
    basePitch: ringPitch,
    modPitch: useConstantBuffer(.75),
    wtTime: settings.snareSkin2WavetableScanTime,
    pitchTime: useConstantBuffer(400 * MS_TO_SAMPLES),
    gainTime: settings.snareSkin2Decay,
    filterTime: settings.snareSkin2Decay,
  });

  multiply(snareSkin2[0], settings.snareSkin2Level, snareSkin2[0]);
  multiply(snareSkin2[1], settings.snareSkin2Level, snareSkin2[1]);

  add(snare[0], snareSkin2[0], snare[0]);
  add(snare[1], snareSkin2[1], snare[1]);

  const snareSnares = useStereoSignal();
  snareSnares[0].fill(0);
  snareSnares[1].fill(0);
  RattleVoice(snareVoice.trigger, snareVoice.gate, snareVoice.velocity, snareSnares, settings.snareSnaresAttack, settings.snareSnaresDecay);

  multiply(snareSnares[0], settings.snareSnaresLevel, snareSnares[0]);
  multiply(snareSnares[1], settings.snareSnaresLevel, snareSnares[1]);

  add(snare[0], snareSnares[0], snare[0]);
  add(snare[1], snareSnares[1], snare[1]);

  add(output[0], snare[0], output[0]);
  add(output[1], snare[1], output[1]);

  const hatCymbalDecay = useMonoSignal();
  const hatNoiseDecay = useMonoSignal();
  const hatNoiseAttack = useMonoSignal();
  const hatCymbalAttack = useMonoSignal();
  for (let i = 0; i < hatCymbalDecay.length; i++) {
    const timingSpec = hatPitchTiming[hatVoice.pitch[i]];
    hatCymbalDecay[i] = timingSpec?.cymbalDecay || 0;
    hatNoiseDecay[i] = timingSpec?.noiseDecay || 0;
    hatNoiseAttack[i] = timingSpec?.noiseAttack || 0;
    hatCymbalAttack[i] = timingSpec?.cymbalAttack || 0;
  }
  const hatCymbal = useStereoSignal();
  hatCymbal[0].fill(0);
  hatCymbal[1].fill(0);
  CymbalVoice({
    ...hatVoice,
    wavetable: hatWavetable,
    output: hatCymbal,
    attackTime: hatCymbalAttack,
    decayTime: hatCymbalDecay,
    carrierRoot: settings.hatCarrierRoot,
    carrierScale: settings.hatCarrierScale,
    carrier1Delta: settings.hatCarrierDelta1,
    carrier2Delta: settings.hatCarrierDelta2,
    carrier3Delta: settings.hatCarrierDelta3,
    carrier4Delta: settings.hatCarrierDelta4,
    modulator1: settings.hatModulator1Freq,
    modulator2: settings.hatModulator2Freq,
    band1Freq: settings.hatCarrierBandpass1Freq,
    band2Freq: settings.hatCarrierBandpass2Freq,
    band1Res: settings.hatCarrierBandpass1Res,
    band2Res: settings.hatCarrierBandpass2Res,
  });
  const hatNoise = useStereoSignal();
  hatNoise[0].fill(0);
  hatNoise[1].fill(0);
  RattleVoice(hatVoice.trigger, hatVoice.gate, hatVoice.velocity, hatNoise, hatNoiseAttack, hatNoiseDecay);
  for (let i = 0; i < hatNoise[0].length; i ++) {
    const timingSpec = hatPitchTiming[hatVoice.pitch[i]];
    const noiseGain = timingSpec?.noiseGain || 0;
    const cymbalGain = timingSpec?.cymbalGain || 0;
    hatNoise[0][i] *= noiseGain;
    hatNoise[1][i] *= noiseGain;
    hatCymbal[0][i] *= cymbalGain;
    hatCymbal[1][i] *= cymbalGain;
  }
  add(hat[0], hatNoise[0], hat[0]);
  add(hat[1], hatNoise[1], hat[1]);
  add(hat[0], hatCymbal[0], hat[0]);
  add(hat[1], hatCymbal[1], hat[1]);

  add(output[0], hat[0], output[0]);
  add(output[1], hat[1], output[1]);

  const tomPitchTime = useMonoSignal();
  multiply(hiTomVoice.velocity, useConstantBuffer(50 * MS_TO_SAMPLES), tomPitchTime);
  add(tomPitchTime, useConstantBuffer(100 * MS_TO_SAMPLES), tomPitchTime);

  for (let i = 0; i < 4; i ++) {
    let voice, currentOutput, resonantPitchBuffer;
    if (i === 0) {
      voice = hiTomVoice;
      currentOutput = hiTom;
      resonantPitchBuffer = settings.hiTomResonantPitch;
    } else if (i === 1) {
      voice = midTomVoice;
      currentOutput = midTom;
      resonantPitchBuffer = settings.midTomResonantPitch;
    } else if (i === 2) {
      voice = lowTomVoice;
      currentOutput = lowTom;
      resonantPitchBuffer = settings.lowTomResonantPitch;
    } else if (i === 3) {
      voice = flrTomVoice;
      currentOutput = flrTom;
      resonantPitchBuffer = settings.flrTomResonantPitch;
    }

    DrumVoice({
      ...voice,
      wavetable: tomWavetable,
      output: currentOutput,
      basePitch: resonantPitchBuffer,
      modPitch: settings.tomModPitch,
      wtTime: settings.tomWavetableScanTime,
      pitchTime: settings.tomPitchDropTime,
      gainTime: settings.tomAmplitudeDecayTime,
      filterTime: settings.tomFilterDropTime,
    });

    add(output[0], currentOutput[0], output[0]);
    add(output[1], currentOutput[1], output[1]);
  }
}

const WAVETABLES = {
  Kick: '[encoded data, 11456 characters]',
  Snare: '[encoded data, 5380 characters]',
  Crash: 'ZSTDKLUv/WBKCPVJANSRCQCCAIAng4KI7oyXjDCFlHfFZzVbSlZiWj5l+HHdelZ7V3EGXsJFWy9+IlklQzo5XkeJ5rCfy0/Um8vutqydG4Zhc2xlZVqiUAxIGUK8QPpE502pWHFh62R3YbFXGkoUPL0wIyr/KPgsKjWqQLtOwV4TcNGB6pJWom2vLLo6w63LktRy3v/oGvMT+yP/3/2G9iXpkNZBwCCoOJBRepNnVlg2TINC3DqlNQc0VTccQEpN+ltFaL5uHW4VaNNgQV2+YGRrz3lzhg2ME4jkezNsGl/NWehe/m0shE+dOrWLyBfVDNrY1+fPQMQAt+Cp9p27k1GLxoQ/gMx9OX3offN+nX/AfwCAANiBi4FWf6p7BnficbRs/mdhZIliGWN+Ztls8nVHgSqO1ZuHqZu2ncJKzXnW790U49fk2uHo2KHJArV7nW6GPHM+Zh1g1V8pY1xn5mn9aNdjnlpOTnhA+TK0J0kg1h29IH8oxDN8QE5MPlVOWu1bAVxPXaFi0202f3+VSq7LxoPcqO03+cL+Of7W9xzsBNwQyS+1f6LhkqWHQ4FJf5KAf4NMhmmHwIXmgEF56G9mZkxevlgrVjpW8FcJWlFb6VpzWDdUC08dSr1G60UDSJdMlFLEWEVe8mJZZ1psnHIaehiCW4mwjlWRNJHZjjaLUIf+g8KBuYClgA6BboFngd2AAID/HwCA1YEnhLCGO4mqi+KNvI/8kFyRrJDijjmMNYmnhpuFIocJjJGULKCJrdO6KcYLzrfRMNENzULGyr2DtASrkqEymMOONoW3e+VyvWtKZzxmfmgWbWlypXZZeNh2VnK/a11kgF1FWHZVllXlWGlf+mhGdeaDWJQRpmm4jcp22wjqPvVt/Hr/0/5B+4b1A+6J5HjYIMkntuefhIe3bnlXpEOgNC8rUydZKO8scDNKOl1AQEVBSRBNSVEeViJbfF9YYk1jpGJSYcJgb2KTZ8Vwwn09jQOdXKq9spC0sq+NpaOYz4trgc16L3jaeJ97Nn+NgvCEGIYghl+FPoQPg/qB+T8AgACEKYbahv6G2oenikWQ9JgppKGwlrwtxvLLVs3WytzFUMAAvCa6L7vSvj7EacpS0B7VLtgm2e3XiNTuzgTHhbxKr32fto0qe3dpQVrCTo9HcESDRIVGEkn0SmhLWUp0SApHwkcbTPFUMmLfcliFy5ekqN+2GcKByq3QadV52VLd/eD441zlH+Rt3+/W5MoVvKCrsJpOijZ7xG3yYWlXuU2ARLU7zDOeLT8qqSpyL5E4TkVZVOhjA3LafAuD8IPIf7R3fm0uY7BaZlX4Uz9WcltCYjZp826DcoRzLHJBb91rMmlLaMZpuW2ic5R6aoEgh/6KvIyGjM2KLYg9hW6CXwCAkoA9gtSEWoj6jPKSX5oeo7asbLZlv9PGDMykzl7OP8uExa69f7T1qhWi55oullWUWJW2mHudZaL/pfmmbqQknqaUHYkIffxxVWkVZKxiB2Wlaqdy9XtlheiNnZTzmLGa65n/ln+SJI3AhxuD+H/vfmaAloR3i8CU3Z/6qwG4zcJLy7PQrNJZ0TXN88ZLv9O26a24pE6bqZHDh7B9mHPHaaFgnFg4UuhNBUy7TAdQo1UfXeZlb29UeW+D0Y2gmO6jdq+aunbECsx90DzRJs6ax2m+wbP8qFuft5ddkhWPQ40qjDaLGYrViJuHq4Y8hleG6Yaxh1mIhYjjh0OGmIN/AIBpfp985nqHebp4mngleTN6i3vyfDR+QH8hgAyBRYIEhF6GLokfjM+O4pArkrKSoZIsknORdpAWjyKNb4rfhnyCgH1SeIRzwG+lbaVt5m9DdFZ6jYFHifGQG5iFniWkIanFrX+yxLcEvoLFRs4Q2E/iK+y59Cb73P6i/5r9Lvnw8nbrQ+PC2kPS/skNwm66+LJiq06jYZpgkEmFZ3k7bXph7VZKTiJIz0RvRNpGr0tpUnVaXWO9bGB2GoC/iQ6TqpsloxCpMK1/rxywQa8crdmplqWJoACbc5VlkFqMpIlUiD6I+Yj9ibuKwYrLidSHGoUKgih/7Hyne3l7THzmfZ8AgBmBwYGqgaWAqn7le7F4h3XhciFxdnDhcCty/3MCduF3bHmqesd7H30Rf+aBvIV1irSP75R1maSc1Z2DnGmYfZEGiI980W+zYj9WfktpQ8c+Kj7BQVlJaFQPYktxG4GkkFSf2awfuTzEY87D137goegN8H32ld3/NP6O+cHx1eYC2ajIbbYpo9uPl31abfJf2lU6T+5LtUs0TiFTQFpgY0ZusnpIiJyWKaVrs9fA8sxZ19bfVeb46vPtme848Pjv5e4U7YrqKufP4nbdIdcB0GzIuMBJuXOybaxGp+SiCZ9om7aXz5Osj3SLaYfPg+iA136jfTd9aX0Gful+vwCAh4PvhR6HCYewhTSDx3+3e1p3+nLHbtNqFWdsY8dfF1xsWPdU/1HVT9JOMk8VUWNU6FhPXkhkkGoGcYx3FH6IhMSKqJAOlt2a/J55onClIqjoqiauPrJzt9a9SMV+zQfWXd795WzsRvFG9ET1PvRU8b3syubN3xTY3c9Tx4a+bLXmq8Sh1Jbxigh+KXCNYYxSnEM8Ne0nHBweEjYKiAQlARMAUgHeBLwK8BKCHXIqqjn6Sg5eYHI8h8WbEa9CwKbOuNkx4S7lFuZ25Ong/dsl1qrPrsgywTS5wbD/pzSft5bljhOIgYJOfnh75XlheaZ5b3p+e598q32Nfjp/tH/fAIBef05+3HwRe/h4mHYFdFlxr24pbOZpAmiEZnhlzmR+ZHFkkmTZZDtltmVLZutmiWcHaE9oSWjxZ19nuGZBZlhmTmd/aTFthnKAefCBiIvtlbqggavgtYm/M8inz7zVXtqI3UHfmt+u3q/c0tlH1jnSt821yArDirwHtWissqILmLyMFoGDdWhqGWDUVr1O3EcbQl09gjlyNhs0dzKLMWExCjKeMzo2BDocP6NFuE1sV8Jiq2/2fVGNS51grQi9wssG2VDkVu3t8/z3efls+PH0K+9I55LdYNIbxj25Say+nwqUfolbgMR4yXJrbqFrWGpyasdrI25JcfN003ibfP+7DduDbRJtuzfhPIqX4sudsUgKKJBDuwOQ2+8BAVPANCz4GfAUMAVsCpB04GCc7T8E',

   'Tom': '[encoded data, 5528 characters]'
}

this.constructDevice = () => constructPerBufferDevice({
  noteInputHint: {
    interface: NoteInterfaceType.Pads,
    usedPitches: {
      48: 'Kick',
      50: 'Snare L',
      51: 'Snare Ghost',
      52: 'Snare R',
      54: 'Hat Closed',
      56: 'Hat Pedal',
      58: 'Hat Open',
    }
  },
  implementation: DigitalDrums,
  constants: {
    kickWavetable: wavetableConstant('Kick Wavetable', WAVETABLES.Kick),
    snareWavetable: wavetableConstant('Snare Wavetable', WAVETABLES.Snare),
    hatWavetable: wavetableConstant('Hat Wavetable', WAVETABLES.Crash),
    tomWavetable: wavetableConstant('Tom Wavetable', WAVETABLES.Tom)
  },
  inputs: {
    input: midi('In'),

    kickResonantPitch: numberSetting('Kick Resonant Pitch', { min: 10, max: 30, defaultValue: 22 }),
    kickAttackPitch: numberSetting('Kick Attack Pitch', { min: 40, max: 55, defaultValue: 43.5 }),
    kickWavetableScanTime: adsrTimeSetting('Kick Wavetable Scan Time', 220),
    kickPitchDropTime: adsrTimeSetting('Kick Pitch Drop Time', 150),
    kickAmplitudeDecayTime: adsrTimeSetting('Kick Amplitude Decay Time', 1400),
    kickFilterDropTime: adsrTimeSetting('Kick Filter Drop Time', 92),

    snareSkin1Pitch: numberSetting('Snare Skin 1 Pitch', { min: 30, max: 60, defaultValue: 42.8 }),
    snareSkin1Level: dbSetting('Snare Skin 1 Level', { defaultValue: -4, max: 0, min: -24 }),
    snareSkin1WavetableScanTime: adsrTimeSetting('Snare Skin 1 Wavetable Scan Time', 30),
    snareSkin1Decay: adsrTimeSetting('Snare Skin 1 Decay', 230),

    snareSkin2Offset: numberSetting('Snare Skin 2 Offset', { min: 5, max: 9, defaultValue: 7.48 }),
    snareSkin2Level: dbSetting('Snare Skin 2 Level', { defaultValue: -15, max: -3, min: -30 }),
    snareSkin2WavetableScanTime: adsrTimeSetting('Snare Skin 2 Wavetable Scan Time', 30),
    snareSkin2Decay: adsrTimeSetting('Snare Skin 2 Decay', 230),

    snareSnaresLevel: dbSetting('Snare Snares Level', { defaultValue: 0 }),
    snareSnaresAttack: adsrTimeSetting('Snare Snares Attack', 17),
    snareSnaresDecay: adsrTimeSetting('Snare Snares Decay', 190),

    hatCarrierDelta1: numberSetting('Hat Carrier Delta 1', { min: 0, max: 100, defaultValue: 1.6 }),
    hatCarrierDelta2: numberSetting('Hat Carrier Delta 2', { min: 0, max: 100, defaultValue: 15.8 }),
    hatCarrierDelta3: numberSetting('Hat Carrier Delta 3', { min: 0, max: 100, defaultValue: 31.8 }),
    hatCarrierDelta4: numberSetting('Hat Carrier Delta 4', { min: 0, max: 100, defaultValue: 51.0 }),
    hatCarrierRoot: numberSetting('Hat Carrier Root', { min: 900, max: 1100, defaultValue: 949 }),
    hatCarrierScale: numberSetting('Hat Carrier Scale', { min: 1, max: 5, defaultValue: 3 }),
    hatModulator1Freq: numberSetting('Hat Modulator 1 Freq', { min: 2000, max: 8000, defaultValue: 4000 }),
    hatModulator2Freq: numberSetting('Hat Modulator 2 Freq', { min: 2000, max: 8000, defaultValue: 5000 }),
    hatCarrierBandpass1Freq: numberSetting('Hat Carrier BPF 1 Freq', { min: 2000, max: 8000, defaultValue: 5000 }),
    hatCarrierBandpass2Freq: numberSetting('Hat Carrier BPF 2 Freq', { min: 2000, max: 8000, defaultValue: 6500 }),
    hatCarrierBandpass1Res: numberSetting('Hat Carrier BPF 1 Res', { min: 0.1, max: 5, defaultValue: 1 }),
    hatCarrierBandpass2Res: numberSetting('Hat Carrier BPF 2 Res', { min: 0.1, max: 5, defaultValue: 2 }),

    hiTomResonantPitch: numberSetting('High Tom Resonant Pitch', { min: 25, max: 60, defaultValue: 50 }),
    midTomResonantPitch: numberSetting('Mid Tom Resonant Pitch', { min: 25, max: 60, defaultValue: 46 }),
    lowTomResonantPitch: numberSetting('Low Tom Resonant Pitch', { min: 25, max: 60, defaultValue: 40 }),
    flrTomResonantPitch: numberSetting('Floor Tom Resonant Pitch', { min: 25, max: 60, defaultValue: 34 }),

    tomModPitch: numberSetting('Tom Modulated Pitch', { min: 0, max: 12, defaultValue: 6 }),
    tomWavetableScanTime: adsrTimeSetting('Tom Wavetable Scan Time', 670),
    tomPitchDropTime: adsrTimeSetting('Tom Pitch Drop Time', 220),
    tomAmplitudeDecayTime: adsrTimeSetting('Tom Amplitude Decay Time', 338),
    tomFilterDropTime: adsrTimeSetting('Tom Filter Drop Time', 110),
  },
  outputs: {
    output: stereo('Out'),
    kick: collapse(stereo('Kick')),
    snare: collapse(stereo('Snare')),
    hat: collapse(stereo('Hat')),
    hiTom: collapse(stereo('High Tom')),
    midTom: collapse(stereo('Mid Tom')),
    lowTom: collapse(stereo('Low Tom')),
    flrTom: collapse(stereo('Floor Tom')),
  },
  layout: row([
    col([
      box(
        col([
          spacer(5),
          label('Hat'),
          spacer(5),
          row([
            col([
              row([spacer(40), label('Carrier')]),
              spacer(10),
              row([
                box(label('Root'), 40),
                box(linear('hatCarrierRoot'), 132, 20, gray300, 4),
              ]),
              spacer(4),
              row([
                box(label('Delta'), 40),
                col([
                  row([
                    knob('hatCarrierDelta1', { size: 30 }),
                    spacer(4),
                    knob('hatCarrierDelta2', { size: 30 }),
                    spacer(4),
                    knob('hatCarrierDelta3', { size: 30 }),
                    spacer(4),
                    knob('hatCarrierDelta4', { size: 30 }),
                  ])
                ]),
              ]),
              spacer(4),
              row([
                box(label('Mult'), 40),
                box(linear('hatCarrierScale'), 132, 20, gray300, 4),
              ])
            ]),
            spacer(4),
            col([
              row([label('BPF')]),
              spacer(4),
              knob('hatCarrierBandpass1Freq', { size: 40 }),
              spacer(4),
              knob('hatCarrierBandpass2Freq', { size: 40 }),
            ]),
            spacer(4),
            col([
              row([label('Mod')]),
              spacer(4),
              knob('hatModulator1Freq', { size: 40 }),
              spacer(4),
              knob('hatModulator2Freq', { size: 40 }),
            ]),
            spacer(4),
            col([
              row([label('Pres.')]),
              spacer(4),
              knob('hatCarrierBandpass1Res', { size: 40 }),
              spacer(4),
              knob('hatCarrierBandpass2Res', { size: 40 }),
            ]),
            spacer(3),
          ]),
          spacer(5),
        ]),
        310,
        'auto',
        gray200,
        8
      ),
      spacer(1),
      box(
        col([
          spacer(5),
          label('Snare'),
          row([
            spacer(5),
            box(label('Skin 1'), 40, 'auto'),
            spacer(2),
            titled.knob('snareSkin1Level', 'Level', 30),
            box(titled.knob('snareSkin1Pitch', 'Pitch', 30), 60, 'auto'),
            col([
              row([
                box(label('Timbre'), 50, 20),
                box(linear('snareSkin1WavetableScanTime'), 90, 20, gray300, 4),
              ]),
              spacer(2),
              row([
                box(label('Mute'), 50, 20),
                box(linear('snareSkin1Decay'), 90, 20, gray300, 4)
              ]),
              spacer(2),
            ]),
            spacer(4),
          ]),
          row([
            spacer(5),
            box(label('Skin 2'), 40, 'auto'),
            spacer(2),
            titled.knob('snareSkin2Level', 'Level', 30),
            box(titled.knob('snareSkin2Offset', 'Shift', 30), 60, 'auto'),
            col([
              row([
                box(label('Timbre'), 50, 20),
                box(linear('snareSkin2WavetableScanTime'), 90, 20, gray300, 4),
              ]),
              spacer(2),
              row([
                box(label('Mute'), 50, 20),
                box(linear('snareSkin2Decay'), 90, 20, gray300, 4)
              ]),
              spacer(2),
            ]),
            spacer(4),
          ]),
          row([
            spacer(5),
            box(label('Snares'), 40, 'auto'),
            spacer(2),
            titled.knob('snareSnaresLevel', 'Level', 30),
            box(spacer(0), 60, 'auto'),
            col([
              row([
                box(label('Attack'), 50, 20),
                box(linear('snareSnaresAttack'), 90, 20, gray300, 4),
              ]),
              spacer(2),
              row([
                box(label('Decay'), 50, 20),
                box(linear('snareSnaresDecay'), 90, 20, gray300, 4)
              ]),
              spacer(2),
            ]),
            spacer(4),
          ])
        ]),
        310,
        'auto',
        gray200,
        8
      ),
      spacer(1),
      box(
        col([
          spacer(5),
          label('Kick'),
          row([
            titled.knob('kickResonantPitch', 'Res. Pitch', 60),
            titled.knob('kickAttackPitch', 'Atk. Pitch', 60),
            col([
              row([
                box(label('Pitch'), 50, 20),
                box(linear('kickPitchDropTime'), 90, 20, gray300, 4),
              ]),
              spacer(2),
              row([
                box(label('Timbre'), 50, 20),
                box(linear('kickWavetableScanTime'), 90, 20, gray300, 4)
              ]),
              spacer(2),
              row([
                box(label('Clarity'), 50, 20),
                box(linear('kickFilterDropTime'), 90, 20, gray300, 4)
              ]),
              spacer(2),
              row([
                box(label('Mute'), 50, 20),
                box(linear('kickAmplitudeDecayTime'), 90, 20, gray300, 4)
              ]),
            ]),
            spacer(4),
          ])
        ]),
        310,
        'auto',
        gray200,
        8
      ),
    ]),
    col([
      box(
        col([
          spacer(5),
          label('Toms'),
          row([
            col([
              row([
                knob('hiTomResonantPitch', { size: 30 }),
                spacer(4),
                knob('midTomResonantPitch', { size: 30 }),
                spacer(4),
                knob('lowTomResonantPitch', { size: 30 }),
                spacer(4),
                knob('flrTomResonantPitch', { size: 30 }),
              ]),
              spacer(4),
              box(label('Resonant Pitches'), 120, 20, gray100, 4),
            ]),
            titled.knob('tomModPitch', 'Mod. Pitch', 30),
            col([
              row([
                box(label('Pitch'), 50, 20),
                box(linear('tomPitchDropTime'), 90, 20, gray300, 4),
              ]),
              spacer(2),
              row([
                box(label('Timbre'), 50, 20),
                box(linear('tomWavetableScanTime'), 90, 20, gray300, 4)
              ]),
            ]),
            col([
              row([
                box(label('Clarity'), 50, 20),
                box(linear('tomFilterDropTime'), 90, 20, gray300, 4)
              ]),
              spacer(2),
              row([
                box(label('Mute'), 50, 20),
                box(linear('tomAmplitudeDecayTime'), 90, 20, gray300, 4)
              ]),
            ]),
            spacer(4),
          ])
        ]),
        310,
        'auto',
        gray200,
        8
      ),
    ]),
  ]),
  presetTags: {
    'Genre': [
      'Funk',
      'Pop',
      'Metal',
      'Drum & Bass'
    ]
  }
});

