Advertisement
NoTextForSpeech

afaffsda

Mar 5th, 2024 (edited)
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.18 KB | None | 0 0
  1.  
  2. const os = require('os');
  3. const playSong = require('./playSong');
  4. const sys = require('sys');
  5.  
  6. class MidiFile {
  7.     static startSequence = [
  8.         [0x4D, 0x54, 0x68, 0x64],
  9.         [0x4D, 0x54, 0x72, 0x6B],
  10.         [0xFF]
  11.     ];
  12.  
  13.     static typeDict = {
  14.         0x00: "Sequence Number",
  15.         0x01: "Text Event",
  16.         0x02: "Copyright Notice",
  17.         0x03: "Sequence/Track Name",
  18.         0x04: "Instrument Name",
  19.         0x05: "Lyric",
  20.         0x06: "Marker",
  21.         0x07: "Cue Point",
  22.         0x20: "MIDI Channel Prefix",
  23.         0x2F: "End of Track",
  24.         0x51: "Set Tempo",
  25.         0x54: "SMTPE Offset",
  26.         0x58: "Time Signature",
  27.         0x59: "Key Signature",
  28.         0x7F: "Sequencer-Specific Meta-event",
  29.         0x21: "Prefix Port",
  30.         0x20: "Prefix Channel",
  31.         0x09: "Other text format [0x09]",
  32.         0x08: "Other text format [0x08]",
  33.         0x0A: "Other text format [0x0A]",
  34.         0x0C: "Other text format [0x0C]"
  35.     };
  36.  
  37.     constructor(midi_file, verbose = false, debug = false) {
  38.         this.verbose = verbose;
  39.         this.debug = debug;
  40.         this.bytes = -1;
  41.         this.headerLength = -1;
  42.         this.headerOffset = 23;
  43.         this.format = -1;
  44.         this.tracks = -1;
  45.         this.division = -1;
  46.         this.divisionType = -1;
  47.         this.itr = 0;
  48.         this.runningStatus = -1;
  49.         this.tempo = 0;
  50.         this.midiRecord_list = [];
  51.         this.record_file = "midiRecord.txt";
  52.         this.midi_file = midi_file;
  53.         this.deltaTimeStarted = false;
  54.         this.deltaTime = 0;
  55.         this.key_press_count = 0;
  56.         this.virtualPianoScale = Array.from("1!2@34$5%6^78*9(0qQwWeErtTyYuiIoOpPasSdDfgGhHjJklLzZxcCvVbBnm");
  57.         this.startCounter = Array(MidiFile.startSequence.length).fill(0);
  58.         this.runningStatusSet = false;
  59.         this.events = [];
  60.         this.notes = [];
  61.         this.success = false;
  62.         console.log("Processing", midi_file);
  63.         try {
  64.             const fs = require('fs');
  65.             this.bytes = fs.readFileSync(this.midi_file);
  66.             this.readEvents();
  67.             console.log(this.key_press_count, "notes processed");
  68.             this.clean_notes();
  69.             this.success = true;
  70.         } finally {
  71.             this.save_record(this.record_file);
  72.         }
  73.     }
  74.  
  75.     checkStartSequence() {
  76.         for (let i = 0; i < this.startSequence.length; i++) {
  77.             if (this.startSequence[i].length === this.startCounter[i]) {
  78.                 return true;
  79.             }
  80.         }
  81.         return false;
  82.     }
  83.  
  84.     skip(i) {
  85.         this.itr += i;
  86.     }
  87.  
  88.     readLength() {
  89.         let contFlag = true;
  90.         let length = 0;
  91.         while (contFlag) {
  92.             if ((this.bytes[this.itr] & 0x80) >> 7 === 0x1) {
  93.                 length = (length << 7) + (this.bytes[this.itr] & 0x7F);
  94.             } else {
  95.                 contFlag = false;
  96.                 length = (length << 7) + (this.bytes[this.itr] & 0x7F);
  97.             }
  98.             this.itr += 1;
  99.         }
  100.         return length;
  101.     }
  102.  
  103.     readMTrk() {
  104.         const length = this.getInt(4);
  105.         this.log("MTrk len", length);
  106.         this.readMidiTrackEvent(length);
  107.     }
  108.  
  109.     readMThd() {
  110.         this.headerLength = this.getInt(4);
  111.         this.log("HeaderLength", this.headerLength);
  112.         this.format = this.getInt(2);
  113.         this.tracks = this.getInt(2);
  114.         const div = this.getInt(2);
  115.         this.divisionType = (div & 0x8000) >> 16;
  116.         this.division = div & 0x7FFF;
  117.         this.log(`Format ${this.format}\nTracks ${this.tracks}\nDivisionType ${this.divisionType}\nDivision ${this.division}`);
  118.     }
  119.  
  120.     readText(length) {
  121.         let s = "";
  122.         const start = this.itr;
  123.         while (this.itr < length + start) {
  124.             s += String.fromCharCode(this.bytes[this.itr]);
  125.             this.itr += 1;
  126.         }
  127.         return s;
  128.     }
  129.  
  130.     readMidiMetaEvent(deltaT) {
  131.         const type = this.bytes[this.itr];
  132.         this.itr += 1;
  133.         const length = this.readLength();
  134.         let eventName;
  135.         try {
  136.             eventName = this.typeDict[type];
  137.         } catch {
  138.             eventName = "Unknown Event " + type;
  139.         }
  140.         this.log("MIDIMETAEVENT", eventName, "LENGTH", length, "DT", deltaT);
  141.         if (type === 0x2F) {
  142.             this.log("END TRACK");
  143.             this.itr += 2;
  144.             return false;
  145.         } else if ([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0C].includes(type)) {
  146.             this.log("\t", this.readText(length));
  147.         } else if (type === 0x51) {
  148.             const tempo = Math.round(60000000 / this.getInt(3));
  149.             this.tempo = tempo;
  150.             this.notes.push([(this.deltaTime / this.division), "tempo=" + tempo]);
  151.             this.log("\tNew tempo is", tempo);
  152.         } else {
  153.             this.itr += length;
  154.         }
  155.         return true;
  156.     }
  157.  
  158.     readMidiTrackEvent(length) {
  159.         this.log("TRACKEVENT");
  160.         this.deltaTime = 0;
  161.         const start = this.itr;
  162.         let continueFlag = true;
  163.         while (length > this.itr - start && continueFlag) {
  164.             const deltaT = this.readLength();
  165.             this.deltaTime += deltaT;
  166.             if (this.bytes[this.itr] === 0xFF) {
  167.                 this.itr += 1;
  168.                 continueFlag = this.readMidiMetaEvent(deltaT);
  169.             } else if (this.bytes[this.itr] >= 0xF0 && this.bytes[this.itr] <= 0xF7) {
  170.                 this.runningStatusSet = false;
  171.                 this.runningStatus = -1;
  172.                 this.log("RUNNING STATUS SET:", "CLEARED");
  173.             } else {
  174.                 this.readVoiceEvent(deltaT);
  175.             }
  176.         }
  177.         this.log("End of MTrk event, jumping from", this.itr, "to", start + length);
  178.         this.itr = start + length;
  179.     }
  180.  
  181.     readVoiceEvent(deltaT) {
  182.         let type, channel;
  183.         if (this.bytes[this.itr] < 0x80 && this.runningStatusSet) {
  184.             type = this.runningStatus;
  185.             channel = type & 0x0F;
  186.         } else {
  187.             type = this.bytes[this.itr];
  188.             channel = this.bytes[this.itr] & 0x0F;
  189.             if (type >= 0x80 && type <= 0xF7) {
  190.                 this.log("RUNNING STATUS SET:", hex(type));
  191.                 this.runningStatus = type;
  192.                 this.runningStatusSet = true;
  193.             }
  194.             this.itr += 1;
  195.         }
  196.         if (type >> 4 === 0x9) {
  197.             const key = this.bytes[this.itr];
  198.             this.itr += 1;
  199.             const velocity = this.bytes[this.itr];
  200.             this.itr += 1;
  201.             let map = key - 23 - 12 - 1;
  202.             while (map >= this.virtualPianoScale.length) {
  203.                 map -= 12;
  204.             }
  205.             while (map < 0) {
  206.                 map += 12;
  207.             }
  208.             if (velocity === 0) {
  209.                 this.log(this.deltaTime / this.division, "~" + this.virtualPianoScale[map]);
  210.                 this.notes.push([(this.deltaTime / this.division), "~" + this.virtualPianoScale[map]]);
  211.             } else {
  212.                 this.log(this.deltaTime / this.division, this.virtualPianoScale[map]);
  213.                 this.notes.push([(this.deltaTime / this.division), this.virtualPianoScale[map]]);
  214.                 this.key_press_count += 1;
  215.             }
  216.         } else if (type >> 4 === 0x8) {
  217.             const key = this.bytes[this.itr];
  218.             this.itr += 1;
  219.             const velocity = this.bytes[this.itr];
  220.             this.itr += 1;
  221.             let map = key - 23 - 12 - 1;
  222.             while (map >= this.virtualPianoScale.length) {
  223.                 map -= 12;
  224.             }
  225.             while (map < 0) {
  226.                 map += 12;
  227.             }
  228.             this.log(this.deltaTime / this.division, "~" + this.virtualPianoScale[map]);
  229.             this.notes.push([(this.deltaTime / this.division), "~" + this.virtualPianoScale[map]]);
  230.         } else if (![0x8, 0x9, 0xA, 0xB, 0xD, 0xE].includes(type >> 4)) {
  231.             this.log("VoiceEvent", hex(type), hex(this.bytes[this.itr]), "DT", deltaT);
  232.             this.itr += 1;
  233.         } else {
  234.             this.log("VoiceEvent", hex(type), hex(this.bytes[this.itr]), hex(this.bytes[this.itr + 1]), "DT", deltaT);
  235.             this.itr += 2;
  236.         }
  237.     }
  238.  
  239.     readEvents() {
  240.         while (this.itr + 1 < this.bytes.length) {
  241.             for (let i = 0; i < this.startCounter.length; i++) {
  242.                 this.startCounter[i] = 0;
  243.             }
  244.             while (this.itr + 1 < this.bytes.length && !this.checkStartSequence()) {
  245.                 for (let i = 0; i < this.startSequence.length; i++) {
  246.                     if (this.bytes[this.itr] === this.startSequence[i][this.startCounter[i]]) {
  247.                         this.startCounter[i] += 1;
  248.                     } else {
  249.                         this.startCounter[i] = 0;
  250.                     }
  251.                 }
  252.                 if (this.itr + 1 < this.bytes.length) {
  253.                     this.itr += 1;
  254.                 }
  255.                 if (this.startCounter[0] === 4) {
  256.                     this.readMThd();
  257.                 } else if (this.startCounter[1] === 4) {
  258.                     this.readMTrk();
  259.                 }
  260.             }
  261.         }
  262.     }
  263.  
  264.     log(...arg) {
  265.         if (this.verbose || this.debug) {
  266.             for (let s = 0; s < arg.length; s++) {
  267.                 try {
  268.                     process.stdout.write(`${arg[s]} `);
  269.                     this.midiRecord_list.push(`${arg[s]} `);
  270.                 } catch {
  271.                     process.stdout.write("[?] ");
  272.                     this.midiRecord_list.push("[?] ");
  273.                 }
  274.             }
  275.             console.log();
  276.             if (this.debug) {
  277.                 const readlineSync = require('readline-sync');
  278.                 readlineSync.question();
  279.             }
  280.             this.midiRecord_list.push("\n");
  281.         } else {
  282.             for (let s = 0; s < arg.length; s++) {
  283.                 try {
  284.                     this.midiRecord_list.push(`${arg[s]} `);
  285.                 } catch {
  286.                     this.midiRecord_list.push("[?] ");
  287.                 }
  288.             }
  289.             this.midiRecord_list.push("\n");
  290.         }
  291.     }
  292.  
  293.     getInt(i) {
  294.         let k = 0;
  295.         for (const n of this.bytes.slice(this.itr, this.itr + i)) {
  296.             k = (k << 8) + n;
  297.         }
  298.         this.itr += i;
  299.         return k;
  300.     }
  301.  
  302.     round(i) {
  303.         const up = Math.floor(i + 1);
  304.         const down = Math.floor(i - 1);
  305.         if (up - i < i - down) {
  306.             return up;
  307.         } else {
  308.             return down;
  309.         }
  310.     }
  311.  
  312.     clean_notes() {
  313.         this.notes = this.notes.sort((a, b) => parseFloat(a[0]) - parseFloat(b[0]));
  314.         if (this.verbose) {
  315.             for (const x of this.notes) {
  316.                 console.log(x);
  317.             }
  318.         }
  319.         let i = 0;
  320.         while (i < this.notes.length - 1) {
  321.             const a_time = this.notes[i][0];
  322.             const b_time = this.notes[i + 1][0];
  323.             if (a_time === b_time) {
  324.                 const a_notes = this.notes[i][1];
  325.                 const b_notes = this.notes[i + 1][1];
  326.                 if (!a_notes.includes("tempo") && !b_notes.includes("tempo") && !a_notes.includes("~") && !b_notes.includes("~")) {
  327.                     this.notes[i][1] += this.notes[i + 1][1];
  328.                     this.notes.splice(i + 1, 1);
  329.                 } else {
  330.                     i += 1;
  331.                 }
  332.             } else {
  333.                 i += 1;
  334.             }
  335.         }
  336.         for (let q = 0; q < this.notes.length; q++) {
  337.             const letterDict = {};
  338.             const newline = [];
  339.             if (!this.notes[q][1].includes("tempo") && !this.notes[q][1].includes("~")) {
  340.                 for (let i = 0; i < this.notes[q][1].length; i++) {
  341.                     if (!(this.notes[q][1][i] in letterDict)) {
  342.                         newline.push(this.notes[q][1][i]);
  343.                         letterDict[this.notes[q][1][i]] = true;
  344.                     }
  345.                 }
  346.                 this.notes[q][1] = newline.join("");
  347.             }
  348.         }
  349.     }
  350.  
  351.     save_song(song_file) {
  352.         console.log("Saving notes to", song_file);
  353.         const fs = require('fs');
  354.         fs.writeFileSync(song_file, `playback_speed=1.1\n${this.notes.map(l => `${l[0]} ${l[1]}`).join("\n")}`);
  355.     }
  356.  
  357.     save_sheet(sheet_file) {
  358.         console.log("Saving sheets to", sheet_file);
  359.         const offset = this.notes[0][0];
  360.         let noteCount = 0;
  361.         const fs = require('fs');
  362.         const stream = fs.createWriteStream(sheet_file);
  363.         for (const [timing, notes] of this.notes) {
  364.             if (!notes.includes("tempo") && !notes.includes("~")) {
  365.                 const note = notes.length > 1 ? `[${notes}]` : notes;
  366.                 noteCount += 1;
  367.                 stream.write(`${note.padStart(7)} `);
  368.                 if (noteCount % 8 === 0) {
  369.                     stream.write("\n");
  370.                 }
  371.             }
  372.         }
  373.         stream.end();
  374.     }
  375.  
  376.     save_record(record_file) {
  377.         console.log("Saving processing log to", record_file);
  378.         const fs = require('fs');
  379.         fs.writeFileSync(record_file, this.midiRecord_list.join(""));
  380.     }
  381. }
  382.  
  383. function get_file_choice() {
  384.     const midi_folder = 'midi';
  385.     if (!fs.existsSync(midi_folder)) {
  386.         fs.mkdirSync(midi_folder);
  387.     }
  388.     const midList = fs.readdirSync(midi_folder).filter(f => f.toLowerCase().endsWith('.mid'));
  389.     if (!midList.length) {
  390.         console.log("No MIDI files detected. Please add MIDI files to the 'midi' folder.");
  391.         return null;
  392.     }
  393.     console.log("\nType the number of a MIDI file and press enter:\n");
  394.     for (let i = 0; i < midList.length; i++) {
  395.         console.log(`${i + 1}: ${midList[i]}`);
  396.     }
  397.     try {
  398.         const readlineSync = require('readline-sync');
  399.         const choice = parseInt(readlineSync.question("> "));
  400.         return path.join(midi_folder, midList[choice - 1]);
  401.     } catch (error) {
  402.         console.log("Invalid selection. Please try again.");
  403.         return null;
  404.     }
  405. }
  406.  
  407. function runPlaySong() {
  408.     try {
  409.         playSong.main();
  410.     } catch (error) {
  411.         console.log(`Failed to run playSong.py: ${error}`);
  412.     }
  413. }
  414.  
  415. function main() {
  416.     let midi_file;
  417.     if (process.argv.length > 2) {
  418.         midi_file = process.argv[2];
  419.         if (!fs.existsSync(midi_file)) {
  420.             console.log(`Error: file not found '${midi_file}'`);
  421.             return 1;
  422.         }
  423.         if (![".mid", ".mid"].some(ext => midi_file.toLowerCase().includes(ext))) {
  424.             console.log(`'${midi_file}' has an incorrect file extension`);
  425.             console.log("Make sure this file ends in '.mid'");
  426.             return 1;
  427.         }
  428.     } else {
  429.         midi_file = get_file_choice();
  430.         if (midi_file === null) {
  431.             return 1;
  432.         }
  433.     }
  434.     try {
  435.         const midi = new MidiFile(midi_file);
  436.         const song_file = "song.txt";
  437.         const sheet_file = "sheetConversion.txt";
  438.         midi.save_song(song_file);
  439.         midi.save_sheet(sheet_file);
  440.         runPlaySong();
  441.     } catch (error) {
  442.         console.log("An error has occurred during processing:\n\n");
  443.         throw error;
  444.         return 1;
  445.     }
  446. }
  447.  
  448. main();
  449.  
  450.  
  451.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement