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 }