Compare commits

..

No commits in common. "refactor" and "main" have entirely different histories.

5 changed files with 279 additions and 318 deletions

View File

@ -1,45 +0,0 @@
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
}
export 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();
}
}

View File

@ -1,106 +0,0 @@
import { loadInstruments } from "./instruments.js";
export class Conductor {
instruments;
constructor() {}
showingWhichContent;
interacted = true;
get instruments() {
return this.instruments;
}
static setTitle() {
const now = new Date();
const future = new Date(now.getTime() + 59 * 1000); // 59 seconds later
const formattedDate = `${Conductor.padZero(
now.getDate()
)}.${Conductor.padZero(now.getMonth() + 1)}.${now.getFullYear()}`;
const formattedTime = `${Conductor.padZero(
now.getHours()
)}:${Conductor.padZero(now.getMinutes())}:${this.padZero(
now.getSeconds()
)}`;
const formattedFutureTime = `${Conductor.padZero(
future.getHours()
)}:${Conductor.padZero(future.getMinutes())}:${Conductor.padZero(
future.getSeconds()
)}`;
title.innerHTML = `${formattedDate}<span class="tab-space"></span>${formattedTime} - ${formattedFutureTime} UTC`;
}
showMainContent() {
Conductor.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();
});
this.fadeAndReplaceADiv("main-content", "loading-screen");
this.fadeAndReplaceADiv("buttons", "buttons");
this.showingWhichContent = "main-content";
document.getElementById("menu-button").onclick = function () {
toggleInfo();
};
}
toggleInfo() {
if (this.showingWhichContent === "main-content") {
this.fadeAndReplaceADiv("about-content", "main-content");
cleanUpAndRestart();
this.showingWhichContent = "about-content";
} else {
this.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";
}
static padZero(num) {
return num.toString().padStart(2, "0");
}
async init() {
const instrumentsKey = document.getElementById("instrument-key-div");
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 = this.instruments[instrument].image;
keyDiv.appendChild(keySvg);
keyDiv.appendChild(keyName);
keyDiv.classList.add("instrument-key");
instrumentsKey.appendChild(keyDiv);
}
}
}

View File

@ -1,83 +0,0 @@
import { Conductor } from "./conductor";
export class Renderer {
timeIndicator;
currStaveNumber;
numStaves;
sheetWindow;
constructor() {
this.numStaves = 6;
this.currStaveNumber = 1;
this.timeIndicator = document.querySelector("#time-indicator");
this.timeIndicator.addEventListener("animationiteration", () => {
this.currStaveNumber++;
if (this.currStaveNumber > this.numStaves) {
this.cleanUpAndRestart();
}
});
this.sheetWindow = document.getElementById("music-window");
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";
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;
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);
};
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;
Conductor.setTitle();
if (reload) location.reload();
}
}

View File

@ -1,37 +1,294 @@
import { Client, BrowserSocketFactory } from "./blitz.js";
import { loadInstruments } from "./instruments.js";
import { sendMidiMessage } from "./midi.js"; 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 () => { document.addEventListener("DOMContentLoaded", async () => {
let showing;
let audioContext;
let interacted = true;
let gainNode;
sendMidiMessage(); sendMidiMessage();
const conductor = new Conductor(); function startAudio() {
await conductor.init(); audioContext = new (window.AudioContext || window.webkitAudioContext)();
const dataStream = new DataStream(); gainNode = audioContext.createGain();
const instruments = await conductor.instruments; gainNode.gain.value = 0;
console.log(instruments); gainNode.connect(audioContext.destination);
}
const renderer = new Renderer(); function muter() {
fadeOutVolume();
}
dataStream.addEventListener("strike", () => { function unMute() {
const keys = Object.keys(instruments); startAudio();
const randomIndex = Math.floor(Math.random() * keys.length); fadeInVolume();
const selectedInstrument = instruments[keys[randomIndex]]; }
renderer.placeIcon(selectedInstrument); function padZero(num) {
playSound(selectedInstrument); 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}<span class="tab-space"></span>${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();
const instNames = Object.keys(instruments);
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();
}
}); });
dataStream.init(); 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();
};
// let currStaveNumber = 1; 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);
conductor.showMainContent(); // source.buffer = samples[`${randomKey}`];
// source.connect(gainNode);
// source.start();
}
};
// let strikeNumber = 0; 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;
// setupWebSocketListeners(); // Setup the WebSocket listeners // Create the icon div and give it its location data
// resetTimeout(); // Start the timeout timer 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
resetTimeout(); // Start the timeout timer
}); });

View File

@ -1,62 +0,0 @@
import { BrowserSocketFactory, Client } from "./blitz";
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.client = new Client(this.socketFactory);
this.lastReceived = Date.now();
this.setupWebSocketListeners();
this.client.connect();
this.resetTimeout();
}
resetTimeout() {
clearTimeout(this.connectionTimeout);
this.connectionTimeout = setTimeout(() => {
console.log("No data received in 14 seconds, reconnecting...");
this.reconnectWebSocket();
}, 14999);
}
reconnectWebSocket() {
if (this.client) {
this.client.close();
}
this.client = new Client(new BrowserSocketFactory());
this.setupWebSocketListeners();
this.client.connect();
this.resetTimeout();
}
setupWebSocketListeners() {
this.client.removeAllListeners?.("data");
this.client.removeAllListeners?.("error");
this.client.removeAllListeners?.("close");
this.client.on("data", (data) => {
this.lastReceived = Date.now();
this.resetTimeout();
this.dispatchEvent(new CustomEvent("strike", { detail: data }));
});
this.client.on("error", (err) => {
console.error("WebSocket error:", err);
this.reconnectWebSocket();
});
this.client.on("close", () => {
console.log("WebSocket closed, attempting to reconnect...");
this.reconnectWebSocket();
});
}
}