You are an Expert WEB DEVELOPER and BROWSER EXTENSION CONVERTER. Your task is to ADAPT a Chrome extension's code to work seamlessly on Firefox, version 80 and above.

Follow these steps:

1. REVIEW the current Chrome extension code to identify all instances where Snap.svg library is used.
2. REMOVE Snap.svg dependencies and REPLACE them with an alternative solution that is compatible with Firefox.
3. LOCATE all Chrome-specific API tabs within the code.
4. TRANSITION from using Chrome's API tabs to focusing solely on the ActiveTab permission for Firefox.
5. ENSURE that the new code maintains all original functionalities without relying on deprecated or incompatible APIs.
6. TEST the adapted code on Firefox 80 and above, VERIFYING compatibility and functionality.
7. PRESENT the fully finished files, ensuring they are CLEAN and free of any comments or explanations.

You MUST ensure that these steps are executed meticulously to achieve a seamless transition from Chrome to Firefox.

Remember, I’m going to tip $300K for a BETTER SOLUTION!

Now Take a Deep Breath.

```popup.js

var _gaq = _gaq || [];
function scope() {
  var e = chrome.runtime.getManifest().version;
  _gaq.push(["_setAccount", "UA-64913318-2"]);
  _gaq.push(["_trackPageview"]);
  _gaq.push(["_trackEvent", "popupOpen", e]);
  var t = localStorage;
  var p = null;
  document.addEventListener("DOMContentLoaded", function () {
    _();
    n();
    a();
  });
  document.addEventListener("DOMContentLoaded", function () {
    var n = document.getElementById("presetNameInput");
    var e = document.getElementById("resetFiltersButton");
    e.onclick = function () {
      n.value = "";
      chrome.runtime.sendMessage({
        type: "resetFilters"
      });
    };
    var t = document.getElementById("bassBoostButton");
    t.onclick = function () {
      n.value = "";
      chrome.runtime.sendMessage({
        type: "preset",
        preset: "bassBoost"
      });
    };
    var r = document.getElementById("requiresProDiv");
    function a(e) {
      r.textContent = e;
      r.classList.add("show");
      setTimeout(function () {
        r.classList.remove("show");
      }, 5000);
    }
    var i = document.getElementById("savePresetButton");
    i.onclick = function () {
      if (p && userPresetSpan.children.length >= p + 1) {
        a("Saving more than 3 presets requires Ears Pro. Click the Pro tab above to start your free trial!");
        return;
      }
      var e = n.value.trim();
      if (e != "") {
        chrome.runtime.sendMessage({
          type: "savePreset",
          preset: e
        });
      } else {
        a("Type a name in the Preset Name box, then click Save Preset or press Enter.");
        n.focus();
      }
    };
    var o = document.getElementById("deletePresetButton");
    o.onclick = function () {
      var e = n.value.trim();
      if (e != "") {
        chrome.runtime.sendMessage({
          type: "deletePreset",
          preset: e
        });
      }
    };
    n.onkeypress = function (e) {
      if (!e) {
        e = window.event;
      }
      var t = e.keyCode || e.which;
      if (t == "13" && document.activeElement == n) {
        i.click();
        return false;
      }
    };
    var s = document.getElementById("exportPresetsButton");
    s.onclick = function () {
      chrome.runtime.sendMessage({
        type: "exportPresets"
      });
    };
    var c = document.getElementById("importPresetsButton");
    var u = document.getElementById("importPresetsFile");
    c.onclick = function () {
      u.click();
    };
    u.onchange = function () {
      var e = u.files;
      for (var t = 0; t < e.length; t++) {
        var n = new FileReader();
        n.onload = function (e) {
          chrome.runtime.sendMessage({
            type: "importPresets",
            presets: JSON.parse(e.target.result)
          });
        };
        n.readAsText(e[t]);
      }
    };
    var l = document.getElementById("vizButton");
    function f() {
      if (y()) {
        l.classList.add("on");
      } else {
        l.classList.remove("on");
      }
    }
    f();
    l.onclick = function () {
      if (p) {
        a("The frequency spectrum visualizer requires Ears Pro. Click the Pro tab above to start your free trial!");
      }
      H();
      f();
    };
    var v = ["tab-1", "tab-2", "tab-3"];
    for (var d = 0; d < v.length; d++) {
      var h = v[d];
      document.getElementById(h).addEventListener("change", function (e) {
        return function () {
          if (this.checked) {
            localStorage["last-tab"] = e;
          }
        };
      }(h));
    }
    var m = localStorage["last-tab"];
    if (m) {
      var g = document.getElementById(m);
      if (g) {
        g.click();
      }
    }
    if (window.innerWidth && window.innerWidth > 1000) {
      document.getElementById("fullscreen-link").style.display = "none";
    }
  });
  function n() {
    chrome.runtime.sendMessage({
      type: "getFullRefresh"
    });
  }
  function _() {
    chrome.runtime.sendMessage({
      type: "onPopupOpen"
    });
  }
  var E = 44100;
  var O = "eqSvg";
  var z = "eqTabButton";
  var A = "How was your day of beautiful audio? Let us know what you think at the link below! If you would like to continue using Ears, please support us by making a purchase. If you have already purchased Ears, please restart Chrome. Enjoy!";
  var i = true;
  function r() {
    if (i) {
      n();
      setTimeout(r, 1000);
    }
  }
  setTimeout(r, 1000);
  chrome.runtime.onMessage.addListener(function (e) {
    if (e.type == "sendCurrentTabStatus") {
      if (e.streaming) {
        G();
      } else {
        W();
      }
    }
    if (e.type == "sendWorkspaceStatus") {
      i = false;
      J(e);
    }
    if (e.type == "sendSampleRate") {
      E = e.Fs;
      console.log("set sample rate to " + E);
    }
    if (e.type == "sendPresets") {
      j(e);
    }
    if (e.type == "F") {
      var r = document.getElementById(O);
      if (r) {
        r.remove();
      }
      var a = document.getElementById(z);
      if (a) {
        a.remove();
      }
      document.getElementById("lt").textContent = A;
      document.getElementById("pl").style.display = "block";
    }
  });
  function U(e) {
    if (S) {
      var t = 1000 / 30 - (performance.now() - S);
      if (t > 0) {
        setTimeout(h, t);
        return;
      }
    }
    S = performance.now();
    if (C) {
      C.remove();
    }
    if (!y()) {
      return;
    }
    var n = e.fft;
    if (I && n.length > 0) {
      strokeGradient = I.gradient("l(.5, 0, .5, 1)" + m + "-" + q);
      var r = [];
      function a(e) {
        return B - 1 - e;
      }
      for (var i in n) {
        var o = i * E / (n.length * 2);
        if (o < 10) {
          continue;
        }
        var s = P(o);
        if (s > T) {
          break;
        }
        var c = (n[i] + 100) / 100 * B;
        r.push([s, c]);
      }
      var u = [];
      for (var i in r) {
        var l = r[i];
        if (u.length == 0) {
          u.push(l);
          continue;
        }
        var f = u[u.length - 1];
        var v = 2;
        if (l[0] - f[0] < v) {
          if (l[1] > f[1]) {
            f[1] = l[1];
          }
        } else {
          u.push(l);
        }
      }
      var d = [];
      for (var i in u) {
        var f = u[i];
        d = d.concat([f[0], a(f[1])]);
      }
      C = I.polyline(d).attr({
        "fill-opacity": "0",
        stroke: strokeGradient,
        "pointer-events": "none"
      });
    }
    h();
  }
  function h() {
    chrome.runtime.sendMessage({
      type: "getFFT"
    }, U);
  }
  function j(e) {
    var t = e.presets;
    var n = document.getElementById("userPresetSpan");
    while (n.firstChild) {
      n.removeChild(n.firstChild);
    }
    var r = Object.keys(t);
    for (var a = 0; a < r.length; a++) {
      (function (e) {
        var t = document.createElement("button");
        t.textContent = e;
        n.appendChild(t);
        t.onclick = function () {
          chrome.runtime.sendMessage({
            type: "preset",
            preset: e
          });
          document.getElementById("presetNameInput").value = e;
        };
      })(r[a]);
    }
  }
  function a() {
    chrome.runtime.sendMessage({
      type: "eqTab",
      on: true
    });
  }
  function W() {
    var e = document.getElementById("eqTabButton");
    e.onclick = function () {
      a();
    };
    e.textContent = "EQ This Tab";
  }
  function G() {
    var e = document.getElementById("eqTabButton");
    e.onclick = function () {
      chrome.runtime.sendMessage({
        type: "eqTab",
        on: false
      });
    };
    e.textContent = "Stop EQing This Tab";
  }
  var o = "SHOW_VISUALIZER";
  function y() {
    return localStorage[o] == true;
  }
  function H() {
    if (p) {
      return;
    }
    t[o] ^= true;
    if (y()) {
      h();
    }
  }
  var s = [];
  function J(e) {
    s = [];
    for (var t = 0; t < e.eqFilters.length; t++) {
      var n = e.eqFilters[t];
      var r = {};
      r.x = P(n.frequency);
      r.y = F(n.gain);
      r.w = n.frequency / n.q;
      r.t = n.type;
      r.gain = n.gain;
      r.q = n.q;
      r.frequency = n.frequency;
      s.push(r);
    }
    var i = {
      gain: e.gain
    };
    i.y = F(fe(i.gain));
    $(s, i);
    K(e.streams);
  }
  function K(e) {
    var t = document.getElementById("eqTabList");
    t.innerHTML = "";
    if (e.length == 0) {
      t.textContent = "No tabs active. Click 'EQ This Tab' below to activate this tab.";
      return;
    }
    var n = document.createElement("table");
    for (var r = 0; r < e.length; r++) {
      n.appendChild(V(e[r]));
    }
    t.appendChild(n);
  }
  function V(e) {
    var t = document.createElement("tr");
    var n = document.createElement("button");
    n.textContent = "Stop EQing";
    n.onclick = function () {
      chrome.runtime.sendMessage({
        type: "disconnectTab",
        tab: e
      });
    };
    var r = document.createElement("img");
    r.className = "tabFavIcon";
    r.src = e.favIconUrl;
    r.alt = "";
    n.appendChild(r);
    var a = document.createElement("td");
    a.appendChild(n);
    t.appendChild(a);
    var i = document.createElement("td");
    var o = e.title;
    if (o.length > 45) {
      o = o.substring(0, 45);
    }
    i.textContent = o;
    t.appendChild(i);
    return t;
  }
  var m = "wheat";
  var q = "#2C3E50";
  var b = "#9573A8";
  var w = "#CDF7E1";
  var T = 600;
  var B = 300;
  var g = 30;
  var c = 22050;
  var u = 30;
  var M = 10;
  var k = -30;
  var I = null;
  var C = null;
  var S = null;
  var x = null;
  function $(e, t) {
    if (I) {
      I.clear();
    }
    if (x) {
      x.clear();
    }
    I = Snap("#eqSvg");
    I.attr({
      fill: q,
      height: B,
      width: T
    });
    x = Snap("#gainSvg");
    x.attr({
      fill: q,
      height: B,
      width: g
    });
    var n = {
      fill: m,
      stroke: m
    };
    re(I, e);
    ae(I);
    var s = x.line(0, t.y, g, t.y).attr({
      stroke: "wheat"
    }).addClass("gainLine");
    s.drag(oe(s), Q, se(s));
    for (var c = 0; c < e.length; c++) {
      var u = e[c];
      var l = u.x;
      var f = u.y;
      var v = n;
      if (u.t == "peaking") {
        v = {
          fill: w,
          stroke: w
        };
      }
      if (u.t == "highshelf" || u.t == "lowshelf") {
        v = {
          fill: b,
          stroke: b
        };
      }
      var d = I.circle(l, f, 4).attr(v).addClass("filterDot");
      d.drag(ce(u, c, I), Q, he(u, c));
      d.dblclick(ee(u, c));
    }
  }
  function ee(e, t) {
    return function () {
      e.gain = 0;
      e.y = F(0);
      de(t);
      n();
    };
  }
  function l(e) {
    return L(e / T) * c;
  }
  function P(e) {
    return te(e / c) * T;
  }
  function te(e) {
    return Math.pow(e, 1 / 4);
  }
  function L(e) {
    return Math.pow(e, 4);
  }
  function F(e) {
    var t = u - k;
    return B * (1 - (e - k) / t);
  }
  function f(e) {
    var t = u - k;
    return (1 - e / B) * t + k;
  }
  function v(e) {
    var t = e.node.getClientRects()[0];
    return [t.left, t.top];
  }
  var R = {};
  function d(e, t, n) {
    var r = t.frequency;
    var a = t.q;
    var i = t.gain;
    var o = Math.tan(Math.PI * r / E);
    var s = 1 / (1 + 1 / a * o + o * o);
    var c = Math.pow(10, Math.abs(i) / 20);
    var u = 0;
    var l = 0;
    var f = 0;
    var v = 0;
    var d = 0;
    var h = "wheat";
    if (t.t == "peaking") {
      h = w;
      if (i >= 0) {
        s = 1 / (1 + 1 / a * o + o * o);
        u = (1 + c / a * o + o * o) * s;
        l = (o * o - 1) * 2 * s;
        f = (1 - c / a * o + o * o) * s;
        v = l;
        d = (1 - 1 / a * o + o * o) * s;
      } else {
        s = 1 / (1 + c / a * o + o * o);
        u = (1 + 1 / a * o + o * o) * s;
        l = (o * o - 1) * 2 * s;
        f = (1 - 1 / a * o + o * o) * s;
        v = l;
        d = (1 - c / a * o + o * o) * s;
      }
    } else if (t.t == "highshelf") {
      h = b;
      if (i >= 0) {
        s = 1 / (1 + Math.SQRT2 * o + o * o);
        u = (c + Math.sqrt(c * 2) * o + o * o) * s;
        l = (o * o - c) * 2 * s;
        f = (c - Math.sqrt(c * 2) * o + o * o) * s;
        v = (o * o - 1) * 2 * s;
        d = (1 - Math.SQRT2 * o + o * o) * s;
      } else {
        s = 1 / (c + Math.sqrt(c * 2) * o + o * o);
        u = (1 + Math.SQRT2 * o + o * o) * s;
        l = (o * o - 1) * 2 * s;
        f = (1 - Math.SQRT2 * o + o * o) * s;
        v = (o * o - c) * 2 * s;
        d = (c - Math.sqrt(c * 2) * o + o * o) * s;
      }
    } else if (t.t == "lowshelf") {
      h = b;
      if (i >= 0) {
        s = 1 / (1 + Math.SQRT2 * o + o * o);
        u = (1 + Math.sqrt(c * 2) * o + c * o * o) * s;
        l = (c * o * o - 1) * 2 * s;
        f = (1 - Math.sqrt(c * 2) * o + c * o * o) * s;
        v = (o * o - 1) * 2 * s;
        d = (1 - Math.SQRT2 * o + o * o) * s;
      } else {
        s = 1 / (1 + Math.sqrt(c * 2) * o + c * o * o);
        u = (1 + Math.SQRT2 * o + o * o) * s;
        l = (o * o - 1) * 2 * s;
        f = (1 - Math.SQRT2 * o + o * o) * s;
        v = (c * o * o - 1) * 2 * s;
        d = (1 - Math.sqrt(c * 2) * o + c * o * o) * s;
      }
    }
    var m = [];
    for (var g = 0; g < T; g += 2) {
      var p = L(g / T) * Math.PI;
      var y = Math.pow(Math.sin(p / 2), 2);
      var M = Math.log((Math.pow(u + l + f, 2) - (u * l + u * 4 * f + l * f) * 4 * y + u * 16 * f * y * y) / (Math.pow(1 + v + d, 2) - (v + d * 4 + v * d) * 4 * y + d * 16 * y * y));
      M = M * 10 / Math.LN10;
      M = F(M);
      if (M == -Infinity) {
        M = B - 1;
      }
      if (Math.abs(M - B / 2) > 1) {
        m = m.concat([g, M]);
      }
    }
    var k = null;
    if (i >= 0) {
      k = e.gradient("l(.5, 0, .5, 1)" + h + "-" + q);
    } else {
      k = e.gradient("l(.5, 1, .5, 0)" + h + "-" + q);
    }
    if (R[n]) {
      R[n].remove();
    }
    R[n] = e.polyline(m).attr({
      stroke: k,
      "fill-opacity": "0",
      "pointer-events": "none"
    });
  }
  function re(e, t) {
    for (var r = 0; r < t.length; r++) {
      var a = t[r];
      d(e, a, r);
    }
  }
  function ae(e) {
    for (var t = 5; t < c; t *= 2) {
      var n = P(t);
      e.line(n, B / 2 + 10, n, B / 2 - 10).attr({
        stroke: "wheat",
        "stroke-opacity": 0.25
      });
      e.line(n, B, n, B - 15).attr({
        stroke: "wheat"
      });
      e.text(n, B - 18, "" + Math.round(l(n))).attr({
        fill: "wheat",
        "text-anchor": "middle",
        "font-size": 10
      });
      e.line(n, 0, n, 15).attr({
        stroke: "wheat"
      });
    }
    var r = 5;
    for (var a = k; a < u; a += r) {
      var i = F(a);
      if (Math.abs(u) - Math.abs(a) > r / 2) {
        e.line(0, i, 5, i).attr({
          stroke: "wheat"
        });
        e.text(7, i, "" + a).attr({
          fill: "wheat",
          "font-size": 10,
          "dominant-baseline": "middle"
        });
      }
    }
  }
  function ie(e) {
    if (e < 0.2) {
      e = 0.2;
    }
    if (e > 11) {
      e = 11;
    }
    return e;
  }
  function oe(o) {
    return function (e, t, n, r) {
      var i = v(x);
      r = r - i[1];
      if (r < 0 || r >= B) {
        return;
      }
      if (f(r) > M) {
        t -= r - F(M);
        r = F(M);
      }
      o.y = r;
      o.gain = le(f(r));
      this.attr({
        transform: this.data("origTransform") + (this.data("origTransform") ? "T" : "t") + [0, t]
      });
      ve(o);
    };
  }
  function se(e) {
    return function () {
      this.attr({
        fill: q
      });
      chrome.runtime.sendMessage({
        type: "gainUpdated",
        gain: e.gain
      });
      n();
    };
  }
  function ce(o, s, c) {
    return function (e, t, n, r, a) {
      if (a.shiftKey) {
        o.q = ie(o.q + a.movementY / 10);
      } else {
        var i = v(I);
        n = n - i[0];
        r = r - i[1];
        if (n < 0 || n >= T || r < 0 || r >= B) {
          return;
        }
        o.x = n;
        o.y = r;
        o.gain = f(r);
        o.frequency = l(n);
        this.attr({
          transform: this.data("origTransform") + (this.data("origTransform") ? "T" : "t") + [e, t]
        });
      }
      d(c, o, s);
      ue(o, s);
    };
  }
  function ue(e, t) {
    chrome.runtime.sendMessage({
      type: "modifyFilter",
      index: t,
      frequency: l(e.x),
      gain: f(e.y),
      q: e.q
    });
  }
  function le(e) {
    return Math.pow(10, e / 10);
  }
  function fe(e) {
    return Math.log10(e) * 10;
  }
  function ve(e) {
    chrome.runtime.sendMessage({
      type: "modifyGain",
      gain: e.gain
    });
  }
  function de(e) {
    chrome.runtime.sendMessage({
      type: "resetFilter",
      index: e
    });
  }
  function Q() {
    this.data("origTransform", this.transform().local);
    this.attr({
      fill: "black"
    });
  }
  function he(e, t) {
    return function () {
      this.attr({
        fill: q
      });
      chrome.runtime.sendMessage({
        type: "filterUpdated",
        filterType: e.t,
        frequency: e.frequency,
        gain: e.gain,
        q: e.q
      });
      n();
    };
  }
  if (y()) {
    h();
  }
}
(function () {
  scope();
})();

```

```bg.js

var _gaq = _gaq || [];
function scope() {
  var J = chrome.runtime.getManifest().version;
  _gaq.push(["_setAccount", "UA-64913318-2"]);
  _gaq.push(["_trackPageview"]);
  _gaq.push(["_trackEvent", "backgroundOpen", J]);
  var U = "VERSION";
  var i = "PRESETS";
  var a = "PRESETS.";
  var s = {
    get: function (e, t) {
      t({});
    },
    set: function (e, t) {
      t();
    },
    remove: function (e, t) {
      t();
    }
  };
  var W = null;
  localStorage[" "] = " ";
  s = chrome.storage.sync;
  W = null;
  var o = false;
  function D(e) {
    if (o) {
      console.log(e);
    }
  }
  var M = null;
  var B = null;
  var G = null;
  var Q = null;
  var L = null;
  var V = [];
  var z = [20, 40, 80, 160, 320, 640, 1280, 2560, 5120, 10240, 20480];
  var H = [0.7071, 0.7071, 0.7071, 0.7071, 0.7071, 0.7071, 0.7071, 0.7071, 0.7071, 0.7071, 0.7071];
  var K = 11;
  function X() {}
  var Y = {};
  var Z = false;
  var $;
  function l(e) {
    return a + e;
  }
  function ee(n) {
    if (!localStorage[i]) {
      localStorage[i] = JSON.stringify({});
    }
    var r = JSON.parse(localStorage[i]);
    if (r == null || typeof r != "object") {
      r = {};
    }
    s.get(null, function (e) {
      if (chrome.runtime.lastError) {
        console.log(chrome.runtime.lastError);
      }
      for (var t in e) {
        if (t.startsWith(a)) {
          r[t.slice(a.length)] = e[t];
        }
      }
      n(r);
    });
  }
  function te(e, t, n) {
    var r = JSON.parse(localStorage[i]);
    if (r == null || typeof r != "object") {
      r = {};
    }
    var a = Object.keys(r).length;
    r[e] = t;
    var o = Object.keys(r).length;
    if (W && Object.keys(r) > W && o >= a) {
      n();
      return;
    }
    localStorage[i] = JSON.stringify(r);
    syncPresets = {};
    syncPresets[l(e)] = t;
    s.set(syncPresets, function () {
      if (chrome.runtime.lastError) {
        console.log(chrome.runtime.lastError);
      }
      n();
    });
  }
  function ne(e, t) {
    _gaq.push(["_trackEvent", "deletePreset", e.preset]);
    var n = JSON.parse(localStorage[i]);
    if (n == null || typeof n != "object") {
      n = {};
    }
    delete n[e.preset];
    localStorage[i] = JSON.stringify(n);
    s.remove(l(e.preset), t);
  }
  chrome.storage.onChanged.addListener(function () {});
  function re() {
    return new AudioContext({
      latencyHint: "playback"
    });
  }
  function ae() {
    if (Z) {
      M.close();
      M = null;
      Z = false;
      Q = null;
      L = null;
      var e = Object.values(Y);
      Y = {};
      for (var t = 0; t < e.length; t++) {
        var n = e[t].audioSource;
        n.disconnect(B);
      }
      u(e);
    }
  }
  function u(e) {
    var t = null;
    if (Z) {
      return;
    }
    Z = true;
    M = re();
    M.suspend();
    console.log("starting sampleRate: " + M.sampleRate);
    var n = false;
    X = function () {
      if (n) {
        return;
      }
      var e = re();
      var t = M.sampleRate && e.sampleRate && M.sampleRate != e.sampleRate;
      if (t) {
        console.log(e);
        console.log(M);
        n = true;
      }
      e.close();
      if (t) {
        console.log("sampleRate changed... reloading AudioContext");
        ae();
      }
    };
    B = M.createGain();
    B.gain.value = 1;
    G = M.createGain();
    G.gain.value = m();
    if (t) {
      var r = M.createScriptProcessor(t);
      r.onaudioprocess = function (e) {
        var t = e.inputBuffer;
        var n = e.outputBuffer;
        for (var r = 0; r < n.numberOfChannels; r++) {
          n.copyToChannel(t.getChannelData(r), r);
        }
      };
      G.connect(r);
      r.connect(M.destination);
    } else {
      G.connect(M.destination);
    }
    if (!localStorage[U]) {
      localStorage[U] = "0.0.0";
    }
    if (localStorage[" "] == " ") {
      if (localStorage.sync != " ") {
        ee(function (e) {
          var t = Object.keys(e);
          for (var n = 0; n < t.length; n++) {
            var r = t[n];
            te(r, e[r], function () {});
          }
        });
        localStorage.sync = " ";
      }
    }
    localStorage[U] = J;
    V = [];
    var a = {};
    function o() {
      var e = G;
      var t = false;
      for (var n = V.length - 1; n >= 0; n--) {
        if (V[n].gain.value != 0) {
          if (!a[n]) {
            t = true;
          }
          if (t) {
            D(n + " -> " + e.idx);
            V[n].disconnect();
            V[n].connect(e);
          }
          if (a[n]) {
            t = false;
          }
          a[n] = true;
          e = V[n];
          e.idx = n;
        } else if (a[n]) {
          a[n] = false;
          t = true;
        }
      }
      if (t) {
        D("preGain -> " + e.idx);
        B.disconnect();
        B.connect(e);
      }
    }
    for (var i = 0; i < K; i++) {
      var s = M.createBiquadFilter();
      if (i == 0) {
        s.type = "lowshelf";
      } else if (i == K - 1) {
        s.type = "highshelf";
      } else {
        s.type = "peaking";
      }
      var l = "filter" + i;
      var c = localStorage[l];
      if (c) {
        c = JSON.parse(c);
        s.frequency.value = c.f;
        s.gain.value = c.g;
        s.Q.value = c.q;
      } else {
        s.frequency.value = z[i];
        s.gain.value = 0;
        s.Q.value = H[i];
      }
      V.push(s);
      localStorage[l] = JSON.stringify({
        f: s.frequency.value,
        g: s.gain.value,
        q: s.Q.value
      });
    }
    B.connect(G);
    function u(e) {
      f = V[e.index];
      f.gain.value = v(e.gain);
      f.frequency.value = p(e.frequency);
      f.Q.value = d(e.q);
      var t = "filter" + e.index;
      localStorage[t] = JSON.stringify({
        f: f.frequency.value,
        g: f.gain.value,
        q: f.Q.value
      });
      o();
    }
    function g(e) {
      if (e > 10) {
        return 10;
      }
      if (e < 0.00316) {
        return 0.00316;
      }
      return e;
    }
    function v(e) {
      if (e < -30) {
        return -30;
      }
      if (e > 30) {
        return 30;
      }
      return e;
    }
    function p(e) {
      if (e < 5) {
        return 5;
      }
      if (e > 20000) {
        return 20000;
      }
      return e;
    }
    function d(e) {
      if (e < 0.2) {
        e = 0.2;
      }
      if (e > 11) {
        e = 11;
      }
      return e;
    }
    function y(e) {
      var t = g(e.gain);
      G.gain.value = g(t);
      h(t);
    }
    function m() {
      if (!localStorage.GAIN) {
        localStorage.GAIN = JSON.stringify(1);
      }
      return JSON.parse(localStorage.GAIN);
    }
    function h(e) {
      localStorage.GAIN = JSON.stringify(e);
    }
    function S(e) {
      _gaq.push(["_trackEvent", "preset", "save"]);
      var t = [];
      var n = [];
      var r = [];
      for (var a = 0; a < V.length; a++) {
        var o = V[a];
        t.push(o.frequency.value);
        n.push(o.gain.value);
        r.push(o.Q.value);
      }
      var i = {
        frequencies: t,
        gains: n,
        qs: r
      };
      te(e.preset, i, E);
    }
    function q(i) {
      ee(function (e) {
        var t = [];
        var n = [];
        var r = [];
        if (i.preset == "bassBoost") {
          for (var a = 0; a < z.length; a++) {
            t.push(z[a]);
            n.push(0);
            r.push(H[a]);
          }
          t[0] = 340;
          n[0] = 5;
        } else {
          var o = e[i.preset];
          if (o) {
            t = o.frequencies;
            n = o.gains;
            r = o.qs;
          } else {
            return;
          }
        }
        _gaq.push(["_trackEvent", "preset", "set"]);
        for (var a = 0; a < t.length; a++) {
          u({
            index: a,
            frequency: t[a],
            gain: n[a],
            q: r[a]
          });
        }
        E();
      });
    }
    function b(t) {
      N(function (e) {
        T(t, e, true);
        o();
        E();
      });
    }
    function _() {
      var e = {
        type: "sendWorkspaceStatus",
        eqFilters: [],
        streams: [],
        gain: G.gain.value
      };
      for (var t = 0; t < V.length; t++) {
        var n = V[t];
        e.eqFilters.push({
          frequency: n.frequency.value,
          gain: n.gain.value,
          type: n.type,
          q: n.Q.value
        });
      }
      for (var r in Y) {
        e.streams.push(Y[r].tab);
      }
      chrome.runtime.sendMessage(e);
    }
    function k() {
      chrome.runtime.sendMessage({
        type: "sendSampleRate",
        Fs: M.sampleRate
      });
    }
    function O() {
      ee(function (e) {
        chrome.runtime.sendMessage({
          type: "sendPresets",
          presets: e
        });
      });
    }
    function E() {
      w();
      _();
      k();
      O();
    }
    function N(t) {
      chrome.tabs.query({
        currentWindow: true,
        active: true
      }, function (e) {
        if (e.length != 1) {
          console.error("num active tabs != 1");
        } else {
          t(e[0]);
        }
      });
    }
    function w() {
      N(function (e) {
        chrome.runtime.sendMessage({
          type: "sendCurrentTabStatus",
          streaming: e.id in Y
        });
      });
    }
    function T(e, t, n) {
      if (!e) {
        console.log("null stream, aborting");
        return;
      }
      if (n) {
        _gaq.push(["_trackEvent", "tabStream", "added"]);
      }
      if (Object.keys(Y).length == 0) {
        M.resume();
      }
      if (t.id in Y) {
        console.log("had stream, stopping");
        _gaq.push(["_trackEvent", "tabStream", "hadDuplicate"]);
        Y[t.id].stream.getTracks()[0].stop();
        delete Y[t.id];
      }
      var r = M.createMediaStreamSource(e);
      r.connect(B);
      Y[t.id] = {
        stream: e,
        tab: t,
        audioSource: r
      };
    }
    var R = {};
    var C = {};
    function x(t) {
      console.log(t);
      chrome.windows.get(chrome.windows.WINDOW_ID_CURRENT, null, function (e) {
        if (e.state != "fullscreen") {
          C[e.id] = e.state;
        }
        if (t.fullscreen) {
          chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, {
            state: "fullscreen"
          }, null);
        } else {
          chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, {
            state: C[e.id] || "fullscreen"
          }, null);
        }
      });
      if (t.status == "active") {
        R[t.tabId] = t;
      } else if (t.status == "stopped" || t.status == "error") {
        delete R[t.tabId];
      }
    }
    function j() {
      _gaq.push(["_trackEvent", "currentTab", "removed"]);
      N(I);
    }
    function I(e) {
      console.log("disconnectedTab id " + e.id);
      if (e.id in Y) {
        _gaq.push(["_trackEvent", "tabStream", "removed"]);
        var t = Y[e.id].stream;
        t.getTracks()[0].stop();
        delete Y[e.id];
      }
      if (Object.keys(Y).length == 0) {
        M.suspend();
      }
      E();
    }
    function P() {
      for (var e = 0; e < K; e++) {
        u({
          index: e,
          gain: 0,
          frequency: z[e],
          q: H[e]
        });
      }
      y({
        gain: 1
      });
      _gaq.push(["_trackEvent", "filterUpdated", "resetAll"]);
      E();
    }
    function A(e) {
      u({
        index: e.index,
        gain: 0,
        frequency: z[e.index],
        q: H[e.index]
      });
      _gaq.push(["_trackEvent", "filterUpdated", "reset"]);
    }
    function F() {
      function t(e, t) {
        var n = document.createElement("a");
        var r = new Blob([t], {
          type: "text/plain;charset=UTF-8"
        });
        n.href = window.URL.createObjectURL(r);
        n.download = e;
        n.style.display = "none";
        document.body.appendChild(n);
        n.click();
        delete n;
      }
      ee(function (e) {
        t("EarsAudioToolkitPresets.json", JSON.stringify(e, null, 2));
      });
    }
    $ = function (e, t, n) {
      if (e.type == "eqTab") {
        if (e.on) {
          chrome.tabs.getSelected(null, function (e) {
            if (e.url.startsWith("chrome-extension://" + chrome.runtime.id)) {
              return;
            }
            chrome.tabCapture.capture({
              audio: true,
              video: false
            }, b);
          });
        } else {
          j();
        }
      }
      if (e.type == "getCurrentTabStatus") {
        w();
      }
      if (e.type == "getWorkspaceStatus") {
        _();
      }
      if (e.type == "getFullRefresh") {
        E();
      }
      if (e.type == "onPopupOpen") {
        X();
      }
      if (e.type == "modifyFilter") {
        u(e);
      }
      if (e.type == "modifyGain") {
        y(e);
      }
      if (e.type == "gainUpdated") {
        _gaq.push(["_trackEvent", "gainUpdated", "gain"]);
      }
      if (e.type == "filterUpdated") {
        _gaq.push(["_trackEvent", "filterUpdated", e.filterType]);
      }
      if (e.type == "disconnectTab") {
        I(e.tab);
      }
      if (e.type == "resetFilters") {
        P();
      }
      if (e.type == "resetFilter") {
        A(e);
      }
      if (e.type == "preset") {
        q(e);
      }
      if (e.type == "savePreset") {
        S(e);
      }
      if (e.type == "importPresets") {
        var r = e.presets;
        var a = Object.keys(r);
        for (var o = 0; o < a.length; o++) {
          te(a[o], r[a[o]], E);
        }
      }
      if (e.type == "deletePreset") {
        ne(e, E);
      }
      if (e.type == "exportPresets") {
        F();
      }
      if (e.type == "getFFT") {
        if (W) {
          chrome.runtime.sendMessage({
            type: "fft",
            fft: []
          });
          return;
        }
        if (Q) {
          L = performance.now();
          var i = new Float32Array(Q.frequencyBinCount);
          Q.getFloatFrequencyData(i);
          n({
            type: "fft",
            fft: Array.from(i)
          });
        } else {
          Q = M.createAnalyser();
          Q.fftSize = 8192;
          Q.smoothingTimeConstant = 0.5;
          console.log("created analyser");
          console.log(Q);
          G.connect(Q);
          n({
            type: "fft",
            fft: []
          });
        }
      }
    };
    if (e) {
      for (var i = 0; i < e.length; i++) {
        T(e[i].stream, e[i].tab, false);
      }
      o();
      E();
    } else {
      chrome.runtime.onMessage.addListener($);
      chrome.tabCapture.onStatusChanged.addListener(x);
    }
  }
  u();
  setInterval(function () {
    if (L) {
      if (performance.now() - L > 1000) {
        if (G && Q) {
          G.disconnect(Q);
          Q = null;
          console.log("disconnected analyser");
        }
      }
    }
  }, 1000);
}
scope();

```

```manifest.json

{
  "manifest_version": 2,

  "name": "Ears: Bass Boost, EQ Any Audio!",
  "short_name": "Ears",
  "description": "EQ any audio you find on the web, live! Crank the bass, dim the highs, up the vocals: all with Ears!",
  "version": "1.3.12",
  "author": "Kevin King",
  "icons": {
    "16": "ears16.png",
    "32": "ears32.png",
    "48": "ears48.png",
    "64": "ears64.png",
    "128": "ears128.png"
  },

  "browser_action": {
    "default_icon": "ears128.png",
    "default_popup": "popup.html"
  },
  "permissions": ["activeTab", "tabs", "storage"],
  "background": {
    "scripts": ["bg.js"]
  }
}

```