forked from chrisdonahue/music-cocreation-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 0
/
script.js
97 lines (82 loc) · 2.96 KB
/
script.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
window.my = window.my || {};
(function(tone, my) {
const RUN_TEST = false;
const LOWEST_PIANO_KEY_MIDI_PITCH = 21;
async function init() {
// (Optional) Run test case
if (RUN_TEST) {
await my.testPianoGenieDecoder();
}
// Initialize Piano Genie
const pianoGenie = new my.PianoGenie();
await pianoGenie.init();
// Retrieve UI buttons
const buttonEls = document.getElementsByTagName("button");
// Initialize synthesizer state
const heldButtonToMidiPitch = new Map();
const synth = new tone.PolySynth(tone.FMSynth).toDestination();
// Callback for note onset
let lastTimeMs = null;
function noteOnset(button) {
if (heldButtonToMidiPitch.has(button)) return;
// Run Piano Genie
const timeMs = new Date().getTime();
const key = pianoGenie.press(timeMs / 1000, button);
// Play note out of synthesizer
if (tone.context.state !== "running") tone.context.resume();
const midiPitch = key + LOWEST_PIANO_KEY_MIDI_PITCH;
synth.triggerAttack(tone.Frequency(midiPitch, "midi").toFrequency());
heldButtonToMidiPitch.set(button, midiPitch);
// I/O report in console
let dt = lastTimeMs === null ? 0 : (timeMs - lastTimeMs) / 1000;
dt = dt.toFixed(3);
const latencyMs = new Date().getTime() - timeMs;
console.log(
`⌚ ${dt}, 🔘 ${button} -> 🎹🧞 -> 🎵 ${midiPitch} (in ${latencyMs}ms)`
);
lastTimeMs = timeMs;
// Show UI
buttonEls[button].setAttribute("active", true);
}
// Callback for note offset
function noteOffset(button) {
if (!heldButtonToMidiPitch.has(button)) return;
const midiPitch = heldButtonToMidiPitch.get(button);
synth.triggerRelease(tone.Frequency(midiPitch, "midi").toFrequency());
heldButtonToMidiPitch.delete(button);
buttonEls[button].removeAttribute("active");
}
// Bind touch control
for (let b = 0; b < my.NUM_BUTTONS; ++b) {
const buttonEl = buttonEls[b];
const doOnset = evt => {
noteOnset(b);
};
const doOffset = evt => {
noteOffset(b);
};
if ("ontouchstart" in window) {
buttonEl.ontouchstart = doOnset;
buttonEl.ontouchend = doOffset;
buttonEl.ontouchleave = doOffset;
} else {
buttonEl.onmousedown = doOnset;
buttonEl.onmouseup = doOffset;
buttonEl.onmouseout = doOffset;
}
}
// Bind keyboard control
document.onkeydown = evt => {
const button = evt.keyCode - 49;
if (button >= 0 && button < my.NUM_BUTTONS) noteOnset(button);
};
document.onkeyup = evt => {
const button = evt.keyCode - 49;
if (button >= 0 && button < my.NUM_BUTTONS) noteOffset(button);
};
// Show UI
document.getElementById("loading").style.display = "none";
document.getElementById("loaded").style.display = "block";
}
document.addEventListener("DOMContentLoaded", init, false);
})(window.Tone, window.my);