k-l-lambda's picture
updated node_modules
4cadbaf
const MIDI = require("./MIDI");
const trackDeltaToAbs = events => {
let tick = 0;
events.forEach(event => {
tick += event.deltaTime;
event.tick = tick;
});
};
const trackAbsToDelta = events => {
let lastTick = 0;
events.sort((e1, e2) => e1.tick - e2.tick).forEach(event => {
event.deltaTime = event.tick - lastTick;
lastTick = event.tick;
});
};
const sliceTrack = (track, startTick, endTick) => {
trackDeltaToAbs(track);
const events = [];
const status = {};
track.forEach(event => {
if (event.tick >= startTick && event.tick <= endTick && event.subtype !== "endOfTrack")
events.push({
...event,
tick: event.tick - startTick,
});
else if (event.tick < startTick) {
switch (event.type) {
case "meta":
status[event.subtype] = event;
break;
}
}
});
Object.values(status).forEach(event => events.push({
...event,
tick: 0,
}));
events.push({
tick: endTick - startTick,
type: "meta",
subtype: "endOfTrack",
});
trackAbsToDelta(events);
return events;
};
const sliceMidi = (midi, startTick, endTick) => ({
header: midi.header,
tracks: midi.tracks.map(track => sliceTrack(track, startTick, endTick)),
});
const TICKS_PER_BEATS = 480;
const EXCLUDE_MIDI_EVENT_SUBTYPES = [
"endOfTrack", "trackName",
"noteOn", "noteOff",
];
function encodeToMIDIData(notation, {startTime, unclosedNoteDuration = 30e+3} = {}) {
notation.microsecondsPerBeat = notation.microsecondsPerBeat || 500000;
const ticksPerBeat = TICKS_PER_BEATS;
const msToTicks = ticksPerBeat * 1000 / notation.microsecondsPerBeat;
const header = { formatType: 0, ticksPerBeat };
const track = [];
if (!Number.isFinite(startTime)) {
if (!notation.notes || !notation.notes[0])
throw new Error("encodeToMidiData: no start time specificed");
startTime = notation.notes[0].start;
}
track.push({ time: startTime, type: "meta", subtype: "copyrightNotice", text: `Composed by MusicWdigets. BUILT on ${new Date(Number(process.env.VUE_APP_BUILD_TIME)).toDateString()}` });
const containsTempo = notation.events && notation.events.find(event => event.subtype == "setTempo");
if (!containsTempo) {
track.push({ time: startTime, type: "meta", subtype: "timeSignature", numerator: 4, denominator: 4, thirtyseconds: 8 });
track.push({ time: startTime, type: "meta", subtype: "setTempo", microsecondsPerBeat: notation.microsecondsPerBeat });
}
//if (notation.correspondences)
// track.push({ time: startTime, type: "meta", subtype: "text", text: "find-corres:" + notation.correspondences.join(",") });
let endTime = startTime || 0;
if (notation.notes) {
for (const note of notation.notes) {
track.push({
time: note.start,
type: "channel",
subtype: "noteOn",
channel: note.channel || 0,
noteNumber: note.pitch,
velocity: note.velocity,
finger: note.finger,
});
endTime = Math.max(endTime, note.start);
if (Number.isFinite(unclosedNoteDuration))
note.duration = note.duration || unclosedNoteDuration;
if (note.duration) {
track.push({
time: note.start + note.duration,
type: "channel",
subtype: "noteOff",
channel: note.channel || 0,
noteNumber: note.pitch,
velocity: 0,
});
endTime = Math.max(endTime, note.start + note.duration);
}
}
}
if (notation.events) {
const events = notation.events.filter(event => !EXCLUDE_MIDI_EVENT_SUBTYPES.includes(event.data.subtype));
for (const event of events) {
track.push({
time: event.time,
...event.data,
});
endTime = Math.max(endTime, event.time);
}
}
track.push({ time: endTime + 100, type: "meta", subtype: "endOfTrack" });
track.sort(function (e1, e2) { return e1.time - e2.time; });
// append finger event after every noteOn event
track.map((event, index) => ({event, index}))
.filter(({event}) => event.subtype == "noteOn" && event.finger != null)
.reverse()
.forEach(({event, index}) => track.splice(index + 1, 0, {
time: event.time,
type: "meta",
subtype: "text",
text: `fingering(${event.finger})`,
}));
track.forEach(event => event.ticks = Math.round((event.time - startTime) * msToTicks));
track.forEach((event, i) => event.deltaTime = (event.ticks - (i > 0 ? track[i - 1].ticks : 0)));
return {header, tracks: [track]};
};
function encodeToMIDI(notation, options) {
const data = encodeToMIDIData(notation, options);
return MIDI.encodeMidiFile(data);
};
module.exports = {
sliceMidi,
encodeToMIDIData,
encodeToMIDI,
};