mirror of
https://github.com/meowarex/TidaLuna-Plugins.git
synced 2026-06-17 19:33:10 +10:00
106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
import type { AudioData } from "../audio";
|
|
import type { Visualizer } from "./types";
|
|
import { createProgram, drawQuad, setUniform1f, setUniform1fv, setUniform2f, setUniform3f, hexToRGB } from "../webgl";
|
|
import { settings } from "../Settings";
|
|
|
|
const BIN_COUNT = 256;
|
|
|
|
const FRAG = `#version 300 es
|
|
precision highp float;
|
|
|
|
uniform vec2 u_resolution;
|
|
uniform float u_amplitudes[${BIN_COUNT}];
|
|
uniform vec3 u_color;
|
|
uniform float u_fill_opacity;
|
|
uniform float u_line_thickness;
|
|
uniform float u_opacity_falloff;
|
|
|
|
out vec4 fragColor;
|
|
|
|
float interpolate(float a, float b, float t) {
|
|
return (1.0 - t) * a + t * b;
|
|
}
|
|
|
|
void main() {
|
|
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
int idx = int(uv.x * float(${BIN_COUNT}));
|
|
int idxL = int((uv.x - 1.0 / u_resolution.x) * float(${BIN_COUNT}));
|
|
int idxR = int((uv.x + 1.0 / u_resolution.x) * float(${BIN_COUNT}));
|
|
idx = clamp(idx, 0, ${BIN_COUNT - 1});
|
|
idxL = clamp(idxL, 0, ${BIN_COUNT - 1});
|
|
idxR = clamp(idxR, 0, ${BIN_COUNT - 1});
|
|
|
|
float amplitude = u_amplitudes[idx];
|
|
float left = u_amplitudes[idxL];
|
|
float right = u_amplitudes[idxR];
|
|
float lowest = min(left, right);
|
|
float dist = (amplitude - uv.y) * u_resolution.y;
|
|
|
|
float a = 0.0;
|
|
a += float(abs(dist) <= u_resolution.x * 0.005 * u_line_thickness || (uv.y >= lowest && uv.y <= amplitude)) * clamp(sign(dist), 0.0, 1.0);
|
|
a += clamp(sign(amplitude - uv.y), 0.0, 1.0) * interpolate(1.0, u_fill_opacity, pow(1.0 - uv.y, 1.0 - u_opacity_falloff));
|
|
a = clamp(a, 0.0, 1.0);
|
|
fragColor = vec4(u_color * a, a);
|
|
}
|
|
`;
|
|
|
|
const amplitudes = new Float32Array(BIN_COUNT);
|
|
|
|
export const createSpectrumLine = (): Visualizer => {
|
|
let gl: WebGL2RenderingContext | null = null;
|
|
let program: WebGLProgram | null = null;
|
|
let w = 0, h = 0;
|
|
|
|
return {
|
|
name: "Spectrum (Line)",
|
|
id: "spectrum-line",
|
|
|
|
init(canvas, _color) {
|
|
gl = canvas.getContext("webgl2", { alpha: true, premultipliedAlpha: true })!;
|
|
if (!gl) throw new Error("WebGL2 not available");
|
|
program = createProgram(gl, FRAG);
|
|
w = canvas.width;
|
|
h = canvas.height;
|
|
gl.viewport(0, 0, w, h);
|
|
gl.enable(gl.BLEND);
|
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
},
|
|
|
|
render(data: AudioData, color: string) {
|
|
if (!gl || !program) return;
|
|
const gain = settings.gain ?? 1.5;
|
|
const binStep = data.byteFrequency.length / BIN_COUNT;
|
|
for (let i = 0; i < BIN_COUNT; i++) {
|
|
amplitudes[i] = Math.min(1, (data.byteFrequency[Math.floor(i * binStep)] / 255) * gain);
|
|
}
|
|
|
|
gl.viewport(0, 0, w, h);
|
|
gl.clearColor(0, 0, 0, 0);
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
gl.useProgram(program);
|
|
|
|
setUniform2f(gl, program, "u_resolution", w, h);
|
|
setUniform1fv(gl, program, "u_amplitudes", amplitudes);
|
|
const [r, g, b] = hexToRGB(color);
|
|
setUniform3f(gl, program, "u_color", r, g, b);
|
|
setUniform1f(gl, program, "u_fill_opacity", settings.fillOpacity ?? 0.3);
|
|
setUniform1f(gl, program, "u_line_thickness", settings.lineThickness ?? 1.5);
|
|
setUniform1f(gl, program, "u_opacity_falloff", settings.opacityFalloff ?? 0.5);
|
|
|
|
drawQuad(gl, program);
|
|
},
|
|
|
|
resize(width, height) {
|
|
w = width;
|
|
h = height;
|
|
if (gl) gl.viewport(0, 0, w, h);
|
|
},
|
|
|
|
dispose() {
|
|
if (gl && program) gl.deleteProgram(program);
|
|
program = null;
|
|
gl = null;
|
|
},
|
|
};
|
|
};
|