99 lines
No EOL
2.4 KiB
HTML
99 lines
No EOL
2.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<meta charset="utf-8">
|
|
<title>piano</title>
|
|
<h1>piano</h1>
|
|
<div>
|
|
<textarea id="input"></textarea>
|
|
</div>
|
|
|
|
<h2>notes held</h2>
|
|
<div id="notes-held"></div>
|
|
|
|
<script>
|
|
const inputArea = document.querySelector('#input');
|
|
const notesDiv = document.querySelector('#notes-held');
|
|
const ctx = new AudioContext;
|
|
const playing = {};
|
|
let tone = {};
|
|
let setupCompleted = false;
|
|
inputArea.onfocus = start;
|
|
inputArea.onblur = pause;
|
|
|
|
// arrange notes like on a piano. the second and fourth rows are each a major scale, but the first and third rows have gaps.
|
|
function usePianoKeys() {
|
|
tone = {};
|
|
("zsxdcvgbhnjm,l.;/q2w3e4rt6y7ui9o0p-[]"
|
|
.split('')
|
|
.map((key, i) => {
|
|
tone[key] = 440 * 2 ** ((i - 20)/12);
|
|
})
|
|
);
|
|
}
|
|
|
|
// arrange notes so that moving three keys sideways is exactly an octave. makes it easy to play octaves. everything else is wonky though...
|
|
function useEqualSteps() {
|
|
const rows = [
|
|
"1234567890-=",
|
|
"qwertyuiop[]",
|
|
"asdfghjkl;'",
|
|
"zxcvbnm,./",
|
|
];
|
|
tone = {};
|
|
rows.map((row, y) => {
|
|
row.split('').map((key, x) => {
|
|
const semitone = x * 4 + y - 20;
|
|
tone[key] = 440 * 2 ** (semitone / 12);
|
|
})
|
|
});
|
|
}
|
|
|
|
function start() {
|
|
if (setupCompleted) {
|
|
addEventListener('keydown', onKeydown);
|
|
return;
|
|
}
|
|
useEqualSteps();
|
|
addEventListener('keydown', onKeydown);
|
|
addEventListener('keyup', onKeyup);
|
|
setupCompleted = true;
|
|
}
|
|
|
|
function pause() {
|
|
removeEventListener('keydown', onKeydown);
|
|
}
|
|
|
|
function onKeydown(ev) {
|
|
let key = ev.key;
|
|
if (key == ' ') key = 'Space';
|
|
if (!tone[key]) return;
|
|
if (playing[key]) return;
|
|
|
|
const gain = new GainNode(ctx, { gain: 2 ** -5 });
|
|
const oscillator = new OscillatorNode(ctx, { type: 'square', frequency: tone[key] });
|
|
gain.connect(ctx.destination);
|
|
oscillator.connect(gain);
|
|
oscillator.start();
|
|
|
|
playing[key] = { gain, oscillator };
|
|
showNotes();
|
|
}
|
|
|
|
function onKeyup(ev) {
|
|
let key = ev.key;
|
|
if (key == ' ') key = 'Space';
|
|
if (!playing[key]) return;
|
|
|
|
const { gain, oscillator } = playing[key];
|
|
oscillator.stop();
|
|
oscillator.disconnect();
|
|
gain.disconnect();
|
|
|
|
delete playing[key];
|
|
showNotes();
|
|
}
|
|
|
|
function showNotes() {
|
|
notesDiv.innerText = Object.keys(playing).join(' ');
|
|
}
|
|
</script> |