diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css index 3ffae588..aa11e0c5 100644 --- a/copyparty/web/browser.css +++ b/copyparty/web/browser.css @@ -497,6 +497,27 @@ input[type="checkbox"]+label { input[type="checkbox"]:checked+label { color: #fc5; } +input.eq_gain { + width: 3em; + text-align: center; + margin: 0 .6em; +} +#audio_eq table { + border-collapse: collapse; +} +#audio_eq td { + text-align: center; +} +#audio_eq a.eq_step { + font-size: 1.5em; + display: block; + padding: 0; +} +#au_eq { + display: block; + margin-top: .5em; + padding: 1.3em .3em; +} diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html index 5e5f23ba..3d567e71 100644 --- a/copyparty/web/browser.html +++ b/copyparty/web/browser.html @@ -55,6 +55,8 @@ {%- endif %}

key notation

+

audio equalizer

+

diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index a97afb78..32c4d1a5 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -508,6 +508,167 @@ try { catch (ex) { } +var audio_eq = (function () { + var r = { + "en": false, + "bands": [31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 16000], + "gains": [0, -1, -2, -3, -4, -4, -3, -2, -1, 0], + "filters": [], + "last_au": null + }; + + try { + r.gains = jread('au_eq_gain', r.gains); + } + catch (ex) { } + + r.draw = function () { + var max = 0; + for (var a = 0; a < r.gains.length; a++) + if (max < r.gains[a]) + max = r.gains[a]; + + if (max > 0) + for (var a = 0; a < r.gains.length; a++) + r.gains[a] -= max; + + jwrite('au_eq_gain', r.gains); + + var txt = QSA('input.eq_gain'); + for (var a = 0; a < r.bands.length; a++) + txt[a].value = r.gains[a]; + }; + + r.apply = function () { + r.draw(); + + var Ctx = window.AudioContext || window.webkitAudioContext; + if (!Ctx) + bcfg_set('au_eq', false); + + if (!Ctx || !mp.au) + return; + + if (!r.en && !mp.ac) + return; + + if (mp.ac) { + for (var a = 0; a < r.filters.length; a++) + r.filters[a].disconnect(); + + mp.acs.disconnect(); + } + + if (!mp.ac || mp.au != r.last_au) { + if (mp.ac) + mp.ac.close(); + + r.last_au = mp.au; + mp.ac = new Ctx(); + mp.acs = mp.ac.createMediaElementSource(mp.au); + } + + if (!r.en) { + mp.acs.connect(mp.ac.destination); + return; + } + + r.filters = []; + for (var a = 0; a < r.bands.length; a++) { + var fi = mp.ac.createBiquadFilter(); + fi.frequency.value = r.bands[a]; + fi.gain.value = r.gains[a]; + fi.Q.value = a == 0 ? 0 : 1; + fi.type = a == 0 ? 'lowshelf' : a == r.bands.length - 1 ? 'highshelf' : 'peaking'; + r.filters.push(fi); + } + for (var a = r.bands.length - 1; a >= 0; a--) { + r.filters[a].connect(a > 0 ? r.filters[a - 1] : mp.ac.destination); + } + mp.acs.connect(r.filters[r.filters.length - 1]); + } + + function eq_step(e) { + ev(e); + var band = parseInt(this.getAttribute('band')), + step = parseFloat(this.getAttribute('step')); + + r.gains[band] += step; + r.apply(); + } + + function adj_band(that, step) { + try { + var band = parseInt(that.getAttribute('band')), + v = parseFloat(that.value); + + if (isNaN(v)) + throw 42; + + r.gains[band] = v + step; + } + catch (ex) { + return; + } + r.apply(); + } + + function eq_mod(e) { + ev(e); + adj_band(this, 0); + } + + function eq_keydown(e) { + var step = e.key == 'ArrowUp' ? 0.25 : e.key == 'ArrowDown' ? -0.25 : 0; + if (step != 0) + adj_band(this, step); + } + + var html = [''], + h2 = [], h3 = [], h4 = []; + + for (var a = 0; a < r.bands.length; a++) { + var hz = r.bands[a]; + if (hz >= 1000) + hz = (hz / 1000) + 'k'; + + hz = (hz + '').split('.')[0]; + html.push(''); + h2.push(''); + h4.push(''); + h3.push(''); + } + html.push(''); + html = html.join('\n'); + html += h2.join('\n') + ''; + html += h3.join('\n') + ''; + html += h4.join('\n') + '
', + 'enable+' + hz + '
'; + ebi('audio_eq').innerHTML = html; + + var stp = QSA('a.eq_step'); + for (var a = 0, aa = stp.length; a < aa; a++) + stp[a].onclick = eq_step; + + var txt = QSA('input.eq_gain'); + for (var a = 0; a < r.gains.length; a++) { + txt[a].oninput = eq_mod; + txt[a].onkeydown = eq_keydown; + } + + r.en = bcfg_get('au_eq', false); + ebi('au_eq').onclick = function (e) { + ev(e); + r.en = !r.en; + bcfg_set('au_eq', r.en); + r.apply(); + }; + + r.draw(); + return r; +})(); + + // plays the tid'th audio file on the page function play(tid, seek, call_depth) { if (mp.order.length == 0) @@ -568,6 +729,8 @@ function play(tid, seek, call_depth) { mp.au = mp.au_native; } + audio_eq.apply(); + mp.au.tid = tid; mp.au.src = url; mp.au.volume = mp.expvol();