const notes = [{
name: 'c',
freq: 261.6
},
{
name: 'c#',
freq: 277.2,
sharp: true
},
{
name: 'd',
freq: 293.7
},
{
name: 'd#',
freq: 311.1,
sharp: true
},
{
name: 'e',
freq: 329.6
},
{
name: 'f',
freq: 349.2
},
{
name: 'f#',
freq: 370.0,
sharp: true
},
{
name: 'g',
freq: 392.0
},
{
name: 'g#',
freq: 415.3,
sharp: true
},
{
name: 'a',
freq: 440.0
},
{
name: 'a#',
freq: 466.2,
sharp: true
},
{
name: 'b',
freq: 493.9
},
];
let playing = {};
let fft;
let bg;
function setup() {
createCanvas(windowWidth, windowHeight);
colorMode(HSB);
bg = color('lightgray');
for (let note of notes) {
note.osc = new p5.Oscillator();
note.osc.freq(note.freq);
note.osc.amp(0);
}
fft = new p5.FFT();
}
function toggleNote(name) {
let note = notes.filter(n => n.name === name)[0];
if (playing[name] === undefined) {
// First play
note.osc.start();
}
if (playing[name] = !playing[name]) {
// fade in a little
note.osc.amp(0.2, 0.2);
} else {
// fade out a little
note.osc.amp(0, 0.4);
}
}
function playNote(name) {
let note = notes.filter(n => n.name === name)[0];
if (playing[name] === undefined) {
// First play
note.osc.start();
}
playing[name] = true;
note.osc.amp(0.2, 0.2);
}
function releaseNote(name) {
let note = notes.filter(n => n.name === name)[0];
playing[name] = false;
note.osc.amp(0, 0.4);
}
function draw() {
background(bg);
let w = width / 3;
let h = min(height, w * 0.8);
drawSpectrumGraph(w, 0, w, h);
drawWaveformGraph(w * 2, 0, w, h);
drawKeyboard();
bg = color((fft.getCentroid() * 1.379) % 360, 30, 50);
}
function drawSpectrumGraph(left, top, w, h) {
let spectrum = fft.analyze();
stroke('limegreen');
fill('darkgreen');
strokeWeight(1);
beginShape();
vertex(left, top + h);
for (let i = 0; i < spectrum.length; i++) {
vertex(
left + map(log(i), 0, log(spectrum.length), 0, w),
top + map(spectrum[i], 0, 255, h, 0)
);
}
vertex(left + w, top + h);
endShape(CLOSE);
}
function drawWaveformGraph(left, top, w, h) {
let waveform = fft.waveform();
stroke('limegreen');
noFill();
strokeWeight(1);
beginShape();
for (let i = 0; i < waveform.length; i++) {
let x = map(i * 5, 0, waveform.length, 0, w);
let y = map(waveform[i], -1, 2, h / 10 * 8, 0);
vertex(left + x, top + y);
}
endShape();
}
function drawKeyboard() {
let w = width / 3;
let h = min(height, w * 0.8);
let x = 1;
let keyWidth = (w - 8) / 7;
let sharpWidth = keyWidth * 0.8;
noStroke();
let sharpKeys = [];
for (let note of notes) {
fill(playing[note.name] ? 'beige' : 'ivory');
if (note.sharp) {
sharpKeys.push({
fill: playing[note.name] ? 'black' : 'dimgray',
rect: [x - sharpWidth / 2, 0, sharpWidth, h / 2, 0, 0, 4, 4]
});
} else {
rect(x, 0, keyWidth, h - 1, 0, 0, 4, 4);
x += keyWidth + 1;
}
}
for (let key of sharpKeys) {
fill(key.fill);
rect(...key.rect);
}
}
let keymap = {
'z': 'c',
's': 'c#',
'x': 'd',
'd': 'd#',
'c': 'e',
'v': 'f',
'g': 'f#',
'b': 'g',
'h': 'g#',
'n': 'a',
'j': 'a#',
'm': 'b',
}
function keyPressed(e) {
let note = keymap[e.key];
if (note) {
playNote(note);
}
}
function keyReleased(e) {
let note = keymap[e.key];
if (note) {
releaseNote(note);
}
}
function mouseClicked() {
if (mouseX < width / 3) {
let w = width / 3;
let h = w * 0.8;
let x = 1;
let keyWidth = (w - 8) / 7;
let sharpWidth = keyWidth * 0.8;
let naturalKeys = [];
let sharpKeys = [];
for (let note of notes) {
if (note.sharp) {
sharpKeys.push({
name: note.name,
bounds: {
left: x - sharpWidth / 2,
top: 0,
right: x - sharpWidth / 2 + sharpWidth,
bottom: h / 2
}
});
} else {
naturalKeys.push({
name: note.name,
bounds: {
left: x,
top: 0,
right: x + keyWidth,
bottom: h - 1
}
});
x += keyWidth + 1;
}
}
for (let {
bounds,
name
} of sharpKeys.concat(naturalKeys)) {
if (mouseX > bounds.left && mouseX < bounds.right &&
mouseY > bounds.top && mouseY < bounds.bottom) {
toggleNote(name);
break;
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/addons/p5.sound.min.js"></script>