330 lines
12 KiB
JavaScript
330 lines
12 KiB
JavaScript
|
var { Midi } = require("@tonejs/midi");
|
||
|
var { isValidFile } = require("./parser.js");
|
||
|
|
||
|
var fs = require("fs");
|
||
|
|
||
|
var compiledTracks = [];
|
||
|
var timeConstant = (5 / 3) * 12;// 20
|
||
|
var midi;
|
||
|
|
||
|
/*
|
||
|
Note structure:
|
||
|
|
||
|
{
|
||
|
midi: 70,
|
||
|
velocity: 0.7559055118110236,
|
||
|
noteOffVelocity: 0,
|
||
|
ticks: 82016,
|
||
|
durationTicks: 480
|
||
|
}
|
||
|
*/
|
||
|
function fixFileName(f) {
|
||
|
if (f.includes("/")) {
|
||
|
var len = f.split("/").length;
|
||
|
f = f.split("/")[len - 1];
|
||
|
}
|
||
|
return f.replace(".midi", "").replace(".mid", "");
|
||
|
}
|
||
|
|
||
|
function toNotebot(midiFile, cb) {
|
||
|
if (!isValidFile(midiFile) || midiFile.indexOf(".mid") == -1) {
|
||
|
throw new Error(`${midiFile} is a invalid file!`);
|
||
|
}
|
||
|
var midiFileName = fixFileName(midiFile);//for output later
|
||
|
var midiData = fs.readFileSync(midiFile);
|
||
|
try {
|
||
|
midi = new Midi(midiData);
|
||
|
} catch(e) {
|
||
|
cb(undefined, e);
|
||
|
return;
|
||
|
}
|
||
|
console.log(`Converting Midi file ${midi.name == "" ? midiFile : midi.name} to notebot format`);
|
||
|
|
||
|
compileMIDI(midi);
|
||
|
var lines = [];
|
||
|
for (var i = 0; i < compiledTracks.length; i++) {
|
||
|
for (var j = 0; j < compiledTracks[i].length; j++) {
|
||
|
lines.push(compiledTracks[i][j]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var finalString = "";
|
||
|
for (var i = 0; i < lines.length; i++) {
|
||
|
finalString += lines[i] + (i === lines.length - 1 ? "" : "\n");
|
||
|
|
||
|
}
|
||
|
var songout = "./songs/" + midiFileName + ".txt";
|
||
|
fs.writeFileSync(songout, finalString);
|
||
|
console.log("Done! wrote song to ./songs/" + midiFileName + ".txt");
|
||
|
fs.unlink(midiFile, () => {});
|
||
|
midi = undefined;
|
||
|
compiledTracks = undefined;
|
||
|
finalString = "";
|
||
|
cb(songout);
|
||
|
}
|
||
|
module.exports.toNotebot = toNotebot;
|
||
|
|
||
|
function isPercussion(channel) {
|
||
|
return channel == 10; //channel 10 reserved for percussion
|
||
|
}
|
||
|
|
||
|
function compileMIDI(midi) {
|
||
|
|
||
|
compiledTracks = [];
|
||
|
|
||
|
for (var i = 0; i < midi.tracks.length; i++) {
|
||
|
compiledTracks[i] = [];
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < midi.tracks.length; i++) {
|
||
|
if (midi.tracks[i].notes.length == 0) {// no notes
|
||
|
console.log(`skipping midi track ${midi.tracks[i].name} (track: ${i}) due to no notes`)
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// then loop through again to compile
|
||
|
if (curInstrument !== null) {
|
||
|
for (var j = 0; j < midi.tracks[i].notes.length; j++) {
|
||
|
var curInstrument = getMinecraftInstrument(midi.tracks[i].instrument.number, midi.tracks[i], 0, midi.tracks[i].notes[i] == undefined ? undefined : midi.tracks[i].notes[i].midi);
|
||
|
compileNoteFromTrack(i, j, curInstrument);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
function compileNoteFromTrack(trackNum, curNote, curInstrument) {
|
||
|
|
||
|
if (midi.tracks[trackNum].notes[curNote] == undefined)
|
||
|
return;
|
||
|
// done playing this track
|
||
|
|
||
|
var noteTime = Math.floor(midi.tracks[trackNum].notes[curNote].time * timeConstant); //convert seconds to minecraft ticks
|
||
|
|
||
|
if (isPercussion(midi.tracks[trackNum].channel)) {
|
||
|
// has to check this again for every note if this track is percussion because percussion can use different notes for different drums
|
||
|
var useInstrument = getMinecraftInstrument(midi.tracks[trackNum].instrument.number, midi.tracks[trackNum], curNote);
|
||
|
} else {
|
||
|
var useInstrument = curInstrument;
|
||
|
}
|
||
|
|
||
|
if (useInstrument == undefined)
|
||
|
return;
|
||
|
compiledTracks[trackNum].push(`${noteTime}:${convertNote(midi.tracks[trackNum].notes[curNote].midi, midi.tracks[trackNum])}:${useInstrument}`);
|
||
|
|
||
|
}
|
||
|
|
||
|
function convertNote(noteVal, midiTrack) {
|
||
|
if(isPercussion(midiTrack.channel)) return 0;
|
||
|
|
||
|
if(noteVal >= 30 && noteVal <= 54) {
|
||
|
return noteVal - 30;
|
||
|
} else if(noteVal >= 54 && noteVal <= 78) {
|
||
|
return noteVal - 54;
|
||
|
} else if(noteVal >= 78 && noteVal <= 102) {
|
||
|
return noteVal - 78;
|
||
|
} else {
|
||
|
return 0; // deafult to 0 if unknown
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
function getMinecraftInstrument(instrumentNumber, midiTrack, drumNoteNumber, midiPitch) {
|
||
|
|
||
|
if (isPercussion(midiTrack.channel)) {
|
||
|
if (midiTrack.notes.length > 0) {
|
||
|
var drumId = midiTrack.notes[drumNoteNumber].midi;
|
||
|
|
||
|
// TODO: drumid 24-34 & 82-87
|
||
|
// https://jazz-soft.net/demo/GeneralMidiPerc.html
|
||
|
|
||
|
|
||
|
if (drumId == 35 || drumId == 36 || drumId == 41 || drumId == 43
|
||
|
|| drumId == 45 || drumId == 47 || drumId == 48 || drumId == 50
|
||
|
|| drumId == 58) return 1; //basedrum
|
||
|
|
||
|
if (drumId == 37 || drumId == 38 || drumId == 40 || drumId == 49
|
||
|
|| drumId == 56 || drumId == 60 || drumId == 61 || drumId == 67
|
||
|
|| drumId == 68 || drumId == 70 || drumId == 78 || drumId == 79) return 2; //snare
|
||
|
|
||
|
if (drumId == 39 || drumId == 42 || drumId == 44 || drumId == 46
|
||
|
|| drumId == 51 || drumId == 52 || drumId == 53 || drumId == 54
|
||
|
|| drumId == 55 || drumId == 57 || drumId == 59 || drumId == 62
|
||
|
|| drumId == 63 || drumId == 64 || drumId == 65 || drumId == 66
|
||
|
|| drumId == 69 || drumId == 71 || drumId == 72 || drumId == 73
|
||
|
|| drumId == 74 || drumId == 75 || drumId == 76 || drumId == 77
|
||
|
|| drumId == 80 || drumId == 81) return 3; //hat
|
||
|
|
||
|
// default to basedrum if nothing found
|
||
|
return 1; //basedrum
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//normal midi instruments https://jazz-soft.net/demo/GeneralMidi.html
|
||
|
|
||
|
var MCInstrument = 0;
|
||
|
|
||
|
if (midiPitch != undefined) {//TODO: make this a bit smaller/less if statements
|
||
|
|
||
|
|
||
|
/* reference:
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {//low (bass, digeridoo)
|
||
|
MCInstrument = 0;
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {//medium (harp, iron xylophone, bit, banjo, pling)
|
||
|
MCInstrument = 0;
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {//high (bells, chimes, xylophone)
|
||
|
MCInstrument = 0;
|
||
|
} */
|
||
|
if (instrumentNumber >= 0 && instrumentNumber <= 7) {// Piano
|
||
|
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 4; //bass
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 0; //harp
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bells
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 8 && instrumentNumber <= 15) {// Chromatic Percussion
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 4; //bass
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 10; //iron xylophone
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 9; // xylophone
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 16 && instrumentNumber <= 23) {// Organ
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 24 && instrumentNumber <= 31) {// Guitar
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 32 && instrumentNumber <= 39) {// Bass
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 40 && instrumentNumber <= 47) {// Strings
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 48 && instrumentNumber <= 55) { // Ensemble
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 56 && instrumentNumber <= 63) {// Brass
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 64 && instrumentNumber <= 71) {// Reed
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 72 && instrumentNumber <= 79) {// Pipe
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit // maybe flute in the future
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 80 && instrumentNumber <= 87) {// Synth Lead
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 88 && instrumentNumber <= 95) {// Synth Pad
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 96 && instrumentNumber <= 103) {// Synth Effects
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 104 && instrumentNumber <= 111) {// Ethnic
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 13; //bit
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} else if (instrumentNumber >= 112 && instrumentNumber <= 119) {// Percussive
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {
|
||
|
MCInstrument = 12; //digeridoo
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {
|
||
|
MCInstrument = 10; //iron xylophone
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {
|
||
|
MCInstrument = 6; //bell
|
||
|
}
|
||
|
|
||
|
} /*else if (instrumentNumber >= 120 && instrumentNumber <= 127) {// Sound Effects
|
||
|
if (midiPitch >= 30 && midiPitch <= 54) {//low (bass, digeridoo)
|
||
|
MCInstrument = 0;
|
||
|
} else if (midiPitch >= 54 && midiPitch <= 78) {//medium (harp, iron xylophone, bit, banjo, pling)
|
||
|
MCInstrument = 0;
|
||
|
} else if (midiPitch >= 78 && midiPitch <= 102) {//high (bells, chimes, xylophone)
|
||
|
MCInstrument = 0;
|
||
|
}
|
||
|
|
||
|
}*/ // very unlikley to see in a midi file so just be harp...
|
||
|
}
|
||
|
|
||
|
// default to harp if nothing found
|
||
|
return MCInstrument; //harp
|
||
|
}
|