adding script vis
This commit is contained in:
@ -22,6 +22,11 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fluid-bg-transition {
|
||||||
|
transition: background-color 0.05s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#hydra-bg {
|
#hydra-bg {
|
||||||
position: fixed; /* ignore margins */
|
position: fixed; /* ignore margins */
|
||||||
top: 0px;
|
top: 0px;
|
||||||
|
|||||||
12
src/API.ts
12
src/API.ts
@ -26,6 +26,7 @@ import {
|
|||||||
} from "superdough";
|
} from "superdough";
|
||||||
import { Speaker } from "./StringExtensions";
|
import { Speaker } from "./StringExtensions";
|
||||||
import { getScaleNotes } from "zifferjs";
|
import { getScaleNotes } from "zifferjs";
|
||||||
|
import { blinkScript } from "./AudioVisualisation";
|
||||||
|
|
||||||
interface ControlChange {
|
interface ControlChange {
|
||||||
channel: number;
|
channel: number;
|
||||||
@ -269,10 +270,13 @@ export class UserAPI {
|
|||||||
* @returns The result of the evaluation
|
* @returns The result of the evaluation
|
||||||
*/
|
*/
|
||||||
args.forEach((arg) => {
|
args.forEach((arg) => {
|
||||||
tryEvaluate(
|
if (arg >= 1 && arg <= 9) {
|
||||||
this.app,
|
blinkScript(this.app, "local", arg);
|
||||||
this.app.universes[this.app.selected_universe].locals[arg]
|
tryEvaluate(
|
||||||
);
|
this.app,
|
||||||
|
this.app.universes[this.app.selected_universe].locals[arg]
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
s = this.script;
|
s = this.script;
|
||||||
|
|||||||
@ -1,2 +1,115 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { analyser, getAnalyzerData } from "superdough";
|
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";
|
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 = (
|
export const installWindowBehaviors = (
|
||||||
app: Editor,
|
app: Editor,
|
||||||
window: Window,
|
window: Window,
|
||||||
preventMultipleTabs: boolean = false
|
preventMultipleTabs: boolean = false
|
||||||
) => {
|
) => {
|
||||||
|
window.addEventListener("resize", () => handleResize(app));
|
||||||
window.addEventListener("beforeunload", () => {
|
window.addEventListener("beforeunload", () => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
46
src/main.ts
46
src/main.ts
@ -24,6 +24,7 @@ import showdown from "showdown";
|
|||||||
import { makeStringExtensions } from "./StringExtensions";
|
import { makeStringExtensions } from "./StringExtensions";
|
||||||
import { installInterfaceLogic } from "./InterfaceLogic";
|
import { installInterfaceLogic } from "./InterfaceLogic";
|
||||||
import { installWindowBehaviors } from "./WindowBehavior";
|
import { installWindowBehaviors } from "./WindowBehavior";
|
||||||
|
import { drawEmptyBlinkers } from "./AudioVisualisation";
|
||||||
|
|
||||||
export class Editor {
|
export class Editor {
|
||||||
// Universes and settings
|
// Universes and settings
|
||||||
@ -56,6 +57,7 @@ export class Editor {
|
|||||||
show_error: boolean = false;
|
show_error: boolean = false;
|
||||||
buttonElements: Record<string, HTMLButtonElement[]> = {};
|
buttonElements: Record<string, HTMLButtonElement[]> = {};
|
||||||
interface: ElementMap = {};
|
interface: ElementMap = {};
|
||||||
|
blinkTimeouts: Record<number, number> = {};
|
||||||
|
|
||||||
// UserAPI
|
// UserAPI
|
||||||
api: UserAPI;
|
api: UserAPI;
|
||||||
@ -79,6 +81,7 @@ export class Editor {
|
|||||||
this.initializeElements();
|
this.initializeElements();
|
||||||
this.initializeButtonGroups();
|
this.initializeButtonGroups();
|
||||||
this.initializeHydra();
|
this.initializeHydra();
|
||||||
|
this.setCanvas();
|
||||||
|
|
||||||
// ================================================================================
|
// ================================================================================
|
||||||
// Loading the universe from local storage
|
// Loading the universe from local storage
|
||||||
@ -127,6 +130,7 @@ export class Editor {
|
|||||||
registerFillKeys(this);
|
registerFillKeys(this);
|
||||||
registerOnKeyDown(this);
|
registerOnKeyDown(this);
|
||||||
installInterfaceLogic(this);
|
installInterfaceLogic(this);
|
||||||
|
drawEmptyBlinkers(this);
|
||||||
|
|
||||||
// ================================================================================
|
// ================================================================================
|
||||||
// Building CodeMirror Editor
|
// Building CodeMirror Editor
|
||||||
@ -381,25 +385,36 @@ export class Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param color the color to flash the background
|
* Flashes the background of the view and its gutters.
|
||||||
* @param duration the duration of the flash
|
* @param {string} color - The color to set.
|
||||||
|
* @param {number} duration - Duration in milliseconds to maintain the color.
|
||||||
*/
|
*/
|
||||||
flashBackground(color: string, duration: number): void {
|
flashBackground(color: string, duration: number): void {
|
||||||
// Set the flashing color
|
const domElement = this.view.dom;
|
||||||
this.view.dom.style.backgroundColor = color;
|
const gutters = domElement.getElementsByClassName(
|
||||||
const gutters = this.view.dom.getElementsByClassName(
|
|
||||||
"cm-gutter"
|
"cm-gutter"
|
||||||
) as HTMLCollectionOf<HTMLElement>;
|
) 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(
|
Array.from(gutters).forEach(
|
||||||
(gutter) => (gutter.style.backgroundColor = color)
|
(gutter) => (gutter.style.backgroundColor = color)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reset to original color after duration
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.view.dom.style.backgroundColor = "";
|
domElement.style.backgroundColor = "";
|
||||||
Array.from(gutters).forEach(
|
Array.from(gutters).forEach(
|
||||||
(gutter) => (gutter.style.backgroundColor = "")
|
(gutter) => (gutter.style.backgroundColor = "")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
domElement.classList.remove("fluid-bg-transition");
|
||||||
|
Array.from(gutters).forEach((gutter) =>
|
||||||
|
gutter.classList.remove("fluid-bg-transition")
|
||||||
|
);
|
||||||
}, duration);
|
}, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,6 +443,23 @@ export class Editor {
|
|||||||
});
|
});
|
||||||
this.hydra = this.hydra_backend.synth;
|
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();
|
let app = new Editor();
|
||||||
|
|||||||
Reference in New Issue
Block a user