mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
audio-eq: flatten frequency response
This commit is contained in:
parent
cd52dea488
commit
cf2d6650ac
|
@ -512,26 +512,35 @@ 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],
|
||||
"gains": [4, 3, 2, 1, 0, 0, 1, 2, 3, 4],
|
||||
"filters": [],
|
||||
"last_au": null
|
||||
};
|
||||
|
||||
var cfg = [ // hz, q, g
|
||||
[31.25 * 0.88, 0, 1.4], // shelf
|
||||
[31.25 * 1.04, 0.7, 0.96], // peak
|
||||
[62.5, 0.7, 1],
|
||||
[125, 0.8, 1],
|
||||
[250, 0.9, 1.03],
|
||||
[500, 0.9, 1.1],
|
||||
[1000, 0.9, 1.1],
|
||||
[2000, 0.9, 1.105],
|
||||
[4000, 0.88, 1.05],
|
||||
[8000 * 1.006, 0.73, 1.24],
|
||||
[16000 * 0.89, 0.7, 1.26], // peak
|
||||
[16000 * 1.13, 0.82, 1.09], // peak
|
||||
[16000 * 1.205, 0, 1.9] // shelf
|
||||
];
|
||||
|
||||
try {
|
||||
r.gains = jread('au_eq_gain', r.gains);
|
||||
var gains = jread('au_eq_gain', r.gains);
|
||||
if (r.gains.length == gains.length)
|
||||
r.gains = 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');
|
||||
|
@ -568,21 +577,36 @@ var audio_eq = (function () {
|
|||
mp.acs = mp.ac.createMediaElementSource(mp.au);
|
||||
}
|
||||
|
||||
r.filters = [];
|
||||
|
||||
if (!r.en) {
|
||||
mp.acs.connect(mp.ac.destination);
|
||||
return;
|
||||
}
|
||||
|
||||
r.filters = [];
|
||||
for (var a = 0; a < r.bands.length; a++) {
|
||||
var max = 0;
|
||||
for (var a = 0; a < r.gains.length; a++)
|
||||
if (max < r.gains[a])
|
||||
max = r.gains[a];
|
||||
|
||||
var gains = []
|
||||
for (var a = 0; a < r.gains.length; a++)
|
||||
gains.push(r.gains[a] - max);
|
||||
|
||||
var t = gains[gains.length - 1];
|
||||
gains.push(t);
|
||||
gains.push(t);
|
||||
gains.unshift(gains[0]);
|
||||
|
||||
for (var a = 0; a < cfg.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';
|
||||
fi.frequency.value = cfg[a][0];
|
||||
fi.gain.value = cfg[a][2] * gains[a];
|
||||
fi.Q.value = cfg[a][1];
|
||||
fi.type = a == 0 ? 'lowshelf' : a == cfg.length - 1 ? 'highshelf' : 'peaking';
|
||||
r.filters.push(fi);
|
||||
}
|
||||
for (var a = r.bands.length - 1; a >= 0; a--) {
|
||||
for (var a = r.filters.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]);
|
||||
|
|
61
copyparty/web/dbg-audio.js
Normal file
61
copyparty/web/dbg-audio.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
var ofun = audio_eq.apply.bind(audio_eq);
|
||||
audio_eq.apply = function () {
|
||||
var ac1 = mp.ac;
|
||||
ofun();
|
||||
var ac = mp.ac,
|
||||
w = 2048,
|
||||
h = 256;
|
||||
|
||||
if (!audio_eq.filters.length) {
|
||||
audio_eq.ana = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var can = ebi('fft_can');
|
||||
if (!can) {
|
||||
can = mknod('canvas');
|
||||
can.setAttribute('id', 'fft_can');
|
||||
can.style.cssText = 'position:absolute;left:0;bottom:5em;width:' + w + 'px;height:' + h + 'px;z-index:9001';
|
||||
document.body.appendChild(can);
|
||||
can.width = w;
|
||||
can.height = h;
|
||||
}
|
||||
var cc = can.getContext('2d');
|
||||
if (!ac)
|
||||
return;
|
||||
|
||||
var ana = ac.createAnalyser();
|
||||
ana.smoothingTimeConstant = 0;
|
||||
ana.fftSize = 8192;
|
||||
|
||||
audio_eq.filters[0].connect(ana);
|
||||
audio_eq.ana = ana;
|
||||
|
||||
var buf = new Uint8Array(ana.frequencyBinCount),
|
||||
colw = can.width / buf.length;
|
||||
|
||||
cc.fillStyle = '#fc0';
|
||||
function draw() {
|
||||
if (ana == audio_eq.ana)
|
||||
requestAnimationFrame(draw);
|
||||
|
||||
ana.getByteFrequencyData(buf);
|
||||
|
||||
cc.clearRect(0, 0, can.width, can.height);
|
||||
|
||||
/*var x = 0, w = 1;
|
||||
for (var a = 0; a < buf.length; a++) {
|
||||
cc.fillRect(x, h - buf[a], w, h);
|
||||
x += w;
|
||||
}*/
|
||||
var mul = Math.pow(w, 4) / buf.length;
|
||||
for (var x = 0; x < w; x++) {
|
||||
var a = Math.floor(Math.pow(x, 4) / mul),
|
||||
v = buf[a];
|
||||
|
||||
cc.fillRect(x, h - v, 1, v);
|
||||
}
|
||||
}
|
||||
draw();
|
||||
};
|
||||
audio_eq.apply();
|
95
docs/biquad.html
Normal file
95
docs/biquad.html
Normal file
|
@ -0,0 +1,95 @@
|
|||
<!DOCTYPE html><html><head></head><body><script>
|
||||
|
||||
setTimeout(location.reload.bind(location), 700);
|
||||
document.documentElement.scrollLeft = 0;
|
||||
|
||||
var can = document.createElement('canvas'),
|
||||
cc = can.getContext('2d'),
|
||||
w = 2048,
|
||||
h = 1024;
|
||||
|
||||
w = 2048;
|
||||
|
||||
can.width = w;
|
||||
can.height = h;
|
||||
document.body.appendChild(can);
|
||||
can.style.cssText = 'width:' + w + 'px;height:' + h + 'px';
|
||||
|
||||
cc.fillStyle = '#000';
|
||||
cc.fillRect(0, 0, w, h);
|
||||
|
||||
var cfg = [ // hz, q, g
|
||||
[31.25 * 0.88, 0, 1.4], // shelf
|
||||
[31.25 * 1.04, 0.7, 0.96], // peak
|
||||
[62.5, 0.7, 1],
|
||||
[125, 0.8, 1],
|
||||
[250, 0.9, 1.03],
|
||||
[500, 0.9, 1.1],
|
||||
[1000, 0.9, 1.1],
|
||||
[2000, 0.9, 1.105],
|
||||
[4000, 0.88, 1.05],
|
||||
[8000 * 1.006, 0.73, 1.24],
|
||||
//[16000 * 1.00, 0.5, 1.75], // peak.v1
|
||||
//[16000 * 1.19, 0, 1.8] // shelf.v1
|
||||
[16000 * 0.89, 0.7, 1.26], // peak
|
||||
[16000 * 1.13, 0.82, 1.09], // peak
|
||||
[16000 * 1.205, 0, 1.9] // shelf
|
||||
];
|
||||
|
||||
var freqs = new Float32Array(22000),
|
||||
sum = new Float32Array(freqs.length),
|
||||
ac = new AudioContext(),
|
||||
step = w / freqs.length,
|
||||
colors = [
|
||||
'rgba(255, 0, 0, 0.7)',
|
||||
'rgba(0, 224, 0, 0.7)',
|
||||
'rgba(0, 64, 255, 0.7)'
|
||||
];
|
||||
|
||||
var order = [];
|
||||
|
||||
for (var a = 0; a < cfg.length; a += 2)
|
||||
order.push(a);
|
||||
|
||||
for (var a = 1; a < cfg.length; a += 2)
|
||||
order.push(a);
|
||||
|
||||
for (var ia = 0; ia < order.length; ia++) {
|
||||
var a = order[ia],
|
||||
fi = ac.createBiquadFilter(),
|
||||
mag = new Float32Array(freqs.length),
|
||||
phase = new Float32Array(freqs.length);
|
||||
|
||||
for (var b = 0; b < freqs.length; b++)
|
||||
freqs[b] = b;
|
||||
|
||||
fi.type = a == 0 ? 'lowshelf' : a == cfg.length - 1 ? 'highshelf' : 'peaking';
|
||||
fi.frequency.value = cfg[a][0];
|
||||
fi.Q.value = cfg[a][1];
|
||||
fi.gain.value = 1;
|
||||
|
||||
fi.getFrequencyResponse(freqs, mag, phase);
|
||||
cc.fillStyle = colors[a % colors.length];
|
||||
for (var b = 0; b < sum.length; b++) {
|
||||
mag[b] -= 1;
|
||||
sum[b] += mag[b] * cfg[a][2];
|
||||
var y = h - (mag[b] * h * 3);
|
||||
cc.fillRect(b * step, y, step, h - y);
|
||||
cc.fillRect(b * step - 1, y - 1, 3, 3);
|
||||
}
|
||||
}
|
||||
|
||||
var min = 999999, max = 0;
|
||||
for (var a = 0; a < sum.length; a++) {
|
||||
min = Math.min(min, sum[a]);
|
||||
max = Math.max(max, sum[a]);
|
||||
}
|
||||
cc.fillStyle = 'rgba(255,255,255,1)';
|
||||
for (var a = 0; a < sum.length; a++) {
|
||||
var v = (sum[a] - min) / (max - min);
|
||||
cc.fillRect(a * step, 0, step, v * h / 2);
|
||||
}
|
||||
|
||||
cc.fillRect(0, 460, w, 1);
|
||||
|
||||
</script></body></html>
|
|
@ -167,7 +167,7 @@ find .. -type f \( -name .DS_Store -or -name ._.DS_Store \) -delete
|
|||
find .. -type f -name ._\* | while IFS= read -r f; do cmp <(printf '\x00\x05\x16') <(head -c 3 -- "$f") && rm -f -- "$f"; done
|
||||
|
||||
echo use smol web deps
|
||||
rm -f copyparty/web/deps/*.full.* copyparty/web/Makefile
|
||||
rm -f copyparty/web/deps/*.full.* copyparty/web/dbg-* copyparty/web/Makefile
|
||||
|
||||
# it's fine dw
|
||||
grep -lE '\.full\.(js|css)' copyparty/web/* |
|
||||
|
|
Loading…
Reference in a new issue