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

Fix incorrect note y calculation and add play/pause logic

parent 5934e12c
......@@ -10,7 +10,7 @@
<input accept="audio/midi" id="fileInput" type="file" />
<h1 id="nameH1"></h1>
<input type="range" id="timeInput" value="0" style="width: 100%;" />
<button id="playButton"></button>
<button id="playPauseButton"></button>
<br />
<canvas id="renderCanvas" style="background: black; padding: 5px;"></canvas>
</body>
......
......@@ -2,10 +2,11 @@ window.addEventListener('load', async _ => {
const fileInput = document.getElementById('fileInput');
const nameH1 = document.getElementById('nameH1');
const timeInput = document.getElementById('timeInput');
const playButton = document.getElementById('playButton');
const playPauseButton = document.getElementById('playPauseButton');
const renderCanvas = document.getElementById('renderCanvas');
let channelNotes = [];
let tempos = [];
fileInput.addEventListener('change', _ => {
const file = fileInput.files[0];
......@@ -26,38 +27,45 @@ window.addEventListener('load', async _ => {
//console.log('tracks[0]', data.tracks[0]); // timeSignature & endOfTracks
//console.log('tracks[1]', data.tracks[1]); // setTempo's
const channel0Notes = [ ...getNotes(data.tracks[2]) ];
const channel1Notes = [ ...getNotes(data.tracks[3]) ];
timeInput.setAttribute('max', Math.max(channel0Notes[channel0Notes.length - 1].offTime, channel1Notes[channel1Notes.length - 1].offTime));
// https://www.youtube.com/watch?v=ppLnKRObtM0
//renderChannelNotes(channel0Notes, length);
//renderChannelNotes(channel1Notes, length);
channelNotes = [ channel0Notes, channel1Notes ];
render(renderCanvas, channelNotes, 0);
channelNotes = [ [ ...getNotes(data.tracks[2]) ], [ ...getNotes(data.tracks[3]) ] ];
tempos = [ ...getTempos(data.tracks[1]) ];
timeInput.setAttribute('max', Math.max(channelNotes[0][channelNotes[0].length - 1].offTime, channelNotes[1][channelNotes[1].length - 1].offTime));
render(renderCanvas, channelNotes, tempos, 0);
});
fileReader.readAsBinaryString(file);
});
// TODO: Replace with RAF
let playbackInterval;
playButton.addEventListener('click', _ => {
playbackInterval = window.setInterval(() => {
timeInput.value = timeInput.valueAsNumber + 1;
render(renderCanvas, channelNotes, timeInput.valueAsNumber);
}, 1);
playPauseButton.addEventListener('click', _ => {
switch (playPauseButton.textContent) {
case '▶': {
playbackInterval = window.setInterval(() => {
timeInput.value = timeInput.valueAsNumber + 1;
render(renderCanvas, channelNotes, tempos, timeInput.valueAsNumber);
}, 1);
playPauseButton.textContent = '❚❚';
break;
}
case '❚❚': {
window.clearInterval(playbackInterval);
playPauseButton.textContent = '▶';
break;
}
default: throw new Error(`Unexpected play pause button text content: ${playPauseButton.textContent}`);
}
});
timeInput.addEventListener('input', _ => {
window.clearInterval(playbackInterval);
render(renderCanvas, channelNotes, timeInput.valueAsNumber);
render(renderCanvas, channelNotes, tempos, timeInput.valueAsNumber);
});
});
// https://stackoverflow.com/a/23071105/2715716
// TODO: Accept tempo changes and reflect that in time
function* getNotes(track) {
let time = 0;
let notes = {};
......@@ -97,11 +105,26 @@ function* getNotes(track) {
}
}
function* getTempos(track) {
let time = 0;
for (const event of track) {
time += event.deltaTime;
switch (event.subtype) {
case 'setTempo': {
yield ({ time, tempo: event.microsecondsPerBeat });
break;
}
default: console.log(event);
}
}
}
// https://stackoverflow.com/a/23071105/2715716
// TODO: Accept tempo changes and reflect that in time
// TODO: Give each channel a different color
// TODO: Fix first notes being off-frame
// TODO: Adapt `canvas` size to window (put in flex and use computed dimensions to set canvas context dimensions)
// TODO: Resize with `window.onresize`
function render(canvas, channels, time) {
function render(canvas, channels, tempos, time) {
const width = 768;
const height = 576;
......@@ -118,8 +141,8 @@ function render(canvas, channels, time) {
// Draw all fill first so that they do not overlap any strokes
context.fillStyle = 'rgb(128, 128, 128)';
for (const note of notes) {
if (!isNoteInView(note)) continue;
const { x, y, w, h } = getNoteXYWH(note, 5, top);
if (y + h < 0 || y > height) continue;
context.fillRect(x, y, w, h);
}
......@@ -128,8 +151,8 @@ function render(canvas, channels, time) {
context.strokeStyle = 'rgb(255, 255, 255)';
for (const note of notes) {
counter++;
if (!isNoteInView(note)) continue;
const { x, y, w, h } = getNoteXYWH(note, 5, top);
if (y + h <= 0 || y > height) continue;
context.strokeRect(x, y, w, h);
context.fillText(`#${counter}: ${note.no}`, x + w, y + h);
}
......@@ -139,12 +162,10 @@ function render(canvas, channels, time) {
context.fillText(time, 0, height);
}
function isNoteInView(note, top, bottom) {
if (note.offTime < bottom) return false;
if (note.onTime > top) return false;
return true;
}
function getNoteXYWH(note, unit, top) {
return { x: note.no * unit, y: top - note.onTime, w: unit, h: note.offTime - note.onTime };
const w = unit;
const h = note.offTime - note.onTime;
const x = note.no * unit;
const y = top - note.onTime - h;
return { x, y, w, 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