a little progress

This commit is contained in:
Joe 2025-04-18 03:57:30 +01:00
parent be327165b8
commit f776fe377a
6 changed files with 120 additions and 37 deletions

View File

@ -8,7 +8,7 @@
"bdrumnew-hit-v7-rr1-sum-(1).mp3" "bdrumnew-hit-v7-rr1-sum-(1).mp3"
], ],
"midi-channel-name": "conductorOrchestralBass", "midi-channel-name": "conductorOrchestralBass",
"image": { "filename": "bass.svg", "yPos": "13%" } "image": { "filename": "bass.svg", "yPos": "13%", "width": "15vw", "height": "15vw" }
}, },
"Snare": { "Snare": {
"directory": "/assets/instruments/snare", "directory": "/assets/instruments/snare",
@ -17,20 +17,20 @@
"ropesnare-low-tsn-main-vl4-rr1.mp3" "ropesnare-low-tsn-main-vl4-rr1.mp3"
], ],
"midi-channel-name": "conductorSnare", "midi-channel-name": "conductorSnare",
"image": { "filename": "snare.svg", "yPos": "26%" } "image": { "filename": "snare.svg", "yPos": "26%", "width": "15vw", "height": "15vw" }
}, },
"Surdo": { "Surdo": {
"directory": "/assets/instruments/surdo", "directory": "/assets/instruments/surdo",
"filenames": ["surdo-5.mp3", "surdo-6.mp3", "surdo-7.mp3"], "filenames": ["surdo-5.mp3", "surdo-6.mp3", "surdo-7.mp3"],
"midi-channel-name":"conductorSnare", "midi-channel-name":"conductorSnare",
"image": { "filename": "surdo.svg", "yPos": "39%" } "image": { "filename": "surdo.svg", "yPos": "39%" , "width": "15vw", "height": "15vw"}
}, },
"Surdo Napa": { "Surdo Napa": {
"directory": "/assets/instruments/surdo-napa", "directory": "/assets/instruments/surdo-napa",
"filenames": ["surdo-1.mp3", "surdo-3.mp3", "surdo-4.mp3"], "filenames": ["surdo-1.mp3", "surdo-3.mp3", "surdo-4.mp3"],
"midi-channel-name": "conductorSurdoNapa", "midi-channel-name": "conductorSurdoNapa",
"image": { "filename": "surdo-napa.svg", "yPos": "42%" } "image": { "filename": "surdo-napa.svg", "yPos": "42%" , "width": "15vw", "height": "15vw"
}, } },
"Timpani Large": { "Timpani Large": {
"directory": "/assets/instruments/timpani-large", "directory": "/assets/instruments/timpani-large",
"filenames": [ "filenames": [
@ -40,8 +40,8 @@
"timpani7d-hit-v2-rr1-main.mp3" "timpani7d-hit-v2-rr1-main.mp3"
], ],
"midi-channel-name":"conductorTimpaniLarge", "midi-channel-name":"conductorTimpaniLarge",
"image": { "filename": "timpani-large.svg", "yPos": "55%" } "image": { "filename": "timpani-large.svg", "yPos": "55%" , "width": "15vw", "height": "15vw"
}, } },
"Timpani Small": { "directory": "/assets/instruments/timpani-small", "Timpani Small": { "directory": "/assets/instruments/timpani-small",
"filenames": [ "filenames": [
"timpani1a-hit-v5-rr1-main.mp3", "timpani1a-hit-v5-rr1-main.mp3",
@ -49,7 +49,7 @@
"timpani4-hit-v2-rr1-sum.mp3" "timpani4-hit-v2-rr1-sum.mp3"
], ],
"midi-channel-name": "conductorTimpaniSmall", "midi-channel-name": "conductorTimpaniSmall",
"image": { "filename": "timpani-small.svg", "yPos": "68%" } "image": { "filename": "timpani-small.svg", "yPos": "68%" , "width": "15vw", "height": "15vw"}
}, },
"Toms": { "Toms": {
"directory": "/assets/instruments/toms", "directory": "/assets/instruments/toms",
@ -59,6 +59,6 @@
"toml-rollm-v2-rr1-mid.mp3" "toml-rollm-v2-rr1-mid.mp3"
], ],
"midi-channel-name": "conductorToms", "midi-channel-name": "conductorToms",
"image": { "filename": "toms.svg", "yPos": "81%" } "image": { "filename": "toms.svg", "yPos": "81%" , "width": "15vw", "height": "15vw"}
} }
} }

View File

@ -34,6 +34,7 @@
<div class="sheet-music-window" id="music-window"> <div class="sheet-music-window" id="music-window">
<canvas id="event-canvas"></canvas>
<object type="image/svg+xml" data="/assets/svg/time-pointer.svg" class="time-indicator" <object type="image/svg+xml" data="/assets/svg/time-pointer.svg" class="time-indicator"
id="time-indicator"></object> id="time-indicator"></object>
</div> </div>

View File

@ -59,6 +59,8 @@ export async function loadInstruments() {
image: imageData, image: imageData,
yPos: config.image.yPos, // Store base64-encoded image data yPos: config.image.yPos, // Store base64-encoded image data
midiChannelName: config["midi-channel-name"], midiChannelName: config["midi-channel-name"],
width: config.image.width,
height: config.image.width,
}; };
} }

View File

@ -5,11 +5,29 @@ export class Renderer {
currStaveNumber; currStaveNumber;
numStaves; numStaves;
sheetWindow; sheetWindow;
eventCanvas;
ctx;
canvasWidth;
canvasHeight;
iconCache = []; // stores drawn icons
imageCache = new Map(); // for memoizing loaded images
constructor() { constructor() {
this.numStaves = 6; this.numStaves = 6;
this.currStaveNumber = 1; this.currStaveNumber = 1;
this.timeIndicator = document.querySelector("#time-indicator"); this.timeIndicator = document.querySelector("#time-indicator");
this.sheetWindow = document.getElementById("music-window");
// Setup canvas
this.eventCanvas = document.getElementById("event-canvas");
this.ctx = this.eventCanvas.getContext("2d");
this.resizeCanvas();
window.addEventListener("resize", () => {
this.resizeCanvas();
this.redrawIcons();
});
this.timeIndicator.addEventListener("animationiteration", () => { this.timeIndicator.addEventListener("animationiteration", () => {
this.currStaveNumber++; this.currStaveNumber++;
@ -18,8 +36,7 @@ export class Renderer {
} }
}); });
this.sheetWindow = document.getElementById("music-window"); // Render stave SVGs
for (let i = 1; i <= this.numStaves; i++) { for (let i = 1; i <= this.numStaves; i++) {
const staveWrapper = document.createElement("div"); const staveWrapper = document.createElement("div");
staveWrapper.classList.add("stave-wrapper"); staveWrapper.classList.add("stave-wrapper");
@ -36,13 +53,19 @@ export class Renderer {
} }
} }
placeIcon = (instrument) => { resizeCanvas() {
this.canvasWidth = this.sheetWindow.offsetWidth;
this.canvasHeight = this.sheetWindow.offsetHeight;
this.eventCanvas.width = this.canvasWidth;
this.eventCanvas.height = this.canvasHeight;
}
async placeIcon(instrument) {
if (this.currStaveNumber > this.numStaves) this.currStaveNumber = 1; if (this.currStaveNumber > this.numStaves) this.currStaveNumber = 1;
const targetWrapper = document.getElementById( const targetWrapper = document.getElementById(
`stave-wrapper-${this.currStaveNumber}` `stave-wrapper-${this.currStaveNumber}`
); );
const rect = this.timeIndicator.getBoundingClientRect(); const rect = this.timeIndicator.getBoundingClientRect();
const sheetLeft = this.sheetWindow.getBoundingClientRect().left; const sheetLeft = this.sheetWindow.getBoundingClientRect().left;
const xPosition = rect.left + window.scrollX - sheetLeft; const xPosition = rect.left + window.scrollX - sheetLeft;
@ -50,34 +73,71 @@ export class Renderer {
const newObject = document.createElement("div"); const newObject = document.createElement("div");
newObject.classList.add("event-icon"); newObject.classList.add("event-icon");
newObject.style.position = "absolute"; newObject.style.position = "absolute";
newObject.style.left = `${ newObject.style.left = `${xPosition}px`;
xPosition - document.documentElement.clientWidth / 200 newObject.style.top = instrument.yPos;
}px`; newObject.style.width = instrument.width;
newObject.style.top = `${instrument.yPos}`; newObject.style.height = instrument.height;
const eventIcon = document.createElement("object"); // ✅ Detect and decode base64 if needed
eventIcon.type = "image/svg+xml"; let svgString = instrument.image;
eventIcon.data = instrument.image; if (svgString.startsWith("data:image/svg+xml;base64,")) {
const base64 = svgString.split(",")[1];
svgString = atob(base64);
}
newObject.appendChild(eventIcon); const parser = new DOMParser();
const svgDoc = parser.parseFromString(svgString, "image/svg+xml");
const svgElement = svgDoc.documentElement;
// ✅ Handle parsing error
if (svgElement.nodeName === "parsererror") {
console.error("Failed to parse SVG:", svgString);
return;
}
svgElement.style.width = "100%";
svgElement.style.height = "100%";
newObject.appendChild(svgElement);
targetWrapper.appendChild(newObject); targetWrapper.appendChild(newObject);
}
async loadImageFromSVG(svgString) {
if (this.imageCache.has(svgString)) {
return this.imageCache.get(svgString);
}
return new Promise((resolve) => {
const blob = new Blob([svgString], { type: "image/svg+xml" });
const url = URL.createObjectURL(blob);
const img = new Image();
img.onload = () => {
URL.revokeObjectURL(url); // clean up after load
this.imageCache.set(svgString, img);
resolve(img);
}; };
img.src = url;
});
}
redrawIcons() {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.iconCache.forEach(({ image, x, y }) => {
this.ctx.drawImage(image, x, y, 24, 24); // Adjust size as needed
});
}
cleanUpAndRestart(reload = false) { cleanUpAndRestart(reload = false) {
const icons = document.querySelectorAll(".event-icon"); this.iconCache = [];
this.timeIndicator = document.getElementById("time-indicator"); this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
icons.forEach((icon) => {
icon.classList.add("fade-out");
icon.addEventListener("transitionend", () => {
icon.remove();
});
});
this.currStaveNumber = 1; this.currStaveNumber = 1;
Conductor.setTitle(); Conductor.setTitle();
if (reload) location.reload(); if (reload) location.reload();
} }
} }
// This will need to be worked on. There is no need for the loadImageFromSVG function here and the placeIcon function will need to be changed to reflect it.
// image size and z-index isn't correct and the canvas will need to be enlarged to allow icons to overhang the staves.

View File

@ -2,7 +2,7 @@ import { sendMidiMessage } from "./midi.js";
import { Conductor } from "./conductor.js"; import { Conductor } from "./conductor.js";
import { DataStream } from "./socket.js"; import { DataStream } from "./socket.js";
import { playSound } from "./audio"; import { playSound } from "./audio";
import { Renderer, placeIcon } from "./render.js"; import { Renderer } from "./render.js";
document.addEventListener("DOMContentLoaded", async () => { document.addEventListener("DOMContentLoaded", async () => {
sendMidiMessage(); sendMidiMessage();

View File

@ -74,6 +74,9 @@ html {
transition: opacity 1s ease; transition: opacity 1s ease;
} }
#event-canvas {
background-color: rgba(255, 255, 255, 0.02); /* lightly visible canvas for debugging */
}
.logo { .logo {
color: #fff; color: #fff;
@ -195,9 +198,26 @@ html {
width: 2px; width: 2px;
/* height: 100%; */ /* height: 100%; */
animation: moveRight 10s linear infinite; animation: moveRight 10s linear infinite;
z-index: 3;
}
#event-canvas {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
z-index: 2; z-index: 2;
} }
#time-indicator {
position: absolute;
top: 0;
bottom: 0;
width: 2px;
background-color: red;
z-index: 3;
will-change: transform;
}
.fade-in { .fade-in {
opacity: 1; opacity: 1;