├── README.md └── MIDIconverter.py /README.md: -------------------------------------------------------------------------------- 1 | # TEXT to MIDI converter 1.0 build 2 | # Main features 3 | `this python script writes .MID file from inputted text in SMF0 format. can be used with external Ai such as Chat-GPT` 4 | 5 | # Text input windows 6 | 7 | it has 4 text windows to input text and function detailed next to them. very easy to use. 8 | listed from up to down all windows. 9 | | Rank | Function | 10 | |-----:|---------------| 11 | | 1| Track name | 12 | | 2| Time siganture| 13 | | 3| BPM | 14 | | 4| Text to .MID | 15 | | 5| Example text | 16 | | 6| Log | 17 | 18 | EMPTY one without description is where you insert text to turn into .MID file such as shown in window under it. 19 | 20 | ![MIDIconvUI](https://github.com/potkolainen/text-to-midi/assets/135180930/82ee6cf9-b70d-4349-83c2-469cd11a648f) 21 | 22 | 23 | # Errors 24 | Log lists events for you, such as .MID save status or errors. 25 | Most common error is when all text fields are not filled. 26 | It will be shown as: 27 | 28 | Error occurred while saving MIDI: invalid literal for int() with base 10: '' 29 | This means track name, time signature or BPM was not inserted 30 | 31 | # Reddit 32 | 33 | https://www.reddit.com/r/musicproduction/comments/13xlfwd/text_to_midi_file/?utm_source=share&utm_medium=android_app&utm_name=androidcss&utm_term=1&utm_content=share_button 34 | If you run in issues. You can comment them there, or here on github. Also you can give feedback and suggestions on reddit. 35 | 36 | # Needed Packages for the python script 37 | python script uses packages that are needed for running the script 38 | | Rank | Packages | 39 | |-----:|-----------| 40 | | 1| Python3.11| 41 | | 2| mido | 42 | | 3| pyperclip | 43 | 44 | Rest are included in standard python library. 45 | For me it helped when i installed Python from microsoft store. My cmd terminal didnt find the one i installed first from python.org 46 | 47 | once python 3.11 is installed open your windows command terminal 48 | type command: 49 | 50 | pip install mido 51 | 52 | And 53 | 54 | pip install pyperclip 55 | 56 | These commands will install nedded packages from python directory. 57 | 58 | 59 | # .exe 60 | includes also .exe file in [standalone BRANCH.](https://github.com/potkolainen/text-to-midi/tree/onefile-pyinstall-(.exe-standalone)) or [1.0 Release](https://github.com/potkolainen/text-to-midi/releases/tag/v1.0exe) 61 | MAIN branch has only Open source python code. 62 | This .exe does not need Python or packages installed. (download .exe from STANDALONE Branch) 63 | 64 | -------------------------------------------------------------------------------- /MIDIconverter.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import filedialog 3 | import mido 4 | 5 | def save_midi_file(track_name, time_signature, bpm, midi_text, file_path): 6 | try: 7 | mid = mido.MidiFile() 8 | track = mido.MidiTrack() 9 | mid.tracks.append(track) 10 | 11 | # Set track name 12 | track.name = track_name 13 | 14 | # Set time signature 15 | numerator, denominator = map(int, time_signature.split("/")) 16 | time_signature_meta = mido.MetaMessage("time_signature", numerator=numerator, denominator=denominator) 17 | track.append(time_signature_meta) 18 | 19 | # Calculate tempo from BPM 20 | tempo = mido.bpm2tempo(float(bpm)) 21 | tempo_meta = mido.MetaMessage("set_tempo", tempo=tempo) 22 | track.append(tempo_meta) 23 | 24 | for line in midi_text.split("\n"): 25 | line = line.strip() 26 | if line.startswith("MetaMessage"): 27 | parts = line.split(",") 28 | if parts[0] == "MetaMessage('end_of_track'": 29 | track.append(mido.MetaMessage("end_of_track", time=int(parts[1].split("=")[1].strip(")")))) 30 | elif line.startswith("note_on") or line.startswith("note_off"): 31 | parts = line.split() 32 | note_on = line.startswith("note_on") 33 | channel = int(parts[1].split("=")[1]) 34 | note = int(parts[2].split("=")[1]) 35 | velocity = int(parts[3].split("=")[1]) 36 | time = int(parts[4].split("=")[1]) 37 | if note_on: 38 | track.append(mido.Message("note_on", channel=channel, note=note, velocity=velocity, time=time)) 39 | else: 40 | track.append(mido.Message("note_off", channel=channel, note=note, velocity=velocity, time=time)) 41 | elif line.startswith("control_change"): 42 | parts = line.split() 43 | channel = int(parts[1].split("=")[1]) 44 | control = int(parts[2].split("=")[1]) 45 | value = int(parts[3].split("=")[1]) 46 | time = int(parts[4].split("=")[1]) 47 | track.append(mido.Message("control_change", channel=channel, control=control, value=value, time=time)) 48 | 49 | # Add the .mid extension to the file path if it doesn't have one 50 | if not file_path.endswith(".mid"): 51 | file_path += ".mid" 52 | 53 | mid.save(file_path) 54 | log_text.insert("1.0", "MIDI file saved successfully!\n") 55 | except Exception as e: 56 | log_text.insert("1.0", f"Error occurred while saving MIDI: {str(e)}\n") 57 | 58 | def select_save_path(): 59 | file_path = filedialog.asksaveasfilename(filetypes=[("MIDI files", "*.mid")]) 60 | if file_path: 61 | save_midi_file(track_name_entry.get(), time_signature_entry.get(), bpm_entry.get(), text_field.get(1.0, tk.END), file_path) 62 | 63 | def copy_example_text(): 64 | example_text = example_label.cget("text") 65 | window.clipboard_clear() 66 | window.clipboard_append(example_text) 67 | window.update() 68 | 69 | # Create the main window 70 | window = tk.Tk() 71 | window.title("MIDI Converter") 72 | window.configure(bg="black") 73 | 74 | # Configure rows and columns to expand and fill available space 75 | window.grid_rowconfigure(0, weight=1) 76 | window.grid_rowconfigure(1, weight=1) 77 | window.grid_rowconfigure(2, weight=1) 78 | window.grid_rowconfigure(3, weight=1) 79 | window.grid_rowconfigure(4, weight=1) 80 | window.grid_rowconfigure(5, weight=1) 81 | window.grid_rowconfigure(6, weight=1) 82 | window.grid_rowconfigure(7, weight=1) 83 | window.grid_columnconfigure(0, weight=1) 84 | window.grid_columnconfigure(1, weight=1) 85 | window.grid_columnconfigure(2, weight=1) 86 | 87 | # Create the track name label and entry 88 | track_name_label = tk.Label(window, text="Enter track name:", bg="black", fg="white") 89 | track_name_label.grid(row=0, column=0, padx=10, pady=5, sticky=tk.W) 90 | 91 | track_name_entry = tk.Entry(window, width=30, bg="lightgray", fg="black") 92 | track_name_entry.grid(row=0, column=1, padx=10, pady=5, sticky=tk.W) 93 | 94 | # Create the time signature label and entry 95 | time_signature_label = tk.Label(window, text="Enter time signature (e.g., 4/4):", bg="black", fg="white") 96 | time_signature_label.grid(row=1, column=0, padx=10, pady=5, sticky=tk.W) 97 | 98 | time_signature_entry = tk.Entry(window, width=30, bg="lightgray", fg="black") 99 | time_signature_entry.grid(row=1, column=1, padx=10, pady=5, sticky=tk.W) 100 | 101 | # Create the BPM label and entry 102 | bpm_label = tk.Label(window, text="Enter BPM:", bg="black", fg="white") 103 | bpm_label.grid(row=2, column=0, padx=10, pady=5, sticky=tk.W) 104 | 105 | bpm_entry = tk.Entry(window, width=30, bg="lightgray", fg="black") 106 | bpm_entry.grid(row=2, column=1, padx=10, pady=5, sticky=tk.W) 107 | 108 | # Create the text field 109 | text_field = tk.Text(window, height=10, width=60, bg="lightgray") 110 | text_field.grid(row=3, column=0, columnspan=2, padx=10, pady=5, sticky="nsew") 111 | 112 | # Create the example text 113 | example_text = """ 114 | note_on channel=0 note=60 velocity=64 time=100 115 | note_off channel=0 note=60 velocity=0 time=50 116 | note_on channel=1 note=64 velocity=72 time=80 117 | note_off channel=1 note=64 velocity=0 time=70 118 | note_on channel=2 note=67 velocity=80 time=60 119 | note_off channel=2 note=67 velocity=0 time=90 120 | control_change channel=0 control=64 value=127 time=0 121 | control_change channel=1 control=64 value=64 time=0 122 | control_change channel=2 control=64 value=32 time=0 123 | """ 124 | 125 | example_label = tk.Label(window, text=example_text, font=("Courier New", 10), bg="lightgray", padx=10, pady=10) 126 | example_label.grid(row=4, column=0, columnspan=2, padx=10, pady=5, sticky="nsew") 127 | 128 | # Create the copy button 129 | copy_button = tk.Button(window, text="Copy Example Text", command=copy_example_text, bg="black", fg="white") 130 | copy_button.grid(row=5, column=0, columnspan=2, padx=10, pady=5) 131 | 132 | # Create the log text 133 | log_text = tk.Text(window, height=6, width=60, bg="lightgray") 134 | log_text.grid(row=6, column=0, columnspan=2, padx=10, pady=5, sticky="nsew") 135 | log_text.insert("1.0", """Above you see example MIDI text that you can copy and paste 136 | to any Ai that is capable of providing you melodies using 137 | it as an EXAMPLE. For now i recommend chat-GPT. 138 | NOTE is ON/OFF, what NOTE, Velocity & When 139 | CONTROL CHANGE is sustain pedal.""") 140 | 141 | # Create the save button 142 | save_button = tk.Button(window, text="Save MIDI File", command=select_save_path, bg="black", fg="white") 143 | save_button.grid(row=7, column=0, columnspan=2, padx=10, pady=5) 144 | 145 | # Run the application 146 | window.mainloop() 147 | --------------------------------------------------------------------------------