adding script vis
This commit is contained in:
@ -22,6 +22,11 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.fluid-bg-transition {
|
||||
transition: background-color 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
#hydra-bg {
|
||||
position: fixed; /* ignore margins */
|
||||
top: 0px;
|
||||
|
||||
12
src/API.ts
12
src/API.ts
@ -26,6 +26,7 @@ import {
|
||||
} from "superdough";
|
||||
import { Speaker } from "./StringExtensions";
|
||||
import { getScaleNotes } from "zifferjs";
|
||||
import { blinkScript } from "./AudioVisualisation";
|
||||
|
||||
interface ControlChange {
|
||||
channel: number;
|
||||
@ -269,10 +270,13 @@ export class UserAPI {
|
||||
* @returns The result of the evaluation
|
||||
*/
|
||||
args.forEach((arg) => {
|
||||
tryEvaluate(
|
||||
this.app,
|
||||
this.app.universes[this.app.selected_universe].locals[arg]
|
||||
);
|
||||
if (arg >= 1 && arg <= 9) {
|
||||
blinkScript(this.app, "local", arg);
|
||||
tryEvaluate(
|
||||
this.app,
|
||||
this.app.universes[this.app.selected_universe].locals[arg]
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
s = this.script;
|
||||
|
||||
@ -1,2 +1,115 @@
|
||||
// @ts-ignore
|
||||
import { analyser, getAnalyzerData } from "superdough";
|
||||
import { type Editor } from "./main";
|
||||
|
||||
/**
|
||||
* Draw a circle at a specific position on the canvas.
|
||||
* @param {number} x - The x-coordinate of the circle's center.
|
||||
* @param {number} y - The y-coordinate of the circle's center.
|
||||
* @param {number} radius - The radius of the circle.
|
||||
* @param {string} color - The fill color of the circle.
|
||||
*/
|
||||
export const drawCircle = (
|
||||
app: Editor,
|
||||
x: number,
|
||||
y: number,
|
||||
radius: number,
|
||||
color: string
|
||||
): void => {
|
||||
// @ts-ignore
|
||||
const canvas: HTMLCanvasElement = app.interface.feedback;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
};
|
||||
|
||||
/**
|
||||
* Blinks a script indicator circle.
|
||||
* @param script - The type of script.
|
||||
* @param no - The shift amount multiplier.
|
||||
*/
|
||||
export const blinkScript = (
|
||||
app: Editor,
|
||||
script: "local" | "global" | "init",
|
||||
no?: number
|
||||
) => {
|
||||
if (no !== undefined && no < 1 && no > 9) return;
|
||||
const blinkDuration =
|
||||
(app.clock.bpm / 60 / app.clock.time_signature[1]) * 200;
|
||||
// @ts-ignore
|
||||
const ctx = app.interface.feedback.getContext("2d"); // Assuming a canvas context
|
||||
|
||||
/**
|
||||
* Draws a circle at a given shift.
|
||||
* @param shift - The pixel distance from the origin.
|
||||
*/
|
||||
const _drawBlinker = (shift: number) => {
|
||||
const horizontalOffset = 50;
|
||||
drawCircle(
|
||||
app,
|
||||
horizontalOffset + shift,
|
||||
app.interface.feedback.clientHeight - 15,
|
||||
8,
|
||||
"#fdba74"
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the circle at a given shift.
|
||||
* @param shift - The pixel distance from the origin.
|
||||
*/
|
||||
const _clearBlinker = (shift: number) => {
|
||||
const x = 50 + shift;
|
||||
const y = app.interface.feedback.clientHeight - 15;
|
||||
const radius = 8;
|
||||
ctx.clearRect(x - radius, y - radius, radius * 2, radius * 2);
|
||||
};
|
||||
|
||||
if (script === "local" && no !== undefined) {
|
||||
const shiftAmount = no * 25;
|
||||
|
||||
// Clear existing timeout if any
|
||||
if (app.blinkTimeouts[shiftAmount]) {
|
||||
clearTimeout(app.blinkTimeouts[shiftAmount]);
|
||||
}
|
||||
|
||||
_drawBlinker(shiftAmount);
|
||||
|
||||
// Save timeout ID for later clearing
|
||||
app.blinkTimeouts[shiftAmount] = setTimeout(() => {
|
||||
_clearBlinker(shiftAmount);
|
||||
// Clear the canvas before drawing new blinkers
|
||||
(app.interface.feedback as HTMLCanvasElement)
|
||||
.getContext("2d")!
|
||||
.clearRect(
|
||||
0,
|
||||
0,
|
||||
(app.interface.feedback as HTMLCanvasElement).width,
|
||||
(app.interface.feedback as HTMLCanvasElement).height
|
||||
);
|
||||
drawEmptyBlinkers(app);
|
||||
}, blinkDuration);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws a series of 9 white circles.
|
||||
* @param app - The Editor application context.
|
||||
*/
|
||||
export const drawEmptyBlinkers = (app: Editor) => {
|
||||
for (let no = 1; no <= 9; no++) {
|
||||
const shiftAmount = no * 25;
|
||||
drawCircle(
|
||||
app,
|
||||
50 + shiftAmount,
|
||||
app.interface.feedback.clientHeight - 15,
|
||||
8,
|
||||
"white"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,10 +1,28 @@
|
||||
import { type Editor } from "./main";
|
||||
|
||||
const handleResize = (app: Editor) => {
|
||||
const canvas = app.interface.feedback as HTMLCanvasElement | null; // add type guard
|
||||
if (!canvas) return;
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
const ctx = canvas.getContext("2d");
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
|
||||
// Assuming the canvas takes up the whole window
|
||||
canvas.width = window.innerWidth * dpr;
|
||||
canvas.height = window.innerHeight * dpr;
|
||||
|
||||
if (ctx) {
|
||||
ctx.scale(dpr, dpr);
|
||||
}
|
||||
};
|
||||
|
||||
export const installWindowBehaviors = (
|
||||
app: Editor,
|
||||
window: Window,
|
||||
preventMultipleTabs: boolean = false
|
||||
) => {
|
||||
window.addEventListener("resize", () => handleResize(app));
|
||||
window.addEventListener("beforeunload", () => {
|
||||
// @ts-ignore
|
||||
event.preventDefault();
|
||||
|
||||
46
src/main.ts
46
src/main.ts
@ -24,6 +24,7 @@ import showdown from "showdown";
|
||||
import { makeStringExtensions } from "./StringExtensions";
|
||||
import { installInterfaceLogic } from "./InterfaceLogic";
|
||||
import { installWindowBehaviors } from "./WindowBehavior";
|
||||
import { drawEmptyBlinkers } from "./AudioVisualisation";
|
||||
|
||||
export class Editor {
|
||||
// Universes and settings
|
||||
@ -56,6 +57,7 @@ export class Editor {
|
||||
show_error: boolean = false;
|
||||
buttonElements: Record<string, HTMLButtonElement[]> = {};
|
||||
interface: ElementMap = {};
|
||||
blinkTimeouts: Record<number, number> = {};
|
||||
|
||||
// UserAPI
|
||||
api: UserAPI;
|
||||
@ -79,6 +81,7 @@ export class Editor {
|
||||
this.initializeElements();
|
||||
this.initializeButtonGroups();
|
||||
this.initializeHydra();
|
||||
this.setCanvas();
|
||||
|
||||
// ================================================================================
|
||||
// Loading the universe from local storage
|
||||
@ -127,6 +130,7 @@ export class Editor {
|
||||
registerFillKeys(this);
|
||||
registerOnKeyDown(this);
|
||||
installInterfaceLogic(this);
|
||||
drawEmptyBlinkers(this);
|
||||
|
||||
// ================================================================================
|
||||
// Building CodeMirror Editor
|
||||
@ -381,25 +385,36 @@ export class Editor {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param color the color to flash the background
|
||||
* @param duration the duration of the flash
|
||||
* Flashes the background of the view and its gutters.
|
||||
* @param {string} color - The color to set.
|
||||
* @param {number} duration - Duration in milliseconds to maintain the color.
|
||||
*/
|
||||
flashBackground(color: string, duration: number): void {
|
||||
// Set the flashing color
|
||||
this.view.dom.style.backgroundColor = color;
|
||||
const gutters = this.view.dom.getElementsByClassName(
|
||||
const domElement = this.view.dom;
|
||||
const gutters = domElement.getElementsByClassName(
|
||||
"cm-gutter"
|
||||
) as HTMLCollectionOf<HTMLElement>;
|
||||
|
||||
domElement.classList.add("fluid-bg-transition");
|
||||
Array.from(gutters).forEach((gutter) =>
|
||||
gutter.classList.add("fluid-bg-transition")
|
||||
);
|
||||
|
||||
domElement.style.backgroundColor = color;
|
||||
Array.from(gutters).forEach(
|
||||
(gutter) => (gutter.style.backgroundColor = color)
|
||||
);
|
||||
|
||||
// Reset to original color after duration
|
||||
setTimeout(() => {
|
||||
this.view.dom.style.backgroundColor = "";
|
||||
domElement.style.backgroundColor = "";
|
||||
Array.from(gutters).forEach(
|
||||
(gutter) => (gutter.style.backgroundColor = "")
|
||||
);
|
||||
|
||||
domElement.classList.remove("fluid-bg-transition");
|
||||
Array.from(gutters).forEach((gutter) =>
|
||||
gutter.classList.remove("fluid-bg-transition")
|
||||
);
|
||||
}, duration);
|
||||
}
|
||||
|
||||
@ -428,6 +443,23 @@ export class Editor {
|
||||
});
|
||||
this.hydra = this.hydra_backend.synth;
|
||||
}
|
||||
|
||||
private setCanvas(): void {
|
||||
const canvas = this.interface.feedback as HTMLCanvasElement | null; // add type guard
|
||||
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
|
||||
// Assuming the canvas takes up the whole window
|
||||
canvas.width = window.innerWidth * dpr;
|
||||
canvas.height = window.innerHeight * dpr;
|
||||
|
||||
if (ctx) {
|
||||
ctx.scale(dpr, dpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let app = new Editor();
|
||||
|
||||
Reference in New Issue
Block a user