From 1393acdc423ab8040c814781823878af8a5a2b75 Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 13 Apr 2025 13:36:30 +0100 Subject: [PATCH] distributing functions to files to go OO --- src/audio.js | 45 ++++++++ src/conductor.js | 133 +++++++++++++++++++++++ src/render.js | 32 ++++++ src/script.js | 275 ----------------------------------------------- src/socket.js | 66 ++++++++++++ 5 files changed, 276 insertions(+), 275 deletions(-) create mode 100644 src/audio.js create mode 100644 src/conductor.js create mode 100644 src/render.js create mode 100644 src/socket.js diff --git a/src/audio.js b/src/audio.js new file mode 100644 index 0000000..d3a22d1 --- /dev/null +++ b/src/audio.js @@ -0,0 +1,45 @@ +let audioContext; +let gainNode; + +function startAudio() { + audioContext = new (window.AudioContext || window.webkitAudioContext)(); + gainNode = audioContext.createGain(); + gainNode.gain.value = 0; + gainNode.connect(audioContext.destination); +} + +function muter() { + fadeOutVolume(); +} + +function unMute() { + startAudio(); + fadeInVolume(); +} + +function fadeInVolume() { + gainNode.gain.setValueAtTime(0, audioContext.currentTime); // Start at 0 + gainNode.gain.linearRampToValueAtTime(1, audioContext.currentTime + 2); // Fade to full volume +} + +function fadeOutVolume() { + gainNode.gain.setValueAtTime(1, audioContext.currentTime); // Start at 1 + gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + 2); // Fade to 0 +} + +function playSound(instrument) { + if (interacted) { + // const source = audioContext.createBufferSource(); + const samples = instrument.samples; + const sampleKeys = Object.keys(samples); + const numSamples = sampleKeys.length; + const randomSampleNumber = Math.floor(Math.random() * (numSamples - 0)); + // const randomKey = + // sampleKeys[randomSampleNumber]; + sendMidiMessage(instrument.midiChannelName, randomSampleNumber); + + // source.buffer = samples[`${randomKey}`]; + // source.connect(gainNode); + // source.start(); + } +} diff --git a/src/conductor.js b/src/conductor.js new file mode 100644 index 0000000..7f93875 --- /dev/null +++ b/src/conductor.js @@ -0,0 +1,133 @@ +class Conductor { + constructor() {} + + showingWhichContent; + interacted = true; + + init() { + const instrumentsKey = document.getElementById("instrument-key-div"); + + 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; + 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(); + } + }); + } + + 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(); + } + + setTitle() { + const now = new Date(); + const future = new Date(now.getTime() + 59 * 1000); // 59 seconds later + + const formattedDate = `${padZero(now.getDate())}.${padZero( + now.getMonth() + 1 + )}.${now.getFullYear()}`; + const formattedTime = `${padZero(now.getHours())}:${padZero( + now.getMinutes() + )}:${padZero(now.getSeconds())}`; + const formattedFutureTime = `${padZero(future.getHours())}:${padZero( + future.getMinutes() + )}:${padZero(future.getSeconds())}`; + + title.innerHTML = `${formattedDate}${formattedTime} - ${formattedFutureTime} UTC`; + } + + showMainContent() { + setTitle(); + const mute = document.getElementById("mute"); + + mute.addEventListener("click", () => { + const mutedSrc = "/assets/svg/sound-on.svg"; + const unMutedSrc = "/assets/svg/mute.svg"; + interacted = !interacted; + interacted ? unMute() : muter(); + mute.src = interacted ? mutedSrc : unMutedSrc; + + startAudio(); + fadeInVolume(); + }); + + fadeAndReplaceADiv("main-content", "loading-screen"); + fadeAndReplaceADiv("buttons", "buttons"); + this.showingWhichContent = "main-content"; + + document.getElementById("menu-button").onclick = function () { + toggleInfo(); + }; + } + + toggleInfo() { + if (this.showingWhichContent === "main-content") { + fadeAndReplaceADiv("about-content", "main-content"); + cleanUpAndRestart(); + this.showingWhichContent = "about-content"; + } else { + fadeAndReplaceADiv("main-content", "about-content"); + this.showingWhichContent = "main-content"; + } + } + + fadeAndReplaceADiv(innyDivId, outtyDivId) { + const outty = document.getElementById(outtyDivId); + const inny = document.getElementById(innyDivId); + outty.classList.remove("fade-in"); + outty.classList.add("fade-out"); + + outty.style.display = "none"; + inny.style.display = "flex"; + inny.style.opacity = "0"; + void inny.offsetWidth; + inny.classList.add("fade-in"); + inny.style.opacity = "1"; + } + + padZero(num) { + return num.toString().padStart(2, "0"); + } +} diff --git a/src/render.js b/src/render.js new file mode 100644 index 0000000..a773096 --- /dev/null +++ b/src/render.js @@ -0,0 +1,32 @@ +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; + + // Create the icon div and give it its location data + 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}`; + + // Create the icon + const eventIcon = document.createElement("object"); + eventIcon.type = "image/svg+xml"; + eventIcon.data = instrument.image; + + // Append icon to div + newObject.appendChild(eventIcon); + + // Append the div to the staveWrapper + staveWrapper.appendChild(newObject); +}; diff --git a/src/script.js b/src/script.js index f98001c..ba591e4 100644 --- a/src/script.js +++ b/src/script.js @@ -3,175 +3,8 @@ import { loadInstruments } from "./instruments.js"; import { sendMidiMessage } from "./midi.js"; document.addEventListener("DOMContentLoaded", async () => { - let showing; - let audioContext; - let interacted = true; - let gainNode; - sendMidiMessage(); - function startAudio() { - audioContext = new (window.AudioContext || window.webkitAudioContext)(); - gainNode = audioContext.createGain(); - gainNode.gain.value = 0; - gainNode.connect(audioContext.destination); - } - - function muter() { - fadeOutVolume(); - } - - function unMute() { - startAudio(); - fadeInVolume(); - } - - function padZero(num) { - return num.toString().padStart(2, "0"); - } - - function setTitle() { - const now = new Date(); - const future = new Date(now.getTime() + 59 * 1000); // 59 seconds later - - const formattedDate = `${padZero(now.getDate())}.${padZero( - now.getMonth() + 1 - )}.${now.getFullYear()}`; - const formattedTime = `${padZero(now.getHours())}:${padZero( - now.getMinutes() - )}:${padZero(now.getSeconds())}`; - const formattedFutureTime = `${padZero(future.getHours())}:${padZero( - future.getMinutes() - )}:${padZero(future.getSeconds())}`; - - title.innerHTML = `${formattedDate}${formattedTime} - ${formattedFutureTime} UTC`; - } - - function fadeInVolume() { - gainNode.gain.setValueAtTime(0, audioContext.currentTime); // Start at 0 - gainNode.gain.linearRampToValueAtTime(1, audioContext.currentTime + 2); // Fade to full volume - } - function fadeOutVolume() { - gainNode.gain.setValueAtTime(1, audioContext.currentTime); // Start at 1 - gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + 2); // Fade to 0 - } - - function showMainContent() { - setTitle(); - const mute = document.getElementById("mute"); - - mute.addEventListener("click", () => { - const mutedSrc = "/assets/svg/sound-on.svg"; - const unMutedSrc = "/assets/svg/mute.svg"; - interacted = !interacted; - interacted ? unMute() : muter(); - mute.src = interacted ? mutedSrc : unMutedSrc; - - startAudio(); - fadeInVolume(); - }); - - fadeAndReplaceADiv("main-content", "loading-screen"); - fadeAndReplaceADiv("buttons", "buttons"); - showing = "main-content"; - - document.getElementById("menu-button").onclick = function () { - toggleInfo(); - }; - } - - function toggleInfo() { - if (showing === "main-content") { - fadeAndReplaceADiv("about-content", "main-content"); - cleanUpAndRestart(); - showing = "about-content"; - } else { - fadeAndReplaceADiv("main-content", "about-content"); - showing = "main-content"; - } - } - - function fadeAndReplaceADiv(innyDivId, outtyDivId) { - const outty = document.getElementById(outtyDivId); - const inny = document.getElementById(innyDivId); - outty.classList.remove("fade-in"); - outty.classList.add("fade-out"); - - outty.style.display = "none"; - inny.style.display = "flex"; - inny.style.opacity = "0"; - void inny.offsetWidth; - inny.classList.add("fade-in"); - inny.style.opacity = "1"; - } - - const socketFactory = new BrowserSocketFactory(); - let client = new Client(socketFactory); - client.connect(); - let connectionTimeout; // Timeout for detecting inactivity - let lastReceived = Date.now(); // Tracks the last time data was received - - function resetTimeout() { - clearTimeout(connectionTimeout); - connectionTimeout = setTimeout(() => { - // 15 seconds of inactivity - console.log("No data received in 15 seconds, reconnecting..."); - reconnectWebSocket(); - }, 15000); - } - - function reconnectWebSocket() { - console.log("Reconnecting WebSocket..."); - - // Clean up the existing WebSocket connection - if (client) { - client.close(); // Ensure the current connection is closed - } - - // 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 - } - - function setupWebSocketListeners() { - client.removeAllListeners("data"); - client.removeAllListeners("error"); - client.removeAllListeners("close"); - - client.on("data", (strike) => { - lastReceived = Date.now(); // Update the last received timestamp - resetTimeout(); // Reset the timeout on any data received - - // Process the strike (existing logic from your original code) - strikeNumber++; - if (strikeNumber == 1) { - const randomInstrumentNumber = Math.floor( - Math.random() * (Object.keys(instruments).length - 1) - ); - const selectedInstrumentName = - Object.keys(instruments)[randomInstrumentNumber]; - - placeIcon(instruments[selectedInstrumentName]); - playSound(instruments[selectedInstrumentName]); - strikeNumber = 0; - } - }); - - client.on("error", (message) => { - console.log("WebSocket error:", message); - reconnectWebSocket(); - }); - - client.on("close", () => { - console.log("WebSocket closed, attempting to reconnect..."); - reconnectWebSocket(); - }); - } - let currStaveNumber = 1; const instruments = await loadInstruments(); @@ -179,114 +12,6 @@ document.addEventListener("DOMContentLoaded", async () => { showMainContent(); - /*** - * Add the instrument images and names into the about section - */ - - const instrumentsKey = document.getElementById("instrument-key-div"); - 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; - 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(); - } - }); - - const 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(); - }; - - const playSound = (instrument) => { - if (interacted) { - // const source = audioContext.createBufferSource(); - const samples = instrument.samples; - const sampleKeys = Object.keys(samples); - const numSamples = sampleKeys.length; - const randomSampleNumber = Math.floor(Math.random() * (numSamples - 1)); - // const randomKey = - // sampleKeys[randomSampleNumber]; - sendMidiMessage(instrument.midiChannelName, randomSampleNumber); - - // source.buffer = samples[`${randomKey}`]; - // source.connect(gainNode); - // source.start(); - } - }; - - 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; - - // Create the icon div and give it its location data - 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}`; - - // Create the icon - const eventIcon = document.createElement("object"); - eventIcon.type = "image/svg+xml"; - eventIcon.data = instrument.image; - - // Append icon to div - newObject.appendChild(eventIcon); - - // Append the div to the staveWrapper - staveWrapper.appendChild(newObject); - }; - let strikeNumber = 0; setupWebSocketListeners(); // Setup the WebSocket listeners diff --git a/src/socket.js b/src/socket.js new file mode 100644 index 0000000..0c3a1dc --- /dev/null +++ b/src/socket.js @@ -0,0 +1,66 @@ +const socketFactory = new BrowserSocketFactory(); +let client = new Client(socketFactory); +client.connect(); +let connectionTimeout; // Timeout for detecting inactivity +let lastReceived = Date.now(); // Tracks the last time data was received + +function resetTimeout() { + clearTimeout(connectionTimeout); + connectionTimeout = setTimeout(() => { + // 14 seconds of inactivity + console.log("No data received in 14 seconds, reconnecting..."); + reconnectWebSocket(); + }, 14999); +} + +function reconnectWebSocket() { + console.log("Reconnecting WebSocket..."); + + // Clean up the existing WebSocket connection + if (client) { + client.close(); // Ensure the current connection is closed + } + + // 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 +} + +function setupWebSocketListeners() { + client.removeAllListeners("data"); + client.removeAllListeners("error"); + client.removeAllListeners("close"); + + client.on("data", (strike) => { + lastReceived = Date.now(); // Update the last received timestamp + resetTimeout(); // Reset the timeout on any data received + + // 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; + } + }); + + client.on("error", (message) => { + console.log("WebSocket error:", message); + reconnectWebSocket(); + }); + + client.on("close", () => { + console.log("WebSocket closed, attempting to reconnect..."); + reconnectWebSocket(); + }); +}