diff --git a/src/audio.js b/src/audio.js index d3a22d1..aae23dd 100644 --- a/src/audio.js +++ b/src/audio.js @@ -27,7 +27,7 @@ function fadeOutVolume() { gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + 2); // Fade to 0 } -function playSound(instrument) { +export function playSound(instrument) { if (interacted) { // const source = audioContext.createBufferSource(); const samples = instrument.samples; diff --git a/src/conductor.js b/src/conductor.js index 7c114f2..cc85a92 100644 --- a/src/conductor.js +++ b/src/conductor.js @@ -1,22 +1,15 @@ import { loadInstruments } from "./instruments.js"; export class Conductor { + instruments; + constructor() {} showingWhichContent; interacted = true; - cleanUpAndRestart(reload = false) { - const icons = document.querySelectorAll(".event-icon"); - const timeIndicator = document.getElementById("time-indicator"); - icons.forEach((icon) => { - icon.classList.add("fade-out"); - icon.addEventListener("transitionend", () => { - icon.remove(); - }); - }); - currStaveNumber = 1; - setTitle(); - if (reload) location.reload(); + + get instruments() { + return this.instruments; } setTitle() { @@ -93,45 +86,19 @@ export class Conductor { async init() { const instrumentsKey = document.getElementById("instrument-key-div"); - const instruments = await loadInstruments(); - const instNames = Object.keys(instruments); + this.instruments = await loadInstruments(); + const instNames = Object.keys(this.instruments); for (const instrument of instNames) { const keyDiv = document.createElement("div"); const keySvg = document.createElement("object"); const keyName = document.createElement("div"); keyName.innerText = instrument.toUpperCase(); keySvg.type = "image/svg+xml"; - keySvg.data = instruments[instrument].image; + keySvg.data = this.instruments[instrument].image; keyDiv.appendChild(keySvg); keyDiv.appendChild(keyName); keyDiv.classList.add("instrument-key"); instrumentsKey.appendChild(keyDiv); } - - const numStaves = 6; - const sheetWindow = document.getElementById("music-window"); - - for (let i = 1; i <= numStaves; i++) { - const staveWrapper = document.createElement("div"); - staveWrapper.classList.add(`stave-wrapper`); - staveWrapper.setAttribute("id", `stave-wrapper-${i}`); - staveWrapper.style.position = "relative"; - const staveObject = document.createElement("object"); - staveObject.type = "image/svg+xml"; - staveObject.data = "assets/svg/stave.svg"; - staveObject.classList.add("stave-svg"); - - staveWrapper.appendChild(staveObject); - sheetWindow.appendChild(staveWrapper); - } - - const timeIndicator = document.querySelector("#time-indicator"); - - timeIndicator.addEventListener("animationiteration", () => { - currStaveNumber++; - if (currStaveNumber > numStaves) { - cleanUpAndRestart(); - } - }); } } diff --git a/src/render.js b/src/render.js index a773096..bd38a5b 100644 --- a/src/render.js +++ b/src/render.js @@ -1,32 +1,83 @@ -const placeIcon = (instrument) => { - if (currStaveNumber > numStaves) currStaveNumber = 1; - // Select the staveWrapper in which to place the div - const staveWrapper = document.getElementById( - `stave-wrapper-${currStaveNumber}` - ); - // Determine the position of the time indicator - const rect = timeIndicator.getBoundingClientRect(); - const sheetLeft = sheetWindow.getBoundingClientRect().left; - const xPosition = rect.left + window.scrollX - sheetLeft; +export class Renderer { + timeIndicator; + currStaveNumber; + numStaves; + sheetWindow; - // Create the icon div and give it its location data - const newObject = document.createElement("div"); - newObject.classList.add("event-icon"); - newObject.style.position = "absolute"; + constructor() { + this.numStaves = 6; + this.currStaveNumber = 1; // ✅ Initialize current stave number + this.timeIndicator = document.querySelector("#time-indicator"); - newObject.style.left = `${ - xPosition - document.documentElement.clientWidth / 200 - }px`; - newObject.style.top = `${instrument.yPos}`; + this.timeIndicator.addEventListener("animationiteration", () => { + this.currStaveNumber++; + if (this.currStaveNumber > this.numStaves) { + this.cleanUpAndRestart(); // ✅ Use this.method + } + }); - // Create the icon - const eventIcon = document.createElement("object"); - eventIcon.type = "image/svg+xml"; - eventIcon.data = instrument.image; + this.sheetWindow = document.getElementById("music-window"); - // Append icon to div - newObject.appendChild(eventIcon); + for (let i = 1; i <= this.numStaves; i++) { + const staveWrapper = document.createElement("div"); + staveWrapper.classList.add("stave-wrapper"); + staveWrapper.setAttribute("id", `stave-wrapper-${i}`); + staveWrapper.style.position = "relative"; - // Append the div to the staveWrapper - staveWrapper.appendChild(newObject); -}; + const staveObject = document.createElement("object"); + staveObject.type = "image/svg+xml"; + staveObject.data = "assets/svg/stave.svg"; + staveObject.classList.add("stave-svg"); + + staveWrapper.appendChild(staveObject); + this.sheetWindow.appendChild(staveWrapper); + } + } + + placeIcon = (instrument) => { + if (this.currStaveNumber > this.numStaves) this.currStaveNumber = 1; + + // ✅ Use a local variable instead of overwriting this.staveWrapper + const targetWrapper = document.getElementById( + `stave-wrapper-${this.currStaveNumber}` + ); + + const rect = this.timeIndicator.getBoundingClientRect(); + const sheetLeft = this.sheetWindow.getBoundingClientRect().left; + const xPosition = rect.left + window.scrollX - sheetLeft; + + const newObject = document.createElement("div"); + newObject.classList.add("event-icon"); + newObject.style.position = "absolute"; + newObject.style.left = `${ + xPosition - document.documentElement.clientWidth / 200 + }px`; + newObject.style.top = `${instrument.yPos}`; + + const eventIcon = document.createElement("object"); + eventIcon.type = "image/svg+xml"; + eventIcon.data = instrument.image; + + newObject.appendChild(eventIcon); + targetWrapper.appendChild(newObject); // ✅ Append to local reference + }; + + cleanUpAndRestart(reload = false) { + const icons = document.querySelectorAll(".event-icon"); + this.timeIndicator = document.getElementById("time-indicator"); + + icons.forEach((icon) => { + icon.classList.add("fade-out"); + icon.addEventListener("transitionend", () => { + icon.remove(); + }); + }); + + this.currStaveNumber = 1; + + // Assuming setTitle() exists globally or is handled elsewhere + setTitle(); + + if (reload) location.reload(); + } +} diff --git a/src/script.js b/src/script.js index eeb073d..5264e24 100644 --- a/src/script.js +++ b/src/script.js @@ -1,20 +1,36 @@ import { sendMidiMessage } from "./midi.js"; import { Conductor } from "./conductor.js"; import { DataStream } from "./socket.js"; +import { playSound } from "./audio"; +import { Renderer, placeIcon } from "./render.js"; document.addEventListener("DOMContentLoaded", async () => { sendMidiMessage(); const conductor = new Conductor(); + await conductor.init(); const dataStream = new DataStream(); - conductor.init(); + const instruments = await conductor.instruments; + console.log(instruments); + + const renderer = new Renderer(); + + dataStream.addEventListener("strike", () => { + const keys = Object.keys(instruments); + const randomIndex = Math.floor(Math.random() * keys.length); + const selectedInstrument = instruments[keys[randomIndex]]; + + renderer.placeIcon(selectedInstrument); + playSound(selectedInstrument); + }); + dataStream.init(); - let currStaveNumber = 1; + // let currStaveNumber = 1; conductor.showMainContent(); - let strikeNumber = 0; + // let strikeNumber = 0; // setupWebSocketListeners(); // Setup the WebSocket listeners // resetTimeout(); // Start the timeout timer diff --git a/src/socket.js b/src/socket.js index 70c7378..00e8183 100644 --- a/src/socket.js +++ b/src/socket.js @@ -1,79 +1,62 @@ -import { Client, BrowserSocketFactory } from "./blitz.js"; -export class DataStream { - socketFactory; - connectionTimeout; - lastReceived; - client; +import { BrowserSocketFactory, Client } from "./blitz"; - constructor() {} +export class DataStream extends EventTarget { + constructor() { + super(); + this.socketFactory = null; + this.client = null; + this.lastReceived = null; + this.connectionTimeout = null; + } async init() { this.socketFactory = new BrowserSocketFactory(); - this.lastReceived = Date.now(); // Tracks the last time data was received - this.client = new Client(socketFactory); - client.connect(); - setupWebSocketListeners(); - resetTimeout(); + this.client = new Client(this.socketFactory); + this.lastReceived = Date.now(); + + this.setupWebSocketListeners(); + this.client.connect(); + this.resetTimeout(); } resetTimeout() { - clearTimeout(connectionTimeout); - connectionTimeout = setTimeout(() => { - // 14 seconds of inactivity + clearTimeout(this.connectionTimeout); + this.connectionTimeout = setTimeout(() => { console.log("No data received in 14 seconds, reconnecting..."); - reconnectWebSocket(); + this.reconnectWebSocket(); }, 14999); } reconnectWebSocket() { - console.log("Reconnecting WebSocket..."); - - // Clean up the existing WebSocket connection - if (client) { - client.close(); // Ensure the current connection is closed + if (this.client) { + this.client.close(); } - - // Create a new client instance and re-establish the connection - client = new Client(new BrowserSocketFactory()); - setupWebSocketListeners(); // Set up listeners for the new WebSocket - client.connect(); // Reconnect - - // Reset timeout after reconnecting - resetTimeout(); // Ensure timeout is re-established after reconnection + this.client = new Client(new BrowserSocketFactory()); + this.setupWebSocketListeners(); + this.client.connect(); + this.resetTimeout(); } setupWebSocketListeners() { - client.removeAllListeners("data"); - client.removeAllListeners("error"); - client.removeAllListeners("close"); + this.client.removeAllListeners?.("data"); + this.client.removeAllListeners?.("error"); + this.client.removeAllListeners?.("close"); - client.on("data", (strike) => { - lastReceived = Date.now(); // Update the last received timestamp - resetTimeout(); // Reset the timeout on any data received + this.client.on("data", (data) => { + this.lastReceived = Date.now(); + this.resetTimeout(); - // Process the strike (existing logic from your original code) - strikeNumber++; - if (strikeNumber == 0) { - const randomInstrumentNumber = Math.floor( - Math.random() * (Object.keys(instruments).length - 0) - ); - const selectedInstrumentName = - Object.keys(instruments)[randomInstrumentNumber]; - - placeIcon(instruments[selectedInstrumentName]); - playSound(instruments[selectedInstrumentName]); - strikeNumber = -1; - } + this.dispatchEvent(new CustomEvent("strike", { detail: data })); }); - client.on("error", (message) => { - console.log("WebSocket error:", message); - reconnectWebSocket(); + this.client.on("error", (err) => { + console.error("WebSocket error:", err); + this.reconnectWebSocket(); }); - client.on("close", () => { + this.client.on("close", () => { console.log("WebSocket closed, attempting to reconnect..."); - reconnectWebSocket(); + this.reconnectWebSocket(); }); } }