Initial commit: simple synth
This commit is contained in:
commit
8fde361246
30
index.css
Normal file
30
index.css
Normal file
@ -0,0 +1,30 @@
|
||||
.whiteKey {
|
||||
color: white;
|
||||
border: solid 1px;
|
||||
border-color: grey;
|
||||
|
||||
height: 67pt;
|
||||
/*
|
||||
max-height: 60pt;
|
||||
max-width: 20pt;
|
||||
*/
|
||||
position: relative;
|
||||
left: 7.5pt;
|
||||
width: 20pt;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.blackKey {
|
||||
color: black;
|
||||
background-color: black;
|
||||
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
height: 40pt;
|
||||
width: 15pt;
|
||||
/*
|
||||
max-height: 40pt;
|
||||
max-width: 15pt;
|
||||
*/
|
||||
display: inline-block;
|
||||
}
|
18
index.html
Normal file
18
index.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<link rel="stylesheet" href="pico.min.css">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
<script src="index.js"></script>
|
||||
<title>Tooner 0</title>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
<div id="keyboard">
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
92
index.js
Normal file
92
index.js
Normal file
@ -0,0 +1,92 @@
|
||||
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();
|
||||
});
|
4
pico.min.css
vendored
Normal file
4
pico.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user