An Introduction to the Web MIDI API - Project
by Keith McMillen
HTML
<div id="content">
<div class="button" data-key="q" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-kick.mp3"></div>
<div class="button" data-key="w" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-snare.mp3"></div>
<div class="button" data-key="e" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-hat-2.mp3"></div>
<div class="button" data-key="r" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-cym.mp3"></div>
<div class="button" data-key="t" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-cym-noise.mp3"></div>
</div>
<div id="device_info">
<div id="key_data"></div>
</div>
CSS
*:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
h4 {
margin: 0 0 5px 0;
}
p {
margin: 0 0 10px 0;
}
#content, #device_info {
max-width: 800px;
/*height: 125px;*/
margin: 0 auto;
padding: 10px 0;
font-family: sans-serif;
font-size: 12px;
line-height: 12px;
letter-spacing: 1.5px;
}
#content, #key_data {
margin-top: 0px;
text-align: center;
}
#inputs, #outputs {
display: inline-block;
width: 49%;
margin-top: 10px;
vertical-align: top;
}
#outputs {
text-align: right;
}
.info {
padding: 20px;
border-radius: 3px;
box-shadow: inset 0 0 10px #ccc;
background-color: rgba(233, 233, 233, 0.25);
}
.small {
border-bottom: 1px solid #ccc;
margin-left: 10px;
}
p:not(.small) {
text-transform: uppercase;
font-weight: 800;
}
.button {
display: inline-block;
width: 100px;
height: 100px;
margin: 10px;
background-color: #00adef;
border-radius: 10px;
opacity: 1;
cursor: pointer;
border: 2px solid white;
transition: all 0.2s;
}
.button.active {
background-color: #9a6aad;
opacity: 0.25;
box-shadow: inset 0px 0px 30px orange;
border: 2px solid rgba(100, 100, 100, 0.3);
animation: shake .2s ease-in-out;
}
@keyframes shake {
0% {
transform: translateX(0);
transform: translateY(0);
transform: scale(1, 1);
}
20% {
transform: translateX(-10px);
transform: translateY(-100px);
transform: scale(0.5, 0.75);
}
40% {
transform: translateX(10px);
transform: translateY(0px);
transform: scale(1.5, 2);
}
60% {
transform: translateX(-10px);
transform: translateY(-50px);
transform: scale(0.5, 0.75);
}
80% {
transform: translateX(10px);
transform: translateY(50px);
transform: scale(1.3, 2);
}
100% {
transform:...
JavaScript
var log = console.log.bind(console),
keyData = document.getElementById("key_data"),
midi
var AudioContext
var context
var btnBox = document.getElementById("content"),
btn = document.getElementsByClassName("button")
var data, cmd, channel, type, note, velocity
try {
AudioContext = window.AudioContext || window.webkitAudioContext // for ios/safari
context = new AudioContext()
} catch (e) {
alert("Web Audio API is not supported in this browser")
}
// request MIDI access
if (navigator.requestMIDIAccess) {
navigator
.requestMIDIAccess({
sysex: false,
})
.then(onMIDISuccess, onMIDIFailure)
} else {
alert("No MIDI support in your browser.")
}
// add event listeners
document.addEventListener("keydown", keyController)
document.addEventListener("keyup", keyController)
for (var i = 0; i < btn.length; i++) {
btn[i].addEventListener("mousedown", clickPlayOn)
btn[i].addEventListener("mouseup", clickPlayOff)
}
// prepare audio files
for (var i = 0; i < btn.length; i++) {
addAudioProperties(btn[i])
}
// this maps the MIDI key value (60 - 64) to our samples
var sampleMap = {
key60: 1,
key61: 2,
key62: 3,
key63: 4,
key64: 5,
}
// user interaction, mouse click
function clickPlayOn(e) {
e.target.classList.add("active")
e.target.play()
}
function clickPlayOff(e) {
e.target.classList.remove("active")
}
// qwerty keyboard controls. [q,w,e,r,t]
function keyController(e) {
if (e.type == "keydown") {
switch (e.keyCode) {
case 81:
btn[0].classList.add("active")
btn[0].play()
break
case 87:
btn[1].classList.add("active")
btn[1].play()
break
case 69:
btn[2].classList.add("active")
btn[2].play()
break
case 82:
btn[3].classList.add("active")
btn[3].play()
break
case 84:
btn[4].classList.add("active")
btn[4].play()
break
default:
//console.log(e);
}
} else if...