nearly sorted with the rendering

This commit is contained in:
Joe 2025-04-23 12:32:44 +01:00
parent 75b66e245e
commit 8993615953
18 changed files with 204 additions and 322 deletions

View File

@ -8,7 +8,12 @@
"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": "100", "height": "100" } "image": {
"filename": "bass.svg",
"yPos": "13%",
"width": "100",
"height": "100"
}
}, },
"Snare": { "Snare": {
"directory": "/assets/instruments/snare", "directory": "/assets/instruments/snare",
@ -17,20 +22,35 @@
"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": "100", "height": "100" } "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": "100", "height": "100"} "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": "100", "height": "100" "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",
"filenames": [ "filenames": [
@ -39,17 +59,28 @@
"timpani7b-hit-v5-rr2-main.mp3", "timpani7b-hit-v5-rr2-main.mp3",
"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": "100", "height": "100" "image": {
} }, "filename": "timpani-large.svg",
"Timpani Small": { "directory": "/assets/instruments/timpani-small", "yPos": "55%",
"width": "100",
"height": "100"
}
},
"Timpani Small": {
"directory": "/assets/instruments/timpani-small",
"filenames": [ "filenames": [
"timpani1a-hit-v5-rr1-main.mp3", "timpani1a-hit-v5-rr1-main.mp3",
"timpani2-hit-v3-rr1-sum.mp3", "timpani2-hit-v3-rr1-sum.mp3",
"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": "100", "height": "100"} "image": {
"filename": "timpani-small.svg",
"yPos": "68%",
"width": "100",
"height": "100"
}
}, },
"Toms": { "Toms": {
"directory": "/assets/instruments/toms", "directory": "/assets/instruments/toms",
@ -59,6 +90,11 @@
"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": "100", "height": "100"} "image": {
"filename": "toms.svg",
"yPos": "81%",
"width": "100",
"height": "100"
}
} }
} }

View File

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23.33 18.79"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style></defs><g id="Layer_1-2"><path class="cls-1" d="M19.09,7.44c0,4.11-3.32,7.44-7.42,7.44s-7.42-3.33-7.42-7.44S7.56,0,11.66,0s7.42,3.33,7.42,7.44"/><path class="cls-2" d="M22.83,9.31c0,4.96-5,8.98-11.16,8.98S.5,14.27.5,9.31"/></g></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23.33 18.79" width="100" height="100"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style></defs><g id="Layer_1-2"><path class="cls-1" d="M19.09,7.44c0,4.11-3.32,7.44-7.42,7.44s-7.42-3.33-7.42-7.44S7.56,0,11.66,0s7.42,3.33,7.42,7.44"/><path class="cls-2" d="M22.83,9.31c0,4.96-5,8.98-11.16,8.98S.5,14.27.5,9.31"/></g></svg>

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 482 B

View File

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.27 23.95"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style></defs><g id="Layer_1-2"><polyline class="cls-2" points="14.97 23.55 .78 12.61 10.65 3.86"/><path class="cls-1" d="M14.98,0c-1.35,2.55-2.72,6.05-3.05,8.78l-1.84-4.44-4.31-2.26c2.8-.05,6.49-1.02,9.2-2.08"/><line class="cls-2" x1=".76" y1="23.55" x2="7.02" y2="17.42"/></g></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.27 23.95" width="100" height="100"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style></defs><g id="Layer_1-2"><polyline class="cls-2" points="14.97 23.55 .78 12.61 10.65 3.86"/><path class="cls-1" d="M14.98,0c-1.35,2.55-2.72,6.05-3.05,8.78l-1.84-4.44-4.31-2.26c2.8-.05,6.49-1.02,9.2-2.08"/><line class="cls-2" x1=".76" y1="23.55" x2="7.02" y2="17.42"/></g></svg>

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 526 B

View File

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.43 23.52"><defs><style>.cls-1{fill:none;stroke:#fff;stroke-miterlimit:10;stroke-width:.87px;}</style></defs><g id="Layer_1-2"><polygon class="cls-1" points="6.71 22.55 .71 10.73 12.72 10.73 6.71 22.55"/><path class="cls-1" d="M1.03,4.12h11.36M2.7.32l8.03,7.6M10.73.32L2.7,7.92"/></g></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.43 23.52" width="100" height="100"><defs><style>.cls-1{fill:none;stroke:#fff;stroke-miterlimit:10;stroke-width:.87px;}</style></defs><g id="Layer_1-2"><polygon class="cls-1" points="6.71 22.55 .71 10.73 12.72 10.73 6.71 22.55"/><path class="cls-1" d="M1.03,4.12h11.36M2.7.32l8.03,7.6M10.73.32L2.7,7.92"/></g></svg>

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 422 B

View File

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.43 23.67"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;stroke-width:.87px;}</style></defs><g id="Layer_1-2"><polygon class="cls-2" points="6.71 22.71 .71 10.89 12.72 10.89 6.71 22.71"/><path class="cls-1" d="M11.33,4.54c0,2.51-2.07,4.54-4.62,4.54S2.09,7.05,2.09,4.54,4.16,0,6.71,0s4.62,2.03,4.62,4.54"/></g></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.43 23.67" width="100" height="100"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;stroke-width:.87px;}</style></defs><g id="Layer_1-2"><polygon class="cls-2" points="6.71 22.71 .71 10.89 12.72 10.89 6.71 22.71"/><path class="cls-1" d="M11.33,4.54c0,2.51-2.07,4.54-4.62,4.54S2.09,7.05,2.09,4.54,4.16,0,6.71,0s4.62,2.03,4.62,4.54"/></g></svg>

Before

Width:  |  Height:  |  Size: 473 B

After

Width:  |  Height:  |  Size: 498 B

View File

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21.29 23.81"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style></defs><g id="Layer_1-2"><polyline class="cls-2" points="0 .5 19.76 .5 5.02 11.32 15.28 19.98"/><path class="cls-1" d="M19.84,23.81c-2.83-1.05-6.66-2.01-9.57-2.06l4.48-2.23,1.92-4.4c.35,2.7,1.76,6.17,3.18,8.69"/><line class="cls-2" x1="1.74" y1="23.81" x2="1.74" y2=".47"/></g></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21.29 23.81" width="100" height="100"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style></defs><g id="Layer_1-2"><polyline class="cls-2" points="0 .5 19.76 .5 5.02 11.32 15.28 19.98"/><path class="cls-1" d="M19.84,23.81c-2.83-1.05-6.66-2.01-9.57-2.06l4.48-2.23,1.92-4.4c.35,2.7,1.76,6.17,3.18,8.69"/><line class="cls-2" x1="1.74" y1="23.81" x2="1.74" y2=".47"/></g></svg>

Before

Width:  |  Height:  |  Size: 507 B

After

Width:  |  Height:  |  Size: 533 B

View File

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.32 23.67"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style></defs><g id="Layer_1-2"><polyline class="cls-2" points="15.02 .4 .79 11.21 10.3 19.39"/><path class="cls-1" d="M15.22,23.67c-2.73-1.05-6.43-2.01-9.24-2.06l4.33-2.23,1.85-4.39c.34,2.7,1.7,6.16,3.07,8.68"/></g></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.32 23.67" width="100" height="100"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style></defs><g id="Layer_1-2"><polyline class="cls-2" points="15.02 .4 .79 11.21 10.3 19.39"/><path class="cls-1" d="M15.22,23.67c-2.73-1.05-6.43-2.01-9.24-2.06l4.33-2.23,1.85-4.39c.34,2.7,1.7,6.16,3.07,8.68"/></g></svg>

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 465 B

View File

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.43 23.96"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{stroke-miterlimit:10;}.cls-2,.cls-3{fill:none;stroke:#fff;stroke-width:.87px;}</style></defs><g id="Layer_1-2"><polygon class="cls-2" points="6.71 22.99 .71 11.18 12.72 11.18 6.71 22.99"/><path class="cls-1" d="M9.11,7.86c0,1.3-1.07,2.36-2.4,2.36s-2.4-1.05-2.4-2.36,1.07-2.36,2.4-2.36,2.4,1.06,2.4,2.36"/><path class="cls-1" d="M9.11,2.36c0,1.3-1.07,2.36-2.4,2.36s-2.4-1.06-2.4-2.36S5.39,0,6.71,0s2.4,1.06,2.4,2.36"/><line class="cls-3" x1="3.46" y1="16.34" x2="9.97" y2="16.34"/></g></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.43 23.96" width="100" height="100"><defs><style>.cls-1{fill:#fff;stroke-width:0px;}.cls-2{stroke-miterlimit:10;}.cls-2,.cls-3{fill:none;stroke:#fff;stroke-width:.87px;}</style></defs><g id="Layer_1-2"><polygon class="cls-2" points="6.71 22.99 .71 11.18 12.72 11.18 6.71 22.99"/><path class="cls-1" d="M9.11,7.86c0,1.3-1.07,2.36-2.4,2.36s-2.4-1.05-2.4-2.36,1.07-2.36,2.4-2.36,2.4,1.06,2.4,2.36"/><path class="cls-1" d="M9.11,2.36c0,1.3-1.07,2.36-2.4,2.36s-2.4-1.06-2.4-2.36S5.39,0,6.71,0s2.4,1.06,2.4,2.36"/><line class="cls-3" x1="3.46" y1="16.34" x2="9.97" y2="16.34"/></g></svg>

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 688 B

View File

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 330.51 33.72"><defs><style>.cls-1{stroke:#828383;stroke-width:.5px;}.cls-1,.cls-2{fill:none;}.cls-3{clip-path:url(#clippath-2);}.cls-2{stroke-width:0px;}.cls-4{clip-path:url(#clippath-1);}.cls-5{opacity:.75;}.cls-6{clip-path:url(#clippath);}</style><clipPath id="clippath"><rect class="cls-2" width="330.51" height="33.72"/></clipPath><clipPath id="clippath-1"><rect class="cls-2" y="0" width="330.51" height="33.72"/></clipPath><clipPath id="clippath-2"><rect class="cls-2" x="-25" y="-22" width="380.51" height="56.72"/></clipPath></defs><g id="Layer_1-2"><g class="cls-6"><g class="cls-4"><g class="cls-5"><g class="cls-3"><rect class="cls-1" x=".25" y=".25" width="33" height="33.22"/><rect class="cls-1" x="132.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x="66.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x="198.27" y=".25" width="33" height="33.22"/><rect class="cls-1" x="264.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x="33.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x="165.27" y=".25" width="33" height="33.22"/><rect class="cls-1" x="99.25" y=".25" width="33" height="33.22"/><rect class="cls-1" x="231.27" y=".25" width="33" height="33.22"/><rect class="cls-1" x="297.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x=".25" y=".25" width="330.01" height="6.56"/><rect class="cls-1" x=".25" y="13.58" width="330.01" height="6.56"/><rect class="cls-1" x=".25" y="6.91" width="330.01" height="6.56"/><rect class="cls-1" x=".25" y="20.24" width="330.01" height="6.56"/><rect class="cls-1" x=".25" y="26.91" width="330.01" height="6.56"/></g></g></g></g></g></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 330.51 33.72" ><defs><style>.cls-1{stroke:#828383;stroke-width:.5px;}.cls-1,.cls-2{fill:none;}.cls-3{clip-path:url(#clippath-2);}.cls-2{stroke-width:0px;}.cls-4{clip-path:url(#clippath-1);}.cls-5{opacity:.75;}.cls-6{clip-path:url(#clippath);}</style><clipPath id="clippath"><rect class="cls-2" width="330.51" height="33.72"/></clipPath><clipPath id="clippath-1"><rect class="cls-2" y="0" width="330.51" height="33.72"/></clipPath><clipPath id="clippath-2"><rect class="cls-2" x="-25" y="-22" width="380.51" height="56.72"/></clipPath></defs><g id="Layer_1-2"><g class="cls-6"><g class="cls-4"><g class="cls-5"><g class="cls-3"><rect class="cls-1" x=".25" y=".25" width="33" height="33.22"/><rect class="cls-1" x="132.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x="66.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x="198.27" y=".25" width="33" height="33.22"/><rect class="cls-1" x="264.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x="33.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x="165.27" y=".25" width="33" height="33.22"/><rect class="cls-1" x="99.25" y=".25" width="33" height="33.22"/><rect class="cls-1" x="231.27" y=".25" width="33" height="33.22"/><rect class="cls-1" x="297.26" y=".25" width="33" height="33.22"/><rect class="cls-1" x=".25" y=".25" width="330.01" height="6.56"/><rect class="cls-1" x=".25" y="13.58" width="330.01" height="6.56"/><rect class="cls-1" x=".25" y="6.91" width="330.01" height="6.56"/><rect class="cls-1" x=".25" y="20.24" width="330.01" height="6.56"/><rect class="cls-1" x=".25" y="26.91" width="330.01" height="6.56"/></g></g></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -15,11 +15,6 @@
<div class="loading"> <div class="loading">
</div> </div>
</div> </div>
<div id="buttons">
<img src="/assets/svg/mute.svg" class="audio" id="mute" />
<img src="/assets/svg/menu.svg" class="menu" id="menu-button" />
</div>
<div id="main-content"> <div id="main-content">
@ -34,61 +29,10 @@
<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>
</div> </div>
<div id="about-content">
<div class="about-title">
<img src="/assets/svg/the-conductor-title.svg"></img>
</div>
<div class="about-content">
<div class="safari">
<div class="instruments-key" id="instrument-key-div"></div>
</div>
<p>
The Conductor translates live lightning strikes from around the world into a graphic score for seven percussion
instruments.
</p>
<p>
The project was conceived and developed by the artist Mishka Henner between 2023 and 2024 with a team of
musicians, sound artists and acoustics engineers during an artist's residency at Energy House 2.0 at the
University of Salford.
</p>
<p>
The Conductor was first performed at the University of Salfords Acoustic Laboratories during the Sounds From
the Other City Festival on 5 May 2024.
</p>
<p>
Click <a href="https://mishkahenner.com/The-Conductor" style="color: #fff; text-decoration:underline">here</a>
to read more about the project. Download a copy of the artist's graphic score <a
href="https://files.cargocollective.com/c20096/CONDUCTOR-SHEET-MUSIC-Black-BG.pdf"
style="color: #fff; text-decoration:underline">here</a>.
</p>
<br />
<div class="production-credits">
<br />
<div>
Project Conception and Design by Mishka Henner.
</div>
Data Capture, Web Design and Coding by Joe Gibson.
<div>
Lightning data from Blitzortung.org
</div>
<p>
With support from the University of Salford Art Collection in partnership with Open Eye Gallery, Liverpool as
part of the LOOK Photo Biennial, and Castlefield Gallery, Manchester. Generously supported by Friends of
Energy House Labs.
</p>
</div>
</div>
</div>
<script type="module" src="/src/script.js"></script> <script type="module" src="/src/script.js"></script>
</body> </body>

View File

@ -16,3 +16,16 @@ sort out the blitzortung connection issue
make the text max size smaller make the text max size smaller
about page make title smaller about page make title smaller
lOOK AT SPACING BETWEEN THE TITLE AND STAVES AND THEN MAYBE ALSO THE STAVE SPACING
TRY BOTH THE SVG RENDER AND THE RASTER RENDERING APPROACH...
SCROLL BAR MUST GO (VERTICAL)
Remove all passwords and google accounts from laptop.
Ensure it boots into landscape mode

11
planeNotes.txt Normal file
View File

@ -0,0 +1,11 @@
placeIcon method:
Now img is an <img> element - does it have a img.naturalHeight/Width property?
Check because we are using these values to draw the img.
What is the sheetWindow.offsetHeight? - This is probably okay but take a look.
Moving forward:
We need to measure the stavewrapper and place the icons using an offset factor based on the stave number.

View File

@ -35,37 +35,7 @@ export class Conductor {
showMainContent() { showMainContent() {
Conductor.setTitle(); 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("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) { fadeAndReplaceADiv(innyDivId, outtyDivId) {
@ -87,20 +57,6 @@ export class Conductor {
} }
async init() { async init() {
const instrumentsKey = document.getElementById("instrument-key-div");
this.instruments = await loadInstruments(); 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

@ -3,64 +3,64 @@ async function loadInstrumentsConfig() {
return await response.json(); return await response.json();
} }
async function fetchAudioData(url) { async function fetchSvgText(url) {
const response = await fetch(url); const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer(); return await response.text();
return arrayBuffer;
} }
async function decodeAudioData(context, arrayBuffer) { function svgTextToImage(svgText, defaultWidth = 100, defaultHeight = 100) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
context.decodeAudioData(arrayBuffer, 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();
async function loadAudioSamples(context, directory, filenames) { // Set the onload callback
const audioFiles = filenames; img.onload = () => {
// Check if naturalWidth and naturalHeight are invalid
const audioBuffers = {}; if (img.naturalWidth === 0 || img.naturalHeight === 0) {
for (const file of audioFiles) { // Set default dimensions if image is invalid (missing width/height in SVG)
const arrayBuffer = await fetchAudioData(`${directory}/${file}`); img.width = defaultWidth;
const audioBuffer = await decodeAudioData(context, arrayBuffer); img.height = defaultHeight;
const fileName = file.split("/").pop(); // Extract filename without path for reference
audioBuffers[fileName] = audioBuffer;
} }
return audioBuffers; // Clean up the object URL to free memory
} URL.revokeObjectURL(url);
async function fetchImageData(url) { resolve(img);
const response = await fetch(url); };
const blob = await response.blob();
return new Promise((resolve, reject) => { // Handle image loading errors
const reader = new FileReader(); img.onerror = (err) => {
reader.onloadend = () => resolve(reader.result); URL.revokeObjectURL(url); // Clean up URL
reader.onerror = reject; reject(err);
reader.readAsDataURL(blob); // Convert to base64 };
img.src = url;
}); });
} }
export async function loadInstruments() { export async function loadInstruments() {
const instrumentsConfig = await loadInstrumentsConfig(); const instrumentsConfig = await loadInstrumentsConfig();
const context = new (window.AudioContext || window.webkitAudioContext)();
const instruments = {}; const instruments = {};
for (const [instrument, config] of Object.entries(instrumentsConfig)) { for (const [instrument, config] of Object.entries(instrumentsConfig)) {
const samples = await loadAudioSamples( const svgText = await fetchSvgText(
context,
config.directory,
config.filenames
);
const imageData = await fetchImageData(
`${config.directory}/${config.image.filename}` `${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] = { instruments[instrument] = {
samples: samples, image: img,
image: imageData, yPos: config.image.yPos,
yPos: config.image.yPos, // Store base64-encoded image data
midiChannelName: config["midi-channel-name"], midiChannelName: config["midi-channel-name"],
width: config.image.width, width: config.image.width,
height: config.image.width, height: config.image.height,
}; };
} }

View File

@ -5,27 +5,20 @@ export class Renderer {
currStaveNumber; currStaveNumber;
numStaves; numStaves;
sheetWindow; sheetWindow;
eventCanvas; canvases = [];
ctx;
canvasWidth; canvasWidth;
canvasHeight; canvasHeight;
iconCache = []; // stores { image, x, y, width, height } iconCache = [];
imageCache = new Map(); // memoized <svgString, HTMLImageElement> iconScale = 0.25;
constructor() { constructor() {
this.numStaves = 6; this.numStaves = 6;
this.currStaveNumber = 1; this.currStaveNumber = 1;
this.timeIndicator = document.querySelector("#time-indicator"); this.timeIndicator = document.getElementById("time-indicator");
this.sheetWindow = document.getElementById("music-window"); 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", () => { window.addEventListener("resize", () => {
this.resizeCanvas();
this.redrawIcons(); this.redrawIcons();
}); });
@ -36,9 +29,15 @@ export class Renderer {
} }
}); });
// 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");
const canvas = document.createElement("canvas");
canvas.id = `canvas-${i}`;
canvas.className = "event-canvas";
const ctx = canvas.getContext("2d");
this.canvases.push({ canvas, ctx });
staveWrapper.classList.add("stave-wrapper"); staveWrapper.classList.add("stave-wrapper");
staveWrapper.setAttribute("id", `stave-wrapper-${i}`); staveWrapper.setAttribute("id", `stave-wrapper-${i}`);
staveWrapper.style.position = "relative"; staveWrapper.style.position = "relative";
@ -48,17 +47,17 @@ export class Renderer {
staveObject.data = "assets/svg/stave.svg"; staveObject.data = "assets/svg/stave.svg";
staveObject.classList.add("stave-svg"); staveObject.classList.add("stave-svg");
staveWrapper.appendChild(canvas);
staveWrapper.appendChild(staveObject); staveWrapper.appendChild(staveObject);
this.sheetWindow.appendChild(staveWrapper); this.sheetWindow.appendChild(staveWrapper);
}
}
resizeCanvas() { // Set canvas size after layout is applied
this.canvasWidth = this.sheetWindow.offsetWidth; requestAnimationFrame(() => {
this.canvasHeight = this.sheetWindow.offsetHeight; const rect = canvas.getBoundingClientRect();
this.eventCanvas.width = this.canvasWidth; canvas.width = rect.width;
this.eventCanvas.height = this.canvasHeight; canvas.height = rect.height;
console.log("Canvas size:", this.canvasWidth, this.canvasHeight); });
}
} }
async placeIcon(instrument) { async placeIcon(instrument) {
@ -68,112 +67,47 @@ export class Renderer {
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 yPercent = parseFloat(instrument.yPos); // e.g., "55%" => 55 const canvasEntry = this.canvases[this.currStaveNumber - 1];
const y = (yPercent / 100) * this.canvasHeight; const canvas = canvasEntry.canvas;
const ctx = canvasEntry.ctx;
// Handle image caching const yPercent = parseFloat(instrument.yPos);
let svgString = instrument.image; const y = (yPercent / 100) * canvas.height;
if (svgString.startsWith("data:image/svg+xml;base64,")) {
const base64 = svgString.split(",")[1];
svgString = atob(base64);
}
// Ensure SVG has dimensions const width = instrument.width * this.iconScale;
svgString = this.ensureSvgHasDimensions( const height = instrument.height * this.iconScale;
svgString,
instrument.width || 50,
instrument.height || 50
);
const img = await this.loadImageFromSVG(svgString); ctx.drawImage(instrument.image, xPosition, y, width, height);
// Fallback if instrument.width/height not provided
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({ this.iconCache.push({
image: img, image: instrument.image,
x: xPosition, x: xPosition,
y, y,
width, width,
height, height,
}); stave: this.currStaveNumber,
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>`
);
}
return svgString;
}
async loadImageFromSVG(svgString) {
if (this.imageCache.has(svgString)) {
return this.imageCache.get(svgString);
}
return new Promise((resolve, reject) => {
const blob = new Blob([svgString], { type: "image/svg+xml" });
const url = URL.createObjectURL(blob);
const img = new Image();
img.onload = () => {
console.log("Image loaded:", img.width, img.height); // should no longer be 0
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;
}); });
} }
redrawIcons() { redrawIcons() {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); this.canvases.forEach(({ ctx, canvas }) => {
this.iconCache.forEach(({ image, x, y, width, height }) => { ctx.clearRect(0, 0, canvas.width, canvas.height);
this.ctx.drawImage(image, x, y, width, height); });
this.iconCache.forEach(({ image, x, y, width, height, stave }) => {
const ctx = this.canvases[stave - 1].ctx;
ctx.drawImage(image, x, y, width, height);
}); });
} }
cleanUpAndRestart(reload = false) { cleanUpAndRestart(reload = false) {
this.iconCache = []; this.iconCache = [];
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.currStaveNumber = 1; this.currStaveNumber = 1;
this.canvases.forEach(({ ctx, canvas }) => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
Conductor.setTitle(); Conductor.setTitle();
if (reload) location.reload(); if (reload) location.reload();
} }

View File

@ -1,7 +1,6 @@
import { sendMidiMessage } from "./midi.js"; 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 { Renderer } from "./render.js"; import { Renderer } from "./render.js";
document.addEventListener("DOMContentLoaded", async () => { document.addEventListener("DOMContentLoaded", async () => {
@ -21,17 +20,11 @@ document.addEventListener("DOMContentLoaded", async () => {
const selectedInstrument = instruments[keys[randomIndex]]; const selectedInstrument = instruments[keys[randomIndex]];
renderer.placeIcon(selectedInstrument); renderer.placeIcon(selectedInstrument);
playSound(selectedInstrument);
// **** add midi trigger +++++++++ ///
}); });
dataStream.init(); dataStream.init();
// let currStaveNumber = 1;
conductor.showMainContent(); conductor.showMainContent();
// let strikeNumber = 0;
// setupWebSocketListeners(); // Setup the WebSocket listeners
// resetTimeout(); // Start the timeout timer
}); });

View File

@ -7,6 +7,8 @@ export class DataStream extends EventTarget {
this.client = null; this.client = null;
this.lastReceived = null; this.lastReceived = null;
this.connectionTimeout = null; this.connectionTimeout = null;
this.test = false;
this.testInterval = null;
} }
async init() { async init() {
@ -38,6 +40,7 @@ export class DataStream extends EventTarget {
} }
setupWebSocketListeners() { setupWebSocketListeners() {
if (this.test === false) {
this.client.removeAllListeners?.("data"); this.client.removeAllListeners?.("data");
this.client.removeAllListeners?.("error"); this.client.removeAllListeners?.("error");
this.client.removeAllListeners?.("close"); this.client.removeAllListeners?.("close");
@ -58,5 +61,11 @@ export class DataStream extends EventTarget {
console.log("WebSocket closed, attempting to reconnect..."); console.log("WebSocket closed, attempting to reconnect...");
this.reconnectWebSocket(); this.reconnectWebSocket();
}); });
} else {
clearInterval(this.testInterval);
this.testInterval = setInterval(() => {
this.dispatchEvent(new CustomEvent("strike", { detail: "fakeData" }));
}, 1000);
}
} }
} }

View File

@ -1,4 +1,3 @@
body { body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -8,6 +7,7 @@ body {
overflow: hidden; overflow: hidden;
} }
object { object {
margin-bottom: 10px; margin-bottom: 10px;
} }
@ -74,14 +74,16 @@ html {
transition: opacity 1s ease; transition: opacity 1s ease;
} }
#event-canvas { .event-canvas {
background: rgba(255, 0, 0, 0.2); /* light red overlay */ background: rgba(255, 0, 0, 0.2); /* light red overlay */
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
z-index: 1000; width: 100%;
} height:100%;
pointer-events: none;
z-index: 9999;
}
.logo { .logo {
color: #fff; color: #fff;
@ -96,10 +98,6 @@ html {
margin: 20px; margin: 20px;
} }
.event-icon {
width: 1vw;
min-width: 10px;
}
.instruments-key { .instruments-key {
position: relative; position: relative;
@ -174,6 +172,7 @@ html {
top: 30px; top: 30px;
width: 50%; width: 50%;
} }
.title-title { .title-title {
font-family: Helvetica; font-family: Helvetica;
color: #fff; color: #fff;
@ -191,42 +190,28 @@ html {
.stave-wrapper { .stave-wrapper {
position: relative; position: relative;
background-color: rgb(24,24,24); background-color: rgb(24,24,24);
border: 1px solid rgb(24,24,24); border: 1px solid rgb(99,99,99);
padding-top: 30px; padding-top: 30px;
padding-bottom: 20px; padding-bottom: 20px;
z-index: 100;
}
.stave-svg {
position: relative;
z-index: 1; z-index: 1;
} }
.time-indicator {
top: 0;
left: 0;
position: absolute;
width: 2px;
/* height: 100%; */
animation: moveRight 10s linear infinite;
z-index: 999;
}
.stave-svg {
position: relative;
z-index: 1; /* Same here */
}
#event-canvas {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
z-index: 999;
}
#time-indicator { #time-indicator {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 2px; width: 2px;
background-color: red; background-color: red;
z-index: 3; z-index: 999;
animation: moveRight 10s linear infinite;
will-change: transform; will-change: transform;
} }
.fade-in { .fade-in {
opacity: 1; opacity: 1;
@ -244,7 +229,8 @@ html {
to { to {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
@keyframes moveRight { @keyframes moveRight {
0% { 0% {
left: 0; left: 0;