Commit c4da68ef authored by Tomáš Hübelbauer's avatar Tomáš Hübelbauer

Draw rudimentary keyboard and note names

parent 774d33d7
......@@ -70,6 +70,33 @@ window.addEventListener('load', async _ => {
});
});
const COLOR_WHITE = 0;
const COLOR_BLACK = 1;
function getNote(number) {
const octave = Math.floor(number / 12);
number = number % 12;
let name;
let color;
switch (number) {
case 0: name = 'C'; color = COLOR_WHITE; break;
case 1: name = 'C#'; color = COLOR_BLACK; break;
case 2: name = 'D'; color = COLOR_WHITE; break;
case 3: name = 'D#'; color = COLOR_BLACK; break;
case 4: name = 'E'; color = COLOR_WHITE; break;
case 5: name = 'F'; color = COLOR_WHITE; break;
case 6: name = 'F#'; color = COLOR_BLACK; break;
case 7: name = 'G'; color = COLOR_WHITE; break;
case 8: name = 'G#'; color = COLOR_BLACK; break;
case 9: name = 'A'; color = COLOR_WHITE; break;
case 10: name = 'A#'; color = COLOR_BLACK; break;
case 11: name = 'B'; color = COLOR_WHITE; break;
default: throw new Error(`Expected number to be within 0 and 11, but it is ${number}.`);
}
return { octave, number, name, color };
}
function* getNotes(track) {
let time = 0;
let notes = {};
......@@ -83,7 +110,7 @@ function* getNotes(track) {
break;
}
case 'noteOff': {
yield ({ onTime: notes[event.noteNumber], offTime: time, no: event.noteNumber });
yield ({ ...getNote(event.noteNumber), onTime: notes[event.noteNumber], offTime: time, no: event.noteNumber });
delete notes[event.noteNumber];
break;
}
......@@ -125,16 +152,18 @@ function* getTempos(track) {
const channelColors = [
undefined,
'#abcdef',
'#fedcba',
[ '#abcdef', '#0080ff' ],
[ '#fedcba', '#ff8000' ],
];
// https://stackoverflow.com/a/23071105/2715716
// TODO: Reflect tempo changes in time
function render(canvas, channels, tempos, time) {
const { width, height } = canvas.getBoundingClientRect();
const rect = canvas.getBoundingClientRect();
const width = rect.width;
let height = rect.height;
const top = height + time;
const unit = width / 100;
const unit = width / 128;
// Apply the implicit dimensions so that context dimensions match.
canvas.width = width;
......@@ -142,36 +171,65 @@ function render(canvas, channels, tempos, time) {
const context = canvas.getContext('2d');
context.fillRect(0, 0, width, height);
const keyboardHeight = height / 5; // 20 % of the height
height -= keyboardHeight;
let channelNumber = 0;
for (const notes of channels) {
channelNumber++;
// Draw all fill first so that they do not overlap any strokes
context.fillStyle = channelColors[channelNumber];
for (const note of notes) {
const { x, y, w, h } = getNoteXYWH(note, unit, top);
if (y + h < 0 || y > height) continue;
switch (note.color) {
case COLOR_WHITE: context.fillStyle = channelColors[channelNumber][0]; break;
case COLOR_BLACK: context.fillStyle = channelColors[channelNumber][1]; break;
default: throw new Error(`Invalid color ${note.color}, should be ${COLOR_WHITE} or ${COLOR_BLACK}.`);
}
context.fillRect(x, y, w, h);
}
// Draw all strokes second so they are not overlapped by any fills
let noteNumber = 0;
context.strokeStyle = 'rgb(255, 255, 255)';
context.fillStyle = 'rgb(255, 255, 255)';
for (const note of notes) {
noteNumber++;
const { x, y, w, h } = getNoteXYWH(note, unit, top);
if (y + h <= 0 || y > height) continue;
context.strokeRect(x, y, w, h);
context.fillText(`#${noteNumber}: ${note.no}`, x + w, y + h);
context.fillText(`${note.name}${note.octave}`, x + w, y + h);
}
}
context.fillStyle = 'red';
context.fillText(time, 0, height);
// TODO: Calculate and render the "overlap" of white keys into the black keys.
for (let number = 0; number < 128; number++) {
const note = getNote(number);
let noteHeight;
let noteWidth; // TODO: Get rid of this hack!!
switch (note.color) {
case COLOR_WHITE: context.fillStyle = 'white'; context.strokeStyle = 'black'; noteHeight = keyboardHeight; noteWidth = unit * 2; break;
case COLOR_BLACK: context.fillStyle = 'black'; context.strokeStyle = 'white'; noteHeight = keyboardHeight / 2; noteWidth = unit; break;
default: throw new Error(`Invalid color ${note.color}, should be ${COLOR_WHITE} or ${COLOR_BLACK}.`);
}
context.fillRect(number * unit, height, noteWidth, noteHeight);
context.strokeRect(number * unit, height, noteWidth, noteHeight);
}
}
function getNoteXYWH(note, unit, top) {
const w = unit;
let w;
switch (note.color) {
case COLOR_WHITE: w = unit; break;
case COLOR_BLACK: w = unit / 2; break;
default: throw new Error(`Invalid color ${note.color}, should be ${COLOR_WHITE} or ${COLOR_BLACK}.`);
}
const h = note.offTime - note.onTime;
const x = note.no * unit;
const y = top - note.onTime - h;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment