How to Use:
- Run the script.
- In the GUI window, you’ll see checkboxes arranged in 4 columns, each containing 12 instruments.
- Select the desired instruments and click the “Select MIDI file” button.
- The script will create a .funscript file with the chosen actions only for the selected instruments and display a confirmation message upon successful export.
instal.bat
pip install mido
mid2funscript.py
import mido
import tkinter as tk
from tkinter import filedialog, messagebox
import os
import json
# Definition of drum instruments with their corresponding MIDI values
drum_instruments = {
"Acoustic Bass Drum": 35,
"Bass Drum 1": 36,
"Side Stick": 37,
"Acoustic Snare": 38,
"Hand Clap": 39,
"Electric Snare": 40,
"Low Floor Tom": 41,
"Closed Hi-Hat": 42,
"High Floor Tom": 43,
"Pedal Hi-Hat": 44,
"Low Tom": 45,
"Open Hi-Hat": 46,
"Low-Mid Tom": 47,
"Hi-Mid Tom": 48,
"Crash Cymbal 1": 49,
"High Tom": 50,
"Ride Cymbal 1": 51,
"Chinese Cymbal": 52,
"Ride Bell": 53,
"Tambourine": 54,
"Splash Cymbal": 55,
"Cowbell": 56,
"Crash Cymbal 2": 57,
"Vibraslap": 58,
"Ride Cymbal 2": 59,
"Hi Bongo": 60,
"Low Bongo": 61,
"Mute Hi Conga": 62,
"Open Hi Conga": 63,
"Low Conga": 64,
"High Timbale": 65,
"Low Timbale": 66,
"High Agogo": 67,
"Low Agogo": 68,
"Cabasa": 69,
"Maracas": 70,
"Short Whistle": 71,
"Long Whistle": 72,
"Short Guiro": 73,
"Long Guiro": 74,
"Claves": 75,
"Hi Wood Block": 76,
"Low Wood Block": 77,
"Mute Cuica": 78,
"Open Cuica": 79,
"Mute Triangle": 80,
"Open Triangle": 81,
"Shaker": 82
}
# Function to export drum notes starts according to user selection
def export_drum_note_starts_to_funscript(midi_file, output_file, selected_instruments):
mid = mido.MidiFile(midi_file)
actions = []
cumulative_time = 0
last_time_ms = None # Stores the last recorded action time
for msg in mid: # Iterate through all events in all tracks
cumulative_time += msg.time
# Convert time to milliseconds and round
current_time_ms = round(cumulative_time * 1000)
# Check if the message has a 'channel' attribute (is not 'MetaMessage') and is on the drum channel
if msg.type == 'note_on' and hasattr(msg, 'channel') and msg.channel == 9 and msg.velocity > 0:
# Filter only selected instruments
if msg.note in selected_instruments:
# Ignore notes that occur at the same time
if current_time_ms != last_time_ms:
if current_time_ms > 0:
# Add action for position 0 (down) and start time only if time > 0
actions.append({"pos": 0, "at": current_time_ms - 1})
# Add action for position 100 (up) and current start time
actions.append({"pos": 100, "at": current_time_ms})
# Update the time of the last recorded action
last_time_ms = current_time_ms
# Define JSON content in funscript format
funscript_data = {
"version": "1.0",
"inverted": False,
"range": 90,
"info": "Automatic generation script from midi file",
"actions": actions
}
# Save to JSON file with .funscript extension
with open(output_file, 'w') as f:
json.dump(funscript_data, f, indent=4)
# Function to select file and export according to user selection
def select_file():
# Open file selection dialog
midi_file = filedialog.askopenfilename(
filetypes=[("MIDI files", "*.mid *.midi")],
title="Select MIDI file"
)
if midi_file:
# Generate output file name with the same name but .funscript extension
output_file = os.path.splitext(midi_file)[0] + '.funscript'
# Get selected instruments
selected_instruments = [
note for instrument, note in drum_instruments.items()
if instrument_vars[instrument].get() == 1
]
# Export note times to Funscript format
export_drum_note_starts_to_funscript(midi_file, output_file, selected_instruments)
# Notify user
messagebox.showinfo("Done", f"Drum channel note starts have been successfully exported to {output_file}")
# Set up GUI
root = tk.Tk()
root.title("MIDI to Funscript Converter - Drum Channel Only")
# Frame for checkboxes
checkbox_frame = tk.Frame(root)
checkbox_frame.pack(pady=10)
# Variables to store checkbox states
instrument_vars = {}
# Create checkboxes in 4 columns of 12 instruments each
columns = 4
instruments_per_column = 12
instrument_names = list(drum_instruments.keys())
for col in range(columns):
frame = tk.Frame(checkbox_frame)
frame.grid(row=0, column=col, padx=10, pady=5)
for row in range(instruments_per_column):
instrument_index = col * instruments_per_column + row
if instrument_index < len(instrument_names):
instrument = instrument_names[instrument_index]
var = tk.IntVar(value=1 if instrument in ["Acoustic Bass Drum", "Bass Drum 1"] else 0)
instrument_vars[instrument] = var
checkbox = tk.Checkbutton(frame, text=instrument, variable=var)
checkbox.pack(anchor="w")
# Button to select file
select_button = tk.Button(root, text="Select MIDI file", command=select_file)
select_button.pack(pady=20)
# Run GUI application
root.mainloop()