mv
This commit is contained in:
parent
26d86f4bc9
commit
cd9363ea49
@ -1,166 +1,166 @@
|
||||
class MidiTrack {
|
||||
constructor(player, chunk) {
|
||||
this.player = player;
|
||||
this.events = chunk.events;
|
||||
|
||||
this.length = 0;
|
||||
for (let i=0; i< this.events.length; i++) {
|
||||
this.length += this.events[i].deltaTime;
|
||||
}
|
||||
|
||||
this.index = 0;
|
||||
this.lastTime = 0;
|
||||
}
|
||||
|
||||
parseEvent(event) {
|
||||
if (event instanceof MidiVoiceEvent) {
|
||||
this.player.parseEvent(event);
|
||||
} else if (event instanceof MidiSetTempoEvent) {
|
||||
this.player.bpm = 60000000 / event.tempo;
|
||||
}
|
||||
}
|
||||
|
||||
tick(delta) {
|
||||
while(true) {
|
||||
if (this.index >= this.events.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
let event = this.events[this.index];
|
||||
|
||||
if (this.lastTime + event.deltaTime - this.player.time <= 0) {
|
||||
this.lastTime += event.deltaTime;
|
||||
this.parseEvent(event);
|
||||
this.index++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MidiPlayer {
|
||||
constructor() {
|
||||
this.tracks = [];
|
||||
|
||||
this.playing = false;
|
||||
this.speed = 1;
|
||||
this.bpm = 120;
|
||||
this.time = 0;
|
||||
}
|
||||
|
||||
loadMidi(midi) {
|
||||
this.tracks = [];
|
||||
this.time = 0;
|
||||
this.length = 0;
|
||||
this.bpm = 120;
|
||||
this.midi = midi;
|
||||
|
||||
if (!(this.midi.chunks[0] instanceof MidiHeaderChunk)) {
|
||||
throw "No header chunk";
|
||||
}
|
||||
|
||||
let header = this.midi.chunks[0];
|
||||
|
||||
this.format = header.format;
|
||||
|
||||
if ((header.division & 0x0800) === 1) {
|
||||
throw "SMPTA time format not yet supported";
|
||||
} else {
|
||||
this.ticksPerQuarterNote = header.division;
|
||||
}
|
||||
|
||||
for (let i=1; i<this.midi.chunks.length; i++) {
|
||||
let chunk = this.midi.chunks[i];
|
||||
if (chunk instanceof MidiTrackChunk) {
|
||||
let track = new MidiTrack(this, chunk);
|
||||
this.length = Math.max(track.length, this.length);
|
||||
this.tracks.push(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseEvent(event) {
|
||||
if (event instanceof MidiNoteOnEvent) {
|
||||
if (event.channel === 9) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.velocity === 0) {
|
||||
piano.localKeyUp(event.key, false);
|
||||
} else {
|
||||
piano.localKeyDown(event.key, event.velocity / 127);
|
||||
}
|
||||
} else if (event instanceof MidiNoteOffEvent) {
|
||||
if (event.channel === 9) {
|
||||
return;
|
||||
}
|
||||
piano.localKeyUp(event.key, false);
|
||||
}
|
||||
}
|
||||
|
||||
tick(delta) {
|
||||
let deltaTicks = delta / 1000 * (this.bpm / 60) * this.ticksPerQuarterNote;
|
||||
this.time += deltaTicks;
|
||||
|
||||
switch(this.format) {
|
||||
case 0:
|
||||
this.tracks[0].tick(deltaTicks);
|
||||
break;
|
||||
case 1:
|
||||
for (let i=0; i<this.tracks.length; i++) {
|
||||
this.tracks[i].tick(deltaTicks);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// Todo
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.time >= this.length) {
|
||||
this.time = this.length;
|
||||
this.pause();
|
||||
}
|
||||
}
|
||||
|
||||
setTime(time) {
|
||||
// TODO skip notes
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
play() {
|
||||
if (this.playing) return;
|
||||
|
||||
this.playing = true;
|
||||
|
||||
let lastTime = Date.now();
|
||||
this.interval = setInterval(() => {
|
||||
let delta = Date.now() - lastTime;
|
||||
lastTime = Date.now();
|
||||
this.tick(delta * this.speed);
|
||||
}, 1000 / 60);
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.playing = false;
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
}
|
||||
|
||||
let midiPlayer = new MidiPlayer();
|
||||
|
||||
function uploadMidi() {
|
||||
let input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = "audio/midi";
|
||||
input.addEventListener("change", function() {
|
||||
let reader = new FileReader();
|
||||
reader.addEventListener("load", function() {
|
||||
let midiFile = new MidiFile(reader.result);
|
||||
midiPlayer.loadMidi(midiFile);
|
||||
console.log(midiFile);
|
||||
});
|
||||
reader.readAsArrayBuffer(input.files[0]);
|
||||
});
|
||||
input.click();
|
||||
}
|
||||
class MidiTrack {
|
||||
constructor(player, chunk) {
|
||||
this.player = player;
|
||||
this.events = chunk.events;
|
||||
|
||||
this.length = 0;
|
||||
for (let i=0; i< this.events.length; i++) {
|
||||
this.length += this.events[i].deltaTime;
|
||||
}
|
||||
|
||||
this.index = 0;
|
||||
this.lastTime = 0;
|
||||
}
|
||||
|
||||
parseEvent(event) {
|
||||
if (event instanceof MidiVoiceEvent) {
|
||||
this.player.parseEvent(event);
|
||||
} else if (event instanceof MidiSetTempoEvent) {
|
||||
this.player.bpm = 60000000 / event.tempo;
|
||||
}
|
||||
}
|
||||
|
||||
tick(delta) {
|
||||
while(true) {
|
||||
if (this.index >= this.events.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
let event = this.events[this.index];
|
||||
|
||||
if (this.lastTime + event.deltaTime - this.player.time <= 0) {
|
||||
this.lastTime += event.deltaTime;
|
||||
this.parseEvent(event);
|
||||
this.index++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MidiPlayer {
|
||||
constructor() {
|
||||
this.tracks = [];
|
||||
|
||||
this.playing = false;
|
||||
this.speed = 1;
|
||||
this.bpm = 120;
|
||||
this.time = 0;
|
||||
}
|
||||
|
||||
loadMidi(midi) {
|
||||
this.tracks = [];
|
||||
this.time = 0;
|
||||
this.length = 0;
|
||||
this.bpm = 120;
|
||||
this.midi = midi;
|
||||
|
||||
if (!(this.midi.chunks[0] instanceof MidiHeaderChunk)) {
|
||||
throw "No header chunk";
|
||||
}
|
||||
|
||||
let header = this.midi.chunks[0];
|
||||
|
||||
this.format = header.format;
|
||||
|
||||
if ((header.division & 0x0800) === 1) {
|
||||
throw "SMPTA time format not yet supported";
|
||||
} else {
|
||||
this.ticksPerQuarterNote = header.division;
|
||||
}
|
||||
|
||||
for (let i=1; i<this.midi.chunks.length; i++) {
|
||||
let chunk = this.midi.chunks[i];
|
||||
if (chunk instanceof MidiTrackChunk) {
|
||||
let track = new MidiTrack(this, chunk);
|
||||
this.length = Math.max(track.length, this.length);
|
||||
this.tracks.push(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseEvent(event) {
|
||||
if (event instanceof MidiNoteOnEvent) {
|
||||
if (event.channel === 9) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.velocity === 0) {
|
||||
piano.localKeyUp(event.key, false);
|
||||
} else {
|
||||
piano.localKeyDown(event.key, event.velocity / 127);
|
||||
}
|
||||
} else if (event instanceof MidiNoteOffEvent) {
|
||||
if (event.channel === 9) {
|
||||
return;
|
||||
}
|
||||
piano.localKeyUp(event.key, false);
|
||||
}
|
||||
}
|
||||
|
||||
tick(delta) {
|
||||
let deltaTicks = delta / 1000 * (this.bpm / 60) * this.ticksPerQuarterNote;
|
||||
this.time += deltaTicks;
|
||||
|
||||
switch(this.format) {
|
||||
case 0:
|
||||
this.tracks[0].tick(deltaTicks);
|
||||
break;
|
||||
case 1:
|
||||
for (let i=0; i<this.tracks.length; i++) {
|
||||
this.tracks[i].tick(deltaTicks);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// Todo
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.time >= this.length) {
|
||||
this.time = this.length;
|
||||
this.pause();
|
||||
}
|
||||
}
|
||||
|
||||
setTime(time) {
|
||||
// TODO skip notes
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
play() {
|
||||
if (this.playing) return;
|
||||
|
||||
this.playing = true;
|
||||
|
||||
let lastTime = Date.now();
|
||||
this.interval = setInterval(() => {
|
||||
let delta = Date.now() - lastTime;
|
||||
lastTime = Date.now();
|
||||
this.tick(delta * this.speed);
|
||||
}, 1000 / 60);
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.playing = false;
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
}
|
||||
|
||||
let midiPlayer = new MidiPlayer();
|
||||
|
||||
function uploadMidi() {
|
||||
let input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = "audio/midi";
|
||||
input.addEventListener("change", function() {
|
||||
let reader = new FileReader();
|
||||
reader.addEventListener("load", function() {
|
||||
let midiFile = new MidiFile(reader.result);
|
||||
midiPlayer.loadMidi(midiFile);
|
||||
console.log(midiFile);
|
||||
});
|
||||
reader.readAsArrayBuffer(input.files[0]);
|
||||
});
|
||||
input.click();
|
||||
}
|
Before (image error) Size: 226 B After (image error) Size: 226 B |
Before (image error) Size: 160 B After (image error) Size: 160 B |
Before (image error) Size: 325 B After (image error) Size: 325 B |
Loading…
x
Reference in New Issue
Block a user