<!-- SS · part 30 -->
th each song. */
(function () {
  'use strict';
  var host = document.getElementById('es-kit'), root = document.getElementById('es'); if (!host) return;
  if (!root || root.getAttribute('data-admin') !== '1') { host.remove(); var tb = document.querySelector('.es-mtabs [data-m="kit"]'); if (tb) tb.remove(); return; }
  var ES = null; function $(s) { return host.querySelector(s); }
  function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]; }); }
  var PADS = [['kick', 'Kick', 0], ['snare', 'Snare', 0], ['hat', 'Hat', 0], ['perc', 'Perc', 0], ['bass', 'Bass (pitched)', 1], ['keys', 'Keys / chords (pitched)', 1], ['lead', 'Lead (pitched)', 1], ['chop', 'Vocal chops (slices)', 0]];
  var LANE_PAD = { bass: 'bass', chords: 'keys', lead: 'lead' };
  var D = { samples: [], kits: [], templates: [] }, BUF = {}, LS = 'es_kit_state_v1';
  var K = Object.assign({ on: false, only: false, kit: '', leadChops: false }, (function () { try { return JSON.parse(localStorage.getItem(LS) || '{}'); } catch (e) { return {}; } })());
  function keep() { try { localStorage.setItem(LS, JSON.stringify(K)); } catch (e) {} }
  function kit() { return D.kits.filter(function (k) { return k.id === K.kit; })[0] || null; }
  function sample(id) { return D.samples.filter(function (s) { return s.id === id; })[0] || null; }
  function say(m) { $('#es-kt-status').textContent = m; }
  // ---------------- buffers
  function loadBuf(id) {
    if (BUF[id]) return Promise.resolve(BUF[id]);
    return fetch('/ss/kits/media/' + encodeURIComponent(id), { credentials: 'same-origin' }).then(function (r) { if (!r.ok) throw new Error('missing'); return r.arrayBuffer(); })
      .then(function (ab) { return new Promise(function (res, rej) { Tone.getContext().rawContext.decodeAudioData(ab, function (b) { BUF[id] = b; res(b); }, rej); }); });
  }
  function loadKitBufs() { var k = kit(); if (!k) return Promise.resolve(); return Promise.all(Object.keys(k.pads).map(function (p) { return loadBuf(k.pads[p].sample).catch(function () {}); })); }
  // ---------------- sampler voice
  function hit(G, pad, midi, time, dur, vel, out, slice) {
    var buf = pad && BUF[pad.sample]; if (!buf) return;
    var ctx = Tone.getContext().rawContext, D0 = buf.duration, st = pad.start, en = pad.end;
    if (slice) { st = slice[0]; en = slice[1]; }
    var rate = midi != null && pad.root ? Math.pow(2, (midi - pad.root) / 12) : 1, off = st * D0, len = Math.max(0.01, (en - st) * D0), natural = len / rate;
    var src = ctx.createBufferSource(); src.buffer = buf; src.playbackRate.value = rate;
    var g = ctx.createGain(), peak = vel * Math.pow(10, (pad.gain || 0) / 20), a = Math.max(0.001, pad.a), held = dur == null ? natural : Math.min(natural, dur), rel = Math.max(0.005, pad.r);
    g.gain.setValueAtTime(0, time); g.gain.linearRampToValueAtTime(peak, time + a);
    g.gain.setTargetAtTime(peak * pad.s, time + a, Math.max(0.005, pad.d / 3));
    g.gain.setTargetAtTime(0, time + Math.max(a, held), rel / 3);
    src.connect(g); Tone.connect(g, out);
    src.start(time, off, len); try { src.stop(Math.min(time + natural, time + Math.max(a, held) + rel * 1.5) + 0.02); } catch (e) {}
    if (pad.choke) { G._choke = G._choke || {}; var prev = G._choke[pad.choke]; if (prev && prev.t < time) { try { prev.g.gain.cancelScheduledValues(time); prev.g.gain.setTargetAtTime(0, time, 0.004); prev.src.stop(time + 0.03); } catch (e) {} } G._choke[pad.choke] = { g: g, src: src, t: time }; }
  }
  function wrap(G, lane) {
    var orig = G.inst[lane]; if (!orig || orig._kit) return;
    var out = G.ch[lane], w;
    if (lane === 'drums') w = { play: function (row, time, vel) { var k = kit(), pad = K.on && k && k.pads[row]; if (pad && BUF[pad.sample]) hit(G, pad, null, time, null, vel, out); else if (!K.only) orig.play(row, time, vel); } };
    else w = { triggerAttackRelease: function (notes, dur, time, vel) {
      var k = kit(), pn = LANE_PAD[lane], pad = K.on && k && k.pads[pn], ch = K.on && k && k.pads.chop;
      var list = Array.isArray(notes) ? notes : [notes], sec = Tone.Time(dur).toSeconds();
      if (lane === 'lead' && K.on && K.leadChops && ch && ch.slices && ch.slices.length && BUF[ch.sample]) { list.forEach(function (n) { var m = Tone.Frequency(n).toMidi(), i = ((m - 60) % ch.slices.length + ch.slices.length) % ch.slices.length; hit(G, ch, null, time, null, vel, out, ch.slices[i]); }); return; }
      if (pad && BUF[pad.sample]) { list.forEach(function (n) { hit(G, pad, Tone.Frequency(n).toMidi(), time, sec, vel / Math.sqrt(list.length), out); }); return; }
      if (!K.only) orig.triggerAttackRelease(notes, dur, time, vel);
    } };
    w._kit = true; w.releaseAll = function () { try { orig.releaseAll && orig.releaseAll(); } catch (e) {} }; w.dispose = function () { try { orig.dispose(); } catch (e) {} };
    G.inst[lane] = w;
  }
  function rewire() { var 