Last active
May 16, 2025 20:53
-
-
Save NAEL2XD/c5a590137903f3f781c2bba013474c2f to your computer and use it in GitHub Desktop.
FNF: MIDI to FNF Psych Engine Compatible chart.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Converts a single midi to a fnf chart for psych engine <0.7.3 | |
| Odd Instruments are DAD section and Even Instruments are BF notes. | |
| REQUIRES pretty_midi and tqdm: pip install pretty_midi, tqdm | |
| You need "import.mid" next to the py file or else you'll get an error! | |
| """ | |
| import pretty_midi | |
| from tqdm import tqdm | |
| import os.path | |
| def checkExist(path): | |
| if not os.path.exists(path): os.mkdir(path) | |
| def checkDelete(path): | |
| if os.path.exists(path): os.remove(path) | |
| pretty_midi.pretty_midi.MAX_TICK = 1e10 | |
| midi = pretty_midi.PrettyMIDI("import.mid") | |
| tempo_times, tempos = midi.get_tempo_changes() | |
| if int(tempos[0]) == 120: | |
| tempos[0] = float(input("Set the BPM: ")) | |
| bpmSpeed = tempos[0]/120 | |
| else: | |
| bpmSpeed = float(input("BPM Mult? 1 for 1x, 2 for 2x, etc.\n> ")) | |
| songName = input("Song name: ") | |
| speed = float(input("Speed: ")) | |
| isDad = input("Start as Must HIT (dad will sing first if it's y)? [y/n] ") == 'n' | |
| instCount = 0 | |
| storage = [] | |
| notes = [] | |
| coolJSON = '{"song":{"player1":"bf","events":[],"gfVersion":"gf","notes":[' | |
| for instrument in tqdm(midi.instruments, desc="Reading..."): | |
| isDad = not isDad | |
| instCount += 1 | |
| count = 0 | |
| coolJSON += '{"sectionNotes":[' | |
| for note in tqdm(instrument.notes): | |
| count += 1 | |
| coolJSON += f'[{(note.start*1000)/bpmSpeed},{note.pitch % 4 + (4 if isDad else 0)},{((note.end*1000) - (note.start*1000))/bpmSpeed}]' | |
| if len(instrument.notes) != count: coolJSON += "," | |
| if len(coolJSON) > 1000000: | |
| storage.append(coolJSON) | |
| coolJSON = "" | |
| coolJSON += '],"lengthInSteps":16,"mustHitSection":false}' + ("," if len(midi.instruments) != instCount else "") | |
| storage.append(coolJSON) | |
| storage.append('],"player2":"dad","song":"' + songName + '","needsVoices":true,"validScore":true,"bpm":' + str(tempos[0]) + ',"speed":' + str(speed) + ',"generatedBy":"Nael\'s MIDI to FNF chart"}}') | |
| coolJSON = "" | |
| for i in tqdm(range(len(storage)), desc="Saving..."): | |
| coolJSON += storage[i] | |
| checkExist('out') | |
| save = open(f'out/{songName}.json', 'w') | |
| save.write(coolJSON) | |
| save.close() | |
| makeAsMod = input("Done saving! Do you want to make it as a mod? [y/n] ") == "y" | |
| if makeAsMod: | |
| convMod = songName.lower().replace(' ', '-').replace('.', '') | |
| checkDelete(f'mods/weeks/{convMod}.json') | |
| checkDelete(f'mods/songs/{convMod}/place your inst here') | |
| checkDelete(f'mods/data/{convMod}/{convMod}.json') | |
| checkExist("mods") | |
| checkExist("mods/data") | |
| checkExist("mods/songs") | |
| checkExist("mods/weeks") | |
| checkExist(f"mods/data/{convMod}") | |
| checkExist(f"mods/songs/{convMod}") | |
| week = '{"songs":[["' + songName + '","bf",[12,181,0]]],"hideFreeplay":false,"weekBackground":"","difficulties":"Normal","weekCharacters":["","",""],"storyName":"Converted Using Nael\'s MIDI to FNF Script","weekName":"","freeplayColor":[146,113,253],"hideStoryMode":false,"weekBefore":"","startUnlocked":false}' | |
| save = open(f'mods/weeks/{convMod}.json', 'w') | |
| save.write(week) | |
| save.close() | |
| save = open(f'mods/songs/{convMod}/place your inst here', 'w') | |
| save.close() | |
| os.rename(f'out/{songName}.json', f'mods/data/{convMod}/{convMod}.json') | |
| print("Done") | |
| os.system("pause") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment