diff --git a/assets/instruments/instruments.json b/assets/instruments/instruments.json index fe045ab..1137269 100644 --- a/assets/instruments/instruments.json +++ b/assets/instruments/instruments.json @@ -7,6 +7,7 @@ "bdrum3-fff-rr1.mp3", "bdrumnew-hit-v7-rr1-sum-(1).mp3" ], + "midi-channel-name": "conductorOrchestralBass", "image": { "filename": "bass.svg", "yPos": "13%" } }, "Snare": { @@ -15,16 +16,19 @@ "ropesnare-low-ns-main-vl3-rr2.mp3", "ropesnare-low-tsn-main-vl4-rr1.mp3" ], + "midi-channel-name": "conductorSnare", "image": { "filename": "snare.svg", "yPos": "26%" } }, "Surdo": { "directory": "/assets/instruments/surdo", "filenames": ["surdo-5.mp3", "surdo-6.mp3", "surdo-7.mp3"], + "midi-channel-name":"conductorSnare", "image": { "filename": "surdo.svg", "yPos": "39%" } }, "Surdo Napa": { "directory": "/assets/instruments/surdo-napa", "filenames": ["surdo-1.mp3", "surdo-3.mp3", "surdo-4.mp3"], + "midi-channel-name": "conductorSurdoNapa", "image": { "filename": "surdo-napa.svg", "yPos": "42%" } }, "Timpani Large": { @@ -32,20 +36,19 @@ "filenames": [ "timpani1-hit-v4-rr1-sum.mp3", "timpani2-roll-v3-rr1-sum.mp3", - "timpani2-roll-v3-rr1-sum.wav", "timpani7b-hit-v5-rr2-main.mp3", "timpani7d-hit-v2-rr1-main.mp3" ], + "midi-channel-name":"conductorTimpaniLarge", "image": { "filename": "timpani-large.svg", "yPos": "55%" } }, - "Timpani Small": { - "directory": "/assets/instruments/timpani-small", + "Timpani Small": { "directory": "/assets/instruments/timpani-small", "filenames": [ "timpani1a-hit-v5-rr1-main.mp3", - "timpani1-roll-v3-rr1-sum.wav", "timpani2-hit-v3-rr1-sum.mp3", "timpani4-hit-v2-rr1-sum.mp3" ], + "midi-channel-name": "conductorTimpaniSmall", "image": { "filename": "timpani-small.svg", "yPos": "68%" } }, "Toms": { @@ -55,6 +58,7 @@ "toml-hitm-v4-rr2-mid.mp3", "toml-rollm-v2-rr1-mid.mp3" ], + "midi-channel-name": "conductorToms", "image": { "filename": "toms.svg", "yPos": "81%" } } } diff --git a/index.html b/index.html index b2d06b1..01a145d 100644 --- a/index.html +++ b/index.html @@ -16,15 +16,17 @@
- - + +
+ +
-
- The Conductor -
+
+ The Conductor +
15.5.2024 08:00:00 - 08:01:00 UTC @@ -46,19 +48,26 @@

- The Conductor translates live lightning strikes from around the world into a graphic score for seven percussion instruments. + The Conductor translates live lightning strikes from around the world into a graphic score for seven percussion + instruments.

- - + +

- 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. + 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.

- The Conductor was first performed at the University of Salford’s Acoustic Laboratories during the Sounds From the Other City Festival on 5 May 2024. + The Conductor was first performed at the University of Salford’s Acoustic Laboratories during the Sounds From + the Other City Festival on 5 May 2024.

- Click here to read more about the project. Download a copy of the artist's graphic score here. + Click here + to read more about the project. Download a copy of the artist's graphic score here.


@@ -66,16 +75,18 @@
Project Conception and Design by Mishka Henner.
- Data Capture, Web Design and Coding by Joe Gibson. + Data Capture, Web Design and Coding by Joe Gibson.
Lightning data from Blitzortung.org

- 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. + 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.

-
+ diff --git a/src/instruments.js b/src/instruments.js index 352135d..36bbaf2 100644 --- a/src/instruments.js +++ b/src/instruments.js @@ -17,7 +17,6 @@ async function decodeAudioData(context, arrayBuffer) { async function loadAudioSamples(context, directory, filenames) { const audioFiles = filenames; - console.log(filenames); const audioBuffers = {}; for (const file of audioFiles) { @@ -59,6 +58,7 @@ export async function loadInstruments() { samples: samples, image: imageData, yPos: config.image.yPos, // Store base64-encoded image data + midiChannelName: config["midi-channel-name"], }; } diff --git a/src/midi.js b/src/midi.js new file mode 100644 index 0000000..1a4eb14 --- /dev/null +++ b/src/midi.js @@ -0,0 +1,30 @@ +export async function sendMidiMessage(midiChannelName, sampleNumber ) { + try { + const midiAccess = await navigator.requestMIDIAccess(); + const availableMidiOutputs = midiAccess.outputs.values(); + let midiOut; + + if (availableMidiOutputs.length === 0) { + console.error("No MIDI output devices found."); + return; + } + + for ( const availableMidiOutput of availableMidiOutputs){ + if (availableMidiOutput.name === midiChannelName){ + midiOut = availableMidiOutput; + break; + } + } + + // MIDI Note On (Play Note) + midiOut.send([0x90, 36 + sampleNumber, 127]); + + // MIDI Note Off after 500ms + setTimeout(() => { + midiOut.send([0x80, 36 + sampleNumber, 0]); // Stop Note + }, 500); + + } catch (error) { + console.error("Failed to get MIDI access:", error); + } +} diff --git a/src/script.js b/src/script.js index da3470c..7c57d7e 100644 --- a/src/script.js +++ b/src/script.js @@ -1,12 +1,15 @@ import { Client, BrowserSocketFactory } from "./blitz.js"; import { loadInstruments } from "./instruments.js"; +import { sendMidiMessage } from "./midi.js"; document.addEventListener("DOMContentLoaded", async () => { let showing; let audioContext; - let interacted = false; + let interacted = true; let gainNode; + sendMidiMessage(); + function startAudio() { audioContext = new (window.AudioContext || window.webkitAudioContext)(); gainNode = audioContext.createGain(); @@ -19,7 +22,7 @@ document.addEventListener("DOMContentLoaded", async () => { } function unMute() { - startAudio(); + startAudio(); fadeInVolume(); } @@ -73,7 +76,6 @@ document.addEventListener("DOMContentLoaded", async () => { showing = "main-content"; document.getElementById("menu-button").onclick = function () { - console.log(showing); toggleInfo(); }; } @@ -141,6 +143,7 @@ document.addEventListener("DOMContentLoaded", async () => { client.removeAllListeners("close"); client.on("data", (strike) => { + lastReceived = Date.now(); // Update the last received timestamp resetTimeout(); // Reset the timeout on any data received @@ -173,7 +176,6 @@ document.addEventListener("DOMContentLoaded", async () => { let currStaveNumber = 1; const instruments = await loadInstruments(); - console.log(instruments); const instNames = Object.keys(instruments); showMainContent(); @@ -184,7 +186,6 @@ document.addEventListener("DOMContentLoaded", async () => { const instrumentsKey = document.getElementById("instrument-key-div"); for (const instrument of instNames) { - console.log(instrument); const keyDiv = document.createElement("div"); const keySvg = document.createElement("object"); const keyName = document.createElement("div"); @@ -239,16 +240,18 @@ document.addEventListener("DOMContentLoaded", async () => { const playSound = (instrument) => { if (interacted) { - const source = audioContext.createBufferSource(); +// const source = audioContext.createBufferSource(); const samples = instrument.samples; const sampleKeys = Object.keys(samples); const numSamples = sampleKeys.length; - const randomKey = - sampleKeys[Math.floor(Math.random() * (numSamples - 1))]; + const randomSampleNumber = Math.floor(Math.random() * (numSamples - 1)); +// const randomKey = +// sampleKeys[randomSampleNumber]; + sendMidiMessage(instrument.midiChannelName, randomSampleNumber); - source.buffer = samples[`${randomKey}`]; - source.connect(gainNode); - source.start(); +// source.buffer = samples[`${randomKey}`]; +// source.connect(gainNode); +// source.start(); } }; diff --git a/styles.css b/styles.css index 67d0404..f9feceb 100644 --- a/styles.css +++ b/styles.css @@ -5,7 +5,7 @@ body { align-items: center; background-color: rgb(24, 24, 24); height: 100vh; - overflow-x: hidden; + overflow: hidden; } object { @@ -17,6 +17,9 @@ html { overflow-x: hidden; } +* { + box-sizing: border-box; + } #main-content { position: absolute; @@ -29,6 +32,7 @@ html { align-items: center; opacity: 0; transition: opacity 2s ease; + overflow: hidden; }