93 lines
2.4 KiB
JavaScript
93 lines
2.4 KiB
JavaScript
const kNumKeys = 49;
|
|
const kFirstKey = 'C2';
|
|
const kNotes = ['C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'G#', 'A', 'Bb', 'B'];
|
|
const kA0Frequency = 27.5;
|
|
const kSemiToneMultiplier = Math.pow(2, 1/12);
|
|
|
|
const audioCtx = new AudioContext();
|
|
|
|
function playNote(frequency, duration) {
|
|
let currentGain = 1.0;
|
|
|
|
const bassBoost = f => {
|
|
const t = f / 20000;
|
|
const a = 4.0;
|
|
const b = 0.2;
|
|
|
|
return b * t + a * (1 - t);
|
|
};
|
|
|
|
while (frequency < 20000) {
|
|
const oscillator = new OscillatorNode(audioCtx);
|
|
const gain = new GainNode(audioCtx);
|
|
|
|
oscillator.type = 'sine';
|
|
oscillator.frequency.value = frequency; // value in hertz
|
|
oscillator.connect(gain);
|
|
gain.gain.setValueAtTime(0, audioCtx.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(bassBoost(frequency) * currentGain, audioCtx.currentTime + 0.010);
|
|
gain.gain.linearRampToValueAtTime(1e-10, audioCtx.currentTime + 0.500);
|
|
gain.connect(audioCtx.destination);
|
|
oscillator.start();
|
|
|
|
setTimeout(() => {oscillator.stop();}, duration);
|
|
|
|
frequency *= 2;
|
|
currentGain *= 0.3;
|
|
}
|
|
}
|
|
|
|
function noteFrequency(key) {
|
|
let [note, octave, ] = key.split(/(\d)/);
|
|
octave = parseInt(octave);
|
|
|
|
const interval = kNotes.indexOf(note) - kNotes.indexOf('A') + octave * 12;
|
|
|
|
let freq = kA0Frequency * Math.pow(kSemiToneMultiplier, interval);
|
|
|
|
return freq;
|
|
}
|
|
|
|
function onKeyClick(event) {
|
|
const note = event.srcElement.id;
|
|
const freq = noteFrequency(note);
|
|
playNote(freq, 500);
|
|
}
|
|
|
|
function makeKey(note) {
|
|
const key = document.createElement('span');
|
|
if (note.includes('#') || note.includes('b')) {
|
|
key.classList.add('blackKey');
|
|
} else {
|
|
key.classList.add('whiteKey');
|
|
}
|
|
key.id = note;
|
|
key.addEventListener('click', onKeyClick);
|
|
|
|
return key;
|
|
}
|
|
|
|
function setupKeyboard() {
|
|
const keyboard = document.getElementById('keyboard');
|
|
keyboard.innerHTML = '';
|
|
|
|
let curIndex = kNotes.indexOf(kFirstKey.charAt(0));
|
|
let octave = parseInt(kFirstKey.charAt(1));
|
|
|
|
for (let i = 0; i < kNumKeys; i++) {
|
|
const note = kNotes[curIndex] + octave;
|
|
const key = makeKey(note)
|
|
keyboard.appendChild(key);
|
|
|
|
curIndex += 1;
|
|
if (curIndex >= kNotes.length) {
|
|
octave += 1;
|
|
curIndex = curIndex % kNotes.length;
|
|
}
|
|
}
|
|
}
|
|
|
|
window.addEventListener('load', e => {
|
|
setupKeyboard();
|
|
});
|