diff --git a/src/API/API.ts b/src/API/API.ts index 4570934..6d322e5 100644 --- a/src/API/API.ts +++ b/src/API/API.ts @@ -122,6 +122,7 @@ export class UserAPI { getThemes: () => string[]; pulseLocation: () => number; clear: () => boolean; + loadHydra: () => void; w: () => number; h: () => number; hc: () => number; @@ -294,6 +295,7 @@ export class UserAPI { this.nextTheme = Theme.nextTheme(this.app); this.getThemes = Theme.getThemes(); this.pulseLocation = Canvas.pulseLocation(this.app); + this.loadHydra = Canvas.loadHydra(this.app); this.clear = Canvas.clear(this.app); this.w = Canvas.w(this.app); this.h = Canvas.h(this.app); diff --git a/src/API/Canvas.ts b/src/API/Canvas.ts index 167178d..e012541 100644 --- a/src/API/Canvas.ts +++ b/src/API/Canvas.ts @@ -2,6 +2,11 @@ import { OscilloscopeConfig } from "../DOM/Visuals/Oscilloscope"; import { ShapeObject, createConicGradient, createLinearGradient, createRadialGradient, drawBackground, drawBox, drawBall, drawBalloid, drawDonut, drawEquilateral, drawImage, drawPie, drawSmiley, drawStar, drawStroke, drawText, drawTriangular } from "../DOM/Visuals/CanvasVisuals"; import { Editor } from "../main"; + +export const loadHydra = (app: Editor) => (): void => { + app.ensureHydraLoaded() +} + export const w = (app: Editor) => (): number => { const canvas: HTMLCanvasElement = app.interface.feedback as HTMLCanvasElement; return canvas.clientWidth; diff --git a/src/Docs/more/visualization.ts b/src/Docs/more/visualization.ts index ecd1148..a91b14c 100644 --- a/src/Docs/more/visualization.ts +++ b/src/Docs/more/visualization.ts @@ -19,8 +19,11 @@ While Topos is mainly being developed as a live coding environment for algorithm ${makeExample( "Hydra integration", - `beat(4) :: hydra.osc(3, 0.5, 2).out()`, - false, + ` +loadHydra() // Load Hydra first! +beat(4) :: hydra.osc(3, 0.5, 2).out() +`, + true, )} Close the documentation to see the effect: ${key_shortcut( @@ -34,7 +37,6 @@ Stopping **Hydra** is simple: ${makeExample( "Stopping Hydra", ` -beat(4) :: stop_hydra() // this one beat(4) :: hydra.hush() // or this one `, false, diff --git a/src/IO/OSC.ts b/src/IO/OSC.ts index b4f8972..dfcb1f8 100644 --- a/src/IO/OSC.ts +++ b/src/IO/OSC.ts @@ -9,6 +9,17 @@ export interface OSCMessage { export let outputSocket = new WebSocket("ws://localhost:3000"); export let inputSocket = new WebSocket("ws://localhost:3001"); + +outputSocket.onerror = (error: Event) => { + console.log("[Topos] Failed to connect to OSC daemon:", error.type); + console.log("[Topos] Note: the daemon must be started before Topos"); +}; + +inputSocket.onerror = (error: Event) => { + console.log("[Topos] Failed to connect to OSC daemon:", error.type); + console.log("[Topos] Note: the daemon must be started before Topos"); +}; + export let oscMessages: any[] = []; inputSocket.addEventListener("message", (event) => { let data = JSON.parse(event.data); @@ -19,7 +30,7 @@ inputSocket.addEventListener("message", (event) => { }); // @ts-ignore -outputSocket.onopen = function (event) { +outputSocket.onopen = function(event) { console.log("Connected to WebSocket Server"); // Send an OSC-like message outputSocket.send( @@ -30,11 +41,11 @@ outputSocket.onopen = function (event) { }), ); - outputSocket.onerror = function (error) { + outputSocket.onerror = function(error) { console.log("Websocket Error:", error); }; - outputSocket.onmessage = function (event) { + outputSocket.onmessage = function(event) { console.log("Received: ", event.data); }; }; diff --git a/src/clock/ClockProcessor.js b/src/clock/ClockProcessor.js index 14ea51c..e4ea681 100644 --- a/src/clock/ClockProcessor.js +++ b/src/clock/ClockProcessor.js @@ -42,6 +42,7 @@ class TransportProcessor extends AudioWorkletProcessor { } return true; } + } registerProcessor("transport", TransportProcessor); diff --git a/src/main.ts b/src/main.ts index e15f285..322e0c7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -100,6 +100,7 @@ export class Editor { outputSocket: WebSocket = outputSocket; // Hydra + public hydra_loaded: boolean = false; public hydra_backend: any; public hydra: any; @@ -125,7 +126,7 @@ export class Editor { this.initializeElements(); this.initializeButtonGroups(); this.setCanvas(this.interface.feedback as HTMLCanvasElement); - this.loadHydraSynthAsync(); + // this.loadHydraSynthAsync(); // ================================================================================ // Loading the universe from local storage @@ -536,38 +537,46 @@ export class Editor { } } - - private loadHydraSynthAsync(): void { - const timeoutDuration = 1000; - var script = document.createElement("script"); - script.src = "https://unpkg.com/hydra-synth"; - script.async = true; - - let timeoutHandle = setTimeout(() => { - script.onerror = null; - script.onload = null; - document.head.removeChild(script); - console.error("Hydra loading timed out"); - this.handleLoadingError(); - }, timeoutDuration); - - script.onload = () => { - clearTimeout(timeoutHandle); - console.log("Hydra loaded successfully"); - this.initializeHydra(); + public ensureHydraLoaded(): Promise { + if (this.hydra_loaded) { + return Promise.resolve(); } - script.onerror = () => { - clearTimeout(timeoutHandle); - console.error("Error loading Hydra script"); - this.handleLoadingError(); + if (this.hydra_backend) { + return Promise.resolve(); } - document.head.appendChild(script); - } + // Starts the loading process + return new Promise((resolve, reject) => { + const timeoutDuration = 1000; + var script = document.createElement("script"); + script.src = "https://unpkg.com/hydra-synth"; + script.async = true; - private handleLoadingError(): void { - console.error("Handling error after failed script load."); + let timeoutHandle = setTimeout(() => { + script.onerror = null; + script.onload = null; + document.head.removeChild(script); + console.error("Hydra loading timed out"); + reject(new Error("Hydra loading timed out")); + }, timeoutDuration); + + script.onload = () => { + clearTimeout(timeoutHandle); + console.log("Hydra loaded successfully"); + this.initializeHydra(); + this.hydra_loaded = true; + resolve(); + }; + + script.onerror = () => { + clearTimeout(timeoutHandle); + console.error("Error loading Hydra script"); + reject(new Error("Error loading Hydra script")); + }; + + document.head.appendChild(script); + }); } private initializeHydra(): void { @@ -583,7 +592,6 @@ export class Editor { this.hydra = this.hydra_backend.synth; this.hydra.setResolution(1280, 768); (globalThis as any).hydra = this.hydra; - // Hydra is overriding the bpm function } private setCanvas(canvas: HTMLCanvasElement): void { diff --git a/yarn.lock b/yarn.lock index 585c630..c2c2327 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1829,15 +1829,10 @@ camelcase-css@^2.0.1: resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== -caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001520: - version "1.0.30001521" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001521.tgz#e9930cf499f7c1e80334b6c1fbca52e00d889e56" - integrity sha512-fnx1grfpEOvDGH+V17eccmNjucGUnCbP6KL+l5KqBIerp26WK/+RQ7CIDE37KGJjaPyqWXXlFUyKiWmvdNNKmQ== - -caniuse-lite@^1.0.30001541: - version "1.0.30001561" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz#752f21f56f96f1b1a52e97aae98c57c562d5d9da" - integrity sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw== +caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001520, caniuse-lite@^1.0.30001541: + version "1.0.30001610" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001610.tgz" + integrity sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA== chalk@^2.4.2: version "2.4.2"