async function loadInstrumentsConfig() { const response = await fetch("/assets/instruments/instruments.json"); return await response.json(); } async function fetchSvgText(url) { const response = await fetch(url); return await response.text(); } function svgTextToImage(svgText, defaultWidth = 100, defaultHeight = 100) { return new Promise((resolve, reject) => { // Create a blob from the SVG text const blob = new Blob([svgText], { type: "image/svg+xml" }); const url = URL.createObjectURL(blob); const img = new Image(); // Set the onload callback img.onload = () => { // Check if naturalWidth and naturalHeight are invalid if (img.naturalWidth === 0 || img.naturalHeight === 0) { // Set default dimensions if image is invalid (missing width/height in SVG) img.width = defaultWidth; img.height = defaultHeight; } // Clean up the object URL to free memory URL.revokeObjectURL(url); resolve(img); }; // Handle image loading errors img.onerror = (err) => { URL.revokeObjectURL(url); // Clean up URL reject(err); }; img.src = url; }); } export async function loadInstruments() { const instrumentsConfig = await loadInstrumentsConfig(); const instruments = {}; for (const [instrument, config] of Object.entries(instrumentsConfig)) { const svgText = await fetchSvgText( `${config.directory}/${config.image.filename}` ); // Pass the width and height to the svgTextToImage function const img = await svgTextToImage( svgText, config.image.width || 100, config.image.height || 100 ); instruments[instrument] = { image: img, yPos: config.image.yPos, midiChannelName: config["midi-channel-name"], width: config.image.width, height: config.image.height, }; } return instruments; }