Compare commits

..

2 Commits

Author SHA1 Message Date
Joe 8993615953 nearly sorted with the rendering 2025-04-23 12:32:44 +01:00
Joe 75b66e245e this is garbage from chatty mc fuckin chat face 2025-04-18 14:35:30 +01:00
18 changed files with 213 additions and 282 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": "15vw", "height": "15vw" } "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": "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",
"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": "15vw", "height": "15vw" "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": "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 +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": "15vw", "height": "15vw"} "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 drawn icons iconCache = [];
imageCache = new Map(); // for memoizing loaded images 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,92 +47,67 @@ 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;
});
}
} }
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 canvasEntry = this.canvases[this.currStaveNumber - 1];
newObject.classList.add("event-icon"); const canvas = canvasEntry.canvas;
newObject.style.position = "absolute"; const ctx = canvasEntry.ctx;
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 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);
}
const parser = new DOMParser(); const width = instrument.width * this.iconScale;
const svgDoc = parser.parseFromString(svgString, "image/svg+xml"); const height = instrument.height * this.iconScale;
const svgElement = svgDoc.documentElement;
// ✅ Handle parsing error ctx.drawImage(instrument.image, xPosition, y, width, height);
if (svgElement.nodeName === "parsererror") {
console.error("Failed to parse SVG:", svgString);
return;
}
svgElement.style.width = "100%"; this.iconCache.push({
svgElement.style.height = "100%"; image: instrument.image,
x: xPosition,
newObject.appendChild(svgElement); y,
targetWrapper.appendChild(newObject); width,
} height,
stave: this.currStaveNumber,
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() { redrawIcons() {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); this.canvases.forEach(({ ctx, canvas }) => {
this.iconCache.forEach(({ image, x, y }) => { ctx.clearRect(0, 0, canvas.width, canvas.height);
this.ctx.drawImage(image, x, y, 24, 24); // Adjust size as needed });
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,9 +74,16 @@ html {
transition: opacity 1s ease; transition: opacity 1s ease;
} }
#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;
width: 100%;
height:100%;
pointer-events: none;
z-index: 9999;
}
.logo { .logo {
color: #fff; color: #fff;
@ -91,10 +98,6 @@ html {
margin: 20px; margin: 20px;
} }
.event-icon {
width: 1vw;
min-width: 10px;
}
.instruments-key { .instruments-key {
position: relative; position: relative;
@ -169,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;
@ -186,38 +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;
} }
.time-indicator { .stave-svg {
top: 0; position: relative;
left: 0; z-index: 1;
position: absolute;
width: 2px;
/* height: 100%; */
animation: moveRight 10s linear infinite;
z-index: 3;
} }
#event-canvas {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
z-index: 2;
}
#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;
@ -235,7 +229,8 @@ html {
to { to {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
@keyframes moveRight { @keyframes moveRight {
0% { 0% {
left: 0; left: 0;