mirror of
https://github.com/9001/copyparty.git
synced 2026-02-26 21:43:08 -07:00
audioplayer: add skip-silence feature (#1265)
* add skip silence option
This commit is contained in:
parent
598df44e87
commit
6694998985
|
|
@ -1456,7 +1456,8 @@ html.dz input {
|
||||||
width: calc(100% - 16.2em);
|
width: calc(100% - 16.2em);
|
||||||
}
|
}
|
||||||
input.drc_v,
|
input.drc_v,
|
||||||
input.eq_gain {
|
input.eq_gain,
|
||||||
|
input.ssconf_v {
|
||||||
width: 3em;
|
width: 3em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0 .6em;
|
margin: 0 .6em;
|
||||||
|
|
@ -1466,7 +1467,8 @@ input.eq_gain {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
#audio_drc td,
|
#audio_drc td,
|
||||||
#audio_eq td {
|
#audio_eq td,
|
||||||
|
#audio_ss td {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
#audio_eq a.eq_step {
|
#audio_eq a.eq_step {
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,7 @@ if (1)
|
||||||
"ml_tint": "tint",
|
"ml_tint": "tint",
|
||||||
"ml_eq": "audio equalizer",
|
"ml_eq": "audio equalizer",
|
||||||
"ml_drc": "dynamic range compressor",
|
"ml_drc": "dynamic range compressor",
|
||||||
|
"ml_ss": "skip silence",
|
||||||
|
|
||||||
"mt_loop": "loop/repeat one song\">🔁",
|
"mt_loop": "loop/repeat one song\">🔁",
|
||||||
"mt_one": "stop after one song\">1️⃣",
|
"mt_one": "stop after one song\">1️⃣",
|
||||||
|
|
@ -326,7 +327,13 @@ if (1)
|
||||||
"mt_xowa": "there are bugs in iOS preventing background playback using this format; please use caf or mp3 instead",
|
"mt_xowa": "there are bugs in iOS preventing background playback using this format; please use caf or mp3 instead",
|
||||||
"mt_tint": "background level (0-100) on the seekbar$Nto make buffering less distracting",
|
"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_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 (only works in firefox; RLS is max 1 in other browsers)$N$N(see wikipedia, they explain it much better)",
|
"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 (only works in firefox; RLS is max 1 in other browsers)$N$N(see wikipedia, they explain it much better)",
|
||||||
|
"mt_ss": "enables skip silence; multiplies playback speed by <code>sspeed</code>$Nat the start/end of audio tracks when volume is under <code>vthresh</code>$Nand the track is within 0 to <code>sthresh</code>% of the start$Nor <code>100-ethresh</code> to 100% of the end of the track",
|
||||||
|
"mt_ssvt": "skip silence volume threshold (0-255)",
|
||||||
|
"mt_ssts": "skip silence active threshold (% of track, start)",
|
||||||
|
"mt_sste": "skip silence active threshold (% of track, end)",
|
||||||
|
"mt_ssrt": "skip silence volume/speed ramp up/down time",
|
||||||
|
"mt_sssm": "skip silence playback speed multiplier",
|
||||||
|
|
||||||
"mb_play": "play",
|
"mb_play": "play",
|
||||||
"mm_hashplay": "play this audio file?",
|
"mm_hashplay": "play this audio file?",
|
||||||
|
|
@ -1313,6 +1320,7 @@ var mpl = (function () {
|
||||||
|
|
||||||
'<div><h3 id="h_drc">' + L.ml_drc + '</h3><div id="audio_drc"></div></div>' +
|
'<div><h3 id="h_drc">' + L.ml_drc + '</h3><div id="audio_drc"></div></div>' +
|
||||||
'<div><h3>' + L.ml_eq + '</h3><div id="audio_eq"></div></div>' +
|
'<div><h3>' + L.ml_eq + '</h3><div id="audio_eq"></div></div>' +
|
||||||
|
'<div><h3>' + L.ml_ss + '</h3><div id="audio_ss"></div></div>' +
|
||||||
'');
|
'');
|
||||||
|
|
||||||
var r = {
|
var r = {
|
||||||
|
|
@ -2583,6 +2591,8 @@ var mpui = (function () {
|
||||||
// occasionally draw buffered regions
|
// occasionally draw buffered regions
|
||||||
if (nth % 5 == 0)
|
if (nth % 5 == 0)
|
||||||
pbar.drawbuf();
|
pbar.drawbuf();
|
||||||
|
|
||||||
|
if (!IE && afilt.ssen) skipSilence();
|
||||||
}
|
}
|
||||||
|
|
||||||
// preload next song
|
// preload next song
|
||||||
|
|
@ -2690,11 +2700,15 @@ var afilt = (function () {
|
||||||
var r = {
|
var r = {
|
||||||
"eqen": false,
|
"eqen": false,
|
||||||
"drcen": false,
|
"drcen": false,
|
||||||
|
"ssen": false,
|
||||||
"bands": [31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
|
"bands": [31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
|
||||||
"gains": [4, 3, 2, 1, 0, 0, 1, 2, 3, 4],
|
"gains": [4, 3, 2, 1, 0, 0, 1, 2, 3, 4],
|
||||||
"drcv": [-24, 30, 12, 0.01, 0.25],
|
"drcv": [-24, 30, 12, 0.01, 0.25],
|
||||||
"drch": ['tresh', 'knee', 'ratio', 'atk', 'rls'],
|
"drch": ['tresh', 'knee', 'ratio', 'atk', 'rls'],
|
||||||
"drck": ['threshold', 'knee', 'ratio', 'attack', 'release'],
|
"drck": ['threshold', 'knee', 'ratio', 'attack', 'release'],
|
||||||
|
"sscl": ['vthresh', "sthresh", "ethresh", 'sspeed', 'rspeed'],
|
||||||
|
"sstt": [L.mt_ssvt, L.mt_ssts, L.mt_sste, L.mt_ssrt, L.mt_sssm],
|
||||||
|
"sscv": [1, 5, 5, 5.0, 0.2],
|
||||||
"drcn": null,
|
"drcn": null,
|
||||||
"filters": [],
|
"filters": [],
|
||||||
"filterskip": [],
|
"filterskip": [],
|
||||||
|
|
@ -2769,6 +2783,7 @@ var afilt = (function () {
|
||||||
r.gains = gains;
|
r.gains = gains;
|
||||||
|
|
||||||
r.drcv = jread('au_drcv', r.drcv);
|
r.drcv = jread('au_drcv', r.drcv);
|
||||||
|
r.sscv = jread('au_sscv', r.sscv);
|
||||||
}
|
}
|
||||||
catch (ex) { }
|
catch (ex) { }
|
||||||
|
|
||||||
|
|
@ -2998,6 +3013,28 @@ var afilt = (function () {
|
||||||
clmod(this, 'err', err);
|
clmod(this, 'err', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function adj_ss() {
|
||||||
|
var err = false;
|
||||||
|
try {
|
||||||
|
var n = this.getAttribute('k'),
|
||||||
|
ov = r.sscv[n],
|
||||||
|
vs = this.value,
|
||||||
|
v = parseFloat(vs);
|
||||||
|
if (!isNum(v) || v + '' != vs)
|
||||||
|
throw new Error('inval v');
|
||||||
|
|
||||||
|
if (v == ov)
|
||||||
|
return;
|
||||||
|
|
||||||
|
r.sscv[n] = v;
|
||||||
|
jwrite('au_sscv', r.sscv);
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
clmod(this, 'err', err);
|
||||||
|
}
|
||||||
|
|
||||||
function eq_mod(e) {
|
function eq_mod(e) {
|
||||||
ev(e);
|
ev(e);
|
||||||
adj_band(this, 0);
|
adj_band(this, 0);
|
||||||
|
|
@ -3057,6 +3094,19 @@ var afilt = (function () {
|
||||||
html += h2.join('\n') + '</tr><table>';
|
html += h2.join('\n') + '</tr><table>';
|
||||||
ebi('audio_drc').innerHTML = html;
|
ebi('audio_drc').innerHTML = html;
|
||||||
|
|
||||||
|
h2 = [];
|
||||||
|
html = ['<table><tr><td rowspan="2">',
|
||||||
|
'<a id="au_ss" class="tgl btn" href="#" tt="' + L.mt_ss + '">' + L.enable + '</a></td>'];
|
||||||
|
|
||||||
|
for (var a = 0; a < r.sscl.length; a++) {
|
||||||
|
html.push('<td tt="' + r.sstt[a] + '">' + r.sscl[a] + '</td>');
|
||||||
|
h2.push('<td><input type="text" class="ssconf_v" ' + NOAC + ' k="' + a + '" value="' + r.sscv[a] + '" /></td>');
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.join('\n') + '</tr><tr>';
|
||||||
|
html += h2.join('\n') + '</tr><table>';
|
||||||
|
ebi('audio_ss').innerHTML = html;
|
||||||
|
|
||||||
var stp = QSA('a.eq_step');
|
var stp = QSA('a.eq_step');
|
||||||
for (var a = 0, aa = stp.length; a < aa; a++)
|
for (var a = 0, aa = stp.length; a < aa; a++)
|
||||||
stp[a].onclick = eq_step;
|
stp[a].onclick = eq_step;
|
||||||
|
|
@ -3070,8 +3120,13 @@ var afilt = (function () {
|
||||||
for (var a = 0; a < txt.length; a++)
|
for (var a = 0; a < txt.length; a++)
|
||||||
txt[a].oninput = txt[a].onkeydown = adj_drc;
|
txt[a].oninput = txt[a].onkeydown = adj_drc;
|
||||||
|
|
||||||
|
txt = QSA('input.ssconf_v');
|
||||||
|
for (var a = 0; a < txt.length; a++)
|
||||||
|
txt[a].oninput = txt[a].onkeydown = adj_ss;
|
||||||
|
|
||||||
bcfg_bind(r, 'eqen', 'au_eq', false, r.apply);
|
bcfg_bind(r, 'eqen', 'au_eq', false, r.apply);
|
||||||
bcfg_bind(r, 'drcen', 'au_drc', false, r.apply);
|
bcfg_bind(r, 'drcen', 'au_drc', false, r.apply);
|
||||||
|
bcfg_bind(r, 'ssen', 'au_ss', false, r.apply);
|
||||||
|
|
||||||
r.draw();
|
r.draw();
|
||||||
return r;
|
return r;
|
||||||
|
|
@ -9929,6 +9984,113 @@ function reload_browser() {
|
||||||
dsel_init();
|
dsel_init();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
var ssint = null;
|
||||||
|
|
||||||
|
function skipSilence() {
|
||||||
|
var ae = mp.au;
|
||||||
|
var ssconf = afilt.sscv;
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
vthresh: ssconf[0],
|
||||||
|
sthresh: ssconf[1],
|
||||||
|
etresh: ssconf[2],
|
||||||
|
sspeed: ssconf[3],
|
||||||
|
rspeed: ssconf[4],
|
||||||
|
loopInterval: 25
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!ae._ssa) {
|
||||||
|
var ctx = new AudioContext();
|
||||||
|
|
||||||
|
var asrc = ctx.createMediaElementSource(ae);
|
||||||
|
var analyser = ctx.createAnalyser();
|
||||||
|
var gnod = ctx.createGain();
|
||||||
|
|
||||||
|
analyser.fftSize = 256;
|
||||||
|
asrc.connect(analyser);
|
||||||
|
analyser.connect(gnod);
|
||||||
|
gnod.connect(ctx.destination);
|
||||||
|
|
||||||
|
ae._analyser = analyser;
|
||||||
|
ae._gnod = gnod;
|
||||||
|
ae._actx = ctx;
|
||||||
|
ae._ssa = true;
|
||||||
|
|
||||||
|
ae.addEventListener('play', function() {
|
||||||
|
if (ctx.state === 'suspended') ctx.resume();
|
||||||
|
startLoop();
|
||||||
|
});
|
||||||
|
|
||||||
|
ae.addEventListener('pause', stopLoop);
|
||||||
|
ae.addEventListener('ended', stopLoop);
|
||||||
|
|
||||||
|
ae.addEventListener('durationchange', function() {
|
||||||
|
if (ae._gnod) ae._gnod.gain.value = 1.0;
|
||||||
|
ae.playbackRate = 1.0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ae.paused && !ae.ended) {
|
||||||
|
startLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLoop() {
|
||||||
|
if (!ssint) {
|
||||||
|
ssint = setInterval(detectSilence, config.loopInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopLoop() {
|
||||||
|
if (ssint) {
|
||||||
|
clearInterval(ssint);
|
||||||
|
ssint = null;
|
||||||
|
}
|
||||||
|
if(ae._gnod) ae._gnod.gain.value = 1.0;
|
||||||
|
ae.playbackRate = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectSilence() {
|
||||||
|
var duration = ae.duration || 0;
|
||||||
|
|
||||||
|
var slimit = duration * (config.sthresh / 100);
|
||||||
|
var elimit = duration * (1 - (config.etresh / 100));
|
||||||
|
var in_limits = ae.currentTime < slimit || ae.currentTime > elimit;
|
||||||
|
|
||||||
|
var tspeed = 1.0;
|
||||||
|
var tvol = 1.0;
|
||||||
|
var is_silent = false;
|
||||||
|
|
||||||
|
if (in_limits) {
|
||||||
|
var analyser = ae._analyser;
|
||||||
|
var da = new Uint8Array(analyser.frequencyBinCount);
|
||||||
|
analyser.getByteFrequencyData(da);
|
||||||
|
|
||||||
|
var maxvol = 0;
|
||||||
|
for (var i = 0; i < da.length; i++) {
|
||||||
|
if (da[i] > maxvol) maxvol = da[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxvol < config.vthresh) {
|
||||||
|
tspeed = config.sspeed;
|
||||||
|
tvol = 0.0;
|
||||||
|
is_silent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_silent) {
|
||||||
|
if (Math.abs(ae.playbackRate - tspeed) > 0.01) {
|
||||||
|
ae.playbackRate += (tspeed - ae.playbackRate) * config.rspeed;
|
||||||
|
}
|
||||||
|
if (Math.abs(ae._gnod.gain.value - tvol) > 0.01) {
|
||||||
|
ae._gnod.gain.value += (tvol - ae._gnod.gain.value) * config.rspeed;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ae.playbackRate = 1.0;
|
||||||
|
ae._gnod.gain.value = 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
treectl.hydrate();
|
treectl.hydrate();
|
||||||
|
|
||||||
J_BRW = 2;
|
J_BRW = 2;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue