<!-- SS · part 50 -->
ctx().decodeAudioData(ab, res, function () { rej(new Error('Bad effect output')); }); }); })
      .then(function (b) { return toRate(b, seg.sampleRate, seg.numberOfChannels); });
  }
  function tone(seg, chain) {
    var ch = Math.max(2, seg.numberOfChannels);
    return Tone.Offline(function () { var pl = new Tone.Player(new Tone.ToneAudioBuffer(seg)), r = chain(); pl.connect(r.input); r.output.toDestination(); pl.start(0); return r.ready; }, seg.duration, ch, seg.sampleRate)
      .then(function (tb) { return tb.get ? tb.get() : tb; }).then(function (b) { return seg.numberOfChannels === 1 ? slice(b, 0, b.length) : b; });
  }
  function wetDry(node, wet) { var i = new Tone.Gain(1), o = new Tone.Gain(1), d = new Tone.Gain(1 - wet * 0.5), wg = new Tone.Gain(wet); i.connect(d); d.connect(o); i.connect(node); node.connect(wg); wg.connect(o); return { input: i, output: o, ready: node.ready }; }
  function js(seg, fn) { var o = mk(seg.numberOfChannels, seg.length, seg.sampleRate); for (var c = 0; c < seg.numberOfChannels; c++) { var a = seg.getChannelData(c), b = new Float32Array(a); fn(b, c, seg); o.copyToChannel(b, c); } return Promise.resolve(o); }
  function peakOf(seg) { var m = 0; for (var c = 0; c < seg.numberOfChannels; c++) { var d = seg.getChannelData(c); for (var i = 0; i < d.length; i++) { var a = Math.abs(d[i]); if (a > m) m = a; } } return m; }
  function process(fx, seg, amt) {
    switch (fx) {
      case 'normalize': var p = peakOf(seg), k = p > 0 ? 0.944 / p : 1; return js(seg, function (b) { for (var i = 0; i < b.length; i++) b[i] *= k; });
      case 'amplify': var gk = Math.pow(10, amt / 20); return js(seg, function (b) { for (var i = 0; i < b.length; i++) b[i] = Math.max(-1, Math.min(1, b[i] * gk)); });
      case 'fadein': return js(seg, function (b) { for (var i = 0; i < b.length; i++) b[i] *= i / b.length; });
      case 'fadeout': return js(seg, function (b) { for (var i = 0; i < b.length; i++) b[i] *= 1 - i / b.length; });
      case 'reverse': return js(seg, function (b) { b.reverse(); });
      case 'karaoke':
        if (seg.numberOfChannels < 2) return Promise.reject(new Error('Vocal remover needs a stereo file (it cancels the center channel).'));
        var L = seg.getChannelData(0), R = seg.getChannelData(1), o = mk(2, seg.length, seg.sampleRate), l2 = new Float32Array(L.length), r2 = new Float32Array(L.length);
        for (var i = 0; i < L.length; i++) { var side = (L[i] - R[i]) * 0.5, m = (L[i] + R[i]) * 0.5 * (1 - amt); l2[i] = side + m; r2[i] = -side + m; }
        o.copyToChannel(l2, 0); o.copyToChannel(r2, 1); return Promise.resolve(o);
      case 'echo': return tone(seg, function () { return wetDry(new Tone.FeedbackDelay({ delayTime: 60 / (ES.S.bpm || 90) * 0.75, feedback: 0.2 + amt * 0.5, wet: 1 }), 0.3 + amt * 0.5); });
      case 'room': case 'hall': case 'cathedral': return tone(seg, function () { var r = new Tone.Reverb({ decay: { room: 1.1, hall: 3.2, cathedral: 7.5 }[fx], preDelay: { room: 0.01, hall: 0.03, cathedral: 0.06 }[fx], wet: 1 }); return wetDry(r, amt); });
      case 'chorus': return tone(seg, function () { var c = new Tone.Chorus({ frequency: 1.5, delayTime: 3.5, depth: 0.4 + amt * 0.5, wet: 1 }).start(); return wetDry(c, 0.3 + amt * 0.6); });
      case 'phaser': return tone(seg, function () { return wetDry(new Tone.Phaser({ frequency: 0.3 + amt * 1.5, octaves: 3, baseFrequency: 350, wet: 1 }), 0.4 + amt * 0.5); });
      case 'distortion': return tone(seg, function () { return wetDry(new Tone.Distortion({ distortion: 0.1 + amt * 0.85, oversample: '2x', wet: 1 }), 0.5 + amt * 0.5); });
      case 'eq': var nm = $('#es-wv-eqp').value, e = window.ESEQ && ESEQ.PRESETS[nm] ? ESEQ.PRESETS[nm]() : null; if (!e) return Promise.reject(new Error('Pick an EQ preset.')); return ESEQ.applyToBuffer(seg, e);
      default: return srv(seg, fx, amt);
    }
  }
  var curFx = null;
  function setFx(fx) {
    curFx = fx; var a = AMT[fx], r = $('#es-wv-amt'); $$('#es-wv-rack button').forEach(function (b) { b.classList.toggle('is-on', b.getAttribute('data-fx') === fx); });
    $('#es-wv-fxn').textContent = FX.filter(function (f) { return f[0] === fx; })[0][1];
    if (a) { r.disabled = false; r.min = a[0]; r.max = a[1]; r.step = a[2]; r.value = a[3]; $('#es-wv-amt-v').textContent = a[3] + a[4]; } else { r.disabled = true; $('#es-wv-amt-v').textContent = '—'; }
    $('#es-wv-eqp').hidden = fx !== 'eq';
  }
  function $$(s) { return host.querySelectorAll(s); }
  $('#es-wv-amt').addEventListener('input', function () { var a = AMT[curFx]; $('#es-wv-amt-v').textContent = this.value + (a ? a[4] : ''); });
  $('#es-wv-eqp').innerHTML = window.ESEQ ? Object.keys(ESEQ.PRESETS).map(function (k) { return '<option>' + k + '</option>'; }).join('') : ''; $('#es-wv-eqp').hidden = true;
  function preview(fx) {
    if (!W.buf) return say('Load a wave first.'); setFx(fx);
    var r = range(), seg = slice(W.buf, r[0], r[1]), amt = +$('#es-wv-amt').value, btns = $$