From a4bad62b60ba93767d7006fe4f6eb72fce3a9dc8 Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 20 Oct 2023 20:51:00 +0000 Subject: [PATCH] add clientside DRC / dynamic range compressor --- copyparty/web/browser.css | 7 +++ copyparty/web/browser.js | 96 +++++++++++++++++++++++++++++++++++---- 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css index cd52c5b2..1a310160 100644 --- a/copyparty/web/browser.css +++ b/copyparty/web/browser.css @@ -1414,14 +1414,17 @@ html.dz input { .opview input.i { width: calc(100% - 16.2em); } +input.drc_v, input.eq_gain { width: 3em; text-align: center; margin: 0 .6em; } +#audio_drc table, #audio_eq table { border-collapse: collapse; } +#audio_drc td, #audio_eq td { text-align: center; } @@ -1430,11 +1433,15 @@ input.eq_gain { display: block; padding: 0; } +#au_drc, #au_eq { display: block; margin-top: .5em; padding: 1.3em .3em; } +#au_drc { + padding: .4em .3em; +} #ico1 { cursor: pointer; } diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index c9cd022e..0251e115 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -233,6 +233,7 @@ var Ls = { "ml_tcode": "transcode", "ml_tint": "tint", "ml_eq": "audio equalizer", + "ml_drc": "dynamic range compressor", "mt_preload": "start loading the next song near the end for gapless playback\">preload", "mt_fullpre": "try to preload the entire song;$N✅ enable on unreliable connections,$N❌ disable on slow connections probably\">full", @@ -250,6 +251,7 @@ var Ls = { "mt_coth": "convert all others (not mp3) to opus\">oth", "mt_tint": "background level (0-100) on the seekbar$Nto make buffering less distracting", "mt_eq": "enables the equalizer and gain control;$N$Nboost <code>0</code> = standard 100% volume (unmodified)$N$Nwidth <code>1  </code> = standard stereo (unmodified)$Nwidth <code>0.5</code> = 50% left-right crossfeed$Nwidth <code>0  </code> = mono$N$Nboost <code>-0.8</code> & width <code>10</code> = vocal removal :^)$N$Nenabling the equalizer makes gapless albums fully gapless, so leave it on with all the values at zero (except width = 1) if you care about that", + "mt_drc": "enables the dynamic range compressor (volume flattener / brickwaller); will also enable EQ to balance the spaghetti, so set all EQ fields except for 'width' to 0 if you don't want it$N$Nlowers the volume of audio above THRESHOLD dB; for every RATIO dB past THRESHOLD there is 1 dB of output, so default values of tresh -24 and ratio 12 means it should never get louder than -22 dB and it is safe to increase the equalizer boost to 0.8, or even 1.8 with ATK 0 and a huge RLS like 90$N$Nplease see wikipedia instead, this is probably wrong", "mb_play": "play", "mm_hashplay": "play this audio file?", @@ -710,6 +712,7 @@ var Ls = { "ml_tcode": "konvertering", "ml_tint": "tint", "ml_eq": "audio equalizer (tonejustering)", + "ml_drc": "compressor (volum-utjevning)", "mt_preload": "hent ned litt av neste sang i forkant,$Nslik at pausen i overgangen blir mindre\">forles", "mt_fullpre": "hent ned hele neste sang, ikke bare litt:$N✅ skru på hvis nettet ditt er ustabilt,$N❌ skru av hvis nettet ditt er tregt\">full", @@ -727,6 +730,7 @@ var Ls = { "mt_coth": "konverter alt annet (men ikke mp3) til opus\">andre", "mt_tint": "nivå av bakgrunnsfarge på søkestripa (0-100),$Ngjør oppdateringer mindre distraherende", "mt_eq": "aktiver tonekontroll og forsterker;$N$Nboost <code>0</code> = normal volumskala$N$Nwidth <code>1  </code> = normal stereo$Nwidth <code>0.5</code> = 50% blanding venstre-høyre$Nwidth <code>0  </code> = mono$N$Nboost <code>-0.8</code> & width <code>10</code> = instrumental :^)$N$Nreduserer også dødtid imellom sangfiler", + "mt_drc": "aktiver volum-utjevning (dynamic range compressor); vil også aktivere tonejustering, så sett alle EQ-feltene bortsett fra 'width' til 0 hvis du ikke vil ha noe EQ$N$Nfilteret vil dempe volumet på alt som er høyere enn TRESH dB; for hver RATIO dB over grensen er det 1dB som treffer høyttalerne, så standardverdiene tresh -24 og ratio 12 skal bety at volumet ikke går høyere enn -22 dB, slik at man trygt kan øke boost-verdien i equalizer'n til rundt 0.8, eller 1.8 kombinert med ATK 0 og RLS 90$N$Ngodt mulig jeg har misforstått litt, så wikipedia forklarer nok bedre", "mb_play": "lytt", "mm_hashplay": "spill denne sangen?", @@ -1386,7 +1390,9 @@ var mpl = (function () { '' + '' + - '

' + L.ml_eq + '

'); + '

' + L.ml_drc + '

' + + '

' + L.ml_eq + '

' + + ''); var r = { "pb_mode": (sread('pb_mode', ['loop', 'next']) || 'next').split('-')[0], @@ -2492,8 +2498,13 @@ function start_actx() { var afilt = (function () { var r = { "eqen": false, + "drcen": false, "bands": [31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 16000], "gains": [4, 3, 2, 1, 0, 0, 1, 2, 3, 4], + "drcv": [-24, 30, 12, 0.003, 0.25], + "drch": ['tresh', 'knee', 'ratio', 'atk', 'rls'], + "drck": ['threshold', 'knee', 'ratio', 'attack', 'release'], + "drcn": null, "filters": [], "filterskip": [], "plugs": [], @@ -2503,16 +2514,18 @@ var afilt = (function () { "acst": {} }; - if (!ACtx) - ebi('audio_eq').parentNode.style.display = 'none'; + function setvis(vis) { + ebi('audio_eq').parentNode.style.display = ebi('audio_drc').parentNode.style.display = (vis ? '' : 'none'); + } + + setvis(ACtx); r.init = function () { start_actx(); if (r.cfg) return; - if (!actx) - ebi('audio_eq').parentNode.style.display = 'none'; + setvis(actx); // some browsers have insane high-frequency boost // (or rather the actual problem is Q but close enough) @@ -2599,12 +2612,20 @@ var afilt = (function () { mp.acs = mpo.acs = null; }; - r.apply = function () { + r.apply = function (v) { r.init(); r.draw(); - if (!actx) - bcfg_set('au_eq', false); + if (!actx) { + bcfg_set('au_eq', r.eqen = false); + bcfg_set('au_drc', r.drcen = false); + } + else if (v === true && r.drcen && !r.eqen) + bcfg_set('au_eq', r.eqen = true); + else if (v === false && !r.eqen) + bcfg_set('au_drc', r.drcen = false); + + r.drcn = null; var plug = false; for (var a = 0; a < r.plugs.length; a++) @@ -2664,6 +2685,17 @@ var afilt = (function () { fi.gain.value = r.amp + 0.94; // +.137 dB measured; now -.25 dB and almost bitperfect r.filters.push(fi); + // wait nevermind, drc goes first + timer.rm(showdrc); + if (r.drcen) { + fi = r.drcn = actx.createDynamicsCompressor(); + for (var a = 0; a < r.drcv.length; a++) + fi[r.drck[a]].value = r.drcv[a]; + + r.filters.push(fi); + timer.add(showdrc); + } + if (Math.round(r.chw * 25) != 25) { var split = actx.createChannelSplitter(2), merge = actx.createChannelMerger(2), @@ -2736,6 +2768,31 @@ var afilt = (function () { clmod(that, 'err', err); } + function adj_drc() { + var err = false; + try { + var n = this.getAttribute('k'), + ov = r.drcv[n], + vs = this.value, + v = parseFloat(vs); + + if (!isNum(v) || v + '' != vs) + throw new Error('inval v'); + + if (v == ov) + return; + + r.drcv[n] = v; + jwrite('au_drc', r.drcv); + if (r.drcn) + r.drcn[r.drck[n]].value = v; + } + catch (ex) { + err = true; + } + clmod(this, 'err', err); + } + function eq_mod(e) { ev(e); adj_band(this, 0); @@ -2747,6 +2804,13 @@ var afilt = (function () { adj_band(this, step); } + function showdrc() { + if (!r.drcn) + return timer.rm(showdrc); + + ebi('h_drc').textContent = f2f(r.drcn.reduction, 1); + } + var html = [''], h2 = [], h3 = [], h4 = []; @@ -2776,6 +2840,18 @@ var afilt = (function () { html += h4.join('\n') + '
', 'enable
'; ebi('audio_eq').innerHTML = html; + h2 = []; + html = ['
']; + + for (var a = 0; a < r.drch.length; a++) { + html.push(''); + h2.push(''); + } + html = html.join('\n') + ''; + html += h2.join('\n') + '
', + 'enable' + r.drch[a] + '
'; + ebi('audio_drc').innerHTML = html; + var stp = QSA('a.eq_step'); for (var a = 0, aa = stp.length; a < aa; a++) stp[a].onclick = eq_step; @@ -2785,8 +2861,12 @@ var afilt = (function () { txt[a].oninput = eq_mod; txt[a].onkeydown = eq_keydown; } + txt = QSA('input.drc_v'); + for (var a = 0; a < txt.length; a++) + txt[a].oninput = txt[a].onkeydown = adj_drc; bcfg_bind(r, 'eqen', 'au_eq', false, r.apply); + bcfg_bind(r, 'drcen', 'au_drc', false, r.apply); r.draw(); return r;