this is garbage from chatty mc fuckin chat face

This commit is contained in:
Joe 2025-04-18 14:35:30 +01:00
parent f776fe377a
commit 75b66e245e
3 changed files with 91 additions and 42 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%", "width": "15vw", "height": "15vw" } "image": { "filename": "bass.svg", "yPos": "13%", "width": "100", "height": "100" }
}, },
"Snare": { "Snare": {
"directory": "/assets/instruments/snare", "directory": "/assets/instruments/snare",
@ -17,19 +17,19 @@
"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%", "width": "15vw", "height": "15vw" } "image": { "filename": "snare.svg", "yPos": "26%", "width": "100", "height": "100" }
}, },
"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%" , "width": "15vw", "height": "15vw"} "image": { "filename": "surdo.svg", "yPos": "39%" , "width": "100", "height": "100"}
}, },
"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%" , "width": "15vw", "height": "15vw" "image": { "filename": "surdo-napa.svg", "yPos": "42%" , "width": "100", "height": "100"
} }, } },
"Timpani Large": { "Timpani Large": {
"directory": "/assets/instruments/timpani-large", "directory": "/assets/instruments/timpani-large",
@ -40,7 +40,7 @@
"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%" , "width": "15vw", "height": "15vw" "image": { "filename": "timpani-large.svg", "yPos": "55%" , "width": "100", "height": "100"
} }, } },
"Timpani Small": { "directory": "/assets/instruments/timpani-small", "Timpani Small": { "directory": "/assets/instruments/timpani-small",
"filenames": [ "filenames": [
@ -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%" , "width": "15vw", "height": "15vw"} "image": { "filename": "timpani-small.svg", "yPos": "68%" , "width": "100", "height": "100"}
}, },
"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%" , "width": "15vw", "height": "15vw"} "image": { "filename": "toms.svg", "yPos": "81%" , "width": "100", "height": "100"}
} }
} }

View File

@ -9,8 +9,8 @@ export class Renderer {
ctx; ctx;
canvasWidth; canvasWidth;
canvasHeight; canvasHeight;
iconCache = []; // stores drawn icons iconCache = []; // stores { image, x, y, width, height }
imageCache = new Map(); // for memoizing loaded images imageCache = new Map(); // memoized <svgString, HTMLImageElement>
constructor() { constructor() {
this.numStaves = 6; this.numStaves = 6;
@ -58,48 +58,78 @@ export class Renderer {
this.canvasHeight = this.sheetWindow.offsetHeight; this.canvasHeight = this.sheetWindow.offsetHeight;
this.eventCanvas.width = this.canvasWidth; this.eventCanvas.width = this.canvasWidth;
this.eventCanvas.height = this.canvasHeight; this.eventCanvas.height = this.canvasHeight;
console.log("Canvas size:", this.canvasWidth, this.canvasHeight);
} }
async placeIcon(instrument) { async placeIcon(instrument) {
if (this.currStaveNumber > this.numStaves) this.currStaveNumber = 1; if (this.currStaveNumber > this.numStaves) this.currStaveNumber = 1;
const targetWrapper = document.getElementById(
`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;
const newObject = document.createElement("div"); const yPercent = parseFloat(instrument.yPos); // e.g., "55%" => 55
newObject.classList.add("event-icon"); const y = (yPercent / 100) * this.canvasHeight;
newObject.style.position = "absolute";
newObject.style.left = `${xPosition}px`;
newObject.style.top = instrument.yPos;
newObject.style.width = instrument.width;
newObject.style.height = instrument.height;
// ✅ Detect and decode base64 if needed // Handle image caching
let svgString = instrument.image; let svgString = instrument.image;
if (svgString.startsWith("data:image/svg+xml;base64,")) { if (svgString.startsWith("data:image/svg+xml;base64,")) {
const base64 = svgString.split(",")[1]; const base64 = svgString.split(",")[1];
svgString = atob(base64); svgString = atob(base64);
} }
const parser = new DOMParser(); // Ensure SVG has dimensions
const svgDoc = parser.parseFromString(svgString, "image/svg+xml"); svgString = this.ensureSvgHasDimensions(
const svgElement = svgDoc.documentElement; svgString,
instrument.width || 50,
instrument.height || 50
);
// ✅ Handle parsing error const img = await this.loadImageFromSVG(svgString);
if (svgElement.nodeName === "parsererror") {
console.error("Failed to parse SVG:", svgString); // Fallback if instrument.width/height not provided
return; const naturalWidth = img.naturalWidth || 50;
const naturalHeight = img.naturalHeight || 50;
const scale = 0.25;
const width = parseFloat(instrument.width || naturalWidth) * scale;
const height = parseFloat(instrument.height || naturalHeight) * scale;
this.ctx.drawImage(img, xPosition, y, width, height);
// Store for redrawing later
this.iconCache.push({
image: img,
x: xPosition,
y,
width,
height,
});
console.log("Drawing position:", xPosition, y);
console.log("width", width, "height", height);
console.log(
"Canvas size:",
this.eventCanvas.width,
this.eventCanvas.height
);
console.log(
"Canvas offset size:",
this.eventCanvas.offsetWidth,
this.eventCanvas.offsetHeight
);
}
ensureSvgHasDimensions(svgString, defaultWidth = 50, defaultHeight = 50) {
const hasWidth = /<svg[^>]*\bwidth=/.test(svgString);
const hasHeight = /<svg[^>]*\bheight=/.test(svgString);
if (!hasWidth || !hasHeight) {
svgString = svgString.replace(
/<svg([^>]*)>/,
`<svg width="${defaultWidth}" height="${defaultHeight}"$1>`
);
} }
svgElement.style.width = "100%"; return svgString;
svgElement.style.height = "100%";
newObject.appendChild(svgElement);
targetWrapper.appendChild(newObject);
} }
async loadImageFromSVG(svgString) { async loadImageFromSVG(svgString) {
@ -107,15 +137,25 @@ export class Renderer {
return this.imageCache.get(svgString); return this.imageCache.get(svgString);
} }
return new Promise((resolve) => { return new Promise((resolve, reject) => {
const blob = new Blob([svgString], { type: "image/svg+xml" }); const blob = new Blob([svgString], { type: "image/svg+xml" });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const img = new Image(); const img = new Image();
img.onload = () => { img.onload = () => {
URL.revokeObjectURL(url); // clean up after load console.log("Image loaded:", img.width, img.height); // should no longer be 0
this.imageCache.set(svgString, img);
resolve(img); const scale = 0.25;
const width = instrument.width * scale || img.width * scale;
const height = instrument.height * scale || img.height * scale;
ctx.drawImage(img, xPosition, y, width, height);
URL.revokeObjectURL(url);
};
img.onerror = (err) => {
console.error("Image failed to load from SVG:", err);
reject(err);
}; };
img.src = url; img.src = url;
@ -124,8 +164,8 @@ export class Renderer {
redrawIcons() { redrawIcons() {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.iconCache.forEach(({ image, x, y }) => { this.iconCache.forEach(({ image, x, y, width, height }) => {
this.ctx.drawImage(image, x, y, 24, 24); // Adjust size as needed this.ctx.drawImage(image, x, y, width, height);
}); });
} }

View File

@ -75,9 +75,14 @@ html {
} }
#event-canvas { #event-canvas {
background-color: rgba(255, 255, 255, 0.02); /* lightly visible canvas for debugging */ background: rgba(255, 0, 0, 0.2); /* light red overlay */
position: absolute;
top: 0;
left: 0;
z-index: 1000;
} }
.logo { .logo {
color: #fff; color: #fff;
width: 20%; width: 20%;
@ -189,6 +194,7 @@ html {
border: 1px solid rgb(24,24,24); border: 1px solid rgb(24,24,24);
padding-top: 30px; padding-top: 30px;
padding-bottom: 20px; padding-bottom: 20px;
z-index: 1;
} }
.time-indicator { .time-indicator {
@ -198,15 +204,18 @@ html {
width: 2px; width: 2px;
/* height: 100%; */ /* height: 100%; */
animation: moveRight 10s linear infinite; animation: moveRight 10s linear infinite;
z-index: 3; z-index: 999;
} }
.stave-svg {
position: relative;
z-index: 1; /* Same here */
}
#event-canvas { #event-canvas {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
pointer-events: none; pointer-events: none;
z-index: 2; z-index: 999;
} }
#time-indicator { #time-indicator {