Better Frequency scope and visualisations

This commit is contained in:
2023-11-14 23:20:29 +01:00
parent 15bfd6c5eb
commit b070dd9fff
3 changed files with 71 additions and 27 deletions

View File

@ -123,6 +123,8 @@ export interface OscilloscopeConfig {
fftSize: number; // multiples of 256 fftSize: number; // multiples of 256
orientation: "horizontal" | "vertical"; orientation: "horizontal" | "vertical";
mode: "3D" | "scope" | "freqscope"; mode: "3D" | "scope" | "freqscope";
offsetX: number;
offsetY: number;
size: number; size: number;
} }
@ -142,47 +144,76 @@ export const runOscilloscope = (
let dataArray = new Float32Array(analyzer.frequencyBinCount); let dataArray = new Float32Array(analyzer.frequencyBinCount);
let freqDataArray = new Uint8Array(analyzer.frequencyBinCount); let freqDataArray = new Uint8Array(analyzer.frequencyBinCount);
const canvasCtx = canvas.getContext("2d")!; const canvasCtx = canvas.getContext("2d")!;
const WIDTH = canvas.width;
const HEIGHT = canvas.height;
let lastDrawTime = 0; let lastDrawTime = 0;
let frameInterval = 1000 / 30; let frameInterval = 1000 / 30;
function drawFrequencyScope() { function drawFrequencyScope(
width: number,
height: number,
offset_height: number,
offset_width: number
) {
// Existing setup code...
analyzer.fftSize = app.osc.fftSize * 4;
analyzer.getByteFrequencyData(freqDataArray); analyzer.getByteFrequencyData(freqDataArray);
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT); canvasCtx.clearRect(0, 0, width, height);
// Ensure the number of bars does not exceed the frequency data length const numBars = Math.min(
const numBars = freqDataArray.length; freqDataArray.length,
const barWidth = WIDTH / numBars; app.osc.orientation === "horizontal" ? width : height
);
const barWidth =
app.osc.orientation === "horizontal" ? width / numBars : height / numBars;
let barHeight; let barHeight;
let x = 0; let x = 0,
y = 0;
for (let i = 0; i < numBars; i++) { for (let i = 0; i < numBars; i++) {
// Calculate index in the frequency data array using logarithmic scale
const logIndex = Math.floor(
Math.pow(i / numBars, 2) * freqDataArray.length
);
barHeight = Math.floor( barHeight = Math.floor(
freqDataArray[logIndex] * ((HEIGHT / 256) * app.osc.size) freqDataArray[i] * ((height / 256) * app.osc.size)
); );
// Color configuration // Create gradient based on orientation
if (app.osc.color === "random") { let gradient;
canvasCtx.fillStyle = `hsl(${Math.random() * 360}, 100%, 50%)`; if (app.osc.orientation === "horizontal") {
gradient = canvasCtx.createLinearGradient(0, 0, width / 2, 0);
} else { } else {
const gradient = canvasCtx.createLinearGradient(0, 0, WIDTH / 2, 0); gradient = canvasCtx.createLinearGradient(0, 0, 0, height / 2);
gradient.addColorStop(0, app.osc.color || `rgb(255, 255, 255)`);
gradient.addColorStop(1, `rgb(${barHeight + 50},50,50)`);
canvasCtx.fillStyle = gradient;
} }
gradient.addColorStop(0, app.osc.color || `rgb(255, 255, 255)`);
gradient.addColorStop(1, `rgb(${barHeight + 50},50,50)`);
canvasCtx.fillStyle = gradient;
canvasCtx.fillRect(x, (HEIGHT - barHeight) / 2, barWidth + 1, barHeight); if (app.osc.orientation === "horizontal") {
canvasCtx.fillRect(
x += barWidth; x + offset_width, // Apply horizontal offset here
(height - barHeight) / 2 + offset_height, // Apply vertical offset here
barWidth + 1,
barHeight
);
x += barWidth;
} else {
canvasCtx.fillRect(
(width - barHeight) / 2 + offset_width, // Apply horizontal offset here
y + offset_height, // Apply vertical offset here
barHeight,
barWidth + 1
);
y += barWidth;
}
} }
} }
function draw() { function draw() {
// Update the canvas position on each cycle
const WIDTH = canvas.width;
const HEIGHT = canvas.height;
const OFFSET_WIDTH = app.osc.offsetX;
const OFFSET_HEIGHT = app.osc.offsetY;
// Apply an offset to the canvas!
canvasCtx.setTransform(1, 0, 0, 1, OFFSET_WIDTH, OFFSET_HEIGHT);
const currentTime = Date.now(); const currentTime = Date.now();
requestAnimationFrame(draw); requestAnimationFrame(draw);
if (currentTime - lastDrawTime < frameInterval) { if (currentTime - lastDrawTime < frameInterval) {
@ -191,7 +222,12 @@ export const runOscilloscope = (
lastDrawTime = currentTime; lastDrawTime = currentTime;
if (!app.osc.enabled) { if (!app.osc.enabled) {
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT); canvasCtx.clearRect(
-OFFSET_WIDTH,
-OFFSET_HEIGHT,
WIDTH + 2 * OFFSET_WIDTH,
HEIGHT + 2 * OFFSET_HEIGHT
);
return; return;
} }
@ -206,9 +242,13 @@ export const runOscilloscope = (
canvasCtx.fillStyle = "rgba(0, 0, 0, 0)"; canvasCtx.fillStyle = "rgba(0, 0, 0, 0)";
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT); canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
if (app.clock.time_position.pulse % app.osc.refresh == 0) { if (app.clock.time_position.pulse % app.osc.refresh == 0) {
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT); canvasCtx.clearRect(
-OFFSET_WIDTH,
-OFFSET_HEIGHT,
WIDTH + 2 * OFFSET_WIDTH,
HEIGHT + 2 * OFFSET_HEIGHT
);
} }
canvasCtx.lineWidth = app.osc.thickness; canvasCtx.lineWidth = app.osc.thickness;
if (app.osc.color === "random") { if (app.osc.color === "random") {
@ -246,7 +286,7 @@ export const runOscilloscope = (
} }
if (app.osc.mode === "freqscope") { if (app.osc.mode === "freqscope") {
drawFrequencyScope(); drawFrequencyScope(WIDTH, HEIGHT, OFFSET_HEIGHT, OFFSET_WIDTH);
} else if (app.osc.mode === "3D") { } else if (app.osc.mode === "3D") {
for (let i = startIndex; i < dataArray.length; i += 2) { for (let i = startIndex; i < dataArray.length; i += 2) {
const x = (dataArray[i] * WIDTH * app.osc.size) / 2 + WIDTH / 4; const x = (dataArray[i] * WIDTH * app.osc.size) / 2 + WIDTH / 4;

View File

@ -14,6 +14,8 @@ scope({
enabled: true, // off by default enabled: true, // off by default
color: "#fdba74", // any valid CSS color or "random" color: "#fdba74", // any valid CSS color or "random"
thickness: 4, // stroke thickness thickness: 4, // stroke thickness
offsetY: 0, // Horizontal offset
offsetX: 0, // Vertical offset
fftSize: 256, // multiples of 128 fftSize: 256, // multiples of 128
orientation: "horizontal", // "vertical" or "horizontal" orientation: "horizontal", // "vertical" or "horizontal"
mode: "scope" | "3D" | "freqscope", // scope mode mode: "scope" | "3D" | "freqscope", // scope mode

View File

@ -76,6 +76,8 @@ export class Editor {
refresh: 1, refresh: 1,
fftSize: 1024, fftSize: 1024,
orientation: "horizontal", orientation: "horizontal",
offsetX: 0,
offsetY: 0,
mode: "scope", mode: "scope",
size: 1, size: 1,
}; };